lex 2 years ago
parent
commit
1fbe1b09c3

File diff suppressed because it is too large
+ 0 - 0
dist/assets/index-legacy.429de423.js


File diff suppressed because it is too large
+ 0 - 0
dist/assets/index.11723e15.css


File diff suppressed because it is too large
+ 0 - 0
dist/assets/index.de087a8e.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/polyfills-legacy.639f6553.js


File diff suppressed because it is too large
+ 1 - 0
dist/assets/polyfills-legacy.efa4ae3d.js


File diff suppressed because it is too large
+ 0 - 0
dist/assets/teacher-legacy.45fe519e.js


File diff suppressed because it is too large
+ 0 - 0
dist/assets/teacher.0f9c3387.js


+ 0 - 0
dist/assets/index.5062a520.css → dist/assets/teacher.a6c9550a.css


+ 9 - 1
dist/index.html

@@ -38,9 +38,13 @@
     <script type="module" crossorigin src="./assets/index.970823a2.js"></script>
     <link rel="modulepreload" href="./assets/vendor.cd162bf7.js">
     <link rel="modulepreload" href="./assets/index.6407f2e3.js">
+<<<<<<< HEAD
     <link rel="stylesheet" href="./assets/vendor.68261ebd.css">
+=======
+>>>>>>> iteration_1.3.4
     <link rel="stylesheet" href="./assets/index.cb2212cc.css">
-    <link rel="stylesheet" href="./assets/index.5062a520.css">
+    <link rel="stylesheet" href="./assets/vendor.68261ebd.css">
+    <link rel="stylesheet" href="./assets/teacher.a6c9550a.css">
     <script type="module">!function(){try{new Function("m","return import(m)")}catch(o){console.warn("vite: loading legacy build because dynamic import is unsupported, syntax error above should be ignored");var e=document.getElementById("vite-legacy-polyfill"),n=document.createElement("script");n.src=e.src,n.onload=function(){System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))},document.body.appendChild(n)}}();</script>
   </head>
 
@@ -49,7 +53,11 @@
     
     <!-- <script type="module" src="/src/teacher/main.ts"></script> -->
     <script nomodule>!function(){var e=document,t=e.createElement("script");if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;e.addEventListener("beforeload",(function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()}),!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}();</script>
+<<<<<<< HEAD
     <script nomodule id="vite-legacy-polyfill" src="./assets/polyfills-legacy.639f6553.js"></script>
+=======
+    <script nomodule id="vite-legacy-polyfill" src="./assets/polyfills-legacy.efa4ae3d.js"></script>
+>>>>>>> iteration_1.3.4
     <script nomodule id="vite-legacy-entry" data-src="./assets/index-legacy.7c84f1ac.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
   </body>
 </html>

+ 13 - 1
dist/teacher.html

@@ -35,12 +35,19 @@
     <meta name="msapplication-tap-highlight" content="no" />
     <title>酷乐秀</title>
     <script src="./flexible.js" charset="UTF-8"></script>
+<<<<<<< HEAD
     <script type="module" crossorigin src="./assets/teacher.efb86ae3.js"></script>
     <link rel="modulepreload" href="./assets/vendor.cd162bf7.js">
     <link rel="modulepreload" href="./assets/index.6407f2e3.js">
     <link rel="stylesheet" href="./assets/index.cb2212cc.css">
+=======
+    <script type="module" crossorigin src="./assets/teacher.0f9c3387.js"></script>
+    <link rel="modulepreload" href="./assets/vendor.cd162bf7.js">
+    <link rel="modulepreload" href="./assets/index.6407f2e3.js">
+>>>>>>> iteration_1.3.4
     <link rel="stylesheet" href="./assets/vendor.68261ebd.css">
-    <link rel="stylesheet" href="./assets/index.5062a520.css">
+    <link rel="stylesheet" href="./assets/index.cb2212cc.css">
+    <link rel="stylesheet" href="./assets/teacher.a6c9550a.css">
     <script type="module">!function(){try{new Function("m","return import(m)")}catch(o){console.warn("vite: loading legacy build because dynamic import is unsupported, syntax error above should be ignored");var e=document.getElementById("vite-legacy-polyfill"),n=document.createElement("script");n.src=e.src,n.onload=function(){System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))},document.body.appendChild(n)}}();</script>
   </head>
 
@@ -48,7 +55,12 @@
     <div id="app"></div>
     
     <script nomodule>!function(){var e=document,t=e.createElement("script");if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;e.addEventListener("beforeload",(function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()}),!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}();</script>
