feat(course): 添加作业组件及文件上传功能

- 新增 HomeWorkComp 组件用于作业内容配置
- 实现作业名称、内容、附件、截至日期和提交模式设置
- 集成 FileUpload 组件支持作业附件上传与管理
- 更新 createCourse.vue 引入并注册 HomeWorkComp
- 修改 addHomework 方法以打开作业设置对话框
- 添加相关依赖包如 fastify 及其生态库
- 调整部分包的引用关系去除 dev 标记
This commit is contained in:
陈昱达
2025-11-25 17:20:50 +08:00
parent 6c87968ab4
commit 13bfa1a58b
4 changed files with 714 additions and 25 deletions

280
package-lock.json generated
View File

@@ -1323,6 +1323,19 @@
"lodash.uniq": "^4.5.0"
}
},
"@fastify/ajv-compiler": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/@fastify/ajv-compiler/-/ajv-compiler-1.1.0.tgz",
"integrity": "sha512-gvCOUNpXsWrIQ3A4aXCLIdblL0tDq42BG/2Xw7oxbil9h11uow10ztS2GuFazNBfjbrsZ5nl+nPl5jDSjj5TSg==",
"requires": {
"ajv": "^6.12.6"
}
},
"@fastify/error": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/@fastify/error/-/error-2.0.0.tgz",
"integrity": "sha512-wI3fpfDT0t7p8E6dA2eTECzzOd+bZsZCJ2Hcv+Onn2b7ZwK3RwD27uW2QDaMtQhAfWQQP+WNK7nKf0twLsBf9w=="
},
"@floating-ui/core": {
"version": "1.2.6",
"resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.2.6.tgz",
@@ -2735,6 +2748,11 @@
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
"dev": true
},
"abstract-logging": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/abstract-logging/-/abstract-logging-2.0.1.tgz",
"integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA=="
},
"accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz",
@@ -2779,7 +2797,6 @@
"version": "6.12.6",
"resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -2960,6 +2977,11 @@
}
}
},
"archy": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/archy/-/archy-1.0.0.tgz",
"integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw=="
},
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz",
@@ -3013,6 +3035,11 @@
"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
"dev": true
},
"atomic-sleep": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
"integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="
},
"autoprefixer": {
"version": "10.4.14",
"resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.14.tgz",
@@ -3027,6 +3054,17 @@
"postcss-value-parser": "^4.2.0"
}
},
"avvio": {
"version": "7.2.5",
"resolved": "https://registry.npmmirror.com/avvio/-/avvio-7.2.5.tgz",
"integrity": "sha512-AOhBxyLVdpOad3TujtC9kL/9r3HnTkxwQ5ggOsYrvvZP1cCFvzHWJd5XxZDFuTn+IN8vkKSG5SEJrd27vCSbeA==",
"requires": {
"archy": "^1.0.0",
"debug": "^4.0.0",
"fastq": "^1.6.1",
"queue-microtask": "^1.1.2"
}
},
"axios": {
"version": "1.4.0",
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.4.0.tgz",
@@ -3787,8 +3825,7 @@
"cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.5.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
"dev": true
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
},
"cookie-signature": {
"version": "1.0.6",
@@ -5227,6 +5264,11 @@
"resolved": "https://registry.npmmirror.com/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
"fast-content-type-parse": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz",
"integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ=="
},
"fast-csv": {
"version": "4.3.6",
"resolved": "https://registry.npmmirror.com/fast-csv/-/fast-csv-4.3.6.tgz",
@@ -5236,11 +5278,15 @@
"@fast-csv/parse": "4.3.6"
}
},
"fast-decode-uri-component": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz",
"integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="
},
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"fast-diff": {
"version": "1.3.0",
@@ -5274,8 +5320,25 @@
"fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
},
"fast-json-stringify": {
"version": "2.7.13",
"resolved": "https://registry.npmmirror.com/fast-json-stringify/-/fast-json-stringify-2.7.13.tgz",
"integrity": "sha512-ar+hQ4+OIurUGjSJD1anvYSDcUflywhKjfxnsW4TBTD7+u0tJufv6DKRWoQk3vI6YBOWMoz0TQtfbe7dxbQmvA==",
"requires": {
"ajv": "^6.11.0",
"deepmerge": "^4.2.2",
"rfdc": "^1.2.0",
"string-similarity": "^4.0.1"
},
"dependencies": {
"deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
}
}
},
"fast-levenshtein": {
"version": "2.0.6",
@@ -5283,11 +5346,55 @@
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true
},
"fast-redact": {
"version": "3.5.0",
"resolved": "https://registry.npmmirror.com/fast-redact/-/fast-redact-3.5.0.tgz",
"integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A=="
},
"fast-safe-stringify": {
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
"integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="
},
"fast-uri": {
"version": "3.1.0",
"resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.1.0.tgz",
"integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="
},
"fastify": {
"version": "3.29.5",
"resolved": "https://registry.npmmirror.com/fastify/-/fastify-3.29.5.tgz",
"integrity": "sha512-FBDgb1gkenZxxh4sTD6AdI6mFnZnsgckpjIXzIvfLSYCa4isfQeD8QWGPib63dxq6btnY0l1j8I0xYhMvUb+sw==",
"requires": {
"@fastify/ajv-compiler": "^1.0.0",
"@fastify/error": "^2.0.0",
"abstract-logging": "^2.0.0",
"avvio": "^7.1.2",
"fast-content-type-parse": "^1.0.0",
"fast-json-stringify": "^2.5.2",
"find-my-way": "^4.5.0",
"flatstr": "^1.0.12",
"light-my-request": "^4.2.0",
"pino": "^6.13.0",
"process-warning": "^1.0.0",
"proxy-addr": "^2.0.7",
"rfdc": "^1.1.4",
"secure-json-parse": "^2.0.0",
"semver": "^7.3.2",
"tiny-lru": "^8.0.1"
},
"dependencies": {
"semver": {
"version": "7.7.3",
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="
}
}
},
"fastq": {
"version": "1.15.0",
"resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.15.0.tgz",
"integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
"dev": true,
"requires": {
"reusify": "^1.0.4"
}
@@ -5371,6 +5478,17 @@
"pkg-dir": "^4.1.0"
}
},
"find-my-way": {
"version": "4.5.1",
"resolved": "https://registry.npmmirror.com/find-my-way/-/find-my-way-4.5.1.tgz",
"integrity": "sha512-kE0u7sGoUFbMXcOG/xpkmz4sRLCklERnBcg7Ftuu1iAxsfEt2S46RLJ3Sq7vshsEy2wJT2hZxE58XZK27qa8kg==",
"requires": {
"fast-decode-uri-component": "^1.0.1",
"fast-deep-equal": "^3.1.3",
"safe-regex2": "^2.0.0",
"semver-store": "^0.3.0"
}
},
"find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz",
@@ -5391,6 +5509,11 @@
"rimraf": "^3.0.2"
}
},
"flatstr": {
"version": "1.0.12",
"resolved": "https://registry.npmmirror.com/flatstr/-/flatstr-1.0.12.tgz",
"integrity": "sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw=="
},
"flatted": {
"version": "3.2.7",
"resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.2.7.tgz",
@@ -5415,8 +5538,7 @@
"forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz",
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"dev": true
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
},
"fraction.js": {
"version": "4.2.0",
@@ -6221,8 +6343,7 @@
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
},
"json-stable-stringify-without-jsonify": {
"version": "1.0.1",
@@ -6372,6 +6493,35 @@
"immediate": "~3.0.5"
}
},
"light-my-request": {
"version": "4.12.0",
"resolved": "https://registry.npmmirror.com/light-my-request/-/light-my-request-4.12.0.tgz",
"integrity": "sha512-0y+9VIfJEsPVzK5ArSIJ8Dkxp8QMP7/aCuxCUtG/tr9a2NoOf/snATE/OUc05XUplJCEnRh6gTkH7xh9POt1DQ==",
"requires": {
"ajv": "^8.1.0",
"cookie": "^0.5.0",
"process-warning": "^1.0.0",
"set-cookie-parser": "^2.4.1"
},
"dependencies": {
"ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"requires": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2"
}
},
"json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
}
}
},
"lilconfig": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-2.1.0.tgz",
@@ -7550,6 +7700,25 @@
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true
},
"pino": {
"version": "6.14.0",
"resolved": "https://registry.npmmirror.com/pino/-/pino-6.14.0.tgz",
"integrity": "sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg==",
"requires": {
"fast-redact": "^3.0.0",
"fast-safe-stringify": "^2.0.8",
"flatstr": "^1.0.12",
"pino-std-serializers": "^3.1.0",
"process-warning": "^1.0.0",
"quick-format-unescaped": "^4.0.3",
"sonic-boom": "^1.0.2"
}
},
"pino-std-serializers": {
"version": "3.2.0",
"resolved": "https://registry.npmmirror.com/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz",
"integrity": "sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg=="
},
"pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-4.2.0.tgz",
@@ -7972,6 +8141,11 @@
"resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"process-warning": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/process-warning/-/process-warning-1.0.0.tgz",
"integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q=="
},
"progress": {
"version": "2.0.3",
"resolved": "https://registry.npmmirror.com/progress/-/progress-2.0.3.tgz",
@@ -7993,7 +8167,6 @@
"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==",
"dev": true,
"requires": {
"forwarded": "0.2.0",
"ipaddr.js": "1.9.1"
@@ -8002,8 +8175,7 @@
"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==",
"dev": true
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
}
}
},
@@ -8031,8 +8203,7 @@
"punycode": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.0.tgz",
"integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
"dev": true
"integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA=="
},
"qrcode.vue": {
"version": "3.4.0",
@@ -8050,8 +8221,12 @@
"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
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
},
"quick-format-unescaped": {
"version": "4.0.4",
"resolved": "https://registry.npmmirror.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="
},
"quill": {
"version": "2.0.3",
@@ -12976,8 +13151,7 @@
"require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"dev": true
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="
},
"requires-port": {
"version": "1.0.0",
@@ -13016,6 +13190,11 @@
"signal-exit": "^3.0.2"
}
},
"ret": {
"version": "0.2.2",
"resolved": "https://registry.npmmirror.com/ret/-/ret-0.2.2.tgz",
"integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ=="
},
"retry": {
"version": "0.13.1",
"resolved": "https://registry.npmmirror.com/retry/-/retry-0.13.1.tgz",
@@ -13025,8 +13204,12 @@
"reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz",
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
"dev": true
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="
},
"rfdc": {
"version": "1.4.1",
"resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz",
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="
},
"rimraf": {
"version": "3.0.2",
@@ -13050,6 +13233,14 @@
"resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
},
"safe-regex2": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/safe-regex2/-/safe-regex2-2.0.0.tgz",
"integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==",
"requires": {
"ret": "~0.2.0"
}
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -13104,6 +13295,11 @@
"compute-scroll-into-view": "^1.0.20"
}
},
"secure-json-parse": {
"version": "2.7.0",
"resolved": "https://registry.npmmirror.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz",
"integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw=="
},
"select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/select-hose/-/select-hose-2.0.0.tgz",
@@ -13125,6 +13321,11 @@
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true
},
"semver-store": {
"version": "0.3.0",
"resolved": "https://registry.npmmirror.com/semver-store/-/semver-store-0.3.0.tgz",
"integrity": "sha512-TcZvGMMy9vodEFSse30lWinkj+JgOBvPn8wRItpQRSayhc+4ssDs335uklkfvQQJgL/WvmHLVj4Ycv2s7QCQMg=="
},
"send": {
"version": "0.18.0",
"resolved": "https://registry.npmmirror.com/send/-/send-0.18.0.tgz",
@@ -13260,6 +13461,11 @@
"send": "0.18.0"
}
},
"set-cookie-parser": {
"version": "2.7.2",
"resolved": "https://registry.npmmirror.com/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
"integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="
},
"set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz",
@@ -13470,6 +13676,14 @@
"resolved": "https://registry.npmmirror.com/snabbdom/-/snabbdom-3.5.1.tgz",
"integrity": "sha512-wHMNIOjkm/YNE5EM3RCbr/+DVgPg6AqQAX1eOxO46zYNvCXjKP5Y865tqQj3EXnaMBjkxmQA5jFuDpDK/dbfiA=="
},
"snowflake-id-js": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/snowflake-id-js/-/snowflake-id-js-1.0.1.tgz",
"integrity": "sha512-LKwQusK2JNQiS4W378PbvJIxwgCZ+VzTpavzxI2qtunQV7tmsGxUj5GNuNn7oFdveSJVa123S0b8L+4kbEw/eg==",
"requires": {
"fastify": "^3.15.1"
}
},
"sockjs": {
"version": "0.3.24",
"resolved": "https://registry.npmmirror.com/sockjs/-/sockjs-0.3.24.tgz",
@@ -13481,6 +13695,15 @@
"websocket-driver": "^0.7.4"
}
},
"sonic-boom": {
"version": "1.4.1",
"resolved": "https://registry.npmmirror.com/sonic-boom/-/sonic-boom-1.4.1.tgz",
"integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==",
"requires": {
"atomic-sleep": "^1.0.0",
"flatstr": "^1.0.12"
}
},
"sortablejs": {
"version": "1.15.0",
"resolved": "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.15.0.tgz",
@@ -13604,6 +13827,11 @@
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"dev": true
},
"string-similarity": {
"version": "4.0.4",
"resolved": "https://registry.npmmirror.com/string-similarity/-/string-similarity-4.0.4.tgz",
"integrity": "sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ=="
},
"string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
@@ -13880,6 +14108,11 @@
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
"dev": true
},
"tiny-lru": {
"version": "8.0.2",
"resolved": "https://registry.npmmirror.com/tiny-lru/-/tiny-lru-8.0.2.tgz",
"integrity": "sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg=="
},
"tiny-warning": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/tiny-warning/-/tiny-warning-1.0.3.tgz",
@@ -14070,7 +14303,6 @@
"version": "4.4.1",
"resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
"requires": {
"punycode": "^2.1.0"
}

