tum il y a 2 ans
Parent
commit
c159e1b993
5 fichiers modifiés avec 136 ajouts et 13 suppressions
  1. 47 0
      package-lock.json
  2. 4 0
      package.json
  3. 45 9
      src/components/CourseMat.vue
  4. 13 2
      src/composable/settings.ts
  5. 27 2
      src/views/CourseDetailPage.vue

+ 47 - 0
package-lock.json

@@ -26,12 +26,16 @@
26 26
         "axios": "^1.6.0",
27 27
         "axios-cache-interceptor": "^1.3.2",
28 28
         "axios-extensions": "^3.1.6",
29
+        "human-number": "^2.0.4",
30
+        "humanize-plus": "^1.8.2",
29 31
         "ionicons": "^7.0.0",
30 32
         "marked": "^10.0.0",
31 33
         "moment": "^2.29.4",
34
+        "pluralize": "^8.0.0",
32 35
         "swiper": "^11.0.3",
33 36
         "vue": "^3.2.45",
34 37
         "vue-core-video-player": "^0.2.0",
38
+        "vue-read-more": "^1.1.1",
35 39
         "vue-router": "^4.1.6",
36 40
         "vue-vimeo-player": "^1.1.2"
37 41
       },
@@ -6373,6 +6377,17 @@
6373 6377
         "node": ">= 6"
6374 6378
       }
6375 6379
     },
6380
+    "node_modules/human-number": {
6381
+      "version": "2.0.4",
6382
+      "resolved": "https://registry.npmjs.org/human-number/-/human-number-2.0.4.tgz",
6383
+      "integrity": "sha512-OENvA941poJU1VGR6s5Nf/GpYNPE+81lmHkIVLO9FgiyHxB+BSlVOJV3lnItk5tfHzcEbZv3kTQrzpZK0+ExRA==",
6384
+      "dependencies": {
6385
+        "round-to": "~5.0.0"
6386
+      },
6387
+      "engines": {
6388
+        "node": ">= 8"
6389
+      }
6390
+    },
6376 6391
     "node_modules/human-signals": {
6377 6392
       "version": "1.1.1",
6378 6393
       "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
@@ -6382,6 +6397,14 @@
6382 6397
         "node": ">=8.12.0"
6383 6398
       }
6384 6399
     },
6400
+    "node_modules/humanize-plus": {
6401
+      "version": "1.8.2",
6402
+      "resolved": "https://registry.npmjs.org/humanize-plus/-/humanize-plus-1.8.2.tgz",
6403
+      "integrity": "sha512-jaLeQyyzjjINGv7O9JJegjsaUcWjSj/1dcXvLEgU3pGdqCdP1PiC/uwr+saJXhTNBHZtmKnmpXyazgh+eceRxA==",
6404
+      "engines": {
6405
+        "node": ">= 0.8.0"
6406
+      }
6407
+    },
6385 6408
     "node_modules/iconv-lite": {
6386 6409
       "version": "0.6.3",
6387 6410
       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
@@ -8004,6 +8027,14 @@
8004 8027
         "node": ">=10.4.0"
8005 8028
       }
8006 8029
     },
8030
+    "node_modules/pluralize": {
8031
+      "version": "8.0.0",
8032
+      "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
8033
+      "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==",
8034
+      "engines": {
8035
+        "node": ">=4"
8036
+      }
8037
+    },
8007 8038
     "node_modules/postcss": {
8008 8039
       "version": "8.4.31",
8009 8040
       "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
@@ -8420,6 +8451,17 @@
8420 8451
         "fsevents": "~2.3.2"
8421 8452
       }
8422 8453
     },
8454
+    "node_modules/round-to": {
8455
+      "version": "5.0.0",
8456
+      "resolved": "https://registry.npmjs.org/round-to/-/round-to-5.0.0.tgz",
8457
+      "integrity": "sha512-i4+Ntwmo5kY7UWWFSDEVN3RjT2PX1FqkZ9iCcAO3sKML3Ady9NgsjM/HLdYKUAnrxK4IlSvXzpBMDvMHZQALRQ==",
8458
+      "engines": {
8459
+        "node": ">=10"
8460
+      },
8461
+      "funding": {
8462
+        "url": "https://github.com/sponsors/sindresorhus"
8463
+      }
8464
+    },
8423 8465
     "node_modules/rrweb-cssom": {
8424 8466
       "version": "0.6.0",
8425 8467
       "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz",
@@ -9737,6 +9779,11 @@
9737 9779
       "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
9738 9780
       "dev": true
9739 9781
     },