+<<<<<<< HEAD
     <script nomodule id="vite-legacy-polyfill" src="./assets/polyfills-legacy.639f6553.js"></script>
     <script nomodule id="vite-legacy-entry" data-src="./assets/teacher-legacy.abc5e980.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+=======
+    <script nomodule id="vite-legacy-polyfill" src="./assets/polyfills-legacy.efa4ae3d.js"></script>
+    <script nomodule id="vite-legacy-entry" data-src="./assets/teacher-legacy.45fe519e.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
+>>>>>>> iteration_1.3.4
   </body>
 </html>

+ 11 - 2
src/components/col-video/index.tsx

@@ -2,7 +2,7 @@ import { defineComponent, PropType } from 'vue'
 import styles from './index.module.less'
 import Plyr from 'plyr'
 import 'plyr/dist/plyr.css'
-import { Button, Icon } from 'vant'
+import { Button, Icon, Loading } from 'vant'
 
 import iconVideoPlay from '@/common/images/icon_video_play.png'
 import { browser } from '@/helpers/utils'
@@ -250,7 +250,16 @@ export default defineComponent({
             style={{
               height: this.height || '210px'
             }}
-          ></div>
+          >
+            <Loading
+              size={36}
+              color="#2dc7aa"
+              vertical
+              style={{ height: '100%', justifyContent: 'center' }}
+            >
+              加载中...
+            </Loading>
+          </div>
         )}
         {/* 试看结束 */}
         {this.trySee && this.computedSeeStatus && !this.loading && (

+ 8 - 0
src/router/routes-common.ts

@@ -196,6 +196,14 @@ export const router = [
     meta: {
       title: '会员中心'
     }
+  },
+  {
+    path: '/coupons',
+    name: 'coupons',
+    component: () => import('@/views/coupons/index'),
+    meta: {
+      title: '优惠券'
+    }
   }
 ]
 

+ 10 - 8
src/student/teacher-dependent/components/single.tsx

@@ -86,14 +86,16 @@ export default defineComponent({
     const userInfo = this.userInfo
     return (
       <div class={styles.single}>
-        <SectionDetail
-          icon="personal"
-          title="个人介绍"
-          size={24}
-          border={false}
-        >
-          <p class={styles.introduction}>{userInfo.introduction}</p>
-        </SectionDetail>
+        {userInfo.introduction && (
+          <SectionDetail
+            icon="personal"
+            title="个人介绍"
+            size={24}
+            border={false}
+          >
+            <p class={styles.introduction}>{userInfo.introduction}</p>
+          </SectionDetail>
+        )}
 
         {userInfo.styleVideo && userInfo.styleVideo.length > 0 && (
           <SectionDetail

+ 1 - 1
src/teacher/share-page/share-vip/index.module.less

@@ -23,7 +23,7 @@
   .memberContainer {
     position: relative;
     margin-top: 15px;
-    padding: 0 14px 65px;
+    padding: 0 14px 25px;
     z-index: 99;
   }
   .memberItem {

BIN
src/views/coupons/images/activeIcon.png


BIN
src/views/coupons/images/icon_close.png


BIN
src/views/coupons/images/icon_expired.png


BIN
src/views/coupons/images/icon_used.png


BIN
src/views/coupons/images/inactiveIcon.png


+ 142 - 0
src/views/coupons/index.module.less

@@ -0,0 +1,142 @@
+.coupons {
+  :global {
+    .van-tab {
+      font-size: 16px;
+    }
+    .van-tab--active {
+      color: #2dc7aa;
+    }
+  }
+}
+
+.list {
+  margin: 12px 14px;
+  padding: 12px 14px;
+  background-color: #fff;
+  border-radius: 10px;
+}
+
+.item {
+  position: relative;
+  border-radius: 10px;
+  background: #fae6e7;
+  padding: 20px 10px 20px 18px;
+  color: #fc1a19;
+  + .item {
+    margin-top: 12px;
+  }
+
+  &.USED,
+  &.EXPIRED {
+    background: #f8f8f8;
+    color: #666666;
+
+    .conditionTag {
+      background-color: #ebebeb;
+    }
+  }
+
+  // 是否可以选择
+  &.select {
+    padding-left: 40px;
+  }
+  // 优惠是否能使用
+  &.disabled {
+    opacity: 0.6;
+  }
+
+  &::before,
+  &::after {
+    position: absolute;
+    width: 16px;
+    height: 16px;
+    border-radius: 50%;
+    content: ' ';
+    z-index: 1;
+    top: 50%;
+    background: #fff;
+    margin-top: -8px;
+  }
+  &:before {
+    left: -8px;
+  }
+
+  &:after {
+    right: -8px;
+  }
+
+  .top,
+  .bottom {
+    display: flex;
+    align-items: center;
+  }
+  .price {
+    font-size: 36px;
+    font-weight: bold;
+    line-height: 42px;
+    width: 112px;
+
+    font-family: PingFangSC-Regular, PingFang SC;
+    .suffix {
+      font-size: 24px;
+      line-height: 33px;
+    }
+    .number {
+      display: inline-block;
+      min-width: 40px;
+      text-align: center;
+    }
+  }
+  .type {
+    max-width: 165px;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    font-size: 16px;
+    font-weight: 500;
+    line-height: 22px;
+  }
+
+  .bottom {
+    padding-top: 5px;
+    font-size: 12px;
+  }
+  .condition {
+    width: 112px;
+  }
+  .conditionTag {
+    background: #fbd0d1;
+    border-radius: 8px;
+    font-weight: 600;
+    line-height: 17px;
+    min-width: 80px;
+    display: inline-block;
+    text-align: center;
+  }
+
+  .iconUsed,
+  .iconExpired {
+    position: absolute;
+    top: 0;
+    right: 0;
+    width: 54px;
+    height: 41px;
+  }
+
+  .iconUsed {
+    background: url('./images/icon_used.png') no-repeat center;
+    background-size: contain;
+  }
+  .iconExpired {
+    background: url('./images/icon_expired.png') no-repeat center;
+    background-size: contain;
+  }
+
+  .img-icon {
+    position: absolute;
+    top: 9px;
+    left: 9px;
+    width: 22px;
+    height: 22px;
+  }
+}

+ 54 - 0
src/views/coupons/index.tsx

@@ -0,0 +1,54 @@
+import request from '@/helpers/request'
+import { state } from '@/state'
+import { Tab, Tabs } from 'vant'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+import List from './list'
+
+export default defineComponent({
+  name: 'coupon-container',
+  data() {
+    return {
+      couponCount: {
+        total: 0,
+        useState: 'USABLE'
+      } as any
+    }
+  },
+  async mounted() {
+    try {
+      // 判断是哪个端
+      const url =
+        state.platformType === 'STUDENT' ? '/api-student' : '/api-teacher'
+      const res = await request.get(`${url}/couponInfo/statInfo`)
+      const result = res.data || []
+      this.couponCount = result.find(result => result.useState === 'USABLE')
+      console.log(result.find(result => result.useState === 'USABLE'))
+    } catch {
+      // TODO: handle
+    }
+  },
+  render() {
+    return (
+      <div class={styles.coupons}>
+        <Tabs color="#2DC7AA" lineWidth={44} sticky animated>
+          <Tab
+            title={`可使用${
+              this.couponCount.total > 0
+                ? '(' + this.couponCount.total + '张)'
+                : ''
+            }`}
+          >
+            <List />
+          </Tab>
+          <Tab title="已使用">
+            <List useState="USED" />
+          </Tab>
+          <Tab title="已失效">
+            <List useState="EXPIRED" />
+          </Tab>
+        </Tabs>
+      </div>
+    )
+  }
+})

+ 72 - 0
src/views/coupons/item.tsx

@@ -0,0 +1,72 @@
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+import dayjs from 'dayjs'
+import { Checkbox } from 'vant'
+import activeIcon from './images/activeIcon.png'
+import inactiveIcon from './images/inactiveIcon.png'
+
+export default defineComponent({
+  name: 'coupon-item',
+  props: {
+    item: {
+      type: Object,
+      default: {}
+    },
+    isSelect: {
+      type: Boolean,
+      default: false
+    },
+    onClick: {
+      type: Function,
+      default: (item: any) => {}
+    }
+  },
+  render() {
+    const item: any = this.item
+    return (
+      <div
+        class={[
+          styles.item,
+          styles[item.useState],
+          this.isSelect && styles.select,
+          item.disabled && styles.disabled
+        ]}
+        onClick={() => {
+          if (item.disabled) return
+          this.onClick(item)
+        }}
+      >
+        {/* 可使用的优惠券 & 有选择按钮的 */}
+        {item.useState === 'USABLE' && this.isSelect && (
+          <img
+            class={styles['img-icon']}
+            src={item.checked ? activeIcon : inactiveIcon}
+          />
+        )}
+        <div class={styles.top}>
+          <div class={styles.price}>
+            <span class={styles.suffix}>¥</span>
+            <span class={styles.number}>{item.discountPrice}</span>
+          </div>
+
+          <div class={styles.type}>{item.couponName}</div>
+        </div>
+
+        <div class={styles.bottom}>
+          <div class={styles.condition}>
+            <span class={styles.conditionTag}>
+              {item.useLimit > 0 ? `满${item.useLimit}可用` : '无门槛'}
+            </span>
+          </div>
+          <div class={styles.useTime}>
+            有效期:{dayjs(item.startTime).format('YYYY.MM.DD')}~
+            {dayjs(item.endTime).format('YYYY.MM.DD')}
+          </div>
+        </div>
+
+        {item.useState === 'USED' && <div class={styles.iconUsed}></div>}
+        {item.useState === 'EXPIRED' && <div class={styles.iconExpired}></div>}
+      </div>
+    )
+  }
+})

+ 86 - 0
src/views/coupons/list.tsx

@@ -0,0 +1,86 @@
+import ColResult from '@/components/col-result'
+import request from '@/helpers/request'
+import { state } from '@/state'
+import { List } from 'vant'
+import { defineComponent, PropType } from 'vue'
+import styles from './index.module.less'
+import Item from './item'
+
+// 使用状态: EXPIRED(已失效) USABLE(可使用) USED(已使用)
+export default defineComponent({
+  name: 'coupon-list',
+  props: {
+    useState: {
+      type: String as PropType<'USABLE' | 'USED' | 'EXPIRED'>,
+      default: 'USABLE'
+    }
+  },
+  data() {
+    return {
+      list: [] as any,
+      dataShow: true, // 判断是否有数据
+      loading: false,
+      finished: false,
+      lockLoad: false,
+      params: {
+        useState: this.useState,
+        page: 1,
+        rows: 20
+      }
+    }
+  },
+  mounted() {
+    this.getList()
+  },
+  methods: {
+    async getList() {
+      try {
+        if (this.lockLoad) return
+        // 上锁
+        this.lockLoad = true
+        // 判断是哪个端
+        const url =
+          state.platformType === 'STUDENT' ? '/api-student' : '/api-teacher'
+        const res = await request.post(`${url}/couponInfo/page`, {
+          data: {
+            ...this.params
+          }
+        })
+        this.loading = false
+        const result = res.data || {}
+        this.list = this.list.concat(result.rows || [])
+        this.finished = result.pageNo >= result.totalPage
+        this.params.page = result.pageNo + 1
+        this.dataShow = this.list.length > 0
+        // 解锁
+        this.lockLoad = false
+      } catch {
+        this.dataShow = false
+        this.finished = true
+      }
+    }
+  },
+  render() {
+    return (
+      <>
+        {this.dataShow ? (
+          <List
+            v-model:loading={this.loading}
+            finished={this.finished}
+            finishedText=" "
+            // 为了处理体验问题
+            class={[this.list.length > 0 && styles.list]}
+            onLoad={this.getList}
+            immediateCheck={false}
+          >
+            {this.list.map((item: any) => (
+              <Item item={item} />
+            ))}
+          </List>
+        ) : (
+          <ColResult btnStatus={false} classImgSize="SMALL" tips="暂无优惠券" />
+        )}
+      </>
+    )
+  }
+})

+ 1 - 1
src/views/order-detail/index.module.less

@@ -30,7 +30,7 @@
 
   :global {
     .van-popup--bottom.van-popup--round {
-      border-radius: 6px 6px 0 0;
+      border-radius: 10px 10px 0 0;
     }
   }
 

+ 25 - 70
src/views/order-detail/index.tsx

@@ -17,7 +17,7 @@ import iconTips from '@common/images/icon_tips.png'
 import Payment from './payment'
 import ColHeader from '@/components/col-header'
 import { state } from '@/state'
-import { orderInfos, orderStatus } from './orderStatus'
+import { orderInfos, orderStatus, resestState } from './orderStatus'
 import OrderVideo from './order-video'
 import OrderLive from './order-live'
 import OrderPractice from './order-practice'
@@ -27,6 +27,7 @@ import { moneyFormat } from '@/helpers/utils'
 import OrderPinao from './order-pinao'
 import { getMusicDetail } from '@/student/trade/tradeOrder'
 import OrderActive from './order-active'
+import UseCoupon from './use-coupons'
 
 export default defineComponent({
   name: 'order-detail',
@@ -41,7 +42,8 @@ export default defineComponent({
       agreeStatus: false,
       popupShow: false,
       paymentStatus: false,
-      orderPrice: 0 // 支付金额
+      orderAmount: 0, // 订单金额,用于使用优惠券,余额,优惠等
+      orderPrice: 0 // 支付金额,最后支付金额
     }
   },
   unmounted() {
@@ -58,63 +60,6 @@ export default defineComponent({
       const orderObject = orderStatus.orderObject
       return orderObject.orderList || []
     }
-    // orderInfos() {
-    //   // 商品列表
-    //   const orderList = orderStatus.orderObject.orderList || []
-    //   return orderList.map((item: any) => {
-    //     const params = {
-    //       goodType: item.orderType,
-    //       goodName: item.goodsName,
-    //       recomUserId: item.recomUserId, // 推荐人id
-    //       bizContent: {}
-    //     }
-    //     if (item.orderType === 'VIDEO') {
-    //       params.bizContent = {
-    //         videoLessonGroupId: item.courseGroupId,
-    //         payMoney: item.coursePrice || 0
-    //       }
-    //     } else if (item.orderType === 'LIVE') {
-    //       params.bizContent = {
-    //         groupId: item.courseGroupId
-    //       }
-    //     } else if (item.orderType === 'PRACTICE') {
-    //       const tempTime = item.classTime || []
-    //       const classCourse: any = []
-    //       tempTime.forEach((time: any) => {
-    //         classCourse.push({
-    //           classDate: time.classDate,
-    //           startTime: time.startTime,
-    //           endTime: time.endTime
-    //         })
-    //       })
-    //       params.bizContent = {
-    //         courseGroupName: item.courseGroupName,
-    //         courseIntroduce: item.courseIntroduce,
-    //         subjectId: item.subjectId,
-    //         singleCourseMinutes: item.singleCourseMinutes,
-    //         courseNum: item.courseNum,
-    //         coursePrice: item.coursePrice,
-    //         teacherId: item.teacherId,
-    //         classTime: classCourse
-    //       }
-    //     } else if (item.orderType === 'VIP') {
-    //       params.bizContent = item.id
-    //     } else if (item.orderType === 'MUSIC') {
-    //       params.bizContent = {
-    //         musicSheetId: item.id,
-    //         actualPrice: item.actualPrice || 0,
-    //         clientType: state.platformType
-    //       }
-    //     } else if (item.orderType === 'PINAO_ROOM') {
-    //       params.bizContent = item.id
-    //     } else if (item.orderType === 'ACTI_REGIST') {
-    //       params.bizContent = {
-    //         activityId: item.activityId
-    //       }
-    //     }
-    //     return params
-    //   })
-    // }
   },
   async mounted() {
     // 判断是否是曲目购买(只有智能陪练才会有入口),其它地方不会有入口
@@ -147,6 +92,7 @@ export default defineComponent({
         //
       }
     }
+    this.orderAmount = orderStatus.orderObject.actualPrice || 0
     this.orderPrice = orderStatus.orderObject.actualPrice || 0
 
     // 0元支付特别处理
@@ -157,7 +103,6 @@ export default defineComponent({
   },
   methods: {
     onAuthSuccess() {
-      // console.log('auth success')
       this.popupShow = false
       this.onSubmit() // 实名成功后自动支付
     },
@@ -230,16 +175,19 @@ export default defineComponent({
     },
     onBackOut() {
       // 关闭订单后需要重置数据
-      orderStatus.orderObject = {
-        orderNo: '',
-        actualPrice: 0,
-        orderName: '',
-        orderDesc: '',
-        orderType: '',
-        recomUserId: null as any,
-        activityId: '',
-        orderList: []
-      }
+      resestState()
+    },
+    onCouponSelect(item: any) {
+      console.log('onCouponSelect', item)
+      let discountCount = 0
+      ;(item || []).forEach((item: any) => {
+        discountCount += Number(item.discountPrice)
+      })
+
+      const lastAmount = Number(
+        (Number(this.orderAmount) - Number(discountCount)).toFixed(2)
+      )
+      this.orderPrice = lastAmount >= 0 ? lastAmount : 0
     }
   },
   render() {
@@ -267,6 +215,13 @@ export default defineComponent({
               }
             })}
 
+            {/* 优惠券使用 */}
+            <UseCoupon
+              orderAmount={this.orderAmount}
+              onCouponSelect={this.onCouponSelect}
+              disabled={orderStatus.orderObject.orderNo ? true : false}
+            />
+
             {/* <div class={styles.tips}>
           <h3>
             <Icon name={iconTips} size={15} />

+ 1 - 1
src/views/order-detail/order-vip/index.tsx

@@ -22,7 +22,7 @@ export default defineComponent({
         <CellGroup
           class={'mb12'}
           border={false}
-          style={{ borderRadius: '8px' }}
+          style={{ borderRadius: '8px', overflow: 'hidden' }}
         >
           <Cell
             center

+ 49 - 25
src/views/order-detail/orderStatus.ts

@@ -13,32 +13,56 @@ type orderType =
   | 'PINAO_ROOM'
   | 'ACTI_REGIST'
   | ''
-export const orderStatus = reactive({
-  orderType: '' as orderType, // 购买类型
-  // liveInfo: {} as any, // 直播购买信息
-  // videoInfo: {} as any, // 视频购买信息
-  // practiceInfo: {} as any, // 视频购买信息
-  // vipInfo: {} as any, // 会员购买信息
-  // musicInfo: {} as any, // 单曲购买信息
-  // orderList: [] as Array<any>, // 存放订单详情,可以是多个商品组合
-  orderInfo: {
-    // 订单信息
-    orderNo: '',
-    actualPrice: 0,
-    payStatus: true
-  },
-  orderObject: {
-    // 订单对象
-    orderNo: '',
-    actualPrice: 0,
-    orderName: '',
-    orderDesc: '',
-    orderType: '' as orderType,
-    recomUserId: null as any,
-    orderList: [] as Array<any>,
-    activityId: '' as any
+
+const original = () => {
+  return {
+    orderType: '' as orderType, // 购买类型
+    orderInfo: {
+      // 订单信息
+      orderNo: '',
+      actualPrice: 0,
+      payStatus: true
+    },
+    orderObject: {
+      // 订单对象
+      orderNo: '',
+      actualPrice: 0,
+      orderName: '',
+      orderDesc: '',
+      orderType: '' as orderType,
+      recomUserId: null as any,
+      orderList: [] as Array<any>,
+      activityId: '' as any
+    }
+    // orderObject: {
+    //   orderNo: '',
+    //   actualPrice: 28,
+    //   orderName: '小酷Ai月度会员',
+    //   orderDesc: '小酷Ai月度会员',
+    //   orderType: 'VIP',
+    //   recomUserId: 0,
+    //   activityId: null,
+    //   orderList: [
+    //     {
+    //       orderType: 'VIP',
+    //       goodsName: '小酷Ai月度会员',
+    //       id: 2,
+    //       title: '月度会员',
+    //       price: 28,
+    //       startTime: '2023-02-28',
+    //       endTime: '2023-03-28'
+    //     }
+    //   ]
+    // }
   }
-})
+}
+
+export const orderStatus = reactive(original())
+
+// 重置对象
+export const resestState = () => {
+  Object.assign(orderStatus, original())
+}
 
 export const orderInfos = () => {
   // 商品列表

+ 226 - 0
src/views/order-detail/use-coupons/choice-coupon.tsx

@@ -0,0 +1,226 @@
+import ColResult from '@/components/col-result'
+import request from '@/helpers/request'
+import { state } from '@/state'
+import Item from '@/views/coupons/item'
+import { Button, Loading } from 'vant'
+import { defineComponent } from 'vue'
+import styles from './index.module.less'
+
+export const list = [
+  {
+    couponType: 'FULL_DISCOUNT',
+    discountPrice: '2',
+    useLimit: '30',
+    endTime: new Date(),
+    id: 1,
+    couponName: '小酷Ai充值券',
+    startTime: new Date(),
+    useState: 'USABLE',
+    useTime: new Date()
+  },
+  {
+    couponType: 'FULL_DISCOUNT',
+    discountPrice: '5',
+    useLimit: '20',
+    endTime: new Date(),
+    id: 2,
+    couponName: '小酷Ai充值券',
+    startTime: new Date(),
+    useState: 'USABLE',
+    useTime: new Date()
+  },
+  {
+    couponType: 'FULL_DISCOUNT',
+    discountPrice: '3',
+    useLimit: '10',
+    endTime: new Date(),
+    id: 3,
+    couponName: '小酷Ai充值券',
+    startTime: new Date(),
+    useState: 'USABLE',
+    useTime: new Date()
+  },
+  {
+    couponType: 'FULL_DISCOUNT',
+    discountPrice: '1',
+    useLimit: '29',
+    endTime: new Date(),
+    id: 0,
+    couponName: '小酷Ai充值券',
+    startTime: new Date(),
+    useState: 'USABLE',
+    useTime: new Date()
+  }
+]
+export default defineComponent({
+  name: 'choice-coupon',
+  props: {
+    orderAmount: {
+      type: Number,
+      default: 0
+    },
+    useCoupon: {
+      type: Array,
+      default: () => []
+    }
+  },
+  emits: ['close', 'submit'],
+  data() {
+    return {
+      list: [],
+      consumeAmount: 0, // 消耗金额
+      dataLoading: false
+    }
+  },
+  computed: {
+    // 使用优惠券的数量
+    useLength() {
+      return this.list.filter((list: any) => list.checked).length || 0
+    }
+  },
+  async mounted() {
+    this.getList()
+  },
+  methods: {
+    async getList() {
+      this.dataLoading = true
+      try {
+        // 判断是哪个端
+        const url =
+          state.platformType === 'STUDENT' ? '/api-student' : '/api-teacher'
+        const res = await request.post(`${url}/couponInfo/page`, {
+          data: {
+            useState: 'USABLE',
+
+            page: 1,
+            rows: 100
+          }
+        })
+        this.dataLoading = false
+        const result = res.data || {}
+        // 处理重复请求数据
+        if (this.list.length > 0 && result.pageNo === 1) return
+        this.list = result.rows || []
+
+        // 处理可用优惠券是否支付使用
+        this.list.forEach((item: any) => {
+          // 如果使用金额大于订单金额则优惠券不可用
+          if (item.useLimit > this.orderAmount) {
+            item.disabled = true
+          } else {
+            item.disabled = false
+          }
+
+          // 处理显示已选择的优惠券
+          this.useCoupon.forEach((coupon: any) => {
+            if (item.id === coupon.id) {
+              item.checked = true
+            } else {
+              item.checked = false
+            }
+          })
+        })
+        // 初始化排序
+        const canUsable = this.list.filter((list: any) => !list.disabled)
+        const canUsed = this.list.filter((list: any) => list.disabled)
+        this.list = [...canUsable, ...canUsed]
+
+        this.calcCoupon()
+      } catch {
+        //
+      }
+    },
+    onSubmit() {
+      // 返回选中的优惠券
+      this.$emit(
+        'submit',
+        this.list.filter((list: any) => list.checked)
+      )
+
+      this.list.forEach((item: any) => {
+        item.checked = false
+      })
+    },
+    onSelect(item: any) {
+      item.checked = !item.checked
+      this.calcCoupon()
+    },
+    calcCoupon() {
+      // 计算优惠券
+      // 已使用的优惠券
+      const useList = this.list.filter((list: any) => list.checked)
+      const limitCount = useList.map((list: any) => {
+        return Number(list.useLimit || 0)
+      })
+      const usePrice =
+        limitCount.length > 0
+          ? limitCount.reduce((sum: any, list: any) => {
+              return sum + list
+            })
+          : 0
+      // 使用优惠券后,可判断的金额
+      const useLastAmount = this.orderAmount - usePrice
+      // 判断使用优惠券之后还有没有其它优惠券可用
+      this.list.forEach((item: any) => {
+        if (Number(item.useLimit) >= useLastAmount && !item.checked) {
+          item.disabled = true
+        } else {
+          item.disabled = false
+        }
+      })
+    }
+  },
+  render() {
+    return (
+      <div class={styles.choiceCoupon}>
+        <div class={styles.couponTitle}>
+          <span>优惠券</span>
+          <i class={styles.iconClose} onClick={() => this.$emit('close')}></i>
+        </div>
+
+        <div class={styles.couponContent}>
+          {!this.dataLoading ? (
+            <>
+              {this.list.length > 0 ? (
+                <>
+                  {this.list.map((item: any) => (
+                    <Item item={item} isSelect onClick={this.onSelect} />
+                  ))}
+                </>
+              ) : (
+                <ColResult
+                  btnStatus={false}
+                  tips="暂无优惠券"
+                  classImgSize="SMALL"
+                />
+              )}
+            </>
+          ) : (
+            <Loading
+              size={48}
+              color="#2dc7aa"
+              vertical
+              style={{ height: '100%', justifyContent: 'center' }}
+            >
+              加载中...
+            </Loading>
+          )}
+        </div>
+
+        <div class={[styles.couponFooter, 'van-hairline--top']}>
+          <div class={styles.couponSelectText}>
+            已选<span>{this.useLength}</span>张
+          </div>
+          <Button
+            type="primary"
+            round
+            style={{ minWidth: '105px', fontSize: '16px' }}
+            onClick={this.onSubmit}
+          >
+            确定
+          </Button>
+        </div>
+      </div>
+    )
+  }
+})

+ 65 - 0
src/views/order-detail/use-coupons/index.module.less

@@ -0,0 +1,65 @@
+.useCoupon {
+  padding-top: 16px;
+  padding-bottom: 16px;
+
+  .couponCount {
+    color: #ff3535;
+    font-size: 16px;
+    font-weight: 600;
+    i {
+      font-style: normal;
+      font-size: 14px;
+    }
+  }
+}
+
+.choiceCoupon {
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+  height: 100%;
+}
+.couponTitle {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  flex-shrink: 0;
+  padding: 0 17px;
+  height: 60px;
+  line-height: 60px;
+  font-size: 18px;
+  font-weight: 600;
+  color: #1a1a1a;
+
+  .iconClose {
+    display: inline-block;
+    width: 24px;
+    height: 24px;
+    background: url('../../coupons/images/icon_close.png') no-repeat center;
+    background-size: contain;
+  }
+}
+
+.couponContent {
+  flex: 1 auto;
+  overflow-y: auto;
+  -webkit-overflow-scrolling: touch;
+  padding: 12px 14px;
+}
+
+.couponFooter {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  line-height: 56px;
+  padding: 0 16px;
+  .couponSelectText {
+    font-size: 16px;
+    color: #1a1a1a;
+    span {
+      padding: 0 9px;
+      font-weight: 600;
+      color: #fc1a19;
+    }
+  }
+}

+ 130 - 0
src/views/order-detail/use-coupons/index.tsx

@@ -0,0 +1,130 @@
+import request from '@/helpers/request'
+import { state } from '@/state'
+import { Cell, Popup } from 'vant'
+import { defineComponent } from 'vue'
+import ChoiceCoupon from './choice-coupon'
+import styles from './index.module.less'
+
+export default defineComponent({
+  name: 'use-conpon',
+  props: {
+    disabled: {
+      type: Boolean,
+      default: false
+    },
+    orderAmount: {
+      type: Number,
+      default: 0
+    }
+  },
+  emits: ['couponSelect'],
+  data() {
+    return {
+      popupStatus: false,
+      popupLoading: false,
+      useCouponList: [] as any,
+      useCouponLoading: false,
+      useCouponCount: 0
+    }
+  },
+  computed: {
+    couponCount() {
+      const limitCount = this.useCouponList.map((list: any) => {
+        return Number(list.discountPrice || 0)
+      })
+      return limitCount.length > 0
+        ? limitCount.reduce((sum: any, list: any) => {
+            return sum + list
+          })
+        : 0
+    }
+  },
+  mounted() {
+    this.getUseableCoupon()
+  },
+  methods: {
+    async getUseableCoupon() {
+      try {
+        this.useCouponLoading = true
+        // 判断是哪个端
+        const url =
+          state.platformType === 'STUDENT' ? '/api-student' : '/api-teacher'
+        const res = await request.get(`${url}/couponInfo/statInfo`)
+        this.useCouponLoading = false
+        const result = (res.data || []).find(
+          result => result.useState === 'USABLE'
+        )
+        this.useCouponCount = result.total || 0
+      } catch {
+        // TODO: handle
+      }
+    },
+    onSubmit(item: any) {
+      // useCouponList
+      this.useCouponList = item
+      this.$emit('couponSelect', item)
+      this.popupStatus = false
+      this.popupLoading = false
+    }
+  },
+  render() {
+    return (
+      <>
+        <Cell
+          title="优惠券"
+          class={styles.useCoupon}
+          style={{ borderRadius: '8px' }}
+          isLink={!this.disabled}
+          clickable={false}
+          v-slots={{
+            value: () =>
+              !this.useCouponLoading && (
+                <>
+                  {/* 判断是否有选择优惠券 */}
+                  {this.couponCount > 0 ? (
+                    <span class={styles.couponCount}>
+                      <i>-¥</i>
+                      {this.couponCount}
+                    </span>
+                  ) : (
+                    <>
+                      {/* 判断是否有可以的优惠券 */}
+                      {this.useCouponCount > 0
+                        ? `${this.useCouponCount}张可使用`
+                        : '暂无可使用优惠券'}
+                    </>
+                  )}
+                </>
+              )
+          }}
+          onClick={() => {
+            if (this.disabled) return
+            this.popupStatus = true
+            this.popupLoading = true
+          }}
+        ></Cell>
+
+        <Popup
+          v-model:show={this.popupStatus}
+          position="bottom"
+          round
+          safeAreaInsetBottom={true}
+          style={{ height: '75%' }}
+          onClosed={() => {
+            this.popupLoading = false
+          }}
+        >
+          {/* 优化体验 */}
+          {this.popupLoading && (
+            <ChoiceCoupon
+              useCoupon={this.useCouponList}
+              orderAmount={this.orderAmount}
+              onClose={() => (this.popupStatus = false)}
+              onSubmit={(item: any) => this.onSubmit(item)}
+            />
+          )}
+        </Popup>
+      </>
+    )
+  }
+})

Some files were not shown because too many files changed in this diff