Преглед на файлове

feat: 老师端云教练ui布局等修改

TIANYONG преди 1 година
родител
ревизия
0d26116118
променени са 29 файла, в които са добавени 550 реда и са изтрити 89 реда
  1. 5 0
      src/helpers/communication.ts
  2. 6 4
      src/helpers/formateMusic.ts
  3. 1 0
      src/page-instrument/component/mode-type-mode/index.tsx
  4. 17 14
      src/page-instrument/custom-plugins/guide-page/student-bottom.tsx
  5. 18 15
      src/page-instrument/custom-plugins/guide-page/student-top.tsx
  6. 10 6
      src/page-instrument/custom-plugins/guide-page/teacher-bootom.tsx
  7. 18 14
      src/page-instrument/custom-plugins/guide-page/teacher-top.tsx
  8. BIN
      src/page-instrument/header-top/image/pc_icon_pausebtn.png
  9. BIN
      src/page-instrument/header-top/image/pc_icon_playbtn.png
  10. BIN
      src/page-instrument/header-top/image/pc_icon_resetbtn.png
  11. 19 4
      src/page-instrument/header-top/index.module.less
  12. 64 11
      src/page-instrument/header-top/index.tsx
  13. 9 0
      src/page-instrument/header-top/settting/index.module.less
  14. 23 4
      src/page-instrument/header-top/settting/index.tsx
  15. 3 0
      src/page-instrument/header-top/title/index.module.less
  16. 2 1
      src/page-instrument/header-top/title/index.tsx
  17. 38 1
      src/page-instrument/view-detail/index.module.less
  18. 40 10
      src/page-instrument/view-detail/index.tsx
  19. 5 0
      src/state.ts
  20. 3 3
      src/view/music-score/index.tsx
  21. 45 0
      src/view/plugins/useDrag/dragbom.tsx
  22. BIN
      src/view/plugins/useDrag/img/left.png
  23. BIN
      src/view/plugins/useDrag/img/modalDragBg.png
  24. BIN
      src/view/plugins/useDrag/img/modalDragBg2.png
  25. BIN
      src/view/plugins/useDrag/img/modalDragDone.png
  26. BIN
      src/view/plugins/useDrag/img/right.png
  27. 65 0
      src/view/plugins/useDrag/index.module.less
  28. 157 0
      src/view/plugins/useDrag/index.ts
  29. 2 2
      vite.config.ts

+ 5 - 0
src/helpers/communication.ts

@@ -473,4 +473,9 @@ export const api_midiMicDelay = (content: any) => {
 		api: "proxyServiceMessage",
 		api: "proxyServiceMessage",
 		content,
 		content,
 	});
 	});
+};
+
+/** 监听老师端,上课页面,功能按钮方向 */
+export const addImagePos = (callback: any) => {
+	listenerMessage("imagePos", callback);
 };
 };

+ 6 - 4
src/helpers/formateMusic.ts

@@ -622,7 +622,9 @@ export const formatXML = (xml: string, xmlUrl?: string): string => {
 	// 处理重复小节信息
 	// 处理重复小节信息
 	parseXmlToRepeat(repeats)
 	parseXmlToRepeat(repeats)
 	// 解析处理evxml
 	// 解析处理evxml
-	analyzeEvxml(xmlParse, xmlUrl);
+	if (state.isEvxml) {
+		analyzeEvxml(xmlParse, xmlUrl);
+	}
 	// const words: any = xmlParse.getElementsByTagName("words");
 	// const words: any = xmlParse.getElementsByTagName("words");
 	// for (const word of words) {
 	// for (const word of words) {
 	// 	if (word && word.textContent?.trim() === "筒音作5") {
 	// 	if (word && word.textContent?.trim() === "筒音作5") {
@@ -1248,9 +1250,9 @@ const analyzeEvxml = (xmlParse: any, xmlUrl?: string) => {
 	state.evXmlBeginTime = firstNoteBeginTime ? firstNoteBeginTime / 1000 : xmlNum ? 60 / state.originSpeed * xmlNum : 0;
 	state.evXmlBeginTime = firstNoteBeginTime ? firstNoteBeginTime / 1000 : xmlNum ? 60 / state.originSpeed * xmlNum : 0;
 	const hasTimeGap = xmlParse.getElementsByTagName("timegap").length > 0;
 	const hasTimeGap = xmlParse.getElementsByTagName("timegap").length > 0;
 	const hasTimes = xmlParse.getElementsByTagName("times").length > 0;
 	const hasTimes = xmlParse.getElementsByTagName("times").length > 0;
-	if (!hasTimeGap && !hasTimes) {
-		state.noTimes.push(xmlUrl)
-	}
+	// if (!hasTimeGap && !hasTimes) {
+	// 	state.noTimes.push(xmlUrl)
+	// }
 	console.log('🚀 ~ evxml解析','有timegap:',hasTimeGap,'有times:',hasTimes)
 	console.log('🚀 ~ evxml解析','有timegap:',hasTimeGap,'有times:',hasTimes)
 }
 }
 
 

+ 1 - 0
src/page-instrument/component/mode-type-mode/index.tsx

@@ -11,6 +11,7 @@ import { studentQueryUserInfo } from "../../api";
 import { usePageVisibility } from "@vant/use";
 import { usePageVisibility } from "@vant/use";
 import GuideIndex from "../../view-figner/guide/guide-index";
 import GuideIndex from "../../view-figner/guide/guide-index";
 import { getQuery } from "/src/utils/queryString";
 import { getQuery } from "/src/utils/queryString";
