lex пре 1 година
родитељ
комит
26e386117d

+ 167 - 3
package-lock.json

@@ -21,7 +21,9 @@
         "dayjs": "^1.11.7",
         "echarts": "^5.4.2",
         "eventemitter3": "^5.0.1",
+        "file-saver": "^2.0.5",
         "html2canvas": "^1.4.1",
+        "jszip": "^3.10.1",
         "lib-flexible": "^0.3.2",
         "lodash": "^4.17.21",
         "lodash-es": "^4.17.21",
@@ -3762,6 +3764,11 @@
         "url": "https://opencollective.com/core-js"
       }
     },
+    "node_modules/core-util-is": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
+      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
+    },
     "node_modules/cos-js-sdk-v5": {
       "version": "1.4.20",
       "resolved": "https://registry.npmmirror.com/cos-js-sdk-v5/-/cos-js-sdk-v5-1.4.20.tgz",
@@ -4777,6 +4784,11 @@
         "node": "^10.12.0 || >=12.0.0"
       }
     },
+    "node_modules/file-saver": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz",
+      "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
+    },
     "node_modules/filelist": {
       "version": "1.0.4",
       "license": "Apache-2.0",
@@ -5449,6 +5461,11 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/immediate": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz",
+      "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
+    },
     "node_modules/import-fresh": {
       "version": "3.3.0",
       "dev": true,
@@ -6373,6 +6390,49 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/jszip": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz",
+      "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+      "dependencies": {
+        "lie": "~3.3.0",
+        "pako": "~1.0.2",
+        "readable-stream": "~2.3.6",
+        "setimmediate": "^1.0.5"
+      }
+    },
+    "node_modules/jszip/node_modules/isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+    },
+    "node_modules/jszip/node_modules/readable-stream": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
+      "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+      "dependencies": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "node_modules/jszip/node_modules/safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+    },
+    "node_modules/jszip/node_modules/string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "dependencies": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
     "node_modules/keycode": {
       "version": "2.2.1",
       "license": "MIT"
@@ -6443,6 +6503,14 @@
       "version": "0.3.2",
       "license": "ISC"
     },
+    "node_modules/lie": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz",
+      "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+      "dependencies": {
+        "immediate": "~3.0.5"
+      }
+    },
     "node_modules/liftoff": {
       "version": "4.0.0",
       "dev": true,
@@ -7360,6 +7428,11 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+    },
     "node_modules/param-case": {
       "version": "3.0.4",
       "dev": true,
@@ -7781,6 +7854,11 @@
         "node": ">= 0.6.0"
       }
     },
+    "node_modules/process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+    },
     "node_modules/proxy-from-env": {
       "version": "1.1.0",
       "license": "MIT"
@@ -8383,6 +8461,11 @@
         "randombytes": "^2.1.0"
       }
     },
+    "node_modules/setimmediate": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz",
+      "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
+    },
     "node_modules/shebang-command": {
       "version": "2.0.0",
       "dev": true,
@@ -9330,7 +9413,6 @@
     },
     "node_modules/util-deprecate": {
       "version": "1.0.2",
-      "dev": true,
       "license": "MIT"
     },
     "node_modules/utrie": {
@@ -12596,6 +12678,11 @@
         "browserslist": "^4.21.9"
       }
     },
+    "core-util-is": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
+      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
+    },
     "cos-js-sdk-v5": {
       "version": "1.4.20",
       "resolved": "https://registry.npmmirror.com/cos-js-sdk-v5/-/cos-js-sdk-v5-1.4.20.tgz",
@@ -13259,6 +13346,11 @@
         "flat-cache": "^3.0.4"
       }
     },
+    "file-saver": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz",
+      "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
+    },
     "filelist": {
       "version": "1.0.4",
       "requires": {
@@ -13645,6 +13737,11 @@
       "dev": true,
       "optional": true
     },
+    "immediate": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz",
+      "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
+    },
     "import-fresh": {
       "version": "3.3.0",
       "dev": true,
@@ -14170,6 +14267,51 @@
     "jsonpointer": {
       "version": "5.0.1"
     },
+    "jszip": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz",
+      "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+      "requires": {
+        "lie": "~3.3.0",
+        "pako": "~1.0.2",
+        "readable-stream": "~2.3.6",
+        "setimmediate": "^1.0.5"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+        },
+        "readable-stream": {
+          "version": "2.3.8",
+          "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
+          "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.3",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~2.0.0",
+            "safe-buffer": "~5.1.1",
+            "string_decoder": "~1.1.1",
+            "util-deprecate": "~1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+        },
+        "string_decoder": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+          "requires": {
+            "safe-buffer": "~5.1.0"
+          }
+        }
+      }
+    },
     "keycode": {
       "version": "2.2.1"
     },
@@ -14216,6 +14358,14 @@
     "lib-flexible": {
       "version": "0.3.2"
     },
+    "lie": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz",
+      "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+      "requires": {
+        "immediate": "~3.0.5"
+      }
+    },
     "liftoff": {
       "version": "4.0.0",
       "dev": true,
@@ -14783,6 +14933,11 @@
         "aggregate-error": "^3.0.0"
       }
     },
+    "pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+    },
     "param-case": {
       "version": "3.0.4",
       "dev": true,
@@ -15024,6 +15179,11 @@
     "process": {
       "version": "0.5.2"
     },
+    "process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+    },
     "proxy-from-env": {
       "version": "1.1.0"
     },
@@ -15392,6 +15552,11 @@
         "randombytes": "^2.1.0"
       }
     },
