ソースを参照

Merge branch 'iteration-20240715'

lex 1 年間 前
コミット
a7dfcd5e2d

BIN
src/components/col-share/images/vip1.png


+ 4 - 0
src/components/col-share/index.module.less

@@ -274,6 +274,10 @@
     padding: 8px 46px;
   }
 
+  .savePictureBtn {
+    border-color: #fff;
+  }
+
   &.shareGroupTenantBtn {
     :global {
       .van-button--primary {

+ 1 - 0
src/components/col-share/index.tsx

@@ -268,6 +268,7 @@ export default defineComponent({
               <Button
                 type="primary"
                 plain
+                class={styles.savePictureBtn}
                 round
                 loading={this.saveLoading}
                 loadingText="保存中"

+ 21 - 0
src/components/the-noticeBar/index.module.less

@@ -0,0 +1,21 @@
+.wrap{
+    max-width: 100%;
+    overflow: hidden;
+    display: flex;
+    align-items: center;
+    &.isAnitaion, &:hover{
+        .notice{
+            width: auto;
+            overflow: initial;
+        }
+    }
+}
+.notice{
+    transition-timing-function: linear;
+    transition-duration: 5s;
+    width: 100%;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    transition-timing-function: linear;
+}

+ 105 - 0
src/components/the-noticeBar/index.tsx

@@ -0,0 +1,105 @@
+import { defineComponent, reactive, ref, watch, onMounted } from 'vue'
+import styles from './index.module.less'
+
+const refAnimation = (callback: any) => {
+  requestAnimationFrame(() => {
+    requestAnimationFrame(() => {
+      callback()
+    })
+  })
+}
+
+export default defineComponent({
+  name: 'TheNoticeBar',
+  props: {
+    text: {
+      type: String,
+      default: ''
+    },
+    isAnimation: {
+      type: Boolean,
+      default: false
+    }
+  },
+  setup(props, { slots }) {
+    const wrapRef = ref()
+    const contentRef = ref()
+    const notiData = reactive({
+      isActive: false,
+      wrapWidth: 0,
+      contentWidth: 0,
+      contentStyle: {
+        transitionDuration: '0s',
+        transform: 'translateX(0px)'
+      },
+      time: null as any
+    })
+    const init = () => {
+      if (notiData.isActive || !contentRef.value || !wrapRef.value) return
+      notiData.isActive = true
+
+      notiData.contentWidth = contentRef.value.getBoundingClientRect().width
+      notiData.wrapWidth = wrapRef.value.getBoundingClientRect().width
+      console.log('进来加载动画', notiData.contentWidth - notiData.wrapWidth)
+
+      if (notiData.contentWidth - notiData.wrapWidth > 0) {
+        startAnimate()
+      }
+    }
+
+    onMounted(() => {
+      init()
+      // startAnimate();
+    })
+    const startAnimate = () => {
+      if (notiData.contentWidth <= notiData.wrapWidth || !notiData.isActive) {
+        notiData.contentStyle.transitionDuration = '0s'
+        notiData.contentStyle.transform = 'translateX(0px)'
+        return
+      }
+
+      notiData.contentStyle.transitionDuration = '5s'
+      notiData.contentStyle.transform = 'translateX(-100%)'
+
+      notiData.time = setTimeout(() => {
+        notiData.contentStyle.transitionDuration = '0s'
+        notiData.contentStyle.transform = `translateX(${notiData.wrapWidth}px)`
+        refAnimation(startAnimate)
+      }, 5 * 1000)
+    }
+    const stopAnimate = () => {
+      clearTimeout(notiData.time)
+      notiData.isActive = false
+      notiData.contentStyle.transitionDuration = '0s'
+      notiData.contentStyle.transform = 'translateX(0px)'
+      notiData.time = null
+    }
+    watch(
+      () => props.isAnimation,
+      val => {
+        if (val) {
+          refAnimation(init)
+        } else {
+          refAnimation(stopAnimate)
+        }
+      }
+    )
+    //    onMouseenter={() => !props.isAnimation && init()}
+    // onMouseleave={() => !props.isAnimation && stopAnimate()}
+    return () => (
+      <div
+        ref={wrapRef}
+        class={[styles.wrap, props.isAnimation ? styles.isAnitaion : '']}
+      >
+        <div
+          ref={contentRef}
+          style={notiData.contentStyle}
+          class={styles.notice}
+        >
+          {props.text}
+          {slots.default && slots.default()}
+        </div>
+      </div>
+    )
+  }
+})

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

@@ -202,4 +202,19 @@
       margin: 0 6%;
     }
   }
+}
+
+.btnGroup {
+  // paddingTop: '12px', position: 'relative'
+  padding-top: 12px;
+  position: relative;
+  background-color: #fff;
+  font-weight: bold;
+
+  :global {
+    .van-button {
+      font-weight: bold;
+      font-size: 15px;
+    }
+  }
 }

+ 2 - 5
src/teacher/share-page/share-vip/index.tsx

@@ -122,10 +122,7 @@ export default defineComponent({
           <img src={equityBg} class={styles.iconEquity} />
         </div>
         <ColSticky position="bottom">
-          <div
-            class={['btnGroup']}
-            style={{ paddingTop: '12px', position: 'relative' }}
-          >
+          <div class={['btnGroup', styles.btnGroup]}>
             {this.discount === 1 && (
               <div class={styles.tagDiscount}>专属优惠</div>
             )}
@@ -136,7 +133,7 @@ export default defineComponent({
               onClick={this.onShare}
               color="linear-gradient(220deg, #DFA164 0%, #FAC87E 100%)"
             >
-              下载小酷Ai开始练习吧!
+              下载酷乐秀开始练习吧!
             </Button>
           </div>
         </ColSticky>

+ 1 - 1
src/views/member-center/components/member-interes/index.tsx

@@ -45,7 +45,7 @@ export default defineComponent({
         ) : (
           <div class={[styles.intro, styles.introVip]}>
             <img src={vip1} />
-            <img src={vip2} />
+            <img src={vip2} style={{ 'margin-top': '-1px' }} />
             <img src={vip3} />
           </div>
         )}

+ 31 - 21
src/views/member-center/index.module.less

@@ -86,7 +86,7 @@
 
   .userInfo {
     padding-top: 5px;
-    max-width: 175px;
+    max-width: 185px;
 
     .userName {
       display: flex;
@@ -116,6 +116,7 @@
 
       span {
         color: #FF623A;
+        font-weight: bold;
       }
     }
   }
@@ -306,7 +307,7 @@
 }
 
 .memberDiscount {
-  margin: 10px 16px 0;
+  margin: 10px 16px 6px;
   position: relative;
   background: url('./new-images/icon-discount-bg.png') no-repeat center;
   background-size: contain;
@@ -327,14 +328,18 @@
   }
 
   .discountName {
-    padding-left: 30px;
-    max-width: 200px;
+    margin-left: 30px;
+    width: 200px;
     white-space: nowrap;
     overflow: hidden;
     text-overflow: ellipsis;
+    background: transparent;
+    font-size: 14px;
+    color: #6B4429;
 
     span {
       color: #FF491A;
+      font-weight: 500;
     }
   }
 
@@ -348,7 +353,7 @@
 }
 
 .btnGroup {
-  padding: 18px 15px;
+  padding: 18px 15px 23px;
   background-color: #fff;
 
   .btn {
@@ -375,7 +380,7 @@
 }
 
 .system-list {
-  width: 100%;
+  width: calc(100% - 16px);
   overflow-x: auto;
   overflow-y: hidden;
   display: flex;
@@ -383,6 +388,8 @@
   user-select: none;
   box-sizing: content-box;
   padding-top: 15px;
+  padding-bottom: 2px;
+  padding-right: 16px;
 
 
   &.list_two {
@@ -405,6 +412,7 @@
   flex: 1 0 auto;
   width: 96px;
   min-height: 120px;
+  // overflow: hidden;
   box-sizing: border-box;
   background: #ffffff;
   border-radius: 8px;
@@ -415,26 +423,26 @@
     margin-left: 16px;
   }
 
-  &:last-child {
-    margin-right: 16px;
-  }
+  // &:last-child {
+  //   margin-right: 16px;
+  // }
 
 
   .discountTag {
+    position: absolute;
+    top: 13px;
+    left: 0;
+    right: 0;
+    width: 100%;
     // background: linear-gradient(180deg, #FF491A 0%, #FF9F7E 100%);
     // color: #FFFFFF;
     display: inline-block;
     width: 48px;
     height: 16px;
-    background: url('./new-images/icon_discount-round.png') no-repeat center;
+    background: url('./new-images/icon_discount-text.png') no-repeat center;
     background-size: contain;
-    margin-top: 1px;
-  }
-
-  &.discountItem {
-    .price {
-      padding: 0;
-    }
+    // margin-top: 1px;
+    margin: 0 auto;
   }
 
   .iconPermanent {
@@ -449,11 +457,12 @@
   }
 
   .s_title {
+    position: relative;
     font-weight: 500;
     font-size: 13px;
     color: #333333;
     line-height: 18px;
-    padding-top: 25px;
+    padding-top: 30px;
     width: 100%;
     text-align: center;
   }
@@ -463,7 +472,7 @@
     font-weight: bold;
     font-size: 28px;
     color: #333333;
-    padding: 6px 0 9px;
+    padding: 3px 0 6px;
 
     span {
       font-size: 16px;
@@ -483,7 +492,7 @@
 
   .extraTip {
     position: absolute;
-    bottom: 0;
+    bottom: -1px;
     left: 0;
     right: 0;
     width: 100%;
@@ -498,7 +507,8 @@
     white-space: nowrap;
     overflow: hidden;
     padding: 2px 8px 1px;
-    border-radius: 0px 0px 7px 7px;
+    border-radius: 0 0 8px 8px;
+    // border-radius: 0 0 8px 8px;
   }
 
   &.active {

+ 29 - 27
src/views/member-center/index.tsx

@@ -1,7 +1,7 @@
 import { computed, defineComponent, onMounted, reactive, shallowRef } from 'vue'
 import styles from './index.module.less'
 import ColHeader from '@/components/col-header'
-import { Button, Image, Popup, Toast } from 'vant'
+import { Button, Image, NoticeBar, Popup, Toast } from 'vant'
 import { state as baseState, setLogin } from '@/state'
 import iconShare from './new-images/icon-share.png'
 import { useEventListener } from '@vant/use'
@@ -19,6 +19,7 @@ import deepClone from '@/helpers/deep-clone'
 import { memberSimpleType, memberType } from '@/constant'
 import dayjs from 'dayjs'
 import { orderStatus } from '../order-detail/orderStatus'
+import TheNoticeBar from '@/components/the-noticeBar'
 
 export default defineComponent({
   name: 'member-center',
@@ -160,7 +161,7 @@ export default defineComponent({
         window.scrollY ||
         window.pageYOffset ||
         document.documentElement.scrollTop
-      state.titleOpacity = height > 100 ? 1 : height / 100
+      state.titleOpacity = height > 80 ? 1 : 0
     })
 
     /** 切换购买类型 */
@@ -221,8 +222,11 @@ export default defineComponent({
             userInfo.value.userVip.svipEndDate || new Date()
           ).toDate()
         } else if (userInfo.value.userVip.vipType === 'VIP') {
+          // 购买Vip时,先有vip的有效时间,如果没有则取SVIP有效时间,都没有默认当前
           startTime = dayjs(
-            userInfo.value.userVip.vipEndDate || new Date()
+            userInfo.value.userVip.vipEndDate ||
+              userInfo.value.userVip.svipEndDate ||
+              new Date()
           ).toDate()
         } else if (userInfo.value.userVip.vipType === 'PERMANENT_SVIP') {
           Toast('您已是永久SVIP会员')
@@ -366,6 +370,7 @@ export default defineComponent({
           color={`rgba(0,0,0, ${state.titleOpacity})`}
           backIconColor="black"
           hideHeader={false}
+          border={false}
           v-slots={{
             right: () => (
               <Image
@@ -400,7 +405,7 @@ export default defineComponent({
             </div>
             <div class={styles.userInfo}>
               <div class={styles.userName}>
-                <span class={styles.name}>{userInfo.value.username}</span>
+                <span class={styles.name}>{userInfo.value.username}测试</span>
                 {userInfo.value.phone && (
                   <span class={styles.phone}>({userInfo.value.phone})</span>
                 )}
@@ -523,11 +528,11 @@ export default defineComponent({
                       src={state.discountTeacher.avatar || iconTeacher}
                       class={styles.discountAvatar}
                     />
-
-                    <span class={styles.discountName}>
-                      {state.discountTeacher.username}老师的
+                    <TheNoticeBar class={styles.discountName} isAnimation>
+                      {state.discountTeacher.username}
+                      老师的
                       <span>专属优惠~</span>
-                    </span>
+                    </TheNoticeBar>
 
                     <Image src={iconGift} class={styles.discountGift} />
                   </div>
@@ -553,8 +558,7 @@ export default defineComponent({
                               styles['system-item'],
                               member.id === state.selectMember.id
                                 ? styles.active
-                                : '',
-                              member.discount === 1 ? styles.discountItem : ''
+                                : ''
                             ]}
                             onClick={() => {
                               state.selectMember = member
@@ -565,10 +569,13 @@ export default defineComponent({
                               <span class={[styles.iconPermanent]}></span>
                             )}
 
-                            <p class={styles.s_title}>{member.title}</p>
-                            {member.discount === 1 && (
-                              <span class={styles.discountTag}></span>
-                            )}
+                            <p class={styles.s_title}>
+                              {member.title}
+                              {/* 优惠标识 */}
+                              {member.discount === 1 && (
+                                <span class={styles.discountTag}></span>
+                              )}
+                            </p>
                             <p class={styles.price}>
                               <span>¥</span>
                               {moneyFormat(calcSalePrice(member), '0,0[.]00')}
@@ -615,19 +622,14 @@ export default defineComponent({
                                 </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
+                                class={[
+                                  styles.oneMaxNum,
+                                  styles.oneMaxNumPrice
+                                ]}
+                              >
+                                ¥{member.originalPrice}
+                              </div>
                             </div>
                             <span
                               class={[

BIN
src/views/member-center/new-images/btn-svip-review.png


BIN
src/views/member-center/new-images/btn-svip.png


BIN
src/views/member-center/new-images/btn-vip-review.png


BIN
src/views/member-center/new-images/btn-vip.png


BIN
src/views/member-center/new-images/icon-discount-bg.png


BIN
src/views/member-center/new-images/icon-equity-bg.png


BIN
src/views/member-center/new-images/icon-permanent.png


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


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

@@ -4,6 +4,7 @@ import { state } from '@/state'
 import request from '@/helpers/request'
 import { browser } from '@/helpers/utils'
 import { Dialog } from 'vant'
+import dayjs from 'dayjs'
 type orderType =
   | 'VIDEO'
   | 'LIVE'
@@ -71,6 +72,29 @@ export const resestState = () => {
   Object.assign(orderStatus, original())
 }
 
+
+// 购买会员多少数量满足条件
+export const memberNeedNumber = (item: any) => {
+  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
+}
+
+
 export const orderInfos = () => {
   // 商品列表
   const orderList = orderStatus.orderObject.orderList || []
@@ -114,7 +138,8 @@ export const orderInfos = () => {
       params.bizContent = item.id
     } else if (item.orderType === "SVIP") {
       params.bizContent = item.id
-      params.vipEndDays = item.vipEndDays
+      const needNumber = memberNeedNumber(item)
+      params.vipEndDays = item.num > needNumber ? item.vipEndDays : null
       params.goodsNum = item.num
     } else if (item.orderType === 'MUSIC') {
       params.bizContent = {
@@ -160,7 +185,9 @@ export const orderTenantInfos = () => {
       params.bizId = item.id
     } else if (item.orderType === "SVIP") {
       params.bizContent = item.id
-      params.vipEndDays = item.vipEndDays
+      // params.vipEndDays = item.vipEndDays
+      const needNumber = memberNeedNumber(item)
+      params.vipEndDays = item.num > needNumber ? item.vipEndDays : null
       params.goodsNum = item.num
     } else if (item.orderType === 'MUSIC') {
       params.bizContent = {