Sfoglia il codice sorgente

添加声部搜索

lex 1 anno fa
parent
commit
ceb4ee5d43

+ 1 - 1
osmd-extended

@@ -1 +1 @@
-Subproject commit 9d41c99bb92f1a836080f4a62e44211dbec45379
+Subproject commit c61975028e8f71a7448a92ba440fc9297a17a2ed

+ 9 - 2
src/page-instrument/api.ts

@@ -39,6 +39,13 @@ export const sysSuggestionAdd = (data: any) => {
 export const api_musicPracticeRecordDetail = (recordId: string) => {
   return request.get("/musicPracticeRecord/detail/" + recordId);
 };
+
+/** 获取声部 */
+export const api_subjectList = (params: any) => {
+  return request.post("/subject/list", {
+    data: params,
+  });
+};
 /** 获取曲谱列表 */
 export const api_musicSheetPage = (data: any) => {
   return request.post("/musicSheet/page", {
@@ -61,8 +68,8 @@ export const getSubjectList = (data: any) => {
  * 获取意见类型
  */
 export const getSuggestionList = (params: any) => {
-  return request.post('/sysSuggestionType/page', {
+  return request.post("/sysSuggestionType/page", {
     data: params,
     requestType: "json",
   });
-};
+};

+ 103 - 0
src/page-instrument/view-figner/change-subject/index.module.less

@@ -0,0 +1,103 @@
+.changeSubject {
+  padding: 18px 0;
+}
+
+.changeSubjectContainer {
+  height: 217px;
+  overflow-x: hidden;
+  overflow-y: auto;
+  padding: 0 15px;
+
+  &::-webkit-scrollbar {
+    display: none;
+  }
+}
+
+.title {
+  display: flex;
+  align-items: center;
+
+  font-size: 16px;
+  font-weight: 500;
+  color: #333333;
+  line-height: 22px;
+
+  &::before {
+    content: '';
+    display: inline-block;
+    width: 4px;
+    height: 11px;
+    background: #1CACF1;
+    border-radius: 3px;
+    margin-right: 6px;
+  }
+}
+
+.subjectContainer {
+  display: flex;
+  align-items: center;
+  flex-wrap: wrap;
+  padding-top: 18px;
+
+  .subjectItem {
+    width: 31%;
+    height: 34px;
+    line-height: 34px;
+    text-align: center;
+    background: #F6F6F6;
+    border-radius: 50px;
+    font-size: 13px;
+    color: #333333;
+    border: 1px solid #F6F6F6;
+    margin-bottom: 12px;
+
+    &:nth-child(3n + 2) {
+      margin-left: 2.333%;
+      margin-right: 2.333%;
+    }
+
+    // &:nth-child(3n + 3) {
+    //   margin-left: 2.333%;
+    // }
+
+    &.arrow::after {
+      content: '';
+      display: inline-block;
+      margin-left: 3px;
+      width: 0;
+      height: 0;
+      border-left: 4px solid transparent;
+      border-right: 4px solid transparent;
+      border-top: 4px solid transparent;
+      border-bottom: 4px solid #777777;
+      transform: translateY(3px) rotate(180deg);
+    }
+
+
+    &.active {
+      border: 1px solid #1CACF1;
+      background: #EBF8FF;
+      color: #1CACF1;
+
+      &::after {
+        border-bottom: 4px solid #1CACF1;
+        transform: translateY(-2px) rotate(0deg);
+      }
+    }
+  }
+
+
+}
+
+.btnGroups {
+  border-top: 1px solid #F2F2F2;
+  display: flex;
+  align-items: center;
+  padding: 18px 15px 0;
+
+  :global {
+    .van-button+.van-button {
+      margin-left: 15px;
+    }
+  }
+}

+ 114 - 0
src/page-instrument/view-figner/change-subject/index.tsx

@@ -0,0 +1,114 @@
+import { defineComponent, onMounted, reactive } from "vue";
+import styles from "./index.module.less";
+import { Button } from "vant";
+
+export default defineComponent({
+  name: "change-subject",
+  props: {
+    subjectList: {
+      type: Array,
+      default: () => [],
+    },
+    subject: {
+      type: String,
+      default: "",
+    },
+  },
+  emits: ["close", "confirm"],
+  setup(props, { emit }) {
+    const state = reactive({
+      subjectValue: props.subject || "",
+      instrumentCode: "",
+      selectList: [] as any,
+    });
+
+    //
+    const selectItem = () => {
+      const i = props.subjectList.find((item: any) => item.value === props.subject);
+      if (!i) {
+        props.subjectList.forEach((item: any) => {
+          if (item.children && item.children.length > 0) {
+            item.children.forEach((child: any) => {
+              if (child.value === props.subject) {
+                state.instrumentCode = child.value;
+              }
+            });
+          }
+          if (state.instrumentCode) {
+            state.subjectValue = item.value;
+          }
+        });
+      }
+    };
+
+    onMounted(() => {
+      selectItem();
+    });
+    return () => (
+      <div class={styles.changeSubject}>
+        <div class={styles.changeSubjectContainer}>
+          <div class={styles.title}>声部</div>
+
+          <div class={styles.subjectContainer}>
+            {props.subjectList.map((item: any) => (
+              <div
+                class={[styles.subjectItem, item.children.length > 0 && styles.arrow, item.value === state.subjectValue && styles.active]}
+                onClick={() => {
+                  state.subjectValue = item.value;
+                  state.selectList = item.children;
+                  if (item.children.length <= 0) {
+                    state.instrumentCode = "";
+                  }
+                }}
+              >
+                {item.text}
+              </div>
+            ))}
+          </div>
+
+          {state.selectList.length > 0 && (
+            <>
+              <div class={styles.title}>乐器</div>
+              <div class={styles.subjectContainer}>
+                {state.selectList.map((item: any) => (
+                  <div
+                    class={[styles.subjectItem, item.value === state.instrumentCode && styles.active]}
+                    onClick={() => {
+                      state.instrumentCode = item.value;
+                    }}
+                  >
+                    {item.text}
+                  </div>
+                ))}
+              </div>
+            </>
+          )}
+        </div>
+
+        <div class={styles.btnGroups}>
+          <Button
+            round
+            block
+            onClick={() => {
+              emit("close");
+              state.subjectValue = props.subject;
+            }}
+          >
+            重置
+          </Button>
+          <Button
+            type="primary"
+            block
+            round
+            color="linear-gradient(90deg, #44C9FF 0%, #259CFE 100%)"
+            onClick={() => {
+              emit("confirm", state.instrumentCode || state.subjectValue);
+            }}
+          >
+            确认
+          </Button>
+        </div>
+      </div>
+    );
+  },
+});

+ 7 - 0
src/page-instrument/view-figner/index.module.less

@@ -1249,4 +1249,11 @@
             }
         }
     }
+}
+
+.changeSubjectPopup {
+    width: 375px;
+    // height: 310px;
+    background: #FFFFFF;
+    border-radius: 12px;
 }

+ 93 - 78
src/page-instrument/view-figner/index.tsx

@@ -15,7 +15,8 @@ import { usePageVisibility } from "@vant/use";
 import { watch } from "vue";
 import icon_loading_img from "./image/icon_loading_img.png";
 import state, { IPlatform } from "/src/state";
-import { getSubjectList } from "../api";
+import { api_subjectList, getSubjectList } from "../api";
+import ChangeSubject from "./change-subject";
 
 export default defineComponent({
   name: "viewFigner",
@@ -68,6 +69,8 @@ export default defineComponent({
       loadingSoundFonts: true,
       loadingSoundProgress: 0,
 
+      changeSubjectShow: false,
+
       huaweiPad: navigator?.userAgent?.includes("UAWEIVRD-W09") ? true : false,
       paddingTop: "",
       paddingLeft: "",
@@ -201,22 +204,22 @@ export default defineComponent({
       data.loadingSoundFonts = false;
     };
 
-    const selectSubjectType = (subject: string) => {
-      data.subjects.forEach((item: any) => {
-        if (item.value === subject) {
-          item.className = styles.selected;
-        } else {
-          item.className = "";
-        }
-      });
-    };
+    // const selectSubjectType = (subject: string) => {
+    //   data.subjects.forEach((item: any) => {
+    //     if (item.value === subject) {
+    //       item.className = styles.selected;
+    //     } else {
+    //       item.className = "";
+    //     }
+    //   });
+    // };
 
     // 切换当前模式
     const onChangeFingeringModel = () => {
       //
       if (playAction.listenLock) return;
       if (playAction.showAnswerLoading) return;
-      data.loadingImg = true
+      data.loadingImg = true;
       if (data.fingeringMode === "scaleMode") {
         if (["pan-flute", "ocarina"].includes(data.subject)) {
           data.viewIndex = 1;
@@ -250,7 +253,7 @@ export default defineComponent({
       data.loadingDom = true;
       getNotes();
 
-      selectSubjectType(data.subject);
+      // selectSubjectType(data.subject);
 
       if (data.fingeringMode === "fingeringMode") {
         if (data.subject === "pan-flute") {
@@ -285,19 +288,40 @@ export default defineComponent({
     // 获取声部
     const getSubjects = async () => {
       try {
-        const subjects = await getSubjectList({
+        // api_subjectList
+        const subjects = await api_subjectList({
           enableFlag: true,
           delFlag: 0,
           page: 1,
           rows: 999,
         });
-        const rows = subjects.data.rows || [];
+
+        const rows = subjects.data || [];
         rows.forEach((row: any) => {
-          data.subjects.push({
+          const tempList: any = {
             text: row.name,
             value: mappingVoicePart(row.code, "INSTRUMENT"),
-            className: "",
-          });
+            id: row.id,
+            children: [] as any,
+          };
+          if (row.instruments && row.instruments.length > 0) {
+            if (row.instruments.length > 1) {
+              row.instruments.forEach((i: any) => {
+                tempList.children.push({
+                  text: i.name,
+                  id: i.id,
+                  value: mappingVoicePart(i.code, "INSTRUMENT"),
+                });
+              });
+            } else {
+              const singleRow = row.instruments[0];
+              if (singleRow.code) {
+                tempList.value = mappingVoicePart(singleRow.code, "INSTRUMENT");
+                tempList.id = singleRow.id;
+              }
+            }
+          }
+          data.subjects.push(tempList);
         });
         // console.log(data.subjects, "subjects");
       } catch {
@@ -391,10 +415,10 @@ export default defineComponent({
     // 排箫,默认0.9显示
     const setDefaultScale = () => {
       if (data.subject === "pan-flute") {
-        data.transform.scale = 0.9
-        data.transform.startScale = 0.9
+        data.transform.scale = 0.9;
+        data.transform.startScale = 0.9;
       }
-    }
+    };
 
     onMounted(() => {
       loadElement();
@@ -402,7 +426,7 @@ export default defineComponent({
     });
     const loadElement = () => {
       const fingeringContainer = document.getElementById("fingeringContainer");
-      setDefaultScale()
+      setDefaultScale();
       // console.log("🚀 ~ fingeringContainer:", fingeringContainer);
       const mc = new Hammer.Manager(fingeringContainer as HTMLElement);
       mc.add(new Hammer.Pan({ threshold: 0, pointers: 0 }));
@@ -910,65 +934,25 @@ export default defineComponent({
                 <img src={icons.icon_back} />
               </button>
 
-              <Popover
-                placement="bottom"
-                class={styles.popoverContainer}
-                actions={data.subjects}
-                onUpdate:show={() => {
+              <div
+                class={styles.baseBtn}
+                onClick={(e) => {
+                  //
                   // 播放音阶时不能切换
-                  if (playStatus.gamut) return;
+                  if (playStatus.gamut) {
+                    return;
+                  }
                   // 开始答题不能切换
-                  if (playStatus.action) return;
-                }}
-                onSelect={(val: any) => {
-                  if (data.subject === val.value) return;
-                  const originalSubject = JSON.parse(JSON.stringify(data.subject));
-                  data.subject = val.value;
-                  data.viewIndex = 0;
-                  data.tipShow = false;
-                  data.loadingDom = true;
-                  fingerData.fingeringInfo = subjectFingering(data.subject);
-                  data.activeTone = {} as any;
-                  resetElement();
-                  resetMode(true, 0);
-                  api_setRequestedOrientation(orientationDirection.value);
-                  // 设置屏幕方向
-                  setTimeout(() => {
-                    const before = ["hulusi-flute", "piccolo", "baroque-recorder"].includes(originalSubject) ? 1 : 0;
-                    if (orientationDirection.value !== before) {
-                      data.paddingTop = "";
-                      data.paddingLeft = "";
-                    }
+                  if (playAction.listenLock) {
+                    return;
+                  }
 
-                    __init();
-                  }, 100);
+                  data.changeSubjectShow = true;
                 }}
               >
-                {{
-                  reference: () => (
-                    <div
-                      class={styles.baseBtn}
-                      onClick={(e) => {
-                        //
-                        // 播放音阶时不能切换
-                        if (playStatus.gamut) {
-                          e.stopPropagation();
-                          e.preventDefault();
-                        }
-                        // 开始答题不能切换
-                        // if (playStatus.action) return;
-                        if (playAction.listenLock) {
-                          e.stopPropagation();
-                          e.preventDefault();
-                        }
-                      }}
-                    >
-                      <img src={icons.icon_change_instrument} />
-                      <span>切换乐器</span>
-                    </div>
-                  ),
-                }}
-              </Popover>
+                <img src={icons.icon_change_instrument} />
+                <span>切换乐器</span>
+              </div>
               <div class={styles.baseBtn} onClick={onChangeFingeringModel}>
                 <img src={modeText.value.icon} />
                 <span>{modeText.value.text}</span>
@@ -995,9 +979,7 @@ export default defineComponent({
                   class={[styles.fingeringContainer]}
                 >
                   <div class={styles.imgs}>
-                    {
-                      !data.loadingImg && <img src={data.fingeringMode === "scaleMode" ? fingerData.subject?.json?.full : fingerData.subject?.json?.full1} />
-                    }
+                    {!data.loadingImg && <img src={data.fingeringMode === "scaleMode" ? fingerData.subject?.json?.full : fingerData.subject?.json?.full1} />}
                     {rs.map((key: number | string, index: number) => {
                       const nk: string = typeof key === "string" ? key.replace("active-", "") : String(key);
                       return <img data-index={nk} src={fingerData.subject?.json?.[nk]} />;
@@ -1294,6 +1276,39 @@ export default defineComponent({
             </div>
           </Popup>
 
+          <Popup v-model:show={data.changeSubjectShow} class={styles.changeSubjectPopup}>
+            <ChangeSubject
+              subjectList={data.subjects}
+              subject={data.subject}
+              onClose={() => (data.changeSubjectShow = false)}
+              onConfirm={(code: any) => {
+                if (data.subject === code) return;
+                const originalSubject = JSON.parse(JSON.stringify(data.subject));
+                data.subject = code;
+                data.viewIndex = 0;
+                data.tipShow = false;
+                data.loadingDom = true;
+                fingerData.fingeringInfo = subjectFingering(data.subject);
+                data.activeTone = {} as any;
+                resetElement();
+                resetMode(true, 0);
+                api_setRequestedOrientation(orientationDirection.value);
+
+                data.changeSubjectShow = false;
+                // 设置屏幕方向
+                setTimeout(() => {
+                  const before = ["hulusi-flute", "piccolo", "baroque-recorder"].includes(originalSubject) ? 1 : 0;
+                  if (orientationDirection.value !== before) {
+                    data.paddingTop = "";
+                    data.paddingLeft = "";
+                  }
+
+                  __init();
+                }, 100);
+              }}
+            />
+          </Popup>
+
           {props.show && !data.loading && !data.loadingSoundFonts && <GuideIndex fingeringMode={data.fingeringMode} showGuide={false} list={["finger"]} />}
         </div>
       );

+ 30 - 31
src/view/fingering/fingering-config.ts

@@ -101,7 +101,6 @@ export const mappingVoicePart = (id: number | string, soruce: "GYM" | "COLEXIU"
     if (typeof code === "string") {
       code = code.toLocaleLowerCase().replace(/ /g, "");
     }
-    console.log(code, "1212");
     const subject: { [_key: string | number]: any } = {
       flute: 2,
       clarinet: 4,
@@ -120,7 +119,7 @@ export const mappingVoicePart = (id: number | string, soruce: "GYM" | "COLEXIU"
       ukulele: 130,
       mouthorgan: 140,
       piano: 150,
-	  baroquerecorder: 'baroque-recorder',
+      baroquerecorder: "baroque-recorder",
       4: "piccolo",
       3: "hulusi-flute",
       1: "pan-flute",
@@ -208,7 +207,7 @@ export const mappingVoicePart = (id: number | string, soruce: "GYM" | "COLEXIU"
       panpipes: "pan-flute",
       ocarina: "ocarina",
       nai: "melodica",
-	  BaroqueRecorder: 'baroque-recorder',
+      BaroqueRecorder: "baroque-recorder",
     };
     let _track;
     if (typeof code === "string") {
@@ -341,15 +340,15 @@ export const subjectFingering = (subjectId: number | string): IFingering => {
         code: "口风琴",
         hasTizhi: false,
       };
-	case "baroque-recorder": // 英式竖笛
-	  return {
-		name: "baroque-recorder",
-		direction: "vertical",
-		width: "3rem",
-		orientation: 1,
-		code: "竖笛",
-		hasTizhi: true,
-	  };	  
+    case "baroque-recorder": // 英式竖笛
+      return {
+        name: "baroque-recorder",
+        direction: "vertical",
+        width: "3rem",
+        orientation: 1,
+        code: "竖笛",
+        hasTizhi: true,
+      };
     default:
       return {};
   }
@@ -550,25 +549,25 @@ export const getFingeringConfig = async (type: IVocals | undefined): Promise<ITy
           marginTop: "auto",
         },
       };
-	case "baroque-recorder":
-		const baroqueRecorder = await import(`./fingering-img/baroque-recorder/index.json`);
-		return {
-			json: baroqueRecorder.default,
-			relationship: relationships.baroqueRecorder,
-		};  	
-	case "baroque-recorder1":
-		const baroqueRecorder1 = await import(`./fingering-img/baroque-recorder1/index.json`);
-		return {
-			json: baroqueRecorder1.default,
-			relationship: relationships.baroqueRecorder,
-		};	
-	case "baroque-recorder2":
-		const baroqueRecorder2 = await import(`./fingering-img/baroque-recorder2/index.json`);
-		return {
-			json: baroqueRecorder2.default,
-			relationship: relationships.baroqueRecorder,
-		};			  
+    case "baroque-recorder":
+      const baroqueRecorder = await import(`./fingering-img/baroque-recorder/index.json`);
+      return {
+        json: baroqueRecorder.default,
+        relationship: relationships.baroqueRecorder,
+      };
+    case "baroque-recorder1":
+      const baroqueRecorder1 = await import(`./fingering-img/baroque-recorder1/index.json`);
+      return {
+        json: baroqueRecorder1.default,
+        relationship: relationships.baroqueRecorder,
+      };
+    case "baroque-recorder2":
+      const baroqueRecorder2 = await import(`./fingering-img/baroque-recorder2/index.json`);
+      return {
+        json: baroqueRecorder2.default,
+        relationship: relationships.baroqueRecorder,
+      };
     default:
       return null;
   }
-};
+};

+ 68 - 68
vite.config.ts

@@ -7,73 +7,73 @@ import postCssPxToRem from "postcss-pxtorem";
 
 // https://vitejs.dev/config/
 export default defineConfig({
-	base: "./",
-	resolve: {},
-	plugins: [
-		// mkcert(), // 本地https
-		legacy({
-			targets: ['Chrome 63'],
-			additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
-			modernPolyfills: true
-		  }),
-		vue(),
-		vueJsx(),
-	],
-	css: {
-		postcss: {
-			plugins: [
-				postCssPxToRem({
-					rootValue: 37.5,
-					propList: ["*"],
-					selectorBlackList: [".norem"],
-				}),
-			],
-		},
-	},
-	build: {
-		rollupOptions: {
-			input: {
-				index: resolve(__dirname, "index.html"),
-				colexiu: resolve(__dirname, "colexiu.html"),
-				orchestra: resolve(__dirname, "orchestra.html"),
-				"report-share": resolve(__dirname, "report-share.html"),
-				instrument: resolve(__dirname, "instrument.html"),
-			},
-		},
-	},
-	server: {
-		cors: true,
-		port: 3000,
-		// https: true,
-		proxy: {
-			"^/gym/.*": {
-				target: "https://mstutest.dayaedu.com",
-				// target: "https://online.dayaedu.com",
-				changeOrigin: true,
-				rewrite: (path) => path.replace(/^\/gym/, ""),
-			},
-			"^/colexiu/.*": {
-				target: "https://dev.colexiu.com",
-				// target: "https://online.colexiu.com",
-				changeOrigin: true,
-				rewrite: (path) => path.replace(/^\/colexiu/, ""),
-			},
-			"^/orchestra/.*": {
-				target: "https://test.lexiaoya.cn",
-				changeOrigin: true,
-				rewrite: (path) => path.replace(/^\/orchestra/, ""),
-			},
-			"^/instrument/.*": {
-				// target: "https://kt.colexiu.com",
-				target: "https://test.lexiaoya.cn",
-				changeOrigin: true,
-				rewrite: (path) => path.replace(/^\/instrument/, ""),
-			},
-		},
-	},
-	preview:{
-		port: 3000,
-		host: '192.168.3.114'
-	}
+  base: "./",
+  resolve: {},
+  plugins: [
+    // mkcert(), // 本地https
+    legacy({
+      targets: ["Chrome 63"],
+      additionalLegacyPolyfills: ["regenerator-runtime/runtime"],
+      modernPolyfills: true,
+    }),
+    vue(),
+    vueJsx(),
+  ],
+  css: {
+    postcss: {
+      plugins: [
+        postCssPxToRem({
+          rootValue: 37.5,
+          propList: ["*"],
+          selectorBlackList: [".norem"],
+        }),
+      ],
+    },
+  },
+  build: {
+    rollupOptions: {
+      input: {
+        index: resolve(__dirname, "index.html"),
+        colexiu: resolve(__dirname, "colexiu.html"),
+        orchestra: resolve(__dirname, "orchestra.html"),
+        "report-share": resolve(__dirname, "report-share.html"),
+        instrument: resolve(__dirname, "instrument.html"),
+      },
+    },
+  },
+  server: {
+    cors: true,
+    port: 3000,
+    // https: true,
+    proxy: {
+      "^/gym/.*": {
+        target: "https://mstutest.dayaedu.com",
+        // target: "https://online.dayaedu.com",
+        changeOrigin: true,
+        rewrite: (path) => path.replace(/^\/gym/, ""),
+      },
+      "^/colexiu/.*": {
+        target: "https://dev.colexiu.com",
+        // target: "https://online.colexiu.com",
+        changeOrigin: true,
+        rewrite: (path) => path.replace(/^\/colexiu/, ""),
+      },
+      "^/orchestra/.*": {
+        target: "https://test.lexiaoya.cn",
+        changeOrigin: true,
+        rewrite: (path) => path.replace(/^\/orchestra/, ""),
+      },
+      "^/instrument/.*": {
+        target: "https://dev.kt.colexiu.com",
+        // target: "https://test.lexiaoya.cn",
+        changeOrigin: true,
+        rewrite: (path) => path.replace(/^\/instrument/, ""),
+      },
+    },
+  },
+  preview: {
+    port: 3000,
+    host: "192.168.3.114",
+  },
 });
 // vite.config.js