提交 bf03f0ee authored 作者: 王鹏飞's avatar 王鹏飞

chore: 修改创建课程页面

上级 a3c488f0
......@@ -81,6 +81,8 @@
"refThrottled": true,
"refWithControl": true,
"resolveComponent": true,
"resolveRef": true,
"resolveUnref": true,
"shallowReactive": true,
"shallowReadonly": true,
"shallowRef": true,
......@@ -108,6 +110,7 @@
"useAttrs": true,
"useBase64": true,
"useBattery": true,
"useBluetooth": true,
"useBreakpoints": true,
"useBroadcastChannel": true,
"useBrowserLocation": true,
......@@ -146,6 +149,7 @@
"useEyeDropper": true,
"useFavicon": true,
"useFetch": true,
"useFileDialog": true,
"useFileSystemAccess": true,
"useFocus": true,
"useFocusWithin": true,
......@@ -154,6 +158,7 @@
"useGamepad": true,
"useGeolocation": true,
"useIdle": true,
"useImage": true,
"useInfiniteScroll": true,
"useIntersectionObserver": true,
"useInterval": true,
......@@ -175,6 +180,7 @@
"useNavigatorLanguage": true,
"useNetwork": true,
"useNow": true,
"useObjectUrl": true,
"useOffsetPagination": true,
"useOnline": true,
"usePageLeave": true,
......@@ -200,12 +206,14 @@
"useSlots": true,
"useSpeechRecognition": true,
"useSpeechSynthesis": true,
"useStepper": true,
"useStorage": true,
"useStorageAsync": true,
"useStyleTag": true,
"useSwipe": true,
"useTemplateRefsList": true,
"useTextSelection": true,
"useTextareaAutosize": true,
"useThrottle": true,
"useThrottleFn": true,
"useThrottledRefHistory": true,
......@@ -232,6 +240,7 @@
"useWindowScroll": true,
"useWindowSize": true,
"watch": true,
"watchArray": true,
"watchAtMost": true,
"watchDebounced": true,
"watchEffect": true,
......@@ -241,6 +250,7 @@
"watchPostEffect": true,
"watchSyncEffect": true,
"watchThrottled": true,
"watchTriggerable": true,
"watchWithFilter": true,
"whenever": true
}
......
......@@ -82,6 +82,8 @@ declare global {
const refThrottled: typeof import('@vueuse/core')['refThrottled']
const refWithControl: typeof import('@vueuse/core')['refWithControl']
const resolveComponent: typeof import('vue')['resolveComponent']
const resolveRef: typeof import('@vueuse/core')['resolveRef']
const resolveUnref: typeof import('@vueuse/core')['resolveUnref']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
......@@ -109,6 +111,7 @@ declare global {
const useAttrs: typeof import('vue')['useAttrs']
const useBase64: typeof import('@vueuse/core')['useBase64']
const useBattery: typeof import('@vueuse/core')['useBattery']
const useBluetooth: typeof import('@vueuse/core')['useBluetooth']
const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints']
const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel']
const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
......@@ -147,6 +150,7 @@ declare global {
const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper']
const useFavicon: typeof import('@vueuse/core')['useFavicon']
const useFetch: typeof import('@vueuse/core')['useFetch']
const useFileDialog: typeof import('@vueuse/core')['useFileDialog']
const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
const useFocus: typeof import('@vueuse/core')['useFocus']
const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
......@@ -155,6 +159,7 @@ declare global {
const useGamepad: typeof import('@vueuse/core')['useGamepad']
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
const useIdle: typeof import('@vueuse/core')['useIdle']
const useImage: typeof import('@vueuse/core')['useImage']
const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver']
const useInterval: typeof import('@vueuse/core')['useInterval']
......@@ -176,6 +181,7 @@ declare global {
const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage']
const useNetwork: typeof import('@vueuse/core')['useNetwork']
const useNow: typeof import('@vueuse/core')['useNow']
const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl']
const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination']
const useOnline: typeof import('@vueuse/core')['useOnline']
const usePageLeave: typeof import('@vueuse/core')['usePageLeave']
......@@ -201,12 +207,14 @@ declare global {
const useSlots: typeof import('vue')['useSlots']
const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis']
const useStepper: typeof import('@vueuse/core')['useStepper']
const useStorage: typeof import('@vueuse/core')['useStorage']
const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync']
const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
const useSwipe: typeof import('@vueuse/core')['useSwipe']
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize']
const useThrottle: typeof import('@vueuse/core')['useThrottle']
const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn']
const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory']
......@@ -233,6 +241,7 @@ declare global {
const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll']
const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
const watch: typeof import('vue')['watch']
const watchArray: typeof import('@vueuse/core')['watchArray']
const watchAtMost: typeof import('@vueuse/core')['watchAtMost']
const watchDebounced: typeof import('@vueuse/core')['watchDebounced']
const watchEffect: typeof import('vue')['watchEffect']
......@@ -242,6 +251,7 @@ declare global {
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
const watchThrottled: typeof import('@vueuse/core')['watchThrottled']
const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable']
const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
const whenever: typeof import('@vueuse/core')['whenever']
}
......@@ -12,8 +12,10 @@
"@tinymce/tinymce-vue": "^5.0.0",
"axios": "^0.27.2",
"blueimp-md5": "^2.19.0",
"dayjs": "^1.11.3",
"echarts": "^5.3.2",
"element-plus": "^2.2.5",
"element-plus": "^2.2.9",
"lodash-es": "^4.17.21",
"pinia": "^2.0.14",
"qs": "^6.10.5",
"sortablejs": "^1.15.0",
......@@ -188,9 +190,9 @@
}
},
"node_modules/@element-plus/icons-vue": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.0.5.tgz",
"integrity": "sha512-jvNWyKcdvPvMDLTWjghrPY+bYHKqh7hbAFIPe+HWR073zilzt33csREzmKx3VwhdlJUW5u0nCqN+0rwI8jlH+w==",
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.0.6.tgz",
"integrity": "sha512-lPpG8hYkjL/Z97DH5Ei6w6o22Z4YdNglWCNYOPcB33JCF2A4wye6HFgSI7hEt9zdLyxlSpiqtgf9XcYU+m5mew==",
"peerDependencies": {
"vue": "^3.2.0"
}
......@@ -216,16 +218,16 @@
}
},
"node_modules/@floating-ui/core": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.2.tgz",
"integrity": "sha512-FRVAkSNU/vGXLIsgbggcs70GkXKEOXgBBbNpYPNHSaKsCAMMd00NrjbtKTesxkdv9xm9N3+XiDlcFGY6WnatBg=="
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.3.tgz",
"integrity": "sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg=="
},
"node_modules/@floating-ui/dom": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.2.tgz",
"integrity": "sha512-z1DnEa7F3d8Fm/eXSbii8UEGpcjZGkQaYYUI0WpEVgD3vBfebDW8j/3ysusxonuMexoigA+A3b/fYH7sEqiwyg==",
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.4.tgz",
"integrity": "sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==",
"dependencies": {
"@floating-ui/core": "^0.7.2"
"@floating-ui/core": "^0.7.3"
}
},
"node_modules/@humanwhocodes/config-array": {
......@@ -375,6 +377,11 @@
"integrity": "sha512-C3064MH72iEfeGCYEGCt7FCxXoAXaMPG0QPnstcxvPmbl54erpISu06d++FY37Smja64iWy5L8wOyHHBghWbJQ==",
"dev": true
},
"node_modules/@types/web-bluetooth": {
"version": "0.0.14",
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.14.tgz",
"integrity": "sha512-5d2RhCard1nQUC3aHcq/gHzWYO6K0WJmAbjO7mQJgCQKtZpgXxv1rOM6O/dBDhDYYVutk1sciOgNSe+5YyfM8A=="
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.26.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.26.0.tgz",
......@@ -799,12 +806,13 @@
}
},
"node_modules/@vueuse/core": {
"version": "8.6.0",
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-8.6.0.tgz",
"integrity": "sha512-VirzExCm/N+QdrEWT7J4uSrvJ5hquKIAU9alQ37kUvIJk9XxCLxmfRnmekYc1kz2+6BnoyuKYXVmrMV351CB4w==",
"version": "8.9.0",
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-8.9.0.tgz",
"integrity": "sha512-eKWehF6gsiLYxnYM/1xgDu16bKED7AWvkk56JIFNQes8OKgktr3Jc1wUy8UWIulrnwCXICUu9YUo+Wkq4r2JNw==",
"dependencies": {
"@vueuse/metadata": "8.6.0",
"@vueuse/shared": "8.6.0",
"@types/web-bluetooth": "^0.0.14",
"@vueuse/metadata": "8.9.0",
"@vueuse/shared": "8.9.0",
"vue-demi": "*"
},
"funding": {
......@@ -824,9 +832,9 @@
}
},
"node_modules/@vueuse/core/node_modules/@vueuse/shared": {
"version": "8.6.0",
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-8.6.0.tgz",
"integrity": "sha512-Y/IVywZo7IfEoSSEtCYpkVEmPV7pU35mEIxV7PbD/D3ly18B3mEsBaPbtDkNM/QP3zAZ5mn4nEkOfddX4uwuIA==",
"version": "8.9.0",
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-8.9.0.tgz",
"integrity": "sha512-Pmu3Fopk/JJjN8b90uQuFrVCc/RPcSA/0zDFRTyn3YIhoB5ESna/1Sac5WZxK+n82g/ERXHHQTetGI9yxEdPfA==",
"dependencies": {
"vue-demi": "*"
},
......@@ -847,9 +855,9 @@
}
},
"node_modules/@vueuse/core/node_modules/vue-demi": {
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.1.tgz",
"integrity": "sha512-xmkJ56koG3ptpLnpgmIzk9/4nFf4CqduSJbUM0OdPoU87NwRuZ6x49OLhjSa/fC15fV+5CbEnrxU4oyE022svg==",
"version": "0.13.2",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.2.tgz",
"integrity": "sha512-41ukrclEbMddAyP7PvxMSYqnOSzPV6r7GNnyTSKSCNTaz19GehxmTiXyP9kwHSUv2+Dr6hHqiUiF7L1VAw2KdQ==",
"hasInstallScript": true,
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
......@@ -872,9 +880,9 @@
}
},
"node_modules/@vueuse/metadata": {
"version": "8.6.0",
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-8.6.0.tgz",
"integrity": "sha512-F+CKPvaExsm7QgRr8y+ZNJFwXasn89rs5wth/HeX9lJ1q8XEt+HJ16Q5Sxh4rfG5YSKXrStveVge8TKvPjMjFA==",
"version": "8.9.0",
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-8.9.0.tgz",
"integrity": "sha512-pjkIbQgJPRUrxK5/iXVKQFGC+OhJ+Vd6fhBsdwgj+NNJEHUotRliYymwdvhnEke/o+kkulT0xMvoK19nyPoiMw==",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
......@@ -1131,9 +1139,9 @@
"dev": true
},
"node_modules/async-validator": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.1.1.tgz",
"integrity": "sha512-p4DO/JXwjs8klJyJL8Q2oM4ks5fUTze/h5k10oPPKMiLe1fj3G1QMzPHNmN1Py4ycOk7WlO2DcGXv1qiESJCZA=="
"version": "4.2.5",
"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz",
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="
},
"node_modules/asynckit": {
"version": "0.4.0",
......@@ -1560,18 +1568,18 @@
"dev": true
},
"node_modules/element-plus": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.2.5.tgz",
"integrity": "sha512-Kl0yn/PQca5YQo3M3NPBP4Xl71NQuMtDx5zNXZGVyl5FjdMujXiFB9SXKYGDUCgFU3d/Rl14vB4Fpmcl2Iz+Hw==",
"version": "2.2.9",
"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.2.9.tgz",
"integrity": "sha512-jYbL0JkCdv95rkT6trZJjCAizLPySa0qcd2cgq+57SKQnCZAcNDDq4GbTuFRnNavdoeCJnuM3HIficTIUpsMOQ==",
"dependencies": {
"@ctrl/tinycolor": "^3.4.1",
"@element-plus/icons-vue": "^2.0.5",
"@floating-ui/dom": "^0.5.2",
"@element-plus/icons-vue": "^2.0.6",
"@floating-ui/dom": "^0.5.4",
"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
"@types/lodash": "^4.14.182",
"@types/lodash-es": "^4.17.6",
"@vueuse/core": "^8.6.0",
"async-validator": "^4.1.1",
"@vueuse/core": "^8.7.5",
"async-validator": "^4.2.5",
"dayjs": "^1.11.3",
"escape-html": "^1.0.3",
"lodash": "^4.17.21",
......@@ -5096,9 +5104,9 @@
"integrity": "sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw=="
},
"@element-plus/icons-vue": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.0.5.tgz",
"integrity": "sha512-jvNWyKcdvPvMDLTWjghrPY+bYHKqh7hbAFIPe+HWR073zilzt33csREzmKx3VwhdlJUW5u0nCqN+0rwI8jlH+w==",
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.0.6.tgz",
"integrity": "sha512-lPpG8hYkjL/Z97DH5Ei6w6o22Z4YdNglWCNYOPcB33JCF2A4wye6HFgSI7hEt9zdLyxlSpiqtgf9XcYU+m5mew==",
"requires": {}
},
"@eslint/eslintrc": {
......@@ -5119,16 +5127,16 @@
}
},
"@floating-ui/core": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.2.tgz",
"integrity": "sha512-FRVAkSNU/vGXLIsgbggcs70GkXKEOXgBBbNpYPNHSaKsCAMMd00NrjbtKTesxkdv9xm9N3+XiDlcFGY6WnatBg=="
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.3.tgz",
"integrity": "sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg=="
},
"@floating-ui/dom": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.2.tgz",
"integrity": "sha512-z1DnEa7F3d8Fm/eXSbii8UEGpcjZGkQaYYUI0WpEVgD3vBfebDW8j/3ysusxonuMexoigA+A3b/fYH7sEqiwyg==",
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.4.tgz",
"integrity": "sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==",
"requires": {
"@floating-ui/core": "^0.7.2"
"@floating-ui/core": "^0.7.3"
}
},
"@humanwhocodes/config-array": {
......@@ -5252,6 +5260,11 @@
"integrity": "sha512-C3064MH72iEfeGCYEGCt7FCxXoAXaMPG0QPnstcxvPmbl54erpISu06d++FY37Smja64iWy5L8wOyHHBghWbJQ==",
"dev": true
},
"@types/web-bluetooth": {
"version": "0.0.14",
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.14.tgz",
"integrity": "sha512-5d2RhCard1nQUC3aHcq/gHzWYO6K0WJmAbjO7mQJgCQKtZpgXxv1rOM6O/dBDhDYYVutk1sciOgNSe+5YyfM8A=="
},
"@typescript-eslint/eslint-plugin": {
"version": "5.26.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.26.0.tgz",
......@@ -5553,35 +5566,36 @@
"requires": {}
},
"@vueuse/core": {
"version": "8.6.0",
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-8.6.0.tgz",
"integrity": "sha512-VirzExCm/N+QdrEWT7J4uSrvJ5hquKIAU9alQ37kUvIJk9XxCLxmfRnmekYc1kz2+6BnoyuKYXVmrMV351CB4w==",
"version": "8.9.0",
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-8.9.0.tgz",
"integrity": "sha512-eKWehF6gsiLYxnYM/1xgDu16bKED7AWvkk56JIFNQes8OKgktr3Jc1wUy8UWIulrnwCXICUu9YUo+Wkq4r2JNw==",
"requires": {
"@vueuse/metadata": "8.6.0",
"@vueuse/shared": "8.6.0",
"@types/web-bluetooth": "^0.0.14",
"@vueuse/metadata": "8.9.0",
"@vueuse/shared": "8.9.0",
"vue-demi": "*"
},
"dependencies": {
"@vueuse/shared": {
"version": "8.6.0",
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-8.6.0.tgz",
"integrity": "sha512-Y/IVywZo7IfEoSSEtCYpkVEmPV7pU35mEIxV7PbD/D3ly18B3mEsBaPbtDkNM/QP3zAZ5mn4nEkOfddX4uwuIA==",
"version": "8.9.0",
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-8.9.0.tgz",
"integrity": "sha512-Pmu3Fopk/JJjN8b90uQuFrVCc/RPcSA/0zDFRTyn3YIhoB5ESna/1Sac5WZxK+n82g/ERXHHQTetGI9yxEdPfA==",
"requires": {
"vue-demi": "*"
}
},
"vue-demi": {
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.1.tgz",
"integrity": "sha512-xmkJ56koG3ptpLnpgmIzk9/4nFf4CqduSJbUM0OdPoU87NwRuZ6x49OLhjSa/fC15fV+5CbEnrxU4oyE022svg==",
"version": "0.13.2",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.2.tgz",
"integrity": "sha512-41ukrclEbMddAyP7PvxMSYqnOSzPV6r7GNnyTSKSCNTaz19GehxmTiXyP9kwHSUv2+Dr6hHqiUiF7L1VAw2KdQ==",
"requires": {}
}
}
},
"@vueuse/metadata": {
"version": "8.6.0",
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-8.6.0.tgz",
"integrity": "sha512-F+CKPvaExsm7QgRr8y+ZNJFwXasn89rs5wth/HeX9lJ1q8XEt+HJ16Q5Sxh4rfG5YSKXrStveVge8TKvPjMjFA=="
"version": "8.9.0",
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-8.9.0.tgz",
"integrity": "sha512-pjkIbQgJPRUrxK5/iXVKQFGC+OhJ+Vd6fhBsdwgj+NNJEHUotRliYymwdvhnEke/o+kkulT0xMvoK19nyPoiMw=="
},
"@xmldom/xmldom": {
"version": "0.7.5",
......@@ -5781,9 +5795,9 @@
}
},
"async-validator": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.1.1.tgz",
"integrity": "sha512-p4DO/JXwjs8klJyJL8Q2oM4ks5fUTze/h5k10oPPKMiLe1fj3G1QMzPHNmN1Py4ycOk7WlO2DcGXv1qiESJCZA=="
"version": "4.2.5",
"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz",
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="
},
"asynckit": {
"version": "0.4.0",
......@@ -6118,18 +6132,18 @@
"dev": true
},
"element-plus": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.2.5.tgz",
"integrity": "sha512-Kl0yn/PQca5YQo3M3NPBP4Xl71NQuMtDx5zNXZGVyl5FjdMujXiFB9SXKYGDUCgFU3d/Rl14vB4Fpmcl2Iz+Hw==",
"version": "2.2.9",
"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.2.9.tgz",
"integrity": "sha512-jYbL0JkCdv95rkT6trZJjCAizLPySa0qcd2cgq+57SKQnCZAcNDDq4GbTuFRnNavdoeCJnuM3HIficTIUpsMOQ==",
"requires": {
"@ctrl/tinycolor": "^3.4.1",
"@element-plus/icons-vue": "^2.0.5",
"@floating-ui/dom": "^0.5.2",
"@element-plus/icons-vue": "^2.0.6",
"@floating-ui/dom": "^0.5.4",
"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
"@types/lodash": "^4.14.182",
"@types/lodash-es": "^4.17.6",
"@vueuse/core": "^8.6.0",
"async-validator": "^4.1.1",
"@vueuse/core": "^8.7.5",
"async-validator": "^4.2.5",
"dayjs": "^1.11.3",
"escape-html": "^1.0.3",
"lodash": "^4.17.21",
......
......@@ -7,7 +7,7 @@
"build": "vue-tsc --noEmit && vite build --mode prod && npm run deploy",
"build:test": "vue-tsc --noEmit && vite build --mode test",
"build:pre": "vue-tsc --noEmit && vite build --mode pre",
"preview": "vite preview --port 5050",
"preview": "vite preview --port 4173",
"typecheck": "vue-tsc --noEmit",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"deploy": "node ./deploy.js"
......@@ -17,8 +17,10 @@
"@tinymce/tinymce-vue": "^5.0.0",
"axios": "^0.27.2",
"blueimp-md5": "^2.19.0",
"dayjs": "^1.11.3",
"echarts": "^5.3.2",
"element-plus": "^2.2.5",
"element-plus": "^2.2.9",
"lodash-es": "^4.17.21",
"pinia": "^2.0.14",
"qs": "^6.10.5",
"sortablejs": "^1.15.0",
......
......@@ -75,7 +75,7 @@ function genNavClassName(data: IMenuItem) {
.app-header {
position: sticky;
top: 0;
z-index: 1000;
z-index: 2001;
padding: 0 20px;
display: flex;
align-items: center;
......
......@@ -37,8 +37,8 @@ export function updateCourse(data: any) {
}
// 获取封面列表
export function getCoverList() {
return httpRequest.get('/api/resource/v1/util/get-cover-list')
export function getCoverList(params: { type: string }) {
return httpRequest.get('/api/resource/v1/util/get-cover-list', { params })
}
// 获取课程详情
......
......@@ -36,8 +36,8 @@ const swiperChange = (type?: string) => {
type === 'prev' ? swiper.value.prev() : swiper.value.next()
}
// 获取封面
getCoverList().then(res => {
const filtersData = res.data.list.filter((i: any) => i.type === '1')
getCoverList({ type: '2' }).then(res => {
const filtersData = res.data.list
let index = 0
while (index < filtersData.length) {
swiperCovers.push(filtersData.slice(index, (index += 8)))
......
<script setup lang="ts">
import { searchLecturer } from '../../api'
import type { Lecturer } from '../../types'
import { uniqBy } from 'lodash-es'
interface Props {
modelValue: Lecturer[]
}
const emit = defineEmits(['change'])
const props = defineProps({
data: {
type: Array
}
})
const props = withDefaults(defineProps<Props>(), { modelValue: () => [] })
const emit = defineEmits<{
(e: 'update:modelValue', modelValue: Lecturer[]): void
}>()
// table回显的值
// let lecturerList: any = ref([])
const listOptions = computed(() => {
return {
columns: [
{ label: '头像', slots: 'table-avatar', align: 'center' },
{ label: '姓名', prop: 'name', align: 'center' },
// { label: '职位', prop: 'title', align: 'center' },
// { label: '机构', prop: 'office', align: 'center' },
{ label: '简介', slots: 'table-summarize', align: 'center' },
{ label: '操作', slots: 'table-operate', align: 'center' }
],
data: allLecturers.value.filter((item: any) => lecturerValue.value.find((i: any) => i === item.id))
data: props.modelValue
}
})
// 讲师弹窗
// 弹窗
const dialogVisible = ref(false)
// 选中的值
const selectList = ref<Lecturer[]>([])
interface ListItem {
name: string
id: string
}
const options = ref<ListItem[]>([])
const loading = ref(false)
// 所有被搜索出来的值
const allLecturers: any = ref([])
// 讲师选中的值
const lecturerValue = ref([])
// 远程搜索
const loading = ref(false)
const options = ref<Lecturer[]>([])
const remoteMethod = (query: string) => {
if (query) {
loading.value = true
searchLecturer({ name: query, 'per-page': '100' }).then((res: any) => {
loading.value = false
options.value = res.data.list
options.value.forEach((item: any) => {
const findItem = allLecturers.value.find((cItem: any) => cItem.id === item.id)
if (!findItem) {
allLecturers.value.push(item)
}
})
})
} else {
options.value = []
}
if (!query) return
loading.value = true
searchLecturer({ name: query, 'per-page': '100' }).then((res: any) => {
loading.value = false
options.value = res.data.list
})
}
watch(
() => props.data,
value => {
if (value?.length) {
const list: any = value.map((item: any) => {
return item.id
})
lecturerValue.value = list
allLecturers.value = value
options.value = value as []
}
dialogVisible,
() => {
// 清空已选数据
selectList.value = []
},
{ immediate: true }
)
// 删除讲师
const removeLectuter = (id: string) => {
const index = lecturerValue.value.findIndex((ids: string) => ids === id)
lecturerValue.value.splice(index, 1)
changeData()
// 删除
const handelRemove = (id: string) => {
emit(
'update:modelValue',
props.modelValue.filter((item: any) => item.id !== id)
)
}
const changeData = () => {
emit('change', lecturerValue.value)
// 确认
const handlePrimary = () => {
const list = [...selectList.value, ...props.modelValue]
// 去重
emit('update:modelValue', uniqBy(list, 'id'))
dialogVisible.value = false
}
</script>
......@@ -95,32 +77,32 @@ const changeData = () => {
<div v-html="row.summarize"></div>
</template>
<template #table-operate="{ row }">
<el-button plain @click="removeLectuter(row.id)">删除</el-button>
<el-button plain @click="handelRemove(row.id)">删除</el-button>
</template>
</AppList>
<el-dialog v-model="dialogVisible" width="400px" title="添加讲师">
<div style="display: flex; justify-content: center">
讲师姓名:
<el-select
@change="changeData"
v-model="lecturerValue"
v-model="selectList"
multiple
filterable
remote
reserve-keyword
placeholder="请输入讲师姓名"
value-key="id"
:reserve-keyword="false"
:remote-method="remoteMethod"
:loading="loading"
style="flex: 1"
>
<el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.id" />
<el-option v-for="item in options" :key="item.id" :label="item.name" :value="item" />
</el-select>
</div>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false">确认</el-button>
<el-button type="primary" @click="handlePrimary">确认</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<style lang="scss"></style>
<script setup lang="ts">
import { searchLive } from '../../api'
import type { Live } from '../../types'
import { uniqBy } from 'lodash-es'
import dayjs from 'dayjs'
const emit = defineEmits(['change'])
const add0 = (m: number) => {
return m < 10 ? '0' + m : m
}
const format = (n: number) => {
const time = new Date(n)
const y = time.getFullYear()
const m = time.getMonth() + 1
const d = time.getDate()
const h = time.getHours()
const mm = time.getMinutes()
const s = time.getSeconds()
return y + '-' + add0(m) + '-' + add0(d) + ' ' + add0(h) + ':' + add0(mm) + ':' + add0(s)
interface Props {
modelValue: Live[]
}
const props = defineProps({
data: {
type: Array
}
})
const props = withDefaults(defineProps<Props>(), { modelValue: () => [] })
const emit = defineEmits<{
(e: 'update:modelValue', modelValue: Live[]): void
}>()
const listOptions = computed(() => {
return {
......@@ -32,88 +21,64 @@ const listOptions = computed(() => {
label: '开始时间',
prop: 'start_time',
align: 'center',
computed({ row }: any) {
if (row.start_time === undefined) {
return '-'
} else {
return format(parseInt(row.start_time) * 1000)
}
computed({ row }: { row: Live }) {
return row.start_time ? dayjs(row.start_time * 1000).format('YYYY-MM-DD HH:mm:ss') : '-'
}
},
{
label: '结束时间',
prop: 'end_time',
align: 'center',
computed({ row }: any) {
if (row.end_time === undefined) {
return '-'
} else {
return format(parseInt(row.end_time) * 1000)
}
computed({ row }: { row: Live }) {
return row.start_time ? dayjs(row.end_time * 1000).format('YYYY-MM-DD HH:mm:ss') : '-'
}
},
{ label: '操作', slots: 'table-operate', align: 'center' }
],
data: allLecturers.value.filter((item: any) => lecturerValue.value.find((i: any) => i === item.id))
data: props.modelValue
}
})
// 直播弹窗
// 弹窗
const dialogVisible = ref(false)
// 选中的值
const selectList = ref<Live[]>([])
interface ListItem {
subject: string
id: string
}
const options = ref<ListItem[]>([])
const loading = ref(false)
// 所有被搜索出来的值
const allLecturers: any = ref([])
// 直播选中的值
const lecturerValue = ref([])
// 远程搜索
const loading = ref(false)
const options = ref<Live[]>([])
const remoteMethod = (query: string) => {
if (query) {
loading.value = true
searchLive({ name: query, 'per-page': '100' }).then((res: any) => {
loading.value = false
options.value = res.data.list
options.value.forEach((item: any) => {
const findItem = allLecturers.value.find((cItem: any) => cItem.id === item.id)
if (!findItem) {
allLecturers.value.push(item)
}
})
})
} else {
options.value = []
}
if (!query) return
loading.value = true
searchLive({ name: query, 'per-page': '100' }).then((res: any) => {
loading.value = false
options.value = res.data.list
})
}
watch(
() => props.data,
value => {
if (value?.length) {
const list: any = value.map((item: any) => {
return item.id
})
lecturerValue.value = list
allLecturers.value = value
options.value = value as []
}
dialogVisible,
() => {
// 清空已选数据
selectList.value = []
},
{ immediate: true }
)
// 删除直播
const removeLectuter = (id: string) => {
const index = lecturerValue.value.findIndex((ids: string) => ids === id)
lecturerValue.value.splice(index, 1)
changeData()
// 删除
const handleRemove = (id: string) => {
emit(
'update:modelValue',
props.modelValue.filter((item: any) => item.id !== id)
)
}
const changeData = () => {
emit('change', lecturerValue.value)
// 确认
const handlePrimary = () => {
const list = [...selectList.value, ...props.modelValue]
// 去重
emit('update:modelValue', uniqBy(list, 'id'))
dialogVisible.value = false
}
</script>
......@@ -128,32 +93,32 @@ const changeData = () => {
<div v-html="row.summarize"></div>
</template>
<template #table-operate="{ row }">
<el-button plain @click="removeLectuter(row.id)">删除</el-button>
<el-button plain @click="handleRemove(row.id)">删除</el-button>
</template>
</AppList>
<el-dialog v-model="dialogVisible" width="400px" title="添加直播">
<div style="display: flex; justify-content: center">
直播会议号:
<el-select
@change="changeData"
v-model="lecturerValue"
v-model="selectList"
multiple
filterable
remote
reserve-keyword
placeholder="会议主题或者会议code"
value-key="id"
:reserve-keyword="false"
:remote-method="remoteMethod"
:loading="loading"
style="flex: 1"
>
<el-option v-for="item in options" :key="item.id" :label="item.subject" :value="item.id" />
<el-option v-for="item in options" :key="item.id" :label="item.subject" :value="item" />
</el-select>
</div>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false">确认</el-button>
<el-button type="primary" @click="handlePrimary">确认</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<style lang="scss"></style>
export interface Lecturer {
avatar: string
id: string
name: string
summarize: string
}
export interface Live {
id: string
join_url: string
meeting_code: string
meeting_id: string
meeting_type: number
start_time: number
end_time: number
subject: string
}
......@@ -13,14 +13,11 @@ import AddExam from '../components/stepOneComponents/AddExam.vue'
// 添加直播
import AddLive from '../components/stepOneComponents/AddLive.vue'
import { ElMessage } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
const store = useMapStore()
const route = useRoute()
const router = useRouter()
// const router = useRouter()
const id = route.query.id as string
// is 编辑 新建
......@@ -44,29 +41,38 @@ const electiveType = computed<ICourseList[]>(() => {
// 下拉选择tree 视频分类
let { list: selectTree } = useGetCategoryList()
const defaultProps = {
children: 'children',
label: 'category_name',
value: 'id'
}
const defaultProps = { children: 'children', label: 'category_name', value: 'id' }
// form
let form = $ref<Record<string, any>>({
const form = reactive<Record<string, any>>({
name: '',
online_type: '',
elective_type: '',
classification: '',
credit: '',
source: '2',
cover: 'https://webapp-pub.oss-cn-beijing.aliyuncs.com/center_resource/course-cover.png'
cover: 'https://webapp-pub.oss-cn-beijing.aliyuncs.com/center_resource/course-cover.png',
lecturer_id: '',
represent: '',
essay: '',
previous_preparation: '',
target: '',
exam_id: '',
live_id: ''
})
// 表单验证
const rules = {
const rules = reactive<FormRules>({
name: [{ required: true, message: '请输入标题' }],
online_type: [{ required: true, message: '请选择类型' }],
elective_type: [{ required: true, message: '请选择类型' }],
classification: [{ required: true, message: '请选择分类' }],
credit: [{ required: true, message: '请输入学分' }],
lecturer_id: [{ required: true, message: '' }],
exam_id: [{ required: true, message: '' }],
live_id: [{ required: true, message: '' }]
}
online_type: [{ required: true, message: '请选择课程类型' }],
elective_type: [{ required: true, message: '请选择选课类型' }],
classification: [{ required: true, message: '请选择课程分类' }],
credit: [
{ required: true, message: '请输入学分' },
{ pattern: /\d/, message: '请输入数字' }
],
lecturer_id: [{ required: true, message: '请添加课程讲师' }]
})
// 课程封面dialog
const dialogVisible = ref(false)
......@@ -75,11 +81,6 @@ const coverChange = (url: string) => {
form.cover = url
}
// 选择讲师
const changeLecturer = (data: any) => {
form.lecturer_id = data.toString()
}
// 选择考试
const changeExam = (data: any) => {
console.log(data, 'data')
......@@ -88,104 +89,96 @@ const changeExam = (data: any) => {
return item.id
})
.toString()
console.log(form.exam_id, 'data')
}
// 选择直播
const changeLive = (data: any) => {
form.live_id = data.toString()
}
// 讲师数据回显
const lecturerList: any = ref([])
// 考试数据回显
const examList: any = ref([])
// 考试数据回显
const liveList: any = ref([])
// 编辑数据回显
if (isUpdate) {
getCourseDetails({ id: id }).then((res: any) => {
form = res.data
lecturerList.value = res.data.lecturers
// 获取详情
let loading = $ref<boolean>(false)
function fetchDetail() {
loading = true
getCourseDetails({ id }).then((res: any) => {
Object.assign(form, res.data)
examList.value = res.data.examinations
liveList.value = res.data.meetings
form.lecturer_id = setDefaultData(lecturerList.value)
form.exam_id = setDefaultData(examList.value)
form.live_id = setDefaultData(liveList.value)
loading = false
})
}
const setDefaultData = (data: object[]) => {
const container: any = []
data.forEach((item: any) => {
container.push(item.id)
})
return container.toString()
onMounted(() => {
isUpdate && fetchDetail()
})
watchEffect(() => {
form.lecturer_id = setDefaultData(form.lecturers)
form.exam_id = setDefaultData(examList.value)
form.live_id = setDefaultData(form.meetings)
})
function setDefaultData(data: any[] = []) {
return data.map((item: any) => item.id).join()
}
const ruleFormRef = $ref<FormInstance>()
const ruleFormRef2 = $ref<FormInstance>()
// 第一步保存后的id
const stepOneId = ref()
// 新建课件
const createCourseForm = () => {
if (form.lecturer_id === '') {
ElMessage.warning('请添加讲师')
return
}
if (isUpdate) {
updateCourse(form).then((res: any) => {
if (res.code === 0) {
// 操作第二部
stepOneId.value = res.data.id
router.push({
path: '/course/update-course/stepTwo',
query: {
id: stepOneId.value,
isEditCourse: '1'
}
})
}
// 提交
function handleSubmit() {
Promise.all([ruleFormRef.validate(), ruleFormRef2.validate()])
.then(() => {
isUpdate ? handleUpdate() : handleCreate()
})
} else {
createCourse(form).then((res: any) => {
if (res.code === 0) {
// 操作第二部
stepOneId.value = res.data.id
router.push({
path: '/course/update-course/stepTwo',
query: {
id: stepOneId.value,
isEditCourse: '0'
}
})
}
.catch(() => {
ElMessage.error('请检查表单是否填写完整')
})
}
}
// 创建课程
function handleCreate() {
createCourse(form).then((res: any) => {
if (res.code === 0) {
// 操作第二部
router.push({
path: '/course/update-course/stepTwo',
query: { id: res.data.id, isEditCourse: '0' }
})
}
})
}
// 修改课程
function handleUpdate() {
updateCourse(form).then((res: any) => {
if (res.code === 0) {
// 操作第二部
router.push({
path: '/course/update-course/stepTwo',
query: { id: res.data.id, isEditCourse: '1' }
})
}
})
}
</script>
<template>
<AppCard :title="isUpdate ? '编辑课程' : '新建课程'">
<AppCard :title="isUpdate ? '编辑课程' : '新建课程'" v-loading="loading">
<!-- 基本信息 -->
<div class="update-course-info">
<div style="display: flex">
<div class="cover" :style="`background-image: url(${form.cover})`">
<div class="upload-btn" @click="dialogVisible = true">添加封面</div>
</div>
<el-form ref="ruleFormRef" :model="form" :rules="rules" style="width: 50%">
<el-form-item label="课程名称" prop="name">
<el-form ref="ruleFormRef2" :model="form" :rules="rules" style="width: 50%" label-suffix=":">
<el-form-item label="课程名称" prop="name">
<el-input v-model="form.name" maxlength="40" />
</el-form-item>
<el-form-item label="课程类型" prop="online_type">
<el-form-item label="课程类型" prop="online_type">
<el-radio-group v-model="form.online_type">
<el-radio :key="item.value" :label="item.value" v-for="item in courseType">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="选课类型" prop="elective_type">
<el-form-item label="选课类型" prop="elective_type">
<el-radio-group v-model="form.elective_type">
<el-radio :key="item.value" :label="item.value" v-for="item in electiveType">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="课程分类" prop="classification">
<el-form-item label="课程分类" prop="classification">
<el-tree-select
:render-after-expand="false"
:props="defaultProps"
......@@ -194,8 +187,8 @@ const createCourseForm = () => {
:data="selectTree"
/>
</el-form-item>
<el-form-item label="课程学分" prop="credit">
<el-input v-model="form.credit" maxlength="2" />
<el-form-item label="课程学分" prop="credit">
<el-input v-model="form.credit" maxlength="3" />
</el-form-item>
</el-form>
</div>
......@@ -204,36 +197,37 @@ const createCourseForm = () => {
label-position="top"
:model="form"
:rules="rules"
label-suffix=":"
style="width: 100%; margin-top: 30px"
>
<el-form-item label="课程讲师" prop="lecturer_id">
<el-form-item label="课程讲师" prop="lecturer_id">
<!-- 添加讲师 -->
<AddLecturer :data="lecturerList" @change="changeLecturer" style="width: 100%"></AddLecturer>
<AddLecturer v-model="form.lecturers" style="width: 100%"></AddLecturer>
</el-form-item>
<el-form-item label="课程简介与描述">
<el-form-item label="课程简介与描述">
<v-editor v-model="form.represent" class="editor" :height="200"></v-editor>
</el-form-item>
<el-form-item label="课程小论文">
<el-form-item label="课程小论文">
<v-editor v-model="form.essay" class="editor" :height="200"></v-editor>
</el-form-item>
<el-form-item label="前期准备与预备知识">
<el-form-item label="前期准备与预备知识">
<v-editor v-model="form.previous_preparation" class="editor" :height="200"></v-editor>
</el-form-item>
<el-form-item label="授课目标">
<el-form-item label="授课目标">
<v-editor v-model="form.target" class="editor" :height="200"></v-editor>
</el-form-item>
<el-form-item label="课程考试" prop="exam_id">
<el-form-item label="课程考试" prop="exam_id">
<!-- 添加考试 -->
<AddExam :data="examList" @change="changeExam" style="width: 100%"></AddExam>
</el-form-item>
<el-form-item label="周期性直播" prop="live_id">
<!-- 添加直播 -->
<AddLive :data="liveList" @change="changeLive" style="width: 100%"></AddLive>
<AddLive v-model="form.meetings" style="width: 100%"></AddLive>
</el-form-item>
</el-form>
</div>
<div class="btn-box" style="display: flex; justify-content: center">
<el-button type="primary" @click="createCourseForm">下一步</el-button>
<el-button type="primary" @click="handleSubmit">下一步</el-button>
</div>
<!-- 添加封面 -->
<AddCourseCover @change="coverChange" v-model:dialogVisible="dialogVisible"></AddCourseCover>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论