From ce14970503d3186e499f4b1fb818b067308b10a2 Mon Sep 17 00:00:00 2001 From: MuzzyZY Date: Wed, 6 Nov 2024 18:32:46 +0800 Subject: [PATCH 001/199] =?UTF-8?q?Update:=E5=85=A8=E9=83=A8=E9=87=87?= =?UTF-8?q?=E7=94=A8arco=E9=87=8D=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard/package-lock.json | 1198 ---- dashboard/package.json | 11 +- dashboard/src/App.vue | 87 +- dashboard/src/components/Footer.vue | 20 + dashboard/src/components/Header.vue | 123 + dashboard/src/components/HeaderToolbar.vue | 77 - dashboard/src/components/Layout.vue | 157 + dashboard/src/components/SideBarMenu.vue | 156 - dashboard/src/main.js | 27 +- dashboard/src/mock/data.json | 6163 ++++++++++++++++++++ dashboard/src/mock/middlewares.js | 12 + dashboard/src/stores/siteStore.js | 16 + dashboard/src/utils/req.js | 53 + dashboard/src/views/Collection.vue | 19 + dashboard/src/views/History.vue | 7 + dashboard/src/views/Home.vue | 115 +- dashboard/src/views/Live.vue | 7 + dashboard/src/views/Settings.vue | 7 + dashboard/src/views/Video.vue | 492 +- dashboard/vite.config.js | 14 +- dashboard/yarn.lock | 511 +- readme.md | 7 + 22 files changed, 7495 insertions(+), 1784 deletions(-) delete mode 100644 dashboard/package-lock.json create mode 100644 dashboard/src/components/Footer.vue create mode 100644 dashboard/src/components/Header.vue delete mode 100644 dashboard/src/components/HeaderToolbar.vue create mode 100644 dashboard/src/components/Layout.vue delete mode 100644 dashboard/src/components/SideBarMenu.vue create mode 100644 dashboard/src/mock/data.json create mode 100644 dashboard/src/mock/middlewares.js create mode 100644 dashboard/src/stores/siteStore.js create mode 100644 dashboard/src/utils/req.js diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json deleted file mode 100644 index b43e5fb..0000000 --- a/dashboard/package-lock.json +++ /dev/null @@ -1,1198 +0,0 @@ -{ - "name": "dashboard", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "dashboard", - "version": "0.0.0", - "dependencies": { - "@primevue/themes": "^4.2.1", - "primevue": "^4.2.1", - "vue": "^3.5.12" - }, - "devDependencies": { - "@primevue/auto-import-resolver": "^4.2.1", - "@vitejs/plugin-vue": "^5.1.4", - "unplugin-vue-components": "^0.27.4", - "vite": "^5.4.10" - } - }, - "node_modules/@antfu/utils": { - "version": "0.7.10", - "resolved": "https://registry.npmmirror.com/@antfu/utils/-/utils-0.7.10.tgz", - "integrity": "sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.26.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "license": "MIT" - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@primeuix/styled": { - "version": "0.3.0", - "resolved": "https://registry.npmmirror.com/@primeuix/styled/-/styled-0.3.0.tgz", - "integrity": "sha512-XsLbmyM1u50A0EDATIHyqm5O/zOCSyNKPk4pNN8HFvEPehbsjf4tkXcRZAyaVvntSCLpV4XGAj7v5EDCQkBRlg==", - "license": "MIT", - "dependencies": { - "@primeuix/utils": "^0.3.0" - }, - "engines": { - "node": ">=12.11.0" - } - }, - "node_modules/@primeuix/utils": { - "version": "0.3.0", - "resolved": "https://registry.npmmirror.com/@primeuix/utils/-/utils-0.3.0.tgz", - "integrity": "sha512-d6ymWez1n+iqwzAVhyOTmrOHl5qnSX2oGlTy97qGuA15gLai+MQaxONHFNdDia8Q7o396v7KK9IvhAx9VET/+A==", - "license": "MIT", - "engines": { - "node": ">=12.11.0" - } - }, - "node_modules/@primevue/auto-import-resolver": { - "version": "4.2.1", - "resolved": "https://registry.npmmirror.com/@primevue/auto-import-resolver/-/auto-import-resolver-4.2.1.tgz", - "integrity": "sha512-NR2jTme+R/p9oapvysh4y8vFR6/w2ymK4lOzx+pZHHb+QGh8lJvkvJ5NWhwOhO9YD4RGOlQGKA4kcY2Z1itafA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@primevue/metadata": "4.2.1" - }, - "engines": { - "node": ">=12.11.0" - } - }, - "node_modules/@primevue/core": { - "version": "4.2.1", - "resolved": "https://registry.npmmirror.com/@primevue/core/-/core-4.2.1.tgz", - "integrity": "sha512-L81TZSZU8zRznIi2g6IWwlZ5wraaE8DrNUJyxieCRCTpbSF3rSlYmhDEuzal8PfE0RuvXpRsxqedTHxz5cdqPg==", - "license": "MIT", - "dependencies": { - "@primeuix/styled": "^0.3.0", - "@primeuix/utils": "^0.3.0" - }, - "engines": { - "node": ">=12.11.0" - }, - "peerDependencies": { - "vue": "^3.3.0" - } - }, - "node_modules/@primevue/icons": { - "version": "4.2.1", - "resolved": "https://registry.npmmirror.com/@primevue/icons/-/icons-4.2.1.tgz", - "integrity": "sha512-TOhxgkcmgBqmlHlf2x+gs4874iHopkow0gRAC5FztZTgTZQrqy8hPIA9b4O1lW7P6GOjGuVIwSH8y2lw6Q8koA==", - "license": "MIT", - "dependencies": { - "@primeuix/utils": "^0.3.0", - "@primevue/core": "4.2.1" - }, - "engines": { - "node": ">=12.11.0" - } - }, - "node_modules/@primevue/metadata": { - "version": "4.2.1", - "resolved": "https://registry.npmmirror.com/@primevue/metadata/-/metadata-4.2.1.tgz", - "integrity": "sha512-XX29c2FtbXo0EX8GoYYT9os0FMxAZBPqq6VTAhbHrIUWzKnR8SVrxWoyy6G0wbzP3qXD4X3T7wUhjvQYHtTzLg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.11.0" - } - }, - "node_modules/@primevue/themes": { - "version": "4.2.1", - "resolved": "https://registry.npmmirror.com/@primevue/themes/-/themes-4.2.1.tgz", - "integrity": "sha512-byp4YejyVdrOpRRbq5vBtaDBFHUq7Wc0aGWwII1fliYbwQ+WXn/hCAYhaXwRrwweHpTiobiWWsS+PRLWJ7fBRw==", - "license": "MIT", - "dependencies": { - "@primeuix/styled": "^0.3.0" - }, - "engines": { - "node": ">=12.11.0" - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.1.3", - "resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.1.3.tgz", - "integrity": "sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.24.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.4.tgz", - "integrity": "sha512-LTw1Dfd0mBIEqUVCxbvTE/LLo+9ZxVC9k99v1v4ahg9Aak6FpqOfNu5kRkeTAn0wphoC4JU7No1/rL+bBCEwhg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@vitejs/plugin-vue": { - "version": "5.1.4", - "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.1.4.tgz", - "integrity": "sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "vite": "^5.0.0", - "vue": "^3.2.25" - } - }, - "node_modules/@vue/compiler-core": { - "version": "3.5.12", - "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.12.tgz", - "integrity": "sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.25.3", - "@vue/shared": "3.5.12", - "entities": "^4.5.0", - "estree-walker": "^2.0.2", - "source-map-js": "^1.2.0" - } - }, - "node_modules/@vue/compiler-dom": { - "version": "3.5.12", - "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz", - "integrity": "sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==", - "license": "MIT", - "dependencies": { - "@vue/compiler-core": "3.5.12", - "@vue/shared": "3.5.12" - } - }, - "node_modules/@vue/compiler-sfc": { - "version": "3.5.12", - "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.12.tgz", - "integrity": "sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.25.3", - "@vue/compiler-core": "3.5.12", - "@vue/compiler-dom": "3.5.12", - "@vue/compiler-ssr": "3.5.12", - "@vue/shared": "3.5.12", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.11", - "postcss": "^8.4.47", - "source-map-js": "^1.2.0" - } - }, - "node_modules/@vue/compiler-ssr": { - "version": "3.5.12", - "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz", - "integrity": "sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==", - "license": "MIT", - "dependencies": { - "@vue/compiler-dom": "3.5.12", - "@vue/shared": "3.5.12" - } - }, - "node_modules/@vue/reactivity": { - "version": "3.5.12", - "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.12.tgz", - "integrity": "sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==", - "license": "MIT", - "dependencies": { - "@vue/shared": "3.5.12" - } - }, - "node_modules/@vue/runtime-core": { - "version": "3.5.12", - "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.12.tgz", - "integrity": "sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==", - "license": "MIT", - "dependencies": { - "@vue/reactivity": "3.5.12", - "@vue/shared": "3.5.12" - } - }, - "node_modules/@vue/runtime-dom": { - "version": "3.5.12", - "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.12.tgz", - "integrity": "sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==", - "license": "MIT", - "dependencies": { - "@vue/reactivity": "3.5.12", - "@vue/runtime-core": "3.5.12", - "@vue/shared": "3.5.12", - "csstype": "^3.1.3" - } - }, - "node_modules/@vue/server-renderer": { - "version": "3.5.12", - "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.12.tgz", - "integrity": "sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==", - "license": "MIT", - "dependencies": { - "@vue/compiler-ssr": "3.5.12", - "@vue/shared": "3.5.12" - }, - "peerDependencies": { - "vue": "3.5.12" - } - }, - "node_modules/@vue/shared": { - "version": "3.5.12", - "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.12.tgz", - "integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==", - "license": "MIT" - }, - "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/local-pkg": { - "version": "0.5.0", - "resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-0.5.0.tgz", - "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mlly": "^1.4.2", - "pkg-types": "^1.0.3" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/magic-string": { - "version": "0.30.12", - "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.12.tgz", - "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mlly": { - "version": "1.7.2", - "resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.7.2.tgz", - "integrity": "sha512-tN3dvVHYVz4DhSXinXIk7u9syPYaJvio118uomkovAtWBT+RdbP6Lfh/5Lvo519YMmwBafwlh20IPTXIStscpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.12.1", - "pathe": "^1.1.2", - "pkg-types": "^1.2.0", - "ufo": "^1.5.4" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkg-types": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.2.1.tgz", - "integrity": "sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.2", - "pathe": "^1.1.2" - } - }, - "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.1.0", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/primevue": { - "version": "4.2.1", - "resolved": "https://registry.npmmirror.com/primevue/-/primevue-4.2.1.tgz", - "integrity": "sha512-cU9ZQVq9fitsQEIrfGeIl7xELBn61JCMxWkzcS9dkr165g29AvUrUNS9ufs1t2NoMJzE8VllwzweF/tSFAr2cw==", - "license": "MIT", - "dependencies": { - "@primeuix/styled": "^0.3.0", - "@primeuix/utils": "^0.3.0", - "@primevue/core": "4.2.1", - "@primevue/icons": "4.2.1" - }, - "engines": { - "node": ">=12.11.0" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/readdirp/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rollup": { - "version": "4.24.4", - "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.24.4.tgz", - "integrity": "sha512-vGorVWIsWfX3xbcyAS+I047kFKapHYivmkaT63Smj77XwvLSJos6M1xGqZnBPFQFBRZDOcG1QnYEIxAvTr/HjA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.6" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.24.4", - "@rollup/rollup-android-arm64": "4.24.4", - "@rollup/rollup-darwin-arm64": "4.24.4", - "@rollup/rollup-darwin-x64": "4.24.4", - "@rollup/rollup-freebsd-arm64": "4.24.4", - "@rollup/rollup-freebsd-x64": "4.24.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.24.4", - "@rollup/rollup-linux-arm-musleabihf": "4.24.4", - "@rollup/rollup-linux-arm64-gnu": "4.24.4", - "@rollup/rollup-linux-arm64-musl": "4.24.4", - "@rollup/rollup-linux-powerpc64le-gnu": "4.24.4", - "@rollup/rollup-linux-riscv64-gnu": "4.24.4", - "@rollup/rollup-linux-s390x-gnu": "4.24.4", - "@rollup/rollup-linux-x64-gnu": "4.24.4", - "@rollup/rollup-linux-x64-musl": "4.24.4", - "@rollup/rollup-win32-arm64-msvc": "4.24.4", - "@rollup/rollup-win32-ia32-msvc": "4.24.4", - "@rollup/rollup-win32-x64-msvc": "4.24.4", - "fsevents": "~2.3.2" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ufo": { - "version": "1.5.4", - "resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.5.4.tgz", - "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/unplugin": { - "version": "1.15.0", - "resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-1.15.0.tgz", - "integrity": "sha512-jTPIs63W+DUEDW207ztbaoO7cQ4p5aVaB823LSlxpsFEU3Mykwxf3ZGC/wzxFJeZlASZYgVrWeo7LgOrqJZ8RA==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.14.0", - "webpack-virtual-modules": "^0.6.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "webpack-sources": "^3" - }, - "peerDependenciesMeta": { - "webpack-sources": { - "optional": true - } - } - }, - "node_modules/unplugin-vue-components": { - "version": "0.27.4", - "resolved": "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-0.27.4.tgz", - "integrity": "sha512-1XVl5iXG7P1UrOMnaj2ogYa5YTq8aoh5jwDPQhemwO/OrXW+lPQKDXd1hMz15qxQPxgb/XXlbgo3HQ2rLEbmXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@antfu/utils": "^0.7.10", - "@rollup/pluginutils": "^5.1.0", - "chokidar": "^3.6.0", - "debug": "^4.3.6", - "fast-glob": "^3.3.2", - "local-pkg": "^0.5.0", - "magic-string": "^0.30.11", - "minimatch": "^9.0.5", - "mlly": "^1.7.1", - "unplugin": "^1.12.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@babel/parser": "^7.15.8", - "@nuxt/kit": "^3.2.2", - "vue": "2 || 3" - }, - "peerDependenciesMeta": { - "@babel/parser": { - "optional": true - }, - "@nuxt/kit": { - "optional": true - } - } - }, - "node_modules/vite": { - "version": "5.4.10", - "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.10.tgz", - "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vue": { - "version": "3.5.12", - "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.12.tgz", - "integrity": "sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==", - "license": "MIT", - "dependencies": { - "@vue/compiler-dom": "3.5.12", - "@vue/compiler-sfc": "3.5.12", - "@vue/runtime-dom": "3.5.12", - "@vue/server-renderer": "3.5.12", - "@vue/shared": "3.5.12" - }, - "peerDependencies": { - "typescript": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/webpack-virtual-modules": { - "version": "0.6.2", - "resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", - "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", - "dev": true, - "license": "MIT" - } - } -} diff --git a/dashboard/package.json b/dashboard/package.json index 298fb43..8031e82 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -6,19 +6,18 @@ "scripts": { "dev": "vite", "build": "vite build", - "preview": "vite preview" + "preview": "vite preview", + "mock": "json-server src/mock/data.json --port 9978 --middlewares src/mock/middlewares.js" }, "dependencies": { - "@primevue/themes": "^4.2.1", + "axios": "^1.7.7", + "json-server": "^1.0.0-beta.3", "pinia": "^2.2.6", - "primeflex": "^3.3.1", - "primeicons": "^7.0.0", - "primevue": "^4.2.1", "vue": "^3.5.12", "vue-router": "^4.4.5" }, "devDependencies": { - "@primevue/auto-import-resolver": "^4.2.1", + "@arco-design/web-vue": "^2.56.3", "@vitejs/plugin-vue": "^5.1.4", "unplugin-vue-components": "^0.27.4", "vite": "^5.4.10" diff --git a/dashboard/src/App.vue b/dashboard/src/App.vue index a7612e1..06eb161 100644 --- a/dashboard/src/App.vue +++ b/dashboard/src/App.vue @@ -1,92 +1,21 @@ diff --git a/dashboard/src/components/Footer.vue b/dashboard/src/components/Footer.vue new file mode 100644 index 0000000..7b61b45 --- /dev/null +++ b/dashboard/src/components/Footer.vue @@ -0,0 +1,20 @@ + + + + + diff --git a/dashboard/src/components/Header.vue b/dashboard/src/components/Header.vue new file mode 100644 index 0000000..adaa680 --- /dev/null +++ b/dashboard/src/components/Header.vue @@ -0,0 +1,123 @@ + + + + + diff --git a/dashboard/src/components/HeaderToolbar.vue b/dashboard/src/components/HeaderToolbar.vue deleted file mode 100644 index e9f9891..0000000 --- a/dashboard/src/components/HeaderToolbar.vue +++ /dev/null @@ -1,77 +0,0 @@ - - - - - diff --git a/dashboard/src/components/Layout.vue b/dashboard/src/components/Layout.vue new file mode 100644 index 0000000..f5d6a7d --- /dev/null +++ b/dashboard/src/components/Layout.vue @@ -0,0 +1,157 @@ + + + + + \ No newline at end of file diff --git a/dashboard/src/components/SideBarMenu.vue b/dashboard/src/components/SideBarMenu.vue deleted file mode 100644 index d2c7f69..0000000 --- a/dashboard/src/components/SideBarMenu.vue +++ /dev/null @@ -1,156 +0,0 @@ - - - - - diff --git a/dashboard/src/main.js b/dashboard/src/main.js index 05f6191..a80cd1e 100644 --- a/dashboard/src/main.js +++ b/dashboard/src/main.js @@ -1,21 +1,16 @@ import {createApp} from 'vue' -import PrimeVue from 'primevue/config'; -import Aura from '@primevue/themes/aura'; -import './style.css' -import 'primeflex/primeflex.css' -import 'primeflex/themes/primeone-light.css' // 亮系主题 -// import 'primeflex/themes/primeone-dark.css' // 暗系主题 -import 'primeicons/primeicons.css'; // 图标 -import router from './router'; // 引入路由 +// import './style.css' import App from './App.vue' -import { createPinia } from 'pinia'; +import router from './router' // 引入路由 +import ArcoVue from '@arco-design/web-vue' +import ArcoVueIcon from '@arco-design/web-vue/es/icon'; +import '@arco-design/web-vue/dist/arco.css' +import {createPinia} from 'pinia' -const app = createApp(App); -app.use(PrimeVue, { - theme: { - preset: Aura - } -}); + +const app = createApp(App) app.use(router) +app.use(ArcoVue); +app.use(ArcoVueIcon); app.use(createPinia()) -app.mount('#app') +app.mount('#app') \ No newline at end of file diff --git a/dashboard/src/mock/data.json b/dashboard/src/mock/data.json new file mode 100644 index 0000000..30a6d21 --- /dev/null +++ b/dashboard/src/mock/data.json @@ -0,0 +1,6163 @@ +{ + "config": { + "wallpaper":"https://tuapi.eees.cc/api.php?category=fengjing&type=302", + "hipy_sites_count":408, + "mode":1, + "spider":"https://hipy.trylyingto.me/files/jar/pg.jar?md5=7633f8ea346c082b7aa163be58aed023", + "homepage":"https://github.com/hjdhnx/hipy-server", + "homeLogo":"https://hipy.trylyingto.me/static/img/logo500x200-1.png", + "sites":[ + { + "key":"hipy_py_cntv央视", + "name":"cntv央视(hipy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/hipy/cntv央视.py", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":0, + "ext":"https://hipy.trylyingto.me/files/hipy/cntv央视.json" + }, + { + "key":"hipy_py_两个BT", + "name":"两个BT(hipy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/hipy/两个BT.py", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":9, + "ext":"https://hipy.trylyingto.me/files/hipy/两个BT.json" + }, + { + "key":"hipy_py_哔滴影视", + "name":"哔滴影视(hipy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/hipy/哔滴影视.py", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":10, + "ext":"https://hipy.trylyingto.me/files/hipy/jars/bidi.jar" + }, + { + "key":"hipy_py_喵次元", + "name":"喵次元(hipy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/hipy/喵次元.py", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":11, + "ext":"" + }, + { + "key":"hipy_py_新浪资源", + "name":"新浪资源(hipy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/hipy/新浪资源.py", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":12, + "ext":"" + }, + { + "key":"hipy_py_樱花动漫", + "name":"樱花动漫(hipy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/hipy/樱花动漫.py", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":14, + "ext":"https://jihulab.com/qiaoji/open/-/raw/main/yinghua" + }, + { + "key":"hipy_js_在线之家", + "name":"在线之家(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":54, + "ext":"https://hipy.trylyingto.me/files/drpy_js/在线之家.js" + }, + { + "key":"hipy_js_555影视[飞]", + "name":"555影视[飞](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":56, + "ext":"https://hipy.trylyingto.me/files/drpy_js/555影视[飞].js" + }, + { + "key":"hipy_js_freeok", + "name":"freeok(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":120, + "ext":"https://hipy.trylyingto.me/files/drpy_js/freeok.js" + }, + { + "key":"hipy_js_厂长资源", + "name":"厂长资源(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":121, + "ext":"https://hipy.trylyingto.me/files/drpy_js/厂长资源.js" + }, + { + "key":"hipy_js_耐看", + "name":"耐看(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":122, + "ext":"https://hipy.trylyingto.me/files/drpy_js/耐看.js" + }, + { + "key":"hipy_py_厂长资源", + "name":"厂长资源(hipy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/hipy/厂长资源.py", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":129, + "ext":"" + }, + { + "key":"hipy_py_在线之家", + "name":"在线之家(hipy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/hipy/在线之家.py", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":130, + "ext":"" + }, + { + "key":"hipy_js_白嫖者联盟", + "name":"白嫖者联盟(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":147, + "ext":"https://hipy.trylyingto.me/files/drpy_js/白嫖者联盟.js" + }, + { + "key":"hipy_js_大米星球", + "name":"大米星球(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":149, + "ext":"https://hipy.trylyingto.me/files/drpy_js/大米星球.js" + }, + { + "key":"hipy_js_6V新版[磁]", + "name":"6V新版[磁](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":150, + "ext":"https://hipy.trylyingto.me/files/drpy_js/6V新版[磁].js" + }, + { + "key":"hipy_js_777影视", + "name":"777影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":152, + "ext":"https://hipy.trylyingto.me/files/drpy_js/777影视.js" + }, + { + "key":"hipy_js_榜一短剧", + "name":"榜一短剧(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":154, + "ext":"https://hipy.trylyingto.me/files/drpy_js/榜一短剧.js" + }, + { + "key":"hipy_js_大米星球[V2]", + "name":"大米星球[V2](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":155, + "ext":"https://hipy.trylyingto.me/files/drpy_js/大米星球[V2].js" + }, + { + "key":"hipy_js_voflix", + "name":"voflix(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":156, + "ext":"https://hipy.trylyingto.me/files/drpy_js/voflix.js" + }, + { + "key":"hipy_js_TVB云播", + "name":"TVB云播(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":192, + "ext":"https://hipy.trylyingto.me/files/drpy_js/TVB云播.js" + }, + { + "key":"hipy_js_侠客影视", + "name":"侠客影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":194, + "ext":"https://hipy.trylyingto.me/files/drpy_js/侠客影视.js" + }, + { + "key":"hipy_js_七年影视", + "name":"七年影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":196, + "ext":"https://hipy.trylyingto.me/files/drpy_js/七年影视.js" + }, + { + "key":"hipy_js_磁力熊[磁]", + "name":"磁力熊[磁](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":197, + "ext":"https://hipy.trylyingto.me/files/drpy_js/磁力熊[磁].js" + }, + { + "key":"hipy_js_一起看", + "name":"一起看(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":0, + "quickSearch":0, + "filterable":1, + "order_num":211, + "ext":"https://hipy.trylyingto.me/files/drpy_js/一起看.js" + }, + { + "key":"hipy_js_可达影视", + "name":"可达影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":212, + "ext":"https://hipy.trylyingto.me/files/drpy_js/可达影视.js" + }, + { + "key":"hipy_js_千神影视", + "name":"千神影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":213, + "ext":"https://hipy.trylyingto.me/files/drpy_js/千神影视.js" + }, + { + "key":"hipy_js_海外剧汇", + "name":"海外剧汇(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":214, + "ext":"https://hipy.trylyingto.me/files/drpy_js/海外剧汇.js" + }, + { + "key":"hipy_js_美剧星球", + "name":"美剧星球(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":215, + "ext":"https://hipy.trylyingto.me/files/drpy_js/美剧星球.js" + }, + { + "key":"hipy_js_橙汁影视", + "name":"橙汁影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":0, + "quickSearch":0, + "filterable":1, + "order_num":216, + "ext":"https://hipy.trylyingto.me/files/drpy_js/橙汁影视.js" + }, + { + "key":"hipy_js_掌心世界", + "name":"掌心世界(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":221, + "ext":"https://hipy.trylyingto.me/files/drpy_js/掌心世界.js" + }, + { + "key":"hipy_js_宝片视频", + "name":"宝片视频(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":223, + "ext":"https://hipy.trylyingto.me/files/drpy_js/宝片视频.js" + }, + { + "key":"hipy_js_易看影视", + "name":"易看影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":224, + "ext":"https://hipy.trylyingto.me/files/drpy_js/易看影视.js" + }, + { + "key":"hipy_js_新茶杯狐", + "name":"新茶杯狐(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":225, + "ext":"https://hipy.trylyingto.me/files/drpy_js/新茶杯狐.js" + }, + { + "key":"hipy_js_看57", + "name":"看57(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":226, + "ext":"https://hipy.trylyingto.me/files/drpy_js/看57.js" + }, + { + "key":"hipy_js_神马电影[搜]", + "name":"神马电影[搜](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":227, + "ext":"https://hipy.trylyingto.me/files/drpy_js/神马电影[搜].js" + }, + { + "key":"hipy_js_极客资源[资]", + "name":"极客资源[资](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":0, + "order_num":228, + "ext":"https://hipy.trylyingto.me/files/drpy_js/极客资源[资].js" + }, + { + "key":"hipy_js_ok资源[资]", + "name":"ok资源[资](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":0, + "order_num":229, + "ext":"https://hipy.trylyingto.me/files/drpy_js/ok资源[资].js" + }, + { + "key":"hipy_js_索尼资源[资]", + "name":"索尼资源[资](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":0, + "order_num":230, + "ext":"https://hipy.trylyingto.me/files/drpy_js/索尼资源[资].js" + }, + { + "key":"hipy_js_卧龙资源[资]", + "name":"卧龙资源[资](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":0, + "order_num":231, + "ext":"https://hipy.trylyingto.me/files/drpy_js/卧龙资源[资].js" + }, + { + "key":"hipy_js_非凡资源[资]", + "name":"非凡资源[资](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":0, + "order_num":232, + "ext":"https://hipy.trylyingto.me/files/drpy_js/非凡资源[资].js" + }, + { + "key":"hipy_js_素白白[优]", + "name":"素白白[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":233, + "ext":"https://hipy.trylyingto.me/files/drpy_js/素白白[优].js" + }, + { + "key":"hipy_js_腾云驾雾[官]", + "name":"腾云驾雾[官](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":234, + "ext":"https://hipy.trylyingto.me/files/drpy_js/腾云驾雾[官].js" + }, + { + "key":"hipy_js_360影视[官]", + "name":"360影视[官](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":235, + "ext":"https://hipy.trylyingto.me/files/drpy_js/360影视[官].js" + }, + { + "key":"hipy_js_奇珍异兽[官]", + "name":"奇珍异兽[官](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":236, + "ext":"https://hipy.trylyingto.me/files/drpy_js/奇珍异兽[官].js" + }, + { + "key":"hipy_js_百忙无果[官]", + "name":"百忙无果[官](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":237, + "ext":"https://hipy.trylyingto.me/files/drpy_js/百忙无果[官].js" + }, + { + "key":"hipy_js_阿里土豆[盘]", + "name":"阿里土豆[盘](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":238, + "ext":"https://hipy.trylyingto.me/files/drpy_js/阿里土豆[盘].js" + }, + { + "key":"hipy_js_网飞猫[优]", + "name":"网飞猫[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":239, + "ext":"https://hipy.trylyingto.me/files/drpy_js/网飞猫[优].js" + }, + { + "key":"hipy_js_哔哩教育[官]", + "name":"哔哩教育[官](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":240, + "ext":"https://hipy.trylyingto.me/files/drpy_js/我的哔哩[官].js?render=1&type=url¶ms=../json/哔哩教育.json" + }, + { + "key":"hipy_js_哔哩大全[官]", + "name":"哔哩大全[官](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":240, + "ext":"https://hipy.trylyingto.me/files/drpy_js/我的哔哩[官].js?render=1&type=url¶ms=../json/哔哩大全.json" + }, + { + "key":"hipy_js_金鹰资源[资]", + "name":"金鹰资源[资](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":0, + "order_num":241, + "ext":"https://hipy.trylyingto.me/files/drpy_js/金鹰资源[资].js" + }, + { + "key":"hipy_js_南瓜影视[优]", + "name":"南瓜影视[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":242, + "ext":"https://hipy.trylyingto.me/files/drpy_js/南瓜影视[优].js" + }, + { + "key":"hipy_js_量子资源[资]", + "name":"量子资源[资](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":0, + "order_num":243, + "ext":"https://hipy.trylyingto.me/files/drpy_js/量子资源[资].js" + }, + { + "key":"hipy_js_烧火影视[优]", + "name":"烧火影视[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":0, + "quickSearch":0, + "filterable":1, + "order_num":244, + "ext":"https://hipy.trylyingto.me/files/drpy_js/烧火影视[优].js" + }, + { + "key":"hipy_js_可可影视[优]", + "name":"可可影视[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":245, + "ext":"https://hipy.trylyingto.me/files/drpy_js/可可影视[优].js" + }, + { + "key":"hipy_js_魔都资源[资]", + "name":"魔都资源[资](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":0, + "order_num":246, + "ext":"https://hipy.trylyingto.me/files/drpy_js/魔都资源[资].js" + }, + { + "key":"hipy_js_农民影视[优]", + "name":"农民影视[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":247, + "ext":"https://hipy.trylyingto.me/files/drpy_js/农民影视[优].js" + }, + { + "key":"hipy_js_爱看机器人[虫]", + "name":"爱看机器人[虫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":248, + "ext":"https://hipy.trylyingto.me/files/drpy_js/爱看机器人[虫].js" + }, + { + "key":"hipy_js_量子影视[资]", + "name":"量子影视[资](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":249, + "ext":"https://hipy.trylyingto.me/files/drpy_js/量子影视[资].js" + }, + { + "key":"hipy_js_暴风资源[资]", + "name":"暴风资源[资](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":0, + "order_num":250, + "ext":"https://hipy.trylyingto.me/files/drpy_js/暴风资源[资].js" + }, + { + "key":"hipy_js_豆瓣[官]", + "name":"豆瓣[官](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":251, + "ext":"https://hipy.trylyingto.me/files/drpy_js/豆瓣[官].js?render=1" + }, + { + "key":"hipy_js_玩偶哥哥[盘]", + "name":"玩偶哥哥[盘](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":252, + "ext":"https://hipy.trylyingto.me/files/drpy_js/玩偶哥哥[盘].js" + }, + { + "key":"hipy_js_LIBVIO[优]", + "name":"LIBVIO[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":253, + "ext":"https://hipy.trylyingto.me/files/drpy_js/LIBVIO[优].js" + }, + { + "key":"hipy_js_兔小贝[儿]", + "name":"兔小贝[儿](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":254, + "ext":"https://hipy.trylyingto.me/files/drpy_js/兔小贝[儿].js" + }, + { + "key":"hipy_js_花子动漫[漫]", + "name":"花子动漫[漫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":255, + "ext":"https://hipy.trylyingto.me/files/drpy_js/花子动漫[漫].js" + }, + { + "key":"hipy_js_优酷[官]", + "name":"优酷[官](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":256, + "ext":"https://hipy.trylyingto.me/files/drpy_js/优酷[官].js" + }, + { + "key":"hipy_js_哔哩影视[官]", + "name":"哔哩影视[官](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":257, + "ext":"https://hipy.trylyingto.me/files/drpy_js/哔哩影视[官].js?render=1" + }, + { + "key":"hipy_js_菜狗[官]", + "name":"菜狗[官](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":258, + "ext":"https://hipy.trylyingto.me/files/drpy_js/菜狗[官].js" + }, + { + "key":"hipy_js_极速资源[资]", + "name":"极速资源[资](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":0, + "order_num":259, + "ext":"https://hipy.trylyingto.me/files/drpy_js/极速资源[资].js" + }, + { + "key":"hipy_js_荐片[优]", + "name":"荐片[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":260, + "ext":"https://hipy.trylyingto.me/files/drpy_js/荐片[优].js" + }, + { + "key":"hipy_js_思古影视[V2]", + "name":"思古影视[V2](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":261, + "ext":"https://hipy.trylyingto.me/files/drpy_js/思古影视[V2].js" + }, + { + "key":"hipy_js_蛋蛋剧", + "name":"蛋蛋剧(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":264, + "ext":"https://hipy.trylyingto.me/files/drpy_js/蛋蛋剧.js" + }, + { + "key":"hipy_js_蛋蛋赞", + "name":"蛋蛋赞(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":265, + "ext":"https://hipy.trylyingto.me/files/drpy_js/蛋蛋赞.js" + }, + { + "key":"hipy_js_时光影院", + "name":"时光影院(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":269, + "ext":"https://hipy.trylyingto.me/files/drpy_js/时光影院.js" + }, + { + "key":"hipy_js_皮皮影视", + "name":"皮皮影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":271, + "ext":"https://hipy.trylyingto.me/files/drpy_js/皮皮影视.js" + }, + { + "key":"hipy_js_影视看吧", + "name":"影视看吧(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":272, + "ext":"https://hipy.trylyingto.me/files/drpy_js/影视看吧.js" + }, + { + "key":"hipy_js_看看影视", + "name":"看看影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":273, + "ext":"https://hipy.trylyingto.me/files/drpy_js/看看影视.js" + }, + { + "key":"hipy_js_酷客影视", + "name":"酷客影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":274, + "ext":"https://hipy.trylyingto.me/files/drpy_js/酷客影视.js" + }, + { + "key":"hipy_js_热播之家", + "name":"热播之家(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":275, + "ext":"https://hipy.trylyingto.me/files/drpy_js/热播之家.js" + }, + { + "key":"hipy_js_蓝光影视", + "name":"蓝光影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":276, + "ext":"https://hipy.trylyingto.me/files/drpy_js/蓝光影视.js" + }, + { + "key":"hipy_js_星云影视", + "name":"星云影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":277, + "ext":"https://hipy.trylyingto.me/files/drpy_js/星云影视.js" + }, + { + "key":"hipy_js_西屋影视", + "name":"西屋影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":278, + "ext":"https://hipy.trylyingto.me/files/drpy_js/西屋影视.js" + }, + { + "key":"hipy_js_孜然影视", + "name":"孜然影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":279, + "ext":"https://hipy.trylyingto.me/files/drpy_js/孜然影视.js" + }, + { + "key":"hipy_js_人人影视", + "name":"人人影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":280, + "ext":"https://hipy.trylyingto.me/files/drpy_js/人人影视.js" + }, + { + "key":"hipy_js_快看影视", + "name":"快看影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":281, + "ext":"https://hipy.trylyingto.me/files/drpy_js/快看影视.js" + }, + { + "key":"hipy_js_星空影院", + "name":"星空影院(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":282, + "ext":"https://hipy.trylyingto.me/files/drpy_js/星空影院.js" + }, + { + "key":"hipy_js_HDmoli", + "name":"HDmoli(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":283, + "ext":"https://hipy.trylyingto.me/files/drpy_js/HDmoli.js" + }, + { + "key":"hipy_js_4khdr", + "name":"4khdr(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":0, + "order_num":284, + "ext":"https://hipy.trylyingto.me/files/drpy_js/4khdr.js" + }, + { + "key":"hipy_js_美益达", + "name":"美益达(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":286, + "ext":"https://hipy.trylyingto.me/files/drpy_js/美益达.js" + }, + { + "key":"hipy_js_干饭影视", + "name":"干饭影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":287, + "ext":"https://hipy.trylyingto.me/files/drpy_js/干饭影视.js" + }, + { + "key":"hipy_js_即看影视", + "name":"即看影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":288, + "ext":"https://hipy.trylyingto.me/files/drpy_js/即看影视.js" + }, + { + "key":"hipy_js_夕云影视", + "name":"夕云影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":289, + "ext":"https://hipy.trylyingto.me/files/drpy_js/夕云影视.js" + }, + { + "key":"hipy_js_8号影院", + "name":"8号影院(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":293, + "ext":"https://hipy.trylyingto.me/files/drpy_js/8号影院.js" + }, + { + "key":"hipy_js_茶语资源[资]", + "name":"茶语资源[资](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":0, + "order_num":294, + "ext":"https://hipy.trylyingto.me/files/drpy_js/茶语资源[资].js" + }, + { + "key":"hipy_js_电影先生", + "name":"电影先生(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":295, + "ext":"https://hipy.trylyingto.me/files/drpy_js/电影先生.js" + }, + { + "key":"hipy_js_鸭奈飞影视", + "name":"鸭奈飞影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":296, + "ext":"https://hipy.trylyingto.me/files/drpy_js/鸭奈飞影视.js" + }, + { + "key":"hipy_js_新视觉", + "name":"新视觉(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":300, + "ext":"https://hipy.trylyingto.me/files/drpy_js/新视觉.js" + }, + { + "key":"hipy_js_4k剧院", + "name":"4k剧院(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":301, + "ext":"https://hipy.trylyingto.me/files/drpy_js/4k剧院.js" + }, + { + "key":"hipy_js_魔方影视", + "name":"魔方影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":303, + "ext":"https://hipy.trylyingto.me/files/drpy_js/魔方影视.js" + }, + { + "key":"hipy_js_往往影视[慢]", + "name":"往往影视[慢](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":304, + "ext":"https://hipy.trylyingto.me/files/drpy_js/往往影视[慢].js" + }, + { + "key":"hipy_js_爱看影院", + "name":"爱看影院(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":305, + "ext":"https://hipy.trylyingto.me/files/drpy_js/爱看影院.js" + }, + { + "key":"hipy_js_一号影院[搜]", + "name":"一号影院[搜](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":306, + "ext":"https://hipy.trylyingto.me/files/drpy_js/一号影院[搜].js" + }, + { + "key":"hipy_js_首发网", + "name":"首发网(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":0, + "quickSearch":0, + "filterable":1, + "order_num":307, + "ext":"https://hipy.trylyingto.me/files/drpy_js/首发网.js" + }, + { + "key":"hipy_js_星辰影视", + "name":"星辰影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":308, + "ext":"https://hipy.trylyingto.me/files/drpy_js/星辰影视.js" + }, + { + "key":"hipy_js_大中国", + "name":"大中国(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":309, + "ext":"https://hipy.trylyingto.me/files/drpy_js/大中国.js" + }, + { + "key":"hipy_js_剧圈圈", + "name":"剧圈圈(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":310, + "ext":"https://hipy.trylyingto.me/files/drpy_js/剧圈圈.js" + }, + { + "key":"hipy_js_短剧TV网", + "name":"短剧TV网(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":311, + "ext":"https://hipy.trylyingto.me/files/drpy_js/短剧TV网.js" + }, + { + "key":"hipy_js_黑木耳资源[资]", + "name":"黑木耳资源[资](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":0, + "order_num":312, + "ext":"https://hipy.trylyingto.me/files/drpy_js/黑木耳资源[资].js" + }, + { + "key":"hipy_js_看客影院", + "name":"看客影院(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":313, + "ext":"https://hipy.trylyingto.me/files/drpy_js/看客影院.js" + }, + { + "key":"hipy_js_iFun", + "name":"iFun(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":314, + "ext":"https://hipy.trylyingto.me/files/drpy_js/iFun.js" + }, + { + "key":"hipy_js_如意影视", + "name":"如意影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":315, + "ext":"https://hipy.trylyingto.me/files/drpy_js/如意影视.js" + }, + { + "key":"hipy_js_电影天堂", + "name":"电影天堂(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":316, + "ext":"https://hipy.trylyingto.me/files/drpy_js/电影天堂.js" + }, + { + "key":"hipy_js_短剧天堂", + "name":"短剧天堂(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":317, + "ext":"https://hipy.trylyingto.me/files/drpy_js/短剧天堂.js" + }, + { + "key":"hipy_js_飘花影院", + "name":"飘花影院(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":318, + "ext":"https://hipy.trylyingto.me/files/drpy_js/飘花影院.js" + }, + { + "key":"hipy_js_我播", + "name":"我播(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":319, + "ext":"https://hipy.trylyingto.me/files/drpy_js/我播.js" + }, + { + "key":"hipy_js_想看影院", + "name":"想看影院(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":320, + "ext":"https://hipy.trylyingto.me/files/drpy_js/想看影院.js" + }, + { + "key":"hipy_js_小鱼影视", + "name":"小鱼影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":321, + "ext":"https://hipy.trylyingto.me/files/drpy_js/小鱼影视.js" + }, + { + "key":"hipy_js_爱看农民[优]", + "name":"爱看农民[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":324, + "ext":"https://hipy.trylyingto.me/files/drpy_js/爱看农民[优].js" + }, + { + "key":"hipy_js_爱看农民2[优]", + "name":"爱看农民2[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":325, + "ext":"https://hipy.trylyingto.me/files/drpy_js/爱看农民2[优].js" + }, + { + "key":"hipy_js_爱看hd", + "name":"爱看hd(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":326, + "ext":"https://hipy.trylyingto.me/files/drpy_js/爱看hd.js" + }, + { + "key":"hipy_js_胖虎影视", + "name":"胖虎影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":327, + "ext":"https://hipy.trylyingto.me/files/drpy_js/胖虎影视.js" + }, + { + "key":"hipy_js_小站盘[搜]", + "name":"小站盘[搜](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":329, + "ext":"https://hipy.trylyingto.me/files/drpy_js/小站盘[搜].js" + }, + { + "key":"hipy_js_狗狗盘[搜]", + "name":"狗狗盘[搜](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":330, + "ext":"https://hipy.trylyingto.me/files/drpy_js/狗狗盘[搜].js" + }, + { + "key":"hipy_js_暖光影视", + "name":"暖光影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":333, + "ext":"https://hipy.trylyingto.me/files/drpy_js/暖光影视.js" + }, + { + "key":"hipy_js_被窝电影", + "name":"被窝电影(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":334, + "ext":"https://hipy.trylyingto.me/files/drpy_js/被窝电影.js" + }, + { + "key":"hipy_js_蚂蚁影视", + "name":"蚂蚁影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":335, + "ext":"https://hipy.trylyingto.me/files/drpy_js/蚂蚁影视.js" + }, + { + "key":"hipy_js_影搜[搜]", + "name":"影搜[搜](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":336, + "ext":"https://hipy.trylyingto.me/files/drpy_js/影搜[搜].js" + }, + { + "key":"hipy_js_易搜[搜]", + "name":"易搜[搜](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":338, + "ext":"https://hipy.trylyingto.me/files/drpy_js/易搜[搜].js" + }, + { + "key":"hipy_js_短剧在线", + "name":"短剧在线(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":345, + "ext":"https://hipy.trylyingto.me/files/drpy_js/短剧在线.js" + }, + { + "key":"hipy_js_哈皮影视[优]", + "name":"哈皮影视[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":346, + "ext":"https://hipy.trylyingto.me/files/drpy_js/哈皮影视[优].js" + }, + { + "key":"hipy_js_笔趣阁[书]", + "name":"笔趣阁[书](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":347, + "ext":"https://hipy.trylyingto.me/files/drpy_js/笔趣阁[书].js" + }, + { + "key":"hipy_js_番茄小说[书]", + "name":"番茄小说[书](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":348, + "ext":"https://hipy.trylyingto.me/files/drpy_js/番茄小说[书].js" + }, + { + "key":"hipy_js_7猫小说[书]", + "name":"7猫小说[书](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":350, + "ext":"https://hipy.trylyingto.me/files/drpy_js/7猫小说[书].js" + }, + { + "key":"hipy_js_蜡笔[盘]", + "name":"蜡笔[盘](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":353, + "ext":"https://hipy.trylyingto.me/files/drpy_js/蜡笔[盘].js" + }, + { + "key":"hipy_js_虾酱追剧", + "name":"虾酱追剧(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":354, + "ext":"https://hipy.trylyingto.me/files/drpy_js/虾酱追剧.js" + }, + { + "key":"hipy_js_黑料不打烊-z", + "name":"黑料不打烊-z(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":356, + "ext":"https://hipy.trylyingto.me/files/drpy_js/黑料不打烊-z.js" + }, + { + "key":"hipy_js_六月听书[听]", + "name":"六月听书[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":358, + "ext":"https://hipy.trylyingto.me/files/drpy_js/六月听书[听].js" + }, + { + "key":"hipy_js_i275听书[听]", + "name":"i275听书[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":360, + "ext":"https://hipy.trylyingto.me/files/drpy_js/i275听书[听].js" + }, + { + "key":"hipy_js_播客[听]", + "name":"播客[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":0, + "quickSearch":0, + "filterable":0, + "order_num":361, + "ext":"https://hipy.trylyingto.me/files/drpy_js/播客[听].js" + }, + { + "key":"hipy_js_爱上你听书网[听]", + "name":"爱上你听书网[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":362, + "ext":"https://hipy.trylyingto.me/files/drpy_js/爱上你听书网[听].js" + }, + { + "key":"hipy_js_海洋听书[听]", + "name":"海洋听书[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":363, + "ext":"https://hipy.trylyingto.me/files/drpy_js/海洋听书[听].js" + }, + { + "key":"hipy_js_有声绘本网[听]", + "name":"有声绘本网[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":364, + "ext":"https://hipy.trylyingto.me/files/drpy_js/有声绘本网[听].js" + }, + { + "key":"hipy_js_博看听书[听]", + "name":"博看听书[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":365, + "ext":"https://hipy.trylyingto.me/files/drpy_js/博看听书[听].js" + }, + { + "key":"hipy_js_有声小说吧[听]", + "name":"有声小说吧[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":367, + "ext":"https://hipy.trylyingto.me/files/drpy_js/有声小说吧[听].js" + }, + { + "key":"hipy_js_顶点小说[书]", + "name":"顶点小说[书](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":369, + "ext":"https://hipy.trylyingto.me/files/drpy_js/顶点小说[书].js" + }, + { + "key":"hipy_js_丫丫电子书[书]", + "name":"丫丫电子书[书](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":372, + "ext":"https://hipy.trylyingto.me/files/drpy_js/丫丫电子书[书].js" + }, + { + "key":"hipy_js_飞翔鸟[书]", + "name":"飞翔鸟[书](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":375, + "ext":"https://hipy.trylyingto.me/files/drpy_js/飞翔鸟[书].js" + }, + { + "key":"hipy_js_顶点小说2[书]", + "name":"顶点小说2[书](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":376, + "ext":"https://hipy.trylyingto.me/files/drpy_js/顶点小说2[书].js" + }, + { + "key":"hipy_js_橘子柚[盘]", + "name":"橘子柚[盘](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":379, + "ext":"https://hipy.trylyingto.me/files/drpy_js/橘子柚[盘].js" + }, + { + "key":"hipy_js_热片网", + "name":"热片网(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":382, + "ext":"https://hipy.trylyingto.me/files/drpy_js/热片网.js" + }, + { + "key":"hipy_js_子子影视", + "name":"子子影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":383, + "ext":"https://hipy.trylyingto.me/files/drpy_js/子子影视.js" + }, + { + "key":"hipy_js_天启", + "name":"天启(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":384, + "ext":"https://hipy.trylyingto.me/files/drpy_js/天启.js" + }, + { + "key":"hipy_js_我看书斋[书]", + "name":"我看书斋[书](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":385, + "ext":"https://hipy.trylyingto.me/files/drpy_js/我看书斋[书].js" + }, + { + "key":"hipy_js_一点视频[密]", + "name":"一点视频[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":386, + "ext":"https://hipy.trylyingto.me/files/drpy_js/一点视频[密].js" + }, + { + "key":"hipy_js_九妖仓库[密]", + "name":"九妖仓库[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":388, + "ext":"https://hipy.trylyingto.me/files/drpy_js/九妖仓库[密].js" + }, + { + "key":"hipy_js_千百视频[密]", + "name":"千百视频[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":389, + "ext":"https://hipy.trylyingto.me/files/drpy_js/千百视频[密].js" + }, + { + "key":"hipy_js_老司视频[密]", + "name":"老司视频[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":390, + "ext":"https://hipy.trylyingto.me/files/drpy_js/老司视频[密].js" + }, + { + "key":"hipy_js_剧圈圈[自动]", + "name":"剧圈圈[自动](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":391, + "ext":"https://hipy.trylyingto.me/files/drpy_js/剧圈圈[自动].js" + }, + { + "key":"hipy_js_黑料不打烊[密]", + "name":"黑料不打烊[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":392, + "ext":"https://hipy.trylyingto.me/files/drpy_js/黑料不打烊[密].js" + }, + { + "key":"hipy_js_爱爱影院[密]", + "name":"爱爱影院[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":393, + "ext":"https://hipy.trylyingto.me/files/drpy_js/爱爱影院[密].js" + }, + { + "key":"hipy_js_29片库[密]", + "name":"29片库[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":394, + "ext":"https://hipy.trylyingto.me/files/drpy_js/29片库[密].js" + }, + { + "key":"hipy_js_草莓秒播[密]", + "name":"草莓秒播[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":395, + "ext":"https://hipy.trylyingto.me/files/drpy_js/草莓秒播[密].js" + }, + { + "key":"hipy_js_绿色仓库[密]", + "name":"绿色仓库[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":396, + "ext":"https://hipy.trylyingto.me/files/drpy_js/绿色仓库[密].js" + }, + { + "key":"hipy_js_Pornhub[密]", + "name":"Pornhub[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":397, + "ext":"https://hipy.trylyingto.me/files/drpy_js/Pornhub[密].js" + }, + { + "key":"hipy_js_乐草TV[密]", + "name":"乐草TV[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":398, + "ext":"https://hipy.trylyingto.me/files/drpy_js/乐草TV[密].js" + }, + { + "key":"hipy_js_酷云影视", + "name":"酷云影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":399, + "ext":"https://hipy.trylyingto.me/files/drpy_js/酷云影视.js" + }, + { + "key":"hipy_js_星辰CT", + "name":"星辰CT(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":400, + "ext":"https://hipy.trylyingto.me/files/drpy_js/星辰CT.js" + }, + { + "key":"hipy_js_地瓜视频[密]", + "name":"地瓜视频[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":402, + "ext":"https://hipy.trylyingto.me/files/drpy_js/地瓜视频[密].js" + }, + { + "key":"hipy_js_神仙影视", + "name":"神仙影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":403, + "ext":"https://hipy.trylyingto.me/files/drpy_js/神仙影视.js" + }, + { + "key":"hipy_js_bilfun(自动)", + "name":"bilfun(自动)(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":404, + "ext":"https://hipy.trylyingto.me/files/drpy_js/bilfun(自动).js" + }, + { + "key":"hipy_js_大师兄影视[优]", + "name":"大师兄影视[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":407, + "ext":"https://hipy.trylyingto.me/files/drpy_js/大师兄影视[优].js" + }, + { + "key":"hipy_js_看戏网", + "name":"看戏网(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":408, + "ext":"https://hipy.trylyingto.me/files/drpy_js/看戏网.js" + }, + { + "key":"hipy_js_黑狐影院", + "name":"黑狐影院(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":409, + "ext":"https://hipy.trylyingto.me/files/drpy_js/黑狐影院.js" + }, + { + "key":"hipy_js_剧集TV", + "name":"剧集TV(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":410, + "ext":"https://hipy.trylyingto.me/files/drpy_js/剧集TV.js" + }, + { + "key":"hipy_js_一起看[优]", + "name":"一起看[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":0, + "quickSearch":0, + "filterable":1, + "order_num":411, + "ext":"https://hipy.trylyingto.me/files/drpy_js/一起看[优].js" + }, + { + "key":"hipy_js_速播小屋", + "name":"速播小屋(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":413, + "ext":"https://hipy.trylyingto.me/files/drpy_js/速播小屋.js" + }, + { + "key":"hipy_js_iku喵[资]", + "name":"iku喵[资](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":0, + "order_num":414, + "ext":"https://hipy.trylyingto.me/files/drpy_js/iku喵[资].js" + }, + { + "key":"hipy_js_天天影视", + "name":"天天影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":415, + "ext":"https://hipy.trylyingto.me/files/drpy_js/天天影视.js" + }, + { + "key":"hipy_js_九牛电影", + "name":"九牛电影(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":416, + "ext":"https://hipy.trylyingto.me/files/drpy_js/九牛电影.js" + }, + { + "key":"hipy_js_最新4K", + "name":"最新4K(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":418, + "ext":"https://hipy.trylyingto.me/files/drpy_js/最新4K.js" + }, + { + "key":"hipy_js_JRKAN直播", + "name":"JRKAN直播(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":419, + "ext":"https://hipy.trylyingto.me/files/drpy_js/JRKAN直播.js" + }, + { + "key":"hipy_js_追剧兔", + "name":"追剧兔(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":420, + "ext":"https://hipy.trylyingto.me/files/drpy_js/追剧兔.js" + }, + { + "key":"hipy_js_无插件直播", + "name":"无插件直播(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":421, + "ext":"https://hipy.trylyingto.me/files/drpy_js/无插件直播.js" + }, + { + "key":"hipy_js_猫视界", + "name":"猫视界(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":422, + "ext":"https://hipy.trylyingto.me/files/drpy_js/猫视界.js" + }, + { + "key":"hipy_js_好趣网[播]", + "name":"好趣网[播](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":423, + "ext":"https://hipy.trylyingto.me/files/drpy_js/好趣网[播].js" + }, + { + "key":"hipy_js_漫小肆[画]", + "name":"漫小肆[画](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":424, + "ext":"https://hipy.trylyingto.me/files/drpy_js/漫小肆[画].js" + }, + { + "key":"hipy_js_清风DJ[听]", + "name":"清风DJ[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":425, + "ext":"https://hipy.trylyingto.me/files/drpy_js/清风DJ[听].js" + }, + { + "key":"hipy_js_麻雀视频[优]", + "name":"麻雀视频[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":426, + "ext":"https://hipy.trylyingto.me/files/drpy_js/麻雀视频[优].js" + }, + { + "key":"hipy_js_第一韩漫[画]", + "name":"第一韩漫[画](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":427, + "ext":"https://hipy.trylyingto.me/files/drpy_js/第一韩漫[画].js" + }, + { + "key":"hipy_js_次元城动漫[漫]", + "name":"次元城动漫[漫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":428, + "ext":"https://hipy.trylyingto.me/files/drpy_js/次元城动漫[漫].js" + }, + { + "key":"hipy_js_OmoFun[漫]", + "name":"OmoFun[漫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":429, + "ext":"https://hipy.trylyingto.me/files/drpy_js/OmoFun[漫].js" + }, + { + "key":"hipy_js_R18撸[密]", + "name":"R18撸[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":430, + "ext":"https://hipy.trylyingto.me/files/drpy_js/R18撸[密].js" + }, + { + "key":"hipy_js_动漫巴士[漫]", + "name":"动漫巴士[漫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":431, + "ext":"https://hipy.trylyingto.me/files/drpy_js/动漫巴士[漫].js" + }, + { + "key":"hipy_js_大米动漫[漫]", + "name":"大米动漫[漫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":432, + "ext":"https://hipy.trylyingto.me/files/drpy_js/大米动漫[漫].js" + }, + { + "key":"hipy_js_樱花动漫[漫]", + "name":"樱花动漫[漫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":433, + "ext":"https://hipy.trylyingto.me/files/drpy_js/樱花动漫[漫].js" + }, + { + "key":"hipy_js_维奇动漫[漫]", + "name":"维奇动漫[漫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":434, + "ext":"https://hipy.trylyingto.me/files/drpy_js/维奇动漫[漫].js" + }, + { + "key":"hipy_js_88看球[球]", + "name":"88看球[球](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":0, + "quickSearch":0, + "filterable":0, + "order_num":435, + "ext":"https://hipy.trylyingto.me/files/drpy_js/88看球[球].js" + }, + { + "key":"hipy_js_云盘资源网[盘]", + "name":"云盘资源网[盘](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":436, + "ext":"https://hipy.trylyingto.me/files/drpy_js/云盘资源网[盘].js" + }, + { + "key":"hipy_js_哔哩直播[官]", + "name":"哔哩直播[官](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":437, + "ext":"https://hipy.trylyingto.me/files/drpy_js/哔哩直播[官].js" + }, + { + "key":"hipy_js_虎牙直播[官]", + "name":"虎牙直播[官](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":438, + "ext":"https://hipy.trylyingto.me/files/drpy_js/虎牙直播[官].js" + }, + { + "key":"hipy_js_动漫网[漫]", + "name":"动漫网[漫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":439, + "ext":"https://hipy.trylyingto.me/files/drpy_js/动漫网[漫].js" + }, + { + "key":"hipy_js_斗鱼直播[官]", + "name":"斗鱼直播[官](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":440, + "ext":"https://hipy.trylyingto.me/files/drpy_js/斗鱼直播[官].js" + }, + { + "key":"hipy_js_恒大影视[密]", + "name":"恒大影视[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":441, + "ext":"https://hipy.trylyingto.me/files/drpy_js/恒大影视[密].js" + }, + { + "key":"hipy_js_爱车MV[听]", + "name":"爱车MV[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":442, + "ext":"https://hipy.trylyingto.me/files/drpy_js/爱车MV[听].js" + }, + { + "key":"hipy_js_驴番[漫]", + "name":"驴番[漫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":443, + "ext":"https://hipy.trylyingto.me/files/drpy_js/驴番[漫].js" + }, + { + "key":"hipy_js_58动漫[漫]", + "name":"58动漫[漫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":444, + "ext":"https://hipy.trylyingto.me/files/drpy_js/58动漫[漫].js" + }, + { + "key":"hipy_js_多多追剧[优]", + "name":"多多追剧[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":445, + "ext":"https://hipy.trylyingto.me/files/drpy_js/多多追剧[优].js" + }, + { + "key":"hipy_js_相声随身听[听]", + "name":"相声随身听[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":446, + "ext":"https://hipy.trylyingto.me/files/drpy_js/相声随身听[听].js" + }, + { + "key":"hipy_js_广播迷FM[听]", + "name":"广播迷FM[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":447, + "ext":"https://hipy.trylyingto.me/files/drpy_js/广播迷FM[听].js" + }, + { + "key":"hipy_js_包子漫画[画]", + "name":"包子漫画[画](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":448, + "ext":"https://hipy.trylyingto.me/files/drpy_js/包子漫画[画].js" + }, + { + "key":"hipy_js_古风漫画[画]", + "name":"古风漫画[画](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":449, + "ext":"https://hipy.trylyingto.me/files/drpy_js/古风漫画[画].js" + }, + { + "key":"hipy_js_七色番[漫]", + "name":"七色番[漫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":450, + "ext":"https://hipy.trylyingto.me/files/drpy_js/七色番[漫].js" + }, + { + "key":"hipy_js_MuteFun[漫]", + "name":"MuteFun[漫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":451, + "ext":"https://hipy.trylyingto.me/files/drpy_js/MuteFun[漫].js" + }, + { + "key":"hipy_js_有声听书网[听]", + "name":"有声听书网[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":452, + "ext":"https://hipy.trylyingto.me/files/drpy_js/有声听书网[听].js" + }, + { + "key":"hipy_js_NT动漫[漫]", + "name":"NT动漫[漫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":453, + "ext":"https://hipy.trylyingto.me/files/drpy_js/NT动漫[漫].js" + }, + { + "key":"hipy_js_星辰[优]", + "name":"星辰[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":454, + "ext":"https://hipy.trylyingto.me/files/drpy_js/星辰[优].js" + }, + { + "key":"hipy_js_飞刀资源[资]", + "name":"飞刀资源[资](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":0, + "order_num":455, + "ext":"https://hipy.trylyingto.me/files/drpy_js/飞刀资源[资].js" + }, + { + "key":"hipy_js_七新电影网", + "name":"七新电影网(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":457, + "ext":"https://hipy.trylyingto.me/files/drpy_js/七新电影网.js" + }, + { + "key":"hipy_js_路视频[密]", + "name":"路视频[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":458, + "ext":"https://hipy.trylyingto.me/files/drpy_js/路视频[密].js" + }, + { + "key":"hipy_js_刺猬影视", + "name":"刺猬影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":459, + "ext":"https://hipy.trylyingto.me/files/drpy_js/刺猬影视.js" + }, + { + "key":"hipy_js_文才[资]", + "name":"文才[资](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":460, + "ext":"https://hipy.trylyingto.me/files/drpy_js/文才[资].js" + }, + { + "key":"hipy_js_采王道长[合]", + "name":"采王道长[合](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":463, + "ext":"https://hipy.trylyingto.me/files/drpy_js/采集之王[合].js?type=url¶ms=../json/采集静态.json$1" + }, + { + "key":"hipy_js_采王zy[密]", + "name":"采王zy[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":463, + "ext":"https://hipy.trylyingto.me/files/drpy_js/采集之王[合].js?type=url¶ms=../json/采集[zy]静态.json$1" + }, + { + "key":"hipy_js_采王成人[密]", + "name":"采王成人[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":463, + "ext":"https://hipy.trylyingto.me/files/drpy_js/采集之王[合].js?type=url¶ms=../json/采集[密]静态.json$1$" + }, + { + "key":"hipy_js_剧哥哥", + "name":"剧哥哥(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":467, + "ext":"https://hipy.trylyingto.me/files/drpy_js/剧哥哥.js" + }, + { + "key":"hipy_js_策驰影院(自动)", + "name":"策驰影院(自动)(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":468, + "ext":"https://hipy.trylyingto.me/files/drpy_js/策驰影院(自动).js" + }, + { + "key":"hipy_js_豆角网", + "name":"豆角网(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":469, + "ext":"https://hipy.trylyingto.me/files/drpy_js/豆角网.js" + }, + { + "key":"hipy_js_西瓜影院", + "name":"西瓜影院(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":470, + "ext":"https://hipy.trylyingto.me/files/drpy_js/西瓜影院.js" + }, + { + "key":"hipy_js_script直播[飞]", + "name":"script直播[飞](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":0, + "quickSearch":0, + "filterable":0, + "order_num":473, + "ext":"https://hipy.trylyingto.me/files/drpy_js/script直播[飞].js" + }, + { + "key":"hipy_js_分享短视频", + "name":"分享短视频(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":474, + "ext":"https://hipy.trylyingto.me/files/drpy_js/分享短视频.js" + }, + { + "key":"hipy_js_贝乐虎[儿]", + "name":"贝乐虎[儿](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":0, + "quickSearch":0, + "filterable":0, + "order_num":475, + "ext":"https://hipy.trylyingto.me/files/drpy_js/贝乐虎[儿].js" + }, + { + "key":"hipy_js_KTV歌厅[听]", + "name":"KTV歌厅[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":476, + "ext":"https://hipy.trylyingto.me/files/drpy_js/KTV歌厅[听].js" + }, + { + "key":"hipy_js_网飞.TV", + "name":"网飞.TV(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":477, + "ext":"https://hipy.trylyingto.me/files/drpy_js/网飞.TV.js" + }, + { + "key":"hipy_js_啊哈DJ[听]", + "name":"啊哈DJ[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":478, + "ext":"https://hipy.trylyingto.me/files/drpy_js/啊哈DJ[听].js" + }, + { + "key":"hipy_js_ASMR[听]", + "name":"ASMR[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":479, + "ext":"https://hipy.trylyingto.me/files/drpy_js/ASMR[听].js" + }, + { + "key":"hipy_js_直播转点播[合]", + "name":"直播转点播[合](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":480, + "ext":"https://hipy.trylyingto.me/files/drpy_js/直播转点播[合].js?type=url¶ms=../json/live2cms.json" + }, + { + "key":"hipy_js_飞狗影院[密]", + "name":"飞狗影院[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":485, + "ext":"https://hipy.trylyingto.me/files/drpy_js/飞狗影院[密].js" + }, + { + "key":"hipy_js_奶狗影视[慢]", + "name":"奶狗影视[慢](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":486, + "ext":"https://hipy.trylyingto.me/files/drpy_js/奶狗影视[慢].js" + }, + { + "key":"hipy_js_饭团影视", + "name":"饭团影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":487, + "ext":"https://hipy.trylyingto.me/files/drpy_js/饭团影视.js" + }, + { + "key":"hipy_js_影剧星球", + "name":"影剧星球(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":488, + "ext":"https://hipy.trylyingto.me/files/drpy_js/影剧星球.js" + }, + { + "key":"hipy_js_影视大全", + "name":"影视大全(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":489, + "ext":"https://hipy.trylyingto.me/files/drpy_js/影视大全.js" + }, + { + "key":"hipy_js_影视控", + "name":"影视控(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":490, + "ext":"https://hipy.trylyingto.me/files/drpy_js/影视控.js" + }, + { + "key":"hipy_js_悠悠影视", + "name":"悠悠影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":491, + "ext":"https://hipy.trylyingto.me/files/drpy_js/悠悠影视.js" + }, + { + "key":"hipy_js_爱你短剧", + "name":"爱你短剧(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":492, + "ext":"https://hipy.trylyingto.me/files/drpy_js/爱你短剧.js" + }, + { + "key":"hipy_js_三集电影[自动]", + "name":"三集电影[自动](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":493, + "ext":"https://hipy.trylyingto.me/files/drpy_js/三集电影[自动].js" + }, + { + "key":"hipy_js_来看点播[自动]", + "name":"来看点播[自动](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":494, + "ext":"https://hipy.trylyingto.me/files/drpy_js/来看点播[自动].js" + }, + { + "key":"hipy_js_冠建影视", + "name":"冠建影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":495, + "ext":"https://hipy.trylyingto.me/files/drpy_js/冠建影视.js" + }, + { + "key":"hipy_js_影渣渣影视", + "name":"影渣渣影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":496, + "ext":"https://hipy.trylyingto.me/files/drpy_js/影渣渣影视.js" + }, + { + "key":"hipy_js_旺旺影视", + "name":"旺旺影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":497, + "ext":"https://hipy.trylyingto.me/files/drpy_js/旺旺影视.js" + }, + { + "key":"hipy_js_神马影院[自动]", + "name":"神马影院[自动](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":498, + "ext":"https://hipy.trylyingto.me/files/drpy_js/神马影院[自动].js" + }, + { + "key":"hipy_js_网飞啦[自动]", + "name":"网飞啦[自动](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":499, + "ext":"https://hipy.trylyingto.me/files/drpy_js/网飞啦[自动].js" + }, + { + "key":"hipy_js_嘀哩嘀哩", + "name":"嘀哩嘀哩(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":500, + "ext":"https://hipy.trylyingto.me/files/drpy_js/嘀哩嘀哩.js" + }, + { + "key":"hipy_js_刷剧网", + "name":"刷剧网(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":501, + "ext":"https://hipy.trylyingto.me/files/drpy_js/刷剧网.js" + }, + { + "key":"hipy_js_auete", + "name":"auete(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":502, + "ext":"https://hipy.trylyingto.me/files/drpy_js/auete.js" + }, + { + "key":"hipy_js_剧迷", + "name":"剧迷(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":503, + "ext":"https://hipy.trylyingto.me/files/drpy_js/剧迷.js" + }, + { + "key":"hipy_js_映播TV", + "name":"映播TV(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":0, + "order_num":504, + "ext":"https://hipy.trylyingto.me/files/drpy_js/映播TV.js" + }, + { + "key":"hipy_js_毒蛇电影[优]", + "name":"毒蛇电影[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":505, + "ext":"https://hipy.trylyingto.me/files/drpy_js/毒蛇电影[优].js" + }, + { + "key":"hipy_js_小女18[密]", + "name":"小女18[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":506, + "ext":"https://hipy.trylyingto.me/files/drpy_js/小女18[密].js" + }, + { + "key":"hipy_js_易点看影院[自动]", + "name":"易点看影院[自动](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":507, + "ext":"https://hipy.trylyingto.me/files/drpy_js/易点看影院[自动].js" + }, + { + "key":"hipy_js_你好帅影院", + "name":"你好帅影院(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":508, + "ext":"https://hipy.trylyingto.me/files/drpy_js/你好帅影院.js" + }, + { + "key":"hipy_js_大千视界", + "name":"大千视界(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":511, + "ext":"https://hipy.trylyingto.me/files/drpy_js/大千视界.js" + }, + { + "key":"hipy_js_游子视频", + "name":"游子视频(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":512, + "ext":"https://hipy.trylyingto.me/files/drpy_js/游子视频.js" + }, + { + "key":"hipy_js_看客视频", + "name":"看客视频(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":514, + "ext":"https://hipy.trylyingto.me/files/drpy_js/看客视频.js" + }, + { + "key":"hipy_js_奈飞中文[自动]", + "name":"奈飞中文[自动](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":515, + "ext":"https://hipy.trylyingto.me/files/drpy_js/奈飞中文[自动].js" + }, + { + "key":"hipy_js_饺子影院", + "name":"饺子影院(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":516, + "ext":"https://hipy.trylyingto.me/files/drpy_js/饺子影院.js" + }, + { + "key":"hipy_js_无忧影视", + "name":"无忧影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":517, + "ext":"https://hipy.trylyingto.me/files/drpy_js/无忧影视.js" + }, + { + "key":"hipy_js_咖啡影视", + "name":"咖啡影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":518, + "ext":"https://hipy.trylyingto.me/files/drpy_js/咖啡影视.js" + }, + { + "key":"hipy_js_笔趣阁13[书]", + "name":"笔趣阁13[书](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":0, + "quickSearch":0, + "filterable":0, + "order_num":519, + "ext":"https://hipy.trylyingto.me/files/drpy_js/笔趣阁13[书].js" + }, + { + "key":"hipy_js_360吧[球]", + "name":"360吧[球](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":520, + "ext":"https://hipy.trylyingto.me/files/drpy_js/360吧[球].js" + }, + { + "key":"hipy_js_看了么", + "name":"看了么(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":521, + "ext":"https://hipy.trylyingto.me/files/drpy_js/看了么.js" + }, + { + "key":"hipy_js_童趣[儿]", + "name":"童趣[儿](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":522, + "ext":"https://hipy.trylyingto.me/files/drpy_js/童趣[儿].js" + }, + { + "key":"hipy_js_36直播[密]", + "name":"36直播[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":0, + "quickSearch":0, + "filterable":0, + "order_num":523, + "ext":"https://hipy.trylyingto.me/files/drpy_js/36直播[密].js" + }, + { + "key":"hipy_js_路漫漫[漫]", + "name":"路漫漫[漫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":524, + "ext":"https://hipy.trylyingto.me/files/drpy_js/路漫漫[漫].js" + }, + { + "key":"hipy_js_老白故事[听]", + "name":"老白故事[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":525, + "ext":"https://hipy.trylyingto.me/files/drpy_js/老白故事[听].js" + }, + { + "key":"hipy_js_世纪DJ音乐网[听]", + "name":"世纪DJ音乐网[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":527, + "ext":"https://hipy.trylyingto.me/files/drpy_js/世纪DJ音乐网[听].js" + }, + { + "key":"hipy_js_闪雷电", + "name":"闪雷电(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":528, + "ext":"https://hipy.trylyingto.me/files/drpy_js/闪雷电.js" + }, + { + "key":"hipy_js_飞鱼影视", + "name":"飞鱼影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":529, + "ext":"https://hipy.trylyingto.me/files/drpy_js/飞鱼影视.js" + }, + { + "key":"hipy_js_天龙影院", + "name":"天龙影院(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":530, + "ext":"https://hipy.trylyingto.me/files/drpy_js/天龙影院.js" + }, + { + "key":"hipy_js_桃子影视[优]", + "name":"桃子影视[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":534, + "ext":"https://hipy.trylyingto.me/files/drpy_js/桃子影视[优].js" + }, + { + "key":"hipy_js_爱看短剧[盘]", + "name":"爱看短剧[盘](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":535, + "ext":"https://hipy.trylyingto.me/files/drpy_js/爱看短剧[盘].js" + }, + { + "key":"hipy_js_爱优影视[自动]", + "name":"爱优影视[自动](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":536, + "ext":"https://hipy.trylyingto.me/files/drpy_js/爱优影视[自动].js" + }, + { + "key":"hipy_js_兄弟影视[优]", + "name":"兄弟影视[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":537, + "ext":"https://hipy.trylyingto.me/files/drpy_js/兄弟影视[优].js" + }, + { + "key":"hipy_js_影视工厂", + "name":"影视工厂(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":538, + "ext":"https://hipy.trylyingto.me/files/drpy_js/影视工厂.js" + }, + { + "key":"hipy_js_至臻[盘]", + "name":"至臻[盘](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":539, + "ext":"https://hipy.trylyingto.me/files/drpy_js/至臻[盘].js" + }, + { + "key":"hipy_js_B站影视", + "name":"B站影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":540, + "ext":"https://hipy.trylyingto.me/files/drpy_js/B站影视.js" + }, + { + "key":"hipy_js_两个BT", + "name":"两个BT(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":541, + "ext":"https://hipy.trylyingto.me/files/drpy_js/两个BT.js" + }, + { + "key":"hipy_js_宇航影视", + "name":"宇航影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":0, + "quickSearch":0, + "filterable":0, + "order_num":542, + "ext":"https://hipy.trylyingto.me/files/drpy_js/宇航影视.js" + }, + { + "key":"hipy_js_小白菜电影", + "name":"小白菜电影(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":543, + "ext":"https://hipy.trylyingto.me/files/drpy_js/小白菜电影.js" + }, + { + "key":"hipy_js_影视工场", + "name":"影视工场(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":544, + "ext":"https://hipy.trylyingto.me/files/drpy_js/影视工场.js" + }, + { + "key":"hipy_js_低端", + "name":"低端(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":545, + "ext":"https://hipy.trylyingto.me/files/drpy_js/低端.js" + }, + { + "key":"hipy_js_爱迪影视", + "name":"爱迪影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":547, + "ext":"https://hipy.trylyingto.me/files/drpy_js/爱迪影视.js" + }, + { + "key":"hipy_js_我爱跟剧", + "name":"我爱跟剧(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":548, + "ext":"https://hipy.trylyingto.me/files/drpy_js/我爱跟剧.js" + }, + { + "key":"hipy_js_北川影视", + "name":"北川影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":549, + "ext":"https://hipy.trylyingto.me/files/drpy_js/北川影视.js" + }, + { + "key":"hipy_js_朴樱影视", + "name":"朴樱影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":550, + "ext":"https://hipy.trylyingto.me/files/drpy_js/朴樱影视.js" + }, + { + "key":"hipy_js_哔嘀影视[优]", + "name":"哔嘀影视[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":551, + "ext":"https://hipy.trylyingto.me/files/drpy_js/哔嘀影视[优].js" + }, + { + "key":"hipy_js_539影视", + "name":"539影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":552, + "ext":"https://hipy.trylyingto.me/files/drpy_js/539影视.js" + }, + { + "key":"hipy_js_348电影网", + "name":"348电影网(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":553, + "ext":"https://hipy.trylyingto.me/files/drpy_js/348电影网.js" + }, + { + "key":"hipy_js_达达龟", + "name":"达达龟(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":554, + "ext":"https://hipy.trylyingto.me/files/drpy_js/达达龟.js" + }, + { + "key":"hipy_js_米爱影视", + "name":"米爱影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":555, + "ext":"https://hipy.trylyingto.me/files/drpy_js/米爱影视.js" + }, + { + "key":"hipy_js_FreeOKLOL", + "name":"FreeOKLOL(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":557, + "ext":"https://hipy.trylyingto.me/files/drpy_js/FreeOKLOL.js" + }, + { + "key":"hipy_js_火狐影视", + "name":"火狐影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":559, + "ext":"https://hipy.trylyingto.me/files/drpy_js/火狐影视.js" + }, + { + "key":"hipy_js_4K-AV", + "name":"4K-AV(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":560, + "ext":"https://hipy.trylyingto.me/files/drpy_js/4K-AV.js" + }, + { + "key":"hipy_js_星芽短剧[优]", + "name":"星芽短剧[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":561, + "ext":"https://hipy.trylyingto.me/files/drpy_js/星芽短剧[优].js" + }, + { + "key":"hipy_js_PTT追剧大师", + "name":"PTT追剧大师(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":562, + "ext":"https://hipy.trylyingto.me/files/drpy_js/PTT追剧大师.js" + }, + { + "key":"hipy_js_PTT[优]", + "name":"PTT[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":563, + "ext":"https://hipy.trylyingto.me/files/drpy_js/PTT[优].js" + }, + { + "key":"hipy_js_酷我听书[听]", + "name":"酷我听书[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":564, + "ext":"https://hipy.trylyingto.me/files/drpy_js/酷我听书[听].js" + }, + { + "key":"hipy_js_喜马拉雅[听]", + "name":"喜马拉雅[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":565, + "ext":"https://hipy.trylyingto.me/files/drpy_js/喜马拉雅[听].js" + }, + { + "key":"hipy_py_emby", + "name":"emby(hipy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/hipy/emby.py", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":567, + "ext":"" + }, + { + "key":"hipy_js_一曲肝肠断", + "name":"一曲肝肠断(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":0, + "quickSearch":0, + "filterable":0, + "order_num":569, + "ext":"https://hipy.trylyingto.me/files/drpy_js/一曲肝肠断.js" + }, + { + "key":"hipy_js_cally66", + "name":"cally66(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":570, + "ext":"https://hipy.trylyingto.me/files/drpy_js/cally66.js" + }, + { + "key":"hipy_js_一支穿云箭", + "name":"一支穿云箭(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":571, + "ext":"https://hipy.trylyingto.me/files/drpy_js/一支穿云箭.js" + }, + { + "key":"hipy_js_中华听书网[听]", + "name":"中华听书网[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":572, + "ext":"https://hipy.trylyingto.me/files/drpy_js/中华听书网[听].js" + }, + { + "key":"hipy_js_番号资源[密]", + "name":"番号资源[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":573, + "ext":"https://hipy.trylyingto.me/files/drpy_js/番号资源[密].js" + }, + { + "key":"hipy_js_310直播[球]", + "name":"310直播[球](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":0, + "quickSearch":0, + "filterable":0, + "order_num":574, + "ext":"https://hipy.trylyingto.me/files/drpy_js/310直播[球].js" + }, + { + "key":"hipy_js_色库[密]", + "name":"色库[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":575, + "ext":"https://hipy.trylyingto.me/files/drpy_js/色库[密].js" + }, + { + "key":"hipy_js_01看球[球]", + "name":"01看球[球](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":578, + "ext":"https://hipy.trylyingto.me/files/drpy_js/01看球[球].js" + }, + { + "key":"hipy_js_速讯影院", + "name":"速讯影院(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":579, + "ext":"https://hipy.trylyingto.me/files/drpy_js/速讯影院.js" + }, + { + "key":"hipy_js_咕咕番[漫]", + "name":"咕咕番[漫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":580, + "ext":"https://hipy.trylyingto.me/files/drpy_js/咕咕番[漫].js" + }, + { + "key":"hipy_js_臭蛋蛋", + "name":"臭蛋蛋(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":581, + "ext":"https://hipy.trylyingto.me/files/drpy_js/臭蛋蛋.js" + }, + { + "key":"hipy_js_金牌影院", + "name":"金牌影院(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":582, + "ext":"https://hipy.trylyingto.me/files/drpy_js/金牌影院.js" + }, + { + "key":"hipy_js_种子音乐[听]", + "name":"种子音乐[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":583, + "ext":"https://hipy.trylyingto.me/files/drpy_js/种子音乐[听].js" + }, + { + "key":"hipy_js_萌番[漫]", + "name":"萌番[漫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":584, + "ext":"https://hipy.trylyingto.me/files/drpy_js/萌番[漫].js" + }, + { + "key":"hipy_js_那兔视频", + "name":"那兔视频(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":585, + "ext":"https://hipy.trylyingto.me/files/drpy_js/那兔视频.js" + }, + { + "key":"hipy_js_旋风视频", + "name":"旋风视频(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":586, + "ext":"https://hipy.trylyingto.me/files/drpy_js/旋风视频.js" + }, + { + "key":"hipy_js_文才2[资]", + "name":"文才2[资](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":0, + "order_num":587, + "ext":"https://hipy.trylyingto.me/files/drpy_js/文才2[资].js" + }, + { + "key":"hipy_js_电影狗", + "name":"电影狗(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":589, + "ext":"https://hipy.trylyingto.me/files/drpy_js/电影狗.js" + }, + { + "key":"hipy_js_家庭影视", + "name":"家庭影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":590, + "ext":"https://hipy.trylyingto.me/files/drpy_js/家庭影视.js" + }, + { + "key":"hipy_js_小宝影院", + "name":"小宝影院(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":591, + "ext":"https://hipy.trylyingto.me/files/drpy_js/小宝影院.js" + }, + { + "key":"hipy_js_看呀看", + "name":"看呀看(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":592, + "ext":"https://hipy.trylyingto.me/files/drpy_js/看呀看.js" + }, + { + "key":"hipy_js_爱弹幕[漫]", + "name":"爱弹幕[漫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":593, + "ext":"https://hipy.trylyingto.me/files/drpy_js/爱弹幕[漫].js" + }, + { + "key":"hipy_js_怡萱动漫[漫]", + "name":"怡萱动漫[漫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":594, + "ext":"https://hipy.trylyingto.me/files/drpy_js/怡萱动漫[漫].js" + }, + { + "key":"hipy_js_银河影视[V2]", + "name":"银河影视[V2](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":595, + "ext":"https://hipy.trylyingto.me/files/drpy_js/银河影视[V2].js" + }, + { + "key":"hipy_js_小马影视[V2]", + "name":"小马影视[V2](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":596, + "ext":"https://hipy.trylyingto.me/files/drpy_js/小马影视[V2].js" + }, + { + "key":"hipy_js_0855影视", + "name":"0855影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":597, + "ext":"https://hipy.trylyingto.me/files/drpy_js/0855影视.js" + }, + { + "key":"hipy_js_河狸影视[V2]", + "name":"河狸影视[V2](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":598, + "ext":"https://hipy.trylyingto.me/files/drpy_js/河狸影视[V2].js" + }, + { + "key":"hipy_js_柠檬影视[V2]", + "name":"柠檬影视[V2](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":599, + "ext":"https://hipy.trylyingto.me/files/drpy_js/柠檬影视[V2].js" + }, + { + "key":"hipy_js_奇米动漫[漫]", + "name":"奇米动漫[漫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":600, + "ext":"https://hipy.trylyingto.me/files/drpy_js/奇米动漫[漫].js" + }, + { + "key":"hipy_js_鸭飞影视[V2]", + "name":"鸭飞影视[V2](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":601, + "ext":"https://hipy.trylyingto.me/files/drpy_js/鸭飞影视[V2].js" + }, + { + "key":"hipy_js_畅梦影视[优]", + "name":"畅梦影视[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":602, + "ext":"https://hipy.trylyingto.me/files/drpy_js/畅梦影视[优].js" + }, + { + "key":"hipy_js_剧巴巴", + "name":"剧巴巴(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":603, + "ext":"https://hipy.trylyingto.me/files/drpy_js/剧巴巴.js" + }, + { + "key":"hipy_js_蜥蜴影视[优]", + "name":"蜥蜴影视[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":604, + "ext":"https://hipy.trylyingto.me/files/drpy_js/蜥蜴影视[优].js" + }, + { + "key":"hipy_js_达达猪", + "name":"达达猪(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":605, + "ext":"https://hipy.trylyingto.me/files/drpy_js/达达猪.js" + }, + { + "key":"hipy_js_UAA[密]", + "name":"UAA[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":606, + "ext":"https://hipy.trylyingto.me/files/drpy_js/UAA[密].js" + }, + { + "key":"hipy_js_牌牌影院", + "name":"牌牌影院(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":607, + "ext":"https://hipy.trylyingto.me/files/drpy_js/牌牌影院.js" + }, + { + "key":"hipy_js_UAA[听]", + "name":"UAA[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":608, + "ext":"https://hipy.trylyingto.me/files/drpy_js/UAA[听].js" + }, + { + "key":"hipy_js_云电影网", + "name":"云电影网(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":609, + "ext":"https://hipy.trylyingto.me/files/drpy_js/云电影网.js" + }, + { + "key":"hipy_js_GO影视", + "name":"GO影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":610, + "ext":"https://hipy.trylyingto.me/files/drpy_js/GO影视.js" + }, + { + "key":"hipy_js_漫画走廊[画密飞]", + "name":"漫画走廊[画密飞](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":611, + "ext":"https://hipy.trylyingto.me/files/drpy_js/漫画走廊[画密飞].js" + }, + { + "key":"hipy_js_eFuns", + "name":"eFuns(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":612, + "ext":"https://hipy.trylyingto.me/files/drpy_js/eFuns.js" + }, + { + "key":"hipy_js_追影TV", + "name":"追影TV(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":0, + "quickSearch":0, + "filterable":0, + "order_num":613, + "ext":"https://hipy.trylyingto.me/files/drpy_js/追影TV.js" + }, + { + "key":"hipy_js_金金虫", + "name":"金金虫(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":614, + "ext":"https://hipy.trylyingto.me/files/drpy_js/金金虫.js" + }, + { + "key":"hipy_js_极点影视", + "name":"极点影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":615, + "ext":"https://hipy.trylyingto.me/files/drpy_js/极点影视.js" + }, + { + "key":"hipy_js_泡泡影院", + "name":"泡泡影院(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":616, + "ext":"https://hipy.trylyingto.me/files/drpy_js/泡泡影院.js" + }, + { + "key":"hipy_js_青龙", + "name":"青龙(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":619, + "ext":"https://hipy.trylyingto.me/files/drpy_js/青龙.js" + }, + { + "key":"hipy_js_88tvs", + "name":"88tvs(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":620, + "ext":"https://hipy.trylyingto.me/files/drpy_js/88tvs.js" + }, + { + "key":"hipy_js_🎒📌央视大全[🎒] ", + "name":"🎒📌央视大全[🎒] (drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":0, + "quickSearch":0, + "filterable":1, + "order_num":621, + "ext":"https://hipy.trylyingto.me/files/drpy_js/🎒📌央视大全[🎒] .js" + }, + { + "key":"hipy_js_片多多[优]", + "name":"片多多[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":622, + "ext":"https://hipy.trylyingto.me/files/drpy_js/片多多[优].js" + }, + { + "key":"hipy_js_926tv[球]", + "name":"926tv[球](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":623, + "ext":"https://hipy.trylyingto.me/files/drpy_js/926tv[球].js" + }, + { + "key":"hipy_js_盘搜", + "name":"盘搜(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":624, + "ext":"https://hipy.trylyingto.me/files/drpy_js/盘搜.js" + }, + { + "key":"hipy_js_大全[央]", + "name":"大全[央](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":625, + "ext":"https://hipy.trylyingto.me/files/drpy_js/大全[央].js" + }, + { + "key":"hipy_js_老王电影[自动]", + "name":"老王电影[自动](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":626, + "ext":"https://hipy.trylyingto.me/files/drpy_js/老王电影[自动].js" + }, + { + "key":"hipy_js_KimiVod", + "name":"KimiVod(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":627, + "ext":"https://hipy.trylyingto.me/files/drpy_js/KimiVod.js" + }, + { + "key":"hipy_js_素白白", + "name":"素白白(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":628, + "ext":"https://hipy.trylyingto.me/files/drpy_js/素白白.js" + }, + { + "key":"hipy_js_焱淼4kapp[优]", + "name":"焱淼4kapp[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":629, + "ext":"https://hipy.trylyingto.me/files/drpy_js/焱淼4kapp[优].js" + }, + { + "key":"hipy_js_木瓜影视", + "name":"木瓜影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":630, + "ext":"https://hipy.trylyingto.me/files/drpy_js/木瓜影视.js" + }, + { + "key":"hipy_js_熊猫TV", + "name":"熊猫TV(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":631, + "ext":"https://hipy.trylyingto.me/files/drpy_js/熊猫TV.js" + }, + { + "key":"hipy_js_全能影视", + "name":"全能影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":632, + "ext":"https://hipy.trylyingto.me/files/drpy_js/全能影视.js" + }, + { + "key":"hipy_js_忍者影视", + "name":"忍者影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":633, + "ext":"https://hipy.trylyingto.me/files/drpy_js/忍者影视.js" + }, + { + "key":"hipy_js_小猫电影院", + "name":"小猫电影院(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":634, + "ext":"https://hipy.trylyingto.me/files/drpy_js/小猫电影院.js" + }, + { + "key":"hipy_js_RJAV[密]", + "name":"RJAV[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":635, + "ext":"https://hipy.trylyingto.me/files/drpy_js/RJAV[密].js" + }, + { + "key":"hipy_js_HBOTV[优]", + "name":"HBOTV[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":636, + "ext":"https://hipy.trylyingto.me/files/drpy_js/HBOTV[优].js" + }, + { + "key":"hipy_js_电影兔", + "name":"电影兔(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":637, + "ext":"https://hipy.trylyingto.me/files/drpy_js/电影兔.js" + }, + { + "key":"hipy_js_libvio", + "name":"libvio(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":638, + "ext":"https://hipy.trylyingto.me/files/drpy_js/libvio.js" + }, + { + "key":"hipy_js_哔嘀影视", + "name":"哔嘀影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":639, + "ext":"https://hipy.trylyingto.me/files/drpy_js/哔嘀影视.js" + }, + { + "key":"hipy_js_歪片星球[资]", + "name":"歪片星球[资](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":640, + "ext":"https://hipy.trylyingto.me/files/drpy_js/歪片星球[资].js" + }, + { + "key":"hipy_js_央视少儿[漫]", + "name":"央视少儿[漫](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":0, + "quickSearch":0, + "filterable":1, + "order_num":641, + "ext":"https://hipy.trylyingto.me/files/drpy_js/央视少儿[漫].js" + }, + { + "key":"hipy_js_央视大全[优]", + "name":"央视大全[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":0, + "quickSearch":0, + "filterable":1, + "order_num":642, + "ext":"https://hipy.trylyingto.me/files/drpy_js/央视大全[优].js" + }, + { + "key":"hipy_js_墨点影视", + "name":"墨点影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":643, + "ext":"https://hipy.trylyingto.me/files/drpy_js/墨点影视.js" + }, + { + "key":"hipy_js_茶杯狐", + "name":"茶杯狐(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":644, + "ext":"https://hipy.trylyingto.me/files/drpy_js/茶杯狐.js" + }, + { + "key":"hipy_js_85k影视", + "name":"85k影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":645, + "ext":"https://hipy.trylyingto.me/files/drpy_js/85k影视.js" + }, + { + "key":"hipy_js_茄子影视[自动]", + "name":"茄子影视[自动](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":646, + "ext":"https://hipy.trylyingto.me/files/drpy_js/茄子影视[自动].js" + }, + { + "key":"hipy_js_影梦影视", + "name":"影梦影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":647, + "ext":"https://hipy.trylyingto.me/files/drpy_js/影梦影视.js" + }, + { + "key":"hipy_js_南瓜影视", + "name":"南瓜影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":648, + "ext":"https://hipy.trylyingto.me/files/drpy_js/南瓜影视.js" + }, + { + "key":"hipy_js_🤡星芽短剧[🤡]", + "name":"🤡星芽短剧[🤡](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":649, + "ext":"https://hipy.trylyingto.me/files/drpy_js/🤡星芽短剧[🤡].js" + }, + { + "key":"hipy_js_绿茶", + "name":"绿茶(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":650, + "ext":"https://hipy.trylyingto.me/files/drpy_js/绿茶.js" + }, + { + "key":"hipy_js_rarbt(全)[优]", + "name":"rarbt(全)[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":651, + "ext":"https://hipy.trylyingto.me/files/drpy_js/rarbt(全)[优].js" + }, + { + "key":"hipy_js_妖狐影视[自动]", + "name":"妖狐影视[自动](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":652, + "ext":"https://hipy.trylyingto.me/files/drpy_js/妖狐影视[自动].js" + }, + { + "key":"hipy_js_cally66影视", + "name":"cally66影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":653, + "ext":"https://hipy.trylyingto.me/files/drpy_js/cally66影视.js" + }, + { + "key":"hipy_js_rarbt[优]", + "name":"rarbt[优](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":654, + "ext":"https://hipy.trylyingto.me/files/drpy_js/rarbt[优].js" + }, + { + "key":"hipy_js_泥巴影院", + "name":"泥巴影院(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":655, + "ext":"https://hipy.trylyingto.me/files/drpy_js/泥巴影院.js" + }, + { + "key":"hipy_js_电影猎手[自动]", + "name":"电影猎手[自动](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":656, + "ext":"https://hipy.trylyingto.me/files/drpy_js/电影猎手[自动].js" + }, + { + "key":"hipy_js_多多影音", + "name":"多多影音(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":657, + "ext":"https://hipy.trylyingto.me/files/drpy_js/多多影音.js" + }, + { + "key":"hipy_js_木偶哥哥[盘]", + "name":"木偶哥哥[盘](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":658, + "ext":"https://hipy.trylyingto.me/files/drpy_js/木偶哥哥[盘].js" + }, + { + "key":"hipy_js_嗷呜动漫", + "name":"嗷呜动漫(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":659, + "ext":"https://hipy.trylyingto.me/files/drpy_js/嗷呜动漫.js" + }, + { + "key":"hipy_js_美剧窝", + "name":"美剧窝(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":660, + "ext":"https://hipy.trylyingto.me/files/drpy_js/美剧窝.js" + }, + { + "key":"hipy_js_Auete影视", + "name":"Auete影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":661, + "ext":"https://hipy.trylyingto.me/files/drpy_js/Auete影视.js" + }, + { + "key":"hipy_js_[密]RjAv", + "name":"[密]RjAv(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":662, + "ext":"https://hipy.trylyingto.me/files/drpy_js/[密]RjAv.js" + }, + { + "key":"hipy_js_努努影院1", + "name":"努努影院1(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":663, + "ext":"https://hipy.trylyingto.me/files/drpy_js/努努影院1.js" + }, + { + "key":"hipy_js_111tv[自动]", + "name":"111tv[自动](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":664, + "ext":"https://hipy.trylyingto.me/files/drpy_js/111tv[自动].js" + }, + { + "key":"hipy_js_泥视频[资]", + "name":"泥视频[资](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":665, + "ext":"https://hipy.trylyingto.me/files/drpy_js/泥视频[资].js" + }, + { + "key":"hipy_js_222听书[听]", + "name":"222听书[听](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":666, + "ext":"https://hipy.trylyingto.me/files/drpy_js/222听书[听].js" + }, + { + "key":"hipy_js_一个g影视", + "name":"一个g影视(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":667, + "ext":"https://hipy.trylyingto.me/files/drpy_js/一个g影视.js" + }, + { + "key":"hipy_js_泽少1", + "name":"泽少1(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":668, + "ext":"https://hipy.trylyingto.me/files/drpy_js/APPV2[模板].js?type=url¶ms=http://122.228.85.203:1000" + }, + { + "key":"hipy_js_泽少2", + "name":"泽少2(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "order_num":668, + "ext":"https://hipy.trylyingto.me/files/drpy_js/APPV2[模板].js?type=url¶ms=http://122.228.85.203:1000" + }, + { + "key":"hipy_js_夸克分享[盘]", + "name":"夸克分享[盘](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":669, + "ext":"https://hipy.trylyingto.me/files/drpy_js/夸克分享[合].js?type=url¶ms=../json/夸克分享.json" + }, + { + "key":"hipy_js_夸克分享2[合]", + "name":"夸克分享2[合](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":672, + "ext":"https://hipy.trylyingto.me/files/drpy_js/夸克分享2[合].js" + }, + { + "key":"hipy_js_xvideos涩涩[密]", + "name":"xvideos涩涩[密](drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":0, + "order_num":673, + "ext":"https://hipy.trylyingto.me/files/drpy_js/xvideos涩涩[密].js" + }, + { + "key":"hipy_js_农民影视gz", + "name":"农民影视gz(drpy_t3)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":1, + "quickSearch":0, + "filterable":1, + "order_num":675, + "ext":"https://hipy.trylyingto.me/files/drpy_js/农民影视gz.js" + }, + { + "key":"Test_jsapi", + "name":"Test_jsapi(drpy)", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/drpy2.min.js", + "searchable":2, + "quickSearch":0, + "filterable":0, + "ext":"https://hipy.trylyingto.me/files/txt/js/jsapi.js", + "jar":"https://hipy.trylyingto.me/files/jar/custom_jsapi.jar", + "order_num":9999 + }, + { + "key":"js_origin", + "name":"JS(原始)", + "type":3, + "api":"https://hipy.trylyingto.me/files/txt/js/原始JS.js", + "searchable":1, + "quickSearch":1, + "filterable":1, + "ext":"", + "order_num":9999 + }, + { + "key":"CMS_非凡资源", + "name":"🥗┃非凡┃资源", + "type":1, + "api":"http://cj.ffzyapi.com/api.php/provide/vod/", + "playurl":"json:http://jx.84jia.com/m3u8ts.php?url=", + "searchable":1, + "quickSearch":1, + "filterable":1, + "categories":[ + "动作片", + "喜剧片", + "科幻片", + "恐怖片", + "爱情片", + "剧情片", + "战争片", + "记录片", + "国产剧", + "欧美剧", + "香港剧", + "韩国剧", + "台湾剧", + "日本剧", + "海外剧", + "泰国剧", + "国产动漫", + "日韩动漫", + "欧美动漫", + "港台动漫", + "海外动漫", + "大陆综艺", + "港台综艺", + "日韩综艺", + "欧美综艺" + ], + "order_num":9999 + }, + { + "key":"CMS_量子资源", + "name":"🥑┃量子┃资源", + "type":1, + "api":"http://cj.lziapi.com/api.php/provide/vod/", + "playurl":"json:http://jx.84jia.com/m3u8ts.php?url=", + "searchable":1, + "quickSearch":1, + "filterable":0, + "categories":[ + "国产剧", + "国产动漫", + "泰国剧", + "台湾剧", + "香港剧", + "欧美剧", + "韩国剧", + "日本剧", + "动漫", + "体育", + "剧情片", + "动作片", + "爱情片", + "喜剧片" + ], + "order_num":9999 + }, + { + "key":"CMS_索尼资源", + "name":"🥑┃索尼┃资源", + "type":1, + "api":"https://suoniapi.com/api.php/provide/vod/from/snm3u8/", + "categories":[ + "动作片", + "喜剧片", + "爱情片", + "科幻片", + "恐怖片", + "剧情片", + "战争片", + "国产剧", + "欧美剧", + "韩剧", + "日剧", + "港剧", + "台剧", + "泰剧", + "海外剧", + "纪录片", + "大陆综艺", + "日韩综艺", + "港台综艺", + "欧美综艺", + "国产动漫", + "日韩动漫", + "欧美动漫", + "动画片", + "港台动漫", + "海外动漫", + "演唱会", + "体育赛事", + "篮球", + "足球", + "预告片", + "斯诺克", + "影视解说" + ], + "searchable":1, + "quickSearch":1, + "order_num":9999 + }, + { + "key":"CMS_爱酷秒", + "name":"🥑┃酷秒┃资源", + "type":0, + "api":"http://caiji.ikum.cc:8099/api.php/provide/vod/at/xml", + "categories":[ + "电影", + "连续剧", + "综艺", + "动漫", + "国产剧", + "港台剧", + "日韩剧", + "欧美剧", + "其他剧", + "儿童", + "蓝光", + "哔哩哔哩" + ], + "searchable":1, + "quickSearch":1, + "order_num":9999 + }, + { + "key":"Live2Cms", + "name":"直播转点播V2", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/live2cms.js", + "searchable":2, + "quickSearch":0, + "filterable":0, + "ext":"https://hipy.trylyingto.me/files/json/live2mv_data.json", + "order_num":9999 + }, + { + "key":"Alist", + "name":"Alist", + "type":3, + "api":"https://hipy.trylyingto.me/files/drpy_libs/alist.min.js", + "searchable":2, + "quickSearch":0, + "filterable":0, + "ext":"https://hipy.trylyingto.me/files/json/alist.json;200;video", + "order_num":9999 + }, + { + "key":"网盘配置", + "name":"网盘及彈幕配置", + "type":3, + "api":"csp_Config", + "searchable":0, + "changeable":0, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json", + "order_num":9999 + }, + { + "key":"Local", + "name":"本地", + "type":3, + "api":"csp_Local", + "order_num":9999 + }, + { + "key":"PushShare", + "name":"我的资源分享", + "type":3, + "api":"csp_PushShare", + "searchable":1, + "quickSearch":1, + "changeable":1, + "filterable":0, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$https://hipy.trylyingto.me/files/json/pushshare.txt$$$db$$$1", + "style":{ + "type":"list", + "ratio":1.1 + }, + "order_num":9999 + }, + { + "key":"AliShare", + "name":"阿里云盘影视分享", + "type":3, + "api":"csp_AliShare", + "searchable":1, + "quickSearch":1, + "changeable":1, + "filterable":0, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$https://hipy.trylyingto.me/files/json/alishare.txt$$$db$$$1", + "style":{ + "type":"list", + "ratio":1.1 + }, + "order_num":9999 + }, + { + "key":"AliShareEBook", + "name":"阿里云盘书籍分享", + "type":3, + "api":"csp_AliShare", + "searchable":0, + "quickSearch":0, + "changeable":0, + "filterable":0, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$https://hipy.trylyingto.me/files/json/alishare.ebook.txt$$$db$$$1", + "style":{ + "type":"list", + "ratio":1.1 + }, + "order_num":9999 + }, + { + "key":"QuarkShare", + "name":"夸克云盘分享", + "type":3, + "api":"csp_QuarkShare", + "searchable":0, + "quickSearch":0, + "changeable":0, + "filterable":0, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$https://hipy.trylyingto.me/files/json/quarkshare.txt", + "style":{ + "type":"list", + "ratio":1.1 + }, + "order_num":9999 + }, + { + "key":"ThunderShare", + "name":"迅雷云盘分享", + "type":3, + "api":"csp_ThunderShare", + "searchable":0, + "quickSearch":0, + "changeable":0, + "filterable":0, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$https://hipy.trylyingto.me/files/json/thundershare.txt", + "style":{ + "type":"list", + "ratio":1.1 + }, + "order_num":9999 + }, + { + "key":"PikPakShare", + "name":"PikPak分享", + "type":3, + "api":"csp_PikPakShare", + "searchable":1, + "quickSearch":1, + "changeable":1, + "filterable":0, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$https://hipy.trylyingto.me/files/json/pikpakclass.json$$$https://hipy.trylyingto.me/files/json/pikpakclass.json.txt.gz", + "style":{ + "type":"list", + "ratio":1.1 + }, + "order_num":9999 + }, + { + "key":"SambaShare", + "name":"Samba分享", + "type":3, + "api":"csp_SambaShare", + "searchable":0, + "quickSearch":0, + "changeable":0, + "filterable":0, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$https://hipy.trylyingto.me/files/json/sambashare.txt", + "order_num":9999 + }, + { + "key":"Wogg", + "name":"玩偶哥哥|网盘", + "type":3, + "api":"csp_Wogg", + "quickSearch":1, + "changeable":1, + "filterable":1, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$https://wogg.link/$$$proxy$$$1$$$https://hipy.trylyingto.me/files/json/wogg.json", + "order_num":9999 + }, + { + "key":"Wo4k", + "name":"玩偶4K|磁力", + "type":3, + "api":"csp_Wo4k", + "quickSearch":1, + "changeable":1, + "filterable":1, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$https://www.wo4k.net/$$$proxy$$$1$$$https://hipy.trylyingto.me/files/json/wogg.json", + "order_num":9999 + }, + { + "key":"Wobg", + "name":"玩偶表哥|网盘", + "type":3, + "api":"csp_Wobg", + "quickSearch":1, + "changeable":1, + "filterable":1, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$https://wobge.run.goorm.io$$$proxy$$$1$$$https://hipy.trylyingto.me/files/json/wogg.json", + "order_num":9999 + }, + { + "key":"yydsys", + "name":"玩你老哥|网盘", + "type":3, + "api":"csp_Wobg", + "quickSearch":1, + "changeable":1, + "filterable":1, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$https://tv.yydsys.top/$$$proxy$$$1$$$https://hipy.trylyingto.me/files/json/wogg.json", + "order_num":9999 + }, + { + "key":"Xinshijue", + "name":"新视觉|网盘", + "type":3, + "api":"csp_Xinshijue", + "quickSearch":1, + "changeable":1, + "filterable":1, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$https://www.80yy3.com/$$$proxy$$$1$$$https://hipy.trylyingto.me/files/json/wogg.json", + "order_num":9999 + }, + { + "key":"Moli", + "name":"HDmoli|网盘", + "type":3, + "api":"csp_Moli", + "quickSearch":1, + "changeable":1, + "filterable":1, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$https://www.hdmoli.pro/$$$proxy$$$1$$$https://hipy.trylyingto.me/files/json/moli.json", + "order_num":9999 + }, + { + "key":"Hdhive", + "name":"影巢|网盘", + "type":3, + "api":"csp_Hdhive", + "quickSearch":1, + "changeable":1, + "filterable":1, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$proxy$$$1", + "order_num":9999 + }, + { + "key":"Ppxzy", + "name":"皮皮虾|网盘", + "type":3, + "api":"csp_Ppxzy", + "quickSearch":1, + "changeable":1, + "filterable":1, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$https://ppxzy.ink$$$proxy$$$1", + "order_num":9999 + }, + { + "key":"校长影视", + "name":"校长影视|网盘", + "type":3, + "api":"csp_XiaoZhang", + "quickSearch":1, + "changeable":1, + "filterable":1, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$https://xzyshd.com$$$proxy$$$1", + "order_num":9999 + }, + { + "key":"Bdys_spider", + "name":"哔滴┃磁力", + "api":"csp_Bdys01", + "type":3, + "filterable":1, + "searchable":1, + "quickSearch":1, + "ext":"https://www.yjys.me/$$$None$$$1", + "order_num":9999 + }, + { + "key":"YunPan", + "name":"云盘资源|网盘", + "type":3, + "api":"csp_YunPan", + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$None$$$proxy$$$1", + "order_num":9999 + }, + { + "key":"YingSo", + "name":"影搜|网盘搜索", + "type":3, + "api":"csp_YingSo", + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json", + "order_num":9999 + }, + { + "key":"混合盘", + "name":"混合盘|网盘搜索", + "type":3, + "api":"csp_HunHePan", + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json", + "style":{ + "type":"list", + "ratio":1.1 + }, + "order_num":9999 + }, + { + "key":"88Pan", + "name":"88网盘|网盘搜索", + "type":3, + "api":"csp_EightEight", + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$https://662688.xyz$$$", + "style":{ + "type":"list", + "ratio":1.1 + }, + "order_num":9999 + }, + { + "key":"PanSearch", + "name":"PanSearch|网盘搜索", + "type":3, + "api":"csp_PanSearch", + "quickSearch":1, + "changeable":1, + "filterable":1, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json", + "order_num":9999 + }, + { + "key":"盘友圈", + "name":"盘友圈|网盘搜索", + "type":3, + "api":"csp_Panyq", + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$site$$$proxy", + "order_num":9999 + }, + { + "key":"秒搜", + "name":"秒搜|网盘搜索", + "type":3, + "api":"csp_MiaoSou", + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json", + "style":{ + "type":"list", + "ratio":1.1 + }, + "order_num":9999 + }, + { + "key":"Funletu", + "name":"趣盘搜|夸克搜索", + "type":3, + "api":"csp_Funletu", + "quickSearch":1, + "changeable":1, + "filterable":1, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json", + "style":{ + "type":"list", + "ratio":1.1 + }, + "order_num":9999 + }, + { + "key":"QuarkPanso", + "name":"夸克盘搜|夸克搜索", + "type":3, + "api":"csp_QuarkPanso", + "quickSearch":1, + "changeable":1, + "filterable":1, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json", + "style":{ + "type":"list", + "ratio":1.1 + }, + "order_num":9999 + }, + { + "key":"DaPanSo", + "name":"大盘搜|网盘搜索", + "type":3, + "api":"csp_DaPanSo", + "quickSearch":1, + "changeable":1, + "filterable":1, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$https://dapanso.com$$$proxy$$$1", + "order_num":9999 + }, + { + "key":"PikaSo", + "name":"皮卡搜|网盘搜索", + "type":3, + "api":"csp_PikaSo", + "quickSearch":1, + "changeable":1, + "filterable":1, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$https://www.pikaso.top/$$$None", + "style":{ + "type":"list", + "ratio":1.1 + }, + "order_num":9999 + }, + { + "key":"Qianfan", + "name":"千帆|网盘搜索", + "type":3, + "api":"csp_Qianfan", + "quickSearch":1, + "changeable":1, + "filterable":1, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$https://pan.qianfan.app$$$None$$$https://hipy.trylyingto.me/files/json/qianfan.txt$$$1", + "order_num":9999 + }, + { + "key":"YunSo", + "name":"小云搜索|网盘搜索", + "type":3, + "api":"csp_YunSo", + "quickSearch":1, + "changeable":1, + "filterable":1, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$https://www.yunso.net$$$None$$$1", + "style":{ + "type":"list", + "ratio":1.1 + }, + "order_num":9999 + }, + { + "key":"YunPanOne", + "name":"云盘One|网盘搜索", + "type":3, + "api":"csp_YunPanOne", + "quickSearch":1, + "changeable":1, + "filterable":1, + "timeout":60, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json$$$None$$$proxy$$$1", + "order_num":9999 + }, + { + "key":"MV_vod", + "name":"电视┃MTV", + "type":1, + "api":"https://mv.wogg.link/mv/vod", + "searchable":1, + "quickSearch":0, + "changeable":0, + "order_num":9999 + }, + { + "key":"酷狗", + "name":"酷狗", + "type":3, + "api":"csp_Kugou", + "searchable":1, + "changeable":0, + "order_num":9999 + }, + { + "key":"Iktv", + "name":"KTV", + "type":3, + "api":"csp_Iktv", + "searchable":1, + "changeable":0, + "order_num":9999 + }, + { + "key":"Yinyuetai", + "name":"音悦台", + "type":3, + "api":"csp_Yinyuetai", + "searchable":1, + "changeable":0, + "order_num":9999 + }, + { + "key":"push_agent", + "name":"推送", + "type":3, + "api":"csp_Push", + "changeable":0, + "timeout":30, + "ext":"https://hipy.trylyingto.me/files/json/tokenm.json", + "order_num":9999 + }, + { + "key":"應用商店", + "name":"應用商店", + "type":3, + "api":"csp_Market", + "searchable":0, + "changeable":0, + "ext":"https://fm.t4tv.hz.cz/json/market.json", + "order_num":9999 + } + ], + "parses":[ + { + "name":"🌐Ⓤ", + "type":0, + "url":"", + "header":{ + "User-Agent":"Mozilla/5.0" + } + }, + { + "name":"轮询", + "type":2, + "url":"Sequence", + "header":{ + "User-Agent":"Mozilla/5.0" + } + }, + { + "name":"并发", + "type":2, + "url":"Parallel", + "header":{ + "User-Agent":"Mozilla/5.0" + } + }, + { + "name":"777", + "url":"https://jx.777jiexi.com/player/?url=", + "type":0, + "ext":{ + "flag":[ + "qiyi", + "imgo", + "爱奇艺", + "奇艺", + "qq", + "qq 预告及花絮", + "腾讯", + "youku", + "优酷", + "pptv", + "PPTV", + "letv", + "乐视", + "leshi", + "mgtv", + "芒果", + "sohu", + "xigua", + "fun", + "风行" + ] + }, + "header":{ + "User-Agent":"Mozilla/5.0" + } + }, + { + "name":"哔哩", + "url":"https://hipy.trylyingto.me/parse/api/哔哩.js?url=", + "type":1, + "ext":{ + "flag":[ + "qiyi", + "imgo", + "爱奇艺", + "奇艺", + "qq", + "qq 预告及花絮", + "腾讯", + "youku", + "优酷", + "pptv", + "PPTV", + "letv", + "乐视", + "leshi", + "mgtv", + "芒果", + "sohu", + "xigua", + "fun", + "风行" + ] + }, + "header":{ + "User-Agent":"Mozilla/5.0" + } + }, + { + "name":"杰森", + "url":"https://hipy.trylyingto.me/parse/api/杰森.js?url=", + "type":1, + "ext":{ + "flag":[ + "qiyi", + "imgo", + "爱奇艺", + "奇艺", + "qq", + "qq 预告及花絮", + "腾讯", + "youku", + "优酷", + "pptv", + "PPTV", + "letv", + "乐视", + "leshi", + "mgtv", + "芒果", + "sohu", + "xigua", + "fun", + "风行" + ] + }, + "header":{ + "User-Agent":"Mozilla/5.0" + } + }, + { + "name":"阳途", + "url":"https://hipy.trylyingto.me/parse/api/阳途.js?url=", + "type":1, + "ext":{ + "flag":[ + "qiyi", + "imgo", + "爱奇艺", + "奇艺", + "qq", + "qq 预告及花絮", + "腾讯", + "youku", + "优酷", + "pptv", + "PPTV", + "letv", + "乐视", + "leshi", + "mgtv", + "芒果", + "sohu", + "xigua", + "fun", + "风行" + ] + }, + "header":{ + "User-Agent":"Mozilla/5.0" + } + }, + { + "name":"夜幕", + "url":"https://hipy.trylyingto.me/parse/api/夜幕.js?url=", + "type":1, + "ext":{ + "flag":[ + "qiyi", + "imgo", + "爱奇艺", + "奇艺", + "qq", + "qq 预告及花絮", + "腾讯", + "youku", + "优酷", + "pptv", + "PPTV", + "letv", + "乐视", + "leshi", + "mgtv", + "芒果", + "sohu", + "xigua", + "fun", + "风行" + ] + }, + "header":{ + "User-Agent":"Mozilla/5.0" + } + }, + { + "name":"虾米", + "url":"https://hipy.trylyingto.me/parse/api/虾米.js?url=", + "type":1, + "ext":{ + "flag":[ + "qiyi", + "imgo", + "爱奇艺", + "奇艺", + "qq", + "qq 预告及花絮", + "腾讯", + "youku", + "优酷", + "pptv", + "PPTV", + "letv", + "乐视", + "leshi", + "mgtv", + "芒果", + "sohu", + "xigua", + "fun", + "风行" + ] + }, + "header":{ + "User-Agent":"Mozilla/5.0" + } + }, + { + "name":"CK", + "url":"https://hipy.trylyingto.me/parse/api/CK.js?url=", + "type":1, + "ext":{ + "flag":[ + "qiyi", + "imgo", + "爱奇艺", + "奇艺", + "qq", + "qq 预告及花絮", + "腾讯", + "youku", + "优酷", + "pptv", + "PPTV", + "letv", + "乐视", + "leshi", + "mgtv", + "芒果", + "sohu", + "xigua", + "fun", + "风行" + ] + }, + "header":{ + "User-Agent":"Mozilla/5.0" + } + }, + { + "name":"盘古", + "url":"https://hipy.trylyingto.me/parse/api/盘古.js?url=", + "type":1, + "ext":{ + "flag":[ + "qiyi", + "imgo", + "爱奇艺", + "奇艺", + "qq", + "qq 预告及花絮", + "腾讯", + "youku", + "优酷", + "pptv", + "PPTV", + "letv", + "乐视", + "leshi", + "mgtv", + "芒果", + "sohu", + "xigua", + "fun", + "风行" + ] + }, + "header":{ + "User-Agent":"Mozilla/5.0" + } + }, + { + "name":"8090g", + "url":"https://www.8090g.cn/jiexi/?url=", + "type":0, + "ext":{ + "flag":[ + "qiyi", + "imgo", + "爱奇艺", + "奇艺", + "qq", + "qq 预告及花絮", + "腾讯", + "youku", + "优酷", + "pptv", + "PPTV", + "letv", + "乐视", + "leshi", + "mgtv", + "芒果", + "sohu", + "xigua", + "fun", + "风行" + ] + }, + "header":{ + "User-Agent":"Mozilla/5.0" + } + }, + { + "name":"小7", + "url":"https://2.08bk.com/?url=", + "type":0, + "ext":{ + "flag":[ + "qiyi", + "imgo", + "爱奇艺", + "奇艺", + "qq", + "qq 预告及花絮", + "腾讯", + "youku", + "优酷", + "pptv", + "PPTV", + "letv", + "乐视", + "leshi", + "mgtv", + "芒果", + "sohu", + "xigua", + "fun", + "风行" + ] + }, + "header":{ + "User-Agent":"Mozilla/5.0" + } + }, + { + "name":"BL解析", + "url":"https://vip.bljiex.com/?v=", + "type":0, + "ext":{ + "flag":[ + "qiyi", + "imgo", + "爱奇艺", + "奇艺", + "qq", + "qq 预告及花絮", + "腾讯", + "youku", + "优酷", + "pptv", + "PPTV", + "letv", + "乐视", + "leshi", + "mgtv", + "芒果", + "sohu", + "xigua", + "fun", + "风行" + ] + }, + "header":{ + "User-Agent":"Mozilla/5.0" + } + }, + { + "name":"keyu", + "type":1, + "url":"http://newjiexi.gotka.top/keyu3.php?url=", + "ext":{ + "flag":[ + "qq", + "腾讯", + "qiyi", + "爱奇艺", + "奇艺", + "youku", + "优酷", + "mgtv", + "芒果", + "letv", + "乐视", + "pptv", + "PPTV", + "sohu", + "bilibili", + "哔哩哔哩", + "哔哩" + ], + "header":{ + "User-Agent":"okhttp/4.1.0" + } + }, + "order_num":9999 + } + ], + "flags":[ + "imgo", + "youku", + "qq", + "qq 预告及花絮", + "iqiyi", + "qiyi", + "fun", + "letv", + "leshi", + "sohu", + "tudou", + "xigua", + "cntv", + "1905", + "pptv", + "mgtv", + "wasu", + "bilibili", + "renrenmi" + ], + "hotSearch":[ + { + "name":"mobilesearch", + "request":{ + "method":"GET", + "header":[ + { + "key":"Referer", + "value":"https://hipy.trylyingto.me" + } + ], + "url":{ + "raw":"https://hipy.trylyingto.me/hotsugg?t={time}" + } + }, + "response":{ + "result":"$.data", + "data":[ + { + "key":"keyword", + "value":"title" + } + ] + } + } + ], + "lives":[ + { + "name":"直播", + "type":0, + "url":"https://hipy.trylyingto.me/files/txt/mytv.txt", + "playerType":1, + "ua":"okhttp/3.12.13", + "epg":"https://epg.mxdyeah.top/api/diyp/?ch={name}&date={date}", + "logo":"https://live.mxdyeah.top/logo/{name}.png" + } + ], + "sniffer":{ + "userAgent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.27", + "isVideoFormat":"http((?!http).){26,}\\.(m3u8|mp4|flv|avi|mkv|wmv|mpg|mpeg|mov|ts|3gp|rm|rmvb|asf|m4a|mp3|wma)", + "custom":[ + { + "url":"/Cloud/Down/AliCloud/", + "mimeType":"text/html", + "encoding":"utf-8", + "header":{ + "Referer":"https://zxzj.vip/" + } + }, + { + "url":"ysting.ysxs8.vip", + "mimeType":"text/html", + "encoding":"utf-8", + "header":{ + "Referer":"http://ysting.ysxs8.vip:81" + } + } + ] + }, + "recommend":[ + { + "name":"豆瓣推荐", + "request":{ + "method":"GET", + "header":[ + { + "key":"Referer", + "value":"https://movie.douban.com/" + } + ], + "url":{ + "raw":"https://movie.douban.com/j/new_search_subjects?sort=U&range=0,10&tags=&playable=1&start=0&year_range=2022,2022" + } + }, + "response":{ + "result":"$.data", + "data":[ + { + "key":"name", + "value":"title" + }, + { + "key":"note", + "value":"rate" + }, + { + "key":"pic", + "value":"cover" + } + ] + }, + "expires":"86400" + } + ], + "rating":[ + { + "name":"rating", + "request":{ + "method":"GET", + "url":{ + "raw":"https://api.wmdb.tv/api/v1/movie/search?q={name}&limit=1" + } + }, + "response":{ + "result":"this", + "data":[ + { + "key":"rating", + "value":"doubanRating" + } + ] + } + } + ], + "doh":[ + { + "ips":[ + "8.8.4.4", + "8.8.8.8" + ], + "name":"Google", + "url":"https://dns.google/dns-query" + }, + { + "ips":[ + "1.1.1.1", + "1.0.0.1", + "2606:4700:4700::1111", + "2606:4700:4700::1001" + ], + "name":"Cloudflare", + "url":"https://cloudflare-dns.com/dns-query" + }, + { + "ips":[ + "94.140.14.140", + "94.140.14.141" + ], + "name":"AdGuard", + "url":"https://dns.adguard.com/dns-query" + }, + { + "ips":[ + "84.200.69.80", + "84.200.70.40" + ], + "name":"DNSWatch", + "url":"https://resolver2.dns.watch/dns-query" + }, + { + "ips":[ + "9.9.9.9", + "149.112.112.112" + ], + "name":"Quad9", + "url":"https://dns.quad9.net/dns-quer" + } + ], + "ijk":[ + { + "group":"软解码", + "options":[ + { + "category":4, + "name":"opensles", + "value":"0" + }, + { + "category":4, + "name":"overlay-format", + "value":"842225234" + }, + { + "category":4, + "name":"framedrop", + "value":"1" + }, + { + "category":4, + "name":"soundtouch", + "value":"1" + }, + { + "category":4, + "name":"start-on-prepared", + "value":"1" + }, + { + "category":1, + "name":"http-detect-range-support", + "value":"0" + }, + { + "category":1, + "name":"fflags", + "value":"fastseek" + }, + { + "category":2, + "name":"skip_loop_filter", + "value":"48" + }, + { + "category":4, + "name":"reconnect", + "value":"1" + }, + { + "category":4, + "name":"enable-accurate-seek", + "value":"0" + }, + { + "category":4, + "name":"mediacodec", + "value":"0" + }, + { + "category":4, + "name":"mediacodec-auto-rotate", + "value":"0" + }, + { + "category":4, + "name":"mediacodec-handle-resolution-change", + "value":"0" + }, + { + "category":4, + "name":"mediacodec-hevc", + "value":"0" + }, + { + "category":1, + "name":"dns_cache_timeout", + "value":"600000000" + }, + { + "category":4, + "name":"max-buffer-size", + "value":"15728640" + } + ] + }, + { + "group":"硬解码", + "options":[ + { + "category":4, + "name":"opensles", + "value":"0" + }, + { + "category":4, + "name":"overlay-format", + "value":"842225234" + }, + { + "category":4, + "name":"framedrop", + "value":"1" + }, + { + "category":4, + "name":"soundtouch", + "value":"1" + }, + { + "category":4, + "name":"start-on-prepared", + "value":"1" + }, + { + "category":1, + "name":"http-detect-range-support", + "value":"0" + }, + { + "category":1, + "name":"fflags", + "value":"fastseek" + }, + { + "category":2, + "name":"skip_loop_filter", + "value":"48" + }, + { + "category":4, + "name":"reconnect", + "value":"1" + }, + { + "category":4, + "name":"max-buffer-size", + "value":"15728640" + }, + { + "category":4, + "name":"enable-accurate-seek", + "value":"0" + }, + { + "category":4, + "name":"mediacodec", + "value":"1" + }, + { + "category":4, + "name":"mediacodec-auto-rotate", + "value":"1" + }, + { + "category":4, + "name":"mediacodec-handle-resolution-change", + "value":"1" + }, + { + "category":4, + "name":"mediacodec-hevc", + "value":"1" + }, + { + "category":1, + "name":"dns_cache_timeout", + "value":"600000000" + } + ] + } + ], + "rules":[ + { + "hosts":[ + "raw.githubusercontent.com", + "googlevideo.com", + "cdn.v82u1l.com", + "cdn.iz8qkg.com", + "cdn.kin6c1.com", + "c.biggggg.com", + "c.olddddd.com", + "haiwaikan.com", + "www.histar.tv", + "youtube.com", + "uhibo.com", + ".*boku.*", + ".*nivod.*", + ".*ulivetv.*", + "wogg.link", + "wogg.xyz" + ], + "name":"proxy" + }, + { + "hosts":[ + "magnet" + ], + "name":"cl", + "regex":[ + "最新", + "直播", + "更新" + ] + }, + { + "hosts":[ + "haiwaikan" + ], + "name":"海外看", + "regex":[ + "10.0099", + "10.3333", + "16.0599", + "8.1748", + "12.33", + "10.85" + ] + }, + { + "hosts":[ + "suonizy" + ], + "name":"索尼", + "regex":[ + "15.1666", + "15.2666" + ] + }, + { + "hosts":[ + "bfzy", + "bfbfvip" + ], + "name":"暴風", + "regex":[ + "#EXTINF.*?\\s+.*?adjump.*?\\.ts\\s+" + ] + }, + { + "hosts":[ + "aws.ulivetv.net" + ], + "name":"星星", + "regex":[ + "#EXT-X-DISCONTINUITY\\r*\\n*#EXTINF:8,[\\s\\S]*?#EXT-X-DISCONTINUITY" + ] + }, + { + "hosts":[ + "vip.lz", + "hd.lz", + "v.cdnlz" + ], + "name":"量子", + "regex":[ + "18.5333" + ] + }, + { + "hosts":[ + "vip.ffzy", + "hd.ffzy" + ], + "name":"非凡", + "regex":[ + "25.1999", + "25.0666", + "25.08", + "20.52" + ] + }, + { + "hosts":[ + "huoshan.com" + ], + "name":"火山嗅探", + "regex":[ + "item_id=" + ] + }, + { + "hosts":[ + "douyin.com" + ], + "name":"抖音嗅探", + "regex":[ + "is_play_url=" + ] + }, + { + "hosts":[ + "api.52wyb.com" + ], + "name":"七新嗅探", + "regex":[ + "m3u8?pt=m3u8" + ] + }, + { + "hosts":[ + "10086.cn" + ], + "name":"czzy", + "regex":[ + "/storageWeb/servlet/downloadServlet" + ] + }, + { + "exclude":[ + ".m3u8" + ], + "hosts":[ + "bytetos.com", + "byteimg.com", + "bytednsdoc.com", + "pstatp.com" + ], + "name":"bdys", + "regex":[ + "/tos-cn" + ] + }, + { + "exclude":[ + ".m3u8" + ], + "hosts":[ + "bdys10.com" + ], + "name":"bdys10", + "regex":[ + "/obj/" + ] + } + ], + "ads":[ + "mimg.0c1q0l.cn", + "www.googletagmanager.com", + "www.google-analytics.com", + "mc.usihnbcq.cn", + "mg.g1mm3d.cn", + "mscs.svaeuzh.cn", + "cnzz.hhttm.top", + "tp.vinuxhome.com", + "cnzz.mmstat.com", + "www.baihuillq.com", + "s23.cnzz.com", + "z3.cnzz.com", + "c.cnzz.com", + "stj.v1vo.top", + "z12.cnzz.com", + "img.mosflower.cn", + "tips.gamevvip.com", + "ehwe.yhdtns.com", + "xdn.cqqc3.com", + "www.jixunkyy.cn", + "sp.chemacid.cn", + "hm.baidu.com", + "s9.cnzz.com", + "z6.cnzz.com", + "um.cavuc.com", + "mav.mavuz.com", + "wofwk.aoidf3.com", + "z5.cnzz.com", + "xc.hubeijieshikj.cn", + "tj.tianwenhu.com", + "xg.gars57.cn", + "k.jinxiuzhilv.com", + "cdn.bootcss.com", + "ppl.xunzhuo123.com", + "xomk.jiangjunmh.top", + "img.xunzhuo123.com", + "z1.cnzz.com", + "s13.cnzz.com", + "xg.huataisangao.cn", + "z7.cnzz.com", + "xg.huataisangao.cn", + "z2.cnzz.com", + "s96.cnzz.com", + "q11.cnzz.com", + "thy.dacedsfa.cn", + "xg.whsbpw.cn", + "s19.cnzz.com", + "z8.cnzz.com", + "s4.cnzz.com", + "f5w.as12df.top", + "ae01.alicdn.com", + "www.92424.cn", + "k.wudejia.com", + "vivovip.mmszxc.top", + "qiu.xixiqiu.com", + "cdnjs.hnfenxun.com", + "cms.qdwght.com", + "wan.51img1.com", + "iqiyi.hbuioo.com", + "vip.ffzyad.com", + "https://lf1-cdn-tos.bytegoofy.com/obj/tos-cn-i-dy/455ccf9e8ae744378118e4bd289288dd" + ], + "drives":[ + { + "name":"阿里", + "password":"43886374072944a2bcc55a0ed129ab48", + "type":"alidrive" + } + ], + "cost_time":477.9 + }, + "home": {"class":[{"type_id":"1","type_name":"电影"},{"type_id":"2","type_name":"连续剧"},{"type_id":"3","type_name":"综艺"},{"type_id":"4","type_name":"动漫"},{"type_id":"26","type_name":"短剧"}],"filters":{"1":[{"key":"cateId","name":"类型","value":[{"n":"全部","v":"1"},{"n":"动作片","v":"5"},{"n":"喜剧片","v":"6"},{"n":"爱情片","v":"7"},{"n":"科幻片","v":"8"},{"n":"恐怖片","v":"9"},{"n":"剧情片","v":"10"},{"n":"战争片","v":"11"},{"n":"惊悚片","v":"16"},{"n":"奇幻片","v":"17"}]},{"key":"area","name":"地区","value":[{"n":"全部","v":""},{"n":"大陆","v":"大陆"},{"n":"香港","v":"香港"},{"n":"台湾","v":"台湾"},{"n":"美国","v":"美国"},{"n":"韩国","v":"韩国"},{"n":"日本","v":"日本"},{"n":"泰国","v":"泰国"},{"n":"新加坡","v":"新加坡"},{"n":"马来西亚","v":"马来西亚"},{"n":"印度","v":"印度"},{"n":"英国","v":"英国"},{"n":"法国","v":"法国"},{"n":"加拿大","v":"加拿大"},{"n":"西班牙","v":"西班牙"},{"n":"俄罗斯","v":"俄罗斯"},{"n":"其它","v":"其它"}]},{"key":"year","name":"年代","value":[{"n":"全部","v":""},{"n":"2023","v":"2023"},{"n":"2022","v":"2022"},{"n":"2021","v":"2021"},{"n":"2020","v":"2020"},{"n":"2019","v":"2019"},{"n":"2018","v":"2018"},{"n":"2017","v":"2017"},{"n":"2016","v":"2016"},{"n":"2015","v":"2015"},{"n":"2014","v":"2014"},{"n":"2013","v":"2013"},{"n":"2012","v":"2012"},{"n":"2011","v":"2011"},{"n":"2010","v":"2010"},{"n":"2009","v":"2009"},{"n":"2008","v":"2008"},{"n":"2007","v":"2007"},{"n":"2006","v":"2006"},{"n":"2005","v":"2005"},{"n":"2004","v":"2004"},{"n":"2003","v":"2003"},{"n":"2002","v":"2002"},{"n":"2001","v":"2001"},{"n":"2000","v":"2000"},{"n":"1999","v":"1999"},{"n":"1998","v":"1998"},{"n":"1997","v":"1997"},{"n":"1996","v":"1996"},{"n":"1995","v":"1995"},{"n":"1994","v":"1994"},{"n":"1993","v":"1993"},{"n":"1992","v":"1992"},{"n":"1991","v":"1991"},{"n":"1990","v":"1990"},{"n":"1989","v":"1989"},{"n":"1988","v":"1988"},{"n":"1987","v":"1987"},{"n":"1986","v":"1986"},{"n":"1985","v":"1985"},{"n":"1984","v":"1984"},{"n":"1983","v":"1983"},{"n":"1982","v":"1982"},{"n":"1981","v":"1981"},{"n":"1980","v":"1980"},{"n":"1979","v":"1979"},{"n":"1978","v":"1978"},{"n":"1977","v":"1977"},{"n":"1976","v":"1976"},{"n":"1975","v":"1975"},{"n":"1974","v":"1974"},{"n":"1973","v":"1973"},{"n":"1972","v":"1972"},{"n":"1971","v":"1971"},{"n":"1970","v":"1970"},{"n":"1969","v":"1969"},{"n":"1968","v":"1968"},{"n":"1967","v":"1967"},{"n":"1966","v":"1966"},{"n":"1965","v":"1965"},{"n":"1964","v":"1964"},{"n":"1963","v":"1963"},{"n":"1962","v":"1962"},{"n":"1961","v":"1961"},{"n":"1960","v":"1960"}]},{"key":"by","name":"排序","value":[{"n":"时间","v":"time"},{"n":"人气","v":"hits"},{"n":"评分","v":"score"}]}],"2":[{"key":"cateId","name":"类型","value":[{"n":"全部","v":"2"},{"n":"国产剧","v":"12"},{"n":"港台泰","v":"13"},{"n":"日韩剧","v":"14"},{"n":"欧美剧","v":"15"}]},{"key":"area","name":"地区","value":[{"n":"全部","v":""},{"n":"大陆","v":"大陆"},{"n":"香港","v":"香港"},{"n":"台湾","v":"台湾"},{"n":"美国","v":"美国"},{"n":"韩国","v":"韩国"},{"n":"日本","v":"日本"},{"n":"泰国","v":"泰国"},{"n":"新加坡","v":"新加坡"},{"n":"马来西亚","v":"马来西亚"},{"n":"印度","v":"印度"},{"n":"英国","v":"英国"},{"n":"法国","v":"法国"},{"n":"加拿大","v":"加拿大"},{"n":"西班牙","v":"西班牙"},{"n":"俄罗斯","v":"俄罗斯"},{"n":"其它","v":"其它"}]},{"key":"year","name":"年代","value":[{"n":"全部","v":""},{"n":"2023","v":"2023"},{"n":"2022","v":"2022"},{"n":"2021","v":"2021"},{"n":"2020","v":"2020"},{"n":"2019","v":"2019"},{"n":"2018","v":"2018"},{"n":"2017","v":"2017"},{"n":"2016","v":"2016"},{"n":"2015","v":"2015"},{"n":"2014","v":"2014"},{"n":"2013","v":"2013"},{"n":"2012","v":"2012"},{"n":"2011","v":"2011"},{"n":"2010","v":"2010"},{"n":"2009","v":"2009"},{"n":"2008","v":"2008"},{"n":"2007","v":"2007"},{"n":"2006","v":"2006"},{"n":"2005","v":"2005"},{"n":"2004","v":"2004"},{"n":"2003","v":"2003"},{"n":"2002","v":"2002"},{"n":"2001","v":"2001"},{"n":"2000","v":"2000"},{"n":"1999","v":"1999"},{"n":"1998","v":"1998"},{"n":"1997","v":"1997"},{"n":"1996","v":"1996"},{"n":"1995","v":"1995"},{"n":"1994","v":"1994"},{"n":"1993","v":"1993"},{"n":"1992","v":"1992"},{"n":"1991","v":"1991"},{"n":"1990","v":"1990"},{"n":"1989","v":"1989"},{"n":"1988","v":"1988"},{"n":"1987","v":"1987"},{"n":"1986","v":"1986"},{"n":"1985","v":"1985"},{"n":"1984","v":"1984"},{"n":"1983","v":"1983"},{"n":"1982","v":"1982"},{"n":"1981","v":"1981"},{"n":"1980","v":"1980"},{"n":"1979","v":"1979"},{"n":"1978","v":"1978"},{"n":"1977","v":"1977"},{"n":"1976","v":"1976"},{"n":"1975","v":"1975"},{"n":"1974","v":"1974"},{"n":"1973","v":"1973"},{"n":"1972","v":"1972"},{"n":"1971","v":"1971"},{"n":"1970","v":"1970"},{"n":"1969","v":"1969"},{"n":"1968","v":"1968"},{"n":"1967","v":"1967"},{"n":"1966","v":"1966"},{"n":"1965","v":"1965"},{"n":"1964","v":"1964"},{"n":"1963","v":"1963"},{"n":"1962","v":"1962"},{"n":"1961","v":"1961"},{"n":"1960","v":"1960"}]},{"key":"by","name":"排序","value":[{"n":"时间","v":"time"},{"n":"人气","v":"hits"},{"n":"评分","v":"score"}]}],"3":[{"key":"area","name":"地区","value":[{"n":"全部","v":""},{"n":"大陆","v":"大陆"},{"n":"香港","v":"香港"},{"n":"台湾","v":"台湾"},{"n":"美国","v":"美国"},{"n":"韩国","v":"韩国"},{"n":"日本","v":"日本"},{"n":"泰国","v":"泰国"},{"n":"新加坡","v":"新加坡"},{"n":"马来西亚","v":"马来西亚"},{"n":"印度","v":"印度"},{"n":"英国","v":"英国"},{"n":"法国","v":"法国"},{"n":"加拿大","v":"加拿大"},{"n":"西班牙","v":"西班牙"},{"n":"俄罗斯","v":"俄罗斯"},{"n":"其它","v":"其它"}]},{"key":"year","name":"年代","value":[{"n":"全部","v":""},{"n":"2023","v":"2023"},{"n":"2022","v":"2022"},{"n":"2021","v":"2021"},{"n":"2020","v":"2020"},{"n":"2019","v":"2019"},{"n":"2018","v":"2018"},{"n":"2017","v":"2017"},{"n":"2016","v":"2016"},{"n":"2015","v":"2015"},{"n":"2014","v":"2014"},{"n":"2013","v":"2013"},{"n":"2012","v":"2012"},{"n":"2011","v":"2011"},{"n":"2010","v":"2010"},{"n":"2009","v":"2009"},{"n":"2008","v":"2008"},{"n":"2007","v":"2007"},{"n":"2006","v":"2006"},{"n":"2005","v":"2005"},{"n":"2004","v":"2004"},{"n":"2003","v":"2003"},{"n":"2002","v":"2002"},{"n":"2001","v":"2001"},{"n":"2000","v":"2000"},{"n":"1999","v":"1999"},{"n":"1998","v":"1998"},{"n":"1997","v":"1997"},{"n":"1996","v":"1996"},{"n":"1995","v":"1995"},{"n":"1994","v":"1994"},{"n":"1993","v":"1993"},{"n":"1992","v":"1992"},{"n":"1991","v":"1991"},{"n":"1990","v":"1990"},{"n":"1989","v":"1989"},{"n":"1988","v":"1988"},{"n":"1987","v":"1987"},{"n":"1986","v":"1986"},{"n":"1985","v":"1985"},{"n":"1984","v":"1984"},{"n":"1983","v":"1983"},{"n":"1982","v":"1982"},{"n":"1981","v":"1981"},{"n":"1980","v":"1980"},{"n":"1979","v":"1979"},{"n":"1978","v":"1978"},{"n":"1977","v":"1977"},{"n":"1976","v":"1976"},{"n":"1975","v":"1975"},{"n":"1974","v":"1974"},{"n":"1973","v":"1973"},{"n":"1972","v":"1972"},{"n":"1971","v":"1971"},{"n":"1970","v":"1970"},{"n":"1969","v":"1969"},{"n":"1968","v":"1968"},{"n":"1967","v":"1967"},{"n":"1966","v":"1966"},{"n":"1965","v":"1965"},{"n":"1964","v":"1964"},{"n":"1963","v":"1963"},{"n":"1962","v":"1962"},{"n":"1961","v":"1961"},{"n":"1960","v":"1960"}]},{"key":"by","name":"排序","value":[{"n":"时间","v":"time"},{"n":"人气","v":"hits"},{"n":"评分","v":"score"}]}],"4":[{"key":"cateId","name":"类型","value":[{"n":"全部","v":"4"},{"n":"动漫剧","v":"18"},{"n":"动漫片","v":"19"}]},{"key":"area","name":"地区","value":[{"n":"全部","v":""},{"n":"大陆","v":"大陆"},{"n":"香港","v":"香港"},{"n":"台湾","v":"台湾"},{"n":"美国","v":"美国"},{"n":"韩国","v":"韩国"},{"n":"日本","v":"日本"},{"n":"泰国","v":"泰国"},{"n":"新加坡","v":"新加坡"},{"n":"马来西亚","v":"马来西亚"},{"n":"印度","v":"印度"},{"n":"英国","v":"英国"},{"n":"法国","v":"法国"},{"n":"加拿大","v":"加拿大"},{"n":"西班牙","v":"西班牙"},{"n":"俄罗斯","v":"俄罗斯"},{"n":"其它","v":"其它"}]},{"key":"year","name":"年代","value":[{"n":"全部","v":""},{"n":"2023","v":"2023"},{"n":"2022","v":"2022"},{"n":"2021","v":"2021"},{"n":"2020","v":"2020"},{"n":"2019","v":"2019"},{"n":"2018","v":"2018"},{"n":"2017","v":"2017"},{"n":"2016","v":"2016"},{"n":"2015","v":"2015"},{"n":"2014","v":"2014"},{"n":"2013","v":"2013"},{"n":"2012","v":"2012"},{"n":"2011","v":"2011"},{"n":"2010","v":"2010"},{"n":"2009","v":"2009"},{"n":"2008","v":"2008"},{"n":"2007","v":"2007"},{"n":"2006","v":"2006"},{"n":"2005","v":"2005"},{"n":"2004","v":"2004"},{"n":"2003","v":"2003"},{"n":"2002","v":"2002"},{"n":"2001","v":"2001"},{"n":"2000","v":"2000"},{"n":"1999","v":"1999"},{"n":"1998","v":"1998"},{"n":"1997","v":"1997"},{"n":"1996","v":"1996"},{"n":"1995","v":"1995"},{"n":"1994","v":"1994"},{"n":"1993","v":"1993"},{"n":"1992","v":"1992"},{"n":"1991","v":"1991"},{"n":"1990","v":"1990"},{"n":"1989","v":"1989"},{"n":"1988","v":"1988"},{"n":"1987","v":"1987"},{"n":"1986","v":"1986"},{"n":"1985","v":"1985"},{"n":"1984","v":"1984"},{"n":"1983","v":"1983"},{"n":"1982","v":"1982"},{"n":"1981","v":"1981"},{"n":"1980","v":"1980"},{"n":"1979","v":"1979"},{"n":"1978","v":"1978"},{"n":"1977","v":"1977"},{"n":"1976","v":"1976"},{"n":"1975","v":"1975"},{"n":"1974","v":"1974"},{"n":"1973","v":"1973"},{"n":"1972","v":"1972"},{"n":"1971","v":"1971"},{"n":"1970","v":"1970"},{"n":"1969","v":"1969"},{"n":"1968","v":"1968"},{"n":"1967","v":"1967"},{"n":"1966","v":"1966"},{"n":"1965","v":"1965"},{"n":"1964","v":"1964"},{"n":"1963","v":"1963"},{"n":"1962","v":"1962"},{"n":"1961","v":"1961"},{"n":"1960","v":"1960"}]},{"key":"by","name":"排序","value":[{"n":"时间","v":"time"},{"n":"人气","v":"hits"},{"n":"评分","v":"score"}]}],"26":[{"key":"area","name":"地区","value":[{"n":"全部","v":""},{"n":"大陆","v":"大陆"},{"n":"香港","v":"香港"},{"n":"台湾","v":"台湾"},{"n":"美国","v":"美国"},{"n":"韩国","v":"韩国"},{"n":"日本","v":"日本"},{"n":"泰国","v":"泰国"},{"n":"新加坡","v":"新加坡"},{"n":"马来西亚","v":"马来西亚"},{"n":"印度","v":"印度"},{"n":"英国","v":"英国"},{"n":"法国","v":"法国"},{"n":"加拿大","v":"加拿大"},{"n":"西班牙","v":"西班牙"},{"n":"俄罗斯","v":"俄罗斯"},{"n":"其它","v":"其它"}]},{"key":"year","name":"年代","value":[{"n":"全部","v":""},{"n":"2023","v":"2023"},{"n":"2022","v":"2022"},{"n":"2021","v":"2021"},{"n":"2020","v":"2020"},{"n":"2019","v":"2019"},{"n":"2018","v":"2018"},{"n":"2017","v":"2017"},{"n":"2016","v":"2016"},{"n":"2015","v":"2015"},{"n":"2014","v":"2014"},{"n":"2013","v":"2013"},{"n":"2012","v":"2012"},{"n":"2011","v":"2011"},{"n":"2010","v":"2010"},{"n":"2009","v":"2009"},{"n":"2008","v":"2008"},{"n":"2007","v":"2007"},{"n":"2006","v":"2006"},{"n":"2005","v":"2005"},{"n":"2004","v":"2004"},{"n":"2003","v":"2003"},{"n":"2002","v":"2002"},{"n":"2001","v":"2001"},{"n":"2000","v":"2000"},{"n":"1999","v":"1999"},{"n":"1998","v":"1998"},{"n":"1997","v":"1997"},{"n":"1996","v":"1996"},{"n":"1995","v":"1995"},{"n":"1994","v":"1994"},{"n":"1993","v":"1993"},{"n":"1992","v":"1992"},{"n":"1991","v":"1991"},{"n":"1990","v":"1990"},{"n":"1989","v":"1989"},{"n":"1988","v":"1988"},{"n":"1987","v":"1987"},{"n":"1986","v":"1986"},{"n":"1985","v":"1985"},{"n":"1984","v":"1984"},{"n":"1983","v":"1983"},{"n":"1982","v":"1982"},{"n":"1981","v":"1981"},{"n":"1980","v":"1980"},{"n":"1979","v":"1979"},{"n":"1978","v":"1978"},{"n":"1977","v":"1977"},{"n":"1976","v":"1976"},{"n":"1975","v":"1975"},{"n":"1974","v":"1974"},{"n":"1973","v":"1973"},{"n":"1972","v":"1972"},{"n":"1971","v":"1971"},{"n":"1970","v":"1970"},{"n":"1969","v":"1969"},{"n":"1968","v":"1968"},{"n":"1967","v":"1967"},{"n":"1966","v":"1966"},{"n":"1965","v":"1965"},{"n":"1964","v":"1964"},{"n":"1963","v":"1963"},{"n":"1962","v":"1962"},{"n":"1961","v":"1961"},{"n":"1960","v":"1960"}]},{"key":"by","name":"排序","value":[{"n":"时间","v":"time"},{"n":"人气","v":"hits"},{"n":"评分","v":"score"}]}]}}, + "homeVod": {"list":[{"vod_name":"黑白诀","vod_pic":"https://pic1.58cdn.com.cn/nowater/im/n_v328855398939b42e690e1a9144d0103e4.jpeg","vod_remarks":"更新至20集","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51694.html"},{"vod_name":"珠帘玉幕","vod_pic":"http://m.ykimg.com/058400006723580D13EB661331FD30E6?x-oss-process=image/resize,w_312/interlace,1/quality,Q_80","vod_remarks":"更新至14集","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51808.html"},{"vod_name":"探晴安","vod_pic":"https://vcover-vt-pic.puui.qpic.cn/vcover_vt_pic/0/mzc00200nhs9g5o1729298236221/260","vod_remarks":"更新至19集","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51700.html"},{"vod_name":"龙骨遗冢","vod_pic":"https://vcover-vt-pic.puui.qpic.cn/vcover_vt_pic/0/mzc002005vz2e4v1727706444682/260","vod_remarks":"","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51848.html"},{"vod_name":"好团圆","vod_pic":"https://puui.qpic.cn/vcover_vt_pic/0/mzc00200kot7j0l1695194882212/260","vod_remarks":"更新至23集","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51718.html"},{"vod_name":"西北岁月","vod_pic":"https://vcover-vt-pic.puui.qpic.cn/vcover_vt_pic/0/mzc002008c8m73o1730794226058/260","vod_remarks":"更新至02集","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51846.html"},{"vod_name":"小巷人家","vod_pic":"https://vcover-vt-pic.puui.qpic.cn/vcover_vt_pic/0/sdp001000atbk1t1730070334/260","vod_remarks":"更新至17集","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51739.html"},{"vod_name":"永夜星河","vod_pic":"https://vcover-vt-pic.puui.qpic.cn/vcover_vt_pic/0/mzc002007sqbpce1719196138249/260","vod_remarks":"更新至12集","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51809.html"},{"vod_name":"加菲猫家族","vod_pic":"https://pic1.58cdn.com.cn/nowater/im/n_v35d466e73a7214d4ba162a5bfbf8f1fd7.jpeg","vod_remarks":"","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51843.html"},{"vod_name":"黑白有界","vod_pic":"https://pic1.58cdn.com.cn/nowater/im/n_v3ae07cf14507e41df8a1efedadfb08dd5.jpeg","vod_remarks":"","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51842.html"},{"vod_name":"大梦归离","vod_pic":"https://pic1.58cdn.com.cn/nowater/im/n_v3f47b89fb0a1a4e27bf65a0b9c8d8902a.jpeg","vod_remarks":"更新至17集","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51717.html"},{"vod_name":"你的谎言也动听","vod_pic":"https://pic1.58cdn.com.cn/nowater/im/n_v3031f14453a114a55846eaf39ccc5824b.jpeg","vod_remarks":"36集全","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51621.html"},{"vod_name":"黑白诀","vod_pic":"","vod_remarks":"更新至20集","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51694.html"},{"vod_name":"珠帘玉幕","vod_pic":"","vod_remarks":"更新至14集","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51808.html"},{"vod_name":"探晴安","vod_pic":"","vod_remarks":"更新至19集","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51700.html"},{"vod_name":"好团圆","vod_pic":"","vod_remarks":"更新至23集","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51718.html"},{"vod_name":"西北岁月","vod_pic":"","vod_remarks":"更新至02集","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51846.html"},{"vod_name":"小巷人家","vod_pic":"","vod_remarks":"更新至17集","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51739.html"},{"vod_name":"永夜星河","vod_pic":"","vod_remarks":"更新至12集","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51809.html"},{"vod_name":"大梦归离","vod_pic":"","vod_remarks":"更新至17集","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51717.html"},{"vod_name":"你的谎言也动听","vod_pic":"","vod_remarks":"36集全","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51621.html"},{"vod_name":"巾帼枭雄之悬崖","vod_pic":"","vod_remarks":"25集全","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51671.html"},{"vod_name":"双重任务","vod_pic":"","vod_remarks":"25集全","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51696.html"},{"vod_name":"春花焰","vod_pic":"","vod_remarks":"32集全","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51552.html"},{"vod_name":"龙骨遗冢","vod_pic":"","vod_remarks":"","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-51848.html"},{"vod_name":"心动的信号第七季","vod_pic":"","vod_remarks":"更新至20241105","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-50951.html"},{"vod_name":"丹道至尊","vod_pic":"","vod_remarks":"更新至第92集","vod_content":"","vod_id":"https://www.wwgz.cn/vod-detail-id-46811.html"}]}, + "category": {"page":1,"pagecount":999,"limit":20,"total":999,"list":[{"vod_id":"https://www.wwgz.cn/vod-detail-id-51598.html","vod_name":"出租大叔","vod_pic":"https://img.lzzyimg.com/upload/vod/20241017-1/36e6ffbcc023a18fa7cbb28700bf3d27.jpg","vod_remarks":"更新至第16集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-29008.html","vod_name":"爱·回家之开心速递","vod_pic":"https://img.liangzipic.com/upload/vod/20220608-1/3bba6648dbdfe6f442e0492730ec908b.jpg","vod_remarks":"更新至第2415集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51841.html","vod_name":"酒醉罗曼史","vod_pic":"https://img.lzzyimg.com/upload/vod/20241104-1/8a931560d638c4d6cd535a132b6804c5.jpg","vod_remarks":"更新至第02集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51451.html","vod_name":"结婚吧,笨蛋啊!","vod_pic":"https://img.lzzyimg.com/upload/vod/20241008-1/817bad29b19d2688ad58d225380d82bb.jpg","vod_remarks":"更新至第22集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-43316.html","vod_name":"丑闻[韩剧]","vod_pic":"https://img.lzzyimg.com/upload/vod/20240618-1/dd1b04ba4162b5c7a373242d57839ed4.jpg","vod_remarks":"更新至第84集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-39722.html","vod_name":"勇敢无双龙秀晶","vod_pic":"https://img.lzzyimg.com/upload/vod/20240507-1/571404fc7aacea62f65f384576760f4f.jpg","vod_remarks":"更新至第116集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51104.html","vod_name":"我的天才女友第四季","vod_pic":"https://img.lzzyimg.com/upload/vod/20240911-1/8ba2ee8e8cc05c702c5e3ae86b068de3.jpg","vod_remarks":"更新至第09集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51428.html","vod_name":"叔不知","vod_pic":"https://img.lzzyimg.com/upload/vod/20241005-1/b70a1db8bf35c868ac11ca916da00789.jpg","vod_remarks":"更新至第12集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51847.html","vod_name":"极道青年","vod_pic":"https://img.lzzyimg.com/upload/vod/20241105-1/f283c807b2ac3439bb1b570a5370af2d.jpg","vod_remarks":"更新至第01集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51682.html","vod_name":"民王R","vod_pic":"https://img.lzzyimg.com/upload/vod/20241023-1/78589c0f17c127b80ba3ad6ac5151e86.jpg","vod_remarks":"更新至第03集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51564.html","vod_name":"怪物2024","vod_pic":"https://img.lzzyimg.com/upload/vod/20241015-1/c10886eca3fbc906d482f6d6f2ad5d21.jpg","vod_remarks":"更新至第04集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51548.html","vod_name":"OCTO~感情搜查官心野朱梨~第二季","vod_pic":"https://img.lzzyimg.com/upload/vod/20241014-1/c917ecd72ee1f4a6f5df91bbf0e4dc3f.jpg","vod_remarks":"更新至第03集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51464.html","vod_name":"被未来的自己愚弄!?","vod_pic":"https://img.lzzyimg.com/upload/vod/20241008-1/1f0f67b527bf436fc5bf6d4e9ebfade8.jpg","vod_remarks":"更新至第17集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51463.html","vod_name":"解谎侦探少女","vod_pic":"https://img.lzzyimg.com/upload/vod/20241008-1/fd1d9f24693d61e4e169841e49ee6fe0.jpg","vod_remarks":"更新至第05集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51471.html","vod_name":"新版露在火中烧","vod_pic":"https://img.lzzyimg.com/upload/vod/20241009-1/5e878911a9de89db0cfe53b35e6dfd87.jpg","vod_remarks":"更新至第08集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51718.html","vod_name":"好团圆","vod_pic":"https://puui.qpic.cn/vcover_vt_pic/0/mzc00200kot7j0l1695194882212/260","vod_remarks":"更新至23集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51846.html","vod_name":"西北岁月","vod_pic":"https://vcover-vt-pic.puui.qpic.cn/vcover_vt_pic/0/mzc002008c8m73o1730794226058/260","vod_remarks":"更新至02集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51739.html","vod_name":"小巷人家","vod_pic":"https://vcover-vt-pic.puui.qpic.cn/vcover_vt_pic/0/sdp001000atbk1t1730070334/260","vod_remarks":"更新至17集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51809.html","vod_name":"永夜星河","vod_pic":"https://vcover-vt-pic.puui.qpic.cn/vcover_vt_pic/0/mzc002007sqbpce1719196138249/260","vod_remarks":"更新至12集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51808.html","vod_name":"珠帘玉幕","vod_pic":"http://m.ykimg.com/058400006723580D13EB661331FD30E6?x-oss-process=image/resize,w_312/interlace,1/quality,Q_80","vod_remarks":"更新至12集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51694.html","vod_name":"黑白诀","vod_pic":"https://pic1.58cdn.com.cn/nowater/im/n_v328855398939b42e690e1a9144d0103e4.jpeg","vod_remarks":"更新至19集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51700.html","vod_name":"探晴安","vod_pic":"https://vcover-vt-pic.puui.qpic.cn/vcover_vt_pic/0/mzc00200nhs9g5o1729298236221/260","vod_remarks":"更新至18集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51840.html","vod_name":"烈焰国度第三季","vod_pic":"https://img.lzzyimg.com/upload/vod/20241104-1/930ee5cd19e62a4f8af4eabaa6bdfaa8.jpg","vod_remarks":"更新至第01集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51839.html","vod_name":"新殊途同归第二季","vod_pic":"https://img.lzzyimg.com/upload/vod/20241105-1/58cb4e74cf811bb8aff9e8de9d04b244.jpg","vod_remarks":"更新至第01集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51812.html","vod_name":"反恐特警组第八季","vod_pic":"https://img.lzzyimg.com/upload/vod/20241101-1/402a230a3e406737e86b8f4be3b14fe7.jpg","vod_remarks":"更新至第02集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51773.html","vod_name":"某人某地第三季","vod_pic":"https://img.lzzyimg.com/upload/vod/20241028-1/f701af724a809cc9a36741d2bc65b76e.jpg","vod_remarks":"更新至第02集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51713.html","vod_name":"海军罪案调查处第二十二季","vod_pic":"https://img.lzzyimg.com/upload/vod/20241025-1/07e11eb2b3f4c45f8c5bb3bf1f6712c3.jpg","vod_remarks":"已完结"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51450.html","vod_name":"系列大片","vod_pic":"https://img.lzzyimg.com/upload/vod/20241007-1/b5a33b435b0c282c01e1efae8e2a0438.jpg","vod_remarks":"更新至第05集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51299.html","vod_name":"芝加哥急救第十季","vod_pic":"https://img.lzzyimg.com/upload/vod/20240927-1/00556ea2ac6ce26dab4f4c6491228b8e.jpg","vod_remarks":"更新至第05集"},{"vod_id":"https://www.wwgz.cn/vod-detail-id-51298.html","vod_name":"芝加哥警署第十二季","vod_pic":"https://img.lzzyimg.com/upload/vod/20240927-1/75b75ec0987b444f72288118aec14500.jpg","vod_remarks":"更新至第05集"}]}, + "detail": {"list":[{"vod_id":"https://www.wwgz.cn/vod-detail-id-51598.html","vod_name":"出租大叔","vod_pic":"https://img.lzzyimg.com/upload/vod/20241017-1/36e6ffbcc023a18fa7cbb28700bf3d27.jpg","type_name":"港台泰","vod_year":"2024","vod_area":"","vod_remarks":"更新日期: 2024-11-06 10:52","vod_actor":"许博文 陆骏光 陈子丰 张达伦 沈贞巧 邱颂伟","vod_director":"陈贤俊","vod_content":"简 介: 生活唔如意﹖噢﹗萬事有大叔﹗看似成熟穩重的西裝精英林木森,為人功利現實又口甜舌滑,愛耍小聰明。在車行工作多年,接觸人多,見盡不同客戶,練就一把好口才,更成為行內 Top Sales。早年離婚的森將女兒林瑪莎的撫養權讓給太太,太太跟女兒移居美國。然而好景不常,疫情過後經濟衰退,車行決定在人工高的他身上開刀,人到中年才失業,愛充大頭鬼的他一時跌下谷低,人生一團糟之際女兒從外國回來,並帶來「出租大叔」的想法﹗森從起初的不願,到渴望靠此生意翻身賺大錢,最後卻發現做「出租大叔」其實是幫助自己理清一團糟的人生,做回最真實的自己⋯⋯ 森索性找來幾個多年好友下海,一同出租,包括:有型才子陸奧、住家男平治、開Cafe的Ben,以及大肚腩的士司機阿田。漸漸地,一班大叔在工作上找回自己的價值,找回生活的熱誠。而林木森在解決客人的問題同時,也解決了自己的問題。其實一切,都是一個學習過程。","vod_play_from":"云播①","vod_play_url":"第01集$https://www.wwgz.cn/vod-play-id-51598-src-1-num-1.html#第02集$https://www.wwgz.cn/vod-play-id-51598-src-1-num-2.html#第03集$https://www.wwgz.cn/vod-play-id-51598-src-1-num-3.html#第04集$https://www.wwgz.cn/vod-play-id-51598-src-1-num-4.html#第05集$https://www.wwgz.cn/vod-play-id-51598-src-1-num-5.html#第06集$https://www.wwgz.cn/vod-play-id-51598-src-1-num-6.html#第07集$https://www.wwgz.cn/vod-play-id-51598-src-1-num-7.html#第08集$https://www.wwgz.cn/vod-play-id-51598-src-1-num-8.html#第09集$https://www.wwgz.cn/vod-play-id-51598-src-1-num-9.html#第10集$https://www.wwgz.cn/vod-play-id-51598-src-1-num-10.html#第11集$https://www.wwgz.cn/vod-play-id-51598-src-1-num-11.html#第12集$https://www.wwgz.cn/vod-play-id-51598-src-1-num-12.html#第13集$https://www.wwgz.cn/vod-play-id-51598-src-1-num-13.html#第14集$https://www.wwgz.cn/vod-play-id-51598-src-1-num-14.html#第15集$https://www.wwgz.cn/vod-play-id-51598-src-1-num-15.html#第16集$https://www.wwgz.cn/vod-play-id-51598-src-1-num-16.html"}]}, + "play": {"parse":0,"url":"https://v.cdnlz3.com/20241017/28077_d5cc9644/index.m3u8"}, + "search": {"page":1,"pagecount":10,"limit":20,"total":100,"list":[{"vod_id":"https://www.wwgz.cn/vod-detail-id-46835.html","vod_name":"斗罗大陆2:绝世唐门","vod_pic":"https://pic.lzzypic.com/upload/vod/20230624-1/2734e7689124b78ca9ad7d35132ac6a8.jpg","vod_remarks":"评分:2.0","vod_content":""},{"vod_id":"https://www.wwgz.cn/vod-detail-id-47739.html","vod_name":"斗罗大陆2绝世唐门第1季·动态漫","vod_pic":"https://pic.lzzypic.com/upload/vod/20221106-1/4674d3d918203d5027ff70100c3804bf.jpg","vod_remarks":"评分:9.0","vod_content":""},{"vod_id":"https://www.wwgz.cn/vod-detail-id-47738.html","vod_name":"斗罗大陆2绝世唐门第2季·动态漫","vod_pic":"https://pic.lzzypic.com/upload/vod/20221106-1/5d900fa007f4a56e708f29605843f689.jpg","vod_remarks":"评分:1.0","vod_content":""},{"vod_id":"https://www.wwgz.cn/vod-detail-id-47737.html","vod_name":"斗罗大陆2绝世唐门第3季·动态漫","vod_pic":"https://pic.lzzypic.com/upload/vod/20221106-1/14e8670414aec665ba3af565f44f7491.jpg","vod_remarks":"评分:1.0","vod_content":""},{"vod_id":"https://www.wwgz.cn/vod-detail-id-47728.html","vod_name":"斗罗大陆3龙王传说第1季·动态漫","vod_pic":"https://pic.lzzypic.com/upload/vod/20221120-1/6a55b5ec170b6b45534bf88fd44c0171.jpg","vod_remarks":"评分:2.0","vod_content":""},{"vod_id":"https://www.wwgz.cn/vod-detail-id-47727.html","vod_name":"斗罗大陆3龙王传说第2季·动态漫","vod_pic":"https://pic.lzzypic.com/upload/vod/20221120-1/6ea8f6dd434f45f4e002037c37085fe9.jpg","vod_remarks":"评分:3.0","vod_content":""},{"vod_id":"https://www.wwgz.cn/vod-detail-id-47687.html","vod_name":"斗罗大陆2绝世唐门第4季·动态漫","vod_pic":"https://pic.lzzypic.com/upload/vod/20221106-1/a69da5f627a363f54947d754bfb42bbc.jpg","vod_remarks":"评分:4.0","vod_content":""},{"vod_id":"https://www.wwgz.cn/vod-detail-id-47586.html","vod_name":"斗罗大陆外传神界传说 动态漫画","vod_pic":"https://pic.lzzypic.com/upload/vod/20230320-1/ada8b410331b1f2d53cbc42f4f2695f5.jpg","vod_remarks":"评分:5.0","vod_content":""},{"vod_id":"https://www.wwgz.cn/vod-detail-id-47585.html","vod_name":"斗罗大陆外传唐门英雄传 动态漫画","vod_pic":"https://pic.lzzypic.com/upload/vod/20230320-1/5d90a08ccad498f7db26bd1135a2cd27.jpg","vod_remarks":"评分:4.0","vod_content":""},{"vod_id":"https://www.wwgz.cn/vod-detail-id-47518.html","vod_name":"斗罗大陆","vod_pic":"https://pic.lzzypic.com/upload/vod/20220322-1/5383e6de7ffb6a10458baf67b3f99a10.jpg","vod_remarks":"评分:8.0","vod_content":""},{"vod_id":"https://www.wwgz.cn/vod-detail-id-47487.html","vod_name":"动态漫画·斗罗大陆4终极斗罗","vod_pic":"https://pic.lzzypic.com/upload/vod/20220815-1/d03a2595158619a87031e5934d4c1c77.jpg","vod_remarks":"评分:2.0","vod_content":""},{"vod_id":"https://www.wwgz.cn/vod-detail-id-47319.html","vod_name":"斗罗大陆2绝世唐门第五季","vod_pic":"https://youku.youkuphoto.com/upload/vod/20230521-1/d3c04e7b08ab266d1501df2c4d22d827.jpg","vod_remarks":"评分:3.0","vod_content":""},{"vod_id":"https://www.wwgz.cn/vod-detail-id-46936.html","vod_name":"动态漫画·斗罗大陆3龙王传说第三季","vod_pic":"https://img.lzzyimg.com/upload/vod/20231117-1/81de84a57bb37e72e96dcc51cf2fee95.jpg","vod_remarks":"评分:1.0","vod_content":""},{"vod_id":"https://www.wwgz.cn/vod-detail-id-32170.html","vod_name":"瑜伽妹斗罗","vod_pic":"https://img.liangzipic.com/upload/vod/20220916-1/b58e840113dd1c74ea5ec51fb8e34f4f.jpg","vod_remarks":"评分:9.0","vod_content":""},{"vod_id":"https://www.wwgz.cn/vod-detail-id-26541.html","vod_name":"斗罗大陆肖战版","vod_pic":"https://img.liangzipic.com/upload/vod/20220504-1/9679347b4a5f3d64e24aac210b563931.jpg","vod_remarks":"评分:1.0","vod_content":""}]} +} \ No newline at end of file diff --git a/dashboard/src/mock/middlewares.js b/dashboard/src/mock/middlewares.js new file mode 100644 index 0000000..bd11bde --- /dev/null +++ b/dashboard/src/mock/middlewares.js @@ -0,0 +1,12 @@ +module.exports = (request, response, next) => { + if (request.method === 'POST') { + request.method = 'GET' + request.query = request.body + } + + // 处理IE8下的文件上传 + if ((request.headers['content-type'] || '').startsWith('multipart/form-data')) { + response.header('content-type', 'text/html') + } + next() +} diff --git a/dashboard/src/stores/siteStore.js b/dashboard/src/stores/siteStore.js new file mode 100644 index 0000000..0a62ba3 --- /dev/null +++ b/dashboard/src/stores/siteStore.js @@ -0,0 +1,16 @@ +// 在 Pinia store 中,currentSite 作为状态存储 +import {defineStore} from 'pinia' + +export const useSiteStore = defineStore('site', () => { + const nowSite = JSON.parse(localStorage.getItem('site-nowSite')) || null + + // 设置当前源并持久化到 localStorage + const setCurrentSite = (site) => { + localStorage.setItem('site-nowSite', JSON.stringify(site)) // 存储到 localStorage + } + + return { + nowSite, + setCurrentSite, + } +}) \ No newline at end of file diff --git a/dashboard/src/utils/req.js b/dashboard/src/utils/req.js new file mode 100644 index 0000000..9c7b0a5 --- /dev/null +++ b/dashboard/src/utils/req.js @@ -0,0 +1,53 @@ +import axios from 'axios'; + +// 创建一个 Axios 实例 +const req = axios.create({ + baseURL: 'http://127.0.0.1:9978', // 默认的 API 根地址 + timeout: 5000, // 请求超时限制 + headers: { + 'Content-Type': 'application/json', // 默认请求头 + }, +}); + +// 请求拦截器 +req.interceptors.request.use( + (config) => { + // 可以在请求发送前做一些处理,例如添加 token + const token = localStorage.getItem('token'); // 假设 token 存储在 localStorage 中 + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; + }, + (error) => { + return Promise.reject(error); + } +); + +// 响应拦截器 +req.interceptors.response.use( + (response) => { + // 如果有需要,可以在这里统一处理响应数据 + return response.data; + }, + (error) => { + // 错误处理,例如 token 失效、网络错误等 + if (error.response) { + // 服务器返回的错误信息 + console.error(`Error ${error.response.status}: ${error.response.data.message}`); + } else { + // 网络或其他错误 + console.error('Network error or timeout', error); + } + return Promise.reject(error); + } +); + +if (process.env.NODE_ENV === 'development') { + req.defaults.baseURL = 'http://127.0.0.1:9978'; // 开发环境使用本地服务器 +} else { + req.defaults.baseURL = 'https://api.example.com'; // 生产环境使用线上 API +} + + +export default req; diff --git a/dashboard/src/views/Collection.vue b/dashboard/src/views/Collection.vue index 26546b5..0cc30f3 100644 --- a/dashboard/src/views/Collection.vue +++ b/dashboard/src/views/Collection.vue @@ -1,4 +1,8 @@ + +.header-left, +.header-right { + display: flex; + align-items: center; +} + +.header-left button, +.header-right button { + margin-right: 10px; +} + +.header-center { + flex-grow: 1; + display: flex; + justify-content: center; +} + +.header-center a-input-search { + width: 300px; +} + \ No newline at end of file diff --git a/dashboard/src/views/Live.vue b/dashboard/src/views/Live.vue index 6d3af0d..ba022e2 100644 --- a/dashboard/src/views/Live.vue +++ b/dashboard/src/views/Live.vue @@ -1,8 +1,15 @@ - + + diff --git a/dashboard/vite.config.js b/dashboard/vite.config.js index 581f299..b477621 100644 --- a/dashboard/vite.config.js +++ b/dashboard/vite.config.js @@ -1,22 +1,20 @@ import {defineConfig} from 'vite' import vue from '@vitejs/plugin-vue' -import Components from 'unplugin-vue-components/vite' -import {PrimeVueResolver} from '@primevue/auto-import-resolver' import path from 'path'; // https://vite.dev/config/ export default defineConfig({ plugins: [ vue(), - Components({ - resolvers: [ - PrimeVueResolver() - ] - }) ], + optimizeDeps: { + include: [ + '@arco-design/web-vue/es/icon' + ] + }, resolve: { alias: { '@': path.resolve(__dirname, 'src'), // 配置别名 }, }, -}) +}) \ No newline at end of file diff --git a/dashboard/yarn.lock b/dashboard/yarn.lock index 419f223..c1027d8 100644 --- a/dashboard/yarn.lock +++ b/dashboard/yarn.lock @@ -7,6 +7,27 @@ resolved "https://registry.npmmirror.com/@antfu/utils/-/utils-0.7.10.tgz" integrity sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww== +"@arco-design/color@^0.4.0": + version "0.4.0" + resolved "https://registry.npmmirror.com/@arco-design/color/-/color-0.4.0.tgz#52ddb40d318ee6df1057ca8c653cc1675023928f" + integrity sha512-s7p9MSwJgHeL8DwcATaXvWT3m2SigKpxx4JA1BGPHL4gfvaQsmQfrLBDpjOJFJuJ2jG2dMt3R3P8Pm9E65q18g== + dependencies: + color "^3.1.3" + +"@arco-design/web-vue@^2.56.3": + version "2.56.3" + resolved "https://registry.npmmirror.com/@arco-design/web-vue/-/web-vue-2.56.3.tgz#789a05836631ca4ac0b43da721502e7ff4d76344" + integrity sha512-D2CPIXRBUPcg37TFsfWROZddCWFZnIwqGpsOhOn2BhmH89UFqtBGpTxyuMdYJEwKNXunp3dVL6V69ZMmJBRPOg== + dependencies: + "@arco-design/color" "^0.4.0" + b-tween "^0.3.3" + b-validate "^1.4.4" + compute-scroll-into-view "^1.0.17" + dayjs "^1.10.3" + number-precision "^1.5.0" + resize-observer-polyfill "^1.5.1" + scroll-into-view-if-needed "^2.2.28" + "@babel/helper-string-parser@^7.25.9": version "7.25.9" resolved "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz" @@ -173,52 +194,10 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@primeuix/styled@^0.3.0": - version "0.3.0" - resolved "https://registry.npmmirror.com/@primeuix/styled/-/styled-0.3.0.tgz" - integrity sha512-XsLbmyM1u50A0EDATIHyqm5O/zOCSyNKPk4pNN8HFvEPehbsjf4tkXcRZAyaVvntSCLpV4XGAj7v5EDCQkBRlg== - dependencies: - "@primeuix/utils" "^0.3.0" - -"@primeuix/utils@^0.3.0": - version "0.3.0" - resolved "https://registry.npmmirror.com/@primeuix/utils/-/utils-0.3.0.tgz" - integrity sha512-d6ymWez1n+iqwzAVhyOTmrOHl5qnSX2oGlTy97qGuA15gLai+MQaxONHFNdDia8Q7o396v7KK9IvhAx9VET/+A== - -"@primevue/auto-import-resolver@^4.2.1": - version "4.2.1" - resolved "https://registry.npmmirror.com/@primevue/auto-import-resolver/-/auto-import-resolver-4.2.1.tgz#da3ca2a15dbfd3b21385bfa6faba76d5a9c5af08" - integrity sha512-NR2jTme+R/p9oapvysh4y8vFR6/w2ymK4lOzx+pZHHb+QGh8lJvkvJ5NWhwOhO9YD4RGOlQGKA4kcY2Z1itafA== - dependencies: - "@primevue/metadata" "4.2.1" - -"@primevue/core@4.2.1": - version "4.2.1" - resolved "https://registry.npmmirror.com/@primevue/core/-/core-4.2.1.tgz" - integrity sha512-L81TZSZU8zRznIi2g6IWwlZ5wraaE8DrNUJyxieCRCTpbSF3rSlYmhDEuzal8PfE0RuvXpRsxqedTHxz5cdqPg== - dependencies: - "@primeuix/styled" "^0.3.0" - "@primeuix/utils" "^0.3.0" - -"@primevue/icons@4.2.1": - version "4.2.1" - resolved "https://registry.npmmirror.com/@primevue/icons/-/icons-4.2.1.tgz" - integrity sha512-TOhxgkcmgBqmlHlf2x+gs4874iHopkow0gRAC5FztZTgTZQrqy8hPIA9b4O1lW7P6GOjGuVIwSH8y2lw6Q8koA== - dependencies: - "@primeuix/utils" "^0.3.0" - "@primevue/core" "4.2.1" - -"@primevue/metadata@4.2.1": - version "4.2.1" - resolved "https://registry.npmmirror.com/@primevue/metadata/-/metadata-4.2.1.tgz" - integrity sha512-XX29c2FtbXo0EX8GoYYT9os0FMxAZBPqq6VTAhbHrIUWzKnR8SVrxWoyy6G0wbzP3qXD4X3T7wUhjvQYHtTzLg== - -"@primevue/themes@^4.2.1": - version "4.2.1" - resolved "https://registry.npmmirror.com/@primevue/themes/-/themes-4.2.1.tgz" - integrity sha512-byp4YejyVdrOpRRbq5vBtaDBFHUq7Wc0aGWwII1fliYbwQ+WXn/hCAYhaXwRrwweHpTiobiWWsS+PRLWJ7fBRw== - dependencies: - "@primeuix/styled" "^0.3.0" +"@polka/url@^1.0.0-next.24": + version "1.0.0-next.28" + resolved "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.28.tgz#d45e01c4a56f143ee69c54dd6b12eade9e270a73" + integrity sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw== "@rollup/pluginutils@^5.1.0": version "5.1.3" @@ -319,6 +298,143 @@ resolved "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.4.tgz" integrity sha512-LTw1Dfd0mBIEqUVCxbvTE/LLo+9ZxVC9k99v1v4ahg9Aak6FpqOfNu5kRkeTAn0wphoC4JU7No1/rL+bBCEwhg== +"@tinyhttp/accepts@2.2.3": + version "2.2.3" + resolved "https://registry.npmmirror.com/@tinyhttp/accepts/-/accepts-2.2.3.tgz#be7601206eeda8bd8350ad82a2307808efcb7831" + integrity sha512-9pQN6pJAJOU3McmdJWTcyq7LLFW8Lj5q+DadyKcvp+sxMkEpktKX5sbfJgJuOvjk6+1xWl7pe0YL1US1vaO/1w== + dependencies: + mime "4.0.4" + negotiator "^0.6.3" + +"@tinyhttp/app@^2.4.0": + version "2.4.0" + resolved "https://registry.npmmirror.com/@tinyhttp/app/-/app-2.4.0.tgz#d91b3f36146d2dc63cf8b1492b94488ecea3cc6d" + integrity sha512-vOPiCemQRJq5twnl06dde6XnWiNbVMdVRFJWW/yC/9G0qgvV2TvzNNTxrdlz6YmyB7vIC7Fg3qS6m6gx8RbBNQ== + dependencies: + "@tinyhttp/cookie" "2.1.1" + "@tinyhttp/proxy-addr" "2.2.0" + "@tinyhttp/req" "2.2.4" + "@tinyhttp/res" "2.2.4" + "@tinyhttp/router" "2.2.3" + header-range-parser "1.1.3" + regexparam "^2.0.2" + +"@tinyhttp/content-disposition@2.2.2": + version "2.2.2" + resolved "https://registry.npmmirror.com/@tinyhttp/content-disposition/-/content-disposition-2.2.2.tgz#1207d18bdd59e1cd38ecf2493ee187f4f592ebe7" + integrity sha512-crXw1txzrS36huQOyQGYFvhTeLeG0Si1xu+/l6kXUVYpE0TjFjEZRqTbuadQLfKGZ0jaI+jJoRyqaWwxOSHW2g== + +"@tinyhttp/content-type@^0.1.4": + version "0.1.4" + resolved "https://registry.npmmirror.com/@tinyhttp/content-type/-/content-type-0.1.4.tgz#112bce3b564213e0ed43fa76fccca4237be3a634" + integrity sha512-dl6f3SHIJPYbhsW1oXdrqOmLSQF/Ctlv3JnNfXAE22kIP7FosqJHxkz/qj2gv465prG8ODKH5KEyhBkvwrueKQ== + +"@tinyhttp/cookie-signature@2.1.1": + version "2.1.1" + resolved "https://registry.npmmirror.com/@tinyhttp/cookie-signature/-/cookie-signature-2.1.1.tgz#ae2caad6aec4ec51d42e7d852ae34a04196d5138" + integrity sha512-VDsSMY5OJfQJIAtUgeQYhqMPSZptehFSfvEEtxr+4nldPA8IImlp3QVcOVuK985g4AFR4Hl1sCbWCXoqBnVWnw== + +"@tinyhttp/cookie@2.1.1": + version "2.1.1" + resolved "https://registry.npmmirror.com/@tinyhttp/cookie/-/cookie-2.1.1.tgz#50ad664f732357a466a14cdc888e88e80dc3440e" + integrity sha512-h/kL9jY0e0Dvad+/QU3efKZww0aTvZJslaHj3JTPmIPC9Oan9+kYqmh3M6L5JUQRuTJYFK2nzgL2iJtH2S+6dA== + +"@tinyhttp/cors@^2.0.1": + version "2.0.1" + resolved "https://registry.npmmirror.com/@tinyhttp/cors/-/cors-2.0.1.tgz#7f5ff7bd47b8e48172d8e43818b8d61eaa84037b" + integrity sha512-qrmo6WJuaiCzKWagv2yA/kw6hIISfF/hOqPWwmI6w0o8apeTMmRN3DoCFvQ/wNVuWVdU5J4KU7OX8aaSOEq51A== + dependencies: + "@tinyhttp/vary" "^0.1.3" + +"@tinyhttp/encode-url@2.1.1": + version "2.1.1" + resolved "https://registry.npmmirror.com/@tinyhttp/encode-url/-/encode-url-2.1.1.tgz#a52bdbd75f541455190d1a16b0a81374c8dc587d" + integrity sha512-AhY+JqdZ56qV77tzrBm0qThXORbsVjs/IOPgGCS7x/wWnsa/Bx30zDUU/jPAUcSzNOzt860x9fhdGpzdqbUeUw== + +"@tinyhttp/etag@2.1.2": + version "2.1.2" + resolved "https://registry.npmmirror.com/@tinyhttp/etag/-/etag-2.1.2.tgz#34fc91933bd1acce3cda3a64e5352ce5514abe4e" + integrity sha512-j80fPKimGqdmMh6962y+BtQsnYPVCzZfJw0HXjyH70VaJBHLKGF+iYhcKqzI3yef6QBNa8DKIPsbEYpuwApXTw== + +"@tinyhttp/forwarded@2.1.1": + version "2.1.1" + resolved "https://registry.npmmirror.com/@tinyhttp/forwarded/-/forwarded-2.1.1.tgz#dbf2cae75a1737b88b71c2a2d1931e5e9ced73c3" + integrity sha512-nO3kq0R1LRl2+CAMlnggm22zE6sT8gfvGbNvSitV6F9eaUSurHP0A8YZFMihSkugHxK+uIegh1TKrqgD8+lyGQ== + +"@tinyhttp/logger@^2.0.0": + version "2.0.0" + resolved "https://registry.npmmirror.com/@tinyhttp/logger/-/logger-2.0.0.tgz#64c0e53497abc82765c17289a05cd75edbd4c04a" + integrity sha512-8DfLQjGDIaIJeivYamVrrpmwmsGwS8wt2DGvzlcY5HEBagdiI4QJy/veAFcUHuaJqufn4wLwmn4q5VUkW8BCpQ== + dependencies: + colorette "^2.0.20" + dayjs "^1.11.10" + http-status-emojis "^2.2.0" + +"@tinyhttp/proxy-addr@2.2.0": + version "2.2.0" + resolved "https://registry.npmmirror.com/@tinyhttp/proxy-addr/-/proxy-addr-2.2.0.tgz#82487f25af4d320d79e613bbecac17c7ad69c8f2" + integrity sha512-WM/PPL9xNvrs7/8Om5nhKbke5FHrP3EfjOOR+wBnjgESfibqn0K7wdUTnzSLp1lBmemr88os1XvzwymSgaibyA== + dependencies: + "@tinyhttp/forwarded" "2.1.1" + ipaddr.js "^2.2.0" + +"@tinyhttp/req@2.2.4": + version "2.2.4" + resolved "https://registry.npmmirror.com/@tinyhttp/req/-/req-2.2.4.tgz#c9dfc40e3a3b3cc1eb48bdc137b5d0cd71501057" + integrity sha512-lQAZIAo0NOeghxFOZS57tQzxpHSPPLs9T68Krq2BncEBImKwqaDKUt7M9Y5Kb+rvC/GwIL3LeErhkg7f5iG4IQ== + dependencies: + "@tinyhttp/accepts" "2.2.3" + "@tinyhttp/type-is" "2.2.4" + "@tinyhttp/url" "2.1.1" + header-range-parser "^1.1.3" + +"@tinyhttp/res@2.2.4": + version "2.2.4" + resolved "https://registry.npmmirror.com/@tinyhttp/res/-/res-2.2.4.tgz#ed79d511f21d6ef226ae907bdecc26f69c93583c" + integrity sha512-ETBRShnO19oJyIg2XQHQoofXPWeTXPAuwnIVYkU8WaftvXd/Vz4y5+WFQDHUzKlmdGOw5fAFnrEU7pIVMeFeVA== + dependencies: + "@tinyhttp/content-disposition" "2.2.2" + "@tinyhttp/cookie" "2.1.1" + "@tinyhttp/cookie-signature" "2.1.1" + "@tinyhttp/encode-url" "2.1.1" + "@tinyhttp/req" "2.2.4" + "@tinyhttp/send" "2.2.3" + "@tinyhttp/vary" "^0.1.3" + es-escape-html "^0.1.1" + mime "4.0.4" + +"@tinyhttp/router@2.2.3": + version "2.2.3" + resolved "https://registry.npmmirror.com/@tinyhttp/router/-/router-2.2.3.tgz#a29a33da89ae7365f5897aed20f44663509273a4" + integrity sha512-O0MQqWV3Vpg/uXsMYg19XsIgOhwjyhTYWh51Qng7bxqXixxx2PEvZWnFjP7c84K7kU/nUX41KpkEBTLnznk9/Q== + +"@tinyhttp/send@2.2.3": + version "2.2.3" + resolved "https://registry.npmmirror.com/@tinyhttp/send/-/send-2.2.3.tgz#726c400af76c62963bd71fe92e6e6838f35a7996" + integrity sha512-o4cVHHGQ8WjVBS8UT0EE/2WnjoybrfXikHwsRoNlG1pfrC/Sd01u1N4Te8cOd/9aNGLr4mGxWb5qTm2RRtEi7g== + dependencies: + "@tinyhttp/content-type" "^0.1.4" + "@tinyhttp/etag" "2.1.2" + mime "4.0.4" + +"@tinyhttp/type-is@2.2.4": + version "2.2.4" + resolved "https://registry.npmmirror.com/@tinyhttp/type-is/-/type-is-2.2.4.tgz#8f5a30bb3cdc93dd02f399e152d88b815b0efc99" + integrity sha512-7F328NheridwjIfefBB2j1PEcKKABpADgv7aCJaE8x8EON77ZFrAkI3Rir7pGjopV7V9MBmW88xUQigBEX2rmQ== + dependencies: + "@tinyhttp/content-type" "^0.1.4" + mime "4.0.4" + +"@tinyhttp/url@2.1.1": + version "2.1.1" + resolved "https://registry.npmmirror.com/@tinyhttp/url/-/url-2.1.1.tgz#77fa8963f5b698bacbdc6912407f946d32c793e1" + integrity sha512-POJeq2GQ5jI7Zrdmj22JqOijB5/GeX+LEX7DUdml1hUnGbJOTWDx7zf2b5cCERj7RoXL67zTgyzVblBJC+NJWg== + +"@tinyhttp/vary@^0.1.3": + version "0.1.3" + resolved "https://registry.npmmirror.com/@tinyhttp/vary/-/vary-0.1.3.tgz#f5bea4769f380c43a158832a8daad8e8b186757c" + integrity sha512-SoL83sQXAGiHN1jm2VwLUWQSQeDAAl1ywOm6T0b0Cg1CZhVsjoiZadmjhxF6FHCCY7OHHVaLnTgSMxTPIDLxMg== + "@types/estree@1.0.6", "@types/estree@^1.0.0": version "1.0.6" resolved "https://registry.npmmirror.com/@types/estree/-/estree-1.0.6.tgz" @@ -427,6 +543,30 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +axios@^1.7.7: + version "1.7.7" + resolved "https://registry.npmmirror.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" + integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +b-tween@^0.3.3: + version "0.3.3" + resolved "https://registry.npmmirror.com/b-tween/-/b-tween-0.3.3.tgz#7a93ed199c98cd41a33ba4c711a0fa7e86db3fa2" + integrity sha512-oEHegcRpA7fAuc9KC4nktucuZn2aS8htymCPcP3qkEGPqiBH+GfqtqoG2l7LxHngg6O0HFM7hOeOYExl1Oz4ZA== + +b-validate@^1.4.4: + version "1.5.3" + resolved "https://registry.npmmirror.com/b-validate/-/b-validate-1.5.3.tgz#f6ac83b70caccbabf1c2eee42a0739bd228f79e6" + integrity sha512-iCvCkGFskbaYtfQ0a3GmcQCHl/Sv1GufXFGuUQ+FE+WJa7A/espLOuFIn09B944V8/ImPj71T4+rTASxO2PAuA== + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz" @@ -451,6 +591,11 @@ braces@^3.0.3, braces@~3.0.2: dependencies: fill-range "^7.1.1" +chalk@^5.3.0: + version "5.3.0" + resolved "https://registry.npmmirror.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + chokidar@^3.6.0: version "3.6.0" resolved "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz" @@ -466,6 +611,63 @@ chokidar@^3.6.0: optionalDependencies: fsevents "~2.3.2" +chokidar@^4.0.1: + version "4.0.1" + resolved "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.1.tgz#4a6dff66798fb0f72a94f616abbd7e1a19f31d41" + integrity sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA== + dependencies: + readdirp "^4.0.1" + +color-convert@^1.9.3: + version "1.9.3" + resolved "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@^1.0.0: + version "1.1.4" + resolved "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.6.0: + version "1.9.1" + resolved "https://registry.npmmirror.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^3.1.3: + version "3.2.1" + resolved "https://registry.npmmirror.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" + integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== + dependencies: + color-convert "^1.9.3" + color-string "^1.6.0" + +colorette@^2.0.20: + version "2.0.20" + resolved "https://registry.npmmirror.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +compute-scroll-into-view@^1.0.17, compute-scroll-into-view@^1.0.20: + version "1.0.20" + resolved "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz#1768b5522d1172754f5d0c9b02de3af6be506a43" + integrity sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg== + confbox@^0.1.8: version "0.1.8" resolved "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz" @@ -476,6 +678,11 @@ csstype@^3.1.3: resolved "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== +dayjs@^1.10.3, dayjs@^1.11.10: + version "1.11.13" + resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c" + integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== + debug@^4.3.6: version "4.3.7" resolved "https://registry.npmmirror.com/debug/-/debug-4.3.7.tgz" @@ -483,11 +690,28 @@ debug@^4.3.6: dependencies: ms "^2.1.3" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +dot-prop@^9.0.0: + version "9.0.0" + resolved "https://registry.npmmirror.com/dot-prop/-/dot-prop-9.0.0.tgz#bae5982fe6dc6b8fddb92efef4f2ddff26779e92" + integrity sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ== + dependencies: + type-fest "^4.18.2" + entities@^4.5.0: version "4.5.0" resolved "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== +es-escape-html@^0.1.1: + version "0.1.1" + resolved "https://registry.npmmirror.com/es-escape-html/-/es-escape-html-0.1.1.tgz#9a582d49754ec6204524952c76a383fe5f03c1c0" + integrity sha512-yUx1o+8RsG7UlszmYPtks+dm6Lho2m8lgHMOsLJQsFI0R8XwUJwiMhM1M4E/S8QLeGyf6MkDV/pWgjQ0tdTSyQ== + esbuild@^0.21.3: version "0.21.5" resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz" @@ -522,6 +746,11 @@ estree-walker@^2.0.2: resolved "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== +eta@^3.5.0: + version "3.5.0" + resolved "https://registry.npmmirror.com/eta/-/eta-3.5.0.tgz#b728b2d4aa3cbce9d08db638719a60b31d2b0ccf" + integrity sha512-e3x3FBvGzeCIHhF+zhK8FZA2vC5uFn6b4HJjegUbIWrDb4mJ7JjTGMJY9VGIbRVpmSwHopNiaJibhjIr+HfLug== + fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.2.tgz" @@ -547,6 +776,20 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" +follow-redirects@^1.15.6: + version "1.15.9" + resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + +form-data@^4.0.0: + version "4.0.1" + resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" + integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" @@ -559,6 +802,31 @@ glob-parent@^5.1.2, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" +header-range-parser@1.1.3, header-range-parser@^1.1.3: + version "1.1.3" + resolved "https://registry.npmmirror.com/header-range-parser/-/header-range-parser-1.1.3.tgz#6414b5b12e3b645d29d85225a58fd207d66d30ef" + integrity sha512-B9zCFt3jH8g09LR1vHL4pcAn8yMEtlSlOUdQemzHMRKMImNIhhszdeosYFfNW0WXKQtXIlWB+O4owHJKvEJYaA== + +http-status-emojis@^2.2.0: + version "2.2.0" + resolved "https://registry.npmmirror.com/http-status-emojis/-/http-status-emojis-2.2.0.tgz#2cf3316f0c1610c4fc94c6fccdada35aa70f992a" + integrity sha512-ompKtgwpx8ff0hsbpIB7oE4ax1LXoHmftsHHStMELX56ivG3GhofTX8ZHWlUaFKfGjcGjw6G3rPk7dJRXMmbbg== + +inflection@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/inflection/-/inflection-3.0.0.tgz#6a956fa90d72a27d22e6b32ec1064877593ee23b" + integrity sha512-1zEJU1l19SgJlmwqsEyFTbScw/tkMHFenUo//Y0i+XEP83gDFdMvPizAD/WGcE+l1ku12PcTVHQhO6g5E0UCMw== + +ipaddr.js@^2.2.0: + version "2.2.0" + resolved "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" + integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz" @@ -583,6 +851,30 @@ is-number@^7.0.0: resolved "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +json-server@^1.0.0-beta.3: + version "1.0.0-beta.3" + resolved "https://registry.npmmirror.com/json-server/-/json-server-1.0.0-beta.3.tgz#dfbbc2cd3fd9faea419586ae045c00a8fd6084ca" + integrity sha512-DwE69Ep5ccwIJZBUIWEENC30Yj8bwr4Ax9W9VoIWAYnB8Sj4ReptscO8/DRHv/nXwVlmb3Bk73Ls86+VZdYkkA== + dependencies: + "@tinyhttp/app" "^2.4.0" + "@tinyhttp/cors" "^2.0.1" + "@tinyhttp/logger" "^2.0.0" + chalk "^5.3.0" + chokidar "^4.0.1" + dot-prop "^9.0.0" + eta "^3.5.0" + inflection "^3.0.0" + json5 "^2.2.3" + lowdb "^7.0.1" + milliparsec "^4.0.0" + sirv "^2.0.4" + sort-on "^6.1.0" + +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + local-pkg@^0.5.0: version "0.5.0" resolved "https://registry.npmmirror.com/local-pkg/-/local-pkg-0.5.0.tgz" @@ -591,6 +883,13 @@ local-pkg@^0.5.0: mlly "^1.4.2" pkg-types "^1.0.3" +lowdb@^7.0.1: + version "7.0.1" + resolved "https://registry.npmmirror.com/lowdb/-/lowdb-7.0.1.tgz#7354a684547d76206b1c730b9434604235b125e5" + integrity sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw== + dependencies: + steno "^4.0.2" + magic-string@^0.30.11: version "0.30.12" resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.12.tgz" @@ -611,6 +910,28 @@ micromatch@^4.0.4: braces "^3.0.3" picomatch "^2.3.1" +milliparsec@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/milliparsec/-/milliparsec-4.0.0.tgz#ba5169aaa4922587c92f22878df9c85730a0d893" + integrity sha512-/wk9d4Z6/9ZvoEH/6BI4TrTCgmkpZPuSRN/6fI9aUHOfXdNTuj/VhLS7d+NqG26bi6L9YmGXutVYvWC8zQ0qtA== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@4.0.4: + version "4.0.4" + resolved "https://registry.npmmirror.com/mime/-/mime-4.0.4.tgz#9f851b0fc3c289d063b20a7a8055b3014b25664b" + integrity sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ== + minimatch@^9.0.5: version "9.0.5" resolved "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz" @@ -628,6 +949,11 @@ mlly@^1.4.2, mlly@^1.7.1, mlly@^1.7.2: pkg-types "^1.2.0" ufo "^1.5.4" +mrmime@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/mrmime/-/mrmime-2.0.0.tgz#151082a6e06e59a9a39b46b3e14d5cfe92b3abb4" + integrity sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw== + ms@^2.1.3: version "2.1.3" resolved "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz" @@ -638,11 +964,21 @@ nanoid@^3.3.7: resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz" integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== +negotiator@^0.6.3: + version "0.6.4" + resolved "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" + integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +number-precision@^1.5.0: + version "1.6.0" + resolved "https://registry.npmmirror.com/number-precision/-/number-precision-1.6.0.tgz#e309d28f80871d36ac9f6ecd974e13afb1ec0de0" + integrity sha512-05OLPgbgmnixJw+VvEh18yNPUo3iyp4BEWJcrLu4X9W05KmMifN7Mu5exYvQXqxxeNWhvIF+j3Rij+HmddM/hQ== + pathe@^1.1.2: version "1.1.2" resolved "https://registry.npmmirror.com/pathe/-/pathe-1.1.2.tgz" @@ -689,31 +1025,21 @@ postcss@^8.4.43, postcss@^8.4.47: picocolors "^1.1.0" source-map-js "^1.2.1" -primeflex@^3.3.1: - version "3.3.1" - resolved "https://registry.npmmirror.com/primeflex/-/primeflex-3.3.1.tgz#361dddf6eb5db50d733e4cddd4b6e376a3d7bd68" - integrity sha512-zaOq3YvcOYytbAmKv3zYc+0VNS9Wg5d37dfxZnveKBFPr7vEIwfV5ydrpiouTft8MVW6qNjfkaQphHSnvgQbpQ== - -primeicons@^7.0.0: - version "7.0.0" - resolved "https://registry.npmmirror.com/primeicons/-/primeicons-7.0.0.tgz#6b25c3fdcb29bb745a3035bdc1ed5902f4a419cf" - integrity sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw== - -primevue@^4.2.1: - version "4.2.1" - resolved "https://registry.npmmirror.com/primevue/-/primevue-4.2.1.tgz#31c206b60ea9fef4c3f069b49a92e8cce3ad0498" - integrity sha512-cU9ZQVq9fitsQEIrfGeIl7xELBn61JCMxWkzcS9dkr165g29AvUrUNS9ufs1t2NoMJzE8VllwzweF/tSFAr2cw== - dependencies: - "@primeuix/styled" "^0.3.0" - "@primeuix/utils" "^0.3.0" - "@primevue/core" "4.2.1" - "@primevue/icons" "4.2.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +readdirp@^4.0.1: + version "4.0.2" + resolved "https://registry.npmmirror.com/readdirp/-/readdirp-4.0.2.tgz#388fccb8b75665da3abffe2d8f8ed59fe74c230a" + integrity sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA== + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz" @@ -721,6 +1047,16 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +regexparam@^2.0.2: + version "2.0.2" + resolved "https://registry.npmmirror.com/regexparam/-/regexparam-2.0.2.tgz#a0f6aa057c67b1c9c09508c45823c0755b1f6e58" + integrity sha512-A1PeDEYMrkLrfyOwv2jwihXbo9qxdGD3atBYQA9JJgreAx8/7rC6IUkWOw2NQlOxLp2wL0ifQbh1HuidDfYA6w== + +resize-observer-polyfill@^1.5.1: + version "1.5.1" + resolved "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + reusify@^1.0.4: version "1.0.4" resolved "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz" @@ -760,11 +1096,46 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +scroll-into-view-if-needed@^2.2.28: + version "2.2.31" + resolved "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz#d3c482959dc483e37962d1521254e3295d0d1587" + integrity sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA== + dependencies: + compute-scroll-into-view "^1.0.20" + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== + dependencies: + is-arrayish "^0.3.1" + +sirv@^2.0.4: + version "2.0.4" + resolved "https://registry.npmmirror.com/sirv/-/sirv-2.0.4.tgz#5dd9a725c578e34e449f332703eb2a74e46a29b0" + integrity sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ== + dependencies: + "@polka/url" "^1.0.0-next.24" + mrmime "^2.0.0" + totalist "^3.0.0" + +sort-on@^6.1.0: + version "6.1.0" + resolved "https://registry.npmmirror.com/sort-on/-/sort-on-6.1.0.tgz#96899b3536e4b2694090842f2e766ba11ccc7459" + integrity sha512-WTECP0nYNWO1n2g5bpsV0yZN9cBmZsF8ThHFbOqVN0HBFRoaQZLLEMvMmJlKHNPYQeVngeI5+jJzIfFqOIo1OA== + dependencies: + dot-prop "^9.0.0" + source-map-js@^1.2.0, source-map-js@^1.2.1: version "1.2.1" resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== +steno@^4.0.2: + version "4.0.2" + resolved "https://registry.npmmirror.com/steno/-/steno-4.0.2.tgz#9bd9b0ffc226a1f9436f29132c8b8e7199d22c50" + integrity sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A== + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz" @@ -772,6 +1143,16 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +totalist@^3.0.0: + version "3.0.1" + resolved "https://registry.npmmirror.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" + integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== + +type-fest@^4.18.2: + version "4.26.1" + resolved "https://registry.npmmirror.com/type-fest/-/type-fest-4.26.1.tgz#a4a17fa314f976dd3e6d6675ef6c775c16d7955e" + integrity sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg== + ufo@^1.5.4: version "1.5.4" resolved "https://registry.npmmirror.com/ufo/-/ufo-1.5.4.tgz" diff --git a/readme.md b/readme.md index f804231..bb24947 100644 --- a/readme.md +++ b/readme.md @@ -3,8 +3,15 @@ yarn create vite yarn add primevue primeicons yarn add unplugin-vue-components yarn add @primevue/auto-import-resolver +yarn add @primevue/themes yarn add primeflex + yarn add vue-router yarn add pinia + +yarn remove primevue primeicons @primevue/auto-import-resolver primeflex @primevue/themes +yarn add --dev @arco-design/web-vue +yarn add json-server +yarn add axios ``` https://juejin.cn/post/7387581121519812617 \ No newline at end of file From cf83c8ea14a8a10324951f6d7f1ff565aa43ec46 Mon Sep 17 00:00:00 2001 From: MuzzyZY Date: Wed, 6 Nov 2024 18:33:59 +0800 Subject: [PATCH 002/199] =?UTF-8?q?Update:=E5=85=A8=E9=83=A8=E9=87=87?= =?UTF-8?q?=E7=94=A8arco=E9=87=8D=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- readme.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index bb24947..c03a089 100644 --- a/readme.md +++ b/readme.md @@ -14,4 +14,6 @@ yarn add --dev @arco-design/web-vue yarn add json-server yarn add axios ``` -https://juejin.cn/post/7387581121519812617 \ No newline at end of file +https://juejin.cn/post/7387581121519812617 + +[arco组件库](https://arco.design/vue/component/layout) \ No newline at end of file From 56bd88533042e7e967b0a1b9abb1cc95a73b1c47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=99=9A=E9=A3=8E=E6=8B=82=E6=9F=B3=E9=A2=9C?= <434857005@qq.com> Date: Thu, 7 Nov 2024 01:12:52 +0800 Subject: [PATCH 003/199] =?UTF-8?q?Update:=E7=BE=8E=E5=8C=96=E5=9B=BE?= =?UTF-8?q?=E6=A0=87=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard/package.json | 7 +- dashboard/src/assets/icon-font/demo.css | 539 +++++++++ .../src/assets/icon-font/demo_index.html | 970 +++++++++++++++ dashboard/src/assets/icon-font/iconfont.css | 151 +++ dashboard/src/assets/icon-font/iconfont.js | 1 + dashboard/src/assets/icon-font/iconfont.json | 247 ++++ dashboard/src/assets/icon-font/iconfont.ttf | Bin 0 -> 19148 bytes dashboard/src/assets/icon-font/iconfont.woff | Bin 0 -> 6936 bytes dashboard/src/assets/icon-font/iconfont.woff2 | Bin 0 -> 5804 bytes dashboard/src/assets/logo.png | Bin 0 -> 6603 bytes dashboard/src/components/Header.vue | 43 +- dashboard/src/components/Layout.vue | 55 +- dashboard/src/router/index.js | 4 + dashboard/src/views/Parser.vue | 23 + dashboard/src/views/Reader.vue | 23 + dashboard/src/views/Video.vue | 4 +- dashboard/yarn.lock | 1053 ++++++++++++----- readme.md | 19 +- 18 files changed, 2840 insertions(+), 299 deletions(-) create mode 100644 dashboard/src/assets/icon-font/demo.css create mode 100644 dashboard/src/assets/icon-font/demo_index.html create mode 100644 dashboard/src/assets/icon-font/iconfont.css create mode 100644 dashboard/src/assets/icon-font/iconfont.js create mode 100644 dashboard/src/assets/icon-font/iconfont.json create mode 100644 dashboard/src/assets/icon-font/iconfont.ttf create mode 100644 dashboard/src/assets/icon-font/iconfont.woff create mode 100644 dashboard/src/assets/icon-font/iconfont.woff2 create mode 100644 dashboard/src/assets/logo.png create mode 100644 dashboard/src/views/Parser.vue create mode 100644 dashboard/src/views/Reader.vue diff --git a/dashboard/package.json b/dashboard/package.json index 8031e82..0662ce6 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -1,17 +1,16 @@ { "name": "dashboard", "private": true, - "version": "0.0.0", - "type": "module", + "version": "0.0.1", "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview", - "mock": "json-server src/mock/data.json --port 9978 --middlewares src/mock/middlewares.js" + "mock": "json-server src/mock/data.json --host 127.0.0.1 --port 9978 --middlewares src/mock/middlewares.js" }, "dependencies": { "axios": "^1.7.7", - "json-server": "^1.0.0-beta.3", + "json-server": "^0.17.4", "pinia": "^2.2.6", "vue": "^3.5.12", "vue-router": "^4.4.5" diff --git a/dashboard/src/assets/icon-font/demo.css b/dashboard/src/assets/icon-font/demo.css new file mode 100644 index 0000000..a67054a --- /dev/null +++ b/dashboard/src/assets/icon-font/demo.css @@ -0,0 +1,539 @@ +/* Logo 字体 */ +@font-face { + font-family: "iconfont logo"; + src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834'); + src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'), + url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg'); +} + +.logo { + font-family: "iconfont logo"; + font-size: 160px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* tabs */ +.nav-tabs { + position: relative; +} + +.nav-tabs .nav-more { + position: absolute; + right: 0; + bottom: 0; + height: 42px; + line-height: 42px; + color: #666; +} + +#tabs { + border-bottom: 1px solid #eee; +} + +#tabs li { + cursor: pointer; + width: 100px; + height: 40px; + line-height: 40px; + text-align: center; + font-size: 16px; + border-bottom: 2px solid transparent; + position: relative; + z-index: 1; + margin-bottom: -1px; + color: #666; +} + + +#tabs .active { + border-bottom-color: #f00; + color: #222; +} + +.tab-container .content { + display: none; +} + +/* 页面布局 */ +.main { + padding: 30px 100px; + width: 960px; + margin: 0 auto; +} + +.main .logo { + color: #333; + text-align: left; + margin-bottom: 30px; + line-height: 1; + height: 110px; + margin-top: -50px; + overflow: hidden; + *zoom: 1; +} + +.main .logo a { + font-size: 160px; + color: #333; +} + +.helps { + margin-top: 40px; +} + +.helps pre { + padding: 20px; + margin: 10px 0; + border: solid 1px #e7e1cd; + background-color: #fffdef; + overflow: auto; +} + +.icon_lists { + width: 100% !important; + overflow: hidden; + *zoom: 1; +} + +.icon_lists li { + width: 100px; + margin-bottom: 10px; + margin-right: 20px; + text-align: center; + list-style: none !important; + cursor: default; +} + +.icon_lists li .code-name { + line-height: 1.2; +} + +.icon_lists .icon { + display: block; + height: 100px; + line-height: 100px; + font-size: 42px; + margin: 10px auto; + color: #333; + -webkit-transition: font-size 0.25s linear, width 0.25s linear; + -moz-transition: font-size 0.25s linear, width 0.25s linear; + transition: font-size 0.25s linear, width 0.25s linear; +} + +.icon_lists .icon:hover { + font-size: 100px; +} + +.icon_lists .svg-icon { + /* 通过设置 font-size 来改变图标大小 */ + width: 1em; + /* 图标和文字相邻时,垂直对齐 */ + vertical-align: -0.15em; + /* 通过设置 color 来改变 SVG 的颜色/fill */ + fill: currentColor; + /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示 + normalize.css 中也包含这行 */ + overflow: hidden; +} + +.icon_lists li .name, +.icon_lists li .code-name { + color: #666; +} + +/* markdown 样式 */ +.markdown { + color: #666; + font-size: 14px; + line-height: 1.8; +} + +.highlight { + line-height: 1.5; +} + +.markdown img { + vertical-align: middle; + max-width: 100%; +} + +.markdown h1 { + color: #404040; + font-weight: 500; + line-height: 40px; + margin-bottom: 24px; +} + +.markdown h2, +.markdown h3, +.markdown h4, +.markdown h5, +.markdown h6 { + color: #404040; + margin: 1.6em 0 0.6em 0; + font-weight: 500; + clear: both; +} + +.markdown h1 { + font-size: 28px; +} + +.markdown h2 { + font-size: 22px; +} + +.markdown h3 { + font-size: 16px; +} + +.markdown h4 { + font-size: 14px; +} + +.markdown h5 { + font-size: 12px; +} + +.markdown h6 { + font-size: 12px; +} + +.markdown hr { + height: 1px; + border: 0; + background: #e9e9e9; + margin: 16px 0; + clear: both; +} + +.markdown p { + margin: 1em 0; +} + +.markdown>p, +.markdown>blockquote, +.markdown>.highlight, +.markdown>ol, +.markdown>ul { + width: 80%; +} + +.markdown ul>li { + list-style: circle; +} + +.markdown>ul li, +.markdown blockquote ul>li { + margin-left: 20px; + padding-left: 4px; +} + +.markdown>ul li p, +.markdown>ol li p { + margin: 0.6em 0; +} + +.markdown ol>li { + list-style: decimal; +} + +.markdown>ol li, +.markdown blockquote ol>li { + margin-left: 20px; + padding-left: 4px; +} + +.markdown code { + margin: 0 3px; + padding: 0 5px; + background: #eee; + border-radius: 3px; +} + +.markdown strong, +.markdown b { + font-weight: 600; +} + +.markdown>table { + border-collapse: collapse; + border-spacing: 0px; + empty-cells: show; + border: 1px solid #e9e9e9; + width: 95%; + margin-bottom: 24px; +} + +.markdown>table th { + white-space: nowrap; + color: #333; + font-weight: 600; +} + +.markdown>table th, +.markdown>table td { + border: 1px solid #e9e9e9; + padding: 8px 16px; + text-align: left; +} + +.markdown>table th { + background: #F7F7F7; +} + +.markdown blockquote { + font-size: 90%; + color: #999; + border-left: 4px solid #e9e9e9; + padding-left: 0.8em; + margin: 1em 0; +} + +.markdown blockquote p { + margin: 0; +} + +.markdown .anchor { + opacity: 0; + transition: opacity 0.3s ease; + margin-left: 8px; +} + +.markdown .waiting { + color: #ccc; +} + +.markdown h1:hover .anchor, +.markdown h2:hover .anchor, +.markdown h3:hover .anchor, +.markdown h4:hover .anchor, +.markdown h5:hover .anchor, +.markdown h6:hover .anchor { + opacity: 1; + display: inline-block; +} + +.markdown>br, +.markdown>p>br { + clear: both; +} + + +.hljs { + display: block; + background: white; + padding: 0.5em; + color: #333333; + overflow-x: auto; +} + +.hljs-comment, +.hljs-meta { + color: #969896; +} + +.hljs-string, +.hljs-variable, +.hljs-template-variable, +.hljs-strong, +.hljs-emphasis, +.hljs-quote { + color: #df5000; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-type { + color: #a71d5d; +} + +.hljs-literal, +.hljs-symbol, +.hljs-bullet, +.hljs-attribute { + color: #0086b3; +} + +.hljs-section, +.hljs-name { + color: #63a35c; +} + +.hljs-tag { + color: #333333; +} + +.hljs-title, +.hljs-attr, +.hljs-selector-id, +.hljs-selector-class, +.hljs-selector-attr, +.hljs-selector-pseudo { + color: #795da3; +} + +.hljs-addition { + color: #55a532; + background-color: #eaffea; +} + +.hljs-deletion { + color: #bd2c00; + background-color: #ffecec; +} + +.hljs-link { + text-decoration: underline; +} + +/* 代码高亮 */ +/* PrismJS 1.15.0 +https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ +code[class*="language-"], +pre[class*="language-"] { + color: black; + background: none; + text-shadow: 0 1px white; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, +pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, +code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; +} + +pre[class*="language-"]::selection, +pre[class*="language-"] ::selection, +code[class*="language-"]::selection, +code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; +} + +@media print { + + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; +} + +:not(pre)>code[class*="language-"], +pre[class*="language-"] { + background: #f5f2f0; +} + +/* Inline code */ +:not(pre)>code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.namespace { + opacity: .7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #9a6e3a; + background: hsla(0, 0%, 100%, .5); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function, +.token.class-name { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/dashboard/src/assets/icon-font/demo_index.html b/dashboard/src/assets/icon-font/demo_index.html new file mode 100644 index 0000000..23b6898 --- /dev/null +++ b/dashboard/src/assets/icon-font/demo_index.html @@ -0,0 +1,970 @@ + + + + + iconfont Demo + + + + + + + + + + + + + +
+

+ + +

+ +
+
+
    + +
  • + +
    2组书室书架
    +
    &#xe608;
    +
  • + +
  • + +
    域名解析
    +
    &#xe600;
    +
  • + +
  • + +
    电视直播
    +
    &#xe62e;
    +
  • + +
  • + +
    历史
    +
    &#xe62f;
    +
  • + +
  • + +
    点播
    +
    &#xe770;
    +
  • + +
  • + +
    首页
    +
    &#xe611;
    +
  • + +
  • + +
    播放
    +
    &#xe612;
    +
  • + +
  • + +
    定制
    +
    &#xe613;
    +
  • + +
  • + +
    更多
    +
    &#xe614;
    +
  • + +
  • + +
    标签
    +
    &#xe615;
    +
  • + +
  • + +
    保险
    +
    &#xe616;
    +
  • + +
  • + +
    保存
    +
    &#xe617;
    +
  • + +
  • + +
    颜色
    +
    &#xe618;
    +
  • + +
  • + +
    数据
    +
    &#xe619;
    +
  • + +
  • + +
    挂号
    +
    &#xe61a;
    +
  • + +
  • + +
    定位
    +
    &#xe61b;
    +
  • + +
  • + +
    钱包
    +
    &#xe61c;
    +
  • + +
  • + +
    复制
    +
    &#xe61d;
    +
  • + +
  • + +
    礼物
    +
    &#xe61e;
    +
  • + +
  • + +
    分享
    +
    &#xe61f;
    +
  • + +
  • + +
    购物
    +
    &#xe620;
    +
  • + +
  • + +
    资质
    +
    &#xe621;
    +
  • + +
  • + +
    收藏
    +
    &#xe622;
    +
  • + +
  • + +
    药箱
    +
    &#xe623;
    +
  • + +
  • + +
    安全
    +
    &#xe624;
    +
  • + +
  • + +
    删除
    +
    &#xe625;
    +
  • + +
  • + +
    邮件
    +
    &#xe626;
    +
  • + +
  • + +
    设置
    +
    &#xe627;
    +
  • + +
  • + +
    完成
    +
    &#xe628;
    +
  • + +
  • + +
    应用
    +
    &#xe629;
    +
  • + +
  • + +
    相机
    +
    &#xe62a;
    +
  • + +
  • + +
    扫描
    +
    &#xe62b;
    +
  • + +
  • + +
    通知
    +
    &#xe62c;
    +
  • + +
  • + +
    转盘
    +
    &#xe62d;
    +
  • + +
+
+

Unicode 引用

+
+ +

Unicode 是字体在网页端最原始的应用方式,特点是:

+
    +
  • 支持按字体的方式去动态调整图标大小,颜色等等。
  • +
  • 默认情况下不支持多色,直接添加多色图标会自动去色。
  • +
+
+

注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)

+
+

Unicode 使用步骤如下:

+

第一步:拷贝项目下面生成的 @font-face

+
@font-face {
+  font-family: 'iconfont';
+  src: url('iconfont.woff2?t=1730912986793') format('woff2'),
+       url('iconfont.woff?t=1730912986793') format('woff'),
+       url('iconfont.ttf?t=1730912986793') format('truetype');
+}
+
+

第二步:定义使用 iconfont 的样式

+
.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+

第三步:挑选相应图标并获取字体编码,应用于页面

+
+<span class="iconfont">&#x33;</span>
+
+
+

"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

+
+
+
+
+
    + +
  • + +
    + 2组书室书架 +
    +
    .icon-2zushushishujia +
    +
  • + +
  • + +
    + 域名解析 +
    +
    .icon-yumingjiexi +
    +
  • + +
  • + +
    + 电视直播 +
    +
    .icon-dianshizhibo +
    +
  • + +
  • + +
    + 历史 +
    +
    .icon-lishi +
    +
  • + +
  • + +
    + 点播 +
    +
    .icon-dianbo +
    +
  • + +
  • + +
    + 首页 +
    +
    .icon-shouye +
    +
  • + +
  • + +
    + 播放 +
    +
    .icon-bofang +
    +
  • + +
  • + +
    + 定制 +
    +
    .icon-dingzhi +
    +
  • + +
  • + +
    + 更多 +
    +
    .icon-gengduo +
    +
  • + +
  • + +
    + 标签 +
    +
    .icon-biaoqian +
    +
  • + +
  • + +
    + 保险 +
    +
    .icon-baoxian +
    +
  • + +
  • + +
    + 保存 +
    +
    .icon-baocun +
    +
  • + +
  • + +
    + 颜色 +
    +
    .icon-yanse +
    +
  • + +
  • + +
    + 数据 +
    +
    .icon-shuju +
    +
  • + +
  • + +
    + 挂号 +
    +
    .icon-guahao +
    +
  • + +
  • + +
    + 定位 +
    +
    .icon-dingwei +
    +
  • + +
  • + +
    + 钱包 +
    +
    .icon-qianbao +
    +
  • + +
  • + +
    + 复制 +
    +
    .icon-fuzhi +
    +
  • + +
  • + +
    + 礼物 +
    +
    .icon-liwu +
    +
  • + +
  • + +
    + 分享 +
    +
    .icon-fenxiang +
    +
  • + +
  • + +
    + 购物 +
    +
    .icon-gouwu +
    +
  • + +
  • + +
    + 资质 +
    +
    .icon-zizhi +
    +
  • + +
  • + +
    + 收藏 +
    +
    .icon-shoucang +
    +
  • + +
  • + +
    + 药箱 +
    +
    .icon-yaoxiang +
    +
  • + +
  • + +
    + 安全 +
    +
    .icon-anquan +
    +
  • + +
  • + +
    + 删除 +
    +
    .icon-shanchu +
    +
  • + +
  • + +
    + 邮件 +
    +
    .icon-youjian +
    +
  • + +
  • + +
    + 设置 +
    +
    .icon-shezhi +
    +
  • + +
  • + +
    + 完成 +
    +
    .icon-wancheng +
    +
  • + +
  • + +
    + 应用 +
    +
    .icon-yingyong +
    +
  • + +
  • + +
    + 相机 +
    +
    .icon-xiangji +
    +
  • + +
  • + +
    + 扫描 +
    +
    .icon-saomiao +
    +
  • + +
  • + +
    + 通知 +
    +
    .icon-tongzhi +
    +
  • + +
  • + +
    + 转盘 +
    +
    .icon-zhuanpan +
    +
  • + +
+
+

font-class 引用

+
+ +

font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。

+

与 Unicode 使用方式相比,具有如下特点:

+
    +
  • 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
  • +
  • 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
  • +
+

使用步骤如下:

+

第一步:引入项目下面生成的 fontclass 代码:

+
<link rel="stylesheet" href="./iconfont.css">
+
+

第二步:挑选相应图标并获取类名,应用于页面:

+
<span class="iconfont icon-xxx"></span>
+
+
+

" + iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

+
+
+
+
+
    + +
  • + +
    2组书室书架
    +
    #icon-2zushushishujia
    +
  • + +
  • + +
    域名解析
    +
    #icon-yumingjiexi
    +
  • + +
  • + +
    电视直播
    +
    #icon-dianshizhibo
    +
  • + +
  • + +
    历史
    +
    #icon-lishi
    +
  • + +
  • + +
    点播
    +
    #icon-dianbo
    +
  • + +
  • + +
    首页
    +
    #icon-shouye
    +
  • + +
  • + +
    播放
    +
    #icon-bofang
    +
  • + +
  • + +
    定制
    +
    #icon-dingzhi
    +
  • + +
  • + +
    更多
    +
    #icon-gengduo
    +
  • + +
  • + +
    标签
    +
    #icon-biaoqian
    +
  • + +
  • + +
    保险
    +
    #icon-baoxian
    +
  • + +
  • + +
    保存
    +
    #icon-baocun
    +
  • + +
  • + +
    颜色
    +
    #icon-yanse
    +
  • + +
  • + +
    数据
    +
    #icon-shuju
    +
  • + +
  • + +
    挂号
    +
    #icon-guahao
    +
  • + +
  • + +
    定位
    +
    #icon-dingwei
    +
  • + +
  • + +
    钱包
    +
    #icon-qianbao
    +
  • + +
  • + +
    复制
    +
    #icon-fuzhi
    +
  • + +
  • + +
    礼物
    +
    #icon-liwu
    +
  • + +
  • + +
    分享
    +
    #icon-fenxiang
    +
  • + +
  • + +
    购物
    +
    #icon-gouwu
    +
  • + +
  • + +
    资质
    +
    #icon-zizhi
    +
  • + +
  • + +
    收藏
    +
    #icon-shoucang
    +
  • + +
  • + +
    药箱
    +
    #icon-yaoxiang
    +
  • + +
  • + +
    安全
    +
    #icon-anquan
    +
  • + +
  • + +
    删除
    +
    #icon-shanchu
    +
  • + +
  • + +
    邮件
    +
    #icon-youjian
    +
  • + +
  • + +
    设置
    +
    #icon-shezhi
    +
  • + +
  • + +
    完成
    +
    #icon-wancheng
    +
  • + +
  • + +
    应用
    +
    #icon-yingyong
    +
  • + +
  • + +
    相机
    +
    #icon-xiangji
    +
  • + +
  • + +
    扫描
    +
    #icon-saomiao
    +
  • + +
  • + +
    通知
    +
    #icon-tongzhi
    +
  • + +
  • + +
    转盘
    +
    #icon-zhuanpan
    +
  • + +
+
+

Symbol 引用

+
+ +

这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章 + 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:

+
    +
  • 支持多色图标了,不再受单色限制。
  • +
  • 通过一些技巧,支持像字体那样,通过 font-size, color 来调整样式。
  • +
  • 兼容性较差,支持 IE9+,及现代浏览器。
  • +
  • 浏览器渲染 SVG 的性能一般,还不如 png。
  • +
+

使用步骤如下:

+

第一步:引入项目下面生成的 symbol 代码:

+
<script src="./iconfont.js"></script>
+
+

第二步:加入通用 CSS 代码(引入一次就行):

+
<style>
+.icon {
+  width: 1em;
+  height: 1em;
+  vertical-align: -0.15em;
+  fill: currentColor;
+  overflow: hidden;
+}
+</style>
+
+

第三步:挑选相应图标并获取类名,应用于页面:

+
<svg class="icon" aria-hidden="true">
+  <use xlink:href="#icon-xxx"></use>
+</svg>
+
+
+
+ +
+
+ + + diff --git a/dashboard/src/assets/icon-font/iconfont.css b/dashboard/src/assets/icon-font/iconfont.css new file mode 100644 index 0000000..3accec6 --- /dev/null +++ b/dashboard/src/assets/icon-font/iconfont.css @@ -0,0 +1,151 @@ +@font-face { + font-family: "iconfont"; /* Project id 4736032 */ + src: url('iconfont.woff2?t=1730912986793') format('woff2'), + url('iconfont.woff?t=1730912986793') format('woff'), + url('iconfont.ttf?t=1730912986793') format('truetype'); +} + +.iconfont { + font-family: "iconfont" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-2zushushishujia:before { + content: "\e608"; +} + +.icon-yumingjiexi:before { + content: "\e600"; +} + +.icon-dianshizhibo:before { + content: "\e62e"; +} + +.icon-lishi:before { + content: "\e62f"; +} + +.icon-dianbo:before { + content: "\e770"; +} + +.icon-shouye:before { + content: "\e611"; +} + +.icon-bofang:before { + content: "\e612"; +} + +.icon-dingzhi:before { + content: "\e613"; +} + +.icon-gengduo:before { + content: "\e614"; +} + +.icon-biaoqian:before { + content: "\e615"; +} + +.icon-baoxian:before { + content: "\e616"; +} + +.icon-baocun:before { + content: "\e617"; +} + +.icon-yanse:before { + content: "\e618"; +} + +.icon-shuju:before { + content: "\e619"; +} + +.icon-guahao:before { + content: "\e61a"; +} + +.icon-dingwei:before { + content: "\e61b"; +} + +.icon-qianbao:before { + content: "\e61c"; +} + +.icon-fuzhi:before { + content: "\e61d"; +} + +.icon-liwu:before { + content: "\e61e"; +} + +.icon-fenxiang:before { + content: "\e61f"; +} + +.icon-gouwu:before { + content: "\e620"; +} + +.icon-zizhi:before { + content: "\e621"; +} + +.icon-shoucang:before { + content: "\e622"; +} + +.icon-yaoxiang:before { + content: "\e623"; +} + +.icon-anquan:before { + content: "\e624"; +} + +.icon-shanchu:before { + content: "\e625"; +} + +.icon-youjian:before { + content: "\e626"; +} + +.icon-shezhi:before { + content: "\e627"; +} + +.icon-wancheng:before { + content: "\e628"; +} + +.icon-yingyong:before { + content: "\e629"; +} + +.icon-xiangji:before { + content: "\e62a"; +} + +.icon-saomiao:before { + content: "\e62b"; +} + +.icon-tongzhi:before { + content: "\e62c"; +} + +.icon-zhuanpan:before { + content: "\e62d"; +} + diff --git a/dashboard/src/assets/icon-font/iconfont.js b/dashboard/src/assets/icon-font/iconfont.js new file mode 100644 index 0000000..34aa394 --- /dev/null +++ b/dashboard/src/assets/icon-font/iconfont.js @@ -0,0 +1 @@ +window._iconfont_svg_string_4736032='',(a=>{var l=(c=(c=document.getElementsByTagName("script"))[c.length-1]).getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var h,t,i,p,F,z=function(l,c){c.parentNode.insertBefore(l,c)};if(l&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(l){console&&console.log(l)}}h=function(){var l,c=document.createElement("div");c.innerHTML=a._iconfont_svg_string_4736032,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(l=document.body).firstChild?z(c,l.firstChild):l.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),h()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(i=h,p=a.document,F=!1,d(),p.onreadystatechange=function(){"complete"==p.readyState&&(p.onreadystatechange=null,M())})}function M(){F||(F=!0,i())}function d(){try{p.documentElement.doScroll("left")}catch(l){return void setTimeout(d,50)}M()}})(window); \ No newline at end of file diff --git a/dashboard/src/assets/icon-font/iconfont.json b/dashboard/src/assets/icon-font/iconfont.json new file mode 100644 index 0000000..3b854b7 --- /dev/null +++ b/dashboard/src/assets/icon-font/iconfont.json @@ -0,0 +1,247 @@ +{ + "id": "4736032", + "name": "影视", + "font_family": "iconfont", + "css_prefix_text": "icon-", + "description": "", + "glyphs": [ + { + "icon_id": "18260541", + "name": "2组书室书架", + "font_class": "2zushushishujia", + "unicode": "e608", + "unicode_decimal": 58888 + }, + { + "icon_id": "29888102", + "name": "域名解析", + "font_class": "yumingjiexi", + "unicode": "e600", + "unicode_decimal": 58880 + }, + { + "icon_id": "8604979", + "name": "电视直播", + "font_class": "dianshizhibo", + "unicode": "e62e", + "unicode_decimal": 58926 + }, + { + "icon_id": "8659214", + "name": "历史", + "font_class": "lishi", + "unicode": "e62f", + "unicode_decimal": 58927 + }, + { + "icon_id": "26401563", + "name": "点播", + "font_class": "dianbo", + "unicode": "e770", + "unicode_decimal": 59248 + }, + { + "icon_id": "42282750", + "name": "首页", + "font_class": "shouye", + "unicode": "e611", + "unicode_decimal": 58897 + }, + { + "icon_id": "42282751", + "name": "播放", + "font_class": "bofang", + "unicode": "e612", + "unicode_decimal": 58898 + }, + { + "icon_id": "42282752", + "name": "定制", + "font_class": "dingzhi", + "unicode": "e613", + "unicode_decimal": 58899 + }, + { + "icon_id": "42282753", + "name": "更多", + "font_class": "gengduo", + "unicode": "e614", + "unicode_decimal": 58900 + }, + { + "icon_id": "42282754", + "name": "标签", + "font_class": "biaoqian", + "unicode": "e615", + "unicode_decimal": 58901 + }, + { + "icon_id": "42282755", + "name": "保险", + "font_class": "baoxian", + "unicode": "e616", + "unicode_decimal": 58902 + }, + { + "icon_id": "42282756", + "name": "保存", + "font_class": "baocun", + "unicode": "e617", + "unicode_decimal": 58903 + }, + { + "icon_id": "42282757", + "name": "颜色", + "font_class": "yanse", + "unicode": "e618", + "unicode_decimal": 58904 + }, + { + "icon_id": "42282758", + "name": "数据", + "font_class": "shuju", + "unicode": "e619", + "unicode_decimal": 58905 + }, + { + "icon_id": "42282759", + "name": "挂号", + "font_class": "guahao", + "unicode": "e61a", + "unicode_decimal": 58906 + }, + { + "icon_id": "42282760", + "name": "定位", + "font_class": "dingwei", + "unicode": "e61b", + "unicode_decimal": 58907 + }, + { + "icon_id": "42282761", + "name": "钱包", + "font_class": "qianbao", + "unicode": "e61c", + "unicode_decimal": 58908 + }, + { + "icon_id": "42282762", + "name": "复制", + "font_class": "fuzhi", + "unicode": "e61d", + "unicode_decimal": 58909 + }, + { + "icon_id": "42282763", + "name": "礼物", + "font_class": "liwu", + "unicode": "e61e", + "unicode_decimal": 58910 + }, + { + "icon_id": "42282764", + "name": "分享", + "font_class": "fenxiang", + "unicode": "e61f", + "unicode_decimal": 58911 + }, + { + "icon_id": "42282765", + "name": "购物", + "font_class": "gouwu", + "unicode": "e620", + "unicode_decimal": 58912 + }, + { + "icon_id": "42282766", + "name": "资质", + "font_class": "zizhi", + "unicode": "e621", + "unicode_decimal": 58913 + }, + { + "icon_id": "42282767", + "name": "收藏", + "font_class": "shoucang", + "unicode": "e622", + "unicode_decimal": 58914 + }, + { + "icon_id": "42282768", + "name": "药箱", + "font_class": "yaoxiang", + "unicode": "e623", + "unicode_decimal": 58915 + }, + { + "icon_id": "42282769", + "name": "安全", + "font_class": "anquan", + "unicode": "e624", + "unicode_decimal": 58916 + }, + { + "icon_id": "42282770", + "name": "删除", + "font_class": "shanchu", + "unicode": "e625", + "unicode_decimal": 58917 + }, + { + "icon_id": "42282771", + "name": "邮件", + "font_class": "youjian", + "unicode": "e626", + "unicode_decimal": 58918 + }, + { + "icon_id": "42282772", + "name": "设置", + "font_class": "shezhi", + "unicode": "e627", + "unicode_decimal": 58919 + }, + { + "icon_id": "42282773", + "name": "完成", + "font_class": "wancheng", + "unicode": "e628", + "unicode_decimal": 58920 + }, + { + "icon_id": "42282774", + "name": "应用", + "font_class": "yingyong", + "unicode": "e629", + "unicode_decimal": 58921 + }, + { + "icon_id": "42282775", + "name": "相机", + "font_class": "xiangji", + "unicode": "e62a", + "unicode_decimal": 58922 + }, + { + "icon_id": "42282776", + "name": "扫描", + "font_class": "saomiao", + "unicode": "e62b", + "unicode_decimal": 58923 + }, + { + "icon_id": "42282777", + "name": "通知", + "font_class": "tongzhi", + "unicode": "e62c", + "unicode_decimal": 58924 + }, + { + "icon_id": "42282778", + "name": "转盘", + "font_class": "zhuanpan", + "unicode": "e62d", + "unicode_decimal": 58925 + } + ] +} diff --git a/dashboard/src/assets/icon-font/iconfont.ttf b/dashboard/src/assets/icon-font/iconfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d7986349f116b74e4ac88562471bc4bd8e69982e GIT binary patch literal 19148 zcmd^HdvsjId7sC5MJ?xdVEA7fy4+FNvtMLP)fK3guEy*IbBxDIJ zlQae=5JDQDG--j9qybVOY1$M@^JwBUX&`M#o2KbWPs<~u$Gi-j%Sy z;=fgUPNh3@=g!QX`DT9KH}lOmbC*y;h=Uv_oOF*|e#HfeJ2J-zq5X*6we9k*!a(Sf zO?dtgp0C}1cx?9TyFY%M5O$OhK00}5aUwG`{3SxPC?T7kIXFIcp#862{v7iD0IqZp z5#o9N5W;^6*Lv{q{83pD%KHht4;`A>Ki2Z?!fS-6-@)@mhsTc2(g_|wI?5x7>9ND( z-UkQ%l#s1ILY(`UR(}xwEIE#BXZHq^75-mwAO-CN*AcSLCX0TyM(-nt zm6Wf;9X*bCg}fWjriBixkcwXEdTI7Fkg&o&JB%Fik6JjHWGli9PZ|+;&p@n$IEjn6 ziHCTJkN8P|1WAa5NrXg61CWc6IB6oyq=h6%lB7_(B59-&o&WEDPT2wUkyE;5AWWK!8*r)zV1`s03{XR7fRcJBf59Y&#%DDiPZOGg855ARW*n zl?K}ZM^b6D9S|jznC*Zqsl;sulu4z@cEFodnr#QL@fs;rjXFKo|skGS++(jzwwgaD$N{8*hair2| zJMbQ@qvd ze|P-Q`9;^q+!LOD&uiXq`o8G@MBrHPve0~ZG_pC`+Td^a=f+RP=Hgd3t!uuh#hI9p zIGw(Q|AZd}8jU1R`pE#fm|RH?5RxgDhiEhP(4sHN(I+MT>MtgYWfE8(!66{CV}`PnurUG`>cQ)o(v!dKJ@H zdZEEb8>T|zmIGM-1`*qZQI+~8+lKqc4XQ zcq&bL$RN3tTtz0y0=b3UNj^p%B~OyClkbq{$jjt4s?i`#(H=TXe}nF!Q*@DjfPR!d zL_bHrLcc}-nf{o*N>4K>Bnic9h-BKFIE2_p-;>N_Z)X`)x z5{`rmeG$1&`i<4#at?#%hMs+LJMIxEvNFdG)zlK6S-6uwNsku zSt%Z>WzI+wc*xaCB{Vpb zN}rXM?V`03HBn3JMT zPLiCIE~ax*R*MnIMruk6h6|1=N~tpy|LY+oh6~hlQmqW!qSq#P+ z{5MlsW+=#L^{LMGk&8cl@ko2;N`P)ubzOC7I@L8dEm7)L1vNAmd@--;R722Un)(c7 zE=}d8>QXf>URMQIHK%5BRduoIS2=U3isDe6s>(T?H#k=icBt@`>SoTJD%Tu%=7RoI z2!@bDW2%!c{XO1NsBoyJ!gO}ECJ=B4#4Syzim6cDp%{wJz6wedU2zyxG1MhfC^~0` zLlKY9z9?n#SY0P0NTvblJ$50ehZc>%0D&u!C+^E7# zQ)3E0wF8wfs-Y5&X7k4>e98uRYDI#IFRMC3I}A#Z z<Gpj{RD15Vy z)*&Z+uAlmzY^{OTQnZ%+x272A11DmrzLe2)^qE6?>>-WY-6#E}6GBkcfu3dPS~jdG zrphcpn7;HgUE$n93qGTNr*MgzsR1*lRQH?kCDMutyyz7SSV}f4zb1W24wZySYBLO5gA!9n0YRZI~WC@W=k0pc>ptp{m#r;wYmc9fqg;QsY8V&)# z9cUB!*M?K#W=Is7VQQ*om`tH6a8#(5q94%HKvB~nLnhF(7>|Z+@FY_@Oos%{f{(?S zVX?4eWGC2HxA~hVE!4o;ic`3S1K>!$cPLp~P9Rqf)uTBSr(#Kw=n%GSAztxOfZ?=- zPhV%aY>@;pGyuAs5LRGir*XgpAuwh40tX#_OsEbHfrkVM zW>Am@Tm*BdBBZMhAOx8QVwxrEDx}|34L77w3c8dej9G>QVoos<=)!~$OG(y2jA80? zHiyPCeHmPvJYo6#sLSoSqj!Z*CA(H1be-4R5OQDjuI6k{{~!Nxe<9Ntj`nVFoqyGn zrP*@n`qHz{;$EIDl{WSl-L5TrT@4L==XuU6=2{+qyd_seu&*)d+PlT&F7|JfIurER z+A5qwW9lSDLZBoj>56_>2#0(@EuF-C?CULUq?XDV351H60q`FwmeV4gQ^+Yo|LD(l zDK3mZBeTh_u4FYK{n-(4rb}TLHeaBQWJZ$xw4Wb8McBVjsti0fihQ3)BAc4^mq6jJcCt83pBj*$c4~`4`UgN`4Gwj=EIxn;S5;9hKpwz|9%oYXdzcnCoTk9gr7OyoM4&N%>tCm@ zxw=#?m#)6%Sp@p~5ttskVrO?xPxsDU-`KUYt5E3LxvSeMe&ogv4{qH0DnF__Xt(Kf zn$_p#RKIXcYidiTzueuXGiN{~bNTg&i0)G<9d^ko0!ugbm-bxq++MrRIXd@r!9b&~Kx)rdyDR9rTKEzQg4yu_Q zNBc5Ev{&j~DK|xIGcA?5t-7VMQjJSm4{i#S-gtycA|E8PYz}3MF*cDI&fr?QFILQk zCSzT3`h2`AhI=)IJN*X?dirPefY&g*0sYIN=H`y(W~zjm-H~|2ZG~ivWGL>76>=e2 z*7NW}xk8Nk?4rJH-!4(SKCgOMHN3{Zm|g=C+S?pTp~#x&j=Ee?_Zbl(EER43`zwBu z4`a2Pz)EZf))QmoebD!z?`Wb_l#G*yR8TD>dB#?iv~(&ny0@5V-M&6AhIZ`w zxYyg)=k=E8zXpSwH^Wt*HO*uajy`?T*X=vWlhIwO2)eIz2Zg#Tn&d;tWdDI|ODZ{V z?Z>)$s@2iGyLWCM%(YML-92n(mi}aL)7HCL*yhMP6@UZ>yhyjRtl!_=)<>*BZK zFh_oCO_0d5H#BGo9w)n0n3 zaJhfq?Gl#?*i)pf==57LxSrfZ-cLTg7C2yejg?rh9Cd&~Fp^11D@anBFoIzWP}1HJ z!C2lxEl(~pF{mReW(T=)(Xx1b7{Xdl!MPnnODkh0Q)vdOy07|)Xp(+6S>2Y5(rcs1 zYW~n5Hb^N0A;+L*A?X_DJX-6!a%1=PbH(BouF$ZngoSI z3j>_DXSmuA`da(B$p;jr%%ST+CEEr?y`95+xEe3affwFKetRu=K|NC@Rk<9NYu@5p z0&6Iu)eky-zNpuGt<&q3H;2y`@%ics<@w!b3G7Z;@7Wiyk~YP2$*tURs{1UKu>>!O z(XueiN?8FNz=EotxZL&wmA@DaJ`wB>{+o|x-Wl51(Xp|;u0TwBA0nu0Mvoh%Qp z$EiqX#QSS6{8TDJ?~A0W@BOtfsxKQ~)l3anU4hI*QYUxmx-wXkP%HoJMx`0mX)kqm6v zU69QE6WHeJxn6tXMOEZB?;f}7E~wgx^_#Pr`cp*+g%(!9?(|TSIPca4%8K(_pVed>rbCmu|>fu z7#)!(KkBa=s=ab+iiUcEkpOm11Es$7@`zVYNIunf^`Wc#LJx>W@j&Q3okN|t>I%cQ z(w-8o?)K^{4hIc(bhmVN4z80&4OY<=ZpMD~9czIAbOo6BRx|{dnB)v(**B!gsZXrf zO6v*8f2E?;%c7}Nls*wn3H5GwDCE9d6-S%w!M(27Fj;T)Gtz%23|*d!JTP=!E?00q zoMTx9pQD&pV4kkOgh&gAkjh&)#9`Lnh@EA(I0dR#RDT6rcbPj!enjD1`-cCM(b0cejv?%tadulpEF;nWRS-H~oG#0`<28?0% z4Io#@(&NDfJ}Xd1(d6I1|3Yl0dguexOgoy*EvYSLvmQqK;^tA6lizJ@rJt`QR)5jj_+4wi=&iPnyVe3h9K;Yg z9G@sR(*ia}?5Qmn5mo>@B!yBr0+au8Y8_FdQ;qB0_iNhy?)80L@pxCcD=zMH=gV?y zrJQ%)C#SFAC-BM`(kkCn2D4st!#Uuz(D zl;`CxmnOJoEs{C*tY9|5u_TPQfJ`2ZswY&H7)wX1pNytijI}n>k2bd69KLARMPbHu zg9Qh-4hET_*Vi6g{at#OH2zj+8*@22*2gr!(1{(B!E`sQ9qz1GG#J%vo~el&df3m62u!EoO)!Uk~$h*%8>fPWooPzbic7=!gC^5(wNamlX?~h>>vfgl>er?l zLkXXoPa1~GwonnjG!&Zw!E_4~!Zpd=z7IM)9;e&wUpLZT{>_rRI9kkII2NaEF0U2{ z;PeD&jT?ab++P30^6vn>TfIdbX%yEZpw$7 zDeW#;ufV8nX-;w{FtfZ#p60gI69&&n(XwgRR{NH5bj1=-Pjo-hor=KZ*&T1Azig|X z47%@g2cN&AElv-`+p5k-MxYXp;Lw}09H=X}JSE5YR9oEdkGG}V!Js=;{dl~sE&j{9 z-yi}Km zFSzW2Y^$|TdI0_>>1+Cv4T*9B*J?KHn_+vqmpr%@2xiWbO}TPe-i45h78^(FY!EapiK%ULD1>t> z@>@?VUpQt-<}qs@00P+ykwEw>I6|r#`cX&#t|41s>k6vFmP*uCy;ovLClUPJdo+RL zuJEu8yF+;5L7u zM_tuRTv7cOX35*_eVO6J!jsV?t1Cp`GJo#C-_K3W=X+U*>V4J#Fd=X>aWyO!p%<^g z+C{aD1J(gNslNc3A$i&^tJV%iVkwiat6uw;C*2A6Ntf5_S`JhnO!N)+wY0#i?`SBd zb$wcod7t&ha9LReWgv6Yd}Lwlse$UQylj8c71 zkUfnh-6t((-5X85cNS-Pc5q?vix1)?bX_GTxL($}K2rUfRr}Dbvx>4^F@$o_K}A^K z^Pa=rc0IWn`{RGO7RW?u_A^^`42C0T@6~0CZ+gzX>D*Zz2f5U!oCTz}u&1f5t!WQJ zzgh_$!wHk~gSc=D1nP?6N8938NK)YSw#6kGXM2TUa8qytoON48U%VdYGuK4cq@8@_ zm;(&t90T9VAXr|DP4g$JqY^Lr82ndT2h!<*k%89Mf%mP!A+V9Qc=a&}5Cz{<@02xL z)u*4UZH{A}I=7A6UaLJo9w$$)h1>;j$Vkpla=~WrjQ3^(wLy|5)nz3=nOiyR3`KES zNMT-;2Ug_c%C34=eg|tkS*N0{*Rpk#&WCRc#DpQ*nr?%#cq-Zp#>E1+$_Cup)b@7z&GEJ17m786b@A@>H6C1zSoo-*mmW%fn<|) zl;LIguV*(XPbnMfi`$o9T(^BNl^Wc>?nNMh>(*i>gHLjZ{Eq${bLKwsF6>ghkKAQ_ z+x+L`De{lx1@g*TU=vx{87{-ry0WVQLyUF2LLRF)^N<<04u0C-(egKRpl~qo#?mi@ zuUh+haZ0q%!sN+AYjt7kO5xy{1z^5jsicn5{z8kt(T}U;qMJrWZn{f))n6neMG>mU7(jHBf~Aww>k(Cm3z#Zr)B=;-k>NB2xc~a9qePU)&%|Qe~NS zWvoYBvK{`VS}V|$>MJ&h|7AA^0$+WwM!YbnMWWrT>Yd9CR(~fEM%xbvaR5=_#0_t> zgKnv{Ofw&|%auGUx%PSMH$`Mv{t5+^0r)ze>@dTQU91-7h)>nR@{5m-T3E$zArxw1 z4e^7uu!-;0w$#E7lsGGY!Nl6G$dVJaFvTxZJX#Ag{CdR~YGID}r)yya;qTYNDt>|D zrCL}+{8BA!l2Piag&ibI_g^?SJ~lsoAhB;TF|~hYdSYgJe)+z0d~)H?*xd5t@_lrC z?#R^4bfPEUy&N?%K0Q9ST+ERhCJXcP6N!nrnZt>#C~y4Gp_#<&+{|_3`{(lq=jUh7 z>*|vA$oTyJnZx)LZjOxObEJ9r2k?2)KC%dZitHydWSUG^{`^~>cOtj^NrXdqbM7sv zZ+?3ex#!3cdPaNoZ$tuCUtaaC5dpiM4}9UPmn zUcGUAO33WUrA{oMAmz~1jSKq3__Ta^Qk|SxxN$)}Ch5>+#rqMdFWN;272TF0W3nKC}Hkk>inifzv1mf1nmL+ykKj4A1eR=J{15U@&y1SCg{b)J8UdX zEdT%@?%O{0H}Es}9o5;qiEo<38z*}M9m*O)k&T19_nT(;#&O;dAOAMkJDLBNXaBa8 z{|}Ut-1iQq-fwk*jc=Ut4ODPe2n3F%4wi4){M%h5-s)T97K+q5JGr?70HlO(+FR#v z0o}Pdd*0dRU<)vK!;BMLYYujGxk5AD7*}8$HBUAJn+5}tMYyxL7a)Bcp zBFD8JEWGb zBdxdgM&9zRnZ~2itY2*(?aZ7W-4am>Us3vR4Cvn8bI1TAm0Tv{wBqRK=+w_?efiFF zy_@H5D(CRDEnpuqu`R}EK>^2rNnJfU$hjAS&?hkx%asqt*Q5#oSs>BGW^EL(ZoqeI zOZr#+lnJ?MIxg^AX<(qLZfCuh03~JT*xuEg`b13D?w<(VInHWm83idXtlzxAOkgHj zv?HPCrC~eYEb?&i^drPf<$=V%(G0?KLe-cS<8>{s-4#SQX`gJRk7N>c%Ao^y9fG;BX8ZQAhgb2V541*t zDf2FOh3oE*F`0_Vv&%1~ex3QB8Jy+~tbRA;Q(HVv7uwAWetAlPU00CB2A(CEDA63C z99lQs+XQiEDPQgsm(HUqFs;j4^8PL8d3K?fq#fIT#u2YzD60^O9DDUT(;z6Qm=K#Q zDAb1S`(iwV1sz$DX?2grB=zCqJeG=;fm{qlAGzaSOXIUb8#(OWPm@hJ!?h`QPRRR!rq7@zSJMkKIFLFNb3zdRe?|C2kD^@2YA zvi_wr(=yXjkOzG?ljls>vc+yVuy%o7>Ft!-xIC65k!?jMYUWz ztKF_yvtXB2pmR;ez{UK1ZlNqyjO4$70QYPDCsZVnL{~h`XrUY zL5QZVl=(R_AQytnQ6p83NNApjuZWRv91Q)b#)R>Snben0@nV85reQx<_9`8B-zJT; z^a9%Tr4Al6#~qB?h?k!>>yXWlIw*vfW7k%cwY9MY%+4te6Req9h3(pfwILAceP7T& z49Yp%mGI>QNWT)>+#uO*a;sx17^bY$8|=7hHMauQ<+x{y^@mue-U0Juq+LagVt@Cc4WBPTbvSOfv}zIZjJFz1(s| zOr*5lA)T`LO<}_D{Lu*?R1LItby~vDvJ}CGbh@9lFw4WOJn^RhLthZ@sdvcH;jjhg zI(_$4Hpoy+!ZYN5THb%OL1Ttk2ln=8Q)esa4Z`SS&?h8Q&)u7;MmeqVayOpn@Kq>P zt#P;_Z4j-92j;i9)HRv00ezPzHI|TMzz2D*M>&Y|iY6N8Kmkma=yPJDJRFBRH3OM> z+Lzw40~l}zKnL2w$A1#*S6Apq{{=K1X3AqrV39A`&j+SKDfT|eS`RDpIAvb^mV$2i znAq#6cm?3WVp8NjNRHh{#3`0OZ&j0> zm$#r0%N|gq>NiX-r0RBMS|7w#Lzp2hR5AyU?B3wbVBCOqB!-HkfkX%&%tYDdKQP68 zgy$@PUXHS+>XxPSVk(*@LI>+UKhgn^B(}!lQ&1a$MQRXU2$plaL?Z%0GP)ZHZW>A- zMezLC3gH1}g(*8gk|f>2k&=$N5or|6+yA7aF5cgX&#M#5(vE2uYc?`5)+>nnipMr} zZ;dx4_76=ALqYwX=m4&avqR$Mo;Nz=Rxa!zR0`DmP=q(A@#FFkiOfR2Azl`hNn}E7 z06hiX5SXZDHO32%s=kJAh+ch6*l1E5i@Y>Klw*g#84#LDTZ_o8AsHid&COX@cDVoz zx58L}7qQ#2eSHNh#jWe>C2Qn9-2I%YX#BqPTVS`V52_mITBd2EY|T<>X6+Qv*xX{= zQQ>(>w{z3gv~&cmCpHi!x3ewJB@(YRv=6%9yfq_Mu^MQ8KP|yztW90Vlx2f{Rz2Gu(?(2eQtas!vttUwr|m`vDZcF&N{DdS8s38 z7FQP)ik)Q4%KzwCDrT6#2LL4FD!QMiVXH0J*%>^yH!R?E#F6$W;6Q=ep6C7KKwCNO2RZplYa@{$s|A~1;2ov$ zqgyftR^Ygg3TB#l);U>5(H|2+>wuIre15BH4o^d+B?GUesz#k%{Gf1r%<~%zMwERy zQebx8oX>ZC%1z`!ysbU^$@Zb z9-!!hhARt{DJJ!qh$ruCbRS}~$H!`ID;cxs975yUtjX(RqAG1h{7J7U$|yi!f*qH@ zH?%v01#NeIJ{+D@e>4l*o%5SbhAvy-hjPxVE4Q1w9Yhj>Eh!X`*;icE6I@65Aj)U* zn1!+8-V7FBdG5A}@amMEj}+)drKHhG$F=H%acaMC$HVkpOjzy{c*|SD3T|kxW3!1- zL{SVu02(Z6P=YG?jz3M97p~XINY|QOX|v;!;FnI1tN9aEkzo?609FA{;<34d%$E)h z1JQzb$CXBpqbB2;AIHo54&G67LPqTFj)s4SWf=v61R-VG1TmzK5Yzaifux|%S`P06 ze|=@VhI>J*ye{z&$BZFE_>mBYg5$AvWX3_CKXBYW&W;?4#7Rot@Xt(7oe*2?=RoLgDwaGtiT!V z_2&fd=l7s%6QzB6U$#so*(7!5JlRa95$nOtFPlcnk_9*%zQ2OysfI-SzwA82dRxK6 zX~OIWr`QcBI5IpL2T&VnINiA(PKxQ@HP&4JIGTV4)2{g#M}bppq6!x=31*F`+)?k3$!J(eh=)GHncx`KV{Mdq5y)JV z_8v_%U$JaT@Hn#|###qAu_bI~4i^c5Nn^$#lp2>mOhwEvNnq85{l}9FqN0@@@6iW$ zNomAGE-1L-^Iq8uynml;nzIYs1yTF|VE))|1b)}adpg_xE8E2{qj~T3YAp-~mLQ3L zhlyEjv>}6F*O#jzO?2?-t6eNfxD1{-UZIl9B;8<-arZQP5!KJQVsuZFHH{*Kb?TIy zMjG{&9a-883ls;Ybj=f|tKKGA&Jc$e8Upd2iluGK$+GY|LcRuLRgZ;zncD);<4Mtv zMw<^gImt|}v!~kzePbkC`;|L0B{qgb$1+x}Q1MVhMrW$91-wA=PWfft*_($1ZHX=j z05(zQTQx|DpZqZ~BGDi*=!Nq8mUyYZ@@IN)Qgn+xSM0;|VoFyw*ky1CR~q@p!TEoh zDP__mh2u4rXMj#e0A^5r8C1Kov+v`{YV4|=V62IYE_y1xTs=^zUwQ5kd$ zcHb`3c4t|suPB{d3^quTkdK%0#BL7IU9H3pfI{)Kn!Rve0PT9)0>n0*6we|n-kt;` z8EHr*8g4NnPBbc*g{E!if`aI|8470W1LfSF2h4zUh>0kOSQC5$FDL>cDV34LwJXk+ zAJI&&6^w8irX7*8TGFPc{h?5RQ4c5Q^vWDEHu^aDTDvxIvbU^1sU%s*Mz`F(Jhwt? z)hBLIUS3SQ>tHOdVo&r(+NybXgID9b+{pe~Xmc0xv_uQcpDr{$H{^D04MN0vzvO0R zvMIZn?`-$gA8FC}8n;)?2fB=F&$m!vZ(7p`34Wd$y{{P+R6!O^aRG`}WR!TO)(1F1 zU7VQszb73Bg_hQoPDJNX3IGGhlQ!G~^!#u8+FeaF&f%PaQ49|FtV!Cm2T~|b0cciz zPq42p!ke(Pzmg!5oIICaqjXUASYzi<&`7-lhH}U--(i>ug3=@EaYFi0uYL%cwAk!!Xdf&CV%*7Y_B_GLN9_d?GWhS+<_#Q+#j<0g$ z_}$G(yezDKaFjOf+Abq8c_%=LYisDCy{Z79jaB@Wc5`BF^ z@F50|nX?om{k)S*>580uF*hJ9b#uk$$@415!#=e&rR}a`x1Vr$C6Jf*6Qo_s<+>If zLey6G)c5){L+*dIxW25y7-J8GSH1-ywRP5R#v?&)bMWo4rYX$ix(jhW=_vYyQ7Lw0!M zRRRr8aFH|m6T0^AX0Go*TNh;|^g2rJi|cB0V;Z|#ZNjc)tm(<0nc2pA?Nn@s@Q>nU zg(2Yv-^6tSOf*ienU2CHw%?tRbqkTpk?wiDUBXvFDNgftx7p8 zEh4t6&QpoPjWU~?1Go5VUh~Mv&srgOqe1 zJ$B25iVl0mgojY3sZAnm(@;m}w74)%30Hxpv-X7I_FsRU74`djWw#Ob$WqKmPZS~l zI7T858*)P=?)Nc!Gb(4E%K&g(;zW4bL z*B>U%6oIBfCS=>o%jRFQrb6bI#kwjA*WjjI+kFbpjeF15N+7k5n&sus=O~_q@H9;Y?pPz7EzATM1 zGbr9X+Eeo4#Y++M)3pkOTA}@GRE5Zc(hBF+RQ-ONU-R}_TwVWA|F%!@3dPhK>! zScv~flmjHE2A?s^NFI=y(t+|KQIZ6dlYd&FxB&()%80KovT5!wLO-9kL*QDoxlHxg zZo^Dg8bwbx;Eg}7>{INL?#{HuLHcewWGiOYbAEknB&Y+?@SB{U{VPki7u{*R?cdPH`_W=xU>*^2)iE=GsKF}PPT;T7_r*z>R;fp+TM)67|^~2gy z$U;ds)Gg(?z441tWI)pxX=fco&g&p$eJEr70ING!nc3_rXjRwz?)^$cqw%dv&a=>2 z^ZvNxPwi#Dw%3M%ZfV|v&#q4AI}?|)A0hcd8vaBZqkPND?d?m=(ua2##Rxx>U7F!= zEXp;dxjHLZy{N5`QWSL(aRPlmbI0@jWVuJHw{-u z(EoK(f_WF&IJc$Dh=_yFnhv7;ZmVgl-*SJcAZ3k3vg4zCM;=_=?NUKcDOmKxczNsS&{*W5u}5l>Dny~j4X=zcaQ zC6lN011y-U{jlfT+o!O`?p~?)ynkJn-m^$DXMLOy-*7fqtb7nqzTm@4f`8zfZUctK zp@6W9^;aEE0#|XCPByU&_%W9q`m`eVsRI5Q(2G%5e_?Yp@$nq`i8oC0w{=TT0zP+O zR|-1eBA<&F^{FrGFXIZ`3f{BB)qXf*_!-7D>a>6*mXRPy^)$H?VkzkrWAe%hdzXc5 zXk6ar`b>%pC6hxDv9x%@$Qf0lDHV9Cr>Svu7W)T%7%%sRjcPN)rmZ-vJtpS(bRz~P zh4L*LTdkTSTyhI8E?B@4F9J_J#PBnh`>o$8ob9@V0)|H9jkY53L zg4nwNs-HcyLc(`M$@kMyY_IIkWNO~9HKDzXPYU|0$cuY{E9Gb!R_r zS%w@)+Tde?p+0v$w@w?C|bWhP3Uo z{XQR+6F-<7t9!V!I*wf)WTY2>cdc(;8f9lP&#lE z(VWQ!m?}7gs4?Lj={DgK^2uNKY1j~(2vd1KUh%SQbC1$nB%%G2gxg`@0i@q6?^N&_5B>? z66q_j_^aTvaxVo^c-=GBE$1z}5pU=$aVk7`tBR|xd3)TMOt2;G1MOL2#b;1#zP^3a F{{xB$^-ll* literal 0 HcmV?d00001 diff --git a/dashboard/src/assets/icon-font/iconfont.woff2 b/dashboard/src/assets/icon-font/iconfont.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e3c79e518b34988ee5ff1c86dbfd5cd8b75ae9b7 GIT binary patch literal 5804 zcmV;d7E|eWPew8T0RR9102ZtO3jhEB07}dN02W;U0RR9100000000000000000000 z0000SR0d!GhE@uJflTTq0X7081Az<+NB{&N1%zh@f_)nqKC=-OY#cx^}Xe~pg>ZKmXnPI4LSc@`p+U9b(P^|d>^UmDAHI&kSnk(N= zvYW|-XqgXO5oB2bAGk8evaL{eLbX+u?CXH02VuHBBilO3vYe<0FD6F9f(iB(*HPIwJ^z zV+ZN%W3bncfA3k=QfJ9f7?#C=m%IX1D8R0kX4EN#MK&OoaR%H3?96I?nx0unyBF7c zDe3Y$ug*btPLiB53&_+5(5|E85EHULB%0wvxF>Xf=0M!87K&c?0_BOUS+pG*vWaQ~mjYvIN{Qm)grSHN-k zAMjL+hSM2L7MsK6@dZMWSV~$(R!&|)QAt^aOpyR8LR-)PfF4m&0RfW(2?XPSK)4(v zL3kXfAbbuOh=2nPM92XL5pke{h&eDoq&P4^q&cuaWH_)vWI1p^>jD7Q0|2ZK0N4OBWUwKq zP$-!bf--49bxPLa5M%hldr0mX(t1!CDzo5;sybzgA~E%ln46|C#6(>MYra?*o$=~+ zz!lRrD6}lY(ps7L4cOKujx7*;GcCi^(wlU7_DOB2Qew>VGNgr}5(S=Vn~vLNU{xo< zH5Pde=Z!6NocJWpI&?FM8GfKAyFW4X9$VxjPn1}qjXQpt&*WS+)E8+eK#L&P_cHYr z8$nvY`64wF@O4_rFkH#JT!)mI#QF8H=A0#)+DX#FFpw25oJpE#BwxgJZpc!23Hw^N zOT)pTa3Nwb0DDSzwwo@_cbm$zP&h~Mjf-dV1Psl}AzfeP=7cM>Peu{SInP-^8vh*g zE)qkC#cATBFmW@h3-_~Ks26l$PWl?(V2Wytxs(DSe3BtcDXmSDOCfybskW`fd4|WV z<>ch9hN#4>7~&;np?Toc;oTyz zHcr3dqgJ{UHIu~%B7j=xGSoztpg~BtX*>J6A6tDQoYwyC+}Kq0}8bli#}pGP!sc&0x2agRqG9P2pTV(F2g~al(elUigyMRE5Tx z2>}o(JMB{5<)V}=hL9$5U-qkNV;wasQEd3C9&FP4vm7e&+2EpC@2A1$RcU|(i-!>l z-U!h_AYJ%48X)GRrlezIkxn}oGMMj5ho$f9f=7`|>9Qp!jxSz%@(tg09mn%tU9@1w z_W28UR(P<3Eq4TnWCCNZuZS4FK9L|(DQNc)Y@s(OTYt(OM|40)J55)TOW#XZa{G{` zmZg}nUl8-9Fq;N)Ax*waF5q0Af}yZ`UUT!nA#2E4DUJRA$Pwd=VAeT97>-c3uWkFF zN~VL9vkuzHB@tMaog2LMino(CB7eWXy* zDWOS(K<}6S2RYfZc10wzQ3N7uPX#Pqe#DZWcWr4+L4y2V$4sP_BbC*gJ zF8_TeF-rSFkgCwg$}j3^h>3>imDdnX3cNU5U7~Xn)Ir&j0MQ^!99#faGrJR^w6dQB z1hSz(a=WMxA~LODwV>n%hs!I40bn1Shems)u%>KvkK|X)Q`ntm z!N%U5ujd|ruxV4*C4p=6s#eE#4{vv6JGX5g+q$+g9(6>uX=2yxs~P>#os%y?TiaRr zZ}n(H5FnOLgT_@RgE7x$z10FPkZsmphIR{PDSHuxYgMhii^ENtn;Qt;A*(aiUpF>P zuJn}U0V1vPd9{t#)tjy~oT{D`?U=9fwdu0ofzH0Dr|iaDNuus92^vhfEBls7D8D(` zJnUA`P*izq0YPW|>O@>UrzD*&yBQ4VH4tC&H+|?1zcm0}s-zu=&Tsa#X{ZZT4ilr@ zR4ttsArU=Z`r3Wi{4H|ZR~;zK+6-5osA5r1G{kk@>Zoz;I=83-NVPeLU&7b55Yn3R zEc#6+{Wsa|!7xa$o%C20g&YTpQ7Brfrt81hejLMaxs^~kmRu%bQ8>&qZ+PQgJKV8< z7hbis*5|YPm+6%UH_>SM9r1^nte_V|)xfgZ1s_#KG@gM?VsdVI*Uh6m0b%&u4Kdm) zCTYB^I06so1(kI6i`<2rFme`x)bZp!ywY^x&t{SyrkesCEPqSd*L%|x|r1K$HLw@WJ1jMxd z;ALtu-|u=@SSer%A934k^R6o{zh-ey279sE=DO;d^V?ynzdMVmO1pIpbMD(ixAq%$ z##)){oo&RqrB(VI#BhQ{yJkGsk+QAL999c;C_O`tk=(@nJdi-i zq;X~xpQOE>Uo{!Vezk0PmRzewdkvHT>5~!bh`C{%y5DT1beZO6OgA}?HK}zyrVApb z&~3w-vH1zRda1>#FJxQGvix(C$TH0D651dMgW>W62R3!I5i8f)e<%ihhyrPt9R`JQ zzbJ=Z48v3-B4-U>XaEW%QRdWx1d9jg85(d|om3emft^X%lt!pfcEJF#I&TF@>@5Td z#|xsq3qTIKW=0Sm=%gGo23QC=8$cnLn)4%tNX))l3|8+7SmV(ttVq{M!L|oVm_nBy zTZT=}+p?BwEf0v;i<>d~#5~qQ*JysBh}omV)~RFLMzYS#Dag!QtCmg8snwxLwVWd&!2R@5|)^C5f8-j8>xq=;c|4(A9{%aGcHjLvSH_o&Ncb5 zV;La9?xJ1*Bj{G(h93YlcLcn^>SEK*ErP0CuYR1WuVh}6*BCa1zZXPuwO#hA)TF;kqO>axEDRxl33QbO6#rR^)-C8w&<#}Iw?DIHd*buG9JlZ)vzp~ zboMpOUG4vL^Jns3xBNQrPR=_6zi$3b@!IB3`>PyQe0j?vZ?bg>w@bUaF`)3f)5Vu_ zF6Tbx_A3I7tMfZQD<@kQdDoZYR>$|(=YIeBrBK}X;B=1K7ul@(PSt^btNK9oE#Bds z-DY1zoiqKQ5$4N0XL8TDWx)5ShJ93)dHDSW4V6^WRaNxCss!yjnDF@$2Mcv{74aje zdy+oR(W6gGo<@m#xAeMvYoFb~no{$7uYGoZM!o23arDB-y*F-pt=id(=)Dz69xl@( zzBRP5&C+Z#HJRJqHMg3Y4?pQUcM~*QTHm#_nVO<*_}@8(U`a_(A9`3HE}@uL|0u@{ zmJ|mKp+|Z1CqAT)*AvMeQI0FB&WUS2321Ol;1f+eM;&$L#D(&6U&-H-e|1j&ta}wN z_za)d&sPJv+$xi8;~~(^Iu;tU;jFPHCucSAe^Y!tg`A> zMV9AA8-uD^<=c|>?i)hI5xoM|MB18E+b`Zd@%%MKt%Z%X6*djos_vzdU1h87_SI!w z$>M!)*D8am#_00gh;EftwHXpM1yupnwymn=hE_GHvDH=-HWsz6$&bHB;UVJYUw-{H zP+NuVZ|NkSg$yQPI^xO0huQ4eVvnuA?_r3q=j&W>guj3%4TBg+aSi+qzJZ(a{=kH# zyajxO@?tMnR`~rL+V$j}qU~6ZDxB4cB?C*++xf@%+tZtRMo#=H#;!s@XcPibN(5jJ z3?S$44k;upc&R}0g*#l#z00QJrFFeeM^)){eKh_M3HM_;WuAJy#M&g0{a%rKH+ZZ$ZfF(iVB%_~&GyJ9_g^M|d! zGny6lGVvHOrZ$T`d$>@nC#@N9V@#Cp(dEk@d?JS4=6)NPRgn9^rcgH3AR+ zh?h;*DY)%J>?!xg`&O;W`kSz3)`U7LRLx+75r%rFWR-Y)BNbItzM8^KMd`xUZwpS_ z2B3k4h6buWYA8llj6<&o3WO zg*W_s%>&(V{_^z{TvRXq^5dqj@3Y+ZwdIMJgRM+a zufA#d>YVl5SC%(l9okAPM~M7lObpCET_-z~t(3M&mD$^Lp;$atpRGwFr zPOo>w>lC})v~|N{vWSZJyQL+Hp4oP37%ZaTvx8 zywGuZCj>uzZ2ZAK7-p`^X_mN=iM^OigR}VT{CB6`tP)4@L{4gYf2c%&V>A zzIp09pV!M0c9=5d6$`Pbb_;&Zv7P=?F*2*skGP8*w*2Kx=DSqnv z@A~_BX(K0J~oMQKXf%(SBH3iqvc>J1B$f2nqCdG;ft6xwWsBX*3CXdGm6O2f_i8rPZ%F zTe)@mGBk0?6qx_n?t^ZAAbpijvhGvq*U%TzRk{A-=E3wpUN&+5Yl$yxkbuPbnpQ!Y@=qS|i?#D-UsiUpbk|MJ=d_@88k z=LLPqm&SG)<9j0|s_aM^Q)#NS?2Js%jgCxWmW<@O)|^`BJ1B^<2B6`}WF&D+kCa&M z6Is;qAyLrs`N)LG2a-g~*^%6~z&kr#BkWs6c*pn%_b;G7C`ZuEtfq66C+m)O6|Rvy z9r@5248JcMqJwmbDBfANnFR<^HVhV*hL)wkC1v0P*EMFh(-;~PMX5h1+t)?V9k$1Q z1onEFc+vyKA;L5N<(os&iRBJSHeIX4o8f8A!H9ujiKav9NL5S3c*L31c690!z}Rv@ z#Rk?Mry@%)R=`2=p$zB&tD88&=js@mh8vTm_3KM^#i1y6E7V@HYLG}Kg%Xq`l^AKn zNhgC$vdAWfT=K}LfI^BWrWBmK5%iZ25I&=_2#}O$0nt> zbhKY=PK2)O%JNWyuhR5;a}T{%@J!kxLoFssqZboXl9WhTA2Q~He8xK*tf}?|iaJlF zBz2KW)P=4(1Z%S=d?=c$D)S`%uc7U=T}qRmkx-4(nXOXT|~g3prM3rVi1rHhE5Qvp%c1*@G4b6x(Eoo6bU7CLI*{P zpwbO3AcP)@^bT|Rt@#6Ht(gz=;U;I@d+s^AoxPv^+(;cQRfemaS3w{UgW6NL9tcFS z1B|@!Q2kku1i@1p;vds=-wZd^6UFey@giGrKmKzQ4J` z@r+)AKaxU?p3;Msno1&6IkKByb>v?ooB5f+_1X!dGr7ICL~&e?(B&O4Hln`yFV7p8 z!8#1b9@~9`PXfh|8u9HW9?HqhLR;U`<) zH|$rb1#WI66~3vD`0Gi5Vy7g26}UBnz@xYZ%rmzBeycEXJjE;onyi47RhgIAQrJn7 z4VNc;S1Ojr9Xq~#drGOq1yVs|3(ma__Zqa={xSfoAyBK@ZY?R#m z_nGyVduulaELcIfx5_l|mtVGO#vKij$lfte9_L+9wgN3i8LnmcyJumN2yE%HObzw| zWed_`{_xhaeM%ZJ^+J03NPB3NcUxFk1C2&EQctG1 zj*1=%_fhA-Ky{Lo+y@`S3T*ux3}YkA%m$@a7r2CK0!r%4#NB0X=^>HA5T3HR8^Pl9 z5P2~g6eAVXjRD;9e9YYJPGfZMJD1BKT%;XvK1M+yjG)&H%>SF==TZuLeay|z^ebt6 z3bSRYb#*e4F)=qj5BYTvYOAXwBAz-smuATMXer(=OVaK$Q2U-%gsP=~bBmI@4Y{Kh zo9NDQDkLjARz_}bZ+`;6EhY8*g|)R11Y#)Jy2qK-CoCi+l%Q|)Vl2BAB4okC9*oDV zPuBk=@IWy}DT{#uik4_*C|#3j0_iq-R9WrWGpFquE>CZDEnNrZ2Feh|H+6ZVI7!kv zy_&lE)$xF^w{O+_{TuQ=f3`fU5mb%sjv{77;BzGG0}linqV+%dJNbv>I3C$&({n!3 zVJjmFy#3sBY1kO`#M`^ODcC=|onRBgDPo+9=qa-_6=%@=NwGwq%DkjZ+aRKdu+?M- z>+0$v@E*p~ErIikfyYtZ9j?_HJ~ucg2a+|Ln(@;FMS1rU8(3iF*H(N)cbuJ8(&CgE z7RzOJ1rRlYR<%w(9`e|KXUPg9=G?FoQ=5~cVqsLPHs?pvyvkoJ@S(JA#0?>sg7u*+5>lc{yMoI8{U>~vQBvUJ$O{y~EfSmh)!jdi|t+9W?G z{ZIWPE{bh|ei%Hi&Yna1fK5$Pb7YTZduMsbV-Aj8mGM_!;qsHRmBQcd`fjb?$wdrf z(^~9U{C(rbjjhi&(qmjyuU@^H)RNz5CLZbb#&p5d-^fTf*Hl@Kc}Z$SOc8M>?$#hq z#2^@OwVIw@!aELGGq|{x_~M!zA9*XzIOwA^;F>%1TB$q=dO(2eMU`PoOOr`L!Y_Bv zUg$F%=LvZy3K!U3E;);26`otNbCETB?%rWKb45a&!{4)Vr$OTThT36qWL#Y8p{HHX z1X@Z%*ZA%4aqbEyWR6~TbcDR3%ZH{)D||sLrC3$mk@d%@tE3={oKR(a{2TVvSNl^x zim7$7-@bkOJ+obM?w1vZfIy|c25wbc{9)eB;M3Isky(%W@!j#IC3_CZ1F4IPg~cyu z;3~FQ+WYab67P=$7h;1$X+646(KsvknaTJee`!;vAbS61^y>YV6Ev~R=X=Sfj|18H zcb>bRv9U<7QHz>JbFAaWMn}!g%7_NMgk?#Px;W66g+6d7g6IGAt(J4uXALc-$YyLyckaS_yx)3Y^f6Yx)&Y4<-DWD(snqJs z%al&Lr|I%or3kVQKV-Kke^(k#6CCP4wyvFomj7te!ZaK5A}j>aDA*5Py&rduSYOHH z&x9IVqilY3&qxNZKZ{)Vm%n)e&8=SZw`CgRNy%b?pkbTL4fCB%6Gqet)!OS!F^Fb;UhzXsN3+S^#&a zoC<59R|>6PwLCv04l-0q*nRdWj1 zeu>I?hQmL?y^&ZMq^%OZ*Vn=iRp;SUFd%>1N~%a3Y(+*k)h=5*Lk8UQ?ZjeSRN!!r zDE0Y>M!hrVh3olHr67!nPB^P)+^2*q`-LOEOH%=Ps2M%*DOG6f;v#y_$U3ZZg?5Me zk9XsIDbw~zP}uIiNm*01s4Be0Xkv1Fyy(RF5g4p_wtFVNHQTeM?)BI(scH?TQj!M* zW8Zxfer+X17*?x1oRv%_3rP;1%U!aa`YUWUbmS>Ue>N84cdijynHjM69q{GWV9_n- zOCnhfVi!&}Zt{*!@mEt*+q$ohaKPShyk_0VNeuP&+Kt(o^f2OJG5f`$(mi0X9elqY`?(?1B~qBS)&lCCPX86|}d$DttT&X}=r zqh{dpfVQpHa6iN9?BNr~z9ABcB(&bp<_gWVMhGb=n8SpRULF5B>w(Dggw2FTD&xy9 z9n#DPvz0*_VtTitdXuuSKvGrH9&WrTTT+Z`(fyUBRAxX@;;FaPmnPJD_9@PDez8yu z1_vJP=;A8GQ*E$c;Mu05QzVyM(}S>^Q~Fs!gnD7wl8ufVD>UU));^E9VTEx-^ty9~ zoap(Sajq$?2oK5j>~OjL$Z=_IT(PXg5VzT#_s2QP6Cx@%ZC*J`66A!c0C zUXo$Ez8CNFCwl-~-D^5AEUo8v;6n1&di6m6w2$~bQFw$)C7fl6mVIO%(U$5AIJ>^H zFh~J{@o)xnO8KlzNdJwa!4)9OEP5c7_$s*!7FrC|k9h47R|RV8dX^FuRh`%dk~c$U zwew+EX%8fUUgni5VN$(3wyXmQtoxz#ZI%e3y8)~T6K;C~4Qn2Ubsdq%5N3!*Si;eOVVWUI}~_=#8PBIYqGAz<-T4SnG?X?fTwBVtc#mwWpUFC}+@ zZAy$3icwX&=zjPr%gxf9#M)(yAl-?)Y~m~W(KHoh6RE&Jr>n_ zOVI!(y!P-}`?{NrjSVL>q!nm7QgW`oQzIaexsK10uZ7EU%Or&!0(n+ z(AVz=OHKc3ZJXxKw55!(hI*o++h=reU)H>>S(ZS|%0M%qP7uC*+kRzkMUVAGGTO)i zbpc60wAln?F|Ww2rJ+6^b~(VTv*3(Cmq(P`Aj+p*A>g4Fh&oIdd6P9a$jrS)bxn(E z_+mZO<;$0QAQ>zxeRXBG`4NH5a_QecTmmpFMM^VFp?~q2PS)er1Y^<&xuwRYEKDmq zN}1+0pAL7d24g=i(%s#?2hzl_N_3$HY@o4u_zHpllZt#ERA>g ztV_z-m&%jMAE3`_yb2n$>Jh*1HsgtQF-S{N3+D3?iE7E}rxFJUr9!jCBuc0Qk~U2E z=JM!E)vk)#{(mm^^ph@oTHl%R-9OfF#C_^$&%8uInibG4w6Lg1effxtYt;Js6)Lj7 z84)(Imd==bm+)E<5Q=KR66vgu+w9LgO-6s~EOOp=9GQX33HDR)MPM@6r2(v$Pxi{V z(u!%9n&<3~zSt^Y?Vp$)$l$e8Ui*NZmVurPOC1xO*(HC#V2`5+WJtqh5!u|mTjub& z$cz4n?13#+Dl071Y2XP_0n~iw#?X!eQY0r-H#Plpi@D<)!KQY?yVW7sVW6dDYO&d+ zBVeU_)+p`WsxQ%68H4?i4ZDY3K$(so*|gRcp2}4uS_I^%)Kl*OVj6Y+Ngru7Ul%10wz@1yN( z8TEo%A;;0l|2=O80|m;S+(^|o)oDM~M#~8xVdw8lwkz}FVwGu_B`00*1K?NfVqD%L z1pB^gwMNLit>dN^|7Z^eD#j4Twrd8z%`LaqRTl!z$lEpQe)Z!Y*Z1yO1#F#FsBiEK zc$l#%g&jSV-A#ROt7JBHd;TmUA_9|wcMv!qGSA;5GWrb;T>lhAWn^2IrcZJUQC`PtdZ z*v?h=>65?)-FB6G!GW&-0BZ37YV8;hva)2{AL|Q;Bsasr{X>H-^>)8E zQ>aLX%Y02vEy8L|3Dym-cYu^%12^UYSga0F*U$j(uAL+_%}S>ITh8bB9-FGHf&O2oq6x6eF_)~gx z#61i2T3{Diepk=P$bR(rzEQb1;F$tR0A(h8ERq@vI@)m%kQsT?I>EBD{{~9QO=~4BG=;*@rJkZz z6bA9Gmh;eUOG{i1*Y@8-lYe+t>Q&QR@B$a}mY0|R14BKEHj>xNaPgToXOn3OA3hYC zk>jF~>Vdg_OjetzeFRYeb@$G+hqPX9wVOVB-AY3VbECtxvVN1M9@H7o#ClNsB zIr+b*pF1bC68vk1C5wgR=;qVjYJ6Msrdd@`96feua}+E$3TIS?!~OSGMYb3Bk`fZG z`B`s&di@CpSt?lH>VG3KzH&M}IN|{$EK`Mt+lMCe`G&Fr0+Lp44KEpai%R$61y!p@ z9{&YSbBh1~`ht>E?pTx|*-D88UgLhVYt1%!v$hhJ`zQb2&_hla))W|)^eNo|;EWp3 zoE?rNPV3_KfA76wF#mEFNc>_^QBesSN)Ldpp~<3bZvAH}x2m?922D%7Pt5AQ#D^~N z6ql&&H#SZVx3?x%Ychtw0)Yu5;ap=2BS(OR|&w(sB6a>wwj!u%}B|+zlnxSHv!>K7Ch>|&3mXhIXUS}^^rEaH&ROy z!|ohwRvx3_8`tMK4Gj&s8@4Gu1%Pz)m3PWmBzbX7p<_MnyY<6l^vPlKPoTaHTm;Pg zrtjQ{p(%iMtubN+c_iPlq0ic$w9%Ru{rf~lqp9Ly)8kJWPzwP;!5Ejt%CBD^8&_7n z4#7(m^4u2_*xFjT1(ZR0S`6Q}Oohex!)S4y5!uy+m-W)yjIgf?cKcnGn<%yZX+qH_ z_TX@j7{FzpGAC$582?l%bP+2T4?5hK=*YJ@uxgS^3p|6(0X~sYQGFd#TDCeDky&na{A~I6BddlWXJGKC6 zU~v7t<`WbTz+M2+BmT110%FpdoqUnxv5fF{I6`qr-(?;DN~BBG*tMFVr&4l8%u zO7>Da(~G6liVx18Rba7d*3xa#%cJl+(yl<^MM+ULgiQUCy&G&%sopddTUzSXNZjh` z5Cd<=*aKjG?8DHedEr$$y7|;HaXMvHq|dOqNUOh>U$}M^H2AEksoA!wsX^ktawdfU zlO5x*o?YOX1lS8DKItJn3h-!t>pzoI&t>IH5^sO2yDhs1T@DIFVxhq*Me;Z2^nLO} z%!_(OM3XcA9qc{7MeGoXtS}dn=oHfZ^-Ir!6hGL*sedbYYeMLwP|&|Apbt&rXxcO`Bg348*o~w3tMP&6=L&6{xIpyzBH?QFA|E z%}vgG+ba1$yEG5sDX6sikk$8GNxG$?LN_)OJJ`?gBf@kYgz_-PWAE#-vK&tPJV#?M zU4&-aH-LSVbbNDkN3tuHo7EL&|A~uN6bQ=)6+k~+nE#6_J|aN|%o755uc#E0;=Q1ve8!RxfMHH_L&&^5Iv6lK02My6mR8bAu_bjckyfI=bYk?W=FMtF)YWY=+g zTteShXRc9NGvQs{jHU$&TN~dm+VCG~L1if>0JyB&UJ?P}s|*XvYDtDOjp!8v%{OD9 z_jBL@@5O^_4?1?z~4_1y;W1qPNW=P-W$FR_7W8pozoVRLSruq0itBohtG2A_#h(81%;%7qI3aPb<@B3+Kn4F7_quAQ~lYFWsK;EFMmKX zK(#&cqH26`m$AGYp!h_DwKD!5AHnkHIUBzuztVfoZ&5}lL+`In+y^QK>wQXloCKiM)D=oc&p8%8hf<==TPwChXT>CYN^AjXhHD6!f^s>P7 z_FH2CliY7`DB%kuf8&^WKZ=XjBRkI}J-SQc0B+^wV2ytn5IBg`P5@2*f5ZC%X8xZt m$FHsJIuN-5z-NxAGu~%1J84$Ba~Gd&Kx$94;JC-A(EkESQjYTg literal 0 HcmV?d00001 diff --git a/dashboard/src/components/Header.vue b/dashboard/src/components/Header.vue index adaa680..ea34aa3 100644 --- a/dashboard/src/components/Header.vue +++ b/dashboard/src/components/Header.vue @@ -41,11 +41,14 @@ - - - + + + + + + @@ -78,14 +81,42 @@ export default defineComponent({ minimize() { Message.info("最小化窗口"); // 最小化窗口的逻辑,可以通过调用系统接口来实现 + this.exitFullScreen() }, maximize() { Message.info("最大化窗口"); // 最大化窗口的逻辑 + this.enterFullScreen() }, closeWindow() { - Message.info("关闭窗口"); + Message.info("将在1秒后关闭窗口"); // 关闭窗口的逻辑,可以通过调用系统接口来实现 + setTimeout(function () { + window.open("about:blank", "_self").close() + }, 1000) + }, + enterFullScreen() { + let element = document.documentElement; + if (element.requestFullscreen) { + element.requestFullscreen(); + } else if (element.mozRequestFullScreen) { /* Firefox */ + element.mozRequestFullScreen(); + } else if (element.webkitRequestFullscreen) { /* Chrome, Safari & Opera */ + element.webkitRequestFullscreen(); + } else if (element.msRequestFullscreen) { /* IE/Edge */ + element.msRequestFullscreen(); + } + }, + exitFullScreen() { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.mozCancelFullScreen) { /* Firefox */ + document.mozCancelFullScreen(); + } else if (document.webkitExitFullscreen) { /* Chrome, Safari and Opera */ + document.webkitExitFullscreen(); + } else if (document.msExitFullscreen) { /* IE/Edge */ + document.msExitFullscreen(); + } }, } }); diff --git a/dashboard/src/components/Layout.vue b/dashboard/src/components/Layout.vue index f5d6a7d..c4e32be 100644 --- a/dashboard/src/components/Layout.vue +++ b/dashboard/src/components/Layout.vue @@ -1,7 +1,20 @@ - @@ -57,7 +56,11 @@ +``` + +### 错误处理 + +所有API调用都包含了错误处理,会返回统一的错误格式: + +```javascript +try { + const result = await videoService.getRecommendVideos('site_key') +} catch (error) { + // error.code - 错误代码 + // error.message - 错误信息 + // error.data - 额外错误数据 + console.error('API调用失败:', error.message) +} +``` + +### 缓存机制 + +视频服务包含5分钟的缓存机制,相同的请求在5分钟内会返回缓存结果,提高性能。 + +## 配置说明 + +### API配置 (config.js) + +```javascript +// 基础配置 +export const BASE_URL = process.env.VUE_APP_API_BASE_URL || '' +export const TIMEOUT = 10000 + +// API路径 +export const API_PATHS = { + MODULE: '/api', // T4模块接口 + PROXY: '/proxy', // 代理接口 + PARSE: '/parse' // 解析接口 +} +``` + +### 请求拦截器 + +请求会自动添加: +- Authorization token(如果存在) +- Cache-Control: no-cache +- 统一的错误处理 + +## 迁移指南 + +### 从旧的req方式迁移 + +**旧方式:** +```javascript +import req from '@/utils/req' + +// 获取数据 +const response = await req.get('/home') +const data = response.data +``` + +**新方式:** +```javascript +import { videoService, siteService } from '@/api/services' + +// 获取数据 +const currentSite = siteService.getCurrentSite() +const data = await videoService.getRecommendVideos(currentSite.key, { + extend: currentSite.ext +}) +``` + +### 主要变化 + +1. **统一的服务接口**:不再直接调用HTTP方法,而是调用语义化的业务方法 +2. **自动错误处理**:统一的错误格式和处理机制 +3. **数据格式化**:返回的数据已经过格式化处理 +4. **缓存支持**:自动缓存机制提高性能 +5. **类型安全**:完整的类型定义和验证 + +## 注意事项 + +1. 所有API调用都需要先设置当前站点 +2. 确保传入正确的extend参数 +3. 处理好异步操作的错误情况 +4. 合理使用缓存机制,避免频繁请求 \ No newline at end of file diff --git a/dashboard/src/api/config.js b/dashboard/src/api/config.js new file mode 100644 index 0000000..d009770 --- /dev/null +++ b/dashboard/src/api/config.js @@ -0,0 +1,63 @@ +/** + * API配置文件 + * 定义接口相关的配置信息和常量 + */ + +// 基础配置 +export const API_CONFIG = { + // 基础URL配置 + BASE_URL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:5707', + + // 超时配置 + TIMEOUT: 30000, + + // 请求头配置 + HEADERS: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } +} + +// 接口路径常量 +export const API_PATHS = { + // 模块数据接口 + MODULE: '/api', + + // 代理接口 + PROXY: '/proxy', + + // 解析接口 + PARSE: '/parse' +} + +// 模块功能类型 +export const MODULE_ACTIONS = { + PLAY: 'play', // 播放接口 + CATEGORY: 'category', // 分类接口 + DETAIL: 'detail', // 详情接口 + ACTION: 'action', // 动作接口 + SEARCH: 'search', // 搜索接口 + REFRESH: 'refresh', // 刷新接口 + HOME: 'home' // 首页接口(默认) +} + +// 响应状态码 +export const RESPONSE_CODES = { + SUCCESS: 200, + NOT_FOUND: 404, + SERVER_ERROR: 500 +} + +// 默认分页配置 +export const PAGINATION = { + DEFAULT_PAGE: 1, + DEFAULT_PAGE_SIZE: 20 +} + +export default { + API_CONFIG, + API_PATHS, + MODULE_ACTIONS, + RESPONSE_CODES, + PAGINATION +} \ No newline at end of file diff --git a/dashboard/src/api/index.js b/dashboard/src/api/index.js new file mode 100644 index 0000000..9a803de --- /dev/null +++ b/dashboard/src/api/index.js @@ -0,0 +1,35 @@ +/** + * API模块统一入口 + * 提供所有业务接口的统一导出 + */ + +// 基础配置和工具 +export { default as request } from './request' +export { default as config } from './config' + +// 业务服务 +export { default as siteService } from './services/site' +export { default as videoService } from './services/video' +export { default as configService } from './services/config' + +// 业务接口模块 +export { default as moduleApi } from './modules/module' +export { default as proxyApi } from './modules/proxy' +export { default as parseApi } from './modules/parse' + +// 便捷导出常用接口 +export { + getHomeData, + getCategoryData, + getVideoDetail, + searchVideos, + refreshModule +} from './modules/module' + +export { + proxyRequest +} from './modules/proxy' + +export { + parseVideo +} from './modules/parse' \ No newline at end of file diff --git a/dashboard/src/api/modules/module.js b/dashboard/src/api/modules/module.js new file mode 100644 index 0000000..4070e78 --- /dev/null +++ b/dashboard/src/api/modules/module.js @@ -0,0 +1,302 @@ +/** + * 模块数据接口 (T4接口) + * 封装 /api/:module 相关的所有接口调用 + */ + +import { get, post } from '../request' +import { API_PATHS, MODULE_ACTIONS, PAGINATION } from '../config' +import axios from 'axios' + +/** + * 构建模块接口URL + * @param {string} module - 模块名称 + * @returns {string} 完整的接口URL + */ +const buildModuleUrl = (module) => { + // 对模块名称进行URL编码以支持中文字符 + const encodedModule = encodeURIComponent(module) + return `${API_PATHS.MODULE}/${encodedModule}` +} + +/** + * 直接调用站点API + * @param {string} apiUrl - 站点API地址 + * @param {object} params - 请求参数 + * @returns {Promise} API响应 + */ +const directApiCall = async (apiUrl, params = {}) => { + try { + const response = await axios.get(apiUrl, { + params, + timeout: 30000, + headers: { + 'Accept': 'application/json' + } + }) + + return response.data + } catch (error) { + console.error('直接API调用失败:', error) + throw error + } +} + +/** + * 获取首页数据(默认接口) + * @param {string} module - 模块名称 + * @param {object} options - 选项参数 + * @param {number} options.filter - 过滤条件(1表示启用,默认启用) + * @param {string} options.extend - 接口数据扩展参数 + * @param {string} options.apiUrl - 站点API地址(可选,如果提供则直接使用) + * @returns {Promise} 首页数据 + */ +export const getHomeData = async (module, options = {}) => { + const { filter = 1, extend, apiUrl } = options + const params = { filter } + + if (extend) { + params.extend = extend + } + + // 如果提供了apiUrl,直接使用站点的API地址 + if (apiUrl) { + return directApiCall(apiUrl, params) + } + + // 否则使用原来的代理方式 + return get(buildModuleUrl(module), params) +} + +/** + * 获取分类数据 + * @param {string} module - 模块名称 + * @param {object} params - 分类参数 + * @param {string} params.ac - 固定值 "category" + * @param {string} params.t - 分类ID + * @param {number} params.pg - 页码(从1开始) + * @param {string} params.ext - base64编码的筛选条件JSON字符串 + * @param {string} params.extend - 接口数据扩展参数 + * @param {string} params.apiUrl - 可选的直接API地址 + * @returns {Promise} 分类数据 + */ +export const getCategoryData = async (module, params) => { + const { + t: typeId, + pg = PAGINATION.DEFAULT_PAGE, + ext, + extend, + apiUrl + } = params + + const requestParams = { + ac: MODULE_ACTIONS.CATEGORY, + t: typeId, + pg + } + + if (ext) { + requestParams.ext = ext + } + + if (extend) { + requestParams.extend = extend + } + + // 如果提供了apiUrl,直接使用站点的API地址 + if (apiUrl) { + return directApiCall(apiUrl, requestParams) + } + + // 否则使用原来的代理方式 + return get(buildModuleUrl(module), requestParams) +} + +/** + * 获取视频详情 + * @param {string} module - 模块名称 + * @param {object} params - 详情参数 + * @param {string} params.ids - 视频ID + * @param {string} params.extend - 接口数据扩展参数 + * @param {string} params.apiUrl - 可选的直接API地址 + * @returns {Promise} 视频详情数据 + */ +export const getVideoDetail = async (module, params) => { + const { ids, extend, apiUrl } = params + + const requestParams = { + ac: MODULE_ACTIONS.DETAIL, + ids + } + + if (extend) { + requestParams.extend = extend + } + + // 如果提供了apiUrl,直接使用站点的API地址 + if (apiUrl) { + return directApiCall(apiUrl, requestParams) + } + + // 否则使用原来的代理方式 + return get(buildModuleUrl(module), requestParams) +} + +/** + * 播放接口 + * @param {string} module - 模块名称 + * @param {object} params - 播放参数 + * @param {string} params.play - 播放地址或ID + * @param {string} params.extend - 接口数据扩展参数 + * @param {string} params.apiUrl - 可选的直接API地址 + * @returns {Promise} 播放数据 + */ +export const getPlayData = async (module, params) => { + const { play, extend, apiUrl } = params + + const requestParams = { + ac: MODULE_ACTIONS.PLAY, + play + } + + if (extend) { + requestParams.extend = extend + } + + // 如果提供了apiUrl,直接使用站点的API地址 + if (apiUrl) { + return directApiCall(apiUrl, requestParams) + } + + // 否则使用原来的代理方式 + return get(buildModuleUrl(module), requestParams) +} + +/** + * 搜索接口 + * @param {string} module - 模块名称 + * @param {object} params - 搜索参数 + * @param {string} params.wd - 搜索关键词 + * @param {number} params.pg - 页码(从1开始) + * @param {string} params.extend - 接口数据扩展参数 + * @param {string} params.apiUrl - 可选的直接API地址 + * @returns {Promise} 搜索结果 + */ +export const searchVideos = async (module, params) => { + const { + wd: keyword, + pg = PAGINATION.DEFAULT_PAGE, + extend, + apiUrl + } = params + + const requestParams = { + wd: keyword, + pg + } + + if (extend) { + requestParams.extend = extend + } + + // 如果提供了apiUrl,直接使用站点的API地址 + if (apiUrl) { + return directApiCall(apiUrl, requestParams) + } + + // 否则使用原来的代理方式 + return get(buildModuleUrl(module), requestParams) +} + +/** + * 动作接口(POST请求) + * @param {string} module - 模块名称 + * @param {object} data - 动作数据 + * @param {string} data.action - 动作类型 + * @param {string} data.extend - 接口数据扩展参数 + * @param {string} data.apiUrl - 可选的直接API地址 + * @returns {Promise} 动作执行结果 + */ +export const executeAction = async (module, data) => { + const { action, extend, apiUrl, ...otherData } = data + + const requestData = { + ac: MODULE_ACTIONS.ACTION, + action, + ...otherData + } + + if (extend) { + requestData.extend = extend + } + + // 如果提供了apiUrl,直接使用站点的API地址(POST请求需要特殊处理) + if (apiUrl) { + // 对于POST请求,我们需要使用axios直接调用 + const axios = (await import('axios')).default + const response = await axios.post(apiUrl, requestData, { + timeout: 30000, + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + } + }) + return response.data + } + + // 否则使用原来的代理方式 + return post(buildModuleUrl(module), requestData) +} + +/** + * 刷新模块数据 + * @param {string} module - 模块名称 + * @param {string} extend - 接口数据扩展参数 + * @param {string} apiUrl - 可选的直接API地址 + * @returns {Promise} 刷新结果 + */ +export const refreshModule = async (module, extend, apiUrl) => { + const params = { + refresh: '1' + } + + if (extend) { + params.extend = extend + } + + // 如果提供了apiUrl,直接使用站点的API地址 + if (apiUrl) { + return directApiCall(apiUrl, params) + } + + // 否则使用原来的代理方式 + return get(buildModuleUrl(module), params) +} + +/** + * 通用模块接口调用 + * @param {string} module - 模块名称 + * @param {object} params - 请求参数 + * @param {string} method - 请求方法 ('GET' | 'POST') + * @returns {Promise} 接口响应 + */ +export const callModuleApi = async (module, params = {}, method = 'GET') => { + const url = buildModuleUrl(module) + + if (method.toUpperCase() === 'POST') { + return post(url, params) + } else { + return get(url, params) + } +} + +// 默认导出所有接口 +export default { + getHomeData, + getCategoryData, + getVideoDetail, + getPlayData, + searchVideos, + executeAction, + refreshModule, + callModuleApi +} \ No newline at end of file diff --git a/dashboard/src/api/modules/parse.js b/dashboard/src/api/modules/parse.js new file mode 100644 index 0000000..25a4dd9 --- /dev/null +++ b/dashboard/src/api/modules/parse.js @@ -0,0 +1,167 @@ +/** + * 解析接口 + * 封装 /parse/:jx 相关的视频解析接口 + */ + +import { get, post } from '../request' +import { API_PATHS } from '../config' + +/** + * 构建解析接口URL + * @param {string} jx - 解析器名称 + * @returns {string} 完整的解析URL + */ +const buildParseUrl = (jx) => { + return `${API_PATHS.PARSE}/${jx}` +} + +/** + * 解析视频地址 + * @param {string} jx - 解析器名称 + * @param {object} params - 解析参数 + * @param {string} params.url - 需要解析的视频URL + * @param {string} params.flag - 解析标识(可选) + * @param {object} params.headers - 自定义请求头(可选) + * @returns {Promise} 解析结果 + */ +export const parseVideo = async (jx, params) => { + const { url, flag, headers, ...otherParams } = params + + if (!url) { + throw new Error('视频URL不能为空') + } + + const requestParams = { + url, + ...otherParams + } + + if (flag) { + requestParams.flag = flag + } + + const requestOptions = {} + if (headers) { + requestOptions.headers = { + ...requestOptions.headers, + ...headers + } + } + + return get(buildParseUrl(jx), requestParams, requestOptions) +} + +/** + * POST方式解析视频(用于复杂参数) + * @param {string} jx - 解析器名称 + * @param {object} data - 解析数据 + * @param {string} data.url - 需要解析的视频URL + * @param {string} data.flag - 解析标识(可选) + * @param {object} data.headers - 自定义请求头(可选) + * @returns {Promise} 解析结果 + */ +export const parseVideoPost = async (jx, data) => { + const { url, headers, ...requestData } = data + + if (!url) { + throw new Error('视频URL不能为空') + } + + const requestOptions = {} + if (headers) { + requestOptions.headers = { + ...requestOptions.headers, + ...headers + } + } + + return post(buildParseUrl(jx), requestData, requestOptions) +} + +/** + * 批量解析视频 + * @param {string} jx - 解析器名称 + * @param {Array} urls - 视频URL数组 + * @param {object} options - 解析选项 + * @param {string} options.flag - 解析标识(可选) + * @param {object} options.headers - 自定义请求头(可选) + * @returns {Promise} 批量解析结果 + */ +export const parseVideoBatch = async (jx, urls, options = {}) => { + if (!Array.isArray(urls) || urls.length === 0) { + throw new Error('视频URL数组不能为空') + } + + const { flag, headers } = options + + const requestData = { + urls, + batch: true + } + + if (flag) { + requestData.flag = flag + } + + const requestOptions = {} + if (headers) { + requestOptions.headers = { + ...requestOptions.headers, + ...headers + } + } + + return post(buildParseUrl(jx), requestData, requestOptions) +} + +/** + * 获取解析器信息 + * @param {string} jx - 解析器名称 + * @returns {Promise} 解析器信息 + */ +export const getParserInfo = async (jx) => { + return get(buildParseUrl(jx), { info: true }) +} + +/** + * 测试解析器可用性 + * @param {string} jx - 解析器名称 + * @param {string} testUrl - 测试URL(可选) + * @returns {Promise} 测试结果 + */ +export const testParser = async (jx, testUrl) => { + const params = { test: true } + + if (testUrl) { + params.url = testUrl + } + + return get(buildParseUrl(jx), params) +} + +/** + * 通用解析接口调用 + * @param {string} jx - 解析器名称 + * @param {object} params - 请求参数 + * @param {string} method - 请求方法 ('GET' | 'POST') + * @returns {Promise} 解析响应 + */ +export const callParseApi = async (jx, params = {}, method = 'GET') => { + const url = buildParseUrl(jx) + + if (method.toUpperCase() === 'POST') { + return post(url, params) + } else { + return get(url, params) + } +} + +// 默认导出所有解析接口 +export default { + parseVideo, + parseVideoPost, + parseVideoBatch, + getParserInfo, + testParser, + callParseApi +} \ No newline at end of file diff --git a/dashboard/src/api/modules/proxy.js b/dashboard/src/api/modules/proxy.js new file mode 100644 index 0000000..f8cd7ee --- /dev/null +++ b/dashboard/src/api/modules/proxy.js @@ -0,0 +1,136 @@ +/** + * 模块代理接口 + * 封装 /proxy/:module/* 相关的代理转发接口 + */ + +import { get, post, put, del } from '../request' +import { API_PATHS } from '../config' + +/** + * 构建代理接口URL + * @param {string} module - 模块名称 + * @param {string} path - 代理路径 + * @returns {string} 完整的代理URL + */ +const buildProxyUrl = (module, path = '') => { + const basePath = `${API_PATHS.PROXY}/${module}` + return path ? `${basePath}/${path}` : basePath +} + +/** + * 代理GET请求 + * @param {string} module - 模块名称 + * @param {string} path - 代理路径 + * @param {object} params - 查询参数 + * @returns {Promise} 代理响应 + */ +export const proxyGet = async (module, path = '', params = {}) => { + const url = buildProxyUrl(module, path) + return get(url, params) +} + +/** + * 代理POST请求 + * @param {string} module - 模块名称 + * @param {string} path - 代理路径 + * @param {object} data - 请求数据 + * @returns {Promise} 代理响应 + */ +export const proxyPost = async (module, path = '', data = {}) => { + const url = buildProxyUrl(module, path) + return post(url, data) +} + +/** + * 代理PUT请求 + * @param {string} module - 模块名称 + * @param {string} path - 代理路径 + * @param {object} data - 请求数据 + * @returns {Promise} 代理响应 + */ +export const proxyPut = async (module, path = '', data = {}) => { + const url = buildProxyUrl(module, path) + return put(url, data) +} + +/** + * 代理DELETE请求 + * @param {string} module - 模块名称 + * @param {string} path - 代理路径 + * @param {object} params - 查询参数 + * @returns {Promise} 代理响应 + */ +export const proxyDelete = async (module, path = '', params = {}) => { + const url = buildProxyUrl(module, path) + return del(url, params) +} + +/** + * 通用代理请求 + * @param {string} module - 模块名称 + * @param {string} path - 代理路径 + * @param {object} options - 请求选项 + * @param {string} options.method - 请求方法 + * @param {object} options.params - 查询参数(GET请求) + * @param {object} options.data - 请求数据(POST/PUT请求) + * @returns {Promise} 代理响应 + */ +export const proxyRequest = async (module, path = '', options = {}) => { + const { method = 'GET', params, data } = options + + switch (method.toUpperCase()) { + case 'GET': + return proxyGet(module, path, params) + case 'POST': + return proxyPost(module, path, data) + case 'PUT': + return proxyPut(module, path, data) + case 'DELETE': + return proxyDelete(module, path, params) + default: + throw new Error(`不支持的请求方法: ${method}`) + } +} + +/** + * 代理文件上传 + * @param {string} module - 模块名称 + * @param {string} path - 代理路径 + * @param {FormData} formData - 文件数据 + * @returns {Promise} 上传响应 + */ +export const proxyUpload = async (module, path = '', formData) => { + const url = buildProxyUrl(module, path) + + return post(url, formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }) +} + +/** + * 代理文件下载 + * @param {string} module - 模块名称 + * @param {string} path - 代理路径 + * @param {object} params - 查询参数 + * @returns {Promise} 下载响应 + */ +export const proxyDownload = async (module, path = '', params = {}) => { + const url = buildProxyUrl(module, path) + + return get(url, params, { + responseType: 'blob' + }) +} + +// 默认导出所有代理接口 +export default { + proxyGet, + proxyPost, + proxyPut, + proxyDelete, + proxyRequest, + proxyUpload, + proxyDownload +} \ No newline at end of file diff --git a/dashboard/src/api/request.js b/dashboard/src/api/request.js new file mode 100644 index 0000000..07e6440 --- /dev/null +++ b/dashboard/src/api/request.js @@ -0,0 +1,157 @@ +/** + * 基础请求工具 + * 封装axios,提供统一的请求处理和错误处理 + */ + +import axios from 'axios' +import { API_CONFIG, RESPONSE_CODES } from './config' + +// 创建axios实例 +const request = axios.create({ + baseURL: API_CONFIG.BASE_URL, + timeout: API_CONFIG.TIMEOUT, + headers: API_CONFIG.HEADERS +}) + +// 请求拦截器 +request.interceptors.request.use( + (config) => { + // 添加认证token(如果存在) + const token = localStorage.getItem('token') + if (token) { + config.headers.Authorization = `Bearer ${token}` + } + + // 添加时间戳防止缓存 + if (config.method === 'get') { + config.params = { + ...config.params, + _t: Date.now() + } + } + + console.log('API Request:', config.method?.toUpperCase(), config.url, config.params || config.data) + return config + }, + (error) => { + console.error('Request Error:', error) + return Promise.reject(error) + } +) + +// 响应拦截器 +request.interceptors.response.use( + (response) => { + const { data, status } = response + + console.log('API Response:', response.config.url, data) + + // 检查HTTP状态码 + if (status !== 200) { + throw new Error(`HTTP Error: ${status}`) + } + + // 检查业务状态码 + if (data.code && data.code !== RESPONSE_CODES.SUCCESS) { + throw new Error(data.msg || `Business Error: ${data.code}`) + } + + return data + }, + (error) => { + console.error('Response Error:', error) + + // 处理网络错误 + if (!error.response) { + throw new Error('网络连接失败,请检查网络设置') + } + + // 处理HTTP错误状态码 + const { status } = error.response + switch (status) { + case 404: + throw new Error('请求的资源不存在') + case 500: + throw new Error('服务器内部错误') + case 401: + throw new Error('未授权访问') + case 403: + throw new Error('访问被禁止') + default: + throw new Error(`请求失败: ${status}`) + } + } +) + +/** + * 通用请求方法 + * @param {string} url - 请求URL + * @param {object} options - 请求选项 + * @returns {Promise} 请求结果 + */ +export const apiRequest = async (url, options = {}) => { + try { + const response = await request({ + url, + ...options + }) + return response + } catch (error) { + console.error('API Request Failed:', error.message) + throw error + } +} + +/** + * GET请求 + * @param {string} url - 请求URL + * @param {object} params - 查询参数 + * @returns {Promise} 请求结果 + */ +export const get = (url, params = {}) => { + return apiRequest(url, { + method: 'GET', + params + }) +} + +/** + * POST请求 + * @param {string} url - 请求URL + * @param {object} data - 请求数据 + * @returns {Promise} 请求结果 + */ +export const post = (url, data = {}) => { + return apiRequest(url, { + method: 'POST', + data + }) +} + +/** + * PUT请求 + * @param {string} url - 请求URL + * @param {object} data - 请求数据 + * @returns {Promise} 请求结果 + */ +export const put = (url, data = {}) => { + return apiRequest(url, { + method: 'PUT', + data + }) +} + +/** + * DELETE请求 + * @param {string} url - 请求URL + * @param {object} params - 查询参数 + * @returns {Promise} 请求结果 + */ +export const del = (url, params = {}) => { + return apiRequest(url, { + method: 'DELETE', + params + }) +} + +export default request \ No newline at end of file diff --git a/dashboard/src/api/services/config.js b/dashboard/src/api/services/config.js new file mode 100644 index 0000000..7269b8d --- /dev/null +++ b/dashboard/src/api/services/config.js @@ -0,0 +1,294 @@ +/** + * 配置管理服务 + * 负责管理应用配置,包括配置地址、站点数据等 + * 支持localStorage持久化存储 + */ + +import axios from 'axios' + +/** + * 配置管理类 + */ +class ConfigService { + constructor() { + this.configUrl = null + this.configData = null + this.lastFetchTime = null + this.cacheExpiry = 5 * 60 * 1000 // 5分钟缓存 + this.loadConfigFromStorage() + } + + /** + * 设置配置地址 + * @param {string} url - 配置地址URL + * @returns {Promise} 设置是否成功 + */ + async setConfigUrl(url) { + try { + if (!url || typeof url !== 'string') { + throw new Error('配置地址不能为空') + } + + // 验证URL格式 + const urlPattern = /^https?:\/\/.+/ + if (!urlPattern.test(url)) { + throw new Error('配置地址格式不正确,请输入有效的HTTP/HTTPS地址') + } + + // 测试配置地址是否可访问 + const isValid = await this.validateConfigUrl(url) + if (!isValid) { + throw new Error('配置地址无法访问或数据格式不正确') + } + + this.configUrl = url + this.saveConfigToStorage() + + console.log('配置地址设置成功:', url) + return true + } catch (error) { + console.error('设置配置地址失败:', error) + throw error + } + } + + /** + * 获取当前配置地址 + * @returns {string|null} 当前配置地址 + */ + getConfigUrl() { + return this.configUrl + } + + /** + * 验证配置地址是否有效 + * @param {string} url - 配置地址 + * @returns {Promise} 是否有效 + */ + async validateConfigUrl(url) { + try { + const response = await axios.get(url, { + timeout: 10000, + headers: { + 'Accept': 'application/json', + 'User-Agent': 'DrPlayer/1.0' + } + }) + + if (!response.data) { + return false + } + + // 验证数据格式是否包含必要字段 + const data = response.data + if (!data.sites || !Array.isArray(data.sites)) { + return false + } + + // 验证sites数组中的数据格式 + if (data.sites.length > 0) { + const firstSite = data.sites[0] + if (!firstSite.key || !firstSite.name || !firstSite.api) { + return false + } + } + + return true + } catch (error) { + console.error('验证配置地址失败:', error) + return false + } + } + + /** + * 获取配置数据 + * @param {boolean} forceRefresh - 是否强制刷新 + * @returns {Promise} 配置数据 + */ + async getConfigData(forceRefresh = false) { + try { + if (!this.configUrl) { + throw new Error('未设置配置地址') + } + + // 检查缓存是否有效 + const now = Date.now() + const isCacheValid = this.configData && + this.lastFetchTime && + (now - this.lastFetchTime) < this.cacheExpiry + + if (!forceRefresh && isCacheValid) { + console.log('使用缓存的配置数据') + return this.configData + } + + console.log('从配置地址获取数据:', this.configUrl) + const response = await axios.get(this.configUrl, { + timeout: 15000, + headers: { + 'Accept': 'application/json', + 'User-Agent': 'DrPlayer/1.0' + } + }) + + if (!response.data) { + throw new Error('配置数据为空') + } + + this.configData = response.data + this.lastFetchTime = now + this.saveConfigToStorage() + + console.log('配置数据获取成功,站点数量:', this.configData.sites?.length || 0) + return this.configData + } catch (error) { + console.error('获取配置数据失败:', error) + throw error + } + } + + /** + * 获取站点列表 + * @param {boolean} forceRefresh - 是否强制刷新 + * @returns {Promise} 站点列表 + */ + async getSites(forceRefresh = false) { + try { + const configData = await this.getConfigData(forceRefresh) + return configData.sites || [] + } catch (error) { + console.error('获取站点列表失败:', error) + throw error + } + } + + /** + * 获取推荐配置 + * @returns {Promise} 推荐配置列表 + */ + async getRecommendConfig() { + try { + const configData = await this.getConfigData() + return configData.recommend || [] + } catch (error) { + console.error('获取推荐配置失败:', error) + return [] + } + } + + /** + * 获取其他配置信息 + * @returns {Promise} 其他配置信息 + */ + async getOtherConfig() { + try { + const configData = await this.getConfigData() + const { sites, ...otherConfig } = configData + return otherConfig + } catch (error) { + console.error('获取其他配置失败:', error) + return {} + } + } + + /** + * 清除配置缓存 + */ + clearCache() { + this.configData = null + this.lastFetchTime = null + console.log('配置缓存已清除') + } + + /** + * 重置配置 + */ + resetConfig() { + this.configUrl = null + this.configData = null + this.lastFetchTime = null + this.saveConfigToStorage() + console.log('配置已重置') + } + + /** + * 从本地存储加载配置 + */ + loadConfigFromStorage() { + try { + const configUrl = localStorage.getItem('drplayer_config_url') + const configData = localStorage.getItem('drplayer_config_data') + const lastFetchTime = localStorage.getItem('drplayer_config_fetch_time') + + if (configUrl) { + this.configUrl = configUrl + } + + if (configData) { + this.configData = JSON.parse(configData) + } + + if (lastFetchTime) { + this.lastFetchTime = parseInt(lastFetchTime) + } + + console.log('从本地存储加载配置成功') + } catch (error) { + console.error('加载配置失败:', error) + } + } + + /** + * 保存配置到本地存储 + */ + saveConfigToStorage() { + try { + if (this.configUrl) { + localStorage.setItem('drplayer_config_url', this.configUrl) + } else { + localStorage.removeItem('drplayer_config_url') + } + + if (this.configData) { + localStorage.setItem('drplayer_config_data', JSON.stringify(this.configData)) + } else { + localStorage.removeItem('drplayer_config_data') + } + + if (this.lastFetchTime) { + localStorage.setItem('drplayer_config_fetch_time', this.lastFetchTime.toString()) + } else { + localStorage.removeItem('drplayer_config_fetch_time') + } + + console.log('配置保存到本地存储成功') + } catch (error) { + console.error('保存配置失败:', error) + } + } + + /** + * 获取配置状态信息 + * @returns {object} 配置状态 + */ + getConfigStatus() { + const hasValidData = this.configData && this.configData.sites && Array.isArray(this.configData.sites) + const sitesCount = hasValidData ? this.configData.sites.length : 0 + + return { + hasConfigUrl: !!this.configUrl, + hasConfigData: !!this.configData, + isValid: hasValidData, + sitesCount: sitesCount, + lastFetchTime: this.lastFetchTime, + cacheAge: this.lastFetchTime ? Date.now() - this.lastFetchTime : null, + isCacheValid: this.configData && this.lastFetchTime && + (Date.now() - this.lastFetchTime) < this.cacheExpiry + } + } +} + +// 创建单例实例 +const configService = new ConfigService() + +export default configService \ No newline at end of file diff --git a/dashboard/src/api/services/index.js b/dashboard/src/api/services/index.js new file mode 100644 index 0000000..663ac5f --- /dev/null +++ b/dashboard/src/api/services/index.js @@ -0,0 +1,19 @@ +/** + * 业务服务统一入口 + * 导出所有业务服务模块 + */ + +import videoService from './video' +import siteService from './site' + +// 导出所有服务 +export { + videoService, + siteService +} + +// 默认导出服务集合 +export default { + video: videoService, + site: siteService +} \ No newline at end of file diff --git a/dashboard/src/api/services/site.js b/dashboard/src/api/services/site.js new file mode 100644 index 0000000..18fd58c --- /dev/null +++ b/dashboard/src/api/services/site.js @@ -0,0 +1,504 @@ +/** + * 站点管理相关业务接口服务 + * 封装站点配置、管理和数据源切换功能 + */ + +import { proxyRequest } from '../modules/proxy' +import { validateModule } from '../utils' +import { createSiteInfo } from '../types' +import configService from './config' + +/** + * 站点服务类 + */ +class SiteService { + constructor() { + this.sites = new Map() + this.currentSite = null + this.loadSitesFromStorage() + this.initializeFromConfig() + } + + /** + * 获取所有站点配置 + * @returns {Array} 站点列表 + */ + getAllSites() { + return Array.from(this.sites.values()) + } + + /** + * 根据key获取站点信息 + * @param {string} siteKey - 站点标识 + * @returns {object|null} 站点信息 + */ + getSiteByKey(siteKey) { + return this.sites.get(siteKey) || null + } + + /** + * 获取当前选中的站点 + * @returns {object|null} 当前站点信息 + */ + getCurrentSite() { + return this.currentSite + } + + /** + * 设置当前站点 + * @param {string} siteKey - 站点标识 + * @returns {boolean} 设置是否成功 + */ + setCurrentSite(siteKey) { + const site = this.sites.get(siteKey) + if (!site) { + console.error('站点不存在:', siteKey) + return false + } + + this.currentSite = site + this.saveSitesToStorage() + + // 触发站点切换事件 + this.emitSiteChange(site) + + return true + } + + /** + * 添加站点 + * @param {object} siteInfo - 站点信息 + * @returns {boolean} 添加是否成功 + */ + addSite(siteInfo) { + try { + const site = this.formatSiteInfo(siteInfo) + + if (!site.key) { + throw new Error('站点标识不能为空') + } + + if (!site.name) { + throw new Error('站点名称不能为空') + } + + if (!site.api) { + throw new Error('API地址不能为空') + } + + this.sites.set(site.key, site) + this.saveSitesToStorage() + + console.log('添加站点成功:', site.name) + return true + } catch (error) { + console.error('添加站点失败:', error) + return false + } + } + + /** + * 更新站点信息 + * @param {string} siteKey - 站点标识 + * @param {object} updates - 更新的信息 + * @returns {boolean} 更新是否成功 + */ + updateSite(siteKey, updates) { + const site = this.sites.get(siteKey) + if (!site) { + console.error('站点不存在:', siteKey) + return false + } + + try { + const updatedSite = { ...site, ...updates } + const formattedSite = this.formatSiteInfo(updatedSite) + + this.sites.set(siteKey, formattedSite) + this.saveSitesToStorage() + + // 如果更新的是当前站点,同步更新 + if (this.currentSite && this.currentSite.key === siteKey) { + this.currentSite = formattedSite + } + + console.log('更新站点成功:', formattedSite.name) + return true + } catch (error) { + console.error('更新站点失败:', error) + return false + } + } + + /** + * 删除站点 + * @param {string} siteKey - 站点标识 + * @returns {boolean} 删除是否成功 + */ + removeSite(siteKey) { + const site = this.sites.get(siteKey) + if (!site) { + console.error('站点不存在:', siteKey) + return false + } + + this.sites.delete(siteKey) + this.saveSitesToStorage() + + // 如果删除的是当前站点,清空当前站点 + if (this.currentSite && this.currentSite.key === siteKey) { + this.currentSite = null + } + + console.log('删除站点成功:', site.name) + return true + } + + /** + * 批量导入站点 + * @param {Array} siteList - 站点列表 + * @returns {object} 导入结果 + */ + importSites(siteList) { + if (!Array.isArray(siteList)) { + throw new Error('站点列表必须是数组') + } + + const result = { + success: 0, + failed: 0, + errors: [] + } + + siteList.forEach((siteInfo, index) => { + try { + const success = this.addSite(siteInfo) + if (success) { + result.success++ + } else { + result.failed++ + result.errors.push(`第${index + 1}个站点添加失败`) + } + } catch (error) { + result.failed++ + result.errors.push(`第${index + 1}个站点添加失败: ${error.message}`) + } + }) + + console.log('批量导入站点完成:', result) + return result + } + + /** + * 导出站点配置 + * @returns {Array} 站点配置列表 + */ + exportSites() { + return this.getAllSites() + } + + /** + * 测试站点连接 + * @param {string} siteKey - 站点标识 + * @returns {Promise} 测试结果 + */ + async testSiteConnection(siteKey) { + const site = this.sites.get(siteKey) + if (!site) { + throw new Error('站点不存在') + } + + try { + // 尝试获取站点首页数据 + const response = await proxyRequest(site.key, '', { + method: 'GET', + params: { test: true } + }) + + return { + success: true, + message: '连接成功', + responseTime: Date.now(), + data: response + } + } catch (error) { + return { + success: false, + message: error.message || '连接失败', + error: error + } + } + } + + /** + * 获取站点统计信息 + * @returns {object} 统计信息 + */ + getSiteStats() { + const sites = this.getAllSites() + + return { + total: sites.length, + searchable: sites.filter(site => site.searchable).length, + filterable: sites.filter(site => site.filterable).length, + quickSearch: sites.filter(site => site.quickSearch).length, + byType: this.groupSitesByType(sites) + } + } + + /** + * 按类型分组站点 + * @param {Array} sites - 站点列表 + * @returns {object} 分组结果 + */ + groupSitesByType(sites) { + const groups = {} + + sites.forEach(site => { + const type = site.type || 0 + if (!groups[type]) { + groups[type] = [] + } + groups[type].push(site) + }) + + return groups + } + + /** + * 搜索站点 + * @param {string} keyword - 搜索关键词 + * @returns {Array} 搜索结果 + */ + searchSites(keyword) { + if (!keyword || keyword.trim().length === 0) { + return this.getAllSites() + } + + const searchTerm = keyword.trim().toLowerCase() + + return this.getAllSites().filter(site => + site.name.toLowerCase().includes(searchTerm) || + site.key.toLowerCase().includes(searchTerm) || + (site.api && site.api.toLowerCase().includes(searchTerm)) + ) + } + + /** + * 格式化站点信息 + * @param {object} rawSite - 原始站点数据 + * @returns {object} 格式化后的站点信息 + */ + formatSiteInfo(rawSite) { + const site = createSiteInfo() + + Object.keys(site).forEach(key => { + if (rawSite[key] !== undefined) { + site[key] = rawSite[key] + } + }) + + // 处理特殊字段 + site.searchable = rawSite.searchable ? 1 : 0 + site.quickSearch = rawSite.quickSearch ? 1 : 0 + site.filterable = rawSite.filterable ? 1 : 0 + site.type = parseInt(rawSite.type) || 0 + site.order = parseInt(rawSite.order) || 0 + + return site + } + + /** + * 从本地存储加载站点配置 + */ + loadSitesFromStorage() { + try { + const sitesData = localStorage.getItem('drplayer_sites') + const currentSiteKey = localStorage.getItem('drplayer_current_site') + + if (sitesData) { + const sites = JSON.parse(sitesData) + sites.forEach(site => { + this.sites.set(site.key, site) + }) + } + + if (currentSiteKey) { + this.currentSite = this.sites.get(currentSiteKey) + } + + console.log('从本地存储加载站点配置成功') + } catch (error) { + console.error('加载站点配置失败:', error) + } + } + + /** + * 保存站点配置到本地存储 + */ + saveSitesToStorage() { + try { + const sites = this.getAllSites() + localStorage.setItem('drplayer_sites', JSON.stringify(sites)) + + if (this.currentSite) { + localStorage.setItem('drplayer_current_site', this.currentSite.key) + } else { + localStorage.removeItem('drplayer_current_site') + } + + console.log('保存站点配置到本地存储成功') + } catch (error) { + console.error('保存站点配置失败:', error) + } + } + + /** + * 触发站点切换事件 + * @param {object} site - 新的站点信息 + */ + emitSiteChange(site) { + // 可以在这里添加事件监听器机制 + console.log('站点已切换:', site.name) + + // 触发自定义事件 + if (typeof window !== 'undefined') { + window.dispatchEvent(new CustomEvent('siteChange', { + detail: { site } + })) + } + } + + /** + * 清空所有站点 + */ + clearAllSites() { + this.sites.clear() + this.currentSite = null + this.saveSitesToStorage() + console.log('已清空所有站点配置') + } + + /** + * 从配置服务初始化站点数据 + */ + async initializeFromConfig() { + try { + const configStatus = configService.getConfigStatus() + if (configStatus.hasConfigUrl) { + await this.loadSitesFromConfig() + } + } catch (error) { + console.error('从配置服务初始化失败:', error) + } + } + + /** + * 从配置地址加载站点数据 + * @param {boolean} forceRefresh - 是否强制刷新 + * @returns {Promise} 站点列表 + */ + async loadSitesFromConfig(forceRefresh = false) { + try { + const sites = await configService.getSites(forceRefresh) + + if (sites && sites.length > 0) { + // 清空现有站点(保留本地添加的站点) + const localSites = Array.from(this.sites.values()).filter(site => site.isLocal) + this.sites.clear() + + // 重新添加本地站点 + localSites.forEach(site => { + this.sites.set(site.key, site) + }) + + // 添加配置中的站点 + sites.forEach(siteData => { + const siteInfo = this.formatSiteInfo(siteData) + siteInfo.isFromConfig = true // 标记为来自配置 + this.sites.set(siteInfo.key, siteInfo) + }) + + this.saveSitesToStorage() + console.log(`从配置加载了 ${sites.length} 个站点`) + + // 触发站点更新事件 + this.emitSitesUpdate() + + return sites + } + + return [] + } catch (error) { + console.error('从配置加载站点失败:', error) + throw error + } + } + + /** + * 刷新配置数据 + * @returns {Promise} 更新后的站点列表 + */ + async refreshConfig() { + try { + return await this.loadSitesFromConfig(true) + } catch (error) { + console.error('刷新配置失败:', error) + throw error + } + } + + /** + * 获取配置状态 + * @returns {object} 配置状态信息 + */ + getConfigStatus() { + return configService.getConfigStatus() + } + + /** + * 设置配置地址 + * @param {string} url - 配置地址 + * @returns {Promise} 设置是否成功 + */ + async setConfigUrl(url) { + try { + const success = await configService.setConfigUrl(url) + if (success) { + // 配置地址设置成功后,立即加载站点数据 + await this.loadSitesFromConfig(true) + } + return success + } catch (error) { + console.error('设置配置地址失败:', error) + throw error + } + } + + /** + * 获取当前配置地址 + * @returns {string|null} 配置地址 + */ + getConfigUrl() { + return configService.getConfigUrl() + } + + /** + * 触发站点更新事件 + */ + emitSitesUpdate() { + if (typeof window !== 'undefined') { + window.dispatchEvent(new CustomEvent('sitesUpdate', { + detail: { + sites: this.getAllSites(), + count: this.sites.size + } + })) + } + } +} + +// 创建单例实例 +const siteService = new SiteService() + +export default siteService \ No newline at end of file diff --git a/dashboard/src/api/services/video.js b/dashboard/src/api/services/video.js new file mode 100644 index 0000000..1fffddd --- /dev/null +++ b/dashboard/src/api/services/video.js @@ -0,0 +1,452 @@ +/** + * 视频相关业务接口服务 + * 封装视频播放、详情、搜索等功能 + */ + +import { + getHomeData, + getCategoryData, + getVideoDetail, + getPlayData, + searchVideos, + refreshModule +} from '../modules/module' +import { parseVideo } from '../modules/parse' +import { encodeFilters, validateModule, validateVideoId } from '../utils' +import { + createVideoInfo, + createSearchParams, + createPaginationInfo, + VIDEO_TYPES, + SORT_TYPES +} from '../types' + +/** + * 视频服务类 + */ +class VideoService { + constructor() { + this.cache = new Map() + this.cacheTimeout = 5 * 60 * 1000 // 5分钟缓存 + } + + /** + * 获取首页推荐视频 + * @param {string} module - 模块名称 + * @param {object} options - 选项参数 + * @param {string} options.apiUrl - API地址 + * @returns {Promise} 首页数据 + */ + async getRecommendVideos(module, options = {}) { + if (!validateModule(module)) { + throw new Error('无效的模块名称') + } + + const { apiUrl, ...otherOptions } = options + const cacheKey = `home_${module}_${JSON.stringify(options)}` + const cached = this.getFromCache(cacheKey) + if (cached) { + return cached + } + + try { + const requestOptions = { ...otherOptions } + if (apiUrl) { + requestOptions.apiUrl = apiUrl + } + const response = await getHomeData(module, requestOptions) + + // 格式化返回数据 + const result = { + categories: response.class || [], + filters: response.filters || {}, + videos: (response.list || []).map(this.formatVideoInfo), + pagination: this.createPagination(response) + } + + this.setCache(cacheKey, result) + return result + } catch (error) { + console.error('获取首页推荐视频失败:', error) + throw error + } + } + + /** + * 获取分类视频列表 + * @param {string} module - 模块名称 + * @param {object} params - 分类参数 + * @param {string} params.typeId - 分类ID + * @param {number} params.page - 页码 + * @param {object} params.filters - 筛选条件 + * @returns {Promise} 分类视频数据 + */ + async getCategoryVideos(module, params) { + if (!validateModule(module)) { + throw new Error('无效的模块名称') + } + + const { typeId, page = 1, filters = {}, apiUrl, extend } = params + + if (!typeId) { + throw new Error('分类ID不能为空') + } + + const cacheKey = `category_${module}_${typeId}_${page}_${JSON.stringify(filters)}` + const cached = this.getFromCache(cacheKey) + if (cached) { + return cached + } + + try { + const requestParams = { + t: typeId, + pg: page + } + + // 编码筛选条件 + if (Object.keys(filters).length > 0) { + requestParams.ext = encodeFilters(filters) + } + + // 添加extend参数 + if (extend) { + requestParams.extend = extend + } + + // 添加apiUrl参数 + if (apiUrl) { + requestParams.apiUrl = apiUrl + } + + const response = await getCategoryData(module, requestParams) + + // 格式化返回数据 + const result = { + videos: (response.list || []).map(this.formatVideoInfo), + pagination: this.createPagination(response, page), + filters: response.filters || {}, + total: response.total || 0 + } + + this.setCache(cacheKey, result) + return result + } catch (error) { + console.error('获取分类视频失败:', error) + throw error + } + } + + /** + * 获取视频详情 + * @param {string} module - 模块名称 + * @param {string} videoId - 视频ID + * @param {string} apiUrl - 站点API地址 + * @returns {Promise} 视频详情数据 + */ + async getVideoDetails(module, videoId, apiUrl) { + if (!validateModule(module)) { + throw new Error('无效的模块名称') + } + + if (!validateVideoId(videoId)) { + throw new Error('无效的视频ID') + } + + const cacheKey = `detail_${module}_${videoId}` + const cached = this.getFromCache(cacheKey) + if (cached) { + return cached + } + + try { + const params = { ids: videoId } + if (apiUrl) { + params.apiUrl = apiUrl + } + const response = await getVideoDetail(module, params) + + if (!response.list || response.list.length === 0) { + throw new Error('视频不存在') + } + + const videoInfo = this.formatVideoInfo(response.list[0]) + + // 解析播放地址 + if (videoInfo.vod_play_url) { + videoInfo.playList = this.parsePlayUrls(videoInfo.vod_play_url, videoInfo.vod_play_from) + } + + this.setCache(cacheKey, videoInfo) + return videoInfo + } catch (error) { + console.error('获取视频详情失败:', error) + throw error + } + } + + /** + * 搜索视频 + * @param {string} module - 模块名称 + * @param {object} params - 搜索参数 + * @param {string} params.keyword - 搜索关键词 + * @param {number} params.page - 页码 + * @param {string} params.extend - 扩展参数 + * @param {string} params.apiUrl - 站点API地址 + * @returns {Promise} 搜索结果 + */ + async searchVideo(module, params) { + if (!validateModule(module)) { + throw new Error('无效的模块名称') + } + + const { keyword, page = 1, extend, apiUrl } = params + + if (!keyword || keyword.trim().length === 0) { + throw new Error('搜索关键词不能为空') + } + + try { + const requestParams = { + wd: keyword.trim(), + pg: page + } + + // 添加extend参数 + if (extend) { + requestParams.extend = extend + } + + // 添加apiUrl参数 + if (apiUrl) { + requestParams.apiUrl = apiUrl + } + + const response = await searchVideos(module, requestParams) + + // 格式化返回数据 + const result = { + videos: (response.list || []).map(this.formatVideoInfo), + pagination: this.createPagination(response, page), + keyword: keyword.trim(), + total: response.total || 0 + } + + return result + } catch (error) { + console.error('搜索视频失败:', error) + throw error + } + } + + /** + * 获取播放地址 + * @param {string} module - 模块名称 + * @param {string} playUrl - 播放地址或ID + * @param {string} apiUrl - 站点API地址 + * @returns {Promise} 播放数据 + */ + async getPlayUrl(module, playUrl, apiUrl) { + if (!validateModule(module)) { + throw new Error('无效的模块名称') + } + + if (!playUrl) { + throw new Error('播放地址不能为空') + } + + try { + const params = { play: playUrl } + if (apiUrl) { + params.apiUrl = apiUrl + } + const response = await getPlayData(module, params) + + return { + url: response.url || playUrl, + headers: response.headers || {}, + parse: response.parse || false, + jx: response.jx || '' + } + } catch (error) { + console.error('获取播放地址失败:', error) + throw error + } + } + + /** + * 解析视频地址 + * @param {string} jx - 解析器名称 + * @param {string} url - 视频地址 + * @param {object} options - 解析选项 + * @returns {Promise} 解析结果 + */ + async parseVideoUrl(jx, url, options = {}) { + if (!jx) { + throw new Error('解析器名称不能为空') + } + + if (!url) { + throw new Error('视频地址不能为空') + } + + try { + const response = await parseVideo(jx, { url, ...options }) + + return { + url: response.url || url, + type: response.type || 'mp4', + headers: response.headers || {}, + success: response.success !== false + } + } catch (error) { + console.error('解析视频地址失败:', error) + throw error + } + } + + /** + * 刷新模块数据 + * @param {string} module - 模块名称 + * @returns {Promise} 刷新结果 + */ + async refreshModuleData(module) { + if (!validateModule(module)) { + throw new Error('无效的模块名称') + } + + try { + // 清除相关缓存 + this.clearModuleCache(module) + + const response = await refreshModule(module) + + return { + success: true, + message: response.msg || '刷新成功', + lastUpdate: response.data?.lastUpdate || new Date().toISOString() + } + } catch (error) { + console.error('刷新模块数据失败:', error) + throw error + } + } + + /** + * 格式化视频信息 + * @param {object} rawVideo - 原始视频数据 + * @returns {object} 格式化后的视频信息 + */ + formatVideoInfo(rawVideo) { + const video = createVideoInfo() + + Object.keys(video).forEach(key => { + if (rawVideo[key] !== undefined) { + video[key] = rawVideo[key] + } + }) + + // 处理特殊字段 + if (rawVideo.vod_hits) { + video.vod_hits = parseInt(rawVideo.vod_hits) || 0 + } + + if (rawVideo.vod_score) { + video.vod_score = parseFloat(rawVideo.vod_score) || 0 + } + + return video + } + + /** + * 解析播放地址列表 + * @param {string} playUrls - 播放地址字符串 + * @param {string} playFrom - 播放来源字符串 + * @returns {Array} 播放地址列表 + */ + parsePlayUrls(playUrls, playFrom) { + if (!playUrls) return [] + + const fromList = playFrom ? playFrom.split('$$$') : ['默认'] + const urlList = playUrls.split('$$$') + + return fromList.map((from, index) => ({ + from: from.trim(), + urls: this.parseEpisodeUrls(urlList[index] || ''), + index + })) + } + + /** + * 解析剧集地址 + * @param {string} episodeUrls - 剧集地址字符串 + * @returns {Array} 剧集地址列表 + */ + parseEpisodeUrls(episodeUrls) { + if (!episodeUrls) return [] + + return episodeUrls.split('#').map((episode, index) => { + const [name, url] = episode.split('$') + return { + name: name || `第${index + 1}集`, + url: url || '', + index + } + }).filter(episode => episode.url) + } + + /** + * 创建分页信息 + * @param {object} response - 响应数据 + * @param {number} currentPage - 当前页码 + * @returns {object} 分页信息 + */ + createPagination(response, currentPage = 1) { + const pagination = createPaginationInfo() + + pagination.page = currentPage + pagination.total = response.total || 0 + pagination.pageSize = response.limit || 20 + pagination.totalPages = Math.ceil(pagination.total / pagination.pageSize) + pagination.hasNext = currentPage < pagination.totalPages + pagination.hasPrev = currentPage > 1 + + return pagination + } + + /** + * 缓存操作 + */ + getFromCache(key) { + const cached = this.cache.get(key) + if (cached && Date.now() - cached.timestamp < this.cacheTimeout) { + return cached.data + } + this.cache.delete(key) + return null + } + + setCache(key, data) { + this.cache.set(key, { + data, + timestamp: Date.now() + }) + } + + clearModuleCache(module) { + for (const key of this.cache.keys()) { + if (key.includes(module)) { + this.cache.delete(key) + } + } + } + + clearAllCache() { + this.cache.clear() + } +} + +// 创建单例实例 +const videoService = new VideoService() + +export default videoService \ No newline at end of file diff --git a/dashboard/src/api/types/index.js b/dashboard/src/api/types/index.js new file mode 100644 index 0000000..c38f13f --- /dev/null +++ b/dashboard/src/api/types/index.js @@ -0,0 +1,218 @@ +/** + * API类型定义 + * 定义接口相关的数据结构、枚举和常量 + */ + +// 视频类型枚举 +export const VIDEO_TYPES = { + MOVIE: 'movie', // 电影 + TV: 'tv', // 电视剧 + VARIETY: 'variety', // 综艺 + CARTOON: 'cartoon', // 动漫 + CHILD: 'child', // 少儿 + DOCUMENTARY: 'doco', // 纪录片 + CHOICE: 'choice' // 精选 +} + +// 视频状态枚举 +export const VIDEO_STATUS = { + NORMAL: 'normal', // 正常 + UPDATING: 'updating', // 更新中 + COMPLETED: 'completed', // 已完结 + PREVIEW: 'preview' // 预告 +} + +// 排序方式枚举 +export const SORT_TYPES = { + TIME: 'time', // 按时间排序 + NAME: 'name', // 按名称排序 + HITS: 'hits', // 按点击量排序 + SCORE: 'score' // 按评分排序 +} + +// 排序顺序枚举 +export const SORT_ORDER = { + ASC: 'asc', // 升序 + DESC: 'desc' // 降序 +} + +// 地区枚举 +export const REGIONS = { + MAINLAND: 'mainland', // 大陆 + HONGKONG: 'hongkong', // 香港 + TAIWAN: 'taiwan', // 台湾 + KOREA: 'korea', // 韩国 + JAPAN: 'japan', // 日本 + USA: 'usa', // 美国 + UK: 'uk', // 英国 + FRANCE: 'france', // 法国 + GERMANY: 'germany', // 德国 + OTHER: 'other' // 其他 +} + +// 年份范围 +export const YEAR_RANGES = { + RECENT: '2020-2024', // 近期 + CLASSIC: '2010-2019', // 经典 + OLD: '2000-2009', // 怀旧 + ANCIENT: '1990-1999' // 古典 +} + +// 请求状态枚举 +export const REQUEST_STATUS = { + IDLE: 'idle', // 空闲 + LOADING: 'loading', // 加载中 + SUCCESS: 'success', // 成功 + ERROR: 'error' // 错误 +} + +// 缓存策略枚举 +export const CACHE_STRATEGY = { + NO_CACHE: 'no-cache', // 不缓存 + CACHE_FIRST: 'cache-first', // 缓存优先 + NETWORK_FIRST: 'network-first', // 网络优先 + CACHE_ONLY: 'cache-only' // 仅缓存 +} + +/** + * 视频信息数据结构 + */ +export const createVideoInfo = () => ({ + vod_id: '', // 视频ID + vod_name: '', // 视频名称 + vod_pic: '', // 视频封面 + vod_remarks: '', // 视频备注 + vod_content: '', // 视频简介 + vod_play_from: '', // 播放来源 + vod_play_url: '', // 播放地址 + vod_time: '', // 更新时间 + vod_year: '', // 年份 + vod_area: '', // 地区 + vod_lang: '', // 语言 + vod_actor: '', // 演员 + vod_director: '', // 导演 + vod_writer: '', // 编剧 + vod_blurb: '', // 简介 + vod_class: '', // 分类 + vod_tag: '', // 标签 + vod_score: '', // 评分 + vod_hits: 0, // 点击量 + vod_duration: '', // 时长 + vod_total: 0, // 总集数 + vod_serial: 0, // 当前集数 + vod_tv: '', // 电视台 + vod_weekday: '', // 播出时间 + vod_status: VIDEO_STATUS.NORMAL +}) + +/** + * 分类信息数据结构 + */ +export const createCategoryInfo = () => ({ + type_id: '', // 分类ID + type_name: '', // 分类名称 + type_sort: 0, // 排序 + type_status: 1 // 状态 +}) + +/** + * 筛选条件数据结构 + */ +export const createFilterInfo = () => ({ + type: '', // 类型 + area: '', // 地区 + year: '', // 年份 + lang: '', // 语言 + sort: SORT_TYPES.TIME, // 排序方式 + order: SORT_ORDER.DESC // 排序顺序 +}) + +/** + * 分页信息数据结构 + */ +export const createPaginationInfo = () => ({ + page: 1, // 当前页码 + pageSize: 20, // 每页数量 + total: 0, // 总数量 + totalPages: 0, // 总页数 + hasNext: false, // 是否有下一页 + hasPrev: false // 是否有上一页 +}) + +/** + * 搜索参数数据结构 + */ +export const createSearchParams = () => ({ + keyword: '', // 搜索关键词 + type: '', // 搜索类型 + page: 1, // 页码 + pageSize: 20, // 每页数量 + filters: createFilterInfo() // 筛选条件 +}) + +/** + * API响应数据结构 + */ +export const createApiResponse = () => ({ + code: 200, // 状态码 + msg: '', // 消息 + data: null, // 数据 + timestamp: Date.now() // 时间戳 +}) + +/** + * 播放信息数据结构 + */ +export const createPlayInfo = () => ({ + url: '', // 播放地址 + type: '', // 播放类型 + headers: {}, // 请求头 + parse: false, // 是否需要解析 + jx: '' // 解析器 +}) + +/** + * 站点信息数据结构 + */ +export const createSiteInfo = () => ({ + key: '', // 站点标识 + name: '', // 站点名称 + type: 0, // 站点类型 + api: '', // API地址 + searchable: 1, // 是否可搜索 + quickSearch: 1, // 是否支持快速搜索 + filterable: 1, // 是否可筛选 + order: 0, // 排序 + ext: '' // 扩展参数 +}) + +/** + * 错误信息数据结构 + */ +export const createErrorInfo = () => ({ + code: '', // 错误码 + message: '', // 错误信息 + details: null, // 错误详情 + timestamp: Date.now() // 时间戳 +}) + +// 默认导出所有类型定义 +export default { + VIDEO_TYPES, + VIDEO_STATUS, + SORT_TYPES, + SORT_ORDER, + REGIONS, + YEAR_RANGES, + REQUEST_STATUS, + CACHE_STRATEGY, + createVideoInfo, + createCategoryInfo, + createFilterInfo, + createPaginationInfo, + createSearchParams, + createApiResponse, + createPlayInfo, + createSiteInfo, + createErrorInfo +} \ No newline at end of file diff --git a/dashboard/src/api/utils/index.js b/dashboard/src/api/utils/index.js new file mode 100644 index 0000000..b40b8da --- /dev/null +++ b/dashboard/src/api/utils/index.js @@ -0,0 +1,270 @@ +/** + * API工具函数 + * 提供常用的数据处理、验证和转换功能 + */ + +/** + * Base64编码 + * @param {string} str - 需要编码的字符串 + * @returns {string} Base64编码结果 + */ +export const base64Encode = (str) => { + try { + return btoa(unescape(encodeURIComponent(str))) + } catch (error) { + console.error('Base64编码失败:', error) + return str + } +} + +/** + * Base64解码 + * @param {string} str - 需要解码的Base64字符串 + * @returns {string} 解码结果 + */ +export const base64Decode = (str) => { + try { + return decodeURIComponent(escape(atob(str))) + } catch (error) { + console.error('Base64解码失败:', error) + return str + } +} + +/** + * 将筛选条件对象转换为Base64编码的JSON字符串 + * @param {object} filters - 筛选条件对象 + * @returns {string} Base64编码的JSON字符串 + */ +export const encodeFilters = (filters) => { + if (!filters || typeof filters !== 'object') { + return '' + } + + try { + const jsonStr = JSON.stringify(filters) + return base64Encode(jsonStr) + } catch (error) { + console.error('筛选条件编码失败:', error) + return '' + } +} + +/** + * 将Base64编码的JSON字符串解码为筛选条件对象 + * @param {string} encodedFilters - Base64编码的JSON字符串 + * @returns {object} 筛选条件对象 + */ +export const decodeFilters = (encodedFilters) => { + if (!encodedFilters || typeof encodedFilters !== 'string') { + return {} + } + + try { + const jsonStr = base64Decode(encodedFilters) + return JSON.parse(jsonStr) + } catch (error) { + console.error('筛选条件解码失败:', error) + return {} + } +} + +/** + * 构建查询参数字符串 + * @param {object} params - 参数对象 + * @returns {string} 查询参数字符串 + */ +export const buildQueryString = (params) => { + if (!params || typeof params !== 'object') { + return '' + } + + const searchParams = new URLSearchParams() + + Object.keys(params).forEach(key => { + const value = params[key] + if (value !== null && value !== undefined && value !== '') { + searchParams.append(key, String(value)) + } + }) + + return searchParams.toString() +} + +/** + * 解析查询参数字符串 + * @param {string} queryString - 查询参数字符串 + * @returns {object} 参数对象 + */ +export const parseQueryString = (queryString) => { + if (!queryString || typeof queryString !== 'string') { + return {} + } + + const params = {} + const searchParams = new URLSearchParams(queryString) + + for (const [key, value] of searchParams) { + params[key] = value + } + + return params +} + +/** + * 验证模块名称 + * @param {string} module - 模块名称 + * @returns {boolean} 是否有效 + */ +export const validateModule = (module) => { + if (!module || typeof module !== 'string') { + return false + } + + // 模块名称不能为空,且长度不能超过100个字符 + // 支持字母、数字、中文、下划线、连字符、方括号、圆括号等常见字符 + if (module.trim().length === 0 || module.length > 100) { + return false + } + + // 不允许包含特殊控制字符 + const invalidChars = /[\x00-\x1F\x7F]/ + return !invalidChars.test(module) +} + +/** + * 验证视频ID + * @param {string} videoId - 视频ID + * @returns {boolean} 是否有效 + */ +export const validateVideoId = (videoId) => { + if (!videoId || typeof videoId !== 'string') { + return false + } + + // 视频ID不能为空且长度合理 + return videoId.trim().length > 0 && videoId.length <= 100 +} + +/** + * 验证URL格式 + * @param {string} url - URL字符串 + * @returns {boolean} 是否有效 + */ +export const validateUrl = (url) => { + if (!url || typeof url !== 'string') { + return false + } + + try { + new URL(url) + return true + } catch { + return false + } +} + +/** + * 格式化错误信息 + * @param {Error|string} error - 错误对象或错误信息 + * @returns {string} 格式化后的错误信息 + */ +export const formatError = (error) => { + if (!error) { + return '未知错误' + } + + if (typeof error === 'string') { + return error + } + + if (error instanceof Error) { + return error.message || '请求失败' + } + + if (error.response && error.response.data) { + const { data } = error.response + return data.msg || data.message || '服务器错误' + } + + return '网络请求失败' +} + +/** + * 深度合并对象 + * @param {object} target - 目标对象 + * @param {object} source - 源对象 + * @returns {object} 合并后的对象 + */ +export const deepMerge = (target, source) => { + if (!target || typeof target !== 'object') { + return source + } + + if (!source || typeof source !== 'object') { + return target + } + + const result = { ...target } + + Object.keys(source).forEach(key => { + if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) { + result[key] = deepMerge(result[key], source[key]) + } else { + result[key] = source[key] + } + }) + + return result +} + +/** + * 防抖函数 + * @param {Function} func - 需要防抖的函数 + * @param {number} delay - 延迟时间(毫秒) + * @returns {Function} 防抖后的函数 + */ +export const debounce = (func, delay) => { + let timeoutId + + return function (...args) { + clearTimeout(timeoutId) + timeoutId = setTimeout(() => func.apply(this, args), delay) + } +} + +/** + * 节流函数 + * @param {Function} func - 需要节流的函数 + * @param {number} delay - 延迟时间(毫秒) + * @returns {Function} 节流后的函数 + */ +export const throttle = (func, delay) => { + let lastCall = 0 + + return function (...args) { + const now = Date.now() + + if (now - lastCall >= delay) { + lastCall = now + return func.apply(this, args) + } + } +} + +// 默认导出所有工具函数 +export default { + base64Encode, + base64Decode, + encodeFilters, + decodeFilters, + buildQueryString, + parseQueryString, + validateModule, + validateVideoId, + validateUrl, + formatError, + deepMerge, + debounce, + throttle +} \ No newline at end of file diff --git a/dashboard/src/components/Breadcrumb.vue b/dashboard/src/components/Breadcrumb.vue index 4a87a7b..9dd5d37 100644 --- a/dashboard/src/components/Breadcrumb.vue +++ b/dashboard/src/components/Breadcrumb.vue @@ -1,8 +1,8 @@ + + \ No newline at end of file diff --git a/dashboard/src/components/VideoGrid.vue b/dashboard/src/components/VideoGrid.vue index 56d918b..b60182b 100644 --- a/dashboard/src/components/VideoGrid.vue +++ b/dashboard/src/components/VideoGrid.vue @@ -12,7 +12,7 @@ :key="video.vod_id" class="video_list_hover" > -
+
import { onMounted, nextTick, ref, onBeforeUnmount, watch } from 'vue'; +import { useRouter } from 'vue-router'; const props = defineProps({ videos: { @@ -72,10 +73,32 @@ const props = defineProps({ const emit = defineEmits(['load-more', 'scroll-bottom']); +const router = useRouter(); const containerRef = ref(null); const scrollbarRef = ref(null); const scrollAreaHeight = ref(0); +// 视频点击处理 +const handleVideoClick = (video) => { + if (video && video.vod_id) { + router.push({ + name: 'VideoDetail', + params: { id: video.vod_id }, + query: { + name: video.vod_name, + pic: video.vod_pic, + year: video.vod_year, + area: video.vod_area, + type: video.vod_type, + remarks: video.vod_remarks, + content: video.vod_content, + actor: video.vod_actor, + director: video.vod_director + } + }); + } +}; + const updateScrollAreaHeight = () => { nextTick(() => { setTimeout(() => { diff --git a/dashboard/src/main.js b/dashboard/src/main.js index a80cd1e..e92ccd0 100644 --- a/dashboard/src/main.js +++ b/dashboard/src/main.js @@ -6,11 +6,41 @@ import ArcoVue from '@arco-design/web-vue' import ArcoVueIcon from '@arco-design/web-vue/es/icon'; import '@arco-design/web-vue/dist/arco.css' import {createPinia} from 'pinia' +import 'viewerjs/dist/viewer.css' +import VueViewer from 'v-viewer' +import ECharts from 'vue-echarts' +import { use } from 'echarts/core' +import { + CanvasRenderer +} from 'echarts/renderers' +import { + BarChart, + LineChart, + PieChart +} from 'echarts/charts' +import { + GridComponent, + TooltipComponent, + LegendComponent, + TitleComponent +} from 'echarts/components' +use([ + CanvasRenderer, + BarChart, + LineChart, + PieChart, + GridComponent, + TooltipComponent, + LegendComponent, + TitleComponent +]) const app = createApp(App) app.use(router) app.use(ArcoVue); app.use(ArcoVueIcon); app.use(createPinia()) +app.use(VueViewer) +app.component('v-chart', ECharts) app.mount('#app') \ No newline at end of file diff --git a/dashboard/src/router/index.js b/dashboard/src/router/index.js index 8f9ef4d..e95e5bd 100644 --- a/dashboard/src/router/index.js +++ b/dashboard/src/router/index.js @@ -1,6 +1,7 @@ import {createRouter, createWebHistory} from 'vue-router'; import Home from '@/views/Home.vue'; import Video from '@/views/Video.vue'; +import VideoDetail from '@/views/VideoDetail.vue'; import Live from '@/views/Live.vue'; import Settings from '@/views/Settings.vue'; import Collection from '@/views/Collection.vue'; @@ -9,19 +10,49 @@ import Reader from '@/views/Reader.vue'; import Parser from '@/views/Parser.vue'; const routes = [ - {path: '/', component: Home}, - {path: '/video', component: Video}, - {path: '/live', component: Live}, - {path: '/settings', component: Settings}, - {path: '/collection', component: Collection}, - {path: '/history', component: History}, - {path: '/reader', component: Reader}, - {path: '/parser', component: Parser}, + {path: '/', component: Home, name: 'Home'}, + {path: '/video', component: Video, name: 'Video'}, + {path: '/video/:id', component: VideoDetail, name: 'VideoDetail', props: true}, + {path: '/live', component: Live, name: 'Live'}, + {path: '/settings', component: Settings, name: 'Settings'}, + {path: '/collection', component: Collection, name: 'Collection'}, + {path: '/history', component: History, name: 'History'}, + {path: '/reader', component: Reader, name: 'Reader'}, + {path: '/parser', component: Parser, name: 'Parser'}, + // 404 fallback路由 - 必须放在最后 + {path: '/:pathMatch(.*)*', redirect: '/'} ]; +// 获取base路径,支持子目录部署 +const getBasePath = () => { + // 在生产环境中,如果设置了VITE_BASE_PATH环境变量,使用它 + if (import.meta.env.PROD && import.meta.env.VITE_BASE_PATH) { + return import.meta.env.VITE_BASE_PATH; + } + // 否则使用Vite的BASE_URL + return import.meta.env.BASE_URL; +}; + const router = createRouter({ - history: createWebHistory(import.meta.env.BASE_URL), + history: createWebHistory(getBasePath()), routes, + // 滚动行为 + scrollBehavior(to, from, savedPosition) { + if (savedPosition) { + return savedPosition; + } else { + return { top: 0 }; + } + } +}); + +// 路由守卫 - 可以在这里添加权限检查等逻辑 +router.beforeEach((to, from, next) => { + // 设置页面标题 + if (to.name) { + document.title = `DrPlayer - ${to.name}`; + } + next(); }); export default router; diff --git a/dashboard/src/stores/favoriteStore.js b/dashboard/src/stores/favoriteStore.js new file mode 100644 index 0000000..b3d51bf --- /dev/null +++ b/dashboard/src/stores/favoriteStore.js @@ -0,0 +1,214 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' + +export const useFavoriteStore = defineStore('favorite', () => { + // 收藏列表 + const favorites = ref([]) + + // 从localStorage加载收藏数据 + const loadFavorites = () => { + try { + const stored = localStorage.getItem('drplayer-favorites') + if (stored) { + favorites.value = JSON.parse(stored) + } + } catch (error) { + console.error('加载收藏数据失败:', error) + favorites.value = [] + } + } + + // 保存收藏数据到localStorage + const saveFavorites = () => { + try { + localStorage.setItem('drplayer-favorites', JSON.stringify(favorites.value)) + } catch (error) { + console.error('保存收藏数据失败:', error) + } + } + + // 添加收藏 + const addFavorite = (videoData) => { + const favoriteItem = { + id: videoData.vod_id, + name: videoData.vod_name, + pic: videoData.vod_pic, + year: videoData.vod_year, + area: videoData.vod_area, + type_name: videoData.type_name, + remarks: videoData.vod_remarks, + director: videoData.vod_director, + actor: videoData.vod_actor, + // 保存API调用信息,用于从收藏进入详情页 + api_info: { + module: videoData.module || '', + api_url: videoData.api_url || '', + site_name: videoData.site_name || '' + }, + created_at: new Date().toISOString(), + updated_at: new Date().toISOString() + } + + // 检查是否已存在 + const existingIndex = favorites.value.findIndex(item => + item.id === favoriteItem.id && + item.api_info.api_url === favoriteItem.api_info.api_url + ) + + if (existingIndex === -1) { + favorites.value.unshift(favoriteItem) + saveFavorites() + return true + } + return false + } + + // 移除收藏 + const removeFavorite = (videoId, apiUrl) => { + const index = favorites.value.findIndex(item => + item.id === videoId && item.api_info.api_url === apiUrl + ) + + if (index !== -1) { + favorites.value.splice(index, 1) + saveFavorites() + return true + } + return false + } + + // 检查是否已收藏 + const isFavorited = (videoId, apiUrl) => { + return favorites.value.some(item => + item.id === videoId && item.api_info.api_url === apiUrl + ) + } + + // 获取收藏项 + const getFavorite = (videoId, apiUrl) => { + return favorites.value.find(item => + item.id === videoId && item.api_info.api_url === apiUrl + ) + } + + // 清空收藏 + const clearFavorites = () => { + favorites.value = [] + saveFavorites() + } + + // 导出收藏数据 + const exportFavorites = () => { + const exportData = { + version: '1.0', + export_time: new Date().toISOString(), + favorites: favorites.value + } + + const blob = new Blob([JSON.stringify(exportData, null, 2)], { + type: 'application/json' + }) + + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = `drplayer-favorites-${new Date().toISOString().split('T')[0]}.json` + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + URL.revokeObjectURL(url) + } + + // 导入收藏数据 + const importFavorites = (file) => { + return new Promise((resolve, reject) => { + const reader = new FileReader() + + reader.onload = (e) => { + try { + const importData = JSON.parse(e.target.result) + + // 验证数据格式 + if (!importData.favorites || !Array.isArray(importData.favorites)) { + throw new Error('无效的收藏数据格式') + } + + // 合并数据,避免重复 + let importCount = 0 + importData.favorites.forEach(item => { + const exists = favorites.value.some(existing => + existing.id === item.id && + existing.api_info.api_url === item.api_info.api_url + ) + + if (!exists) { + favorites.value.push({ + ...item, + updated_at: new Date().toISOString() + }) + importCount++ + } + }) + + saveFavorites() + resolve(importCount) + } catch (error) { + reject(error) + } + } + + reader.onerror = () => { + reject(new Error('文件读取失败')) + } + + reader.readAsText(file) + }) + } + + // 计算属性 + const favoriteCount = computed(() => favorites.value.length) + + const favoritesByType = computed(() => { + const grouped = {} + favorites.value.forEach(item => { + // 根据站源名称中的标识进行分类 + const siteName = item.api_info?.site_name || '' + let type = '影视' // 默认分类 + + if (siteName.includes('[书]')) { + type = '小说' + } else if (siteName.includes('[画]')) { + type = '漫画' + } else if (siteName.includes('[密]')) { + type = '密' + } else if (siteName.includes('[听]')) { + type = '音频' + } else if (siteName.includes('[儿]')) { + type = '少儿' + } + + if (!grouped[type]) { + grouped[type] = [] + } + grouped[type].push(item) + }) + return grouped + }) + + // 初始化时加载数据 + loadFavorites() + + return { + favorites, + favoriteCount, + favoritesByType, + addFavorite, + removeFavorite, + isFavorited, + getFavorite, + clearFavorites, + exportFavorites, + importFavorites, + loadFavorites + } +}) \ No newline at end of file diff --git a/dashboard/src/stores/historyStore.js b/dashboard/src/stores/historyStore.js new file mode 100644 index 0000000..2eda638 --- /dev/null +++ b/dashboard/src/stores/historyStore.js @@ -0,0 +1,210 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' + +export const useHistoryStore = defineStore('history', () => { + // 状态 + const histories = ref([]) + + // 计算属性 + const historyCount = computed(() => histories.value.length) + + const sortedHistories = computed(() => { + return [...histories.value].sort((a, b) => new Date(b.updated_at) - new Date(a.updated_at)) + }) + + const historiesByType = computed(() => { + const grouped = {} + histories.value.forEach(item => { + // 根据站源名称中的标识进行分类(与Collection.vue中的逻辑保持一致) + const siteName = item.api_info?.site_name || '' + let type = '影视' // 默认分类 + + if (siteName.includes('[书]')) { + type = '小说' + } else if (siteName.includes('[画]')) { + type = '漫画' + } else if (siteName.includes('[密]')) { + type = '密' + } else if (siteName.includes('[听]')) { + type = '音频' + } else if (siteName.includes('[儿]')) { + type = '少儿' + } + + if (!grouped[type]) { + grouped[type] = [] + } + grouped[type].push(item) + }) + return grouped + }) + + // 方法 + const loadHistories = () => { + try { + const stored = localStorage.getItem('drplayer_histories') + if (stored) { + histories.value = JSON.parse(stored) + } + } catch (error) { + console.error('加载观看历史失败:', error) + histories.value = [] + } + } + + const saveHistories = () => { + try { + localStorage.setItem('drplayer_histories', JSON.stringify(histories.value)) + } catch (error) { + console.error('保存观看历史失败:', error) + } + } + + const addToHistory = (videoInfo, routeInfo, episodeInfo) => { + const now = new Date().toISOString() + + // 检查是否已存在相同的视频 + const existingIndex = histories.value.findIndex( + item => item.id === videoInfo.id && item.api_info.api_url === videoInfo.api_info.api_url + ) + + const historyItem = { + ...videoInfo, + current_route_name: routeInfo.name, + current_route_index: routeInfo.index, + current_episode_name: episodeInfo.name, + current_episode_index: episodeInfo.index, + current_episode_url: episodeInfo.url, + updated_at: now + } + + if (existingIndex !== -1) { + // 更新已存在的记录 + histories.value[existingIndex] = { + ...histories.value[existingIndex], + ...historyItem + } + } else { + // 添加新记录 + historyItem.created_at = now + histories.value.push(historyItem) + } + + saveHistories() + } + + const removeFromHistory = (item) => { + const index = histories.value.findIndex( + h => h.id === item.id && h.api_info.api_url === item.api_info.api_url + ) + + if (index !== -1) { + histories.value.splice(index, 1) + saveHistories() + } + } + + const clearHistories = () => { + histories.value = [] + saveHistories() + } + + const exportHistories = () => { + const dataStr = JSON.stringify(histories.value, null, 2) + const dataBlob = new Blob([dataStr], { type: 'application/json' }) + + const link = document.createElement('a') + link.href = URL.createObjectURL(dataBlob) + link.download = `drplayer_histories_${new Date().toISOString().split('T')[0]}.json` + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + } + + const importHistories = (jsonData) => { + try { + const importedHistories = JSON.parse(jsonData) + + if (!Array.isArray(importedHistories)) { + throw new Error('导入的数据格式不正确') + } + + // 验证数据结构 + const validHistories = importedHistories.filter(item => { + return item.id && + item.name && + item.pic && + item.api_info && + item.current_route_name && + item.current_episode_name + }) + + // 合并历史记录,避免重复 + validHistories.forEach(importedItem => { + const existingIndex = histories.value.findIndex( + item => item.id === importedItem.id && item.api_info.api_url === importedItem.api_info.api_url + ) + + if (existingIndex === -1) { + histories.value.push(importedItem) + } else { + // 如果导入的记录更新,则更新现有记录 + const existingItem = histories.value[existingIndex] + const importedTime = new Date(importedItem.updated_at) + const existingTime = new Date(existingItem.updated_at) + + if (importedTime > existingTime) { + histories.value[existingIndex] = importedItem + } + } + }) + + saveHistories() + return validHistories.length + } catch (error) { + throw new Error(`导入失败: ${error.message}`) + } + } + + const getHistoryByVideo = (videoId, apiUrl) => { + return histories.value.find( + item => item.id === videoId && item.api_info.api_url === apiUrl + ) + } + + // 获取某个视频的观看进度信息 + const getWatchProgress = (videoId, apiUrl) => { + const history = getHistoryByVideo(videoId, apiUrl) + if (!history) return null + + return { + routeName: history.current_route_name, + routeIndex: history.current_route_index, + episodeName: history.current_episode_name, + episodeIndex: history.current_episode_index, + episodeUrl: history.current_episode_url, + lastWatchTime: history.updated_at + } + } + + return { + // 状态 + histories, + + // 计算属性 + historyCount, + sortedHistories, + historiesByType, + + // 方法 + loadHistories, + saveHistories, + addToHistory, + removeFromHistory, + clearHistories, + exportHistories, + importHistories, + getHistoryByVideo, + getWatchProgress + } +}) \ No newline at end of file diff --git a/dashboard/src/stores/siteStore.js b/dashboard/src/stores/siteStore.js index 0a62ba3..b82bef9 100644 --- a/dashboard/src/stores/siteStore.js +++ b/dashboard/src/stores/siteStore.js @@ -1,16 +1,55 @@ // 在 Pinia store 中,currentSite 作为状态存储 import {defineStore} from 'pinia' +import {ref, watch} from 'vue' +import siteService from '@/api/services/site' export const useSiteStore = defineStore('site', () => { - const nowSite = JSON.parse(localStorage.getItem('site-nowSite')) || null + // 使用 ref 创建响应式状态,优先从 siteService 获取 + const nowSite = ref(siteService.getCurrentSite() || JSON.parse(localStorage.getItem('site-nowSite')) || null) - // 设置当前源并持久化到 localStorage + // 设置当前源并同步到 siteService const setCurrentSite = (site) => { - localStorage.setItem('site-nowSite', JSON.stringify(site)) // 存储到 localStorage + nowSite.value = site // 更新响应式状态 + + // 同步到两个存储系统 + localStorage.setItem('site-nowSite', JSON.stringify(site)) // 兼容旧系统 + + // 同步到 siteService(如果传入的是完整站点对象) + if (site && site.key) { + siteService.setCurrentSite(site.key) + } + + console.log('站点已切换:', site) } + // 从 siteService 同步站点信息 + const syncFromSiteService = () => { + const currentSite = siteService.getCurrentSite() + if (currentSite && (!nowSite.value || currentSite.key !== nowSite.value.key)) { + nowSite.value = currentSite + localStorage.setItem('site-nowSite', JSON.stringify(currentSite)) + console.log('从 siteService 同步站点:', currentSite) + } + } + + // 监听 siteService 的站点变化事件 + if (typeof window !== 'undefined') { + window.addEventListener('siteChange', (event) => { + const { site } = event.detail + if (site && (!nowSite.value || site.key !== nowSite.value.key)) { + nowSite.value = site + localStorage.setItem('site-nowSite', JSON.stringify(site)) + console.log('响应 siteService 站点变化:', site) + } + }) + } + + // 初始化时同步一次 + syncFromSiteService() + return { nowSite, setCurrentSite, + syncFromSiteService, } }) \ No newline at end of file diff --git a/dashboard/src/views/Collection.vue b/dashboard/src/views/Collection.vue index 5302b9d..6dd1a1a 100644 --- a/dashboard/src/views/Collection.vue +++ b/dashboard/src/views/Collection.vue @@ -1,51 +1,388 @@ - diff --git a/dashboard/src/views/History.vue b/dashboard/src/views/History.vue index 9afe017..cf7328b 100644 --- a/dashboard/src/views/History.vue +++ b/dashboard/src/views/History.vue @@ -1,22 +1,400 @@ - diff --git a/dashboard/src/views/Home.vue b/dashboard/src/views/Home.vue index 7865c48..faf556a 100644 --- a/dashboard/src/views/Home.vue +++ b/dashboard/src/views/Home.vue @@ -1,20 +1,586 @@ \ No newline at end of file diff --git a/dashboard/src/views/Settings.vue b/dashboard/src/views/Settings.vue index 5768694..f499ea4 100644 --- a/dashboard/src/views/Settings.vue +++ b/dashboard/src/views/Settings.vue @@ -557,8 +557,8 @@ loadConfig() .settings-content { flex: 1; width: 100%; - max-width: 1200px; - margin: 0 auto; + max-width: none; + margin: 0; padding: 20px 24px 40px; display: flex; flex-direction: column; diff --git a/dashboard/src/views/Video.vue b/dashboard/src/views/Video.vue index e479087..15994c9 100644 --- a/dashboard/src/views/Video.vue +++ b/dashboard/src/views/Video.vue @@ -186,9 +186,8 @@ const handleConfirmClear = () => { const handleConfirmChange = (site) => { form.now_site = site; - // 同时更新两个store + // 使用 siteStore 统一管理站点切换 setCurrentSite(site); - siteService.setCurrentSite(site.key); form.now_site_title = site.name; form.visible = false; getClassList(site); //获取分类列表 @@ -335,7 +334,23 @@ const exitSearch = () => { // 处理视频点击事件 const handleVideoClick = (video) => { - // 这里可以添加视频点击后的处理逻辑,比如跳转到播放页面 + if (video && video.vod_id) { + router.push({ + name: 'VideoDetail', + params: { id: video.vod_id }, + query: { + name: video.vod_name, + pic: video.vod_pic, + year: video.vod_year, + area: video.vod_area, + type: video.vod_type, + remarks: video.vod_remarks, + content: video.vod_content, + actor: video.vod_actor, + director: video.vod_director + } + }); + } }; @@ -370,6 +385,8 @@ onBeforeUnmount(() => { - current-time元素额外高度:14px (padding + border) - 总计:67px */ height: calc(100% - 67px); + /* 使用100%最小高度,继承父容器 */ + /* min-height: 100%; */ display: flex; flex-direction: column; overflow: hidden; diff --git a/dashboard/src/views/VideoDetail.vue b/dashboard/src/views/VideoDetail.vue new file mode 100644 index 0000000..8275e01 --- /dev/null +++ b/dashboard/src/views/VideoDetail.vue @@ -0,0 +1,1174 @@ + + + + + \ No newline at end of file diff --git a/dashboard/vite.config.js b/dashboard/vite.config.js index b477621..9f12369 100644 --- a/dashboard/vite.config.js +++ b/dashboard/vite.config.js @@ -1,20 +1,48 @@ -import {defineConfig} from 'vite' +import {defineConfig, loadEnv} from 'vite' import vue from '@vitejs/plugin-vue' import path from 'path'; // https://vite.dev/config/ -export default defineConfig({ +export default defineConfig(({ command, mode }) => { + // 加载环境变量 + const env = loadEnv(mode, process.cwd(), '') + + return { + // 基础路径配置,支持子目录部署 + // 开发环境使用 '/',生产环境可以通过环境变量设置 + base: mode === 'production' ? (env.VITE_BASE_PATH || './') : '/', + plugins: [ vue(), ], + + // 构建配置 + build: { + // 输出目录 + outDir: 'dist', + // 静态资源目录 + assetsDir: 'assets', + // 生成相对路径的资源引用 + rollupOptions: { + output: { + // 静态资源文件名格式 + assetFileNames: 'assets/[name]-[hash][extname]', + chunkFileNames: 'assets/[name]-[hash].js', + entryFileNames: 'assets/[name]-[hash].js' + } + } + }, + optimizeDeps: { include: [ '@arco-design/web-vue/es/icon' ] }, + resolve: { alias: { '@': path.resolve(__dirname, 'src'), // 配置别名 }, }, + } }) \ No newline at end of file diff --git a/dashboard/yarn.lock b/dashboard/yarn.lock index be314db..69d9060 100644 --- a/dashboard/yarn.lock +++ b/dashboard/yarn.lock @@ -9,14 +9,14 @@ "@arco-design/color@^0.4.0": version "0.4.0" - resolved "https://registry.npmmirror.com/@arco-design/color/-/color-0.4.0.tgz#52ddb40d318ee6df1057ca8c653cc1675023928f" + resolved "https://registry.npmmirror.com/@arco-design/color/-/color-0.4.0.tgz" integrity sha512-s7p9MSwJgHeL8DwcATaXvWT3m2SigKpxx4JA1BGPHL4gfvaQsmQfrLBDpjOJFJuJ2jG2dMt3R3P8Pm9E65q18g== dependencies: color "^3.1.3" "@arco-design/web-vue@^2.56.3": version "2.56.3" - resolved "https://registry.npmmirror.com/@arco-design/web-vue/-/web-vue-2.56.3.tgz#789a05836631ca4ac0b43da721502e7ff4d76344" + resolved "https://registry.npmmirror.com/@arco-design/web-vue/-/web-vue-2.56.3.tgz" integrity sha512-D2CPIXRBUPcg37TFsfWROZddCWFZnIwqGpsOhOn2BhmH89UFqtBGpTxyuMdYJEwKNXunp3dVL6V69ZMmJBRPOg== dependencies: "@arco-design/color" "^0.4.0" @@ -347,7 +347,7 @@ "@vue/devtools-api@^6.6.3", "@vue/devtools-api@^6.6.4": version "6.6.4" - resolved "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz#cbe97fe0162b365edc1dba80e173f90492535343" + resolved "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz" integrity sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g== "@vue/reactivity@3.5.12": @@ -390,7 +390,7 @@ accepts@~1.3.7, accepts@~1.3.8: version "1.3.8" - resolved "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + resolved "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== dependencies: mime-types "~2.1.34" @@ -403,12 +403,12 @@ acorn@^8.12.1, acorn@^8.14.0: ansi-regex@^5.0.1: version "5.0.1" - resolved "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + resolved "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" @@ -423,31 +423,31 @@ anymatch@~3.1.2: array-flatten@1.1.1: version "1.1.1" - resolved "https://registry.npmmirror.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + resolved "https://registry.npmmirror.com/array-flatten/-/array-flatten-1.1.1.tgz" integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== asynckit@^0.4.0: version "0.4.0" - resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -axios@^1.7.7: - version "1.7.7" - resolved "https://registry.npmmirror.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" - integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== +axios@1.12.0: + version "1.12.0" + resolved "https://registry.npmmirror.com/axios/-/axios-1.12.0.tgz#11248459be05a5ee493485628fa0e4323d0abfc3" + integrity sha512-oXTDccv8PcfjZmPGlWsPSwtOJCZ/b6W5jAMCNcfwJbCzDckwG0jrYJFaWH1yvivfCXjVzV/SPDEhMB3Q+DSurg== dependencies: follow-redirects "^1.15.6" - form-data "^4.0.0" + form-data "^4.0.4" proxy-from-env "^1.1.0" b-tween@^0.3.3: version "0.3.3" - resolved "https://registry.npmmirror.com/b-tween/-/b-tween-0.3.3.tgz#7a93ed199c98cd41a33ba4c711a0fa7e86db3fa2" + resolved "https://registry.npmmirror.com/b-tween/-/b-tween-0.3.3.tgz" integrity sha512-oEHegcRpA7fAuc9KC4nktucuZn2aS8htymCPcP3qkEGPqiBH+GfqtqoG2l7LxHngg6O0HFM7hOeOYExl1Oz4ZA== b-validate@^1.4.4: version "1.5.3" - resolved "https://registry.npmmirror.com/b-validate/-/b-validate-1.5.3.tgz#f6ac83b70caccbabf1c2eee42a0739bd228f79e6" + resolved "https://registry.npmmirror.com/b-validate/-/b-validate-1.5.3.tgz" integrity sha512-iCvCkGFskbaYtfQ0a3GmcQCHl/Sv1GufXFGuUQ+FE+WJa7A/espLOuFIn09B944V8/ImPj71T4+rTASxO2PAuA== balanced-match@^1.0.0: @@ -457,7 +457,7 @@ balanced-match@^1.0.0: basic-auth@~2.0.1: version "2.0.1" - resolved "https://registry.npmmirror.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" + resolved "https://registry.npmmirror.com/basic-auth/-/basic-auth-2.0.1.tgz" integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== dependencies: safe-buffer "5.1.2" @@ -469,7 +469,7 @@ binary-extensions@^2.0.0: body-parser@1.20.3, body-parser@^1.19.0: version "1.20.3" - resolved "https://registry.npmmirror.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" + resolved "https://registry.npmmirror.com/body-parser/-/body-parser-1.20.3.tgz" integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== dependencies: bytes "3.1.2" @@ -501,12 +501,20 @@ braces@^3.0.3, braces@~3.0.2: bytes@3.1.2: version "3.1.2" - resolved "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + resolved "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + call-bind@^1.0.7: version "1.0.7" - resolved "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + resolved "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.7.tgz" integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== dependencies: es-define-property "^1.0.0" @@ -517,7 +525,7 @@ call-bind@^1.0.7: chalk@^4.1.2: version "4.1.2" - resolved "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + resolved "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" @@ -540,7 +548,7 @@ chokidar@^3.6.0: cliui@^8.0.1: version "8.0.1" - resolved "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + resolved "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz" integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== dependencies: string-width "^4.2.0" @@ -549,31 +557,31 @@ cliui@^8.0.1: color-convert@^1.9.3: version "1.9.3" - resolved "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + resolved "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + resolved "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" - resolved "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + resolved "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" - resolved "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + resolved "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== color-string@^1.6.0: version "1.9.1" - resolved "https://registry.npmmirror.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + resolved "https://registry.npmmirror.com/color-string/-/color-string-1.9.1.tgz" integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== dependencies: color-name "^1.0.0" @@ -581,7 +589,7 @@ color-string@^1.6.0: color@^3.1.3: version "3.2.1" - resolved "https://registry.npmmirror.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" + resolved "https://registry.npmmirror.com/color/-/color-3.2.1.tgz" integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== dependencies: color-convert "^1.9.3" @@ -589,21 +597,21 @@ color@^3.1.3: combined-stream@^1.0.8: version "1.0.8" - resolved "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + resolved "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" compressible@~2.0.18: version "2.0.18" - resolved "https://registry.npmmirror.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + resolved "https://registry.npmmirror.com/compressible/-/compressible-2.0.18.tgz" integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== dependencies: mime-db ">= 1.43.0 < 2" compression@^1.7.4: version "1.7.5" - resolved "https://registry.npmmirror.com/compression/-/compression-1.7.5.tgz#fdd256c0a642e39e314c478f6c2cd654edd74c93" + resolved "https://registry.npmmirror.com/compression/-/compression-1.7.5.tgz" integrity sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q== dependencies: bytes "3.1.2" @@ -616,7 +624,7 @@ compression@^1.7.4: compute-scroll-into-view@^1.0.17, compute-scroll-into-view@^1.0.20: version "1.0.20" - resolved "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz#1768b5522d1172754f5d0c9b02de3af6be506a43" + resolved "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz" integrity sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg== confbox@^0.1.8: @@ -626,34 +634,34 @@ confbox@^0.1.8: connect-pause@^0.1.1: version "0.1.1" - resolved "https://registry.npmmirror.com/connect-pause/-/connect-pause-0.1.1.tgz#b269b2bb82ddb1ac3db5099c0fb582aba99fb37a" + resolved "https://registry.npmmirror.com/connect-pause/-/connect-pause-0.1.1.tgz" integrity sha512-a1gSWQBQD73krFXdUEYJom2RTFrWUL3YvXDCRkyv//GVXc79cdW9MngtRuN9ih4FDKBtfJAJId+BbDuX+1rh2w== content-disposition@0.5.4: version "0.5.4" - resolved "https://registry.npmmirror.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + resolved "https://registry.npmmirror.com/content-disposition/-/content-disposition-0.5.4.tgz" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== dependencies: safe-buffer "5.2.1" content-type@~1.0.4, content-type@~1.0.5: version "1.0.5" - resolved "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + resolved "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== cookie-signature@1.0.6: version "1.0.6" - resolved "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + resolved "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.6.tgz" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== cookie@0.7.1: version "0.7.1" - resolved "https://registry.npmmirror.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" + resolved "https://registry.npmmirror.com/cookie/-/cookie-0.7.1.tgz" integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== cors@^2.8.5: version "2.8.5" - resolved "https://registry.npmmirror.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + resolved "https://registry.npmmirror.com/cors/-/cors-2.8.5.tgz" integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== dependencies: object-assign "^4" @@ -666,33 +674,33 @@ csstype@^3.1.3: dayjs@^1.10.3: version "1.11.13" - resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c" + resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz" integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== debug@*, debug@^4.3.6: version "4.3.7" - resolved "https://registry.npmmirror.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + resolved "https://registry.npmmirror.com/debug/-/debug-4.3.7.tgz" integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== dependencies: ms "^2.1.3" debug@2.6.9: version "2.6.9" - resolved "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + resolved "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" debug@3.1.0: version "3.1.0" - resolved "https://registry.npmmirror.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + resolved "https://registry.npmmirror.com/debug/-/debug-3.1.0.tgz" integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== dependencies: ms "2.0.0" define-data-property@^1.1.4: version "1.1.4" - resolved "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + resolved "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz" integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== dependencies: es-define-property "^1.0.0" @@ -701,37 +709,54 @@ define-data-property@^1.1.4: delayed-stream@~1.0.0: version "1.0.0" - resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== depd@2.0.0, depd@~2.0.0: version "2.0.0" - resolved "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + resolved "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== destroy@1.2.0: version "1.2.0" - resolved "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + resolved "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + +echarts@^5.6.0: + version "5.6.0" + resolved "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz" + integrity sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA== + dependencies: + tslib "2.3.0" + zrender "5.6.1" + ee-first@1.1.1: version "1.1.1" - resolved "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + resolved "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + resolved "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== encodeurl@~1.0.2: version "1.0.2" - resolved "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + resolved "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== encodeurl@~2.0.0: version "2.0.0" - resolved "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + resolved "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz" integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== entities@^4.5.0: @@ -741,7 +766,7 @@ entities@^4.5.0: errorhandler@^1.5.1: version "1.5.1" - resolved "https://registry.npmmirror.com/errorhandler/-/errorhandler-1.5.1.tgz#b9ba5d17cf90744cd1e851357a6e75bf806a9a91" + resolved "https://registry.npmmirror.com/errorhandler/-/errorhandler-1.5.1.tgz" integrity sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A== dependencies: accepts "~1.3.7" @@ -749,16 +774,38 @@ errorhandler@^1.5.1: es-define-property@^1.0.0: version "1.0.0" - resolved "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + resolved "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.0.tgz" integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== dependencies: get-intrinsic "^1.2.4" +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + es-errors@^1.3.0: version "1.3.0" - resolved "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + resolved "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + esbuild@^0.21.3: version "0.21.5" resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz" @@ -790,12 +837,12 @@ esbuild@^0.21.3: escalade@^3.1.1: version "3.2.0" - resolved "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + resolved "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== escape-html@~1.0.3: version "1.0.3" - resolved "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + resolved "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz" integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== estree-walker@^2.0.2: @@ -805,12 +852,12 @@ estree-walker@^2.0.2: etag@~1.8.1: version "1.8.1" - resolved "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + resolved "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== express-urlrewrite@^1.4.0: version "1.4.0" - resolved "https://registry.npmmirror.com/express-urlrewrite/-/express-urlrewrite-1.4.0.tgz#985ee022773bac7ed32126f1cf9ec8ee48e1290a" + resolved "https://registry.npmmirror.com/express-urlrewrite/-/express-urlrewrite-1.4.0.tgz" integrity sha512-PI5h8JuzoweS26vFizwQl6UTF25CAHSggNv0J25Dn/IKZscJHWZzPrI5z2Y2jgOzIaw2qh8l6+/jUcig23Z2SA== dependencies: debug "*" @@ -818,7 +865,7 @@ express-urlrewrite@^1.4.0: express@^4.17.1: version "4.21.1" - resolved "https://registry.npmmirror.com/express/-/express-4.21.1.tgz#9dae5dda832f16b4eec941a4e44aa89ec481b281" + resolved "https://registry.npmmirror.com/express/-/express-4.21.1.tgz" integrity sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ== dependencies: accepts "~1.3.8" @@ -880,7 +927,7 @@ fill-range@^7.1.1: finalhandler@1.3.1: version "1.3.1" - resolved "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" + resolved "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.3.1.tgz" integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== dependencies: debug "2.6.9" @@ -893,26 +940,28 @@ finalhandler@1.3.1: follow-redirects@^1.15.6: version "1.15.9" - resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz" integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== -form-data@^4.0.0: - version "4.0.1" - resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" - integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw== +form-data@^4.0.4: + version "4.0.4" + resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.4.tgz#784cdcce0669a9d68e94d11ac4eea98088edd2c4" + integrity sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow== dependencies: asynckit "^0.4.0" combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" mime-types "^2.1.12" forwarded@0.2.0: version "0.2.0" - resolved "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + resolved "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== fresh@0.5.2: version "0.5.2" - resolved "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + resolved "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== fsevents@~2.3.2, fsevents@~2.3.3: @@ -922,17 +971,17 @@ fsevents@~2.3.2, fsevents@~2.3.3: function-bind@^1.1.2: version "1.1.2" - resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== get-caller-file@^2.0.5: version "2.0.5" - resolved "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + resolved "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: version "1.2.4" - resolved "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + resolved "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz" integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== dependencies: es-errors "^1.3.0" @@ -941,6 +990,30 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: has-symbols "^1.0.3" hasown "^2.0.0" +get-intrinsic@^1.2.6: + version "1.3.0" + resolved "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz" @@ -950,39 +1023,56 @@ glob-parent@^5.1.2, glob-parent@~5.1.2: gopd@^1.0.1: version "1.0.1" - resolved "https://registry.npmmirror.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + resolved "https://registry.npmmirror.com/gopd/-/gopd-1.0.1.tgz" integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== dependencies: get-intrinsic "^1.1.3" +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + graceful-fs@^4.1.3: version "4.2.11" - resolved "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + resolved "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + resolved "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-property-descriptors@^1.0.2: version "1.0.2" - resolved "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + resolved "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz" integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== dependencies: es-define-property "^1.0.0" has-proto@^1.0.1: version "1.0.3" - resolved "https://registry.npmmirror.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + resolved "https://registry.npmmirror.com/has-proto/-/has-proto-1.0.3.tgz" integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== has-symbols@^1.0.3: version "1.0.3" - resolved "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + resolved "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.0.3.tgz" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== -hasown@^2.0.0: +has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.0, hasown@^2.0.2: version "2.0.2" resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== @@ -991,7 +1081,7 @@ hasown@^2.0.0: http-errors@2.0.0: version "2.0.0" - resolved "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + resolved "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz" integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== dependencies: depd "2.0.0" @@ -1002,24 +1092,24 @@ http-errors@2.0.0: iconv-lite@0.4.24: version "0.4.24" - resolved "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + resolved "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" inherits@2.0.4: version "2.0.4" - resolved "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + resolved "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== ipaddr.js@1.9.1: version "1.9.1" - resolved "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + resolved "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== is-arrayish@^0.3.1: version "0.3.2" - resolved "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + resolved "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.2.tgz" integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== is-binary-path@~2.1.0: @@ -1036,7 +1126,7 @@ is-extglob@^2.1.1: is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + resolved "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-glob@^4.0.1, is-glob@~4.0.1: @@ -1053,29 +1143,29 @@ is-number@^7.0.0: is-promise@^2.1.0: version "2.2.2" - resolved "https://registry.npmmirror.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + resolved "https://registry.npmmirror.com/is-promise/-/is-promise-2.2.2.tgz" integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== isarray@0.0.1: version "0.0.1" - resolved "https://registry.npmmirror.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + resolved "https://registry.npmmirror.com/isarray/-/isarray-0.0.1.tgz" integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== jju@^1.1.0: version "1.4.0" - resolved "https://registry.npmmirror.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" + resolved "https://registry.npmmirror.com/jju/-/jju-1.4.0.tgz" integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA== json-parse-helpfulerror@^1.0.3: version "1.0.3" - resolved "https://registry.npmmirror.com/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz#13f14ce02eed4e981297b64eb9e3b932e2dd13dc" + resolved "https://registry.npmmirror.com/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz" integrity sha512-XgP0FGR77+QhUxjXkwOMkC94k3WtqEBfcnjWqhRd82qTat4SWKRE+9kUnynz/shm3I4ea2+qISvTIeGTNU7kJg== dependencies: jju "^1.1.0" json-server@^0.17.4: version "0.17.4" - resolved "https://registry.npmmirror.com/json-server/-/json-server-0.17.4.tgz#d4ef25a516e26d9ba86fd6db2f9d81a5f405421e" + resolved "https://registry.npmmirror.com/json-server/-/json-server-0.17.4.tgz" integrity sha512-bGBb0WtFuAKbgI7JV3A864irWnMZSvBYRJbohaOuatHwKSRFUfqtQlrYMrB6WbalXy/cJabyjlb7JkHli6dYjQ== dependencies: body-parser "^1.19.0" @@ -1106,19 +1196,24 @@ local-pkg@^0.5.0: mlly "^1.4.2" pkg-types "^1.0.3" +lodash-es@^4.17.21: + version "4.17.21" + resolved "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz" + integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== + lodash-id@^0.14.1: version "0.14.1" - resolved "https://registry.npmmirror.com/lodash-id/-/lodash-id-0.14.1.tgz#dffa1f1f8b90d1803bb0d70b7d7547e10751e80b" + resolved "https://registry.npmmirror.com/lodash-id/-/lodash-id-0.14.1.tgz" integrity sha512-ikQPBTiq/d5m6dfKQlFdIXFzvThPi2Be9/AHxktOnDSfSxE1j9ICbBT5Elk1ke7HSTgM38LHTpmJovo9/klnLg== lodash@4, lodash@^4.17.21: version "4.17.21" - resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== lowdb@^1.0.0: version "1.0.0" - resolved "https://registry.npmmirror.com/lowdb/-/lowdb-1.0.0.tgz#5243be6b22786ccce30e50c9a33eac36b20c8064" + resolved "https://registry.npmmirror.com/lowdb/-/lowdb-1.0.0.tgz" integrity sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ== dependencies: graceful-fs "^4.1.3" @@ -1134,14 +1229,19 @@ magic-string@^0.30.11: dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + media-typer@0.3.0: version "0.3.0" - resolved "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + resolved "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== merge-descriptors@1.0.3: version "1.0.3" - resolved "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + resolved "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz" integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== merge2@^1.3.0: @@ -1151,7 +1251,7 @@ merge2@^1.3.0: method-override@^3.0.0: version "3.0.0" - resolved "https://registry.npmmirror.com/method-override/-/method-override-3.0.0.tgz#6ab0d5d574e3208f15b0c9cf45ab52000468d7a2" + resolved "https://registry.npmmirror.com/method-override/-/method-override-3.0.0.tgz" integrity sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA== dependencies: debug "3.1.0" @@ -1161,7 +1261,7 @@ method-override@^3.0.0: methods@~1.1.2: version "1.1.2" - resolved "https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + resolved "https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== micromatch@^4.0.4: @@ -1174,24 +1274,24 @@ micromatch@^4.0.4: mime-db@1.52.0: version "1.52.0" - resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== "mime-db@>= 1.43.0 < 2": version "1.53.0" - resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.53.0.tgz#3cb63cd820fc29896d9d4e8c32ab4fcd74ccb447" + resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.53.0.tgz" integrity sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg== mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" - resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" mime@1.6.0: version "1.6.0" - resolved "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + resolved "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== minimatch@^9.0.5: @@ -1213,7 +1313,7 @@ mlly@^1.4.2, mlly@^1.7.1, mlly@^1.7.2: morgan@^1.10.0: version "1.10.0" - resolved "https://registry.npmmirror.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" + resolved "https://registry.npmmirror.com/morgan/-/morgan-1.10.0.tgz" integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== dependencies: basic-auth "~2.0.1" @@ -1224,7 +1324,7 @@ morgan@^1.10.0: ms@2.0.0: version "2.0.0" - resolved "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + resolved "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== ms@2.1.3, ms@^2.1.3: @@ -1234,17 +1334,17 @@ ms@2.1.3, ms@^2.1.3: nanoid@^3.1.23, nanoid@^3.3.7: version "3.3.7" - resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz" integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== negotiator@0.6.3: version "0.6.3" - resolved "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + resolved "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== negotiator@~0.6.4: version "0.6.4" - resolved "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" + resolved "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.4.tgz" integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== normalize-path@^3.0.0, normalize-path@~3.0.0: @@ -1254,51 +1354,51 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: number-precision@^1.5.0: version "1.6.0" - resolved "https://registry.npmmirror.com/number-precision/-/number-precision-1.6.0.tgz#e309d28f80871d36ac9f6ecd974e13afb1ec0de0" + resolved "https://registry.npmmirror.com/number-precision/-/number-precision-1.6.0.tgz" integrity sha512-05OLPgbgmnixJw+VvEh18yNPUo3iyp4BEWJcrLu4X9W05KmMifN7Mu5exYvQXqxxeNWhvIF+j3Rij+HmddM/hQ== object-assign@^4: version "4.1.1" - resolved "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + resolved "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== object-inspect@^1.13.1: version "1.13.2" - resolved "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" + resolved "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.2.tgz" integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== on-finished@2.4.1: version "2.4.1" - resolved "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + resolved "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== dependencies: ee-first "1.1.1" on-finished@~2.3.0: version "2.3.0" - resolved "https://registry.npmmirror.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + resolved "https://registry.npmmirror.com/on-finished/-/on-finished-2.3.0.tgz" integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== dependencies: ee-first "1.1.1" on-headers@~1.0.2: version "1.0.2" - resolved "https://registry.npmmirror.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + resolved "https://registry.npmmirror.com/on-headers/-/on-headers-1.0.2.tgz" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" - resolved "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + resolved "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== path-to-regexp@0.1.10: version "0.1.10" - resolved "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b" + resolved "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz" integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w== path-to-regexp@^1.0.3: version "1.9.0" - resolved "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-1.9.0.tgz#5dc0753acbf8521ca2e0f137b4578b917b10cf24" + resolved "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-1.9.0.tgz" integrity sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g== dependencies: isarray "0.0.1" @@ -1325,12 +1425,12 @@ picomatch@^4.0.2: pify@^3.0.0: version "3.0.0" - resolved "https://registry.npmmirror.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + resolved "https://registry.npmmirror.com/pify/-/pify-3.0.0.tgz" integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== pinia@^2.2.6: version "2.2.6" - resolved "https://registry.npmmirror.com/pinia/-/pinia-2.2.6.tgz#ff93f35b8c02033eaedc8c92ad5f10f215d6c804" + resolved "https://registry.npmmirror.com/pinia/-/pinia-2.2.6.tgz" integrity sha512-vIsR8JkDN5Ga2vAxqOE2cJj4VtsHnzpR1Fz30kClxlh0yCHfec6uoMeM3e/ddqmwFUejK3NlrcQa/shnpyT4hA== dependencies: "@vue/devtools-api" "^6.6.3" @@ -1347,14 +1447,14 @@ pkg-types@^1.0.3, pkg-types@^1.2.0: please-upgrade-node@^3.2.0: version "3.2.0" - resolved "https://registry.npmmirror.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942" + resolved "https://registry.npmmirror.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz" integrity sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg== dependencies: semver-compare "^1.0.0" pluralize@^8.0.0: version "8.0.0" - resolved "https://registry.npmmirror.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" + resolved "https://registry.npmmirror.com/pluralize/-/pluralize-8.0.0.tgz" integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== postcss@^8.4.43, postcss@^8.4.47: @@ -1368,7 +1468,7 @@ postcss@^8.4.43, postcss@^8.4.47: proxy-addr@~2.0.7: version "2.0.7" - resolved "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + resolved "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== dependencies: forwarded "0.2.0" @@ -1376,12 +1476,12 @@ proxy-addr@~2.0.7: proxy-from-env@^1.1.0: version "1.1.0" - resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== qs@6.13.0: version "6.13.0" - resolved "https://registry.npmmirror.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" + resolved "https://registry.npmmirror.com/qs/-/qs-6.13.0.tgz" integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== dependencies: side-channel "^1.0.6" @@ -1393,12 +1493,12 @@ queue-microtask@^1.2.2: range-parser@~1.2.1: version "1.2.1" - resolved "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + resolved "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== raw-body@2.5.2: version "2.5.2" - resolved "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + resolved "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.2.tgz" integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== dependencies: bytes "3.1.2" @@ -1415,12 +1515,12 @@ readdirp@~3.6.0: require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + resolved "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== resize-observer-polyfill@^1.5.1: version "1.5.1" - resolved "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + resolved "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== reusify@^1.0.4: @@ -1464,34 +1564,34 @@ run-parallel@^1.1.9: safe-buffer@5.1.2: version "5.1.2" - resolved "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + resolved "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== safe-buffer@5.2.1: version "5.2.1" - resolved "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + resolved "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== "safer-buffer@>= 2.1.2 < 3": version "2.1.2" - resolved "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + resolved "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== scroll-into-view-if-needed@^2.2.28: version "2.2.31" - resolved "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz#d3c482959dc483e37962d1521254e3295d0d1587" + resolved "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz" integrity sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA== dependencies: compute-scroll-into-view "^1.0.20" semver-compare@^1.0.0: version "1.0.0" - resolved "https://registry.npmmirror.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + resolved "https://registry.npmmirror.com/semver-compare/-/semver-compare-1.0.0.tgz" integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow== send@0.19.0: version "0.19.0" - resolved "https://registry.npmmirror.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" + resolved "https://registry.npmmirror.com/send/-/send-0.19.0.tgz" integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== dependencies: debug "2.6.9" @@ -1510,7 +1610,7 @@ send@0.19.0: serve-static@1.16.2: version "1.16.2" - resolved "https://registry.npmmirror.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" + resolved "https://registry.npmmirror.com/serve-static/-/serve-static-1.16.2.tgz" integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== dependencies: encodeurl "~2.0.0" @@ -1520,12 +1620,12 @@ serve-static@1.16.2: server-destroy@^1.0.1: version "1.0.1" - resolved "https://registry.npmmirror.com/server-destroy/-/server-destroy-1.0.1.tgz#f13bf928e42b9c3e79383e61cc3998b5d14e6cdd" + resolved "https://registry.npmmirror.com/server-destroy/-/server-destroy-1.0.1.tgz" integrity sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ== set-function-length@^1.2.1: version "1.2.2" - resolved "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + resolved "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz" integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== dependencies: define-data-property "^1.1.4" @@ -1537,12 +1637,12 @@ set-function-length@^1.2.1: setprototypeof@1.2.0: version "1.2.0" - resolved "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + resolved "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== side-channel@^1.0.6: version "1.0.6" - resolved "https://registry.npmmirror.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + resolved "https://registry.npmmirror.com/side-channel/-/side-channel-1.0.6.tgz" integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== dependencies: call-bind "^1.0.7" @@ -1552,7 +1652,7 @@ side-channel@^1.0.6: simple-swizzle@^0.2.2: version "0.2.2" - resolved "https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + resolved "https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz" integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== dependencies: is-arrayish "^0.3.1" @@ -1564,19 +1664,19 @@ source-map-js@^1.2.0, source-map-js@^1.2.1: statuses@2.0.1: version "2.0.1" - resolved "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + resolved "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== steno@^0.4.1: version "0.4.4" - resolved "https://registry.npmmirror.com/steno/-/steno-0.4.4.tgz#071105bdfc286e6615c0403c27e9d7b5dcb855cb" + resolved "https://registry.npmmirror.com/steno/-/steno-0.4.4.tgz" integrity sha512-EEHMVYHNXFHfGtgjNITnka0aHhiAlo93F7z2/Pwd+g0teG9CnM3JIINM7hVVB5/rhw9voufD7Wukwgtw2uqh6w== dependencies: graceful-fs "^4.1.3" string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" - resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" @@ -1585,14 +1685,14 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" - resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + resolved "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" @@ -1606,12 +1706,17 @@ to-regex-range@^5.0.1: toidentifier@1.0.1: version "1.0.1" - resolved "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + resolved "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +tslib@2.3.0: + version "2.3.0" + resolved "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz" + integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== + type-is@~1.6.18: version "1.6.18" - resolved "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + resolved "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== dependencies: media-typer "0.3.0" @@ -1624,12 +1729,12 @@ ufo@^1.5.4: unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" - resolved "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + resolved "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== unplugin-vue-components@^0.27.4: version "0.27.4" - resolved "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-0.27.4.tgz#748468b2cb6a856235bdd512c33b629c33c3d23a" + resolved "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-0.27.4.tgz" integrity sha512-1XVl5iXG7P1UrOMnaj2ogYa5YTq8aoh5jwDPQhemwO/OrXW+lPQKDXd1hMz15qxQPxgb/XXlbgo3HQ2rLEbmXQ== dependencies: "@antfu/utils" "^0.7.10" @@ -1653,14 +1758,27 @@ unplugin@^1.12.1: utils-merge@1.0.1: version "1.0.1" - resolved "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + resolved "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== +v-viewer@^3.0.22: + version "3.0.22" + resolved "https://registry.npmmirror.com/v-viewer/-/v-viewer-3.0.22.tgz" + integrity sha512-uYyP5FPT4K/Sd5D1mhB2HMVV8jnf6zYy2HD1PHCNAO6s2Iway+Wls60pwh7y4F3e2Nlc9549Pvy2HXaq8PKrAg== + dependencies: + lodash-es "^4.17.21" + viewerjs "^1.11.6" + vary@^1, vary@~1.1.2: version "1.1.2" - resolved "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + resolved "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== +viewerjs@^1.11.6: + version "1.11.7" + resolved "https://registry.npmmirror.com/viewerjs/-/viewerjs-1.11.7.tgz" + integrity sha512-0JuVqOmL5v1jmEAlG5EBDR3XquxY8DWFQbFMprOXgaBB0F7Q/X9xWdEaQc59D8xzwkdUgXEMSSknTpriq95igg== + vite@^5.4.10: version "5.4.10" resolved "https://registry.npmmirror.com/vite/-/vite-5.4.10.tgz" @@ -1672,14 +1790,26 @@ vite@^5.4.10: optionalDependencies: fsevents "~2.3.3" +vue-demi@^0.13.11: + version "0.13.11" + resolved "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz" + integrity sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A== + vue-demi@^0.14.10: version "0.14.10" - resolved "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz#afc78de3d6f9e11bf78c55e8510ee12814522f04" + resolved "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz" integrity sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg== +vue-echarts@^7.0.3: + version "7.0.3" + resolved "https://registry.npmmirror.com/vue-echarts/-/vue-echarts-7.0.3.tgz" + integrity sha512-/jSxNwOsw5+dYAUcwSfkLwKPuzTQ0Cepz1LxCOpj2QcHrrmUa/Ql0eQqMmc1rTPQVrh2JQ29n2dhq75ZcHvRDw== + dependencies: + vue-demi "^0.13.11" + vue-router@^4.4.5: version "4.4.5" - resolved "https://registry.npmmirror.com/vue-router/-/vue-router-4.4.5.tgz#bdf535e4cf32414ebdea6b4b403593efdb541388" + resolved "https://registry.npmmirror.com/vue-router/-/vue-router-4.4.5.tgz" integrity sha512-4fKZygS8cH1yCyuabAXGUAsyi1b2/o/OKgu/RUb+znIYOxPRxdkytJEx+0wGcpBE1pX6vUgh5jwWOKRGvuA/7Q== dependencies: "@vue/devtools-api" "^6.6.4" @@ -1702,7 +1832,7 @@ webpack-virtual-modules@^0.6.2: wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" @@ -1711,17 +1841,17 @@ wrap-ansi@^7.0.0: y18n@^5.0.5: version "5.0.8" - resolved "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + resolved "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yargs-parser@^21.1.1: version "21.1.1" - resolved "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + resolved "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs@^17.0.1: version "17.7.2" - resolved "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + resolved "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: cliui "^8.0.1" @@ -1731,3 +1861,10 @@ yargs@^17.0.1: string-width "^4.2.3" y18n "^5.0.5" yargs-parser "^21.1.1" + +zrender@5.6.1: + version "5.6.1" + resolved "https://registry.npmmirror.com/zrender/-/zrender-5.6.1.tgz" + integrity sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag== + dependencies: + tslib "2.3.0" From a8ce1c2d23f3c949b0e2f708161cf6e0933c8b66 Mon Sep 17 00:00:00 2001 From: Taois Date: Sat, 27 Sep 2025 10:37:02 +0800 Subject: [PATCH 023/199] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0drplayer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化部署方式,完善首页,收藏,历史 --- .gitignore | 1 + dashboard/.env.production | 4 +- dashboard/.env.production.apps | 3 + dashboard/.env.production.root | 3 + dashboard/build-for-fastify.js | 64 + dashboard/{ => docs}/DEPLOYMENT.md | 0 dashboard/docs/FASTIFY_DEPLOYMENT.md | 175 ++ dashboard/docs/NGINX_DEPLOYMENT.md | 194 ++ dashboard/{ => docs}/README.md | 0 dashboard/docs/nginx-root.conf | 97 + dashboard/docs/nginx-subdir.conf | 115 ++ dashboard/fastify-spa-config.js | 116 ++ dashboard/fastify-spa-routes.js | 87 + dashboard/package-lock.json | 2776 -------------------------- dashboard/package.json | 12 +- dashboard/production-server.js | 228 +++ dashboard/src/api/services/config.js | 6 +- dashboard/src/views/Settings.vue | 153 +- dashboard/vite.config.js | 2 +- dashboard/yarn.lock | 2078 ++++++++++++------- 20 files changed, 2534 insertions(+), 3580 deletions(-) create mode 100644 dashboard/.env.production.apps create mode 100644 dashboard/.env.production.root create mode 100644 dashboard/build-for-fastify.js rename dashboard/{ => docs}/DEPLOYMENT.md (100%) create mode 100644 dashboard/docs/FASTIFY_DEPLOYMENT.md create mode 100644 dashboard/docs/NGINX_DEPLOYMENT.md rename dashboard/{ => docs}/README.md (100%) create mode 100644 dashboard/docs/nginx-root.conf create mode 100644 dashboard/docs/nginx-subdir.conf create mode 100644 dashboard/fastify-spa-config.js create mode 100644 dashboard/fastify-spa-routes.js delete mode 100644 dashboard/package-lock.json create mode 100644 dashboard/production-server.js diff --git a/.gitignore b/.gitignore index c6bba59..3eba158 100644 --- a/.gitignore +++ b/.gitignore @@ -128,3 +128,4 @@ dist .yarn/build-state.yml .yarn/install-state.gz .pnp.* +/dashboard/apps/drplayer/ diff --git a/dashboard/.env.production b/dashboard/.env.production index 9d90ae9..5d31245 100644 --- a/dashboard/.env.production +++ b/dashboard/.env.production @@ -1,7 +1,7 @@ # .env.production # 生产环境配置 # 子目录部署配置 - 例如部署到 /apps/ 目录 -VITE_BASE_PATH=/apps/drplayer/ +# VITE_BASE_PATH=/apps/drplayer/ # 如果部署到根目录,使用以下配置: -# VITE_BASE_PATH=./ \ No newline at end of file +VITE_BASE_PATH=./ \ No newline at end of file diff --git a/dashboard/.env.production.apps b/dashboard/.env.production.apps new file mode 100644 index 0000000..b251bf3 --- /dev/null +++ b/dashboard/.env.production.apps @@ -0,0 +1,3 @@ +# .env.production.apps +# 子目录部署配置 - 部署到 /apps/drplayer/ 目录 +VITE_BASE_PATH=/apps/drplayer/ \ No newline at end of file diff --git a/dashboard/.env.production.root b/dashboard/.env.production.root new file mode 100644 index 0000000..c54e8d9 --- /dev/null +++ b/dashboard/.env.production.root @@ -0,0 +1,3 @@ +# .env.production.root +# 根目录部署配置 +VITE_BASE_PATH=./ \ No newline at end of file diff --git a/dashboard/build-for-fastify.js b/dashboard/build-for-fastify.js new file mode 100644 index 0000000..d7cdb35 --- /dev/null +++ b/dashboard/build-for-fastify.js @@ -0,0 +1,64 @@ +// 为Fastify部署构建Vue应用的脚本 +import { execSync } from 'child_process'; +import path from 'path'; +import fs from 'fs'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +async function buildForFastify() { + console.log('🚀 开始为Fastify部署构建Vue应用...'); + + try { + // 1. 设置环境变量 + process.env.NODE_ENV = 'production'; + process.env.VITE_BASE_PATH = '/apps/drplayer/'; + + console.log('📦 正在构建应用...'); + + // 2. 执行构建 + execSync('yarn build', { + stdio: 'inherit', + env: { + ...process.env, + VITE_BASE_PATH: '/apps/drplayer/' + } + }); + + // 3. 检查构建结果 + const distPath = path.join(__dirname, 'dist'); + const indexPath = path.join(distPath, 'index.html'); + + if (fs.existsSync(indexPath)) { + console.log('✅ 构建成功!'); + console.log(`📁 构建文件位置: ${distPath}`); + console.log(''); + console.log('📋 部署步骤:'); + console.log('1. 将 dist/ 目录的内容复制到您的后端 apps/drplayer/ 目录'); + console.log('2. 在Fastify应用中添加SPA路由支持(参考 fastify-spa-routes.js)'); + console.log('3. 重启Fastify服务器'); + console.log('4. 访问 http://your-server/apps/drplayer/'); + console.log(''); + console.log('🔧 Fastify配置示例:'); + console.log('```javascript'); + console.log('// 添加到您的Fastify应用中'); + console.log('import { addSPARoutes } from "./fastify-spa-routes.js";'); + console.log('await fastify.register(addSPARoutes, { appsDir: options.appsDir });'); + console.log('```'); + } else { + throw new Error('构建失败:未找到index.html文件'); + } + + } catch (error) { + console.error('❌ 构建失败:', error.message); + process.exit(1); + } +} + +// 如果直接运行此脚本 +if (import.meta.url === `file://${process.argv[1]}`) { + buildForFastify(); +} + +export { buildForFastify }; \ No newline at end of file diff --git a/dashboard/DEPLOYMENT.md b/dashboard/docs/DEPLOYMENT.md similarity index 100% rename from dashboard/DEPLOYMENT.md rename to dashboard/docs/DEPLOYMENT.md diff --git a/dashboard/docs/FASTIFY_DEPLOYMENT.md b/dashboard/docs/FASTIFY_DEPLOYMENT.md new file mode 100644 index 0000000..ff0003e --- /dev/null +++ b/dashboard/docs/FASTIFY_DEPLOYMENT.md @@ -0,0 +1,175 @@ +# Vue SPA + Fastify 部署指南 + +## 问题说明 + +Vue单页应用使用`createWebHistory`路由模式时,刷新页面会出现404错误。这是因为: + +1. 用户访问 `/apps/drplayer/settings` +2. 浏览器向服务器请求 `/apps/drplayer/settings` 文件 +3. 服务器找不到该文件,返回404 +4. 需要让服务器将所有SPA路由请求都返回到 `index.html` + +## 解决方案 + +通过Fastify提供静态文件服务 + SPA路由回退机制,完全可以解决刷新404问题。 + +## 配置步骤 + +### 1. 构建Vue应用 + +```bash +# 方法1:使用构建脚本(推荐) +node build-for-fastify.js + +# 方法2:手动设置环境变量 +set VITE_BASE_PATH=/apps/drplayer/ +yarn build +``` + +### 2. 复制构建文件 + +将 `dist/` 目录的所有内容复制到您的后端 `apps/drplayer/` 目录: + +``` +your-backend/ +├── apps/ +│ └── drplayer/ +│ ├── index.html +│ ├── assets/ +│ │ ├── index-xxx.js +│ │ └── index-xxx.css +│ └── ... +└── server.js +``` + +### 3. 配置Fastify服务器 + +#### 方法1:使用提供的路由模块(推荐) + +```javascript +import { addSPARoutes } from './fastify-spa-routes.js'; +import fastifyStatic from '@fastify/static'; + +// 您现有的静态文件配置 +await fastify.register(fastifyStatic, { + root: options.appsDir, + prefix: '/apps/', + decorateReply: false, +}); + +// 添加SPA路由支持 +await fastify.register(addSPARoutes, { + appsDir: options.appsDir +}); +``` + +#### 方法2:直接在现有代码中添加 + +```javascript +import path from 'path'; +import fs from 'fs'; + +// 在您现有的Fastify应用中添加这些路由 +fastify.get('/apps/drplayer/*', async (request, reply) => { + const requestedPath = request.params['*']; + const fullPath = path.join(options.appsDir, 'drplayer', requestedPath); + + try { + await fs.promises.access(fullPath); + return reply.callNotFound(); // 让静态文件服务处理 + } catch (error) { + // 返回index.html让Vue Router处理 + const indexPath = path.join(options.appsDir, 'drplayer', 'index.html'); + const indexContent = await fs.promises.readFile(indexPath, 'utf8'); + return reply + .type('text/html') + .header('Cache-Control', 'no-cache, no-store, must-revalidate') + .send(indexContent); + } +}); + +// 处理根路径 +fastify.get('/apps/drplayer', async (request, reply) => { + return reply.redirect(301, '/apps/drplayer/'); +}); + +fastify.get('/apps/drplayer/', async (request, reply) => { + const indexPath = path.join(options.appsDir, 'drplayer', 'index.html'); + const indexContent = await fs.promises.readFile(indexPath, 'utf8'); + return reply + .type('text/html') + .header('Cache-Control', 'no-cache, no-store, must-revalidate') + .send(indexContent); +}); +``` + +## 工作原理 + +1. **静态文件服务**:`@fastify/static` 处理所有存在的静态文件(JS、CSS、图片等) +2. **路由回退**:当请求的文件不存在时,返回 `index.html` +3. **Vue Router接管**:`index.html` 加载后,Vue Router根据URL显示对应组件 + +## 路由优先级 + +``` +请求: /apps/drplayer/assets/index-xxx.js +↓ +静态文件存在 → 直接返回文件 + +请求: /apps/drplayer/settings +↓ +静态文件不存在 → 返回 index.html → Vue Router处理 +``` + +## 缓存策略 + +- **HTML文件**:不缓存(`no-cache`),确保路由更新 +- **静态资源**:长期缓存(1年),提高性能 + +## 测试验证 + +1. 启动Fastify服务器 +2. 访问 `http://localhost:3000/apps/drplayer/` +3. 导航到不同页面(如设置页面) +4. 刷新页面,确认不出现404错误 +5. 检查浏览器开发者工具,确认静态资源正常加载 + +## 常见问题 + +### Q: 刷新后页面空白? +A: 检查 `VITE_BASE_PATH` 是否正确设置为 `/apps/drplayer/` + +### Q: 静态资源404? +A: 确认构建时的base路径与Fastify的prefix匹配 + +### Q: API请求失败? +A: 确保API路由在SPA路由之前注册,避免被SPA回退拦截 + +## 性能优化 + +1. **启用Gzip压缩**: +```javascript +import fastifyCompress from '@fastify/compress'; +await fastify.register(fastifyCompress); +``` + +2. **设置静态资源缓存**: +```javascript +await fastify.register(fastifyStatic, { + // ... 其他配置 + setHeaders: (res, path) => { + if (path.endsWith('.html')) { + res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); + } else if (path.match(/\.(js|css|png|jpg|jpeg|gif|ico|svg)$/)) { + res.setHeader('Cache-Control', 'public, max-age=31536000'); + } + } +}); +``` + +## 总结 + +✅ **可以解决404问题**:通过Fastify的路由回退机制 +✅ **性能良好**:静态文件直接服务,只有路由请求才回退 +✅ **配置简单**:只需添加几个路由处理器 +✅ **开发友好**:支持热重载和开发服务器 \ No newline at end of file diff --git a/dashboard/docs/NGINX_DEPLOYMENT.md b/dashboard/docs/NGINX_DEPLOYMENT.md new file mode 100644 index 0000000..2a394dd --- /dev/null +++ b/dashboard/docs/NGINX_DEPLOYMENT.md @@ -0,0 +1,194 @@ +# Vue SPA Nginx部署指南 + +本文档说明如何解决Vue单页应用(SPA)在静态部署时的404问题,并提供完整的Nginx配置方案。 + +## 问题原因 + +Vue Router使用`createWebHistory`模式时,路由是通过浏览器的History API实现的。当用户直接访问或刷新非根路径页面时(如`/settings`),服务器会尝试查找对应的物理文件,但这些文件并不存在,因此返回404错误。 + +## 解决方案 + +通过Nginx配置`try_files`指令,将所有不匹配静态文件的请求重定向到`index.html`,让Vue Router接管路由处理。 + +## 部署方式 + +### 方式一:根目录部署 + +适用于将DrPlayer部署在域名根目录的情况,如:`https://example.com/` + +#### 1. 构建应用 +```bash +# 使用根目录打包脚本 +yarn build:root +``` + +#### 2. 部署文件 +将`dist`目录中的所有文件复制到服务器的网站根目录: +```bash +# 示例路径 +/var/www/html/drplayer/ +``` + +#### 3. Nginx配置 +使用提供的`nginx-root.conf`配置文件: +```bash +# 复制配置文件 +sudo cp nginx-root.conf /etc/nginx/sites-available/drplayer +sudo ln -s /etc/nginx/sites-available/drplayer /etc/nginx/sites-enabled/ + +# 或者直接编辑默认配置 +sudo nano /etc/nginx/sites-available/default +``` + +### 方式二:子目录部署 + +适用于将DrPlayer部署在域名子目录的情况,如:`https://example.com/apps/drplayer/` + +#### 1. 构建应用 +```bash +# 使用子目录打包脚本 +yarn build:apps +``` + +#### 2. 部署文件 +将`dist`目录中的所有文件复制到服务器的指定子目录: +```bash +# 示例路径 +/var/www/html/drplayer/ +``` + +#### 3. Nginx配置 +使用提供的`nginx-subdir.conf`配置文件: +```bash +# 复制配置文件 +sudo cp nginx-subdir.conf /etc/nginx/sites-available/drplayer-subdir +sudo ln -s /etc/nginx/sites-available/drplayer-subdir /etc/nginx/sites-enabled/ +``` + +## 配置文件说明 + +### 核心配置项 + +1. **try_files指令** + ```nginx + try_files $uri $uri/ /index.html; + ``` + 这是解决SPA路由404问题的关键配置。 + +2. **静态资源缓存** + ```nginx + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + ``` + +3. **Gzip压缩** + ```nginx + gzip on; + gzip_types text/plain text/css application/json application/javascript; + ``` + +4. **安全头设置** + ```nginx + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + ``` + +### 路径配置 + +- **根目录部署**:修改`root`指令指向您的实际部署路径 +- **子目录部署**:修改`alias`指令指向您的实际部署路径 + +## 部署步骤 + +### 1. 准备服务器环境 +```bash +# 安装Nginx(Ubuntu/Debian) +sudo apt update +sudo apt install nginx + +# 安装Nginx(CentOS/RHEL) +sudo yum install nginx +# 或者 +sudo dnf install nginx +``` + +### 2. 配置Nginx +```bash +# 备份原配置 +sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.backup + +# 应用新配置 +sudo cp nginx-root.conf /etc/nginx/sites-available/drplayer +sudo ln -s /etc/nginx/sites-available/drplayer /etc/nginx/sites-enabled/ + +# 测试配置 +sudo nginx -t + +# 重启Nginx +sudo systemctl restart nginx +``` + +### 3. 部署应用文件 +```bash +# 创建部署目录 +sudo mkdir -p /var/www/html/drplayer + +# 复制构建文件 +sudo cp -r dist/* /var/www/html/drplayer/ + +# 设置权限 +sudo chown -R www-data:www-data /var/www/html/drplayer +sudo chmod -R 755 /var/www/html/drplayer +``` + +### 4. 验证部署 +1. 访问首页:`http://your-domain/` +2. 直接访问子页面:`http://your-domain/settings` +3. 刷新页面确认不会出现404错误 + +## 常见问题 + +### 1. 刷新页面仍然404 +- 检查Nginx配置中的`try_files`指令是否正确 +- 确认`root`或`alias`路径是否正确 +- 检查文件权限是否正确 + +### 2. 静态资源加载失败 +- 检查`base`路径配置是否与部署路径匹配 +- 确认静态资源文件是否存在 +- 检查Nginx静态文件配置 + +### 3. API请求失败 +- 配置API代理(如果后端API在不同端口) +- 检查CORS设置 +- 确认API路径配置 + +## 性能优化建议 + +1. **启用Gzip压缩**:减少传输文件大小 +2. **设置静态资源缓存**:提高加载速度 +3. **使用CDN**:加速静态资源访问 +4. **启用HTTP/2**:提高传输效率 +5. **配置SSL证书**:启用HTTPS + +## 安全建议 + +1. **隐藏Nginx版本信息** +2. **设置安全响应头** +3. **禁止访问敏感文件** +4. **配置防火墙规则** +5. **定期更新系统和软件** + +## 监控和日志 + +```nginx +# 访问日志 +access_log /var/log/nginx/drplayer_access.log; + +# 错误日志 +error_log /var/log/nginx/drplayer_error.log; +``` + +通过以上配置,您的Vue SPA应用将能够正确处理所有路由,解决刷新页面404的问题。 \ No newline at end of file diff --git a/dashboard/README.md b/dashboard/docs/README.md similarity index 100% rename from dashboard/README.md rename to dashboard/docs/README.md diff --git a/dashboard/docs/nginx-root.conf b/dashboard/docs/nginx-root.conf new file mode 100644 index 0000000..38c6beb --- /dev/null +++ b/dashboard/docs/nginx-root.conf @@ -0,0 +1,97 @@ +# Nginx配置文件 - 根目录部署 +# 适用于将DrPlayer部署在域名根目录的情况 +# 例如: https://example.com/ + +server { + listen 80; + server_name localhost; # 请替换为您的域名 + + # 网站根目录,指向Vue构建后的dist目录 + root /var/www/html/drplayer; # 请替换为您的实际路径 + index index.html; + + # 启用gzip压缩 + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_proxied any; + gzip_comp_level 6; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml+rss + application/atom+xml + image/svg+xml; + + # 静态资源缓存配置 + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; + } + + # API代理配置(如果需要) + # location /api/ { + # proxy_pass http://backend-server; + # proxy_set_header Host $host; + # proxy_set_header X-Real-IP $remote_addr; + # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # proxy_set_header X-Forwarded-Proto $scheme; + # } + + # Vue Router History模式支持 + # 所有不匹配静态文件的请求都返回index.html + location / { + try_files $uri $uri/ /index.html; + + # 安全头设置 + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + } + + # 禁止访问隐藏文件 + location ~ /\. { + deny all; + access_log off; + log_not_found off; + } + + # 禁止访问备份文件 + location ~ ~$ { + deny all; + access_log off; + log_not_found off; + } + + # 错误页面 + error_page 404 /index.html; + error_page 500 502 503 504 /index.html; +} + +# HTTPS配置示例(可选) +# server { +# listen 443 ssl http2; +# server_name localhost; # 请替换为您的域名 +# +# ssl_certificate /path/to/your/certificate.crt; +# ssl_certificate_key /path/to/your/private.key; +# +# # SSL配置 +# ssl_protocols TLSv1.2 TLSv1.3; +# ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384; +# ssl_prefer_server_ciphers off; +# +# # 其他配置与HTTP相同 +# root /var/www/html/drplayer; +# index index.html; +# +# location / { +# try_files $uri $uri/ /index.html; +# } +# } \ No newline at end of file diff --git a/dashboard/docs/nginx-subdir.conf b/dashboard/docs/nginx-subdir.conf new file mode 100644 index 0000000..ed85d8c --- /dev/null +++ b/dashboard/docs/nginx-subdir.conf @@ -0,0 +1,115 @@ +# Nginx配置文件 - 子目录部署 +# 适用于将DrPlayer部署在域名子目录的情况 +# 例如: https://example.com/apps/drplayer/ + +server { + listen 80; + server_name localhost; # 请替换为您的域名 + + # 网站根目录 + root /var/www/html; + index index.html; + + # 启用gzip压缩 + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_proxied any; + gzip_comp_level 6; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml+rss + application/atom+xml + image/svg+xml; + + # DrPlayer应用配置 - 子目录部署 + location /apps/drplayer/ { + alias /var/www/html/drplayer/; # 请替换为您的实际路径 + + # 静态资源缓存配置 + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; + } + + # Vue Router History模式支持 + # 所有不匹配静态文件的请求都返回index.html + try_files $uri $uri/ /apps/drplayer/index.html; + + # 安全头设置 + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + } + + # 处理DrPlayer的根路径访问 + location = /apps/drplayer { + return 301 /apps/drplayer/; + } + + # API代理配置(如果需要) + # location /apps/drplayer/api/ { + # proxy_pass http://backend-server/api/; + # proxy_set_header Host $host; + # proxy_set_header X-Real-IP $remote_addr; + # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # proxy_set_header X-Forwarded-Proto $scheme; + # } + + # 其他应用或默认站点配置 + location / { + try_files $uri $uri/ =404; + } + + # 禁止访问隐藏文件 + location ~ /\. { + deny all; + access_log off; + log_not_found off; + } + + # 禁止访问备份文件 + location ~ ~$ { + deny all; + access_log off; + log_not_found off; + } + + # 错误页面 + error_page 404 /404.html; + error_page 500 502 503 504 /50x.html; +} + +# HTTPS配置示例(可选) +# server { +# listen 443 ssl http2; +# server_name localhost; # 请替换为您的域名 +# +# ssl_certificate /path/to/your/certificate.crt; +# ssl_certificate_key /path/to/your/private.key; +# +# # SSL配置 +# ssl_protocols TLSv1.2 TLSv1.3; +# ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384; +# ssl_prefer_server_ciphers off; +# +# # 其他配置与HTTP相同 +# root /var/www/html; +# index index.html; +# +# location /apps/drplayer/ { +# alias /var/www/html/drplayer/; +# try_files $uri $uri/ /apps/drplayer/index.html; +# } +# +# location = /apps/drplayer { +# return 301 /apps/drplayer/; +# } +# } \ No newline at end of file diff --git a/dashboard/fastify-spa-config.js b/dashboard/fastify-spa-config.js new file mode 100644 index 0000000..ab2ac9b --- /dev/null +++ b/dashboard/fastify-spa-config.js @@ -0,0 +1,116 @@ +// Fastify SPA配置示例 - 解决Vue Router刷新404问题 +// 适用于将Vue应用作为Fastify静态文件服务的子目录部署 + +import Fastify from 'fastify'; +import fastifyStatic from '@fastify/static'; +import path from 'path'; +import fs from 'fs'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const fastify = Fastify({ logger: true }); + +// 注册静态文件插件 +async function setupSPA(fastify, options) { + // 1. 注册静态文件服务 + await fastify.register(fastifyStatic, { + root: options.appsDir, // 应用程序文件根目录 + prefix: '/apps/', // URL访问前缀 + decorateReply: false, // 禁用 sendFile 装饰器,避免冲突 + // 设置静态文件缓存 + setHeaders: (res, path) => { + if (path.endsWith('.html')) { + // HTML文件不缓存,确保路由更新 + res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); + } else if (path.match(/\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$/)) { + // 静态资源长期缓存 + res.setHeader('Cache-Control', 'public, max-age=31536000'); + } + } + }); + + // 2. SPA路由回退处理 - 关键配置 + // 处理DrPlayer应用的所有路由,回退到index.html + fastify.get('/apps/drplayer/*', async (request, reply) => { + const requestedPath = request.params['*']; + const fullPath = path.join(options.appsDir, 'drplayer', requestedPath); + + try { + // 检查请求的文件是否存在 + await fs.promises.access(fullPath); + // 如果文件存在,让静态文件服务处理 + return reply.callNotFound(); + } catch (error) { + // 文件不存在,返回index.html让Vue Router处理 + const indexPath = path.join(options.appsDir, 'drplayer', 'index.html'); + + try { + const indexContent = await fs.promises.readFile(indexPath, 'utf8'); + reply + .type('text/html') + .header('Cache-Control', 'no-cache, no-store, must-revalidate') + .send(indexContent); + } catch (indexError) { + reply.code(404).send({ error: 'Application not found' }); + } + } + }); + + // 3. 处理根路径访问,重定向到带斜杠的路径 + fastify.get('/apps/drplayer', async (request, reply) => { + reply.redirect(301, '/apps/drplayer/'); + }); + + // 4. 处理DrPlayer应用根路径 + fastify.get('/apps/drplayer/', async (request, reply) => { + const indexPath = path.join(options.appsDir, 'drplayer', 'index.html'); + + try { + const indexContent = await fs.promises.readFile(indexPath, 'utf8'); + reply + .type('text/html') + .header('Cache-Control', 'no-cache, no-store, must-revalidate') + .send(indexContent); + } catch (error) { + reply.code(404).send({ error: 'Application not found' }); + } + }); + + // 5. API路由(如果需要) + fastify.register(async function (fastify) { + // 您的API路由 + fastify.get('/api/health', async (request, reply) => { + return { status: 'ok' }; + }); + }); +} + +// 使用示例 +async function start() { + try { + // 配置选项 + const options = { + appsDir: path.join(__dirname, 'apps'), // 您的apps目录路径 + }; + + // 注册SPA配置 + await fastify.register(setupSPA, options); + + // 启动服务器 + await fastify.listen({ port: 3000, host: '0.0.0.0' }); + console.log('Server is running on http://localhost:3000'); + console.log('DrPlayer app available at: http://localhost:3000/apps/drplayer/'); + } catch (err) { + fastify.log.error(err); + process.exit(1); + } +} + +// 如果直接运行此文件 +if (import.meta.url === `file://${process.argv[1]}`) { + start(); +} + +export { setupSPA }; \ No newline at end of file diff --git a/dashboard/fastify-spa-routes.js b/dashboard/fastify-spa-routes.js new file mode 100644 index 0000000..08f90d0 --- /dev/null +++ b/dashboard/fastify-spa-routes.js @@ -0,0 +1,87 @@ +// Fastify SPA路由配置片段 +// 在您现有的Fastify应用中添加以下代码来支持Vue SPA路由 + +import path from 'path'; +import fs from 'fs'; + +// 在您现有的Fastify应用中添加这些路由 +async function addSPARoutes(fastify, options) { + // 支持的SPA应用列表,可以配置多个应用 + const spaApps = options.spaApps || ['drplayer']; + + // 为每个SPA应用注册路由回退 + for (const appName of spaApps) { + // 1. 处理根路径重定向 + fastify.get(`/apps/${appName}`, async (request, reply) => { + return reply.redirect(301, `/apps/${appName}/`); + }); + + // 2. 处理应用根路径 + fastify.get(`/apps/${appName}/`, async (request, reply) => { + const indexPath = path.join(options.appsDir, appName, 'index.html'); + + try { + const indexContent = await fs.promises.readFile(indexPath, 'utf8'); + return reply + .type('text/html') + .header('Cache-Control', 'no-cache, no-store, must-revalidate') + .send(indexContent); + } catch (error) { + return reply.code(404).send({ error: `${appName} application not found` }); + } + }); + } + + // 3. 设置404处理器来处理SPA路由回退 + fastify.setNotFoundHandler(async (request, reply) => { + const url = request.url; + + // 检查是否是SPA应用的路由请求 + for (const appName of spaApps) { + const appPrefix = `/apps/${appName}/`; + + if (url.startsWith(appPrefix)) { + // 检查是否是静态资源请求(有文件扩展名) + const urlPath = url.replace(appPrefix, ''); + const hasExtension = /\.[a-zA-Z0-9]+(\?.*)?$/.test(urlPath); + + if (!hasExtension) { + // 没有扩展名,可能是Vue路由,返回index.html + const indexPath = path.join(options.appsDir, appName, 'index.html'); + + try { + const indexContent = await fs.promises.readFile(indexPath, 'utf8'); + return reply + .type('text/html') + .header('Cache-Control', 'no-cache, no-store, must-revalidate') + .send(indexContent); + } catch (error) { + return reply.code(404).send({ error: `${appName} application not found` }); + } + } + } + } + + // 不是SPA应用路由,返回默认404 + return reply.code(404).send({ error: 'Not Found' }); + }); +} + +export { addSPARoutes }; + +// 使用方法: +// import { addSPARoutes } from './fastify-spa-routes.js'; +// import fastifyStatic from '@fastify/static'; +// +// // 在您的Fastify应用中: +// await fastify.register(fastifyStatic, { +// root: options.appsDir, +// prefix: '/apps/', +// decorateReply: false, +// }); +// +// // 添加SPA路由支持 +// await fastify.register(addSPARoutes, { +// appsDir: options.appsDir, +// spaApps: ['drplayer', 'admin-panel'] // 指定哪些应用需要SPA路由支持 +// }); \ No newline at end of file diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json deleted file mode 100644 index 9524960..0000000 --- a/dashboard/package-lock.json +++ /dev/null @@ -1,2776 +0,0 @@ -{ - "name": "dashboard", - "version": "0.0.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "dashboard", - "version": "0.0.1", - "dependencies": { - "axios": "^1.7.7", - "echarts": "^5.6.0", - "json-server": "^0.17.4", - "pinia": "^2.2.6", - "v-viewer": "^3.0.22", - "vue": "^3.5.12", - "vue-echarts": "^7.0.3", - "vue-router": "^4.4.5" - }, - "devDependencies": { - "@arco-design/web-vue": "^2.56.3", - "@vitejs/plugin-vue": "^5.1.4", - "unplugin-vue-components": "^0.27.4", - "vite": "^5.4.10" - } - }, - "node_modules/@antfu/utils": { - "version": "0.7.10", - "resolved": "https://registry.npmmirror.com/@antfu/utils/-/utils-0.7.10.tgz", - "integrity": "sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@arco-design/color": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@arco-design/color/-/color-0.4.0.tgz", - "integrity": "sha512-s7p9MSwJgHeL8DwcATaXvWT3m2SigKpxx4JA1BGPHL4gfvaQsmQfrLBDpjOJFJuJ2jG2dMt3R3P8Pm9E65q18g==", - "dev": true, - "license": "MIT", - "dependencies": { - "color": "^3.1.3" - } - }, - "node_modules/@arco-design/web-vue": { - "version": "2.56.3", - "resolved": "https://registry.npmmirror.com/@arco-design/web-vue/-/web-vue-2.56.3.tgz", - "integrity": "sha512-D2CPIXRBUPcg37TFsfWROZddCWFZnIwqGpsOhOn2BhmH89UFqtBGpTxyuMdYJEwKNXunp3dVL6V69ZMmJBRPOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@arco-design/color": "^0.4.0", - "b-tween": "^0.3.3", - "b-validate": "^1.4.4", - "compute-scroll-into-view": "^1.0.17", - "dayjs": "^1.10.3", - "number-precision": "^1.5.0", - "resize-observer-polyfill": "^1.5.1", - "scroll-into-view-if-needed": "^2.2.28" - }, - "peerDependencies": { - "vue": "^3.1.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.26.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "license": "MIT" - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.1.3", - "resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.1.3.tgz", - "integrity": "sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.24.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.4.tgz", - "integrity": "sha512-LTw1Dfd0mBIEqUVCxbvTE/LLo+9ZxVC9k99v1v4ahg9Aak6FpqOfNu5kRkeTAn0wphoC4JU7No1/rL+bBCEwhg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@vitejs/plugin-vue": { - "version": "5.1.4", - "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.1.4.tgz", - "integrity": "sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "vite": "^5.0.0", - "vue": "^3.2.25" - } - }, - "node_modules/@vue/compiler-core": { - "version": "3.5.12", - "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.12.tgz", - "integrity": "sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.25.3", - "@vue/shared": "3.5.12", - "entities": "^4.5.0", - "estree-walker": "^2.0.2", - "source-map-js": "^1.2.0" - } - }, - "node_modules/@vue/compiler-dom": { - "version": "3.5.12", - "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz", - "integrity": "sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==", - "license": "MIT", - "dependencies": { - "@vue/compiler-core": "3.5.12", - "@vue/shared": "3.5.12" - } - }, - "node_modules/@vue/compiler-sfc": { - "version": "3.5.12", - "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.12.tgz", - "integrity": "sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.25.3", - "@vue/compiler-core": "3.5.12", - "@vue/compiler-dom": "3.5.12", - "@vue/compiler-ssr": "3.5.12", - "@vue/shared": "3.5.12", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.11", - "postcss": "^8.4.47", - "source-map-js": "^1.2.0" - } - }, - "node_modules/@vue/compiler-ssr": { - "version": "3.5.12", - "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz", - "integrity": "sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==", - "license": "MIT", - "dependencies": { - "@vue/compiler-dom": "3.5.12", - "@vue/shared": "3.5.12" - } - }, - "node_modules/@vue/devtools-api": { - "version": "6.6.4", - "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz", - "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", - "license": "MIT" - }, - "node_modules/@vue/reactivity": { - "version": "3.5.12", - "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.12.tgz", - "integrity": "sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==", - "license": "MIT", - "dependencies": { - "@vue/shared": "3.5.12" - } - }, - "node_modules/@vue/runtime-core": { - "version": "3.5.12", - "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.12.tgz", - "integrity": "sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==", - "license": "MIT", - "dependencies": { - "@vue/reactivity": "3.5.12", - "@vue/shared": "3.5.12" - } - }, - "node_modules/@vue/runtime-dom": { - "version": "3.5.12", - "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.12.tgz", - "integrity": "sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==", - "license": "MIT", - "dependencies": { - "@vue/reactivity": "3.5.12", - "@vue/runtime-core": "3.5.12", - "@vue/shared": "3.5.12", - "csstype": "^3.1.3" - } - }, - "node_modules/@vue/server-renderer": { - "version": "3.5.12", - "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.12.tgz", - "integrity": "sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==", - "license": "MIT", - "dependencies": { - "@vue/compiler-ssr": "3.5.12", - "@vue/shared": "3.5.12" - }, - "peerDependencies": { - "vue": "3.5.12" - } - }, - "node_modules/@vue/shared": { - "version": "3.5.12", - "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.12.tgz", - "integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==", - "license": "MIT" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ansi-styles/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmmirror.com/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/b-tween": { - "version": "0.3.3", - "resolved": "https://registry.npmmirror.com/b-tween/-/b-tween-0.3.3.tgz", - "integrity": "sha512-oEHegcRpA7fAuc9KC4nktucuZn2aS8htymCPcP3qkEGPqiBH+GfqtqoG2l7LxHngg6O0HFM7hOeOYExl1Oz4ZA==", - "dev": true, - "license": "MIT" - }, - "node_modules/b-validate": { - "version": "1.5.3", - "resolved": "https://registry.npmmirror.com/b-validate/-/b-validate-1.5.3.tgz", - "integrity": "sha512-iCvCkGFskbaYtfQ0a3GmcQCHl/Sv1GufXFGuUQ+FE+WJa7A/espLOuFIn09B944V8/ImPj71T4+rTASxO2PAuA==", - "dev": true, - "license": "MIT" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/basic-auth/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==", - "license": "MIT" - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmmirror.com/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-convert/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmmirror.com/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmmirror.com/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "license": "MIT", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compressible/node_modules/mime-db": { - "version": "1.53.0", - "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.53.0.tgz", - "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.7.5", - "resolved": "https://registry.npmmirror.com/compression/-/compression-1.7.5.tgz", - "integrity": "sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "compressible": "~2.0.18", - "debug": "2.6.9", - "negotiator": "~0.6.4", - "on-headers": "~1.0.2", - "safe-buffer": "5.2.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compute-scroll-into-view": { - "version": "1.0.20", - "resolved": "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz", - "integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==", - "dev": true, - "license": "MIT" - }, - "node_modules/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/connect-pause": { - "version": "0.1.1", - "resolved": "https://registry.npmmirror.com/connect-pause/-/connect-pause-0.1.1.tgz", - "integrity": "sha512-a1gSWQBQD73krFXdUEYJom2RTFrWUL3YvXDCRkyv//GVXc79cdW9MngtRuN9ih4FDKBtfJAJId+BbDuX+1rh2w==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmmirror.com/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "license": "MIT" - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT" - }, - "node_modules/dayjs": { - "version": "1.11.13", - "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz", - "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", - "dev": true, - "license": "MIT" - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/echarts": { - "version": "5.6.0", - "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz", - "integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "2.3.0", - "zrender": "5.6.1" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/errorhandler": { - "version": "1.5.1", - "resolved": "https://registry.npmmirror.com/errorhandler/-/errorhandler-1.5.1.tgz", - "integrity": "sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.7", - "escape-html": "~1.0.3" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmmirror.com/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express-urlrewrite": { - "version": "1.4.0", - "resolved": "https://registry.npmmirror.com/express-urlrewrite/-/express-urlrewrite-1.4.0.tgz", - "integrity": "sha512-PI5h8JuzoweS26vFizwQl6UTF25CAHSggNv0J25Dn/IKZscJHWZzPrI5z2Y2jgOzIaw2qh8l6+/jUcig23Z2SA==", - "license": "MIT", - "dependencies": { - "debug": "*", - "path-to-regexp": "^1.0.3" - } - }, - "node_modules/express-urlrewrite/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/express-urlrewrite/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/express-urlrewrite/node_modules/path-to-regexp": { - "version": "1.9.0", - "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-1.9.0.tgz", - "integrity": "sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==", - "license": "MIT", - "dependencies": { - "isarray": "0.0.1" - } - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmmirror.com/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "license": "MIT" - }, - "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmmirror.com/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "license": "MIT" - }, - "node_modules/jju": { - "version": "1.4.0", - "resolved": "https://registry.npmmirror.com/jju/-/jju-1.4.0.tgz", - "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", - "license": "MIT" - }, - "node_modules/json-parse-helpfulerror": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz", - "integrity": "sha512-XgP0FGR77+QhUxjXkwOMkC94k3WtqEBfcnjWqhRd82qTat4SWKRE+9kUnynz/shm3I4ea2+qISvTIeGTNU7kJg==", - "license": "MIT", - "dependencies": { - "jju": "^1.1.0" - } - }, - "node_modules/json-server": { - "version": "0.17.4", - "resolved": "https://registry.npmmirror.com/json-server/-/json-server-0.17.4.tgz", - "integrity": "sha512-bGBb0WtFuAKbgI7JV3A864irWnMZSvBYRJbohaOuatHwKSRFUfqtQlrYMrB6WbalXy/cJabyjlb7JkHli6dYjQ==", - "license": "MIT", - "dependencies": { - "body-parser": "^1.19.0", - "chalk": "^4.1.2", - "compression": "^1.7.4", - "connect-pause": "^0.1.1", - "cors": "^2.8.5", - "errorhandler": "^1.5.1", - "express": "^4.17.1", - "express-urlrewrite": "^1.4.0", - "json-parse-helpfulerror": "^1.0.3", - "lodash": "^4.17.21", - "lodash-id": "^0.14.1", - "lowdb": "^1.0.0", - "method-override": "^3.0.0", - "morgan": "^1.10.0", - "nanoid": "^3.1.23", - "please-upgrade-node": "^3.2.0", - "pluralize": "^8.0.0", - "server-destroy": "^1.0.1", - "yargs": "^17.0.1" - }, - "bin": { - "json-server": "lib/cli/bin.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/local-pkg": { - "version": "0.5.0", - "resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-0.5.0.tgz", - "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mlly": "^1.4.2", - "pkg-types": "^1.0.3" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "license": "MIT" - }, - "node_modules/lodash-id": { - "version": "0.14.1", - "resolved": "https://registry.npmmirror.com/lodash-id/-/lodash-id-0.14.1.tgz", - "integrity": "sha512-ikQPBTiq/d5m6dfKQlFdIXFzvThPi2Be9/AHxktOnDSfSxE1j9ICbBT5Elk1ke7HSTgM38LHTpmJovo9/klnLg==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/lowdb": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/lowdb/-/lowdb-1.0.0.tgz", - "integrity": "sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.3", - "is-promise": "^2.1.0", - "lodash": "4", - "pify": "^3.0.0", - "steno": "^0.4.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/magic-string": { - "version": "0.30.12", - "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.12.tgz", - "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/method-override": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/method-override/-/method-override-3.0.0.tgz", - "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", - "license": "MIT", - "dependencies": { - "debug": "3.1.0", - "methods": "~1.1.2", - "parseurl": "~1.3.2", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/method-override/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mlly": { - "version": "1.7.2", - "resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.7.2.tgz", - "integrity": "sha512-tN3dvVHYVz4DhSXinXIk7u9syPYaJvio118uomkovAtWBT+RdbP6Lfh/5Lvo519YMmwBafwlh20IPTXIStscpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.12.1", - "pathe": "^1.1.2", - "pkg-types": "^1.2.0", - "ufo": "^1.5.4" - } - }, - "node_modules/morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmmirror.com/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", - "license": "MIT", - "dependencies": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/morgan/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/number-precision": { - "version": "1.6.0", - "resolved": "https://registry.npmmirror.com/number-precision/-/number-precision-1.6.0.tgz", - "integrity": "sha512-05OLPgbgmnixJw+VvEh18yNPUo3iyp4BEWJcrLu4X9W05KmMifN7Mu5exYvQXqxxeNWhvIF+j3Rij+HmddM/hQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", - "license": "MIT" - }, - "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/pinia": { - "version": "2.2.6", - "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.2.6.tgz", - "integrity": "sha512-vIsR8JkDN5Ga2vAxqOE2cJj4VtsHnzpR1Fz30kClxlh0yCHfec6uoMeM3e/ddqmwFUejK3NlrcQa/shnpyT4hA==", - "license": "MIT", - "dependencies": { - "@vue/devtools-api": "^6.6.3", - "vue-demi": "^0.14.10" - }, - "funding": { - "url": "https://github.com/sponsors/posva" - }, - "peerDependencies": { - "@vue/composition-api": "^1.4.0", - "typescript": ">=4.4.4", - "vue": "^2.6.14 || ^3.5.11" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/pkg-types": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.2.1.tgz", - "integrity": "sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.2", - "pathe": "^1.1.2" - } - }, - "node_modules/please-upgrade-node": { - "version": "3.2.0", - "resolved": "https://registry.npmmirror.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", - "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", - "license": "MIT", - "dependencies": { - "semver-compare": "^1.0.0" - } - }, - "node_modules/pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmmirror.com/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.1.0", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmmirror.com/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resize-observer-polyfill": { - "version": "1.5.1", - "resolved": "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", - "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", - "dev": true, - "license": "MIT" - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rollup": { - "version": "4.24.4", - "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.24.4.tgz", - "integrity": "sha512-vGorVWIsWfX3xbcyAS+I047kFKapHYivmkaT63Smj77XwvLSJos6M1xGqZnBPFQFBRZDOcG1QnYEIxAvTr/HjA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.6" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.24.4", - "@rollup/rollup-android-arm64": "4.24.4", - "@rollup/rollup-darwin-arm64": "4.24.4", - "@rollup/rollup-darwin-x64": "4.24.4", - "@rollup/rollup-freebsd-arm64": "4.24.4", - "@rollup/rollup-freebsd-x64": "4.24.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.24.4", - "@rollup/rollup-linux-arm-musleabihf": "4.24.4", - "@rollup/rollup-linux-arm64-gnu": "4.24.4", - "@rollup/rollup-linux-arm64-musl": "4.24.4", - "@rollup/rollup-linux-powerpc64le-gnu": "4.24.4", - "@rollup/rollup-linux-riscv64-gnu": "4.24.4", - "@rollup/rollup-linux-s390x-gnu": "4.24.4", - "@rollup/rollup-linux-x64-gnu": "4.24.4", - "@rollup/rollup-linux-x64-musl": "4.24.4", - "@rollup/rollup-win32-arm64-msvc": "4.24.4", - "@rollup/rollup-win32-ia32-msvc": "4.24.4", - "@rollup/rollup-win32-x64-msvc": "4.24.4", - "fsevents": "~2.3.2" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/scroll-into-view-if-needed": { - "version": "2.2.31", - "resolved": "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz", - "integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==", - "dev": true, - "license": "MIT", - "dependencies": { - "compute-scroll-into-view": "^1.0.20" - } - }, - "node_modules/semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", - "license": "MIT" - }, - "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmmirror.com/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/server-destroy": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/server-destroy/-/server-destroy-1.0.1.tgz", - "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", - "license": "ISC" - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/steno": { - "version": "0.4.4", - "resolved": "https://registry.npmmirror.com/steno/-/steno-0.4.4.tgz", - "integrity": "sha512-EEHMVYHNXFHfGtgjNITnka0aHhiAlo93F7z2/Pwd+g0teG9CnM3JIINM7hVVB5/rhw9voufD7Wukwgtw2uqh6w==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.3" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tslib": { - "version": "2.3.0", - "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", - "license": "0BSD" - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ufo": { - "version": "1.5.4", - "resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.5.4.tgz", - "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/unplugin": { - "version": "1.15.0", - "resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-1.15.0.tgz", - "integrity": "sha512-jTPIs63W+DUEDW207ztbaoO7cQ4p5aVaB823LSlxpsFEU3Mykwxf3ZGC/wzxFJeZlASZYgVrWeo7LgOrqJZ8RA==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.14.0", - "webpack-virtual-modules": "^0.6.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "webpack-sources": "^3" - }, - "peerDependenciesMeta": { - "webpack-sources": { - "optional": true - } - } - }, - "node_modules/unplugin-vue-components": { - "version": "0.27.4", - "resolved": "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-0.27.4.tgz", - "integrity": "sha512-1XVl5iXG7P1UrOMnaj2ogYa5YTq8aoh5jwDPQhemwO/OrXW+lPQKDXd1hMz15qxQPxgb/XXlbgo3HQ2rLEbmXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@antfu/utils": "^0.7.10", - "@rollup/pluginutils": "^5.1.0", - "chokidar": "^3.6.0", - "debug": "^4.3.6", - "fast-glob": "^3.3.2", - "local-pkg": "^0.5.0", - "magic-string": "^0.30.11", - "minimatch": "^9.0.5", - "mlly": "^1.7.1", - "unplugin": "^1.12.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@babel/parser": "^7.15.8", - "@nuxt/kit": "^3.2.2", - "vue": "2 || 3" - }, - "peerDependenciesMeta": { - "@babel/parser": { - "optional": true - }, - "@nuxt/kit": { - "optional": true - } - } - }, - "node_modules/unplugin-vue-components/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/unplugin-vue-components/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/v-viewer": { - "version": "3.0.22", - "resolved": "https://registry.npmmirror.com/v-viewer/-/v-viewer-3.0.22.tgz", - "integrity": "sha512-uYyP5FPT4K/Sd5D1mhB2HMVV8jnf6zYy2HD1PHCNAO6s2Iway+Wls60pwh7y4F3e2Nlc9549Pvy2HXaq8PKrAg==", - "license": "MIT", - "dependencies": { - "lodash-es": "^4.17.21", - "viewerjs": "^1.11.6" - }, - "peerDependencies": { - "viewerjs": "^1.11.0", - "vue": "^3.0.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/viewerjs": { - "version": "1.11.7", - "resolved": "https://registry.npmmirror.com/viewerjs/-/viewerjs-1.11.7.tgz", - "integrity": "sha512-0JuVqOmL5v1jmEAlG5EBDR3XquxY8DWFQbFMprOXgaBB0F7Q/X9xWdEaQc59D8xzwkdUgXEMSSknTpriq95igg==", - "license": "MIT" - }, - "node_modules/vite": { - "version": "5.4.10", - "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.10.tgz", - "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vue": { - "version": "3.5.12", - "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.12.tgz", - "integrity": "sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==", - "license": "MIT", - "dependencies": { - "@vue/compiler-dom": "3.5.12", - "@vue/compiler-sfc": "3.5.12", - "@vue/runtime-dom": "3.5.12", - "@vue/server-renderer": "3.5.12", - "@vue/shared": "3.5.12" - }, - "peerDependencies": { - "typescript": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/vue-demi": { - "version": "0.14.10", - "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", - "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } - } - }, - "node_modules/vue-echarts": { - "version": "7.0.3", - "resolved": "https://registry.npmmirror.com/vue-echarts/-/vue-echarts-7.0.3.tgz", - "integrity": "sha512-/jSxNwOsw5+dYAUcwSfkLwKPuzTQ0Cepz1LxCOpj2QcHrrmUa/Ql0eQqMmc1rTPQVrh2JQ29n2dhq75ZcHvRDw==", - "license": "MIT", - "dependencies": { - "vue-demi": "^0.13.11" - }, - "peerDependencies": { - "@vue/runtime-core": "^3.0.0", - "echarts": "^5.5.1", - "vue": "^2.7.0 || ^3.1.1" - }, - "peerDependenciesMeta": { - "@vue/runtime-core": { - "optional": true - } - } - }, - "node_modules/vue-echarts/node_modules/vue-demi": { - "version": "0.13.11", - "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz", - "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } - } - }, - "node_modules/vue-router": { - "version": "4.4.5", - "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.4.5.tgz", - "integrity": "sha512-4fKZygS8cH1yCyuabAXGUAsyi1b2/o/OKgu/RUb+znIYOxPRxdkytJEx+0wGcpBE1pX6vUgh5jwWOKRGvuA/7Q==", - "license": "MIT", - "dependencies": { - "@vue/devtools-api": "^6.6.4" - }, - "funding": { - "url": "https://github.com/sponsors/posva" - }, - "peerDependencies": { - "vue": "^3.2.0" - } - }, - "node_modules/webpack-virtual-modules": { - "version": "0.6.2", - "resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", - "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/zrender": { - "version": "5.6.1", - "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.6.1.tgz", - "integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==", - "license": "BSD-3-Clause", - "dependencies": { - "tslib": "2.3.0" - } - } - } -} diff --git a/dashboard/package.json b/dashboard/package.json index bfec268..b63974f 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -5,24 +5,32 @@ "scripts": { "dev": "vite", "build": "vite build", + "build:root": "vite build --mode production.root", + "build:apps": "vite build --mode production.apps", + "build:fastify": "node build-for-fastify.js", "preview": "vite preview", + "start": "node production-server.js", + "start:prod": "node production-server.js", "mock1": "json-server src/mock/data.json --host 127.0.0.1 --port 9978 --middlewares src/mock/middlewares.js", "mock2": "json-server public/mock/data.json --host 127.0.0.1 --port 9978 --middlewares public/mock/middlewares.js" }, "dependencies": { + "@fastify/static": "^8.2.0", "axios": "1.12.0", "echarts": "^5.6.0", + "fastify": "^5.6.1", "json-server": "^0.17.4", "pinia": "^2.2.6", "v-viewer": "^3.0.22", + "viewerjs": "^1.11.0", "vue": "^3.5.12", "vue-echarts": "^7.0.3", "vue-router": "^4.4.5" }, "devDependencies": { "@arco-design/web-vue": "^2.56.3", - "@vitejs/plugin-vue": "^5.1.4", + "@vitejs/plugin-vue": "^6.0.1", "unplugin-vue-components": "^0.27.4", - "vite": "^5.4.10" + "vite": "^7.1.7" } } diff --git a/dashboard/production-server.js b/dashboard/production-server.js new file mode 100644 index 0000000..ba6e739 --- /dev/null +++ b/dashboard/production-server.js @@ -0,0 +1,228 @@ +/** + * 生产服务器 - DrPlayer Dashboard + */ + +import Fastify from 'fastify'; +import fastifyStatic from '@fastify/static'; +import {addSPARoutes} from './fastify-spa-routes.js'; +import path from 'path'; +import {fileURLToPath} from 'url'; +import fs from 'fs/promises'; +import {execSync} from 'child_process'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// 构建和部署函数 +async function buildAndDeploy() { + console.log('🔨 开始构建应用...'); + + try { + // 执行构建命令 + execSync('yarn build:fastify', { stdio: 'inherit', cwd: __dirname }); + console.log('✅ 构建完成'); + + // 确保apps目录存在 + const appsDir = path.join(__dirname, 'apps'); + const drplayerDir = path.join(appsDir, 'drplayer'); + + await fs.mkdir(drplayerDir, { recursive: true }); + console.log('📁 创建apps目录'); + + // 复制dist内容到apps/drplayer + const distDir = path.join(__dirname, 'dist'); + await copyDirectory(distDir, drplayerDir); + console.log('📋 复制文件到apps/drplayer'); + + } catch (error) { + console.error('❌ 构建失败:', error.message); + process.exit(1); + } +} + +// 递归复制目录 +async function copyDirectory(src, dest) { + await fs.mkdir(dest, { recursive: true }); + const entries = await fs.readdir(src, { withFileTypes: true }); + + for (const entry of entries) { + const srcPath = path.join(src, entry.name); + const destPath = path.join(dest, entry.name); + + if (entry.isDirectory()) { + await copyDirectory(srcPath, destPath); + } else { + await fs.copyFile(srcPath, destPath); + } + } +} + +const fastify = Fastify({ + logger: true +}); + +// 配置选项 +const PORT = 8008; +const options = { + appsDir: path.join(__dirname, 'apps'), + port: PORT +}; + +// 注册静态文件服务 +await fastify.register(fastifyStatic, { + root: options.appsDir, + prefix: '/apps/', + decorateReply: false, +}); + +// 注册SPA路由支持 +await fastify.register(addSPARoutes, { + appsDir: options.appsDir, + spaApps: ['drplayer'] +}); + +// 根路径 - 显示应用列表 +fastify.get('/', async (request, reply) => { + // 读取package.json获取版本信息 + let version = '1.0.0'; + try { + const packageJsonPath = path.join(__dirname, 'package.json'); + const packageJsonContent = await fs.readFile(packageJsonPath, 'utf8'); + const packageJson = JSON.parse(packageJsonContent); + version = packageJson.version || '1.0.0'; + } catch (error) { + console.warn('无法读取package.json版本信息:', error.message); + } + + const html = ` + + + + + + DrPlayer Dashboard + + + +
+

🎬 DrPlayer Dashboard

+ +
+ ✅ 服务器运行正常 - 端口: ${PORT} +
+ + + + +
+ +`; + + return reply.type('text/html').send(html); +}); + +// 健康检查 +fastify.get('/health', async (request, reply) => { + return {status: 'ok', timestamp: new Date().toISOString()}; +}); + +// 启动服务器 +const start = async () => { + try { + // 先构建和部署 + await buildAndDeploy(); + + // 启动服务器 + await fastify.listen({port: PORT, host: '0.0.0.0'}); + console.log(`🚀 生产服务器启动成功!`); + console.log(`📱 访问地址: http://localhost:${PORT}/apps/drplayer/`); + console.log(`🔍 健康检查: http://localhost:${PORT}/health`); + } catch (err) { + fastify.log.error(err); + process.exit(1); + } +}; + +// 优雅关闭 +process.on('SIGINT', async () => { + console.log('\n🛑 正在关闭服务器...'); + await fastify.close(); + process.exit(0); +}); + +start(); \ No newline at end of file diff --git a/dashboard/src/api/services/config.js b/dashboard/src/api/services/config.js index 7269b8d..1108756 100644 --- a/dashboard/src/api/services/config.js +++ b/dashboard/src/api/services/config.js @@ -70,8 +70,7 @@ class ConfigService { const response = await axios.get(url, { timeout: 10000, headers: { - 'Accept': 'application/json', - 'User-Agent': 'DrPlayer/1.0' + 'Accept': 'application/json' } }) @@ -126,8 +125,7 @@ class ConfigService { const response = await axios.get(this.configUrl, { timeout: 15000, headers: { - 'Accept': 'application/json', - 'User-Agent': 'DrPlayer/1.0' + 'Accept': 'application/json' } }) diff --git a/dashboard/src/views/Settings.vue b/dashboard/src/views/Settings.vue index f499ea4..c0a6528 100644 --- a/dashboard/src/views/Settings.vue +++ b/dashboard/src/views/Settings.vue @@ -22,7 +22,7 @@ class="config-input" >
@@ -51,13 +51,20 @@
-
- +
+
+ + + + {{ configStatus.message }} +
@@ -71,7 +78,7 @@
- +
数据源
管理视频数据来源
@@ -176,7 +183,7 @@
- +
广告过滤
自动过滤广告内容
@@ -401,6 +408,25 @@ diff --git a/dashboard/src/components/SearchResults.vue b/dashboard/src/components/SearchResults.vue index 953748b..358530d 100644 --- a/dashboard/src/components/SearchResults.vue +++ b/dashboard/src/components/SearchResults.vue @@ -100,9 +100,13 @@ import { ref, nextTick, onMounted, onBeforeUnmount, watch } from 'vue' import { useRouter } from 'vue-router' import { usePaginationStore } from '@/stores/paginationStore' +import { usePageStateStore } from '@/stores/pageStateStore' +import { useVisitedStore } from '@/stores/visitedStore' const router = useRouter() const paginationStore = usePaginationStore() +const pageStateStore = usePageStateStore() +const visitedStore = useVisitedStore() // Props const props = defineProps({ @@ -133,6 +137,16 @@ const props = defineProps({ hasMore: { type: Boolean, default: false + }, + // 新增:来源页面信息 + sourceRoute: { + type: Object, + default: () => ({}) + }, + // 新增:滚动位置 + scrollPosition: { + type: Number, + default: 0 } }) @@ -179,7 +193,14 @@ const updateScrollAreaHeight = () => { const searchHeader = document.querySelector('.search-header'); const headerHeight = searchHeader ? searchHeader.offsetHeight : 60; - const newHeight = Math.max(containerHeight - headerHeight, 400); + // 根据搜索结果动态调整高度减值 + // 如果没有结果或结果很少,减去100px强制触发滚动条 + // 如果有足够搜索结果,只减去少量padding空间 + const hasEnoughContent = props.videos && props.videos.length >= 17; + const heightReduction = hasEnoughContent ? 4 : 100; + + const newHeight = Math.max(containerHeight - headerHeight - heightReduction, 400); + console.log(`搜索结果数量: ${props.videos?.length || 0}, 内容充足: ${hasEnoughContent}, 高度减值: ${heightReduction}, 最终高度: ${newHeight}`); scrollAreaHeight.value = newHeight; }, 100); // 延迟确保DOM完全渲染 }); @@ -239,6 +260,31 @@ const checkTextOverflow = () => { // 视频点击处理 const handleVideoClick = (video) => { if (video && video.vod_id) { + // 记录最后点击的视频 + visitedStore.setLastClicked(video.vod_id, video.vod_name) + + // 保存当前搜索状态 + if (props.keyword) { + // 正确获取滚动位置 + const scrollContainer = scrollbarRef.value?.$el?.querySelector('.arco-scrollbar-container'); + const currentScrollPosition = scrollContainer?.scrollTop || 0; + + pageStateStore.saveSearchState( + props.keyword, + props.currentPage, + props.videos, + props.hasMore, + props.loading, + currentScrollPosition + ); + console.log('保存搜索状态:', { + keyword: props.keyword, + currentPage: props.currentPage, + videosCount: props.videos.length, + scrollPosition: currentScrollPosition + }); + } + router.push({ name: 'VideoDetail', params: { id: video.vod_id }, @@ -251,7 +297,12 @@ const handleVideoClick = (video) => { remarks: video.vod_remarks, content: video.vod_content, actor: video.vod_actor, - director: video.vod_director + director: video.vod_director, + // 添加来源页面信息 + sourceRouteName: props.sourceRoute?.name, + sourceRouteParams: JSON.stringify(props.sourceRoute?.params || {}), + sourceRouteQuery: JSON.stringify(props.sourceRoute?.query || {}), + fromSearch: 'true' // 标识来自搜索结果 } }); } @@ -269,6 +320,24 @@ onMounted(() => { updateScrollAreaHeight() updateGlobalStats() window.addEventListener('resize', updateScrollAreaHeight) + + // 恢复滚动位置 + if (props.scrollPosition > 0) { + nextTick(() => { + // 使用requestAnimationFrame确保DOM已渲染 + requestAnimationFrame(() => { + const scrollContainer = scrollbarRef.value?.$el?.querySelector('.arco-scrollbar-container'); + if (scrollContainer) { + // 使用平滑滚动 + scrollContainer.scrollTo({ + top: props.scrollPosition, + behavior: 'smooth' + }); + console.log('SearchResults恢复滚动位置:', props.scrollPosition); + } + }); + }); + } }) onBeforeUnmount(() => { diff --git a/dashboard/src/components/VideoCard.vue b/dashboard/src/components/VideoCard.vue index 6602ddb..257e0a8 100644 --- a/dashboard/src/components/VideoCard.vue +++ b/dashboard/src/components/VideoCard.vue @@ -1,6 +1,7 @@ + + \ No newline at end of file diff --git a/dashboard/src/stores/favoriteStore.js b/dashboard/src/stores/favoriteStore.js index b3d51bf..8052dc2 100644 --- a/dashboard/src/stores/favoriteStore.js +++ b/dashboard/src/stores/favoriteStore.js @@ -209,6 +209,7 @@ export const useFavoriteStore = defineStore('favorite', () => { clearFavorites, exportFavorites, importFavorites, - loadFavorites + loadFavorites, + saveFavorites } }) \ No newline at end of file diff --git a/dashboard/src/views/Collection.vue b/dashboard/src/views/Collection.vue index 9bb473c..772c83a 100644 --- a/dashboard/src/views/Collection.vue +++ b/dashboard/src/views/Collection.vue @@ -35,6 +35,12 @@ 导出收藏 + + + API地址管理 + 导入历史 + + + API地址管理 + @@ -121,7 +144,7 @@ const props = defineProps({ }) // Emits -const emit = defineEmits(['close', 'error', 'player-change', 'next-episode']) +const emit = defineEmits(['close', 'error', 'player-change', 'next-episode', 'episode-selected']) // 响应式数据 const artPlayerContainer = ref(null) @@ -138,6 +161,11 @@ const autoNextTimer = ref(null) // 自动下一集定时器 const showAutoNextDialog = ref(false) // 显示自动下一集对话框 const countdownEnabled = ref(false) // 倒计时开关,默认关闭 +// 选集弹窗相关数据 +const showEpisodeDialog = ref(false) // 显示选集弹窗 +const episodeListRef = ref(null) // 选集列表容器引用 +const currentEpisodeRef = ref(null) // 当前选集按钮引用 + // 链接类型判断函数 const isDirectVideoLink = (url) => { if (!url) return false @@ -322,6 +350,15 @@ const initArtPlayer = async (url) => { playNextEpisode() }, }, + { + position: 'right', + html: props.episodes.length > 1 ? '选集' : '', + tooltip: props.episodes.length > 1 ? '选择集数' : '', + style: props.episodes.length > 1 ? {} : { display: 'none' }, + click: function () { + toggleEpisodeDialog() + }, + }, { position: 'right', html: '关闭', @@ -603,6 +640,79 @@ const toggleCountdown = () => { } } +// 滚动到当前选集位置 +const scrollToCurrentEpisode = async () => { + // 等待DOM更新 + await nextTick() + + if (!episodeListRef.value || props.currentEpisodeIndex < 0) { + return + } + + // 查找当前选集按钮 + const currentButton = episodeListRef.value.querySelector('.episode-item.current') + if (!currentButton) { + return + } + + const container = episodeListRef.value + const containerHeight = container.clientHeight + const containerScrollHeight = container.scrollHeight + const buttonTop = currentButton.offsetTop + const buttonHeight = currentButton.offsetHeight + + // 计算滚动位置,让当前选集出现在容器的中间偏上位置(约30%处) + const targetPosition = buttonTop + (buttonHeight / 2) - (containerHeight * 0.3) + + // 确保滚动位置在有效范围内 + const maxScrollTop = containerScrollHeight - containerHeight + const targetScrollTop = Math.max(0, Math.min(targetPosition, maxScrollTop)) + + // 只有当需要滚动的距离超过一定阈值时才执行滚动 + const currentScrollTop = container.scrollTop + const scrollDistance = Math.abs(targetScrollTop - currentScrollTop) + + if (scrollDistance > 50) { // 滚动距离超过50px才执行 + container.scrollTo({ + top: targetScrollTop, + behavior: 'smooth' + }) + console.log(`自动滚动到当前选集: 第${props.currentEpisodeIndex + 1}集,滚动距离: ${scrollDistance}px`) + } else { + console.log(`当前选集已在可视区域中心,无需滚动: 第${props.currentEpisodeIndex + 1}集`) + } +} + +// 切换选集弹窗显示状态 +const toggleEpisodeDialog = async () => { + showEpisodeDialog.value = !showEpisodeDialog.value + console.log('选集弹窗:', showEpisodeDialog.value ? '显示' : '隐藏') + + // 如果弹窗打开,等待弹窗动画完成后再滚动 + if (showEpisodeDialog.value) { + // 延迟350ms,等待弹窗动画完成(CSS动画时长为300ms) + setTimeout(async () => { + await scrollToCurrentEpisode() + }, 350) + } +} + +// 关闭选集弹窗 +const closeEpisodeDialog = () => { + showEpisodeDialog.value = false +} + +// 选择剧集 +const selectEpisode = (episode) => { + console.log('选择剧集:', episode) + + // 关闭弹窗 + closeEpisodeDialog() + + // 发送选集事件给父组件 + emit('episode-selected', episode) +} + // 监听视频URL变化 watch(() => props.videoUrl, async (newUrl) => { if (newUrl && props.visible) { @@ -929,4 +1039,142 @@ onUnmounted(() => { .btn-cancel:hover { background: #555; } + +/* 选集弹窗样式 */ +.episode-dialog { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 10000; + display: flex; + align-items: center; + justify-content: center; +} + +.episode-dialog-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.7); + backdrop-filter: blur(4px); +} + +.episode-dialog-content { + position: relative; + background: white; + border-radius: 12px; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); + max-width: 600px; + max-height: 80vh; + width: 90%; + overflow: hidden; + animation: episodeDialogShow 0.3s ease-out; +} + +@keyframes episodeDialogShow { + from { + opacity: 0; + transform: scale(0.9) translateY(-20px); + } + to { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +.episode-dialog-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px 24px; + border-bottom: 1px solid #e8e8e8; + background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); +} + +.episode-dialog-header h3 { + margin: 0; + font-size: 18px; + font-weight: 600; + color: #2c3e50; +} + +.episode-close-btn { + background: none; + border: none; + font-size: 24px; + cursor: pointer; + color: #666; + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + transition: all 0.2s ease; +} + +.episode-close-btn:hover { + background: rgba(0, 0, 0, 0.1); + color: #333; +} + +.episode-list { + padding: 16px; + max-height: 60vh; + overflow-y: auto; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 12px; +} + +.episode-item { + display: flex; + align-items: center; + padding: 12px 16px; + border: 2px solid #e8e8e8; + border-radius: 8px; + background: white; + cursor: pointer; + transition: all 0.2s ease; + text-align: left; + min-height: 60px; +} + +.episode-item:hover { + border-color: #23ade5; + background: #f8fcff; + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(35, 173, 229, 0.2); +} + +.episode-item.current { + border-color: #23ade5; + background: linear-gradient(135deg, #23ade5 0%, #1e90ff 100%); + color: white; + box-shadow: 0 4px 12px rgba(35, 173, 229, 0.3); +} + +.episode-item.current:hover { + background: linear-gradient(135deg, #1e90ff 0%, #23ade5 100%); +} + +.episode-number { + font-size: 16px; + font-weight: bold; + margin-right: 12px; + min-width: 24px; + text-align: center; +} + +.episode-name { + font-size: 14px; + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} \ No newline at end of file diff --git a/dashboard/src/stores/favoriteStore.js b/dashboard/src/stores/favoriteStore.js index 8052dc2..607e193 100644 --- a/dashboard/src/stores/favoriteStore.js +++ b/dashboard/src/stores/favoriteStore.js @@ -43,7 +43,8 @@ export const useFavoriteStore = defineStore('favorite', () => { api_info: { module: videoData.module || '', api_url: videoData.api_url || '', - site_name: videoData.site_name || '' + site_name: videoData.site_name || '', + ext: videoData.ext || null // 添加站源扩展配置 }, created_at: new Date().toISOString(), updated_at: new Date().toISOString() diff --git a/dashboard/src/stores/historyStore.js b/dashboard/src/stores/historyStore.js index 2eda638..36a282b 100644 --- a/dashboard/src/stores/historyStore.js +++ b/dashboard/src/stores/historyStore.js @@ -63,6 +63,11 @@ export const useHistoryStore = defineStore('history', () => { const addToHistory = (videoInfo, routeInfo, episodeInfo) => { const now = new Date().toISOString() + // 调试:检查传入的videoInfo + console.log('=== historyStore.addToHistory 调试 ===') + console.log('传入的videoInfo.api_info:', videoInfo.api_info) + console.log('传入的videoInfo.api_info.ext:', videoInfo.api_info.ext) + // 检查是否已存在相同的视频 const existingIndex = histories.value.findIndex( item => item.id === videoInfo.id && item.api_info.api_url === videoInfo.api_info.api_url @@ -84,16 +89,24 @@ export const useHistoryStore = defineStore('history', () => { ...histories.value[existingIndex], ...historyItem } + console.log('更新后的历史记录api_info:', histories.value[existingIndex].api_info) } else { // 添加新记录 historyItem.created_at = now histories.value.push(historyItem) + console.log('新添加的历史记录api_info:', historyItem.api_info) } - + + console.log('=== historyStore.addToHistory 调试结束 ===') saveHistories() } const removeFromHistory = (item) => { + if (!item || !item.id || !item.api_info || !item.api_info.api_url) { + console.error('删除历史记录失败:参数无效', item) + return false + } + const index = histories.value.findIndex( h => h.id === item.id && h.api_info.api_url === item.api_info.api_url ) @@ -101,6 +114,11 @@ export const useHistoryStore = defineStore('history', () => { if (index !== -1) { histories.value.splice(index, 1) saveHistories() + console.log('删除历史记录成功:', item.name) + return true + } else { + console.warn('未找到要删除的历史记录:', item) + return false } } diff --git a/dashboard/src/views/BookGallery.vue b/dashboard/src/views/BookGallery.vue index b1644f0..0b1409d 100644 --- a/dashboard/src/views/BookGallery.vue +++ b/dashboard/src/views/BookGallery.vue @@ -399,10 +399,11 @@ const goToDetail = async (item) => { const siteInfo = { name: item.api_info.site_name, api: item.api_info.api_url, - key: item.api_info.module + key: item.api_info.module, + ext: item.api_info.ext || null // 从收藏数据中获取extend参数 } - console.log('从书画柜进入详情页,使用临时站源:', siteInfo.name) + console.log('从书画柜进入详情页,使用临时站源:', siteInfo.name, '扩展参数:', siteInfo.ext) // 跳转到详情页,传递站源信息 router.push({ @@ -424,6 +425,7 @@ const goToDetail = async (item) => { tempSiteName: siteInfo.name, tempSiteApi: siteInfo.api, tempSiteKey: siteInfo.key, + tempSiteExt: siteInfo.ext, // 添加extend参数传递 // 添加来源页面信息 sourceRouteName: 'BookGallery', sourceRouteParams: JSON.stringify({}), diff --git a/dashboard/src/views/Collection.vue b/dashboard/src/views/Collection.vue index 1ab6fdb..c37b79a 100644 --- a/dashboard/src/views/Collection.vue +++ b/dashboard/src/views/Collection.vue @@ -339,14 +339,20 @@ const removeFavorite = (item) => { const goToDetail = async (item) => { try { + // 调试:打印完整的收藏项 + console.log('收藏项完整数据:', item) + console.log('收藏api_info:', item.api_info) + // 不再切换全局站源,而是通过路由参数传递站源信息 const siteInfo = { name: item.api_info.site_name, api: item.api_info.api_url, - key: item.api_info.module + key: item.api_info.module, + ext: item.api_info.ext || null // 从收藏数据中获取extend参数 } - console.log('从收藏进入详情页,使用临时站源:', siteInfo.name) + console.log('从收藏进入详情页,使用临时站源:', siteInfo.name, '扩展参数:', siteInfo.ext) + console.log('siteInfo完整对象:', siteInfo) // 跳转到详情页,传递站源信息 router.push({ @@ -368,6 +374,7 @@ const goToDetail = async (item) => { tempSiteName: siteInfo.name, tempSiteApi: siteInfo.api, tempSiteKey: siteInfo.key, + tempSiteExt: siteInfo.ext, // 添加extend参数传递 // 添加来源页面信息 sourceRouteName: 'Collection', sourceRouteParams: JSON.stringify({}), diff --git a/dashboard/src/views/History.vue b/dashboard/src/views/History.vue index a254ffc..5ff32e9 100644 --- a/dashboard/src/views/History.vue +++ b/dashboard/src/views/History.vue @@ -234,6 +234,18 @@ const filteredHistory = computed(() => { // 生命周期 onMounted(() => { historyStore.loadHistories() + + // 临时调试:检查现有历史记录数据结构 + setTimeout(() => { + console.log('=== 历史记录数据结构调试 ===') + console.log('历史记录总数:', historyStore.historyCount) + if (historyStore.histories.length > 0) { + console.log('第一条历史记录完整数据:', historyStore.histories[0]) + console.log('第一条历史记录api_info:', historyStore.histories[0].api_info) + console.log('第一条历史记录是否有ext字段:', 'ext' in historyStore.histories[0].api_info) + } + console.log('=== 调试结束 ===') + }, 1000) }) // 方法 @@ -309,24 +321,28 @@ const removeHistory = (item) => { cancelText: '保留', okButtonProps: { status: 'danger' }, onOk: () => { - const success = historyStore.removeFromHistory(item.id, item.api_info.api_url) - if (success) { - Message.success('已删除历史记录') - } + historyStore.removeFromHistory(item) + Message.success('已删除历史记录') } }) } const goToDetail = async (item) => { try { + // 调试:打印完整的历史记录项 + console.log('历史记录项完整数据:', item) + console.log('历史记录api_info:', item.api_info) + // 通过路由参数传递站源信息 const siteInfo = { name: item.api_info.site_name, api: item.api_info.api_url, - key: item.api_info.module + key: item.api_info.module, + ext: item.api_info.ext || null // 从历史数据中获取extend参数 } - console.log('从历史进入详情页,使用临时站源:', siteInfo.name) + console.log('从历史进入详情页,使用临时站源:', siteInfo.name, '扩展参数:', siteInfo.ext) + console.log('siteInfo完整对象:', siteInfo) // 跳转到详情页,传递站源信息和历史记录信息 router.push({ @@ -348,6 +364,7 @@ const goToDetail = async (item) => { tempSiteName: siteInfo.name, tempSiteApi: siteInfo.api, tempSiteKey: siteInfo.key, + tempSiteExt: siteInfo.ext, // 添加extend参数传递 // 传递历史记录信息,用于恢复播放状态 historyRoute: item.current_route_name, historyEpisode: item.current_episode_name, diff --git a/dashboard/src/views/VideoDetail.vue b/dashboard/src/views/VideoDetail.vue index 487345f..e02c611 100644 --- a/dashboard/src/views/VideoDetail.vue +++ b/dashboard/src/views/VideoDetail.vue @@ -77,6 +77,7 @@ @close="handlePlayerClose" @player-change="handlePlayerTypeChange" @next-episode="handleNextEpisode" + @episode-selected="handleEpisodeSelected" /> @@ -230,7 +231,29 @@ const currentSiteInfo = ref({ // 视频播放器相关 const showVideoPlayer = ref(false) -const playerType = ref('default') // 'default' 或 'artplayer' + +// 从localStorage读取用户的播放器偏好,默认为'default' +const getPlayerPreference = () => { + try { + const saved = localStorage.getItem('drplayer_preferred_player_type') + return saved && ['default', 'artplayer'].includes(saved) ? saved : 'default' + } catch (error) { + console.warn('读取播放器偏好失败:', error) + return 'default' + } +} + +// 保存播放器偏好到localStorage +const savePlayerPreference = (type) => { + try { + localStorage.setItem('drplayer_preferred_player_type', type) + console.log('播放器偏好已保存:', type) + } catch (error) { + console.warn('保存播放器偏好失败:', error) + } +} + +const playerType = ref(getPlayerPreference()) // 'default' 或 'artplayer' // 图片查看器相关 const viewerImages = ref([]) @@ -366,6 +389,10 @@ const loadVideoDetail = async () => { let module, apiUrl, siteName, extend if ((fromCollection || fromHistory || fromPush||fromSpecialAction) && route.query.tempSiteKey) { + // 调试:打印接收到的路由参数 + console.log('VideoDetail接收到的路由参数:', route.query) + console.log('tempSiteExt参数值:', route.query.tempSiteExt) + // 从收藏、历史或推送进入,使用临时站源信息,不影响全局状态 module = route.query.tempSiteKey apiUrl = route.query.tempSiteApi @@ -495,7 +522,8 @@ const toggleFavorite = async () => { // API信息使用当前站源信息 module: currentSiteInfo.value.key, api_url: currentSiteInfo.value.api, - site_name: currentSiteInfo.value.name + site_name: currentSiteInfo.value.name, + ext: currentSiteInfo.value.ext || null // 添加站源扩展配置 } const success = favoriteStore.addFavorite(favoriteData) @@ -692,7 +720,11 @@ const handlePlayerClose = () => { // 处理播放器类型变更 const handlePlayerTypeChange = (newType) => { + console.log('切换播放器类型:', newType) playerType.value = newType + + // 保存用户的播放器偏好 + savePlayerPreference(newType) } // 处理自动下一集事件 @@ -708,6 +740,23 @@ const handleNextEpisode = (nextEpisodeIndex) => { } } +// 处理选集选择事件 +const handleEpisodeSelected = (episode) => { + console.log('从播放器选择剧集:', episode) + + // 查找选集在当前路线中的索引 + const episodeIndex = currentRouteEpisodes.value.findIndex(ep => + ep.name === episode.name && ep.url === episode.url + ) + + if (episodeIndex !== -1) { + selectEpisode(episodeIndex) + } else { + console.warn('未找到选集:', episode) + Message.warning('选集切换失败') + } +} + const selectEpisode = (index) => { currentEpisode.value = index @@ -739,7 +788,8 @@ const selectEpisode = (index) => { api_info: { module: currentSiteInfo.value.key, api_url: currentSiteInfo.value.api, - site_name: currentSiteInfo.value.name + site_name: currentSiteInfo.value.name, + ext: currentSiteInfo.value.ext || null // 添加站源扩展配置 } } @@ -754,6 +804,12 @@ const selectEpisode = (index) => { url: currentRouteEpisodes.value[index].url } + // 调试:检查添加历史记录时的ext参数 + console.log('=== 添加历史记录调试 ===') + console.log('currentSiteInfo.value.ext:', currentSiteInfo.value.ext) + console.log('videoInfo.api_info.ext:', videoInfo.api_info.ext) + console.log('=== 调试结束 ===') + historyStore.addToHistory(videoInfo, routeInfo, episodeInfo) } } @@ -819,29 +875,86 @@ const playVideo = () => { } } +// 智能查找第一个m3u8选集 +const findFirstM3u8Episode = () => { + console.log('开始智能查找第一个m3u8选集...') + + // 遍历所有线路 + for (let routeIndex = 0; routeIndex < playRoutes.value.length; routeIndex++) { + const route = playRoutes.value[routeIndex] + console.log(`检查线路 ${routeIndex}: ${route.name}`) + + // 遍历当前线路的所有选集 + for (let episodeIndex = 0; episodeIndex < route.episodes.length; episodeIndex++) { + const episode = route.episodes[episodeIndex] + + // 检查URL是否包含m3u8 + if (episode.url && episode.url.toLowerCase().includes('.m3u8')) { + console.log(`找到第一个m3u8选集: 线路${routeIndex} - ${episode.name}`) + return { + routeIndex, + episodeIndex, + route: route.name, + episode: episode.name, + url: episode.url + } + } + } + } + + console.log('未找到m3u8选集,将使用默认选集') + return null +} + const playFirstEpisode = () => { - // 播放第一个线路的第一个选集(受排序影响) - if (playRoutes.value.length > 0) { - currentRoute.value = 0 + // 首先尝试智能查找第一个m3u8选集 + const m3u8Episode = findFirstM3u8Episode() + + if (m3u8Episode) { + // 找到m3u8选集,播放该选集 + console.log(`智能播放m3u8选集: ${m3u8Episode.route} - ${m3u8Episode.episode}`) + currentRoute.value = m3u8Episode.routeIndex nextTick(() => { - if (currentRouteEpisodes.value.length > 0) { - currentEpisode.value = 0 - - nextTick(() => { - if (currentEpisodeUrl.value) { - console.log('播放第一个选集:', currentRouteEpisodes.value[0].name) - Message.info(`开始播放: ${currentRouteEpisodes.value[0].name}`) - - // 启动内置播放器 - showVideoPlayer.value = true - - // 添加到历史记录 - updateHistoryRecord() - } - }) - } + currentEpisode.value = m3u8Episode.episodeIndex + + nextTick(() => { + if (currentEpisodeUrl.value) { + console.log('播放m3u8选集:', m3u8Episode.episode) + Message.info(`智能播放: ${m3u8Episode.episode}`) + + // 启动内置播放器 + showVideoPlayer.value = true + + // 添加到历史记录 + updateHistoryRecord() + } + }) }) + } else { + // 未找到m3u8选集,播放第一个线路的第一个选集(默认行为) + if (playRoutes.value.length > 0) { + currentRoute.value = 0 + + nextTick(() => { + if (currentRouteEpisodes.value.length > 0) { + currentEpisode.value = 0 + + nextTick(() => { + if (currentEpisodeUrl.value) { + console.log('播放默认选集:', currentRouteEpisodes.value[0].name) + Message.info(`开始播放: ${currentRouteEpisodes.value[0].name}`) + + // 启动内置播放器 + showVideoPlayer.value = true + + // 添加到历史记录 + updateHistoryRecord() + } + }) + } + }) + } } } @@ -860,7 +973,8 @@ const updateHistoryRecord = () => { api_info: { module: currentSiteInfo.value.key, api_url: currentSiteInfo.value.api, - site_name: currentSiteInfo.value.name + site_name: currentSiteInfo.value.name, + ext: currentSiteInfo.value.ext || null // 添加站源扩展配置 } } @@ -875,6 +989,12 @@ const updateHistoryRecord = () => { url: currentRouteEpisodes.value[currentEpisode.value].url } + // 调试:检查更新历史记录时的ext参数 + console.log('=== 更新历史记录调试 ===') + console.log('currentSiteInfo.value.ext:', currentSiteInfo.value.ext) + console.log('videoInfo.api_info.ext:', videoInfo.api_info.ext) + console.log('=== 调试结束 ===') + historyStore.addToHistory(videoInfo, routeInfo, episodeInfo) } } From 6582d134ac82a94ff6e4229b080befc1eaa8639e Mon Sep 17 00:00:00 2001 From: Taois Date: Wed, 1 Oct 2025 15:36:43 +0800 Subject: [PATCH 054/199] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E6=8D=A2?= =?UTF-8?q?=E6=BA=90=E5=88=87=E6=8D=A2=E7=95=8C=E9=9D=A2=E5=92=8C=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E3=80=81=E4=BF=AE=E5=A4=8DT4=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E7=9A=84extend=E4=BC=A0=E9=80=92=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard/src/api/modules/module.js | 74 ++- dashboard/src/components/SourceDialog.vue | 551 ++++++++++++++++++++-- dashboard/src/views/Video.vue | 44 +- 3 files changed, 587 insertions(+), 82 deletions(-) diff --git a/dashboard/src/api/modules/module.js b/dashboard/src/api/modules/module.js index 53cae53..e698f0d 100644 --- a/dashboard/src/api/modules/module.js +++ b/dashboard/src/api/modules/module.js @@ -7,6 +7,30 @@ import { get, post } from '../request' import { API_PATHS, MODULE_ACTIONS, PAGINATION } from '../config' import axios from 'axios' +/** + * 处理extend参数,确保对象类型转换为JSON字符串 + * @param {string|object} extend - 扩展参数 + * @returns {string|undefined} 处理后的extend参数 + */ +const processExtendParam = (extend) => { + if (!extend) { + return undefined + } + + // 如果extend是对象类型,转换为JSON字符串 + if (typeof extend === 'object' && extend !== null) { + try { + return JSON.stringify(extend) + } catch (error) { + console.warn('extend参数JSON序列化失败:', error) + return undefined + } + } + + // 如果已经是字符串,直接返回 + return extend +} + /** * 构建模块接口URL * @param {string} module - 模块名称 @@ -46,7 +70,7 @@ const directApiCall = async (apiUrl, params = {}) => { * @param {string} module - 模块名称 * @param {object} options - 选项参数 * @param {number} options.filter - 过滤条件(1表示启用,默认启用) - * @param {string} options.extend - 接口数据扩展参数 + * @param {string|object} options.extend - 接口数据扩展参数(对象类型会自动转换为JSON字符串) * @param {string} options.apiUrl - 站点API地址(可选,如果提供则直接使用) * @returns {Promise} 首页数据 */ @@ -54,8 +78,9 @@ export const getHomeData = async (module, options = {}) => { const { filter = 1, extend, apiUrl } = options const params = { filter } - if (extend) { - params.extend = extend + const processedExtend = processExtendParam(extend) + if (processedExtend) { + params.extend = processedExtend } // 如果提供了apiUrl,直接使用站点的API地址 @@ -75,7 +100,7 @@ export const getHomeData = async (module, options = {}) => { * @param {string} params.t - 分类ID * @param {number} params.pg - 页码(从1开始) * @param {string} params.ext - base64编码的筛选条件JSON字符串 - * @param {string} params.extend - 接口数据扩展参数 + * @param {string|object} params.extend - 接口数据扩展参数(对象类型会自动转换为JSON字符串) * @param {string} params.apiUrl - 可选的直接API地址 * @returns {Promise} 分类数据 */ @@ -98,8 +123,9 @@ export const getCategoryData = async (module, params) => { requestParams.ext = ext } - if (extend) { - requestParams.extend = extend + const processedExtend = processExtendParam(extend) + if (processedExtend) { + requestParams.extend = processedExtend } // 如果提供了apiUrl,直接使用站点的API地址 @@ -116,7 +142,7 @@ export const getCategoryData = async (module, params) => { * @param {string} module - 模块名称 * @param {object} params - 详情参数 * @param {string} params.ids - 视频ID - * @param {string} params.extend - 接口数据扩展参数 + * @param {string|object} params.extend - 接口数据扩展参数(对象类型会自动转换为JSON字符串) * @param {string} params.apiUrl - 可选的直接API地址 * @returns {Promise} 视频详情数据 */ @@ -128,8 +154,9 @@ export const getVideoDetail = async (module, params) => { ids } - if (extend) { - requestParams.extend = extend + const processedExtend = processExtendParam(extend) + if (processedExtend) { + requestParams.extend = processedExtend } // 如果提供了apiUrl,直接使用站点的API地址 @@ -146,7 +173,7 @@ export const getVideoDetail = async (module, params) => { * @param {string} module - 模块名称 * @param {object} params - 播放参数 * @param {string} params.play - 播放地址或ID - * @param {string} params.extend - 接口数据扩展参数 + * @param {string|object} params.extend - 接口数据扩展参数(对象类型会自动转换为JSON字符串) * @param {string} params.apiUrl - 可选的直接API地址 * @returns {Promise} 播放数据 */ @@ -158,8 +185,9 @@ export const getPlayData = async (module, params) => { play } - if (extend) { - requestParams.extend = extend + const processedExtend = processExtendParam(extend) + if (processedExtend) { + requestParams.extend = processedExtend } // 如果提供了apiUrl,直接使用站点的API地址 @@ -177,7 +205,7 @@ export const getPlayData = async (module, params) => { * @param {object} params - 搜索参数 * @param {string} params.wd - 搜索关键词 * @param {number} params.pg - 页码(从1开始) - * @param {string} params.extend - 接口数据扩展参数 + * @param {string|object} params.extend - 接口数据扩展参数(对象类型会自动转换为JSON字符串) * @param {string} params.apiUrl - 可选的直接API地址 * @returns {Promise} 搜索结果 */ @@ -194,8 +222,9 @@ export const searchVideos = async (module, params) => { pg } - if (extend) { - requestParams.extend = extend + const processedExtend = processExtendParam(extend) + if (processedExtend) { + requestParams.extend = processedExtend } // 如果提供了apiUrl,直接使用站点的API地址 @@ -212,7 +241,7 @@ export const searchVideos = async (module, params) => { * @param {string} module - 模块名称 * @param {object} data - 动作数据 * @param {string} data.action - 动作类型 - * @param {string} data.extend - 接口数据扩展参数 + * @param {string|object} data.extend - 接口数据扩展参数(对象类型会自动转换为JSON字符串) * @param {string} data.apiUrl - 可选的直接API地址 * @returns {Promise} 动作执行结果 */ @@ -225,8 +254,9 @@ export const executeAction = async (module, data) => { ...otherData } - if (extend) { - requestData.extend = extend + const processedExtend = processExtendParam(extend) + if (processedExtend) { + requestData.extend = processedExtend } console.log('executeAction调用参数:', { @@ -238,7 +268,6 @@ export const executeAction = async (module, data) => { // 如果提供了apiUrl,直接使用站点的API地址 if (apiUrl) { - const axios = (await import('axios')).default console.log('直接调用API:', apiUrl, requestData) // 如果是测试用的JSON文件,使用GET请求 @@ -275,7 +304,7 @@ export const executeAction = async (module, data) => { /** * 刷新模块数据 * @param {string} module - 模块名称 - * @param {string} extend - 接口数据扩展参数 + * @param {string|object} extend - 接口数据扩展参数(对象类型会自动转换为JSON字符串) * @param {string} apiUrl - 可选的直接API地址 * @returns {Promise} 刷新结果 */ @@ -284,8 +313,9 @@ export const refreshModule = async (module, extend, apiUrl) => { refresh: '1' } - if (extend) { - params.extend = extend + const processedExtend = processExtendParam(extend) + if (processedExtend) { + params.extend = processedExtend } // 如果提供了apiUrl,直接使用站点的API地址 diff --git a/dashboard/src/components/SourceDialog.vue b/dashboard/src/components/SourceDialog.vue index c98f18f..d86fd03 100644 --- a/dashboard/src/components/SourceDialog.vue +++ b/dashboard/src/components/SourceDialog.vue @@ -1,4 +1,33 @@ +.change_rule_dialog .arco-modal-footer { + border-top: 1px solid var(--color-border-2); + padding: 16px 20px; +} + diff --git a/dashboard/src/views/Video.vue b/dashboard/src/views/Video.vue index b86b1b4..66739ca 100644 --- a/dashboard/src/views/Video.vue +++ b/dashboard/src/views/Video.vue @@ -159,24 +159,48 @@ const getNowSite = () => { if (currentSite) { form.now_site = currentSite; form.now_site_title = currentSite.name; + // 同步到 siteStore + setCurrentSite(currentSite); } else if (nowSite && nowSite.name) { form.now_site = nowSite; form.now_site_title = nowSite.name; // 同步到siteService siteService.setCurrentSite(nowSite.key); + } else { + // 如果都没有,清空当前源 + form.now_site = {}; + form.now_site_title = "hipy影视"; } }; const checkNowSite = () => { - form.new_site = form.now_site; - if (!form.new_site.key && form.sites.length > 0) { - form.new_site = form.sites[0]; - } else if ( - form.new_site.key && - !form.sites.map((i) => i.key).includes(form.new_site.key) - ) { - form.new_site = form.sites[0]; + // 确保 form.now_site 有值,如果没有则从 siteService 获取 + if (!form.now_site || !form.now_site.key) { + const currentSite = siteService.getCurrentSite(); + if (currentSite) { + form.now_site = currentSite; + form.now_site_title = currentSite.name; + } else if (form.sites.length > 0) { + // 如果没有当前源,设置第一个可用源 + const firstSite = form.sites.find(site => site.type === 4) || form.sites[0]; + form.now_site = firstSite; + form.now_site_title = firstSite.name; + siteService.setCurrentSite(firstSite.key); + } + } else { + // 检查当前源是否在站点列表中 + const siteExists = form.sites.some(site => site.key === form.now_site.key); + if (!siteExists && form.sites.length > 0) { + // 如果当前源不在列表中,设置第一个可用源 + const firstSite = form.sites.find(site => site.type === 4) || form.sites[0]; + form.now_site = firstSite; + form.now_site_title = firstSite.name; + siteService.setCurrentSite(firstSite.key); + } } + + // 设置 new_site 用于换源对话框 + form.new_site = form.now_site; }; // 启动定时器 @@ -427,7 +451,9 @@ const handleRefreshList = () => { const handleOpenForm = () => { form.visible = true; - form.form_title = `请选择数据源(${form.sites.length})`; + // 过滤出 type 为 4 的数据源 + const type4Sites = form.sites.filter(site => site.type === 4); + form.form_title = `请选择数据源(${type4Sites.length})`; checkNowSite(); }; From 88c1f778fc9fd50aebb6a227f8e293e3699b85bc Mon Sep 17 00:00:00 2001 From: Taois Date: Wed, 1 Oct 2025 16:06:19 +0800 Subject: [PATCH 055/199] =?UTF-8?q?feat:=20=E7=89=87=E5=A4=B4=E7=89=87?= =?UTF-8?q?=E5=B0=BE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/players/ArtVideoPlayer.vue | 361 +++++++++++++++- .../src/components/players/VideoPlayer.vue | 391 +++++++++++++++++- 2 files changed, 750 insertions(+), 2 deletions(-) diff --git a/dashboard/src/components/players/ArtVideoPlayer.vue b/dashboard/src/components/players/ArtVideoPlayer.vue index 0befcbd..75eb704 100644 --- a/dashboard/src/components/players/ArtVideoPlayer.vue +++ b/dashboard/src/components/players/ArtVideoPlayer.vue @@ -38,6 +38,16 @@
+
+ + + + + + + 片头片尾 +
+
@@ -95,12 +105,65 @@
+ + +
+
+
+
+

片头片尾设置

+ +
+
+
+
+ + 跳过片头 +
+
+ + +
+
+
+
+ + 跳过片尾 +
+
+ + +
+
+
+

• 片头跳过:播放开始时自动跳过指定时长

+

• 片尾跳过:视频结束前指定时长时自动切换下一集(需开启自动连播)

+
+
+ +
+
@@ -695,6 +914,166 @@ onUnmounted(() => { background: #555; } +/* 片头片尾设置弹窗样式 */ +.skip-settings-dialog { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 2000; + display: flex; + align-items: center; + justify-content: center; +} + +.skip-settings-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.7); + backdrop-filter: blur(4px); +} + +.skip-settings-content { + position: relative; + background: white; + border-radius: 12px; + width: 400px; + max-width: 90vw; + box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3); + overflow: hidden; +} + +.skip-settings-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px 20px; + background: #f8f9fa; + border-bottom: 1px solid #e9ecef; +} + +.skip-settings-header h3 { + margin: 0; + font-size: 16px; + font-weight: 600; + color: #2c3e50; +} + +.skip-close-btn { + background: none; + border: none; + font-size: 20px; + color: #666; + cursor: pointer; + padding: 0; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; + transition: all 0.2s ease; +} + +.skip-close-btn:hover { + background: #e9ecef; + color: #333; +} + +.skip-settings-body { + padding: 20px; +} + +.skip-setting-row { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 16px; +} + +.skip-setting-label { + display: flex; + align-items: center; + gap: 8px; + font-size: 14px; + color: #333; +} + +.skip-setting-input { + display: flex; + align-items: center; + gap: 6px; +} + +.skip-setting-input .unit { + font-size: 12px; + color: #666; +} + +.skip-setting-tip { + margin-top: 16px; + padding: 12px; + background: #f8f9fa; + border-radius: 6px; + border-left: 3px solid #1890ff; +} + +.skip-setting-tip p { + margin: 0; + font-size: 12px; + color: #666; + line-height: 1.5; +} + +.skip-setting-tip p + p { + margin-top: 4px; +} + +.skip-settings-footer { + display: flex; + justify-content: flex-end; + gap: 8px; + padding: 16px 20px; + background: #f8f9fa; + border-top: 1px solid #e9ecef; +} + +.btn-save { + padding: 6px 16px; + background: #1890ff; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + transition: all 0.2s ease; +} + +.btn-save:hover { + background: #40a9ff; +} + +.skip-settings-footer .btn-cancel { + padding: 6px 16px; + background: #f5f5f5; + color: #666; + border: 1px solid #d9d9d9; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + transition: all 0.2s ease; +} + +.skip-settings-footer .btn-cancel:hover { + background: #e6f7ff; + border-color: #91d5ff; + color: #1890ff; +} + /* 响应式设计 */ @media (max-width: 768px) { .player-header { @@ -710,5 +1089,15 @@ onUnmounted(() => { .video-player { min-height: 200px; } + + .skip-settings-content { + width: 350px; + } + + .skip-setting-row { + flex-direction: column; + align-items: flex-start; + gap: 8px; + } } \ No newline at end of file From 8d24b7d5547d405856c587cbd9284a85bf8cc017 Mon Sep 17 00:00:00 2001 From: Taois Date: Wed, 1 Oct 2025 17:02:48 +0800 Subject: [PATCH 056/199] =?UTF-8?q?feat:=20=E6=92=AD=E6=94=BE=E5=99=A8?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E6=8B=86=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/players/ArtVideoPlayer.vue | 466 +++------------- .../src/components/players/PlayerHeader.vue | 290 ++++++++++ .../components/players/SkipSettingsDialog.vue | 369 +++++++++++++ .../src/components/players/VideoPlayer.vue | 513 +++--------------- dashboard/src/composables/useSkipSettings.js | 226 ++++++++ 5 files changed, 1040 insertions(+), 824 deletions(-) create mode 100644 dashboard/src/components/players/PlayerHeader.vue create mode 100644 dashboard/src/components/players/SkipSettingsDialog.vue create mode 100644 dashboard/src/composables/useSkipSettings.js diff --git a/dashboard/src/components/players/ArtVideoPlayer.vue b/dashboard/src/components/players/ArtVideoPlayer.vue index 75eb704..2c51b7b 100644 --- a/dashboard/src/components/players/ArtVideoPlayer.vue +++ b/dashboard/src/components/players/ArtVideoPlayer.vue @@ -1,63 +1,18 @@ @@ -168,6 +81,9 @@ import { Message } from '@arco-design/web-vue' import { IconClose } from '@arco-design/web-vue/es/icon' import Artplayer from 'artplayer' import Hls from 'hls.js' +import PlayerHeader from './PlayerHeader.vue' +import SkipSettingsDialog from './SkipSettingsDialog.vue' +import { useSkipSettings } from '@/composables/useSkipSettings' // Props - 已添加 HLS 支持、动态高度自适应和自动下一集功能 const props = defineProps({ @@ -229,17 +145,34 @@ const showEpisodeDialog = ref(false) // 显示选集弹窗 const episodeListRef = ref(null) // 选集列表容器引用 const currentEpisodeRef = ref(null) // 当前选集按钮引用 -// 片头片尾跳过功能相关数据 -const showSkipSettingsDialog = ref(false) // 显示片头片尾设置弹窗 -const skipIntroEnabled = ref(false) // 片头跳过开关 -const skipOutroEnabled = ref(false) // 片尾跳过开关 -const skipIntroSeconds = ref(90) // 片头跳过秒数 -const skipOutroSeconds = ref(90) // 片尾跳过秒数 -const skipIntroApplied = ref(false) // 片头跳过是否已应用 -const skipOutroTimer = ref(null) // 片尾跳过定时器 - -// 计算属性 -const skipEnabled = computed(() => skipIntroEnabled.value || skipOutroEnabled.value) +// 使用片头片尾设置组合式函数 +const { + showSkipSettingsDialog, + skipIntroEnabled, + skipOutroEnabled, + skipIntroSeconds, + skipOutroSeconds, + skipEnabled, + initSkipSettings, + resetSkipState, + applySkipSettings, + handleTimeUpdate, + closeSkipSettingsDialog, + saveSkipSettings: saveSkipSettingsComposable +} = useSkipSettings({ + onSkipToNext: () => { + if (autoNextEnabled.value && hasNextEpisode()) { + playNextEpisode() + } + }, + getCurrentTime: () => artPlayerInstance.value?.video?.currentTime || 0, + setCurrentTime: (time) => { + if (artPlayerInstance.value?.video) { + artPlayerInstance.value.video.currentTime = time + } + }, + getDuration: () => artPlayerInstance.value?.video?.duration || 0 +}) // 链接类型判断函数 const isDirectVideoLink = (url) => { @@ -295,15 +228,8 @@ const initArtPlayer = async (url) => { // 重置重连状态 resetRetryState() - // 重置片头跳过标记和片尾定时器 - skipIntroApplied.value = false - if (skipOutroTimer.value) { - clearInterval(skipOutroTimer.value) - skipOutroTimer.value = null - } - - // 加载片头片尾设置 - loadSkipSettings() + // 重置片头片尾状态 + resetSkipState() // 等待 DOM 更新后计算动态高度 await nextTick() @@ -496,6 +422,10 @@ const initArtPlayer = async (url) => { applySkipSettings() }) + art.on('video:timeupdate', () => { + handleTimeUpdate() + }) + art.on('video:playing', () => { console.log('视频开始播放') // 视频开始播放时,重置重连计数器 @@ -550,12 +480,6 @@ const closePlayer = () => { // 重置重连状态 resetRetryState() - // 清理片尾跳过定时器 - if (skipOutroTimer.value) { - clearInterval(skipOutroTimer.value) - skipOutroTimer.value = null - } - // 清理播放器实例 if (artPlayerInstance.value) { // 清理 HLS 实例 @@ -571,110 +495,23 @@ const closePlayer = () => { emit('close') } -// 片头片尾跳过功能相关方法 -// 关闭片头片尾设置弹窗 -const closeSkipSettingsDialog = () => { - showSkipSettingsDialog.value = false -} - -// 保存片头片尾设置 -const saveSkipSettings = () => { - const settings = { - skipIntroEnabled: skipIntroEnabled.value, - skipOutroEnabled: skipOutroEnabled.value, - skipIntroSeconds: skipIntroSeconds.value, - skipOutroSeconds: skipOutroSeconds.value - } - - localStorage.setItem('videoPlayerSkipSettings', JSON.stringify(settings)) - console.log('片头片尾设置已保存:', settings) - - closeSkipSettingsDialog() - - // 重新应用设置 - applySkipSettings() - - Message.success('设置已保存') -} -// 加载片头片尾设置 -const loadSkipSettings = () => { - try { - const savedSettings = localStorage.getItem('videoPlayerSkipSettings') - if (savedSettings) { - const settings = JSON.parse(savedSettings) - skipIntroEnabled.value = settings.skipIntroEnabled || false - skipOutroEnabled.value = settings.skipOutroEnabled || false - skipIntroSeconds.value = settings.skipIntroSeconds || 90 - skipOutroSeconds.value = settings.skipOutroSeconds || 90 - console.log('已加载片头片尾设置:', settings) - } - } catch (error) { - console.error('加载片头片尾设置失败:', error) - } -} -// 应用片头片尾设置 -const applySkipSettings = () => { - if (!artPlayerInstance.value) return - - // 应用片头跳过 - if (skipIntroEnabled.value && skipIntroSeconds.value > 0 && !skipIntroApplied.value) { - const currentTime = artPlayerInstance.value.currentTime - if (currentTime < skipIntroSeconds.value) { - artPlayerInstance.value.currentTime = skipIntroSeconds.value - skipIntroApplied.value = true - console.log(`已跳过片头 ${skipIntroSeconds.value} 秒`) - Message.info(`已跳过片头 ${skipIntroSeconds.value} 秒`) - } - } - - // 设置片尾跳过 - if (skipOutroEnabled.value && skipOutroSeconds.value > 0) { - setupOutroSkip() - } +// 处理播放器类型变更 +const handlePlayerTypeChange = (newType) => { + emit('player-change', newType) } -// 设置片尾跳过 -const setupOutroSkip = () => { - if (!artPlayerInstance.value || !autoNextEnabled.value) return - - // 清除之前的定时器 - if (skipOutroTimer.value) { - clearInterval(skipOutroTimer.value) - skipOutroTimer.value = null - } - - // 监听时间更新 - const handleTimeUpdate = () => { - if (!artPlayerInstance.value) return - - const currentTime = artPlayerInstance.value.currentTime - const duration = artPlayerInstance.value.duration - - if (duration > 0 && currentTime > 0) { - const remainingTime = duration - currentTime - - // 当剩余时间小于等于设置的片尾跳过时间时,自动切换下一集 - if (remainingTime <= skipOutroSeconds.value && hasNextEpisode()) { - console.log(`剩余时间 ${remainingTime.toFixed(1)} 秒,触发片尾跳过`) - - // 移除事件监听器,避免重复触发 - artPlayerInstance.value.off('video:timeupdate', handleTimeUpdate) - - // 直接播放下一集,不显示倒计时 - playNextEpisode() - } - } - } - - // 添加时间更新监听器 - artPlayerInstance.value.on('video:timeupdate', handleTimeUpdate) +// 打开片头片尾设置弹窗 +const openSkipSettingsDialog = () => { + showSkipSettingsDialog.value = true } -// 处理播放器类型变更 -const handlePlayerTypeChange = (newType) => { - emit('player-change', newType) +// 保存片头片尾设置 +const saveSkipSettings = (settings) => { + saveSkipSettingsComposable(settings) + Message.success('片头片尾设置已保存') + closeSkipSettingsDialog() } // 处理重连逻辑 @@ -950,6 +787,8 @@ onMounted(() => { console.log('ArtVideoPlayer 组件已挂载 - 动态高度版本') // 添加窗口大小变化监听 window.addEventListener('resize', handleResize) + // 初始化片头片尾设置 + initSkipSettings() }) // 组件卸载时清理资源 @@ -1374,166 +1213,5 @@ onUnmounted(() => { white-space: nowrap; } -/* 片头片尾设置弹窗样式 */ -.skip-settings-dialog { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 10000; - display: flex; - align-items: center; - justify-content: center; -} - -.skip-settings-overlay { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.7); - backdrop-filter: blur(4px); -} - -.skip-settings-content { - position: relative; - background: white; - border-radius: 12px; - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); - max-width: 500px; - width: 90%; - overflow: hidden; - animation: skipSettingsShow 0.3s ease-out; -} -@keyframes skipSettingsShow { - from { - opacity: 0; - transform: scale(0.9) translateY(-20px); - } - to { - opacity: 1; - transform: scale(1) translateY(0); - } -} - -.skip-settings-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 20px 24px; - border-bottom: 1px solid #e8e8e8; - background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); -} - -.skip-settings-header h3 { - margin: 0; - font-size: 18px; - font-weight: 600; - color: #2c3e50; -} - -.skip-settings-close-btn { - background: none; - border: none; - font-size: 24px; - cursor: pointer; - color: #666; - width: 32px; - height: 32px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 50%; - transition: all 0.2s ease; -} - -.skip-settings-close-btn:hover { - background: rgba(0, 0, 0, 0.1); - color: #333; -} - -.skip-settings-body { - padding: 24px; -} - -.skip-setting-row { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 20px; - padding: 16px; - background: #f8f9fa; - border-radius: 8px; - border: 1px solid #e9ecef; -} - -.skip-setting-label { - display: flex; - align-items: center; - gap: 12px; - font-size: 14px; - font-weight: 500; - color: #495057; -} - -.skip-setting-input { - display: flex; - align-items: center; - gap: 8px; -} - -.skip-setting-input .unit { - font-size: 14px; - color: #6c757d; -} - -.skip-setting-tip { - margin-top: 16px; - padding: 16px; - background: #e3f2fd; - border-radius: 8px; - border-left: 4px solid #2196f3; -} - -.skip-setting-tip p { - margin: 0 0 8px 0; - font-size: 13px; - color: #1565c0; - line-height: 1.5; -} - -.skip-setting-tip p:last-child { - margin-bottom: 0; -} - -.skip-settings-footer { - display: flex; - justify-content: flex-end; - gap: 12px; - padding: 16px 24px; - border-top: 1px solid #e8e8e8; - background: #f8f9fa; -} - -/* 响应式设计 */ -@media (max-width: 768px) { - .skip-settings-content { - max-width: 95%; - margin: 20px; - } - - .skip-setting-row { - flex-direction: column; - align-items: flex-start; - gap: 12px; - } - - .skip-setting-input { - width: 100%; - justify-content: flex-end; - } -} \ No newline at end of file diff --git a/dashboard/src/components/players/PlayerHeader.vue b/dashboard/src/components/players/PlayerHeader.vue new file mode 100644 index 0000000..62fe782 --- /dev/null +++ b/dashboard/src/components/players/PlayerHeader.vue @@ -0,0 +1,290 @@ + + + + + \ No newline at end of file diff --git a/dashboard/src/components/players/SkipSettingsDialog.vue b/dashboard/src/components/players/SkipSettingsDialog.vue new file mode 100644 index 0000000..6e1091f --- /dev/null +++ b/dashboard/src/components/players/SkipSettingsDialog.vue @@ -0,0 +1,369 @@ + + + + + \ No newline at end of file diff --git a/dashboard/src/components/players/VideoPlayer.vue b/dashboard/src/components/players/VideoPlayer.vue index 0fc0287..4ea5f66 100644 --- a/dashboard/src/components/players/VideoPlayer.vue +++ b/dashboard/src/components/players/VideoPlayer.vue @@ -1,62 +1,19 @@ @@ -151,6 +64,9 @@ import { ref, watch, onMounted, onUnmounted, nextTick, computed } from 'vue' import { Message } from '@arco-design/web-vue' import { IconClose } from '@arco-design/web-vue/es/icon' import Hls from 'hls.js' +import PlayerHeader from './PlayerHeader.vue' +import SkipSettingsDialog from './SkipSettingsDialog.vue' +import { useSkipSettings } from '@/composables/useSkipSettings' // Props const props = defineProps({ @@ -198,30 +114,6 @@ const autoNextCountdown = ref(10) const countdownTimer = ref(null) const isProcessingAutoNext = ref(false) // 防止重复触发自动连播 -// 片头片尾跳过功能相关数据 -const showSkipSettingsDialog = ref(false) -const skipIntroEnabled = ref(false) -const skipOutroEnabled = ref(false) -const skipIntroSeconds = ref(30) -const skipOutroSeconds = ref(30) -const skipIntroApplied = ref(false) // 标记片头跳过是否已应用 -const skipOutroTimer = ref(null) // 片尾跳过定时器 - -// 计算属性:是否启用了任一跳过功能 -const skipEnabled = computed(() => { - return skipIntroEnabled.value || skipOutroEnabled.value -}) - -// 切换自动连播 -const toggleAutoNext = () => { - autoNext.value = !autoNext.value -} - -// 切换倒计时显示 -const toggleCountdown = () => { - showCountdown.value = !showCountdown.value -} - // 检查是否有下一集 const hasNextEpisode = () => { return props.episodes && props.episodes.length > 0 && @@ -236,6 +128,15 @@ const getNextEpisode = () => { return null } +// 隐藏自动下一集对话框 +const hideAutoNextDialog = () => { + showAutoNextDialog.value = false + if (countdownTimer.value) { + clearInterval(countdownTimer.value) + countdownTimer.value = null + } +} + // 播放下一集 const playNextEpisode = () => { if (hasNextEpisode()) { @@ -249,6 +150,42 @@ const playNextEpisode = () => { } } +// 使用片头片尾跳过功能组合式函数 +const { + showSkipSettingsDialog, + skipIntroEnabled, + skipOutroEnabled, + skipIntroSeconds, + skipOutroSeconds, + skipEnabled, + initSkipSettings, + applySkipSettings, + handleTimeUpdate, + resetSkipState, + openSkipSettingsDialog, + closeSkipSettingsDialog, + saveSkipSettings: saveSkipSettingsComposable +} = useSkipSettings({ + onSkipToNext: playNextEpisode, + getCurrentTime: () => videoPlayer.value?.currentTime || 0, + setCurrentTime: (time) => { + if (videoPlayer.value) { + videoPlayer.value.currentTime = time + } + }, + getDuration: () => videoPlayer.value?.duration || 0 +}) + +// 切换自动连播 +const toggleAutoNext = () => { + autoNext.value = !autoNext.value +} + +// 切换倒计时显示 +const toggleCountdown = () => { + showCountdown.value = !showCountdown.value +} + // 显示自动下一集对话框 const showAutoNextDialogFunc = () => { if (!autoNext.value || !hasNextEpisode()) return @@ -264,15 +201,6 @@ const showAutoNextDialogFunc = () => { }, 1000) } -// 隐藏自动下一集对话框 -const hideAutoNextDialog = () => { - showAutoNextDialog.value = false - if (countdownTimer.value) { - clearInterval(countdownTimer.value) - countdownTimer.value = null - } -} - // 取消自动下一集 const cancelAutoNext = () => { hideAutoNextDialog() @@ -331,15 +259,8 @@ const initVideoPlayer = (url) => { console.log('初始化视频播放器:', url) - // 重置片头跳过标记和片尾定时器 - skipIntroApplied.value = false - if (skipOutroTimer.value) { - clearInterval(skipOutroTimer.value) - skipOutroTimer.value = null - } - - // 加载片头片尾设置 - loadSkipSettings() + // 重置片头片尾跳过状态 + resetSkipState() // 首先判断链接类型 if (!isDirectVideoLink(url)) { @@ -489,12 +410,14 @@ const initVideoPlayer = (url) => { video.removeEventListener('error', handleError) video.removeEventListener('loadstart', handleLoadStart) video.removeEventListener('timeupdate', handleTimeUpdate) + video.removeEventListener('ended', handleVideoEnded) // 添加新的事件监听器 video.addEventListener('loadedmetadata', handleLoadedMetadata) video.addEventListener('error', handleError) video.addEventListener('loadstart', handleLoadStart) video.addEventListener('timeupdate', handleTimeUpdate) + video.addEventListener('ended', handleVideoEnded) // 开始加载视频 video.load() @@ -508,115 +431,14 @@ const initVideoPlayer = (url) => { // 片头片尾跳过功能相关方法 // 关闭片头片尾设置弹窗 -const closeSkipSettingsDialog = () => { - showSkipSettingsDialog.value = false -} - // 保存片头片尾设置 -const saveSkipSettings = () => { - const settings = { - skipIntroEnabled: skipIntroEnabled.value, - skipOutroEnabled: skipOutroEnabled.value, - skipIntroSeconds: skipIntroSeconds.value, - skipOutroSeconds: skipOutroSeconds.value - } - - localStorage.setItem('videoSkipSettings', JSON.stringify(settings)) +const saveSkipSettings = (settings) => { + saveSkipSettingsComposable(settings) Message.success('片头片尾设置已保存') closeSkipSettingsDialog() - - // 应用新设置 - applySkipSettings() } -// 加载片头片尾设置 -const loadSkipSettings = () => { - try { - const saved = localStorage.getItem('videoSkipSettings') - if (saved) { - const settings = JSON.parse(saved) - skipIntroEnabled.value = settings.skipIntroEnabled || false - skipOutroEnabled.value = settings.skipOutroEnabled || false - skipIntroSeconds.value = settings.skipIntroSeconds || 30 - skipOutroSeconds.value = settings.skipOutroSeconds || 30 - } - } catch (error) { - console.error('加载片头片尾设置失败:', error) - } -} -// 应用片头片尾设置 -const applySkipSettings = () => { - if (!videoPlayer.value) return - - const video = videoPlayer.value - - // 应用片头跳过 - if (skipIntroEnabled.value && skipIntroSeconds.value > 0 && !skipIntroApplied.value) { - if (video.currentTime < skipIntroSeconds.value) { - video.currentTime = skipIntroSeconds.value - skipIntroApplied.value = true - console.log(`跳过片头 ${skipIntroSeconds.value} 秒`) - } - } - - // 设置片尾跳过 - if (skipOutroEnabled.value && skipOutroSeconds.value > 0) { - setupOutroSkip() - } -} - -// 设置片尾跳过 -const setupOutroSkip = () => { - if (!videoPlayer.value || skipOutroTimer.value) return - - const video = videoPlayer.value - - const checkOutroSkip = () => { - if (!video.duration || !skipOutroEnabled.value) return - - const remainingTime = video.duration - video.currentTime - - // 如果剩余时间小于等于设定的片尾跳过时间,且开启了自动连播 - if (remainingTime <= skipOutroSeconds.value && autoNext.value && hasNextEpisode()) { - console.log(`剩余时间 ${remainingTime.toFixed(1)} 秒,跳过片尾`) - - // 清理定时器 - if (skipOutroTimer.value) { - clearInterval(skipOutroTimer.value) - skipOutroTimer.value = null - } - - // 触发自动下一集 - if (showCountdown.value) { - showAutoNextDialogFunc() - } else { - setTimeout(() => { - playNextEpisode() - }, 500) - } - } - } - - // 每秒检查一次 - skipOutroTimer.value = setInterval(checkOutroSkip, 1000) -} - -// 处理视频时间更新事件 -const handleTimeUpdate = () => { - if (!videoPlayer.value) return - - const video = videoPlayer.value - - // 检查片头跳过 - if (skipIntroEnabled.value && skipIntroSeconds.value > 0 && !skipIntroApplied.value) { - if (video.currentTime < skipIntroSeconds.value && video.currentTime > 0) { - video.currentTime = skipIntroSeconds.value - skipIntroApplied.value = true - console.log(`跳过片头 ${skipIntroSeconds.value} 秒`) - } - } -} // 关闭播放器 const closePlayer = () => { @@ -672,6 +494,11 @@ watch(() => props.visible, (newVisible) => { } }) +// 组件挂载时初始化 +onMounted(() => { + initSkipSettings() +}) + // 组件卸载时清理资源 onUnmounted(() => { console.log('VideoPlayer组件卸载,清理播放器资源') @@ -693,12 +520,6 @@ onUnmounted(() => { clearInterval(countdownTimer.value) countdownTimer.value = null } - - // 清理片尾跳过定时器 - if (skipOutroTimer.value) { - clearInterval(skipOutroTimer.value) - skipOutroTimer.value = null - } }) @@ -914,165 +735,7 @@ onUnmounted(() => { background: #555; } -/* 片头片尾设置弹窗样式 */ -.skip-settings-dialog { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: 2000; - display: flex; - align-items: center; - justify-content: center; -} - -.skip-settings-overlay { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.7); - backdrop-filter: blur(4px); -} - -.skip-settings-content { - position: relative; - background: white; - border-radius: 12px; - width: 400px; - max-width: 90vw; - box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3); - overflow: hidden; -} - -.skip-settings-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 16px 20px; - background: #f8f9fa; - border-bottom: 1px solid #e9ecef; -} - -.skip-settings-header h3 { - margin: 0; - font-size: 16px; - font-weight: 600; - color: #2c3e50; -} - -.skip-close-btn { - background: none; - border: none; - font-size: 20px; - color: #666; - cursor: pointer; - padding: 0; - width: 24px; - height: 24px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 4px; - transition: all 0.2s ease; -} -.skip-close-btn:hover { - background: #e9ecef; - color: #333; -} - -.skip-settings-body { - padding: 20px; -} - -.skip-setting-row { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 16px; -} - -.skip-setting-label { - display: flex; - align-items: center; - gap: 8px; - font-size: 14px; - color: #333; -} - -.skip-setting-input { - display: flex; - align-items: center; - gap: 6px; -} - -.skip-setting-input .unit { - font-size: 12px; - color: #666; -} - -.skip-setting-tip { - margin-top: 16px; - padding: 12px; - background: #f8f9fa; - border-radius: 6px; - border-left: 3px solid #1890ff; -} - -.skip-setting-tip p { - margin: 0; - font-size: 12px; - color: #666; - line-height: 1.5; -} - -.skip-setting-tip p + p { - margin-top: 4px; -} - -.skip-settings-footer { - display: flex; - justify-content: flex-end; - gap: 8px; - padding: 16px 20px; - background: #f8f9fa; - border-top: 1px solid #e9ecef; -} - -.btn-save { - padding: 6px 16px; - background: #1890ff; - color: white; - border: none; - border-radius: 4px; - cursor: pointer; - font-size: 14px; - transition: all 0.2s ease; -} - -.btn-save:hover { - background: #40a9ff; -} - -.skip-settings-footer .btn-cancel { - padding: 6px 16px; - background: #f5f5f5; - color: #666; - border: 1px solid #d9d9d9; - border-radius: 4px; - cursor: pointer; - font-size: 14px; - transition: all 0.2s ease; -} - -.skip-settings-footer .btn-cancel:hover { - background: #e6f7ff; - border-color: #91d5ff; - color: #1890ff; -} /* 响应式设计 */ @media (max-width: 768px) { @@ -1089,15 +752,5 @@ onUnmounted(() => { .video-player { min-height: 200px; } - - .skip-settings-content { - width: 350px; - } - - .skip-setting-row { - flex-direction: column; - align-items: flex-start; - gap: 8px; - } } \ No newline at end of file diff --git a/dashboard/src/composables/useSkipSettings.js b/dashboard/src/composables/useSkipSettings.js new file mode 100644 index 0000000..8a902bf --- /dev/null +++ b/dashboard/src/composables/useSkipSettings.js @@ -0,0 +1,226 @@ +import { ref, computed, onUnmounted } from 'vue' + +/** + * 片头片尾跳过功能的组合式函数 + * @param {Object} options - 配置选项 + * @param {Function} options.onSkipToNext - 跳转到下一集的回调函数 + * @param {Function} options.getCurrentTime - 获取当前播放时间的函数 + * @param {Function} options.setCurrentTime - 设置播放时间的函数 + * @param {Function} options.getDuration - 获取视频总时长的函数 + * @returns {Object} 返回片头片尾跳过相关的响应式数据和方法 + */ +export function useSkipSettings(options = {}) { + const { + onSkipToNext = () => {}, + getCurrentTime = () => 0, + setCurrentTime = () => {}, + getDuration = () => 0 + } = options + + // 响应式数据 + const showSkipSettingsDialog = ref(false) + const skipIntroEnabled = ref(false) + const skipOutroEnabled = ref(false) + const skipIntroSeconds = ref(90) + const skipOutroSeconds = ref(90) + const skipIntroApplied = ref(false) + const skipOutroTimer = ref(null) + + // 计算属性 + const skipEnabled = computed(() => { + return skipIntroEnabled.value || skipOutroEnabled.value + }) + + // 本地存储键名 + const STORAGE_KEY = 'drplayer_skip_settings' + + /** + * 从本地存储加载设置 + */ + const loadSkipSettings = () => { + try { + const saved = localStorage.getItem(STORAGE_KEY) + if (saved) { + const settings = JSON.parse(saved) + skipIntroEnabled.value = settings.skipIntroEnabled || false + skipOutroEnabled.value = settings.skipOutroEnabled || false + skipIntroSeconds.value = settings.skipIntroSeconds || 90 + skipOutroSeconds.value = settings.skipOutroSeconds || 90 + } + } catch (error) { + console.warn('加载片头片尾设置失败:', error) + } + } + + /** + * 保存设置到本地存储 + */ + const saveSkipSettings = (settings) => { + try { + // 更新响应式数据 + skipIntroEnabled.value = settings.skipIntroEnabled + skipOutroEnabled.value = settings.skipOutroEnabled + skipIntroSeconds.value = settings.skipIntroSeconds + skipOutroSeconds.value = settings.skipOutroSeconds + + // 保存到本地存储 + const settingsToSave = { + skipIntroEnabled: settings.skipIntroEnabled, + skipOutroEnabled: settings.skipOutroEnabled, + skipIntroSeconds: settings.skipIntroSeconds, + skipOutroSeconds: settings.skipOutroSeconds + } + localStorage.setItem(STORAGE_KEY, JSON.stringify(settingsToSave)) + + // 应用新设置 + applySkipSettings() + } catch (error) { + console.warn('保存片头片尾设置失败:', error) + } + } + + /** + * 应用片头跳过设置 + */ + const applyIntroSkip = () => { + if (!skipIntroEnabled.value || skipIntroApplied.value) return + + const currentTime = getCurrentTime() + if (currentTime < skipIntroSeconds.value) { + setCurrentTime(skipIntroSeconds.value) + skipIntroApplied.value = true + console.log(`已跳过片头 ${skipIntroSeconds.value} 秒`) + } + } + + /** + * 设置片尾跳过逻辑 + */ + const setupOutroSkip = () => { + if (!skipOutroEnabled.value) return + + const duration = getDuration() + if (duration <= 0) return + + const currentTime = getCurrentTime() + const timeToSkip = duration - skipOutroSeconds.value + + if (currentTime >= timeToSkip && !skipOutroTimer.value) { + skipOutroTimer.value = setTimeout(() => { + console.log(`已跳过片尾 ${skipOutroSeconds.value} 秒,跳转到下一集`) + onSkipToNext() + }, 1000) // 延迟1秒执行,避免频繁触发 + } + } + + /** + * 应用片头片尾设置 + */ + const applySkipSettings = () => { + // 应用片头跳过 + applyIntroSkip() + + // 设置片尾跳过 + setupOutroSkip() + } + + /** + * 处理时间更新事件 + */ + const handleTimeUpdate = () => { + // 应用片头跳过(仅在未应用时) + if (skipIntroEnabled.value && !skipIntroApplied.value) { + applyIntroSkip() + } + + // 处理片尾跳过 + if (skipOutroEnabled.value) { + const duration = getDuration() + const currentTime = getCurrentTime() + + if (duration > 0) { + const timeToSkip = duration - skipOutroSeconds.value + + if (currentTime >= timeToSkip && !skipOutroTimer.value) { + skipOutroTimer.value = setTimeout(() => { + console.log(`已跳过片尾 ${skipOutroSeconds.value} 秒,跳转到下一集`) + onSkipToNext() + }, 1000) + } + } + } + } + + /** + * 重置跳过状态 + */ + const resetSkipState = () => { + skipIntroApplied.value = false + if (skipOutroTimer.value) { + clearTimeout(skipOutroTimer.value) + skipOutroTimer.value = null + } + } + + /** + * 初始化片头片尾设置 + */ + const initSkipSettings = () => { + resetSkipState() + loadSkipSettings() + } + + /** + * 打开设置弹窗 + */ + const openSkipSettingsDialog = () => { + showSkipSettingsDialog.value = true + } + + /** + * 关闭设置弹窗 + */ + const closeSkipSettingsDialog = () => { + showSkipSettingsDialog.value = false + } + + /** + * 清理资源 + */ + const cleanup = () => { + if (skipOutroTimer.value) { + clearTimeout(skipOutroTimer.value) + skipOutroTimer.value = null + } + } + + // 组件卸载时清理资源 + onUnmounted(() => { + cleanup() + }) + + return { + // 响应式数据 + showSkipSettingsDialog, + skipIntroEnabled, + skipOutroEnabled, + skipIntroSeconds, + skipOutroSeconds, + skipIntroApplied, + skipOutroTimer, + + // 计算属性 + skipEnabled, + + // 方法 + loadSkipSettings, + saveSkipSettings, + applySkipSettings, + handleTimeUpdate, + resetSkipState, + initSkipSettings, + openSkipSettingsDialog, + closeSkipSettingsDialog, + cleanup + } +} \ No newline at end of file From a462d446c17f747fe8f92cb138f87a0421c39b13 Mon Sep 17 00:00:00 2001 From: Taois Date: Wed, 1 Oct 2025 18:43:46 +0800 Subject: [PATCH 057/199] =?UTF-8?q?feat:=20=E4=BF=AE=E5=A4=8D=E8=B7=B3?= =?UTF-8?q?=E8=BF=87=E7=89=87=E5=A4=B4=E7=89=87=E5=B0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/players/ArtVideoPlayer.vue | 229 ++++++++++++++++-- .../src/components/players/VideoPlayer.vue | 13 + dashboard/src/composables/useSkipSettings.js | 181 +++++++++++--- 3 files changed, 374 insertions(+), 49 deletions(-) diff --git a/dashboard/src/components/players/ArtVideoPlayer.vue b/dashboard/src/components/players/ArtVideoPlayer.vue index 2c51b7b..f07b15b 100644 --- a/dashboard/src/components/players/ArtVideoPlayer.vue +++ b/dashboard/src/components/players/ArtVideoPlayer.vue @@ -156,9 +156,12 @@ const { initSkipSettings, resetSkipState, applySkipSettings, + applyIntroSkipImmediate, handleTimeUpdate, closeSkipSettingsDialog, - saveSkipSettings: saveSkipSettingsComposable + saveSkipSettings: saveSkipSettingsComposable, + onUserSeekStart, + onUserSeekEnd } = useSkipSettings({ onSkipToNext: () => { if (autoNextEnabled.value && hasNextEpisode()) { @@ -247,16 +250,38 @@ const initArtPlayer = async (url) => { return } - // 清理之前的播放器实例 + // 如果播放器实例已存在,使用 switchUrl 方法切换视频源 if (artPlayerInstance.value) { - // 清理 HLS 实例 - if (artPlayerInstance.value.hls) { - artPlayerInstance.value.hls.destroy() - artPlayerInstance.value.hls = null - } + console.log('使用 switchUrl 方法切换视频源:', url) - artPlayerInstance.value.destroy() - artPlayerInstance.value = null + try { + // 使用 switchUrl 方法切换视频源,这样可以保持全屏状态和其他用户设置 + await artPlayerInstance.value.switchUrl(url) + console.log('视频源切换成功') + + // 重新应用片头片尾设置 + applySkipSettings() + + return // 切换成功,直接返回 + } catch (error) { + console.error('switchUrl 切换失败,回退到销毁重建方式:', error) + // 如果 switchUrl 失败,回退到原来的销毁重建方式 + + // 清理缓冲区清理定时器 + if (artPlayerInstance.value.bufferCleanupInterval) { + clearInterval(artPlayerInstance.value.bufferCleanupInterval) + artPlayerInstance.value.bufferCleanupInterval = null + } + + // 清理 HLS 实例 + if (artPlayerInstance.value.hls) { + artPlayerInstance.value.hls.destroy() + artPlayerInstance.value.hls = null + } + + artPlayerInstance.value.destroy() + artPlayerInstance.value = null + } } try { @@ -307,8 +332,42 @@ const initArtPlayer = async (url) => { const hls = new Hls({ // HLS 配置选项 enableWorker: true, - lowLatencyMode: true, - backBufferLength: 90, + lowLatencyMode: false, // 关闭低延迟模式,提高稳定性 + + // 缓冲区配置 - 关键优化 + backBufferLength: 15, // 减少后缓冲长度,避免内存占用过多 + maxBufferLength: 30, // 减少最大缓冲长度到30秒,避免内存问题 + maxBufferSize: 30 * 1000 * 1000, // 减少最大缓冲大小到30MB + maxBufferHole: 0.3, // 减少最大缓冲空洞到0.3秒 + + // 网络配置 + maxLoadingDelay: 3, // 减少最大加载延迟到3秒 + maxRetryDelay: 6, // 减少最大重试延迟到6秒 + maxRetry: 2, // 减少最大重试次数到2次,避免过度重试 + + // 片段配置 + fragLoadingTimeOut: 15000, // 减少片段加载超时到15秒 + manifestLoadingTimeOut: 8000, // 减少清单加载超时到8秒 + fragLoadingMaxRetry: 2, // 片段加载最大重试次数 + manifestLoadingMaxRetry: 2, // 清单加载最大重试次数 + + // 启用自动质量切换 + enableSoftwareAES: true, + startLevel: -1, // 自动选择起始质量 + capLevelToPlayerSize: true, // 根据播放器大小限制质量 + + // 错误恢复配置 + liveSyncDurationCount: 3, + liveMaxLatencyDurationCount: Infinity, + liveDurationInfinity: false, + + // 新增配置项,提高稳定性 + nudgeOffset: 0.1, // 微调偏移量 + nudgeMaxRetry: 3, // 微调最大重试次数 + maxSeekHole: 2, // 最大寻址空洞 + + // 调试配置(生产环境可关闭) + debug: false, }) hls.loadSource(url) @@ -322,25 +381,118 @@ const initArtPlayer = async (url) => { console.log('HLS manifest 解析完成') }) + // 错误重试计数器 + let networkErrorRetries = 0 + let mediaErrorRetries = 0 + const maxErrorRetries = 2 // 减少重试次数 + hls.on(Hls.Events.ERROR, (event, data) => { - console.error('HLS 错误:', data) + // 只记录致命错误,减少控制台噪音 if (data.fatal) { + console.error('HLS 致命错误:', data.type, data.details) + switch (data.type) { case Hls.ErrorTypes.NETWORK_ERROR: - console.log('网络错误,尝试恢复...') - hls.startLoad() + networkErrorRetries++ + + if (networkErrorRetries <= maxErrorRetries) { + console.log(`网络错误恢复中... (${networkErrorRetries}/${maxErrorRetries})`) + // 延迟重试,避免频繁请求 + setTimeout(() => { + hls.startLoad() + }, 1000 * networkErrorRetries) // 递增延迟 + } else { + console.error('网络错误重试次数超限') + Message.error('网络连接不稳定,请检查网络后重试') + hls.destroy() + } break + case Hls.ErrorTypes.MEDIA_ERROR: - console.log('媒体错误,尝试恢复...') - hls.recoverMediaError() + mediaErrorRetries++ + + if (mediaErrorRetries <= maxErrorRetries) { + console.log(`媒体错误恢复中... (${mediaErrorRetries}/${maxErrorRetries})`) + setTimeout(() => { + hls.recoverMediaError() + }, 500 * mediaErrorRetries) // 递增延迟 + } else { + console.error('媒体错误恢复次数超限') + Message.error('视频解码错误,请尝试刷新页面') + hls.destroy() + } break + default: - console.log('无法恢复的错误,销毁 HLS 实例') + // 对于其他致命错误,不显示用户提示,只记录日志 + console.error('无法恢复的HLS错误:', data.details) hls.destroy() break } + } else { + // 非致命错误,只在调试模式下记录 + if (data.details !== 'bufferAppendError' && data.details !== 'bufferStalledError') { + console.debug('HLS 非致命错误:', data.details) + } + + // 对于缓冲区错误,尝试自动恢复 + if (data.details === 'bufferStalledError') { + console.debug('检测到缓冲停滞,自动处理中...') + // HLS.js 会自动处理这类错误,无需手动干预 + } } }) + + // 监听缓冲区事件,用于性能优化 + hls.on(Hls.Events.BUFFER_APPENDED, () => { + // 缓冲区数据追加成功,可以在这里做一些清理工作 + }) + + hls.on(Hls.Events.BUFFER_EOS, () => { + console.debug('缓冲区到达流结束') + }) + + // 监听缓冲区清理事件 + hls.on(Hls.Events.BUFFER_FLUSHED, () => { + console.debug('缓冲区已清理') + }) + + // 重置错误计数器(当播放成功时) + hls.on(Hls.Events.FRAG_LOADED, () => { + // 片段加载成功,重置错误计数 + if (networkErrorRetries > 0 || mediaErrorRetries > 0) { + console.log('连接恢复正常,重置错误计数器') + networkErrorRetries = 0 + mediaErrorRetries = 0 + } + }) + + // 监听质量切换事件 + hls.on(Hls.Events.LEVEL_SWITCHED, (event, data) => { + console.debug(`质量切换到: ${data.level}`) + }) + + // 定期清理缓冲区,避免内存占用过多 + let bufferCleanupInterval = setInterval(() => { + if (hls && video && !video.paused) { + const currentTime = video.currentTime + // 清理当前播放位置前15秒以外的缓冲区 + if (currentTime > 15) { + try { + hls.trigger(Hls.Events.BUFFER_FLUSHING, { + startOffset: 0, + endOffset: currentTime - 15, + type: 'video' + }) + } catch (e) { + console.debug('缓冲区清理失败:', e) + } + } + } + }, 30000) // 每30秒清理一次 + + // 存储清理定时器,用于后续清理 + art.bufferCleanupInterval = bufferCleanupInterval } else if (video.canPlayType('application/vnd.apple.mpegurl')) { // Safari 原生支持 HLS video.src = url @@ -412,6 +564,8 @@ const initArtPlayer = async (url) => { art.on('video:loadstart', () => { console.log('开始加载视频') + // 重置片头片尾跳过状态 + resetSkipState() }) art.on('video:canplay', () => { @@ -426,10 +580,37 @@ const initArtPlayer = async (url) => { handleTimeUpdate() }) + // 监听用户拖动进度条事件 + art.on('video:seeking', () => { + onUserSeekStart() + }) + + art.on('video:seeked', () => { + onUserSeekEnd() + }) + art.on('video:playing', () => { console.log('视频开始播放') // 视频开始播放时,重置重连计数器 resetRetryState() + + // 立即尝试片头跳过(针对视频刚开始播放的情况) + const immediateSkipped = applyIntroSkipImmediate() + + // 如果立即跳过未执行,则使用常规跳过逻辑 + if (!immediateSkipped) { + applySkipSettings() + + // 为了确保片头跳过生效,再次检查(短延迟) + setTimeout(() => { + applySkipSettings() + }, 50) // 减少延迟到50ms + } + }) + + // 监听全屏状态变化 + art.on('fullscreen', (isFullscreen) => { + console.log('全屏状态变化:', isFullscreen) }) art.on('video:error', (err) => { @@ -750,6 +931,7 @@ const selectEpisode = (episode) => { watch(() => props.videoUrl, async (newUrl) => { if (newUrl && props.visible) { resetRetryState() // 重置重连状态 + resetSkipState() // 重置片头片尾跳过状态 await nextTick() await initArtPlayer(newUrl) } @@ -803,6 +985,19 @@ onUnmounted(() => { // 销毁播放器实例 if (artPlayerInstance.value) { + // 清理缓冲区清理定时器 + if (artPlayerInstance.value.bufferCleanupInterval) { + clearInterval(artPlayerInstance.value.bufferCleanupInterval) + artPlayerInstance.value.bufferCleanupInterval = null + } + + // 清理 HLS 实例 + if (artPlayerInstance.value.hls) { + artPlayerInstance.value.hls.destroy() + artPlayerInstance.value.hls = null + } + + // 销毁播放器实例 artPlayerInstance.value.destroy() artPlayerInstance.value = null } diff --git a/dashboard/src/components/players/VideoPlayer.vue b/dashboard/src/components/players/VideoPlayer.vue index 4ea5f66..f02c23c 100644 --- a/dashboard/src/components/players/VideoPlayer.vue +++ b/dashboard/src/components/players/VideoPlayer.vue @@ -403,12 +403,23 @@ const initVideoPlayer = (url) => { const handleLoadStart = () => { console.log('开始加载视频') + // 重置片头片尾跳过状态 + resetSkipState() + } + + const handlePlaying = () => { + console.log('视频开始播放') + // 延迟一点应用跳过设置,确保视频已经开始播放 + setTimeout(() => { + applySkipSettings() + }, 100) } // 移除之前的事件监听器(如果有) video.removeEventListener('loadedmetadata', handleLoadedMetadata) video.removeEventListener('error', handleError) video.removeEventListener('loadstart', handleLoadStart) + video.removeEventListener('playing', handlePlaying) video.removeEventListener('timeupdate', handleTimeUpdate) video.removeEventListener('ended', handleVideoEnded) @@ -416,6 +427,7 @@ const initVideoPlayer = (url) => { video.addEventListener('loadedmetadata', handleLoadedMetadata) video.addEventListener('error', handleError) video.addEventListener('loadstart', handleLoadStart) + video.addEventListener('playing', handlePlaying) video.addEventListener('timeupdate', handleTimeUpdate) video.addEventListener('ended', handleVideoEnded) @@ -473,6 +485,7 @@ const handlePlayerTypeChange = (newType) => { // 监听视频URL变化 watch(() => props.videoUrl, (newUrl) => { if (newUrl && props.visible) { + resetSkipState() // 重置片头片尾跳过状态 nextTick(() => { initVideoPlayer(newUrl) }) diff --git a/dashboard/src/composables/useSkipSettings.js b/dashboard/src/composables/useSkipSettings.js index 8a902bf..7f74a24 100644 --- a/dashboard/src/composables/useSkipSettings.js +++ b/dashboard/src/composables/useSkipSettings.js @@ -3,7 +3,7 @@ import { ref, computed, onUnmounted } from 'vue' /** * 片头片尾跳过功能的组合式函数 * @param {Object} options - 配置选项 - * @param {Function} options.onSkipToNext - 跳转到下一集的回调函数 + * @param {Function} options.onSkipToNext - 跳转到下一集的回调函数(仅用于片尾跳过到下一集的场景) * @param {Function} options.getCurrentTime - 获取当前播放时间的函数 * @param {Function} options.setCurrentTime - 设置播放时间的函数 * @param {Function} options.getDuration - 获取视频总时长的函数 @@ -24,7 +24,13 @@ export function useSkipSettings(options = {}) { const skipIntroSeconds = ref(90) const skipOutroSeconds = ref(90) const skipIntroApplied = ref(false) + const skipOutroApplied = ref(false) const skipOutroTimer = ref(null) + const lastSkipTime = ref(0) // 记录上次跳过的时间戳,用于防抖 + + // 用户交互状态 + const userSeeking = ref(false) // 用户是否正在拖动进度条 + const lastUserSeekTime = ref(0) // 上次用户拖动的时间戳 // 计算属性 const skipEnabled = computed(() => { @@ -40,12 +46,21 @@ export function useSkipSettings(options = {}) { const loadSkipSettings = () => { try { const saved = localStorage.getItem(STORAGE_KEY) + console.log('从 localStorage 加载设置:', saved) if (saved) { const settings = JSON.parse(saved) skipIntroEnabled.value = settings.skipIntroEnabled || false skipOutroEnabled.value = settings.skipOutroEnabled || false skipIntroSeconds.value = settings.skipIntroSeconds || 90 skipOutroSeconds.value = settings.skipOutroSeconds || 90 + console.log('加载的设置:', { + skipIntroEnabled: skipIntroEnabled.value, + skipOutroEnabled: skipOutroEnabled.value, + skipIntroSeconds: skipIntroSeconds.value, + skipOutroSeconds: skipOutroSeconds.value + }) + } else { + console.log('没有找到保存的设置,使用默认值') } } catch (error) { console.warn('加载片头片尾设置失败:', error) @@ -80,36 +95,117 @@ export function useSkipSettings(options = {}) { } /** - * 应用片头跳过设置 + * 立即应用片头跳过逻辑(用于视频刚开始播放时) + */ + const applyIntroSkipImmediate = () => { + console.log('applyIntroSkipImmediate 被调用') + + if (!skipIntroEnabled.value) { + console.log('片头跳过未启用') + return + } + + if (skipIntroApplied.value) { + console.log('片头跳过已应用,跳过') + return + } + + const currentTime = getCurrentTime() + const now = Date.now() + + // 检查用户是否正在拖动或刚刚拖动过(3秒内) + if (userSeeking.value || (lastUserSeekTime.value > 0 && now - lastUserSeekTime.value < 3000)) { + console.log('用户正在拖动进度条或刚刚拖动过,跳过自动片头跳过') + return + } + + // 立即跳过模式:如果当前时间很小(小于等于1秒)且在片头跳过范围内,立即跳过 + if (currentTime <= 1 && currentTime <= skipIntroSeconds.value) { + console.log(`立即跳过片头:从 ${currentTime} 秒跳转到 ${skipIntroSeconds.value} 秒`) + setCurrentTime(skipIntroSeconds.value) + skipIntroApplied.value = true + lastSkipTime.value = now + console.log(`已立即跳过片头 ${skipIntroSeconds.value} 秒`) + return true // 返回 true 表示已执行跳过 + } + + return false // 返回 false 表示未执行跳过 + } + + /** + * 应用片头跳过逻辑 */ const applyIntroSkip = () => { - if (!skipIntroEnabled.value || skipIntroApplied.value) return + console.log('applyIntroSkip 被调用', { + skipIntroEnabled: skipIntroEnabled.value, + skipIntroApplied: skipIntroApplied.value, + currentTime: getCurrentTime(), + skipIntroSeconds: skipIntroSeconds.value, + userSeeking: userSeeking.value + }) + + if (!skipIntroEnabled.value) { + console.log('片头跳过未启用') + return + } + + if (skipIntroApplied.value) { + console.log('片头跳过已应用,跳过') + return + } const currentTime = getCurrentTime() - if (currentTime < skipIntroSeconds.value) { + const now = Date.now() + + // 检查用户是否正在拖动或刚刚拖动过(3秒内) + if (userSeeking.value || (lastUserSeekTime.value > 0 && now - lastUserSeekTime.value < 3000)) { + console.log('用户正在拖动进度条或刚刚拖动过,跳过自动片头跳过') + return + } + + // 防抖:如果距离上次跳过不足1秒,则忽略(但如果是新视频,lastSkipTime为0,允许跳过) + // 减少防抖时间,提高响应速度 + if (lastSkipTime.value > 0 && now - lastSkipTime.value < 1000) { + console.log('防抖限制,跳过') + return + } + + // 如果当前时间在片头跳过范围内,则跳过 + if (currentTime <= skipIntroSeconds.value) { + console.log(`准备跳过片头:从 ${currentTime} 秒跳转到 ${skipIntroSeconds.value} 秒`) setCurrentTime(skipIntroSeconds.value) skipIntroApplied.value = true + lastSkipTime.value = now console.log(`已跳过片头 ${skipIntroSeconds.value} 秒`) + } else { + console.log(`当前时间 ${currentTime} 秒已超过片头跳过范围 ${skipIntroSeconds.value} 秒`) } } /** - * 设置片尾跳过逻辑 + * 应用片尾跳过逻辑 */ - const setupOutroSkip = () => { - if (!skipOutroEnabled.value) return + const applyOutroSkip = () => { + if (!skipOutroEnabled.value || skipOutroApplied.value) return const duration = getDuration() if (duration <= 0) return const currentTime = getCurrentTime() const timeToSkip = duration - skipOutroSeconds.value + const now = Date.now() + + // 防抖:如果距离上次跳过不足2秒,则忽略 + if (now - lastSkipTime.value < 2000) { + return + } - if (currentTime >= timeToSkip && !skipOutroTimer.value) { - skipOutroTimer.value = setTimeout(() => { - console.log(`已跳过片尾 ${skipOutroSeconds.value} 秒,跳转到下一集`) - onSkipToNext() - }, 1000) // 延迟1秒执行,避免频繁触发 + // 当播放时间达到片尾跳过点时,直接跳转到视频结束位置 + if (currentTime >= timeToSkip && currentTime < duration - 1) { + setCurrentTime(duration - 0.1) // 跳转到接近结束的位置,触发 ended 事件 + skipOutroApplied.value = true + lastSkipTime.value = now + console.log(`已跳过片尾 ${skipOutroSeconds.value} 秒,跳转到视频结束位置`) } } @@ -117,11 +213,16 @@ export function useSkipSettings(options = {}) { * 应用片头片尾设置 */ const applySkipSettings = () => { - // 应用片头跳过 - applyIntroSkip() + // 优先尝试立即片头跳过(用于视频刚开始播放时) + const immediateSkipped = applyIntroSkipImmediate() - // 设置片尾跳过 - setupOutroSkip() + // 如果立即跳过未执行,则使用常规片头跳过 + if (!immediateSkipped) { + applyIntroSkip() + } + + // 应用片尾跳过 + applyOutroSkip() } /** @@ -133,21 +234,9 @@ export function useSkipSettings(options = {}) { applyIntroSkip() } - // 处理片尾跳过 - if (skipOutroEnabled.value) { - const duration = getDuration() - const currentTime = getCurrentTime() - - if (duration > 0) { - const timeToSkip = duration - skipOutroSeconds.value - - if (currentTime >= timeToSkip && !skipOutroTimer.value) { - skipOutroTimer.value = setTimeout(() => { - console.log(`已跳过片尾 ${skipOutroSeconds.value} 秒,跳转到下一集`) - onSkipToNext() - }, 1000) - } - } + // 应用片尾跳过(仅在未应用时) + if (skipOutroEnabled.value && !skipOutroApplied.value) { + applyOutroSkip() } } @@ -156,12 +245,37 @@ export function useSkipSettings(options = {}) { */ const resetSkipState = () => { skipIntroApplied.value = false + skipOutroApplied.value = false + lastSkipTime.value = 0 // 重置防抖时间戳 + + // 重置用户交互状态 + userSeeking.value = false + lastUserSeekTime.value = 0 + + // 清除片尾跳过定时器(如果存在) if (skipOutroTimer.value) { clearTimeout(skipOutroTimer.value) skipOutroTimer.value = null } } + /** + * 标记用户开始拖动进度条 + */ + const onUserSeekStart = () => { + userSeeking.value = true + console.log('用户开始拖动进度条') + } + + /** + * 标记用户结束拖动进度条 + */ + const onUserSeekEnd = () => { + userSeeking.value = false + lastUserSeekTime.value = Date.now() + console.log('用户结束拖动进度条') + } + /** * 初始化片头片尾设置 */ @@ -216,11 +330,14 @@ export function useSkipSettings(options = {}) { loadSkipSettings, saveSkipSettings, applySkipSettings, + applyIntroSkipImmediate, handleTimeUpdate, resetSkipState, initSkipSettings, openSkipSettingsDialog, closeSkipSettingsDialog, - cleanup + cleanup, + onUserSeekStart, + onUserSeekEnd } } \ No newline at end of file From a8004e72c463ffa8a5ae149610205b77e616ad40 Mon Sep 17 00:00:00 2001 From: Taois Date: Wed, 1 Oct 2025 18:59:11 +0800 Subject: [PATCH 058/199] =?UTF-8?q?feat:=20=E5=80=8D=E9=80=9F=E5=AE=9A?= =?UTF-8?q?=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/players/ArtVideoPlayer.vue | 15 +++- .../src/components/players/VideoPlayer.vue | 72 +++++++++++++++++++ dashboard/src/composables/useSkipSettings.js | 57 +++++++++++++-- 3 files changed, 137 insertions(+), 7 deletions(-) diff --git a/dashboard/src/components/players/ArtVideoPlayer.vue b/dashboard/src/components/players/ArtVideoPlayer.vue index f07b15b..7971f2a 100644 --- a/dashboard/src/components/players/ArtVideoPlayer.vue +++ b/dashboard/src/components/players/ArtVideoPlayer.vue @@ -81,6 +81,9 @@ import { Message } from '@arco-design/web-vue' import { IconClose } from '@arco-design/web-vue/es/icon' import Artplayer from 'artplayer' import Hls from 'hls.js' + +// 配置自定义倍速选项 +Artplayer.PLAYBACK_RATE = [0.5, 0.75, 1, 1.25, 1.5, 2, 2.5, 3, 4, 5] import PlayerHeader from './PlayerHeader.vue' import SkipSettingsDialog from './SkipSettingsDialog.vue' import { useSkipSettings } from '@/composables/useSkipSettings' @@ -161,7 +164,9 @@ const { closeSkipSettingsDialog, saveSkipSettings: saveSkipSettingsComposable, onUserSeekStart, - onUserSeekEnd + onUserSeekEnd, + onFullscreenChangeStart, + onFullscreenChangeEnd } = useSkipSettings({ onSkipToNext: () => { if (autoNextEnabled.value && hasNextEpisode()) { @@ -611,6 +616,14 @@ const initArtPlayer = async (url) => { // 监听全屏状态变化 art.on('fullscreen', (isFullscreen) => { console.log('全屏状态变化:', isFullscreen) + + // 标记全屏状态开始变化 + onFullscreenChangeStart() + + // 500ms后标记全屏状态变化结束 + setTimeout(() => { + onFullscreenChangeEnd() + }, 500) }) art.on('video:error', (err) => { diff --git a/dashboard/src/components/players/VideoPlayer.vue b/dashboard/src/components/players/VideoPlayer.vue index f02c23c..8e2df1e 100644 --- a/dashboard/src/components/players/VideoPlayer.vue +++ b/dashboard/src/components/players/VideoPlayer.vue @@ -26,6 +26,28 @@ 您的浏览器不支持视频播放 + +
+ + +
+
@@ -113,6 +135,7 @@ const showAutoNextDialog = ref(false) const autoNextCountdown = ref(10) const countdownTimer = ref(null) const isProcessingAutoNext = ref(false) // 防止重复触发自动连播 +const currentSpeed = ref(1) // 当前播放倍速 // 检查是否有下一集 const hasNextEpisode = () => { @@ -208,6 +231,14 @@ const cancelAutoNext = () => { isProcessingAutoNext.value = false } +// 改变播放倍速 +const changePlaybackRate = () => { + if (videoPlayer.value) { + videoPlayer.value.playbackRate = parseFloat(currentSpeed.value) + console.log('播放倍速已设置为:', currentSpeed.value) + } +} + // 链接类型判断函数 const isDirectVideoLink = (url) => { if (!url) return false @@ -748,6 +779,47 @@ onUnmounted(() => { background: #555; } +/* 倍速控制器样式 */ +.speed-control { + position: absolute; + top: 10px; + right: 10px; + background: rgba(0, 0, 0, 0.7); + padding: 8px 12px; + border-radius: 6px; + display: flex; + align-items: center; + gap: 8px; + z-index: 10; +} + +.speed-control label { + color: white; + font-size: 14px; + font-weight: 500; +} + +.speed-selector { + background: rgba(255, 255, 255, 0.9); + border: 1px solid #ddd; + border-radius: 4px; + padding: 4px 8px; + font-size: 14px; + cursor: pointer; + outline: none; + transition: all 0.2s ease; +} + +.speed-selector:hover { + background: white; + border-color: #23ade5; +} + +.speed-selector:focus { + border-color: #23ade5; + box-shadow: 0 0 0 2px rgba(35, 173, 229, 0.2); +} + /* 响应式设计 */ diff --git a/dashboard/src/composables/useSkipSettings.js b/dashboard/src/composables/useSkipSettings.js index 7f74a24..f639570 100644 --- a/dashboard/src/composables/useSkipSettings.js +++ b/dashboard/src/composables/useSkipSettings.js @@ -28,9 +28,13 @@ export function useSkipSettings(options = {}) { const skipOutroTimer = ref(null) const lastSkipTime = ref(0) // 记录上次跳过的时间戳,用于防抖 - // 用户交互状态 - const userSeeking = ref(false) // 用户是否正在拖动进度条 - const lastUserSeekTime = ref(0) // 上次用户拖动的时间戳 + //// 用户交互状态 + const userSeeking = ref(false) + const lastUserSeekTime = ref(0) + + // 全屏状态跟踪 + const isFullscreenChanging = ref(false) + const lastFullscreenChangeTime = ref(0) // 上次用户拖动的时间戳 // 计算属性 const skipEnabled = computed(() => { @@ -119,6 +123,18 @@ export function useSkipSettings(options = {}) { return } + // 检查是否正在全屏切换或刚刚切换过(2秒内) + if (isFullscreenChanging.value || (lastFullscreenChangeTime.value > 0 && now - lastFullscreenChangeTime.value < 2000)) { + console.log('正在全屏切换或刚刚切换过,跳过自动片头跳过') + return + } + + // 检查是否正在全屏切换或刚刚切换过(2秒内) + if (isFullscreenChanging.value || (lastFullscreenChangeTime.value > 0 && now - lastFullscreenChangeTime.value < 2000)) { + console.log('正在全屏切换或刚刚切换过,跳过自动片头跳过') + return + } + // 立即跳过模式:如果当前时间很小(小于等于1秒)且在片头跳过范围内,立即跳过 if (currentTime <= 1 && currentTime <= skipIntroSeconds.value) { console.log(`立即跳过片头:从 ${currentTime} 秒跳转到 ${skipIntroSeconds.value} 秒`) @@ -163,6 +179,12 @@ export function useSkipSettings(options = {}) { return } + // 检查是否正在全屏切换或刚刚切换过(2秒内) + if (isFullscreenChanging.value || (lastFullscreenChangeTime.value > 0 && now - lastFullscreenChangeTime.value < 2000)) { + console.log('正在全屏切换或刚刚切换过,跳过自动片头跳过') + return + } + // 防抖:如果距离上次跳过不足1秒,则忽略(但如果是新视频,lastSkipTime为0,允许跳过) // 减少防抖时间,提高响应速度 if (lastSkipTime.value > 0 && now - lastSkipTime.value < 1000) { @@ -252,6 +274,10 @@ export function useSkipSettings(options = {}) { userSeeking.value = false lastUserSeekTime.value = 0 + // 重置全屏状态 + isFullscreenChanging.value = false + lastFullscreenChangeTime.value = 0 + // 清除片尾跳过定时器(如果存在) if (skipOutroTimer.value) { clearTimeout(skipOutroTimer.value) @@ -260,7 +286,7 @@ export function useSkipSettings(options = {}) { } /** - * 标记用户开始拖动进度条 + * 用户开始拖动进度条 */ const onUserSeekStart = () => { userSeeking.value = true @@ -268,7 +294,7 @@ export function useSkipSettings(options = {}) { } /** - * 标记用户结束拖动进度条 + * 用户结束拖动进度条 */ const onUserSeekEnd = () => { userSeeking.value = false @@ -276,6 +302,23 @@ export function useSkipSettings(options = {}) { console.log('用户结束拖动进度条') } + /** + * 全屏状态开始变化 + */ + const onFullscreenChangeStart = () => { + isFullscreenChanging.value = true + console.log('全屏状态开始变化') + } + + /** + * 全屏状态变化结束 + */ + const onFullscreenChangeEnd = () => { + isFullscreenChanging.value = false + lastFullscreenChangeTime.value = Date.now() + console.log('全屏状态变化结束') + } + /** * 初始化片头片尾设置 */ @@ -338,6 +381,8 @@ export function useSkipSettings(options = {}) { closeSkipSettingsDialog, cleanup, onUserSeekStart, - onUserSeekEnd + onUserSeekEnd, + onFullscreenChangeStart, + onFullscreenChangeEnd } } \ No newline at end of file From 1733bbd69397840c4524b4ac3ab2ec75f5baad50 Mon Sep 17 00:00:00 2001 From: Taois Date: Wed, 1 Oct 2025 19:27:01 +0800 Subject: [PATCH 059/199] =?UTF-8?q?feat:=20=E6=92=AD=E6=94=BE=E5=99=A8?= =?UTF-8?q?=E7=95=8C=E9=9D=A2=E6=94=AF=E6=8C=81=E9=80=89=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/players/ArtVideoPlayer.vue | 519 ++++++++++++++---- 1 file changed, 403 insertions(+), 116 deletions(-) diff --git a/dashboard/src/components/players/ArtVideoPlayer.vue b/dashboard/src/components/players/ArtVideoPlayer.vue index 7971f2a..06b2319 100644 --- a/dashboard/src/components/players/ArtVideoPlayer.vue +++ b/dashboard/src/components/players/ArtVideoPlayer.vue @@ -38,28 +38,7 @@
- -
-
-
-
-

选择集数

- -
-
- -
-
-
+ { tooltip: props.episodes.length > 1 ? '选择集数' : '', style: props.episodes.length > 1 ? {} : { display: 'none' }, click: function () { - toggleEpisodeDialog() + toggleEpisodeLayer() }, }, { @@ -555,7 +531,31 @@ const initArtPlayer = async (url) => { }, ], // 图层配置 - layers: [], + layers: [ + { + name: 'episodeLayer', + html: '', + style: { + position: 'absolute', + top: '0', + left: '0', + width: '100%', + height: '100%', + background: 'rgba(0, 0, 0, 0.8)', + display: 'none', + zIndex: '100', + padding: '0', + boxSizing: 'border-box', + overflow: 'hidden' + }, + click: function(event) { + // 点击背景关闭layer + if (event.target.classList.contains('episode-layer-background')) { + hideEpisodeLayer() + } + } + } + ], // 插件配置 plugins: [], }) @@ -910,36 +910,166 @@ const scrollToCurrentEpisode = async () => { } } -// 切换选集弹窗显示状态 -const toggleEpisodeDialog = async () => { - showEpisodeDialog.value = !showEpisodeDialog.value - console.log('选集弹窗:', showEpisodeDialog.value ? '显示' : '隐藏') +// 创建选集layer的HTML内容 +const createEpisodeLayerHTML = () => { + if (!props.episodes || props.episodes.length === 0) { + return '
' + } + + const episodeItems = props.episodes.map((episode, index) => { + const isCurrentEpisode = index === props.currentEpisodeIndex + return ` + + ` + }).join('') + + return ` +
+
+
+

选择集数

+ +
+
+ ${episodeItems} +
+
+
+ ` +} + +// 显示选集layer +const showEpisodeLayer = () => { + if (!artPlayerInstance.value) return - // 如果弹窗打开,等待弹窗动画完成后再滚动 - if (showEpisodeDialog.value) { - // 延迟350ms,等待弹窗动画完成(CSS动画时长为300ms) - setTimeout(async () => { - await scrollToCurrentEpisode() - }, 350) + try { + // 更新layer内容和样式 + artPlayerInstance.value.layers.update({ + name: 'episodeLayer', + html: createEpisodeLayerHTML(), + style: { + position: 'absolute', + top: '0', + left: '0', + width: '100%', + height: '100%', + background: 'rgba(0, 0, 0, 0.8)', + display: 'flex', + zIndex: '100', + padding: '0', + boxSizing: 'border-box', + overflow: 'hidden', + alignItems: 'center', + justifyContent: 'center' + } + }) + + // 添加事件监听器 + nextTick(() => { + const episodeLayer = artPlayerInstance.value.layers.episodeLayer + if (episodeLayer) { + // 使用事件委托处理点击事件 + episodeLayer.addEventListener('click', handleEpisodeLayerClick) + } + }) + + console.log('显示选集layer') + } catch (error) { + console.error('显示选集layer失败:', error) } } -// 关闭选集弹窗 -const closeEpisodeDialog = () => { - showEpisodeDialog.value = false +// 处理选集layer的点击事件 +const handleEpisodeLayerClick = (event) => { + const target = event.target.closest('.episode-layer-item') + const closeBtn = event.target.closest('.episode-layer-close') + const background = event.target.closest('.episode-layer-background') + + if (closeBtn || (background && event.target === background)) { + // 点击关闭按钮或背景,隐藏layer + hideEpisodeLayer() + } else if (target) { + // 点击选集项 + const episodeIndex = parseInt(target.dataset.episodeIndex) + if (!isNaN(episodeIndex)) { + selectEpisodeFromLayer(episodeIndex) + hideEpisodeLayer() + } + } } -// 选择剧集 -const selectEpisode = (episode) => { - console.log('选择剧集:', episode) +// 隐藏选集layer +const hideEpisodeLayer = () => { + if (!artPlayerInstance.value) return - // 关闭弹窗 - closeEpisodeDialog() + try { + // 移除事件监听器 + const episodeLayer = artPlayerInstance.value.layers.episodeLayer + if (episodeLayer) { + episodeLayer.removeEventListener('click', handleEpisodeLayerClick) + } + + // 隐藏layer + artPlayerInstance.value.layers.update({ + name: 'episodeLayer', + html: '', + style: { + position: 'absolute', + top: '0', + left: '0', + width: '100%', + height: '100%', + background: 'rgba(0, 0, 0, 0.8)', + display: 'none', + zIndex: '100', + padding: '0', + boxSizing: 'border-box', + overflow: 'hidden' + } + }) + console.log('隐藏选集layer') + } catch (error) { + console.error('隐藏选集layer失败:', error) + } +} + +// 切换选集layer显示状态 +const toggleEpisodeLayer = () => { + if (!artPlayerInstance.value) return + + try { + const episodeLayer = artPlayerInstance.value.layers.episodeLayer + if (episodeLayer && episodeLayer.style.display !== 'none') { + hideEpisodeLayer() + } else { + showEpisodeLayer() + } + } catch (error) { + console.error('切换选集layer失败:', error) + // 如果出错,尝试直接显示 + showEpisodeLayer() + } +} + +// 从layer中选择剧集 +const selectEpisodeFromLayer = (episodeIndex) => { + console.log('从layer选择剧集:', episodeIndex) // 发送选集事件给父组件 - emit('episode-selected', episode) + const episode = props.episodes[episodeIndex] + if (episode) { + emit('episode-selected', episode) + } } +// 原有的选集弹窗函数已移除,现在使用ArtPlayer的layer功能 + // 监听视频URL变化 watch(() => props.videoUrl, async (newUrl) => { if (newUrl && props.visible) { @@ -1283,142 +1413,299 @@ onUnmounted(() => { background: #555; } -/* 选集弹窗样式 */ -.episode-dialog { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 10000; - display: flex; +/* 原有的选集弹窗样式已移除,现在使用ArtPlayer的layer功能 */ + +/* 选集Layer样式 - 现代化设计 */ +:deep(.art-layer[data-name="episodeLayer"]) { + display: flex !important; align-items: center; justify-content: center; + background: rgba(0, 0, 0, 0.85) !important; + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); } -.episode-dialog-overlay { +:deep(.episode-layer-background) { position: absolute; top: 0; left: 0; width: 100%; height: 100%; - background: rgba(0, 0, 0, 0.7); - backdrop-filter: blur(4px); + display: flex; + align-items: center; + justify-content: center; + padding: 24px; + box-sizing: border-box; } -.episode-dialog-content { - position: relative; - background: white; - border-radius: 12px; - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); - max-width: 600px; - max-height: 80vh; - width: 90%; +:deep(.episode-layer-content) { + background: rgba(20, 20, 20, 0.95); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 16px; + box-shadow: + 0 32px 64px rgba(0, 0, 0, 0.4), + 0 0 0 1px rgba(255, 255, 255, 0.05); + max-width: 900px; + max-height: 60vh; + width: 95%; overflow: hidden; - animation: episodeDialogShow 0.3s ease-out; + animation: episodeLayerShow 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); } -@keyframes episodeDialogShow { +@keyframes episodeLayerShow { from { opacity: 0; - transform: scale(0.9) translateY(-20px); + transform: scale(0.8) translateY(-40px); + filter: blur(4px); } to { opacity: 1; transform: scale(1) translateY(0); + filter: blur(0); } } -.episode-dialog-header { +:deep(.episode-layer-header) { display: flex; justify-content: space-between; align-items: center; - padding: 20px 24px; - border-bottom: 1px solid #e8e8e8; - background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); + padding: 16px 20px 12px; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); + background: rgba(255, 255, 255, 0.02); } -.episode-dialog-header h3 { +:deep(.episode-layer-header h3) { margin: 0; - font-size: 18px; - font-weight: 600; - color: #2c3e50; + font-size: 20px; + font-weight: 700; + color: #ffffff; + letter-spacing: -0.02em; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); } -.episode-close-btn { - background: none; - border: none; - font-size: 24px; +:deep(.episode-layer-close) { + background: rgba(255, 255, 255, 0.08); + border: 1px solid rgba(255, 255, 255, 0.12); + font-size: 18px; cursor: pointer; - color: #666; - width: 32px; - height: 32px; + color: rgba(255, 255, 255, 0.8); + width: 36px; + height: 36px; display: flex; align-items: center; justify-content: center; border-radius: 50%; - transition: all 0.2s ease; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + font-weight: 300; } -.episode-close-btn:hover { - background: rgba(0, 0, 0, 0.1); - color: #333; +:deep(.episode-layer-close:hover) { + background: rgba(255, 255, 255, 0.15); + border-color: rgba(255, 255, 255, 0.2); + color: #ffffff; + transform: scale(1.05); } -.episode-list { - padding: 16px; - max-height: 60vh; +:deep(.episode-layer-close:active) { + transform: scale(0.95); +} + +:deep(.episode-layer-list) { + padding: 16px 20px 20px; + max-height: 45vh; overflow-y: auto; display: grid; - grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + grid-template-columns: repeat(3, 1fr); gap: 12px; } -.episode-item { +/* 自定义滚动条 */ +:deep(.episode-layer-list::-webkit-scrollbar) { + width: 6px; +} + +:deep(.episode-layer-list::-webkit-scrollbar-track) { + background: rgba(255, 255, 255, 0.05); + border-radius: 3px; +} + +:deep(.episode-layer-list::-webkit-scrollbar-thumb) { + background: rgba(255, 255, 255, 0.2); + border-radius: 3px; + transition: background 0.3s ease; +} + +:deep(.episode-layer-list::-webkit-scrollbar-thumb:hover) { + background: rgba(255, 255, 255, 0.3); +} + +:deep(.episode-layer-item) { display: flex; align-items: center; padding: 12px 16px; - border: 2px solid #e8e8e8; - border-radius: 8px; - background: white; + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 10px; + background: rgba(255, 255, 255, 0.04); cursor: pointer; - transition: all 0.2s ease; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); text-align: left; - min-height: 60px; + min-height: 56px; + position: relative; + overflow: hidden; } -.episode-item:hover { - border-color: #23ade5; - background: #f8fcff; - transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(35, 173, 229, 0.2); +:deep(.episode-layer-item::before) { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.05) 100%); + opacity: 0; + transition: opacity 0.3s ease; + pointer-events: none; } -.episode-item.current { - border-color: #23ade5; - background: linear-gradient(135deg, #23ade5 0%, #1e90ff 100%); - color: white; - box-shadow: 0 4px 12px rgba(35, 173, 229, 0.3); +:deep(.episode-layer-item:hover) { + border-color: rgba(64, 150, 255, 0.4); + background: rgba(64, 150, 255, 0.08); + transform: translateY(-2px) scale(1.02); + box-shadow: + 0 8px 32px rgba(64, 150, 255, 0.15), + 0 0 0 1px rgba(64, 150, 255, 0.2); +} + +:deep(.episode-layer-item:hover::before) { + opacity: 1; +} + +:deep(.episode-layer-item.current) { + border-color: rgba(64, 150, 255, 0.6); + background: linear-gradient(135deg, rgba(64, 150, 255, 0.2) 0%, rgba(100, 180, 255, 0.15) 100%); + color: #ffffff; + box-shadow: + 0 8px 32px rgba(64, 150, 255, 0.25), + 0 0 0 1px rgba(64, 150, 255, 0.4), + inset 0 1px 0 rgba(255, 255, 255, 0.1); + transform: scale(1.02); } -.episode-item.current:hover { - background: linear-gradient(135deg, #1e90ff 0%, #23ade5 100%); +:deep(.episode-layer-item.current::before) { + opacity: 1; + background: linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.08) 100%); } -.episode-number { +:deep(.episode-layer-item.current:hover) { + background: linear-gradient(135deg, rgba(64, 150, 255, 0.25) 0%, rgba(100, 180, 255, 0.2) 100%); + transform: translateY(-2px) scale(1.04); + box-shadow: + 0 12px 40px rgba(64, 150, 255, 0.3), + 0 0 0 1px rgba(64, 150, 255, 0.5), + inset 0 1px 0 rgba(255, 255, 255, 0.15); +} + +:deep(.episode-layer-number) { font-size: 16px; - font-weight: bold; + font-weight: 700; margin-right: 12px; - min-width: 24px; - text-align: center; + min-width: 28px; + height: 28px; + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.1); + border-radius: 6px; + color: rgba(255, 255, 255, 0.9); + border: 1px solid rgba(255, 255, 255, 0.15); + transition: all 0.3s ease; } -.episode-name { +.episode-layer-item.current .episode-layer-number { + background: rgba(64, 150, 255, 0.3); + border-color: rgba(64, 150, 255, 0.4); + color: #ffffff; + box-shadow: 0 2px 8px rgba(64, 150, 255, 0.2); +} + +:deep(.episode-layer-name) { font-size: 14px; + font-weight: 500; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + color: rgba(255, 255, 255, 0.9); + line-height: 1.3; + letter-spacing: -0.01em; +} + +:deep(.episode-layer-item.current .episode-layer-name) { + color: #ffffff; + font-weight: 600; +} + +/* 响应式设计 */ +@media (max-width: 1200px) { + :deep(.episode-layer-list) { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 768px) { + :deep(.episode-layer-content) { + max-width: 95%; + margin: 0 12px; + max-height: 70vh; + } + + :deep(.episode-layer-list) { + grid-template-columns: 1fr; + padding: 16px 20px 20px; + gap: 12px; + max-height: 50vh; + } + + :deep(.episode-layer-item) { + min-height: 60px; + padding: 12px 14px; + } + + :deep(.episode-layer-number) { + min-width: 26px; + height: 26px; + font-size: 15px; + margin-right: 10px; + } + + :deep(.episode-layer-name) { + font-size: 13px; + } +} + +@media (max-width: 480px) { + :deep(.episode-layer-background) { + padding: 12px; + } + + :deep(.episode-layer-content) { + max-height: 75vh; + } + + :deep(.episode-layer-header) { + padding: 14px 16px 10px; + } + + :deep(.episode-layer-header h3) { + font-size: 18px; + } + + :deep(.episode-layer-list) { + max-height: 55vh; + padding: 12px 16px 16px; + } } From 921d85241c796c0f38b6018c45ecf5db147cf0af Mon Sep 17 00:00:00 2001 From: Taois Date: Wed, 1 Oct 2025 19:35:26 +0800 Subject: [PATCH 060/199] =?UTF-8?q?feat:=20=E6=92=AD=E6=94=BE=E5=99=A8?= =?UTF-8?q?=E7=95=8C=E9=9D=A2=E4=BC=98=E5=8C=96=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/players/ArtVideoPlayer.vue | 9 --- .../src/components/players/VideoPlayer.vue | 11 --- dashboard/src/composables/useSkipSettings.js | 76 ++++++------------- 3 files changed, 24 insertions(+), 72 deletions(-) diff --git a/dashboard/src/components/players/ArtVideoPlayer.vue b/dashboard/src/components/players/ArtVideoPlayer.vue index 06b2319..5634a62 100644 --- a/dashboard/src/components/players/ArtVideoPlayer.vue +++ b/dashboard/src/components/players/ArtVideoPlayer.vue @@ -568,13 +568,11 @@ const initArtPlayer = async (url) => { }) art.on('video:loadstart', () => { - console.log('开始加载视频') // 重置片头片尾跳过状态 resetSkipState() }) art.on('video:canplay', () => { - console.log('视频可以播放') // 视频可以播放时,重置重连计数器 resetRetryState() // 应用片头片尾设置 @@ -595,7 +593,6 @@ const initArtPlayer = async (url) => { }) art.on('video:playing', () => { - console.log('视频开始播放') // 视频开始播放时,重置重连计数器 resetRetryState() @@ -615,8 +612,6 @@ const initArtPlayer = async (url) => { // 监听全屏状态变化 art.on('fullscreen', (isFullscreen) => { - console.log('全屏状态变化:', isFullscreen) - // 标记全屏状态开始变化 onFullscreenChangeStart() @@ -751,7 +746,6 @@ const handleRetry = (url) => { const resetRetryState = () => { retryCount.value = 0 isRetrying.value = false - console.log('重连状态已重置') } // 计算动态高度 @@ -829,13 +823,11 @@ const cancelAutoNext = () => { // 立即播放下一集 const playNextEpisode = () => { if (!hasNextEpisode()) { - console.log('已经是最后一集') Message.info('已经是最后一集了') return } const nextEpisode = getNextEpisode() - console.log('播放下一集:', nextEpisode.name) // 清理倒计时 cancelAutoNext() @@ -850,7 +842,6 @@ const playNextEpisode = () => { // 切换自动下一集开关 const toggleAutoNext = () => { autoNextEnabled.value = !autoNextEnabled.value - console.log('自动下一集开关:', autoNextEnabled.value ? '开启' : '关闭') if (!autoNextEnabled.value) { cancelAutoNext() diff --git a/dashboard/src/components/players/VideoPlayer.vue b/dashboard/src/components/players/VideoPlayer.vue index 8e2df1e..006b707 100644 --- a/dashboard/src/components/players/VideoPlayer.vue +++ b/dashboard/src/components/players/VideoPlayer.vue @@ -288,14 +288,11 @@ const isDirectVideoLink = (url) => { const initVideoPlayer = (url) => { if (!videoPlayer.value || !url) return - console.log('初始化视频播放器:', url) - // 重置片头片尾跳过状态 resetSkipState() // 首先判断链接类型 if (!isDirectVideoLink(url)) { - console.log('检测到网页链接,在新窗口打开:', url) Message.info('检测到网页链接,正在新窗口打开...') window.open(url, '_blank') emit('close') // 关闭播放器 @@ -312,11 +309,8 @@ const initVideoPlayer = (url) => { // 视频结束事件处理函数 const handleVideoEnded = () => { - console.log('视频播放结束') - // 防抖:如果正在处理自动连播,则忽略 if (isProcessingAutoNext.value) { - console.log('正在处理自动连播,忽略重复的ended事件') return } @@ -402,12 +396,10 @@ const initVideoPlayer = (url) => { } } else { // 处理其他格式的视频(mp4, webm, avi等) - console.log('播放普通视频格式:', url) video.src = url // 添加事件监听器 const handleLoadedMetadata = () => { - console.log('视频元数据加载完成,开始播放') video.play().catch(err => { console.warn('自动播放失败:', err) Message.warning('自动播放失败,请手动点击播放') @@ -422,7 +414,6 @@ const initVideoPlayer = (url) => { // 如果播放失败,再次检查是否为网页链接 if (!isDirectVideoLink(url)) { - console.log('播放失败,检测到可能是网页链接,在新窗口打开:', url) Message.info('视频播放失败,检测到网页链接,正在新窗口打开...') window.open(url, '_blank') emit('close') // 关闭播放器 @@ -433,13 +424,11 @@ const initVideoPlayer = (url) => { } const handleLoadStart = () => { - console.log('开始加载视频') // 重置片头片尾跳过状态 resetSkipState() } const handlePlaying = () => { - console.log('视频开始播放') // 延迟一点应用跳过设置,确保视频已经开始播放 setTimeout(() => { applySkipSettings() diff --git a/dashboard/src/composables/useSkipSettings.js b/dashboard/src/composables/useSkipSettings.js index f639570..fa44eab 100644 --- a/dashboard/src/composables/useSkipSettings.js +++ b/dashboard/src/composables/useSkipSettings.js @@ -102,16 +102,8 @@ export function useSkipSettings(options = {}) { * 立即应用片头跳过逻辑(用于视频刚开始播放时) */ const applyIntroSkipImmediate = () => { - console.log('applyIntroSkipImmediate 被调用') - - if (!skipIntroEnabled.value) { - console.log('片头跳过未启用') - return - } - - if (skipIntroApplied.value) { - console.log('片头跳过已应用,跳过') - return + if (!skipIntroEnabled.value || skipIntroApplied.value) { + return false } const currentTime = getCurrentTime() @@ -119,29 +111,20 @@ export function useSkipSettings(options = {}) { // 检查用户是否正在拖动或刚刚拖动过(3秒内) if (userSeeking.value || (lastUserSeekTime.value > 0 && now - lastUserSeekTime.value < 3000)) { - console.log('用户正在拖动进度条或刚刚拖动过,跳过自动片头跳过') - return - } - - // 检查是否正在全屏切换或刚刚切换过(2秒内) - if (isFullscreenChanging.value || (lastFullscreenChangeTime.value > 0 && now - lastFullscreenChangeTime.value < 2000)) { - console.log('正在全屏切换或刚刚切换过,跳过自动片头跳过') - return + return false } // 检查是否正在全屏切换或刚刚切换过(2秒内) if (isFullscreenChanging.value || (lastFullscreenChangeTime.value > 0 && now - lastFullscreenChangeTime.value < 2000)) { - console.log('正在全屏切换或刚刚切换过,跳过自动片头跳过') - return + return false } // 立即跳过模式:如果当前时间很小(小于等于1秒)且在片头跳过范围内,立即跳过 if (currentTime <= 1 && currentTime <= skipIntroSeconds.value) { - console.log(`立即跳过片头:从 ${currentTime} 秒跳转到 ${skipIntroSeconds.value} 秒`) + console.log(`立即跳过片头:从 ${currentTime.toFixed(1)}s 跳转到 ${skipIntroSeconds.value}s`) setCurrentTime(skipIntroSeconds.value) skipIntroApplied.value = true lastSkipTime.value = now - console.log(`已立即跳过片头 ${skipIntroSeconds.value} 秒`) return true // 返回 true 表示已执行跳过 } @@ -152,21 +135,7 @@ export function useSkipSettings(options = {}) { * 应用片头跳过逻辑 */ const applyIntroSkip = () => { - console.log('applyIntroSkip 被调用', { - skipIntroEnabled: skipIntroEnabled.value, - skipIntroApplied: skipIntroApplied.value, - currentTime: getCurrentTime(), - skipIntroSeconds: skipIntroSeconds.value, - userSeeking: userSeeking.value - }) - - if (!skipIntroEnabled.value) { - console.log('片头跳过未启用') - return - } - - if (skipIntroApplied.value) { - console.log('片头跳过已应用,跳过') + if (!skipIntroEnabled.value || skipIntroApplied.value) { return } @@ -175,32 +144,25 @@ export function useSkipSettings(options = {}) { // 检查用户是否正在拖动或刚刚拖动过(3秒内) if (userSeeking.value || (lastUserSeekTime.value > 0 && now - lastUserSeekTime.value < 3000)) { - console.log('用户正在拖动进度条或刚刚拖动过,跳过自动片头跳过') return } // 检查是否正在全屏切换或刚刚切换过(2秒内) if (isFullscreenChanging.value || (lastFullscreenChangeTime.value > 0 && now - lastFullscreenChangeTime.value < 2000)) { - console.log('正在全屏切换或刚刚切换过,跳过自动片头跳过') return } // 防抖:如果距离上次跳过不足1秒,则忽略(但如果是新视频,lastSkipTime为0,允许跳过) - // 减少防抖时间,提高响应速度 if (lastSkipTime.value > 0 && now - lastSkipTime.value < 1000) { - console.log('防抖限制,跳过') return } // 如果当前时间在片头跳过范围内,则跳过 if (currentTime <= skipIntroSeconds.value) { - console.log(`准备跳过片头:从 ${currentTime} 秒跳转到 ${skipIntroSeconds.value} 秒`) + console.log(`已跳过片头:从 ${currentTime.toFixed(1)}s 跳转到 ${skipIntroSeconds.value}s`) setCurrentTime(skipIntroSeconds.value) skipIntroApplied.value = true lastSkipTime.value = now - console.log(`已跳过片头 ${skipIntroSeconds.value} 秒`) - } else { - console.log(`当前时间 ${currentTime} 秒已超过片头跳过范围 ${skipIntroSeconds.value} 秒`) } } @@ -247,19 +209,29 @@ export function useSkipSettings(options = {}) { applyOutroSkip() } + // 防抖变量 + let timeUpdateDebounceTimer = null + /** * 处理时间更新事件 */ const handleTimeUpdate = () => { - // 应用片头跳过(仅在未应用时) - if (skipIntroEnabled.value && !skipIntroApplied.value) { - applyIntroSkip() + // 防抖:减少频繁调用,每200ms最多执行一次 + if (timeUpdateDebounceTimer) { + clearTimeout(timeUpdateDebounceTimer) } + + timeUpdateDebounceTimer = setTimeout(() => { + // 应用片头跳过(仅在未应用时) + if (skipIntroEnabled.value && !skipIntroApplied.value) { + applyIntroSkip() + } - // 应用片尾跳过(仅在未应用时) - if (skipOutroEnabled.value && !skipOutroApplied.value) { - applyOutroSkip() - } + // 应用片尾跳过(仅在未应用时) + if (skipOutroEnabled.value && !skipOutroApplied.value) { + applyOutroSkip() + } + }, 200) } /** From 48d45c2e37838dcd74652a078f790706c9c89f43 Mon Sep 17 00:00:00 2001 From: Taois Date: Wed, 1 Oct 2025 19:52:22 +0800 Subject: [PATCH 061/199] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E5=85=8D?= =?UTF-8?q?=E5=97=85=E6=8E=A2=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard/src/api/modules/module.js | 86 +++++++++++++- dashboard/src/api/services/video.js | 47 ++++++++ dashboard/src/views/VideoDetail.vue | 174 +++++++++++++++++++++++++--- 3 files changed, 293 insertions(+), 14 deletions(-) diff --git a/dashboard/src/api/modules/module.js b/dashboard/src/api/modules/module.js index e698f0d..2b0b7d8 100644 --- a/dashboard/src/api/modules/module.js +++ b/dashboard/src/api/modules/module.js @@ -173,18 +173,24 @@ export const getVideoDetail = async (module, params) => { * @param {string} module - 模块名称 * @param {object} params - 播放参数 * @param {string} params.play - 播放地址或ID + * @param {string} params.flag - 源标识(线路名称) * @param {string|object} params.extend - 接口数据扩展参数(对象类型会自动转换为JSON字符串) * @param {string} params.apiUrl - 可选的直接API地址 * @returns {Promise} 播放数据 */ export const getPlayData = async (module, params) => { - const { play, extend, apiUrl } = params + const { play, flag, extend, apiUrl } = params const requestParams = { ac: MODULE_ACTIONS.PLAY, play } + // 添加flag参数支持 + if (flag) { + requestParams.flag = flag + } + const processedExtend = processExtendParam(extend) if (processedExtend) { requestParams.extend = processedExtend @@ -199,6 +205,83 @@ export const getPlayData = async (module, params) => { return get(buildModuleUrl(module), requestParams) } +/** + * 播放解析接口 - 专门用于选集播放解析 + * @param {string} module - 模块名称 + * @param {object} params - 播放参数 + * @param {string} params.play - 播放地址或ID(选集链接) + * @param {string} params.flag - 源标识(线路名称) + * @param {string|object} params.extend - 接口数据扩展参数 + * @param {string} params.apiUrl - 可选的直接API地址 + * @returns {Promise} 播放解析结果 + */ +export const parsePlayUrl = async (module, params) => { + try { + console.log('T4播放解析请求:', { module, params }) + + const playData = await getPlayData(module, params) + console.log('T4播放解析响应:', playData) + + // 处理解析结果 + const result = { + success: true, + data: playData, + // 解析播放类型 + playType: 'direct', // 默认直链 + url: '', + needParse: false, + needSniff: false, + message: '' + } + + // 检查返回数据格式 + if (playData && typeof playData === 'object') { + // 检查parse字段 + if (playData.parse === 0) { + // 直链播放 + result.playType = 'direct' + result.url = playData.url || playData.play_url || '' + result.needParse = false + result.needSniff = false + result.message = '直链播放' + } else if (playData.parse === 1) { + // 需要嗅探 + result.playType = 'sniff' + result.url = playData.url || playData.play_url || '' + result.needSniff = true + result.message = '需要嗅探才能播放,尽情期待' + } else if (playData.jx === 1) { + // 需要解析 + result.playType = 'parse' + result.url = playData.url || playData.play_url || '' + result.needParse = true + result.message = '需要解析才能播放,尽情期待' + } else { + // 默认处理为直链 + result.url = playData.url || playData.play_url || playData + result.message = '直链播放' + } + } else if (typeof playData === 'string') { + // 如果返回的是字符串,直接作为播放地址 + result.url = playData + result.message = '直链播放' + } + + return result + } catch (error) { + console.error('T4播放解析失败:', error) + return { + success: false, + error: error.message || '播放解析失败', + playType: 'error', + url: '', + needParse: false, + needSniff: false, + message: '播放解析失败: ' + (error.message || '未知错误') + } + } +} + /** * 搜索接口 * @param {string} module - 模块名称 @@ -350,6 +433,7 @@ export default { getCategoryData, getVideoDetail, getPlayData, + parsePlayUrl, searchVideos, executeAction, refreshModule, diff --git a/dashboard/src/api/services/video.js b/dashboard/src/api/services/video.js index b00ff42..f9b1183 100644 --- a/dashboard/src/api/services/video.js +++ b/dashboard/src/api/services/video.js @@ -8,6 +8,7 @@ import { getCategoryData, getVideoDetail, getPlayData, + parsePlayUrl, searchVideos, refreshModule, executeAction @@ -323,6 +324,52 @@ class VideoService { } } + /** + * 解析选集播放地址 - T4接口专用 + * @param {string} module - 模块名称 + * @param {object} params - 播放参数 + * @param {string} params.play - 播放地址或ID(选集链接) + * @param {string} params.flag - 源标识(线路名称) + * @param {string} params.apiUrl - API地址 + * @param {string} params.extend - 扩展参数 + * @returns {Promise} 播放解析结果 + */ + async parseEpisodePlayUrl(module, params) { + if (!validateModule(module)) { + throw new Error('无效的模块名称') + } + + const { play, flag, apiUrl, extend } = params + + if (!play) { + throw new Error('播放地址不能为空') + } + + try { + console.log('VideoService: 开始解析选集播放地址', { module, params }) + + const parseParams = { play, extend } + + // 添加flag参数(线路名称) + if (flag) { + parseParams.flag = flag + } + + // 添加API地址 + if (apiUrl) { + parseParams.apiUrl = apiUrl + } + + const result = await parsePlayUrl(module, parseParams) + console.log('VideoService: 选集播放解析结果', result) + + return result + } catch (error) { + console.error('VideoService: 解析选集播放地址失败:', error) + throw error + } + } + /** * 执行T4 Action动作 * @param {string} module - 模块名称 diff --git a/dashboard/src/views/VideoDetail.vue b/dashboard/src/views/VideoDetail.vue index e02c611..8442102 100644 --- a/dashboard/src/views/VideoDetail.vue +++ b/dashboard/src/views/VideoDetail.vue @@ -50,8 +50,8 @@
+ + + +
+
+ {{ parseDialogConfig.message }} +
+
+
+ +
+
+ 敬请期待后续版本支持! +
+
+
+ + +
@@ -186,6 +216,7 @@ import { usePageStateStore } from '@/stores/pageStateStore' import VideoPlayer from '@/components/players/VideoPlayer.vue' import ArtVideoPlayer from '@/components/players/ArtVideoPlayer.vue' import EpisodeSelector from '@/components/players/EpisodeSelector.vue' +import ActionDialog from '@/components/actions/ActionDialog.vue' import { IconLeft, IconPlayArrow, @@ -231,6 +262,16 @@ const currentSiteInfo = ref({ // 视频播放器相关 const showVideoPlayer = ref(false) +// 解析后的播放URL(用于T4接口解析结果) +const parsedVideoUrl = ref('') + +// 解析提示弹窗相关 +const showParseDialog = ref(false) +const parseDialogConfig = ref({ + title: '', + message: '', + type: '' // 'sniff' 或 'parse' +}) // 从localStorage读取用户的播放器偏好,默认为'default' const getPlayerPreference = () => { @@ -331,6 +372,11 @@ const currentEpisodeUrl = computed(() => { return episode?.url || '' }) +// 实际播放URL(优先使用解析后的URL) +const actualVideoUrl = computed(() => { + return parsedVideoUrl.value || currentEpisodeUrl.value +}) + const currentEpisodeName = computed(() => { const episodes = playRoutes.value[currentRoute.value]?.episodes || [] const episode = episodes[currentEpisode.value] @@ -757,22 +803,84 @@ const handleEpisodeSelected = (episode) => { } } -const selectEpisode = (index) => { +const selectEpisode = async (index) => { currentEpisode.value = index - // 获取当前选集的URL + // 获取当前选集的URL和线路信息 const episodeUrl = currentRouteEpisodes.value[index]?.url + const routeName = playRoutes.value[currentRoute.value]?.name - // 启动内置播放器播放所有格式的视频 - if (episodeUrl) { - console.log('启动内置播放器播放视频:', episodeUrl) - showVideoPlayer.value = true - - Message.success(`开始播放: ${currentEpisodeName.value}`) - } else { + if (!episodeUrl) { console.log('选集URL为空,无法播放') Message.error('选集URL为空,无法播放') + return } + + try { + console.log('开始解析选集播放地址:', { episodeUrl, routeName }) + Message.info('正在解析播放地址...') + + // 调用T4播放API进行解析 + const parseParams = { + play: episodeUrl, + flag: routeName, + apiUrl: currentSiteInfo.value.api, + extend: currentSiteInfo.value.ext + } + + const parseResult = await videoService.parseEpisodePlayUrl(currentSiteInfo.value.key, parseParams) + console.log('选集播放解析结果:', parseResult) + + // 根据解析结果处理播放 + if (parseResult.playType === 'direct') { + // parse:0 - 直链播放 + console.log('启动内置播放器播放直链视频:', parseResult.url) + parsedVideoUrl.value = parseResult.url + showVideoPlayer.value = true + Message.success(`开始播放: ${currentEpisodeName.value}`) + } else if (parseResult.playType === 'sniff') { + // parse:1 - 需要嗅探 + console.log('需要嗅探播放:', parseResult) + // 清空解析URL,不启动播放器 + parsedVideoUrl.value = '' + + // 显示嗅探提示弹窗 + parseDialogConfig.value = { + title: '播放提示', + message: '该视频需要嗅探才能播放,当前版本暂不支持此功能。', + type: 'sniff' + } + showParseDialog.value = true + } else if (parseResult.playType === 'parse') { + // jx:1 - 需要解析 + console.log('需要解析播放:', parseResult) + // 清空解析URL,不启动播放器 + parsedVideoUrl.value = '' + + // 显示解析提示弹窗 + parseDialogConfig.value = { + title: '播放提示', + message: '该视频需要解析才能播放,当前版本暂不支持此功能。', + type: 'parse' + } + showParseDialog.value = true + } else { + // 其他情况,回退到原始播放方式 + console.log('使用原始播放方式:', episodeUrl) + parsedVideoUrl.value = '' + showVideoPlayer.value = true + Message.success(`开始播放: ${currentEpisodeName.value}`) + } + } catch (error) { + console.error('解析选集播放地址失败:', error) + Message.error('解析播放地址失败,请稍后重试') + + // 解析失败时回退到原始播放方式 + console.log('回退到原始播放方式:', episodeUrl) + parsedVideoUrl.value = '' + showVideoPlayer.value = true + Message.warning(`播放可能不稳定: ${currentEpisodeName.value}`) + } // 添加到历史记录 if (videoDetail.value && currentRouteEpisodes.value[index]) { @@ -1760,4 +1868,44 @@ onUnmounted(() => { font-size: 14px; } } + +/* 解析提示弹窗样式 */ +.parse-dialog-content { + padding: 20px 0; + text-align: center; +} + +.parse-message { + font-size: 16px; + color: var(--color-text-1); + margin-bottom: 20px; + line-height: 1.5; +} + +.parse-hint { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 16px; + background: var(--color-bg-2); + border-radius: 8px; + border-left: 4px solid var(--color-primary); +} + +.hint-icon { + color: var(--color-primary); + font-size: 18px; +} + +.hint-text { + color: var(--color-text-2); + font-size: 14px; +} + +.parse-dialog-footer { + display: flex; + justify-content: center; + padding-top: 16px; +} \ No newline at end of file From 838d341f7e3fd717f7930bc590e5ad8cbfc93545 Mon Sep 17 00:00:00 2001 From: Taois Date: Wed, 1 Oct 2025 23:17:02 +0800 Subject: [PATCH 062/199] =?UTF-8?q?feat:=20=E4=BF=AE=E5=A4=8D=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E5=99=A8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/players/ArtVideoPlayer.vue | 32 +- .../src/components/players/VideoPlayer.vue | 22 +- dashboard/src/router/index.js | 2 + dashboard/src/views/ActionTest.vue | 33 +- dashboard/src/views/VideoDetail.vue | 90 +- dashboard/src/views/VideoTest.vue | 819 ++++++++++++++++++ 6 files changed, 902 insertions(+), 96 deletions(-) create mode 100644 dashboard/src/views/VideoTest.vue diff --git a/dashboard/src/components/players/ArtVideoPlayer.vue b/dashboard/src/components/players/ArtVideoPlayer.vue index 5634a62..728a667 100644 --- a/dashboard/src/components/players/ArtVideoPlayer.vue +++ b/dashboard/src/components/players/ArtVideoPlayer.vue @@ -1,5 +1,5 @@ @@ -233,6 +229,9 @@ import { IconExclamationCircle } from '@arco-design/web-vue/es/icon' import liveService from '@/api/services/live.js' +import PlayerHeader from '@/components/players/PlayerHeader.vue' +import DebugInfoDialog from '@/components/players/DebugInfoDialog.vue' +import { processVideoUrl } from '@/utils/proxyPlayer' const router = useRouter() @@ -250,6 +249,22 @@ const videoLoading = ref(false) const videoError = ref('') const videoPlayer = ref(null) +// PlayerHeader相关状态 +const debugMode = ref(false) + +// 从addressSettings中读取代理状态 +const getProxyEnabledFromSettings = () => { + try { + const savedAddresses = JSON.parse(localStorage.getItem('addressSettings') || '{}') + return savedAddresses.proxyPlayEnabled === true + } catch (error) { + console.error('读取代理设置失败:', error) + return false + } +} + +const proxyEnabled = ref(getProxyEnabledFromSettings()) + // 计算属性 const filteredGroups = computed(() => { if (!liveData.value) return [] @@ -289,6 +304,25 @@ const currentChannels = computed(() => { return channels }) +// 将线路转换为画质选择格式 +const routeQualities = computed(() => { + if (!selectedChannel.value || !selectedChannel.value.routes) return [] + + return selectedChannel.value.routes.map(route => ({ + name: route.name, + id: route.id, + url: route.url + })) +}) + +// 当前画质名称 +const currentQualityName = computed(() => { + if (!selectedChannel.value || !selectedChannel.value.routes) return '默认' + + const currentRoute = selectedChannel.value.routes.find(route => route.id === currentRouteId.value) + return currentRoute ? currentRoute.name : '默认' +}) + // 方法 const loadLiveData = async (forceRefresh = false) => { loading.value = true @@ -344,6 +378,12 @@ const selectChannel = (channel) => { } // 重置视频播放器 if (videoPlayer.value) { + const newSrc = getVideoUrl() + console.log('=== selectChannel 设置视频源 ===') + console.log('设置前 videoPlayer.src:', videoPlayer.value.src) + console.log('新的 src:', newSrc) + videoPlayer.value.src = newSrc + console.log('设置后 videoPlayer.src:', videoPlayer.value.src) videoPlayer.value.load() } setupMpegtsPlayer() @@ -357,8 +397,15 @@ function setupMpegtsPlayer() { mpegtsPlayer.destroy() mpegtsPlayer = null } - const url = getCurrentChannelUrl() + + // 使用getVideoUrl函数获取正确的URL + const url = getVideoUrl() + console.log('=== setupMpegtsPlayer ===') + console.log('mpegts播放器使用的URL:', url) + console.log('当前videoPlayer.src:', videoPlayer.value?.src) + if (!url || !videoPlayer.value) return + // 判断是否为 mpegts 流(简单判断 .ts 或 mpegts 协议) if (url.endsWith('.ts') || url.includes('mpegts') || url.includes('udpxy') || url.includes('/udp/') || url.includes('rtp://') || url.includes('udp://')) { @@ -379,6 +426,9 @@ onUnmounted(() => { mpegtsPlayer.destroy() mpegtsPlayer = null } + + // 清理事件监听器 + window.removeEventListener('addressSettingsChanged', handleAddressSettingsChange) }) const getCurrentChannelUrl = () => { if (!selectedChannel.value) return '' @@ -391,6 +441,31 @@ const getCurrentChannelUrl = () => { return selectedChannel.value.url || '' } +// 获取代理后的频道URL +const getProxyChannelUrl = () => { + const originalUrl = getCurrentChannelUrl() + if (!originalUrl) return '' + + // 对于直播流,通常不需要特殊的请求头,但可以通过代理播放来处理跨域问题 + return processVideoUrl(originalUrl, {}) +} + +// 获取视频URL(根据代理设置) +const getVideoUrl = () => { + const originalUrl = getCurrentChannelUrl() + const proxyUrl = getProxyChannelUrl() + const finalUrl = proxyEnabled.value ? proxyUrl : originalUrl + + console.log('=== getVideoUrl 调试信息 ===') + console.log('代理状态:', proxyEnabled.value) + console.log('原始URL:', originalUrl) + console.log('代理URL:', proxyUrl) + console.log('最终URL:', finalUrl) + console.log('========================') + + return finalUrl +} + // 切换线路 const switchRoute = (event) => { const routeId = Number(event.target ? event.target.value : event) @@ -401,10 +476,15 @@ const switchRoute = (event) => { videoError.value = '' nextTick(() => { if (videoPlayer.value) { - videoPlayer.value.src = route.url - videoPlayer.value.load() - } - setupMpegtsPlayer() + const newSrc = getVideoUrl() + console.log('=== switchRoute 设置视频源 ===') + console.log('设置前 videoPlayer.src:', videoPlayer.value.src) + console.log('新的 src:', newSrc) + videoPlayer.value.src = newSrc + console.log('设置后 videoPlayer.src:', videoPlayer.value.src) + videoPlayer.value.load() + } + setupMpegtsPlayer() }) Message.success(`已切换到${route.name}`) } @@ -473,6 +553,95 @@ const retryVideo = () => { } } +// PlayerHeader事件处理方法 +const handleQualityChange = (qualityName) => { + // 根据画质名称找到对应的线路 + if (!selectedChannel.value || !selectedChannel.value.routes) return + + const route = selectedChannel.value.routes.find(r => r.name === qualityName) + if (route) { + switchRoute(route.id) + } +} + +const handleProxyChange = (proxyUrl) => { + console.log('=== 代理播放地址变更 ===') + console.log('新代理URL:', proxyUrl) + console.log('变更前代理状态:', proxyEnabled.value) + + try { + // 获取现有的地址设置 + const savedAddresses = JSON.parse(localStorage.getItem('addressSettings') || '{}') + + // 保存代理设置到addressSettings(与PlayerHeader保持一致) + if (proxyUrl === 'disabled') { + savedAddresses.proxyPlayEnabled = false + // 不清除proxyPlay地址,保留用户配置 + proxyEnabled.value = false + } else { + savedAddresses.proxyPlayEnabled = true + savedAddresses.proxyPlay = proxyUrl + proxyEnabled.value = true + } + + // 保存到localStorage + localStorage.setItem('addressSettings', JSON.stringify(savedAddresses)) + + // 触发自定义事件,通知其他组件设置已变化 + window.dispatchEvent(new CustomEvent('addressSettingsChanged')) + + console.log('变更后代理状态:', proxyEnabled.value) + console.log('保存的addressSettings:', savedAddresses) + } catch (error) { + console.error('保存代理播放设置失败:', error) + } + + // 如果当前有选中的频道,重新加载视频以应用代理设置 + if (selectedChannel.value) { + // 销毁当前播放器 + if (mpegtsPlayer) { + mpegtsPlayer.destroy() + mpegtsPlayer = null + } + + // 重新设置视频源 + if (videoPlayer.value) { + videoError.value = '' + videoLoading.value = true + + // 使用getVideoUrl函数获取正确的URL + const newUrl = getVideoUrl() + videoPlayer.value.src = newUrl + videoPlayer.value.load() + + // 重新设置mpegts播放器 + nextTick(() => { + setupMpegtsPlayer() + }) + } + } + + Message.success(`代理播放设置: ${proxyUrl === 'disabled' ? '已关闭' : '已启用'}`) +} + +const handleToggleDebug = () => { + debugMode.value = !debugMode.value + console.log('调试模式:', debugMode.value ? '开启' : '关闭') + Message.info(`调试模式${debugMode.value ? '已开启' : '已关闭'}`) +} + + + +const handleClosePlayer = () => { + selectedChannel.value = null + currentRouteId.value = 1 + videoError.value = '' + if (mpegtsPlayer) { + mpegtsPlayer.destroy() + mpegtsPlayer = null + } +} + // 监听选中频道变化 watch(selectedChannel, (newChannel) => { if (newChannel) { @@ -505,15 +674,27 @@ const testLocalM3U = async () => { } } +// 监听地址设置变化 +const handleAddressSettingsChange = () => { + const newProxyEnabled = getProxyEnabledFromSettings() + if (proxyEnabled.value !== newProxyEnabled) { + proxyEnabled.value = newProxyEnabled + console.log('代理状态已同步更新:', newProxyEnabled) + } +} + // 组件挂载时加载数据 -onMounted(() => { - // 先尝试正常加载,如果失败则测试本地M3U - loadLiveData().then(() => { - if (!liveData.value) { - console.log('正常加载失败,尝试测试本地M3U文件') - testLocalM3U() - } - }) +onMounted(async () => { + try { + await loadLiveData() + } catch (error) { + console.error('加载直播数据失败:', error) + // 如果加载失败,尝试测试本地M3U + testLocalM3U() + } + + // 监听地址设置变化事件 + window.addEventListener('addressSettingsChanged', handleAddressSettingsChange) }) @@ -751,48 +932,7 @@ onMounted(() => { overflow: hidden; } -.channel-header { - padding: 16px; - border-bottom: 1px solid var(--color-border-2); - background: var(--color-bg-3); -} -.channel-title h4 { - margin: 0 0 8px 0; - font-size: 16px; - color: var(--color-text-1); -} - -.channel-url { - font-size: 12px; - color: var(--color-text-3); - word-break: break-all; -} - -.route-switcher { - margin-top: 12px; - display: flex; - align-items: flex-start; - gap: 12px; - flex-wrap: wrap; -} - -.route-label { - font-size: 12px; - color: var(--color-text-2); - white-space: nowrap; - margin-top: 4px; -} - -.route-radio-group { - display: flex; - flex-wrap: wrap; - gap: 16px; -} - -.route-radio { - margin-right: 0 !important; -} .video-container { flex: 1; From eaa01d9ec08abf90fda70cc2f075b9bae4ed7259 Mon Sep 17 00:00:00 2001 From: Taois Date: Sat, 11 Oct 2025 08:39:13 +0800 Subject: [PATCH 156/199] =?UTF-8?q?feat:=E8=81=9A=E5=90=88=E6=90=9C?= =?UTF-8?q?=E7=B4=A2=E5=9F=BA=E7=A1=80=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard/src/components/Header.vue | 245 ++++- .../src/components/SearchSettingsModal.vue | 629 ++++++++++++ dashboard/src/router/index.js | 2 + dashboard/src/views/SearchAggregation.vue | 952 ++++++++++++++++++ 4 files changed, 1788 insertions(+), 40 deletions(-) create mode 100644 dashboard/src/components/SearchSettingsModal.vue create mode 100644 dashboard/src/views/SearchAggregation.vue diff --git a/dashboard/src/components/Header.vue b/dashboard/src/components/Header.vue index 52ea094..2c37eb2 100644 --- a/dashboard/src/components/Header.vue +++ b/dashboard/src/components/Header.vue @@ -2,30 +2,57 @@
- - - - - - - - - + + + +
-
- +
+
+ + + + +
@@ -68,17 +95,35 @@
+ + + + + \ No newline at end of file diff --git a/dashboard/src/router/index.js b/dashboard/src/router/index.js index 11809d5..1060de2 100644 --- a/dashboard/src/router/index.js +++ b/dashboard/src/router/index.js @@ -12,6 +12,7 @@ import ActionTest from '@/views/ActionTest.vue'; import ActionDebugTest from '@/views/ActionDebugTest.vue'; import VideoTest from '@/views/VideoTest.vue'; import CSPTest from '@/views/CSPTest.vue'; +import SearchAggregation from '@/views/SearchAggregation.vue'; const routes = [ @@ -28,6 +29,7 @@ const routes = [ {path: '/action-debug-test', component: ActionDebugTest, name: 'ActionDebugTest'}, {path: '/video-test', component: VideoTest, name: 'VideoTest'}, {path: '/csp-test', component: CSPTest, name: 'CSPTest'}, + {path: '/search', component: SearchAggregation, name: 'SearchAggregation'}, // 404 fallback路由 - 必须放在最后 {path: '/:pathMatch(.*)*', redirect: '/'} diff --git a/dashboard/src/views/SearchAggregation.vue b/dashboard/src/views/SearchAggregation.vue new file mode 100644 index 0000000..bdb5d81 --- /dev/null +++ b/dashboard/src/views/SearchAggregation.vue @@ -0,0 +1,952 @@ + + + + + \ No newline at end of file From 2bd389aff48c60c9d15afe23677e232ba72497f1 Mon Sep 17 00:00:00 2001 From: Taois Date: Sun, 12 Oct 2025 06:32:43 +0800 Subject: [PATCH 157/199] =?UTF-8?q?feat:=E6=90=9C=E7=B4=A2=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E7=95=8C=E9=9D=A2=E9=87=8D=E7=BD=AE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard/src/components/SearchSettingsModal.vue | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/dashboard/src/components/SearchSettingsModal.vue b/dashboard/src/components/SearchSettingsModal.vue index 999d936..6708d6f 100644 --- a/dashboard/src/components/SearchSettingsModal.vue +++ b/dashboard/src/components/SearchSettingsModal.vue @@ -290,16 +290,8 @@ export default defineComponent({ // 重置为默认配置(选择所有可用源) selectedSources.value = availableSources.value.map(source => source.key); updateSelectAllState(); - - // 清除localStorage中的配置 - try { - localStorage.removeItem('searchAggregationSettings'); - console.log('已重置搜索源配置'); - Message.success('已重置为默认配置'); - } catch (error) { - console.error('重置配置失败:', error); - Message.error('重置配置失败'); - } + // 不进行持久化,需点击“确定”才保存 + Message.info('已恢复默认选择,点击“确定”后保存'); }; const onSearchFilterChange = () => { From 9e5aa75e41cf61d2c6dee08b3b3f605b6d1afc52 Mon Sep 17 00:00:00 2001 From: Taois Date: Sun, 12 Oct 2025 07:32:13 +0800 Subject: [PATCH 158/199] =?UTF-8?q?feat:=E4=BC=98=E5=8C=96=E6=90=9C?= =?UTF-8?q?=E7=B4=A2=E5=89=8D=E7=95=8C=E9=9D=A2=E7=9A=84=E5=B8=83=E5=B1=80?= =?UTF-8?q?=E5=92=8C=E6=89=A7=E8=A1=8C=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard/src/components/Header.vue | 26 ++- dashboard/src/views/SearchAggregation.vue | 223 +++++++++++++++++----- 2 files changed, 197 insertions(+), 52 deletions(-) diff --git a/dashboard/src/components/Header.vue b/dashboard/src/components/Header.vue index 2c37eb2..4696308 100644 --- a/dashboard/src/components/Header.vue +++ b/dashboard/src/components/Header.vue @@ -38,9 +38,12 @@ v-model="searchValue" placeholder="搜索内容..." enter-button="搜索" + allow-clear @search="onSearch" + @keyup.enter="onSearch(searchValue)" @click="handleSearchClick" @input="handleSearchInput" + @clear="handleSearchClear" /> 1) { + this.$router.back(); + } else { + // 如果没有历史记录,返回到首页 + this.$router.push({ name: 'Home' }); + } }, goForward() { Message.info("后退按钮"); @@ -222,8 +231,19 @@ export default defineComponent({ } }, handleSearchInput(value) { - // 搜索输入时的处理(可以用于实时搜索建议等) - // 暂时不做特殊处理 + // 搜索输入时的处理:在聚合搜索页写入草稿以生成建议 + if (this.isSearchAggregationPage) { + const query = { ...this.$route.query, keywordDraft: value }; + this.$router.push({ name: 'SearchAggregation', query }); + } + }, + handleSearchClear() { + // 清除输入内容,同时清空聚搜页的草稿 + if (this.isSearchAggregationPage) { + const query = { ...this.$route.query }; + delete query.keywordDraft; + this.$router.push({ name: 'SearchAggregation', query }); + } }, openSearchSettings() { // 打开搜索设置弹窗 diff --git a/dashboard/src/views/SearchAggregation.vue b/dashboard/src/views/SearchAggregation.vue index bdb5d81..f691d06 100644 --- a/dashboard/src/views/SearchAggregation.vue +++ b/dashboard/src/views/SearchAggregation.vue @@ -4,8 +4,54 @@
- -
+ +
+
+
+

+ + 最近搜索记录 +

+ 清空 +
+
+ + {{ tag }} + +
+
+
+ + +
+ +
+
+

+ + 猜你想搜 +

+
+
+ + {{ suggestion }} + +
+
+ + + +

@@ -37,27 +83,6 @@

- -
-
-

- - 猜你喜欢 -

-
-
- - {{ suggestion }} -
-
-
-
-
@@ -152,7 +177,7 @@
- +

正在搜索 {{ getSourceName(activeSource) }}...

@@ -185,6 +210,16 @@ import { defineComponent, ref, computed, onMounted, watch } from 'vue'; import { useRoute, useRouter } from 'vue-router'; import { Message } from '@arco-design/web-vue'; +import { + IconHistory, + IconBulb, + IconFire, + IconRefresh, + IconCheckCircle, + IconCloseCircle, + IconExclamationCircle, + IconEmpty +} from '@arco-design/web-vue/es/icon'; import SearchSettingsModal from '@/components/SearchSettingsModal.vue'; import siteService from '@/api/services/site'; import videoService from '@/api/services/video'; @@ -192,7 +227,15 @@ import videoService from '@/api/services/video'; export default defineComponent({ name: 'SearchAggregation', components: { - SearchSettingsModal + SearchSettingsModal, + IconHistory, + IconBulb, + IconFire, + IconRefresh, + IconCheckCircle, + IconCloseCircle, + IconExclamationCircle, + IconEmpty }, setup() { const route = useRoute(); @@ -202,6 +245,7 @@ export default defineComponent({ const searchKeyword = ref(''); const hasSearched = ref(false); const showSearchSettings = ref(false); + const recentSearches = ref([]); // 搜索源和结果 const searchSources = ref([]); @@ -323,6 +367,29 @@ export default defineComponent({ ); await Promise.allSettled(searchPromises); + // 记录最近搜索 + try { + const HISTORY_KEY = 'drplayer_search_history'; + const stored = localStorage.getItem(HISTORY_KEY); + let history = []; + try { history = stored ? JSON.parse(stored) : []; } catch { history = []; } + const k = searchKeyword.value; + // 过滤空字符串和无效值 + if (k && k.trim()) { + const idx = history.findIndex(item => item === k); + if (idx !== -1) history.splice(idx, 1); + history.unshift(k); + // 过滤历史记录中的空字符串 + history = history.filter(item => item && item.trim()); + if (history.length > 10) history = history.slice(0, 10); + localStorage.setItem(HISTORY_KEY, JSON.stringify(history)); + // console.log('保存搜索历史记录:',history); + // 直接更新最近搜索记录 + recentSearches.value = [...history]; + } + } catch (e) { + console.error('保存搜索历史失败:', e); + } }; const searchSource = async (source, keyword) => { @@ -485,6 +552,29 @@ export default defineComponent({ hotSearchTags.value = shuffled.slice(0, 12); // 显示12个标签 }; + // 最近搜索读取与清空 + const loadRecentSearches = () => { + try { + const HISTORY_KEY = 'drplayer_search_history'; + const stored = localStorage.getItem(HISTORY_KEY); + let history = stored ? JSON.parse(stored) : []; + if (!Array.isArray(history)) history = []; + // 过滤空字符串和无效值 + recentSearches.value = history.filter(item => item && item.trim()); + // 如果过滤后的数据与原数据不同,更新localStorage + if (recentSearches.value.length !== history.length) { + localStorage.setItem(HISTORY_KEY, JSON.stringify(recentSearches.value)); + } + } catch { + recentSearches.value = []; + } + }; + const clearRecentSearches = () => { + localStorage.removeItem('drplayer_search_history'); + recentSearches.value = []; + Message.success('已清空最近搜索记录'); + }; + // 监听路由参数 watch(() => route.query.keyword, (keyword) => { if (keyword) { @@ -492,11 +582,21 @@ export default defineComponent({ performSearch(keyword); } }, { immediate: true }); + // 监听输入草稿用于生成建议 + watch(() => route.query.keywordDraft, (draft) => { + const val = typeof draft === 'string' ? draft : ''; + // 只有在没有进行搜索时才更新searchKeyword.value,避免在搜索过程中被重置 + if (!hasSearched.value) { + searchKeyword.value = val; + } + onSearchInput(val); + }); // 组件挂载时初始化 onMounted(() => { loadSearchSources(); randomizeHotSearchTags(); + loadRecentSearches(); // 显示当前配置状态 const settings = getSearchSettings(); @@ -532,7 +632,10 @@ export default defineComponent({ handleImageError, onPageChange, onPageSizeChange, - randomizeHotSearchTags + randomizeHotSearchTags, + // 最近搜索 + recentSearches, + clearRecentSearches }; } }); @@ -562,7 +665,32 @@ export default defineComponent({ } .hot-search-section { + padding: 20px; + max-width: 800px; + margin: 0 auto; margin-bottom: 40px; + margin-top: 0; +} + +.recent-search-section { + padding: 20px; + max-width: 800px; + margin: 0 auto 20px auto; +} +.recent-search-tags { + display: flex; + flex-wrap: wrap; + gap: 12px; +} +.recent-tag { + cursor: pointer; + transition: all 0.2s ease; + border-radius: 16px; + padding: 6px 16px; +} +.recent-tag:hover { + background: var(--color-fill-2); + transform: translateY(-1px); } .section-header { @@ -616,43 +744,40 @@ export default defineComponent({ transform: translateY(-1px); } +/* 最近搜索记录浮动区域 */ +.recent-search-floating { + padding: 20px; + max-width: 800px; + margin: 0 auto; + margin-bottom: 8px; +} + /* 搜索建议样式 */ .search-suggestions { padding: 20px; max-width: 800px; margin: 0 auto; + margin-bottom: 8px; } -.suggestions-list { +.suggestions-tags { display: flex; - flex-direction: column; - gap: 8px; + flex-wrap: wrap; + gap: 12px; } -.suggestion-item { - display: flex; - align-items: center; - gap: 12px; - padding: 12px 16px; - border-radius: 8px; +.suggestion-tag { cursor: pointer; transition: all 0.2s ease; - background: var(--color-bg-2); -} - -.suggestion-item:hover { - background: var(--color-fill-2); - transform: translateX(4px); -} - -.suggestion-icon { - color: var(--color-text-3); - font-size: 14px; + border-radius: 16px; + padding: 6px 16px; } -.suggestion-text { - color: var(--color-text-1); - font-size: 14px; +.suggestion-tag:hover { + background: var(--color-primary-1); + border-color: var(--color-primary-6); + color: var(--color-primary-6); + transform: translateY(-1px); } /* 搜索结果样式 */ From e466021a23135f235c30616b581216adc5cf7d86 Mon Sep 17 00:00:00 2001 From: Taois Date: Sun, 12 Oct 2025 07:43:54 +0800 Subject: [PATCH 159/199] =?UTF-8?q?feat:=E6=9C=80=E8=BF=91=E6=90=9C?= =?UTF-8?q?=E7=B4=A2=E8=AE=B0=E5=BD=95=E5=92=8C=E7=83=AD=E9=97=A8=E6=90=9C?= =?UTF-8?q?=E7=B4=A2=E7=82=B9=E5=87=BB=E5=A4=84=E7=90=86=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard/src/components/Header.vue | 47 +++++++++++++++++++++++ dashboard/src/views/SearchAggregation.vue | 42 ++++++++++++++++---- 2 files changed, 81 insertions(+), 8 deletions(-) diff --git a/dashboard/src/components/Header.vue b/dashboard/src/components/Header.vue index 4696308..7782cab 100644 --- a/dashboard/src/components/Header.vue +++ b/dashboard/src/components/Header.vue @@ -55,6 +55,17 @@ + + +
@@ -128,6 +139,11 @@ export default defineComponent({ return route.name === 'SearchAggregation'; }); + // 检测是否有搜索结果(当在搜索页面且有搜索关键词时) + const hasSearchResults = computed(() => { + return isSearchAggregationPage.value && route.query.keyword; + }); + // 从localStorage获取聚搜功能状态 const getSearchAggregationStatus = () => { try { @@ -176,6 +192,7 @@ export default defineComponent({ searchValue, showSearchSettings: ref(false), isSearchAggregationPage, + hasSearchResults, router }; }, @@ -255,6 +272,11 @@ export default defineComponent({ Message.success(`已选择 ${selectedCount} 个搜索源`); this.showSearchSettings = false; }, + closeSearchResults() { + // 关闭搜索结果,回到搜索页面的初始状态 + this.searchValue = ''; + this.$router.push({ name: 'SearchAggregation' }); + }, minimize() { Message.info("最小化窗口"); // 最小化窗口的逻辑,可以通过调用系统接口来实现 @@ -486,6 +508,31 @@ export default defineComponent({ background: var(--color-fill-4) !important; } +/* 关闭搜索按钮样式 */ +.close-search-btn { + width: 36px !important; + height: 36px !important; + border-radius: 8px !important; + border: 1px solid var(--color-border-2) !important; + background: var(--color-bg-2) !important; + color: var(--color-text-2) !important; + transition: all 0.2s ease !important; + flex-shrink: 0; +} + +.close-search-btn:hover { + background: var(--color-danger-light-1) !important; + border-color: var(--color-danger-light-3) !important; + color: var(--color-danger-6) !important; + transform: translateY(-1px); + box-shadow: 0 2px 8px rgba(245, 63, 63, 0.15) !important; +} + +.close-search-btn:active { + transform: translateY(0) !important; + background: var(--color-danger-light-2) !important; +} + .search-container :deep(.arco-input-search:focus-within) { border-color: var(--color-primary-6); box-shadow: 0 0 0 2px var(--color-primary-1); diff --git a/dashboard/src/views/SearchAggregation.vue b/dashboard/src/views/SearchAggregation.vue index f691d06..5b61bec 100644 --- a/dashboard/src/views/SearchAggregation.vue +++ b/dashboard/src/views/SearchAggregation.vue @@ -12,14 +12,19 @@ 最近搜索记录 - 清空 + + + 清空 +
{{ tag }} @@ -218,7 +223,8 @@ import { IconCheckCircle, IconCloseCircle, IconExclamationCircle, - IconEmpty + IconEmpty, + IconDelete } from '@arco-design/web-vue/es/icon'; import SearchSettingsModal from '@/components/SearchSettingsModal.vue'; import siteService from '@/api/services/site'; @@ -235,7 +241,8 @@ export default defineComponent({ IconCheckCircle, IconCloseCircle, IconExclamationCircle, - IconEmpty + IconEmpty, + IconDelete }, setup() { const route = useRoute(); @@ -497,13 +504,24 @@ export default defineComponent({ }; const searchHotTag = (tag) => { - searchKeyword.value = tag; - performSearch(tag); + router.push({ + name: 'SearchAggregation', + query: { keyword: tag } + }); }; const searchSuggestion = (suggestion) => { - searchKeyword.value = suggestion; - performSearch(suggestion); + router.push({ + name: 'SearchAggregation', + query: { keyword: suggestion } + }); + }; + + const searchRecentTag = (tag) => { + router.push({ + name: 'SearchAggregation', + query: { keyword: tag } + }); }; const onSearchSettingsConfirm = (settings) => { @@ -580,6 +598,13 @@ export default defineComponent({ if (keyword) { searchKeyword.value = keyword; performSearch(keyword); + } else { + // 当没有keyword参数时,重置搜索状态 + hasSearched.value = false; + searchKeyword.value = ''; + searchResults.value = {}; + activeSource.value = ''; + currentPage.value = 1; } }, { immediate: true }); // 监听输入草稿用于生成建议 @@ -627,6 +652,7 @@ export default defineComponent({ onSearchInput, searchHotTag, searchSuggestion, + searchRecentTag, onSearchSettingsConfirm, playVideo, handleImageError, From ef3b5a377570695d80401c3227aae3ebc942ea88 Mon Sep 17 00:00:00 2001 From: Taois Date: Sun, 12 Oct 2025 07:53:54 +0800 Subject: [PATCH 160/199] =?UTF-8?q?feat:=E6=90=9C=E7=B4=A2=E7=95=8C?= =?UTF-8?q?=E9=9D=A2=E4=B8=8D=E5=86=8D=E5=87=BA=E7=8E=B0=E5=A4=96=E5=B1=82?= =?UTF-8?q?=E6=BB=9A=E5=8A=A8=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard/src/components/Layout.vue | 18 +++++++-- dashboard/src/views/SearchAggregation.vue | 49 +++++++++++++++-------- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/dashboard/src/components/Layout.vue b/dashboard/src/components/Layout.vue index c4c919a..189d19b 100644 --- a/dashboard/src/components/Layout.vue +++ b/dashboard/src/components/Layout.vue @@ -57,7 +57,7 @@
-
+
@@ -71,7 +71,7 @@ + + \ No newline at end of file diff --git a/dashboard/src/views/SearchAggregation.vue b/dashboard/src/views/SearchAggregation.vue index c021100..2d4c4f8 100644 --- a/dashboard/src/views/SearchAggregation.vue +++ b/dashboard/src/views/SearchAggregation.vue @@ -137,90 +137,20 @@ 共 {{ searchResults[activeSource].length }} 条结果
- - - - -
-
- - - -
- -
- -
- - - -
- - - -
- -
- -
- -
- -
-
-
-
-

{{ video.vod_name }}

-
- {{ video.vod_year }} - {{ video.vod_area }} -
-
-
-
-
- - -
- -
加载更多...
-
- - -
- 没有更多数据了 -
- - -
-
+ />
@@ -279,6 +209,7 @@ import { } from '@arco-design/web-vue/es/icon'; import SearchSettingsModal from '@/components/SearchSettingsModal.vue'; import ActionRenderer from '@/components/actions/ActionRenderer.vue'; +import SearchVideoGrid from '@/components/SearchVideoGrid.vue'; import { getFileTypeIcon, isFolder, isDirectoryFile } from '@/utils/fileTypeUtils'; import { usePaginationStore } from '@/stores/paginationStore'; import { usePageStateStore } from '@/stores/pageStateStore'; @@ -291,6 +222,7 @@ export default defineComponent({ components: { SearchSettingsModal, ActionRenderer, + SearchVideoGrid, IconHistory, IconBulb, IconFire, @@ -368,6 +300,14 @@ export default defineComponent({ } return searchResults.value[activeSource.value].slice(0, displayedCount.value); }); + + // 处理后的显示结果,为SearchVideoGrid组件准备数据 + const processedDisplayedResults = computed(() => { + return displayedResults.value.map(video => ({ + ...video, + fileType: isFolder(video) ? 'folder' : (isDirectoryFile(video) ? getFileTypeIcon(video.vod_name) : null) + })); + }); const hasMoreData = computed(() => { if (!activeSource.value) { @@ -646,31 +586,33 @@ export default defineComponent({ // 滚动事件处理 const handleScroll = (e) => { - // 获取真正的滚动容器(arco-scrollbar内部容器) - const rawTarget = e?.target || e?.srcElement; - const container = rawTarget?.closest ? rawTarget.closest('.arco-scrollbar-container') : rawTarget; - if (!container) return; - - const scrollHeight = container.scrollHeight - container.clientHeight; - const scrollTop = container.scrollTop; - - // 实时更新滚动位置 - scrollPosition.value = scrollTop; - - // 防抖保存滚动位置(使用更长的延迟避免过于频繁) - if (scrollSaveTimer) { - clearTimeout(scrollSaveTimer); - } - scrollSaveTimer = setTimeout(() => { - if (hasSearched.value && searchKeyword.value) { - debouncedSavePageState(); - console.log('🔄 [状态保存] 滚动位置变化,触发状态保存:', scrollTop); + // 使用SearchVideoGrid组件的方法获取滚动位置 + if (scrollbarRef.value?.getScrollTop && scrollbarRef.value?.getScrollContainer) { + const scrollTop = scrollbarRef.value.getScrollTop(); + const container = scrollbarRef.value.getScrollContainer(); + + if (!container) return; + + const scrollHeight = container.scrollHeight - container.clientHeight; + + // 实时更新滚动位置 + scrollPosition.value = scrollTop; + + // 防抖保存滚动位置(使用更长的延迟避免过于频繁) + if (scrollSaveTimer) { + clearTimeout(scrollSaveTimer); + } + scrollSaveTimer = setTimeout(() => { + if (hasSearched.value && searchKeyword.value) { + debouncedSavePageState(); + console.log('🔄 [状态保存] 滚动位置变化,触发状态保存:', scrollTop); + } + }, 1000); // 1秒防抖延迟,避免滚动时过于频繁保存 + + // 当滚动到距离底部50px以内时触发加载 + if (scrollHeight - scrollTop < 50 && hasMoreData.value && !loadingMore.value) { + loadMore(); } - }, 1000); // 1秒防抖延迟,避免滚动时过于频繁保存 - - // 当滚动到距离底部50px以内时触发加载 - if (scrollHeight - scrollTop < 50 && hasMoreData.value && !loadingMore.value) { - loadMore(); } }; @@ -711,6 +653,11 @@ export default defineComponent({ loadingMore.value = false; } }; + + // 处理SearchVideoGrid组件的load-more事件 + const handleLoadMore = () => { + loadMore(); + }; // 动态计算滚动区域高度 const updateScrollAreaHeight = () => { @@ -812,7 +759,17 @@ export default defineComponent({ const displayedResults = Math.min(displayedCount.value, totalResults); const sourceName = getSourceName(activeSource.value); + // 计算当前页数和总页数 + const currentPage = Math.ceil(displayedResults / pageSize.value) || 1; + const totalPages = Math.ceil(totalResults / pageSize.value) || 1; + let statsText = `搜索"${searchKeyword.value}":${sourceName} - 已显示${displayedResults}条,共${totalResults}条`; + + // 添加页数信息 + if (totalResults > 0) { + statsText += ` (第${currentPage}页/共${totalPages}页)`; + } + // 检查是否还有更多数据可以加载 const hasMore = hasMoreData.value; if (hasMore) { @@ -1085,9 +1042,8 @@ export default defineComponent({ // 保存滚动位置 const saveScrollPosition = () => { - const scrollContainer = scrollbarRef.value?.$el?.querySelector('.arco-scrollbar-container'); - if (scrollContainer) { - scrollPosition.value = scrollContainer.scrollTop; + if (scrollbarRef.value?.getScrollTop) { + scrollPosition.value = scrollbarRef.value.getScrollTop(); console.log('🔄 [滚动位置] 保存滚动位置:', scrollPosition.value); } }; @@ -1099,21 +1055,27 @@ export default defineComponent({ const delay = Math.min(100 * Math.pow(2, retryCount), 1000); // 指数退避,最大1秒 const attemptRestore = () => { - const scrollContainer = scrollbarRef.value?.$el?.querySelector('.arco-scrollbar-container'); - if (scrollContainer) { - // 检查容器是否有内容 - const hasContent = scrollContainer.scrollHeight > scrollContainer.clientHeight; - if (hasContent) { - scrollContainer.scrollTop = scrollPosition.value; - console.log('🔄 [滚动位置] 恢复滚动位置:', scrollPosition.value); - return true; + if (scrollbarRef.value?.scrollTo && scrollbarRef.value?.getScrollContainer) { + const scrollContainer = scrollbarRef.value.getScrollContainer(); + if (scrollContainer) { + // 检查容器是否有内容 + const hasContent = scrollContainer.scrollHeight > scrollContainer.clientHeight; + if (hasContent) { + scrollbarRef.value.scrollTo({ top: scrollPosition.value }); + console.log('🔄 [滚动位置] 恢复滚动位置:', scrollPosition.value); + return true; + } else if (retryCount < maxRetries) { + console.log(`🔄 [滚动位置] 容器内容未完全加载,${delay}ms后重试 (${retryCount + 1}/${maxRetries})`); + setTimeout(() => restoreScrollPosition(retryCount + 1), delay); + return false; + } } else if (retryCount < maxRetries) { - console.log(`🔄 [滚动位置] 容器内容未完全加载,${delay}ms后重试 (${retryCount + 1}/${maxRetries})`); + console.log(`🔄 [滚动位置] 滚动容器未找到,${delay}ms后重试 (${retryCount + 1}/${maxRetries})`); setTimeout(() => restoreScrollPosition(retryCount + 1), delay); return false; } } else if (retryCount < maxRetries) { - console.log(`🔄 [滚动位置] 滚动容器未找到,${delay}ms后重试 (${retryCount + 1}/${maxRetries})`); + console.log(`🔄 [滚动位置] SearchVideoGrid组件方法未就绪,${delay}ms后重试 (${retryCount + 1}/${maxRetries})`); setTimeout(() => restoreScrollPosition(retryCount + 1), delay); return false; } @@ -1356,6 +1318,7 @@ export default defineComponent({ errorStates, activeSource, displayedResults, + processedDisplayedResults, hasMoreData, loadingMore, scrollbarRef, @@ -1378,6 +1341,7 @@ export default defineComponent({ handleVideoClick, handleImageError, handleScroll, + handleLoadMore, handleActionClose, randomizeHotSearchTags, clearPageState, @@ -1706,221 +1670,7 @@ export default defineComponent({ font-size: 14px; } -/* 搜索结果滚动容器 */ -.search-scroll-container { - border-radius: 8px; - border: 1px solid var(--color-border-2); -} -.search-results-grid { - padding: 16px; -} - -/* 视频卡片样式 */ -.video-card-item { - width: 100%; -} - -.video-card { - background: var(--color-bg-2); - border-radius: 8px; - overflow: hidden; - cursor: pointer; - transition: all 0.3s ease; - border: 1px solid var(--color-border-2); - height: 100%; - display: flex; - flex-direction: column; -} - -.video-card:hover { - transform: translateY(-2px); - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1); - border-color: var(--color-primary-light-4); -} - -.video-poster { - position: relative; - width: 100%; - height: 200px; - overflow: hidden; - flex-shrink: 0; - background: var(--color-fill-2); - display: flex; - align-items: center; - justify-content: center; -} - -.video-poster img, -.video-poster-img { - width: 100%; - height: 100%; - object-fit: cover; - transition: transform 0.3s ease; -} - -.video-card:hover .video-poster img, -.video-card:hover .video-poster-img { - transform: scale(1.05); -} - -.folder-icon-container, -.file-icon-container { - display: flex; - align-items: center; - justify-content: center; - width: 100%; - height: 100%; - background: var(--color-fill-3); -} - -.folder-icon { - font-size: 48px; - color: var(--color-text-3); -} - -.play-overlay { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 50px; - height: 50px; - background: rgba(0, 0, 0, 0.7); - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - color: white; - font-size: 20px; - opacity: 0; - transition: opacity 0.3s ease; -} - -.video-card:hover .play-overlay { - opacity: 1; -} - -.action-badge { - position: absolute; - top: 8px; - right: 8px; - width: 24px; - height: 24px; - background: var(--color-warning-6); - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - color: white; - font-size: 12px; - z-index: 2; -} - -.video-info { - padding: 12px; - flex: 1; - display: flex; - flex-direction: column; - justify-content: space-between; -} - -.video-title { - margin: 0 0 8px 0; - font-size: 14px; - font-weight: 500; - color: var(--color-text-1); - line-height: 1.4; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.video-desc { - margin: 0 0 8px 0; - font-size: 12px; - color: var(--color-text-3); - line-height: 1.3; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.video-meta { - display: flex; - align-items: center; - gap: 8px; - font-size: 12px; - color: var(--color-text-3); - flex-wrap: wrap; -} - -.video-note { - background: var(--color-primary-light-1); - color: var(--color-primary-6); - padding: 2px 6px; - border-radius: 4px; - font-size: 11px; - white-space: nowrap; -} - -.video-year, -.video-area { - color: var(--color-text-3); -} - -.video-remarks-overlay { - position: absolute; - top: 4px; - right: 4px; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; - padding: 3px 6px; - border-radius: 4px; - font-size: 10px; - font-weight: 500; - max-width: 60%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); - backdrop-filter: blur(4px); - z-index: 2; -} - -.meta-item { - font-size: 11px; - color: var(--color-text-4); - background: var(--color-fill-2); - padding: 2px 6px; - border-radius: 4px; -} - -/* 加载更多和无更多数据提示 */ -.loading-container, -.no-more-data { - display: flex; - align-items: center; - justify-content: center; - gap: 8px; - padding: 20px; - color: var(--color-text-3); - font-size: 14px; -} - -.loading-container { - color: var(--color-primary-6); -} - -.loading-text { - margin-left: 8px; -} - -.bottom-spacer { - height: 20px; -} @@ -1967,11 +1717,7 @@ export default defineComponent({ margin-bottom: 0; } - .video-grid { - grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); - gap: 16px; - padding: 16px; - } + .search-header { padding: 0 16px; From 1cd91c1d2707c812dd48ec6d79e1f0c89050f97c Mon Sep 17 00:00:00 2001 From: Taois Date: Sun, 12 Oct 2025 13:04:25 +0800 Subject: [PATCH 176/199] =?UTF-8?q?feat:=E8=BE=B9=E6=A1=86=E6=95=88?= =?UTF-8?q?=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard/src/components/Header.vue | 75 ++++++++++++++++++----------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/dashboard/src/components/Header.vue b/dashboard/src/components/Header.vue index 9e868d5..9b34116 100644 --- a/dashboard/src/components/Header.vue +++ b/dashboard/src/components/Header.vue @@ -513,25 +513,37 @@ export default defineComponent({ .search-container { display: flex; align-items: center; - gap: 8px; + gap: 0; width: 100%; max-width: 450px; + border: 1px solid var(--color-border-2); + border-radius: 8px; + background: var(--color-bg-1); + padding: 4px; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05); + transition: all 0.2s ease; +} + +.search-container:hover { + border-color: var(--color-border-3); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); } /* 搜索框样式 */ .search-container :deep(.arco-input-search) { flex: 1; - border-radius: 8px; - background: var(--color-bg-1); - border: 1px solid var(--color-border-2); - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05); + border-radius: 4px; + background: transparent; + border: none; + box-shadow: none; transition: all 0.2s ease; cursor: pointer; } .search-container :deep(.arco-input-search:hover) { - border-color: var(--color-border-3); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + background: transparent; + border: none; + box-shadow: none; } /* 聚合搜索页面时的搜索框样式 */ @@ -552,59 +564,66 @@ export default defineComponent({ /* 搜索设置按钮样式 */ .search-settings-btn { - width: 36px !important; - height: 36px !important; - border-radius: 8px !important; - border: 1px solid var(--color-border-2) !important; - background: var(--color-bg-2) !important; + width: 32px !important; + height: 32px !important; + border-radius: 4px !important; + border: none !important; + background: transparent !important; color: var(--color-text-2) !important; transition: all 0.2s ease !important; flex-shrink: 0; + margin-left: 4px; } .search-settings-btn:hover { - background: var(--color-fill-3) !important; - border-color: var(--color-border-3) !important; + background: var(--color-fill-2) !important; + border: none !important; color: var(--color-text-1) !important; - transform: translateY(-1px); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !important; + transform: none; + box-shadow: none !important; } .search-settings-btn:active { - transform: translateY(0) !important; - background: var(--color-fill-4) !important; + transform: none !important; + background: var(--color-fill-3) !important; } /* 关闭搜索按钮样式 */ .close-search-btn { - width: 36px !important; - height: 36px !important; - border-radius: 8px !important; - border: 1px solid var(--color-border-2) !important; - background: var(--color-bg-2) !important; + width: 32px !important; + height: 32px !important; + border-radius: 4px !important; + border: none !important; + background: transparent !important; color: var(--color-text-2) !important; transition: all 0.2s ease !important; flex-shrink: 0; + margin-left: 4px; } .close-search-btn:hover { background: var(--color-danger-light-1) !important; - border-color: var(--color-danger-light-3) !important; + border: none !important; color: var(--color-danger-6) !important; - transform: translateY(-1px); - box-shadow: 0 2px 8px rgba(245, 63, 63, 0.15) !important; + transform: none; + box-shadow: none !important; } .close-search-btn:active { - transform: translateY(0) !important; + transform: none !important; background: var(--color-danger-light-2) !important; } -.search-container :deep(.arco-input-search:focus-within) { +.search-container:focus-within { border-color: var(--color-primary-6); box-shadow: 0 0 0 2px var(--color-primary-1); } +.search-container :deep(.arco-input-search:focus-within) { + border: none; + box-shadow: none; +} + .search-container :deep(.arco-input-wrapper) { border-radius: 8px; background: transparent; From a5de6a85f15e992f18bb85b5e21f406690fa31f4 Mon Sep 17 00:00:00 2001 From: Taois Date: Sun, 12 Oct 2025 13:12:56 +0800 Subject: [PATCH 177/199] =?UTF-8?q?feat:=E8=81=9A=E6=90=9C=E5=A4=87?= =?UTF-8?q?=E4=BB=BD=E8=BF=98=E5=8E=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard/src/services/backupService.js | 23 ++++++++++++++++++++++- dashboard/src/services/resetService.js | 13 ++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/dashboard/src/services/backupService.js b/dashboard/src/services/backupService.js index 10de923..b688b47 100644 --- a/dashboard/src/services/backupService.js +++ b/dashboard/src/services/backupService.js @@ -29,6 +29,11 @@ const BACKUP_KEYS = { LIVE_CONFIG_URL: 'drplayer_live_config_url', CURRENT_SITE: 'drplayer_current_site', + // 聚合搜索相关 + SEARCH_AGGREGATION_SETTINGS: 'searchAggregationSettings', // 聚合搜索源选择设置 + SEARCH_AGGREGATION_PAGE_STATE: 'pageState_searchAggregation', // 聚合搜索页面状态 + SEARCH_HISTORY: 'drplayer_search_history', // 搜索历史记录 + // 其他功能设置 SKIP_SETTINGS: 'skipSettings', PARSER_CONFIG: 'parserConfig', @@ -146,6 +151,8 @@ export const collectBackupData = () => { parserConfig: getLocalStorageData(BACKUP_KEYS.PARSER_CONFIG, {}), // 解析器数据 parsers: getLocalStorageData(BACKUP_KEYS.PARSERS, []), + // 聚合搜索设置 + searchAggregationSettings: getLocalStorageData(BACKUP_KEYS.SEARCH_AGGREGATION_SETTINGS, {}), // 侧边栏状态 sidebarCollapsed: getLocalStorageData(BACKUP_KEYS.SIDEBAR_COLLAPSED, false), // 页面状态 @@ -160,6 +167,10 @@ export const collectBackupData = () => { watchHistory: getLocalStorageData(BACKUP_KEYS.WATCH_HISTORY, []), // 历史页面数据(historyStore使用) histories: getLocalStorageData(BACKUP_KEYS.HISTORIES, []), + // 搜索历史记录 + searchHistory: getLocalStorageData(BACKUP_KEYS.SEARCH_HISTORY, []), + // 聚合搜索页面状态 + searchAggregationPageState: getLocalStorageData(BACKUP_KEYS.SEARCH_AGGREGATION_PAGE_STATE, {}), // 每日统计 dailyStats: getLocalStorageData(BACKUP_KEYS.DAILY_STATS, {}), // 周统计 @@ -283,6 +294,14 @@ export const restoreBackupData = (backupData) => { failedCount++ errors.push(`解析器数据`) } + } else if (key === 'searchAggregationSettings') { + // 特殊处理聚合搜索设置 + if (setLocalStorageData(BACKUP_KEYS.SEARCH_AGGREGATION_SETTINGS, value)) { + restoredCount++ + } else { + failedCount++ + errors.push(`聚合搜索设置`) + } } else { const storageKey = Object.values(BACKUP_KEYS).find(k => k.includes(key) || key.includes(k.split('_').pop())) if (storageKey) { @@ -312,7 +331,9 @@ export const restoreBackupData = (backupData) => { watchHistory: BACKUP_KEYS.WATCH_HISTORY, dailyStats: BACKUP_KEYS.DAILY_STATS, weeklyStats: BACKUP_KEYS.WEEKLY_STATS, - histories: BACKUP_KEYS.HISTORIES // 历史页面数据 + histories: BACKUP_KEYS.HISTORIES, // 历史页面数据 + searchHistory: BACKUP_KEYS.SEARCH_HISTORY, // 搜索历史记录 + searchAggregationPageState: BACKUP_KEYS.SEARCH_AGGREGATION_PAGE_STATE // 聚合搜索页面状态 } for (const [key, value] of Object.entries(backupData.userData)) { diff --git a/dashboard/src/services/resetService.js b/dashboard/src/services/resetService.js index 23324c1..0d2d01b 100644 --- a/dashboard/src/services/resetService.js +++ b/dashboard/src/services/resetService.js @@ -32,7 +32,8 @@ const DEFAULT_CONFIGS = { autoLive: false, secureDns: false, cspBypass: true, - referrerPolicy: 'no-referrer' + referrerPolicy: 'no-referrer', + searchAggregation: false // 聚合搜索功能默认关闭 }, // CSP配置默认值 @@ -50,6 +51,11 @@ const DEFAULT_CONFIGS = { // 页面状态默认值 pageState: {}, + // 聚合搜索设置默认值 + searchAggregationSettings: { + selectedSources: [] // 默认没有选中任何搜索源 + }, + // 侧边栏状态默认值 sidebarCollapsed: false } @@ -64,6 +70,11 @@ const CLEAR_DATA_KEYS = [ 'drplayer_weekly_stats', // 周统计 'drplayer_parsers', // 解析器数据 + // 聚合搜索相关 + 'searchAggregationSettings', // 聚合搜索源选择设置 + 'pageState_searchAggregation', // 聚合搜索页面状态 + 'drplayer_search_history', // 搜索历史记录 + // 站点数据 'siteStore', // 站点存储 'drplayer_config_url', // 配置地址 From 62cf48f3ac1d942287cec34e904baae905de956c Mon Sep 17 00:00:00 2001 From: Taois Date: Sun, 12 Oct 2025 13:26:06 +0800 Subject: [PATCH 178/199] fix:type error --- dashboard/src/components/SearchResults.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dashboard/src/components/SearchResults.vue b/dashboard/src/components/SearchResults.vue index fe91a06..266c493 100644 --- a/dashboard/src/components/SearchResults.vue +++ b/dashboard/src/components/SearchResults.vue @@ -116,7 +116,7 @@ const props = defineProps({ }, // 扩展参数,用于T4接口调用 extend: { - type: String, + type: [Object, String], default: '' }, // API URL,用于直接调用站点API From cb3c597fd53dc56f55d083f9ff9a6c3d2ec6b09e Mon Sep 17 00:00:00 2001 From: Taois Date: Sun, 12 Oct 2025 16:35:51 +0800 Subject: [PATCH 179/199] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/assets/icon_font/demo_index.html | 52 +- dashboard/src/assets/icon_font/iconfont.css | 14 +- dashboard/src/assets/icon_font/iconfont.js | 2 +- dashboard/src/assets/icon_font/iconfont.json | 14 + dashboard/src/assets/icon_font/iconfont.ttf | Bin 16152 -> 16776 bytes dashboard/src/assets/icon_font/iconfont.woff | Bin 10260 -> 10700 bytes dashboard/src/assets/icon_font/iconfont.woff2 | Bin 8692 -> 9128 bytes dashboard/src/components/Layout.vue | 17 +- .../downloader/AddDownloadTaskDialog.vue | 473 ++++++++++++++++ .../downloader/ChapterDetailsDialog.vue | 460 ++++++++++++++++ .../downloader/DownloadTaskItem.vue | 406 ++++++++++++++ .../components/downloader/NovelDownloader.vue | 299 ++++++++++ dashboard/src/router/index.js | 1 + dashboard/src/services/downloadService.js | 510 ++++++++++++++++++ dashboard/src/stores/downloadStore.js | 121 +++++ dashboard/src/views/BookGallery.vue | 16 +- dashboard/src/views/VideoDetail.vue | 146 ++++- 17 files changed, 2515 insertions(+), 16 deletions(-) create mode 100644 dashboard/src/components/downloader/AddDownloadTaskDialog.vue create mode 100644 dashboard/src/components/downloader/ChapterDetailsDialog.vue create mode 100644 dashboard/src/components/downloader/DownloadTaskItem.vue create mode 100644 dashboard/src/components/downloader/NovelDownloader.vue create mode 100644 dashboard/src/services/downloadService.js create mode 100644 dashboard/src/stores/downloadStore.js diff --git a/dashboard/src/assets/icon_font/demo_index.html b/dashboard/src/assets/icon_font/demo_index.html index cc7d52b..f84605f 100644 --- a/dashboard/src/assets/icon_font/demo_index.html +++ b/dashboard/src/assets/icon_font/demo_index.html @@ -54,6 +54,18 @@

    +
  • + +
    下载
    +
    &#xfcb9;
    +
  • + +
  • + +
    夸克
    +
    &#xe67d;
    +
  • +
  • folder
    @@ -264,9 +276,9 @@

    第一步:拷贝项目下面生成的 @font-face@font-face { font-family: 'iconfont'; - src: url('iconfont.woff2?t=1759693018509') format('woff2'), - url('iconfont.woff?t=1759693018509') format('woff'), - url('iconfont.ttf?t=1759693018509') format('truetype'); + src: url('iconfont.woff2?t=1760257974279') format('woff2'), + url('iconfont.woff?t=1760257974279') format('woff'), + url('iconfont.ttf?t=1760257974279') format('truetype'); }

    第二步:定义使用 iconfont 的样式

    @@ -292,6 +304,24 @@

    第三步:挑选相应图标并获取字体编码,应用于页面
      +
    • + +
      + 下载 +
      +
      .icon-xiazai +
      +
    • + +
    • + +
      + 夸克 +
      +
      .icon-kuake +
      +
    • +
    • @@ -607,6 +637,22 @@

      第二步:挑选相应图标并获取类名,应用于页面:
        +
      • + +
        下载
        +
        #icon-xiazai
        +
      • + +
      • + +
        夸克
        +
        #icon-kuake
        +
      • +
      • -&i|YBlE;adH_VYVw>m8a z!%d~Ta$O}QI<}=|;=vebIHwdkjfM}xnS8Mo*NhO$vZv{(e&-ZN1igF&Q8}Ty3MBv* z9dQtDXf9HxH%^CGxe?-g^|5}(6-Nf$u?@0%LKOtKhSzG8!lzTIbsPPy>4+-uiK7F? zx*mbv?UPp$;g4^(Udw~jkbUglVJ0hii{U+q{8U?Wq2gYi|Qv|)pBC1SpHef!cl=s=3}^C~6d#Ihr3 zW$kh)VcFocGw$rV5MZevbYiemnv5yC+Y| ztYK^&yp~n(W?-mMmfo#z1rsx7>-Jk$L zYbaZB_x`$L?T6l@G`0#FJ8#6~hOukpi0)i9A0zgQ*2zZPLCYg{^M{FCwBmScRH!uy7hy4n!UH3}ZG~F=|`{p+R0Q)cx z0$3%G*?aS`D7y@t{n^xVpb|9iSq5`nopevct12H!521u)qJk5i`?Kt6MIo$*FtPSh zBos&qCtiHYx_T7AQ?M6+Q}FV`-0Fn{BM?caWd|X2`k4|%4f=CN>N*5j=EMoGC-s%H@LJ{}Q#nlBoGOSNnMRzpwg_-p)5 z^is5LK02Y`*mFZww*bNk))Qnsd`<8^_vYQZ8&RDKSgWZ`iv#2 zs9w7g6)ZvUNygPAm!NF6z0jJBaoA@6cAti6;@UqYv? z4#p+5sY)LdToW9*bumXRUfg7{(knF0Ka)UCBT6}g^PL4n@v$|rL&PN>^~qr+{P2brQqE>)1>fFTGYxEwPQAL@D|G= zTWOd3nD4?#k=DW>gkT8RUI<2jZOsr0tY{hrk^Y28#LuA;5ey*kEdz*JSGCMrrIy$K z4DGS0QmPAJQ$?`5P2#uYRUI+~Y^=1Q;_bC9Q5OxTv)TV&`B7jw|2Nqw&IA5$bWog6 zciLAo_sVnfb{wz(;vM0282>|;D2+dBy;z@JwnB`;_?!L}1I-U!i$v@``-PvT&DLF< z@c#%Fg?A$>E|lAifAu5%cY_Cfrp)N;b^PBL%4t_1Aj}c?KLVsb@+zgbP9P-lc3d(C z2q2^_{Fikm0MMcv0RNky#e?_jt!AJH0)&E$lOVu}d_jbQFfY|q6lCbADL9~@iJ+kp zTCQA221it8?Ot(2z$II!T7?DhP7l3{X9unK4DXL3d+8U0mj`m6VaPCjy^KQME=5BkXDTbiL2NIYC90RH<^+YSnAh{4MXO)TvvqeuIXM8aHX$OwoGj9n!*ck4pDps zwu0|%ZZcV5fz(TJyF12~Eh(~mHRXv_Zg4m7KBGq-53H7D&#C-n%XshxyA-lxSP~d? S<@9p3tQPPD1tk=oOaK5A{**8P diff --git a/dashboard/src/components/Layout.vue b/dashboard/src/components/Layout.vue index 189d19b..869cf29 100644 --- a/dashboard/src/components/Layout.vue +++ b/dashboard/src/components/Layout.vue @@ -57,7 +57,7 @@
        -
        +
        @@ -104,6 +104,11 @@ export default defineComponent({ return route.path === '/search' || route.name === 'SearchAggregation'; }); + // 检测是否为下载管理页面 + const isDownloadManagerPage = computed(() => { + return route.path === '/download-manager' || route.name === 'DownloadManager'; + }); + const siderCollapsed = ref(false); const menuItems = ref([ {id: 1, name: '主页', icon: 'icon-zhuye', route: '/'}, @@ -113,6 +118,7 @@ export default defineComponent({ {id: 6, name: '解析', icon: 'icon-jiexi', route: '/parser'}, {id: 7, name: '收藏', icon: 'icon-shoucang', route: '/collection'}, {id: 8, name: '历史', icon: 'icon-lishi', route: '/history'}, + {id: 11, name: '下载', icon: 'icon-xiazai', route: '/download-manager'}, {id: 10, name: '测试', icon: 'icon-ceshi', route: '/action-test'}, {id: 9, name: '设置', icon: 'icon-shezhi', route: '/settings'} ]); @@ -143,7 +149,8 @@ export default defineComponent({ logoDesc, onClickMenuItem, onSiderCollapse, - isSearchPage + isSearchPage, + isDownloadManagerPage }; } }); @@ -223,6 +230,12 @@ export default defineComponent({ padding: 0; /* 移除padding,让搜索页面完全控制布局 */ } +/* 下载管理页面的内容包装器 - 移除滚动和padding,让组件自己控制布局 */ +.content-wrapper.download-manager-page { + overflow: hidden; + padding: 0; /* 移除padding,让下载管理页面完全控制布局 */ +} + /* 固定的底部 */ .fixed-footer { height: 48px; diff --git a/dashboard/src/components/downloader/AddDownloadTaskDialog.vue b/dashboard/src/components/downloader/AddDownloadTaskDialog.vue new file mode 100644 index 0000000..7bbf6de --- /dev/null +++ b/dashboard/src/components/downloader/AddDownloadTaskDialog.vue @@ -0,0 +1,473 @@ + + + + + \ No newline at end of file diff --git a/dashboard/src/components/downloader/ChapterDetailsDialog.vue b/dashboard/src/components/downloader/ChapterDetailsDialog.vue new file mode 100644 index 0000000..456988f --- /dev/null +++ b/dashboard/src/components/downloader/ChapterDetailsDialog.vue @@ -0,0 +1,460 @@ + + + + + \ No newline at end of file diff --git a/dashboard/src/components/downloader/DownloadTaskItem.vue b/dashboard/src/components/downloader/DownloadTaskItem.vue new file mode 100644 index 0000000..3d80ec8 --- /dev/null +++ b/dashboard/src/components/downloader/DownloadTaskItem.vue @@ -0,0 +1,406 @@ + + + + + \ No newline at end of file diff --git a/dashboard/src/components/downloader/NovelDownloader.vue b/dashboard/src/components/downloader/NovelDownloader.vue new file mode 100644 index 0000000..81298b6 --- /dev/null +++ b/dashboard/src/components/downloader/NovelDownloader.vue @@ -0,0 +1,299 @@ + + + + + \ No newline at end of file diff --git a/dashboard/src/router/index.js b/dashboard/src/router/index.js index 1060de2..f33142f 100644 --- a/dashboard/src/router/index.js +++ b/dashboard/src/router/index.js @@ -23,6 +23,7 @@ const routes = [ {path: '/settings', component: Settings, name: 'Settings'}, {path: '/collection', component: Collection, name: 'Collection'}, {path: '/book-gallery', component: BookGallery, name: 'BookGallery'}, + {path: '/download-manager', component: () => import('@/components/downloader/NovelDownloader.vue'), name: 'DownloadManager'}, {path: '/history', component: History, name: 'History'}, {path: '/parser', component: Parser, name: 'Parser'}, {path: '/action-test', component: ActionTest, name: 'ActionTest'}, diff --git a/dashboard/src/services/downloadService.js b/dashboard/src/services/downloadService.js new file mode 100644 index 0000000..3032368 --- /dev/null +++ b/dashboard/src/services/downloadService.js @@ -0,0 +1,510 @@ +import videoService from '../api/services/video.js' + +class DownloadService { + constructor() { + this.tasks = new Map() + this.isDownloading = false + this.currentTask = null + this.downloadQueue = [] + this.maxConcurrent = 3 + this.activeDownloads = new Set() + + // 从本地存储恢复任务 + this.loadTasksFromStorage() + + // 定期保存任务状态 + setInterval(() => { + this.saveTasksToStorage() + }, 5000) + } + + // 创建新的下载任务 + createTask(novelInfo, selectedChapters, settings = {}) { + const taskId = this.generateTaskId() + const task = { + id: taskId, + novelTitle: novelInfo.title, + novelId: novelInfo.id, + novelUrl: novelInfo.url, + totalChapters: selectedChapters.length, + completedChapters: 0, + failedChapters: 0, + status: 'pending', // pending, downloading, paused, completed, failed + progress: 0, + createdAt: Date.now(), + updatedAt: Date.now(), + settings: { + fileName: settings.fileName || novelInfo.title, + concurrent: settings.concurrent || 3, + retryCount: settings.retryCount || 3, + chapterInterval: settings.chapterInterval || 1000, + ...settings + }, + chapters: selectedChapters.map((chapter, index) => ({ + index, + name: chapter.name || `第${index + 1}章`, + url: chapter.url, + status: 'pending', // pending, downloading, completed, failed + progress: 0, + content: '', + size: 0, + error: null, + retryCount: 0, + startTime: null, + completeTime: null + })), + error: null, + downloadedSize: 0, + totalSize: 0, + startTime: null, + completeTime: null + } + + this.tasks.set(taskId, task) + this.saveTasksToStorage() + + return task + } + + // 开始下载任务 + async startTask(taskId) { + const task = this.tasks.get(taskId) + if (!task) { + throw new Error('任务不存在') + } + + if (task.status === 'downloading') { + return + } + + task.status = 'downloading' + task.startTime = task.startTime || Date.now() // 设置开始时间 + task.updatedAt = Date.now() + this.updateTask(task) + + // 添加到下载队列 + if (!this.downloadQueue.includes(taskId)) { + this.downloadQueue.push(taskId) + } + + // 开始处理队列 + this.processDownloadQueue() + } + + // 暂停下载任务 + pauseTask(taskId) { + const task = this.tasks.get(taskId) + if (!task) return + + task.status = 'paused' + task.updatedAt = Date.now() + this.updateTask(task) + + // 从队列中移除 + const queueIndex = this.downloadQueue.indexOf(taskId) + if (queueIndex > -1) { + this.downloadQueue.splice(queueIndex, 1) + } + + // 停止正在下载的章节 + this.activeDownloads.delete(taskId) + } + + // 取消下载任务 + cancelTask(taskId) { + const task = this.tasks.get(taskId) + if (!task) return + + this.pauseTask(taskId) + + // 重置所有下载中的章节状态 + task.chapters.forEach(chapter => { + if (chapter.status === 'downloading') { + chapter.status = 'pending' + chapter.progress = 0 + chapter.startTime = null + } + }) + + task.status = 'pending' + task.progress = 0 + this.updateTask(task) + } + + // 删除下载任务 + deleteTask(taskId) { + const task = this.tasks.get(taskId) + if (!task) return + + this.cancelTask(taskId) + this.tasks.delete(taskId) + this.saveTasksToStorage() + } + + // 重试失败的章节 + async retryChapter(taskId, chapterIndex) { + const task = this.tasks.get(taskId) + if (!task) return + + const chapter = task.chapters[chapterIndex] + if (!chapter) return + + chapter.status = 'pending' + chapter.error = null + chapter.retryCount = 0 + chapter.progress = 0 + + this.updateTask(task) + + // 如果任务正在下载,立即处理这个章节 + if (task.status === 'downloading') { + this.downloadChapter(task, chapter) + } + } + + // 处理下载队列 + async processDownloadQueue() { + if (this.downloadQueue.length === 0) return + + const taskId = this.downloadQueue[0] + const task = this.tasks.get(taskId) + + if (!task || task.status !== 'downloading') { + this.downloadQueue.shift() + this.processDownloadQueue() + return + } + + this.currentTask = task + await this.downloadTask(task) + + // 任务完成后处理下一个 + this.downloadQueue.shift() + this.currentTask = null + + if (this.downloadQueue.length > 0) { + setTimeout(() => this.processDownloadQueue(), 1000) + } + } + + // 下载单个任务 + async downloadTask(task) { + const pendingChapters = task.chapters.filter(ch => ch.status === 'pending') + + if (pendingChapters.length === 0) { + this.completeTask(task) + return + } + + // 并发下载章节 + const concurrent = Math.min(task.settings.concurrent, pendingChapters.length) + const downloadPromises = [] + + for (let i = 0; i < concurrent; i++) { + downloadPromises.push(this.downloadChaptersConcurrently(task)) + } + + await Promise.all(downloadPromises) + this.completeTask(task) + } + + // 并发下载章节 + async downloadChaptersConcurrently(task) { + while (task.status === 'downloading') { + const chapter = task.chapters.find(ch => ch.status === 'pending') + if (!chapter) break + + await this.downloadChapter(task, chapter) + + // 章节间隔 + if (task.settings.chapterInterval > 0) { + await this.sleep(task.settings.chapterInterval) + } + } + } + + // 下载单个章节 + async downloadChapter(task, chapter) { + if (chapter.status !== 'pending') return + + chapter.status = 'downloading' + chapter.startTime = Date.now() + chapter.progress = 0 + this.updateTask(task) + + try { + console.log(`开始下载章节: ${chapter.name}`) + + // 调用正确的T4播放接口获取章节内容 + const parseParams = { + play: chapter.url, + flag: task.settings.flag || '', + apiUrl: task.settings.apiUrl || '', + extend: task.settings.extend || '' + } + + const parseResult = await videoService.parseEpisodePlayUrl(task.settings.module, parseParams) + console.log('章节播放解析结果:', parseResult) + + // 解析小说内容 + let novelContent = null + if (parseResult.url && parseResult.url.startsWith('novel://')) { + const novelData = parseResult.url.replace('novel://', '') + novelContent = JSON.parse(novelData) + } else { + throw new Error('无法解析小说内容,返回的不是小说格式') + } + + // 更新章节内容和状态 + chapter.content = novelContent.content || '' + chapter.size = new Blob([chapter.content]).size + chapter.status = 'completed' + chapter.progress = 100 + chapter.completeTime = Date.now() + + task.completedChapters++ + task.downloadedSize += chapter.size + + console.log(`章节下载完成: ${chapter.name}`) + } catch (error) { + console.error('下载章节失败:', error) + + chapter.status = 'failed' + chapter.error = error.message + chapter.retryCount++ + task.failedChapters++ + + // 自动重试 + if (chapter.retryCount < task.settings.retryCount) { + console.log(`章节下载失败,准备重试 (${chapter.retryCount}/${task.settings.retryCount}): ${chapter.name}`) + await this.sleep(2000) + chapter.status = 'pending' + chapter.error = null + task.failedChapters-- + } else { + console.log(`章节下载失败,已达到最大重试次数: ${chapter.name}`) + } + } + + this.updateTaskProgress(task) + this.updateTask(task) + } + + // 构造章节URL + buildChapterUrl(novelUrl, chapterUrl) { + // 如果章节URL是完整的,直接使用 + if (chapterUrl.startsWith('http')) { + return chapterUrl + } + + // 如果是相对路径,拼接基础URL + if (chapterUrl.startsWith('/')) { + const baseUrl = new URL(novelUrl).origin + return baseUrl + chapterUrl + } + + // 其他情况,基于小说URL构造 + return `${novelUrl}/${chapterUrl}` + } + + // 解析小说内容(基于BookReader.vue的逻辑) + parseNovelContent(data) { + if (typeof data === 'string') { + // 如果是novel://协议的内容 + if (data.startsWith('novel://')) { + try { + const content = decodeURIComponent(data.substring(8)) + return content + } catch (error) { + return data + } + } + return data + } + + // 如果是对象,尝试提取文本内容 + if (typeof data === 'object') { + return data.content || data.text || JSON.stringify(data) + } + + return String(data) + } + + // 完成任务 + completeTask(task) { + const allCompleted = task.chapters.every(ch => ch.status === 'completed') + const hasFailed = task.chapters.some(ch => ch.status === 'failed') + + if (allCompleted) { + task.status = 'completed' + task.completeTime = Date.now() + } else if (hasFailed) { + task.status = 'failed' + } else { + task.status = 'paused' + } + + // 计算总大小 + task.totalSize = task.chapters.reduce((total, chapter) => total + (chapter.size || 0), 0) + + task.updatedAt = Date.now() + this.updateTask(task) + } + + // 更新任务进度 + updateTaskProgress(task) { + const completedCount = task.chapters.filter(ch => ch.status === 'completed').length + task.progress = Math.round((completedCount / task.totalChapters) * 100) + task.completedChapters = completedCount + task.failedChapters = task.chapters.filter(ch => ch.status === 'failed').length + + // 实时更新已下载大小和总大小 + task.downloadedSize = task.chapters + .filter(ch => ch.status === 'completed') + .reduce((total, chapter) => total + (chapter.size || 0), 0) + task.totalSize = task.chapters.reduce((total, chapter) => total + (chapter.size || 0), 0) + } + + // 更新任务 + updateTask(task) { + task.updatedAt = Date.now() + this.tasks.set(task.id, { ...task }) + + // 触发事件通知UI更新 + this.notifyTaskUpdate(task) + } + + // 导出为TXT文件 + exportToTxt(taskId) { + const task = this.tasks.get(taskId) + if (!task) return null + + const completedChapters = task.chapters + .filter(ch => ch.status === 'completed') + .sort((a, b) => a.index - b.index) + + if (completedChapters.length === 0) { + throw new Error('没有已完成的章节可以导出') + } + + let content = `${task.novelTitle}\n\n` + + completedChapters.forEach(chapter => { + content += `${chapter.name}\n\n` + content += `${chapter.content}\n\n` + content += '---\n\n' + }) + + const blob = new Blob([content], { type: 'text/plain;charset=utf-8' }) + const url = URL.createObjectURL(blob) + + const link = document.createElement('a') + link.href = url + link.download = `${task.settings.fileName}.txt` + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + + URL.revokeObjectURL(url) + + return { + fileName: `${task.settings.fileName}.txt`, + size: blob.size, + chapterCount: completedChapters.length + } + } + + // 获取所有任务 + getAllTasks() { + return Array.from(this.tasks.values()).sort((a, b) => b.createdAt - a.createdAt) + } + + // 根据状态过滤任务 + getTasksByStatus(status) { + return this.getAllTasks().filter(task => { + if (status === 'all') return true + if (status === 'downloaded') return task.status === 'completed' + if (status === 'downloading') return task.status === 'downloading' + if (status === 'failed') return task.status === 'failed' + if (status === 'pending') return task.status === 'pending' || task.status === 'paused' + return task.status === status + }) + } + + // 获取任务统计 + getTaskStats() { + const tasks = this.getAllTasks() + return { + total: tasks.length, + completed: tasks.filter(t => t.status === 'completed').length, + downloading: tasks.filter(t => t.status === 'downloading').length, + failed: tasks.filter(t => t.status === 'failed').length, + pending: tasks.filter(t => t.status === 'pending' || t.status === 'paused').length + } + } + + // 保存任务到本地存储 + saveTasksToStorage() { + try { + const tasksData = Array.from(this.tasks.entries()) + localStorage.setItem('novel_download_tasks', JSON.stringify(tasksData)) + } catch (error) { + console.error('保存下载任务失败:', error) + } + } + + // 从本地存储加载任务 + loadTasksFromStorage() { + try { + const tasksData = localStorage.getItem('novel_download_tasks') + if (tasksData) { + const tasks = JSON.parse(tasksData) + this.tasks = new Map(tasks) + + // 重置所有下载中的任务状态 + this.tasks.forEach(task => { + if (task.status === 'downloading') { + task.status = 'paused' + task.chapters.forEach(chapter => { + if (chapter.status === 'downloading') { + chapter.status = 'pending' + chapter.progress = 0 + chapter.startTime = null + } + }) + } + }) + } + } catch (error) { + console.error('加载下载任务失败:', error) + this.tasks = new Map() + } + } + + // 生成任务ID + generateTaskId() { + return 'task_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9) + } + + // 睡眠函数 + sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)) + } + + // 事件通知(可以扩展为EventEmitter) + notifyTaskUpdate(task) { + // 这里可以实现事件发布,通知UI组件更新 + if (this.onTaskUpdate) { + this.onTaskUpdate(task) + } + } + + // 设置任务更新回调 + setTaskUpdateCallback(callback) { + this.onTaskUpdate = callback + } +} + +// 创建单例实例 +export const downloadService = new DownloadService() +export default downloadService \ No newline at end of file diff --git a/dashboard/src/stores/downloadStore.js b/dashboard/src/stores/downloadStore.js new file mode 100644 index 0000000..a17e726 --- /dev/null +++ b/dashboard/src/stores/downloadStore.js @@ -0,0 +1,121 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' +import { downloadService } from '@/services/downloadService' + +export const useDownloadStore = defineStore('download', () => { + // 任务列表 + const tasks = ref([]) + + // 加载状态 + const loading = ref(false) + + // 从downloadService加载任务 + const loadTasks = () => { + tasks.value = downloadService.getAllTasks() + } + + // 保存任务到downloadService + const saveTasks = () => { + downloadService.saveTasksToStorage() + } + + // 添加下载任务 + const addTask = (taskData) => { + const task = downloadService.createTask(taskData) + loadTasks() // 重新加载任务列表 + return task + } + + // 开始任务 + const startTask = (taskId) => { + downloadService.startTask(taskId) + loadTasks() + } + + // 暂停任务 + const pauseTask = (taskId) => { + downloadService.pauseTask(taskId) + loadTasks() + } + + // 恢复任务 + const resumeTask = (taskId) => { + downloadService.startTask(taskId) + loadTasks() + } + + // 取消任务 + const cancelTask = (taskId) => { + downloadService.cancelTask(taskId) + loadTasks() + } + + // 删除任务 + const deleteTask = (taskId) => { + downloadService.deleteTask(taskId) + loadTasks() + } + + // 重试任务 + const retryTask = (taskId) => { + downloadService.startTask(taskId) + loadTasks() + } + + // 重试章节 + const retryChapter = (taskId, chapterIndex) => { + downloadService.retryChapter(taskId, chapterIndex) + loadTasks() + } + + // 导出任务 + const exportTask = (taskId) => { + return downloadService.exportToTxt(taskId) + } + + // 清理已完成的任务 + const clearCompleted = () => { + const completedTasks = tasks.value.filter(task => task.status === 'completed') + completedTasks.forEach(task => { + downloadService.deleteTask(task.id) + }) + loadTasks() + } + + // 计算属性 + const taskStats = computed(() => { + return downloadService.getTaskStats() + }) + + // 根据状态过滤任务 + const getTasksByStatus = (status) => { + return downloadService.getTasksByStatus(status) + } + + // 设置任务更新回调 + downloadService.setTaskUpdateCallback(() => { + loadTasks() + }) + + // 初始化时加载任务 + loadTasks() + + return { + tasks, + loading, + taskStats, + loadTasks, + saveTasks, + addTask, + startTask, + pauseTask, + resumeTask, + cancelTask, + deleteTask, + retryTask, + retryChapter, + exportTask, + clearCompleted, + getTasksByStatus + } +}) \ No newline at end of file diff --git a/dashboard/src/views/BookGallery.vue b/dashboard/src/views/BookGallery.vue index 11f2580..2cbbad9 100644 --- a/dashboard/src/views/BookGallery.vue +++ b/dashboard/src/views/BookGallery.vue @@ -41,6 +41,12 @@ 导出收藏 + + + 下载管理 + + + +
        @@ -313,6 +351,8 @@ import EpisodeSelector from '@/components/players/EpisodeSelector.vue' import BookReader from '@/components/readers/BookReader.vue' import ComicReader from '@/components/readers/ComicReader.vue' import ActionDialog from '@/components/actions/ActionDialog.vue' +import AddDownloadTaskDialog from '@/components/downloader/AddDownloadTaskDialog.vue' +import downloadService from '@/services/downloadService' import { IconLeft, IconPlayArrow, @@ -323,7 +363,8 @@ import { IconBook, IconImage, IconRefresh, - IconSound + IconSound, + IconDownload } from '@arco-design/web-vue/es/icon' const route = useRoute() @@ -393,6 +434,9 @@ const initialQuality = ref('') const needsParsing = ref(false) const parseData = ref(null) const selectedParser = ref(null) + +// 下载相关数据 +const showDownloadTaskDialog = ref(false) const availableParsers = ref([]) // 小说阅读器相关 @@ -559,6 +603,13 @@ const isNovelContent = computed(() => { return parsedNovelContent.value !== null }) +// 判断当前类型是否为小说(用于下载按钮显示) +const isNovelType = computed(() => { + const siteName = currentSiteInfo.value?.name || '' + // 通过站源名称标识判断,或者通过内容判断 + return siteName.includes('[书]') || isNovelContent.value +}) + // 判断当前内容是否为漫画 const isComicContent = computed(() => { return showComicReader.value @@ -2086,6 +2137,67 @@ const copyPlayUrl = async () => { } } +// 显示下载对话框 +const showDownloadDialog = () => { + showDownloadTaskDialog.value = true +} + +// 关闭下载对话框 +const closeDownloadDialog = () => { + showDownloadTaskDialog.value = false +} + +// 确认下载任务 +const handleDownloadConfirm = async (taskData) => { + try { + console.log('确认下载任务:', taskData) + + // 准备小说信息 + const novelInfo = { + title: taskData.novelDetail.vod_name, + id: taskData.novelDetail.vod_id, + url: currentEpisodeUrl.value || '', + author: taskData.novelDetail.vod_actor || '未知', + description: taskData.novelDetail.vod_content || '' + } + + // 准备选中的章节信息 + const selectedChapters = taskData.selectedChapters.map(index => { + const chapter = taskData.chapters[index] + return { + name: chapter.name || `第${index + 1}章`, + url: chapter.url || chapter.vod_play_url || '', + index: index + } + }) + + // 准备下载设置,包含必要的站点信息 + const downloadSettings = { + ...taskData.settings, + module: currentActiveSiteInfo.value?.key || '', + apiUrl: currentActiveSiteInfo.value?.api || '', + extend: currentActiveSiteInfo.value?.ext || '', + flag: playRoutes.value[currentRoute.value]?.name || '' + } + + console.log('下载设置:', downloadSettings) + console.log('当前站点信息:', currentActiveSiteInfo.value) + + // 创建下载任务 + const task = downloadService.createTask(novelInfo, selectedChapters, downloadSettings) + + // 开始下载任务 + await downloadService.startTask(task.id) + + Message.success(`下载任务已创建:${taskData.novelDetail.vod_name}`) + closeDownloadDialog() + + } catch (error) { + console.error('创建下载任务失败:', error) + Message.error('创建下载任务失败:' + error.message) + } +} + // 监听路由变化(包括参数和查询参数) watch(() => [route.params.id, route.query], () => { if (route.params.id) { @@ -2648,7 +2760,9 @@ onUnmounted(() => { border: 1px solid #dee2e6; } -.play-actions { +.play-actions, +.action-buttons, +.action-buttons-row { display: flex; gap: 16px; align-items: center; @@ -2656,6 +2770,11 @@ onUnmounted(() => { justify-content: center; } +.download-row { + margin-top: 16px; + justify-content: flex-start !important; +} + .play-btn { min-width: 140px; height: 44px; @@ -2682,6 +2801,21 @@ onUnmounted(() => { transform: translateY(-1px); } +.download-btn { + min-width: 140px; + height: 44px; + border-radius: 8px; + font-weight: 600; + font-size: 16px; + box-shadow: 0 4px 12px rgba(0, 180, 42, 0.3); + transition: all 0.2s ease; +} + +.download-btn:hover { + transform: translateY(-2px); + box-shadow: 0 6px 16px rgba(0, 180, 42, 0.4); +} + .no-play-section { text-align: center; padding: 40px; From 0e5f5951d6c3cdf36ce877c5f86a3e8f917956af Mon Sep 17 00:00:00 2001 From: Taois Date: Sun, 12 Oct 2025 17:22:20 +0800 Subject: [PATCH 180/199] =?UTF-8?q?feat:=20=E4=B9=A6=E7=94=BB=E6=9F=9C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=9C=AC=E5=9C=B0=E5=9B=BE=E4=B9=A6=E7=9A=84?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=92=8C=E9=98=85=E8=AF=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../downloader/DownloadTaskItem.vue | 41 +- .../components/downloader/NovelDownloader.vue | 4 +- .../src/components/readers/BookmarkDialog.vue | 395 ++++++++++++ dashboard/src/router/index.js | 1 + dashboard/src/services/downloadService.js | 32 + dashboard/src/services/localBookService.js | 533 +++++++++++++++ dashboard/src/stores/downloadStore.js | 88 ++- dashboard/src/views/BookGallery.vue | 327 +++++++++- dashboard/src/views/LocalBookReader.vue | 610 ++++++++++++++++++ dashboard/src/views/VideoDetail.vue | 3 +- 10 files changed, 1987 insertions(+), 47 deletions(-) create mode 100644 dashboard/src/components/readers/BookmarkDialog.vue create mode 100644 dashboard/src/services/localBookService.js create mode 100644 dashboard/src/views/LocalBookReader.vue diff --git a/dashboard/src/components/downloader/DownloadTaskItem.vue b/dashboard/src/components/downloader/DownloadTaskItem.vue index 3d80ec8..95db6ab 100644 --- a/dashboard/src/components/downloader/DownloadTaskItem.vue +++ b/dashboard/src/components/downloader/DownloadTaskItem.vue @@ -110,12 +110,29 @@ - -