skyblued 2 years ago
parent
commit
c05260ba8a

+ 5 - 0
src/subpages/colexiu/buttons/index.tsx

@@ -247,6 +247,7 @@ export default defineComponent({
             {/* route.query?.modelType 就不显示模式按钮  */}
             {!route.query?.modelType && modelType.value !== 'init' && !detailState.frozenMode && (
               <Button
+                data-step="m1"
                 class={[styles.button, styles.hasText]}
                 disabled={(runtime.evaluatingStatus && !startButtonShow.value) || followRef.value?.data.start}
                 onClick={() => {
@@ -332,6 +333,7 @@ export default defineComponent({
             {modelType.value === 'practice' && (
               <>
                 <Button
+                  data-step="m2"
                   class={[styles.button, styles.hasText]}
                   onClick={() => RuntimeUtils.changeMode(runtime.mode === 'background' ? 'music' : 'background')}
                   disabled={changeModeIsDisabled}
@@ -340,6 +342,7 @@ export default defineComponent({
                   <span>{runtime.mode === 'background' ? '伴奏' : '原声'}</span>
                 </Button>
                 <Button
+                  data-step="m3"
                   class={[styles.button, styles.hasText]}
                   onClick={RuntimeUtils.sectionChange}
                   disabled={runtime.evaluatingStatus}
@@ -354,6 +357,7 @@ export default defineComponent({
                   <span>选段</span>
                 </Button>
                 <Button
+                  data-step="m4"
                   class={[styles.button, styles.hasText]}
                   onClick={() => {
                     SettingState.sett.fingering = !SettingState.sett.fingering
@@ -377,6 +381,7 @@ export default defineComponent({
                 vSlots={{
                   reference: () => (
                     <Button
+                      data-step="m5"
                       class={[styles.button, styles.hasText, styles.speedButton]}
                       disabled={runtime.evaluatingStatus || runtime.playState === 'play'}
                       onClick={() => {

+ 3 - 3
src/subpages/colexiu/buttons/model-wraper.tsx

@@ -27,9 +27,9 @@ export default defineComponent({
           >
             {props.show && (
               <div class={styles.wrap}>
-                <img onClick={() => props.onChangeModelType!('practice')} src={model1} />
-                <img onClick={() => props.onChangeModelType!('follow')} src={model3} />
-                <img onClick={() => props.onChangeModelType!('evaluation')} src={model2} />
+                <img data-step="step0" onClick={() => props.onChangeModelType!('practice')} src={model1} />
+                <img data-step="step1" onClick={() => props.onChangeModelType!('follow')} src={model3} />
+                <img data-step="step2" onClick={() => props.onChangeModelType!('evaluation')} src={model2} />
               </div>
             )}
           </Transition>

+ 3 - 0
src/subpages/colexiu/index.tsx

@@ -41,6 +41,7 @@ import { restPromptMain } from '/src/helpers/restPrompt'
 import ProductJson from './popups/productJson'
 import { useRoute } from 'vue-router'
 import styles from './index.module.less'
+import Tips from './tips'
 
 const search = useOriginSearch()
 const browserInfo = browser()
@@ -414,6 +415,8 @@ export default defineComponent({
           <MusicList />
           {/* 保存json */}
           <ProductJson ref={productRef} />
+          {/* 引导 */}
+          <Tips />
         </div>
       )
     }

+ 3 - 3
src/subpages/colexiu/popups/follow/index.tsx

@@ -62,7 +62,7 @@ async function updatePlayTime(time: number){
       data: {
         musicSheetId: id,
         sysMusicScoreId: id,
-        feature: 'CLOUD_STUDY_TRAIN',
+        feature: search.search || 'PRACTICE',
         playTime: time,
         deviceType: getPlatform(),
         behaviorId,
@@ -197,11 +197,11 @@ export default defineComponent({
               </Button>
             )}
           </Transition>
-          <div class={styles.title}>
+          {/* <div class={styles.title}>
             <span>音符频率: {noteFrequency.value.toFixed(2)}</span>
             <span style={{ color: 'red', marginLeft: '10px' }}>拾音频率: {audioFrequency.value.toFixed(2)}</span>
             <span style={{ marginLeft: '10px' }}>拾音长度: {data.list.length}</span>
-          </div>
+          </div> */}
         </div>
       </Teleport>
     )

BIN
src/subpages/colexiu/tips/images/icon-hand2.png


BIN
src/subpages/colexiu/tips/images/icon-header.png


+ 5 - 0
src/subpages/colexiu/tips/images/index.ts

@@ -0,0 +1,5 @@
+const modules = (import.meta as any).globEager(`../images/**`)
+export const getImage = (name: string) => {
+    // console.log("🚀 ~ modules", modules[`../images/${name}`]?.default)
+    return modules[`../images/${name}`]?.default || ''
+}

BIN
src/subpages/colexiu/tips/images/m1.png


BIN
src/subpages/colexiu/tips/images/m2.png


BIN
src/subpages/colexiu/tips/images/m3.png


BIN
src/subpages/colexiu/tips/images/m4.png


BIN
src/subpages/colexiu/tips/images/m5.png


BIN
src/subpages/colexiu/tips/images/step0.png


BIN
src/subpages/colexiu/tips/images/step1.png


BIN
src/subpages/colexiu/tips/images/step2.png


+ 62 - 0
src/subpages/colexiu/tips/index.module.less

@@ -0,0 +1,62 @@
+.tipsContainer {
+  background: transparent;
+  width: 100vw;
+  height: 100vh;
+}
+.backBtn {
+  position: absolute;
+  left: 24px;
+  top: 12px;
+  padding: 4px 10px;
+  border-radius: 20px;
+  border: 1px solid #ffffff;
+  font-size: 6px;
+  color: #fff;
+  text-align: center;
+  &:active {
+    opacity: 0.8;
+  }
+}
+.content {
+  position: relative;
+  width: 100%;
+  height: 100%;
+}
+
+.parent {
+  position: absolute;
+  padding: 4px;
+  background: rgba(248, 249, 237, 1);
+  border-radius: 20px;
+  img {
+    width: 100%;
+    height: 100%;
+  }
+}
+.item {
+  position: absolute;
+  width: 128px;
+  z-index: 10;
+  .img {
+    position: absolute;
+    width: 100%;
+  }
+  .iconHead {
+    position: absolute;
+    left: 30px;
+    width: 15px;
+    height: 21px;
+  }
+  .btns {
+    position: absolute;
+    display: flex;
+    width: 100%;
+    padding: 0 12px;
+    .btn {
+      width: 51px;
+      height: 16px;
+      line-height: 16px;
+      padding: 0;
+    }
+  }
+}

+ 55 - 0
src/subpages/colexiu/tips/index.tsx

@@ -0,0 +1,55 @@
+import { Button, Popup } from 'vant'
+import {} from 'vant'
+import { defineComponent, nextTick, onMounted, reactive, ref, watch } from 'vue'
+import state from '/src/pages/detail/state'
+import styles from './index.module.less'
+import Tip1 from './tip1'
+import Tip2 from './tip2'
+import { modelType } from '../buttons'
+
+export default defineComponent({
+  name: 'tips-step',
+  setup(props, ctx) {
+    const tipShow = ref(false)
+    const guide = reactive({
+        tip1: false,
+        tip2: false
+    })
+    watch(
+      () => state.initRendered,
+      () => {
+        const isFirstTip = localStorage.getItem('isFirstTip')
+        if (state.initRendered && !isFirstTip) {
+          tipShow.value = true
+          guide.tip1 = true
+          console.log('首次渲染结束')
+        }
+      }
+    )
+    watch(modelType, () => {
+        console.log(modelType.value)
+        const isFirstModel = localStorage.getItem('isFirstModel')
+        if(modelType.value === 'practice' && !isFirstModel){
+            tipShow.value = true
+            guide.tip1 = false
+            guide.tip2 = true
+            console.log('模式更改')
+        }
+    })
+    const setLocalData = (key: string) => {
+        localStorage.setItem(key, 'ok')
+    }
+    return () => (
+      <Popup teleport="body" closeOnClickOverlay={false} class={styles.tipsContainer} v-model:show={tipShow.value}>
+        {guide.tip1 && <Tip1 onHanldeStop={() => {
+            tipShow.value = false
+            setLocalData('isFirstTip')
+        }} />}
+        {guide.tip2 && <Tip2 onHanldeStop={() => {
+            tipShow.value = false
+            setLocalData('isFirstModel')
+        }} />}
+      </Popup>
+    )
+  },
+})

+ 150 - 0
src/subpages/colexiu/tips/tip1.tsx

@@ -0,0 +1,150 @@
+import { Button, Popup } from 'vant'
+import {} from 'vant'
+import { defineComponent, nextTick, onMounted, reactive, ref, watch } from 'vue'
+import state from '/src/pages/detail/state'
+import styles from './index.module.less'
+import { getImage } from './images'
+
+export default defineComponent({
+  name: 'tips-tip1',
+  emits: ['hanldeStop'],
+  setup(props, {emit}) {
+    const steps = [
+      {
+        img: getImage('step0.png'),
+        btnsStyle: {
+          top: '-2.13333rem',
+        },
+        handStyle: {
+          top: '-0.42667rem',
+        },
+        imgStyle: {
+          top: '-2.4rem',
+        },
+      },
+      {
+        img: getImage('step1.png'),
+        btnsStyle: {
+          top: '-2.2rem',
+        },
+        handStyle: {
+          top: '-0.42667rem',
+        },
+        imgStyle: {
+          top: '-2.4rem',
+        },
+      },
+      {
+        img: getImage('step2.png'),
+        btnsStyle: {
+          top: '-0.7rem',
+          left: '-2.5rem',
+          'justify-content': 'space-evenly',
+          padding: 0,
+        },
+        handStyle: {
+          top: '-0.42667rem',
+        },
+        imgStyle: {
+          top: '-2.7rem',
+          left: '-2.3rem',
+        },
+      },
+    ]
+    const contentRef = ref<HTMLElement>()
+    const data = reactive({
+      show: false,
+      step: 0,
+      rect: {} as any,
+    })
+    // 开始引导
+    const handleStart = () => {
+      getEle()
+    }
+    onMounted(() => {
+      handleStart()
+    })
+    // 移除之前的
+    const hanldeRemove = () => {
+      const nodes = document.querySelectorAll(`.${styles.parent}`)
+      for (let n of nodes) {
+        n.remove()
+      }
+    }
+    /**
+     * 获取需要引导的元素的位置大小
+     */
+    const getEle = () => {
+      hanldeRemove()
+      const ele = document.querySelector(`[data-step='step${data.step}']`)
+      const eleRect = ele?.getBoundingClientRect()
+      const eleNode: any = ele?.cloneNode(true)
+      if (ele) {
+        const parentElement = document.createElement('div')
+        parentElement.classList.add(styles.parent)
+        parentElement.style.left = eleRect?.left + 'px'
+        parentElement.style.top = eleRect?.top + 'px'
+        // parentElement.style.width = eleRect?.width + 'px'
+        parentElement.style.height = eleRect?.height + 'px'
+        parentElement.style.transform = 'scale(1.5)'
+        data.rect = eleRect || {}
+        parentElement.appendChild(eleNode)
+        contentRef.value?.append(parentElement)
+        console.log('🚀 ~ eleRect', contentRef.value, eleRect, `[data-step='step${data.step}']`)
+      }
+    }
+
+    const handleNext = (step?: number) => {
+      if (step !== undefined){
+        data.step = step
+      } else {
+        data.step += 1
+      }
+      getEle()
+    }
+    const handleStop = () => {
+        emit('hanldeStop')
+    }
+    return () => {
+      const item = steps[data.step]
+      return (
+        <div class={styles.content} ref={contentRef}>
+          <div class={styles.backBtn} onClick={() => handleStop()}>跳过引导</div>
+          <div
+            class={styles.item}
+            style={{
+              left: `${data.rect?.left}px`,
+              top: `${data.rect?.top}px`,
+            }}
+          >
+            <img class={styles.img} style={item.imgStyle} src={item.img} />
+            <img class={styles.iconHead} style={item.handStyle} src={getImage('icon-header.png')} />
+            <div class={styles.btns} style={item.btnsStyle}>
+              {data.step + 1 == steps.length ? (
+                <>
+                  <Button
+                    class={styles.btn}
+                    round
+                    color="transparent"
+                    style={{ 'border-color': '#fff' }}
+                    type="primary"
+                    onClick={() => handleNext(0)}
+                  >
+                    再看一遍
+                  </Button>
+                  <Button class={styles.btn} round type="primary" onClick={() => handleStop()}>
+                    完成
+                  </Button>
+                </>
+              ) : (
+                <Button class={styles.btn} round type="primary" onClick={() => handleNext()}>
+                  下一步 ({data.step + 1}/{steps.length})
+                </Button>
+              )}
+            </div>
+          </div>
+        </div>
+      )
+    }
+  },
+})

+ 195 - 0
src/subpages/colexiu/tips/tip2.tsx

@@ -0,0 +1,195 @@
+import { Button, Popup } from 'vant'
+import {} from 'vant'
+import { defineComponent, nextTick, onMounted, reactive, ref, watch } from 'vue'
+import styles from './index.module.less'
+import { getImage } from './images'
+
+export default defineComponent({
+  name: 'tips-tip1',
+  emits: ['hanldeStop'],
+  setup(props, { emit }) {
+    const steps = [
+      {
+        img: getImage('m1.png'),
+        btnsStyle: {
+          top: '2.6rem',
+        },
+        handStyle: {
+          top: '0.8rem',
+          left: '0.22rem',
+        },
+        imgStyle: {
+          top: '0.8rem',
+          left: '-0.1rem',
+        },
+      },
+      {
+        img: getImage('m2.png'),
+        btnsStyle: {
+          top: '2.6rem',
+        },
+        handStyle: {
+          top: '0.8rem',
+          left: '0.22rem',
+        },
+        imgStyle: {
+          width: '2.66667rem',
+          top: '0.9rem',
+          left: '-0.1rem',
+        },
+      },
+      {
+        img: getImage('m3.png'),
+        btnsStyle: {
+          top: '2.8rem',
+        },
+        handStyle: {
+          top: '0.8rem',
+          left: '0.22rem',
+        },
+        imgStyle: {
+          width: '2.45333rem',
+          top: '0.8rem',
+          left: '-0.1rem',
+        },
+      },
+      {
+        img: getImage('m4.png'),
+        btnsStyle: {
+          top: '2.8rem',
+          left: '-1.3rem',
+        },
+        handStyle: {
+          top: '0.8rem',
+          left: '0.22rem',
+        },
+        imgStyle: {
+          width: '2.45333rem',
+          top: '0.8rem',
+          left: '-1.6rem',
+        },
+      },
+      {
+        img: getImage('m5.png'),
+        btnsStyle: {
+          top: '2.6rem',
+          left: '-1.9rem',
+          width: '2.93333rem',
+          'justify-content': 'space-evenly',
+          padding: 0,
+        },
+        handStyle: {
+          top: '0.8rem',
+          left: '0.22rem',
+        },
+        imgStyle: {
+          top: '0.7rem',
+          left: '-1.9rem',
+          width: '2.93333rem',
+        },
+      },
+    ]
+    const contentRef = ref<HTMLElement>()
+    const data = reactive({
+      show: false,
+      step: 0,
+      rect: {} as any,
+    })
+    // 开始引导
+    const handleStart = () => {
+      getEle()
+    }
+    onMounted(() => {
+      handleStart()
+    })
+    // 移除之前的
+    const hanldeRemove = () => {
+      const nodes = document.querySelectorAll(`.${styles.parent}`)
+      for (let n of nodes) {
+        n.remove()
+      }
+    }
+    /**
+     * 获取需要引导的元素的位置大小
+     */
+    const getEle = () => {
+      hanldeRemove()
+      const ele = document.querySelector(`[data-step='m${data.step + 1}']`)
+      const eleRect = ele?.getBoundingClientRect()
+      const eleNode: any = ele?.cloneNode(true)
+      if (ele && eleRect && contentRef.value) {
+        const parentElement = document.createElement('div')
+        parentElement.classList.add(styles.parent)
+        parentElement.style.left = (eleRect.left - eleRect.width / 2) + 'px'
+        parentElement.style.top = eleRect.top + 'px'
+        // parentElement.style.width = eleRect?.width + 'px'
+        // parentElement.style.height = eleRect?.height + 'px'
+        parentElement.style.borderRadius = '0.1rem'
+        data.rect = {
+          ...eleRect,
+          left : eleRect.left - eleRect.width / 2,
+          top: eleRect.top + 2
+        } || {}
+        parentElement.appendChild(eleNode)
+        contentRef.value?.append(parentElement)
+        console.log('🚀 ~ eleRect', contentRef.value, eleRect, `[data-step='step${data.step}']`)
+      }
+    }
+
+    const handleNext = (step?: number) => {
+      if (step !== undefined) {
+        data.step = step
+      } else {
+        data.step += 1
+      }
+      getEle()
+    }
+    const handleStop = () => {
+      emit('hanldeStop')
+    }
+    return () => {
+      const item = steps[data.step]
+      return (
+        <div class={styles.content} ref={contentRef}>
+          <div class={styles.backBtn} onClick={() => handleStop()}>
+            跳过引导
+          </div>
+          <div
+            class={styles.item}
+            style={{
+              width: '2.18667rem',
+              left: `${data.rect?.left}px`,
+              top: `${data.rect?.top}px`,
+            }}
+          >
+            <img class={styles.img} style={item.imgStyle} src={item.img} />
+            <img class={styles.iconHead} style={item.handStyle} src={getImage('icon-hand2.png')} />
+            <div class={styles.btns} style={item.btnsStyle}>
+              {data.step + 1 == steps.length ? (
+                <>
+                  <Button
+                    class={styles.btn}
+                    round
+                    color="transparent"
+                    style={{ 'border-color': '#fff' }}
+                    type="primary"
+                    onClick={() => handleNext(0)}
+                  >
+                    再看一遍
+                  </Button>
+                  <Button class={styles.btn} round type="primary" onClick={() => handleStop()}>
+                    完成
+                  </Button>
+                </>
+              ) : (
+                <Button class={styles.btn} round type="primary" onClick={() => handleNext()}>
+                  下一步 ({data.step + 1}/{steps.length})
+                </Button>
+              )}
+            </div>
+          </div>
+        </div>
+      )
+    }
+  },
+})

+ 15 - 0
src/subpages/colexiu/uses/use-query.ts

@@ -2,6 +2,20 @@ import qs from 'query-string'
 
 export type queryResult = {[key: string]: string | object | null | string[]}
 
+// UNIT_TEST("单元测试"),
+// PRACTICE("自主练习"),
+// EVALUATION("评测"),
+// LESSON_TRAINING("课后练习"),
+type IFeature = 'UNIT_TEST' | 'PRACTICE' | 'EVALUATION' | 'LESSON_TRAINING'
+const getFeature = (search: any): IFeature => {
+  if (search.feature){
+    return search.feature
+  }
+  if (search.lessonTrainingId){
+    return 'LESSON_TRAINING'
+  }
+  return 'PRACTICE'
+}
 export const useOriginSearch = (): queryResult => {
   const originSearchString = location.search || location.hash.replaceAll('#/', '')
   const parseSearch = qs.parse(originSearchString)
@@ -21,6 +35,7 @@ export const useOriginSearch = (): queryResult => {
       parseSearch.setting = JSON.parse(decodeURIComponent(parseSearch.setting as string) as string)
     } catch (error) {}
   }
+  parseSearch.feature = getFeature(parseSearch)
   return parseSearch
 }