Browse Source

Merge branch 'feature-tianyong' into klx-dev

TIANYONG 4 months ago
parent
commit
7ea84a029e

+ 1 - 0
src/page-instrument/App.tsx

@@ -44,6 +44,7 @@ export default defineComponent({
         }
         const student = res?.data || {};
         setUserInfo(student);
+        state.vipType = res.data.vipType || ''
         storeData.platformType = student.clientType === "STUDENT" ? "STUDENT" : "";
       } catch (error) {
         storeData.status = "error";

+ 1 - 0
src/page-instrument/component/the-music-list/list.tsx

@@ -41,6 +41,7 @@ export default defineComponent({
       recentFlag: props.recentFlag ? true : null, // 最近练习
       favoriteFlag: props.favoriteFlag ? true : null, // 我的收藏
       excludeMusicId: props.recentFlag || props.favoriteFlag ? null : state.examSongId,
+      albumId: query.albumId ? query.albumId : null,
     });
     const data = reactive({
       isFocus: false,

+ 27 - 1
src/page-instrument/component/vip/permission.tsx

@@ -1,9 +1,13 @@
 import { Popup } from 'vant'
-import { defineComponent, reactive } from 'vue'
+import { defineComponent, reactive, watch } from 'vue'
 import styles from './index.module.less'
 import Member from './member'
 import Demand from './demand'
 import MemberAndDemand from './memberAndDemand'
+import { usePageVisibility } from "@vant/use"
+import { studentQueryUserInfo } from "/src/page-instrument/api";
+import { getMusicSheetDetail } from "/src/utils/baseApi"
+import state from "/src/state";
 
 export type IActive = 'member' | 'demand' | 'memberAndDemand'
 export const permissionPopup = reactive({
@@ -13,6 +17,28 @@ export const permissionPopup = reactive({
   musicPrice: 0
 })
 
+const pageVisibility = usePageVisibility()
+watch(pageVisibility, async (value) => {
+  if (value === "visible") {
+    const res = await studentQueryUserInfo()
+    state.vipType = res.data.vipType
+    if (permissionPopup.active === 'member' && (state.vipType === 'VIP' || state.vipType.includes('SVIP'))) {
+      permissionPopup.show = false
+    }
+    if (permissionPopup.show && (permissionPopup.active === 'demand' || permissionPopup.active === 'memberAndDemand')) {
+      getMusicSheetDetail(state.examSongId).then(res => {
+        if (res?.code === 200) {
+          state.musicBuyInfo.buyed = res?.data?.buyed
+           // 已经购买
+           if(state.musicBuyInfo.buyed){
+            permissionPopup.show = false
+           }
+        }
+     })
+    }
+  }
+})
+
 export default defineComponent({
   name: 'ColexiuPermission',
   setup(props, { expose }) {

+ 11 - 27
src/page-instrument/custom-plugins/guide-driver/index.tsx

@@ -1154,7 +1154,7 @@ export const EvaluatingReportDriver = defineComponent({
       options.config.stagePadding = 0;
       try {
         const rect = options.state.activeElement?.getBoundingClientRect();
-        popover.wrapper.style.marginLeft = -(rect?.width || 0) / 2 + 16 + "px";
+        popover.wrapper.style.marginLeft = ((rect?.width || 0) / 2) * position - 4 + "px";
       } catch {}
     };
 
@@ -1169,17 +1169,12 @@ export const EvaluatingReportDriver = defineComponent({
               title: "",
               description: "",
               popoverClass: "popoverClass popoverClassReport2",
-              align: "end",
+              align: "start",
               side: "bottom",
               nextBtnText: "下一步 (1/2)",
               showButtons: ["next"],
               onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
-                options.config.stageRadius = 12;
-                options.config.stagePadding = 0;
-                try {
-                  const rect = options.state.activeElement?.getBoundingClientRect();
-                  popover.wrapper.style.marginLeft = ((rect?.width || 0) / 2) * -1 + 16 + "px";
-                } catch {}
+                driverInitialPosition(popover, options);
               },
             },
           },
@@ -1199,7 +1194,7 @@ export const EvaluatingReportDriver = defineComponent({
                 options.config.stagePadding = 5;
                 try {
                   const rect = options.state.activeElement?.getBoundingClientRect();
-                  popover.wrapper.style.marginLeft = ((rect?.width || 0) / 2) * -1 + 16 + "px";
+                  popover.wrapper.style.marginLeft = ((rect?.width || 0) / 2) * -1 + 4 + "px";
                 } catch {}
               },
               onPrevClick: () => {
@@ -1228,7 +1223,7 @@ export const EvaluatingReportDriver = defineComponent({
                 options.config.stagePadding = 0;
                 try {
                   const rect = options.state.activeElement?.getBoundingClientRect();
-                  popover.wrapper.style.marginLeft = ((rect?.width || 0) / 2) * -1 + 16 + "px";
+                  popover.wrapper.style.marginLeft = ((rect?.width || 0) / 2) * -1 + 4 + "px";
                 } catch {}
               },
               onPrevClick: () => {
@@ -1250,7 +1245,7 @@ export const EvaluatingReportDriver = defineComponent({
             title: "",
             description: "",
             popoverClass: "popoverClass popoverClassReport1",
-            align: "end",
+            align: "start",
             side: "bottom",
             nextBtnText: `下一步 (1/${count})`,
             showButtons: ["next"],
@@ -1265,17 +1260,12 @@ export const EvaluatingReportDriver = defineComponent({
             title: "",
             description: "",
             popoverClass: "popoverClass popoverClassReport2",
-            align: "end",
+            align: "start",
             side: "bottom",
             nextBtnText: `下一步 (2/${count})`,
             showButtons: ["next"],
             onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
-              options.config.stageRadius = 12;
-              options.config.stagePadding = 0;
-              try {
-                const rect = options.state.activeElement?.getBoundingClientRect();
-                popover.wrapper.style.marginLeft = ((rect?.width || 0) / 2) * -1 + 16 + "px";
-              } catch {}
+              driverInitialPosition(popover, options);
             },
           },
         },
@@ -1288,18 +1278,12 @@ export const EvaluatingReportDriver = defineComponent({
               title: "",
               description: "",
               popoverClass: "popoverClass popoverClassReport3",
-              align: "end",
+              align: "start",
               side: "bottom",
               nextBtnText: "下一步 (3/4)",
               showButtons: ["next"],
               onPopoverRender: (popover: PopoverDOM, options: { config: Config; state: State }) => {
-                // driverInitialPosition(popover, options, -1);
-                options.config.stageRadius = 12;
-                options.config.stagePadding = 0;
-                try {
-                  const rect = options.state.activeElement?.getBoundingClientRect();
-                  popover.wrapper.style.marginLeft = ((rect?.width || 0) / 2) * -1 + 16 + "px";
-                } catch {}
+                driverInitialPosition(popover, options);
               },
             },
           },
@@ -1319,7 +1303,7 @@ export const EvaluatingReportDriver = defineComponent({
                 options.config.stagePadding = 5;
                 try {
                   const rect = options.state.activeElement?.getBoundingClientRect();
-                  popover.wrapper.style.marginLeft = ((rect?.width || 0) / 2) * -1 + 16 + "px";
+                  popover.wrapper.style.marginLeft = ((rect?.width || 0) / 2) * -1 + 4 + "px";
                 } catch {}
               },
               onPrevClick: () => {

+ 2 - 18
src/page-instrument/custom-plugins/helper-model/screen-model/index.tsx

@@ -7,32 +7,16 @@ export default defineComponent({
 	name: "screenModel",
 	emits: ["close"],
 	setup(props, { emit }) {
-		const getTvIconUrl = () => {
-			if (location.origin.indexOf("test") > -1) {
-				return "https://test.gym.lexiaoya.cn/mteacher/#/guide";
-			} else if (location.origin.indexOf("dev") > -1) {
-				return "http://test.gym.lexiaoya.cn/mteacher/#/guide";
-			}
-			return "https://gym.lexiaoya.cn/mteacher/#/guide";
-		};
-		const getEnvHostname = () => {
-			if (location.origin.indexOf("test") > -1) {
-				return "https://test.gym.lexiaoya.cn/mdaya";
-			} else if (location.origin.indexOf("dev") > -1) {
-				return "http://test.gym.lexiaoya.cn/mdaya";
-			}
-			return "https://gym.lexiaoya.cn/mdaya";
-		};
 		return () => (
 			<>
 				<img class={styles.closeBtn} src={iconBack} onClick={() => emit("close")} />
 				<div class={styles.container}>
 					<Tabs swipeable animated>
 						<Tab name="投屏" title="投屏">
-							<iframe src={getTvIconUrl()} />
+							<iframe src={location.origin + '/student/#/guide'} />
 						</Tab>
 						<Tab name="帮助" title="帮助">
-							<iframe src={getEnvHostname() + '/#/KeepRepaire?mode=accompany'}/>
+							<iframe src={location.origin + '/student/#/helpCenter?platformType=ANALYSIS'} />
 						</Tab>
 					</Tabs>
 				</div>

File diff suppressed because it is too large
+ 0 - 0
src/page-instrument/header-top/image/modeTitle.json


BIN
src/page-instrument/header-top/image/mode_svip.png


BIN
src/page-instrument/header-top/image/mode_vip.png


+ 22 - 6
src/page-instrument/header-top/index.module.less

@@ -358,7 +358,7 @@
 
 .disabled {
     pointer-events: none;
-    opacity: .4;
+    opacity: 0;
 }
 
 .playBtn {
@@ -477,7 +477,7 @@
 
     .modeTitle {
         width: auto;
-        height: 28px;
+        height: 50px;
         position: absolute;
         left: 50%;
         top: 22px;
@@ -492,20 +492,36 @@
         position: relative;
         top: 50%;
         transform: translateY(-42%);
-
         &.twoModeBox {
             justify-content: center;
-
-            >.modeImg+.modeImg {
+            >.modeItem+.modeItem {
                 margin-left: 150px;
             }
         }
-        > .modeImg {
+        .modeItem {
+            position: relative;
             cursor: pointer;
             width: calc((100% - 2*32px)/3);
             max-width: 220px;
             height: intrinsic;
         }
+        .modeImg {
+            width: 100%;
+        }
+        .vipIcon {
+            position: absolute;
+            left: 30px;
+            top: 12px;
+            width: 55px;
+            height: 21px;
+        }
+        .svipIcon {
+            position: absolute;
+            left: 30px;
+            top: 12px;
+            width: 63px;
+            height: 21px;
+        }
     }
 }
 

+ 26 - 0
src/page-instrument/header-top/index.tsx

@@ -32,11 +32,37 @@ import { smoothAnimationState } from "../view-detail/smoothAnimation";
 import { isMusicList, musicListShow } from "../component/the-music-list";
 import { EvaluatingDriver, FollowDriver, PractiseDriver } from "../custom-plugins/guide-driver";
 import { fingerRef } from "/src/page-instrument/view-detail/index"
+import { permissionPopup } from "/src/page-instrument/component/vip/permission";
 
 const ModeView = defineAsyncComponent(() =>
   import('./modeView')
 )
 
+/** 校验是否能切换模式,会员的曲子,酷乐秀跟练模式、评测模式需要svip */
+export const checkMusicBuy = (item: any) => {
+  let checked = true;
+  // 学生端或者老师端需要校验点播和会员曲目
+  if (state.systemType === "student" || state.systemType === "teacher") {
+    // 如果是点播的曲子,并且还没有购买,需要弹窗提醒
+    if (item.paymentType === "CHARGE" && !item.buyed) {
+      permissionPopup.active = "demand"
+      permissionPopup.musicId = item.id
+      permissionPopup.musicPrice = item.musicPrice
+      permissionPopup.show = true
+      checked = false
+    }
+    // 如果是vip的曲子,当前用户不是会员时,需要弹窗提醒
+    if (storeData.user.vipType === "NOT_VIP" && item.paymentType !== "FREE") {
+      permissionPopup.active = item.paymentType.includes('CHARGE') ? "memberAndDemand" : "member"
+      permissionPopup.musicId = item.id
+      permissionPopup.musicPrice = item.musicPrice
+      permissionPopup.show = true
+      checked = false
+    }
+  }
+  return checked;
+};
+
 /** 头部数据和方法 */
 export const headTopData = reactive({
   /** 模式 */

+ 55 - 19
src/page-instrument/header-top/modeView.tsx

@@ -1,4 +1,4 @@
-import { defineComponent, onMounted, watch, reactive, ref, nextTick } from "vue";
+import { defineComponent, onMounted, watch, reactive, ref, nextTick, computed } from "vue";
 import styles from "./index.module.less";
 import backImg from "./image/icon-back.png";
 import titmeImg from "./image/modeTitle.png";
@@ -6,12 +6,11 @@ import nameImg from "./image/zt.png";
 import lxMode from "./image/lxMode.json";
 import glMode from "./image/glMode.json";
 import pcMode from "./image/pcMode.json";
-// import lxImg from "./image/lxImg.png";
-// import glImg from "./image/glImg.png";
-// import pcImg from "./image/pcImg.png";
-import { headTopData } from "./index";
+import modeTitle from "./image/modeTitle.json";
+import modeVip from "./image/mode_vip.png";
+import modesVip from "./image/mode_svip.png";
+import { headTopData, checkMusicBuy } from "./index";
 import { getQuery } from "/src/utils/queryString";
-import { storeData } from "/src/store";
 import state from "/src/state";
 import { studentQueryUserInfo } from "../api";
 import { usePageVisibility } from "@vant/use";
@@ -21,6 +20,7 @@ import { Popup } from "vant";
 import AbnormalPop from "/src/view/abnormal-pop";
 import { smoothAnimationState } from "../view-detail/smoothAnimation";
 import { browser } from "/src/utils";
+import { stat } from "fs";
 
 export default defineComponent({
   name: "modeView",
@@ -28,6 +28,7 @@ export default defineComponent({
     const modeImgDom1 = ref();
     const modeImgDom2 = ref();
     const modeImgDom3 = ref();
+    const modeImgDom4 = ref();
     watch(
       () => headTopData.modeType,
       (value, oldValue) => {
@@ -38,10 +39,12 @@ export default defineComponent({
             modeImgDom1.value?.pause();
             modeImgDom2.value?.pause();
             modeImgDom3.value?.pause();
+            modeImgDom4.value?.stop();
           } else if (value === "init") {
             modeImgDom1.value?.play();
             modeImgDom2.value?.play();
             modeImgDom3.value?.play();
+            modeImgDom4.value?.goToAndPlay(0);
           }
         });
       }
@@ -58,6 +61,16 @@ export default defineComponent({
     );      
     const browserInfo = browser();
     const isPad =  navigator?.userAgent?.includes("UAWEIVRD-W09") || browserInfo?.iPad || browserInfo.isTablet;
+
+    // vip图标
+    const showVip = computed(() => {
+			return state.vipType === "NOT_VIP" && state.paymentType !== "FREE" && !state.musicBuyInfo.buyed
+		});
+    // svip图标
+    const showsVip = computed(() => {
+			return !state.vipType?.includes("SVIP") && state.paymentType !== "FREE" && !state.musicBuyInfo.buyed
+		});
+
     return () => (
       <div class={[styles.modeView, isPad && styles.isiPad, headTopData.modeType !== "init" && styles.hidden]}>
         <img
@@ -76,14 +89,25 @@ export default defineComponent({
             headTopData.modeType = "show";
           }}
         />
-        <img src={titmeImg} class={styles.modeTitle} />
+        {/* <img src={titmeImg} class={styles.modeTitle} /> */}
+        {
+          <Vue3Lottie ref={modeImgDom4} class={styles.modeTitle} animationData={modeTitle} autoPlay={false} loop={false} delay={2000}></Vue3Lottie>
+        }
         <div class={[styles.modeBox, ((!state.isPercussion && !state.enableEvaluation) || (state.isPercussion && state.enableEvaluation) || (state.isPercussion && !state.enableEvaluation)) && styles.twoModeBox]}>
-          <Vue3Lottie ref={modeImgDom1} class={styles.modeImg} animationData={lxMode} autoPlay={false} loop={true} onClick={() => {
-            if(state.isSingleLine){
-              smoothAnimationState.isShow.value = state.melodyLine;
-            }
-            headTopData.handleChangeModeType("practise")
-            }}></Vue3Lottie>
+          <div class={styles.modeItem}>
+            <Vue3Lottie ref={modeImgDom1} class={styles.modeImg} animationData={lxMode} autoPlay={false} loop={true} onClick={() => {
+              if (checkMusicBuy(state.musicBuyInfo)) {
+                if(state.isSingleLine){
+                  smoothAnimationState.isShow.value = state.melodyLine;
+                }
+                headTopData.handleChangeModeType("practise")
+              }
+              }}></Vue3Lottie>
+              {
+                showVip.value && <img class={styles.vipIcon} src={modeVip} />
+              }
+          </div>
+
           {/* <img class={styles.modeImg} src={lxImg} 
             onClick={() => {
               if(state.isSingleLine){
@@ -92,16 +116,28 @@ export default defineComponent({
               headTopData.handleChangeModeType("practise")
             }} /> */}
           {!state.isPercussion && 
-              <Vue3Lottie ref={modeImgDom2} class={styles.modeImg} animationData={glMode} autoPlay={false} loop={true} onClick={() => headTopData.handleChangeModeType("follow")}></Vue3Lottie>
+            <div class={styles.modeItem}>
+              <Vue3Lottie ref={modeImgDom2} class={styles.modeImg} animationData={glMode} autoPlay={false} loop={true} onClick={() => { 
+                if (checkMusicBuy(state.musicBuyInfo)) {
+                  headTopData.handleChangeModeType("follow")
+                }
+                }}></Vue3Lottie>
+              { showsVip.value && <img class={styles.svipIcon} src={modesVip} /> }
+            </div>
               // <img class={styles.modeImg} src={glImg} 
               //   onClick={() => headTopData.handleChangeModeType("follow")} />
           }
           {state.enableEvaluation && 
-            <Vue3Lottie ref={modeImgDom3} class={styles.modeImg} animationData={pcMode} autoPlay={false} loop={true} onClick={() => {
-               // 点击评测模式进入评测模块的需要检测耳机状态,通过返回按钮进入评测模块的,不检测耳机状态
-              evaluatingData.needCheckErjiStatus = true;
-              headTopData.handleChangeModeType("evaluating")
-            }}></Vue3Lottie>
+            <div class={styles.modeItem}>
+              <Vue3Lottie ref={modeImgDom3} class={styles.modeImg} animationData={pcMode} autoPlay={false} loop={true} onClick={() => {
+                if (checkMusicBuy(state.musicBuyInfo)) {
+                  // 点击评测模式进入评测模块的需要检测耳机状态,通过返回按钮进入评测模块的,不检测耳机状态
+                  evaluatingData.needCheckErjiStatus = true;
+                  headTopData.handleChangeModeType("evaluating")
+                }
+              }}></Vue3Lottie>
+              { showsVip.value && <img class={styles.svipIcon} src={modesVip} /> }
+            </div>
             // <img class={styles.modeImg} src={pcImg} 
             //   onClick={() => {
             //       // 点击评测模式进入评测模块的需要检测耳机状态,通过返回按钮进入评测模块的,不检测耳机状态

+ 15 - 2
src/state.ts

@@ -553,8 +553,12 @@ const state = reactive({
   guideInfo: null as any,
   noteCoords: [] as any,
   specialPosInit: false,
-  /** 资源类型 */
+  /** 曲子付费类型 */
   paymentType: null,
+  /** 曲子付费信息 */
+  musicBuyInfo: null as any,
+  // vip类型 VIP:会员 SVIP:SVIP,PERMANENT_SVIP:永久SVIP,NOT_VIP:不是vip
+  vipType: '' as any,
   /** 播放模式,默认练习模式 */
   defaultModeType: 1,
   /** 音符最多歌词次数 */
@@ -750,6 +754,7 @@ const handlePlaying = () => {
           return;
         }
         // #8698 bug修复
+        
         if (state.modeType === "practise" && state.sectionStatus) {
           // 练习作业,练习完一次需要增加练习次数
           if (query.workRecord) {
@@ -1729,7 +1734,15 @@ const setState = (data: any, index: number) => {
   state.detailId = data.bizId;
   state.musicPrice = data.musicPrice || 0;
   state.xmlUrl = data.xmlFileUrl;
-  state.paymentType = data.useStatus
+  // 曲子付费类型
+  state.paymentType = data.paymentType;
+  // 是否已经购买
+  state.musicBuyInfo = {
+    id: data.bizId,
+    musicPrice: data.musicPrice,
+    paymentType: data.paymentType,
+    buyed: data.buyed
+  }
   state.partIndex = index >= 0 ? index : 0;
   state.trackId = data.track;
   state.subjectId = data.subjectIds ? data.subjectIds.split(',')?.[0] : 0;

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