9782
+    "node_modules/vue-read-more": {
9783
+      "version": "1.1.1",
9784
+      "resolved": "https://registry.npmjs.org/vue-read-more/-/vue-read-more-1.1.1.tgz",
9785
+      "integrity": "sha512-FFv0y5Pg1763fsLo6MA5RSpibThNEoJG6teSRH+rRIQe8O0LAeU4juEn6MRHZN0qOwtXOlbOjO8oit7YSd1NcA=="
9786
+    },
9740 9787
     "node_modules/vue-router": {
9741 9788
       "version": "4.2.5",
9742 9789
       "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.2.5.tgz",

+ 4 - 0
package.json

@@ -30,12 +30,16 @@
30 30
     "axios": "^1.6.0",
31 31
     "axios-cache-interceptor": "^1.3.2",
32 32
     "axios-extensions": "^3.1.6",
33
+    "human-number": "^2.0.4",
34
+    "humanize-plus": "^1.8.2",
33 35
     "ionicons": "^7.0.0",
34 36
     "marked": "^10.0.0",
35 37
     "moment": "^2.29.4",
38
+    "pluralize": "^8.0.0",
36 39
     "swiper": "^11.0.3",
37 40
     "vue": "^3.2.45",
38 41
     "vue-core-video-player": "^0.2.0",
42
+    "vue-read-more": "^1.1.1",
39 43
     "vue-router": "^4.1.6",
40 44
     "vue-vimeo-player": "^1.1.2"
41 45
   },

+ 45 - 9
src/components/CourseMat.vue

@@ -1,21 +1,30 @@
1 1
 <template>
2
-  <ion-card :router-link="'/tabs/mat_detail/'+obj.id">
3
-    <ion-img alt="Silhouette of mountains" :src="thmb"  v-if="thmb" class="img_16_9"/>
4
-    <ion-card-header>
2
+  <ion-card >
3
+    <ion-img  :router-link="'/tabs/mat_detail/'+obj.id" alt="Silhouette of mountains" :src="thmb"  v-if="thmb" class="img_16_9"/>
4
+    <ion-card-header :router-link="'/tabs/mat_detail/'+obj.id">
5 5
       <ion-card-title>{{ name }}</ion-card-title>
6 6
     </ion-card-header>
7 7
 
8
-    <ion-card-content class='ion-text-wrap'>
8
+    <ion-card-content class='ion-text-wrap ion-no-padding ion-padding-horizontal' :router-link="'/tabs/mat_detail/'+obj.id">
9 9
       {{ dt }}
10 10
     </ion-card-content>
11
+    <ion-button fill="clear" @click="likeMatClick"><ion-icon :icon="heart" :color="isLike ? 'danger' : 'medium'  "></ion-icon> 
12
+      <template v-if="obj.total_likes > 0">
13
+        &nbsp;&nbsp;
14
+      {{  Humanize.compactInteger(obj.total_likes, 1) }} {{ pluralize('Like', obj.total_likes) }}
15
+      </template>
16
+    </ion-button>
11 17
   </ion-card>
12 18
 </template>
13 19
 
14 20
 <script setup lang="ts">
15
-  import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent, IonIcon, onIonViewWillEnter, IonCard, IonCardHeader, IonCardTitle, IonCardSubtitle, IonCardContent, IonImg } from '@ionic/vue';
21
+  import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent, IonIcon, onIonViewWillEnter, IonCard, IonCardHeader, IonCardTitle, IonCardSubtitle, IonCardContent, IonImg, IonButton } from '@ionic/vue';
16 22
   import moment from 'moment'
17
-import { getTrainers, listCourses } from '@/composable/settings';
23
+  import { getTrainers, listCourses, isLogin, getPref, likeMat } from '@/composable/settings';
18 24
   import { ref, onMounted  } from 'vue'
25
+  import { heart } from 'ionicons/icons';
26
+  import pluralize from 'pluralize';
27
+  import Humanize from 'humanize-plus/dist/humanize';
19 28
 
