Explorar o código

添加pc端节奏练习

lex hai 1 ano
pai
achega
cc215267c3

+ 2 - 2
src/helpers/deep-clone.ts

@@ -3,8 +3,8 @@ const deepClone = (obj: any) => {
   const clone = Object.assign({}, obj);
   Object.keys(clone).forEach(
     key =>
-      (clone[key] =
-        typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key])
+    (clone[key] =
+      typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key])
   );
   if (Array.isArray(obj)) {
     clone.length = obj.length;

+ 21 - 0
src/styles/index.less

@@ -10,6 +10,27 @@
   --van-dialog-message-font-size: 16px;
 }
 
+img {
+  /* -webkit-touch-callout: none; */
+  -moz-user-select: none;
+  /* 火狐浏览器 */
+  -webkit-user-drag: none;
+  /* 谷歌、Safari和Opera浏览器 */
+  -webkit-user-select: none;
+  /* 谷歌、Safari和Opera浏览器 */
+  -ms-user-select: none;
+  /* IE10+浏览器 */
+  user-select: none;
+  /* 通用 */
+  -webkit-touch-callout: none;
+}
+
+body {
+  user-select: none;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+}
+
 // 默认输入框光标颜色
 input,
 textarea {

BIN=BIN
src/views/tempo-practice/images/pc_bg.png


BIN=BIN
src/views/tempo-practice/images/setting-arrow-active.png


BIN=BIN
src/views/tempo-practice/images/setting-arrow-default.png


+ 61 - 33
src/views/tempo-practice/index.module.less

@@ -6,15 +6,18 @@
   height: 100vh;
   overflow: hidden;
   display: flex;
-  flex-direction: column;
+  // flex-direction: column;
   background: url("./images/bg.png") no-repeat center center / cover;
 
-  display: flex;
+  .containerLeft {
+    height: 100%;
+  }
 
   &.modal {
-    .head{
+    .head {
       padding: 0 23px 8px 23px;
     }
+
     .iconBack {
       opacity: 0;
       pointer-events: none;
@@ -31,28 +34,6 @@
       justify-content: center;
     }
   }
-
-  // &.dpi3 {
-  //   .beatSection {
-
-  //     .beat {
-  //       width: 102px;
-  //       height: 136px;
-  //     }
-
-  //     &.small {
-  //       width: 50%;
-
-  //       // margin: 0 16px;
-  //       &.dpi3 {
-  //         .beat {
-  //           width: 60px;
-  //           height: 72px;
-  //         }
-  //       }
-  //     }
-  //   }
-  // }
 }
 
 .conCon {
@@ -62,7 +43,6 @@
 }
 
 .pc {
-
   .container {
     max-width: 1200px;
     gap: 30px 0;
@@ -78,16 +58,11 @@
       }
     }
 
-
-
     &.small {
       .beat {
         width: 139px !important;
         height: 191px !important;
 
-        // width: 65px;
-        //   height: 86px;
-
         img {
           width: 108px;
         }
@@ -374,9 +349,62 @@
     }
   }
 }
