Browse Source

添加节奏练习页面

lex 9 months ago
parent
commit
f1715aa30a

+ 11 - 0
package-lock.json

@@ -20,6 +20,7 @@
         "eventemitter3": "^5.0.1",
         "howler": "^2.2.3",
         "html2canvas": "^1.4.1",
+        "mobile-drag-drop": "^3.0.0-rc.0",
         "moveable": "^0.52.0",
         "naive-ui": "^2.34.4",
         "numeral": "^2.0.6",
@@ -6500,6 +6501,11 @@
         "node": ">=10"
       }
     },
+    "node_modules/mobile-drag-drop": {
+      "version": "3.0.0-rc.0",
+      "resolved": "https://registry.npmmirror.com/mobile-drag-drop/-/mobile-drag-drop-3.0.0-rc.0.tgz",
+      "integrity": "sha512-f8wIDTbBYLBW/+5sei1cqUE+StyDpf/LP+FRZELlVX6tmOOmELk84r3wh1z3woxCB9G5octhF06K5COvFjGgqg=="
+    },
     "node_modules/moveable": {
       "version": "0.52.0",
       "resolved": "https://registry.npmmirror.com/moveable/-/moveable-0.52.0.tgz",
@@ -14247,6 +14253,11 @@
       "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
       "dev": true
     },
+    "mobile-drag-drop": {
+      "version": "3.0.0-rc.0",
+      "resolved": "https://registry.npmmirror.com/mobile-drag-drop/-/mobile-drag-drop-3.0.0-rc.0.tgz",
+      "integrity": "sha512-f8wIDTbBYLBW/+5sei1cqUE+StyDpf/LP+FRZELlVX6tmOOmELk84r3wh1z3woxCB9G5octhF06K5COvFjGgqg=="
+    },
     "moveable": {
       "version": "0.52.0",
       "resolved": "https://registry.npmmirror.com/moveable/-/moveable-0.52.0.tgz",

+ 1 - 0
package.json

@@ -34,6 +34,7 @@
     "eventemitter3": "^5.0.1",
     "howler": "^2.2.3",
     "html2canvas": "^1.4.1",
+    "mobile-drag-drop": "^3.0.0-rc.0",
     "moveable": "^0.52.0",
     "naive-ui": "^2.34.4",
     "numeral": "^2.0.6",

+ 8 - 8
src/hooks/useDrag/index.ts

@@ -55,14 +55,14 @@ export default function useDrag(
     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'
-        };
+        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);

+ 16 - 0
src/main.ts

@@ -16,6 +16,22 @@ import { Lazyload, setToastDefaultOptions } from 'vant';
 import useErrorLog from './hooks/useErrorLog';
 setToastDefaultOptions({ duration: 3000 });
 
