Browse Source

fix:修复bug

seamwang 2 years ago
parent
commit
01878b2f05
5 changed files with 567 additions and 538 deletions
  1. 53 53
      src/common/utils/fetch.js
  2. 420 414
      src/pages/class/detail.vue
  3. 64 64
      src/pages/index/components/card.vue
  4. 29 6
      src/pages/index/index.vue
  5. 1 1
      src/store/user.js

+ 53 - 53
src/common/utils/fetch.js

@@ -1,53 +1,53 @@
1
-import store from '@/store/index'
2
-
3
-// 统一请求方法
4
-export default function fetch(url, params = {}, method = 'POST', showFail = true) {
5
-  let showLoading = true
6
-  if (params && params.notShowLoading) {
7
-    showLoading = false
8
-  }
9
-  if (showLoading) {
10
-    uni.showLoading({
11
-      mask: true,
12
-      title: '加载中...'
13
-    })
14
-  }
15
-  url = process.env.VUE_APP_BASE_URL + url
16
-  params.token = store.getters.token
17
-  params.student_id = params.student_id || store.getters.kid
18
-  return new Promise((resolve, reject) => {
19
-    uni.request({
20
-      url,
21
-      data: params,
22
-      method,
23
-      header: {},
24
-      success: (res) => {
25
-        uni.hideLoading()
26
-        // 0:错误; 1:正确; 10000:没有token -2: 需要绑定手机
27
-        const rs = res.data
28
-        if (rs && rs.code === 1) {
29
-          resolve(rs)
30
-        } else if (rs.code === 10000) {
31
-          // // 跳转至登录页
32
-          uni.showModal({
33
-            title: '请先登录',
34
-            content: '您还没有登录,请先去登录',
35
-            success: res => {
36
-              if (res.confirm) {
37
-                uni.navigateTo({ url: '/pages/login/index' })
38
-              }
39
-            }
40
-          })
41
-        } else {
42
-          if (showFail) uni.showToast({ title: rs.msg, icon: 'none', duration: 2000 })// 直接提示错误
43
-          resolve(rs)
44
-        }
45
-      },
46
-      fail: (err) => {
47
-        uni.hideLoading()
48
-        uni.showToast({ title: err, icon: 'none', duration: 2000 })
49
-        reject(err)
50
-      }
51
-    })
52
-  })
53
-}
1
+import store from '@/store/index'
2
+
3
+// 统一请求方法
4
+export default function fetch(url, params = {}, method = 'POST', showFail = true) {
5
+  let showLoading = true
6
+  if (params && params.notShowLoading) {
7
+    showLoading = false
8
+  }
9
+  if (showLoading) {
10
+    uni.showLoading({
11
+      mask: true,
12
+      title: '加载中...'
13
+    })
14
+  }
15
+  url = process.env.VUE_APP_BASE_URL + url
16
+  params.token = store.getters.token
17
+  params.student_id = params.student_id || store.getters.kid
18
+  return new Promise((resolve, reject) => {
19
+    uni.request({
20
+      url,
21
+      data: params,
22
+      method,
23
+      header: {},
24
+      success: (res) => {
25
+        uni.hideLoading()
26
+        // 0:错误; 1:正确; 10000:没有token -2: 需要绑定手机
27
+        const rs = res.data
28
+        if (rs && rs.code === 1) {
29
+          resolve(rs)
30
+        } else if (rs.code === 10000) {
31
+          // // 跳转至登录页
32
+          uni.showModal({
33
+            title: '请先登录',
34
+            content: '您还没有登录,请先去登录',
35
+            success: res => {
36
+              if (res.confirm) {
37
+                uni.navigateTo({ url: '/pages/login/index' })
38
+              }
39
+            }
40
+          })
41
+        } else {
42
+          if (showFail) uni.showToast({ title: rs.msg, icon: 'none', duration: 2000 })// 直接提示错误
43
+          resolve(rs)
44
+        }
45
+      },
46
+      fail: (err) => {
47
+        uni.hideLoading()
48
+        uni.showToast({ title: err, icon: 'none', duration: 2000 })
49
+        reject(err)
50
+      }
51
+    })
52
+  })
53
+}

+ 420 - 414
src/pages/class/detail.vue