+
 export default defineComponent({
 export default defineComponent({
   name: "modelWraper",
   name: "modelWraper",
 
 

+ 17 - 14
src/page-instrument/custom-plugins/guide-page/student-bottom.tsx

@@ -5,6 +5,7 @@ import styles from "./index.module.less";
 import { getImage } from "./images";
 import { getImage } from "./images";
 import { getQuery } from "/src/utils/queryString";
 import { getQuery } from "/src/utils/queryString";
 import { getGuidance, setGuidance } from "./api";
 import { getGuidance, setGuidance } from "./api";
+import state from "/src/state";
 
 
 export default defineComponent({
 export default defineComponent({
 	name: "studentB-guide",
 	name: "studentB-guide",
@@ -85,21 +86,23 @@ export default defineComponent({
 		const guideInfo = ref({} as any)
 		const guideInfo = ref({} as any)
 		const getAllGuidance = async()=>{
 		const getAllGuidance = async()=>{
 		  try{
 		  try{
-		  const res = await getGuidance({guideTag:'guideInfo'})
-		  if(res.data){
-			guideInfo.value = JSON.parse(res.data?.guideValue) || null
-		  }else{
-			guideInfo.value = {}
-		  }
-	  
-	  
-		  if (guideInfo.value && guideInfo.value.studentB) {
-			tipShow.value = false;
-		  } else {
-			tipShow.value = true;
-		  }
+			if (state.guideInfo) {
+				guideInfo.value = state.guideInfo
+			} else {
+				const res = await getGuidance({guideTag:'guideInfo'})
+				if(res.data){
+				  guideInfo.value = JSON.parse(res.data?.guideValue) || null
+				}else{
+				  guideInfo.value = {}
+				}
+			}
+			if (guideInfo.value && guideInfo.value.studentB) {
+				tipShow.value = false;
+			} else {
+				tipShow.value = true;
+			}
 		  }catch(e){
 		  }catch(e){
-		  console.log(e)
+		  	console.log(e)
 		  }
 		  }
 		  // const guideInfo = localStorage.getItem('teacher-guideInfo');
 		  // const guideInfo = localStorage.getItem('teacher-guideInfo');
 	  
 	  

+ 18 - 15
src/page-instrument/custom-plugins/guide-page/student-top.tsx

@@ -7,6 +7,7 @@ import { useRoute } from "vue-router";
 import { getQuery } from "/src/utils/queryString";
 import { getQuery } from "/src/utils/queryString";
 import { getGuidance, setGuidance } from "./api";
 import { getGuidance, setGuidance } from "./api";
 import { headTopData } from "/src/page-instrument/header-top/index";
 import { headTopData } from "/src/page-instrument/header-top/index";
+import state from "/src/state";
 
 
 export default defineComponent({
 export default defineComponent({
   name: "studnetT-guide",
   name: "studnetT-guide",
@@ -181,21 +182,23 @@ export default defineComponent({
   const guideInfo = ref({} as any)
   const guideInfo = ref({} as any)
   const getAllGuidance = async()=>{
   const getAllGuidance = async()=>{
     try{
     try{
-    const res = await getGuidance({guideTag:'guideInfo'})
-    if(res.data){
-      guideInfo.value = JSON.parse(res.data?.guideValue) || null
-    }else{
-      guideInfo.value = {}
-    }
-
-
-    if (guideInfo.value && guideInfo.value.studnetT) {
-      tipShow.value = false;
-    } else {
-      tipShow.value = headTopData.modeType !== "init" ? true : false;
-    }
-    }catch(e){
-    console.log(e)
+			if (state.guideInfo) {
+				guideInfo.value = state.guideInfo
+			} else {
+				const res = await getGuidance({guideTag:'guideInfo'})
+				if(res.data){
+				  guideInfo.value = JSON.parse(res.data?.guideValue) || null
+				}else{
+				  guideInfo.value = {}
+				}
+			}
+      if (guideInfo.value && guideInfo.value.studnetT) {
+        tipShow.value = false;
+      } else {
+        tipShow.value = headTopData.modeType !== "init" ? true : false;
+      }
+    } catch(e) {
+      console.log(e)
     }
     }
     // const guideInfo = localStorage.getItem('teacher-guideInfo');
     // const guideInfo = localStorage.getItem('teacher-guideInfo');
 
 

+ 10 - 6
src/page-instrument/custom-plugins/guide-page/teacher-bootom.tsx

@@ -6,6 +6,7 @@ import { getImage } from "./images";
 import { getQuery } from "/src/utils/queryString";
 import { getQuery } from "/src/utils/queryString";
 import {getGuidance,setGuidance} from './api'
 import {getGuidance,setGuidance} from './api'
 import { headTopData } from "/src/page-instrument/header-top/index";
 import { headTopData } from "/src/page-instrument/header-top/index";
+import state from "/src/state";
 
 
 export default defineComponent({
 export default defineComponent({
 	name: "aiTeacher-guide",
 	name: "aiTeacher-guide",
@@ -110,14 +111,17 @@ export default defineComponent({
 		const guideInfo = ref({} as any)
 		const guideInfo = ref({} as any)
 		const getAllGuidance = async()=>{
 		const getAllGuidance = async()=>{
 		  try{
 		  try{
-			const res = await getGuidance({guideTag:'guideInfo'})
-			if(res.data){
-			  guideInfo.value = JSON.parse(res.data?.guideValue) || null
-			}else{
-			  guideInfo.value = {}
+			if (state.guideInfo) {
+				guideInfo.value = state.guideInfo
+			} else {
+				const res = await getGuidance({guideTag:'guideInfo'})
+				if(res.data){
+				  guideInfo.value = JSON.parse(res.data?.guideValue) || null
+				}else{
+				  guideInfo.value = {}
+				}
 			}
 			}
 	
 	
-	
 			if (guideInfo.value && guideInfo.value.teacherBottom) {
 			if (guideInfo.value && guideInfo.value.teacherBottom) {
 			  tipShow.value = false;
 			  tipShow.value = false;
 			} else {
 			} else {

+ 18 - 14
src/page-instrument/custom-plugins/guide-page/teacher-top.tsx

@@ -5,6 +5,8 @@ import styles from "./index.module.less";
 import { getImage } from "./images";
 import { getImage } from "./images";
 import { getQuery } from "/src/utils/queryString";
 import { getQuery } from "/src/utils/queryString";
 import {getGuidance,setGuidance} from './api'
 import {getGuidance,setGuidance} from './api'
+import state from "/src/state";
+
 export default defineComponent({
 export default defineComponent({
   name: "teacherTop-guide",
   name: "teacherTop-guide",
   emits: ["close"],
   emits: ["close"],
@@ -147,21 +149,23 @@ export default defineComponent({
   const guideInfo = ref({} as any)
   const guideInfo = ref({} as any)
   const getAllGuidance = async()=>{
   const getAllGuidance = async()=>{
     try{
     try{
-    const res = await getGuidance({guideTag:'guideInfo'})
-    if(res.data){
-      guideInfo.value = JSON.parse(res.data?.guideValue) || null
-    }else{
-      guideInfo.value = {}
-    }
-
-
-    if (guideInfo.value && guideInfo.value.teacherTop) {
-      tipShow.value = false;
-    } else {
-      tipShow.value = true;
-    }
+			if (state.guideInfo) {
+				guideInfo.value = state.guideInfo
+			} else {
+				const res = await getGuidance({guideTag:'guideInfo'})
+				if(res.data){
+				  guideInfo.value = JSON.parse(res.data?.guideValue) || null
+				}else{
+				  guideInfo.value = {}
+				}
+			}
+      if (guideInfo.value && guideInfo.value.teacherTop) {
+        tipShow.value = false;
+      } else {
+        tipShow.value = true;
+      }
     }catch(e){
     }catch(e){
-    console.log(e)
+      console.log(e)
     }
     }
     // const guideInfo = localStorage.getItem('teacher-guideInfo');
     // const guideInfo = localStorage.getItem('teacher-guideInfo');
 
 

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


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


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


+ 19 - 4
src/page-instrument/header-top/index.module.less

@@ -11,6 +11,7 @@
     animation: headerDown .3s .5s ease-in-out forwards;
     animation: headerDown .3s .5s ease-in-out forwards;
 
 
     &.headRightTop {
     &.headRightTop {
+        transform: translateY(100%);
         transition: margin-top .2s ease;
         transition: margin-top .2s ease;
         margin-top: 0;
         margin-top: 0;
     }
     }
@@ -60,8 +61,10 @@
     align-items: center;
     align-items: center;
     margin-left: auto;
     margin-left: auto;
     height: 100%;
     height: 100%;
-
-
+}
+.pcHeadRight {
+    width: 100%;
+    justify-content: center;
 }
 }
 
 
 .btn {
 .btn {
@@ -180,12 +183,18 @@
         }
         }
     }
     }
 
 
-    &.playButton {
+    &.playLeftButton {
         left: 32px !important;
         left: 32px !important;
         right: auto !important;
         right: auto !important;
         bottom: 12px !important;
         bottom: 12px !important;
     }
     }
 
 
+    &.playRightButton {
+        right: 32px !important;
+        left: auto !important;
+        bottom: 12px !important;
+    }
+
     &.playButtonHide {
     &.playButtonHide {
         transition: bottom .2s ease;
         transition: bottom .2s ease;
         bottom: 60px !important;
         bottom: 60px !important;
@@ -205,12 +214,18 @@
         height: 36px;
         height: 36px;
     }
     }
 
 
-    &.pauseButton {
+    &.pauseLeftButton {
         left: 88px !important;
         left: 88px !important;
         right: auto !important;
         right: auto !important;
         bottom: 12px !important;
         bottom: 12px !important;
     }
     }
 
 
+    &.pauseRightButton {
+        right: 88px !important;
+        left: auto !important;
+        bottom: 12px !important;
+    }
+
     &.playButtonHide {
     &.playButtonHide {
         transition: bottom .2s ease;
         transition: bottom .2s ease;
         bottom: 60px !important;
         bottom: 60px !important;

+ 64 - 11
src/page-instrument/header-top/index.tsx

@@ -1,4 +1,4 @@
-import { Transition, computed, defineComponent, onMounted, onUnmounted, reactive, ref, watch } from "vue";
+import { Transition, computed, defineComponent, onMounted, onUnmounted, reactive, ref, watch, toRef } from "vue";
 import styles from "./index.module.less";
 import styles from "./index.module.less";
 
 
 import iconBack from "./image/icon-back.svg";
 import iconBack from "./image/icon-back.svg";
@@ -24,6 +24,9 @@ import store from "store";
 import "../component/the-modal-tip/index.module.less";
 import "../component/the-modal-tip/index.module.less";
 import { metronomeData } from "../../helpers/metronome";
 import { metronomeData } from "../../helpers/metronome";
 import { toggleMusicSheet } from "/src/view/plugins/toggleMusicSheet"
 import { toggleMusicSheet } from "/src/view/plugins/toggleMusicSheet"
+import useDrag from "/src/view/plugins/useDrag/index";
+import Dragbom from "/src/view/plugins/useDrag/dragbom";
+import { getGuidance, setGuidance } from "../custom-plugins/guide-page/api";
 
 
 /** 头部数据和方法 */
 /** 头部数据和方法 */
 export const headTopData = reactive({
 export const headTopData = reactive({
@@ -81,6 +84,7 @@ export const headTopData = reactive({
 export const headData = reactive({
 export const headData = reactive({
   speedShow: false,
   speedShow: false,
   musicTypeShow: false,
   musicTypeShow: false,
+  showGragGuide: false, // 是否显示可拖动弹窗的引导页(老师端需要显示)
 });
 });
 
 
 export default defineComponent({
 export default defineComponent({
@@ -296,6 +300,20 @@ export default defineComponent({
       }
       }
     };
     };
 
 
+    const parentClassName = "settingBoxClass_drag";
+    const userId = storeData.user?.id ? String(storeData.user?.id) : '';
+    const positionInfo = state.platform !== IPlatform.PC ? {
+      styleDrag: { value: null }
+    } : useDrag(
+      [
+        `${parentClassName} .van-tabs__wrap`,
+        `${parentClassName} .bom_drag`
+      ],
+      parentClassName,
+      toRef(headTopData, 'settingMode'),
+      userId
+    )
+
     onMounted(() => {
     onMounted(() => {
       getQueryModelSetModelType();
       getQueryModelSetModelType();
       window.addEventListener("message", changePlay);
       window.addEventListener("message", changePlay);
@@ -316,6 +334,40 @@ export default defineComponent({
       store.set("musicscoresetting", state.setting);
       store.set("musicscoresetting", state.setting);
     });
     });
 
 
+    // 获取引导页信息
+		const getAllGuidance = async()=>{
+      let guideInfo: any = null;
+		  try{
+        const res = await getGuidance({guideTag:'guideInfo'})
+        if(res.data){
+          guideInfo = JSON.parse(res.data?.guideValue) || null
+        }else{
+          guideInfo = {}
+        }
+        state.guideInfo = guideInfo;
+        if (guideInfo && guideInfo.teacherDrag) {
+          headData.showGragGuide = false;
+        } else {
+          headData.showGragGuide = true;
+        }
+		  }catch(e){
+		    console.log(e)
+		  }
+	  
+		}
+		getAllGuidance()    
+
+    // 完成拖动弹窗引导页
+    const handleGuide = async () => {
+      state.guideInfo.teacherDrag = true;
+			try{
+				const res = await setGuidance({guideTag:'guideInfo',guideValue:JSON.stringify(state.guideInfo)})
+      }catch(e){
+        console.log(e)
+      }   
+      headData.showGragGuide = false;
+    }
+
     return () => (
     return () => (
       <>
       <>
         <div
         <div
@@ -336,10 +388,10 @@ export default defineComponent({
           <div class={[styles.back, "headTopBackBtn", !headTopData.showBack && styles.hidenBack]} onClick={handleBack}>
           <div class={[styles.back, "headTopBackBtn", !headTopData.showBack && styles.hidenBack]} onClick={handleBack}>
             <img src={iconBack} />
             <img src={iconBack} />
           </div>
           </div>
-          {query.iscurseplay === "play" ? null : <Title class="pcTitle" text={state.examSongName} rightView={false} />}
+          {(query.iscurseplay === "play" || state.platform === IPlatform.PC) ? null : <Title class="pcTitle" text={state.examSongName} rightView={false} />}
 
 
           <div
           <div
-            class={[styles.headRight]}
+            class={[styles.headRight, state.platform === IPlatform.PC && styles.pcHeadRight]}
             onClick={(e: Event) => {
             onClick={(e: Event) => {
               e.stopPropagation();
               e.stopPropagation();
             }}
             }}
@@ -436,7 +488,7 @@ export default defineComponent({
               <span>指法</span>
               <span>指法</span>
             </div>
             </div>
 
 
-            <Popover trigger="manual" v-model:show={headData.speedShow} placement="bottom" overlay={false}>
+            <Popover trigger="manual" v-model:show={headData.speedShow} placement={state.platform === IPlatform.PC ? "top" : "bottom"} overlay={false}>
               {{
               {{
                 reference: () => (
                 reference: () => (
                   <div
                   <div
@@ -459,7 +511,7 @@ export default defineComponent({
             </Popover>
             </Popover>
             {
             {
               state.enableNotation ? 
               state.enableNotation ? 
-              <Popover trigger="manual" v-model:show={headData.musicTypeShow} placement="bottom-end" overlay={false}>
+              <Popover trigger="manual" v-model:show={headData.musicTypeShow} placement={state.platform === IPlatform.PC ? "top-end" : "bottom-end"} overlay={false}>
                 {{
                 {{
                   reference: () => (
                   reference: () => (
                     <div
                     <div
@@ -490,12 +542,12 @@ export default defineComponent({
         <div
         <div
           id="studnetT-7"
           id="studnetT-7"
           style={{ display: playBtn.value.display ? "" : "none" }}
           style={{ display: playBtn.value.display ? "" : "none" }}
-          class={[styles.btn, styles.playBtn, playBtn.value.disabled && styles.disabled, state.platform === IPlatform.PC && styles.playButton, state.platform === IPlatform.PC && !state.attendHideMenu && styles.playButtonHide]}
+          class={[styles.btn, styles.playBtn, playBtn.value.disabled && styles.disabled, state.platform === IPlatform.PC && state.playBtnDirection === 'left' ? styles.playLeftButton : state.platform === IPlatform.PC && state.playBtnDirection === 'right' ? styles.playRightButton : '', state.platform === IPlatform.PC && !state.attendHideMenu && styles.playButtonHide]}
           onClick={() => togglePlay()}
           onClick={() => togglePlay()}
         >
         >
           <div class={styles.btnWrap}>
           <div class={styles.btnWrap}>
-            <img style={{ display: state.playState === "play" ? "none" : "" }} class={styles.iconBtn} src={headImg("icon_play.svg")} />
-            <img style={{ display: state.playState === "play" ? "" : "none" }} class={styles.iconBtn} src={headImg("icon_pause.svg")} />
+            <img style={{ display: state.playState === "play" ? "none" : "" }} class={styles.iconBtn} src={headImg(state.platform === IPlatform.PC ? "pc_icon_playbtn.png" : "icon_play.svg")} />
+            <img style={{ display: state.playState === "play" ? "" : "none" }} class={styles.iconBtn} src={headImg(state.platform === IPlatform.PC ? "pc_icon_pausebtn.png" : "icon_pause.svg")} />
             <Circle style={{ opacity: state.playState === "play" ? 1 : 0 }} class={styles.progress} stroke-width={80} currentRate={state.playProgress} rate={100} color="#FFC830" />
             <Circle style={{ opacity: state.playState === "play" ? 1 : 0 }} class={styles.progress} stroke-width={80} currentRate={state.playProgress} rate={100} color="#FFC830" />
           </div>
           </div>
         </div>
         </div>
@@ -504,14 +556,15 @@ export default defineComponent({
         <div
         <div
           id="tips-step-9"
           id="tips-step-9"
           style={{ display: resetBtn.value.display ? "" : "none" }}
           style={{ display: resetBtn.value.display ? "" : "none" }}
-          class={[styles.btn, styles.resetBtn, resetBtn.value.disabled && styles.disabled, state.platform === IPlatform.PC && styles.pauseButton, state.platform === IPlatform.PC && !state.attendHideMenu && styles.playButtonHide]}
+          class={[styles.btn, styles.resetBtn, resetBtn.value.disabled && styles.disabled, state.platform === IPlatform.PC && state.playBtnDirection === 'left' ? styles.pauseLeftButton : state.platform === IPlatform.PC && state.playBtnDirection === 'right' ? styles.pauseRightButton : '', state.platform === IPlatform.PC && !state.attendHideMenu && styles.playButtonHide]}
           onClick={() => handleResetPlay()}
           onClick={() => handleResetPlay()}
         >
         >
-          <img class={styles.iconBtn} src={headImg("icon_resetbtn.svg")} />
+          <img class={styles.iconBtn} src={headImg(state.platform === IPlatform.PC ? "pc_icon_resetbtn.png" : "icon_resetbtn.svg")} />
         </div>
         </div>
 
 
-        <Popup v-model:show={headTopData.settingMode} class="popup-custom van-scale center-closeBtn" transition="van-scale" teleport="body" closeable>
+        <Popup v-model:show={headTopData.settingMode} class="popup-custom van-scale center-closeBtn settingBoxClass_drag" transition="van-scale" teleport="body" closeable style={positionInfo.styleDrag.value}>
           <Settting />
           <Settting />
+          { state.platform === IPlatform.PC && <Dragbom showGuide={headData.showGragGuide} onGuideDone={handleGuide}  /> }
         </Popup>
         </Popup>
 
 
         {/* 模式切换 */}
         {/* 模式切换 */}

+ 9 - 0
src/page-instrument/header-top/settting/index.module.less

@@ -74,6 +74,15 @@
             font-size: 12px;
             font-size: 12px;
         }
         }
     }
     }
+
+    &.pcContent {
+        height: 230px;
+        :global {
+            .van-tab__panel {
+                height: 180px;
+            } 
+        }
+    }
 }
 }
 
 
 .noticebar {
 .noticebar {

+ 23 - 4
src/page-instrument/header-top/settting/index.tsx

@@ -1,4 +1,4 @@
-import { defineComponent, nextTick, reactive, watch } from "vue";
+import { defineComponent, nextTick, reactive, watch, toRef } from "vue";
 import styles from "./index.module.less";
 import styles from "./index.module.less";
 import iconClose from "../image/close2.svg";
 import iconClose from "../image/close2.svg";
 import {
 import {
@@ -30,6 +30,9 @@ import Recommendation from "../../custom-plugins/helper-model/recommendation";
 import { svg2canvas } from "/src/utils/svg2canvas";
 import { svg2canvas } from "/src/utils/svg2canvas";
 import { getQuery } from "/src/utils/queryString";
 import { getQuery } from "/src/utils/queryString";
 import { browser } from "/src/utils";
 import { browser } from "/src/utils";
+import { storeData } from "/src/store";
+import useDrag from "/src/view/plugins/useDrag/index";
+import Dragbom from "/src/view/plugins/useDrag/dragbom";
 
 
 export default defineComponent({
 export default defineComponent({
 	name: "header-settting",
 	name: "header-settting",
@@ -98,10 +101,24 @@ export default defineComponent({
 			}
 			}
 			state.setting.frequency = currentFrequency >= 0 ? currentFrequency : 0
 			state.setting.frequency = currentFrequency >= 0 ? currentFrequency : 0
 		}
 		}
-		
+
+		const parentClassName = "recommenBoxClass_drag";
+		const userId = storeData.user?.id ? String(storeData.user?.id) : '';
+		const positionInfo = state.platform !== IPlatform.PC ? {
+			styleDrag: { value: null }
+		  } : useDrag(
+		  [
+			`${parentClassName} .van-tabs__wrap`,
+			`${parentClassName} .bom_drag`
+		  ],
+		  parentClassName,
+		  toRef(helperData, 'recommendationShow'),
+		  userId
+		)
+	
 		return () => (
 		return () => (
 			<div class={styles["header-settting"]}>
 			<div class={styles["header-settting"]}>
-				<div class={styles.content}>
+				<div class={[styles.content, state.platform === IPlatform.PC && styles.pcContent]}>
 					<Tabs border animated swipeable>
 					<Tabs border animated swipeable>
 						<Tab title="全局设置">
 						<Tab title="全局设置">
 							<NoticeBar
 							<NoticeBar
@@ -282,16 +299,18 @@ export default defineComponent({
 				</Popup>
 				</Popup>
 				<Popup
 				<Popup
 					v-model:show={helperData.recommendationShow}
 					v-model:show={helperData.recommendationShow}
-					class="popup-custom van-scale center-closeBtn"
+					class="popup-custom van-scale center-closeBtn recommenBoxClass_drag"
 					transition="van-scale"
 					transition="van-scale"
 					teleport="body"
 					teleport="body"
 					closeable
 					closeable
+					style={positionInfo.styleDrag.value}
 				>
 				>
 					<Recommendation
 					<Recommendation
 						onClose={() => {
 						onClose={() => {
 							helperData.recommendationShow = false;
 							helperData.recommendationShow = false;
 						}}
 						}}
 					/>
 					/>
+					{ state.platform === IPlatform.PC && <Dragbom /> }
 				</Popup>
 				</Popup>
 			</div>
 			</div>
 		);
 		);

+ 3 - 0
src/page-instrument/header-top/title/index.module.less

@@ -11,6 +11,9 @@
     flex: 1;
     flex: 1;
     padding: 0;
     padding: 0;
   }
   }
+  &.pcContainer {
+    width: 500px;
+  }
 }
 }
 
 
 .icon {
 .icon {

+ 2 - 1
src/page-instrument/header-top/title/index.tsx

@@ -1,6 +1,7 @@
 import { defineComponent } from 'vue'
 import { defineComponent } from 'vue'
 import { NoticeBar } from 'vant'
 import { NoticeBar } from 'vant'
 import styles from './index.module.less'
 import styles from './index.module.less'
+import state, { IPlatform } from "/src/state";
 
 
 import MusicIcon from '../image/music.png'
 import MusicIcon from '../image/music.png'
 import ArrowIcon from '../image/arrow.svg'
 import ArrowIcon from '../image/arrow.svg'
@@ -22,7 +23,7 @@ export default defineComponent({
   },
   },
   render() {
   render() {
     return (
     return (
-      <div class={styles.container}>
+      <div class={[styles.container, state.platform === IPlatform.PC && styles.pcContainer]}>
         <NoticeBar
         <NoticeBar
           text={this.text}
           text={this.text}
           color="#000"
           color="#000"

+ 38 - 1
src/page-instrument/view-detail/index.module.less

@@ -15,18 +15,45 @@
     height: 100vh;
     height: 100vh;
     overflow: hidden;
     overflow: hidden;
     --header-height: 62px;
     --header-height: 62px;
+    --pc-header-height: 54px;
     background: var(--container-background);
     background: var(--container-background);
 
 
     .headHeight {
     .headHeight {
         position: relative;
         position: relative;
         width: 100%;
         width: 100%;
         height: var(--header-height);
         height: var(--header-height);
-        transition: margin .3s;
+        transition: all .3s;
+        // bottom: 0;
         z-index: 10;
         z-index: 10;
 
 
         &.headHide {
         &.headHide {
             margin-top: calc(0Px - var(--header-height));
             margin-top: calc(0Px - var(--header-height));
         }
         }
+
+        &.pcHeadHideBottom {
+            margin-bottom: calc(-var(--header-height));
+        }
+    }
+
+    .pcHead {
+        height: var(--pc-header-height);
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        transform: translateY(-100%);
+        animation: headerDown .3s .5s ease-in-out forwards;
+        transition: all .3s;
+        &.pcHeadHide {
+            margin-top: calc(0Px - var(--pc-header-height));
+        }
+        &.pcHeadHideBottom {
+            bottom: calc(var(--header-height));
+        }
+        :global {
+            .van-notice-bar__wrap {
+                justify-content: center;
+            }
+        }
     }
     }
 
 
     .container {
     .container {
@@ -40,8 +67,12 @@
         overflow: hidden;
         overflow: hidden;
         //overflow-x: scroll;
         //overflow-x: scroll;
     }
     }
+    .pcContainer {
+        height: calc(100vh - var(--header-height) - var(--pc-header-height));
+    }
 }
 }
 
 
+
 :global {
 :global {
     #cursorImg-0, #cursor-copy {
     #cursorImg-0, #cursor-copy {
         width: 2PX !important;
         width: 2PX !important;
@@ -207,3 +238,9 @@
         }        
         }        
     }
     }
 }
 }
+
+@keyframes headerDown {
+    100% {
+        transform: translateY(0%);
+    }
+}

+ 40 - 10
src/page-instrument/view-detail/index.tsx

@@ -11,7 +11,7 @@ import { sysMusicScoreAccompanimentQueryPage } from "../api";
 import EvaluatModel from "../evaluat-model";
 import EvaluatModel from "../evaluat-model";
 import HeaderTop from "../header-top";
 import HeaderTop from "../header-top";
 import styles from "./index.module.less";
 import styles from "./index.module.less";
-import { api_cloudAccompanyMessage, api_cloudLoading, api_keepScreenLongLight, api_openCamera, api_openWebView, api_setEventTracking, api_setRequestedOrientation, api_setStatusBarVisibility, isSpecialShapedScreen } from "/src/helpers/communication";
+import { api_cloudAccompanyMessage, api_cloudLoading, api_keepScreenLongLight, api_openCamera, api_openWebView, api_setEventTracking, api_setRequestedOrientation, api_setStatusBarVisibility, isSpecialShapedScreen, addImagePos } from "/src/helpers/communication";
 import { getQuery } from "/src/utils/queryString";
 import { getQuery } from "/src/utils/queryString";
 import Evaluating, { evaluatingData } from "/src/view/evaluating";
 import Evaluating, { evaluatingData } from "/src/view/evaluating";
 import MeasureSpeed from "/src/view/plugins/measure-speed";
 import MeasureSpeed from "/src/view/plugins/measure-speed";
@@ -33,7 +33,7 @@ import { usePageVisibility } from "@vant/use";
 import { initMidi } from "/src/helpers/midiPlay"
 import { initMidi } from "/src/helpers/midiPlay"
 import TheAudio from "/src/components/the-audio"
 import TheAudio from "/src/components/the-audio"
 import tickWav from "/src/assets/tick.wav";
 import tickWav from "/src/assets/tick.wav";
-
+import Title from "../header-top/title";
 
 
 const DelayCheck = defineAsyncComponent(() =>
 const DelayCheck = defineAsyncComponent(() =>
   import('/src/page-instrument/evaluat-model/delay-check')
   import('/src/page-instrument/evaluat-model/delay-check')
@@ -128,6 +128,16 @@ export default defineComponent({
     };
     };
     // console.log(route.params, query)
     // console.log(route.params, query)
 
 
+    // 处理老师端上课页面,按钮方向
+    const handleImagePos = (res: any) => {
+      if (res?.data) {
+        state.playBtnDirection = res.data.data === 'right' ? 'right' : 'left';
+        if (state.fingeringInfo.direction === "vertical" && state.setting.displayFingering) {
+          state.playBtnDirection = 'left';
+        }
+      }
+    };
+
     onMounted(async () => {
     onMounted(async () => {
       (window as any).appName = "colexiu";
       (window as any).appName = "colexiu";
       const id = query.id || "43554";
       const id = query.id || "43554";
@@ -148,6 +158,7 @@ export default defineComponent({
         state.setting.displayFingering = false
         state.setting.displayFingering = false
       }      
       }      
       // api_setEventTracking();
       // api_setEventTracking();
+      addImagePos(handleImagePos);
     });
     });
 
 
     /** 渲染完成 */
     /** 渲染完成 */
@@ -196,7 +207,10 @@ export default defineComponent({
         handleInitTick(beatLengthInMilliseconds, osmd?.Sheet?.SheetPlaybackSetting?.Rhythm?.Numerator || 4);
         handleInitTick(beatLengthInMilliseconds, osmd?.Sheet?.SheetPlaybackSetting?.Rhythm?.Numerator || 4);
       // }
       // }
       // api_cloudLoading();
       // api_cloudLoading();
-
+      state.playBtnDirection = query.imagePos === 'right' ? 'right' : 'left';
+      if (state.fingeringInfo.direction === "vertical" && state.setting.displayFingering) {
+        state.playBtnDirection = 'left';
+      }
       state.musicRendered = true;
       state.musicRendered = true;
 
 
       evaluatCreateMusicPlayer();
       evaluatCreateMusicPlayer();
@@ -269,9 +283,10 @@ export default defineComponent({
     watch(
     watch(
       () => state.playState,
       () => state.playState,
       () => {
       () => {
-        if (state.platform != IPlatform.PC) {
-          detailData.headerHide = state.playState === "play" ? true : false;
-        }
+        // if (state.platform != IPlatform.PC) {
+        //   detailData.headerHide = state.playState === "play" ? true : false;
+        // }
+        detailData.headerHide = state.playState === "play" ? true : false;
         sendParentMessage(state.playState);
         sendParentMessage(state.playState);
       }
       }
     );
     );
@@ -356,17 +371,28 @@ export default defineComponent({
             </div>
             </div>
           )}
           )}
         </Transition>
         </Transition>
+        {/** 学生端头部标题&功能按钮 */}
         {
         {
-          !state.isPreView && 
+          (!state.isPreView && state.platform !== IPlatform.PC) && 
           <div class={[styles.headHeight, detailData.headerHide && styles.headHide]}>{state.musicRendered && <HeaderTop />}</div>
           <div class={[styles.headHeight, detailData.headerHide && styles.headHide]}>{state.musicRendered && <HeaderTop />}</div>
         }
         }
+        {/** 老师端标题 */}
+        {
+          state.platform === IPlatform.PC && 
+          <div class={[styles.pcHead, detailData.headerHide && styles.pcHeadHide]}>
+            <Title text={state.examSongName} rightView={false} />
+          </div>
+        }
         <div
         <div
           id="scrollContainer"
           id="scrollContainer"
           style={{ ...fingerConfig.value.container, height: detailData.headerHide ? "100vh" : "" }}
           style={{ ...fingerConfig.value.container, height: detailData.headerHide ? "100vh" : "" }}
-          class={[styles.container, !state.setting.displayCursor && "hideCursor", browsInfo.xiaomi && styles.xiaomi]}
+          class={[styles.container, !state.setting.displayCursor && "hideCursor", browsInfo.xiaomi && styles.xiaomi, state.platform === IPlatform.PC && styles.pcContainer]}
           onClick={(e: Event) => {
           onClick={(e: Event) => {
             e.stopPropagation();
             e.stopPropagation();
-            if (state.playState === "play" && state.platform != IPlatform.PC) {
+            // if (state.playState === "play" && state.platform != IPlatform.PC) {
+            //   detailData.headerHide = !detailData.headerHide;
+            // }
+            if (state.playState === "play") {
               detailData.headerHide = !detailData.headerHide;
               detailData.headerHide = !detailData.headerHide;
             }
             }
           }}
           }}
@@ -396,7 +422,11 @@ export default defineComponent({
             </div>
             </div>
           )}
           )}
         </div>
         </div>
-
+        {/** 老师端底部功能按钮 */}
+        {
+          (!state.isPreView && state.platform === IPlatform.PC) && 
+          <div class={[styles.headHeight, detailData.headerHide && styles.pcHeadHideBottom]}>{state.musicRendered && <HeaderTop />}</div>
+        }
         {/* 节拍器,跟练需要播放系统节拍器,所以不需要判断needTick状态 */}
         {/* 节拍器,跟练需要播放系统节拍器,所以不需要判断needTick状态 */}
         {/* {state.needTick && <Tick />} */}
         {/* {state.needTick && <Tick />} */}
         <Tick />
         <Tick />

+ 5 - 0
src/state.ts

@@ -460,6 +460,11 @@ const state = reactive({
   /** 是否是evxml */
   /** 是否是evxml */
   isEvxml: false,
   isEvxml: false,
   noTimes: [] as any,
   noTimes: [] as any,
+  attendHideMenu: true,
+  /** 老师端:播放、暂停、重播按钮布局方向 */
+  playBtnDirection: "left" as "left" | "right",
+  /** 引导页信息 */
+  guideInfo: null as any,
 });
 });
 const browserInfo = browser();
 const browserInfo = browser();
 let offset_duration = 0;
 let offset_duration = 0;

+ 3 - 3
src/view/music-score/index.tsx

@@ -2,7 +2,7 @@ import { computed, defineComponent, onMounted, reactive, ref } from "vue";
 import { formatXML, onlyVisible } from "../../helpers/formateMusic";
 import { formatXML, onlyVisible } from "../../helpers/formateMusic";
 // // @ts-ignore
 // // @ts-ignore
 import { OpenSheetMusicDisplay } from "/osmd-extended/src";
 import { OpenSheetMusicDisplay } from "/osmd-extended/src";
-import state, { EnumMusicRenderType } from "/src/state";
+import state, { EnumMusicRenderType, IPlatform } from "/src/state";
 import Selection from "../selection";
 import Selection from "../selection";
 import styles from "./index.module.less";
 import styles from "./index.module.less";
 import queryString from "query-string";
 import queryString from "query-string";
@@ -116,10 +116,10 @@ export default defineComponent({
 			// osmd.EngravingRules.CompactMode = true // 紧凑模式
 			// osmd.EngravingRules.CompactMode = true // 紧凑模式
 			osmd.EngravingRules.PageRightMargin = state.isSingleLine ? (window.innerWidth+200)/10 : 2;
 			osmd.EngravingRules.PageRightMargin = state.isSingleLine ? (window.innerWidth+200)/10 : 2;
 			osmd.EngravingRules.FixedMeasureWidth = state.isSingleLine ? true : false; // 是否固定小节的宽度(小节同一宽度渲染)
 			osmd.EngravingRules.FixedMeasureWidth = state.isSingleLine ? true : false; // 是否固定小节的宽度(小节同一宽度渲染)
-			osmd.EngravingRules.PageTopMargin = 10;
+			osmd.EngravingRules.PageTopMargin = state.platform === IPlatform.PC ? 4 : 10; // 老师端顶部间距
 			osmd.EngravingRules.PageTopMarginNarrow = 3;
 			osmd.EngravingRules.PageTopMarginNarrow = 3;
 			osmd.EngravingRules.PageLeftMargin = 2;
 			osmd.EngravingRules.PageLeftMargin = 2;
-			osmd.EngravingRules.PageBottomMargin = 2;
+			osmd.EngravingRules.PageBottomMargin = state.platform === IPlatform.PC ? 1 : 2;
 			osmd.EngravingRules.DYMusicScoreType =
 			osmd.EngravingRules.DYMusicScoreType =
 				state.musicRenderType === EnumMusicRenderType.staff ? "staff" : "jianpu";
 				state.musicRenderType === EnumMusicRenderType.staff ? "staff" : "jianpu";
 			// 如果为固定调,需要加入全局
 			// 如果为固定调,需要加入全局

+ 45 - 0
src/view/plugins/useDrag/dragbom.tsx

@@ -0,0 +1,45 @@
+import { defineComponent, computed, reactive, onMounted } from 'vue';
+import styles from './index.module.less';
+// 底部拖动区域
+export default defineComponent({
+  name: 'dragBom',
+  emits: ["guideDone"],
+	props: {
+		/** 是否显示引导 */
+		showGuide: {
+			type: Boolean,
+			default: false,
+		},
+	},
+  setup(props, { emit }) {
+    const data = reactive({
+      guidePos: "bottom" as "bottom" | "top",
+    });
+
+    const initGuidePos = () => {
+      const pageHeight = document.documentElement.clientHeight || document.body.clientHeight;
+      const guideHeight = document.querySelector('.bom_guide')?.getBoundingClientRect().height || 0;
+      const dragTop = document.querySelector('.bom_drag')?.getBoundingClientRect()?.top || 0;
+      data.guidePos = pageHeight - dragTop < guideHeight ? "top" : "bottom";
+    }
+    onMounted(() => {
+      initGuidePos();
+    });
+    return () => (
+      <>
+        <div class={[styles.dragBom, 'bom_drag']}>
+          <div class={styles.box}></div>
+          <div class={[styles.box, styles.right]}></div>
+        </div>
+        {
+          props.showGuide && 
+          <div class={[styles.guide, data.guidePos === "top" && styles.guideTop, 'bom_guide']}>
+            <div class={styles.guideBg}></div>
+            <div class={styles.guideDone} onClick={() => emit("guideDone")}></div>
+          </div>          
+        }
+
+      </>
+    );
+  }
+});

BIN
src/view/plugins/useDrag/img/left.png


BIN
src/view/plugins/useDrag/img/modalDragBg.png


BIN
src/view/plugins/useDrag/img/modalDragBg2.png


BIN
src/view/plugins/useDrag/img/modalDragDone.png


BIN
src/view/plugins/useDrag/img/right.png


+ 65 - 0
src/view/plugins/useDrag/index.module.less

@@ -0,0 +1,65 @@
+.dragBom {
+  width: 100%;
+  height: 20px;
+  display: flex;
+  justify-content: space-between;
+  // border-radius: 0 0 8px 8px;
+  //overflow: hidden;
+  position: absolute;
+  bottom: 0;
+  .box {
+    width: 20px;
+    height: 100%;
+    background: url('./img/left.png') no-repeat;
+    background-size: 100% 100%;
+    border-bottom-left-radius: 18px;
+    border-bottom-right-radius: 18px;
+    &.right {
+      background: url('./img/right.png') no-repeat;
+      background-size: 100% 100%;
+    }
+  }
+}
+.guide {
+  position: absolute;
+  left: 0;
+  top: calc(100% - 10px);
+  &::before {
+    content: "";
+    display: block;
+    position: fixed;
+    left: 0;
+    top: 0;
+    z-index: 9;
+    width: 100vw;
+    height: 100vh;
+    background: rgba(0,0,0,0.2);
+  }
+  .guideBg {
+    position: relative;
+    z-index: 99;
+    width: 200px;
+    height: 102px;
+    background: url('./img/modalDragBg.png') no-repeat;
+    background-size: 100% 100%;
+  }
+  .guideDone {
+    position: absolute;
+    z-index: 99;
+    left: 34.6%;
+    top: 72.2%;
+    width: 50px;
+    height: 20px;
+    background: url('./img/modalDragDone.png') no-repeat;
+    background-size: 100% 100%;
+    cursor: pointer;
+  }
+  &.guideTop {
+    top: initial;
+    bottom: 2px;
+    .guideBg {
+      background: url('./img/modalDragBg2.png') no-repeat;
+      background-size: 100% 100%;
+    }
+  }
+}

+ 157 - 0
src/view/plugins/useDrag/index.ts

@@ -0,0 +1,157 @@
+// 弹窗拖动
+import { ref, Ref, watch, nextTick, computed } from 'vue';
+
+type posType = {
+  top: number;
+  left: number;
+};
+
+/**
+ * @params classList  可拖动地方的class值,也为唯一值
+ * @params boxClass  容器class值必须为唯一值,这个class和useid拼接 作为缓存主键
+ * @params dragShow  弹窗是否显示
+ * @params userId    当前用户id
+ */
+export default function useDrag(
+  classList: string[],
+  boxClass: string,
+  dragShow: Ref<boolean>,
+  userId: string
+) {
+  const pos = ref<posType>({
+    top: -1, // -1 为初始值 代表没有缓存 默认居中
+    left: -1
+  });
+  const useIdDargClass = userId + boxClass;
+  watch(dragShow, () => {
+    if (dragShow.value) {
+      // 初始化pos值
+      initPos();
+      window.addEventListener('resize', refreshPos);
+      nextTick(() => {
+        const boxClassDom = document.querySelector(
+          `.${boxClass}`
+        ) as HTMLElement;
+        if (!boxClassDom) {
+          return;
+        }
+        classList.map((className: string) => {
+          const classDom = document.querySelector(
+            `.${className}`
+          ) as HTMLElement;
+          if (classDom) {
+            classDom.style.cursor = 'move';
+            drag(classDom, boxClassDom, pos);
+          }
+        });
+      });
+    } else {
+      window.removeEventListener('resize', refreshPos);
+      setCachePos(useIdDargClass, pos.value);
+    }
+  });
+  const styleDrag = computed(() => {
+    // 没有设置拖动的时候保持原本的
+    return pos.value.left === -1 && pos.value.top === -1
+      ? {}
+      : {
+          position: 'fixed',
+          left: `${pos.value.left}px`,
+          top: `${pos.value.top}px`,
+          transform: 'initial',
+          transformOrigin: 'initial',
+          margin: 'initial',
+          transition: 'initial'
+        };
+  });
+  function initPos() {
+    const posCache = getCachePos(useIdDargClass);
+    // 有缓存 用缓存的值,没有缓存用默认
+    if (posCache) {
+      pos.value = posCache;
+    }
+  }
+  function refreshPos() {
+    const boxClassDom = document.querySelector(`.${boxClass}`) as HTMLElement;
+    if (!boxClassDom) return;
+    const parentElementRect = boxClassDom.getBoundingClientRect();
+    const clientWidth = document.documentElement.clientWidth;
+    const clientHeight = document.documentElement.clientHeight;
+    const { top, left } = pos.value;
+    const maxLeft = clientWidth - parentElementRect.width;
+    const maxTop = clientHeight - parentElementRect.height;
+    let moveX = left;
+    let moveY = top;
+    const minLeft = 0;
+    const minTop = 0;
+    moveX = moveX < minLeft ? minLeft : moveX > maxLeft ? maxLeft : moveX;
+    moveY = moveY < minTop ? minTop : moveY > maxTop ? maxTop : moveY;
+    pos.value = {
+      top: moveY,
+      left: moveX
+    };
+  }
+  return {
+    pos,
+    styleDrag
+  };
+}
+
+// 拖动
+function drag(el: HTMLElement, parentElement: HTMLElement, pos: Ref<posType>) {
+  function mousedown(e: MouseEvent) {
+    const parentElementRect = parentElement.getBoundingClientRect();
+    const downX = e.clientX;
+    const downY = e.clientY;
+    const clientWidth = document.documentElement.clientWidth;
+    const clientHeight = document.documentElement.clientHeight;
+    const maxLeft = clientWidth - parentElementRect.width;
+    const maxTop = clientHeight - parentElementRect.height;
+    const minLeft = 0;
+    const minTop = 0;
+    function onMousemove(e: MouseEvent) {
+      let moveX = parentElementRect.left + (e.clientX - downX);
+      let moveY = parentElementRect.top + (e.clientY - downY);
+      moveX = moveX < minLeft ? minLeft : moveX > maxLeft ? maxLeft : moveX;
+      moveY = moveY < minTop ? minTop : moveY > maxTop ? maxTop : moveY;
+      pos.value = {
+        top: moveY,
+        left: moveX
+      };
+    }
+    function onMouseup() {
+      document.removeEventListener('mousemove', onMousemove);
+      document.removeEventListener('mouseup', onMouseup);
+    }
+    document.addEventListener('mousemove', onMousemove);
+    document.addEventListener('mouseup', onMouseup);
+  }
+  el.addEventListener('mousedown', mousedown);
+}
+
+// 缓存
+const localStorageName = 'dragCachePos';
+function getCachePos(useIdDargClass: string): null | undefined | posType {
+  const localCachePos = localStorage.getItem(localStorageName);
+  if (localCachePos) {
+    try {
+      return JSON.parse(localCachePos)[useIdDargClass];
+    } catch {
+      return null;
+    }
+  }
+  return null;
+}
+function setCachePos(useIdDargClass: string, pos: posType) {
+  const localCachePos = localStorage.getItem(localStorageName);
+  let cachePosObj: Record<string, any> = {};
+  if (localCachePos) {
+    try {
+      cachePosObj = JSON.parse(localCachePos);
+    } catch {
+      //
+    }
+  }
+  cachePosObj[useIdDargClass] = pos;
+  localStorage.setItem(localStorageName, JSON.stringify(cachePosObj));
+}

+ 2 - 2
vite.config.ts

@@ -76,9 +76,9 @@ export default defineConfig({
 				// target: "https://kt.colexiu.com",
 				// target: "https://kt.colexiu.com",
 				// target: "https://test.lexiaoya.cn",
 				// target: "https://test.lexiaoya.cn",
 				// target: "https://dev.kt.colexiu.com",
 				// target: "https://dev.kt.colexiu.com",
-				target: "https://test.resource.colexiu.com", // 内容平台开发环境,内容平台开发,需在url链接上加上isCbs=true
+				// target: "https://test.resource.colexiu.com", // 内容平台开发环境,内容平台开发,需在url链接上加上isCbs=true
 				// target: "https://dev.resource.colexiu.com",
 				// target: "https://dev.resource.colexiu.com",
-				// target: "https://test.kt.colexiu.com",
+				target: "https://test.kt.colexiu.com",
 				// target: "https://mec.colexiu.com",
 				// target: "https://mec.colexiu.com",
 				changeOrigin: true,
 				changeOrigin: true,
 				rewrite: (path) => path.replace(/^\/instrument/, ""),
 				rewrite: (path) => path.replace(/^\/instrument/, ""),