+import 'mobile-drag-drop/default.css';
+import { polyfill } from 'mobile-drag-drop';
+
+// 可选引入滚动行为的脚本
+import { scrollBehaviourDragImageTranslateOverride } from 'mobile-drag-drop/scroll-behaviour';
+
+// 可选的配置项
+const options = {
+  // 使用这个选项以应用滚动行为
+  dragImageTranslateOverride: scrollBehaviourDragImageTranslateOverride
+};
+
+// 初始化polyfill
+polyfill(options);
+
+
 // 获取token
 promisefiyPostMessage({ api: 'getToken' }).then((res: any) => {
   const content = res.content;

+ 44 - 14
src/views/tempo-practice/beat-tick.ts

@@ -1,7 +1,7 @@
-import { reactive } from 'vue';
+import { reactive, toRef, toRefs } from 'vue';
 import tockAndTick from './tockAndTick.json';
 import { Howl } from 'howler';
-import { initSelectScorePart, setting } from './setting';
+import { initSelectScorePart, initSelectScorePartModal, setting, setting_modal } from './setting';
 import { beatDesc } from './beat-desc';
 
 const beatData = reactive({
@@ -20,6 +20,8 @@ const beatData = reactive({
   show: false
 });
 
+let defaultSetting = {} as any
+
 const handlePlay = (i: number, source: any, timer: any) => {
   let payBeatTime = new Date().getTime();
   return new Promise(resolve => {
@@ -30,10 +32,14 @@ const handlePlay = (i: number, source: any, timer: any) => {
 
     let timeSppedEnum = 16.7;
     const proofTime = () => {
-      if (setting.playState !== 'play') {
+      if (defaultSetting.playState !== 'play') {
         return;
       }
       setTimeout(() => {
+        if (beatData.tickEnd) {
+          resolve(i);
+          return;
+        }
         const currentTime = new Date().getTime();
         // 两次定时任务时间间隔
         const diffTime = currentTime - payBeatTime;
@@ -70,19 +76,28 @@ const handlePlay = (i: number, source: any, timer: any) => {
   });
 };
 
-/** 开始节拍器 */
-export const handleStartBeat = async () => {
+/** 开始节拍器
+ * @param {boolean} 是否开启了设置
+ */
+export const handleStartBeat = async (settingStatus = false) => {
+  const tempSetting = settingStatus ? setting_modal : setting
+  defaultSetting = {
+    playState: tempSetting.playState,
+    speed: tempSetting.speed,
+    scorePart: tempSetting.scorePart
+  }
+
   beatData.show = true;
   beatData.tickEnd = false;
   beatData.index = 0;
-  beatData.beatLengthInMilliseconds = (60 / setting.speed) * 1000;
+  beatData.beatLengthInMilliseconds = (60 / defaultSetting.speed) * 1000;
   // let startTime = +new Date();
-  for (let i = 0; i < setting.scorePart.length; i++) {
+  for (let i = 0; i < defaultSetting.scorePart.length; i++) {
     if (beatData.tickEnd) return false;
-    for (let j = 0; j < setting.scorePart[i].length; j++) {
+    for (let j = 0; j < defaultSetting.scorePart[i].length; j++) {
       if (beatData.tickEnd) return false;
       // 提前结束, 直接放回false
-      const part = setting.scorePart[i][j];
+      const part = defaultSetting.scorePart[i][j];
       const params = {
         ...part,
         ...beatDesc[part.index]
@@ -127,14 +142,23 @@ export const handleStartBeat = async () => {
           type: attr.type
         });
         beatData.loopTime = time;
-        initSelectScorePart(i, j);
+        if (settingStatus) {
+          initSelectScorePartModal(i, j)
+        } else {
+          initSelectScorePart(i, j);
+        }
+
+        defaultSetting.playState = settingStatus ? setting_modal.playState : setting.playState
+        if (defaultSetting.playState !== 'play') {
+          hendleEndBeat(settingStatus)
+        }
       }
     }
   }
 
   // console.log(+new Date() - startTime);
   beatData.show = false;
-  handleStartBeat();
+  handleStartBeat(settingStatus);
   return true;
 };
 
@@ -152,8 +176,14 @@ export const handleInitBeat = (
   beatData.len = beat;
 };
 
-/** 节拍器暂停 */
-export const hendleEndBeat = () => {
+/** 节拍器暂停
+ * @param {boolean} 是否开启了设置
+ */
+export const hendleEndBeat = (settingStatus = false) => {
   beatData.tickEnd = true;
-  initSelectScorePart();
+  if (settingStatus) {
+    initSelectScorePartModal()
+  } else {
+    initSelectScorePart();
+  }
 };

+ 44 - 8
src/views/tempo-practice/index.module.less

@@ -139,6 +139,7 @@
 
 
     .beat {
+      transition: all .1s ease;
       border: 2px solid #fff;
       // width: 65px;
       // height: 86px;
@@ -167,10 +168,11 @@
     width: 118px;
     height: 156px;
     box-shadow: 0px 2px 16px 0px #76C3D2;
-    border-radius: 14px;
+    border-radius: 5px;
     border: 3px solid #fff;
     background: #FFFFFF;
     position: relative;
+    transition: all .1s ease;
 
     .direction {
       position: absolute;
@@ -204,6 +206,14 @@
       width: 96px;
     }
 
+    &.disabledChange {
+
+      &::before,
+      &::after {
+        display: none;
+      }
+    }
+
     &::before,
     &::after {
       content: '';
@@ -368,16 +378,43 @@
   &.leftShow {
     .beatSection {
       .beat {
-        width: 42px;
-        height: 56px;
+        width: 85px;
+        height: 105px;
+        transition: all .1s ease;
 
         img {
-          width: 34px;
+          width: 64px;
+        }
+      }
+
+      &.small {
+        &:nth-child(2n + 1) {
+          justify-content: flex-end;
+          padding-right: 4px;
+        }
+
+        &:nth-child(2n + 2) {
+          justify-content: flex-start;
+          padding-left: 4px;
+        }
+
+        .beat {
+          width: 42px !important;
+          height: 56px !important;
+          transition: all .1s ease;
+
+          img {
+            width: 28px;
+          }
         }
       }
     }
 
     .footer {
+      &>div {
+        margin: 0 7px;
+      }
+
       .play {
         width: 42px;
         height: 42px;
@@ -393,8 +430,7 @@
 }
 
 .containerRight {
-
-  width: 311px;
+  width: 281px;
   transition: all .2s ease;
 
   &.rightHide {
@@ -406,5 +442,5 @@
 
 .settingModalShow {
   height: 100% !important;
-  width: 311px !important;
-}
+  width: 281px !important;
+}

+ 149 - 78
src/views/tempo-practice/index.tsx

@@ -31,7 +31,9 @@ import {
   renderScore,
   setting,
   elementDirection,
-  setting_modal
+  setting_modal,
+  initSelectScorePartModal,
+  renderScoreModal
 } from './setting';
 import { handleStartTick, hendleEndTick } from './tick';
 import { handleStartBeat, hendleEndBeat } from './beat-tick';
@@ -89,7 +91,8 @@ export default defineComponent({
       playPos: (route.query.imagePos || 'left') as 'left' | 'right' // 数字课堂老师端上课 镜像字段
     });
     // 返回
-    const goback = () => {
+    const goback = (e: any) => {
+      e.stopPropagation();
       if (route.query.backBtnType === 'microapp') {
         // microapp 老师端应用里面打开单独处理返回逻辑
         window.parent.postMessage(
@@ -108,14 +111,35 @@ export default defineComponent({
       postMessage({ api: 'goBack' });
     };
 
+    /* 修改设置的某个值 */
+    const updateSettingValue = (key: string, value: any) => {
+      if (state.settingStatus) {
+        setting_modal[key] = value;
+      } else {
+        setting[key] = value;
+      }
+    };
+    /* 获取设置的某一个值 */
+    const getSettingValue = (key: string) => {
+      let value: any = null;
+      if (state.settingStatus) {
+        value = setting_modal[key];
+      } else {
+        value = setting[key];
+      }
+      return value;
+    };
+
     /** 播放切换 */
     const handlePlay = async () => {
-      if (setting.playState === 'pause') {
-        setting.playState = 'play';
-        if (setting.playType === 'beat') {
-          await handleStartTick();
+      const playState = getSettingValue('playState');
+      const playType = getSettingValue('playType');
+      if (playState === 'pause') {
+        updateSettingValue('playState', 'play');
+        if (playType === 'beat') {
+          await handleStartTick(state.settingStatus);
         } else {
-          await handleStartBeat();
+          await handleStartBeat(state.settingStatus);
         }
       } else {
         handleStop();
@@ -124,19 +148,23 @@ export default defineComponent({
     /** 播放类型 */
     const handlePlayType = () => {
       handleStop();
+      // 判断是否有设置
+
       if (setting.playType === 'beat') {
-        setting.playType = 'tempo';
+        updateSettingValue('playType', 'tempo');
       } else {
-        setting.playType = 'beat';
+        updateSettingValue('playType', 'beat');
       }
     };
 
     const handleStop = () => {
-      setting.playState = 'pause';
-      if (setting.playType === 'beat') {
-        hendleEndTick();
+      const playType = getSettingValue('playType');
+      updateSettingValue('playState', 'pause');
+
+      if (playType === 'beat') {
+        hendleEndTick(state.settingStatus);
       } else {
-        hendleEndBeat();
+        hendleEndBeat(state.settingStatus);
       }
     };
 
@@ -190,7 +218,7 @@ export default defineComponent({
         if (route.query.dataJson) {
           dataJson = JSON.parse(route.query.dataJson as any);
         }
-        console.log(dataJson, 'dataJson', props.dataJson);
+        // console.log(dataJson, 'dataJson', props.dataJson);
 
         setting.element = dataJson.element;
         setting.beat = dataJson.beat;
@@ -234,8 +262,7 @@ export default defineComponent({
         setting_modal[i] = JSON.parse(JSON.stringify(setting[i]));
       }
 
-      console.log(setting_modal, 'setting');
-      if (state.win === 'pc') {
+      if (state.win === 'pc' || state.platform === 'modal') {
         state.settingPcStatus = true;
       } else {
         state.settingStatus = true;
@@ -271,7 +298,7 @@ export default defineComponent({
       settingBoxDragData = useDrag(
         [`${settingBoxClass} .iconTitBoxMove`, `${settingBoxClass} .bom_drag`],
         settingBoxClass,
-        toRef(state, 'settingStatus'),
+        toRef(state, 'settingPcStatus'),
         stateData.user.data.id
       );
     }
@@ -280,6 +307,7 @@ export default defineComponent({
     return () => (
       <div
         onClick={() => {
+          state.settingStatus = false;
           window.parent.postMessage(
             {
               api: 'clickTempo'
@@ -310,11 +338,14 @@ export default defineComponent({
             <div class={styles.title}>
               <img src={icon_title} />
             </div>
-            {state.modeType !== 'courseware' && state.platform !== 'modal' ? (
+            {state.modeType !== 'courseware' &&
+            state.platform !== 'modal' &&
+            !state.settingStatus ? (
               <div
                 class={styles.back}
                 style={{ cursor: 'pointer' }}
-                onClick={() => {
+                onClick={(e: any) => {
+                  e.stopPropagation();
                   onOpenSetting();
                 }}>
                 <img src={icon_setting} />
@@ -326,50 +357,87 @@ export default defineComponent({
 
           <div class={styles.conCon}>
             <div class={styles.container}>
-              {setting.scorePart?.map((item: any, i: number) => (
+              {getSettingValue('scorePart')?.map((item: any, i: number) => (
                 <div
                   class={[
                     styles.beatSection,
-                    setting.scorePart.length >= 2 &&
+                    getSettingValue('scorePart').length >= 2 &&
                       item.length !== 1 &&
                       styles.small
                   ]}>
                   {item.map((child: any, jIndex: number) => (
                     <div
-                      class={[styles.beat, child.selected ? styles.active : '']}
+                      class={[
+                        styles.beat,
+                        child.selected ? styles.active : '',
+                        state.settingStatus && styles.disabledChange
+                      ]}
+                      // draggable={true}
+                      onDragenter={(e: any) => {
+                        e.preventDefault();
+                      }}
+                      onDragover={(e: any) => {
+                        e.preventDefault();
+                      }}
+                      onDrop={(e: any) => {
+                        let dropItem = e.dataTransfer.getData('text');
+                        dropItem = dropItem ? JSON.parse(dropItem) : {};
+                        // 判断是否有数据
+                        if (dropItem.url) {
+                          handleStop();
+                          setting_modal.scorePart.forEach(
+                            (part: Array<any>, ci: number) => {
+                              part.forEach((child: any, cj: number) => {
+                                if (i === ci && jIndex === cj) {
+                                  child.url = dropItem.url;
+                                }
+                              });
+                            }
+                          );
+                        }
+                      }}
                       onClick={(e: any) => {
                         e.stopPropagation();
+                        // 编辑时可以操作
+                        if (state.settingStatus) {
+                          handleStop();
+                          initSelectScorePartModal();
+                          child.selected = true;
+                        }
                       }}>
-                      <div class={styles.direction}>
-                        <div
-                          class={styles.up}
-                          style={{ cursor: 'pointer' }}
-                          onClick={() => {
-                            if (setting.playState === 'play') return;
-                            if (setting.tempo.length <= 1) {
-                              showToast('无法切换,请选择至少2种节奏型');
-                              return;
-                            }
-                            // const obj = randomScoreElement(child.index);
-                            const obj = elementDirection('up', child.index);
-                            child.index = obj.index;
-                            child.url = obj.url;
-                          }}></div>
-                        <div
-                          class={styles.down}
-                          style={{ cursor: 'pointer' }}
-                          onClick={() => {
-                            if (setting.playState === 'play') return;
-                            if (setting.tempo.length <= 1) {
-                              showToast('无法切换,请选择至少2种节奏型');
-                              return;
-                            }
-                            // const obj = randomScoreElement(child.index);
-                            const obj = elementDirection('down', child.index);
-                            child.index = obj.index;
-                            child.url = obj.url;
-                          }}></div>
-                      </div>
+                      {/* 编辑时不可上下切换 */}
+                      {!state.settingStatus && (
+                        <div class={styles.direction}>
+                          <div
+                            class={styles.up}
+                            style={{ cursor: 'pointer' }}
+                            onClick={() => {
+                              if (setting.playState === 'play') return;
+                              if (setting.tempo.length <= 1) {
+                                showToast('无法切换,请选择至少2种节奏型');
+                                return;
+                              }
+                              // const obj = randomScoreElement(child.index);
+                              const obj = elementDirection('up', child.index);
+                              child.index = obj.index;
+                              child.url = obj.url;
+                            }}></div>
+                          <div
+                            class={styles.down}
+                            style={{ cursor: 'pointer' }}
+                            onClick={() => {
+                              if (setting.playState === 'play') return;
+                              if (setting.tempo.length <= 1) {
+                                showToast('无法切换,请选择至少2种节奏型');
+                                return;
+                              }
+                              // const obj = randomScoreElement(child.index);
+                              const obj = elementDirection('down', child.index);
+                              child.index = obj.index;
+                              child.url = obj.url;
+                            }}></div>
+                        </div>
+                      )}
                       <div class={styles.imgSection}>
                         <img src={getImage(child.url)} />
                       </div>
@@ -397,7 +465,7 @@ export default defineComponent({
                   </div>
                 )}
                 <div class={styles.play} onClick={handlePlay}>
-                  {setting.playState === 'pause' ? (
+                  {getSettingValue('playState') === 'pause' ? (
                     <img src={iconPause} />
                   ) : (
                     <img src={iconPlay} />
@@ -417,7 +485,7 @@ export default defineComponent({
             )}
             {/* 播放类型 */}
             <div class={styles.playType} onClick={handlePlayType}>
-              {setting.playType === 'beat' ? (
+              {getSettingValue('playType') === 'beat' ? (
                 <img src={beat} />
               ) : (
                 <img src={tempo} />
@@ -427,7 +495,11 @@ export default defineComponent({
             <div
               class={styles.randomTempo}
               onClick={() => {
-                renderScore();
+                if (state.settingStatus) {
+                  renderScoreModal();
+                } else {
+                  renderScore();
+                }
                 handleStop();
               }}>
               <img src={randDom} />
@@ -438,14 +510,15 @@ export default defineComponent({
                 src={iconPlus}
                 class={styles.speedPlus}
                 onClick={() => {
-                  if (setting.speed <= 40) return;
-                  setting.speed -= 1;
+                  const speed = getSettingValue('speed');
+                  if (speed <= 40) return;
+                  updateSettingValue('speed', speed - 1);
                   handleStop();
 
                   state.speedList.forEach((item: any) => {
-                    if (item.value === setting.speed) {
+                    if (item.value === getSettingValue('speed')) {
                       item.color = '#1CACF1';
-                      setting.speed = setting.speed;
+                      updateSettingValue('speed', getSettingValue('speed'));
                     } else {
                       item.color = '#060606';
                     }
@@ -457,11 +530,12 @@ export default defineComponent({
                 class={styles.popupContainer}
                 actions={state.speedList}
                 onSelect={(val: any) => {
-                  if (val.value === setting.speed) return;
+                  const speed = getSettingValue('speed');
+                  if (val.value === speed) return;
                   state.speedList.forEach((item: any) => {
                     if (item.value === val.value) {
                       item.color = '#1CACF1';
-                      setting.speed = val.value;
+                      updateSettingValue('speed', val.value);
                     } else {
                       item.color = '#060606';
                     }
@@ -470,7 +544,9 @@ export default defineComponent({
                 }}>
                 {{
                   reference: () => (
-                    <div class={styles.speedNum}>{setting.speed}</div>
+                    <div class={styles.speedNum}>
+                      {getSettingValue('speed')}
+                    </div>
                   )
                 }}
               </Popover>
@@ -479,14 +555,16 @@ export default defineComponent({
                 src={iconAdd}
                 class={styles.speedAdd}
                 onClick={() => {
-                  if (setting.speed >= 200) return;
-                  setting.speed += 1;
+                  const speed = getSettingValue('speed');
+
+                  if (speed >= 200) return;
+                  updateSettingValue('speed', speed + 1);
                   handleStop();
 
                   state.speedList.forEach((item: any) => {
-                    if (item.value === setting.speed) {
+                    if (item.value === getSettingValue('speed')) {
                       item.color = '#1CACF1';
-                      setting.speed = setting.speed;
+                      updateSettingValue('speed', getSettingValue('speed'));
                     } else {
                       item.color = '#060606';
                     }
@@ -497,7 +575,7 @@ export default defineComponent({
             {/* 播放 */}
             {state.playPos === 'right' && (
               <div class={styles.play} onClick={handlePlay}>
-                {setting.playState === 'pause' ? (
+                {getSettingValue('playState') === 'pause' ? (
                   <img src={iconPause} />
                 ) : (
                   <img src={iconPlay} />
@@ -520,12 +598,12 @@ export default defineComponent({
           class={[
             styles.containerRight,
             state.settingStatus ? '' : styles.rightHide
-          ]}>
+          ]}
+          onClick={(e: any) => {
+            e.stopPropagation();
+          }}>
           <SettingModal
             class={styles.settingModalShow}
-            // onGuideDone={setGuidanceShow}
-            // showGuide={guidanceShow.value}
-            dataJson={state.dataJson}
             onClose={() => (state.settingStatus = false)}
           />
         </div>
@@ -539,15 +617,8 @@ export default defineComponent({
           <SettingPcModal
             onGuideDone={setGuidanceShow}
             showGuide={guidanceShow.value}
-            // dataJson={state.dataJson}
             onClose={() => (state.settingPcStatus = false)}
           />
-          {/* <SettingModal
-            onGuideDone={setGuidanceShow}
-            showGuide={guidanceShow.value}
-            dataJson={state.dataJson}
-            onClose={() => (state.settingStatus = false)}
-          /> */}
         </Popup>
       </div>
     );

+ 59 - 8
src/views/tempo-practice/setting-modal/index.module.less

@@ -10,12 +10,17 @@
     width: 750px;
     height: 536px;
 
-    .paramContent {
-      gap: 8px;
+    .settingContent {
+      padding: 0 20px;
+    }
+
+    .beatContent {
+      gap: 8px 0;
 
       .btn {
         width: 78px;
         height: 31px;
+        margin: 0 4px;
       }
 
       img {
@@ -57,10 +62,44 @@
       cursor: pointer;
     }
   }
+
+
+  &.modalS {
+    .beatContent {
+      gap: 8px 0;
+
+      .btn {
+        margin: 0 4px;
+      }
+    }
+
+    .settingContent {
+      padding: 0 10px;
+
+      .iArrow {
+        width: 12px;
+        height: 12px;
+      }
+
+      .collapseContainer {
+        :global {
+          .van-collapse-item__title {
+            font-size: 12px;
+            padding: 4px;
+          }
+
+          .van-collapse-item__content {
+            padding: 10px 3px 0;
+          }
+        }
+
+      }
+    }
+  }
 }
 
 .settingContent {
-  padding: 0 20px 0 20px;
+  padding: 0 12px 55px 7px;
   overflow-y: auto;
   height: 100%;
   position: relative;
@@ -87,6 +126,11 @@
   }
 
   .collapseContainer {
+
+    &.paddingBottom {
+      margin-bottom: 8px;
+    }
+
     :global {
       .van-collapse-item__title {
         display: flex;
@@ -141,7 +185,7 @@
     background: #F5F6F7;
     border: none;
     padding: 0;
-    margin: 0 6px;
+    margin: 0 4px;
 
     &.active {
       background: #19AEFF;
@@ -149,6 +193,12 @@
     }
   }
 
+  &.beatContent {
+    .btn {
+      margin: 4px 6px;
+    }
+  }
+
   &.tempo {
     // gap: 8px 8px;
     padding-bottom: 0;
@@ -156,6 +206,7 @@
 
     &>div {
       background: #F5F6F7;
+      margin: 4px;
     }
   }
 
@@ -164,13 +215,13 @@
   }
 
   img {
-    width: 46px;
-    height: 46px;
+    width: 44px;
+    height: 44px;
     background: #F5F6F7;
     border-radius: 4px;
-    margin: 4px;
     cursor: pointer;
   }
+
 }
 
 .btnGroup {
@@ -178,7 +229,7 @@
   bottom: 0;
   left: 0;
   right: 0;
-  z-index: 9;
+  z-index: 12;
   display: flex;
   align-items: center;
   justify-content: center;

+ 13 - 5
src/views/tempo-practice/setting-modal/index.tsx

@@ -49,6 +49,7 @@ export default defineComponent({
     };
     const state = reactive({
       win: route.query.win,
+      platform: route.query.platform,
       activeNames: ['base'] as any, // 折叠面板
       element:
         tempDeepClone(setting_modal.element) ||
@@ -160,6 +161,7 @@ export default defineComponent({
           props.class,
           styles.settingContainer,
           state.win === 'pc' ? styles.pcS : '',
+          state.platform === 'modal' && state.win !== 'pc' ? styles.modalS : '',
           'settingContainer_pc'
         ]}>
         {/* <div class={styles.title}></div> */}
@@ -184,7 +186,10 @@ export default defineComponent({
               name="base"
               border={false}
               isLink={false}
-              class={styles.collapseContainer}>
+              class={[
+                styles.collapseContainer,
+                state.activeNames.includes('base') ? '' : styles.paddingBottom
+              ]}>
               {{
                 icon: () => (
                   <img
@@ -216,7 +221,7 @@ export default defineComponent({
                       ))}
                     </div>
                     <div class={styles.parmaTitle}>拍号</div>
-                    <div class={styles.paramContent}>
+                    <div class={[styles.paramContent, styles.beatContent]}>
                       {Object.keys(beatList).map((item: any) => (
                         <Button
                           round
@@ -283,6 +288,7 @@ export default defineComponent({
                         <div
                           draggable={true}
                           onDragstart={(e: any) => {
+                            console.log('1111');
                             e.dataTransfer.setData(
                               'text',
                               JSON.stringify({
@@ -309,9 +315,11 @@ export default defineComponent({
           </Collapse>
           {/* <div class={styles.settingParams}></div> */}
         </div>
-        {/* <div class={styles.btnGroup}>
-          <Button class={styles.btnSubmit} onClick={onSubmit}></Button>
-        </div> */}
+        {!state.win && !state.platform && (
+          <div class={styles.btnGroup}>
+            <Button class={styles.btnSubmit} onClick={onSubmit}></Button>
+          </div>
+        )}
       </div>
     );
   }

+ 54 - 2
src/views/tempo-practice/setting-pc-modal/index.module.less

@@ -9,6 +9,58 @@
     height: 100% !important;
     width: 354px !important;
   }
+
+  &.modal {
+    height: 300px;
+
+    .conCon {
+      width: 402px;
+    }
+
+    .beatSection {
+      .beat {
+        border-radius: 4px;
+        width: 65px;
+        height: 85px;
+
+        img {
+          width: 48px;
+        }
+      }
+
+      &.small {
+        &:nth-child(2n + 1) {
+          justify-content: flex-end;
+          padding-right: 6px;
+        }
+
+        &:nth-child(2n + 2) {
+          justify-content: flex-start;
+          padding-left: 6px;
+        }
+
+        .beat {
+          width: 35px !important;
+          height: 45px !important;
+          margin: 0 4px;
+
+          img {
+            width: 25px;
+          }
+        }
+      }
+    }
+
+    .settingModalShow {
+      width: 264px !important;
+    }
+
+    .btnSubmit {
+      width: 102px;
+      height: 32px;
+      margin-left: -51px;
+    }
+  }
 }
 
 .conCon {
@@ -39,8 +91,8 @@
 
   .beatSection {
     .beat {
-      width: 206px;
-      height: 284px;
+      width: 118px;
+      height: 156px;
 
       img {
         width: 140px;

+ 7 - 37
src/views/tempo-practice/setting-pc-modal/index.tsx

@@ -1,6 +1,5 @@
 import { defineComponent, reactive, ref } from 'vue';
 import styles from './index.module.less';
-import Draggable from 'vuedraggable';
 import SettingModal from '../setting-modal';
 import Dragbom from '@/hooks/useDrag/dragbom';
 import { useRoute } from 'vue-router';
@@ -22,47 +21,19 @@ export default defineComponent({
     const route = useRoute();
     const settingModalRef = ref();
     const state = reactive({
+      platform: route.query.platform,
+      win: route.query.win,
       activeBeat: ''
     });
 
     return () => (
-      <div class={styles.settingPcModal}>
+      <div
+        class={[
+          styles.settingPcModal,
+          state.platform === 'modal' && state.win !== 'pc' ? styles.modal : ''
+        ]}>
         <div class={styles.conCon}>
           <div class={styles.container}>
-            {/* <Draggable
-              v-model:modelValue={setting_modal.scorePart}
-              itemKey="itemIndex">
-              {{
-                item: (element: any) => {
-                  const item = element.element;
-                  return (
-                    <div
-                      class={[
-                        styles.beatSection,
-                        setting_modal.scorePart.length >= 2 &&
-                          item.length !== 1 &&
-                          styles.small
-                      ]}>
-                      {item.map((child: any, jIndex: number) => (
-                        <div
-                          class={[
-                            styles.beat,
-                            child.selected ? styles.active : ''
-                          ]}
-                          data-id={'beat-'}
-                          onClick={(e: any) => {
-                            e.stopPropagation();
-                          }}>
-                          <div class={styles.imgSection}>
-                            <img src={getImage(child.url)} />
-                          </div>
-                        </div>
-                      ))}
-                    </div>
-                  );
-                }
-              }}
-            </Draggable> */}
             {setting_modal.scorePart?.map((item: any, i: number) => (
               <div
                 class={[
@@ -74,7 +45,6 @@ export default defineComponent({
                 {item.map((child: any, jIndex: number) => (
                   <div
                     class={[styles.beat, child.selected ? styles.active : '']}
-                    data-id={`beat-${i}-${jIndex}`}
                     draggable={false}
                     onDragenter={(e: any) => {
                       e.preventDefault();

+ 4 - 4
src/views/tempo-practice/setting.ts

@@ -69,7 +69,7 @@ for (let i = 15; i <= 31; i++) {
   tempNum2.push(i)
 }
 export const tempo8 = temp2;
-export const tempo8Num = tempo8
+export const tempo8Num = tempNum2
 
 /** 随机生成元素 */
 export const randomScoreElement = (element?: string) => {
@@ -201,7 +201,7 @@ export const initSelectScorePart = (i?: number, j?: number) => {
 
 
 
-/** 随机生成元素 */
+/** 随机生成元素  设置中 */
 export const randomScoreElementModal = (element?: string) => {
   // const tempoList = setting.tempo;
   let tempoList = tempo4Num || [] as any
@@ -237,7 +237,7 @@ export const randomScoreElementModal = (element?: string) => {
     }
   }
 };
-/*** 生成谱面 */
+/*** 生成谱面  设置中 */
 export const renderScoreModal = () => {
   const barLine = Number(setting_modal.barLine);
   const beatA = setting_modal.beat.split('-').map(i => Number(i));
@@ -270,7 +270,7 @@ export const renderScoreModal = () => {
   initSelectScorePartModal();
 };
 
-/** 初始化选中状态 */
+/** 初始化选中状态 设置中 */
 export const initSelectScorePartModal = (i?: number, j?: number) => {
   setting_modal.scorePart.forEach((part: Array<any>) => {
     part.forEach((item: any) => {

+ 47 - 14
src/views/tempo-practice/tick.ts

@@ -1,7 +1,7 @@
 import { reactive } from 'vue';
 import tockAndTick from './tockAndTick.json';
 import { Howl } from 'howler';
-import { initSelectScorePart, setting } from './setting';
+import { initSelectScorePart, initSelectScorePartModal, setting, setting_modal } from './setting';
 import { beatDesc } from './beat-desc';
 
 const tickData = reactive({
@@ -17,6 +17,12 @@ const tickData = reactive({
   index: 0,
   show: false
 });
+// console.log(setting, 'setting')
+let defaultSetting = {
+  // playState: setting.playState,
+  // speed: setting.speed,
+  // scorePart: setting.scorePart
+} as any
 
 let diffTime = 0; // 每一次节拍时间差
 const handlePlay = (i: number, source: any) => {
@@ -28,10 +34,14 @@ const handlePlay = (i: number, source: any) => {
     }
     let timeSppedEnum = 16.7;
     const proofTime = () => {
-      if (setting.playState !== 'play') {
+      if (defaultSetting.playState !== 'play') {
         return;
       }
       setTimeout(() => {
+        if (tickData.tickEnd) {
+          resolve(i);
+          return;
+        }
         const currentTime = new Date().getTime();
         // 两次定时任务时间间隔
         const diffTime = currentTime - payBeatTime;
@@ -84,8 +94,17 @@ export const handleInitTick = (
   tickData.afterBeat = afterBeat;
 };
 
-/** 开始节拍器 */
-export const handleStartTick = async () => {
+/** 开始节拍器
+ * @param {boolean} 是否开启了设置
+*/
+export const handleStartTick = async (settingStatus = false) => {
+  const tempSetting = settingStatus ? setting_modal : setting
+  defaultSetting = {
+    playState: tempSetting.playState,
+    speed: tempSetting.speed,
+    scorePart: tempSetting.scorePart
+  }
+
   tickData.show = true;
   tickData.tickEnd = false;
   if (tickData.state !== 'ok') {
@@ -98,15 +117,15 @@ export const handleStartTick = async () => {
     tickData.state = 'ok';
   }
   tickData.index = 0;
-  tickData.beatLengthInMilliseconds = (60 / setting.speed) * 1000;
+  tickData.beatLengthInMilliseconds = (60 / defaultSetting.speed) * 1000;
 
   if (tickData.afterBeat === 8) {
     tickData.beatLengthInMilliseconds = tickData.beatLengthInMilliseconds * 0.5;
   }
 
-  for (let i = 0; i < setting.scorePart.length; i++) {
+  for (let i = 0; i < defaultSetting.scorePart.length; i++) {
     if (tickData.tickEnd) return false;
-    const temp = setting.scorePart[i];
+    const temp = defaultSetting.scorePart[i];
     let len = temp.length;
     if (tickData.afterBeat === 8) {
       len = tickData.len;
@@ -119,22 +138,36 @@ export const handleStartTick = async () => {
         j === 0 ? tickData.source1 : j === len ? null : tickData.source2;
 
       await handlePlay(j, source);
-      if (tickData.afterBeat === 8) {
-        initSelectScorePart(i, Math.floor((j <= 0 ? 1 : j) / 3));
+
+      const tempJ = tickData.afterBeat === 8 ? Math.floor((j <= 0 ? 1 : j) / 3) : j
+      if (settingStatus) {
+        initSelectScorePartModal(i, tempJ)
       } else {
-        initSelectScorePart(i, j);
+        initSelectScorePart(i, tempJ);
+      }
+
+      defaultSetting.playState = settingStatus ? setting_modal.playState : setting.playState
+      if (defaultSetting.playState !== 'play') {
+        hendleEndTick(settingStatus)
       }
     }
   }
 
   // console.log(+new Date() - startTime);
   tickData.show = false;
-  handleStartTick();
+  handleStartTick(settingStatus);
   return true;
 };
 
-/** 节拍器暂停 */
-export const hendleEndTick = () => {
+/** 节拍器暂停
+ * @param {boolean} 是否开启了设置
+ */
+export const hendleEndTick = (settingStatus = false) => {
   tickData.tickEnd = true;
-  initSelectScorePart();
+  // 添加延期是因为播放时定时任务播放最多有16.7毫秒的延迟
+  if (settingStatus) {
+    initSelectScorePartModal()
+  } else {
+    initSelectScorePart();
+  }
 };