Browse Source

添加朗读功能

lex 9 months ago
parent
commit
a137a9ada6

+ 267 - 266
index.html

@@ -1,266 +1,267 @@
-<!DOCTYPE html>
-
-<html lang="zh-CN">
-
-
-<head>
-  <meta charset="UTF-8" />
-  <link rel="icon" href="/favicon.ico" />
-  <meta name="viewport"
-    content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover">
-  <meta http-equiv="Cache" content="no-cache">
-  <meta http-equiv="pragram" content="no-cache">
-  <meta http-equiv="cache-control" content="no-cache, no-store, must-revalidate">
-  <meta http-equiv="pragma" content="no-cache">
-  <meta http-equiv="expires" content="0">
-  <meta name="viewport" content="width=device-width,initial-scale=1.0">
-  <meta name="apple-mobile-web-app-capable" content="yes" />
-  <!-- 自动将HTTP请求升级成安全的HTTPS请求 -->
-  <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" />
-  <!-- 设置苹果工具栏颜色 -->
-  <meta name="apple-mobile-web-app-status-bar-style" content="black" />
-  <!-- 忽略页面中的数字识别为电话,忽略email识别 -->
-  <meta name="format-detection" content="telphone=no, email=no" />
-  <!-- 启用360浏览器的极速模式(webkit) -->
-  <meta name="renderer" content="webkit" />
-  <!-- 避免IE使用兼容模式 -->
-  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
-  <meta name="HandheldFriendly" content="true" />
-  <!-- 设置在apple上以应用模式启动时,是否全屏 -->
-  <meta name="apple-touch-fullscreen" content="yes" />
-  <!-- windows phone 点击无高光 -->
-  <meta name="msapplication-tap-highlight" content="no" />
-  <meta name="referrer" content="no-referrer" />
-  <title>音乐数字课堂</title>
-
-  <style>
-    body {
-      background: #f1f5ff;
-      width: 100%;
-    }
-
-    .bgImg {
-      width: 456px;
-      height: 522px;
-    }
-
-    .btnImg {
-      width: 288px;
-      position: absolute;
-      left: 50%;
-      margin-left: -144px;
-      bottom: 31px;
-      cursor: pointer;
-      z-index: 100;
-
-    }
-
-    .imgWrap {
-      width: 456px;
-      height: 522px;
-      margin: 140px auto 0;
-      position: relative;
-      z-index: 100;
-    }
-
-    /* margin-top: 300px; */
-    .textWrap {
-      margin-top: 140px;
-      text-align: center;
-      width: 500px;
-      position: absolute;
-      top: 141px;
-      left: 50%;
-      margin-left: -288px;
-    }
-
-    .subMsg {
-      margin-top: 32px;
-      font-size: 22px;
-      line-height: 30px;
-      color: #777;
-    }
-
-    .subBtn {
-      width: 231px;
-      height: 62px;
-      background: #198CFE;
-      border-radius: 18px;
-      border: none;
-      font-size: 22px;
-      font-weight: 600;
-      color: #FFFFFF;
-      line-height: 30px;
-      cursor: pointer;
-      margin-top: 32px;
-      list-style: none outside none;
-      text-decoration: none;
-    }
-  </style>
-  <script type="text/javascript">
-    function gotoLinlk() {
-      console.log('点击')
-      var agent = navigator.userAgent.toLowerCase();
-      var isMac = function () { return /macintosh|mac os x/i.test(navigator.userAgent); }();
-      if (agent.indexOf("win32") >= 0 || agent.indexOf("wow32") >= 0) {
-        window.open('https://oss.dayaedu.com/appstore/ChromeStandaloneSetup32.exe');
-      }
-      if (agent.indexOf("win64") >= 0 || agent.indexOf("wow64") >= 0) {
-        window.open('https://oss.dayaedu.com/appstore/ChromeStandaloneSetup64.exe');
-      }
-      if (isMac) {
-        window.open('https://oss.dayaedu.com/appstore/googlechrome-mac.dmg');
-      }
-    }
-    function getChromeVersion() {
-      var arr = navigator.userAgent.split(' ');
-      var chromeVersion = '';
-      for (var i = 0; i < arr.length; i++) {
-        if (/chrome/i.test(arr[i])) chromeVersion = arr[i];
-      }
-      if (chromeVersion) {
-        return Number(chromeVersion.split('/')[1].split('.')[0]);
-      } else {
-        return false;
-      }
-    };
-    function IsFF() {
-      var sAgent = window.navigator.userAgent.toLowerCase();
-
-      if (sAgent.indexOf("firefox") != -1) {
-        return true;
-      }
-      return false;
-    }
-
-    function isChrome() {
-      var isChromium = window.chrome;
-      var winNav = window.navigator;
-      var vendorName = winNav.vendor;
-      var isOpera = typeof window.opr !== 'undefined';
-      var isIEedge = winNav.userAgent.indexOf('Edge') > -1;
-      var isIOSChrome = winNav.userAgent.match('CriOS');
-      return (
-        isIOSChrome ||
-        (isChromium !== null &&
-          typeof isChromium !== 'undefined' &&
-          vendorName === 'Google Inc.' &&
-          isOpera === false &&
-          isIEedge === false)
-      );
-    };
-    function IEVersion() {
-      var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串
-      var isIE = userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1; //判断是否IE<11浏览器
-      var isEdge = userAgent.indexOf("Edge") > -1 && !isIE; //判断是否IE的Edge浏览器
-      var isIE11 = userAgent.indexOf('Trident') > -1 && userAgent.indexOf("rv:11.0") > -1;
-      if (isIE) {
-        var reIE = new RegExp("MSIE (\\d+\\.\\d+);");
-        reIE.test(userAgent);
-        var fIEVersion = parseFloat(RegExp["$1"]);
-        if (fIEVersion == 7) {
-          return 7;
-        } else if (fIEVersion == 8) {
-          return 8;
-        } else if (fIEVersion == 9) {
-          return 9;
-        } else if (fIEVersion == 10) {
-          return 10;
-        } else {
-          return 6;//IE版本<=7
-        }
-      } else if (isEdge) {
-        return 'edge';//edge
-      } else if (isIE11) {
-        return 11; //IE11
-      } else {
-        return -1;//不是ie浏览器
-      }
-    }
-    (function (window) {
-      const reloadPage = async () => {
-        if ('serviceWorker' in navigator) {
-          // let refreshing = false
-          const flag = (await caches.keys()).length > 0;
-          // console.log(caches.keys(), `0904路由更新有缓存${flag}`);
-          caches.keys().then(function (cacheNames) {
-            cacheNames.forEach(function (cacheName) {
-              caches.delete(cacheName);
-            });
-          });
-          // 尝试监听install
-        }
-        console.log('index.html')
-        window.location.reload();
-      }
-      // let refreshing = false
-
-
-      // if ('serviceWorker' in navigator) {
-      //   // let refreshing = false
-      //   let flag = caches.keys().length > 0
-      //   console.log(caches.keys(), 'caches.keys()', `是否有缓存${flag}`);
-      //   caches.keys().then(function (cacheNames) {
-      //     cacheNames.forEach(function (cacheName) {
-      //       caches.delete(cacheName);
-      //     });
-      //     if (flag) {
-      //       window.location.reload()
-      //     }
-      //   });
-
-      //   // 尝试监听install
-
-
-      // }
-      // let refreshing = false
-      // navigator.serviceWorker.addEventListener('controllerchange', () => {
-      //   console.log('controllerchange')
-      //   if (refreshing) {
-      //     return
-      //   }
-      //   refreshing = true;
-      //   window.location.reload();
-      // })
-
-
-      if (!isChrome()) {
-        // document.writeln("<div class='imgWrap'><img src='https://oss.dayaedu.com/gyt/basic/1688699993534.png' class='bgImg' alt=''><img src='https://oss.dayaedu.com/gyt/basic/1688699779209.png' class='btnImg' alt=''></div>");
-
-
-        if (IEVersion() < 9 && IEVersion() != -1) {
-
-          document.writeln(" <div class='textWrap'><h1>当前浏览器版本过低</h1><p class='subMsg'>为了保证良好的上课体验,推荐您使用谷歌浏览器</p> <button class='subBtn' onclick='gotoLinlk()'>下载谷歌浏览器</button>");
-          document.execCommand("Stop");
-        } else {
-          document.writeln(" <div class='imgWrap'><img onabort='this.src=this.src' src='https://oss.dayaedu.com/gyt/basic/1688699993534.png' class='bgImg' alt=''><img src='https://oss.dayaedu.com/gyt/basic/1688699779209.png'  onabort='this.src=this.src' onclick='gotoLinlk()' class='btnImg' alt=''></div>");
-          document.execCommand("Stop");
-        }
-      } else {
-        if (getChromeVersion() < 90) {
-
-          document.writeln(" <div class='imgWrap'><img src='https://oss.dayaedu.com/gyt/basic/1688699993534.png' class='bgImg' alt=''><img src='https://oss.dayaedu.com/gyt/basic/1688699779209.png' onclick='gotoLinlk()' class='btnImg' alt=''></div>");
-          document.execCommand("Stop");
-          window.stop()
-          console.log('Stop')
-        }
-
-        // if (IsFF()) {
-
-      }
-
-    })(window);
-  </script>
-</head>
-
-<!-- 按钮 https://oss.dayaedu.com/gyt/basic/1688699779209.png -->
-<!-- 背景 https://oss.dayaedu.com/gyt/basic/1688699993534.png -->
-
-<body class="myBody">
-
-  <div id="app"></div>
-  <script type="module" src="/src/main.ts"></script>
-
-</body>
-
-</html>
+<!DOCTYPE html>
+
+<html lang="zh-CN">
+
+
+<head>
+  <meta charset="UTF-8" />
+  <link rel="icon" href="/favicon.ico" />
+  <meta name="viewport"
+    content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover">
+  <meta http-equiv="Cache" content="no-cache">
+  <meta http-equiv="pragram" content="no-cache">
+  <meta http-equiv="cache-control" content="no-cache, no-store, must-revalidate">
+  <meta http-equiv="pragma" content="no-cache">
+  <meta http-equiv="expires" content="0">
+  <meta name="viewport" content="width=device-width,initial-scale=1.0">
+  <meta name="apple-mobile-web-app-capable" content="yes" />
+  <!-- 自动将HTTP请求升级成安全的HTTPS请求 -->
+  <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" />
+  <!-- 设置苹果工具栏颜色 -->
+  <meta name="apple-mobile-web-app-status-bar-style" content="black" />
+  <!-- 忽略页面中的数字识别为电话,忽略email识别 -->
+  <meta name="format-detection" content="telphone=no, email=no" />
+  <!-- 启用360浏览器的极速模式(webkit) -->
+  <meta name="renderer" content="webkit" />
+  <!-- 避免IE使用兼容模式 -->
+  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+  <meta name="HandheldFriendly" content="true" />
+  <!-- 设置在apple上以应用模式启动时,是否全屏 -->
+  <meta name="apple-touch-fullscreen" content="yes" />
+  <!-- windows phone 点击无高光 -->
+  <meta name="msapplication-tap-highlight" content="no" />
+  <meta name="referrer" content="no-referrer" />
+  <script data-key="responsivevoice.js" src="https://code.responsivevoice.org/responsivevoice.js?key=5tX5zUJU" ></script>
+  <title>音乐数字课堂</title>
+
+  <style>
+    body {
+      background: #f1f5ff;
+      width: 100%;
+    }
+
+    .bgImg {
+      width: 456px;
+      height: 522px;
+    }
+
+    .btnImg {
+      width: 288px;
+      position: absolute;
+      left: 50%;
+      margin-left: -144px;
+      bottom: 31px;
+      cursor: pointer;
+      z-index: 100;
+
+    }
+
+    .imgWrap {
+      width: 456px;
+      height: 522px;
+      margin: 140px auto 0;
+      position: relative;
+      z-index: 100;
+    }
+
+    /* margin-top: 300px; */
+    .textWrap {
+      margin-top: 140px;
+      text-align: center;
+      width: 500px;
+      position: absolute;
+      top: 141px;
+      left: 50%;
+      margin-left: -288px;
+    }
+
+    .subMsg {
+      margin-top: 32px;
+      font-size: 22px;
+      line-height: 30px;
+      color: #777;
+    }
+
+    .subBtn {
+      width: 231px;
+      height: 62px;
+      background: #198CFE;
+      border-radius: 18px;
+      border: none;
+      font-size: 22px;
+      font-weight: 600;
+      color: #FFFFFF;
+      line-height: 30px;
+      cursor: pointer;
+      margin-top: 32px;
+      list-style: none outside none;
+      text-decoration: none;
+    }
+  </style>
+  <script type="text/javascript">
+    function gotoLinlk() {
+      console.log('点击')
+      var agent = navigator.userAgent.toLowerCase();
+      var isMac = function () { return /macintosh|mac os x/i.test(navigator.userAgent); }();
+      if (agent.indexOf("win32") >= 0 || agent.indexOf("wow32") >= 0) {
+        window.open('https://oss.dayaedu.com/appstore/ChromeStandaloneSetup32.exe');
+      }
+      if (agent.indexOf("win64") >= 0 || agent.indexOf("wow64") >= 0) {
+        window.open('https://oss.dayaedu.com/appstore/ChromeStandaloneSetup64.exe');
+      }
+      if (isMac) {
+        window.open('https://oss.dayaedu.com/appstore/googlechrome-mac.dmg');
+      }
+    }
+    function getChromeVersion() {
+      var arr = navigator.userAgent.split(' ');
+      var chromeVersion = '';
+      for (var i = 0; i < arr.length; i++) {
+        if (/chrome/i.test(arr[i])) chromeVersion = arr[i];
+      }
+      if (chromeVersion) {
+        return Number(chromeVersion.split('/')[1].split('.')[0]);
+      } else {
+        return false;
+      }
+    };
+    function IsFF() {
+      var sAgent = window.navigator.userAgent.toLowerCase();
+
+      if (sAgent.indexOf("firefox") != -1) {
+        return true;
+      }
+      return false;
+    }
+
+    function isChrome() {
+      var isChromium = window.chrome;
+      var winNav = window.navigator;
+      var vendorName = winNav.vendor;
+      var isOpera = typeof window.opr !== 'undefined';
+      var isIEedge = winNav.userAgent.indexOf('Edge') > -1;
+      var isIOSChrome = winNav.userAgent.match('CriOS');
+      return (
+        isIOSChrome ||
+        (isChromium !== null &&
+          typeof isChromium !== 'undefined' &&
+          vendorName === 'Google Inc.' &&
+          isOpera === false &&
+          isIEedge === false)
+      );
+    };
+    function IEVersion() {
+      var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串
+      var isIE = userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1; //判断是否IE<11浏览器
+      var isEdge = userAgent.indexOf("Edge") > -1 && !isIE; //判断是否IE的Edge浏览器
+      var isIE11 = userAgent.indexOf('Trident') > -1 && userAgent.indexOf("rv:11.0") > -1;
+      if (isIE) {
+        var reIE = new RegExp("MSIE (\\d+\\.\\d+);");
+        reIE.test(userAgent);
+        var fIEVersion = parseFloat(RegExp["$1"]);
+        if (fIEVersion == 7) {
+          return 7;
+        } else if (fIEVersion == 8) {
+          return 8;
+        } else if (fIEVersion == 9) {
+          return 9;
+        } else if (fIEVersion == 10) {
+          return 10;
+        } else {
+          return 6;//IE版本<=7
+        }
+      } else if (isEdge) {
+        return 'edge';//edge
+      } else if (isIE11) {
+        return 11; //IE11
+      } else {
+        return -1;//不是ie浏览器
+      }
+    }
+    (function (window) {
+      const reloadPage = async () => {
+        if ('serviceWorker' in navigator) {
+          // let refreshing = false
+          const flag = (await caches.keys()).length > 0;
+          // console.log(caches.keys(), `0904路由更新有缓存${flag}`);
+          caches.keys().then(function (cacheNames) {
+            cacheNames.forEach(function (cacheName) {
+              caches.delete(cacheName);
+            });
+          });
+          // 尝试监听install
+        }
+        console.log('index.html')
+        window.location.reload();
+      }
+      // let refreshing = false
+
+
+      // if ('serviceWorker' in navigator) {
+      //   // let refreshing = false
+      //   let flag = caches.keys().length > 0
+      //   console.log(caches.keys(), 'caches.keys()', `是否有缓存${flag}`);
+      //   caches.keys().then(function (cacheNames) {
+      //     cacheNames.forEach(function (cacheName) {
+      //       caches.delete(cacheName);
+      //     });
+      //     if (flag) {
+      //       window.location.reload()
+      //     }
+      //   });
+
+      //   // 尝试监听install
+
+
+      // }
+      // let refreshing = false
+      // navigator.serviceWorker.addEventListener('controllerchange', () => {
+      //   console.log('controllerchange')
+      //   if (refreshing) {
+      //     return
+      //   }
+      //   refreshing = true;
+      //   window.location.reload();
+      // })
+
+
+      if (!isChrome()) {
+        // document.writeln("<div class='imgWrap'><img src='https://oss.dayaedu.com/gyt/basic/1688699993534.png' class='bgImg' alt=''><img src='https://oss.dayaedu.com/gyt/basic/1688699779209.png' class='btnImg' alt=''></div>");
+
+
+        if (IEVersion() < 9 && IEVersion() != -1) {
+
+          document.writeln(" <div class='textWrap'><h1>当前浏览器版本过低</h1><p class='subMsg'>为了保证良好的上课体验,推荐您使用谷歌浏览器</p> <button class='subBtn' onclick='gotoLinlk()'>下载谷歌浏览器</button>");
+          document.execCommand("Stop");
+        } else {
+          document.writeln(" <div class='imgWrap'><img onabort='this.src=this.src' src='https://oss.dayaedu.com/gyt/basic/1688699993534.png' class='bgImg' alt=''><img src='https://oss.dayaedu.com/gyt/basic/1688699779209.png'  onabort='this.src=this.src' onclick='gotoLinlk()' class='btnImg' alt=''></div>");
+          document.execCommand("Stop");
+        }
+      } else {
+        if (getChromeVersion() < 90) {
+
+          document.writeln(" <div class='imgWrap'><img src='https://oss.dayaedu.com/gyt/basic/1688699993534.png' class='bgImg' alt=''><img src='https://oss.dayaedu.com/gyt/basic/1688699779209.png' onclick='gotoLinlk()' class='btnImg' alt=''></div>");
+          document.execCommand("Stop");
+          window.stop()
+          console.log('Stop')
+        }
+
+        // if (IsFF()) {
+
+      }
+
+    })(window);
+  </script>
+</head>
+
+<!-- 按钮 https://oss.dayaedu.com/gyt/basic/1688699779209.png -->
+<!-- 背景 https://oss.dayaedu.com/gyt/basic/1688699993534.png -->
+
+<body class="myBody">
+
+  <div id="app"></div>
+  <script type="module" src="/src/main.ts"></script>
+
+</body>
+
+</html>

