소스 검색

Merge branch 'dev' of http://192.168.1.245:11111/shanming/onlineEducation-front into dev

zhangsq 8 달 전
부모
커밋
82637fd968

+ 279 - 18
package-lock.json

@@ -14,6 +14,12 @@
 				"@ant-design/icons-vue": "6.1.0",
 				"@antv/g2plot": "2.4.28",
 				"@chenfengyuan/vue-qrcode": "2.0.0",
+				"@codemirror/lang-css": "^6.3.1",
+				"@codemirror/lang-html": "^6.4.9",
+				"@codemirror/lang-javascript": "^6.2.4",
+				"@codemirror/lang-json": "^6.0.2",
+				"@codemirror/lang-xml": "^6.1.0",
+				"@codemirror/theme-one-dark": "^6.1.3",
 				"@highlightjs/vue-plugin": "2.1.0",
 				"@tinymce/tinymce-vue": "5.0.0",
 				"@vue-office/docx": "^1.2.0",
@@ -21,7 +27,6 @@
 				"@vue-office/pdf": "1.2.0",
 				"ant-design-vue": "3.2.14",
 				"axios": "1.1.3",
-				"codemirror-editor-vue3": "^2.8.0",
 				"cropperjs": "1.5.12",
 				"dayjs": "1.11.7",
 				"echarts": "5.4.0",
@@ -49,6 +54,7 @@
 				"tinymce": "^6.2.0",
 				"video.js": "^8.23.3",
 				"vue": "3.2.44",
+				"vue-codemirror": "^6.1.1",
 				"vue-cropper": "1.0.5",
 				"vue-demi": "0.13.11",
 				"vue-i18n": "9.2.2",
@@ -988,6 +994,156 @@
 				"vue": "^3.0.0"
 			}
 		},
+		"node_modules/@codemirror/autocomplete": {
+			"version": "6.18.6",
+			"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz",
+			"integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==",
+			"dependencies": {
+				"@codemirror/language": "^6.0.0",
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.17.0",
+				"@lezer/common": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/commands": {
+			"version": "6.8.1",
+			"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz",
+			"integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==",
+			"dependencies": {
+				"@codemirror/language": "^6.0.0",
+				"@codemirror/state": "^6.4.0",
+				"@codemirror/view": "^6.27.0",
+				"@lezer/common": "^1.1.0"
+			}
+		},
+		"node_modules/@codemirror/lang-css": {
+			"version": "6.3.1",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz",
+			"integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==",
+			"dependencies": {
+				"@codemirror/autocomplete": "^6.0.0",
+				"@codemirror/language": "^6.0.0",
+				"@codemirror/state": "^6.0.0",
+				"@lezer/common": "^1.0.2",
+				"@lezer/css": "^1.1.7"
+			}
+		},
+		"node_modules/@codemirror/lang-html": {
+			"version": "6.4.9",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.9.tgz",
+			"integrity": "sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==",
+			"dependencies": {
+				"@codemirror/autocomplete": "^6.0.0",
+				"@codemirror/lang-css": "^6.0.0",
+				"@codemirror/lang-javascript": "^6.0.0",
+				"@codemirror/language": "^6.4.0",
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.17.0",
+				"@lezer/common": "^1.0.0",
+				"@lezer/css": "^1.1.0",
+				"@lezer/html": "^1.3.0"
+			}
+		},
+		"node_modules/@codemirror/lang-javascript": {
+			"version": "6.2.4",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.4.tgz",
+			"integrity": "sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==",
+			"dependencies": {
+				"@codemirror/autocomplete": "^6.0.0",
+				"@codemirror/language": "^6.6.0",
+				"@codemirror/lint": "^6.0.0",
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.17.0",
+				"@lezer/common": "^1.0.0",
+				"@lezer/javascript": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/lang-json": {
+			"version": "6.0.2",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz",
+			"integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==",
+			"dependencies": {
+				"@codemirror/language": "^6.0.0",
+				"@lezer/json": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/lang-xml": {
+			"version": "6.1.0",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.1.0.tgz",
+			"integrity": "sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==",
+			"dependencies": {
+				"@codemirror/autocomplete": "^6.0.0",
+				"@codemirror/language": "^6.4.0",
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.0.0",
+				"@lezer/common": "^1.0.0",
+				"@lezer/xml": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/language": {
+			"version": "6.11.1",
+			"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.1.tgz",
+			"integrity": "sha512-5kS1U7emOGV84vxC+ruBty5sUgcD0te6dyupyRVG2zaSjhTDM73LhVKUtVwiqSe6QwmEoA4SCiU8AKPFyumAWQ==",
+			"dependencies": {
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.23.0",
+				"@lezer/common": "^1.1.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.0.0",
+				"style-mod": "^4.0.0"
+			}
+		},
+		"node_modules/@codemirror/lint": {
+			"version": "6.8.5",
+			"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz",
+			"integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==",
+			"dependencies": {
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.35.0",
+				"crelt": "^1.0.5"
+			}
+		},
+		"node_modules/@codemirror/search": {
+			"version": "6.5.11",
+			"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.11.tgz",
+			"integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==",
+			"peer": true,
+			"dependencies": {
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.0.0",
+				"crelt": "^1.0.5"
+			}
+		},
+		"node_modules/@codemirror/state": {
+			"version": "6.5.2",
+			"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz",
+			"integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
+			"dependencies": {
+				"@marijn/find-cluster-break": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/theme-one-dark": {
+			"version": "6.1.3",
+			"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz",
+			"integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==",
+			"dependencies": {
+				"@codemirror/language": "^6.0.0",
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.0.0",
+				"@lezer/highlight": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/view": {
+			"version": "6.37.2",
+			"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.37.2.tgz",
+			"integrity": "sha512-XD3LdgQpxQs5jhOOZ2HRVT+Rj59O4Suc7g2ULvZ+Yi8eCkickrkZ5JFuoDhs2ST1mNI5zSsNYgR3NGa4OUrbnw==",
+			"dependencies": {
+				"@codemirror/state": "^6.5.0",
+				"crelt": "^1.0.6",
+				"style-mod": "^4.1.0",
+				"w3c-keyname": "^2.2.4"
+			}
+		},
 		"node_modules/@ctrl/tinycolor": {
 			"version": "3.6.1",
 			"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
@@ -1641,6 +1797,77 @@
 				"@jridgewell/sourcemap-codec": "^1.4.14"
 			}
 		},
+		"node_modules/@lezer/common": {
+			"version": "1.2.3",
+			"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz",
+			"integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA=="
+		},
+		"node_modules/@lezer/css": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.2.1.tgz",
+			"integrity": "sha512-2F5tOqzKEKbCUNraIXc0f6HKeyKlmMWJnBB0i4XW6dJgssrZO/YlZ2pY5xgyqDleqqhiNJ3dQhbrV2aClZQMvg==",
+			"dependencies": {
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.3.0"
+			}
+		},
+		"node_modules/@lezer/highlight": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz",
+			"integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==",
+			"dependencies": {
+				"@lezer/common": "^1.0.0"
+			}
+		},
+		"node_modules/@lezer/html": {
+			"version": "1.3.10",
+			"resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.10.tgz",
+			"integrity": "sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==",
+			"dependencies": {
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.0.0"
+			}
+		},
+		"node_modules/@lezer/javascript": {
+			"version": "1.5.1",
+			"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.1.tgz",
+			"integrity": "sha512-ATOImjeVJuvgm3JQ/bpo2Tmv55HSScE2MTPnKRMRIPx2cLhHGyX2VnqpHhtIV1tVzIjZDbcWQm+NCTF40ggZVw==",
+			"dependencies": {
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.1.3",
+				"@lezer/lr": "^1.3.0"
+			}
+		},
+		"node_modules/@lezer/json": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz",
+			"integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==",
+			"dependencies": {
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.0.0"
+			}
+		},
+		"node_modules/@lezer/lr": {
+			"version": "1.4.2",
+			"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz",
+			"integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==",
+			"dependencies": {
+				"@lezer/common": "^1.0.0"
+			}
+		},
+		"node_modules/@lezer/xml": {
+			"version": "1.0.6",
+			"resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.6.tgz",
+			"integrity": "sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==",
+			"dependencies": {
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.0.0"
+			}
+		},
 		"node_modules/@ljharb/resumer": {
 			"version": "0.0.1",
 			"resolved": "https://registry.npmjs.org/@ljharb/resumer/-/resumer-0.0.1.tgz",
@@ -1665,6 +1892,11 @@
 				"node": ">= 0.4"
 			}
 		},
+		"node_modules/@marijn/find-cluster-break": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz",
+			"integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="
+		},
 		"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
 			"version": "5.1.1-v1",
 			"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
@@ -3524,24 +3756,18 @@
 			}
 		},
 		"node_modules/codemirror": {
-			"version": "5.65.19",
-			"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.19.tgz",
-			"integrity": "sha512-+aFkvqhaAVr1gferNMuN8vkTSrWIFvzlMV9I2KBLCWS2WpZ2+UAkZjlMZmEuT+gcXTi6RrGQCkWq1/bDtGqhIA==",
-			"license": "MIT"
-		},
-		"node_modules/codemirror-editor-vue3": {
-			"version": "2.8.0",
-			"resolved": "https://registry.npmjs.org/codemirror-editor-vue3/-/codemirror-editor-vue3-2.8.0.tgz",
-			"integrity": "sha512-ebYGNhBpLmQNLguXzNyMMkn6K8v3lcS5/Ncvdn6YS4bLGEHE67MfsJIS/WV0L7I6WavUuFlY/Rs/AJKChIwSwg==",
-			"license": "MIT",
+			"version": "6.0.2",
+			"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz",
+			"integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==",
+			"peer": true,
 			"dependencies": {
-				"codemirror": "^5",
-				"diff-match-patch": "^1.0.5"
-			},
-			"peerDependencies": {
-				"codemirror": "^5",
-				"diff-match-patch": "^1.0.5",
-				"vue": "^3.x"
+				"@codemirror/autocomplete": "^6.0.0",
+				"@codemirror/commands": "^6.0.0",
+				"@codemirror/language": "^6.0.0",
+				"@codemirror/lint": "^6.0.0",
+				"@codemirror/search": "^6.0.0",
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.0.0"
 			}
 		},
 		"node_modules/color-convert": {
@@ -3636,6 +3862,11 @@
 				"url": "https://opencollective.com/core-js"
 			}
 		},
+		"node_modules/crelt": {
+			"version": "1.0.6",
+			"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
+			"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="
+		},
 		"node_modules/cropperjs": {
 			"version": "1.5.12",
 			"resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.5.12.tgz",
@@ -9014,6 +9245,11 @@
 				"vue": "3.2.44"
 			}
 		},
+		"node_modules/snowy-form-design/node_modules/codemirror": {
+			"version": "5.65.19",
+			"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.19.tgz",
+			"integrity": "sha512-+aFkvqhaAVr1gferNMuN8vkTSrWIFvzlMV9I2KBLCWS2WpZ2+UAkZjlMZmEuT+gcXTi6RrGQCkWq1/bDtGqhIA=="
+		},
 		"node_modules/snowy-form-design/node_modules/codemirror-editor-vue3": {
 			"version": "2.3.0",
 			"resolved": "https://registry.npmjs.org/codemirror-editor-vue3/-/codemirror-editor-vue3-2.3.0.tgz",
@@ -9265,6 +9501,11 @@
 			"dev": true,
 			"license": "MIT"
 		},
+		"node_modules/style-mod": {
+			"version": "4.1.2",
+			"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz",
+			"integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw=="
+		},
 		"node_modules/supports-color": {
 			"version": "7.2.0",
 			"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -10226,6 +10467,21 @@
 				"@vue/shared": "3.2.44"
 			}
 		},
+		"node_modules/vue-codemirror": {
+			"version": "6.1.1",
+			"resolved": "https://registry.npmjs.org/vue-codemirror/-/vue-codemirror-6.1.1.tgz",
+			"integrity": "sha512-rTAYo44owd282yVxKtJtnOi7ERAcXTeviwoPXjIc6K/IQYUsoDkzPvw/JDFtSP6T7Cz/2g3EHaEyeyaQCKoDMg==",
+			"dependencies": {
+				"@codemirror/commands": "6.x",
+				"@codemirror/language": "6.x",
+				"@codemirror/state": "6.x",
+				"@codemirror/view": "6.x"
+			},
+			"peerDependencies": {
+				"codemirror": "6.x",
+				"vue": "3.x"
+			}
+		},
 		"node_modules/vue-cropper": {
 			"version": "1.0.5",
 			"resolved": "https://registry.npmjs.org/vue-cropper/-/vue-cropper-1.0.5.tgz",
@@ -10665,6 +10921,11 @@
 			"integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==",
 			"license": "MIT"
 		},