@@ -1,414 +1,420 @@
1
-<template>
2
-	<view class="page">
3
-		<cu-custom :isBack="true"></cu-custom>
4
-		<view class="header" :style="[{top:topHeader + 'px'}]">
5
-			<view class="header-title">
6
-				<view class="header-title-main">
7
-						<view class="margin-bottom-xs">{{tabList[cate_type].title}}</view>
8
-						<view class="point"></view>
9
-				</view>
10
-			</view>
11
-			<swiper-tab :menuList="tabList[cate_type].list" @changeTab="changeTab"></swiper-tab>
12
-		</view>
13
-		<scroll-view v-if="currentIndex===0" :scroll-y="true"
14
-		:style="[{height:'calc(100vh - 70px - '+ topHeader+'px)'}]"
15
-		class="scroll-main">
16
-			<view class="content">
17
-				<view class="course-img">
18
-					<swiper class="swiper" :indicator-dots="swiper.indicatorDots" :autoplay="swiper.autoplay" :interval="swiper.interval" :duration="swiper.duration">
19
-						<swiper-item v-for="(item,index) in detail.image" :key="index">
20
-							<view class="swiper-item">
21
-								  <image :src="item" mode="widthFix"></image>
22
-							</view>
23
-						</swiper-item>
24
-					</swiper>
25
-				</view>
26
-				<!-- 详情 -->
27
-				<view class="detail">
28
-					<view class="detail-title">{{detail.name}}</view>
29
-					<view class="detail-item">
30
-						 <view class="detail-item-point"></view>
31
-						 <view class="detail-label">周期</view>
32
-						 <view class="detail-text">{{detail.period}}<text class="text-gray">({{detail.time[0]}})</text></view>
33
-					</view>
34
-					<view class="detail-item">
35
-						<view class="detail-item-point"></view>
36
-							<view class="detail-label">适用年级</view>
37
-						 <view class="detail-text">{{detail.grade}}年级通用</view>
38
-					</view>
39
-					<view class="detail-item">
40
-						<view class="detail-item-point"></view>
41
-							<view class="detail-label">课时</view>
42
-						 <view class="detail-text">{{detail.class_total}}课时</view>
43
-					</view>
44
-					<view class="detail-item">
45
-						<view class="detail-item-point"></view>
46
-							<view class="detail-label">单课时价</view>
47
-						 <view class="detail-text">¥{{detail.single_money}}</view>
48
-					</view>
49
-					<view class="detail-item">
50
-						<view class="detail-item-point"></view>
51
-							<view class="detail-label">补贴</view>
52
-						 <view class="detail-text">¥{{detail.subsidy}}/节课</view>
53
-					</view>
54
-					<view class="detail-item">
55
-						 <view class="detail-item-point"></view>
56
-							<view class="detail-label">培养目标</view>
57
-						 <view class="detail-text">{{detail.tenet}}</view>
58
-					</view>
59
-				</view>
60
-			</view>
61
-			<!-- 老师介绍 -->
62
-			<view class="teacher flex margin-top-sm shadow" v-if="cate_type===0">
63
-					<image mode="scaleToFill" :src="defaultTeacher" class="avatar lg"></image>
64
-					<view class="flex-sub margin-left">
65
-						 <view class="teacher-title">{{detail.teachers.username||''}}老师授课</view>
66
-						 <view class="teacher-item">毕业于{{detail.teachers.agency_name||''}}</view>
67
-						 <view class="teacher-item">所属机构:{{detail.teachers.orz||''}}</view>
68
-						 <view class="teacher-exp flex">
69
-							 <view class="exp-item">{{detail.teachers.honor||''}}</view>
70
-							 <view class="exp-item">{{detail.teachers.introduce||''}}</view>
71
-						 </view>
72
-					</view>
73
-			</view>
74
-			<!-- 道具选择 -->
75
-			<view class="shop margin-top-sm shadow" v-if="detail.prop&&detail.prop.length>0">
76
-					<view class="shop-title">请选择课程所需要的道具</view>
77
-					<radio-group @change="radioChange" class="margin-top-xs">
78
-						<label  v-for="radio in radios" :key="radio.value" class="margin-right-sm">
79
-								<radio :value="radio.value" class="cyan" :checked="radio.checked" style="transform:scale(0.7)"/>{{radio.name}}
80
-						</label>
81
-					</radio-group>
82
-          <checkbox-group @change="checkboxChange" v-if="needTool==='1'" class="checkbox-group margin-top-xs">
83
-							<label v-for="prop in detail.prop" :key="prop.id">
84
-								<view>
85
-									<checkbox :value="prop.id" class="cyan" style="transform:scale(0.7)" :checked="goods.props.includes(prop.id)"/>{{prop.name}}<text>¥{{prop.money}}</text>
86
-								</view>
87
-							</label>
88
-					</checkbox-group>
89
-			</view>
90
-			<!-- 评论 -->
91
-			<view class="comment">
92
-				<rich-text :nodes="detail.present"></rich-text>
93
-			</view>
94
-		</scroll-view>
95
-		<!-- 评价页 -->
96
-		<scroll-view
97
-		:style="[{height:'calc(100vh - 70px - '+ topHeader+'px)'}]"
98
-		class="scroll-main"
99
-		v-if="currentIndex===1">
100
-			<view class="cu-list menu-avatar comment">
101
-					<view class="cu-item" v-for="(comment,index) in comments" :key="index">
102
-						<image mode="scaleToFill" :src="comment.img" class="avatar md left"></image>
103
-						<view class="content">
104
-							<view class="text-grey">{{comment.name}}</view>
105
-							<view class="text-gray text-content text-df">
106
-								{{comment.content}}
107
-							</view>
108
-							<view class="margin-top-sm flex justify-between">
109
-								<view class="text-gray text-df">{{comment.date}}</view>
110
-								<!-- <view>
111
-									<text class="cuIcon-appreciatefill text-red"></text>
112
-									<text class="cuIcon-messagefill text-gray margin-left-sm"></text>
113
-								</view> -->
114
-							</view>
115
-						</view>
116
-					</view>
117
-			</view>
118
-		</scroll-view>
119
-		<!-- 视频页 -->
120
-		<scroll-view
121
-		:style="[{height:'calc(100vh - 70px - '+ topHeader+'px)'}]"
122
-		class="scroll-main"
123
-		v-if="currentIndex===2">
124
-				<view class="list">
125
-					<view class="cu-card case" v-for="(video,index) in detail.video" :key="index">
126
-						<view class="cu-item shadow">
127
-							<view class="image">
128
-								<video :src="video" style="width:100%;"></video>
129
-							</view>
130
-						</view>
131
-					</view>
132
-				</view>
133
-		</scroll-view>
134
-		<!-- 大纲页 -->
135
-		<scroll-view
136
-		:scroll-y="true"
137
-	  :style="[{height:'calc(100vh - 70px - '+ topHeader+'px)'}]"
138
-		class="scroll-main"
139
-		v-if="currentIndex===3">
140
-				<view class="cu-bar bg-white">
141
-					<view class="action border-title">
142
-						<text class="cuIcon-title text-student"></text>
143
-						<text class="text-xl text-bold">{{tabList[cate_type].list[currentIndex]}}</text>
144
-					</view>
145
-				</view>
146
-				<view class="bg-white padding-lr">
147
-					<view class="text-content text-lg" v-for="(outline,index) in detail.out_line" :key="index">
148
-						{{outline.course_content}}
149
-					</view>
150
-				</view>
151
-		</scroll-view>
152
-		<view class="static flex shadow">
153
-			<view class="static-price">
154
-				<text class="satic-label text-lg">小计:</text>
155
-				<text class="text-price text-red text-lg">{{goods.sum_money}}</text>
156
-			</view>
157
-			<view class="static-choose text-ellipsis" v-if="goods.choose.length>0">(已选:{{goods.choose.join(',')}})</view>
158
-			<view class="static-order flex align-center">
159
-				 <shop-cart :class_attend_id="attend_id"></shop-cart>
160
-				 <button class="cu-btn round bg-student text-white margin-left-xs" :class="{'disabled':carts.includes(attend_id)||!detail.enable}" @tap="addCart">报课</button>
161
-			</view>
162
-		</view>
163
-	</view>
164
-</template>
165
-
166
-<script>
167
-import { _detail, _comments, _joinShop } from '@/api/course'
168
-import swiperTab from '@/components/swiper-tab.vue'
169
-import shopCart from '@/components/shop-cart.vue'
170
-import NP from 'number-precision'
171
-import { mapGetters } from 'vuex'
172
-export default {
173
-  components: {
174
-    swiperTab, shopCart
175
-  },
176
-  data() {
177
-    return {
178
-      topHeader: this.globalCustomBarHeight,
179
-      swiper: {
180
-        indicatorDots: true,
181
-        autoplay: true,
182
-        interval: 2000,
183
-        duration: 500
184
-      },
185
-      defaultTeacher: '/static/imgs/class/logo0.png',
186
-      attend_id: 0,
187
-      cate_type: 0,
188
-      currentIndex: 0,
189
-      money: 0, // 初始金额
190
-      comments: [], // 评论
191
-      detail: {}, // 详情
192
-      needTool: '1', // 需要道具
193
-      radios: [
194
-        { value: '1', name: '需要道具', checked: true },
195
-        { value: '2', name: '不需要道具', checked: false }
196
-      ],
197
-      goods: { // 动态选中值
198
-        props: [], // 选中值
199
-        choose: [], // 选中名称
200
-        sum_money: 0// 金额总计
201
-      }, // 选中课程
202
-      tabList: [
203
-        {
204
-          title: '课程详细',
205
-          list: ['课程介绍', '课程评价', '课程视频', '课程大纲'],
206
-          enable: '报课',
207
-          disable: '报课',
208
-          options: [
209
-            { title: '周期', content: ':正在加载,请稍后', des: ':正在加载,请稍后' },
210
-            { title: '适用年级', content: ':正在加载,请稍后', des: '' },
211
-            { title: '课时', content: ':正在加载,请稍后', des: '' },
212
-            { title: '单课时价', content: ':正在加载,请稍后', des: '' },
213
-            { title: '补贴', content: ':正在加载,请稍后', des: '' },
214
-            { title: '培养目标', content: ':正在加载,请稍后', des: '' }
215
-          ]
216
-        },
217
-        {
218
-          title: '餐饮详细',
219
-          list: ['餐饮介绍', '餐饮评价', '餐饮视频', '每日菜谱'],
220
-          enable: '报餐',
221
-          disable: '报餐',
222
-          options: [
223
-            { title: '周期', content: ':正在加载,请稍后', des: ':正在加载,请稍后' },
224
-            { title: '适用年级', content: ':正在加载,请稍后', des: '' },
225
-            { title: '用餐次数', content: ':正在加载,请稍后', des: '' },
226
-            { title: '单次价格', content: ':正在加载,请稍后', des: '' },
227
-            { title: '补贴', content: ':正在加载,请稍后', des: '' },
228
-            { title: '用餐介绍', content: ':正在加载,请稍后', des: '' }
229
-          ]
230
-        },
231
-        {
232
-          title: '活动详细',
233
-          list: ['活动介绍', '活动评价', '活动视频', '活动大纲'],
234
-          enable: '报名',
235
-          disable: '报名',
236
-          options: [
237
-            { title: '周期', content: ':正在加载,请稍后', des: ':正在加载,请稍后' },
238
-            { title: '适用年级', content: ':正在加载,请稍后', des: '' },
239
-            { title: '活动次数', content: ':正在加载,请稍后', des: '' },
240
-            { title: '单次价格', content: ':正在加载,请稍后', des: '' },
241
-            { title: '补贴', content: ':正在加载,请稍后', des: '' },
242
-            { title: '活动目标', content: ':正在加载,请稍后', des: '' }
243
-          ]
244
-        }
245
-      ]
246
-    }
247
-  },
248
-  computed: {
249
-    ...mapGetters([
250
-      'carts'
251
-    ])
252
-  },
253
-  onLoad(option) {
254
-    this.attend_id = Number(decodeURIComponent(option.attend_id))
255
-    if (this.attend_id === 0) {
256
-      uni.navigateBack()
257
-    }
258
-    this.get_detail()
259
-    this.get_comments()
260
-  },
261
-  methods: {
262
-    get_detail() {
263
-      _detail({ class_attend_id: this.attend_id }).then(res => {
264
-        this.detail = res.data
265
-        this.money = res.data.sum_money
266
-        const props = res.data.prop
267
-        if (props && props.length > 0) {
268
-          this.setDefaultGoods(props)
269
-        } else {
270
-          this.goods.sum_money = this.money
271
-        }
272
-      })
273
-    },
274
-    addCart() {
275
-      if (this.carts.includes(this.attend_id)) {
276
-        uni.showToast({ title: '课程已存在,请勿重复添加!', icon: 'none' })
277
-        setTimeout(() => {
278
-          this.globalNavigateTo('classCart')
279
-        }, 1000)
280
-        return false
281
-      }
282
-      if (!this.detail.enable) {
283
-        uni.showToast({ title: '课程已停止!', icon: 'none' })
284
-        return false
285
-      }
286
-      const props = this.goods.props.join(',')
287
-      _joinShop({ class_attend_id: this.attend_id, prop: props }).then(res => {
288
-        this.globalNavigateTo('classCart')
289
-      })
290
-    },
291
-    setDefaultGoods(props) {
292
-      this.goods.props[0] = props[0].id
293
-      this.goods.sum_money = NP.plus(this.money, props[0].money)
294
-      this.goods.choose[0] = props[0].name
295
-    },
296
-    get_comments() {
297
-      _comments({ class_attend_id: this.attend_id }).then(res => {
298
-        this.comemnts = res.data
299
-      })
300
-    },
301
-    changeTab(index) {
302
-      this.currentIndex = index
303
-    },
304
-    radioChange(e) {
305
-      const value = e.detail.value
306
-      this.needTool = value
307
-      if (value === '1') {
308
-        this.setDefaultGoods(this.detail.prop)
309
-      } else {
310
-        this.goods.sum_money = this.money
311
-        this.goods.props = []
312
-        this.goods.choose = []
313
-      }
314
-    },
315
-    checkboxChange(e) {
316
-      const values = e.detail.value
317
-      const choose = []
318
-      const props = []
319
-      let money = 0
320
-      values.forEach(item => {
321
-        const one = this.detail.prop.find(e => e.id === Number(item))
322
-        choose.push(one.name)
323
-        props.push(Number(item))
324
-        money = NP.plus(money, Number(one.money))
325
-      })
326
-      const sum = NP.plus(this.money, money)
327
-      this.goods = {
328
-        props: props,
329
-        choose: choose,
330
-        sum_money: sum
331
-      }
332
-    }
333
-  }
334
-}
335
-</script>
336
-
337
-<style lang="scss" scoped>
338
-.scroll-main{
339
-  padding-top:86px;
340
-}
341
-.detail{
342
-	padding:20rpx;
343
-	background-color:#fff;
344
-	&-title{
345
-		margin-bottom:1rem;
346
-		font-size:22px;
347
-	}
348
-	&-item{
349
-    margin: 20rpx 0;
350
-    display: flex;
351
-    color: #999;
352
-		&-point{
353
-			margin-top: 7px;
354
-			margin-right: 7px;
355
-			border-radius: 50%;
356
-			width: 5px;
357
-			height: 5px;
358
-			background: #5ecfde;
359
-		}
360
-	}
361
-	&-label{
362
-		width: 66px;
363
-    padding-right: 6px;
364
-    text-align: justify;
365
-    text-align-last: justify;
366
-	}
367
-	&-text{
368
-		color:#000;
369
-	}
370
-}
371
-.teacher{
372
-	padding:20rpx;
373
-	background:#fff;
374
-	font-size:14px;
375
-	color:#999;
376
-	&-title{
377
-		font-size:18px;
378
-		color:#000;
379
-	}
380
-}
381
-.shop{
382
-	padding:20rpx;
383
-	background:#fff;
384
-}
385
-.checkbox-group{
386
-	height:80px;
387
-	overflow-y:scroll;
388
-	border:1px solid #f5f5f5;
389
-}
390
-.comment{
391
-	padding:20rpx;
392
-	min-height:60px;
393
-}
394
-.static{
395
-    position: fixed;
396
-    left: 0;
397
-    right: 0;
398
-    bottom: 0;
399
-    display: flex;
400
-    background: #fff;
401
-    height: 70px;
402
-    align-items: center;
403
-    padding: 0 0.8rem;
404
-    justify-content: space-between;
405
-		&-price{
406
-			font-size:14px;
407
-		}
408
-		&-choose{
409
-      color: #666;
410
-			font-size: 12px;
411
-    	width: calc(100vw - 240px);
412
-		}
413
-}
414
-</style>
1
+<template>
2
+	<view class="page">
3
+		<cu-custom :isBack="true"></cu-custom>
4
+		<view class="header" :style="[{top:topHeader + 'px'}]">
5
+			<view class="header-title">
6
+				<view class="header-title-main">
7
+						<view class="margin-bottom-xs">{{tabList[cate_type].title}}</view>
8
+						<view class="point"></view>
9
+				</view>
10
+			</view>
11
+			<swiper-tab :menuList="tabList[cate_type].list" @changeTab="changeTab"></swiper-tab>
12
+		</view>
13
+		<scroll-view v-if="currentIndex===0" :scroll-y="true"
14
+		:style="[{height:'calc(100vh - 70px - '+ topHeader+'px)'}]"
15
+		class="scroll-main">
16
+			<view class="content">
17
+				<view class="course-img">
18
+					<swiper class="swiper" :indicator-dots="swiper.indicatorDots" :autoplay="swiper.autoplay" :interval="swiper.interval" :duration="swiper.duration">
19
+						<swiper-item v-for="(item,index) in detail.image" :key="index">
20
+							<view class="swiper-item">
21
+								  <image :src="item" mode="widthFix"></image>
22
+							</view>
23
+						</swiper-item>
24
+					</swiper>
25
+				</view>
26
+				<!-- 详情 -->
27
+				<view class="detail">
28
+					<view class="detail-title">{{detail.name}}</view>
29
+					<view class="detail-item">
30
+						 <view class="detail-item-point"></view>
31
+						 <view class="detail-label">周期</view>
32
+						 <view class="detail-text">{{detail.period}}<text class="text-gray">({{detail.time[0]}})</text></view>
33
+					</view>
34
+					<view class="detail-item">
35
+						<view class="detail-item-point"></view>
36
+							<view class="detail-label">适用年级</view>
37
+						 <view class="detail-text">{{detail.grade}}年级通用</view>
38
+					</view>
39
+					<view class="detail-item">
40
+						<view class="detail-item-point"></view>
41
+							<view class="detail-label">课时</view>
42
+						 <view class="detail-text">{{detail.class_total}}课时</view>
43
+					</view>
44
+					<view class="detail-item">
45
+						<view class="detail-item-point"></view>
46
+							<view class="detail-label">单课时价</view>
47
+						 <view class="detail-text">¥{{detail.single_money}}</view>
48
+					</view>
49
+					<view class="detail-item">
50
+						<view class="detail-item-point"></view>
51
+							<view class="detail-label">补贴</view>
52
+						 <view class="detail-text">¥{{detail.subsidy}}/节课</view>
53
+					</view>
54
+					<view class="detail-item">
55
+						 <view class="detail-item-point"></view>
56
+							<view class="detail-label">培养目标</view>
57
+						 <view class="detail-text">{{detail.tenet}}</view>
58
+					</view>
59
+				</view>
60
+			</view>
61
+			<!-- 老师介绍 -->
62
+			<view class="teacher flex margin-top-sm shadow" v-if="cate_type===0">
63
+					<image mode="scaleToFill" :src="defaultTeacher" class="avatar lg"></image>
64
+					<view class="flex-sub margin-left">
65
+						 <view class="teacher-title">{{detail.teachers.username||''}}老师授课</view>
66
+						 <view class="teacher-item">毕业于{{detail.teachers.agency_name||''}}</view>
67
+						 <view class="teacher-item">所属机构:{{detail.teachers.orz||''}}</view>
68
+						 <view class="teacher-exp flex">
69
+							 <view class="exp-item">{{detail.teachers.honor||''}}</view>
70
+							 <view class="exp-item">{{detail.teachers.introduce||''}}</view>
71
+						 </view>
72
+					</view>
73
+			</view>
74
+			<!-- 道具选择 -->
75
+			<view class="shop margin-top-sm shadow" v-if="detail.prop&&detail.prop.length>0">
76
+					<view class="shop-title">请选择课程所需要的道具</view>
77
+					<radio-group @change="radioChange" class="margin-top-xs">
78
+						<label  v-for="radio in radios" :key="radio.value" class="margin-right-sm">
79
+								<radio :value="radio.value" class="cyan" :checked="radio.checked" style="transform:scale(0.7)"/>{{radio.name}}
80
+						</label>
81
+					</radio-group>
82
+          <checkbox-group @change="checkboxChange" v-if="needTool==='1'" class="checkbox-group margin-top-xs">
83
+							<label v-for="prop in detail.prop" :key="prop.id">
84
+								<view>
85
+									<checkbox :value="prop.id" class="cyan" style="transform:scale(0.7)" :checked="goods.props.includes(prop.id)"/>{{prop.name}}<text>¥{{prop.money}}</text>
86
+								</view>
87
+							</label>
88
+					</checkbox-group>
89
+			</view>
90
+			<!-- 评论 -->
91
+			<view class="comment">
92
+				<rich-text :nodes="detail.present"></rich-text>
93
+			</view>
94
+		</scroll-view>
95
+		<!-- 评价页 -->
96
+		<scroll-view
97
+		:style="[{height:'calc(100vh - 70px - '+ topHeader+'px)'}]"
98
+		class="scroll-main"
99
+		v-if="currentIndex===1">
100
+			<view class="cu-list menu-avatar comment">
101
+					<view class="cu-item" v-for="(comment,index) in comments" :key="index">
102
+						<image mode="scaleToFill" :src="comment.img" class="avatar md left"></image>
103
+						<view class="content">
104
+							<view class="text-grey">{{comment.name}}</view>
105
+							<view class="text-gray text-content text-df">
106
+								{{comment.content}}
107
+							</view>
108
+							<view class="margin-top-sm flex justify-between">
109
+								<view class="text-gray text-df">{{comment.date}}</view>
110
+								<!-- <view>
111
+									<text class="cuIcon-appreciatefill text-red"></text>
112
+									<text class="cuIcon-messagefill text-gray margin-left-sm"></text>
113
+								</view> -->
114
+							</view>
115
+						</view>
116
+					</view>
117
+			</view>
118
+		</scroll-view>
119
+		<!-- 视频页 -->
120
+		<scroll-view
121
+		:style="[{height:'calc(100vh - 70px - '+ topHeader+'px)'}]"
122
+		class="scroll-main"
123
+		v-if="currentIndex===2">
124
+				<view class="list">
125
+					<view class="cu-card case" v-for="(video,index) in detail.video" :key="index">
126
+						<view class="cu-item shadow">
127
+							<view class="image">
128
+								<video :src="video" style="width:100%;"></video>
129
+							</view>
130
+						</view>
131
+					</view>
132
+				</view>
133
+		</scroll-view>
134
+		<!-- 大纲页 -->
135
+		<scroll-view
136
+		:scroll-y="true"
137
+	  :style="[{height:'calc(100vh - 70px - '+ topHeader+'px)'}]"
138
+		class="scroll-main"
139
+		v-if="currentIndex===3">
140
+				<view class="cu-bar bg-white">
141
+					<view class="action border-title">
142
+						<text class="cuIcon-title text-student"></text>
143
+						<text class="text-xl text-bold">{{tabList[cate_type].list[currentIndex]}}</text>
144
+					</view>
145
+				</view>
146
+				<view class="bg-white padding-lr">
147
+					<view class="text-content text-lg" v-for="(outline,index) in detail.out_line" :key="index">
148
+						{{outline.course_content}}
149
+					</view>
150
+				</view>
151
+		</scroll-view>
152
+		<view class="static flex shadow">
153
+			<view class="static-price">
154
+				<text class="satic-label text-lg">小计:</text>
155
+				<text class="text-price text-red text-lg">{{goods.sum_money}}</text>
156
+			</view>
157
+			<view class="static-choose text-ellipsis" v-if="goods.choose.length>0">(已选:{{goods.choose.join(',')}})</view>
158
+			<view class="static-order flex align-center">
159
+				 <shop-cart :class_attend_id="attend_id"></shop-cart>
160
+				 <button class="cu-btn round bg-student text-white margin-left-xs" :class="{'disabled':carts.includes(attend_id)||!detail.enable}" @tap="addCart">报课</button>
161
+			</view>
162
+		</view>
163
+	</view>
164
+</template>
165
+
166
+<script>
167
+import { _detail, _comments, _joinShop } from '@/api/course'
168
+import swiperTab from '@/components/swiper-tab.vue'
169
+import shopCart from '@/components/shop-cart.vue'
170
+import NP from 'number-precision'
171
+import { mapGetters } from 'vuex'
172
+export default {
173
+  components: {
174
+    swiperTab, shopCart
175
+  },
176
+  data() {
177
+    return {
178
+      topHeader: this.globalCustomBarHeight,
179
+      swiper: {
180
+        indicatorDots: true,
181
+        autoplay: true,
182
+        interval: 2000,
183
+        duration: 500
184
+      },
185
+      defaultTeacher: '/static/imgs/class/logo0.png',
186
+      attend_id: 0,
187
+      cate_type: 0,
188
+      currentIndex: 0,
189
+      money: 0, // 初始金额
190
+      comments: [], // 评论
191
+      detail: {}, // 详情
192
+      needTool: '1', // 需要道具
193
+      disableBtn: false, // 避免多次重复点击
194
+      radios: [
195
+        { value: '1', name: '需要道具', checked: true },
196
+        { value: '2', name: '不需要道具', checked: false }
197
+      ],
198
+      goods: { // 动态选中值
199
+        props: [], // 选中值
200
+        choose: [], // 选中名称
201
+        sum_money: 0// 金额总计
202
+      }, // 选中课程
203
+      tabList: [
204
+        {
205
+          title: '课程详细',
206
+          list: ['课程介绍', '课程评价', '课程视频', '课程大纲'],
207
+          enable: '报课',
208
+          disable: '报课',
209
+          options: [
210
+            { title: '周期', content: ':正在加载,请稍后', des: ':正在加载,请稍后' },
211
+            { title: '适用年级', content: ':正在加载,请稍后', des: '' },
212
+            { title: '课时', content: ':正在加载,请稍后', des: '' },
213
+            { title: '单课时价', content: ':正在加载,请稍后', des: '' },
214
+            { title: '补贴', content: ':正在加载,请稍后', des: '' },
215
+            { title: '培养目标', content: ':正在加载,请稍后', des: '' }
216
+          ]
217
+        },
218
+        {
219
+          title: '餐饮详细',
220
+          list: ['餐饮介绍', '餐饮评价', '餐饮视频', '每日菜谱'],
221
+          enable: '报餐',
222
+          disable: '报餐',
223
+          options: [
224
+            { title: '周期', content: ':正在加载,请稍后', des: ':正在加载,请稍后' },
225
+            { title: '适用年级', content: ':正在加载,请稍后', des: '' },
226
+            { title: '用餐次数', content: ':正在加载,请稍后', des: '' },
227
+            { title: '单次价格', content: ':正在加载,请稍后', des: '' },
228
+            { title: '补贴', content: ':正在加载,请稍后', des: '' },
229
+            { title: '用餐介绍', content: ':正在加载,请稍后', des: '' }
230
+          ]
231
+        },
232
+        {
233
+          title: '活动详细',
234
+          list: ['活动介绍', '活动评价', '活动视频', '活动大纲'],
235
+          enable: '报名',
236
+          disable: '报名',
237
+          options: [
238
+            { title: '周期', content: ':正在加载,请稍后', des: ':正在加载,请稍后' },
239
+            { title: '适用年级', content: ':正在加载,请稍后', des: '' },
240
+            { title: '活动次数', content: ':正在加载,请稍后', des: '' },
241
+            { title: '单次价格', content: ':正在加载,请稍后', des: '' },
242
+            { title: '补贴', content: ':正在加载,请稍后', des: '' },
243
+            { title: '活动目标', content: ':正在加载,请稍后', des: '' }
244
+          ]
245
+        }
246
+      ]
247
+    }
248
+  },
249
+  computed: {
250
+    ...mapGetters([
251
+      'carts'
252
+    ])
253
+  },
254
+  onLoad(option) {
255
+    this.attend_id = Number(decodeURIComponent(option.attend_id))
256
+    if (this.attend_id === 0) {
257
+      uni.navigateBack()
258
+    }
259
+    this.get_detail()
260
+    this.get_comments()
261
+  },
262
+  methods: {
263
+    get_detail() {
264
+      _detail({ class_attend_id: this.attend_id }).then(res => {
265
+        this.detail = res.data
266
+        this.money = res.data.sum_money
267
+        const props = res.data.prop
268
+        if (props && props.length > 0) {
269
+          this.setDefaultGoods(props)
270
+        } else {
271
+          this.goods.sum_money = this.money
272
+        }
273
+      })
274
+    },
275
+    addCart() {
276
+      if (this.disableBtn) return false
277
+      this.disableBtn = true
278
+      if (this.carts.includes(this.attend_id)) {
279
+        uni.showToast({ title: '课程已存在,请勿重复添加!', icon: 'none' })
280
+        setTimeout(() => {
281
+          this.disableBtn = false
282
+          this.globalNavigateTo('classCart')
283
+        }, 1000)
284
+        return false
285
+      }
286
+      if (!this.detail.enable) {
287
+        uni.showToast({ title: '课程已停止!', icon: 'none' })
288
+        this.disableBtn = false
289
+        return false
290
+      }
291
+      const props = this.goods.props.join(',')
292
+      _joinShop({ class_attend_id: this.attend_id, prop: props }).then(res => {
293
+        this.disableBtn = false
294
+        this.globalNavigateTo('classCart')
295
+      })
296
+    },
297
+    setDefaultGoods(props) {
298
+      this.goods.props[0] = props[0].id
299
+      this.goods.sum_money = NP.plus(this.money, props[0].money)
300
+      this.goods.choose[0] = props[0].name
301
+    },
302
+    get_comments() {
303
+      _comments({ class_attend_id: this.attend_id }).then(res => {
304
+        this.comemnts = res.data
305
+      })
306
+    },
307
+    changeTab(index) {
308
+      this.currentIndex = index
309
+    },
310
+    radioChange(e) {
311
+      const value = e.detail.value
312
+      this.needTool = value
313
+      if (value === '1') {
314
+        this.setDefaultGoods(this.detail.prop)
315
+      } else {
316
+        this.goods.sum_money = this.money
317
+        this.goods.props = []
318
+        this.goods.choose = []
319
+      }
320
+    },
321
+    checkboxChange(e) {
322
+      const values = e.detail.value
323
+      const choose = []
324
+      const props = []
325
+      let money = 0
326
+      values.forEach(item => {
327
+        const one = this.detail.prop.find(e => e.id === Number(item))
328
+        choose.push(one.name)
329
+        props.push(Number(item))
330
+        money = NP.plus(money, Number(one.money))
331
+      })
332
+      const sum = NP.plus(this.money, money)
333
+      this.goods = {
334
+        props: props,
335
+        choose: choose,
336
+        sum_money: sum
337
+      }
338
+    }
339
+  }
340
+}
341
+</script>
342
+
343
+<style lang="scss" scoped>
344
+.scroll-main{
345
+  padding-top:86px;
346
+}
347
+.detail{
348
+	padding:20rpx;
349
+	background-color:#fff;
350
+	&-title{
351
+		margin-bottom:1rem;
352
+		font-size:22px;
353
+	}
354
+	&-item{
355
+    margin: 20rpx 0;
356
+    display: flex;
357
+    color: #999;
358
+		&-point{
359
+			margin-top: 7px;
360
+			margin-right: 7px;
361
+			border-radius: 50%;
362
+			width: 5px;
363
+			height: 5px;
364
+			background: #5ecfde;
365
+		}
366
+	}
367
+	&-label{
368
+		width: 66px;
369
+    padding-right: 6px;
370
+    text-align: justify;
371
+    text-align-last: justify;
372
+	}
373
+	&-text{
374
+		color:#000;
375
+	}
376
+}
377
+.teacher{
378
+	padding:20rpx;
379
+	background:#fff;
380
+	font-size:14px;
381
+	color:#999;
382
+	&-title{
383
+		font-size:18px;
384
+		color:#000;
385
+	}
386
+}
387
+.shop{
388
+	padding:20rpx;
389
+	background:#fff;
390
+}
391
+.checkbox-group{
392
+	height:80px;
393
+	overflow-y:scroll;
394
+	border:1px solid #f5f5f5;
395
+}
396
+.comment{
397
+	padding:20rpx;
398
+	min-height:60px;
399
+}
400
+.static{
401
+    position: fixed;
402
+    left: 0;
403
+    right: 0;
404
+    bottom: 0;
405
+    display: flex;
406
+    background: #fff;
407
+    height: 70px;
408
+    align-items: center;
409
+    padding: 0 0.8rem;
410
+    justify-content: space-between;
411
+		&-price{
412
+			font-size:14px;
413
+		}
414
+		&-choose{
415
+      color: #666;
416
+			font-size: 12px;
417
+    	width: calc(100vw - 240px);
418
+		}
419
+}
420
+</style>