+    "setimmediate": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz",
+      "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
+    },
     "shebang-command": {
       "version": "2.0.0",
       "dev": true,
@@ -15948,8 +16113,7 @@
       "version": "1.1.12"
     },
     "util-deprecate": {
-      "version": "1.0.2",
-      "dev": true
+      "version": "1.0.2"
     },
     "utrie": {
       "version": "1.0.2",

+ 2 - 0
package.json

@@ -35,7 +35,9 @@
     "dayjs": "^1.11.7",
     "echarts": "^5.4.2",
     "eventemitter3": "^5.0.1",
+    "file-saver": "^2.0.5",
     "html2canvas": "^1.4.1",
+    "jszip": "^3.10.1",
     "lib-flexible": "^0.3.2",
     "lodash": "^4.17.21",
     "lodash-es": "^4.17.21",

BIN
src/common/images/icon-sort-asc.png


BIN
src/common/images/icon-sort-default.png


BIN
src/common/images/icon-sort-desc.png


+ 90 - 41
src/components/card-type/index.tsx

@@ -14,6 +14,8 @@ import AudioPlayer from './audio-player';
 import VideoPlayer from './video-player';
 import { PageEnum } from '/src/enums/pageEnum';
 import { api_musicSheetDetail } from '/src/api/user';
+import JSZip, { file } from 'jszip';
+import { saveAs } from 'file-saver';
 
 type itemType = {
   id: string | number;
@@ -117,47 +119,80 @@ export default defineComponent({
       return typeImg;
     };
 
+    // 获取文件blob格式
+    const getFileBlob = (url: string) => {
+      return new Promise((resolve, reject) => {
+        const request = new XMLHttpRequest();
+        request.open('GET', url, true);
+        request.responseType = 'blob';
+        request.onload = (res: any) => {
+          if (res.target.status == 200) {
+            resolve(res.target.response);
+          } else {
+            reject(res);
+          }
+        };
+        request.send();
+      });
+    };
+
+    // 多个文件下载
+    const downLoadMultiFile = (files: any, filesName: string) => {
+      const zip = new JSZip();
+      const result = [];
+      for (const i in files) {
+        const promise = getFileBlob(files[i].url).then((res: any) => {
+          zip.file(files[i].name, res, { binary: true });
+        });
+        result.push(promise);
+      }
+      Promise.all(result)
+        .then(() => {
+          zip.generateAsync({ type: 'blob' }).then(res => {
+            saveAs(
+              res,
+              filesName
+                ? filesName + Date.now() + '.zip'
+                : `文件夹${Date.now()}.zip`
+            );
+          });
+        })
+        .catch(() => {
+          message.error('下载失败');
+        });
+
+      downloadStatus.value = false;
+    };
+
     const downloadFile = (filename: string, fileUrl: string) => {
       // 发起Fetch请求
       fetch(fileUrl)
         .then(response => response.blob())
         .then(blob => {
-          // 创建一个Blob URL
-          const blobUrl = URL.createObjectURL(blob);
-
-          // 创建一个链接元素
-          const link = document.createElement('a');
-
-          // 设置链接的href属性为Blob URL
-          link.href = blobUrl;
-
-          // 设置下载时文件的名称
-          link.download = filename;
-
-          // 将链接元素添加到文档中
-          document.body.appendChild(link);
-
-          // 模拟点击链接以触发文件下载
-          link.click();
-
-          // 移除链接元素
-          document.body.removeChild(link);
-
-          // 释放Blob URL
-          URL.revokeObjectURL(blobUrl);
-
+          saveAs(blob, filename);
           setTimeout(() => {
             downloadStatus.value = false;
           }, 100);
         })
-        .catch(error => {
-          console.error('文件下载失败', error);
+        .catch(() => {
           message.error('下载失败');
         });
 
       downloadStatus.value = false;
     };
 
+    const getFileName = (url: any) => {
+      // 使用正则表达式获取文件名
+      const tempUrl = url.split('?');
+      const fileNameRegex = /\/([^\\/]+)$/; // 匹配最后一个斜杠后的内容
+      const match = tempUrl[0].match(fileNameRegex);
+
+      if (match) {
+        return match[1];
+      } else {
+        return '';
+      }
+    };
     const onDownload = async (e: MouseEvent) => {
       e.stopPropagation();
       e.preventDefault();
@@ -168,16 +203,31 @@ export default defineComponent({
       }
       if (downloadStatus.value) return false;
       downloadStatus.value = true;
+      const suffix: any = item.content?.split('.');
+      const fileName = item.title + '.' + suffix[suffix?.length - 1];
       if (item.type === 'MUSIC') {
         const { data } = await api_musicSheetDetail(item.content);
-        console.log(data, '1212');
+        const urls = [];
+        if (data.xmlFileUrl) {
+          urls.push({
+            url: data.xmlFileUrl,
+            name: getFileName(data.xmlFileUrl)
+          });
+        }
+        if (data.background && data.background.length > 0) {
+          data.background.forEach((item: any) => {
+            urls.push({
+              url: item.audioFileUrl,
+              name: getFileName(item.audioFileUrl)
+            });
+          });
+        }
+        downLoadMultiFile(urls, item.title);
+
         // setTimeout(() => {
         //   downloadStatus.value = false;
         // }, 1000);
       } else {
-        // const link = document.createElement('a');
-        const suffix: any = item.content?.split('.');
-        const fileName = item.title + '.' + suffix[suffix?.length - 1];
         downloadFile(fileName, item.content);
       }
     };
@@ -185,6 +235,7 @@ export default defineComponent({
     return () => (
       <div
         onClick={() => emit('click', props.item)}
+        key={props.item.id}
         draggable={!props.draggable ? false : props.item.exist ? false : true}
         class={[
           styles['card-section'],
@@ -293,17 +344,15 @@ export default defineComponent({
                 {/* 收藏 */}
                 <div class={styles.btnGroup}>
                   {props.isDownload && (
-                    <NSpin
-                      show={downloadStatus.value}
-                      class={styles.btnItem}
-                      size={'small'}>
-                      <img
-                        src={iconDownload}
-                        key="3"
-                        class={[styles.iconCollect]}
-                        onClick={onDownload}
-                      />
-                    </NSpin>
+                    <div class={styles.btnItem} onClick={onDownload}>
+                      <NSpin show={downloadStatus.value} size={'small'}>
+                        <img
+                          src={iconDownload}
+                          key="3"
+                          class={[styles.iconCollect]}
+                        />
+                      </NSpin>
+                    </div>
                   )}
                   {props.isShowCollect && (
                     <div

+ 1 - 1
src/views/attend-class/component/tools/pen.tsx

@@ -99,7 +99,7 @@ export default defineComponent({
             {/* <div class={styles.modelAttendContent}>
               {data.modalAttendMessage}
             </div> */}
-            <NSpace class={styles.btnGroupModal}>
+            <NSpace class={styles.btnGroupModal} justify="center">
               <NButton
                 type="default"
                 round

BIN
src/views/attend-class/image/right_icon1.png


BIN
src/views/attend-class/image/right_icon2.png


BIN
src/views/attend-class/image/right_icon9.png


+ 54 - 8
src/views/attend-class/index.module.less

@@ -613,6 +613,7 @@
   width: 100%;
   height: 100%;
   position: relative;
+
   .pptBox {
     position: absolute;
     width: 100%;
@@ -637,6 +638,7 @@
   align-items: center;
   padding: 10px 0;
   transition: all 0.3s;
+
   .rightItem {
     width: 54px;
     height: 54px;
@@ -647,11 +649,14 @@
     align-items: center;
     justify-content: center;
     z-index: 9;
-    &.active, &:hover {
+
+    &.active,
+    &:hover {
       background: RGBA(255, 255, 255, 0.3);
       border-radius: 8px;
       z-index: 9;
     }
+
     &::after {
       content: "";
       position: absolute;
@@ -662,12 +667,14 @@
       height: 1px;
       background: rgba(255, 255, 255, 0.2);
     }
+
     img {
       width: 34px;
       height: 34px;
       margin: auto;
       z-index: 9;
     }
+
     .rightTips {
       position: absolute;
       right: 24px;
@@ -687,66 +694,80 @@
       z-index: 1;
       opacity: 0;
     }
+
     &:hover {
       .rightTips {
         opacity: 1;
       }
     }
   }
+
   .rightItem:last-child {
     width: 54px;
     height: 36px;
+
     img {
       width: 18px;
       height: 18px;
     }
+
     &::after {
       display: none;
     }
   }
+
   .itemDisabled {
     opacity: 0.7;
     cursor: not-allowed;
   }
-} 
+}
+
 .rightColumnZ {
   z-index: -1;
 }
+
 .rightColumnHide {
-  transform: translate(80px,-50%);
+  transform: translate(80px, -50%);
+
   .rightTips {
     display: none;
   }
 }
+
 .rightHideIcon {
   width: 30px;
   height: 60px;
   position: absolute;
   right: 0;
   top: 50%;
-  transform: translate(60px,-50%);
+  transform: translate(60px, -50%);
   z-index: 10;
   cursor: pointer;
   // transition: all 0.5s;
 }
+
 .rightIconShow {
-  animation:rightIn 0.3s ease 0.3s;
+  animation: rightIn 0.3s ease 0.3s;
   animation-fill-mode: forwards;
 }
+
 @keyframes rightIn {
   0% {
-    transform: translate(60px,-50%);
+    transform: translate(60px, -50%);
   }
+
   100% {
-    transform: translate(0,-50%);
+    transform: translate(0, -50%);
   }
 }
+
 .bottomColumn {
   position: fixed;
   right: 40px;
   bottom: 26px;
   display: flex;
   z-index: 2;
+
   .bottomItem {
     width: 54px;
     height: 54px;
@@ -758,6 +779,7 @@
     margin-right: 16px;
     position: relative;
     cursor: pointer;
+
     .bottomTips {
       position: absolute;
       left: 50%;
@@ -775,22 +797,46 @@
       font-weight: 500;
       color: #FFFFFF;
     }
-    &.active, &:hover {
+
+    &.active,
+    &:hover {
       background: rgba(0, 0, 0, 0.5);
+
       .bottomTips {
         opacity: 1;
       }
     }
+
     img {
       width: 40px;
       height: 40px;
     }
   }
+
   .bottomItem:last-child {
     margin-right: 0;
   }
+
   .itemDisabled {
     opacity: 0.7;
     cursor: not-allowed;
   }
+}
+
+.selectMusicModal {
+  position: relative;
+  width: 1352px;
+
+  :global {
+    .n-card-header {
+      position: absolute;
+      top: 0;
+      left: 0;
+      right: 0;
+
+      .n-card-header__main {
+        color: #fff;
+      }
+    }
+  }
 }

+ 99 - 228
src/views/attend-class/index.tsx

@@ -62,17 +62,17 @@ import Chapter from './model/chapter';
 import { useRouter } from 'vue-router';
 import { useUserStore } from '@/store/modules/users';
 
-import iconBeatIcon from './new-image/icon-beatIcon.png';
-import iconChange from './new-image/icon-change.png';
-import iconDown from './new-image/icon-down.png';
-import iconMenu from './new-image/icon-menu.png';
+// import iconBeatIcon from './new-image/icon-beatIcon.png';
+// import iconChange from './new-image/icon-change.png';
+// import iconDown from './new-image/icon-down.png';
+// import iconMenu from './new-image/icon-menu.png';
 import iconNote from './new-image/icon-note.png';
-import iconOverClass from './new-image/icon-overclass.png';
-import iconSetTime from './new-image/icon-setTime.png';
-import iconToneIcon from './new-image/icon-toneIcon.png';
-import iconUp from './new-image/icon-up.png';
+// import iconOverClass from './new-image/icon-overclass.png';
+// import iconSetTime from './new-image/icon-setTime.png';
+// import iconToneIcon from './new-image/icon-toneIcon.png';
+// import iconUp from './new-image/icon-up.png';
 import iconWhite from './new-image/icon-white.png';
-import iconWork from './new-image/icon-work.png';
+// import iconWork from './new-image/icon-work.png';
 
 import rightIconEnd from './image/right_icon1.png';
 import rightIconArrange from './image/right_icon2.png';
@@ -82,11 +82,13 @@ import rightIconMetronome from './image/right_icon5.png';
 import rightIconTuner from './image/right_icon6.png';
 import rightIconTimer from './image/right_icon7.png';
 import rightIconPackUp from './image/right_icon8.png';
+import rightIconMusic from './image/right_icon9.png';
 import bottomIconSwitch from './image/bottom_icon1.png';
 import bottomIconResource from './image/bottom_icon2.png';
 import bottomIconPre from './image/bottom_icon3.png';
 import bottomIconNext from './image/bottom_icon4.png';
 import rightHideIcon from './image/right_hide_icon.png';
+import SelectResources from '../prepare-lessons/model/select-resources';
 
 export type ToolType = 'init' | 'pen' | 'whiteboard';
 export type ToolItem = {
@@ -186,6 +188,7 @@ export default defineComponent({
       removeTitle: '',
       removeContent: '',
 
+      selectResourceStatus: false,
       videoState: 'init' as 'init' | 'play',
       videoItemRef: null as any,
       animationState: 'start' as 'start' | 'end'
@@ -317,19 +320,19 @@ export default defineComponent({
       }
     });
 
-    const onFullScreen = () => {
-      if (data.type === 'preview') {
-        const el: any = document.querySelector('#app');
-
-        if (el.mozRequestFullScreen) {
-          el.mozRequestFullScreen();
-        } else if (el.webkitRequestFullscreen) {
-          el.webkitRequestFullscreen();
-        } else if (el.requestFullScreen) {
-          el.requestFullscreen();
-        }
-      }
-    };
+    // const onFullScreen = () => {
+    //   if (data.type === 'preview') {
+    //     const el: any = document.querySelector('#app');
+
+    //     if (el.mozRequestFullScreen) {
+    //       el.mozRequestFullScreen();
+    //     } else if (el.webkitRequestFullscreen) {
+    //       el.webkitRequestFullscreen();
+    //     } else if (el.requestFullScreen) {
+    //       el.requestFullscreen();
+    //     }
+    //   }
+    // };
 
     const popupData = reactive({
       open: false,
@@ -387,25 +390,25 @@ export default defineComponent({
       }
     };
 
-    const activeName = computed(() => {
-      let name = '';
-      // data.knowledgePointList.forEach((item: any, index: number) => {
-      //   if (popupData.activeIndex === index) {
-      //     name = item.title;
-      //   }
-      // });
-      popupData.chapterDetails.forEach((chapter: any) => {
-        if (chapter.id === data.lessonCoursewareDetailId) {
-          name = chapter.name;
-          chapter.knowledgeList?.forEach((know: any) => {
-            if (know.id === data.detailId) {
-              name += ' - ' + know.name;
-            }
-          });
-        }
-      });
-      return name;
-    });
+    // const activeName = computed(() => {
+    //   let name = '';
+    //   // data.knowledgePointList.forEach((item: any, index: number) => {
+    //   //   if (popupData.activeIndex === index) {
+    //   //     name = item.title;
+    //   //   }
+    //   // });
+    //   popupData.chapterDetails.forEach((chapter: any) => {
+    //     if (chapter.id === data.lessonCoursewareDetailId) {
+    //       name = chapter.name;
+    //       chapter.knowledgeList?.forEach((know: any) => {
+    //         if (know.id === data.detailId) {
+    //           name += ' - ' + know.name;
+    //         }
+    //       });
+    //     }
+    //   });
+    //   return name;
+    // });
 
     /**停止所有的播放 */
     const handleStop = () => {
@@ -857,28 +860,28 @@ export default defineComponent({
       }
     });
 
-    const toggleListenerKeyUp = (type: string) => {
-      if (type === 'remove') {
-        document.body.removeEventListener('keyup', () => {
-          listenerKeyUpState.value = false;
-        });
-      } else {
-        // 监听页面键盘事件 - 上下切换
-        document.body.addEventListener('keyup', (e: KeyboardEvent) => {
-          // console.log(e, 'e');
-          if (e.code === 'ArrowLeft') {
-            // if (popupData.activeIndex === 0) return;
-            setModalOpen();
-            handlePreAndNext('up');
-          } else if (e.code === 'ArrowRight') {
-            // if (popupData.activeIndex === data.itemList.length - 1) return;
-            setModalOpen();
-            handlePreAndNext('down');
-          }
-        });
-        listenerKeyUpState.value = true;
-      }
-    };
+    // const toggleListenerKeyUp = (type: string) => {
+    //   if (type === 'remove') {
+    //     document.body.removeEventListener('keyup', () => {
+    //       listenerKeyUpState.value = false;
+    //     });
+    //   } else {
+    //     // 监听页面键盘事件 - 上下切换
+    //     document.body.addEventListener('keyup', (e: KeyboardEvent) => {
+    //       // console.log(e, 'e');
+    //       if (e.code === 'ArrowLeft') {
+    //         // if (popupData.activeIndex === 0) return;
+    //         setModalOpen();
+    //         handlePreAndNext('up');
+    //       } else if (e.code === 'ArrowRight') {
+    //         // if (popupData.activeIndex === data.itemList.length - 1) return;
+    //         setModalOpen();
+    //         handlePreAndNext('down');
+    //       }
+    //     });
+    //     listenerKeyUpState.value = true;
+    //   }
+    // };
 
     // 监听切换到ppt课件时,手动移除键盘监听器
     // watch(() => popupData.activeIndex, () => {
@@ -910,20 +913,21 @@ export default defineComponent({
 
     /** 打开教学工具 */
     const openStudyTool = (item: ToolItem) => {
-      const activeItem = data.itemList[popupData.activeIndex];
-      // 暂停视频和曲谱的播放
-      if (activeItem.type === 'VIDEO' && activeItem.videoEle) {
-        activeItem.videoEle?.pause();
-      }
-      if (activeItem.type === 'SONG' && activeItem.audioEle) {
-        activeItem.audioEle?.stop();
-      }
-      if (activeItem.type === 'MUSIC') {
-        activeItem.iframeRef?.contentWindow?.postMessage(
-          { api: 'setPlayState' },
-          '*'
-        );
-      }
+      // const activeItem = data.itemList[popupData.activeIndex];
+      // // 暂停视频和曲谱的播放
+      // if (activeItem.type === 'VIDEO' && activeItem.videoEle) {
+      //   activeItem.videoEle?.pause();
+      // }
+      // if (activeItem.type === 'SONG' && activeItem.audioEle) {
+      //   activeItem.audioEle?.stop();
+      // }
+      // if (activeItem.type === 'MUSIC') {
+      //   activeItem.iframeRef?.contentWindow?.postMessage(
+      //     { api: 'setPlayState' },
+      //     '*'
+      //   );
+      // }
+      handleStop();
       clearModel();
       popupData.toolOpen = false;
       studyData.type = item.type;
@@ -956,12 +960,6 @@ export default defineComponent({
       if (val == 'toneIcon') {
         showModalTone.value = true;
       }
-      // if (val == 'iconNote2') {
-      //   if (NPopoverRef.value) {
-      //     NPopoverRef.value.setShow(false);
-      //   }
-      //   eventGlobal.emit('teacher-guideInfo-attend-class', 'attend-class');
-      // }
     };
 
     // 是否允许上一页
@@ -973,7 +971,6 @@ export default defineComponent({
        * 4,判断当前章节上面还没有其它章节;
        * 5,判断上面章节里面课程是否有资源;
        */
-
       if (popupData.activeIndex > 0) {
         return true;
       }
@@ -1119,6 +1116,11 @@ export default defineComponent({
         id: 2
       },
       {
+        name: '曲目资源',
+        icon: rightIconMusic,
+        id: 9
+      },
+      {
         name: '批注',
         icon: rightIconPostil,
         id: 3
@@ -1230,6 +1232,11 @@ export default defineComponent({
         case 8:
           rightColumnShow.value = false;
           break;
+        case 9:
+          // 选择曲目时需要暂停所有播放
+          handleStop();
+          data.selectResourceStatus = true;
+          break;
         default:
           break;
       }
@@ -1462,150 +1469,6 @@ export default defineComponent({
           </div>
         </div>
 
-        {/* 头部样式 */}
-        {/* <div
-          style={{ transform: activeData.model ? '' : 'translateY(-100%)' }}
-          class={styles.headerContainer}>
-          <div class={styles.menu}>{activeName.value}</div>
-        </div> */}
-        {/* 布置作业按钮 */}
-
-        {/* <div
-          onClick={(e: any) => {
-            e.stopPropagation();
-            if (activeData.timer){
-              setModelOpen();
-            }
-          }}
-          class={[
-            styles.switchDisplaySection,
-            activeData.model ? '' : styles.sectionAnimate
-          ]}>
-          <NSpace class={styles.switchSpace}>
-            <div
-              class={styles.btnItem}
-              onClick={async () => {
-                if (data.type === 'preview') {
-                  handleStop();
-                  data.removeVisiable = true;
-                  data.removeTitle = '结束预览';
-                  data.removeContent = '请确认是否结束预览?';
-                } else {
-                  data.removeVisiable = true;
-                  data.removeTitle = '结束课程';
-                  data.removeContent = '请确认是否结束课程?';
-                }
-              }}>
-              <img src={iconOverClass} />
-              <p>{data.type !== 'preview' ? '结束课程' : '结束预览'}</p>
-            </div>
-            {data.type !== 'preview' && (
-              <div
-                class={[
-                  styles.btnItem,
-                  data.preStudentNum <= 0 ? styles.btnsDisabled : ''
-                ]}
-                onClick={async () => {
-                  // 学生人数必须大于0,才可以布置作业
-                  if (data.preStudentNum <= 0) return;
-                  const res = await lessonPreTrainingPage({
-                    coursewareKnowledgeDetailId: data.detailId,
-                    subjectId: data.subjectId,
-                    page: 1,
-                    rows: 99
-                  });
-                  if (res.data.rows && res.data.rows.length) {
-                    data.modalAttendMessage =
-                      '本节课已设置课后作业,是否布置?';
-                  }
-                  data.modelAttendStatus = true;
-                }}>
-                <img src={iconWork} />
-                <p>布置作业</p>
-              </div>
-            )}
-
-            <div
-              class={styles.btnItem}
-              onClick={() =>
-                openStudyTool({
-                  type: 'pen',
-                  icon: iconNote,
-                  name: '批注'
-                })
-              }>
-              <img src={iconNote} />
-              <p>批注</p>
-            </div>
-            <div
-              class={styles.btnItem}
-              onClick={() =>
-                openStudyTool({
-                  type: 'whiteboard',
-                  icon: iconWhite,
-                  name: '白板'
-                })
-              }>
-              <img src={iconWhite} />
-              <p>白板</p>
-            </div>
-            <div
-              class={styles.btnItem}
-              onClick={() => startShowModal('beatIcon')}>
-              <img src={iconToneIcon} />
-              <p>节拍器</p>
-            </div>
-            <div
-              class={styles.btnItem}
-              onClick={() => startShowModal('toneIcon')}>
-              <img src={iconSetTime} />
-              <p>调音器</p>
-            </div>
-            <div
-              class={styles.btnItem}
-              onClick={() => startShowModal('setTimeIcon')}>
-              <img src={iconBeatIcon} />
-              <p>计时器</p>
-            </div>
-          </NSpace>
-          <NSpace class={styles.switchSpace}>
-            <div
-              class={styles.btnItem}
-              onClick={() => (popupData.chapterOpen = true)}>
-              <img src={iconChange} />
-              <p>切换章节</p>
-            </div>
-            <div class={styles.btnItem} onClick={() => (popupData.open = true)}>
-              <img src={iconMenu} />
-              <p>资源列表</p>
-            </div>
-            <div
-              class={[
-                styles.btnItem,
-                !isUpArrow.value ? styles.btnsDisabled : ''
-              ]}
-              onClick={() => {
-                if (!isUpArrow.value) return;
-                handlePreAndNext('up');
-              }}>
-              <img src={iconUp} />
-              <p>上一个</p>
-            </div>
-            <div
-              class={[
-                styles.btnItem,
-                !isDownArrow.value ? styles.btnsDisabled : ''
-              ]}
-              onClick={() => {
-                if (!isDownArrow.value) return;
-                handlePreAndNext('down');
-              }}>
-              <img src={iconDown} />
-              <p>下一个</p>
-            </div>
-          </NSpace>
-        </div> */}
-
         {/* 右边操作栏 */}
         <div
           class={[
@@ -1848,6 +1711,14 @@ export default defineComponent({
             <TimerMeter></TimerMeter>
           </div>
         </NModal>
+
+        <NModal
+          v-model:show={data.selectResourceStatus}
+          class={['modalTitle', styles.selectMusicModal]}
+          preset="card"
+          title={'选择资源'}>
+          <SelectResources from="class" />
+        </NModal>
         <NModal
           transformOrigin="center"
           v-model:show={data.removeVisiable}

+ 108 - 22
src/views/home/components/practiceRanking.tsx

@@ -1,22 +1,13 @@
-import { defineComponent, reactive, onMounted, computed } from 'vue';
+import { defineComponent, reactive, onMounted, computed, nextTick } from 'vue';
 import styles from '../index2.module.less';
-import {
-  NButton,
-  NDataTable,
-  NForm,
-  NFormItem,
-  NImage,
-  NSelect,
-  NSpace
-} from 'naive-ui';
-import SearchInput from '@/components/searchInput';
-import CSelect from '@/components/CSelect';
+import { NDataTable, NTooltip } from 'naive-ui';
 import Pagination from '@/components/pagination';
-import add from './images/add.png';
 import { getMinutes, getSecend, getTimes } from '/src/utils/dateFormat';
 import { getTestList } from '../../classList/api';
-import dayjs from 'dayjs';
 import TheEmpty from '/src/components/TheEmpty';
+import iconSortDefault from '@/common/images/icon-sort-default.png';
+import iconSortDesc from '@/common/images/icon-sort-desc.png';
+import iconSortAsc from '@/common/images/icon-sort-asc.png';
 export default defineComponent({
   name: 'student-studentList',
   props: {
@@ -78,11 +69,20 @@ export default defineComponent({
       }
     };
     expose({ getList });
-    onMounted(() => {
-      getList();
+    onMounted(async () => {
+      await getList();
+
+      nextTick(() => {
+        // 把默认的排序删除
+        const dom = document.querySelectorAll('.n-data-table-sorter');
+        dom.forEach((item: any) => {
+          item.style.display = 'none';
+        });
+      });
     });
 
     const handleSorterChange = (sroter: any) => {
+      console.log(sroter, '12');
       if (!sroter) {
         searchForm.ase = 0;
         searchForm.sortType = 1;
@@ -123,19 +123,76 @@ export default defineComponent({
     };
 
     const practiceDaysRef = reactive({
-      title: '练习天数',
+      title() {
+        return (
+          <NTooltip showArrow={false} placement="top-start">
+            {{
+              trigger: () => (
+                <div class={styles.cell}>
+                  练习天数
+                  <img
+                    class={styles.sortIcon}
+                    src={
+                      practiceDaysRef.sortOrder === 'descend'
+                        ? iconSortDesc
+                        : practiceDaysRef.sortOrder === 'ascend'
+                        ? iconSortAsc
+                        : iconSortDefault
+                    }
+                  />
+                </div>
+              ),
+              default:
+                practiceDaysRef.sortOrder === 'descend'
+                  ? '点击升序'
+                  : practiceDaysRef.sortOrder === 'ascend'
+                  ? '取消排序'
+                  : '点击降序'
+            }}
+          </NTooltip>
+        );
+      },
       key: 'practiceDays',
       sorter: true,
-      sortOrder: false,
+      sortOrder: false as any,
       render(row: any) {
         return <>{row.practiceDays ? row.practiceDays : 0}天</>;
       }
     });
     const practiceDurationRef = reactive({
-      title: '练习总时长',
+      // title: '练习总时长',
+      title() {
+        return (
+          <NTooltip showArrow={false} placement="top-start">
+            {{
+              trigger: () => (
+                <div class={styles.cell}>
+                  练习总时长
+                  <img
+                    class={styles.sortIcon}
+                    src={
+                      practiceDurationRef.sortOrder === 'descend'
+                        ? iconSortDesc
+                        : practiceDurationRef.sortOrder === 'ascend'
+                        ? iconSortAsc
+                        : iconSortDefault
+                    }
+                  />
+                </div>
+              ),
+              default:
+                practiceDaysRef.sortOrder === 'descend'
+                  ? '点击升序'
+                  : practiceDaysRef.sortOrder === 'ascend'
+                  ? '取消排序'
+                  : '点击降序'
+            }}
+          </NTooltip>
+        );
+      },
       key: 'practiceDuration',
       sorter: true,
-      sortOrder: false,
+      sortOrder: false as any,
       render(row: any) {
         return (
           <>
@@ -152,10 +209,39 @@ export default defineComponent({
       }
     });
     const practiceDurationAvgRef = reactive({
-      title: '平均练习时长',
+      // title: '平均练习时长',
+      title() {
+        return (
+          <NTooltip showArrow={false} placement="top-start">
+            {{
+              trigger: () => (
+                <div class={styles.cell}>
+                  平均练习时长
+                  <img
+                    class={styles.sortIcon}
+                    src={
+                      practiceDurationAvgRef.sortOrder === 'descend'
+                        ? iconSortDesc
+                        : practiceDurationAvgRef.sortOrder === 'ascend'
+                        ? iconSortAsc
+                        : iconSortDefault
+                    }
+                  />
+                </div>
+              ),
+              default:
+                practiceDaysRef.sortOrder === 'descend'
+                  ? '点击升序'
+                  : practiceDaysRef.sortOrder === 'ascend'
+                  ? '取消排序'
+                  : '点击降序'
+            }}
+          </NTooltip>
+        );
+      },
       key: 'practiceDurationAvg',
       sorter: true,
-      sortOrder: false,
+      sortOrder: false as any,
       render(row: any) {
         return (
           <>

+ 11 - 0
src/views/home/index2.module.less

@@ -899,4 +899,15 @@
 
 .showUpdatePassword {
   width: 514px;
+}
+
+.cell {
+  display: flex;
+  align-items: center;
+
+  .sortIcon {
+    margin-left: 7px;
+    width: 13px;
+    height: 13px;
+  }
 }

+ 56 - 43
src/views/prepare-lessons/components/lesson-main/courseware/index.tsx

@@ -1,4 +1,4 @@
-import { defineComponent, onMounted, reactive, watch } from 'vue';
+import { defineComponent, nextTick, onMounted, reactive, watch } from 'vue';
 import styles from './index.module.less';
 import {
   NButton,
@@ -32,6 +32,7 @@ import { state } from '/src/state';
 import SubjectSync from '../../../model/subject-sync';
 import { eventGlobal } from '/src/utils';
 import iconTips from '../../../images/icon-tips.png';
+import { useElementBounding } from '@vueuse/core';
 export default defineComponent({
   name: 'courseware-modal',
   setup() {
@@ -302,27 +303,6 @@ export default defineComponent({
           'prepareLessonSubjectId'
         );
         // // 先取 上次上课声部,在取班级声部 最后取缓存
-        // const subjectId =
-        //   forms.courseScheduleSubjectId ||
-        //   forms.subjectId ||
-        //   localStorageSubjectId
-        //     ? Number(
-        //         forms.courseScheduleSubjectId ||
-        //           forms.subjectId ||
-        //           localStorageSubjectId
-        //       )
-        //     : null;
-        // // 判断浏览器上面是否有
-        // const index = subjectList.findIndex(
-        //   (subject: any) => subject.id == subjectId
-        // );
-
-        // if (subjectId && index >= 0) {
-        //   prepareStore.setSubjectId(subjectId);
-        // } else {
-        //   // 判断是否有缓存
-        //   prepareStore.setSubjectId(subjectList[0].id);
-        // }
         let subjectId = null;
         let index = -1;
         if (forms.courseScheduleSubjectId) {
@@ -385,6 +365,17 @@ export default defineComponent({
       }
     );
 
+    const isPointInsideElement = (element: any, x: number, y: number) => {
+      const rect = element.getBoundingClientRect();
+      return (
+        x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom
+      );
+    };
+    const isPointOnLeft = (element: any, x: number) => {
+      const rect = element.getBoundingClientRect();
+      const elementCenterX = rect.left + rect.width / 2;
+      return x < elementCenterX;
+    };
     onMounted(async () => {
       prepareStore.setClassGroupId(route.query.classGroupId as any);
       // 获取教材分类列表
@@ -393,12 +384,31 @@ export default defineComponent({
       await getList();
 
       // 动态添加数据
-      eventGlobal.on('onPrepareAddItem', (item: any) => {
+      eventGlobal.on('onPrepareAddItem', (item: any, point?: any) => {
         forms.drag = true;
-        forms.coursewareList.push(item);
-        prepareStore.setCoursewareList(forms.coursewareList);
-
         forms.isEdit = true;
+        nextTick(() => {
+          if (point) {
+            const dom = document.querySelectorAll('.row-nav');
+            dom.forEach((child: any, index: number) => {
+              const status = isPointInsideElement(child, point.x, point.y);
+              if (status) {
+                const array: any = forms.coursewareList;
+                const left = isPointOnLeft(child, point.x);
+                if (!left) {
+                  array.splice(index + 1, 0, item);
+                } else {
+                  array.splice(index, 0, item);
+                }
+                forms.coursewareList = array;
+                prepareStore.setCoursewareList(forms.coursewareList);
+              }
+            });
+          } else {
+            forms.coursewareList.push(item);
+            prepareStore.setCoursewareList(forms.coursewareList);
+          }
+        });
       });
     });
 
@@ -406,13 +416,6 @@ export default defineComponent({
       <div class={styles.coursewareModal}>
         <div class={styles.btnGroup}>
           {forms.drag ? (
-            // <div class={styles.tipsContainer}>
-            //   <div class={styles.tipsLeft}>
-            //     <img src={iconTips} class={styles.iconTips} />
-            //     <span class={styles.tips}>拖动可将资源进行排序哦~</span>
-            //   </div>
-            //   <span class={styles.btnNoTips}>不再提醒</span>
-            // </div>
             !forms.tipsStatus ? (
               <div class={styles.tipsContainer}>
                 <div class={styles.tipsLeft}>
@@ -543,20 +546,30 @@ export default defineComponent({
                 e.preventDefault();
               }}
               onDrop={(e: any) => {
+                console.log(e, 'event');
                 let dropItem = e.dataTransfer.getData('text');
                 dropItem = dropItem ? JSON.parse(dropItem) : {};
                 // 判断是否有数据
                 if (dropItem.id) {
-                  eventGlobal.emit('onPrepareAddItem', {
-                    materialId: dropItem.id,
-                    coverImg: dropItem.coverImg,
-                    type: dropItem.type,
-                    title: dropItem.title,
-                    isCollect: dropItem.isCollect,
-                    isSelected: dropItem.isSelected,
-                    content: dropItem.content,
-                    removeFlag: false
-                  });
+                  // 获取拖拽的目标元素
+
+                  eventGlobal.emit(
+                    'onPrepareAddItem',
+                    {
+                      materialId: dropItem.id,
+                      coverImg: dropItem.coverImg,
+                      type: dropItem.type,
+                      title: dropItem.title,
+                      isCollect: dropItem.isCollect,
+                      isSelected: dropItem.isSelected,
+                      content: dropItem.content,
+                      removeFlag: false
+                    },
+                    {
+                      x: e.clientX,
+                      y: e.clientY
+                    }
+                  );
                 }
               }}>
               {forms.coursewareList.length > 0 && (

+ 1 - 1
src/views/prepare-lessons/components/resource-main/components/select-music/index.tsx

@@ -165,7 +165,7 @@ export default defineComponent({
     onMounted(() => {
       getList();
 
-      eventGlobal.on('onTrainDragItem', (item: any) => {
+      eventGlobal.on('onTrainDragItem', (item: any, point?: any) => {
         console.log('onTrainDragItem', Date.now());
         onAdd(item);
       });

+ 11 - 18
src/views/prepare-lessons/model/select-resources/index.tsx

@@ -10,6 +10,11 @@ export default defineComponent({
     type: {
       type: String,
       default: 'myResources'
+    },
+    /** 从哪里使用 */
+    from: {
+      type: String,
+      default: ''
     }
   },
   emits: ['select'],
@@ -48,26 +53,14 @@ export default defineComponent({
           onUpdate:value={(val: string) => {
             tabType.value = val;
           }}>
-          <NTabPane
-            name="myResources"
-            tab="我的资源"
-            // displayDirective="show:lazy"
-          >
-            <SelectItem type="myResources" />
+          <NTabPane name="myResources" tab="我的资源">
+            <SelectItem type="myResources" from={props.from} />
           </NTabPane>
-          <NTabPane
-            name="shareResources"
-            tab="共享资源"
-            // displayDirective="show:lazy"
-          >
-            <SelectItem type="shareResources" />
+          <NTabPane name="shareResources" tab="共享资源">
+            <SelectItem type="shareResources" from={props.from} />
           </NTabPane>
-          <NTabPane
-            name="myCollect"
-            tab="我的收藏"
-            // displayDirective="show:lazy"
-          >
-            <SelectItem type="myCollect" />
+          <NTabPane name="myCollect" tab="我的收藏">
+            <SelectItem type="myCollect" from={props.from} />
           </NTabPane>
         </NTabs>
       </div>

+ 149 - 0
src/views/prepare-lessons/model/select-resources/select-item/class-search-group/index.module.less

@@ -0,0 +1,149 @@
+.searchGroup {
+  position: relative;
+  padding: 0 40px;
+
+
+  .btnType {
+    gap: 0px 24px !important;
+
+    :global {
+      .n-button {
+        height: 37px;
+        padding: 0 24px;
+        font-size: 18px;
+        color: rgba(0, 0, 0, .6);
+
+        &.n-button--primary-type {
+          font-weight: bold;
+          color: #fff;
+        }
+      }
+    }
+  }
+
+  :global {
+    .n-form {
+      position: relative;
+    }
+
+    .n-form-item {
+      .n-form-item-label {
+        font-size: max(17px, 13px);
+        font-weight: 600;
+        color: #131415;
+        line-height: 24px;
+      }
+
+      .n-button {
+        height: 32px;
+        font-size: max(17px, 13px);
+        border-radius: 8px;
+        color: rgba(0, 0, 0, 0.6);
+      }
+
+      .n-button--primary-type {
+        color: #131415;
+        background-color: #D2ECFF !important;
+      }
+    }
+
+    .n-form-item-feedback-wrapper {
+      min-height: 14px;
+    }
+  }
+
+  .inputSearch {
+    position: absolute;
+    top: 4px;
+    right: 0px;
+    width: 360px;
+    height: 42px;
+    font-size: 16px;
+    --n-height: 42px !important;
+
+    img {
+      width: 18px;
+      height: 18px;
+    }
+
+    :global {
+      .n-input-wrapper {
+        padding-left: 12px;
+        padding-right: 4px;
+        height: 42px !important;
+      }
+
+      .n-button {
+        height: 34px;
+        font-size: 15px;
+        font-weight: 500;
+        width: auto;
+      }
+    }
+  }
+
+  .searchCatatory {
+    display: flex;
+    justify-content: space-between;
+    padding-bottom: 20px;
+    border-bottom: 1px solid #F2F2F2;
+    margin-bottom: 20px;
+
+    .addTrain {
+      height: 37px;
+      border-radius: 8px;
+      font-size: 18px;
+      background-color: #E8F4FF;
+      color: #0378EC;
+
+      img {
+        width: 16px;
+        height: 16px;
+        margin-right: 8px;
+      }
+    }
+  }
+}
+
+.spaceSection {
+  width: 69%;
+  gap: 8px 12px !important;
+
+  &>div {
+    line-height: var(--n-blank-height);
+  }
+}
+
+
+.collapseWrap {
+  width: 98%;
+  display: flex;
+  flex-direction: row;
+  align-items: flex-end;
+
+}
+
+.collaoseBtn {
+  width: 32px;
+  height: 32px;
+  cursor: pointer;
+}
+
+.collaoseBtn.isStart {
+  transform: rotate(-180deg);
+}
+
+.collapsSection {
+  // padding-top: 10px;
+}
+
+.isHidden {
+  overflow: hidden;
+  transition: 1s all ease-in;
+}
+
+.hideButton {
+  visibility: hidden;
+  height: 0 !important;
+  line-height: 0 !important;
+}

+ 249 - 0
src/views/prepare-lessons/model/select-resources/select-item/class-search-group/index.tsx

@@ -0,0 +1,249 @@
+import {
+  PropType,
+  defineComponent,
+  nextTick,
+  onMounted,
+  reactive,
+  ref,
+  toRef
+} from 'vue';
+import styles from './index.module.less';
+import { NButton, NForm, NFormItem, NImage, NSpace } from 'naive-ui';
+import { resourceTypeArray } from '/src/utils/searchArray';
+import { useCatchStore } from '/src/store/modules/catchData';
+import { useThrottleFn } from '@vueuse/core';
+import TheSearch from '/src/components/TheSearch';
+import isCollaose from '/src/views/natural-resources/images/isCollaose.png';
+
+export default defineComponent({
+  name: 'resource-search-group',
+  props: {
+    type: {
+      type: String as PropType<'shareResources' | 'myResources' | 'myCollect'>,
+      default: 'shareResources'
+    },
+    subjectId: {
+      type: String,
+      default: ''
+    }
+  },
+  emits: ['search'],
+  setup(props, { emit }) {
+    const subjectId = toRef(props.subjectId);
+    const catchStore = useCatchStore();
+    const forms = reactive({
+      type: 'MUSIC', //
+      name: '',
+      subjectId: subjectId.value as any,
+      bookVersionId: null
+    });
+    const resourceType = ref([] as any);
+
+    const onSearch = () => {
+      emit('search', forms);
+    };
+
+    const throttleFn = useThrottleFn(() => onSearch(), 500);
+
+    // const collapseWrapRef = ref();
+    // const divDomList = ref([] as any);
+    // const orginHeight = ref(0);
+    // const hiddenHeight = ref(0);
+    // const line = ref(0);
+    // const isCollapse = ref(false);
+    // const loadingCollapse = ref(false); // 是否加载完成
+    // const musicCateRef = (el: any) => {
+    //   if (el?.selfElRef) {
+    //     divDomList.value.push(el.selfElRef.parentNode);
+    //   }
+    // };
+    // const setCollapse = (flag: boolean) => {
+    //   isCollapse.value = flag;
+    //   getLive();
+    // };
+    // const getLive = () => {
+    //   try {
+    //     divDomList.value = [...new Set(divDomList.value)];
+    //     let offsetLeft = -1;
+    //     divDomList.value.forEach((item: any, index: number) => {
+    //       if (index === 0) {
+    //         line.value = 1;
+    //         offsetLeft = item.offsetLeft;
+    //       } else if (item.offsetLeft === offsetLeft && index != 0) {
+    //         // 如果某个标签的offsetLeft和第一个标签的offsetLeft相等  说明增加了一行
+    //         line.value++;
+    //       }
+    //       if (!isCollapse.value) {
+    //         if (line.value > 1) {
+    //           //从第3行开始 隐藏标签
+    //           item.style.display = 'none';
+
+    //           // 显示展开按钮  class名chu是在前面动态添加的
+    //         } else {
+    //           item.style.display = 'block';
+    //         }
+    //       } else {
+    //         item.style.display = 'block';
+    //       }
+    //     });
+    //     loadingCollapse.value = true;
+    //   } catch {
+    //     //
+    //   }
+    // };
+    onMounted(async () => {
+      // if (props.type === 'myCollect') {
+      //   resourceType.value.push({
+      //     label: '全部',
+      //     value: ''
+      //   });
+      //   forms.type = ''; // 默认全部
+      // }
+      resourceTypeArray.forEach((item: any) => {
+        // if (props.type === 'myResources') {
+        //   item.value !== 'MUSIC' && resourceType.value.push(item);
+        // } else {
+        resourceType.value.push(item);
+        // }
+      });
+
+      // 获取教材分类列表
+      await catchStore.getMusicSheetCategory();
+      // 获取声部
+      await catchStore.getSubjects();
+
+      // 这里开始
+      // musicCateRef
+      // if (forms.type === 'MUSIC') {
+      //   orginHeight.value = collapseWrapRef.value?.offsetHeight;
+      //   hiddenHeight.value = collapseWrapRef.value?.offsetHeight / line.value;
+      //   // 默认隐藏
+      //   getLive();
+      // }
+    });
+    return () => (
+      <div class={styles.searchGroup}>
+        {/* <div class={styles.searchCatatory}> */}
+        {/* <NSpace size="small" class={styles.btnType}>
+            {resourceType.value.map((item: any) => (
+              <NButton
+                type={forms.type === item.value ? 'primary' : 'default'}
+                secondary={forms.type === item.value ? false : true}
+                round
+                size="small"
+                focusable={false}
+                onClick={() => {
+                  forms.type = item.value;
+                  onSearch();
+
+                  try {
+                    nextTick(() => {
+                      if (forms.type === 'MUSIC') {
+                        orginHeight.value = collapseWrapRef.value?.offsetHeight;
+                        hiddenHeight.value =
+                          collapseWrapRef.value?.offsetHeight / line.value;
+                        // 默认隐藏
+                        getLive();
+                      } else {
+                        divDomList.value = [];
+                      }
+                    });
+                  } catch {
+                    //
+                  }
+                }}>
+                {item.label}
+              </NButton>
+            ))}
+          </NSpace> */}
+
+        {/* </div> */}
+        <NForm labelAlign="left" labelPlacement="left">
+          {/* {forms.type === 'MUSIC' && props.type === 'shareResources' && (
+            <div class={styles.collapsSection}>
+              <NFormItem label="教材:">
+                <div
+                  class={[
+                    styles.collapseWrap,
+                    loadingCollapse.value ? '' : styles.hideButton,
+                    isCollapse.value ? '' : styles.isHidden
+                  ]}
+                  ref={collapseWrapRef}>
+                  <NSpace class={[styles.spaceSection]}>
+                    {catchStore.getAllMusicCategories.map((music: any) => (
+                      <NButton
+                        ref={musicCateRef}
+                        secondary={forms.bookVersionId === music.id}
+                        quaternary={forms.bookVersionId !== music.id}
+                        strong
+                        focusable={false}
+                        type={
+                          forms.bookVersionId === music.id
+                            ? 'primary'
+                            : 'default'
+                        }
+                        onClick={() => {
+                          forms.bookVersionId = music.id;
+                          onSearch();
+                        }}>
+                        {music.name}
+                      </NButton>
+                    ))}
+                    {line.value > 1 && (
+                      <div
+                        style={{
+                          height: 'var(--n-blank-height)',
+                          position: 'absolute',
+                          display: 'flex',
+                          alignItems: 'center'
+                        }}
+                        onClick={() => {
+                          setCollapse(!isCollapse.value);
+                        }}>
+                        <NImage
+                          previewDisabled
+                          src={isCollaose}
+                          class={[
+                            styles.collaoseBtn,
+                            isCollapse.value ? styles.isStart : ''
+                          ]}></NImage>
+                      </div>
+                    )}
+                  </NSpace>
+                </div>
+              </NFormItem>
+            </div>
+          )} */}
+
+          <NFormItem label="声部:">
+            <NSpace class={styles.spaceSection}>
+              {catchStore.getSubjectAllList.map((music: any) => (
+                <NButton
+                  secondary={forms.subjectId === music.id}
+                  quaternary={forms.subjectId !== music.id}
+                  strong
+                  focusable={false}
+                  type={forms.subjectId === music.id ? 'primary' : 'default'}
+                  onClick={() => {
+                    forms.subjectId = music.id;
+                    onSearch();
+                  }}>
+                  {music.name}
+                </NButton>
+              ))}
+            </NSpace>
+          </NFormItem>
+
+          <TheSearch
+            class={styles.inputSearch}
+            round
+            onSearch={(val: string) => {
+              forms.name = val;
+              throttleFn();
+            }}
+          />
+        </NForm>
+      </div>
+    );
+  }
+});

+ 20 - 54
src/views/prepare-lessons/model/select-resources/select-item/index.tsx

@@ -7,16 +7,16 @@ import {
   watch
 } from 'vue';
 import ResourceSearchGroup from './resource-search-group';
-import { NScrollbar, NSpin, useDialog, useMessage } from 'naive-ui';
+import { NScrollbar, NSpin } from 'naive-ui';
 import styles from './index.module.less';
 import CardType from '/src/components/card-type';
 import { favorite, materialQueryPage } from '/src/views/natural-resources/api';
 import TheEmpty from '/src/components/TheEmpty';
 import { usePrepareStore } from '/src/store/modules/prepareLessons';
-import { saveCourseware } from '../../../api';
 import { useDebounceFn, useResizeObserver } from '@vueuse/core';
 import CardPreview from '/src/components/card-preview';
 import { eventGlobal } from '/src/utils';
+import ClassSearchGroup from './class-search-group';
 
 const formatType = (type: string) => {
   if (type === 'shareResources') {
@@ -34,11 +34,15 @@ export default defineComponent({
     type: {
       type: String as PropType<'shareResources' | 'myResources' | 'myCollect'>,
       default: 'shareResources'
+    },
+    /** 从哪里使用 */
+    from: {
+      type: String,
+      default: ''
     }
   },
   setup(props) {
     const prepareStore = usePrepareStore();
-    const message = useMessage();
     const { type } = toRefs(props);
     const className = 'resourceSearchGroup' + +new Date();
     const state = reactive({
@@ -99,12 +103,6 @@ export default defineComponent({
       }
     };
 
-    // const onSearch = async (item: any) => {
-    //   state.pagination.page = 1;
-    //   state.tableList = [];
-    //   state.searchGroup = Object.assign(state.searchGroup, item);
-    //   getList();
-    // };
     const throttledFnSearch = useDebounceFn(item => {
       state.pagination.page = 1;
       state.tableList = [];
@@ -114,41 +112,7 @@ export default defineComponent({
 
     // 添加资源
     const onAdd = async (item: any) => {
-      // dialog.warning({
-      //   title: '提示',
-      //   content: `是否添加"${item.title}"资源?`,
-      //   positiveText: '确定',
-      //   negativeText: '取消',
-      //   onPositiveClick: async () => {
       try {
-        // const temp: any = [];
-        // prepareStore.getCoursewareList.forEach((item: any) => {
-        //   temp.push({
-        //     materialId: item.materialId,
-        //     materialName: item.title,
-        //     materialType: item.type,
-        //     id: item.id
-        //   });
-        // });
-
-        // // 保存课件
-        // await saveCourseware({
-        //   coursewareDetailKnowledgeId: prepareStore.getSelectKey,
-        //   lessonCoursewareId: prepareStore.getLessonCoursewareId,
-        //   lessonCoursewareDetailId: prepareStore.getLessonCoursewareDetailId,
-        //   materialList: [
-        //     ...temp,
-        //     {
-        //       materialName: item.title,
-        //       materialType: item.type,
-        //       materialId: item.id
-        //     }
-        //   ],
-        //   subjectId: prepareStore.getSubjectId
-        // });
-
-        // message.success('添加成功');
-        // prepareStore.setIsAddResource(true);
         eventGlobal.emit('onPrepareAddItem', {
           materialId: item.id,
           coverImg: item.coverImg,
@@ -162,8 +126,6 @@ export default defineComponent({
       } catch {
         //
       }
-      //   }
-      // });
     };
 
     watch(
@@ -204,10 +166,6 @@ export default defineComponent({
         (entries: any) => {
           const entry = entries[0];
           const { height } = entry.contentRect;
-          // document.documentElement.style.setProperty(
-          //   '--modal-lesson-search-height',
-          //   height + 'px'
-          // );
           state.searchHeight = height + 'px';
         }
       );
@@ -215,11 +173,19 @@ export default defineComponent({
     return () => (
       <div>
         <div class={className}>
-          <ResourceSearchGroup
-            type={props.type}
-            subjectId={prepareStore.getSubjectId as any}
-            onSearch={(item: any) => throttledFnSearch(item)}
-          />
+          {props.from === 'class' ? (
+            <ClassSearchGroup
+              type={props.type}
+              subjectId={prepareStore.getSubjectId as any}
+              onSearch={(item: any) => throttledFnSearch(item)}
+            />
+          ) : (
+            <ResourceSearchGroup
+              type={props.type}
+              subjectId={prepareStore.getSubjectId as any}
+              onSearch={(item: any) => throttledFnSearch(item)}
+            />
+          )}
         </div>
         <NScrollbar
           class={styles.listContainer}