View File

@@ -0,0 +1,122 @@
<script setup>
import {
ElForm,
ElFormItem,
ElInput,
ElRadio,
ElRadioGroup,
ElUpload,
ElDatePicker,
ElButton,
} from "element-plus";
import { Close } from "@element-plus/icons-vue";
defineOptions({
resType: 60,
});
import FileUpload from "@/components/FileUpload/index.vue";
const props = defineProps({
dialogVideoForm: {
type: Object,
default: () => ({
name: "",
filePath: "",
isDrag: true,
completeSetup: 0,
setupTage: 0,
openType: "",
}),
},
isPreview: {
type: Boolean,
default: false,
},
});
import { useMediaComponent } from "@/hooks/useMediaComponent";
// Emit updates to parent component
const emit = defineEmits(["update:dialogVideoForm"]);
// 使用hook处理公共逻辑
const { localDialogVideoForm, updateFormValue, fileBaseUrl } =
useMediaComponent(props, emit);
// 作业上传
const uploadHomeworkFile = (res) => {
localDialogVideoForm.value.file = res.result.filePath;
updateFormValue("file", res.result.filePath);
};
const removeHomeworkFile = () => {
localDialogVideoForm.value.file = "";
updateFormValue("file", "");
};
</script>
<template>
<el-form label-position="right" label-width="100px">
<el-form-item label="名称">
<el-input
v-model="localDialogVideoForm.name"
:disabled="isPreview"
placeholder="作业的名称(限50字以内)"
maxlength="50"
show-word-limit
@update:modelValue="(val) => updateFormValue('name', val)"
></el-input>
</el-form-item>
<el-form-item label="内容">
<el-input
type="textarea"
placeholder="限255字以内"
show-word-limit
v-model="localDialogVideoForm.content"
:disabled="isPreview"
maxlength="255"
@update:modelValue="(val) => updateFormValue('content', val)"
></el-input>
</el-form-item>
<el-form-item label="附件">
<div v-if="localDialogVideoForm.file !== ''" class="flex align-center">
<a :href="fileBaseUrl + localDialogVideoForm.file" target="_blank"
>下载作业附件
</a>
<Close
@click="removeHomeworkFile"
v-if="!isPreview"
class="ml10"
style="color: red; width: 15px; cursor: pointer"
></Close>
</div>
<file-upload
v-else
dir="files"
:isShowTip="false"
:disabled="isPreview"
@success="uploadHomeworkFile"
@remove="removeHomeworkFile"
></file-upload>
</el-form-item>
<el-form-item label="截至日期">
<el-date-picker
v-model="localDialogVideoForm.deadTime"
:disabled="isPreview"
value-format="yyyy-MM-dd HH:mm:ss"
type="datetime"
placeholder="选择日期时间"
@update:modelValue="(val) => updateFormValue('deadTime', val)"
></el-date-picker>
</el-form-item>
<el-form-item label="提交模式">
<el-radio-group
:model-value="localDialogVideoForm.submitMode"
:disabled="isPreview"
@update:modelValue="(val) => updateFormValue('submitMode', val)"
>
<el-radio :label="1">附件文档</el-radio>
<el-radio :label="2">在线填写</el-radio>
<el-radio :label="3">前两种任选</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</template>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,331 @@
<template>
<div class="upload-file">
<el-upload
:action="uploadFileUrl"
:before-upload="handleBeforeUpload"
:file-list="fileList"
:limit="limit"
:multiple="limit > 1"
:on-error="handleUploadError"
:on-exceed="handleExceed"
:on-success="handleUploadSuccess"
:show-file-list="showList"
:data="data"
:headers="headers"
class="upload-file-uploader"
ref="upload"
:disabled="disabled"
>
<!-- 上传按钮 -->
<el-button
:icon="loading ? 'el-icon-upload2' : ''"
:size="size"
type="primary"
:loading="isLoading"
:disabled="disabled"
>{{ text }}</el-button
>
<!-- 上传提示 -->
<div class="el-upload__tip" slot="tip" v-if="showTip">
请上传
<template v-if="fileSize">
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
</template>
<template v-if="fileType">
格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
</template>
的文件
</div>
</el-upload>
<!-- 文件列表 -->
<transition-group
class="upload-file-list el-upload-list el-upload-list--text"
name="el-fade-in-linear"
tag="ul"
>
<li
:key="file.uid"
class="el-upload-list__item ele-upload-list__item-content"
v-for="(file, index) in list"
>
<el-link :href="file.url" :underline="false" target="_blank">
<span class="el-icon-document"> {{ getFileName(file.name) }} </span>
</el-link>
<div class="ele-upload-list__item-content-action">
<el-link :underline="false" @click="handleDelete(index)" type="danger"
>删除</el-link
>
</div>
</li>
</transition-group>
</div>
</template>
<script setup>
import { ref, reactive, computed, onMounted, watch } from "vue";
import { getCookie } from "@/api/method";
import { ElMessage, ElUpload, ElButton } from "element-plus";
// 定义 props
const props = defineProps({
disabled: {
type: Boolean,
default: false,
},
// 值
value: [String, Object, Array],
beforeMsg: {
type: String,
default: "",
},
showList: {
type: Boolean,
default: false,
},
url: {
type: String,
default: "/xboe/sys/xuploader/file/upload",
},
limit: {
type: Number,
default: 1,
},
dir: {
type: String,
default: "files",
},
text: {
type: String,
default: "选择文件并上传",
},
size: {
type: String,
default: "small",
},
// 大小限制(MB)
fileSize: {
type: Number,
default: 1024,
},
scorm: {
type: String,
default: "",
},
// 文件类型, 例如['png', 'jpg', 'jpeg']
fileType: {
type: Array,
default: () => [
"doc",
"xls",
"ppt",
"docx",
"xlsx",
"pptx",
"png",
"pdf",
"jpg",
"gif",
"mp4",
"mp3",
"zip",
],
},
// 是否显示提示
isShowTip: {
type: Boolean,
default: true,
},
loading: {
type: Boolean,
default: false,
},
});
// 定义 emits
const emit = defineEmits(["success", "remove"]);
// 响应式数据
const uploadFileUrl = ref(process.env.VUE_APP_SYS_API + props.url);
const headers = reactive({
"XBOE-Access-Token": getCookie("token"),
});
const isLoading = ref(false);
const fileList = ref([]);
const data = reactive({
dir: props.dir,
});
const upload = ref(null);
// 计算属性
const showTip = computed(() => {
return props.isShowTip && (props.fileType || props.fileSize);
});
const list = computed(() => {
let temp = 1;
if (props.value) {
// 首先将值转为数组
const list = Array.isArray(props.value) ? props.value : [props.value];
// 然后将数组转为对象数组
return list.map((item) => {
if (typeof item === "string") {
item = { name: item, url: item };
}
item.uid = item.uid || new Date().getTime() + temp++;
return item;
});
} else {
fileList.value = [];
return [];
}
});
// 方法
// 上传前校检格式和大小
const handleBeforeUpload = (file) => {
if (props.beforeMsg) {
ElMessage({ message: props.beforeMsg, type: "error", offset: 100 });
return false;
}
// 校检文件类型
if (props.fileType) {
let fileExtension = "";
if (file.name.lastIndexOf(".") > -1) {
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
}
const isTypeOk = props.fileType.some((type) => {
if (file.type.indexOf(type) > -1) return true;
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
return false;
});
if (!isTypeOk) {
ElMessage({
message: `文件格式不正确, 请上传${props.fileType.join("/")}格式文件!`,
type: "error",
offset: 100,
});
return false;
}
if (props.scorm && props.scorm == fileExtension) {
data.dir = "scorm";
}
}
// 校检文件大小
if (props.fileSize) {
const isLt = file.size / 1024 / 1024 < props.fileSize;
if (!isLt) {
ElMessage({
message: `上传文件大小不能超过 ${props.fileSize} MB!`,
type: "error",
offset: 100,
});
return false;
}
}
if (props.loading) {
isLoading.value = true;
}
return true;
};
// 文件个数超出
const handleExceed = (res) => {
ElMessage({
message: `一次性最多上传${props.limit}个文件`,
type: "error",
offset: 100,
});
};
// 上传失败
const handleUploadError = (err) => {
isLoading.value = false;
ElMessage({
message: "上传失败, 请重试",
type: "error",
offset: 100,
});
};
// 上传成功回调
const handleUploadSuccess = (res, file, fileList) => {
if (res.status == 200) {
isLoading.value = false;
ElMessage({ message: "上传成功", type: "success", offset: 100 });
let delIdx = -1;
fileList.some((fl, flIndx) => {
if (fl.uid == file.uid) {
delIdx = flIndx;
return true;
} else {
return false;
}
});
if (delIdx > -1) {
fileList.splice(delIdx, 1);
}
emit("success", res);
} else {
isLoading.value = false;
if (props.limit == 1) {
fileList.value = [];
}
ElMessage({ message: "上传失败", type: "error", offset: 100 });
}
};
// 删除文件
const handleDelete = (index) => {
fileList.value.splice(index, 1);
emit("remove", "");
};
// 获取文件名称
const getFileName = (name) => {
if (name.lastIndexOf("/") > -1) {
return name.slice(name.lastIndexOf("/") + 1).toLowerCase();
} else {
return "";
}
};
// 生命周期钩子
onMounted(() => {
//计算是http还是https
let urlPre = window.location.protocol;
uploadFileUrl.value =
urlPre +
uploadFileUrl.value.substring(uploadFileUrl.value.indexOf(":") + 1);
fileList.value = list.value;
});
// 监听器
watch(list, (newVal) => {
fileList.value = newVal;
});
</script>
<style scoped lang="scss">
.upload-file-uploader {
margin-bottom: 5px;
}
.upload-file-list .el-upload-list__item {
border: 1px solid #e4e7ed;
line-height: 2;
margin-bottom: 10px;
position: relative;
}
.upload-file-list .ele-upload-list__item-content {
display: flex;
justify-content: space-between;
align-items: center;
color: inherit;
}
.ele-upload-list__item-content-action .el-link {
margin-right: 10px;
}
</style>

View File

@@ -15,6 +15,7 @@ import DocComp from "@/components/CreatedCourse/preview/DocComp.vue";
import LinkComp from "@/components/CreatedCourse/preview/LinkComp.vue";
import ScormComp from "@/components/CreatedCourse/preview/ScormComp.vue";
import PaperComp from "@/components/CreatedCourse/preview/PaperComp.vue";
import HomeWorkComp from "@/components/CreatedCourse/preview/HomeWorkComp.vue";
import { getType } from "@/hooks/useCreateCourseMaps";
const mapComponents = [
VideoComp,
@@ -24,6 +25,7 @@ const mapComponents = [
LinkComp,
ScormComp,
PaperComp,
HomeWorkComp,
];
// 使用课程数据hook
@@ -86,7 +88,9 @@ const courseOperations = {
isNext.value = false;
},
addHomework: () => {
console.log("添加作业功能调用");
courseMetadata.resType = 60;
chooseItemData.value.resType = 60;
showSettingDialog.value = true;
},
addAssessment: () => {
console.log("添加评估功能调用");