+ 64 - 64
src/pages/index/components/card.vue

@@ -1,64 +1,64 @@
1
-<template>
2
-	<view class="card flex">
3
-		<view class=card-left>
4
-			<img mode="aspectFill" :src="item.image" alt="">
5
-		</view>
6
-		<view class="card-right flex-sub">
7
-			<view class="card-title">{{item.attend_name}}</view>
8
-			<view class="text-gray text-sm flex justify-between"><text>{{item.day}}</text><text class="text-student">¥{{item.money}}</text></view>
9
-			<view class="text-gray text-sm">{{item.grade_desc}}</view>
10
-			<view class="margin-top-sm" @tap="goDetail(item)">
11
-				<button class="cu-btn round  bg-student text-white button-hover" v-if="item.enable">报课</button>
12
-				<button class="cu-btn round  bg-grey button-hover text-white disabled" v-else>停止</button>
13
-			</view>
14
-		</view>
15
-	</view>
16
-</template>
17
-
18
-<script>
19
-export default {
20
-  props: {
21
-    item: {
22
-      type: Object,
23
-      default() {
24
-        return {}
25
-      }
26
-    }
27
-  },
28
-  data() {
29
-    return {
30
-      key: 1212
31
-    }
32
-  },
33
-  methods: {
34
-    goDetail(item) {
35
-      if (!item.enable) {
36
-        uni.showToast({ title: '课程已停止!', icon: 'none' })
37
-        return false
38
-      }
39
-      this.globalNavigateTo('classDetail', { attend_id: this.item.class_attend_id })
40
-    }
41
-  }
42
-}
43
-</script>
44
-
45
-<style lang="scss" scoped>
46
-@import '~@/common/css/mixin.scss';
47
-.card{
48
-	padding:30rpx;
49
-	width:100%;
50
-	&-title{
51
-		@include title(10px,14px);
52
-	}
53
-	&-left{
54
-		margin-right:20rpx;
55
-		width:240rpx;
56
-		image{
57
-			height:96px;
58
-		}
59
-	}
60
-	&-right{
61
-
62
-	}
63
-}
64
-</style>
1
+<template>
2
+	<view class="card flex" @tap="goDetail(item)">
3
+		<view class=card-left>
4
+			<img mode="aspectFill" :src="item.image" alt="">
5
+		</view>
6
+		<view class="card-right flex-sub">
7
+			<view class="card-title">{{item.attend_name}}</view>
8
+			<view class="text-gray text-sm flex justify-between"><text>{{item.day}}</text><text class="text-student">¥{{item.money}}</text></view>
9
+			<view class="text-gray text-sm">{{item.grade_desc}}</view>
10
+			<view class="margin-top-sm">
11
+				<button class="cu-btn round  bg-student text-white button-hover" v-if="item.enable">报课</button>
12
+				<button class="cu-btn round  bg-grey button-hover text-white disabled" v-else>停止</button>
13
+			</view>
14
+		</view>
15
+	</view>
16
+</template>
17
+
18
+<script>
19
+export default {
20
+  props: {
21
+    item: {
22
+      type: Object,
23
+      default() {
24
+        return {}
25
+      }
26
+    }
27
+  },
28
+  data() {
29
+    return {
30
+      key: 1212
31
+    }
32
+  },
33
+  methods: {
34
+    goDetail(item) {
35
+      if (!item.enable) {
36
+        uni.showToast({ title: '课程已停止!', icon: 'none' })
37
+        return false
38
+      }
39
+      this.globalNavigateTo('classDetail', { attend_id: this.item.class_attend_id })
40
+    }
41
+  }
42
+}
43
+</script>
44
+
45
+<style lang="scss" scoped>
46
+@import '~@/common/css/mixin.scss';
47
+.card{
48
+	padding:30rpx;
49
+	width:100%;
50
+	&-title{
51
+		@include title(10px,14px);
52
+	}
53
+	&-left{
54
+		margin-right:20rpx;
55
+		width:240rpx;
56
+		image{
57
+			height:96px;
58
+		}
59
+	}
60
+	&-right{
61
+
62
+	}
63
+}
64
+</style>

