lex vor 8 Monaten
Ursprung
Commit
462e3d093a

BIN
src/common/images/icon_member.png


BIN
src/common/images/icon_member_svip.png


+ 10 - 1
src/constant/index.ts

@@ -34,7 +34,16 @@ export const memberType = {
   MONTH: '月度会员',
   QUARTERLY: '季度会员',
   YEAR_HALF: '半年会员',
-  YEAR: '年度会员'
+  YEAR: '年度会员',
+  PERPETUAL: '永久会员'
+}
+
+export const memberSimpleType = {
+  MONTH: '月度',
+  QUARTERLY: '季度',
+  YEAR_HALF: '半年',
+  YEAR: '年度',
+  PERPETUAL: '永久'
 }
 
 export const courseType = {

+ 1 - 1
src/helpers/request.ts

@@ -88,7 +88,7 @@ request.interceptors.response.use(
       throw new Error(msg)
     }
     const data = await res.clone().json()
-    const otherCode = [200, 0, 999, 5004]
+    const otherCode = [200, 0, 999, 5004, 998]
     if (!otherCode.includes(data.code)) {
       let msg = data.msg || data.message || '处理失败,请重试'
       if (initRequest) {

BIN
src/teacher/share-page/share-vip/images/icon-equity-bg.png


BIN
src/teacher/share-page/share-vip/images/icon-title-3.png


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

@@ -3,12 +3,14 @@
   position: relative;
   background-color: #fff;
 
+
   .intro {
     background: url('../images/share-vip-tips.png') no-repeat center;
     background-size: contain;
     height: 142px;
     font-size: 14px;
     color: #bb6e3a;
+
     p {
       padding: 45px 25px 0;
       text-align: justify;
@@ -18,20 +20,37 @@
 
   .shareBanner {
     width: 100%;
+    font-size: 0;
   }
 
   .memberContainer {
     position: relative;
-    margin-top: 15px;
-    padding: 0 14px 25px;
+    // margin-top: 15px;
+    // padding: 0 14px 25px;
     z-index: 99;
+    overflow: hidden;
+  }
+
+  .iconTitle {
+    width: 258px;
+    height: 28px;
+    margin: 18px auto 17px;
+    display: block;
+  }
+
+  .iconEquity {
+    margin: 0 16px;
+    width: calc(100% - 32px);
   }
+
   .memberItem {
     padding-top: 20px;
+
     .title {
       font-size: 16px;
       color: #333333;
       font-weight: 500;
+
       span {
         color: #f7b500;
       }
@@ -42,9 +61,11 @@
     display: flex;
     justify-content: space-between;
     flex-wrap: wrap;
+
     .function_item__content {
       height: 100%;
     }
+
     .function_item {
       width: 80px;
       padding: 12px 0;
@@ -54,15 +75,19 @@
       background-color: #faefe3;
       text-align: center;
     }
+
     .function_text {
       font-size: 12px;
       color: #814014;
       line-height: 16px;
     }
   }
+
   .system-list::-webkit-scrollbar {
-    display: none; /* Chrome Safari */
+    display: none;
+    /* Chrome Safari */
   }
+
   .system-list {
     width: 100%;
     overflow-x: auto;
@@ -75,6 +100,7 @@
     padding-bottom: 10px;
     margin-bottom: 10px;
   }
+
   .system-item {
     display: flex;
     flex-direction: column;
@@ -88,42 +114,48 @@
     border-radius: 12px;
     border: 1px solid #e5e5e5;
     margin-right: 10px;
+
     .title {
       font-size: 14px;
       font-weight: 500;
       color: #333333;
       line-height: 20px;
     }
+
     .price {
       color: #dc9362;
       font-size: 25px;
       line-height: 1.5;
+
       span {
         font-size: 16px;
       }
     }
+
     .originalPrice {
       color: #937059;
       font-size: 13px;
     }
 
     &.active {
-      background: linear-gradient(
-        215deg,
-        #ffe7c4 0%,
-        rgba(250, 211, 156, 0.21) 100%
-      );
+      background: linear-gradient(215deg,
+          #ffe7c4 0%,
+          rgba(250, 211, 156, 0.21) 100%);
       border: 1px solid #b1652e;
       position: relative;
+
       .title {
         color: #814014;
       }
+
       .price {
         color: #b1652e;
       }
+
       .originalPrice {
         color: #937059;
       }
+
       &::before {
         content: '\e728';
         font: 14px/1 'vant-icon';
@@ -164,9 +196,10 @@
     left: 0;
     background: rgba(0, 0, 0, 0.5);
     z-index: 9999;
+
     img {
       width: 88%;
       margin: 0 6%;
     }
   }
-}
+}

+ 35 - 28
src/teacher/share-page/share-vip/index.tsx

@@ -10,6 +10,9 @@ import { shareCall, initJumpNativePage } from '../share'
 import { browser } from '@/helpers/utils'
 import { postMessage } from '@/helpers/native-message'
 import qs from 'query-string'
+import iconTitle from './images/icon-title-3.png'
+import equityBg from './images/icon-equity-bg.png'
+import ColSticky from '@/components/col-sticky'
 
 export const getAssetsHomeFile = (fileName: string) => {
   const path = `../../../views/member-center/images/${fileName}`
@@ -54,16 +57,16 @@ export default defineComponent({
   },
   async mounted() {
     try {
-      const res = await request.post(
-        `/api-teacher/open/memberPriceSettings/vipPermissions`
-      )
-      const result = res.data || []
-      this.functionList = result.map((item: any) => {
-        return {
-          title: item.paramName,
-          icon: getAssetsHomeFile(`${item.paramValue}.png`)
-        }
-      })
+      // const res = await request.post(
+      //   `/api-teacher/open/memberPriceSettings/vipPermissions`
+      // )
+      // const result = res.data || []
+      // this.functionList = result.map((item: any) => {
+      //   return {
+      //     title: item.paramName,
+      //     icon: getAssetsHomeFile(`${item.paramValue}.png`)
+      //   }
+      // })
 
       if (this.activityId) {
         const active = await request.post(
@@ -94,7 +97,7 @@ export default defineComponent({
       <div class={styles['member-center']}>
         <Image src={shareBanner} class={styles.shareBanner} />
         <div class={styles.memberContainer}>
-          <div class={[styles.intro]}>
+          {/* <div class={[styles.intro]}>
             <p>
               酷乐秀会员可使用包括平台提供的所有训练乐谱,并专享“
               <b>小酷Ai</b>
@@ -114,25 +117,29 @@ export default defineComponent({
                 ))}
               </div>
             </div>
-          )}
+          )} */}
+          <img src={iconTitle} class={styles.iconTitle} />
+          <img src={equityBg} class={styles.iconEquity} />
         </div>
-        <div
-          class={['btnGroup']}
-          style={{ paddingTop: '12px', position: 'relative' }}
-        >
-          {this.discount === 1 && (
-            <div class={styles.tagDiscount}>专属优惠</div>
-          )}
-          <Button
-            block
-            round
-            type="primary"
-            onClick={this.onShare}
-            color="linear-gradient(220deg, #DFA164 0%, #FAC87E 100%)"
+        <ColSticky position="bottom">
+          <div
+            class={['btnGroup']}
+            style={{ paddingTop: '12px', position: 'relative' }}
           >
-            下载小酷Ai开始练习吧!
-          </Button>
-        </div>
+            {this.discount === 1 && (
+              <div class={styles.tagDiscount}>专属优惠</div>
+            )}
+            <Button
+              block
+              round
+              type="primary"
+              onClick={this.onShare}
+              color="linear-gradient(220deg, #DFA164 0%, #FAC87E 100%)"
+            >
+              下载小酷Ai开始练习吧!
+            </Button>
+          </div>
+        </ColSticky>
 
         {this.wxStatus && (
           <div

+ 148 - 10
src/views/member-center/index.module.less

@@ -44,8 +44,43 @@
       right: -3px;
       width: 18px;
       height: 18px;
-      background: url('./new-images/icon-vip-disabled.png') no-repeat center;
-      background-size: contain;
+
+    }
+
+    &.userVip {
+      .showMemeber {
+        background: url('./new-images/icon-vip-disabled.png') no-repeat center;
+        background-size: contain;
+      }
+
+      &.isVip {
+        .userImg {
+          border: 1px solid #F0AF88;
+        }
+
+        .showMemeber {
+          background: url('./new-images/icon-vip.png') no-repeat center;
+          background-size: contain;
+        }
+      }
+    }
+
+    &.userSVip {
+      .showMemeber {
+        background: url('./new-images/icon-svip-disabled.png') no-repeat center;
+        background-size: contain;
+      }
+
+      &.isVip {
+        .userImg {
+          border: 1px solid #F0AF88;
+        }
+
+        .showMemeber {
+          background: url('./new-images/icon-svip.png') no-repeat center;
+          background-size: contain;
+        }
+      }
     }
   }
 
@@ -321,11 +356,15 @@
     color: #662610 !important;
     line-height: 24px;
   }
+
+  .btnDisabled {
+    opacity: .4;
+  }
 }
 
 
 .system_list_section {
-  margin: 0 6px;
+  margin: 0 16px;
 }
 
 .system-list::-webkit-scrollbar {
@@ -361,6 +400,7 @@
   flex-direction: column;
   align-items: center;
   justify-content: flex-start;
+  flex: 1 0 auto;
   width: 96px;
   min-height: 120px;
   box-sizing: border-box;
@@ -369,9 +409,26 @@
   border: 1px solid #E7E7E7;
   margin-left: 10px;
 
-  // &:first-child {
-  //   margin-left: 0;
-  // }
+  &:first-child {
+    margin-left: 0;
+  }
+
+
+  .discountTag {
+    background: linear-gradient(180deg, #FF491A 0%, #FF9F7E 100%);
+    border-radius: 8px;
+    font-weight: 500;
+    font-size: 12px;
+    color: #FFFFFF;
+    padding: 2px 4px;
+    margin-top: 1px;
+  }
+
+  &.discountItem {
+    .price {
+      padding: 0;
+    }
+  }
 
   .iconPermanent {
     position: absolute;
@@ -395,14 +452,15 @@
   }
 
   .price {
-    font-family: DINAlternate, DINAlternate;
+    font-family: DINAlternate-Bold, DINAlternate;
     font-weight: bold;
-    font-size: 30px;
+    font-size: 28px;
     color: #333333;
     padding: 6px 0 9px;
 
     span {
       font-size: 16px;
+      vertical-align: middle;
     }
   }
 
@@ -410,6 +468,10 @@
     color: #999999;
     font-size: 12px;
     line-height: 16px;
+
+    &.originalPriceHide {
+      opacity: 0;
+    }
   }
 
   .extraTip {
@@ -421,6 +483,9 @@
     box-sizing: border-box;
     background: #FFF5E0;
     line-height: 21px;
+    font-weight: 500;
+    font-size: 12px;
+    color: #84520F;
     text-align: center;
     text-overflow: ellipsis;
     white-space: nowrap;
@@ -452,7 +517,8 @@
     justify-content: space-between;
     padding: 0 20px;
     width: 100%;
-    margin: 0 16px;
+    // margin: 0 16px;
+    margin: 0;
     min-height: 81px;
     border: 2px solid #0ED8B0;
     background: linear-gradient(51deg, #E3F9F2 0%, #BCF8E5 100%);
@@ -461,6 +527,19 @@
       left: -2px;
     }
 
+    .discountTag {
+      position: absolute;
+      left: -2px;
+      top: -2px;
+      background: url('./new-images/icon_discount.png') no-repeat center;
+      background-size: contain;
+      width: 48px;
+      height: 16px;
+      padding: 0;
+      margin: 0;
+      border-radius: 0;
+    }
+
 
     .itemBg {
       position: absolute;
@@ -496,7 +575,6 @@
   }
 
   .oneMaxNum {
-
     margin-left: 4px;
     display: inline-block;
     background: linear-gradient(90deg, #BEFCE8 0%, #BFFAE8 100%);
@@ -506,6 +584,10 @@
     padding: 1px 2px;
   }
 
+  .oneMaxNumPrice {
+    text-decoration: line-through;
+  }
+
   .oneBtn {
     position: relative;
     z-index: 1;
@@ -519,6 +601,10 @@
       background: url('./new-images/btn-svip-review.png') no-repeat center;
       background-size: contain;
     }
+
+    &.onBtnDisbled {
+      opacity: .4;
+    }
   }
 }
 
@@ -576,4 +662,56 @@
       }
     }
   }
+}
+
+// 弹窗样式
+.dialogContainer {
+  width: 287px;
+  box-sizing: border-box;
+  background: #FFFFFF;
+  border-radius: 12px;
+  padding: 16px 24px 22px;
+  text-align: center;
+
+  .dialogTitle {
+    font-weight: 500;
+    font-size: 18px;
+    color: #333333;
+    line-height: 25px;
+  }
+
+  .dialogContent {
+    padding: 16px 0 21px;
+    font-size: 15px;
+    color: #777777;
+    line-height: 26px;
+  }
+
+  .dialogBtnGroup {
+    padding: 0 16px;
+  }
+
+  .dialogBtn {
+    font-weight: 500;
+    font-size: 16px;
+    color: #FFFFFF;
+    line-height: 22px;
+  }
+}
+
+.discountTips {
+  background: linear-gradient(203deg, rgba(254, 237, 197, 0.5) 0%, rgba(255, 198, 179, 0.5) 100%);
+  border-radius: 6px;
+  padding: 6px 3px 6px 8px;
+  // font-weight: 500;
+  font-size: 12px;
+  line-height: 17px;
+  color: #6B4429;
+  margin-top: 12px;
+  margin-left: 16px;
+  margin-right: 16px;
+
+  span {
+    color: #FF491A;
+  }
 }

+ 541 - 117
src/views/member-center/index.tsx

@@ -1,8 +1,8 @@
-import { computed, defineComponent, reactive } from 'vue'
+import { computed, defineComponent, onMounted, reactive, shallowRef } from 'vue'
 import styles from './index.module.less'
 import ColHeader from '@/components/col-header'
-import { Button, Image, Popup } from 'vant'
-import { state as baseState } from '@/state'
+import { Button, Image, Popup, Toast } from 'vant'
+import { state as baseState, setLogin } from '@/state'
 import iconShare from './new-images/icon-share.png'
 import { useEventListener } from '@vant/use'
 import ColShare from '@/components/col-share'
@@ -14,16 +14,37 @@ import request from '@/helpers/request'
 import MemberInteres from './components/member-interes'
 import ColSticky from '@/components/col-sticky'
 import { moneyFormat } from '@/helpers/utils'
+import { useRoute, useRouter } from 'vue-router'
+import deepClone from '@/helpers/deep-clone'
+import { memberSimpleType, memberType } from '@/constant'
+import dayjs from 'dayjs'
+import { orderStatus } from '../order-detail/orderStatus'
 
 export default defineComponent({
   name: 'member-center',
   setup() {
+    const route = useRoute()
+    const router = useRouter()
+    const vipList = shallowRef([] as any)
+    const svipList = shallowRef([] as any)
     const state = reactive({
+      activityId: route.query.activityId,
+      recomUserId: route.query.recomUserId,
       titleOpacity: 0,
       shareStatus: false, // 分享
+      dialogVisiable: false,
       shareUrl: '',
       shareDiscount: 0,
-      tabActive: 'SVIP' // 当前选中
+      discountTeacher: {
+        avatar: '',
+        discount: 0,
+        username: ''
+      }, // 优惠折扣老师
+      apiSuffix:
+        baseState.platformType === 'STUDENT' ? '/api-student' : '/api-teacher',
+      tabActive: 'SVIP' as 'SVIP' | 'VIP', // 当前选中
+      selectMember: {} as any, // 选中的数据
+      memberShowList: [] // 购买商品信息
     })
 
     const userInfo = computed(() => {
@@ -33,10 +54,80 @@ export default defineComponent({
         phone: users?.phone,
         avatar: users?.heardUrl,
         id: users?.userId,
-        memberRankSettingId: users?.memberRankSettingId,
-        isVip: users?.isVip,
-        membershipDays: users?.membershipDays,
-        membershipEndTime: users?.membershipEndTime
+        // memberRankSettingId: users?.memberRankSettingId,
+        // isVip: users?.isVip,
+        // membershipDays: users?.membershipDays,
+        // membershipEndTime: users?.membershipEndTime
+        userVip: users?.userVip
+      }
+    })
+
+    // 是否为永久会员
+    const isPermanent = computed(() => {
+      return userInfo.value.userVip?.vipType === 'PERMANENT_SVIP' ? true : false
+    })
+
+    // 购买按钮文案
+    const btnSubmitText = computed(() => {
+      if (isPermanent.value) {
+        return '您已是永久SVIP会员'
+      } else if (userMemberStatus.value === 'EXPIREVIP') {
+        return '立即续费'
+      } else if (userMemberStatus.value === 'NOT_VIP') {
+        if (state.selectMember?.id) {
+          // ¥988/永久SVIP 立即开通
+          return `¥${calcSalePrice(state.selectMember)}/${
+            memberSimpleType[state.selectMember?.period]
+          }${state.tabActive} 立即开通`
+        } else {
+          return '立即开通'
+        }
+      }
+      return '立即续费'
+    })
+
+    /** 当前用户会员状态 动态判断vip svip */
+    const userMemberStatus = computed(() => {
+      // vip类型 VIP:会员 SVIP:SVIP,PERMANENT_SVIP:永久SVIP,NOT_VIP:不是vip
+      // vip过期类型 VIP:会员 SVIP:SVIP,ALL_VIP:全vip
+      if (state.tabActive === 'SVIP') {
+        if (userInfo.value.userVip?.vipType === 'PERMANENT_SVIP') {
+          return 'PERMANENT'
+        } else if (userInfo.value.userVip?.vipType === 'SVIP') {
+          return 'VIP'
+        } else if (
+          ['SVIP', 'ALL_VIP'].includes(userInfo.value.userVip?.expireVipType)
+        ) {
+          return 'EXPIREVIP'
+        } else if (userInfo.value.userVip?.vipType === 'NOT_VIP') {
+          return 'NOT_VIP'
+        }
+      } else if (state.tabActive === 'VIP') {
+        if (userInfo.value.userVip?.vipType === 'VIP') {
+          return 'VIP'
+        } else if (
+          ['VIP', 'ALL_VIP'].includes(userInfo.value.userVip?.expireVipType)
+        ) {
+          return 'EXPIREVIP'
+        } else if (userInfo.value.userVip?.vipType === 'NOT_VIP') {
+          return 'NOT_VIP'
+        }
+      }
+      return 'NOT_VIP'
+    })
+
+    /** 会员信息 */
+    const memberInfos = computed(() => {
+      return {
+        memberLength: state.memberShowList.length,
+        vipLength: vipList.value.legnth,
+        svipLength: svipList.value.legnth,
+        onlyVip:
+          vipList.value.length > 0 && svipList.value.length <= 0 ? true : false,
+        onlySVip:
+          svipList.value.length > 0 && vipList.value.length <= 0 ? true : false,
+        hasAll:
+          vipList.value.length > 0 && svipList.value.length > 0 ? true : false
       }
     })
 
@@ -71,9 +162,199 @@ export default defineComponent({
     })
 
     /** 切换购买类型 */
-    const onChangeTab = (type: string) => {
+    const onChangeTab = (type: 'SVIP' | 'VIP') => {
+      if (type === 'SVIP') {
+        state.memberShowList = deepClone(svipList.value)
+        state.selectMember = state.memberShowList[0]
+      } else if (type === 'VIP') {
+        state.memberShowList = deepClone(vipList.value)
+        state.selectMember = state.memberShowList[0]
+      }
       state.tabActive = type
     }
+
+    const calcSalePrice = (item: any) => {
+      // discount
+      if (item.discount === 1) {
+        const tempPrice = Number(
+          (item.salePrice - item.discountPrice).toFixed(2)
+        )
+        return tempPrice >= 0 ? tempPrice : 0
+      }
+      return item.salePrice
+    }
+
+    const onSubmit = async () => {
+      try {
+        const { data } = await request.post(
+          `${state.apiSuffix}/memberPriceSettings/list`,
+          {
+            data: {
+              status: 1
+            }
+          }
+        )
+        const result = data.list || []
+        const selectItem = result.find(
+          (item: any) => item.id === state.selectMember?.id
+        )
+        // 状态、售价变更时
+        if (
+          !selectItem ||
+          (selectItem && selectItem.salePrice !== state.selectMember.salePrice)
+        ) {
+          state.dialogVisiable = true
+          return
+        }
+
+        const member: any = state.selectMember
+        // 判断是否有会员
+        let startTime = new Date()
+        // vip类型 VIP:会员 SVIP:SVIP,PERMANENT_SVIP:永久SVIP,NOT_VIP:不是vip
+        // vip过期类型 VIP:会员 SVIP:SVIP,ALL_VIP:全vip
+        if (userInfo.value.userVip.vipType === 'SVIP') {
+          startTime = dayjs(
+            userInfo.value.userVip.svipEndDate || new Date()
+          ).toDate()
+        } else if (userInfo.value.userVip.vipType === 'VIP') {
+          startTime = dayjs(
+            userInfo.value.userVip.vipEndDate || new Date()
+          ).toDate()
+        } else if (userInfo.value.userVip.vipType === 'PERMANENT_SVIP') {
+          Toast('您已是永久SVIP会员')
+          return
+        }
+        let endTime = new Date()
+        if (member.period === 'MONTH') {
+          endTime = dayjs(startTime).add(1, 'month').toDate()
+        } else if (member.period === 'QUARTERLY') {
+          endTime = dayjs(startTime).add(3, 'month').toDate()
+        } else if (member.period === 'YEAR_HALF') {
+          endTime = dayjs(startTime).add(6, 'month').toDate()
+        } else if (member.period === 'YEAR') {
+          endTime = dayjs(startTime).add(1, 'year').toDate()
+        }
+
+        orderStatus.orderObject.orderType = state.tabActive
+        orderStatus.orderObject.orderName = `小酷Ai ${state.tabActive} ${member.title}`
+        orderStatus.orderObject.orderDesc = `小酷Ai ${state.tabActive} ${member.title}`
+        orderStatus.orderObject.actualPrice = calcSalePrice(member)
+        orderStatus.orderObject.recomUserId = state.recomUserId
+        orderStatus.orderObject.activityId = state.activityId
+        orderStatus.orderObject.orderNo = ''
+
+        orderStatus.orderObject.orderList = [
+          {
+            orderType: state.tabActive,
+            goodsName: `小酷Ai ${state.tabActive} ${member.title}`,
+            id: member.id,
+            title: member.title,
+            num: 1, // 购买个数
+            salePrice: member.salePrice,
+            period: member.period,
+            vipEndDays: userInfo.value.userVip?.vipEndDays || 0, // 会员剩余天数
+            discount: member.discount, // 是否有折扣
+            discountPrice: member.discountPrice, // 折扣金额
+            price: calcSalePrice(member),
+            startTime: dayjs(startTime).format('YYYY-MM-DD'),
+            endTime: dayjs(endTime).format('YYYY-MM-DD'),
+            recomUserId: state.recomUserId
+          }
+        ]
+        router.push({
+          path: '/orderDetail',
+          query: {
+            orderType: state.tabActive
+          }
+        })
+      } catch {
+        //
+      }
+    }
+
+    /** 格式化分类信息 */
+    const formatMemberList = () => {
+      console.log(vipList.value, svipList.value, 'vipList.value')
+      const onlyVip =
+        vipList.value.length > 0 && svipList.value.length <= 0 ? true : false
+      const onlySVip =
+        svipList.value.length > 0 && vipList.value.length <= 0 ? true : false
+      const hasAll =
+        vipList.value.length > 0 && svipList.value.length > 0 ? true : false
+
+      if (hasAll) {
+        state.tabActive = 'SVIP'
+        state.memberShowList = deepClone(svipList.value)
+      } else if (onlySVip) {
+        state.tabActive = 'SVIP'
+        state.memberShowList = deepClone(svipList.value)
+      } else if (onlyVip) {
+        state.tabActive = 'VIP'
+        state.memberShowList = deepClone(vipList.value)
+      }
+
+      if (state.memberShowList.length > 0) {
+        // 判断是否有数据更新了,需要重新为选择的赋值
+        const item = state.memberShowList.find(
+          (item: any) => item.id === state.selectMember?.id
+        )
+        if (item) {
+          state.selectMember = item
+        } else {
+          state.selectMember = state.memberShowList[0]
+        }
+      }
+    }
+
+    const _init = async () => {
+      try {
+        const { data } = await request.post(
+          `${state.apiSuffix}/memberPriceSettings/list`,
+          {
+            data: {
+              activityId: Number(state.activityId),
+              userId: state.recomUserId,
+              status: 1
+            }
+          }
+        )
+        const { list, ...more } = data
+        state.discountTeacher = {
+          ...more
+        }
+        const result = list || []
+        const vipTemp = [] as any
+        const svipTemp = [] as any
+        result.forEach((item: any) => {
+          item.title = memberType[item.period]
+          if (item.vipType === 'VIP' && item.period !== 'DAY') {
+            vipTemp.push(item)
+          } else if (item.vipType === 'SVIP' && item.period !== 'DAY') {
+            svipTemp.push(item)
+          }
+        })
+        vipList.value = vipTemp ? vipTemp.reverse() : []
+        svipList.value = svipTemp ? svipTemp.reverse() : []
+
+        formatMemberList()
+      } catch {
+        //
+      }
+    }
+    onMounted(async () => {
+      try {
+        const userInfo = await request.get(
+          baseState.platformType === 'TEACHER'
+            ? '/api-teacher/teacher/queryUserInfo'
+            : '/api-student/student/queryUserInfo'
+        )
+        setLogin(userInfo.data)
+
+        _init()
+      } catch {
+        //
+      }
+    })
     return () => (
       <div class={styles.memberCenter}>
         <ColHeader
@@ -95,7 +376,17 @@ export default defineComponent({
         <div class={styles.memberContainer}>
           <i class={styles.showBrid}></i>
           <div class={styles.userSection}>
-            <div class={styles.userImgSection}>
+            <div
+              class={[
+                styles.userImgSection,
+                state.tabActive === 'VIP' ? styles.userVip : styles.userSVip,
+                userMemberStatus.value === 'PERMANENT' ||
+                userMemberStatus.value === 'VIP'
+                  ? styles.isVip
+                  : ''
+                // userMemberStatus.value === 'EXPIREVIP' ? styles.expireVip : ''
+              ]}
+            >
               <Image
                 class={styles.userImg}
                 src={userInfo.value.avatar || iconStudent}
@@ -111,137 +402,243 @@ export default defineComponent({
                 )}
               </div>
               <div class={styles.member_time}>
-                您已是<span>永久SVIP</span>
+                {userMemberStatus.value === 'PERMANENT' && (
+                  <>
+                    您已是<span>永久SVIP</span>
+                  </>
+                )}
+                {userMemberStatus.value === 'VIP' && (
+                  <>
+                    有效期至{' '}
+                    {state.tabActive === 'VIP'
+                      ? userInfo.value.userVip.vipEndDate
+                      : userInfo.value.userVip.svipEndDate}
+                  </>
+                )}
+                {userMemberStatus.value === 'EXPIREVIP' && (
+                  <>
+                    您的{state.tabActive}已过期,续费后{state.tabActive}
+                    权益可继续使用
+                  </>
+                )}
+                {userMemberStatus.value === 'NOT_VIP' && (
+                  <>您还未开通{state.tabActive}会员哦~</>
+                )}
               </div>
             </div>
           </div>
 
           <div class={styles.memberSection}>
-            {/* <div class={styles.member_tabs}>
-              <div
-                class={[
-                  styles.member_tab,
-                  state.tabActive === 'VIP' ? styles.member_tab_active : ''
-                ]}
-                onClick={() => onChangeTab('VIP')}
-              >
-                <div class={[styles.top_tab, styles.top_tab_vip]}>
-                  <i class={[styles.icon_member]}></i>
-                  <span class={styles.icon_text}>
-                    <i class={styles.bottom_line}></i>
-                  </span>
+            {memberInfos.value.hasAll ? (
+              <>
+                <div class={styles.member_tabs}>
+                  <div
+                    class={[
+                      styles.member_tab,
+                      state.tabActive === 'VIP' ? styles.member_tab_active : ''
+                    ]}
+                    onClick={() => onChangeTab('VIP')}
+                  >
+                    <div class={[styles.top_tab, styles.top_tab_vip]}>
+                      <i class={[styles.icon_member]}></i>
+                      <span class={styles.icon_text}>
+                        <i class={styles.bottom_line}></i>
+                      </span>
+                    </div>
+                    <div class={styles.vip_member_tip}></div>
+                  </div>
+                  <div
+                    class={[
+                      styles.member_tab,
+                      state.tabActive === 'SVIP' ? styles.member_tab_active : ''
+                    ]}
+                    onClick={() => onChangeTab('SVIP')}
+                  >
+                    <div class={[styles.top_tab, styles.top_tab_svip]}>
+                      <i class={[styles.icon_member]}></i>
+                      <span class={styles.icon_text}>
+                        <i class={styles.bottom_line}></i>
+                      </span>
+                    </div>
+                    <div class={styles.svip_member_tip}></div>
+                  </div>
                 </div>
-                <div class={styles.vip_member_tip}></div>
-              </div>
-              <div
-                class={[
-                  styles.member_tab,
-                  state.tabActive === 'SVIP' ? styles.member_tab_active : ''
-                ]}
-                onClick={() => onChangeTab('SVIP')}
-              >
-                <div class={[styles.top_tab, styles.top_tab_svip]}>
-                  <i class={[styles.icon_member]}></i>
-                  <span class={styles.icon_text}>
-                    <i class={styles.bottom_line}></i>
-                  </span>
+              </>
+            ) : memberInfos.value.onlyVip ? (
+              <div class={styles.member_tabs}>
+                <div
+                  class={[
+                    styles.member_tab,
+                    styles.member_tab_active,
+                    styles.member_tab_single
+                  ]}
+                >
+                  <div class={[styles.top_tab, styles.top_tab_vip]}>
+                    <i class={[styles.icon_member]}></i>
+                    <span class={styles.icon_text}>
+                      <i class={styles.bottom_line}></i>
+                    </span>
+                  </div>
+                  <div class={styles.vip_member_tip}></div>
                 </div>
-                <div class={styles.svip_member_tip}></div>
               </div>
-            </div> */}
-
-            <div class={styles.member_tabs}>
-              <div
-                class={[
-                  styles.member_tab,
-                  styles.member_tab_active,
-                  styles.member_tab_single
-                ]}
-              >
-                <div class={[styles.top_tab, styles.top_tab_vip]}>
-                  <i class={[styles.icon_member]}></i>
-                  <span class={styles.icon_text}>
-                    <i class={styles.bottom_line}></i>
-                  </span>
+            ) : memberInfos.value.onlySVip ? (
+              // 只有SVIP
+              <div class={styles.member_tabs}>
+                <div
+                  class={[
+                    styles.member_tab,
+                    styles.member_tab_active,
+                    styles.member_tab_single
+                  ]}
+                >
+                  <div class={[styles.top_tab, styles.top_tab_svip]}>
+                    <i class={[styles.icon_member]}></i>
+                    <span class={styles.icon_text}>
+                      <i class={styles.bottom_line}></i>
+                    </span>
+                  </div>
+                  <div class={styles.vip_member_tip}></div>
                 </div>
-                <div class={styles.vip_member_tip}></div>
               </div>
-            </div>
+            ) : null}
 
             {/* 判断是否有推荐老师 */}
-            <div class={styles.memberDiscount}>
-              <Image src={iconTeacher} class={styles.discountAvatar} />
+            {state.discountTeacher.discount == 1 && (
+              <div class={styles.memberDiscount}>
+                <Image
+                  src={state.discountTeacher.avatar || iconTeacher}
+                  class={styles.discountAvatar}
+                />
 
-              <span class={styles.discountName}>
-                {/* {this.discountTeacher.username} */}
-                王老师的<span>专属优惠~</span>
-              </span>
+                <span class={styles.discountName}>
+                  {state.discountTeacher.username}老师的<span>专属优惠~</span>
+                </span>
+
+                <Image src={iconGift} class={styles.discountGift} />
+              </div>
+            )}
 
-              <Image src={iconGift} class={styles.discountGift} />
-            </div>
             {/* 选择会员模式 */}
             <div class={styles.system_list_section}>
               <div
                 class={[
                   styles['system-list'],
                   state.tabActive === 'VIP' ? styles.system_list_vip : '',
-                  styles.list_one
+                  memberInfos.value.memberLength === 2 ? styles.list_two : '',
+                  memberInfos.value.memberLength === 1 ? styles.list_one : ''
                 ]}
               >
-                {/*   <div class={[styles['system-item'], styles.active]}>
-                  <span class={[styles.iconPermanent]}></span>
-
-                  <p class={styles.s_title}>永久会员</p>
-                  <p class={styles.price}>
-                    <span>¥</span>
-                    {moneyFormat(998, '0,0[.]00')}
-                  </p>
-                  <del class={styles.originalPrice}>
-                    ¥{moneyFormat(1888, '0,0[.]00')}
-                  </del>
-                </div>
-                <div class={[styles['system-item']]}>
-                  <p class={styles.s_title}>年度会员</p>
-                  <p class={styles.price}>
-                    <span>¥</span>
-                    {moneyFormat(998, '0,0[.]00')}
-                  </p>
-                  <del class={styles.originalPrice}>
-                    ¥{moneyFormat(1888, '0,0[.]00')}
-                  </del>
-                </div> */}
-                {/* <div class={[styles['system-item']]}>
-                <p class={styles.s_title}>月度会员</p>
-                <p class={styles.price}>
-                  <span>¥</span>
-                  {moneyFormat(998, '0,0[.]00')}
-                </p>
-                <del class={styles.originalPrice}>
-                  ¥{moneyFormat(1888, '0,0[.]00')}
-                </del>
-
-                <p class={styles.extraTip}>每天约¥2</p>
-              </div> */}
-                {/* 一条数据的样式 */}
-                <div class={[styles['system-item']]}>
-                  <span class={[styles.iconPermanent]}></span>
-                  <div class={styles.oneInfo}>
-                    <div class={styles.priceS}>
-                      <p class={styles.price}>
-                        <span>¥</span>
-                        {moneyFormat(998, '0,0[.]00')}
-                      </p>
-                      <p class={styles.s_title}>永久会员</p>
-                    </div>
-                    <div class={styles.oneMaxNum}>限量1000份</div>
-                  </div>
-                  <span class={[styles.oneBtn, styles.onBtnRenew]}></span>
+                {memberInfos.value.memberLength >= 2 ? (
+                  <>
+                    {state.memberShowList.map((member: any) => (
+                      <div
+                        class={[
+                          styles['system-item'],
+                          member.id === state.selectMember.id
+                            ? styles.active
+                            : '',
+                          member.discount === 1 ? styles.discountItem : ''
+                        ]}
+                        onClick={() => {
+                          state.selectMember = member
+                        }}
+                      >
+                        {/* 只有永久才会有数量提示 */}
+                        {member.period === 'PERPETUAL' && (
+                          <span class={[styles.iconPermanent]}></span>
+                        )}
 
-                  <i class={styles.itemBg}></i>
-                </div>
+                        <p class={styles.s_title}>{member.title}</p>
+                        {member.discount === 1 && (
+                          <span class={styles.discountTag}>专属优惠</span>
+                        )}
+                        <p class={styles.price}>
+                          <span>¥</span>
+                          {moneyFormat(calcSalePrice(member), '0,0[.]00')}
+                        </p>
+                        <del
+                          class={[
+                            styles.originalPrice,
+                            calcSalePrice(member) >= member.originalPrice ||
+                            member.desc
+                              ? styles.originalPriceHide
+                              : ''
+                          ]}
+                        >
+                          ¥{moneyFormat(member.originalPrice, '0,0[.]00')}
+                        </del>
+
+                        <p class={styles.extraTip}>{member.desc}</p>
+                      </div>
+                    ))}
+                  </>
+                ) : (
+                  <>
+                    {/* 一条数据的样式 */}
+                    {state.memberShowList.map((member: any) => (
+                      <div class={[styles['system-item']]}>
+                        {/* 只有永久才会有数量提示 */}
+                        {member.period === 'PERPETUAL' && (
+                          <span class={[styles.iconPermanent]}></span>
+                        )}
+
+                        {member.discount === 1 && (
+                          <span class={styles.discountTag}></span>
+                        )}
+                        <div class={styles.oneInfo}>
+                          <div class={styles.priceS}>
+                            <p class={styles.price}>
+                              <span>¥</span>
+                              {moneyFormat(calcSalePrice(member), '0,0[.]00')}
+                            </p>
+                            <p class={styles.s_title}>{member.title}</p>
+                          </div>
+                          {/* 只有永久才会有数量提示 */}
+                          {member.period === 'PERPETUAL' ? (
+                            <div class={styles.oneMaxNum}>限量1000份</div>
+                          ) : (
+                            <div
+                              class={[styles.oneMaxNum, styles.oneMaxNumPrice]}
+                            >
+                              ¥{member.originalPrice}
+                            </div>
+                          )}
+                        </div>
+                        <span
+                          class={[
+                            styles.oneBtn,
+                            ['EXPIREVIP', 'VIP', 'PERMANENT'].includes(
+                              userMemberStatus.value
+                            )
+                              ? styles.onBtnRenew
+                              : '',
+                            userMemberStatus.value === 'PERMANENT'
+                              ? styles.onBtnDisbled
+                              : ''
+                          ]}
+                          onClick={() => {
+                            if (userMemberStatus.value === 'PERMANENT') return
+                            onSubmit()
+                          }}
+                        ></span>
+                        <i class={styles.itemBg}></i>
+                      </div>
+                    ))}
+                  </>
+                )}
               </div>
             </div>
 
+            {/* 是选择会员 会员天数大于0 */}
+            {state.tabActive === 'VIP' &&
+              userInfo.value.userVip?.vipEndDays > 0 && (
+                <div class={styles.discountTips}>
+                  购买VIP的会员续费SVIP年度会员,
+                  <span>原VIP会员天数升级为SVIP</span>
+                </div>
+              )}
+
             <MemberInteres type={state.tabActive} />
           </div>
         </div>
@@ -251,9 +648,10 @@ export default defineComponent({
             <Button
               block
               color="linear-gradient( 241deg, #FFD984 0%, #FFEAB9 100%)"
-              class={styles.btn}
+              class={[styles.btn, isPermanent.value ? styles.btnDisabled : '']}
+              onClick={onSubmit}
             >
-              立即续费
+              {btnSubmitText.value}
             </Button>
           </div>
         </ColSticky>
@@ -281,6 +679,32 @@ export default defineComponent({
             </div>
           </ColShare>
         </Popup>
+
+        <Popup
+          v-model:show={state.dialogVisiable}
+          style={{ background: 'transparent' }}
+          closeOnClickOverlay={false}
+        >
+          <div class={styles.dialogContainer}>
+            <div class={styles.dialogTitle}>提示</div>
+            <div class={styles.dialogContent}>产品信息已更新,请重新选择</div>
+
+            <div class={styles.dialogBtnGroup}>
+              <Button
+                round
+                type="primary"
+                block
+                class={styles.dialogBtn}
+                onClick={() => {
+                  _init()
+                  state.dialogVisiable = false
+                }}
+              >
+                重新选择
+              </Button>
+            </div>
+          </div>
+        </Popup>
       </div>
     )
   }

BIN
src/views/member-center/new-images/icon_discount.png


+ 42 - 0
src/views/order-detail/index.module.less

@@ -83,6 +83,13 @@
       color: #999999;
       line-height: 20px;
 
+      .goodsNum {
+        font-size: 12px;
+        color: #777777;
+        padding-right: 6px;
+        font-weight: 400;
+      }
+
       .price {
         display: inline-block;
         font-size: 28px;
@@ -111,4 +118,39 @@
 .qrcodePopup {
   width: 90%;
   overflow: initial;
+}
+
+// 弹窗样式
+.dialogContainer {
+  width: 287px;
+  box-sizing: border-box;
+  background: #FFFFFF;
+  border-radius: 12px;
+  padding: 16px 24px 22px;
+  text-align: center;
+
+  .dialogTitle {
+    font-weight: 500;
+    font-size: 18px;
+    color: #333333;
+    line-height: 25px;
+  }
+
+  .dialogContent {
+    padding: 16px 0 21px;
+    font-size: 15px;
+    color: #777777;
+    line-height: 26px;
+  }
+
+  .dialogBtnGroup {
+    padding: 0 16px;
+  }
+
+  .dialogBtn {
+    font-weight: 500;
+    font-size: 16px;
+    color: #FFFFFF;
+    line-height: 22px;
+  }
 }

+ 95 - 22
src/views/order-detail/index.tsx

@@ -18,7 +18,7 @@ import qs from 'query-string'
 import Payment from './payment'
 import UrlPayment from '../adapay/payment'
 import ColHeader from '@/components/col-header'
-import { state } from '@/state'
+import { setLogin, state } from '@/state'
 import {
   beforeSubmit,
   orderInfos,
@@ -47,6 +47,10 @@ export default defineComponent({
     const query = this.$route.query
     return {
       loading: false, // 是否加载中,为了处理0元订单()
+      dialogVisiable: false,
+      dialogContent: '',
+      dialogBtnText: '确定',
+      dialogType: 'back' as 'back' | 'refresh',
       orderType: query.orderType as string,
       recomUserId: query.recomUserId, // 推荐人id
       activityId: query.activityId, // 活动编号
@@ -84,6 +88,18 @@ export default defineComponent({
       // 商品列表
       const orderObject = orderStatus.orderObject
       return orderObject.orderList || []
+    },
+    goodsNum() {
+      const orderList = orderStatus.orderObject.orderList || []
+      let num = 0
+      orderList.forEach((item: any) => {
+        if (item.num) {
+          num += item.num
+        } else {
+          num += 1
+        }
+      })
+      return num
     }
   },
   async mounted() {
@@ -139,14 +155,10 @@ export default defineComponent({
     })
 
     if (!orderStatus.orderObject.orderType) {
-      Dialog.alert({
-        title: '提示',
-        message: '商品信息已更新,请返回后重新购买',
-        confirmButtonText: '确定',
-        confirmButtonColor: '#2dc7aa'
-      }).then(() => {
-        this.$router.back()
-      })
+      this.dialogVisiable = true
+      this.dialogContent = '产品信息已更新,请重新选择'
+      this.dialogBtnText = '确定'
+      this.dialogType = 'back'
       return
     }
   },
@@ -264,14 +276,12 @@ export default defineComponent({
             }
           }
         }
-
         return
       }
 
       // 正常支付
       try {
         const orderObject = orderStatus.orderObject
-
         if (this.paymentVersion === 'V1') {
           const url =
             state.platformType === 'TEACHER'
@@ -324,14 +334,17 @@ export default defineComponent({
           const result = res.data || {}
           // 支付成功
           if (res.code === 999) {
-            Dialog.alert({
-              title: '提示',
-              message: '商品信息已更新,请返回后重新购买',
-              confirmButtonText: '确定',
-              confirmButtonColor: '#2dc7aa'
-            }).then(() => {
-              this.$router.back()
-            })
+            this.dialogVisiable = true
+            this.dialogContent = '产品信息已更新,请重新选择'
+            this.dialogBtnText = '确定'
+            this.dialogType = 'back'
+            return
+          }
+          if (res.code === 998) {
+            this.dialogVisiable = true
+            this.dialogContent = '您当前VIP天数更新,请刷新后尝试'
+            this.dialogBtnText = '刷新'
+            this.dialogType = 'refresh'
             return
           }
           if (result.status == 'PAID') {
@@ -362,6 +375,24 @@ export default defineComponent({
         }
       }
     },
+    async onDialogConfirm() {
+      this.dialogVisiable = false
+      if (this.dialogType === 'back') {
+        this.$router.back()
+      } else if (this.dialogType === 'refresh') {
+        // 更新会员天数
+        const userInfo = await request.get(
+          state.platformType === 'TEACHER'
+            ? '/api-teacher/teacher/queryUserInfo'
+            : '/api-student/student/queryUserInfo'
+        )
+        setLogin(userInfo.data)
+        const vipEndDays = userInfo.data.userVip.vipEndDays || 0
+        orderStatus.orderObject.orderList.forEach((item: any) => {
+          item.vipEndDays = vipEndDays
+        })
+      }
+    },
     onBackOut() {
       // 关闭订单后需要重置数据
       resestState()
@@ -377,12 +408,12 @@ export default defineComponent({
         (Number(this.orderAmount) - Number(discountCount)).toFixed(2)
       )
       this.orderPrice = lastAmount >= 0 ? lastAmount : 0
-
       // 设置优惠券编号
       const couponIds = (item || []).map((item: any) => {
         return item.couponIssueId
       })
       orderStatus.orderObject.couponId = couponIds.join(',') || ''
+      orderStatus.orderObject.couponDiscountPrice += discountCount
     },
     onConfirm(val: any) {
       const config: any = this.orderInfo.paymentConfig || {}
@@ -475,8 +506,26 @@ export default defineComponent({
                 return <OrderLive item={item} />
               } else if (item.orderType === 'PRACTICE') {
                 return <OrderPractice item={item} />
-              } else if (item.orderType === 'VIP') {
-                return <OrderVip item={item} />
+              } else if (
+                item.orderType === 'VIP' ||
+                item.orderType === 'SVIP'
+              ) {
+                return (
+                  <OrderVip
+                    item={item}
+                    onPriceChange={(price: number) => {
+                      // 重置金额
+                      this.orderAmount = Number(price)
+                      const lastAmount = Number(
+                        (
+                          Number(this.orderAmount) -
+                          Number(orderStatus.orderObject.couponDiscountPrice)
+                        ).toFixed(2)
+                      )
+                      this.orderPrice = lastAmount >= 0 ? lastAmount : 0
+                    }}
+                  />
+                )
               } else if (item.orderType === 'MUSIC') {
                 return <OrderMusic item={item} />
               } else if (item.orderType === 'PIANO_ROOM') {
@@ -519,6 +568,7 @@ export default defineComponent({
 
               <div class={styles.btnGroup}>
                 <div class={styles.priceSection}>
+                  <span class={styles.goodsNum}>共{this.goodsNum || 1}件</span>
                   支付金额:
                   <div class={styles.price}>
                     <span class={styles.priceUnit}>¥</span>
@@ -596,6 +646,29 @@ export default defineComponent({
             // orderType={orderType.value}
           />
         </Popup>
+
+        <Popup
+          v-model:show={this.dialogVisiable}
+          style={{ background: 'transparent' }}
+          closeOnClickOverlay={false}
+        >
+          <div class={styles.dialogContainer}>
+            <div class={styles.dialogTitle}>提示</div>
+            <div class={styles.dialogContent}>{this.dialogContent}</div>
+
+            <div class={styles.dialogBtnGroup}>
+              <Button
+                round
+                type="primary"
+                block
+                class={styles.dialogBtn}
+                onClick={this.onDialogConfirm}
+              >
+                {this.dialogBtnText}
+              </Button>
+            </div>
+          </div>
+        </Popup>
       </div>
     )
   }

+ 46 - 0
src/views/order-detail/order-vip/index.module.less

@@ -9,6 +9,9 @@
 
 .titleClass {
   padding-left: 15px;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
 }
 
 .title {
@@ -74,4 +77,47 @@
       padding: 15px;
     }
   }
+}
+
+.discountTips {
+  background: linear-gradient(203deg, rgba(254, 237, 197, 0.5) 0%, rgba(255, 198, 179, 0.5) 100%);
+  border-radius: 6px;
+  padding: 6px 3px 6px 8px;
+  font-weight: 500;
+  font-size: 12px;
+  line-height: 17px;
+  color: #6B4429;
+  margin-bottom: 12px;
+
+  span {
+    color: #FF491A;
+  }
+}
+
+.addNum {
+  display: flex;
+  justify-content: flex-end;
+  --van-stepper-input-height: 22px;
+  --van-stepper-button-round-theme-color: #2DC7AA;
+
+  :global {
+    .van-stepper--round .van-stepper__minus {
+      // width: 22px;
+      // height: 22px;
+      border-color: #F7F8F9;
+      background-color: #F7F8F9;
+      color: #333333;
+    }
+
+    .van-stepper--round .van-stepper__input {
+      background: #F7F8F9;
+      border-radius: 6px;
+      margin: 0 8px;
+    }
+
+    .van-stepper--round .van-stepper__plus--disabled,
+    .van-stepper--round .van-stepper__minus--disabled {
+      opacity: 0.6;
+    }
+  }
 }

+ 99 - 11
src/views/order-detail/order-vip/index.tsx

@@ -1,11 +1,14 @@
-import { Cell, CellGroup, Icon, Image } from 'vant'
+import { Cell, CellGroup, Icon, Image, Stepper } from 'vant'
 import { defineComponent } from 'vue'
 import styles from './index.module.less'
-import { orderStatus } from '../orderStatus'
+// import { orderStatus } from '../orderStatus'
 
 import iconMember from '@common/images/icon_member.png'
-import iconTimer from '@common/images/icon_timer.png'
-import request from '@/helpers/request'
+import iconMemberSvip from '@common/images/icon_member_svip.png'
+import dayjs from 'dayjs'
+import { memberSimpleType } from '@/constant'
+// import iconTimer from '@common/images/icon_timer.png'
+// import request from '@/helpers/request'
 
 export default defineComponent({
   name: 'OrderVideo',
@@ -15,6 +18,29 @@ export default defineComponent({
       default: {}
     }
   },
+  emits: ['priceChange'],
+  computed: {
+    needBuyNums() {
+      const item = this.item
+      if (item.vipEndDays > 0) {
+        const endTime = dayjs(item.startTime).add(item.vipEndDays, 'day')
+        const unit = item.period === 'YEAR' ? 'years' : 'months'
+        const months = endTime.diff(dayjs(item.startTime), unit)
+        if (item.period === 'MONTH') {
+          return months + 1
+        } else if (item.period === 'QUARTERLY') {
+          return Math.ceil((months + 1) / 3)
+        } else if (item.period === 'YEAR_HALF') {
+          return Math.ceil((months + 1) / 6)
+        } else if (item.period === 'YEAR') {
+          return months + 1
+        } else {
+          return 1
+        }
+      }
+      return 1
+    }
+  },
   render() {
     const item = this.item
     return (
@@ -24,20 +50,82 @@ export default defineComponent({
             // center
             titleClass={styles.titleClass}
             v-slots={{
-              icon: () => <Image class={styles.memberLogo} src={iconMember} />,
+              icon: () => (
+                <Image
+                  class={styles.memberLogo}
+                  src={item.orderType === 'SVIP' ? iconMemberSvip : iconMember}
+                />
+              ),
               title: () => (
-                <div class={styles.container}>
-                  <div class={styles.title}>小酷Ai{item.title}</div>
-                  <div class={styles.price}>
-                    <i>¥</i>
-                    {(this as any).$filters.moneyFormat(item.price)}
+                <>
+                  <div class={styles.container}>
+                    <div class={styles.title}>{item.goodsName}</div>
+                    <div class={styles.price}>
+                      <i>¥</i>
+                      {(this as any).$filters.moneyFormat(item.price)}
+                    </div>
                   </div>
-                </div>
+                  <div class={styles.addNum}>
+                    <Stepper
+                      disableInput
+                      disabled={item.period === 'PERPETUAL'}
+                      v-model={item.num}
+                      theme="round"
+                      min={1}
+                      max={99}
+                      onChange={() => {
+                        console.log(item)
+
+                        let endTime = new Date()
+                        if (item.period === 'MONTH') {
+                          endTime = dayjs(item.startTime)
+                            .add(1 * item.num, 'month')
+                            .toDate()
+                        } else if (item.period === 'QUARTERLY') {
+                          endTime = dayjs(item.startTime)
+                            .add(3 * item.num, 'month')
+                            .toDate()
+                        } else if (item.period === 'YEAR_HALF') {
+                          endTime = dayjs(item.startTime)
+                            .add(6 * item.num, 'month')
+                            .toDate()
+                        } else if (item.period === 'YEAR') {
+                          endTime = dayjs(item.startTime)
+                            .add(1 * item.num, 'year')
+                            .toDate()
+                        }
+                        item.endTime = dayjs(endTime).format('YYYY-MM-DD')
+                        // 价格变化
+                        this.$emit(
+                          'priceChange',
+                          (item.price * item.num).toFixed(2)
+                        )
+                      }}
+                    ></Stepper>
+                  </div>
+                </>
               )
             }}
           />
         </CellGroup>
 
+        {item.orderType === 'SVIP' && item.vipEndDays > 0 && (
+          <div class={styles.discountTips}>
+            {item.num >= this.needBuyNums ? (
+              <>
+                当前购买SVIP已达免费升级标准,
+                <span>VIP({item.vipEndDays}天)可免费升级为SVIP</span>
+              </>
+            ) : (
+              <>
+                购买{this.needBuyNums}个{memberSimpleType[item.period]}
+                SVIP会员,
+                <span>剩余VIP({item.vipEndDays}天)可免费升级为SVIP</span>
+              </>
+            )}
+          </div>
+        )}
+
         <CellGroup
           class={['mb12', styles.cellGroup, styles.cellGroupTimer]}
           border={false}

+ 12 - 2
src/views/order-detail/orderStatus.ts

@@ -10,6 +10,7 @@ type orderType =
   | 'PRACTICE'
   | 'GOODS'
   | 'VIP'
+  | 'SVIP'
   | 'MUSIC'
   | 'PIANO_ROOM'
   | 'ACTI_REGIST'
@@ -37,6 +38,7 @@ const original = () => {
       orderList: [] as Array<any>, // 商品信息
       activityId: '' as any, // 活动编号
       couponId: '' as string, // 优惠券编号
+      couponDiscountPrice: 0 as number, // 优惠券扣减金额
       discountPrice: 0 as number // 优惠
     } as any
     // orderObject: {
@@ -78,7 +80,7 @@ export const orderInfos = () => {
       goodName: item.goodsName,
       recomUserId: item.recomUserId, // 推荐人id
       bizContent: {}
-    }
+    } as any
     if (item.orderType === 'VIDEO') {
       params.bizContent = {
         videoLessonGroupId: item.courseGroupId,
@@ -110,6 +112,10 @@ export const orderInfos = () => {
       }
     } else if (item.orderType === 'VIP') {
       params.bizContent = item.id
+    } else if (item.orderType === "SVIP") {
+      params.bizContent = item.id
+      params.vipEndDays = item.vipEndDays
+      params.goodsNum = item.num
     } else if (item.orderType === 'MUSIC') {
       params.bizContent = {
         musicSheetId: item.id,
@@ -148,10 +154,14 @@ export const orderTenantInfos = () => {
       goodName: item.goodsName,
       goodNum: 1,
       bizContent: {}
-    }
+    } as any
     if (item.orderType === 'VIP') {
       params.bizContent = item.id
       params.bizId = item.id
+    } else if (item.orderType === "SVIP") {
+      params.bizContent = item.id
+      params.vipEndDays = item.vipEndDays
+      params.goodsNum = item.num
     } else if (item.orderType === 'MUSIC') {
       params.bizContent = {
         musicSheetId: item.id,

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

@@ -11,6 +11,7 @@ import styles from './index.module.less'
 export const couponEnum = {
   UNIVERSAL: 'UNIVERSAL',
   VIP: 'VIP',
+  SVIP: 'SVIP',
   PIANO_ROOM: 'PIANO',
   GOODS: 'MALL',
   MUSIC: 'MUSIC',