+		"node_modules/w3c-keyname": {
+			"version": "2.2.8",
+			"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
+			"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
+		},
 		"node_modules/warning": {
 			"version": "4.0.3",
 			"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",

+ 7 - 1
package.json

@@ -23,6 +23,12 @@
 		"@ant-design/icons-vue": "6.1.0",
 		"@antv/g2plot": "2.4.28",
 		"@chenfengyuan/vue-qrcode": "2.0.0",
+		"@codemirror/lang-css": "^6.3.1",
+		"@codemirror/lang-html": "^6.4.9",
+		"@codemirror/lang-javascript": "^6.2.4",
+		"@codemirror/lang-json": "^6.0.2",
+		"@codemirror/lang-xml": "^6.1.0",
+		"@codemirror/theme-one-dark": "^6.1.3",
 		"@highlightjs/vue-plugin": "2.1.0",
 		"@tinymce/tinymce-vue": "5.0.0",
 		"@vue-office/docx": "^1.2.0",
@@ -30,7 +36,6 @@
 		"@vue-office/pdf": "1.2.0",
 		"ant-design-vue": "3.2.14",
 		"axios": "1.1.3",
-		"codemirror-editor-vue3": "^2.8.0",
 		"cropperjs": "1.5.12",
 		"dayjs": "1.11.7",
 		"echarts": "5.4.0",
@@ -58,6 +63,7 @@
 		"tinymce": "^6.2.0",
 		"video.js": "^8.23.3",
 		"vue": "3.2.44",
+		"vue-codemirror": "^6.1.1",
 		"vue-cropper": "1.0.5",
 		"vue-demi": "0.13.11",
 		"vue-i18n": "9.2.2",

+ 1 - 1
src/api/myResource/file.js

@@ -76,7 +76,7 @@ export const batchDeleteRecoveryFile = (p) => request('recoveryfile/batchdelete'
  * 文件公共接口
  */
 // 文件预览
-export const getFilePreview = (p) => request('filetransfer/preview', p, 'get')
+export const getFilePreview = (p) => request('filetransfer/preview', p, 'get', { skipCodeCheck: true })
 // 文件修改
 export const modifyFileContent = (p) => request('file/update', p, 'post')
 

BIN
src/assets/images/myResource/audio/wave.gif


+ 6 - 16
src/libs/globalFunction/file.js

@@ -74,12 +74,6 @@ const fileFunction = {
 	 * @returns {string}  文件下载路径
 	 */
 	getDownloadFilePath(row) {
-		console.log(
-			'获取下载地址',
-			`${config.baseContext}/filetransfer/downloadfile?userFileId=${row.userFileId}&shareBatchNum=${
-				row.shareBatchNum == null ? '' : row.shareBatchNum
-			}&extractionCode=${row.extractionCode == null ? '' : row.extractionCode}`
-		)
 		return `${config.baseContext}/filetransfer/downloadfile?userFileId=${row.userFileId}&shareBatchNum=${
 			row.shareBatchNum == null ? '' : row.shareBatchNum
 		}&extractionCode=${row.extractionCode == null ? '' : row.extractionCode}`
@@ -213,7 +207,7 @@ const fileFunction = {
 		console.log('handleImgPreview--', router.currentRoute.value.query.fileType)
 		// 图片分类下 - 传递整个页面的图片列表;非图片分类下 - 由单个图片构建图片列表
 		const imgList =
-			router.currentRoute.value.query.fileType && Number(router.currentRoute.value.query.fileType.fileType) === 1
+			router.currentRoute.value.query.fileType && Number(router.currentRoute.value.query.fileType) === 1
 				? imgInfoList.map((item) => {
 						return {
 							...item,
@@ -229,7 +223,7 @@ const fileFunction = {
 						}
 				  ]
 		const defaultIndex =
-			router.currentRoute.value.query.fileType && Number(router.currentRoute.value.query.fileType.fileType) === 1
+			router.currentRoute.value.query.fileType && Number(router.currentRoute.value.query.fileType) === 1
 				? currentIndex
 				: 0
 
@@ -244,8 +238,7 @@ const fileFunction = {
 	handleVideoPreview(currentIndex, videoInfo = {}, videoInfoList = []) {
 		// 视频分类下 - 传递整个页面的视频列表;非视频分类下 - 由单个视频构建视频列表
 		const videoList =
-			router.currentRoute.value.query.fileType.fileType &&
-			Number(router.currentRoute.value.query.fileType.fileType) === 3
+			router.currentRoute.value.query.fileType && Number(router.currentRoute.value.query.fileType) === 3
 				? videoInfoList.map((item) => {
 						return {
 							...item,
@@ -261,8 +254,7 @@ const fileFunction = {
 						}
 				  ]
 		const defaultIndex =
-			router.currentRoute.value.query.fileType.fileType &&
-			Number(router.currentRoute.value.query.fileType.fileType) === 3
+			router.currentRoute.value.query.fileType && Number(router.currentRoute.value.query.fileType) === 3
 				? currentIndex
 				: 0
 		fileOperationPlugins.openBox.videoPreview({ videoList, defaultIndex })
@@ -276,7 +268,7 @@ const fileFunction = {
 	handleAudioPreview(currentIndex, audioInfo = {}, audioInfoList = []) {
 		// 音频分类下 - 传递整个页面的音频列表;非音频分类下 - 由单个音频构建音频列表
 		const audioList =
-			router.currentRoute.value.query.fileType && Number(router.currentRoute.value.query.fileType.fileType) === 4
+			router.currentRoute.value.query.fileType && Number(router.currentRoute.value.query.fileType) === 4
 				? audioInfoList.map((item) => {
 						return {
 							...item,
@@ -292,7 +284,7 @@ const fileFunction = {
 						}
 				  ]
 		const defaultIndex =
-			router.currentRoute.value.query.fileType && Number(router.currentRoute.value.query.fileType.fileType) === 4
+			router.currentRoute.value.query.fileType && Number(router.currentRoute.value.query.fileType) === 4
 				? currentIndex
 				: 0
 		fileOperationPlugins.openBox.audioPreview({ audioList, defaultIndex })
@@ -306,7 +298,6 @@ const fileFunction = {
 	 */
 	handleFileNameClick(row, currentIndex, fileList = []) {
 		const myResourceStore = useMyResourceStore()
-		console.log('查看')
 		// 如果当前文件在回收站中,则不允许预览
 		if (row.deleteFlag !== undefined && row.deleteFlag !== 0) {
 			return false
@@ -338,7 +329,6 @@ const fileFunction = {
 				// 	}
 				// })
 			} else if (Number(router.currentRoute.value.query.fileType) !== 6) {
-				console.log('打开网盘文件夹', row)
 				// 回收站页面不允许打开文件夹
 				// 网盘页面
 				myResourceStore.changeQuery({

+ 0 - 2
src/store/myResource.js

@@ -67,7 +67,6 @@ export const useMyResourceStore = defineStore('myResource', {
 		 * @param {string} data 文件展示模式
 		 */
 		changeFileModel(data) {
-			console.log('改变文件展示模式', data)
 			localStorage.setItem('qiwen_file_model', data)
 			this.fileModel = data
 		},
@@ -85,7 +84,6 @@ export const useMyResourceStore = defineStore('myResource', {
 		 * @param {array} data 批量操作模式下,被选中的文件列表
 		 */
 		changeSelectedFiles(data) {
-			console.log('批量操作模式下,被选中的文件列表', data)
 			this.selectedFiles = data
 		},
 		/**

+ 6 - 0
src/utils/reSourceRequest.js

@@ -87,6 +87,12 @@ service.interceptors.response.use(
 				return
 			}
 		}
+
+		// 如果设置了skipCodeCheck参数,则直接返回响应数据
+		if (response.config.skipCodeCheck) {
+			return Promise.resolve(response.data)
+		}
+
 		const data = response.data
 		const code = data.code
 		if (reloadCodes.includes(code)) {

+ 0 - 10
src/views/myResource/common/FileTable.vue

@@ -164,14 +164,6 @@
 				showOverflowTooltip: true
 			}
 		]
-		console.log(
-			'selectedColumnList: ',
-			selectedColumnList.value,
-			' screenWidth ',
-			screenWidth.value,
-			' screenWidth.value > 768 ',
-			screenWidth.value > 768
-		)
 		// if (![0, 8].includes(Number(route.query.fileType)) && routeName.value !== 'Share' && screenWidth.value > 768) {
 		if (
 			![0, 8].includes(Number(myResourceStore.getQuery.fileType ? myResourceStore.getQuery.fileType : 0)) &&
@@ -362,10 +354,8 @@
 	}
 
 	const onSelectChange = (changableRowKeys) => {
-		console.log('selectedRowKeys changed: ', changableRowKeys)
 		selectedRowKeys.value = changableRowKeys
 		const list = changableRowKeys.map((id) => props.fileList.find((file) => file.userFileId === id)).filter(Boolean)
-		console.log('list', list)
 		myResourceStore.changeSelectedFiles(list)
 		myResourceStore.changeIsBatchOperation(changableRowKeys.length !== 0)
 	}

+ 312 - 281
src/views/myResource/file/box/audioPreview/BoxMask.vue

@@ -1,222 +1,241 @@
 <template>
-	<a-modal
-		class="audio-preview-wrapper"
-		v-model:visible="visible"
-		:footer="null"
-		:closable="false"
-		:maskClosable="false"
-		:width="'100%'"
-		:bodyStyle="{ padding: 0 }"
-		:centered="true"
-	>
-		<img class="audio-background" :src="musicImgUrl" alt="背景图" />
-
-		<!-- 右上角操作 -->
-		<div class="operate-box">
-			<a-tooltip placement="bottom">
-				<template #title>
-					<div style="line-height: 2">
-						操作提示: <br />
-						1. 按 Esc 键可退出查看;<br />
-						2. 支持键盘控制:<br />
-						&nbsp;&nbsp;空格 - 暂停/播放<br />
-						&nbsp;&nbsp;左方向键 - 播放上一个<br />
-						&nbsp;&nbsp;右方向键 - 播放下一个<br />
-						&nbsp;&nbsp;上方向键 - 音量调大<br />
-						&nbsp;&nbsp;下方向键 - 音量减小<br />
+	<transition name="zoom-in-top">
+		<div class="audio-preview-wrapper" v-show="visible">
+			<img class="audio-background" :src="musicImgUrl" alt="背景图" />
+			<!-- 右上角操作 -->
+			<div class="operate-box">
+				<a-tooltip placement="bottom">
+					<template #title>
+						<div style="line-height: 2">
+							操作提示: <br />
+							1. 按 Esc 键可退出查看;<br />
+							2. 支持键盘控制:<br />
+							&nbsp;&nbsp;空格 - 暂停/播放<br />
+							&nbsp;&nbsp;左方向键 - 播放上一个<br />
+							&nbsp;&nbsp;右方向键 - 播放下一个<br />
+							&nbsp;&nbsp;上方向键 - 音量调大<br />
+							&nbsp;&nbsp;下方向键 - 音量减小<br />
+						</div>
+					</template>
+					<i class="tip-icon anticon anticon-bulb"></i>
+				</a-tooltip>
+				<i class="close-icon anticon anticon-close" title="关闭(Escape)" @click="handleClosePreview"></i>
+			</div>
+			<audio
+				ref="audioRef"
+				:src="activeFileObj.fileUrl"
+				controls
+				style="position: fixed; top: 0; left: 0; display: none"
+				@loadedmetadata="handleLoadedmetadata"
+				@timeupdate="handleTimeUpdate"
+				@ended="handleChangeAudioIndex('next')"
+			></audio>
+			<div class="audio-list-wrapper">
+				<!-- 音频列表 -->
+				<ul class="audio-list">
+					<li class="audio-list-header">
+						<span class="name">音频名称</span>
+						<span class="audio-size">大小</span>
+						<span class="path">路径</span>
+					</li>
+					<div class="audio-list-body">
+						<li
+							class="audio-item"
+							v-for="(item, index) in audioList"
+							:key="index"
+							:class="[activeIndex === index ? 'active' : '']"
+							:title="isPlay ? '暂停' : '播放'"
+							@click="handleChangeAudioIndex('manual', index)"
+						>
+							<span class="name">
+								<span class="sequence" v-show="activeIndex !== index">
+									{{ index + 1 }}
+								</span>
+								<img class="wave" :src="activePlayIcon" alt="波浪动图" v-show="activeIndex === index && isPlay" />
+								<i class="no-wave anticon anticon-bar-chart" v-show="activeIndex === index && !isPlay"></i>
+								<span class="text">{{ item.fileName }}.{{ item.extendName }}</span>
+							</span>
+							<i class="play-icon iconfont icon-icon-7" v-show="activeIndex === index && !isPlay"></i>
+							<i class="pause-icon iconfont icon-icon-3" v-show="activeIndex === index && isPlay"></i>
+							<a class="download" :href="$file.getDownloadFilePath(item)" target="_blank" title="下载">
+								<i class="download-icon anticon anticon-download"></i>
+							</a>
+							<i
+								class="share-icon anticon anticon-share-alt"
+								title="分享"
+								@click.stop="
+									$openDialog.shareFile({
+										fileInfo: [
+											{
+												userFileId: item.userFileId
+											}
+										]
+									})
+								"
+							></i>
+							<span class="audio-size">{{ $file.calculateFileSize(item.fileSize) }}</span>
+							<span class="path">{{ item.filePath }}</span>
+						</li>
 					</div>
-				</template>
-				<question-circle-outlined class="tip-icon" />
-			</a-tooltip>
-			<close-outlined class="close-icon" title="关闭(Escape)" @click="handleClosePreview" />
-		</div>
-
-		<audio
-			ref="audioRef"
-			:src="activeFileObj.fileUrl"
-			controls
-			style="position: fixed; top: 0; left: 0; display: none"
-			@loadedmetadata="handleLoadedmetadata"
-			@timeupdate="handleTimeUpdate"
-			@ended="handleChangeAudioIndex('next')"
-		/>
-
-		<div class="audio-list-wrapper">
-			<!-- 音频列表 -->
-			<ul class="audio-list">
-				<li class="audio-list-header">
-					<span class="name">音频名称</span>
-					<span class="audio-size">大小</span>
-					<span class="path">路径</span>
-				</li>
-				<div class="audio-list-body">
-					<li
-						v-for="(item, index) in audioList"
-						:key="index"
-						class="audio-item"
-						:class="{ active: activeIndex === index }"
-						:title="isPlay ? '暂停' : '播放'"
-						@click="handleChangeAudioIndex('manual', index)"
+				</ul>
+				<!-- 歌曲图片和歌词 -->
+				<div class="img-and-lyrics">
+					<img class="audio-img" :src="musicImgUrl" alt="歌曲图片" />
+					<div class="audio-name">{{ activeFileObj.fileName }}.{{ activeFileObj.extendName }}</div>
+					<div class="album-artist" v-show="audioInfo.artist">歌手:{{ audioInfo.artist }}</div>
+					<div class="album-name" v-if="audioInfo.album">专辑:{{ audioInfo.album }}</div>
+					<ul
+						class="lyrics-list"
+						ref="lyricsListRef"
+						:class="{ one: lyricsList.length === 1 }"
+						v-if="lyricsList.length"
 					>
-						<span class="name">
-							<span class="sequence" v-show="activeIndex !== index">
-								{{ index + 1 }}
-							</span>
-							<img class="wave" :src="activePlayIcon" alt="波浪动图" v-show="activeIndex === index && isPlay" />
-							<i class="no-wave el-icon-s-data" v-show="activeIndex === index && !isPlay"></i>
-							<span class="text">{{ item.fileName }}.{{ item.extendName }}</span>
-						</span>
-						<i class="play-icon iconfont icon-icon-7" v-show="activeIndex === index && !isPlay"></i>
-						<i class="pause-icon iconfont icon-icon-3" v-show="activeIndex === index && isPlay"></i>
-						<a class="download" :href="$file.getDownloadFilePath(item)" target="_blank" title="下载">
-							<i class="download-icon el-icon-download"></i>
-						</a>
-						<i
-							class="share-icon el-icon-share"
-							title="分享"
-							@click.stop="
-								$openDialog.shareFile({
-									fileInfo: [
-										{
-											userFileId: item.userFileId
-										}
-									]
-								})
-							"
-						></i>
-						<span class="audio-size">{{ $file.calculateFileSize(item.fileSize) }}</span>
-						<span class="path">{{ item.filePath }}</span>
-					</li>
+						<li
+							class="lyrics-item"
+							ref="lyricsLineRef"
+							v-for="(item, index) in lyricsList"
+							:key="index"
+							:class="{
+								active: currentLyricsLineIndex === index
+							}"
+							@click="handleChangeProgress(transferTimeToSeconds(item.time))"
+						>
+							{{ item.text }}
+						</li>
+					</ul>
 				</div>
-			</ul>
-
-			<!-- 歌曲图片和歌词 -->
-			<div class="img-and-lyrics">
-				<img class="audio-img" :src="musicImgUrl" alt="歌曲图片" />
-				<div class="audio-name">{{ activeFileObj.fileName }}.{{ activeFileObj.extendName }}</div>
-				<div class="album-artist" v-show="audioInfo.artist">歌手:{{ audioInfo.artist }}</div>
-				<div class="album-name" v-if="audioInfo.album">专辑:{{ audioInfo.album }}</div>
-				<ul class="lyrics-list" ref="lyricsListRef" :class="{ one: lyricsList.length === 1 }" v-if="lyricsList.length">
-					<li
-						class="lyrics-item"
-						ref="lyricsLineRef"
-						v-for="(item, index) in lyricsList"
-						:key="index"
-						:class="{
-							active: currentLyricsLineIndex === index
-						}"
-						@click="handleChangeProgress(transferTimeToSeconds(item.time))"
-					>
-						{{ item.text }}
-					</li>
-				</ul>
 			</div>
-		</div>
-
-		<!-- 底部音乐控件 -->
-		<div class="control-wrapper">
-			<div class="control-left">
-				<step-backward-outlined
-					class="operate-icon"
-					title="上一个(按左方向键)"
-					@click="handleChangeAudioIndex('pre')"
-				/>
-				<play-circle-outlined
-					v-if="!isPlay"
-					class="operate-icon"
-					title="播放(按空格键)"
-					@click="handleClickPlayIcon"
-				/>
-				<pause-circle-outlined v-else class="operate-icon" title="暂停(按空格键)" @click="handleClickPauseIcon" />
-				<step-forward-outlined
-					class="operate-icon"
-					title="下一个(按右方向键)"
-					@click="handleChangeAudioIndex('next')"
-				/>
-				<a-slider
-					class="progress-bar control-item"
-					v-model:value="currentTime"
-					:step="progressStep"
-					:max="audioInfo.duration"
-					:tooltip-visible="true"
-					:tip-formatter="(val) => transferSecondsToTime(val)"
-					@mousedown="isDrop = true"
-					@mouseup="isDrop = false"
-					@change="handleChangeProgress"
-				/>
-				<span class="time control-item">
-					{{ transferSecondsToTime(currentTime) }} / {{ transferSecondsToTime(audioInfo.duration) }}
-				</span>
-			</div>
-
-			<div class="control-right">
-				<i
-					class="operate-icon cycle-type iconfont"
-					:class="cycleTypeMap[String(cycleType)].icon"
-					:title="cycleTypeMap[String(cycleType)].text"
-					@click="handleChangeCycleType"
-				></i>
-				<a
-					class="operate-icon download-link"
-					:href="$file.getDownloadFilePath(activeFileObj)"
-					target="_blank"
-					title="下载"
-				>
-					<i class="download-icon el-icon-download"></i>
-				</a>
-				<i
-					class="operate-icon share-icon el-icon-share"
-					title="分享"
-					@click.stop="
-						$openDialog.shareFile({
-							fileInfo: [
-								{
-									userFileId: item.userFileId
-								}
-							]
-						})
-					"
-				></i>
-				<i
-					class="operate-icon volume-icon control-item iconfont"
-					:class="volume === 0 ? 'icon-jingyin01' : 'icon-yinliang101'"
-					@click="handleClickVolumeIcon"
-				></i>
-				<el-slider
-					class="volume-bar control-item"
-					v-model="volume"
-					:step="0.01"
-					:max="1"
-					:format-tooltip="(val) => Math.floor(val * 100)"
-					height="100px"
-					title="可按上下方向键调节音量"
-					@input="handleChangeVolumeBar"
-				></el-slider>
+			<!-- 底部音乐控件 -->
+			<div class="control-wrapper">
+				<div class="control-left">
+					<i
+						class="operate-icon iconfont icon-shangyishou"
+						title="上一个(按左方向键)"
+						@click="handleChangeAudioIndex('pre')"
+					></i>
+					<i
+						class="operate-icon play-icon iconfont icon-icon-7"
+						v-show="!isPlay"
+						title="播放(按空格键)"
+						@click="handleClickPlayIcon"
+					></i>
+					<i
+						class="operate-icon pause-icon iconfont icon-icon-3"
+						v-show="isPlay"
+						title="暂停(按空格键)"
+						@click="handleClickPauseIcon"
+					></i>
+					<i
+						class="operate-icon iconfont icon-xiayishou"
+						title="下一个(按右方向键)"
+						@click="handleChangeAudioIndex('next')"
+					></i>
+					<a-slider
+						class="progress-bar control-item"
+						v-model:value="currentTime"
+						:step="progressStep"
+						:max="audioInfo.duration"
+						:tip-formatter="(val) => transferSecondsToTime(val)"
+						@mousedown="isDrop = true"
+						@mouseup="isDrop = false"
+						@change="handleChangeProgress"
+					/>
+					<span class="time control-item"
+						>{{ transferSecondsToTime(currentTime) }} / {{ transferSecondsToTime(audioInfo.duration) }}</span
+					>
+				</div>
+				<div class="control-right">
+					<i
+						class="operate-icon cycle-type iconfont"
+						:class="cycleTypeMap[String(cycleType)].icon"
+						:title="cycleTypeMap[String(cycleType)].text"
+						@click="handleChangeCycleType"
+					></i>
+					<a
+						class="operate-icon download-link"
+						:href="$file.getDownloadFilePath(activeFileObj)"
+						target="_blank"
+						title="下载"
+					>
+						<i class="download-icon anticon anticon-download"></i>
+					</a>
+					<i
+						class="operate-icon share-icon anticon anticon-share-alt"
+						title="分享"
+						@click.stop="
+							$openDialog.shareFile({
+								fileInfo: [
+									{
+										userFileId: activeFileObj.userFileId
+									}
+								]
+							})
+						"
+					></i>
+					<i
+						class="operate-icon volume-icon control-item iconfont"
+						:class="volume === 0 ? 'icon-jingyin01' : 'icon-yinliang101'"
+						@click="handleClickVolumeIcon"
+					></i>
+					<a-slider
+						class="volume-bar control-item"
+						v-model:value="volume"
+						:step="0.01"
+						:max="1"
+						:tip-formatter="(val) => Math.floor(val * 100)"
+						title="可按上下方向键调节音量"
+						@change="handleChangeVolumeBar"
+					/>
+				</div>
 			</div>
 		</div>
-	</a-modal>
+	</transition>
 </template>
 
 <script setup>
-	import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
-	import musicPng from '@/assets/images/myResource/file/file_music.png'
-	import { message } from 'ant-design-vue'
-	import {
-		QuestionCircleOutlined,
-		CloseOutlined,
-		StepBackwardOutlined,
-		StepForwardOutlined,
-		PlayCircleOutlined,
-		PauseCircleOutlined
-	} from '@ant-design/icons-vue'
-	import { getFileDetail } from '@/api/myResource/file'
+	import { ref, computed, watch, onUnmounted, getCurrentInstance } from 'vue'
+	import { getFileDetail } from '@/api/myResource/file.js'
 	import { Base64 } from 'js-base64'
-
-	const props = defineProps({
-		visible: {
-			type: Boolean,
-			default: false
+	import waveGif from '@/assets/images/myResource/audio/wave.gif'
+	import musicImg from '@/assets/images/myResource/file/file_music.png'
+
+	const { proxy } = getCurrentInstance()
+
+	// 定义响应式数据
+	const visible = ref(false) // 音频预览组件是否可见
+	const activeIndex = ref(0) // 当前打开的音频索引
+	const activePlayIcon = ref(waveGif)
+	const cycleType = ref(1) // 音频播放的循环模式
+	const isPlay = ref(false) // 是否正在播放
+	const currentTime = ref(0) // 当前播放的秒
+	const isDrop = ref(false) // 是否正在拖拽播放进度滑块
+	const volume = ref(0) // 音量
+	const audioInfo = ref({}) // 音频信息
+	const lyricsList = ref([]) // 歌词列表
+	const currentLyricsLineIndex = ref(0) // 当前高亮的歌词行索引,从 0 开始
+	const loading = ref(false) // 加载状态
+	const audioRef = ref(null) // 音频元素引用
+	const lyricsListRef = ref(null) // 歌词列表引用
+	const lyricsLineRef = ref([]) // 歌词行引用
+	const defaultIndex = ref(0) // 默认索引
+
+	// 音频循环模式和图标对应的 Map
+	const cycleTypeMap = {
+		1: {
+			icon: 'icon-xunhuanbofang',
+			text: '列表循环'
+		},
+		2: {
+			icon: 'icon-danquxunhuan1',
+			text: '单曲循环'
 		},
+		3: {
+			icon: 'icon-suijibofang1',
+			text: '随机播放'
+		}
+	}
+	// 定义 props 接收父组件传递的参数
+	const props = defineProps({
 		audioList: {
 			type: Array,
 			default: () => []
@@ -231,53 +250,55 @@
 		}
 	})
 
-	const emit = defineEmits(['update:visible'])
-
-	const visible = ref(props.visible)
-	const activeIndex = ref(0)
-	const isPlay = ref(false)
-	const currentTime = ref(0)
-	const isDrop = ref(false)
-	const volume = ref(0)
-	const audioInfo = ref({})
-	const lyricsList = ref([])
-	const currentLyricsLineIndex = ref(0)
-
+	// 计算属性
+	// 当前显示的文件信息
 	const activeFileObj = computed(() => {
 		const res = props.audioList.length ? props.audioList[activeIndex.value] : {}
 		return res
 	})
 
+	// 隐藏的 audio 标签
 	const audioElement = computed(() => {
-		return refs.audioRef
+		return audioRef.value
 	})
 
+	// 歌曲封面
 	const musicImgUrl = computed(() => {
-		return audioInfo.value.albumImage ? `data:image/jpeg;base64,${audioInfo.value.albumImage}` : musicPng
+		return audioInfo.value.albumImage ? `data:image/jpeg;base64,${audioInfo.value.albumImage}` : musicImg
 	})
 
+	// 播放进度条步长
 	const progressStep = computed(() => {
 		return audioInfo.value.duration / 100
 	})
 
-	watch(
-		() => props.visible,
-		(newValue) => {
-			visible.value = newValue
-			if (newValue) {
-				activeIndex.value = props.defaultIndex
-				getFileDetailData()
-				document.addEventListener('keyup', handleAddKeyupEvent)
-			} else {
-				document.removeEventListener('keyup', handleAddKeyupEvent)
-			}
+	// 监听音频预览组件状态
+	watch(visible, (newValue) => {
+		if (newValue) {
+			activeIndex.value = defaultIndex.value
+			getFileDetailData()
+			// 添加键盘相关事件
+			document.addEventListener('keyup', handleAddKeyupEvent)
+		} else {
+			// 移除键盘相关事件
+			document.removeEventListener('keyup', handleAddKeyupEvent)
 		}
-	)
+	})
 
+	// 监听当前索引变化
 	watch(activeIndex, () => {
 		getFileDetailData()
 	})
 
+	// 组件卸载时移除事件监听
+	onUnmounted(() => {
+		document.removeEventListener('keyup', handleAddKeyupEvent)
+	})
+
+	/**
+	 * DOM 绑定 Esc 键、左方向键、右方向键的键盘按下事件
+	 * @param {event} event 事件
+	 */
 	function handleAddKeyupEvent(event) {
 		switch (event.code) {
 			// 关闭预览
@@ -317,6 +338,9 @@
 		}
 	}
 
+	/**
+	 * 获取文件信息
+	 */
 	function getFileDetailData() {
 		handleClickPauseIcon()
 		loading.value = true
@@ -357,7 +381,7 @@
 						}
 					})
 					// 当切换完歌曲时,歌词重新滚动到顶部
-					refs.lyricsListRef.scrollTo({
+					lyricsListRef.value.scrollTo({
 						top: 0,
 						behavior: 'smooth'
 					})
@@ -369,6 +393,9 @@
 			})
 	}
 
+	/**
+	 * 获取播放器参数
+	 */
 	function handleLoadedmetadata(event) {
 		const audioDom = event.target
 		volume.value = audioDom.volume || 0.5
@@ -376,6 +403,10 @@
 		handleClickPlayIcon()
 	}
 
+	/**
+	 * 将秒转化为时分秒
+	 * @param {number} duration 总秒数
+	 */
 	function transferSecondsToTime(duration) {
 		const hour = Math.floor(duration / 3600)
 		const minutes = Math.floor(duration / 60)
@@ -385,11 +416,18 @@
 		}`
 	}
 
+	/**
+	 * 将分秒转化为秒
+	 * @param {string} time 分秒,格式 00:00
+	 */
 	function transferTimeToSeconds(time) {
 		const timeList = time.split('.')[0].split(':')
 		return Number(timeList[1]) + Number(timeList[0]) * 60
 	}
 
+	/**
+	 * 当前播放时间改变时触发
+	 */
 	function handleTimeUpdate(event) {
 		// 如果正在拖拽进度滑块,函数结束,不计算当前时间
 		if (isDrop.value) return
@@ -407,8 +445,8 @@
 					// 使高亮歌词行永远保持在第二行
 					if (currentLyricsLineIndex.value > 2) {
 						// 平滑滚动
-						refs.lyricsListRef.scrollTo({
-							top: refs.lyricsLineRef[index].clientHeight * (index - 2),
+						lyricsListRef.value.scrollTo({
+							top: lyricsLineRef.value[index].clientHeight * (index - 2),
 							behavior: 'smooth'
 						})
 					}
@@ -417,11 +455,17 @@
 		}
 	}
 
+	/**
+	 * 拖动播放进度滑块触发
+	 */
 	function handleChangeProgress(progress) {
 		audioElement.value.currentTime = progress
 		isDrop.value = false
 	}
 
+	/**
+	 * 切换循环播放类型
+	 */
 	function handleChangeCycleType() {
 		if (cycleType.value === 3) {
 			cycleType.value = 1
@@ -430,16 +474,29 @@
 		}
 	}
 
+	/**
+	 * 点击播放图标触发
+	 * @description 开始播放音频
+	 */
 	function handleClickPlayIcon() {
 		isPlay.value = true
 		audioElement.value.play()
 	}
 
+	/**
+	 * 点击暂停图标触发
+	 * @description 暂停音频
+	 */
 	function handleClickPauseIcon() {
 		isPlay.value = false
 		audioElement.value.pause()
 	}
 
+	/**
+	 * 切换、暂停或播放歌曲
+	 * @param {string} type pre - 上一首 | next - 下一首 | manual 手动切换
+	 * @param {number} index 手动切换的音频索引,从 0 开始
+	 */
 	function handleChangeAudioIndex(type, index) {
 		// 如果当前手动切换
 		if (type === 'manual') {
@@ -457,22 +514,22 @@
 			// 判断当前循环播放类型
 			switch (cycleType.value) {
 				case 3: {
-					let activeIndex = 0
+					let newActiveIndex = 0
 					do {
-						activeIndex = Math.floor(Math.random() * (props.audioList.length - 1)) + 1
-					} while (activeIndex === activeIndex.value)
-					activeIndex.value = activeIndex
+						newActiveIndex = Math.floor(Math.random() * (props.audioList.value.length - 1)) + 1
+					} while (activeIndex.value === newActiveIndex)
+					activeIndex.value = newActiveIndex
 					break
 				}
 				default: {
 					if (type === 'pre') {
 						if (activeIndex.value === 0) {
-							activeIndex.value = props.audioList.length - 1
+							activeIndex.value = props.audioList.value.length - 1
 						} else {
 							activeIndex.value--
 						}
 					} else if (type === 'next') {
-						if (activeIndex.value === props.audioList.length - 1) {
+						if (activeIndex.value === props.audioList.value.length - 1) {
 							activeIndex.value = 0
 						} else {
 							activeIndex.value++
@@ -484,30 +541,30 @@
 		}
 	}
 
+	/**
+	 * 点击音量图标
+	 */
 	function handleClickVolumeIcon() {
 		volume.value = volume.value === 0 ? 0.5 : 0
 		handleChangeVolumeBar(volume.value)
 	}
 
-	function handleChangeVolumeBar(volume) {
-		audioElement.value.volume = Number(volume.toFixed(1))
+	/**
+	 * 音量滑块改变时触发
+	 */
+	function handleChangeVolumeBar(value) {
+		audioElement.value.volume = Number(value.toFixed(1))
 	}
 
-	// 关闭音频预览
+	/**
+	 * 关闭音频预览
+	 */
 	function handleClosePreview() {
 		visible.value = false
-		props.callback('cancel')
+		callback('cancel')
 	}
 
-	onMounted(() => {
-		visible.value = props.visible
-		activeIndex.value = props.defaultIndex
-	})
-
-	onUnmounted(() => {
-		document.removeEventListener('keyup', handleAddKeyupEvent)
-	})
-
+	// 暴露给父组件的方法和属性
 	defineExpose({
 		visible
 	})
@@ -756,19 +813,6 @@
 				.progress-bar {
 					margin-right: 16px;
 					flex: 1;
-					// >>> .el-slider__runway {
-					//   height: 2px;
-					//   .el-slider__button-wrapper {
-					//     top: -17px;
-					//     .el-slider__button {
-					//       border: none;
-					//     }
-					//   }
-					//   .el-slider__bar {
-					//     height: 100%;
-					//     background: @Warning;
-					//   }
-					// }
 				}
 			}
 
@@ -810,19 +854,6 @@
 
 				.volume-bar {
 					width: 100px;
-					// >>> .el-slider__runway {
-					//   height: 2px;
-					//   .el-slider__button-wrapper {
-					//     top: -19px;
-					//     .el-slider__button {
-					//       border: none;
-					//     }
-					//   }
-					//   .el-slider__bar {
-					//     height: 100%;
-					//     background: @Warning;
-					//   }
-					// }
 				}
 			}
 		}

+ 2 - 1
src/views/myResource/file/box/audioPreview/index.js

@@ -2,7 +2,7 @@ import { createApp } from 'vue'
 import Antd from 'ant-design-vue'
 // 导入组件
 import AudioPreview from './BoxMask.vue'
-
+import snowy from '@/snowy'
 let audioPreviewInstance = null
 let audioPreviewApp = null
 
@@ -21,6 +21,7 @@ const initInstanceAudioPreview = (audioList, defaultIndex, callback) => {
 	})
 	// 注册 Ant Design Vue 组件
 	audioPreviewApp.use(Antd)
+	audioPreviewApp.use(snowy)
 	audioPreviewInstance = audioPreviewApp.mount(mountNode)
 	return mountNode
 }

+ 278 - 225
src/views/myResource/file/box/codePreview/BoxMask.vue

@@ -1,109 +1,88 @@
 <template>
-	<transition
-		name="fade"
-		enter-active-class="ant-fade-enter ant-fade-enter-active"
-		leave-active-class="ant-fade-leave ant-fade-leave-active"
-	>
-		<div class="code-preview-wrapper" v-show="visible" @keydown.ctrl.s.prevent="handleModifyFileContent">
-			<!-- 顶部信息栏 -->
+	<transition name="fade-in">
+		<div class="code-preview-wrapper" v-show="visible" @keydown.s.ctrl.prevent="handleModifyFileContent">
+			<!-- 顶部信息栏 & 工具栏 -->
 			<div class="tip-wrapper" v-if="visible">
-				<div class="name" :title="$getFileNameComplete(fileInfo)">
-					{{ $getFileNameComplete(fileInfo) }}
+				<div class="name" :title="$file.getFileNameComplete(fileInfo)">
+					{{ $file.getFileNameComplete(fileInfo) }}
 					<span class="un-save" v-show="isModify && !codeMirrorOptions.readOnly">(未保存)</span>
 				</div>
-				<div class="editor-preview">在线预览{{ codeMirrorOptions.readOnly ? '' : ' & 编辑' }}</div>
+				<div class="editor-preveiw">在线预览{{ codeMirrorOptions.readOnly ? '' : ' & 编辑' }}</div>
 				<div class="tool-wrapper">
 					<a
 						class="item download-link"
 						target="_blank"
-						:href="$getDownloadFilePath(fileInfo)"
-						:download="$getFileNameComplete(fileInfo)"
+						:href="$file.getDownloadFilePath(fileInfo)"
+						:download="$file.getFileNameComplete(fileInfo)"
 					>
-						<download-outlined title="下载" />
+						<DownloadOutlined title="下载" />
 					</a>
 					<a-tooltip placement="bottom">
 						<template #title>
-							<div>
-								操作提示:<br />
-								1. 按 Esc 键可退出查看;<br />
-								2. 支持在线编辑、保存、下载
-							</div>
+							操作提示: <br />
+							1. 按 Esc 键可退出查看;<br />
+							2. 支持在线编辑、保存、下载
 						</template>
 						<div class="item text-wrapper">
 							<span class="text">操作提示</span>
-							<bulb-outlined />
+							<BulbOutlined />
 						</div>
 					</a-tooltip>
-					<close-outlined class="item" title="关闭预览" @click="closeCodePreview" />
+					<CloseOutlined class="item" title="关闭预览" @click="closeCodePreview" />
 				</div>
 			</div>
-
 			<!-- 代码编辑区域 -->
 			<div class="code-editor-wrapper">
 				<div class="operate-wrapper">
-					<save-outlined
-						v-show="isModify && !codeMirrorOptions.readOnly"
+					<SaveOutlined
 						class="save-icon"
 						title="保存(ctrl+s)"
+						v-show="isModify && !codeMirrorOptions.readOnly"
 						@click="handleModifyFileContent"
 					/>
-					<a-form class="editor-set-form" :model="codeMirrorOptions" layout="inline" size="small">
-						<a-form-item>
-							<a-checkbox v-model:checked="codeMirrorOptions.lineWrapping" @change="handleChangeCodeMirrorOption">
-								自动换行
-							</a-checkbox>
+					<a-form class="editor-set-form" :model="codeMirrorOptions" layout="inline" size="small" label-align="right">
+						<a-form-item class="line-wrapper">
+							<a-checkbox v-model:checked="codeMirrorOptions.lineWrapping" @change="handleChangeCodeMirrorOption"
+								>自动换行</a-checkbox
+							>
 						</a-form-item>
-
-						<a-form-item>
-							<a-select v-model:value="codeMirrorCustomOptions.fontSize" style="width: 96px" show-search>
-								<a-select-option v-for="size in fontSizeList" :key="size" :value="size">
-									{{ size }} px
-								</a-select-option>
+						<a-form-item class="font-size">
+							<a-select v-model:value="codeMirrorCustomOptions.fontSize" filterable>
+								<a-select-option v-for="(item, index) in fontSizeList" :key="index" :value="item"
+									>{{ item }} px</a-select-option
+								>
 							</a-select>
 						</a-form-item>
-
-						<a-form-item label="代码语言">
-							<a-select
-								v-model:value="codeMirrorOptions.mode"
-								style="width: 120px"
-								show-search
-								@change="handleChangeCodeMirrorOption"
-							>
-								<a-select-option
-									v-for="[key, value] in Object.entries(fileSuffixCodeModeMap)"
-									:key="key"
-									:value="value[1].mime"
-								>
-									{{ value[1].language }}
-								</a-select-option>
+						<a-form-item label="代码语言" class="lanaguage">
+							<a-select v-model:value="codeMirrorLanguage" filterable @change="handleChangeCodeMirrorOption">
+								<a-select-option v-for="[key, value] in fileSuffixCodeModeMap.entries()" :key="key" :value="key">{{
+									value.language
+								}}</a-select-option>
 							</a-select>
 						</a-form-item>
-
-						<a-form-item label="主题">
-							<a-select
-								v-model:value="codeMirrorOptions.theme"
-								style="width: 190px"
-								show-search
-								@change="handleChangeCodeMirrorOption"
-							>
+						<a-form-item label="主题" class="theme">
+							<a-select v-model:value="codeMirrorTheme" filterable @change="handleChangeCodeMirrorOption">
 								<a-select-option value="default">default</a-select-option>
-								<a-select-option v-for="theme in codeMirrorThemeList" :key="theme" :value="theme">
-									{{ theme }}
-								</a-select-option>
+								<a-select-option v-for="(item, index) in codeMirrorThemeList" :key="index" :value="item">{{
+									item
+								}}</a-select-option>
 							</a-select>
 						</a-form-item>
 					</a-form>
 				</div>
-
 				<a-spin :spinning="codeMirrorLoading">
-					<!-- <Codemirror
-						v-if="isShow"
-						ref="codemirrorRef"
-						v-model:value="codeMirrorText"
-						:options="codeMirrorOptions"
+					<Codemirror
 						class="code-editor"
+						ref="codemirrorRef"
+						v-model="codeMirrorText"
 						:style="{ fontSize: `${codeMirrorCustomOptions.fontSize}px` }"
-					/> -->
+						:extensions="extensions"
+						:disabled="codeMirrorOptions.readOnly"
+						:indent-with-tab="true"
+						:tab-size="codeMirrorOptions.tabSize"
+						:placeholder="'请输入代码...'"
+						v-if="isShow"
+					/>
 				</a-spin>
 			</div>
 		</div>
@@ -111,37 +90,43 @@
 </template>
 
 <script setup>
-	import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
+	import { ref, reactive, computed, watch, onMounted, onUnmounted, getCurrentInstance } from 'vue'
 	import { message } from 'ant-design-vue'
-	// import { Codemirror } from 'codemirror-editor-vue3'
-	// 启用基础样式
-	// import 'codemirror-editor-vue3/dist/style.css'
-	// // 启用编程语言
-	// import 'codemirror/mode/javascript/javascript.js'
-	// import 'codemirror/mode/xml/xml.js'
-	// import 'codemirror/mode/css/css.js'
-	// // 启用主题
-	// import 'codemirror/lib/codemirror.css'
-	// import 'codemirror/theme/monokai.css'
-	// import 'codemirror/theme/material.css'
 	import { DownloadOutlined, BulbOutlined, CloseOutlined, SaveOutlined } from '@ant-design/icons-vue'
-	import { useMyResourceStore } from '@/store/myResource'
-	import { getFilePreview, modifyFileContent } from '@/api/myResource/file'
-	import { fontSizeList, fileSuffixCodeModeMap, codeMirrorThemeList } from '@/libs/map'
+	import { Codemirror } from 'vue-codemirror'
+	import { basicSetup } from 'codemirror'
+	import { oneDark } from '@codemirror/theme-one-dark'
+	import { javascript } from '@codemirror/lang-javascript'
+	import { html } from '@codemirror/lang-html'
+	import { css } from '@codemirror/lang-css'
+	import { json } from '@codemirror/lang-json'
+	import { xml } from '@codemirror/lang-xml'
+	import { EditorView } from '@codemirror/view'
+	import { fontSizeList, fileSuffixCodeModeMap, codeMirrorThemeList } from '@/libs/map.js'
+	import { useMyResourceStore } from '@/store/myResource.js'
+	import { getFilePreview, modifyFileContent } from '@/api/myResource/file.js'
+	import tool from '@/utils/tool'
 
-	// import 'codemirror/lib/codemirror.css'
-	// import './theme.js'
-	// import './mode.js'
-	// import './fold.js'
-
-	// 状态管理
-	const store = useMyResourceStore()
+	const props = defineProps({
+		fileInfo: Object,
+		isEdit: Boolean,
+		callback: Function
+	})
+	const myResource = useMyResourceStore()
 
 	const visible = ref(false)
+	defineExpose({ visible })
+
 	const originalCodeText = ref('')
 	const codeMirrorText = ref('')
 	const codeMirrorLoading = ref(false)
-	const codeMirrorOptions = ref({
+	const isShow = ref(true)
+	const codemirrorRef = ref(null)
+	const codeMirrorLanguage = ref('html')
+	const codeMirrorTheme = ref('default')
+
+	// CodeMirror 配置
+	const codeMirrorOptions = reactive({
 		tabSize: 4,
 		mode: 'text/html',
 		theme: 'default',
@@ -153,38 +138,137 @@
 		lineWrapping: true,
 		gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers']
 	})
-	const codeMirrorCustomOptions = ref({
+
+	const codeMirrorCustomOptions = reactive({
 		fontSize: 14
 	})
-	const isShow = ref(true)
 
-	// 计算属性
-	const screenWidth = computed(() => store.screenWidth)
+	// 根据语言和主题生成扩展
+	const extensions = computed(() => {
+		const exts = [basicSetup]
+
+		// 添加语言支持
+		switch (codeMirrorLanguage.value) {
+			case 'js':
+			case 'javascript':
+				exts.push(javascript())
+				break
+			case 'html':
+				exts.push(html())
+				break
+			case 'css':
+				exts.push(css())
+				break
+			case 'json':
+				exts.push(json())
+				break
+			case 'xml':
+				exts.push(xml())
+				break
+			default:
+				exts.push(html())
+		}
+
+		// 添加主题
+		if (codeMirrorTheme.value === 'one-dark') {
+			exts.push(oneDark)
+		} else {
+			// 默认亮色主题
+			exts.push(
+				EditorView.theme({
+					'&': {
+						height: '100%',
+						fontSize: `${codeMirrorCustomOptions.fontSize}px`,
+						color: '#e0e0e0' // 添加默认文本颜色为浅灰色
+					},
+					'.cm-scroller': {
+						overflow: 'auto'
+					},
+					'.cm-content': {
+						color: '#e0e0e0' // 确保内容区域文本为浅灰色
+					},
+					'.cm-line': {
+						color: '#e0e0e0' // 确保每行文本为浅灰色
+					},
+					// 语法高亮颜色
+					'.cm-comment': { color: '#6a9955' }, // 注释
+					'.cm-keyword': { color: '#569cd6' }, // 关键字
+					'.cm-string': { color: '#ce9178' }, // 字符串
+					'.cm-number': { color: '#b5cea8' }, // 数字
+					'.cm-property': { color: '#9cdcfe' }, // 属性
+					'.cm-operator': { color: '#d4d4d4' }, // 操作符
+					'.cm-meta': { color: '#dcdcaa' }, // 元数据
+					'.cm-atom': { color: '#4ec9b0' }, // 原子
+					'.cm-variable': { color: '#9cdcfe' }, // 变量
+					'.cm-tag': { color: '#569cd6' }, // 标签
+					'.cm-attribute': { color: '#9cdcfe' } // 属性
+				})
+			)
+		}
+
+		// 添加行号
+		if (codeMirrorOptions.lineNumbers) {
+			exts.push(EditorView.lineWrapping)
+		}
+
+		return exts
+	})
+
+	const screenWidth = computed(() => myResource.screenWidth)
 	const isModify = computed(() => originalCodeText.value !== codeMirrorText.value)
 
-	// 方法
-	function getCodeText() {
+	watch(visible, (val) => {
+		if (val) {
+			let fileSuffix = props.fileInfo.extendName.toLowerCase()
+			if (fileSuffix === 'yaml') {
+				fileSuffix = 'yml'
+			}
+			if (fileSuffixCodeModeMap.has(fileSuffix)) {
+				codeMirrorLanguage.value = fileSuffix
+				codeMirrorOptions.mode = fileSuffixCodeModeMap.get(fileSuffix).mime
+			}
+			codeMirrorOptions.readOnly = !props.isEdit
+			codeMirrorTheme.value = localStorage.getItem('qiwen_file_codemirror_theme') || 'default'
+			getCodeText()
+			document.addEventListener('keyup', handleEscKey)
+		} else {
+			document.removeEventListener('keyup', handleEscKey)
+		}
+	})
+
+	watch(
+		() => codeMirrorTheme.value,
+		(val) => {
+			localStorage.setItem('qiwen_file_codemirror_theme', val)
+		}
+	)
+
+	const getCodeText = () => {
 		codeMirrorLoading.value = true
 		getFilePreview({
-			userFileId: fileInfo.userFileId,
+			userFileId: props.fileInfo.userFileId,
 			isMin: false,
-			shareBatchNum: fileInfo.shareBatchNum,
-			extractionCode: fileInfo.extractionCode,
-			token: $common.getCookies($config.tokenKeyName)
-		}).then((res) => {
-			codeMirrorLoading.value = false
-			originalCodeText.value = typeof res === 'object' ? JSON.stringify(res) : res
-			codeMirrorText.value = originalCodeText.value + ''
+			shareBatchNum: props.fileInfo.shareBatchNum,
+			extractionCode: props.fileInfo.extractionCode,
+			token: tool.data.get('TOKEN')
 		})
+			.then((res) => {
+				codeMirrorLoading.value = false
+				originalCodeText.value = typeof res === 'object' ? JSON.stringify(res) : res
+				codeMirrorText.value = originalCodeText.value + ''
+			})
+			.catch(() => {
+				codeMirrorLoading.value = false
+			})
 	}
 
-	function handleModifyFileContent() {
-		if (!isModify.value || codeMirrorOptions.value.readOnly) {
+	const handleModifyFileContent = () => {
+		if (!isModify.value || codeMirrorOptions.readOnly) {
 			return false
 		}
 		codeMirrorLoading.value = true
 		modifyFileContent({
-			userFileId: fileInfo.userFileId,
+			userFileId: props.fileInfo.userFileId,
 			fileContent: codeMirrorText.value
 		})
 			.then((res) => {
@@ -202,65 +286,38 @@
 			})
 	}
 
-	function handleChangeCodeMirrorOption() {
+	const handleChangeCodeMirrorOption = () => {
 		isShow.value = false
-		isShow.value = true
+		setTimeout(() => {
+			isShow.value = true
+		}, 0)
 	}
 
-	function closeCodePreview() {
+	const closeCodePreview = () => {
 		visible.value = false
-		callback('cancel')
+		props.callback('cancel')
 	}
 
-	// 监听
-	watch(visible, (val) => {
-		if (val) {
-			let fileSuffix = fileInfo.extendName.toLowerCase()
-			if (fileSuffix === 'yaml') {
-				fileSuffix = 'yml'
-			}
-			if (fileSuffixCodeModeMap.has(fileSuffix)) {
-				codeMirrorOptions.value.mode = fileSuffixCodeModeMap.get(fileSuffix).mime
-			}
-			codeMirrorOptions.value.readOnly = !isEdit //  设置编辑器是否只读
-			codeMirrorOptions.value.theme = localStorage.getItem('qiwen_file_codemirror_theme') || 'default'
-			getCodeText()
-			// 添加键盘 Esc 事件
-			nextTick(() => {
-				document.addEventListener('keyup', (e) => {
-					if (e.keyCode === 27) {
-						closeCodePreview()
-					}
-				})
-			})
-		} else {
-			document.removeEventListener('keyup', (e) => {
-				if (e.keyCode === 27) {
-					closeCodePreview()
-				}
-			})
+	const handleEscKey = (e) => {
+		if (e.keyCode === 27) {
+			closeCodePreview()
 		}
-	})
+	}
 
-	watch(
-		() => codeMirrorOptions.value.theme,
-		(val) => {
-			localStorage.setItem('qiwen_file_codemirror_theme', val)
+	onMounted(() => {
+		if (visible.value) {
+			document.addEventListener('keyup', handleEscKey)
 		}
-	)
-
-	// 生命周期钩子
-	onMounted(() => {})
-
-	onUnmounted(() => {})
+	})
 
-	defineExpose({
-		visible
+	onUnmounted(() => {
+		document.removeEventListener('keyup', handleEscKey)
 	})
 </script>
 
 <style lang="less" scoped>
 	@import '@/style/myResource/varibles.less';
+	@import '@/style/myResource/mixins.less';
 
 	.code-preview-wrapper {
 		position: fixed;
@@ -288,14 +345,6 @@
 				background: rgba(0, 0, 0, 0.8);
 			}
 		}
-		@keyframes imgPreviewAnimation {
-			0% {
-				background: transparent;
-			}
-			100% {
-				background: rgba(0, 0, 0, 0.8);
-			}
-		}
 		.tip-wrapper {
 			position: fixed;
 			top: 0;
@@ -318,7 +367,7 @@
 				text-overflow: ellipsis;
 				white-space: nowrap;
 				.un-save {
-					color: @Warning;
+					color: @warning-color;
 					font-size: 14px;
 				}
 			}
@@ -346,6 +395,7 @@
 				}
 			}
 		}
+
 		.code-editor-wrapper {
 			margin: 56px auto 0 auto;
 			width: 90vw;
@@ -355,9 +405,10 @@
 				justify-content: space-between;
 				align-items: center;
 				border-radius: 8px 8px 0 0;
-				border-bottom: 1px solid @BorderBase;
+				border-bottom: 1px solid @border-color-base;
 				padding: 8px 16px;
-				background: #fff;
+				background: #1e1e1e; /* 修改为暗色背景 */
+				color: #e0e0e0; /* 添加文字颜色为浅色 */
 				.save-icon {
 					font-size: 20px;
 					cursor: pointer;
@@ -370,74 +421,76 @@
 				.editor-set-form {
 					flex: 1;
 					text-align: right;
-					// >>> .el-form-item {
-					//   margin-bottom: 0;
-					//   &.font-size {
-					//     .el-form-item__content {
-					//       .el-select {
-					//         width: 96px;
-					//       }
-					//     }
-					//   }
-					//   &.lanaguage {
-					//     .el-form-item__content {
-					//       .el-select {
-					//         width: 120px;
-					//       }
-					//     }
-					//   }
-					//   &.theme {
-					//     .el-form-item__content {
-					//       .el-select {
-					//         width: 190px;
-					//       }
-					//     }
-					//   }
-					// }
+					:deep(.ant-form-item) {
+						margin-bottom: 0;
+						/* 修改表单项文字颜色 */
+						color: #e0e0e0;
+						.ant-form-item-label > label {
+							color: #e0e0e0;
+						}
+						.ant-checkbox-wrapper {
+							color: #e0e0e0;
+						}
+						&.font-size {
+							.ant-form-item-control-input-content {
+								.ant-select {
+									width: 96px;
+								}
+							}
+						}
+						&.lanaguage {
+							.ant-form-item-control-input-content {
+								.ant-select {
+									width: 120px;
+								}
+							}
+						}
+						&.theme {
+							.ant-form-item-control-input-content {
+								.ant-select {
+									width: 190px;
+								}
+							}
+						}
+					}
 				}
 			}
 			.code-editor {
 				height: calc(100vh - 129px);
-				//   >>> .CodeMirror {
-				//     border-radius: 0 0 8px 8px;
-				//     height: inherit;
-				//     font-size: inherit;
-				//     * {
-				//       font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, Courier, monospace !important;
-				//     }
-				//     .CodeMirror-vscrollbar, .CodeMirror-hscrollbar {
-				//       display: none !important;
-				//     }
-				//     .CodeMirror-scroll {
-				//       width: 100%;
-				//       padding: 8px 0 0 0;
-				//       line-height: 1.5;
-				//       font-size: inherit;
-				//       setScrollbar(12px, transparent, #C0C4CC);
-				//     }
-				//   }
+				background: #1e1e1e; /* 添加暗色背景 */
+				:deep(.cm-editor) {
+					border-radius: 0 0 8px 8px;
+					height: inherit;
+					font-size: inherit;
+					background: #1e1e1e; /* 添加暗色背景 */
+					color: #e0e0e0; /* 确保编辑器文本为浅灰色 */
+					* {
+						font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, Courier, monospace !important;
+					}
+					.cm-scroller {
+						width: 100%;
+						padding: 8px 0 0 0;
+						line-height: 1.5;
+						font-size: inherit;
+						overflow: auto;
+						background: #1e1e1e; /* 添加暗色背景 */
+					}
+					.cm-content {
+						color: #e0e0e0; /* 确保内容区域文本为浅灰色 */
+					}
+					.cm-line {
+						color: #e0e0e0; /* 确保每行文本为浅灰色 */
+					}
+					.cm-gutters {
+						background: #1e1e1e; /* 行号区域背景 */
+						border-right: 1px solid #333; /* 行号右侧边框 */
+						color: #858585; /* 行号颜色 */
+					}
+					.cm-activeLineGutter {
+						background-color: #2c2c2c; /* 当前行的行号背景 */
+					}
+				}
 			}
 		}
 	}
-	.editor-preveiw {
-		margin-left: -102px;
-	}
-	.editor-preveiw[data-v-8897ab08] {
-		margin-left: -102px;
-	}
-	/deep/ .el-checkbox__input.is-checked + .el-checkbox__label {
-		color: #29175b;
-	}
-	/deep/ .el-checkbox__input.is-checked .el-checkbox__inner,
-	.el-checkbox__input.is-indeterminate .el-checkbox__inner {
-		background-color: #29175b;
-		border-color: #29175b;
-	}
-	/deep/ .el-select .el-input.is-focus .el-input__inner {
-		border-color: #29175b;
-	}
-	/deep/ .el-select-dropdown__item.selected {
-		color: #29175b !important;
-		font-weight: 700;
-	}
 </style>

+ 0 - 7
src/views/myResource/file/box/codePreview/fold.js

@@ -1,7 +0,0 @@
-// 折叠
-import 'codemirror/addon/fold/foldgutter.css'
-import 'codemirror/addon/fold/foldcode'
-import 'codemirror/addon/fold/foldgutter'
-import 'codemirror/addon/fold/brace-fold'
-import 'codemirror/addon/fold/comment-fold'
-import 'codemirror/addon/fold/indent-fold'

+ 2 - 1
src/views/myResource/file/box/codePreview/index.js

@@ -2,7 +2,7 @@ import { createApp } from 'vue'
 import Antd from 'ant-design-vue'
 // 导入组件
 import CodePreview from './BoxMask.vue'
-
+import snowy from '@/snowy'
 let codePreviewInstance = null
 let codePreviewApp = null
 
@@ -21,6 +21,7 @@ const initInstanceCodePreview = (fileInfo, isEdit, callback) => {
 	})
 	// 注册 Ant Design Vue 组件
 	codePreviewApp.use(Antd)
+	codePreviewApp.use(snowy)
 	codePreviewInstance = codePreviewApp.mount(mountNode)
 	return mountNode
 }

+ 0 - 24
src/views/myResource/file/box/codePreview/mode.js

@@ -1,24 +0,0 @@
-/**
- * 这里引入几个常用的语言解析模式
- * 全量语言模式参考,https://codemirror.net/mode/
- */
-import 'codemirror/mode/clike/clike.js' //  C | C++ | Objective-C | Scala | Ceylon | Java 语言
-import 'codemirror/mode/javascript/javascript.js' //  JavaScript 语言
-import 'codemirror/mode/css/css.js' //  css 语言 | less 预编译器 | scss 预编译器
-import 'codemirror/mode/go/go.js' //  Go 语言
-import 'codemirror/mode/nginx/nginx.js' //  Nginx 语言
-import 'codemirror/mode/php/php.js' //  PHP 语言
-import 'codemirror/mode/powershell/powershell.js' //  Bat 文件
-import 'codemirror/mode/properties/properties.js' //  properties 文件
-import 'codemirror/mode/python/python.js' //  Python 语言
-import 'codemirror/mode/r/r.js' //  R 语言
-import 'codemirror/mode/rust/rust.js' //  R 语言
-import 'codemirror/mode/sass/sass.js' //  sass 预编译器
-import 'codemirror/mode/shell/shell.js' //  sass 预编译器
-import 'codemirror/mode/stylus/stylus.js' //  stylus 预编译器
-import 'codemirror/mode/swift/swift.js' //  Swift 语言
-import 'codemirror/mode/vue/vue.js' //  vue.js 框架
-import 'codemirror/mode/sql/sql.js' //  SQL 语言
-import 'codemirror/mode/xml/xml.js' //  xml 语言
-import 'codemirror/mode/yaml/yaml' //  YAML 语言
-import 'codemirror/mode/htmlmixed/htmlmixed.js' //  html 标记语言

+ 0 - 69
src/views/myResource/file/box/codePreview/theme.js

@@ -1,69 +0,0 @@
-/**
- * codemirror 所有的高亮代码主题
- */
-import 'codemirror/theme/3024-day.css'
-import 'codemirror/theme/3024-night.css'
-import 'codemirror/theme/abbott.css'
-import 'codemirror/theme/abcdef.css'
-import 'codemirror/theme/ambiance-mobile.css'
-import 'codemirror/theme/ambiance.css'
-import 'codemirror/theme/ayu-dark.css'
-import 'codemirror/theme/ayu-mirage.css'
-import 'codemirror/theme/base16-dark.css'
-import 'codemirror/theme/base16-light.css'
-import 'codemirror/theme/bespin.css'
-import 'codemirror/theme/blackboard.css'
-import 'codemirror/theme/cobalt.css'
-import 'codemirror/theme/colorforth.css'
-import 'codemirror/theme/darcula.css'
-import 'codemirror/theme/dracula.css'
-import 'codemirror/theme/duotone-dark.css'
-import 'codemirror/theme/duotone-light.css'
-import 'codemirror/theme/eclipse.css'
-import 'codemirror/theme/elegant.css'
-import 'codemirror/theme/erlang-dark.css'
-import 'codemirror/theme/gruvbox-dark.css'
-import 'codemirror/theme/hopscotch.css'
-import 'codemirror/theme/icecoder.css'
-import 'codemirror/theme/idea.css'
-import 'codemirror/theme/isotope.css'
-import 'codemirror/theme/juejin.css'
-import 'codemirror/theme/lesser-dark.css'
-import 'codemirror/theme/liquibyte.css'
-import 'codemirror/theme/lucario.css'
-import 'codemirror/theme/material-darker.css'
-import 'codemirror/theme/material-ocean.css'
-import 'codemirror/theme/material-palenight.css'
-import 'codemirror/theme/material.css'
-import 'codemirror/theme/mbo.css'
-import 'codemirror/theme/mdn-like.css'
-import 'codemirror/theme/midnight.css'
-import 'codemirror/theme/monokai.css'
-import 'codemirror/theme/moxer.css'
-import 'codemirror/theme/neat.css'
-import 'codemirror/theme/neo.css'
-import 'codemirror/theme/night.css'
-import 'codemirror/theme/nord.css'
-import 'codemirror/theme/oceanic-next.css'
-import 'codemirror/theme/panda-syntax.css'
-import 'codemirror/theme/paraiso-dark.css'
-import 'codemirror/theme/paraiso-light.css'
-import 'codemirror/theme/pastel-on-dark.css'
-import 'codemirror/theme/railscasts.css'
-import 'codemirror/theme/rubyblue.css'
-import 'codemirror/theme/seti.css'
-import 'codemirror/theme/shadowfox.css'
-import 'codemirror/theme/solarized.css'
-import 'codemirror/theme/ssms.css'
-import 'codemirror/theme/the-matrix.css'
-import '_public/codemirror/css/tomorrow-night.css' //  自定义高亮样式文件
-import 'codemirror/theme/tomorrow-night-bright.css'
-import 'codemirror/theme/tomorrow-night-eighties.css'
-import 'codemirror/theme/ttcn.css'
-import 'codemirror/theme/twilight.css'
-import 'codemirror/theme/vibrant-ink.css'
-import 'codemirror/theme/xq-dark.css'
-import 'codemirror/theme/xq-light.css'
-import 'codemirror/theme/yeti.css'
-import 'codemirror/theme/yonce.css'
-import 'codemirror/theme/zenburn.css'

+ 1 - 0
src/views/myResource/file/box/contextMenu/Box.vue

@@ -613,6 +613,7 @@
 				position: absolute;
 				display: none;
 				list-style: none;
+				margin-left: -15px;
 				.unzip-item {
 					width: 200px;
 					.setEllipsis(1);

+ 47 - 43
src/views/myResource/file/box/uploadFile/Box.vue

@@ -5,7 +5,6 @@
 			class="uploader-app"
 			ref="uploaderRef"
 			:options="options"
-			:opts="opts"
 			:autoStart="false"
 			:fileStatusText="fileStatusText"
 			@files-added="handleFilesAdded"
@@ -77,12 +76,10 @@
 </template>
 
 <script setup>
-	import { ref, computed, defineExpose, nextTick, inject } from 'vue'
+	import { ref, computed, defineExpose, nextTick, inject, getCurrentInstance } from 'vue'
 	import { message } from 'ant-design-vue'
 	import { useMyResourceStore } from '@/store/myResource'
 	import SparkMD5 from 'spark-md5'
-	import sysConfig from '@/config/index'
-	import rsConfig from '@/config/reSource'
 	import tool from '@/utils/tool'
 	import {
 		UploadOutlined,
@@ -101,7 +98,7 @@
 		callType: 1,
 		callback: () => {}
 	})
-
+	const { proxy } = getCurrentInstance()
 	const store = useMyResourceStore()
 
 	// refs
@@ -130,7 +127,7 @@
 	})
 
 	const options = ref({
-		target: `${sysConfig.API_URL}${rsConfig.baseContext}/filetransfer/uploadfile`,
+		target: `${proxy.$RESOURCE_CONFIG.baseContext}/filetransfer/uploadfile`,
 		chunkSize: 1024 * 1024,
 		fileParameterName: 'file',
 		maxChunkRetries: 3,
@@ -163,7 +160,7 @@
 	})
 
 	const attrs = ref({
-		accept: 'image/*,application/*,text/*'
+		accept: '*'
 	})
 
 	const panelShow = ref(false)
@@ -261,45 +258,52 @@
 	}
 
 	const handleFilesAdded = (filesSource) => {
-		const inconformityFileArr = []
-		const files = filesSource.filter((file) => {
-			const isFile = isImageOrDocument(file)
-			if (isFile) {
-				return file
-			} else {
-				inconformityFileArr.push(file)
-			}
-		})
-
-		if (inconformityFileArr.length > 0) {
-			filesSource.ignored = true
-			message.warning('文件夹中有不符合要求的文件', {
-				duration: 0,
-				content: '无'
-				// content: () => {
-				// 	let warringHtml = ''
-				// 	inconformityFileArr.forEach((item, index) => {
-				// 		warringHtml += `<p><i style="margin-right:5px">${index + 1}.</i>${item.name}</p>`
-				// 	})
-				// 	return (
-				// 		<div>
-				// 			<div style="color:#E6A23C;width:290px">{warringHtml}</div>
-				// 			<p style="color:#f56c6c">只可以上传图片和文档!文件大小不能为0!</p>
-				// 		</div>
-				// 	)
-				// }
-			})
-			return false
-		}
-
-		const filesTotalSize = files.reduce((pre, next) => pre + next.size, 0)
+		// const inconformityFileArr = []
+		// const files = filesSource.filter((file) => {
+		// 	const isFile = isImageOrDocument(file)
+		// 	if (isFile) {
+		// 		return file
+		// 	} else {
+		// 		inconformityFileArr.push(file)
+		// 	}
+		// })
+
+		// if (inconformityFileArr.length > 0) {
+		// 	filesSource.ignored = true
+		// 	message.warning('文件夹中有不符合要求的文件', {
+		// 		duration: 0,
+		// 		content: '无'
+		// 		// content: () => {
+		// 		// 	let warringHtml = ''
+		// 		// 	inconformityFileArr.forEach((item, index) => {
+		// 		// 		warringHtml += `<p><i style="margin-right:5px">${index + 1}.</i>${item.name}</p>`
+		// 		// 	})
+		// 		// 	return (
+		// 		// 		<div>
+		// 		// 			<div style="color:#E6A23C;width:290px">{warringHtml}</div>
+		// 		// 			<p style="color:#f56c6c">只可以上传图片和文档!文件大小不能为0!</p>
+		// 		// 		</div>
+		// 		// 	)
+		// 		// }
+		// 	})
+		// 	return false
+		// }
+
+		// const filesTotalSize = files.reduce((pre, next) => pre + next.size, 0)
+		const filesTotalSize = filesSource
+			.map((item) => item.size)
+			.reduce((pre, next) => {
+				return pre + next
+			}, 0)
 
 		if (remainderStorageValue.value < filesTotalSize) {
-			message.warning(`剩余存储空间不足,请重新选择${files.length > 1 ? '批量' : ''}文件`)
-			filesSource.ignored = true
+			// 批量选择的文件超出剩余存储空间
+			message.warning(`剩余存储空间不足,请重新选择${filesSource.length > 1 ? '批量' : ''}文件`)
+			filesSource.ignored = true // 本次选择的文件过滤掉
 		} else {
-			filesLength.value += files.length
-			files.forEach((file) => {
+			// 批量或单个选择的文件未超出剩余存储空间,正常上传
+			filesLength.value += filesSource.length
+			filesSource.forEach((file) => {
 				dropBoxShow.value = false
 				panelShow.value = true
 				collapse.value = false

+ 2 - 2
src/views/myResource/file/box/uploadFile/index.js

@@ -4,7 +4,7 @@ import UploadFileBox from './Box.vue'
 import uploader from 'vue-simple-uploader'
 import 'vue-simple-uploader/dist/style.css'
 import Antd from 'ant-design-vue'
-
+import snowy from '@/snowy'
 let appInstance = null
 let vm = null
 let currentPropsData = null // 保存当前的 props 数据
@@ -31,7 +31,7 @@ const initInstanceUploadFile = (params, uploadWay, serviceEl, callType, callback
 
 	appInstance.use(uploader)
 	appInstance.use(Antd)
-
+	appInstance.use(snowy)
 	vm = appInstance.mount(container)
 	document.body.appendChild(container)
 

+ 2 - 1
src/views/myResource/file/box/videoPreview/VideoPlayer.vue

@@ -53,7 +53,8 @@
 	onMounted(() => {
 		player = videojs(videoPlayer.value, options)
 		window.videojs = videojs
-		require('video.js/dist/lang/zh-CN.js')
+		// 使用 require 加载中文语言包
+		import('video.js/dist/lang/zh-CN.js')
 	})
 
 	onBeforeUnmount(() => {

+ 2 - 0
src/views/myResource/file/box/videoPreview/index.js

@@ -1,5 +1,6 @@
 import { createApp } from 'vue'
 import Antd from 'ant-design-vue'
+import snowy from '@/snowy'
 // 导入组件
 import VideoPreview from './BoxMask.vue'
 
@@ -21,6 +22,7 @@ const initInstanceVideoPreview = (videoList, defaultIndex, callback) => {
 	})
 	// 注册 Ant Design Vue 组件
 	videoPreviewApp.use(Antd)
+	videoPreviewApp.use(snowy)
 	videoPreviewInstance = videoPreviewApp.mount(mountNode)
 	return mountNode
 }

+ 203 - 0
src/views/myResource/file/dialog/unzipFile/Dialog copy.vue

@@ -0,0 +1,203 @@
+<template>
+	<a-modal
+		title="解压缩文件"
+		v-model:visible="visible"
+		:maskClosable="false"
+		:closable="false"
+		@afterVisibleChange="handleVisibleChange"
+		@cancel="handleDialogClose"
+	>
+		<div class="unzip-tree-wrapper" v-if="unzipMode === 2">
+			<div class="target-path">
+				<span class="label">目标路径:</span>
+				<a-input v-model:value="targetPath" readonly size="small" />
+			</div>
+
+			<a-spin :spinning="loading">
+				<a-tree
+					:tree-data="fileTree"
+					:field-names="{ children: 'children', title: 'label', key: 'id' }"
+					highlight-current
+					:expandedKeys="defaultExpandedKeys"
+					@update:expandedKeys="(val) => (defaultExpandedKeys = val)"
+					node-key="id"
+					@select="handleNodeClick"
+					:showLine="true"
+				>
+					<template #title="{ dataRef }">
+						<span class="custom-tree-node">
+							<span class="label">{{ dataRef.label }}</span>
+							<a-button type="link" size="small" class="add-folder-btn" @click.stop="handleAddFolderBtnClick(dataRef)">
+								新建文件夹
+							</a-button>
+						</span>
+					</template>
+				</a-tree>
+			</a-spin>
+		</div>
+		<div class="unzip-text" v-else>
+			<a-spin />
+			正在解压缩,请稍等片刻...
+		</div>
+
+		<template #footer>
+			<template v-if="unzipMode === 2">
+				<a-button @click="handleDialogClose">取 消</a-button>
+				<a-button type="primary" :loading="sureBtnLoading" @click="handleDialogSure"> 确 定 </a-button>
+			</template>
+		</template>
+	</a-modal>
+</template>
+
+<script setup>
+	import { ref } from 'vue'
+	import { message } from 'ant-design-vue'
+	import { getFoldTree, unzipFile } from '@/api/myResource/file'
+	const { proxy } = getCurrentInstance()
+
+	const props = defineProps({
+		unzipMode: Number,
+		userFileId: String,
+		callback: Function
+	})
+
+	const visible = ref(false)
+	const targetPath = ref('/')
+	const fileTree = ref([])
+	const loading = ref(false)
+	const defaultExpandedKeys = ref([])
+	const sureBtnLoading = ref(false)
+
+	const handleDialogClose = () => {
+		visible.value = false
+		props.callback('cancel')
+	}
+
+	const handleVisibleChange = (val) => {
+		if (val) {
+			if (props.unzipMode === 2) {
+				initFileTree()
+			} else {
+				handleUnzipFile()
+			}
+		}
+	}
+
+	const initFileTree = async (id) => {
+		try {
+			loading.value = true
+			const res = await getFoldTree()
+			if (res.success) {
+				fileTree.value = [res.data]
+				defaultExpandedKeys.value = id ? [id] : [fileTree.value[0].id]
+			} else {
+				message.error(res.message)
+			}
+		} finally {
+			loading.value = false
+		}
+	}
+
+	const handleNodeClick = (selectedKeys, { node }) => {
+		targetPath.value = node.filePath || '/'
+	}
+
+	const handleAddFolderBtnClick = async (data) => {
+		const result = await proxy.$openDialog.addFolder({
+			filePath: data.filePath || '/'
+		})
+		if (result === 'confirm') {
+			initFileTree(data.id)
+		}
+	}
+
+	const handleUnzipFile = async () => {
+		try {
+			sureBtnLoading.value = true
+			const reqData = {
+				unzipMode: props.unzipMode,
+				userFileId: props.userFileId,
+				...(props.unzipMode === 2 ? { filePath: targetPath.value } : {})
+			}
+
+			const res = await unzipFile(reqData)
+			if (res.success) {
+				message.success('解压成功')
+				visible.value = false
+				props.callback('confirm')
+			} else {
+				message.error(res.message)
+			}
+		} finally {
+			sureBtnLoading.value = false
+		}
+	}
+
+	const handleDialogSure = () => {
+		handleUnzipFile()
+	}
+
+	defineExpose({
+		visible
+	})
+</script>
+
+<style lang="less" scoped>
+	@import '@/style/myResource/varibles.less';
+
+	.unzip-tree-wrapper {
+		height: 300px;
+		overflow: auto;
+
+		.target-path {
+			display: flex;
+			align-items: center;
+			margin-bottom: 16px;
+
+			.label {
+				width: 80px;
+			}
+
+			.ant-input {
+				flex: 1;
+			}
+		}
+
+		.custom-tree-node {
+			width: 100%;
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+
+			.add-folder-btn {
+				display: none;
+			}
+
+			&:hover .add-folder-btn {
+				display: inline-block;
+			}
+		}
+	}
+
+	.unzip-text {
+		padding: 40px 0 64px 0;
+		text-align: center;
+
+		.ant-spin {
+			margin-right: 8px;
+		}
+	}
+
+	.ant-btn-primary {
+		&,
+		&:hover,
+		&:focus {
+			background-color: @primary-color;
+			border-color: @primary-color;
+		}
+	}
+
+	.ant-tree-node-selected {
+		background-color: @primary-color;
+	}
+</style>

+ 225 - 103
src/views/myResource/file/dialog/unzipFile/Dialog.vue

@@ -1,202 +1,324 @@
 <template>
+	<!-- 解压缩文件 -->
 	<a-modal
 		title="解压缩文件"
-		v-model:visible="visible"
+		:visible="internalVisible"
 		:maskClosable="false"
-		:closable="false"
-		@afterVisibleChange="handleVisibleChange"
+		@ok="handleDialogSure"
 		@cancel="handleDialogClose"
+		:afterClose="handleDialogClose"
 	>
 		<div class="unzip-tree-wrapper" v-if="unzipMode === 2">
+			<!-- 选择的目标路径 -->
 			<div class="target-path">
 				<span class="label">目标路径:</span>
-				<a-input v-model:value="targetPath" readonly size="small" />
+				<a-input class="content" v-model:value="targetPath" readonly size="small"></a-input>
 			</div>
-
+			<!-- 文件目录树 -->
 			<a-spin :spinning="loading">
-				<a-directory-tree
-					:treeData="fileTree"
-					:fieldNames="{
-						children: 'children',
-						title: 'label'
-					}"
-					:defaultExpandedKeys="defaultExpandedKeys"
+				<a-tree
+					:tree-data="fileTree"
+					:field-names="{ children: 'children', title: 'label', key: 'id' }"
+					highlight-current
+					:expandedKeys="defaultExpandedKeys"
+					@update:expandedKeys="(val) => (defaultExpandedKeys = val)"
 					@select="handleNodeClick"
+					:showLine="true"
 				>
-					<template #title="{ node, data }">
-						<div class="custom-tree-node">
-							<span class="label">{{ node.title }}</span>
-							<a-button type="link" size="small" class="add-folder-btn" @click.stop="handleAddFolderBtnClick(data)">
+					<template #title="{ dataRef }">
+						<span class="custom-tree-node">
+							<span class="label">{{ dataRef.label }}</span>
+							<a-button class="add-folder-btn" type="link" size="small" @click.stop="handleAddFolderBtnClick(dataRef)">
 								新建文件夹
 							</a-button>
-						</div>
+						</span>
 					</template>
-				</a-directory-tree>
+				</a-tree>
 			</a-spin>
 		</div>
-		<div class="unzip-text" v-else>
-			<a-spin />
-			正在解压缩,请稍等片刻...
-		</div>
-
+		<div class="unzip-text" v-else><a-spin /> 正在解压缩,请稍等片刻...</div>
 		<template #footer>
-			<template v-if="unzipMode === 2">
+			<div v-if="unzipMode === 2">
 				<a-button @click="handleDialogClose">取 消</a-button>
-				<a-button type="primary" :loading="sureBtnLoading" @click="handleDialogSure"> 确 定 </a-button>
-			</template>
+				<a-button type="primary" :loading="sureBtnLoading" @click="handleDialogSure">确 定</a-button>
+			</div>
 		</template>
 	</a-modal>
 </template>
 
 <script setup>
-	import { ref } from 'vue'
-	import { message } from 'ant-design-vue'
+	import { ref, getCurrentInstance, watch } from 'vue'
 	import { getFoldTree, unzipFile } from '@/api/myResource/file'
+	import { message } from 'ant-design-vue'
+
 	const { proxy } = getCurrentInstance()
 
 	const props = defineProps({
-		unzipMode: Number,
-		userFileId: String,
+		visible: {
+			type: Boolean,
+			default: false
+		},
+		unzipMode: {
+			type: Number,
+			default: 0
+		},
+		userFileId: {
+			type: String,
+			default: ''
+		},
 		callback: Function
 	})
 
-	const visible = ref(false)
-	const targetPath = ref('/')
-	const fileTree = ref([])
-	const loading = ref(false)
+	const emit = defineEmits(['update:visible'])
+	const internalVisible = ref(props.visible) // 使用 internalVisible 作为主要的响应式状态
+	const targetPath = ref('/') //  目标路径
+	const fileTree = ref([]) //  文件夹目录树
+	const loading = ref(false) //  文件夹目录树 loading 状态
 	const defaultExpandedKeys = ref([])
-	const sureBtnLoading = ref(false)
+	const sureBtnLoading = ref(false) //  确定按钮 loading 状态
+
+	watch(
+		internalVisible, // 监听内部的 internalVisible ref
+		(newVal, oldVal) => {
+			if (newVal && !oldVal) {
+				// 仅在从 false 变为 true 时调用,即对话框打开时
+				handleDialogOpen()
+			}
+		}
+	)
+
+	// 如果父组件通过 prop 更新 visible,也需要同步到 internalVisible
+	watch(
+		() => props.visible,
+		(newVal) => {
+			internalVisible.value = newVal
+		}
+	)
 
+	/**
+	 * 取消按钮点击事件 & 对话框关闭的回调
+	 */
 	const handleDialogClose = () => {
-		visible.value = false
+		internalVisible.value = false
+		emit('update:visible', false) // 更新父组件的 visible 值
 		props.callback('cancel')
 	}
 
-	const handleVisibleChange = (val) => {
-		if (val) {
-			if (props.unzipMode === 2) {
-				initFileTree()
-			} else {
-				handleUnzipFile()
-			}
+	/**
+	 * 对话框打开的回调
+	 */
+	const handleDialogOpen = () => {
+		if (props.unzipMode === 2) {
+			initFileTree()
+		} else {
+			handleUnzipFile()
 		}
 	}
 
+	/**
+	 * 初始化文件目录树
+	 */
 	const initFileTree = async (id) => {
+		loading.value = true
 		try {
-			loading.value = true
 			const res = await getFoldTree()
 			if (res.success) {
 				fileTree.value = [res.data]
-				defaultExpandedKeys.value = id ? [id] : [fileTree.value[0].id]
+				defaultExpandedKeys.value = id ? [id] : fileTree.value.length > 0 ? [fileTree.value[0].id] : []
 			} else {
 				message.error(res.message)
 			}
+		} catch (error) {
+			message.error('获取文件夹树失败')
+			console.error(error)
 		} finally {
 			loading.value = false
 		}
 	}
 
-	const handleNodeClick = (selectedKeys, { node }) => {
-		targetPath.value = node.filePath || '/'
+	/**
+	 * 目录树节点点击回调函数
+	 * @param {Array} selectedKeys 当前选中的节点的 key
+	 * @param {object} e event 对象,包含 {selected: bool, selectedNodes, node, event}
+	 */
+	const handleNodeClick = (selectedKeys, e) => {
+		if (e.node && e.node.dataRef) {
+			targetPath.value = e.node.dataRef.filePath ? e.node.dataRef.filePath : '/'
+		}
 	}
 
+	/**
+	 * 新建文件夹按钮点击事件
+	 * @description 调用新建文件夹服务,并在弹窗确认回调事件中刷新文件夹树
+	 */
 	const handleAddFolderBtnClick = async (data) => {
-		const result = await proxy.$openDialog.addFolder({
-			filePath: data.filePath || '/'
-		})
-		if (result === 'confirm') {
+		try {
+			await proxy.$openDialog.addFolder({
+				filePath: data.filePath || '/'
+			})
 			initFileTree(data.id)
+		} catch (error) {
+			console.log('Add folder cancelled or failed', error)
 		}
 	}
 
+	/**
+	 * 确定按钮点击事件
+	 * @description 调用移动文件接口
+	 */
+	const handleDialogSure = () => {
+		sureBtnLoading.value = true
+		handleUnzipFile()
+	}
+
+	/**
+	 * 解压文件
+	 */
 	const handleUnzipFile = async () => {
-		try {
-			sureBtnLoading.value = true
-			const reqData = {
-				unzipMode: props.unzipMode,
-				userFileId: props.userFileId,
-				...(props.unzipMode === 2 ? { filePath: targetPath.value } : {})
+		// unzipMode 解压模式 0-解压到当前文件夹, 1-自动创建该文件名目录,并解压到目录里, 2-手动选择解压目录
+		let reqData = {
+			unzipMode: props.unzipMode,
+			userFileId: props.userFileId
+		}
+		if (props.unzipMode === 2) {
+			reqData = {
+				...reqData,
+				filePath: targetPath.value //  解压的目标路径
 			}
+		}
 
+		try {
 			const res = await unzipFile(reqData)
+			sureBtnLoading.value = false
 			if (res.success) {
 				message.success('解压成功')
-				visible.value = false
+				internalVisible.value = false
+				emit('update:visible', false)
 				props.callback('confirm')
 			} else {
 				message.error(res.message)
 			}
-		} finally {
+		} catch (error) {
 			sureBtnLoading.value = false
+			message.error('解压失败')
+			console.error(error)
 		}
 	}
 
-	const handleDialogSure = () => {
-		handleUnzipFile()
-	}
-
+	// 暴露给模板或者父组件(如果需要ref访问)
 	defineExpose({
-		visible
+		visible: internalVisible // 暴露 internalVisible
 	})
 </script>
 
 <style lang="less" scoped>
 	@import '@/style/myResource/varibles.less';
+	@import '@/style/myResource/mixins.less';
 
-	.unzip-tree-wrapper {
-		height: 300px;
-		overflow: auto;
-
-		.target-path {
+	// 使用 :deep 替代 >>>
+	:deep(.ant-modal) {
+		.ant-modal-header {
 			display: flex;
-			align-items: center;
-			margin-bottom: 16px;
+		}
 
-			.label {
-				width: 80px;
-			}
+		.ant-modal-body {
+			padding: 10px 30px;
 
-			.ant-input {
-				flex: 1;
-			}
-		}
+			.unzip-tree-wrapper {
+				height: 300px;
+				overflow: auto;
+				&::-webkit-scrollbar {
+					width: 6px;
+				}
+				&::-webkit-scrollbar-thumb {
+					background: #ccc; // 滚动条颜色
+					border-radius: 3px;
+				}
+				&::-webkit-scrollbar-track {
+					background: #f1f1f1; // 轨道颜色
+				}
 
-		.custom-tree-node {
-			width: 100%;
-			display: flex;
-			align-items: center;
-			justify-content: space-between;
+				.target-path {
+					display: flex;
+					align-items: center;
+					margin-bottom: 10px;
 
-			.add-folder-btn {
-				display: none;
-			}
+					.label {
+						width: 80px;
+						flex-shrink: 0;
+					}
+
+					.content {
+						flex: 1;
+					}
+				}
+
+				.ant-tree {
+					.ant-tree-treenode {
+						width: 100%;
+						// antd v4+ 使用 .ant-tree-treenode
+						.ant-tree-node-content-wrapper {
+							height: 34px;
+							font-size: 16px;
+							display: flex;
+							align-items: center;
+						}
+						&:hover {
+							.add-folder-btn {
+								display: inline-block;
+							}
+						}
+
+						.ant-tree-switcher .ant-tree-switcher-icon svg {
+							font-size: 18px;
+						}
+
+						.custom-tree-node {
+							flex: 1;
+							display: flex;
+							align-items: center;
+							justify-content: space-between;
+							font-size: 14px;
+							padding-right: 8px;
 
-			&:hover .add-folder-btn {
-				display: inline-block;
+							.add-folder-btn {
+								color: #000000;
+								display: none;
+								margin-left: 8px;
+							}
+						}
+					}
+				}
+			}
+			.unzip-text {
+				padding: 40px 0 64px 0;
+				text-align: center;
 			}
 		}
 	}
 
-	.unzip-text {
-		padding: 40px 0 64px 0;
-		text-align: center;
+	:deep(.ant-tree .ant-tree-treenode .custom-tree-node) {
+		display: flex;
+		justify-content: space-between;
+		width: 100%;
+	}
 
-		.ant-spin {
-			margin-right: 8px;
-		}
+	:deep(.ant-tree .ant-tree-treenode:hover .custom-tree-node .add-folder-btn) {
+		color: @primary-color !important;
+		display: inline-block !important;
 	}
 
-	.ant-btn-primary {
-		&,
-		&:hover,
-		&:focus {
-			background-color: @primary-color;
-			border-color: @primary-color;
-		}
+	:deep(.ant-tree .ant-tree-treenode .custom-tree-node .add-folder-btn) {
+		color: #000000;
+		display: none !important;
+		margin-left: 8px;
 	}
 
-	.ant-tree-node-selected {
-		background-color: @primary-color;
+	:deep(.ant-tree) {
+		.ant-tree-treenode,
+		.ant-tree-node-content-wrapper,
+		.ant-tree-title {
+			width: 100%;
+		}
 	}
 </style>