+ 29 - 6
src/pages/index/index.vue

@@ -180,17 +180,40 @@ export default {
180 180
   },
181 181
   computed: {
182 182
     ...mapGetters([
183
-      'kid'
183
+      'kid', 'token'
184 184
     ])
185 185
   },
186 186
   onShow() {
187
-    if (!this.kid) {
188
-      return this.globalNavigateTo('myStudents')
189
-    }
190
-    this.params = Object.assign({}, this.init)
191
-    this.get_list(true)// true初始化加载过滤项
187
+    // 登录后才进入后续操作
188
+    this.checkLogin().then(res => {
189
+      if (res) {
190
+        if (!this.kid) {
191
+          return this.globalNavigateTo('myStudents')
192
+        }
193
+        this.params = Object.assign({}, this.init)
194
+        this.get_list(true)// true初始化加载过滤项
195
+      }
196
+    })
192 197
   },
193 198
   methods: {
199
+    checkLogin() {
200
+      return new Promise((resolve, reject) => {
201
+        if (this.token) { // 跳转至登录页
202
+          resolve(true)
203
+        } else {
204
+          uni.showModal({
205
+            title: '请先登录',
206
+            content: '您还没有登录,请先去登录',
207
+            success: res => {
208
+              if (res.confirm) {
209
+                uni.navigateTo({ url: '/pages/login/index' })
210
+              }
211
+            }
212
+          })
213
+          resolve(false)
214
+        }
215
+      })
216
+    },
194 217
     goCourse(type) {
195 218
       this.params.list_type = type
196 219
       this.search()

+ 1 - 1
src/store/user.js

@@ -35,7 +35,7 @@ export default {
35 35
       state.phone = ''
36 36
       state.kid = 0
37 37
       state.carts = null
38
-      uni.clearStorage()
38
+      uni.clearStorageSync()
39 39
     },
40 40
     SET_USER(state, user) {
41 41
       state.user = user