+ 0 - 0
src/hooks/useSpeak/index.ts


+ 506 - 465
src/views/content-information/content-instrument/detail.module.less

@@ -1,465 +1,506 @@
-.container {
-  display: flex;
-  flex-direction: column;
-  height: 100%;
-
-  .iconBack {
-    width: 36px;
-    height: 36px;
-  }
-
-  :global {
-    .n-breadcrumb>ul {
-      display: flex;
-      align-items: center;
-
-      .n-breadcrumb-item {
-        display: flex;
-        align-items: center;
-      }
-
-      .n-breadcrumb-item__separator {
-        display: none;
-      }
-
-      .n-breadcrumb-item__link {
-        padding: 5px 18px;
-        background: #FFFFFF;
-        border-radius: 16px;
-        color: #21225D;
-        line-height: 20px;
-      }
-    }
-
-    .n-breadcrumb .n-breadcrumb-item:last-child .n-breadcrumb-item__link {
-      color: #fff;
-      background: var(--product-color);
-    }
-
-  }
-
-  &> :global(.n-space) {
-    height: 36px;
-    flex-shrink: 0;
-  }
-
-  .separator {
-    width: 9px;
-    height: 15px;
-    margin: 0 16px;
-  }
-}
-
-.wrap {
-  padding-top: 15px;
-  flex: 1;
-  transition: padding .3s;
-  overflow: hidden;
-
-  &.wrapBottom {
-    padding-bottom: 108px;
-
-  }
-}
-
-.content {
-  display: flex;
-  flex-direction: column;
-  height: 100%;
-  background: #DDF2FF;
-  border-radius: 20px;
-  // max-height: 90vh;
-}
-
-.tools {
-  padding: 20px;
-  display: flex;
-  align-items: center;
-  flex-shrink: 0;
-
-  :global {
-    .n-input {
-      margin-left: auto;
-      width: 361px;
-    }
-
-    .n-input__input-el {
-      height: 100%;
-      line-height: 100%;
-    }
-  }
-}
-
-.contentWrap {
-  position: relative;
-  flex: 1;
-  display: flex;
-  padding: 20px 55px 20px 20px;
-  overflow: hidden;
-  gap: 0 32px;
-}
-
-.musicList {
-  background-color: #fff;
-  border-radius: 16px;
-
-  width: 470px;
-  min-width: 294px;
-  height: 100%;
-  overflow-x: hidden;
-  overflow-y: auto;
-  min-width: 330Px;
-
-  &::-webkit-scrollbar {
-    width: 0;
-    display: none;
-  }
-
-  .instrumentGroup {
-    padding-top: 27px;
-    padding-bottom: 20px;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    flex-direction: column;
-
-    .instrumentImg {
-      width: 125px;
-      height: 125px;
-      overflow: hidden;
-      border-radius: 50%;
-
-      &.otherImg {
-        width: 125px;
-        height: 143px;
-      }
-
-      img {
-        width: 100%;
-      }
-    }
-
-    .instrumentName {
-      padding: 13px 0 5px;
-      font-size: max(18px, 14Px);
-      font-weight: 600;
-      color: #131415;
-      line-height: 25px;
-      letter-spacing: 1px;
-    }
-
-    .instrumentTag {
-      font-size: max(13px, 12Px);
-      color: #777777;
-      line-height: 18px;
-    }
-  }
-
-
-
-  .wrapList {
-    width: 470px;
-    padding: 0 17px;
-    min-width: 294px;
-    min-height: 100%;
-    // background: #fff;
-    border-radius: 16px;
-
-    :global {
-      .n-empty .n-empty__description {
-        font-size: calc(14px, 12Px);
-      }
-    }
-
-    .titlec {
-      padding: 20px 0;
-      font-size: max(18px, 14Px);
-      font-weight: 600;
-      color: #000000;
-      line-height: 25px;
-      border-top: 1px solid #F2F2F2;
-      display: flex;
-      align-items: center;
-    }
-
-    .icon2 {
-      width: 23px;
-      height: 23px;
-      margin-right: 8px;
-      background: url('../images/icon-2.png') no-repeat center;
-      background-size: contain;
-    }
-  }
-
-  .empty {
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    height: 50vh;
-    // height: 100%;
-  }
-}
-
-.itemContainer {
-  width: 100%;
-  border-radius: 16px;
-  padding: 4px 8px;
-  // background-color: #fff;
-
-  &:first-child {
-    padding-top: 8px;
-  }
-
-  &:last-child {
-    // border-radius: 0 0 16px 16px;
-    padding-bottom: 8px;
-  }
-}
-
-.item {
-  position: relative;
-  display: flex;
-  align-items: center;
-  padding: 10px;
-  border-radius: 12px;
-
-  cursor: pointer;
-
-  &:hover {
-    background-color: rgba(0, 0, 0, .05);
-  }
-
-  &.active {
-    background-color: #DDF2FF;
-
-    .arrow {
-      opacity: 1;
-    }
-  }
-
-  .img {
-    position: relative;
-    width: 60px;
-    height: 60px;
-    border-radius: 18px;
-    margin-right: 12px;
-    overflow: hidden;
-    flex-shrink: 0;
-
-    :global {
-      .n-image {
-        width: 60px;
-        height: 60px;
-      }
-    }
-
-    img {
-      transition: opacity .3s;
-      opacity: 0;
-      height: 100%;
-      width: 100%;
-    }
-
-    img[data-loaded="true"] {
-      opacity: 1;
-    }
-  }
-
-  .title {
-    flex: 1;
-    overflow: hidden;
-    display: flex;
-    flex-direction: column;
-    align-items: flex-start;
-
-    .titleName {
-      font-size: calc(17px, 12Px);
-      font-weight: 600;
-      color: #131415;
-      line-height: 28px;
-      width: 100%;
-    }
-
-    .titleDes {
-      font-size: 14px;
-      font-weight: 400;
-      color: #777777;
-      line-height: 20px;
-      max-width: 100%;
-      white-space: nowrap;
-      text-overflow: ellipsis;
-      overflow: hidden;
-    }
-  }
-
-  .btn {
-    margin-left: auto;
-    width: 84px;
-    height: 40px;
-    background: linear-gradient(to right, #44CAFF, #259DFE);
-    border: none;
-    padding: 0;
-    font-weight: bold !important;
-    flex-shrink: 0;
-    min-width: 62px;
-    min-height: 30px;
-
-    :global {
-      .n-button__content {
-        &>img {
-          margin-left: 10px;
-          width: 9px;
-          height: 12px;
-        }
-      }
-    }
-  }
-
-  .arrow {
-    position: absolute;
-    top: 50%;
-    right: 12px;
-    transform: translate(124%, -50%);
-    opacity: 0;
-  }
-
-  .showPlayLoading {
-    opacity: 0;
-  }
-
-}
-
-.loadingWrap {
-  display: flex;
-  justify-content: center;
-  min-height: 80px;
-}
-
-.musicStaff {
-  display: flex;
-  flex-direction: column;
-  position: relative;
-  left: -8px;
-  flex: 1;
-  background-color: #fff;
-  border-radius: 16px;
-  // height: 100%;
-  z-index: 1;
-  overflow: hidden;
-
-  &::-webkit-scrollbar {
-    width: 0;
-    display: none;
-  }
-
-
-  .musicTitle {
-    padding: 27px 27px 13px;
-    font-size: max(18px, 14Px);
-    font-weight: 600;
-    color: #000000;
-    line-height: 25px;
-    display: flex;
-    align-items: center;
-
-    .icon1,
-    .icon3 {
-      display: inline-block;
-      width: 23px;
-      height: 23px;
-      margin-right: 8px;
-      background: url('../images/icon-1.png') no-repeat center;
-      background-size: contain;
-    }
-
-    .icon3 {
-      background: url('../images/icon-3.png') no-repeat center;
-      background-size: contain;
-    }
-  }
-
-  .musicContent {
-    flex: 1;
-    overflow-y: auto;
-    height: 100%;
-    padding: 0 27px;
-
-    &>img {
-      width: 100%;
-    }
-
-    section,
-    &>div {
-      font-size: inherit !important;
-    }
-  }
-}
-
-.staffImgs {
-  flex: 1;
-  overflow-y: auto;
-  height: 100%;
-  padding: 0 30px;
-
-  &>img {
-    width: 100%;
-  }
-}
-
-
-:global {
-
-  .van-fade-enter-active,
-  .van-fade-leave-active {
-    transition: all 0.3s;
-  }
-
-  .van-fade-enter-from,
-  .van-fade-leave-to {
-    opacity: 0;
-  }
-}
-
-.changeSizeSection {
-  position: absolute;
-  right: 10px;
-  bottom: 50%;
-  width: 35px;
-  transform: translate(0, 50%);
-  background: #fff;
-  border-radius: 7px;
-  display: flex;
-  align-items: center;
-  flex-direction: column;
-  padding: 13px 0;
-
-  .iconT {
-    width: 15px;
-    height: 15px;
-  }
-
-  .iconAddT,
-  .iconPlusT {
-    width: 23px;
-    height: 23px;
-    cursor: pointer;
-  }
-
-  .iconAddT {
-    margin-top: 13px;
-    margin-bottom: 8px;
-  }
-
-  .iconPlusT {
-    margin-top: 8px;
-  }
-
-  :global {
-    .n-slider {
-      height: 125px;
-      --n-handle-size: 15px !important;
-      --n-rail-height: 0 !important;
-    }
-
-  }
-}
+.container {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+
+  .iconBack {
+    width: 36px;
+    height: 36px;
+  }
+
+  :global {
+    .n-breadcrumb>ul {
+      display: flex;
+      align-items: center;
+
+      .n-breadcrumb-item {
+        display: flex;
+        align-items: center;
+      }
+
+      .n-breadcrumb-item__separator {
+        display: none;
+      }
+
+      .n-breadcrumb-item__link {
+        padding: 5px 18px;
+        background: #FFFFFF;
+        border-radius: 16px;
+        color: #21225D;
+        line-height: 20px;
+      }
+    }
+
+    .n-breadcrumb .n-breadcrumb-item:last-child .n-breadcrumb-item__link {
+      color: #fff;
+      background: var(--product-color);
+    }
+
+  }
+
+  &> :global(.n-space) {
+    height: 36px;
+    flex-shrink: 0;
+  }
+
+  .separator {
+    width: 9px;
+    height: 15px;
+    margin: 0 16px;
+  }
+}
+
+.wrap {
+  padding-top: 15px;
+  flex: 1;
+  transition: padding .3s;
+  overflow: hidden;
+
+  &.wrapBottom {
+    padding-bottom: 108px;
+
+  }
+}
+
+.content {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  background: #DDF2FF;
+  border-radius: 20px;
+  // max-height: 90vh;
+}
+
+.tools {
+  padding: 20px;
+  display: flex;
+  align-items: center;
+  flex-shrink: 0;
+
+  :global {
+    .n-input {
+      margin-left: auto;
+      width: 361px;
+    }
+
+    .n-input__input-el {
+      height: 100%;
+      line-height: 100%;
+    }
+  }
+}
+
+.contentWrap {
+  position: relative;
+  flex: 1;
+  display: flex;
+  padding: 20px 55px 20px 20px;
+  overflow: hidden;
+  gap: 0 32px;
+}
+
+.musicList {
+  background-color: #fff;
+  border-radius: 16px;
+
+  width: 470px;
+  min-width: 294px;
+  height: 100%;
+  overflow-x: hidden;
+  overflow-y: auto;
+  min-width: 330Px;
+
+  &::-webkit-scrollbar {
+    width: 0;
+    display: none;
+  }
+
+  .instrumentGroup {
+    padding-top: 27px;
+    padding-bottom: 20px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex-direction: column;
+
+    .instrumentImg {
+      width: 125px;
+      height: 125px;
+      overflow: hidden;
+      border-radius: 50%;
+
+      &.otherImg {
+        width: 125px;
+        height: 143px;
+      }
+
+      img {
+        width: 100%;
+      }
+    }
+
+    .instrumentName {
+      padding: 13px 0 5px;
+      font-size: max(18px, 14Px);
+      font-weight: 600;
+      color: #131415;
+      line-height: 25px;
+      letter-spacing: 1px;
+    }
+
+    .instrumentTag {
+      font-size: max(13px, 12Px);
+      color: #777777;
+      line-height: 18px;
+    }
+  }
+
+
+
+  .wrapList {
+    width: 470px;
+    padding: 0 17px;
+    min-width: 294px;
+    min-height: 100%;
+    // background: #fff;
+    border-radius: 16px;
+
+    :global {
+      .n-empty .n-empty__description {
+        font-size: calc(14px, 12Px);
+      }
+    }
+
+    .titlec {
+      padding: 20px 0;
+      font-size: max(18px, 14Px);
+      font-weight: 600;
+      color: #000000;
+      line-height: 25px;
+      border-top: 1px solid #F2F2F2;
+      display: flex;
+      align-items: center;
+    }
+
+    .icon2 {
+      width: 23px;
+      height: 23px;
+      margin-right: 8px;
+      background: url('../images/icon-2.png') no-repeat center;
+      background-size: contain;
+    }
+  }
+
+  .empty {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    height: 50vh;
+    // height: 100%;
+  }
+}
+
+.itemContainer {
+  width: 100%;
+  border-radius: 16px;
+  padding: 4px 8px;
+  // background-color: #fff;
+
+  &:first-child {
+    padding-top: 8px;
+  }
+
+  &:last-child {
+    // border-radius: 0 0 16px 16px;
+    padding-bottom: 8px;
+  }
+}
+
+.item {
+  position: relative;
+  display: flex;
+  align-items: center;
+  padding: 10px;
+  border-radius: 12px;
+
+  cursor: pointer;
+
+  &:hover {
+    background-color: rgba(0, 0, 0, .05);
+  }
+
+  &.active {
+    background-color: #DDF2FF;
+
+    .arrow {
+      opacity: 1;
+    }
+  }
+
+  .img {
+    position: relative;
+    width: 60px;
+    height: 60px;
+    border-radius: 18px;
+    margin-right: 12px;
+    overflow: hidden;
+    flex-shrink: 0;
+
+    :global {
+      .n-image {
+        width: 60px;
+        height: 60px;
+      }
+    }
+
+    img {
+      transition: opacity .3s;
+      opacity: 0;
+      height: 100%;
+      width: 100%;
+    }
+
+    img[data-loaded="true"] {
+      opacity: 1;
+    }
+  }
+
+  .title {
+    flex: 1;
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+    align-items: flex-start;
+
+    .titleName {
+      font-size: calc(17px, 12Px);
+      font-weight: 600;
+      color: #131415;
+      line-height: 28px;
+      width: 100%;
+    }
+
+    .titleDes {
+      font-size: 14px;
+      font-weight: 400;
+      color: #777777;
+      line-height: 20px;
+      max-width: 100%;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+      overflow: hidden;
+    }
+  }
+
+  .btn {
+    margin-left: auto;
+    width: 84px;
+    height: 40px;
+    background: linear-gradient(to right, #44CAFF, #259DFE);
+    border: none;
+    padding: 0;
+    font-weight: bold !important;
+    flex-shrink: 0;
+    min-width: 62px;
+    min-height: 30px;
+
+    :global {
+      .n-button__content {
+        &>img {
+          margin-left: 10px;
+          width: 9px;
+          height: 12px;
+        }
+      }
+    }
+  }
+
+  .arrow {
+    position: absolute;
+    top: 50%;
+    right: 12px;
+    transform: translate(124%, -50%);
+    opacity: 0;
+  }
+
+  .showPlayLoading {
+    opacity: 0;
+  }
+
+}
+
+.loadingWrap {
+  display: flex;
+  justify-content: center;
+  min-height: 80px;
+}
+
+.musicStaff {
+  display: flex;
+  flex-direction: column;
+  position: relative;
+  left: -8px;
+  flex: 1;
+  background-color: #fff;
+  border-radius: 16px;
+  // height: 100%;
+  z-index: 1;
+  overflow: hidden;
+
+  &::-webkit-scrollbar {
+    width: 0;
+    display: none;
+  }
+
+
+  .musicTitle {
+    padding: 27px 27px 13px;
+    font-size: max(18px, 14Px);
+    font-weight: 600;
+    color: #000000;
+    line-height: 25px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+
+    .musicTitleLeft {
+      display: flex;
+      align-items: center;
+    }
+
+    .icon1,
+    .icon3 {
+      display: inline-block;
+      width: 23px;
+      height: 23px;
+      margin-right: 8px;
+      background: url('../images/icon-1.png') no-repeat center;
+      background-size: contain;
+    }
+
+    .icon3 {
+      background: url('../images/icon-3.png') no-repeat center;
+      background-size: contain;
+    }
+
+    .musicTitleRight {
+      .textRead, .textClose {
+        padding: 7px 8px;
+        background: #E8F4FF;
+        border-radius: 13px;
+        font-weight: 500;
+        font-size: max(13px, 12Px);
+        color: #0378EC;
+        line-height: 18px;
+        display: flex;
+        align-items: center;
+      }
+
+      .icon {
+        display: inline-block;
+        margin-right: 5px;
+        width: 15px;
+        height: 15px;
+      }
+
+      .textRead {
+        .icon {
+          background: url('../images/icon-speak-sound.png');
+          background-size: contain;
+        }
+      }
+      .textClose {
+        .icon {
+          background: url('../images/icon-speak-close.png');
+          background-size: contain;
+        }
+      }
+    }
+  }
+
+  .musicContent {
+    flex: 1;
+    overflow-y: auto;
+    height: 100%;
+    padding: 0 27px;
+    user-select: text;
+
+    &>img {
+      width: 100%;
+    }
+
+    section,
+    &>div {
+      font-size: inherit !important;
+    }
+  }
+}
+
+.staffImgs {
+  flex: 1;
+  overflow-y: auto;
+  height: 100%;
+  padding: 0 30px;
+
+  &>img {
+    width: 100%;
+  }
+}
+
+
+:global {
+
+  .van-fade-enter-active,
+  .van-fade-leave-active {
+    transition: all 0.3s;
+  }
+
+  .van-fade-enter-from,
+  .van-fade-leave-to {
+    opacity: 0;
+  }
+}
+
+.changeSizeSection {
+  position: absolute;
+  right: 10px;
+  bottom: 50%;
+  width: 35px;
+  transform: translate(0, 50%);
+  background: #fff;
+  border-radius: 7px;
+  display: flex;
+  align-items: center;
+  flex-direction: column;
+  padding: 13px 0;
+
+  .iconT {
+    width: 15px;
+    height: 15px;
+  }
+
+  .iconAddT,
+  .iconPlusT {
+    width: 23px;
+    height: 23px;
+    cursor: pointer;
+  }
+
+  .iconAddT {
+    margin-top: 13px;
+    margin-bottom: 8px;
+  }
+
+  .iconPlusT {
+    margin-top: 8px;
+  }
+
+  :global {
+    .n-slider {
+      height: 125px;
+      --n-handle-size: 15px !important;
+      --n-rail-height: 0 !important;
+    }
+
+  }
+}

+ 413 - 319
src/views/content-information/content-instrument/detail.tsx

@@ -1,319 +1,413 @@
-import {
-  NBreadcrumb,
-  NBreadcrumbItem,
-  NButton,
-  NImage,
-  NSlider,
-  NSpace,
-  NSpin
-} from 'naive-ui';
-import { computed, defineComponent, onMounted, reactive } from 'vue';
-import styles from './detail.module.less';
-import icon_back from '../../xiaoku-music/images/icon_back.png';
-import icon_arrow from '../../xiaoku-music/images/icon_arrow.png';
-import icon_play from '../../xiaoku-music/images/icon_play.png';
-import icon_pause from '../../xiaoku-music/images/icon_pause.png';
-import icon_default from '../../xiaoku-music/images/icon_default.png';
-import icon_separator from '../../xiaoku-music/images/icon_separator.png';
-import iconT from '../images/icon-t.png';
-import iconAddT from '../images/icon-add-t.png';
-import iconPlusT from '../images/icon-plus-t.png';
-import { useRoute, useRouter } from 'vue-router';
-import PlayLoading from '../../xiaoku-music/component/play-loading';
-import TheNoticeBar from '/src/components/TheNoticeBar';
-import TheEmpty from '/src/components/TheEmpty';
-import PlayItem from '../../xiaoku-music/component/play-item';
-import { api_knowledgeWiki_detail } from '../api';
-import { state } from '/src/state';
-
-export default defineComponent({
-  name: 'instrument-detail',
-  setup() {
-    const route = useRoute();
-    const router = useRouter();
-    const forms = reactive({
-      page: 1,
-      rows: 20,
-      status: true,
-      name: '', // 关键词
-      type: route.query.type
-    });
-    const data = reactive({
-      loading: false,
-      finshed: false,
-      reshing: false,
-      details: {} as any,
-      list: [] as any,
-      listActive: 0,
-      playState: 'pause' as 'play' | 'pause',
-      showPlayer: false,
-      showPreivew: false,
-      previewUrl: '',
-      showCloseBtn: true,
-      fontSize: 18 // 默认18
-    });
-
-    /** 选中的item */
-    const activeItem = computed(() => {
-      return data.list[data.listActive] || {};
-    });
-
-    /** 播放曲目 */
-    const handlePlay = (item: any) => {
-      const index = data.list.findIndex((_item: any) => _item.id === item.id);
-      if (index > -1) {
-        if (data.listActive === index) {
-          data.playState = data.playState === 'play' ? 'pause' : 'play';
-        } else {
-          data.playState = 'play';
-        }
-        data.showPlayer = true;
-        data.listActive = index;
-      }
-    };
-
-    /** 音频控制 */
-    const handleChangeAudio = (
-      type: 'play' | 'pause' | 'pre' | 'next' | 'favitor'
-    ) => {
-      if (type === 'play') {
-        data.playState = 'play';
-      } else if (type === 'pause') {
-        data.playState = 'pause';
-      } else if (type === 'pre') {
-        if (data.list[data.listActive - 1]) {
-          handlePlay(data.list[data.listActive - 1]);
-        }
-      } else if (type === 'next') {
-        if (data.list[data.listActive + 1]) {
-          handlePlay(data.list[data.listActive + 1]);
-        }
-      }
-    };
-
-    const getDetail = async () => {
-      data.loading = true;
-      let res = {} as any;
-      try {
-        res = await api_knowledgeWiki_detail({ id: route.query.id });
-      } catch (error) {
-        console.log(error);
-      }
-      if (data.reshing) {
-        data.list = [];
-        data.reshing = false;
-      }
-
-      data.finshed = true;
-      data.list = res.data.knowledgeWikiResources || [];
-      data.list.forEach((item: any) => {
-        item.audioFileUrl = item.url;
-        item.musicSheetName = item.name;
-      });
-      const knowledgeWikiCategories = res.data.knowledgeWikiCategories || [];
-      res.data.knowledgeName =
-        knowledgeWikiCategories.length > 0
-          ? knowledgeWikiCategories[0].knowledgeWikiCategoryTypeName
-          : '';
-      res.data.intros = res.data.intros.replace(
-        /<video/gi,
-        '<video style="width: 100% !important;" controlslist="nodownload"'
-      );
-      data.details = res.data;
-      data.loading = false;
-    };
-
-    onMounted(() => {
-      getDetail();
-    });
-    return () => (
-      <div class={styles.container}>
-        <NSpace align="center" wrapItem={false} size={16}>
-          <img
-            style={{ cursor: 'pointer' }}
-            src={icon_back}
-            class={styles.iconBack}
-            onClick={() => {
-              const path =
-                forms.type === 'MUSICIAN'
-                  ? '/content-musician'
-                  : '/content-instruments';
-              router.push({ path });
-            }}
-          />
-          <NBreadcrumb separator="">
-            <NBreadcrumbItem
-              onClick={() => {
-                const path =
-                  forms.type === 'MUSICIAN'
-                    ? '/content-musician'
-                    : '/content-instruments';
-                router.push({ path });
-              }}>
-              {forms.type === 'MUSICIAN' ? '音乐家' : '乐器百科'}
-            </NBreadcrumbItem>
-            <img class={styles.separator} src={icon_separator} />
-            <NBreadcrumbItem>{route.query.name}</NBreadcrumbItem>
-          </NBreadcrumb>
-        </NSpace>
-
-        <div class={[styles.wrap, data.showPlayer ? styles.wrapBottom : '']}>
-          <div class={styles.content}>
-            <div class={styles.contentWrap}>
-              <div class={[styles.musicList, 'musicList-container']}>
-                <div class={styles.wrapList}>
-                  <div class={styles.instrumentGroup}>
-                    <NImage
-                      class={[
-                        styles.instrumentImg,
-                        forms.type === 'MUSICIAN' && styles.otherImg
-                      ]}
-                      src={data.details?.avatar}
-                      objectFit="cover"
-                    />
-
-                    <p class={styles.instrumentName}>{data.details.name}</p>
-                    <p class={styles.instrumentTag}>
-                      {data.details.knowledgeName}
-                    </p>
-                  </div>
-
-                  <div class={styles.titlec}>
-                    <i class={styles.icon2}></i>代表作
-                  </div>
-
-                  {data.list.map((item: any, index: any) => {
-                    return (
-                      <div class={styles.itemContainer}>
-                        <div
-                          class={[
-                            styles.item
-                            // data.listActive === index && styles.active
-                          ]}
-                          onClick={(e: Event) => {
-                            e.stopPropagation();
-                            handlePlay(item);
-                          }}>
-                          <div class={styles.img}>
-                            <NImage
-                              lazy
-                              objectFit="cover"
-                              previewDisabled={true}
-                              src={item.titleImg || icon_default}
-                              onLoad={e => {
-                                (e.target as any).dataset.loaded = 'true';
-                              }}
-                            />
-                            <PlayLoading
-                              class={[
-                                data.listActive === index &&
-                                data.playState === 'play'
-                                  ? ''
-                                  : styles.showPlayLoading
-                              ]}
-                            />
-                          </div>
-                          <div class={styles.title}>
-                            <div class={styles.titleName}>
-                              <TheNoticeBar
-                                text={item.name}
-                                style={{ marginRight: '12px' }}
-                              />
-                            </div>
-                          </div>
-
-                          <NButton
-                            color="#259CFE"
-                            textColor="#fff"
-                            round
-                            class={styles.btn}
-                            type="primary"
-                            onClick={(e: Event) => {
-                              e.stopPropagation();
-                              handlePlay(item);
-                            }}>
-                            播放
-                            <img
-                              src={
-                                data.listActive === index &&
-                                data.playState === 'play'
-                                  ? icon_pause
-                                  : icon_play
-                              }
-                            />
-                          </NButton>
-
-                          <img class={styles.arrow} src={icon_arrow} />
-                        </div>
-                      </div>
-                    );
-                  })}
-                  {!data.finshed && (
-                    <div class={styles.loadingWrap}>
-                      <NSpin show={true}></NSpin>
-                    </div>
-                  )}
-                  {!data.loading && data.list.length === 0 && (
-                    <div class={styles.empty}>
-                      <TheEmpty description="暂无代表作"></TheEmpty>
-                    </div>
-                  )}
-                </div>
-              </div>
-
-              <div class={styles.musicStaff}>
-                <div class={styles.musicTitle}>
-                  <i
-                    class={
-                      forms.type === 'MUSICIAN' ? styles.icon3 : styles.icon1
-                    }></i>
-                  {forms.type === 'MUSICIAN' ? '个人简介' : '乐器简介'}
-                </div>
-                <div
-                  class={styles.musicContent}
-                  v-html={data.details?.intros}
-                  style={{ fontSize: data.fontSize + 'px' }}></div>
-              </div>
-
-              <div class={styles.changeSizeSection}>
-                <img src={iconT} class={styles.iconT} />
-                <img
-                  src={iconAddT}
-                  class={styles.iconAddT}
-                  onClick={() => {
-                    if (data.fontSize >= 32) return;
-                    data.fontSize += 1;
-                  }}
-                />
-                <NSlider
-                  v-model:value={data.fontSize}
-                  placement="left"
-                  vertical
-                  min={12}
-                  max={32}
-                />
-                <img
-                  src={iconPlusT}
-                  class={styles.iconPlusT}
-                  onClick={() => {
-                    if (data.fontSize <= 12) return;
-                    data.fontSize -= 1;
-                  }}
-                />
-              </div>
-            </div>
-          </div>
-        </div>
-
-        {data.list.length !== 0 && (
-          <PlayItem
-            show={data.showPlayer}
-            playState={data.playState}
-            item={activeItem.value}
-            onChange={value => handleChangeAudio(value)}
-          />
-        )}
-      </div>
-    );
-  }
-});
+import {
+  NBreadcrumb,
+  NBreadcrumbItem,
+  NButton,
+  NImage,
+  NSlider,
+  NSpace,
+  NSpin
+} from 'naive-ui';
+import {
+  computed,
+  defineComponent,
+  onMounted,
+  onUnmounted,
+  reactive
+} from 'vue';
+import styles from './detail.module.less';
+import icon_back from '../../xiaoku-music/images/icon_back.png';
+import icon_arrow from '../../xiaoku-music/images/icon_arrow.png';
+import icon_play from '../../xiaoku-music/images/icon_play.png';
+import icon_pause from '../../xiaoku-music/images/icon_pause.png';
+import icon_default from '../../xiaoku-music/images/icon_default.png';
+import icon_separator from '../../xiaoku-music/images/icon_separator.png';
+import iconT from '../images/icon-t.png';
+import iconAddT from '../images/icon-add-t.png';
+import iconPlusT from '../images/icon-plus-t.png';
+import { useRoute, useRouter } from 'vue-router';
+import PlayLoading from '../../xiaoku-music/component/play-loading';
+import TheNoticeBar from '/src/components/TheNoticeBar';
+import TheEmpty from '/src/components/TheEmpty';
+import PlayItem from '../../xiaoku-music/component/play-item';
+import { api_knowledgeWiki_detail } from '../api';
+import { state } from '/src/state';
+
+export default defineComponent({
+  name: 'instrument-detail',
+  setup() {
+    const route = useRoute();
+    const router = useRouter();
+    const forms = reactive({
+      page: 1,
+      rows: 20,
+      status: true,
+      name: '', // 关键词
+      type: route.query.type
+    });
+    const data = reactive({
+      loading: false,
+      finshed: false,
+      reshing: false,
+      details: {} as any,
+      list: [] as any,
+      listActive: 0,
+      playState: 'pause' as 'play' | 'pause',
+      showPlayer: false,
+      showPreivew: false,
+      previewUrl: '',
+      showCloseBtn: true,
+      fontSize: 18, // 默认18
+
+      isSpeak: false // 是否在播放
+    });
+
+    /** 选中的item */
+    const activeItem = computed(() => {
+      return data.list[data.listActive] || {};
+    });
+
+    /** 播放曲目 */
+    const handlePlay = (item: any) => {
+      const index = data.list.findIndex((_item: any) => _item.id === item.id);
+      if (index > -1) {
+        if (data.listActive === index) {
+          data.playState = data.playState === 'play' ? 'pause' : 'play';
+        } else {
+          data.playState = 'play';
+        }
+        data.showPlayer = true;
+        data.listActive = index;
+      }
+    };
+
+    /** 音频控制 */
+    const handleChangeAudio = (
+      type: 'play' | 'pause' | 'pre' | 'next' | 'favitor'
+    ) => {
+      if (type === 'play') {
+        data.playState = 'play';
+      } else if (type === 'pause') {
+        data.playState = 'pause';
+      } else if (type === 'pre') {
+        if (data.list[data.listActive - 1]) {
+          handlePlay(data.list[data.listActive - 1]);
+        }
+      } else if (type === 'next') {
+        if (data.list[data.listActive + 1]) {
+          handlePlay(data.list[data.listActive + 1]);
+        }
+      }
+    };
+
+    const getDetail = async () => {
+      data.loading = true;
+      let res = {} as any;
+      try {
+        res = await api_knowledgeWiki_detail({ id: route.query.id });
+      } catch (error) {
+        console.log(error);
+      }
+      if (data.reshing) {
+        data.list = [];
+        data.reshing = false;
+      }
+
+      data.finshed = true;
+      data.list = res.data.knowledgeWikiResources || [];
+      data.list.forEach((item: any) => {
+        item.audioFileUrl = item.url;
+        item.musicSheetName = item.name;
+      });
+      const knowledgeWikiCategories = res.data.knowledgeWikiCategories || [];
+      res.data.knowledgeName =
+        knowledgeWikiCategories.length > 0
+          ? knowledgeWikiCategories[0].knowledgeWikiCategoryTypeName
+          : '';
+      res.data.intros = res.data.intros.replace(
+        /<video/gi,
+        '<video style="width: 100% !important;" controlslist="nodownload"'
+      );
+      data.details = res.data;
+      data.loading = false;
+    };
+
+    const getSleectText = () => {
+      const selection: any = window.getSelection();
+      console.log(selection.toString());
+
+      // console.log(document.querySelector('#musicContent')?.textContent);
+
+      if (selection.rangeCount > 0) {
+        const range = selection.getRangeAt(0);
+        let container = range.commonAncestorContainer;
+        if (container.nodeType === 3) {
+          // 文本节点
+          container = container.parentNode;
+        }
+        const rect = container.getBoundingClientRect();
+        const startOffset = range.startOffset;
+        const endOffset = range.endOffset;
+
+        // 计算选中部分在容器内的起始和结束位置
+        const startX =
+          rect.left +
+          (startOffset > 0
+            ? (container.offsetWidth * startOffset) /
+              container.textContent.length
+            : 0);
+        const endX =
+          rect.left +
+          (endOffset > 0
+            ? (container.offsetWidth * endOffset) / container.textContent.length
+            : 0);
+
+        console.log({
+          startX: startX,
+          startY: rect.top,
+          endX: endX,
+          endY: rect.bottom
+        });
+      }
+    };
+
+    const onCloseSpeak = () => {};
+    const onAllSpeak = () => {
+      try {
+        const text = document.querySelector('#musicContent')?.textContent;
+        // 事件监听(如果需要)
+        // @ts-ignore
+        responsiveVoice.speak(text, 'Chinese Male', {
+          onstart: () => {
+            console.log('开始朗读');
+            data.isSpeak = true;
+          },
+          onend: () => {
+            console.log('朗读结束');
+            data.isSpeak = false;
+          },
+          onerror: e => {
+            console.error('朗读错误:', e);
+            data.isSpeak = false;
+          }
+        });
+      } catch (e: any) {
+        console.log(e, '12');
+      }
+    };
+
+    onMounted(async () => {
+      // init();
+      getDetail();
+
+      document.addEventListener('mouseup', getSleectText);
+    });
+
+    onUnmounted(() => {
+      document.removeEventListener('mouseup', getSleectText);
+    });
+    return () => (
+      <div class={styles.container}>
+        <NSpace align="center" wrapItem={false} size={16}>
+          <img
+            style={{ cursor: 'pointer' }}
+            src={icon_back}
+            class={styles.iconBack}
+            onClick={() => {
+              const path =
+                forms.type === 'MUSICIAN'
+                  ? '/content-musician'
+                  : '/content-instruments';
+              router.push({ path });
+            }}
+          />
+          <NBreadcrumb separator="">
+            <NBreadcrumbItem
+              onClick={() => {
+                const path =
+                  forms.type === 'MUSICIAN'
+                    ? '/content-musician'
+                    : '/content-instruments';
+                router.push({ path });
+              }}>
+              {forms.type === 'MUSICIAN' ? '音乐家' : '乐器百科'}
+            </NBreadcrumbItem>
+            <img class={styles.separator} src={icon_separator} />
+            <NBreadcrumbItem>{route.query.name}</NBreadcrumbItem>
+          </NBreadcrumb>
+        </NSpace>
+
+        <div class={[styles.wrap, data.showPlayer ? styles.wrapBottom : '']}>
+          <div class={styles.content}>
+            <div class={styles.contentWrap}>
+              <div class={[styles.musicList, 'musicList-container']}>
+                <div class={styles.wrapList}>
+                  <div class={styles.instrumentGroup}>
+                    <NImage
+                      class={[
+                        styles.instrumentImg,
+                        forms.type === 'MUSICIAN' && styles.otherImg
+                      ]}
+                      src={data.details?.avatar}
+                      objectFit="cover"
+                    />
+
+                    <p class={styles.instrumentName}>{data.details.name}</p>
+                    <p class={styles.instrumentTag}>
+                      {data.details.knowledgeName}
+                    </p>
+                  </div>
+
+                  <div class={styles.titlec}>
+                    <i class={styles.icon2}></i>代表作
+                  </div>
+
+                  {data.list.map((item: any, index: any) => {
+                    return (
+                      <div class={styles.itemContainer}>
+                        <div
+                          class={[
+                            styles.item
+                            // data.listActive === index && styles.active
+                          ]}
+                          onClick={(e: Event) => {
+                            e.stopPropagation();
+                            handlePlay(item);
+                          }}>
+                          <div class={styles.img}>
+                            <NImage
+                              lazy
+                              objectFit="cover"
+                              previewDisabled={true}
+                              src={item.titleImg || icon_default}
+                              onLoad={e => {
+                                (e.target as any).dataset.loaded = 'true';
+                              }}
+                            />
+                            <PlayLoading
+                              class={[
+                                data.listActive === index &&
+                                data.playState === 'play'
+                                  ? ''
+                                  : styles.showPlayLoading
+                              ]}
+                            />
+                          </div>
+                          <div class={styles.title}>
+                            <div class={styles.titleName}>
+                              <TheNoticeBar
+                                text={item.name}
+                                style={{ marginRight: '12px' }}
+                              />
+                            </div>
+                          </div>
+
+                          <NButton
+                            color="#259CFE"
+                            textColor="#fff"
+                            round
+                            class={styles.btn}
+                            type="primary"
+                            onClick={(e: Event) => {
+                              e.stopPropagation();
+                              handlePlay(item);
+                            }}>
+                            播放
+                            <img
+                              src={
+                                data.listActive === index &&
+                                data.playState === 'play'
+                                  ? icon_pause
+                                  : icon_play
+                              }
+                            />
+                          </NButton>
+
+                          <img class={styles.arrow} src={icon_arrow} />
+                        </div>
+                      </div>
+                    );
+                  })}
+                  {!data.finshed && (
+                    <div class={styles.loadingWrap}>
+                      <NSpin show={true}></NSpin>
+                    </div>
+                  )}
+                  {!data.loading && data.list.length === 0 && (
+                    <div class={styles.empty}>
+                      <TheEmpty description="暂无代表作"></TheEmpty>
+                    </div>
+                  )}
+                </div>
+              </div>
+
+              <div class={styles.musicStaff}>
+                <div class={styles.musicTitle}>
+                  <div class={styles.musicTitleLeft}>
+                    <i
+                      class={
+                        forms.type === 'MUSICIAN' ? styles.icon3 : styles.icon1
+                      }></i>
+                    {forms.type === 'MUSICIAN' ? '个人简介' : '乐器简介'}
+                  </div>
+
+                  <div class={styles.musicTitleRight}>
+                    {data.isSpeak ? (
+                      <span class={styles.textClose} onClick={onCloseSpeak}>
+                        <i class={styles.icon}></i>关闭朗读
+                      </span>
+                    ) : (
+                      <span class={styles.textRead} onClick={onAllSpeak}>
+                        <i class={styles.icon}></i>全文朗读
+                      </span>
+                    )}
+                  </div>
+                </div>
+                <div
+                  class={styles.musicContent}
+                  id="musicContent"
+                  v-html={data.details?.intros}
+                  style={{ fontSize: data.fontSize + 'px' }}></div>
+              </div>
+
+              <div class={styles.changeSizeSection}>
+                <img src={iconT} class={styles.iconT} />
+                <img
+                  src={iconAddT}
+                  class={styles.iconAddT}
+                  onClick={() => {
+                    if (data.fontSize >= 32) return;
+                    data.fontSize += 1;
+                  }}
+                />
+                <NSlider
+                  v-model:value={data.fontSize}
+                  placement="left"
+                  vertical
+                  min={12}
+                  max={32}
+                />
+                <img
+                  src={iconPlusT}
+                  class={styles.iconPlusT}
+                  onClick={() => {
+                    if (data.fontSize <= 12) return;
+                    data.fontSize -= 1;
+                  }}
+                />
+              </div>
+            </div>
+          </div>
+        </div>
+
+        {data.list.length !== 0 && (
+          <PlayItem
+            show={data.showPlayer}
+            playState={data.playState}
+            item={activeItem.value}
+            onChange={value => handleChangeAudio(value)}
+          />
+        )}
+      </div>
+    );
+  }
+});

BIN
src/views/content-information/images/icon-speak-arrow.png


BIN
src/views/content-information/images/icon-speak-close.png


BIN
src/views/content-information/images/icon-speak-line.png


BIN
src/views/content-information/images/icon-speak-sound.png