-:global{
-  .settingBoxClass_drag .settingContainer_pc{
+
+:global {
+  .settingBoxClass_drag .settingContainer_pc {
     border-radius: 16px;
     height: 50vh !important;
   }
 }
+
+
+.containerLeft {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  transition: all .2s ease;
+
+
+  &.leftShow {
+    .beatSection {
+      .beat {
+        width: 42px;
+        height: 56px;
+
+        img {
+          width: 34px;
+        }
+      }
+    }
+
+    .footer {
+      .play {
+        width: 42px;
+        height: 42px;
+      }
+
+      .playType {
+        width: 175px;
+        height: 39px;
+      }
+    }
+  }
+
+}
+
+.containerRight {
+
+  width: 311px;
+  transition: all .2s ease;
+
+  &.rightHide {
+    transform: translateX(100%);
+    width: 0;
+    transition: all .2s ease;
+  }
+}
+
+.settingModalShow {
+  height: 100% !important;
+  width: 311px !important;
+}

+ 255 - 205
src/views/tempo-practice/index.tsx

@@ -30,7 +30,8 @@ import {
   randomScoreElement,
   renderScore,
   setting,
-  elementDirection
+  elementDirection,
+  setting_modal
 } from './setting';
 import { handleStartTick, hendleEndTick } from './tick';
 import { handleStartBeat, hendleEndBeat } from './beat-tick';
@@ -39,6 +40,7 @@ import { useRoute } from 'vue-router';
 import useDrag from '@/hooks/useDrag';
 import useDragGuidance from '@/hooks/useDrag/useDragGuidance';
 import { state as stateData } from '@/state';
+import SettingPcModal from './setting-pc-modal';
 
 export default defineComponent({
   name: 'tempo-practice',
@@ -63,6 +65,7 @@ export default defineComponent({
       platform: route.query.platform,
       win: route.query.win,
       settingStatus: false,
+      settingPcStatus: false,
       speedList: [
         { text: '40', value: 40, color: '#060606' },
         { text: '50', value: 50, color: '#060606' },
@@ -87,7 +90,8 @@ export default defineComponent({
     });
     // 返回
     const goback = () => {
-      if (route.query.backBtnType === 'microapp') { // microapp 老师端应用里面打开单独处理返回逻辑
+      if (route.query.backBtnType === 'microapp') {
+        // microapp 老师端应用里面打开单独处理返回逻辑
         window.parent.postMessage(
           {
             api: 'iframe_exit'
@@ -196,6 +200,14 @@ export default defineComponent({
         setting.playType = dataJson.playType;
         setting.speed = dataJson.speed;
 
+        setting_modal.element = dataJson.element;
+        setting_modal.beat = dataJson.beat;
+        setting_modal.barLine = dataJson.barLine;
+        setting_modal.tempo = dataJson.tempo;
+        setting_modal.scorePart = dataJson.scorePart;
+        setting_modal.playType = dataJson.playType;
+        setting_modal.speed = dataJson.speed;
+
         state.dataJson = dataJson;
       } catch {
         //
@@ -214,6 +226,22 @@ export default defineComponent({
     //   }
     // );
 
+    /** 打开设置 */
+    const onOpenSetting = () => {
+      handleStop();
+      // 初始化设置的数据
+      for (let i in setting) {
+        setting_modal[i] = JSON.parse(JSON.stringify(setting[i]));
+      }
+
+      console.log(setting_modal, 'setting');
+      if (state.win === 'pc') {
+        state.settingPcStatus = true;
+      } else {
+        state.settingStatus = true;
+      }
+    };
+
     onMounted(() => {
       if (route.query.modeType) {
         state.modeType = route.query.modeType;
@@ -265,239 +293,261 @@ export default defineComponent({
           state.platform === 'modal' ? styles.modal : '',
           state.modeType === 'courseware' ? styles.courseware : ''
         ]}>
-        <div class={styles.head}>
-          {state.modeType !== 'courseware' && (
-            <div
-              class={[styles.back, styles.iconBack]}
-              onClick={goback}
-              style={{ cursor: 'pointer' }}>
-              <img src={icon_back} />
-            </div>
-          )}
-          <div class={styles.title}>
-            <img src={icon_title} />
-          </div>
-          {state.modeType !== 'courseware' && state.platform !== 'modal' ? (
-            <div
-              class={styles.back}
-              style={{ cursor: 'pointer' }}
-              onClick={() => {
-                handleStop();
-                state.settingStatus = true;
-              }}>
-              <img src={icon_setting} />
+        <div
+          class={[
+            styles.containerLeft,
+            state.settingStatus ? styles.leftShow : ''
+          ]}>
+          <div class={styles.head}>
+            {state.modeType !== 'courseware' && (
+              <div
+                class={[styles.back, styles.iconBack]}
+                onClick={goback}
+                style={{ cursor: 'pointer' }}>
+                <img src={icon_back} />
+              </div>
+            )}
+            <div class={styles.title}>
+              <img src={icon_title} />
             </div>
-          ) : (
-            <div class={styles.back}></div>
-          )}
-        </div>
-
-        <div class={styles.conCon}>
-          <div class={styles.container}>
-            {setting.scorePart?.map((item: any, i: number) => (
+            {state.modeType !== 'courseware' && state.platform !== 'modal' ? (
               <div
-                class={[
-                  styles.beatSection,
-                  setting.scorePart.length >= 2 &&
-                    item.length !== 1 &&
-                    styles.small
-                ]}>
-                {item.map((child: any, jIndex: number) => (
-                  <div
-                    class={[styles.beat, child.selected ? styles.active : '']}
-                    onClick={(e: any) => {
-                      e.stopPropagation();
-                    }}>
-                    <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>
-                  </div>
-                ))}
+                class={styles.back}
+                style={{ cursor: 'pointer' }}
+                onClick={() => {
+                  onOpenSetting();
+                }}>
+                <img src={icon_setting} />
               </div>
-            ))}
+            ) : (
+              <div class={styles.back}></div>
+            )}
           </div>
-        </div>
 
-        <div
-          class={styles.footer}
-          onClick={(e: any) => {
-            e.stopPropagation();
-          }}>
-          {/* 播放 */}
-          {state.playPos === 'left' && (
-            <>
-              {route.query.back === 'show' && (
+          <div class={styles.conCon}>
+            <div class={styles.container}>
+              {setting.scorePart?.map((item: any, i: number) => (
                 <div
-                  class={[styles.play]}
-                  onClick={goback}
-                  style={{ cursor: 'pointer' }}>
-                  <img src={icon_back1} />
+                  class={[
+                    styles.beatSection,
+                    setting.scorePart.length >= 2 &&
+                      item.length !== 1 &&
+                      styles.small
+                  ]}>
+                  {item.map((child: any, jIndex: number) => (
+                    <div
+                      class={[styles.beat, child.selected ? styles.active : '']}
+                      onClick={(e: any) => {
+                        e.stopPropagation();
+                      }}>
+                      <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>
+                    </div>
+                  ))}
                 </div>
-              )}
-              <div class={styles.play} onClick={handlePlay}>
-                {setting.playState === 'pause' ? (
-                  <img src={iconPause} />
-                ) : (
-                  <img src={iconPlay} />
-                )}
-              </div>
-            </>
-          )}
-          {/* 老师端来的时候的设置按钮 */}
-          {state.platform === 'modal' && state.playPos === 'right' && (
-            <div
-              class={styles.setting}
-              onClick={() => {
-                handleStop();
-                state.settingStatus = true;
-              }}>
-              <img src={setImg} />
+              ))}
             </div>
-          )}
-          {/* 播放类型 */}
-          <div class={styles.playType} onClick={handlePlayType}>
-            {setting.playType === 'beat' ? (
-              <img src={beat} />
-            ) : (
-              <img src={tempo} />
-            )}
           </div>
-          {/* 随机生成 */}
+
           <div
-            class={styles.randomTempo}
-            onClick={() => {
-              renderScore();
-              handleStop();
+            class={styles.footer}
+            onClick={(e: any) => {
+              e.stopPropagation();
             }}>
-            <img src={randDom} />
-          </div>
-          {/* 速度 */}
-          <div class={styles.speedChange}>
-            <img
-              src={iconPlus}
-              class={styles.speedPlus}
-              onClick={() => {
-                if (setting.speed <= 40) return;
-                setting.speed -= 1;
-                handleStop();
-
-                state.speedList.forEach((item: any) => {
-                  if (item.value === setting.speed) {
-                    item.color = '#1CACF1';
-                    setting.speed = setting.speed;
-                  } else {
-                    item.color = '#060606';
-                  }
-                });
-              }}
-            />
-            <Popover
-              placement="top"
-              class={styles.popupContainer}
-              actions={state.speedList}
-              onSelect={(val: any) => {
-                if (val.value === setting.speed) return;
-                state.speedList.forEach((item: any) => {
-                  if (item.value === val.value) {
-                    item.color = '#1CACF1';
-                    setting.speed = val.value;
-                  } else {
-                    item.color = '#060606';
-                  }
-                });
-                handleStop();
-              }}>
-              {{
-                reference: () => (
-                  <div class={styles.speedNum}>{setting.speed}</div>
-                )
-              }}
-            </Popover>
-
-            <img
-              src={iconAdd}
-              class={styles.speedAdd}
-              onClick={() => {
-                if (setting.speed >= 200) return;
-                setting.speed += 1;
-                handleStop();
-
-                state.speedList.forEach((item: any) => {
-                  if (item.value === setting.speed) {
-                    item.color = '#1CACF1';
-                    setting.speed = setting.speed;
-                  } else {
-                    item.color = '#060606';
-                  }
-                });
-              }}
-            />
-          </div>
-          {/* 播放 */}
-          {state.playPos === 'right' && (
-            <div class={styles.play} onClick={handlePlay}>
-              {setting.playState === 'pause' ? (
-                <img src={iconPause} />
+            {/* 播放 */}
+            {state.playPos === 'left' && (
+              <>
+                {route.query.back === 'show' && (
+                  <div
+                    class={[styles.play]}
+                    onClick={goback}
+                    style={{ cursor: 'pointer' }}>
+                    <img src={icon_back1} />
+                  </div>
+                )}
+                <div class={styles.play} onClick={handlePlay}>
+                  {setting.playState === 'pause' ? (
+                    <img src={iconPause} />
+                  ) : (
+                    <img src={iconPlay} />
+                  )}
+                </div>
+              </>
+            )}
+            {/* 老师端来的时候的设置按钮 */}
+            {state.platform === 'modal' && state.playPos === 'right' && (
+              <div
+                class={styles.setting}
+                onClick={() => {
+                  onOpenSetting();
+                }}>
+                <img src={setImg} />
+              </div>
+            )}
+            {/* 播放类型 */}
+            <div class={styles.playType} onClick={handlePlayType}>
+              {setting.playType === 'beat' ? (
+                <img src={beat} />
               ) : (
-                <img src={iconPlay} />
+                <img src={tempo} />
               )}
             </div>
-          )}
-          {/* 老师端来的时候的设置按钮 */}
-          {state.platform === 'modal' && state.playPos === 'left' && (
+            {/* 随机生成 */}
             <div
-              class={styles.setting}
+              class={styles.randomTempo}
               onClick={() => {
+                renderScore();
                 handleStop();
-                state.settingStatus = true;
               }}>
-              <img src={setImg} />
+              <img src={randDom} />
             </div>
-          )}
+            {/* 速度 */}
+            <div class={styles.speedChange}>
+              <img
+                src={iconPlus}
+                class={styles.speedPlus}
+                onClick={() => {
+                  if (setting.speed <= 40) return;
+                  setting.speed -= 1;
+                  handleStop();
+
+                  state.speedList.forEach((item: any) => {
+                    if (item.value === setting.speed) {
+                      item.color = '#1CACF1';
+                      setting.speed = setting.speed;
+                    } else {
+                      item.color = '#060606';
+                    }
+                  });
+                }}
+              />
+              <Popover
+                placement="top"
+                class={styles.popupContainer}
+                actions={state.speedList}
+                onSelect={(val: any) => {
+                  if (val.value === setting.speed) return;
+                  state.speedList.forEach((item: any) => {
+                    if (item.value === val.value) {
+                      item.color = '#1CACF1';
+                      setting.speed = val.value;
+                    } else {
+                      item.color = '#060606';
+                    }
+                  });
+                  handleStop();
+                }}>
+                {{
+                  reference: () => (
+                    <div class={styles.speedNum}>{setting.speed}</div>
+                  )
+                }}
+              </Popover>
+
+              <img
+                src={iconAdd}
+                class={styles.speedAdd}
+                onClick={() => {
+                  if (setting.speed >= 200) return;
+                  setting.speed += 1;
+                  handleStop();
+
+                  state.speedList.forEach((item: any) => {
+                    if (item.value === setting.speed) {
+                      item.color = '#1CACF1';
+                      setting.speed = setting.speed;
+                    } else {
+                      item.color = '#060606';
+                    }
+                  });
+                }}
+              />
+            </div>
+            {/* 播放 */}
+            {state.playPos === 'right' && (
+              <div class={styles.play} onClick={handlePlay}>
+                {setting.playState === 'pause' ? (
+                  <img src={iconPause} />
+                ) : (
+                  <img src={iconPlay} />
+                )}
+              </div>
+            )}
+            {/* 老师端来的时候的设置按钮 */}
+            {state.platform === 'modal' && state.playPos === 'left' && (
+              <div
+                class={styles.setting}
+                onClick={() => {
+                  onOpenSetting();
+                }}>
+                <img src={setImg} />
+              </div>
+            )}
+          </div>
+        </div>
+        <div
+          class={[
+            styles.containerRight,
+            state.settingStatus ? '' : styles.rightHide
+          ]}>
+          <SettingModal
+            class={styles.settingModalShow}
+            // onGuideDone={setGuidanceShow}
+            // showGuide={guidanceShow.value}
+            dataJson={state.dataJson}
+            onClose={() => (state.settingStatus = false)}
+          />
         </div>
 
         <Popup
           style={
             state.platform === 'modal' ? settingBoxDragData.styleDrag.value : {}
           }
-          v-model:show={state.settingStatus}
+          v-model:show={state.settingPcStatus}
           class={[styles.settingPopup, settingBoxClass]}>
-          <SettingModal
+          <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>
     );

+ 44 - 9
src/views/tempo-practice/setting-modal/index.module.less

@@ -3,7 +3,7 @@
   width: 430px;
   height: 86vh;
   background: #fff;
-  border-radius: 26px;
+  border-radius: 18px 0 0 18px;
   padding: 26px 0 20px;
 
   &.pcS {
@@ -11,7 +11,7 @@
     height: 536px;
 
     .paramContent {
-      gap: 18px;
+      gap: 8px;
 
       .btn {
         width: 78px;
@@ -19,8 +19,8 @@
       }
 
       img {
-        width: 80px;
-        height: 80px;
+        width: 64px;
+        height: 64px;
       }
     }
   }
@@ -35,13 +35,15 @@
     height: 34px;
     background: url('../images/icon-set-title.png') no-repeat center center / contain;
   }
-  .iconTitBox{
+
+  .iconTitBox {
     position: absolute;
     top: 0;
     left: 0;
     width: 100%;
-    height: 40px;
-    z-index: 9;
+    height: 0;
+    z-index: 11;
+
     .iconClose {
       position: absolute;
       right: 13px;
@@ -58,9 +60,11 @@
 }
 
 .settingContent {
-  padding: 0 26px;
+  padding: 0 20px 0 20px;
   overflow-y: auto;
   height: 100%;
+  position: relative;
+  z-index: 10;
 
   &::-webkit-scrollbar {
     width: 4px;
@@ -75,6 +79,33 @@
     border-radius: 0;
     background: rgba(0, 0, 0, 0.1);
   }
+
+  .iArrow {
+    width: 17px;
+    height: 17px;
+    margin-right: 3px;
+  }
+
+  .collapseContainer {
+    :global {
+      .van-collapse-item__title {
+        display: flex;
+        align-items: center;
+        padding: 8px;
+        background: #E5F0FB;
+        border-radius: 6px;
+        font-weight: 500;
+        font-size: 16px;
+        color: #000000;
+        cursor: pointer;
+      }
+
+      .van-collapse-item__content {
+        padding: 18px 3px 0;
+      }
+    }
+
+  }
 }
 
 .settingParams {
@@ -123,6 +154,9 @@
     padding-bottom: 0;
     margin: -4px;
 
+    &>div {
+      background: #F5F6F7;
+    }
   }
 
   .active {
@@ -135,6 +169,7 @@
     background: #F5F6F7;
     border-radius: 4px;
     margin: 4px;
+    cursor: pointer;
   }
 }
 
@@ -160,4 +195,4 @@
     background: url('../images/btn-5.png') no-repeat center center / contain;
     border: none;
   }
-}
+}

+ 239 - 109
src/views/tempo-practice/setting-modal/index.tsx

@@ -1,45 +1,63 @@
-import { computed, defineComponent, onMounted, reactive } from 'vue';
+import {
+  PropType,
+  computed,
+  defineComponent,
+  onMounted,
+  reactive,
+  watch
+} from 'vue';
 import styles from './index.module.less';
-import { Button, showToast } from 'vant';
+import { Button, Collapse, CollapseItem, showToast } from 'vant';
 import {
   barLineList,
   beatList,
   elementList,
   renderScore,
+  renderScoreModal,
   setting,
+  setting_modal,
   tempo4,
   tempo8
 } from '../setting';
 import { getImage } from '../images/music';
 import { hendleEndTick } from '../tick';
 import { hendleEndBeat } from '../beat-tick';
-import deepClone from '@/helpers/deep-clone';
 import { useRoute } from 'vue-router';
-import Dragbom from '@/hooks/useDrag/dragbom';
+import settingArrowActive from '../images/setting-arrow-active.png';
+import settingArrowDefault from '../images/setting-arrow-default.png';
+import deepClone from '@/helpers/deep-clone';
 
 export default defineComponent({
-  emits: ['close',"guideDone"],
+  emits: ['close'],
   props: {
-    dataJson: {
-      type: Object,
+    class: {
+      type: Object as PropType<any>,
       default: () => {}
-    },
-    /** 是否显示引导 */
-		showGuide: {
-			type: Boolean,
-			default: false,
-		}
+    }
+    // dataJson: {
+    //   type: Object,
+    //   default: () => {}
+    // }
   },
   name: 'setting-modal',
-  setup(props, { emit }) {
+  setup(props, { emit, expose }) {
     const route = useRoute();
-    const { element, beat, barLine, tempo } = props.dataJson;
+    // const { element, beat, barLine, tempo } = props.dataJson;
+
+    const tempDeepClone = (val: any) => {
+      return JSON.parse(JSON.stringify(val));
+    };
     const state = reactive({
       win: route.query.win,
-      element: element || ('jianpu' as 'jianpu' | 'staff'), // 元素
-      beat: beat || ('4-4' as '4-2' | '4-3' | '4-4' | '8-3' | '8-6'), // 拍号
-      barLine: barLine || ('1' as '1' | '2' | '4'), // 小节数
-      tempo: tempo || (['1', '2', '3'] as any[]) // 节奏形筛选
+      activeNames: ['base'] as any, // 折叠面板
+      element:
+        tempDeepClone(setting_modal.element) ||
+        ('jianpu' as 'jianpu' | 'staff'), // 元素
+      beat:
+        tempDeepClone(setting_modal.beat) ||
+        ('4-4' as '4-2' | '4-3' | '4-4' | '8-3' | '8-6'), // 拍号
+      barLine: tempDeepClone(setting_modal.barLine) || ('1' as '1' | '2' | '4'), // 小节数
+      tempo: tempDeepClone(setting_modal.tempo) || (['1', '2', '3'] as any[]) // 节奏形筛选
     });
 
     const tempoList = computed(() => {
@@ -51,35 +69,63 @@ export default defineComponent({
       return tempo4;
     });
 
-    const onChangeTempo = (item: any) => {
-      const index = state.tempo.indexOf(item);
-      if (index !== -1) {
-        state.tempo.splice(index, 1);
-      } else {
-        state.tempo.push(item);
+    // 重置选中数据
+    watch(
+      () => setting_modal,
+      () => {
+        state.element =
+          tempDeepClone(setting_modal.element) ||
+          ('jianpu' as 'jianpu' | 'staff'); // 元素
+        state.beat =
+          tempDeepClone(setting_modal.beat) ||
+          ('4-4' as '4-2' | '4-3' | '4-4' | '8-3' | '8-6'); // 拍号
+        state.barLine =
+          tempDeepClone(setting_modal.barLine) || ('1' as '1' | '2' | '4'); // 小节数
+        state.tempo =
+          tempDeepClone(setting_modal.tempo) || (['1', '2', '3'] as any[]); // 节奏形筛选
+      },
+      {
+        deep: true
       }
+    );
+
+    const getBeatUrl = (value: any) => {
+      const prefix = state.element === 'jianpu' ? 'j-' : 'f-';
+      return prefix + value + '.png';
+    };
+    const onChangeTempo = (item: any) => {
+      setting_modal.scorePart.forEach((part: Array<any>) => {
+        part.forEach((child: any) => {
+          if (child.selected) {
+            child.url = getBeatUrl(item);
+
+            child.selected = false;
+          }
+        });
+      });
     };
 
     const handleStop = () => {
-      setting.playState = 'pause';
-      if (setting.playType === 'beat') {
+      setting_modal.playState = 'pause';
+      if (setting_modal.playType === 'beat') {
         hendleEndTick();
       } else {
         hendleEndBeat();
       }
     };
 
-    const onSubmit = () => {
-      if (state.tempo.length <= 0) {
-        showToast('节奏型不能为空');
-        return;
-      }
+    /** 数据有变化时重置 */
+    const onChangeResetTempo = () => {
+      // if (state.tempo.length <= 0) {
+      //   showToast('节奏型不能为空');
+      //   return;
+      // }
       let status = false; // 是否有更改
       if (
-        setting.element !== state.element ||
-        setting.beat !== state.beat ||
-        setting.barLine !== state.barLine ||
-        setting.tempo.join(',') !== state.tempo.join(',')
+        setting_modal.element !== state.element ||
+        setting_modal.beat !== state.beat ||
+        setting_modal.barLine !== state.barLine ||
+        setting_modal.tempo.join(',') !== state.tempo.join(',')
       ) {
         status = true;
       }
@@ -87,101 +133,185 @@ export default defineComponent({
       // 判断是否有数据变化
       handleStop();
       if (status) {
-        setting.element = JSON.parse(JSON.stringify(state.element));
-        setting.beat = JSON.parse(JSON.stringify(state.beat)); //state.beat;
-        setting.barLine = JSON.parse(JSON.stringify(state.barLine)); // state.barLine;
-        setting.tempo = JSON.parse(JSON.stringify(state.tempo)); // state.tempo;
-        renderScore();
+        setting_modal.element = tempDeepClone(state.element);
+        setting_modal.beat = tempDeepClone(state.beat); //state.beat;
+        setting_modal.barLine = tempDeepClone(state.barLine); // state.barLine;
+        setting_modal.tempo = tempDeepClone(state.tempo); // state.tempo;
+        renderScoreModal();
+      }
+    };
+
+    const onSubmit = () => {
+      // 初始化设置的数据
+      for (let i in setting_modal) {
+        setting[i] = JSON.parse(JSON.stringify(setting_modal[i]));
       }
 
       emit('close');
     };
 
+    expose({
+      onSubmit
+    });
+
     return () => (
       <div
-        class={[styles.settingContainer, state.win === 'pc' ? styles.pcS : '','settingContainer_pc']}>
-        <div class={styles.title}></div>
-        <div class={[styles.iconTitBox,'iconTitBoxMove']}>
+        class={[
+          props.class,
+          styles.settingContainer,
+          state.win === 'pc' ? styles.pcS : '',
+          'settingContainer_pc'
+        ]}>
+        {/* <div class={styles.title}></div> */}
+        <div class={[styles.iconTitBox, 'iconTitBoxMove']}>
           <i
             class={styles.iconClose}
             onClick={() => {
               emit('close');
               setTimeout(() => {
-                state.element = JSON.parse(JSON.stringify(setting.element));
-                state.beat = JSON.parse(JSON.stringify(setting.beat)); //state.beat;
-                state.barLine = JSON.parse(JSON.stringify(setting.barLine)); // state.barLine;
-                state.tempo = JSON.parse(JSON.stringify(setting.tempo)); // state.tempo;
+                state.element = tempDeepClone(setting_modal.element);
+                state.beat = tempDeepClone(setting_modal.beat); //state.beat;
+                state.barLine = tempDeepClone(setting_modal.barLine); // state.barLine;
+                state.tempo = tempDeepClone(setting_modal.tempo); // state.tempo;
               }, 300);
             }}></i>
         </div>
 
         <div class={styles.settingContent}>
-          <div class={styles.settingParams}>
-            <div class={styles.parmaTitle}>元素</div>
-            <div class={styles.paramContent}>
-              {Object.keys(elementList).map((item: any) => (
-                <Button
-                  round
-                  class={[styles.btn, state.element === item && styles.active]}
-                  onClick={() => {
-                    state.element = item;
-                  }}>
-                  {elementList[item]}
-                </Button>
-              ))}
-            </div>
-            <div class={styles.parmaTitle}>拍号</div>
-            <div class={styles.paramContent}>
-              {Object.keys(beatList).map((item: any) => (
-                <Button
-                  round
-                  class={[styles.btn, state.beat === item && styles.active]}
-                  onClick={() => {
-                    state.beat = item;
-                    if (['4-2', '4-3', '4-4'].includes(state.beat)) {
-                      state.tempo = ['1', '2', '3'];
-                    } else if (['8-3', '8-6'].includes(state.beat)) {
-                      state.tempo = ['15', '16', '17'];
+          <Collapse v-model={state.activeNames} border={false}>
+            <CollapseItem
+              title="基础设置"
+              name="base"
+              border={false}
+              isLink={false}
+              class={styles.collapseContainer}>
+              {{
+                icon: () => (
+                  <img
+                    src={
+                      state.activeNames.includes('base')
+                        ? settingArrowActive
+                        : settingArrowDefault
                     }
-                  }}>
-                  {beatList[item]}
-                </Button>
-              ))}
-            </div>
-            <div class={styles.parmaTitle}>每页显示小节数量</div>
-            <div class={styles.paramContent}>
-              {Object.keys(barLineList).map((item: any) => (
-                <Button
-                  round
-                  class={[styles.btn, state.barLine === item && styles.active]}
-                  onClick={() => {
-                    state.barLine = item;
-                  }}>
-                  {barLineList[item]}
-                </Button>
-              ))}
-            </div>
-            <div class={styles.parmaTitle}>节奏型筛选</div>
-            <div class={[styles.paramContent, styles.tempo]}>
-              {Object.keys(tempoList.value).map((item: any) => (
-                <>
+                    class={styles.iArrow}
+                  />
+                ),
+                default: () => (
+                  <>
+                    <div class={styles.parmaTitle}>元素</div>
+                    <div class={styles.paramContent}>
+                      {Object.keys(elementList).map((item: any) => (
+                        <Button
+                          round
+                          class={[
+                            styles.btn,
+                            state.element === item && styles.active
+                          ]}
+                          onClick={() => {
+                            state.element = item;
+                            onChangeResetTempo();
+                          }}>
+                          {elementList[item]}
+                        </Button>
+                      ))}
+                    </div>
+                    <div class={styles.parmaTitle}>拍号</div>
+                    <div class={styles.paramContent}>
+                      {Object.keys(beatList).map((item: any) => (
+                        <Button
+                          round
+                          class={[
+                            styles.btn,
+                            state.beat === item && styles.active
+                          ]}
+                          onClick={() => {
+                            state.beat = item;
+                            if (['4-2', '4-3', '4-4'].includes(state.beat)) {
+                              state.tempo = ['1', '2', '3'];
+                            } else if (['8-3', '8-6'].includes(state.beat)) {
+                              state.tempo = ['15', '16', '17'];
+                            }
+                            onChangeResetTempo();
+                          }}>
+                          {beatList[item]}
+                        </Button>
+                      ))}
+                    </div>
+                    <div class={styles.parmaTitle}>每页显示小节数量</div>
+                    <div class={styles.paramContent}>
+                      {Object.keys(barLineList).map((item: any) => (
+                        <Button
+                          round
+                          class={[
+                            styles.btn,
+                            state.barLine === item && styles.active
+                          ]}
+                          onClick={() => {
+                            state.barLine = item;
+                            onChangeResetTempo();
+                          }}>
+                          {barLineList[item]}
+                        </Button>
+                      ))}
+                    </div>
+                  </>
+                )
+              }}
+            </CollapseItem>
+            <CollapseItem
+              title="节奏型"
+              name="beat"
+              border={false}
+              isLink={false}
+              class={styles.collapseContainer}>
+              {{
+                icon: () => (
                   <img
-                    onClick={() => onChangeTempo(item)}
-                    class={state.tempo.includes(item) && styles.active}
-                    src={getImage(
-                      (state.element === 'jianpu' ? 'j-' : 'f-') +
-                        tempoList.value[item]
-                    )}
+                    src={
+                      state.activeNames.includes('beat')
+                        ? settingArrowActive
+                        : settingArrowDefault
+                    }
+                    class={styles.iArrow}
                   />
-                </>
-              ))}
-            </div>
-          </div>
+                ),
+                default: () => (
+                  <>
+                    <div class={styles.parmaTitle}>节奏型筛选</div>
+                    <div class={[styles.paramContent, styles.tempo]}>
+                      {Object.keys(tempoList.value).map((item: any) => (
+                        <div
+                          draggable={true}
+                          onDragstart={(e: any) => {
+                            e.dataTransfer.setData(
+                              'text',
+                              JSON.stringify({
+                                index: item,
+                                url: getBeatUrl(item)
+                              })
+                            );
+                          }}
+                          onClick={() => onChangeTempo(item)}>
+                          <img
+                            // class={state.tempo.includes(item) && styles.active}
+                            src={getImage(
+                              (state.element === 'jianpu' ? 'j-' : 'f-') +
+                                tempoList.value[item]
+                            )}
+                          />
+                        </div>
+                      ))}
+                    </div>
+                  </>
+                )
+              }}
+            </CollapseItem>
+          </Collapse>
+          {/* <div class={styles.settingParams}></div> */}
         </div>
-        <div class={styles.btnGroup}>
+        {/* <div class={styles.btnGroup}>
           <Button class={styles.btnSubmit} onClick={onSubmit}></Button>
-          {route.query.platform === 'modal' && <Dragbom showGuide={props.showGuide} onGuideDone={() => emit("guideDone")}></Dragbom>}
-        </div>
+        </div> */}
       </div>
     );
   }

+ 170 - 0
src/views/tempo-practice/setting-pc-modal/index.module.less

@@ -0,0 +1,170 @@
+.settingPcModal {
+  height: 400px;
+  display: flex;
+  background-color: #fff;
+  border-radius: 16px;
+  overflow: hidden;
+
+  .settingModalShow {
+    height: 100% !important;
+    width: 354px !important;
+  }
+}
+
+.conCon {
+  position: relative;
+  flex: 1 auto;
+  display: flex;
+  align-items: center;
+  background: url("../images/pc_bg.png") no-repeat center center / cover;
+  width: 639px;
+
+  .btnSubmit {
+    position: absolute;
+    bottom: 11px;
+    left: 50%;
+    margin-left: -81px;
+    width: 162px;
+    height: 51px;
+    cursor: pointer;
+    z-index: 99;
+  }
+}
+
+.pc {
+  .container {
+    max-width: 1200px;
+    gap: 30px 0;
+  }
+
+  .beatSection {
+    .beat {
+      width: 206px;
+      height: 284px;
+
+      img {
+        width: 140px;
+      }
+    }
+
+    &.small {
+      .beat {
+        width: 139px !important;
+        height: 191px !important;
+
+        img {
+          width: 108px;
+        }
+      }
+    }
+  }
+
+  .footer {
+    padding-bottom: 36px;
+  }
+}
+
+.container {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex: 1 auto;
+  flex-wrap: wrap;
+  // gap: 15px 0;
+  // margin-bottom: 15px;
+  max-width: 900px;
+  margin: 0 auto;
+}
+
+.beatSection {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  // margin-bottom: 15px;
+  margin: 7px 0;
+
+  &.small {
+    width: 50%;
+    // margin: 0 16px;
+
+    &:nth-child(2n + 1) {
+      justify-content: flex-end;
+      padding-right: 12px;
+    }
+
+    &:nth-child(2n + 2) {
+      justify-content: flex-start;
+      padding-left: 12px;
+    }
+
+
+    .beat {
+      border: 2px solid #fff;
+      // width: 65px;
+      // height: 86px;
+      width: 60px;
+      height: 72px;
+      cursor: pointer;
+      margin: 0 7px;
+
+      &::before,
+      &::after {
+        width: 19px;
+        height: 5px;
+      }
+
+      img {
+        width: 48px;
+      }
+    }
+  }
+
+  .beat {
+    display: flex;
+    align-items: center;
+    flex-direction: column;
+    margin: 0 13px;
+    width: 118px;
+    height: 156px;
+    box-shadow: 0px 2px 16px 0px #76C3D2;
+    border-radius: 14px;
+    border: 3px solid #fff;
+    background: #FFFFFF;
+    position: relative;
+
+
+    &.active {
+      border: 3px solid rgba(255, 167, 0, 1);
+    }
+
+    .imgSection {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      flex: 1;
+    }
+
+    img {
+      width: 96px;
+    }
+
+    // &::before,
+    // &::after {
+    //   content: '';
+    //   display: block;
+    //   width: 30px;
+    //   height: 7px;
+    //   background: url('../images/icon-arrow.png') no-repeat center center / contain;
+    //   margin: 0 auto;
+    // }
+
+    // &::before {
+    //   margin-top: 3px;
+    // }
+
+    // &::after {
+    //   margin-bottom: 3px;
+    //   transform: rotate(180deg);
+    // }
+  }
+}

+ 139 - 0
src/views/tempo-practice/setting-pc-modal/index.tsx

@@ -0,0 +1,139 @@
+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';
+import { initSelectScorePartModal, setting_modal } from '../setting';
+import { getImage } from '../images/music';
+import btn5 from '../images/btn-5.png';
+
+export default defineComponent({
+  name: 'setting-pc-modal',
+  props: {
+    /** 是否显示引导 */
+    showGuide: {
+      type: Boolean,
+      default: false
+    }
+  },
+  emits: ['close', 'guideDone'],
+  setup(props, { emit }) {
+    const route = useRoute();
+    const settingModalRef = ref();
+    const state = reactive({
+      activeBeat: ''
+    });
+
+    return () => (
+      <div class={styles.settingPcModal}>
+        <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={[
+                  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-${i}-${jIndex}`}
+                    draggable={false}
+                    onDragenter={(e: any) => {
+                      e.preventDefault();
+                    }}
+                    onDragover={(e: any) => {
+                      e.preventDefault();
+                    }}
+                    onDrop={(e: any) => {
+                      let dropItem = e.dataTransfer.getData('text');
+                      dropItem = dropItem ? JSON.parse(dropItem) : {};
+                      console.log(e, 'event', dropItem);
+                      // 判断是否有数据
+                      if (dropItem.url) {
+                        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();
+                      initSelectScorePartModal();
+                      child.selected = true;
+                    }}>
+                    <div class={styles.imgSection}>
+                      <img src={getImage(child.url)} />
+                    </div>
+                  </div>
+                ))}
+              </div>
+            ))}
+          </div>
+
+          <img
+            class={styles.btnSubmit}
+            src={btn5}
+            onClick={() => {
+              settingModalRef.value?.onSubmit();
+            }}
+          />
+        </div>
+
+        <SettingModal
+          class={styles.settingModalShow}
+          ref={settingModalRef}
+          onClose={() => emit('close')}
+        />
+
+        {route.query.platform === 'modal' && (
+          <Dragbom
+            showGuide={props.showGuide}
+            onGuideDone={() => emit('guideDone')}></Dragbom>
+        )}
+      </div>
+    );
+  }
+});

+ 129 - 5
src/views/tempo-practice/setting.ts

@@ -1,6 +1,7 @@
 import { reactive } from 'vue';
 import { handleInitTick } from './tick';
 import { handleInitBeat } from './beat-tick';
+import deepClone from '@/helpers/deep-clone';
 
 export const setting = reactive({
   element: 'jianpu' as 'jianpu' | 'staff', // 元素
@@ -11,7 +12,21 @@ export const setting = reactive({
   playState: 'pause' as 'pause' | 'play',
   playType: 'tempo' as 'beat' | 'tempo',
   speed: 60 // 默认速度
-});
+} as any);
+
+/**
+ * 临时设置的数据
+ */
+export const setting_modal = reactive({
+  element: 'jianpu' as 'jianpu' | 'staff', // 元素
+  beat: '4-4' as '4-2' | '4-3' | '4-4' | '8-3' | '8-6', // 拍号
+  barLine: '1' as '1' | '2' | '4', // 小节数
+  tempo: ['1', '2', '3'] as any[], // 节奏形筛选
+  scorePart: [] as any, // 生成谱面
+  playState: 'pause' as 'pause' | 'play',
+  playType: 'tempo' as 'beat' | 'tempo',
+  speed: 60 // 默认速度
+} as any);
 
 /** 元素 */
 export const elementList = {
@@ -38,21 +53,33 @@ export const barLineList = {
 /** 节奏型筛选 */
 // 简谱
 const temp: any = {};
+const tempNum = [] as any
 for (let i = 1; i <= 14; i++) {
   temp[i] = i + '.png';
+  tempNum.push(i)
 }
 export const tempo4 = temp;
+export const tempo4Num = tempNum
 
 // 五线谱
 const temp2: any = {};
+const tempNum2 = [] as any
 for (let i = 15; i <= 31; i++) {
   temp2[i] = i + '.png';
+  tempNum2.push(i)
 }
 export const tempo8 = temp2;
+export const tempo8Num = tempo8
 
 /** 随机生成元素 */
 export const randomScoreElement = (element?: string) => {
-  const tempoList = setting.tempo;
+  // const tempoList = setting.tempo;
+  let tempoList = tempo4Num || [] as any
+  if (['4-2', '4-3', '4-4'].includes(setting_modal.beat)) {
+    tempoList = tempo4Num;
+  } else if (['8-3', '8-6'].includes(setting_modal.beat)) {
+    tempoList = tempo8Num;
+  }
   const prefix = setting.element === 'jianpu' ? 'j-' : 'f-';
   if (element) {
     const newArr = tempoList.filter((item: any) => item !== element);
@@ -85,7 +112,13 @@ export const elementDirection = (type: string, index: number) => {
   const prefix = setting.element === 'jianpu' ? 'j-' : 'f-';
   let ele = '';
   let i = 0;
-  const tempoList = setting.tempo;
+  // const tempoList = setting.tempo;
+  let tempoList = tempo4Num || [] as any
+  if (['4-2', '4-3', '4-4'].includes(setting_modal.beat)) {
+    tempoList = tempo4Num;
+  } else if (['8-3', '8-6'].includes(setting_modal.beat)) {
+    tempoList = tempo8Num;
+  }
   const toIndex = tempoList.findIndex((t: any) => Number(t) === index);
   if (type === 'up') {
     if (toIndex <= 0) {
@@ -113,7 +146,10 @@ export const elementDirection = (type: string, index: number) => {
   };
 };
 
-/** 生成谱面 */
+/**
+ * 生成谱面
+ * @param {temp} 是否临时生成
+ * */
 export const renderScore = () => {
   const barLine = Number(setting.barLine);
   const beatA = setting.beat.split('-').map(i => Number(i));
@@ -121,7 +157,6 @@ export const renderScore = () => {
   if (beatA[0] === 8) {
     beat = beat / 3;
   }
-  console.log(beat, 'beat');
   const tempBeat: any = [];
   for (let i = 0; i < barLine; i++) {
     tempBeat[i] = [];
@@ -131,6 +166,7 @@ export const renderScore = () => {
       };
     }
   }
+
   setting.scorePart = tempBeat;
 
   const beatLengthInMilliseconds = (60 / setting.speed) * 1000;
@@ -144,6 +180,9 @@ export const renderScore = () => {
     handleInitBeat(beatLengthInMilliseconds, Number(beatA[1]) || 4);
   }
   initSelectScorePart();
+
+  // 初始化设置里面的数据
+  setting_modal.scorePart = deepClone(setting.scorePart)
 };
 
 /** 初始化选中状态 */
@@ -158,3 +197,88 @@ export const initSelectScorePart = (i?: number, j?: number) => {
     setting.scorePart[i][j].selected = true;
   }
 };
+
+
+
+
+/** 随机生成元素 */
+export const randomScoreElementModal = (element?: string) => {
+  // const tempoList = setting.tempo;
+  let tempoList = tempo4Num || [] as any
+  if (['4-2', '4-3', '4-4'].includes(setting_modal.beat)) {
+    tempoList = tempo4Num;
+  } else if (['8-3', '8-6'].includes(setting_modal.beat)) {
+    tempoList = tempo8Num;
+  }
+
+  const prefix = setting_modal.element === 'jianpu' ? 'j-' : 'f-';
+  if (element) {
+    const newArr = tempoList.filter((item: any) => item !== element);
+    // 生成一个0到newArr长度之间的随机索引
+    const randomIndex = Math.floor(Math.random() * newArr.length);
+    return {
+      url: prefix + newArr[randomIndex] + '.png',
+      index: newArr[randomIndex]
+    };
+  } else {
+    // 如果只有一个就直接返回
+    if (tempoList.length === 1) {
+      return {
+        url: prefix + tempoList[0] + '.png',
+        index: tempoList[0]
+      };
+    } else {
+      const randomIndex = Math.floor(Math.random() * tempoList.length);
+      const randomItem = tempoList[randomIndex];
+      return {
+        url: prefix + randomItem + '.png',
+        index: randomItem
+      };
+    }
+  }
+};
+/*** 生成谱面 */
+export const renderScoreModal = () => {
+  const barLine = Number(setting_modal.barLine);
+  const beatA = setting_modal.beat.split('-').map(i => Number(i));
+  let beat = beatA[1];
+  if (beatA[0] === 8) {
+    beat = beat / 3;
+  }
+  const tempBeat: any = [];
+  for (let i = 0; i < barLine; i++) {
+    tempBeat[i] = [];
+    for (let j = 0; j < beat; j++) {
+      tempBeat[i][j] = {
+        ...randomScoreElementModal()
+      };
+    }
+  }
+
+  setting_modal.scorePart = tempBeat;
+
+  const beatLengthInMilliseconds = (60 / setting.speed) * 1000;
+  if (setting_modal.playType === 'beat') {
+    handleInitTick(
+      beatLengthInMilliseconds,
+      Number(beatA[1]) || 4,
+      Number(beatA[0])
+    );
+  } else {
+    handleInitBeat(beatLengthInMilliseconds, Number(beatA[1]) || 4);
+  }
+  initSelectScorePartModal();
+};
+
+/** 初始化选中状态 */
+export const initSelectScorePartModal = (i?: number, j?: number) => {
+  setting_modal.scorePart.forEach((part: Array<any>) => {
+    part.forEach((item: any) => {
+      item.selected = false;
+    });
+  });
+
+  if (i !== undefined && j !== undefined && setting_modal.scorePart[i][j]) {
+    setting_modal.scorePart[i][j].selected = true;
+  }
+};