20 29
   const props = defineProps({
21 30
     obj: Object,
@@ -25,11 +34,23 @@ import { getTrainers, listCourses } from '@/composable/settings';
25 34
   const dt = ref()
26 35
   const thmb = ref()
27 36
   const name = ref()
28
-
29
-  onMounted(() => {
37
+  let pref = null
38
+  const isLike = ref(false)
39
+  onMounted(async() => {
30 40
     console.log("--- onmounted ---")
31 41
     console.log("name === ", props.obj)
42
+
32 43
     const obj = props.obj
44
+    if(await isLogin()) {
45
+      pref = await getPref()
46
+      console.debug(" pref = ", pref)
47
+      for(const m of pref.mat_likes) {
48
+        if(obj.id == m.id) {
49
+          isLike.value = true
50
+          break
51
+        }
52
+      }
53
+    }
33 54
     let temp  = obj.display_datetime || obj.vimeo_created || obj.created_at 
34 55
     dt.value = moment(temp).format('LLL'); 
35 56
 
@@ -45,7 +66,22 @@ import { getTrainers, listCourses } from '@/composable/settings';
45 66
     }
46 67
     name.value = obj.vimeo_name || props.courseName
47 68
   })
48
-  
69
+  const likeMatClick = async() => {
70
+    console.debug(props.obj)
71
+    pref = await likeMat(props.obj.id)
72
+    console.debug(pref)
73
+    isLike.value = false
74
+    for(const m of pref.mat_likes) {
75
+      if(props.obj.id == m.id) {
76
+        isLike.value = true
77
+        props.obj.total_likes += 1
78
+        break
79
+      }
80
+    }
81
+    if(isLike.value == false) {
82
+      props.obj.total_likes -= 1
83
+    }
84
+  } 
49 85
 </script>
50 86
 
51 87
 <style scoped>

+ 13 - 2
src/composable/settings.ts

@@ -9,8 +9,8 @@ import { Preferences } from '@capacitor/preferences';
9 9
 
10 10
 export const TOKEN = '173cb9e357a861abd91e8008fab9246e0cc116af'
11 11
 //export const BASE_URL = 'http://192.168.1.35:8020/'
12
-export const BASE_URL = 'http://localhost:8020/'
13
-//export const BASE_URL = 'https://www.tigermuaythai.live/'
12
+//export const BASE_URL = 'http://localhost:8020/'
13
+export const BASE_URL = 'https://www.tigermuaythai.live/'
14 14
 
15 15
 //const axios = setupCache(Axios); 
16 16
 //
@@ -378,3 +378,14 @@ export const likeCourse = async (cid) => {
378 378
   console.info(data)
379 379
   return data
380 380
 }
381
+export const likeMat = async (cid) => {
382
+  const token = await Preferences.get({ key: 'token' });
383
+  console.log("token = ", token)
384
+  const { data } = await axios.get(BASE_URL + `backend/api/profiles/like_mat/?cid=${cid}`, {
385
+    headers: { 
386
+    'Authorization': `Token ${token.value}`
387
+    }
388
+  })
389
+  console.info(data)
390
+  return data
391
+}

+ 27 - 2
src/views/CourseDetailPage.vue

@@ -15,9 +15,13 @@
15 15
           <ion-title size="large" v-if="course">{{ course.name }}</ion-title>
16 16
         </ion-toolbar>
17 17
       </ion-header>
18
+        <ion-img :src="course.feature_img" v-if="course" />
18 19
         <template v-if="processing">
19 20
             <ion-progress-bar type="indeterminate" class='ion-margin-vertical'></ion-progress-bar>
20 21
         </template>
22
+        <read-more class='ion-padding' v-if="render_body" more-str="read more" :text="render_body" link="#" less-str="read less" max-chars="200"></read-more>
23
+
24
+      <h1 class='ion-padding'>Trainings</h1>
21 25
       <ion-grid>
22 26
         <ion-row>
23 27
           <ion-col size="12" size-md="4" size-lg="2" v-for="c in mats">
@@ -31,17 +35,18 @@
31 35
     </ion-content>
32 36
   </ion-page>
33 37
 </template>
34
-
35 38
 <script setup lang="ts">
36 39
 import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent,   IonNavLink,
37 40
     IonButton,
38 41
     IonButtons,
39 42
     IonBackButton, onIonViewWillEnter, IonRow, IonGrid, IonCol, IonProgressBar,  IonInfiniteScroll,
40
-    IonInfiniteScrollContent, InfiniteScrollCustomEvent  } from '@ionic/vue';
43
+    IonInfiniteScrollContent, InfiniteScrollCustomEvent, IonImg  } from '@ionic/vue';
41 44
 import { useRoute } from 'vue-router';
42 45
 import { listCourseMats, getCourse, listCourseMatsByLevel, callUrl} from '@/composable/settings';
43 46
 import { ref } from 'vue';
44 47
 import CourseMat from '@/components/CourseMat.vue'
48
+import { marked } from 'marked';
49
+import ReadMore from 'vue-read-more/components/ReadMoreComponent.vue';
45 50
 
46 51
 const route = useRoute();
47 52
 const { id } = route.params;
@@ -50,8 +55,11 @@ const mats = ref([])
50 55
 const course = ref()
51 56
 let next_url = null
52 57
 const processing = ref(false)
58
+const render_body = ref()
59
+
53 60
 onIonViewWillEnter(async () => {
54 61
   course.value = await getCourse(id)
62
+  render_body.value = marked(course.value.body)
55 63
   processing.value = true
56 64
   listCourseMatsByLevel(course.value.clevels).then( (data) => { 
57 65
     mats.value = data.results;
@@ -73,3 +81,20 @@ onIonViewWillEnter(async () => {
73 81
     }
74 82
   };
75 83
 </script>
84
+
85
+<style>
86
+ion-title {
87
+  font-size:1.5rem;
88
+}
89
+#readmore {
90
+  display:block;
91
+  width:100%;
92
+  text-align:center;
93
+  text-transform:capitalize;
94
+  text-decoration:none;
95
+  font-weight:bold;
96
+  background-color:#ccc;
97
+  padding:0.5rem 0;
98
+  color:#333;
99
+}
100
+</style>