feat:初始化 -融骅
3
front/.env.development
Normal file
@@ -0,0 +1,3 @@
|
||||
VITE_APP_BASE_URL = /api
|
||||
VITE_APP_WS_URL = ws://localhost:3000
|
||||
VITE_APP_IFRAME_URL = http://192.168.2.99:8080
|
||||
3
front/.env.production
Normal file
@@ -0,0 +1,3 @@
|
||||
VITE_APP_BASE_URL = ''
|
||||
VITE_APP_WS_URL = ''
|
||||
VITE_APP_IFRAME_URL = /text-book
|
||||
3
front/.eslintignore
Normal file
@@ -0,0 +1,3 @@
|
||||
*.d.ts
|
||||
dist
|
||||
public
|
||||
37
front/.eslintrc.cjs
Normal file
@@ -0,0 +1,37 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true
|
||||
},
|
||||
extends: [
|
||||
'plugin:vue/vue3-essential',
|
||||
'standard'
|
||||
],
|
||||
globals: {
|
||||
$fetch: 'readonly'
|
||||
},
|
||||
overrides: [
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module'
|
||||
},
|
||||
plugins: [
|
||||
'vue'
|
||||
],
|
||||
rules: {
|
||||
'no-undef': 0,
|
||||
'vue/multi-word-component-names': 0,
|
||||
'vue/no-deprecated-slot-attribute': 0,
|
||||
'vue/no-deprecated-slot-scope-attribute': 0,
|
||||
'vue/no-deprecated-v-on-native-modifier': 0,
|
||||
'vue/no-v-text-v-html-on-component': 0,
|
||||
'vue/no-v-for-template-key-on-child': 0,
|
||||
'vue/no-deprecated-dollar-listeners-api': 0,
|
||||
'vue/no-deprecated-dollar-scopedslots-api': 0,
|
||||
'vue/no-deprecated-v-bind-sync': 0,
|
||||
'vue/no-deprecated-destroyed-lifecycle': 0,
|
||||
'vue/no-mutating-props': 0,
|
||||
'vue/no-deprecated-events-api': 0
|
||||
}
|
||||
}
|
||||
28
front/.gitignore
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
start.bat
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
2
front/.stylelintignore
Normal file
@@ -0,0 +1,2 @@
|
||||
dist
|
||||
public
|
||||
6
front/.stylelintrc.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
extends: ['stylelint-config-recommended', 'stylelint-config-standard'],
|
||||
rules: {
|
||||
indentation: 2
|
||||
}
|
||||
}
|
||||
13
front/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"editor.tabSize": 2,
|
||||
"stylelint.validate": [
|
||||
"css",
|
||||
"scss",
|
||||
"postcss"
|
||||
],
|
||||
"editor.codeActionsOnSave":{
|
||||
"source.fixAll": true
|
||||
},
|
||||
"editor.suggest.snippetsPreventQuickSuggestions": false,
|
||||
"docwriter.style": "Auto-detect"
|
||||
}
|
||||
3
front/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# 怀励【前端】项目
|
||||
|
||||
## GIT 工作流
|
||||
3
front/bin/publish.sh
Normal file
@@ -0,0 +1,3 @@
|
||||
npm run build
|
||||
scp -r ./dist/* root@192.168.2.51:/oboskyapp/hl/front
|
||||
rm -rf dist
|
||||
39
front/index.html
Normal file
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>DF-27模拟发射训练管理系统</title>
|
||||
<script src="/js/vue.min.js"></script>
|
||||
<script src="/js/vue-router.min.js"></script>
|
||||
<script src="/js/axios.min.js"></script>
|
||||
<script src="/js/element-ui.min.js"></script>
|
||||
<script src="/js/g2.min.js"></script>
|
||||
<script src="/config.js"></script>
|
||||
<script src="/js/mpegts.js"></script>
|
||||
<!-- <script src="/js/config.js"></script> -->
|
||||
<script>
|
||||
// var GyConfig={
|
||||
// baseUrl:'https://192.168.2.13:8080',
|
||||
// webRtcUrl:'webrtc://192.168.2.13:8080/live/livestream/'
|
||||
// }
|
||||
var GyConfig = {
|
||||
baseUrl: 'https://' + location.hostname + ':8080',
|
||||
webRtcUrl: 'webrtc://' + location.hostname + ':8080/live/livestream/'
|
||||
}
|
||||
// console.log('a');
|
||||
// console.log(MqttApiAddres,'aaaaaaaaaaaaaaaaaaaaa');
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body oncontextmenu="return false">
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
<link rel="stylesheet" href="/font/iconfont.css">
|
||||
<script src="/js/socket.io.min.js" async></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
15
front/jsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"allowSyntheticDefaultImports": false,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"],
|
||||
"~/*": ["*"]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
14476
front/package-lock.json
generated
Normal file
43
front/package.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "电子化教材及教学资源管理系统",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"publish": "sh bin/publish.sh",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint src/**/*.{js,jsx,vue,ts,tsx} --fix"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.25.0",
|
||||
"eslint-config-standard": "^17.0.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-n": "^15.3.0",
|
||||
"eslint-plugin-promise": "^6.1.0",
|
||||
"eslint-plugin-vue": "^9.6.0",
|
||||
"sass": "^1.32.6",
|
||||
"stylelint": "^14.13.0",
|
||||
"stylelint-config-recommended": "^9.0.0",
|
||||
"stylelint-config-standard": "^28.0.0",
|
||||
"vite": "^3.1.0",
|
||||
"vite-plugin-importer": "^0.2.5",
|
||||
"vite-plugin-vue2": "^2.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@antv/data-set": "^0.11.8",
|
||||
"@antv/g2": "^4.2.8",
|
||||
"@antv/g6": "^4.8.23",
|
||||
"element-ui": "^2.15.10",
|
||||
"html2canvas": "^1.4.1",
|
||||
"html2pdf": "^0.0.11",
|
||||
"jspdf": "^2.5.1",
|
||||
"precompiled-mqtt": "^4.3.14-beta",
|
||||
"qiankun": "^2.10.13",
|
||||
"socket.io-client": "^4.5.4",
|
||||
"vue": "^2.6.14",
|
||||
"vue-router": "^3.5.3",
|
||||
"wangeditor": "^4.7.15",
|
||||
"xlsx": "^0.18.5"
|
||||
}
|
||||
}
|
||||
1
front/public/default.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="80" height="80" viewBox="0 0 80 80"><g clip-path="url(#master_svg0_134_900)"><g><path d="M63.6903,27.24900859375Q59.0306,20.06630859375,55.2778,18.9663143158C61.8787,23.884608593750002,64.6605,30.67880859375,65.1788,32.94420859375C65.696,35.20860859375,67.4442,43.10390859375,63.6259,50.67500859375C59.8076,58.24620859375,57.0249,60.57600859375,53.1432,60.57600859375Q43.2422,60.57600859375,43.8249,52.03370859375L40.4595,52.03370859375L37.0941,52.03370859375Q37.6768,60.57600859375,27.7758,60.57600859375C23.8931,60.57600859375,21.110329999999998,58.24620859375,17.29205,50.67500859375C13.473766,43.10390859375,15.22087,35.20860859375,15.73917,32.94420859375C16.25647,30.67980859375,19.03917,23.88459859375,25.6402,18.96630859375Q21.88728,20.06630859375,17.22764,27.24900859375C12.567999,34.43170859375,12.37476,43.16830859375,13.733406,49.70390859375C15.09205,56.23950859375,24.217100000000002,70.73470859375,40.4595,70.73470859375C56.7018,70.73470859375,65.82589999999999,56.23950859375,67.18549999999999,49.70390859375C68.5432,43.16730859375,68.3489,34.43170859375,63.6903,27.24900859375Z" fill="#eee" fill-opacity="1"/></g><g><path d="M25.67194809375,56.465543359375005Q29.45753609375,48.408743359375,34.65031609375,46.904443359375Q33.19455609375,43.604463359375,31.88348609375,35.741943359375Q24.41041609375,41.420323359375,25.67194809375,56.465543359375005Z" fill="#eee" fill-opacity="1"/></g><g><path d="M35.541115625,46.985653125C37.005815625,46.157253125,38.666705625,46.036353125000005,39.634905625,46.051153125C40.184905625,46.059153125,40.733925625,46.059153125,41.283925625,46.051153125C42.252065625,46.036353125000005,43.911965625,46.157253125,45.377665625,46.985653125Q46.946365625,44.381353125000004,48.645965625,34.140453125C50.096765625,25.393983125,48.078165625,18.984253125,47.457765625,17.299569125C47.359665625,17.032000125,47.118865625,16.843712125,46.835365625,16.814972125C45.647265625,16.693079125,42.390865625,16.376953125,40.460395625000004,16.376953125C38.528955625,16.376953125,35.271565625,16.693079125,34.083375625,16.814972125C33.799945625,16.843712125,33.561125625,17.032000125,33.462025625,17.298577125C32.842655625,18.981273125,30.822019625,25.392003125000002,32.273824625,34.140453125Q33.971405625,44.381353125000004,35.541115625,46.985653125Z" fill="#eee" fill-opacity="1"/></g><g><path d="M46.2666015625,46.904443359375Q51.4593915625,48.408743359375,55.2449815625,56.465543359375005Q56.5065015625,41.420323359375,49.0324715625,35.741943359375C49.0334515625,35.741945266725,47.7223615625,43.604463359375,46.2666015625,46.904443359375Z" fill="#eee" fill-opacity="1"/></g><g><path d="M35.451904296875,47.811092703125Q36.859114296875,49.072625703125,37.150464296875,51.256765703125L43.702894296875,51.256765703125Q43.994244296874996,49.266855703125,45.158654296875,47.811092703125Q42.440374296875,46.985595703125,40.305774296875,46.985595703125Q38.171194296875,46.985595703125,35.451904296875,47.811092703125Z" fill="#eee" fill-opacity="1"/></g><g><path d="M46.4609546875,15.45532Q43.4522746875,9.825495,40.458484687500004,9Q37.4656946875,9.825495,34.4560546875,15.45532L40.458484687500004,15.45532L46.4609546875,15.45532Z" fill="#eee" fill-opacity="1"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
485
front/public/font/iconfont.css
Normal file
@@ -0,0 +1,485 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 3739288 */
|
||||
src: url('iconfont.ttf?t=1678435180946') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.i-cai:before {
|
||||
content: "\e654";
|
||||
}
|
||||
|
||||
.i-zan:before {
|
||||
content: "\e6d0";
|
||||
}
|
||||
|
||||
.i-xls:before {
|
||||
content: "\e63c";
|
||||
}
|
||||
|
||||
.i-shipin:before {
|
||||
content: "\e63d";
|
||||
}
|
||||
|
||||
.i-yinyue:before {
|
||||
content: "\e63e";
|
||||
}
|
||||
|
||||
.i-ppt:before {
|
||||
content: "\e63f";
|
||||
}
|
||||
|
||||
.i-pptx:before {
|
||||
content: "\e640";
|
||||
}
|
||||
|
||||
.i-xlsx:before {
|
||||
content: "\e643";
|
||||
}
|
||||
|
||||
.i-tupiao:before {
|
||||
content: "\e64e";
|
||||
}
|
||||
|
||||
.i-doc:before {
|
||||
content: "\e650";
|
||||
}
|
||||
|
||||
.i-docx:before {
|
||||
content: "\e651";
|
||||
}
|
||||
|
||||
.i-pdf:before {
|
||||
content: "\e653";
|
||||
}
|
||||
|
||||
.i-wenjianjia:before {
|
||||
content: "\e64d";
|
||||
}
|
||||
|
||||
.i-pingjia:before {
|
||||
content: "\e63a";
|
||||
}
|
||||
|
||||
.i-kaoheceping:before {
|
||||
content: "\e63b";
|
||||
}
|
||||
|
||||
.i-zanting:before {
|
||||
content: "\e693";
|
||||
}
|
||||
|
||||
.i-bofang:before {
|
||||
content: "\e733";
|
||||
}
|
||||
|
||||
.i-xitongguanli:before {
|
||||
content: "\e683";
|
||||
}
|
||||
|
||||
.i-zaixiankecheng:before {
|
||||
content: "\e639";
|
||||
}
|
||||
|
||||
.i-zaixianjiaoxue:before {
|
||||
content: "\e66b";
|
||||
}
|
||||
|
||||
.i-ksrk-mnks:before {
|
||||
content: "\e637";
|
||||
}
|
||||
|
||||
.i-dao-hang:before {
|
||||
content: "\e635";
|
||||
}
|
||||
|
||||
.i-dbsy-twdn:before {
|
||||
content: "\e689";
|
||||
}
|
||||
|
||||
.i-csrk-ctgg:before {
|
||||
content: "\e652";
|
||||
}
|
||||
|
||||
.i-dbsy-sjpg:before {
|
||||
content: "\e62d";
|
||||
}
|
||||
|
||||
.i-dbsy-zbsk:before {
|
||||
content: "\e633";
|
||||
}
|
||||
|
||||
.i-ksrk-sczy:before {
|
||||
content: "\e627";
|
||||
}
|
||||
|
||||
.i-ksrk-kcgl:before {
|
||||
content: "\e628";
|
||||
}
|
||||
|
||||
.i-ksrk-tjst:before {
|
||||
content: "\e62b";
|
||||
}
|
||||
|
||||
.i-ksrk-jlsj:before {
|
||||
content: "\e664";
|
||||
}
|
||||
|
||||
.i-ksrk-cjzb:before {
|
||||
content: "\e62c";
|
||||
}
|
||||
|
||||
.i-bg-upload:before {
|
||||
content: "\e64b";
|
||||
}
|
||||
|
||||
.i-lock:before {
|
||||
content: "\e6c3";
|
||||
}
|
||||
|
||||
.i-user:before {
|
||||
content: "\e73f";
|
||||
}
|
||||
|
||||
.i-logo:before {
|
||||
content: "\e64a";
|
||||
}
|
||||
|
||||
.i-zy-folder:before {
|
||||
content: "\e85e";
|
||||
}
|
||||
|
||||
.i-j-ksap-bianji2:before {
|
||||
content: "\ec7c";
|
||||
}
|
||||
|
||||
.i-x-ctgg-gonggulianxi:before {
|
||||
content: "\e626";
|
||||
}
|
||||
|
||||
.i-h-sczy-cuowu:before {
|
||||
content: "\e68d";
|
||||
}
|
||||
|
||||
.i-h-sczy-wancheng:before {
|
||||
content: "\e625";
|
||||
}
|
||||
|
||||
.i-h-yhgl-shoujitianchong:before {
|
||||
content: "\e6b9";
|
||||
}
|
||||
|
||||
.i-h-yhgl-farenshenfenzheng:before {
|
||||
content: "\e621";
|
||||
}
|
||||
|
||||
.i-h-yhgl-gender:before {
|
||||
content: "\e6d7";
|
||||
}
|
||||
|
||||
.i-h-yhgl-pipeizhiwei:before {
|
||||
content: "\e624";
|
||||
}
|
||||
|
||||
.i-h-yhgl-zuzhijiagoujiekou:before {
|
||||
content: "\e89a";
|
||||
}
|
||||
|
||||
.i-h-yhgl-wo:before {
|
||||
content: "\e636";
|
||||
}
|
||||
|
||||
.i-h-yhgl-youxiang:before {
|
||||
content: "\e62f";
|
||||
}
|
||||
|
||||
.i-h-jsgl-duoren:before {
|
||||
content: "\e6d6";
|
||||
}
|
||||
|
||||
.i-x-wdkk-shijian:before {
|
||||
content: "\e638";
|
||||
}
|
||||
|
||||
.i-x-wdkk-jxxx:before {
|
||||
content: "\e72d";
|
||||
}
|
||||
|
||||
.i-x-wdkk-tongji:before {
|
||||
content: "\e65b";
|
||||
}
|
||||
|
||||
.i-x-wdkk-jiangshi:before {
|
||||
content: "\e660";
|
||||
}
|
||||
|
||||
.i-x-wdkk-kaishixuexi:before {
|
||||
content: "\e64f";
|
||||
}
|
||||
|
||||
.i-x-kskk-biaoji:before {
|
||||
content: "\e672";
|
||||
}
|
||||
|
||||
.i-x-zxks-canjiakaoshi:before {
|
||||
content: "\e61f";
|
||||
}
|
||||
|
||||
.i-j-kcgl--zuo:before {
|
||||
content: "\e62a";
|
||||
}
|
||||
|
||||
.i-j-hfzb--playCircle:before {
|
||||
content: "\ea82";
|
||||
}
|
||||
|
||||
.i-j-jxzb-yuan:before {
|
||||
content: "\e629";
|
||||
}
|
||||
|
||||
.i-j-jxzb-checkin:before {
|
||||
content: "\e61e";
|
||||
}
|
||||
|
||||
.i-j-jxzb-wenzi:before {
|
||||
content: "\e6ed";
|
||||
}
|
||||
|
||||
.i-j-jxzb-juxing:before {
|
||||
content: "\e620";
|
||||
}
|
||||
|
||||
.i-j-jxzb-shanchu:before {
|
||||
content: "\e8b6";
|
||||
}
|
||||
|
||||
.i-j-jxzb-chexiao:before {
|
||||
content: "\e641";
|
||||
}
|
||||
|
||||
.i-j-jxzb-xiangpi:before {
|
||||
content: "\e622";
|
||||
}
|
||||
|
||||
.i-j-jxzb-line:before {
|
||||
content: "\e6bb";
|
||||
}
|
||||
|
||||
.i-j-jxzb-quanping:before {
|
||||
content: "\e61c";
|
||||
}
|
||||
|
||||
.i-j-jxzb-nuanchang:before {
|
||||
content: "\e666";
|
||||
}
|
||||
|
||||
.i-j-jxzb-datika:before {
|
||||
content: "\e631";
|
||||
}
|
||||
|
||||
.i-j-jxzb-yangshengqi:before {
|
||||
content: "\e619";
|
||||
}
|
||||
|
||||
.i-j-jxzb-diannao:before {
|
||||
content: "\e61b";
|
||||
}
|
||||
|
||||
.i-j-jxzb--shiping:before {
|
||||
content: "\e62e";
|
||||
}
|
||||
|
||||
.i-j-jxzb-maikefeng:before {
|
||||
content: "\e64c";
|
||||
}
|
||||
|
||||
.i-j-jxzb-kejian:before {
|
||||
content: "\e659";
|
||||
}
|
||||
|
||||
.i-j-jxzb-baiban:before {
|
||||
content: "\e61d";
|
||||
}
|
||||
|
||||
.i-mima:before {
|
||||
content: "\e617";
|
||||
}
|
||||
|
||||
.i-caidan:before {
|
||||
content: "\e7af";
|
||||
}
|
||||
|
||||
.i-zy-lb:before {
|
||||
content: "\e616";
|
||||
}
|
||||
|
||||
.i-zy-slt:before {
|
||||
content: "\e675";
|
||||
}
|
||||
|
||||
.i-zy-sj-d:before {
|
||||
content: "\e647";
|
||||
}
|
||||
|
||||
.i-zy-sj:before {
|
||||
content: "\e648";
|
||||
}
|
||||
|
||||
.i-zy-sj-u:before {
|
||||
content: "\e649";
|
||||
}
|
||||
|
||||
.i-tb-tuichudenglu:before {
|
||||
content: "\e6ec";
|
||||
}
|
||||
|
||||
.i-zy-dx:before {
|
||||
content: "\e644";
|
||||
}
|
||||
|
||||
.i-zy-dx-u:before {
|
||||
content: "\e645";
|
||||
}
|
||||
|
||||
.i-zy-dx-d:before {
|
||||
content: "\e646";
|
||||
}
|
||||
|
||||
.i-j-zblb-huifang:before {
|
||||
content: "\e618";
|
||||
}
|
||||
|
||||
.i-j-rypj-xiazai:before {
|
||||
content: "\e856";
|
||||
}
|
||||
|
||||
.i-j-rypj-kaishi:before {
|
||||
content: "\e614";
|
||||
}
|
||||
|
||||
.i-j-rypj-zhongxin:before {
|
||||
content: "\e615";
|
||||
}
|
||||
|
||||
.i-j-rgpj-xiangqing:before {
|
||||
content: "\e634";
|
||||
}
|
||||
|
||||
.i-j-fbks-rili:before {
|
||||
content: "\e73d";
|
||||
}
|
||||
|
||||
.i-j-fbks-yulan:before {
|
||||
content: "\e642";
|
||||
}
|
||||
|
||||
.i-j-ksap-shanchu:before {
|
||||
content: "\e68c";
|
||||
}
|
||||
|
||||
.i-j-ksap-bianji:before {
|
||||
content: "\e6d4";
|
||||
}
|
||||
|
||||
.i-j-ksap-renyuan:before {
|
||||
content: "\e61a";
|
||||
}
|
||||
|
||||
.i-j-ksap-fuzhi:before {
|
||||
content: "\e632";
|
||||
}
|
||||
|
||||
.i-j-fbks-pingyu:before {
|
||||
content: "\e623";
|
||||
}
|
||||
|
||||
.i-x-ktbj-tianjia:before {
|
||||
content: "\e630";
|
||||
}
|
||||
|
||||
.i-x-ktbj-add-plus-circle:before {
|
||||
content: "\e600";
|
||||
}
|
||||
|
||||
.i-x-ktbj-close-circle:before {
|
||||
content: "\e601";
|
||||
}
|
||||
|
||||
.i-x-ktbj-double-quotes-left:before {
|
||||
content: "\e602";
|
||||
}
|
||||
|
||||
.i-x-ktbj-clean:before {
|
||||
content: "\e603";
|
||||
}
|
||||
|
||||
.i-x-ktbj-bold:before {
|
||||
content: "\e604";
|
||||
}
|
||||
|
||||
.i-x-ktbj-image:before {
|
||||
content: "\e605";
|
||||
}
|
||||
|
||||
.i-x-ktbj-italic:before {
|
||||
content: "\e606";
|
||||
}
|
||||
|
||||
.i-x-ktbj-paperclip-attechment-tilt:before {
|
||||
content: "\e607";
|
||||
}
|
||||
|
||||
.i-x-ktbj-move-vertical:before {
|
||||
content: "\e608";
|
||||
}
|
||||
|
||||
.i-x-ktbj-redo:before {
|
||||
content: "\e609";
|
||||
}
|
||||
|
||||
.i-x-ktbj-underline:before {
|
||||
content: "\e60a";
|
||||
}
|
||||
|
||||
.i-x-ktbj-text-align-justify:before {
|
||||
content: "\e60b";
|
||||
}
|
||||
|
||||
.i-x-ktbj-table-remove:before {
|
||||
content: "\e60c";
|
||||
}
|
||||
|
||||
.i-x-ktbj-text-align-left:before {
|
||||
content: "\e60d";
|
||||
}
|
||||
|
||||
.i-x-ktbj-list-checked:before {
|
||||
content: "\e60e";
|
||||
}
|
||||
|
||||
.i-x-ktbj-move-horizontal:before {
|
||||
content: "\e60f";
|
||||
}
|
||||
|
||||
.i-x-ktbj-text-align-right:before {
|
||||
content: "\e610";
|
||||
}
|
||||
|
||||
.i-x-ktbj-bg-color:before {
|
||||
content: "\e611";
|
||||
}
|
||||
|
||||
.i-x-ktbj-painter:before {
|
||||
content: "\e612";
|
||||
}
|
||||
|
||||
.i-x-ktbj-font-color:before {
|
||||
content: "\e613";
|
||||
}
|
||||
|
||||
BIN
front/public/font/iconfont.ttf
Normal file
BIN
front/public/imgs/entry/1.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
front/public/imgs/entry/2.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
front/public/imgs/entry/3.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
front/public/imgs/entry/4.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
front/public/imgs/entry/5.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
front/public/imgs/entry/assess.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
front/public/imgs/entry/course.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
front/public/imgs/entry/evaluate.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
front/public/imgs/entry/resource.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
front/public/imgs/entry/textbook.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
front/public/imgs/login-bg.jpg
Normal file
|
After Width: | Height: | Size: 554 KiB |
BIN
front/public/imgs/login-bottom.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
front/public/imgs/login-draw.png
Normal file
|
After Width: | Height: | Size: 472 KiB |
BIN
front/public/imgs/login-top.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
front/public/imgs/preview-full-volume.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
front/public/imgs/preview-question-by-question.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
2
front/public/js/axios.min.js
vendored
Normal file
1
front/public/js/element-ui.min.js
vendored
Normal file
7
front/public/js/socket.io.min.js
vendored
Normal file
11
front/public/js/vue-router.min.js
vendored
Normal file
6
front/public/js/vue.min.js
vendored
Normal file
6
front/public/js/vuex.min.js
vendored
Normal file
1
front/public/logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="54.91796875" height="61.734619140625" viewBox="0 0 54.91796875 61.734619140625"><path d="M50.6903,18.24900859375Q46.0306,11.06630859375,42.2778,9.9663143158C48.8787,14.88460859375,51.6605,21.67880859375,52.1788,23.944208593749998C52.696,26.20860859375,54.4442,34.10390859375,50.6259,41.67500859375C46.8076,49.24620859375,44.0249,51.57600859375,40.1432,51.57600859375Q30.2422,51.57600859375,30.8249,43.03370859375L27.4595,43.03370859375L24.0941,43.03370859375Q24.6768,51.57600859375,14.7758,51.57600859375C10.8931,51.57600859375,8.11033,49.24620859375,4.29205,41.67500859375C0.473766,34.10390859375,2.22087,26.20860859375,2.73917,23.944208593749998C3.25647,21.67980859375,6.03917,14.884598593749999,12.6402,9.96630859375Q8.88728,11.06630859375,4.22764,18.24900859375C-0.432001,25.431708593750002,-0.62524,34.16830859375,0.733406,40.70390859375C2.09205,47.23950859375,11.2171,61.73470859375,27.4595,61.73470859375C43.7018,61.73470859375,52.8259,47.23950859375,54.1855,40.70390859375C55.5432,34.16730859375,55.3489,25.431708593750002,50.6903,18.24900859375Z" fill="#10a6b4" fill-opacity="1"/><path d="M12.67194709375,47.465543359375005Q16.45753609375,39.408743359375,21.65031609375,37.904443359375Q20.194556093750002,34.604463359375,18.88348609375,26.741943359375Q11.41041609375,32.420323359375,12.67194709375,47.465543359375005Z" fill="#10a6b4" fill-opacity="1"/><path d="M22.541115625,37.985653125C24.005815625,37.157253125,25.666705625,37.036353125000005,26.634905625000002,37.051153125C27.184905625,37.059153125,27.733925624999998,37.059153125,28.283925625000002,37.051153125C29.252065625,37.036353125000005,30.911965625,37.157253125,32.377665625,37.985653125Q33.946365625,35.381353125000004,35.645965625,25.140453125C37.096765625,16.393983125,35.078165625,9.984253125,34.457765625,8.299569125C34.359665625,8.032000125,34.118865625,7.843712125,33.835365625,7.814972125C32.647265625,7.693079125,29.390865625,7.376953125,27.460395625,7.376953125C25.528955625000002,7.376953125,22.271565625,7.693079125,21.083375625,7.814972125C20.799945625,7.843712125,20.561125625,8.032000125,20.462025625,8.298577125C19.842655625,9.981273125,17.822019625,16.392003125000002,19.273824625,25.140453125Q20.971405625,35.381353125000004,22.541115625,37.985653125Z" fill="#10a6b4" fill-opacity="1"/><path d="M33.2666015625,37.904443359375Q38.4593915625,39.408743359375,42.2449815625,47.465543359375005Q43.5065015625,32.420323359375,36.0324715625,26.741943359375C36.0334515625,26.741945266725,34.7223615625,34.604463359375,33.2666015625,37.904443359375Z" fill="#10a6b4" fill-opacity="1"/><path d="M22.451904296875,38.811092703125Q23.859114296875,40.072625703125,24.150464296875,42.256765703125L30.702894296875,42.256765703125Q30.994244296875,40.266855703125,32.158654296875,38.811092703125Q29.440374296875,37.985595703125,27.305774296875,37.985595703125Q25.171194296875,37.985595703125,22.451904296875,38.811092703125Z" fill="#10a6b4" fill-opacity="1"/><path d="M33.4609546875,6.45532Q30.4522746875,0.825495,27.4584846875,0Q24.4656946875,0.825495,21.4560546875,6.45532L27.4584846875,6.45532L33.4609546875,6.45532Z" fill="#10a6b4" fill-opacity="1"/></svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
1
front/public/svg/404.svg
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
1
front/public/svg/nomore.svg
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
1
front/public/svg/none.svg
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
1
front/public/svg/noseach.svg
Normal file
|
After Width: | Height: | Size: 11 KiB |
1
front/public/svg/timeout.svg
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
83
front/src/api/assessment-evaluation/exam.js
Normal file
@@ -0,0 +1,83 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【查】获取所有考试分类
|
||||
* @returns [{}]
|
||||
*/
|
||||
export const findAllExamClassifyApi = () =>
|
||||
fetch({ url: '/assessmentEvaluation/ExamClassify', method: 'get' })
|
||||
|
||||
/**
|
||||
* 【增】新增考试分类
|
||||
*/
|
||||
export const createExamClassifyApi = (data) =>
|
||||
fetch({ url: '/assessmentEvaluation/ExamClassify', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【删】删除考试分类
|
||||
*/
|
||||
export const deleteExamClassifyApi = (id) =>
|
||||
fetch({ url: '/assessmentEvaluation/ExamClassify/' + id, method: 'delete' })
|
||||
/**
|
||||
* 【改】删除考试分类
|
||||
*/
|
||||
export const editExamClassifyApi = ({ id, ...data }) =>
|
||||
fetch({ url: '/assessmentEvaluation/ExamClassify/' + id, method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【查】分页查询考试列表
|
||||
* @param {Object} params 分页条件
|
||||
* @returns
|
||||
*/
|
||||
export const pagingExamListApi = (params) => fetch({ url: '/assessmentEvaluation/exam/paging', method: 'get', params })
|
||||
|
||||
/**
|
||||
* 【删】批量删除考试
|
||||
* @param {array} data 考试id列表
|
||||
* @returns
|
||||
*/
|
||||
export const batchDeleteExamApi = (data) => fetch({ url: '/assessmentEvaluation/exam', method: 'delete', data })
|
||||
/**
|
||||
* 【增】新增考试
|
||||
* @param {array} data 考试信息
|
||||
* @returns
|
||||
*/
|
||||
export const createExamApi = (data) => fetch({ url: '/assessmentEvaluation/exam', method: 'post', data })
|
||||
/**
|
||||
* 【查】根据ID查询考试详情
|
||||
* @param {array} id
|
||||
* @returns
|
||||
*/
|
||||
export const getExamDetailsByIdApi = (id) => fetch({ url: '/assessmentEvaluation/exam/item/' + id, method: 'get' })
|
||||
/**
|
||||
* 【改】根据ID修改考试详情
|
||||
* @param {array} data
|
||||
* @returns
|
||||
*/
|
||||
export const patchExamDetailsByIdApi = ({ id, ...data }) => fetch({ url: '/assessmentEvaluation/exam/item/' + id, method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【查】查询所有老师
|
||||
* @returns
|
||||
*/
|
||||
export const getAllTeacher = () => fetch({ url: '/system/user/findAllTeacher', method: 'get' })
|
||||
/**
|
||||
* 【查】根据用户查用户组织架构
|
||||
* @returns
|
||||
*/
|
||||
export const getMyOrg = () => fetch({ url: '/system/org/findOrgByUserId', method: 'get' })
|
||||
/**
|
||||
* 【查】所有组织架构
|
||||
* @returns
|
||||
*/
|
||||
export const getAllOrg = () => fetch({ url: '/system/org', method: 'get' })
|
||||
/**
|
||||
* 【查】根据组织架构查学生列表
|
||||
* @returns
|
||||
*/
|
||||
export const getStudentByOrgId = (orgId) => fetch({ url: '/system/user/findStudentByOrgId', method: 'get', params: { orgId } })
|
||||
/**
|
||||
* 【查】根据试卷ID查所有试题 用于预览
|
||||
* @returns
|
||||
*/
|
||||
export const getQuestionsByPaperApi = (paperId) => fetch({ url: '/assessmentEvaluation/exampaper/questionsByPaper/' + paperId, method: 'get' })
|
||||
30
front/src/api/assessment-evaluation/humanEval.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【查】分页查询人工判卷考卷及信息
|
||||
* {currentPage,pageSize,classifyId,isPractive,title}
|
||||
*/
|
||||
export const pagingGradePaperApi = (params) =>
|
||||
fetch({ url: '/assessmentEvaluation/StudentOnlineExam/pagingGrade', method: 'get', params })
|
||||
|
||||
/**
|
||||
* 修改是否为匿名评卷
|
||||
* @param {object} param0 试卷id+是否匿名
|
||||
* @returns
|
||||
*/
|
||||
export const patchIsAnonymousApi = ({ id, ...data }) =>
|
||||
fetch({ url: '/assessmentEvaluation/exam/updateIsAnonymous/' + id, method: 'patch', data })
|
||||
/**
|
||||
* 人工判卷考试的学员考试查询 分页
|
||||
* @param {object} param0 试卷id+是否匿名
|
||||
* @returns
|
||||
*/
|
||||
export const pagingGradeDetailsApi = ({ id, ...params }) =>
|
||||
fetch({ url: '/assessmentEvaluation/StudentOnlineExam/pagingGradeDetails/' + id, method: 'get', params })
|
||||
/**
|
||||
* 传入修改数组,修改题目result相关字段
|
||||
* @param {Array} data
|
||||
* @returns
|
||||
*/
|
||||
export const patchSomeExamResult = (histroyId, data) =>
|
||||
fetch({ url: '/assessmentEvaluation/OnlineExamHistory/result/' + histroyId, method: 'patch', data })
|
||||
27
front/src/api/assessment-evaluation/mistake.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【查】分页查询错题巩固列表
|
||||
* {currentPage,pageSize,title}
|
||||
*/
|
||||
export const pagingMistakeListApi = (params) =>
|
||||
fetch({ url: '/assessmentEvaluation/mistake/paging', method: 'get', params })
|
||||
|
||||
/**
|
||||
* 【查】根据试题分类查询所有错退
|
||||
* {currentPage,pageSize,title}
|
||||
*/
|
||||
export const getMistakesByclassifyId = (classifyId, showUserAnswer = 1) =>
|
||||
fetch({ url: '/assessmentEvaluation/mistake/questions/' + classifyId, method: 'get', params: { showUserAnswer } })
|
||||
/**
|
||||
* 【改】上传错题答题模式的答案
|
||||
* {currentPage,pageSize,title}
|
||||
*/
|
||||
export const patchMistakes = (classifyId, mistakes) =>
|
||||
fetch({ url: '/assessmentEvaluation/mistake/submitMistakes/' + classifyId, method: 'patch', data: mistakes })
|
||||
/**
|
||||
* 【查】根据试题分类查最后一次考试ID
|
||||
* {currentPage,pageSize,title}
|
||||
*/
|
||||
export const getLastExamHistoryId = (classifyId) =>
|
||||
fetch({ url: '/assessmentEvaluation/mistake/lastExamHistory/' + classifyId, method: 'get' })
|
||||
49
front/src/api/assessment-evaluation/onlineTest.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【查】分页查询分配给本学员的在线考试
|
||||
* @param {object} params 分页参数
|
||||
* @returns
|
||||
*/
|
||||
export const pagingOnlineExamListApi = (params) => fetch({ url: '/assessmentEvaluation/StudentOnlineExam/paging', method: 'get', params })
|
||||
|
||||
/**
|
||||
* 【查】根据在线考试ID获取本次考试所有信息
|
||||
* @param {number} id 考试ID
|
||||
* @returns
|
||||
*/
|
||||
export const getOnlineExamAllDataApi = (id) => fetch({ url: '/assessmentEvaluation/OnlineExamHistory/questions/' + id, method: 'get' })
|
||||
|
||||
/**
|
||||
* 【增】根据在线考试ID新增考试历史记录
|
||||
* @param {{onlineExamId?: number,isPracticeExam?: number}} id 考试ID
|
||||
* @returns
|
||||
*/
|
||||
export const createExamHistoryApi = (data) => fetch({ url: '/assessmentEvaluation/OnlineExamHistory', method: 'post', data })
|
||||
/**
|
||||
* 【增】根据在线考试ID新增考试历史记录
|
||||
* @param {{onlineExamId?: number,isPracticeExam?: number}} id 考试ID
|
||||
* @returns
|
||||
*/
|
||||
export const createSimExamHistoryApi = (data) => fetch({ url: '/assessmentEvaluation/OnlineExamHistory/simtest', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【增】考试完成,提交试卷
|
||||
* @param {number} id 考试ID
|
||||
* @param {array} answers 考试答案列表
|
||||
* @returns
|
||||
*/
|
||||
export const submitExamApi = ({ id, answers }) => fetch({ url: '/assessmentEvaluation/OnlineExamHistory/submitExam/' + id, method: 'post', data: answers })
|
||||
/**
|
||||
* 【查】查询最后一次考试的历史记录
|
||||
* @param {number} id 考试ID
|
||||
* @returns
|
||||
*/
|
||||
export const getLastedHistoryApi = (id) => fetch({ url: '/assessmentEvaluation/StudentOnlineExam/lastExamHistory/' + id, method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】根据在线考试ID获取考试答题结果
|
||||
* @param {number} id 考试ID
|
||||
* @returns
|
||||
*/
|
||||
export const getOnlineExamResultApi = (id) => fetch({ url: '/assessmentEvaluation/OnlineExamHistory/examQuestionResult/' + id, method: 'get' })
|
||||
99
front/src/api/assessment-evaluation/paper.js
Normal file
@@ -0,0 +1,99 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【查】获取所有试卷分类
|
||||
* @returns [{}]
|
||||
*/
|
||||
export const findAllPaperClassifyApi = () =>
|
||||
fetch({ url: '/assessmentEvaluation/ExamPaperClassify', method: 'get' })
|
||||
|
||||
/**
|
||||
* 【增】新增试卷分类
|
||||
*/
|
||||
export const createPaperClassifyApi = (data) =>
|
||||
fetch({ url: '/assessmentEvaluation/ExamPaperClassify', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【删】删除试卷分类
|
||||
*/
|
||||
export const deletePaperClassifyApi = (id) =>
|
||||
fetch({ url: '/assessmentEvaluation/ExamPaperClassify/' + id, method: 'delete' })
|
||||
/**
|
||||
* 【改】修改试卷分类
|
||||
*/
|
||||
export const editPaperClassifyApi = ({ id, ...data }) =>
|
||||
fetch({ url: '/assessmentEvaluation/ExamPaperClassify/' + id, method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【查】分页查询试卷
|
||||
* {currentPage,pageSize,classifyId,isPractive,title}
|
||||
*/
|
||||
export const pagingFindPaperApi = (params) =>
|
||||
fetch({ url: '/assessmentEvaluation/exampaper/paging', method: 'get', params })
|
||||
/**
|
||||
* 【删】批量删除试卷
|
||||
*/
|
||||
export const deleteSomePapersApi = (data) =>
|
||||
fetch({ url: '/assessmentEvaluation/exampaper', method: 'delete', data })
|
||||
/**
|
||||
* 【删】修改试卷信息
|
||||
*/
|
||||
export const patchPaperApi = ({ id, ...data }) =>
|
||||
fetch({ url: '/assessmentEvaluation/exampaper/item/' + id, method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【增】新增试卷
|
||||
* @param {object} data
|
||||
* @returns
|
||||
*/
|
||||
export const createPaperApi = (data) =>
|
||||
fetch({ url: '/assessmentEvaluation/exampaper', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【查】根据试卷ID查询试卷详情
|
||||
* @param {number|string} id
|
||||
* @returns
|
||||
*/
|
||||
export const getPaperInfoByIdApi = (id) =>
|
||||
fetch({ url: '/assessmentEvaluation/exampaper/item/' + id, method: 'get' })
|
||||
/**
|
||||
* 【改】根据试卷ID修改试卷
|
||||
* @param {object} param0 信息
|
||||
* @returns
|
||||
*/
|
||||
export const patchPaperInfoByIdApi = ({ id, ...data }) =>
|
||||
fetch({ url: '/assessmentEvaluation/exampaper/item/' + id, method: 'patch', data })
|
||||
/**
|
||||
* 【增】根据试卷ID复制试卷
|
||||
* @param {object} param0 信息
|
||||
* @returns
|
||||
*/
|
||||
export const copyPaperInfoByIdApi = ({ id, ...data }) =>
|
||||
fetch({ url: '/assessmentEvaluation/exampaper/copy/' + id, method: 'post', data })
|
||||
|
||||
/**
|
||||
*【改】批量移动试卷到分类
|
||||
* @param {Object{ids:[],classifyId:number}} data
|
||||
* @returns
|
||||
*/
|
||||
export const batchMovePaperClassify = (data) => fetch({ url: '/assessmentEvaluation/exampaper/putchAllClassifyId', method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【增】新增模拟试卷
|
||||
* @param {object} data
|
||||
* @returns
|
||||
*/
|
||||
export const createSimTestApi = (data) =>
|
||||
fetch({ url: '/assessmentEvaluation/simtest', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【查】分页查询试卷
|
||||
* {currentPage,pageSize,classifyId,isPractive,title}
|
||||
*/
|
||||
export const pagingFindSimPaperApi = (params) =>
|
||||
fetch({ url: '/assessmentEvaluation/simtest/paging', method: 'get', params })
|
||||
/**
|
||||
* 【查】试卷被哪些考试引用
|
||||
*/
|
||||
export const checkQuoteApi = (id) =>
|
||||
fetch({ url: '/assessmentEvaluation/exampaper/checkQuote/' + id, method: 'get' })
|
||||
111
front/src/api/assessment-evaluation/questions.js
Normal file
@@ -0,0 +1,111 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【查】获取所有试题分类
|
||||
* @returns [{}]
|
||||
*/
|
||||
export const findAllQuestionsClassifyApi = () =>
|
||||
fetch({ url: '/assessmentEvaluation/questionClassify', method: 'get' })
|
||||
|
||||
/**
|
||||
* 【增】新增试题分类
|
||||
*/
|
||||
export const createQuestionsClassifyApi = (data) =>
|
||||
fetch({ url: '/assessmentEvaluation/questionClassify', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【删】删除试题分类
|
||||
*/
|
||||
export const deleteQuestionsClassifyApi = (id) =>
|
||||
fetch({ url: '/assessmentEvaluation/questionClassify/' + id, method: 'delete' })
|
||||
/**
|
||||
* 【改】修改试题分类
|
||||
*/
|
||||
export const editQuestionsClassifyApi = ({ id, ...data }) =>
|
||||
fetch({ url: '/assessmentEvaluation/questionClassify/' + id, method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【查】获取所有试题列表
|
||||
*/
|
||||
export const pagingFindQuestionsApi = (params) =>
|
||||
fetch({ url: '/assessmentEvaluation/questions/paging', method: 'get', params })
|
||||
/**
|
||||
* 【查】获取所有筛选条件选项(包含试题类型和难易程度)
|
||||
*/
|
||||
export const getAllQueryParamsOptionsApi = () =>
|
||||
fetch({ url: '/assessmentEvaluation/questions/queryParams', method: 'get' })
|
||||
/**
|
||||
* 【查】只获取试题类型
|
||||
*/
|
||||
export const getQuestionTypeOptionsApi = () =>
|
||||
fetch({ url: '/assessmentEvaluation/questionType', method: 'get' })
|
||||
/**
|
||||
* 【查】获取所有试题难度选项
|
||||
* @returns [{}]
|
||||
*/
|
||||
export const getQuestionDifficultyLevelOptionsApi = () =>
|
||||
fetch({ url: '/assessmentEvaluation/questionDifficultyLevel', method: 'get' })
|
||||
/**
|
||||
* 【刪】多选删除试题
|
||||
* @param string[] data
|
||||
*/
|
||||
export const deleteSomeQuestionsApi = (data) =>
|
||||
fetch({ url: '/assessmentEvaluation/questions', method: 'delete', data })
|
||||
/**
|
||||
* 【查】获取所有知识点
|
||||
*/
|
||||
export const getAllknowledgePointApi = () =>
|
||||
fetch({ url: '/assessmentEvaluation/knowledgePoint', method: 'get' })
|
||||
/**
|
||||
* 【增】新增知识点
|
||||
* @param string name
|
||||
*/
|
||||
export const createKnowledgePointApi = (name) =>
|
||||
fetch({ url: '/assessmentEvaluation/knowledgePoint', method: 'post', data: { name } })
|
||||
/**
|
||||
* 【删】删除知识点
|
||||
*/
|
||||
export const deleteKnowledgePointApi = (id) =>
|
||||
fetch({ url: '/assessmentEvaluation/knowledgePoint/' + id, method: 'delete' })
|
||||
/**
|
||||
* 【增】删除知识点
|
||||
*/
|
||||
export const createQuestionApi = (data) =>
|
||||
fetch({ url: '/assessmentEvaluation/questions', method: 'post', data })
|
||||
/**
|
||||
* 【查】根据ID查试题内容
|
||||
*/
|
||||
export const getQuestionByIdApi = (id) =>
|
||||
fetch({ url: '/assessmentEvaluation/questions/item/' + id, method: 'get' })
|
||||
/**
|
||||
* 【改】修改试题
|
||||
*/
|
||||
export const patchQuestionByIdApi = ({ id, ...data }) =>
|
||||
fetch({ url: '/assessmentEvaluation/questions/item/' + id, method: 'patch', data })
|
||||
/**
|
||||
* 【改】修改试题
|
||||
*/
|
||||
export const studentCanUseApi = ({ id, ...data }) =>
|
||||
fetch({ url: '/assessmentEvaluation/questions/item/studentCanUse/' + id, method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【查】根据类型查试题总数
|
||||
* @param {number} type 类型ID
|
||||
* @returns 总数
|
||||
*/
|
||||
export const getQuestionCountByTypeIdApi = (params) =>
|
||||
fetch({ url: '/assessmentEvaluation/questions/count', method: 'get', params })
|
||||
/**
|
||||
* 【查】随机抽提
|
||||
*/
|
||||
export const getRandQuestionApi = (params) =>
|
||||
fetch({ url: '/assessmentEvaluation/questions/random', method: 'get', params })
|
||||
|
||||
export const importQuestionsApi = (classifyId, data) =>
|
||||
fetch({ url: '/assessmentEvaluation/questions/import/' + classifyId, method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【查】导出试题
|
||||
*/
|
||||
export const exportQuestionsApi = (classifyId) =>
|
||||
fetch({ url: '/assessmentEvaluation/questions/export/' + classifyId, method: 'get' })
|
||||
88
front/src/api/evaluation/index copy.js
Normal file
@@ -0,0 +1,88 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【增】 - 评价
|
||||
*
|
||||
* @param {Object} data
|
||||
* @param {string} data.name 评价名
|
||||
* @param {string} data.appModule 应用模块
|
||||
* @returns
|
||||
*/
|
||||
export const createEvaluationApi = (data) =>
|
||||
fetch({ url: 'evaluation', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【删】 - 评价
|
||||
* @param {number} id
|
||||
* @returns
|
||||
*/
|
||||
export const deleteEvaluationApi = (id) =>
|
||||
fetch({ url: 'evaluation/' + id, method: 'delete' })
|
||||
|
||||
/**
|
||||
* 【改】 - 评价
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const updateEvaluationApi = ({ id, name }) =>
|
||||
fetch({ url: 'evaluation/' + id, method: 'patch', data: { name } })
|
||||
|
||||
/**
|
||||
* 【查】 - 评价
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export const findAllEvaluationApi = () =>
|
||||
fetch({ url: 'evaluation', method: 'get' })
|
||||
|
||||
/**
|
||||
* 【增】 - 评价指标
|
||||
*
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const createEvaluationIndicatorApi = ({ name, evaluationId }) =>
|
||||
fetch({ url: 'evaluation/indicator', method: 'post', data: { name, evaluationId } })
|
||||
|
||||
/**
|
||||
* 【改】 - 评价指标
|
||||
*
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const updateEvaluationIndicatorApi = ({ id, name, evaluationId }) =>
|
||||
fetch({ url: 'evaluation/indicator/' + id, method: 'patch', data: { name, evaluationId } })
|
||||
|
||||
/**
|
||||
* 【删】 - 评价指标
|
||||
* @param {number} id
|
||||
* @returns
|
||||
*/
|
||||
export const deleteEvaluationIndicatorApi = (id) =>
|
||||
fetch({ url: 'evaluation/indicator/' + id, method: 'delete' })
|
||||
|
||||
/**
|
||||
* 【查】 - 学生评分
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export const findAllStudentEvaluationApi = (orgId) =>
|
||||
fetch({ url: 'evaluation/indicator/findAllStudentEvaluation', method: 'get', params: { orgId } })
|
||||
|
||||
/**
|
||||
* 【增】 - 学生评分
|
||||
*
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const createStudentEvaluationScoreApi = (data) =>
|
||||
fetch({ url: 'evaluation/indicator/createStudentEvaluationScore', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【改】 - 学生评分
|
||||
*
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const updateStudentEvaluationScoreApi = ({ id, ...data }) =>
|
||||
fetch({ url: 'evaluation/indicator/updateStudentEvaluationScore/' + id, method: 'patch', data })
|
||||
41
front/src/api/evaluation/index.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【增】 - 评价指标
|
||||
*
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const createEvaluationIndicatorApi = (data) =>
|
||||
fetch({ url: 'evaluation/indicator', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【查】 -获取评价指标信息
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const getssEvaluationIndicatorApi = () =>
|
||||
fetch({ url: 'evaluation/indicator', method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 -获取老师所管辖的学生评价信息
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const getAllTakeChargeStudentApi = (data) =>
|
||||
fetch({ url: 'evaluation', method: 'get', data })
|
||||
|
||||
/**
|
||||
* 【改】 -评分
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const evaluationApi = (data) =>
|
||||
fetch({ url: 'evaluation', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【查】 -学员查询自己的评分
|
||||
* @returns
|
||||
*/
|
||||
export const getSleftEvaluationApi = () =>
|
||||
fetch({ url: 'evaluation/my', method: 'get' })
|
||||
50
front/src/api/online-course/course-notes.js
Normal file
@@ -0,0 +1,50 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【增】 - 添加笔记
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const addCourseNoteAPI = (data) => {
|
||||
return fetch({ url: 'course-notes', method: 'post', data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【查】 - 获取笔记列表
|
||||
* @param {number} page 页码
|
||||
* @param {number} pageSize 页面大小
|
||||
* @returns
|
||||
*/
|
||||
export const getCourseNotesListAPI = (data) => {
|
||||
return fetch({ url: `course-notes?page=${data.page}&pageSize=${data.pageSize}`, method: 'get' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【查】 - 获取笔记详情
|
||||
* @param {number} id 要查询的ID
|
||||
* @returns
|
||||
*/
|
||||
export const getOneCourseNotesListAPI = (id) => {
|
||||
return fetch({ url: 'course-notes/' + id, method: 'get' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【删】 - 删除笔记
|
||||
* @param {number} id
|
||||
* @returns
|
||||
*/
|
||||
export const deleteNoteAPI = (id) => {
|
||||
return fetch({ url: 'course-notes/' + id, method: 'delete' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【改】 - 编辑笔记
|
||||
* @param {number} id
|
||||
* @param {string} explain
|
||||
* @param {string} content
|
||||
* @param {string} title
|
||||
* @returns
|
||||
*/
|
||||
export const editorNoteAPI = (data) => {
|
||||
return fetch({ url: 'course-notes/' + data.id, method: 'patch', data })
|
||||
}
|
||||
97
front/src/api/online-course/my-course.js
Normal file
@@ -0,0 +1,97 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【查】获课程评价
|
||||
*/
|
||||
export const getCourseEvaluate = ({ courseId, ...data }) => {
|
||||
return fetch({ url: 'online-course/course-evaluate/' + courseId, method: 'get', params: data })
|
||||
}
|
||||
/**
|
||||
* 获取所有课程列表
|
||||
* @param page
|
||||
* @param pageSize
|
||||
*/
|
||||
export const getMyCourseListAllAPI = (data) => {
|
||||
return fetch({ url: 'my-course', method: 'post', data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【查】获取某学习记录下,课程的习题信息
|
||||
* @param {number} recordId
|
||||
*/
|
||||
export const getOneRecordExercisesAPI = (recordId) => {
|
||||
return fetch({ url: 'my-course/exercise/' + recordId, method: 'get' })
|
||||
}
|
||||
/**
|
||||
* 【查】获取某学习记录下,课程的习题信息
|
||||
* @param {number} recordId
|
||||
*/
|
||||
export const getCommentByCourseIdApi = (courseId) => {
|
||||
return fetch({ url: 'my-course/comment/' + courseId, method: 'get' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取分类课程列表
|
||||
* @param {number} page
|
||||
* @param {number} pageSize
|
||||
* @param {number} type
|
||||
* @returns
|
||||
*/
|
||||
export const getMyCourseListTypeAPI = (data) => {
|
||||
return fetch({ url: 'my-course/type', method: 'post', data })
|
||||
}
|
||||
/**
|
||||
* 获取某个课程的详细信息
|
||||
*/
|
||||
export const getCourseInfoAPI = (id) => {
|
||||
return fetch({ url: `my-course/${id}`, method: 'get' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新或保存课程的评价信息
|
||||
*/
|
||||
|
||||
export const updateEvealuateAPI = (data) => {
|
||||
return fetch({ url: 'my-course/eveal', method: 'post', data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新课件学习信息
|
||||
*/
|
||||
|
||||
export const updateCourewareAPI = (data) => {
|
||||
return fetch({ url: 'my-course/ware', method: 'post', data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【改】更新课件学习信息
|
||||
* @param {Array} answer
|
||||
*/
|
||||
export const submitAnswerAPI = (data) => {
|
||||
return fetch({ url: '/my-course/exercise', method: 'post', data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【增】【改】更新讨论学习信息
|
||||
* @param {number} recordId
|
||||
* @param {Anumber|null} replyId
|
||||
* @param {Anumber|null} replyUserId
|
||||
* @param {string} content
|
||||
* @param {Anumber|null} replyUserInfo
|
||||
*/
|
||||
|
||||
export const submitDisAPI = (data) => {
|
||||
return fetch({ url: 'my-course/discussion', method: 'post', data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【增】【改】提交讨论学习信息
|
||||
* @param {number|null} id
|
||||
* @param {number|null} recordId
|
||||
* @param {number|null} disId
|
||||
* @param {number} type
|
||||
*/
|
||||
|
||||
export const submitDisKudosAPI = (data) => {
|
||||
return fetch({ url: 'my-course/discussion/kudos', method: 'post', data })
|
||||
}
|
||||
72
front/src/api/online-course/online-FAQ.js
Normal file
@@ -0,0 +1,72 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【查】-获取我的提问列表
|
||||
* @param {number} page
|
||||
* @param {number} pageSize
|
||||
* @returns
|
||||
*/
|
||||
export const getMyIssuesAPI = (data) => {
|
||||
return fetch({ url: `online-issues?page=${data.page}&pageSize=${data.pageSize}`, method: 'get' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【查】-获取我的回答
|
||||
* @param {number} page
|
||||
* @param {number} pageSize
|
||||
* @returns
|
||||
*/
|
||||
export const getMyAnswerAPI = (data) => {
|
||||
return fetch({ url: `online-issues/my-answer?page=${data.page}&pageSize=${data.pageSize}`, method: 'get' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【增】-新建问题
|
||||
* @param {string} title
|
||||
* @param {string} content
|
||||
* @param {Array} answerPersonIds
|
||||
* @returns
|
||||
*/
|
||||
export const createIssuesAPI = (data) => {
|
||||
return fetch({ url: 'online-issues', method: 'post', data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【改】-编辑问题
|
||||
* @param {string} title
|
||||
* @param {string} content
|
||||
* 当前解答人的id
|
||||
* @param {Array} answerPersonIds
|
||||
* 保存过要删除的id
|
||||
* @param {Array} delAnswerPersonIds
|
||||
* @returns
|
||||
*/
|
||||
export const editorIssuesAPI = (data) => {
|
||||
return fetch({ url: 'online-issues/update-issues', method: 'post', data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【改】- 回复问题
|
||||
* @param {string} id
|
||||
* @param {string} content
|
||||
* @returns
|
||||
*/
|
||||
export const answerAPI = (data) => {
|
||||
return fetch({ url: 'online-issues/answer', method: 'post', data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【查】-
|
||||
* @param {string} id
|
||||
* @returns看。l
|
||||
*/
|
||||
export const getIssuesInfo = (id) => {
|
||||
return fetch({ url: `online-issues/${id}`, method: 'get' })
|
||||
}
|
||||
|
||||
/**
|
||||
*【查】-当前用户老师管理的学生
|
||||
*/
|
||||
export const getMyTeacherStudentAPI = () => {
|
||||
return fetch({ url: 'online-issues/myteacher', method: 'get' })
|
||||
}
|
||||
72
front/src/api/online-course/online-course.js
Normal file
@@ -0,0 +1,72 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
// 分类查询
|
||||
export const getCourseClassfiyApi = () => {
|
||||
return fetch({ url: 'online-course/classify', method: 'get' })
|
||||
}
|
||||
/**
|
||||
* 添加分类
|
||||
*
|
||||
*/
|
||||
export const addCourseClassfiyApi = (data) => {
|
||||
return fetch({ url: 'online-course/classify', method: 'post', data })
|
||||
}
|
||||
/**
|
||||
* 【改】 - 编辑分类
|
||||
* @param {number} id
|
||||
* @param {string} name
|
||||
* @returns
|
||||
*/
|
||||
export const editorCourseClassfiyApi = (data) => {
|
||||
return fetch({ url: 'online-course/classify/' + data.id, method: 'patch', data })
|
||||
}
|
||||
|
||||
export const deleteCourseClassfiyApi = (data) => {
|
||||
return fetch({ url: 'online-course/classify/delete', method: 'post', data })
|
||||
}
|
||||
/**
|
||||
* 获取课程列表
|
||||
*/
|
||||
export const getCourseListApi = (data) => {
|
||||
return fetch({ url: `online-course/course?page=${data.page}&pageSize=${data.pageSize}&classify=${data.classify}`, method: 'get' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改课程状态
|
||||
*/
|
||||
export const setCourseStatusApi = (data) => {
|
||||
return fetch({ url: `online-course/course_status/${data.id}?status=${data.status}`, method: 'get' })
|
||||
}
|
||||
/**
|
||||
* 查询课程列表
|
||||
*/
|
||||
export const searchCourseListApi = (data) => {
|
||||
return fetch({ url: `online-course/course_search/?page=${data.page}&pageSize=${data.pageSize}&value=${data.value}&classify=${data.classify}`, method: 'get' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除课程——可批量
|
||||
*/
|
||||
export const deleteCourseApi = (data) => {
|
||||
return fetch({ url: 'online-course/batch_delete', method: 'post', data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加课程
|
||||
*/
|
||||
export const addCourseApi = (data) => {
|
||||
return fetch({ url: 'online-course', method: 'post', data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取课程详情
|
||||
*/
|
||||
export const getCourseInfoApi = (id) => {
|
||||
return fetch({ url: `online-course/course/${id}`, method: 'get' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加课程
|
||||
*/
|
||||
export const editorCourseApi = (id, data) => {
|
||||
return fetch({ url: `online-course/course/${id}`, method: 'patch', data })
|
||||
}
|
||||
77
front/src/api/online-teaching/live-manage.js
Normal file
@@ -0,0 +1,77 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【增】 - 新建直播
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const addLiveAPI = (data) => {
|
||||
return fetch({ url: 'live-teaching', method: 'post', data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【查】 - 获取直播列表
|
||||
* @param {number} page 页码
|
||||
* @param {number} pageSize 页面大小
|
||||
* @returns
|
||||
*/
|
||||
export const getLiveListAPI = (params) => {
|
||||
return fetch({ url: 'live-teaching', method: 'get', params })
|
||||
}
|
||||
/**
|
||||
* 【查】 - 获取某直播详情
|
||||
* @param {number} id 页码
|
||||
* @returns
|
||||
*/
|
||||
export const getLiveInfoAPI = (id) => {
|
||||
return fetch({ url: 'live-teaching/' + id, method: 'get' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【查】 - 直播时获取某直播详情
|
||||
* @param {number} id 页码
|
||||
* @returns
|
||||
*/
|
||||
export const getLivePublishInfoAPI = (id) => {
|
||||
return fetch({ url: 'live-teaching/live-publish/' + id, method: 'get' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【查】 - 搜索直播列表
|
||||
* @param {number} page 页码
|
||||
* @param {number} pageSize 页面大小
|
||||
* @param {string} value 搜索内容
|
||||
* @returns
|
||||
*/
|
||||
export const searchLiveListAPI = (data) => {
|
||||
return fetch({ url: 'live-teaching/search', method: 'post', data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【删】 - 删除直播
|
||||
* @param {[number]} id
|
||||
* @returns
|
||||
*/
|
||||
export const deleteLiveAPI = (id) => {
|
||||
return fetch({ url: 'live-teaching/' + id.join(','), method: 'delete' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【改】 - 编辑直播信息
|
||||
* @param {object} data
|
||||
* @param {number} id
|
||||
* @returns
|
||||
*/
|
||||
export const editorLiveAPI = (data) => {
|
||||
return fetch({ url: 'live-teaching', method: 'patch', data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【改】 - 更新直播部分信息
|
||||
* @param {object} data
|
||||
* @param {number} data.id
|
||||
* @returns
|
||||
*/
|
||||
export const updateLiveInfoAPI = (data) => {
|
||||
return fetch({ url: 'live-teaching/update', method: 'post', data })
|
||||
}
|
||||
57
front/src/api/online-teaching/live-student.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【查】 - 获取我的直播列表
|
||||
* @param {number} page 页码
|
||||
* @param {number} pageSize 页面大小
|
||||
* @returns
|
||||
*/
|
||||
export const getMyLiveListAPI = (data) => {
|
||||
return fetch({ url: `live-class?page=${data.page}&pageSize=${data.pageSize}`, method: 'get' })
|
||||
}
|
||||
/**
|
||||
* 【查】 - 获取我的直播列表
|
||||
* @param {number} page 页码
|
||||
* @param {number} pageSize 页面大小
|
||||
* @param {string} value 页面大小
|
||||
* @returns
|
||||
*/
|
||||
export const searchMyLiveListAPI = (data) => {
|
||||
return fetch({ url: `live-class/search?page=${data.page}&pageSize=${data.pageSize}&value=${data.value}`, method: 'get' })
|
||||
}
|
||||
/**
|
||||
* 【查】 - 获取直播间学院列表
|
||||
* @param {number} id 直播间ID
|
||||
* @returns
|
||||
*/
|
||||
export const getLiveStudentList = (id) => {
|
||||
return fetch({ url: `live-class/liveStudent/${id}`, method: 'get' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【查】 - 参加直播时获取直播详情
|
||||
* @param {number} id 直播ID
|
||||
* @returns
|
||||
*/
|
||||
export const getLiveWatchInfoAPI = (id) => {
|
||||
return fetch({ url: `live-class/${id}`, method: 'get' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 【查】 - 查询直播下的所有试题
|
||||
* @param {number} id 直播ID
|
||||
* @returns
|
||||
*/
|
||||
export const getLiveQuestionAPI = (id) => {
|
||||
return fetch({ url: `live-class/question/${id}`, method: 'get' })
|
||||
}
|
||||
/**
|
||||
* 【增】 - 回答练习题
|
||||
* @param {object} data 答案信息
|
||||
* @param {Array} data.answerInfo 答案信息
|
||||
* @param {number} data.liveId 直播id
|
||||
* @returns
|
||||
*/
|
||||
export const liveAnsweringQuestionAPI = (data) => {
|
||||
return fetch({ url: 'live-class/answering', method: 'post', data })
|
||||
}
|
||||
38
front/src/api/statistic/index.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
const CACHES = {}
|
||||
|
||||
/**
|
||||
* 【查】 - 获取统计数据
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export const findStatisticDataApi = async (fun, { noCatch, ...params } = {}) => {
|
||||
const module = window.__CHART_MODULE__
|
||||
const cacheKey = noCatch ? undefined : `${module}_${fun}_${JSON.stringify(params)}`
|
||||
|
||||
if (cacheKey && CACHES[cacheKey]) return CACHES[cacheKey]
|
||||
|
||||
const { data } = await fetch({ url: 'statistic', params: { module, fun, ...params } })
|
||||
|
||||
if (cacheKey) CACHES[cacheKey] = data
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* 上报数据
|
||||
*
|
||||
* @param {Object} data 存储数据
|
||||
* @param {string} data.type 数据类型
|
||||
* @param {string} data.page 上报页面
|
||||
*
|
||||
* @param {number?} data.id 上报记录ID,为空时创建,不为空时则修改
|
||||
* @param {string?} data.field01 备用字段
|
||||
* @param {string?} data.field02 备用字段
|
||||
* @param {string?} data.field03 备用字段
|
||||
* @param {string?} data.remarks 备注说明
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const reportDataApi = (data) =>
|
||||
fetch({ url: 'statistic/reportData', method: 'post', data: { ...data, page: location.href.split(location.host)[1] } })
|
||||
49
front/src/api/system/index.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 登陆
|
||||
*
|
||||
* @param {string} username 用户名
|
||||
* @param {string} password 密码
|
||||
* @returns
|
||||
*/
|
||||
export const loginApi = (username, password) =>
|
||||
fetch({ url: '/system/login', method: 'post', data: { username, password } })
|
||||
|
||||
/**
|
||||
* 校验管理员密码
|
||||
*
|
||||
* @param {string} password
|
||||
* @returns
|
||||
*/
|
||||
export const checkAdminPasswordApi = (password) =>
|
||||
fetch({ url: '/system/checkAdminPassword', params: { password } })
|
||||
|
||||
/**
|
||||
* 单文件上传
|
||||
*
|
||||
* @param {File} file 文件
|
||||
* @param {Object} options 参数
|
||||
* @param {Object} options.data 携带Body请求参数
|
||||
* @param {string} options.data.path 文件存储路径
|
||||
* @param {number} options.data.classifyId 文件分类
|
||||
* @param {()=>void} options.onUploadProgress 上传进度回调
|
||||
* @param {any} options.signal 用于取消请求
|
||||
* @returns
|
||||
*/
|
||||
export const uploadApi = (file, options) =>
|
||||
fetch({
|
||||
url: '/resource/upload',
|
||||
method: 'post',
|
||||
timeout: 0,
|
||||
...options,
|
||||
formData: { ...options?.data, file }
|
||||
})
|
||||
|
||||
/**
|
||||
* 【查】 - 角色功能
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export const findRoleFeatureApi = () =>
|
||||
fetch({ url: 'system/feature', method: 'get' })
|
||||
43
front/src/api/system/org.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【增】 - 组织机构
|
||||
*
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const createOrgApi = (data) =>
|
||||
fetch({ url: 'system/org', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【删】 - 组织机构
|
||||
* @param {number} id
|
||||
* @returns
|
||||
*/
|
||||
export const deleteOrgApi = (id) =>
|
||||
fetch({ url: 'system/org/' + id, method: 'delete' })
|
||||
|
||||
/**
|
||||
* 【改】 - 组织机构
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const updateOrgApi = ({ id, ...data }) =>
|
||||
fetch({ url: 'system/org/' + id, method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【查】 - 组织机构
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export const findAllOrgApi = () =>
|
||||
fetch({ url: 'system/org', method: 'get' })
|
||||
|
||||
/**
|
||||
* 通过用户ID查询组织
|
||||
*
|
||||
* @param {string} userId 用户ID,可为空,默认当前用户ID
|
||||
* @returns
|
||||
*/
|
||||
export const findOrgByUserIdApi = (userId) =>
|
||||
fetch({ url: 'system/org/findOrgByUserId', method: 'get', params: { userId } })
|
||||
150
front/src/api/system/resource.js
Normal file
@@ -0,0 +1,150 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【增】 - 资源类型
|
||||
*
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const createResourceClassifyApi = (data) =>
|
||||
fetch({ url: 'resource/classify', method: 'post', data })
|
||||
/**
|
||||
* 【查】 - 共享资源评论区
|
||||
*
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const pagingDis = (params) =>
|
||||
fetch({ url: 'resource/pagingDis', method: 'get', params })
|
||||
/**
|
||||
* 【增】 - 共享资源评论区评论
|
||||
*
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const submitDisAPI = (data) =>
|
||||
fetch({ url: 'resource/discussion', method: 'post', data })
|
||||
/**
|
||||
* 【删】 - 删除共享资源评论区
|
||||
*
|
||||
* @param {number} id
|
||||
* @returns
|
||||
*/
|
||||
export const deleteDisAPI = (id) =>
|
||||
fetch({ url: 'resource/discussion/' + id, method: 'delete' })
|
||||
|
||||
/**
|
||||
* 【增】 - 资源文件
|
||||
*
|
||||
* @param {Array} data 文件夹名
|
||||
* @returns
|
||||
*/
|
||||
export const createResourceApi = (data) =>
|
||||
fetch({ url: 'resource/createResource', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【增】 - 资源文件夹
|
||||
*
|
||||
* @param {string} name 文件夹名
|
||||
* @returns
|
||||
*/
|
||||
export const createFolderApi = (data) =>
|
||||
fetch({ url: 'resource/createFolder', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【删】 - 资源类型
|
||||
*
|
||||
* @param {number} id
|
||||
* @returns
|
||||
*/
|
||||
export const deleteResourceClassifyApi = (id) =>
|
||||
fetch({ url: 'resource/classify/' + id, method: 'delete' })
|
||||
|
||||
/**
|
||||
* 【改】 - 资源类型
|
||||
*
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const updateResourceClassifyApi = ({ id, ...data }) =>
|
||||
fetch({ url: 'resource/classify/' + id, method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【查】 - 资源类型
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export const findAllResourceClassifyApi = () =>
|
||||
fetch({ url: 'resource/classify', method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 所有资源
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export const findAllResourceApi = (params) =>
|
||||
fetch({ url: 'resource', method: 'get', params })
|
||||
|
||||
/**
|
||||
* 【查】 - 资源信息
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export const findResourceApi = (id) =>
|
||||
fetch({ url: 'resource/findOne/' + id, method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 资源文件夹
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export const findAllFolderApi = () =>
|
||||
fetch({ url: 'resource/findAllFolder', method: 'get' })
|
||||
|
||||
/**
|
||||
* 【改】 - 资源名称
|
||||
*
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const updateResourceNameApi = (id, name) =>
|
||||
fetch({ url: 'resource/rename/' + id, method: 'patch', data: { name } })
|
||||
|
||||
/**
|
||||
* 【改】 - 资源-资源类型
|
||||
*
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const updateResourceClassifyIdsApi = (ids, classifyIds) =>
|
||||
fetch({
|
||||
url: 'resource/updateClassify',
|
||||
method: 'patch',
|
||||
data: { ids, classifyIds: classifyIds.toString(), classifyId: classifyIds[classifyIds.length - 1] }
|
||||
})
|
||||
|
||||
/**
|
||||
* 【改】 - 资源路径
|
||||
*
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const updateResourcePathApi = (ids, path) =>
|
||||
fetch({ url: 'resource/updatePath', method: 'patch', data: { ids, path } })
|
||||
|
||||
/**
|
||||
* 【删】 - 资源
|
||||
*
|
||||
* @param {number} id
|
||||
* @returns
|
||||
*/
|
||||
export const deleteResourceApi = (id) =>
|
||||
fetch({ url: 'resource/' + id, method: 'delete' })
|
||||
|
||||
/**
|
||||
* 【改】 - 文件转换
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export const fileConverApi = (id) =>
|
||||
fetch({ url: 'resource/fileConver/' + id, timeout: 0, method: 'patch' })
|
||||
34
front/src/api/system/role.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【增】 - 角色
|
||||
*
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const createRoleApi = (data) =>
|
||||
fetch({ url: 'system/role', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【删】 - 角色
|
||||
* @param {number} id
|
||||
* @returns
|
||||
*/
|
||||
export const deleteRoleApi = (id) =>
|
||||
fetch({ url: 'system/role/' + id, method: 'delete' })
|
||||
|
||||
/**
|
||||
* 【改】 - 角色
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const updateRoleApi = ({ id, ...data }) =>
|
||||
fetch({ url: 'system/role/' + id, method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【查】 - 角色
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export const findAllRoleApi = () =>
|
||||
fetch({ url: 'system/role', method: 'get' })
|
||||
91
front/src/api/system/user.js
Normal file
@@ -0,0 +1,91 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【增】 - 用户
|
||||
*
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const createUserApi = (data) =>
|
||||
fetch({ url: 'system/user', method: 'post', data })
|
||||
export const registerApi = (data) =>
|
||||
fetch({ url: 'system/user/register', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【删】 - 删除多个用户
|
||||
* @param {string[]} ids
|
||||
* @returns
|
||||
*/
|
||||
export const deleteUsersApi = (ids) =>
|
||||
fetch({ url: 'system/user?ids=' + ids, method: 'delete' })
|
||||
|
||||
/**
|
||||
* 【改】 - 用户
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const updateUserApi = ({ id, ...data }) =>
|
||||
fetch({ url: 'system/user/' + id, method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【查】 - 角色
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export const findAllUserApi = (params) =>
|
||||
fetch({ url: 'system/user', method: 'get', params })
|
||||
|
||||
/**
|
||||
* 【查】 - 查询所有教员
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export const findAllTeacherApi = () =>
|
||||
fetch({ url: 'system/user/findAllTeacher', method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 根据角色查询学员
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export const findUserByRoleApi = (baseRoleId) =>
|
||||
fetch({ url: '/system/user/role/' + baseRoleId, method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 根据组织ID查询学员
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export const findStudentByOrgIdApi = (orgId) =>
|
||||
fetch({ url: 'system/user/findStudentByOrgId', method: 'get', params: { orgId } })
|
||||
|
||||
/**
|
||||
* 【查】 - 查询老师所管辖的所有学生
|
||||
*
|
||||
* @param {string} teacherId 老师ID,默认当前登陆人
|
||||
* @returns
|
||||
*/
|
||||
export const findStudentByTeacherApi = (teacherId) =>
|
||||
fetch({ url: 'system/user/findStudentByTeacher', method: 'get', params: { teacherId } })
|
||||
|
||||
/**
|
||||
* 【改】 - 修改用户密码
|
||||
* @param {Object} data
|
||||
* @returns
|
||||
*/
|
||||
export const updatePasswordApi = (data) =>
|
||||
fetch({ url: 'system/user/updatePassword', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【改】 - 重置用户密码
|
||||
* @returns
|
||||
*/
|
||||
export const resetPasswordApi = (id) =>
|
||||
fetch({ url: 'system/user/resetPassword/' + id, method: 'patch' })
|
||||
|
||||
/**
|
||||
* 【新增】 - 批量添加
|
||||
* @returns
|
||||
*/
|
||||
export const batchCreateUserApi = (data) =>
|
||||
fetch({ url: 'system/user/batchCreate', method: 'post', data })
|
||||
96
front/src/api/training/device.js
Normal file
@@ -0,0 +1,96 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* -----------------------设备分类-----------------------
|
||||
* 【查】 - 获取所有设备分类
|
||||
*/
|
||||
export const getDeviceClassifyApi = () =>
|
||||
fetch({ url: '/train/device/classify', method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 新增设备分类
|
||||
* @param {object} data
|
||||
*/
|
||||
|
||||
export const createDeviceClassifyApi = (data) =>
|
||||
fetch({ url: '/train/device/classify', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【改】 - 编辑设备分类
|
||||
* @param {object} data
|
||||
* @returns
|
||||
*/
|
||||
export const editorDeviceClassifyApi = (id, data) =>
|
||||
fetch({ url: '/train/device/classify/' + id, method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【删】 - 删除设备分类
|
||||
* @param {object} data
|
||||
* @returns
|
||||
*/
|
||||
export const deleteDeviceClassifyApi = (id) =>
|
||||
fetch({ url: '/train/device/classify/' + id, method: 'delete' })
|
||||
|
||||
/**
|
||||
* -----------------------设备-----------------------
|
||||
* 【查】 - 分页查询设备
|
||||
* @param {object} data
|
||||
*/
|
||||
|
||||
export const paginggetDeviceApi = (params) =>
|
||||
fetch({ url: '/train/device/device/paging', method: 'get', params })
|
||||
|
||||
/**
|
||||
* 【查】 - 查询所有设备
|
||||
*/
|
||||
|
||||
export const getDeviceApi = () =>
|
||||
fetch({ url: '/train/device/device', method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 查询设备详情
|
||||
* @param {object} data
|
||||
*/
|
||||
|
||||
export const getDeviceDetailsApi = (id) =>
|
||||
fetch({ url: '/train/device/device/details/' + id, method: 'get' })
|
||||
|
||||
/**
|
||||
* 【增】 - 增加设备
|
||||
* @param {object} data
|
||||
*/
|
||||
|
||||
export const createDevicesApi = (data) =>
|
||||
fetch({ url: '/train/device/device', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【改】 - 增加设备
|
||||
* @param {object} data
|
||||
*/
|
||||
|
||||
export const editorDevicesApi = (id, data) =>
|
||||
fetch({ url: '/train/device/device/' + id, method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【查】 - 查询设备拥有的部件及对应的部件状态
|
||||
* @param {Array<Number>} data
|
||||
*/
|
||||
|
||||
export const getDevicesInfoApi = (data) =>
|
||||
fetch({ url: '/train/device/device/findOperationContent', method: 'post', data: { devices: data } })
|
||||
|
||||
/**
|
||||
* 【查】 - 删除设备
|
||||
* @param {object} data
|
||||
*/
|
||||
|
||||
export const deleteDevicesApi = (data) =>
|
||||
fetch({ url: '/train/device/device/all', method: 'delete', data })
|
||||
|
||||
/**
|
||||
* 【删】 - 删除设备
|
||||
* @param {object} data
|
||||
*/
|
||||
|
||||
export const testConnectDeviceApi = (params) =>
|
||||
fetch({ url: '/train/device/device/ping', method: 'get', params })
|
||||
19
front/src/api/training/mqtt.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* -----------------------设备分类-----------------------
|
||||
* 【查】 - 获取所有设备分类
|
||||
*/
|
||||
export const getMqttOnlineClient = async () => {
|
||||
return fetch({
|
||||
url: '/train/trainModule/proxy',
|
||||
method: 'post',
|
||||
data: {
|
||||
url: MqttApiAddres + 'clients',
|
||||
auth: {
|
||||
...MqttApiKey
|
||||
},
|
||||
method: 'GET'
|
||||
}
|
||||
})
|
||||
}
|
||||
41
front/src/api/training/operation.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【查】 - 获取所有部件状态
|
||||
*/
|
||||
export const getOperationApi = () =>
|
||||
fetch({ url: '/train/device/operation', method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 分页获取所有部件状态
|
||||
* @param {object} paging
|
||||
* @returns
|
||||
*/
|
||||
export const getOperationPageApi = (params) =>
|
||||
fetch({ url: '/train/device/operation/paging', method: 'get', params })
|
||||
|
||||
/**
|
||||
* 【增】 - 新增部件状态
|
||||
* @param {object} data
|
||||
* @returns
|
||||
*/
|
||||
export const createOperationApi = (data) =>
|
||||
fetch({ url: '/train/device/operation', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【改】 - 编辑部件状态
|
||||
* @param {id} number
|
||||
* @param {object} data
|
||||
* @returns
|
||||
*/
|
||||
export const editorOperationApi = (id, data) =>
|
||||
fetch({ url: '/train/device/operation/' + id, method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【改】 - 编辑部件状态
|
||||
* @param {Array[number]} ids
|
||||
* @param {object} data
|
||||
* @returns
|
||||
*/
|
||||
export const deleteOperationApi = (data) =>
|
||||
fetch({ url: '/train/device/operation/all', method: 'delete', data })
|
||||
41
front/src/api/training/post.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【查】 - 获取所有号位
|
||||
*/
|
||||
export const getPostApi = () =>
|
||||
fetch({ url: '/train/device/position', method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 分页获取所有号位
|
||||
* @param {object} paging
|
||||
* @returns
|
||||
*/
|
||||
export const getPostPageApi = (paging) =>
|
||||
fetch({ url: `/train/device/position/paging/?currentPage=${paging.currentPage}&pageSize=${paging.pageSize}`, method: 'get' })
|
||||
|
||||
/**
|
||||
* 【增】 - 新增号位
|
||||
* @param {object} data
|
||||
* @returns
|
||||
*/
|
||||
export const createPostApi = (data) =>
|
||||
fetch({ url: '/train/device/position', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【改】 - 编辑号位
|
||||
* @param {id} number
|
||||
* @param {object} data
|
||||
* @returns
|
||||
*/
|
||||
export const editorPostApi = (id, data) =>
|
||||
fetch({ url: '/train/device/position/' + id, method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【改】 - 编辑号位
|
||||
* @param {Array[number]} ids
|
||||
* @param {object} data
|
||||
* @returns
|
||||
*/
|
||||
export const deletePostApi = (data) =>
|
||||
fetch({ url: '/train/device/position/all', method: 'delete', data })
|
||||
123
front/src/api/training/subject.js
Normal file
@@ -0,0 +1,123 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* -----------------------科目分类-----------------------
|
||||
* 【查】 - 获取所有科目分类
|
||||
*/
|
||||
export const getSubjectClassifyApi = (params) =>
|
||||
fetch({ url: '/train/subject/classify', method: 'get', params })
|
||||
|
||||
/**
|
||||
* 【查】 - 新增科目分类
|
||||
* @param {object} data
|
||||
*/
|
||||
|
||||
export const createSubjectClassifyApi = (data) =>
|
||||
fetch({ url: '/train/subject/classify', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【改】 - 编辑科目分类
|
||||
* @param {object} data
|
||||
* @returns
|
||||
*/
|
||||
export const editorSubjectClassifyApi = (id, data) =>
|
||||
fetch({ url: '/train/subject/classify/' + id, method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【删】 - 删除科目分类
|
||||
* @param {object} data
|
||||
* @returns
|
||||
*/
|
||||
export const deleteSubjectClassifyApi = (id) =>
|
||||
fetch({ url: '/train/subject/classify/' + id, method: 'delete' })
|
||||
|
||||
/**
|
||||
* -----------------------科目类型-----------------------
|
||||
* 【查】 - 查询科目所所有类型
|
||||
* @param {object} data
|
||||
*/
|
||||
export const getSubjectTypeApi = () =>
|
||||
fetch({ url: '/train/subject/traintype', method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 新增科目分类类型
|
||||
* @param {object} data
|
||||
*/
|
||||
|
||||
export const createSubjectTypefyApi = (data) =>
|
||||
fetch({ url: '/train/subject/traintype', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【改】 - 编辑科目分类类型
|
||||
* @param {object} data
|
||||
* @returns
|
||||
*/
|
||||
export const editorSubjectTypeApi = (id, data) =>
|
||||
fetch({ url: '/train/subject/traintype/' + id, method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【删】 - 删除科目分类类型
|
||||
* @param {object} data
|
||||
* @returns
|
||||
*/
|
||||
export const deleteSubjectTypeApi = (id) =>
|
||||
fetch({ url: '/train/subject/traintype/' + id, method: 'delete' })
|
||||
|
||||
/**
|
||||
* -----------------------科目-----------------------
|
||||
* 【查】 - 分页查询科目
|
||||
* @param {object} data
|
||||
*/
|
||||
|
||||
export const pagingGetSubjectApi = (params) =>
|
||||
fetch({ url: '/train/subject/subject/paging', method: 'get', params })
|
||||
|
||||
/**
|
||||
* 【改】 - 修改科目状态
|
||||
* @param {object} data
|
||||
*/
|
||||
|
||||
export const changeSubjectStatusApi = (data) =>
|
||||
fetch({ url: '/train/subject/subject/status', method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【查】 - 查询科目详情
|
||||
* @param {object} data
|
||||
*/
|
||||
|
||||
export const getSubjectDetailsApi = (id) =>
|
||||
fetch({ url: '/train/subject/subject/details/' + id, method: 'get' })
|
||||
|
||||
/**
|
||||
* 【增】 - 增加科目
|
||||
* @param {object} data
|
||||
*/
|
||||
|
||||
export const createSubjectApi = (data) =>
|
||||
fetch({ url: '/train/subject/subject', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【复制】 - 增加科目
|
||||
* @param {object} data
|
||||
*/
|
||||
|
||||
export const copySubjectApi = (data) =>
|
||||
fetch({ url: '/train/subject/subject/copySubject', method: 'post', data })
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 【改】 - 编辑科目
|
||||
* @param {object} data
|
||||
*/
|
||||
|
||||
export const editorSubjectApi = (id, data) =>
|
||||
fetch({ url: '/train/subject/subject/' + id, method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【删】 - 删除科目
|
||||
* @param {object} data
|
||||
*/
|
||||
|
||||
export const deleteSubjectApi = (data) =>
|
||||
fetch({ url: '/train/subject/subject/all', method: 'delete', data })
|
||||
72
front/src/api/training/train-evealuation.js
Normal file
@@ -0,0 +1,72 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【查】 - 获取训练分数详情
|
||||
*/
|
||||
export const getScoreApi = (id) =>
|
||||
fetch({ url: '/train/trainModule/score/' + id, method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 获取训练步骤详情
|
||||
*/
|
||||
export const getStepApi = (id) =>
|
||||
fetch({ url: '/train/trainModule/analysis/step/' + id, method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 获取用户训练综合信息
|
||||
*/
|
||||
export const getUserTrainInfoApi = (id) =>
|
||||
fetch({ url: '/train/trainModule/analysis/user/basic/' + id, method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 获取用户号位选择信息
|
||||
*/
|
||||
export const getUserPositionfoApi = (id) =>
|
||||
fetch({ url: '/train/trainModule/analysis/user/position/' + id, method: 'get' })
|
||||
/**
|
||||
* 【查】 - 获取用户训练用时信息
|
||||
*/
|
||||
export const getUserTrainDurationApi = (id) =>
|
||||
fetch({ url: '/train/trainModule/analysis/user/duration/' + id, method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 获取用户科目成绩信息
|
||||
*/
|
||||
export const getUserSubjectGradeApi = (id) =>
|
||||
fetch({ url: '/train/trainModule/analysis/user/scoreLine/' + id, method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 获取用户号位成绩信息
|
||||
*/
|
||||
export const getUserPositionGradeApi = (id) =>
|
||||
fetch({ url: '/train/trainModule/analysis/user/positionScore/' + id, method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 获取所有训练场次科目成绩信息
|
||||
*/
|
||||
export const getUserTrainSubjectGradeApi = (id) =>
|
||||
fetch({ url: '/train/trainModule/analysis/user/subjectScore/' + id, method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 获取用户所操作设备的正确错误占比
|
||||
*/
|
||||
export const getUserOperationResultApi = (id) =>
|
||||
fetch({ url: '/train/trainModule/analysis/user/deviceOper/' + id, method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 获取各班组各号位平均成绩统计
|
||||
*/
|
||||
export const getTeamGroupAgeScoresApi = (id) =>
|
||||
fetch({ url: '/train/trainModule/analysis/subject/posAvgScore/' + id, method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 各号位的各班组操作步骤统计
|
||||
*/
|
||||
export const positionGroupOpeartionApi = (id) =>
|
||||
fetch({ url: '/train/trainModule/analysis/subject/posAvgOperation/' + id, method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 各号位的各班组操作步骤统计
|
||||
*/
|
||||
export const getTeamGroupDurationApi = (id) =>
|
||||
fetch({ url: '/train/trainModule/analysis/subject/posAvgDuration/' + id, method: 'get' })
|
||||
99
front/src/api/training/train.js
Normal file
@@ -0,0 +1,99 @@
|
||||
import fetch from '@/utils/fetch'
|
||||
|
||||
/**
|
||||
* 【查】 - 获取所有训练
|
||||
*/
|
||||
export const getPostApi = () =>
|
||||
fetch({ url: '/train/device/position', method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 分页获取所有号位
|
||||
* @param {object} paging
|
||||
* @returns
|
||||
*/
|
||||
export const getPostPageApi = (paging) =>
|
||||
fetch({ url: `/train/device/position/paging/?currentPage=${paging.currentPage}&pageSize=${paging.pageSize}`, method: 'get' })
|
||||
|
||||
/**
|
||||
* 【增】 - 新增训练
|
||||
* @param {object} data
|
||||
* @returns
|
||||
*/
|
||||
export const createTrainApi = (data) =>
|
||||
fetch({ url: '/train/trainModule', method: 'post', data })
|
||||
|
||||
/**
|
||||
* 【查】 - 获取所有训练信息
|
||||
* @returns
|
||||
*/
|
||||
export const getTrainListApi = (status) =>
|
||||
fetch({ url: '/train/trainModule?', method: 'get', params: { status } })
|
||||
|
||||
/**
|
||||
* 【查】 - 获取所有训练步骤
|
||||
* @param {number} id
|
||||
* @returns
|
||||
*/
|
||||
export const getTrainStepApi = (id) =>
|
||||
fetch({ url: '/train/trainModule/step/' + id, method: 'get' })
|
||||
|
||||
/**
|
||||
* 【查】 - 获取训练详细信息
|
||||
* @param {number} id
|
||||
* @returns
|
||||
*/
|
||||
export const getTrainDetailsApi = (id) =>
|
||||
fetch({ url: '/train/trainModule/details/' + id, method: 'get' })
|
||||
|
||||
/**
|
||||
* 【改】 - 获取所有训练信息
|
||||
* @param {number} id
|
||||
* @param {object} data
|
||||
* @returns
|
||||
*/
|
||||
export const patchStepInfoApi = ({ id, data }) =>
|
||||
fetch({ url: '/train/trainModule/log/' + id, method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【改】 - 编辑训练
|
||||
* @param {id} number
|
||||
* @param {object} data
|
||||
* @returns
|
||||
*/
|
||||
export const editorTrainApi = (id, data) =>
|
||||
fetch({ url: '/train/trainModule/status/' + id, method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【删】 - 删除训练
|
||||
* @param {number} id
|
||||
* @returns
|
||||
*/
|
||||
export const deleteTrainApi = (id) =>
|
||||
fetch({ url: '/train/trainModule/' + id, method: 'delete' })
|
||||
|
||||
/**
|
||||
* 【删】 - 清空训练
|
||||
* @param {Array} ids
|
||||
* @returns
|
||||
*/
|
||||
export const deleteAllTrainApi = () =>
|
||||
fetch({ url: '/train/trainModule/all', method: 'delete' })
|
||||
|
||||
|
||||
/**
|
||||
* 【改】 - 更新训练的步骤信息
|
||||
* @param {number} ids
|
||||
* @param {object} data
|
||||
* @returns
|
||||
*/
|
||||
export const updateStepApi = (id, data) =>
|
||||
fetch({ url: '/train/trainModule/log/' + id, method: 'patch', data })
|
||||
|
||||
/**
|
||||
* 【增】 - 训练结果保存
|
||||
* @param {number} ids
|
||||
* @param {object} data
|
||||
* @returns
|
||||
*/
|
||||
export const saveTrainScoreApi = (id, data) =>
|
||||
fetch({ url: '/train/trainModule/score/' + id, method: 'post', data })
|
||||
386
front/src/components/business/Preview.vue
Normal file
@@ -0,0 +1,386 @@
|
||||
<script lang="jsx">
|
||||
import { getAcceptType } from '@/utils'
|
||||
import { fileConverApi } from '@/api/system/resource'
|
||||
|
||||
import { dataReportMixin } from '@/utils/data-report'
|
||||
|
||||
function image (h) {
|
||||
return (
|
||||
<div class="r_t_image">
|
||||
<img
|
||||
src={this.src}
|
||||
draggable="false"
|
||||
style={`transform:translate(${this.image.x}px, ${this.image.y}px) scale(${this.image.s}) rotate(${this.image.r}deg)`}
|
||||
onmousedown={this.onMove}
|
||||
/>
|
||||
<div class="actions">
|
||||
<i class="el-icon-zoom-in" onclick={() => this.imageScale(1)} />
|
||||
<i class="el-icon-refresh-right" onclick={() => this.imageRotate(1)} />
|
||||
<i class="el-icon-full-screen" onclick={() => this.onFullScreen()} />
|
||||
<i class="el-icon-refresh-left" onclick={() => this.imageRotate(-1)} />
|
||||
<i class="el-icon-zoom-out" onclick={() => this.imageScale(-1)} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function video (h) {
|
||||
return (
|
||||
<div class="r_t_mp4">
|
||||
<video
|
||||
src={this.src}
|
||||
controls
|
||||
disablePictureInPicture
|
||||
controlsList="nodownload noremoteplayback noplaybackrate"
|
||||
class={{ r_t_mp4: 1, timeline: this.timeline }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function audio (h) {
|
||||
return (
|
||||
<div class="r_t_mp3">
|
||||
<div class="left">
|
||||
<i onclick={this.playAudio} class={'icon i-' + ['bofang', 'zanting'][this.mp3.state]} />
|
||||
</div>
|
||||
<div class="right">
|
||||
<p data-label="文件名称">{this.resource.name}</p>
|
||||
<p data-label="文件大小">{this.resource.size.formatFileSize()}</p>
|
||||
<p data-label="上传时间">{new Date(this.resource.createTime).format('yyyy-MM-dd')}</p>
|
||||
<div class="progress" onclick={this.onClickProgress} style={{ '--progress': this.mp3.current_time / this.mp3.duration }} />
|
||||
<div class="times" data-current-time={this.formatAudioTime(this.mp3.current_time)} data-duration-time={this.formatAudioTime(this.mp3.duration)}>
|
||||
</div>
|
||||
<audio ref="audioRef" src={this.src} oncanplay={this.onAudioCanplay} onended={() => (this.mp3.state = 0)} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function pdf (h) {
|
||||
return (
|
||||
<div class="r_t_pdf">
|
||||
<iframe src={this.src} />
|
||||
|
||||
<div class="actions">
|
||||
<i class="el-icon-full-screen" onclick={() => this.onFullScreen()} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const components = { image, audio, video, pdf }
|
||||
|
||||
export default {
|
||||
props: {
|
||||
timeline: { type: Boolean },
|
||||
resource: { type: Object }
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
src: null,
|
||||
isFullScreen: false,
|
||||
mp3: { state: 0, current_time: 0, duration: 0 },
|
||||
image: { x: 0, y: 0, s: 1, r: 0 },
|
||||
errorMsg: null
|
||||
}),
|
||||
|
||||
created () {
|
||||
const type = getAcceptType(this.resource.mimetype)
|
||||
if (!type) return
|
||||
|
||||
this.viewType = components[type] ? type : 'pdf'
|
||||
this.src = this.resource.preview?.fileLinkTransfer()
|
||||
|
||||
if (!this.src) this.toConver(this.resource)
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
this.isFullScreen = !!document.fullscreenElement
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
async toConver (resource) {
|
||||
try {
|
||||
const { data } = await fileConverApi(resource.id)
|
||||
resource.preview = data
|
||||
if (this.resource === resource) this.src = data.fileLinkTransfer()
|
||||
} catch (err) {
|
||||
this.errorMsg = '文件转换失败'
|
||||
}
|
||||
},
|
||||
|
||||
imageScale (s) {
|
||||
const _s = this.image.s + s * 0.2
|
||||
if (_s >= 0.6 && _s <= 2) this.image.s = _s
|
||||
},
|
||||
|
||||
imageRotate (r) {
|
||||
this.image.r = this.image.r + r * 90
|
||||
},
|
||||
|
||||
onFullScreen () {
|
||||
if (this.isFullScreen) {
|
||||
document.exitFullscreen()
|
||||
} else {
|
||||
this.$el.requestFullscreen()
|
||||
}
|
||||
},
|
||||
|
||||
onMove (el) {
|
||||
const ox = this.image.x
|
||||
const oy = this.image.y
|
||||
const sx = el.pageX
|
||||
const sy = el.pageY
|
||||
document.body.onmouseup = () => {
|
||||
el.target.onmouseup = null
|
||||
el.target.onmousemove = null
|
||||
}
|
||||
el.target.onmousemove = (e) => {
|
||||
this.image.x = ox + e.pageX - sx
|
||||
this.image.y = oy + e.pageY - sy
|
||||
}
|
||||
},
|
||||
|
||||
onClickProgress (e) {
|
||||
this.$refs.audioRef.currentTime = this.mp3.duration * e.offsetX / 300
|
||||
},
|
||||
|
||||
formatAudioTime (t) {
|
||||
return `${Math.floor(t / 60).toString().padStart(2, 0)}:${Math.ceil(t % 60).toString().padStart(2, 0)}`
|
||||
},
|
||||
|
||||
onAudioCanplay (e) {
|
||||
const handleTimes = ({ target }) => {
|
||||
this.mp3.current_time = target.currentTime
|
||||
this.mp3.duration = target.duration
|
||||
}
|
||||
e.target.addEventListener('timeupdate', handleTimes)
|
||||
handleTimes(e)
|
||||
},
|
||||
|
||||
playAudio () {
|
||||
if (this.$refs.audioRef.paused) {
|
||||
this.$refs.audioRef.play()
|
||||
this.mp3.state = 1
|
||||
} else {
|
||||
this.$refs.audioRef.pause()
|
||||
this.mp3.state = 0
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
render (h) {
|
||||
if (!this.viewType) return <div class="no_resource_preview">该文件暂不支持预览</div>
|
||||
|
||||
if (this.errorMsg) return <div class="load_file_error">{this.errorMsg}</div>
|
||||
|
||||
if (!this.src) return <div class="load_file">文件加载中,请稍后...</div>
|
||||
|
||||
return (
|
||||
<div class={{ resource_layout: 1, 'is-full': this.isFullScreen }}>{
|
||||
components[this.viewType]?.call(this, h)
|
||||
}</div>
|
||||
)
|
||||
},
|
||||
|
||||
mixins: [
|
||||
dataReportMixin('RESOURCE_VIEW', {
|
||||
isCancelReport () { return !this.viewType },
|
||||
handleReportData () { return { field01: this.resource.id } }
|
||||
}, true)
|
||||
]
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.no_resource_preview,
|
||||
.load_file_error,
|
||||
.load_file {
|
||||
text-align: center;
|
||||
line-height: 180px;
|
||||
color: $--color-primary;
|
||||
background-color: #fafafa;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.load_file_error {
|
||||
color: $--color-danger;
|
||||
}
|
||||
|
||||
.resource_layout {
|
||||
display: flex;
|
||||
min-width: 580px;
|
||||
min-height: 150px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
.actions {
|
||||
padding: 10px;
|
||||
position: absolute;
|
||||
bottom: 12px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
background-color: rgba(0, 0, 0, .6);
|
||||
border-radius: 88px;
|
||||
|
||||
i {
|
||||
margin: 0 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.r_t_image {
|
||||
img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
cursor: move;
|
||||
}
|
||||
}
|
||||
|
||||
.r_t_pdf {
|
||||
width: 100%;
|
||||
height: 70vh;
|
||||
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-full .r_t_pdf {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.r_t_mp3 {
|
||||
margin: 30px 0;
|
||||
display: flex;
|
||||
|
||||
.left {
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
border: 2px solid rgba($color: $--color-primary, $alpha: 0.6);
|
||||
box-shadow: 0 0 6px 1px rgba($color: $--color-primary, $alpha: 0.3);
|
||||
border-radius: 6px;
|
||||
background-color: rgba($color: $--color-primary, $alpha: 0.1);
|
||||
|
||||
.icon {
|
||||
margin-top: -25px;
|
||||
margin-left: -25px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
font-size: 50px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
box-shadow: inherit;
|
||||
border-radius: inherit;
|
||||
background-color: #208ac6;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
margin-left: 24px;
|
||||
|
||||
> p {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 16px;
|
||||
width: 300px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: 1.3;
|
||||
|
||||
&::before {
|
||||
content: attr(data-label) ':';
|
||||
color: #aaa;
|
||||
}
|
||||
}
|
||||
|
||||
.progress {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 6px;
|
||||
height: 8px;
|
||||
border-radius: 30px;
|
||||
background-color: #f5f5f5;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: calc(100% - 100% * var(--progress));
|
||||
background-color: #208ac6;
|
||||
border-radius: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.times {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: #aaa;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: attr(data-current-time);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: attr(data-duration-time);
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-left: -16px;
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
background-color: #87D2D9;
|
||||
background-image: linear-gradient(260deg,#40b8c3,#78d5de);
|
||||
box-shadow: 3px 4px 8px 0 #c0c4cc;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.r_t_mp4 {
|
||||
&,
|
||||
video {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-height: 66vh;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
video:not(.timeline)::-webkit-media-controls-timeline {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@keyframes rotating {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
47
front/src/components/layout/ActionBar.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div :class="['action_bar', { 't-center': center, 't-right': right }]">
|
||||
<el-button v-if="!noCencel" round @click="handleClick('onCancel')">
|
||||
{{ cencelTxt }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="!noConfirm"
|
||||
type="primary"
|
||||
:loading="confirmLoading"
|
||||
round
|
||||
@click="handleClick('onConfirm')"
|
||||
>
|
||||
{{ confirmTxt }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
noConfirm: { type: Boolean },
|
||||
noCencel: { type: Boolean },
|
||||
center: { type: Boolean },
|
||||
right: { type: Boolean },
|
||||
cencelTxt: { type: String, default: '取消' },
|
||||
confirmTxt: { type: String, default: '确定' },
|
||||
confirmLoading: { type: Boolean }
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleClick (name) {
|
||||
this.$emit(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.action_bar {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
.el-button + .el-button {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
112
front/src/components/layout/DialogLayout.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<div class="dialog_layout" v-if="visible">
|
||||
<div class="_ctx" :style="{ width }">
|
||||
<h2 class="_title">
|
||||
{{ title }}<i class="_close el-icon-close" @click="onCancel" v-if="showClose" />
|
||||
</h2>
|
||||
<div class="_body" :style="{ height: bodyHeight }"><slot /></div>
|
||||
<ActionBar
|
||||
:class="{ shadow_bar: shadowBar }"
|
||||
center
|
||||
v-bind="actionBarOption"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ActionBar from './ActionBar.vue'
|
||||
|
||||
export default {
|
||||
components: { ActionBar },
|
||||
|
||||
props: {
|
||||
visible: { type: Boolean },
|
||||
shadowBar: { type: Boolean, default: true },
|
||||
title: { type: String },
|
||||
showClose: { type: Boolean, default: true },
|
||||
width: { type: String },
|
||||
bodyHeight: { type: String },
|
||||
actionBarOption: { type: Object }
|
||||
},
|
||||
|
||||
watch: {
|
||||
visible: {
|
||||
immediate: true,
|
||||
handler (v) {
|
||||
if (!v) return
|
||||
|
||||
this.$nextTick(() => document.body.appendChild(this.$el))
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy () {
|
||||
this.$emit('update:visible', false)
|
||||
this.$el.parentNode?.removeChild(this.$el)
|
||||
},
|
||||
|
||||
methods: {
|
||||
onCancel () {
|
||||
this.$emit('onCancel')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.dialog_layout {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 99;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
|
||||
> ._ctx {
|
||||
min-width: 300px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 46%;
|
||||
transform: translate(-50%, -50%);
|
||||
border-radius: 6px;
|
||||
background-color: #fff;
|
||||
overflow: hidden;
|
||||
|
||||
> ._title {
|
||||
padding: 12px;
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
background-color: #87d2d9;
|
||||
position: relative;
|
||||
|
||||
> ._close {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
> ._body {
|
||||
padding: 12px;
|
||||
max-height: 80vh;
|
||||
overflow: hidden auto;
|
||||
}
|
||||
|
||||
> .action_bar {
|
||||
padding: 0 12px 12px;
|
||||
|
||||
&.shadow_bar {
|
||||
padding: 8px 12px;
|
||||
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
57
front/src/components/layout/FormLayout.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<script>
|
||||
export default {
|
||||
inheritAttrs: false,
|
||||
|
||||
methods: {
|
||||
async validate () {
|
||||
try {
|
||||
await this.$refs.form_layout.validate()
|
||||
} catch (err) {
|
||||
const msg = Object.values(err)[0][0].message
|
||||
this.$message.error(msg)
|
||||
throw msg
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
render (h) {
|
||||
const { items, ...props } = this.$attrs
|
||||
const childs = []
|
||||
for (let i = 0, len = items.length; i < len; i++) {
|
||||
const { model, style, ...itemProps } = items[i]
|
||||
childs.push(h(
|
||||
'el-form-item',
|
||||
{
|
||||
style: { width: props.inline === '' ? '36%' : null, ...style },
|
||||
props: itemProps,
|
||||
scopedSlots: {
|
||||
default: () => {
|
||||
if (itemProps.prop && this.$scopedSlots[itemProps.prop]) return this.$scopedSlots[itemProps.prop](items[i])
|
||||
|
||||
const { tag, on, attrs = {}, ...modelProps } = model
|
||||
attrs.maxlength ??= 99
|
||||
|
||||
return h(tag, {
|
||||
props: { ...modelProps, value: props.model[itemProps.prop] },
|
||||
attrs: { placeholder: tag === 'el-input' ? '请输入' : modelProps.placeholder || '请选择', ...attrs },
|
||||
on: { input: (val) => (props.model[itemProps.prop] = val), ...on }
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
))
|
||||
}
|
||||
return h('el-form', { ref: 'form_layout', props, class: 'form_layout' }, childs)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.form_layout {
|
||||
.el-cascader,
|
||||
.el-select,
|
||||
.el-date-editor.el-input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
133
front/src/components/layout/SearchTreeMenu.vue
Normal file
@@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<div class="search_tree_menu v-menu">
|
||||
<h2 class="v-title">{{ title }}</h2>
|
||||
|
||||
<SearchInput v-model="search" />
|
||||
|
||||
<div class="mt-12 t-right">
|
||||
<el-button v-if="$listeners.onCopy" type="warning" circle plain icon="el-icon-copy-document" size="mini" title="复制" @click="handleCopy" />
|
||||
<el-button v-if="$listeners.onEdit" type="primary" circle plain icon="el-icon-edit" size="mini" title="编辑" @click="handleEdit" />
|
||||
<el-button v-if="$listeners.onCreate" type="success" circle plain icon="el-icon-plus" size="mini" title="创建" @click="handleCreate" />
|
||||
<el-button v-if="$listeners.onDelete" type="danger" circle plain icon="el-icon-delete" size="mini" title="删除" @click="handleDelete" />
|
||||
</div>
|
||||
|
||||
<div class="tree_layout">
|
||||
<ElTree
|
||||
ref="treeRef"
|
||||
default-expand-all
|
||||
:expand-on-click-node="false"
|
||||
highlight-current
|
||||
:props="{ label: 'name' }"
|
||||
v-bind="$attrs"
|
||||
:data="treeData"
|
||||
:node-key="treeNodeKey"
|
||||
:filter-node-method="filterNode"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { SearchInput } from '../widget'
|
||||
|
||||
export default {
|
||||
components: { SearchInput },
|
||||
|
||||
props: {
|
||||
title: { type: String },
|
||||
treeNodeKey: { type: String, default: 'id' },
|
||||
treeData: { type: Array, default: () => [] },
|
||||
treeProps: { type: Object, default: () => ({}) }
|
||||
},
|
||||
|
||||
data: () => ({ search: '' }),
|
||||
|
||||
watch: {
|
||||
search (v) {
|
||||
this.$refs.treeRef.filter(v)
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
filterNode (value, data) {
|
||||
if (!value) return true
|
||||
return data.name.includes(value)
|
||||
},
|
||||
|
||||
/**
|
||||
* (key) 待被选节点的 key,若为 null 则取消当前高亮的节点
|
||||
*/
|
||||
setCurrentKey (key) {
|
||||
return this.$refs.treeRef.setCurrentKey(key)
|
||||
},
|
||||
|
||||
getCurrentNode () {
|
||||
return this.$refs.treeRef.getCurrentNode()
|
||||
},
|
||||
|
||||
handleCreate () {
|
||||
this.$emit('onCreate', this.getCurrentNode())
|
||||
},
|
||||
|
||||
handleEdit () {
|
||||
const checkedTreeNode = this.getCurrentNode()
|
||||
if (!checkedTreeNode) return this.$message.error('请选择要修改的数据')
|
||||
this.$emit('onEdit', checkedTreeNode)
|
||||
},
|
||||
|
||||
handleCopy () {
|
||||
const checkedTreeNode = this.getCurrentNode()
|
||||
if (!checkedTreeNode) return this.$message.error('请选择要复制的数据')
|
||||
this.$emit('onCopy', checkedTreeNode)
|
||||
},
|
||||
|
||||
handleDelete () {
|
||||
const checkedTreeNode = this.getCurrentNode()
|
||||
if (!checkedTreeNode) return this.$message.error('请选择要删除的数据')
|
||||
this.$emit('onDelete', checkedTreeNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.search_tree_menu .tree_layout {
|
||||
|
||||
.el-tree-node__label {
|
||||
color: $--color-text-regular;
|
||||
}
|
||||
|
||||
.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content .el-tree-node__label {
|
||||
color: $--color-primary;
|
||||
}
|
||||
|
||||
.el-tree-node:focus > .el-tree-node__content,
|
||||
.el-tree-node__content:hover {
|
||||
background-color: #ecf8f9;
|
||||
}
|
||||
|
||||
.el-tree__empty-text {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search_tree_menu.v-menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.v-title {
|
||||
margin-bottom: 26px;
|
||||
}
|
||||
|
||||
.tree_layout {
|
||||
flex: 1;
|
||||
margin-top: 14px;
|
||||
padding-top: 14px;
|
||||
border-top: 1px solid #d8d8d8;
|
||||
overflow: overlay;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
129
front/src/components/layout/TableLayout.vue
Normal file
@@ -0,0 +1,129 @@
|
||||
<script>
|
||||
import Pagination from '../widget/Pagination.vue'
|
||||
|
||||
export default {
|
||||
inheritAttrs: false,
|
||||
|
||||
props: {
|
||||
selection: { type: Boolean },
|
||||
pageInfo: { type: Object }
|
||||
},
|
||||
|
||||
created () {
|
||||
this.handleColumn = {
|
||||
actions: (actions, props) => {
|
||||
return actions.map((v) => this.$createElement(
|
||||
'el-button',
|
||||
{
|
||||
props: { type: 'text', icon: 'el-icon-' + v.type },
|
||||
class: '_action_ ' + v.type,
|
||||
on: { click: () => v.click && v.click(v.type, props, actions) }
|
||||
},
|
||||
v.label
|
||||
))
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
toggleRowSelections (rows, selected) {
|
||||
for (const row of rows) {
|
||||
this.$refs.tableRef.toggleRowSelection(row, selected)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
render (h) {
|
||||
const { column, data, calcMaxHeight, ...tableProps } = this.$attrs
|
||||
const cols = []
|
||||
|
||||
if (this.selection) cols.push(h('el-table-column', { props: { type: 'selection', width: '44' } }))
|
||||
|
||||
for (let i = 0, len = column.length; i < len; i++) {
|
||||
const { on, actions, ...props } = column[i]
|
||||
cols.push(h('el-table-column', {
|
||||
props,
|
||||
on,
|
||||
scopedSlots: props.type === 'index'
|
||||
? undefined
|
||||
: {
|
||||
default: (properties) => {
|
||||
if (props.prop && this.$scopedSlots[props.prop]) return this.$scopedSlots[props.prop](properties)
|
||||
return properties.row[properties.column.property]
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
return h('div', { class: 'table_layout' }, [
|
||||
h('el-table', {
|
||||
ref: 'tableRef',
|
||||
style: { '--table-max-height': calcMaxHeight },
|
||||
props: { data, stripe: true, border: true, ...tableProps },
|
||||
on: { ...this.$listeners, 'current-change': this.$listeners['table-current-change'] }
|
||||
}, cols),
|
||||
|
||||
this.pageInfo && h(Pagination, { props: { pageInfo: this.pageInfo }, on: this.$listeners })
|
||||
])
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.table_layout {
|
||||
|
||||
.el-table {
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0px 2px 10px 0px rgba(16,166,180,0.2);
|
||||
border: 1px solid #208ac6;
|
||||
|
||||
.cell {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
th.el-table__cell {
|
||||
padding: 6px 0;
|
||||
background-color: $--color-primary;
|
||||
|
||||
.cell {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
._action_.edit {
|
||||
color: $--color-primary;
|
||||
}
|
||||
|
||||
._action_.view {
|
||||
color: $--color-primary;
|
||||
}
|
||||
|
||||
._action_.delete {
|
||||
color: $--color-danger;
|
||||
}
|
||||
|
||||
.el-table__body-wrapper {
|
||||
max-height: calc(100vh - var(--table-max-height));
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.el-table__header .el-table__cell {
|
||||
border-right-color: rgba($color: #fff, $alpha: 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.el-table::before, .el-table--group::after, .el-table--border::after {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell{
|
||||
background-color: #f1fafa;
|
||||
}
|
||||
|
||||
.el-pagination {
|
||||
margin-top: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
13
front/src/components/layout/index.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import ActionBar from './ActionBar.vue'
|
||||
import FormLayout from './FormLayout.vue'
|
||||
import TableLayout from './TableLayout.vue'
|
||||
import DialogLayout from './DialogLayout.vue'
|
||||
import SearchTreeMenu from './SearchTreeMenu.vue'
|
||||
|
||||
export {
|
||||
ActionBar,
|
||||
FormLayout,
|
||||
TableLayout,
|
||||
DialogLayout,
|
||||
SearchTreeMenu
|
||||
}
|
||||
33
front/src/components/widget/Pagination.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
pageInfo: { type: Object, default: () => ({}) }
|
||||
},
|
||||
|
||||
render (h) {
|
||||
const onPageInfoChange = this.$listeners['page-info-change'] && {
|
||||
'size-change': (size) => {
|
||||
// eslint-disable-next-line vue/no-mutating-props
|
||||
this.pageInfo.size = size
|
||||
this.$listeners?.['page-info-change']()
|
||||
},
|
||||
'current-change': (current) => {
|
||||
// eslint-disable-next-line vue/no-mutating-props
|
||||
this.pageInfo.current = current
|
||||
this.$listeners?.['page-info-change']()
|
||||
}
|
||||
}
|
||||
return h('el-pagination', {
|
||||
props: {
|
||||
background: true,
|
||||
layout: 'total, sizes, prev, pager, next, jumper',
|
||||
currentPage: this.pageInfo.current,
|
||||
pageSize: this.pageInfo.size,
|
||||
pageSizes: [10, 30, 50, 100],
|
||||
...this.pageInfo
|
||||
},
|
||||
on: { ...this.$listeners, ...onPageInfoChange }
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
61
front/src/components/widget/QueryInput.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<div class="query_input el-input el-input--small">
|
||||
<input
|
||||
v-model="value"
|
||||
type="text"
|
||||
placeholder="请输入查询内容"
|
||||
class="el-input__inner"
|
||||
@keyup.enter="query"
|
||||
/>
|
||||
<div class="suf" @click="query">查询</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data: () => ({ value: '' }),
|
||||
|
||||
watch: {
|
||||
'$attrs.value': {
|
||||
immediate: true,
|
||||
handler (v) {
|
||||
this.value = v
|
||||
}
|
||||
},
|
||||
|
||||
value (v) {
|
||||
this.$emit('input', v)
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
query () {
|
||||
this.$emit('query')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.query_input {
|
||||
width: 188px;
|
||||
position: relative;
|
||||
|
||||
.el-input__inner {
|
||||
padding-right: 51px;
|
||||
}
|
||||
|
||||
.suf {
|
||||
padding: 0 10px 0 6px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 12px;
|
||||
color: $--color-text-secondary;
|
||||
border-left: 1px solid $--color-text-secondary;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
55
front/src/components/widget/SearchInput.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div class="search_input el-input el-input--small">
|
||||
<input
|
||||
v-model="value"
|
||||
type="text"
|
||||
placeholder="请输入搜索内容"
|
||||
class="el-input__inner"
|
||||
/>
|
||||
<i class="el-icon-search" title="搜索" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data: () => ({ value: '' }),
|
||||
|
||||
watch: {
|
||||
'$attrs.value': {
|
||||
immediate: true,
|
||||
handler (v) {
|
||||
this.value = v
|
||||
}
|
||||
},
|
||||
|
||||
value (v) {
|
||||
this.$emit('input', v)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.search_input {
|
||||
.el-input__inner {
|
||||
padding-right: 51px;
|
||||
}
|
||||
|
||||
.el-icon-search {
|
||||
width: 36px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
background-color: $--color-primary;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
9
front/src/components/widget/index.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import Pagination from './Pagination.vue'
|
||||
import QueryInput from './QueryInput.vue'
|
||||
import SearchInput from './SearchInput.vue'
|
||||
|
||||
export {
|
||||
Pagination,
|
||||
QueryInput,
|
||||
SearchInput
|
||||
}
|
||||
26
front/src/main.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import './utils/prototype'
|
||||
import './utils/capture'
|
||||
import './utils/data-report'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
|
||||
import 'element-ui/packages/theme-chalk/src/index.scss'
|
||||
import './styles/index.scss'
|
||||
import './styles/element-ui.scss'
|
||||
import './utils/iframe-message'
|
||||
/**
|
||||
* 挂载全局 $ELEMENT 属性
|
||||
*/
|
||||
window.Vue.prototype.$ELEMENT = { size: 'small', zIndex: 99 }
|
||||
|
||||
/**
|
||||
* 挂载全局 $store 属性
|
||||
*/
|
||||
window.Vue.prototype.$store = store
|
||||
/**
|
||||
* 创建 Vue 实例
|
||||
*/
|
||||
new window.Vue({
|
||||
router,
|
||||
render: (h) => h('RouterView')
|
||||
}).$mount('#app')
|
||||
223
front/src/router/feature.js
Normal file
@@ -0,0 +1,223 @@
|
||||
export const KeepAliveLayout = {
|
||||
template: '<KeepAlive><RouterView /></KeepAlive>'
|
||||
}
|
||||
|
||||
export const features = [
|
||||
{
|
||||
path: 'electronic-textbook',
|
||||
meta: { title: '电子化教材', icon: 'i-xitongguanli' },
|
||||
component: () => import('@/views/electronic-textbook')
|
||||
},
|
||||
// {
|
||||
// path: 'app1',
|
||||
// meta: { title: '测试', icon: 'i-xitongguanli', type: 'qiankun' }
|
||||
// component: () => import('@/views/test')
|
||||
// },
|
||||
{
|
||||
path: 'system',
|
||||
meta: { title: '系统管理', icon: 'i-xitongguanli' },
|
||||
component: KeepAliveLayout,
|
||||
children: [
|
||||
{
|
||||
path: 'user',
|
||||
meta: { title: '用户管理' },
|
||||
component: () => import('@/views/system/user')
|
||||
},
|
||||
{
|
||||
path: 'org',
|
||||
meta: { title: '组织机构管理' },
|
||||
component: () => import('@/views/system/org')
|
||||
},
|
||||
{
|
||||
path: 'role',
|
||||
meta: { title: '角色管理' },
|
||||
component: () => import('@/views/system/role')
|
||||
},
|
||||
{
|
||||
path: 'electronic-textbook-manage',
|
||||
meta: { title: '手册管理', icon: 'i-xitongguanli' },
|
||||
component: () => import('@/views/textbook-manage')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'train',
|
||||
meta: { title: '训练管理', icon: 'i-zaixiankecheng' },
|
||||
component: KeepAliveLayout,
|
||||
children: [
|
||||
{
|
||||
path: 'subject',
|
||||
meta: { title: '科目设置' },
|
||||
component: () => import('@/views/training/subject')
|
||||
},
|
||||
{
|
||||
path: 'training',
|
||||
meta: { title: '组织训练' },
|
||||
component: () => import('@/views/training/training')
|
||||
},
|
||||
{
|
||||
path: 'device',
|
||||
meta: { title: '设备管理' },
|
||||
component: () => import('@/views/training/device')
|
||||
},
|
||||
{
|
||||
path: 'evealuation',
|
||||
meta: { title: '统计分析' },
|
||||
component: () => import('@/views/training/evealuation')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'resource',
|
||||
meta: { title: '资源管理', icon: 'i-zaixiankecheng' },
|
||||
component: KeepAliveLayout,
|
||||
children: [
|
||||
{
|
||||
path: 'resource-manage',
|
||||
meta: { title: '资源管理' },
|
||||
component: () => import('@/views/system/resource')
|
||||
},
|
||||
{
|
||||
path: 'resource-share',
|
||||
meta: { title: '资源分享' },
|
||||
component: () => import('@/views/system/resource/share')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'online-course',
|
||||
meta: { title: '在线授课', icon: 'i-zaixianjiaoxue' },
|
||||
component: KeepAliveLayout,
|
||||
children: [
|
||||
{
|
||||
path: 'course-manage',
|
||||
meta: { title: '课程管理' },
|
||||
component: () => import('@/views/online-course/course-manage')
|
||||
},
|
||||
{
|
||||
path: 'my-course',
|
||||
meta: { title: '我的课程' },
|
||||
component: () => import('@/views/online-course/my-course')
|
||||
},
|
||||
{
|
||||
path: 'class-notes',
|
||||
meta: { title: '课堂笔记' },
|
||||
component: () => import('@/views/online-course/class-notes')
|
||||
},
|
||||
{
|
||||
path: 'online-FAQ',
|
||||
meta: { title: '在线答疑' },
|
||||
component: () => import('@/views/online-course/online-FAQ')
|
||||
},
|
||||
{
|
||||
path: 'live-lectures',
|
||||
meta: { title: '直播授课' },
|
||||
component: () => import('@/views/online-teaching/live-lectures')
|
||||
},
|
||||
{
|
||||
path: 'live-in-class',
|
||||
meta: { title: '直播上课' },
|
||||
component: () => import('@/views/online-teaching/live-in-class')
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// {
|
||||
// path: 'online-teaching',
|
||||
// meta: { title: '在线教学', icon: 'i-zaixianjiaoxue' },
|
||||
// component: KeepAliveLayout,
|
||||
// children: [
|
||||
|
||||
// ]
|
||||
// },
|
||||
|
||||
{
|
||||
path: 'assessment-evaluation',
|
||||
meta: { title: '课程考核', icon: 'i-kaoheceping' },
|
||||
component: KeepAliveLayout,
|
||||
children: [
|
||||
{
|
||||
path: 'question-bank-manage',
|
||||
meta: { title: '题库管理' },
|
||||
component: () =>
|
||||
import('@/views/assessment-evaluation/question-bank-manage')
|
||||
},
|
||||
{
|
||||
path: 'examination-paper-manage',
|
||||
meta: { title: '试卷管理' },
|
||||
component: () =>
|
||||
import('@/views/assessment-evaluation/examination-paper-manage')
|
||||
},
|
||||
{
|
||||
path: 'exam-arrangement',
|
||||
meta: { title: '考试安排' },
|
||||
component: () =>
|
||||
import('@/views/assessment-evaluation/exam-arrangement')
|
||||
},
|
||||
{
|
||||
path: 'human-evaluation',
|
||||
meta: { title: '人工评卷' },
|
||||
component: () =>
|
||||
import('@/views/assessment-evaluation/human-evaluation')
|
||||
},
|
||||
{
|
||||
path: 'simulation-test',
|
||||
meta: { title: '模拟考试' },
|
||||
component: () =>
|
||||
import('@/views/assessment-evaluation/simulation-test')
|
||||
},
|
||||
{
|
||||
path: 'online-test',
|
||||
meta: { title: '在线考试' },
|
||||
component: () => import('@/views/assessment-evaluation/online-test')
|
||||
},
|
||||
{
|
||||
path: 'wrong-topic-consolidate',
|
||||
meta: { title: '错题巩固' },
|
||||
component: () =>
|
||||
import('@/views/assessment-evaluation/wrong-topic-consolidate')
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: 'evaluation',
|
||||
meta: { title: '综合评价', icon: 'i-pingjia' },
|
||||
component: KeepAliveLayout,
|
||||
children: [
|
||||
{
|
||||
path: 'student-evaluation',
|
||||
meta: { title: '学员评价' },
|
||||
component: () => import('@/views/evaluation/student-evaluation')
|
||||
},
|
||||
{
|
||||
path: 'evaluation-setting',
|
||||
meta: { title: '评价设置' },
|
||||
component: () => import('@/views/evaluation/evaluation-setting')
|
||||
},
|
||||
{
|
||||
path: 'my-evaluation',
|
||||
meta: { title: '我的评价' },
|
||||
component: () => import('@/views/evaluation/my-evaluation')
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
export function findAllFeature () {
|
||||
const fn = (list) => {
|
||||
const ls = []
|
||||
for (const v of list) {
|
||||
const item = { name: v.path, label: v.meta.title }
|
||||
if (v.children && v.children.length) {
|
||||
item.children = fn(v.children)
|
||||
} else {
|
||||
item.children = []
|
||||
}
|
||||
|
||||
ls.push(item)
|
||||
}
|
||||
return ls
|
||||
}
|
||||
return fn(features)
|
||||
}
|
||||
77
front/src/router/index.js
Normal file
@@ -0,0 +1,77 @@
|
||||
import { lst } from '../utils'
|
||||
import { features, KeepAliveLayout } from './feature'
|
||||
import { apps } from '@/utils/qiankun-init'
|
||||
import routes from './routes'
|
||||
const router = new window.VueRouter({
|
||||
mode: 'history',
|
||||
base: '',
|
||||
routes: [
|
||||
{ path: '/login', meta: { noToken: true }, component: () => import('@/views/login') },
|
||||
{ path: '/register', meta: { noToken: true }, component: () => import('@/views/register') },
|
||||
{ path: '/404', meta: { noToken: true }, component: () => import('@/views/404') },
|
||||
{ path: '/entry', meta: { noToken: true }, component: () => import('@/views/entry') }
|
||||
]
|
||||
})
|
||||
|
||||
const HomePage = {
|
||||
path: '/',
|
||||
meta: { title: '训练管理' },
|
||||
component: () => import('@/views/home'),
|
||||
children: [{ path: '/', meta: { title: '数据统计' }, component: () => import('@/views/statistic') },
|
||||
{ path: '/print-paper/:paperId?', meta: { noToken: false, key: 'printPaper' }, component: () => import('@/views/assessment-evaluation/examination-paper-manage/components/PrintPaper.vue') }
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
export function initRoutes () {
|
||||
const moduleAuth = lst.get('module')
|
||||
if (!moduleAuth) return
|
||||
|
||||
const fn = (list) => {
|
||||
const routeList = []
|
||||
const menuList = []
|
||||
for (const v of list) {
|
||||
if (moduleAuth.includes(v.path)) {
|
||||
const menu = { name: v.path, label: v.meta.title, icon: v.meta.icon }
|
||||
if (v.children && v.children.length) {
|
||||
const child = fn(v.children)
|
||||
routeList.push({ ...v, children: child.routeList })
|
||||
menu.children = child.menuList
|
||||
} else {
|
||||
routeList.push(v)
|
||||
|
||||
if (routes[v.path]) {
|
||||
routeList.push({ path: v.path, meta: v.meta, component: KeepAliveLayout, children: routes[v.path] })
|
||||
}
|
||||
}
|
||||
menuList.push(menu)
|
||||
}
|
||||
}
|
||||
return { routeList, menuList }
|
||||
}
|
||||
apps && features.push(...apps.map(i => {
|
||||
return {
|
||||
path: i.activeRule,
|
||||
meta: { title: i.name }
|
||||
}
|
||||
}))
|
||||
const { routeList, menuList } = fn(features)
|
||||
console.log(menuList)
|
||||
lst.save('menu', menuList)
|
||||
HomePage.children.push(...routeList)
|
||||
// router.addRoutes([HomePage, { path: '*', redirect: '/404' }])
|
||||
router.addRoutes([HomePage])
|
||||
}
|
||||
|
||||
initRoutes()
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
console.log(to.meta.noToken || lst.get('token', '').startsWith('Bearer '))
|
||||
if (to.meta.noToken || lst.get('token', '').startsWith('Bearer ')) {
|
||||
next()
|
||||
} else {
|
||||
next('/login')
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
62
front/src/router/routes.js
Normal file
@@ -0,0 +1,62 @@
|
||||
/**********************************************************************************************************************
|
||||
格式: [从属页面的 path 属性值]: [ { 路由对象 }, { 路由对象 } ]
|
||||
|
||||
例子如下:
|
||||
'question-bank-manage': [
|
||||
{ path: 'add-question', meta: { title: '添加试题' }, component: () => import('@/views/assessment-evaluation/add-question') }
|
||||
]
|
||||
**********************************************************************************************************************/
|
||||
export default {
|
||||
'course-manage': [
|
||||
{ path: 'course-evaluate/:courseId', meta: { title: '课程评价' }, component: () => import('@/views/online-course/course-manage/course-evaluate') },
|
||||
{ path: 'course-comment/:courseId', meta: { title: '课程评论' }, component: () => import('@/views/online-course/course-manage/course-comment') },
|
||||
{ path: 'add-coures', meta: { title: '添加课程' }, component: () => import('@/views/online-course/course-manage/add-coures') },
|
||||
{ path: 'edit-coures/:courseId', meta: { title: '编辑课程' }, component: () => import('@/views/online-course/course-manage/add-coures') }
|
||||
],
|
||||
'my-course': [
|
||||
{ path: 'course-study/:courseId', meta: { title: '课程学习' }, component: () => import('@/views/online-course/course-study') },
|
||||
{ path: 'course-exercies', meta: { title: '课后习题' }, component: () => import('@/views/online-course/course-exercises/course-exercises.vue') }
|
||||
],
|
||||
'question-bank-manage': [
|
||||
{ path: 'add-modify-question/:questionId?', meta: { title: '添加试题' }, component: () => import('@/views/assessment-evaluation/question-bank-manage/add-modify-question') }
|
||||
],
|
||||
'examination-paper-manage': [
|
||||
{ path: 'add-modify-paper', meta: { title: '添加试卷' }, component: () => import('@/views/assessment-evaluation/examination-paper-manage/add-modify-paper') },
|
||||
{ path: 'add-modify-paper/:paperId?', meta: { title: '编辑试卷' }, component: () => import('@/views/assessment-evaluation/examination-paper-manage/add-modify-paper') }
|
||||
],
|
||||
'exam-arrangement': [
|
||||
{ path: 'add-modify-exam', meta: { title: '发布考试' }, component: () => import('@/views/assessment-evaluation/exam-arrangement/add-modify-exam') },
|
||||
{ path: 'add-modify-exam/:examId?', meta: { title: '编辑考试' }, component: () => import('@/views/assessment-evaluation/exam-arrangement/add-modify-exam') },
|
||||
{ path: 'add-modify-exam/:examId?/:type?', meta: { title: '复制考试' }, component: () => import('@/views/assessment-evaluation/exam-arrangement/add-modify-exam') }
|
||||
],
|
||||
'online-test': [
|
||||
{ path: 'begin-online-exam/:studentExamId', meta: { title: '在线考试' }, component: () => import('@/views/assessment-evaluation/online-test/begin-online-exam') }
|
||||
],
|
||||
'simulation-test': [
|
||||
{ path: 'add-sim-test/:paperId?', meta: { title: '新建考试' }, component: () => import('@/views/assessment-evaluation/simulation-test/add-modify-sim-test') }
|
||||
],
|
||||
'human-evaluation': [
|
||||
{ path: 'eval-details/:examId?', meta: { title: '人员评卷' }, component: () => import('@/views/assessment-evaluation/human-evaluation/eval-details') },
|
||||
{ path: 'begin-eval/:studentExamId?', meta: { title: '开始判卷' }, component: () => import('@/views/assessment-evaluation/online-test/begin-online-exam') }
|
||||
],
|
||||
'wrong-topic-consolidate': [
|
||||
{ path: 'mistake-again/:classifyId/:mode?', meta: { title: '错题巩固' }, component: () => import('@/views/assessment-evaluation/wrong-topic-consolidate/mistakes-again') },
|
||||
{ path: 'history-exam-priview/:studentExamId', meta: { title: '试卷预览' }, component: () => import('@/views/assessment-evaluation/online-test/begin-online-exam') }
|
||||
],
|
||||
'live-lectures': [
|
||||
{ path: 'live-editor', meta: { title: '新建直播' }, component: () => import('@/views/online-teaching/add-live') },
|
||||
{ path: 'live-play', meta: { title: '进行直播' }, component: () => import('@/views/online-teaching/live-play') }
|
||||
// { path: 'live-playback', meta: { title: '直播回放' }, component: () => import('@/views/online-teaching/live-playback') },
|
||||
],
|
||||
'live-in-class': [
|
||||
{ path: 'live-watch', meta: { title: '观看直播' }, component: () => import('@/views/online-teaching/live-watch') },
|
||||
{ path: 'live-watch/answering', meta: { title: '直播练习题' }, component: () => import('@/views/online-teaching/live-answering-question') }
|
||||
],
|
||||
device: [
|
||||
{ path: 'create-device', meta: { title: '设备编辑' }, component: () => import('@/views/training/device/device-create/index.vue') }
|
||||
],
|
||||
user: [
|
||||
{ path: 'batch-add', meta: { title: '批量添加' }, component: () => import('@/views/system/user/batch-add-user') }],
|
||||
subject: [{ path: 'subject-editor', meta: { title: '编辑科目' }, component: () => import('@/views/training/subject/editor-subject') }]
|
||||
// training: [{ path: 'create-train', meta: { title: '添加训练' }, component: () => import('@/views/training/training/create-train') }]
|
||||
}
|
||||
144
front/src/store/index.js
Normal file
@@ -0,0 +1,144 @@
|
||||
import { checkAdminPasswordApi, uploadApi } from '@/api/system/index'
|
||||
import { findResourceApi } from '@/api/system/resource'
|
||||
import { lst, getDistFile, getAcceptType } from '@/utils/index'
|
||||
|
||||
const store = {
|
||||
user: undefined,
|
||||
|
||||
isOpenWin: location.hash.includes('win=1'),
|
||||
|
||||
statistic_tab_active: 0,
|
||||
|
||||
upload_dialog: { visible: false, form_data: undefined, tasks: [] },
|
||||
|
||||
update_password_dialog: { visible: false },
|
||||
|
||||
resource_preview_dialog: { visible: false, resource: null },
|
||||
|
||||
class_notes_dialog: { visible: false },
|
||||
online_FAQ_Dialog: { visible: false },
|
||||
isFullscreen: false
|
||||
}
|
||||
|
||||
const actions = {
|
||||
setUser (user) {
|
||||
if (!user) return
|
||||
lst.save('user', this.user = user)
|
||||
},
|
||||
setFullscreen (status) {
|
||||
this.$set(this, 'isFullscreen', status)
|
||||
this.isFullscreen = status
|
||||
},
|
||||
setToken (token) {
|
||||
if (!token) return
|
||||
lst.save('token', this.token = 'Bearer ' + token)
|
||||
},
|
||||
|
||||
async logout (isShowTip) {
|
||||
if (isShowTip) await this.$confirm('将要退出登录,是否继续?', '退出提示', { type: 'warning' })
|
||||
lst.clear()
|
||||
window.location.reload()
|
||||
},
|
||||
|
||||
showUpdatePasswordDialog () {
|
||||
this.update_password_dialog.visible = true
|
||||
},
|
||||
|
||||
showUploadDialog (formData) {
|
||||
this.upload_dialog.visible = true
|
||||
this.upload_dialog.form_data = formData
|
||||
},
|
||||
|
||||
showClassNotesDialog () {
|
||||
this.class_notes_dialog.visible = true
|
||||
},
|
||||
|
||||
showOnlineFaqDialog () {
|
||||
this.online_FAQ_Dialog.visible = true
|
||||
},
|
||||
|
||||
async showResourcePreviewDialog (resource) {
|
||||
if (!resource) return
|
||||
|
||||
let res = resource.id ? resource : null
|
||||
if (!res) {
|
||||
const { data } = await findResourceApi(resource)
|
||||
res = data
|
||||
}
|
||||
this.resource_preview_dialog.resource = res
|
||||
this.resource_preview_dialog.visible = true
|
||||
},
|
||||
|
||||
async upload () {
|
||||
const fs = await getDistFile()
|
||||
const tasks = []
|
||||
|
||||
for (const file of fs) {
|
||||
const name = file.name
|
||||
if (file.size > 1024 * 1024 * 500) {
|
||||
return window.ELEMENT.Message.error(`文件【${file.name}】体积过大,上传失败`)
|
||||
}
|
||||
const type = getAcceptType(file.type)
|
||||
if (!type) {
|
||||
return window.ELEMENT.Message.error(`文件【${file.name}】的格式暂不支持`)
|
||||
}
|
||||
tasks.push({ name, file, size: file.size, status: 0, progress: 0, type })
|
||||
}
|
||||
|
||||
Promise.allSettled(tasks.map(async (task, index) => {
|
||||
task.onUploadProgress = ({ loaded, total }) => {
|
||||
task.progress = loaded / total * 100 | 0
|
||||
}
|
||||
|
||||
try {
|
||||
task.id = new Date().getTime() + Math.random().toString().substring(3, 8)
|
||||
this.upload_dialog.tasks.push(task)
|
||||
task.status = 1
|
||||
task.abortCtrl = new AbortController()
|
||||
await uploadApi(task.file, { data: this.upload_dialog.form_data, signal: task.abortCtrl.signal, onUploadProgress: task.onUploadProgress })
|
||||
task.status = 2
|
||||
this.$emit('onUploadSuccess', { task, index, tasks })
|
||||
} catch (err) {
|
||||
task.status = 3
|
||||
task.error = err
|
||||
this.$emit('onUploadError', task)
|
||||
throw err
|
||||
}
|
||||
})).then(() => {
|
||||
this.$emit('onUploadEnd', tasks)
|
||||
})
|
||||
},
|
||||
|
||||
async inputCheckAdminPassword (title = '重要提示') {
|
||||
const { value } = await this.$prompt('请输入超级管理员密码', title, {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
inputType: 'password',
|
||||
inputPattern: /^(?![0-9]+$)(?![a-zA-Z]+$)[a-zA-Z0-9]{6,12}$/,
|
||||
inputPlaceholder: '请输入',
|
||||
inputErrorMessage: '密码输入不合法,若是初始密码请修改后再试'
|
||||
})
|
||||
|
||||
return checkAdminPasswordApi(value)
|
||||
}
|
||||
}
|
||||
|
||||
export default new window.Vue({
|
||||
data: store,
|
||||
methods: actions,
|
||||
created () {
|
||||
this.setUser(lst.get('user'))
|
||||
},
|
||||
|
||||
computed: {
|
||||
ws () {
|
||||
if (!this.__WS__) {
|
||||
this.__WS__ = window.io(
|
||||
import.meta.env.VITE_APP_WS_URL,
|
||||
{ transports: ['websocket'], auth: { token: lst.get('token') } }
|
||||
)
|
||||
}
|
||||
return this.__WS__
|
||||
}
|
||||
}
|
||||
})
|
||||
103
front/src/styles/element-ui.scss
Normal file
@@ -0,0 +1,103 @@
|
||||
.el-message-box {
|
||||
border: none;
|
||||
|
||||
&__header {
|
||||
background-color: #87D2D9;
|
||||
padding: 12px 15px;
|
||||
}
|
||||
|
||||
&__title span {
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&__headerbtn &__close {
|
||||
color: #333;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
&__content {
|
||||
padding: 20px 16px 16px;
|
||||
}
|
||||
|
||||
&__btns {
|
||||
text-align: center;
|
||||
|
||||
.el-button {
|
||||
border-radius: 50px;
|
||||
|
||||
+ .el-button {
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-tabs {
|
||||
&__item {
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.el-table {
|
||||
.el-checkbox__input {
|
||||
.el-checkbox__inner {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 1px solid $--color-primary;
|
||||
background: rgba($--color-primary, 0.1);
|
||||
border-radius: 20%;
|
||||
|
||||
&::after {
|
||||
background: rgba($--color-primary, 1);
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
border-radius: 30%;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%) scale(0);
|
||||
border: none;
|
||||
transition: transform 0.05s ease-in 0.05s;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-checked {
|
||||
.el-checkbox__inner {
|
||||
&::after {
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.middle {
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.el-table__header .el-table-column--selection {
|
||||
.el-checkbox__input {
|
||||
.el-checkbox__inner {
|
||||
border: 1px solid #fff;
|
||||
|
||||
&::after {
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-form {
|
||||
.el-form-item__label {
|
||||
color: $--color-text-primary;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
|
||||
&::after {
|
||||
content: ':';
|
||||
}
|
||||
}
|
||||
}
|
||||
238
front/src/styles/index.scss
Normal file
@@ -0,0 +1,238 @@
|
||||
/* stylelint-disable font-family-no-missing-generic-family-keyword */
|
||||
[class^="i-"],
|
||||
[class*=" i-"] {
|
||||
font-family: iconfont !important;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
background-color: #f3f8ff;
|
||||
font-family: arial, Helvetica, sans-serif;
|
||||
overflow: hidden;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
color: $--color-text-primary;
|
||||
}
|
||||
|
||||
.copyright::after {
|
||||
content: "Copyright@2023北京灏博云天科技有限公司";
|
||||
display: block;
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
bottom: 6px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
letter-spacing: 2px;
|
||||
font-size: 15px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
textarea {
|
||||
font-family: inherit !important;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #eee;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.flex-h {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-v {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.flex-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex-middle-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flex-1 {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.p-12 {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.mb-12 {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.mr-12 {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.ml-12 {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.mt-12 {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.w-100 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.color-primary {
|
||||
color: $--color-primary;
|
||||
}
|
||||
|
||||
.color-white {
|
||||
color: #fff;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: rgba($color: #fff, $alpha: 80%);
|
||||
}
|
||||
}
|
||||
|
||||
.color-warning {
|
||||
color: $--color-warning;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: rgba($color: $--color-warning, $alpha: 80%);
|
||||
}
|
||||
}
|
||||
|
||||
.el-cascader-node {
|
||||
height: fit-content !important;
|
||||
}
|
||||
|
||||
.color-danger {
|
||||
color: $--color-danger;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: rgba($color: $--color-danger, $alpha: 80%);
|
||||
}
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
/* stylelint-disable-next-line value-no-vendor-prefix */
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.ellipsis-1 {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.ellipsis-2 {
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
.ellipsis-3 {
|
||||
-webkit-line-clamp: 3;
|
||||
}
|
||||
|
||||
.t-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.t-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.t-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.v-empty {
|
||||
margin: 8vh auto 0;
|
||||
color: #ccc;
|
||||
user-select: none;
|
||||
|
||||
p {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.v-card {
|
||||
border-radius: 4px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 8px 0 rgb(16 166 180 / 20%);
|
||||
}
|
||||
|
||||
.v-title {
|
||||
padding-left: 6px;
|
||||
font-size: 15px;
|
||||
border-left: 6px solid $--color-primary;
|
||||
}
|
||||
|
||||
.v-page {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.v-menu {
|
||||
margin-right: 18px;
|
||||
padding-right: 18px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 226px;
|
||||
height: inherit;
|
||||
border-right: 1px solid #e9e8ee;
|
||||
}
|
||||
|
||||
.v-ctx {
|
||||
flex: 1;
|
||||
height: inherit;
|
||||
overflow: hidden overlay;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-input {
|
||||
input {
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
.el-input__suffix {
|
||||
background-color: white;
|
||||
height: calc(100% - 2px);
|
||||
right: 3px;
|
||||
top: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.self-button {
|
||||
height: 20px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
12
front/src/styles/variables.scss
Normal file
@@ -0,0 +1,12 @@
|
||||
$--color-primary: #208ac6;
|
||||
$--color-success: #02c761;
|
||||
$--color-danger: #f04343;
|
||||
$--color-warning: #ffae00;
|
||||
$--color-text-primary: #333;
|
||||
$--color-text-regular: #666;
|
||||
$--color-text-secondary: #999;
|
||||
$--background-color-base: #f1fafa;
|
||||
|
||||
$--border-color-lighter: #d8d8d8;
|
||||
|
||||
$--font-path: "element-ui/lib/theme-chalk/fonts";
|
||||
19
front/src/utils/capture.js
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* 异常捕获
|
||||
*/
|
||||
const ErrorCapture = {
|
||||
|
||||
/**
|
||||
* 处理IMG异常
|
||||
* @param {Event} event
|
||||
*/
|
||||
IMG ({ target }) {
|
||||
target.src = target.className.includes('avatar') ? '/logo.svg' : '/default.svg'
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 捕获全局error事件
|
||||
*/
|
||||
document.addEventListener('error', (e) => ErrorCapture[e.target.tagName]?.(e), true)
|
||||
57
front/src/utils/data-report.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import { reportDataApi } from '@/api/statistic/index'
|
||||
|
||||
export const REPORT_TYPES = {
|
||||
ONLINE_COURSE: '在线课程',
|
||||
LIVE_TEACHING: '教学直播',
|
||||
MOCK_EXAMINATION: '模拟考试',
|
||||
ERROR_CONSOLIDATION: '错题巩固',
|
||||
CLASS_NOTE: '课堂笔记',
|
||||
ONLINE_FAQ: '在线答疑',
|
||||
RESOURCE_VIEW: '资源查看',
|
||||
SYSTEM_USE: '系统使用'
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据上报 mixin
|
||||
*
|
||||
* @param {[keyof REPORT_TYPES]} type 上报类型
|
||||
*
|
||||
* @param {Object} options 方法
|
||||
* @param {boolean} options.isListenerUnload 是否弹出关闭页面提示框
|
||||
* @param {()=>boolean} options.isCancelReport 是否取消上报
|
||||
* @param {()=>{ id?:number, field01?:string, field02?:string, field03?:string, remarks?:string }} options.handleReportData 处理上报数据
|
||||
* field01 备用字段
|
||||
* field02 备用字段
|
||||
* field03 备用字段
|
||||
* remarks 备注说明
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export function dataReportMixin (type, { isListenerUnload, ...methods } = {}) {
|
||||
if (!type) throw Error('上报类型错误')
|
||||
|
||||
return {
|
||||
mounted () {
|
||||
if (this.isCancelReport?.()) return console.warn('上报已中断,上报数据:', type)
|
||||
|
||||
this.reportData()
|
||||
if (isListenerUnload) window.addEventListener('beforeunload', this.reportData)
|
||||
},
|
||||
|
||||
beforeDestroy () {
|
||||
if (isListenerUnload) window.removeEventListener('beforeunload', this.reportData)
|
||||
this.reportData()
|
||||
},
|
||||
|
||||
methods: {
|
||||
...methods,
|
||||
|
||||
async reportData (e) {
|
||||
e && (e.returnValue = '将要退出,是否继续?')
|
||||
|
||||
reportDataApi({ ...this.handleReportData?.(), id: this.__report_data_id__, type })
|
||||
.then(({ data }) => (this.__report_data_id__ = data))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
63
front/src/utils/fetch.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import { lst } from './index'
|
||||
|
||||
const _instance = window.axios.create({
|
||||
baseURL: import.meta.env.VITE_APP_BASE_URL,
|
||||
timeout: 100000
|
||||
})
|
||||
|
||||
_instance.interceptors.request.use(
|
||||
(config) => {
|
||||
const _token = lst.get('token')
|
||||
_token && (config.headers.Authorization = _token)
|
||||
|
||||
if (config.formData) {
|
||||
config.headers['Content-Type'] = 'multipart/form-data;'
|
||||
config.data = new FormData()
|
||||
for (const k in config.formData) config.data.append(k, config.formData[k])
|
||||
}
|
||||
|
||||
return config
|
||||
},
|
||||
(e) => {
|
||||
throw e
|
||||
}
|
||||
)
|
||||
|
||||
_instance.interceptors.response.use(
|
||||
({ data, status, config }) => {
|
||||
if ([201, 200].includes(status) && (data.code === 0 || config.noCatch)) return data
|
||||
throw new Error(data.msg)
|
||||
},
|
||||
(e) => {
|
||||
throw e
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* @param {Object} option 选项
|
||||
* @param {string} option.url 请求地址
|
||||
* @param {string} option.headers 请求头
|
||||
* @param {{}} option.params URL 参数
|
||||
* @param {{}} option.data body 参数
|
||||
* @param {{}} option.formData FormData 对象参数
|
||||
* @param {'get'|'post'|'patch'|'delete'} option.method
|
||||
*/
|
||||
export default async (option) => {
|
||||
try {
|
||||
return await _instance(option)
|
||||
} catch (err) {
|
||||
let _msg
|
||||
if (err.response) {
|
||||
if (err.response.data.code === 401) {
|
||||
lst.clear()
|
||||
location.href = '/'
|
||||
}
|
||||
_msg = err.response.data.msg
|
||||
}
|
||||
|
||||
if (!option.noNotify) {
|
||||
window.ELEMENT.Message.error(_msg || err.message || '系统繁忙,请稍后再试!')
|
||||
}
|
||||
throw new Error(_msg || err)
|
||||
}
|
||||
}
|
||||
18
front/src/utils/iframe-message.js
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
const vue = new Vue()
|
||||
window.addEventListener('message', async (e) => {
|
||||
if (e.data.type === 'confim') {
|
||||
// console.log($confirm)
|
||||
await confirm(e)
|
||||
}
|
||||
})
|
||||
async function confirm (e) {
|
||||
vue.$confirm(e.data.title, '操作提示', { type: 'warning' })
|
||||
.then(() => {
|
||||
console.log(e)
|
||||
e.source.postMessage({ status: true, id: e.data.id }, '*')
|
||||
})
|
||||
.catch(() => {
|
||||
e.source.postMessage({ status: false, id: e.data.id }, '*')
|
||||
})
|
||||
}
|
||||
112
front/src/utils/index.js
Normal file
@@ -0,0 +1,112 @@
|
||||
import storage from './storage'
|
||||
|
||||
export function mapToTree (map) {
|
||||
const list = []
|
||||
for (const k in map) {
|
||||
if (map[k].pid === 0) {
|
||||
list.push(map[k])
|
||||
} else {
|
||||
map[map[k].pid].children ??= []
|
||||
map[map[k].pid].children.push(map[k])
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件时 accept 的枚举类
|
||||
*/
|
||||
export const ACCEPT_MAP = {
|
||||
image: { icon: 'i-tupiao', color: '#ff6a4d', name: '图片文件', accept: 'image/*' },
|
||||
audio: { icon: 'i-yinyue', color: '#9660f5', name: '音频文件', accept: 'audio/*' },
|
||||
video: { icon: 'i-shipin', color: '#55d1e0', name: '视频文件', accept: 'video/*' },
|
||||
ppt: { icon: 'i-ppt', color: '#ff9357', name: '演示文稿', accept: 'application/vnd.ms-powerpoint' },
|
||||
pptx: { icon: 'i-pptx', color: '#ff9357', name: '演示文稿', accept: 'application/vnd.openxmlformats-officedocument.presentationml.presentation' },
|
||||
xls: { icon: 'i-xls', color: '#28c17a', name: '表格文件', accept: 'application/vnd.ms-excel' },
|
||||
xlsx: { icon: 'i-xlsx', color: '#28c17a', name: '表格文件', accept: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' },
|
||||
pdf: { icon: 'i-pdf', color: '#ff738e', name: '文本文件', accept: 'application/pdf' },
|
||||
doc: { icon: 'i-doc', color: '#39afff', name: '文本文件', accept: 'application/msword' },
|
||||
docx: { icon: 'i-docx', color: '#39afff', name: '文本文件', accept: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' },
|
||||
folder: { icon: 'i-wenjianjia', color: '#ffd94a', name: '文件夹', accept: '' }
|
||||
}
|
||||
|
||||
export function getAcceptType (accept) {
|
||||
if (!accept) return
|
||||
|
||||
for (const type in ACCEPT_MAP) {
|
||||
if (accept.startsWith(ACCEPT_MAP[type].accept.replace(/\*$/, ''))) return type
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 颜色生成器
|
||||
*
|
||||
* @param {string} value
|
||||
* @returns
|
||||
*/
|
||||
export function colorGenerator (value) {
|
||||
if (!value) return
|
||||
return Number(value.split('').reduce((n, s) => n + s.charCodeAt(), '')).toString(16).replace(/.*(.{6})$/, '#$1')
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件工具类
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {[keyof ACCEPT_MAP]} options.accepts 指定可选的文件类型
|
||||
* @param {boolean} options.multiple 是否允许用户选择多个文件
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function getDistFile ({ accepts = [], multiple = false } = {}) {
|
||||
if (!getDistFile.el) { // 如果没有 Input Dom对象,则创建
|
||||
getDistFile.el = document.createElement('input')
|
||||
getDistFile.el.type = 'file'
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
getDistFile.el.setAttribute(
|
||||
'accept',
|
||||
(accepts?.length ? accepts : Object.keys(ACCEPT_MAP))
|
||||
.map(k => ACCEPT_MAP[k].accept).join(' , ')
|
||||
)
|
||||
getDistFile.el.setAttribute('multiple', multiple)
|
||||
|
||||
getDistFile.el.onchange = () => {
|
||||
resolve([...getDistFile.el.files])
|
||||
getDistFile.el.value = null
|
||||
}
|
||||
getDistFile.el.click()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载
|
||||
*
|
||||
* @param {string} src 文件地址
|
||||
* @param {string} name 文件下载名
|
||||
* @returns
|
||||
*/
|
||||
export async function download (src, name) {
|
||||
if (!src) return
|
||||
const href = await fetch(src).then(res => {
|
||||
if (res.status === 200) return res.arrayBuffer()
|
||||
throw Error('文件下载出错')
|
||||
})
|
||||
|
||||
if (!download.el) {
|
||||
download.el = document.createElement('a')
|
||||
}
|
||||
|
||||
download.el.download = name || ''
|
||||
download.el.href = URL.createObjectURL(new Blob([href]))
|
||||
download.el.click()
|
||||
}
|
||||
|
||||
/**
|
||||
* 本地存储 localStorage
|
||||
*/
|
||||
export const lst = storage(localStorage)
|
||||
|
||||
/**
|
||||
* 本地存储 sessionStorage
|
||||
*/
|
||||
export const sst = storage(sessionStorage)
|
||||
435
front/src/utils/prototype.js
vendored
Normal file
@@ -0,0 +1,435 @@
|
||||
import jsPDF from 'jspdf'
|
||||
import html2canvas from 'html2canvas'
|
||||
import { font } from './simhei-normal'
|
||||
/* eslint-disable no-extend-native */
|
||||
String.prototype.fetchValue = function (object) {
|
||||
return this.split('.').reduce((o, k) => (o === undefined || o === null ? undefined : o[k]), object || {})
|
||||
}
|
||||
|
||||
String.prototype.resolveObject = function (value) {
|
||||
const [first, ...os] = this.split('.').reverse()
|
||||
return os.reduce((o, k) => ({ [k]: o }), { [first]: value })
|
||||
}
|
||||
|
||||
String.prototype.fileLinkTransfer = function () {
|
||||
if (this) return import.meta.env.VITE_APP_BASE_URL + '/file-bucket/' + this
|
||||
}
|
||||
|
||||
File.prototype.fileLinkTransfer = function () {
|
||||
if (this) return URL.createObjectURL(this)
|
||||
}
|
||||
|
||||
Number.prototype.formatFileSize = function (fractionDigits = 2, type = 'string') {
|
||||
if (isNaN(this)) return this
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||
let size = this
|
||||
let index = 0
|
||||
while (size > 1024 && index < units.length - 1) {
|
||||
size = size / 1024
|
||||
index++
|
||||
}
|
||||
if (type === 'object') return { value: size.toFixed(fractionDigits), unit: units[index] }
|
||||
return size.toFixed(fractionDigits) + units[index]
|
||||
}
|
||||
|
||||
Date.prototype.format = function (fmt) {
|
||||
const o = {
|
||||
'M+': this.getMonth() + 1, // 月份
|
||||
'd+': this.getDate(), // 日
|
||||
'h+': this.getHours(), // 小时
|
||||
'm+': this.getMinutes(), // 分
|
||||
's+': this.getSeconds(), // 秒
|
||||
'q+': Math.floor((this.getMonth() + 3) / 3), // 季度
|
||||
S: this.getMilliseconds() // 毫秒
|
||||
}
|
||||
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length))
|
||||
for (const k in o) {
|
||||
if (new RegExp('(' + k + ')').test(fmt)) { fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length)) }
|
||||
}
|
||||
return fmt
|
||||
}
|
||||
|
||||
Date.prototype.toTypeStr = function (timestamp, fmt) {
|
||||
if (typeof timestamp === 'string') {
|
||||
fmt = timestamp
|
||||
timestamp = undefined
|
||||
}
|
||||
const time = (timestamp || new Date().getTime()) - this.getTime()
|
||||
return time < 60 * 1000
|
||||
? '刚刚'
|
||||
: time < 60 * 60 * 1000
|
||||
? Math.floor(time / (60 * 1000)) + '分钟前'
|
||||
: time < 24 * 60 * 60 * 1000
|
||||
? Math.floor(time / (60 * 60 * 1000)) + '小时前'
|
||||
: time < 48 * 60 * 60 * 1000
|
||||
? '昨天'
|
||||
: this.format(fmt || 'yyyy-MM-dd hh:mm')
|
||||
}
|
||||
|
||||
Array.generate = function (length = 0, fn) {
|
||||
const _list = []
|
||||
if (fn) {
|
||||
let i = 0
|
||||
while (i < length) _list.push(fn(i++))
|
||||
}
|
||||
return _list
|
||||
}
|
||||
|
||||
Array.prototype.toTree = function ({ key = 'id', parentKey = 'pid', childKey = 'children' } = {}) {
|
||||
const list = this // JSON.parse(JSON.stringify(this))
|
||||
const map = {}
|
||||
let index = this.length
|
||||
while (index--) {
|
||||
map[list[index][key]] = list[index]
|
||||
}
|
||||
|
||||
const tree = []
|
||||
const len = list.length
|
||||
while (++index < len) {
|
||||
const item = list[index]
|
||||
const pid = item[parentKey]
|
||||
if (map[pid]) {
|
||||
map[pid][childKey] ??= []
|
||||
map[pid][childKey].push(item)
|
||||
} else {
|
||||
tree.push(item)
|
||||
}
|
||||
}
|
||||
return { map, tree }
|
||||
}
|
||||
|
||||
// window.Vue.prototype.$html2Pdf = async function (id, isPrint) {
|
||||
// const ele = document.getElementById(id)
|
||||
// console.log(ele)
|
||||
// html2canvas(ele, { useCORS: true }).then(canvas => {
|
||||
// // 未生成pdf的html页面高度
|
||||
// // 未生成pdf的html页面高度
|
||||
// let leftHeight = canvas.height
|
||||
|
||||
// const a4Width = 595.28
|
||||
// const a4Height = 841.89 // A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277
|
||||
// // 一页pdf显示html页面生成的canvas高度;
|
||||
// const a4HeightRef = Math.floor((canvas.width / a4Width) * a4Height)
|
||||
|
||||
// // pdf页面偏移
|
||||
// let position = 0
|
||||
|
||||
// const pageData = canvas.toDataURL('image/jpeg', 1.0)
|
||||
|
||||
// const pdf = new jsPDF('p', 'pt', 'a4') // A4纸,纵向
|
||||
// let index = 1
|
||||
// const canvas1 = document.createElement('canvas')
|
||||
// let height
|
||||
// pdf.setDisplayMode('fullwidth', 'continuous', 'FullScreen')
|
||||
|
||||
// const pdfName = 'title'
|
||||
// function createImpl (canvas) {
|
||||
// console.log(leftHeight, a4HeightRef)
|
||||
// if (leftHeight > 0) {
|
||||
// index++
|
||||
|
||||
// let checkCount = 0
|
||||
// if (leftHeight > a4HeightRef) {
|
||||
// let i = position + a4HeightRef
|
||||
// for (i = position + a4HeightRef; i >= position; i--) {
|
||||
// let isWrite = true
|
||||
// for (let j = 0; j < canvas.width; j++) {
|
||||
// const c = canvas.getContext('2d').getImageData(j, i, 1, 1).data
|
||||
|
||||
// if (c[0] !== 0xff || c[1] !== 0xff || c[2] !== 0xff) {
|
||||
// isWrite = false
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// if (isWrite) {
|
||||
// checkCount++
|
||||
// if (checkCount >= 10) {
|
||||
// break
|
||||
// }
|
||||
// } else {
|
||||
// checkCount = 0
|
||||
// }
|
||||
// }
|
||||
// height = Math.round(i - position) || Math.min(leftHeight, a4HeightRef)
|
||||
// if (height <= 0) {
|
||||
// height = a4HeightRef
|
||||
// }
|
||||
// } else {
|
||||
// height = leftHeight
|
||||
// }
|
||||
|
||||
// canvas1.width = canvas.width
|
||||
// canvas1.height = height
|
||||
|
||||
// console.log(index, 'height:', height, 'pos', position)
|
||||
|
||||
// const ctx = canvas1.getContext('2d')
|
||||
// ctx.drawImage(
|
||||
// canvas,
|
||||
// 0,
|
||||
// position,
|
||||
// canvas.width,
|
||||
// height,
|
||||
// 0,
|
||||
// 0,
|
||||
// canvas.width,
|
||||
// height
|
||||
// )
|
||||
|
||||
// const pageHeight = Math.round((a4Width / canvas.width) * height)
|
||||
// // pdf.setPageSize(null, pageHeight)
|
||||
// if (position !== 0) {
|
||||
// pdf.addPage()
|
||||
// }
|
||||
// pdf.addImage(
|
||||
// canvas1.toDataURL('image/jpeg', 1.0),
|
||||
// 'JPEG',
|
||||
// 20,
|
||||
// 20,
|
||||
// a4Width,
|
||||
// (a4Width / canvas1.width) * height
|
||||
// )
|
||||
// leftHeight -= height
|
||||
// position += height
|
||||
// if (leftHeight > 0) {
|
||||
// setTimeout(createImpl, 500, canvas)
|
||||
// } else {
|
||||
// console.log(pdf)
|
||||
// pdf.autoPrint()
|
||||
// window.open(pdf.output('bloburl'), '_blank')
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // 当内容未超过pdf一页显示的范围,无需分页
|
||||
// if (leftHeight < a4HeightRef) {
|
||||
// pdf.addImage(
|
||||
// pageData,
|
||||
// 'JPEG',
|
||||
// 20,
|
||||
// 20,
|
||||
// a4Width,
|
||||
// (a4Width / canvas.width) * leftHeight
|
||||
// )
|
||||
// console.log(pdf)
|
||||
// pdf.autoPrint()
|
||||
// window.open(pdf.output('bloburl'), '_blank')
|
||||
// } else {
|
||||
// try {
|
||||
// pdf.deletePage(0)
|
||||
// setTimeout(createImpl, 500, canvas)
|
||||
// } catch (err) {
|
||||
// // console.log(err);
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
// window.Vue.prototype.$html2Pdf = async function (id, isPrint) {
|
||||
// const ele = document.getElementById(id)
|
||||
// const pageWidth = 595.28 - 40 // A4纸的宽高 减去左右边距
|
||||
// const pageHeight = 841.89 - 40
|
||||
// // a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
|
||||
|
||||
// html2canvas(ele, {
|
||||
// // allowTaint: true,
|
||||
// // taintTest: false,
|
||||
// useCORS: true,
|
||||
// backgroundColor: '#FFF',
|
||||
// width: ele.offsetWidth - 15, // 因为多出的需要剪裁掉,
|
||||
// height: ele.offsetHeight
|
||||
// // async: true,
|
||||
// // scale: '1', // 放大倍数
|
||||
// // dpi: '192', // 精度
|
||||
// // scrollY: ele.top, // 关键代码
|
||||
// // height: ele.height + 50 // 加高度,避免截取不全
|
||||
// }).then(canvas => {
|
||||
// const contentWidth = canvas.width
|
||||
// const contentHeight = canvas.height
|
||||
// const pageData = canvas.toDataURL('image/jpeg/png', 1)
|
||||
// const PDF = new jsPDF('p', 'mm', 'a4') // , true
|
||||
// let pageCount = 1
|
||||
// // canvas图片的高
|
||||
// const imgHeight = pageWidth / contentWidth * contentHeight // canvas的宽与PDF的宽比列一样的时候,canvas的高缩放后的值
|
||||
|
||||
// let leftHeight = imgHeight
|
||||
// let position = 0
|
||||
// // 如果图片的高小于A4纸的高,不分页
|
||||
// if (leftHeight < pageHeight) {
|
||||
// PDF.addImage(pageData, 'JPEG', 20, 0, pageWidth, imgHeight)
|
||||
// } else {
|
||||
// // 分页
|
||||
// // let index = 0
|
||||
// while (leftHeight > 0) {
|
||||
// pageCount++
|
||||
|
||||
// PDF.addImage(pageData, 'JPEG', 20, position, pageWidth, imgHeight)
|
||||
// leftHeight = leftHeight - pageHeight
|
||||
// position -= pageHeight
|
||||
// if (leftHeight > 0) {
|
||||
// PDF.addPage()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// PDF.addFileToVFS('blobs', font)
|
||||
|
||||
// PDF.addFont('blobs', 'font_blobs', 'normal')
|
||||
// // 通过字体名使用字体
|
||||
// PDF.setFont('font_blobs')
|
||||
|
||||
// for (let i = 1; i < pageCount; i++) {
|
||||
// console.log(i)
|
||||
// PDF.setPage(i)
|
||||
// PDF.setFontSize(8)
|
||||
// PDF.text('当前第 ' + String(i) + '页 共 ' + String(pageCount) + '页', PDF.internal.pageSize.width / 2, pageHeight + 35, {
|
||||
// align: 'center'
|
||||
// })
|
||||
// }
|
||||
// PDF.autoPrint()
|
||||
// window.open(PDF.output('bloburl'), '_blank')
|
||||
// })
|
||||
// }
|
||||
const isSplit = (nodes, index, pageHeight) => {
|
||||
// 计算当前这块dom是否跨越了a4大小,以此分割
|
||||
if (nodes[index].offsetTop + nodes[index].offsetHeight < pageHeight && nodes[index + 1] && nodes[index + 1].offsetTop + nodes[index + 1].offsetHeight > pageHeight) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
const formatPdf = (ele) => {
|
||||
const ST = document.documentElement.scrollTop || document.body.scrollTop
|
||||
const SL = document.documentElement.scrollLeft || document.body.scrollLeft
|
||||
document.documentElement.scrollTop = 0
|
||||
document.documentElement.scrollLeft = 0
|
||||
document.body.scrollTop = 0
|
||||
document.body.scrollLeft = 0
|
||||
// const ST = document.documentElement.scrollTop || document.body.scrollTop
|
||||
// const SL = document.documentElement.scrollLeft || document.body.scrollLeft
|
||||
document.documentElement.scrollTop = 0
|
||||
document.documentElement.scrollLeft = 0
|
||||
document.body.scrollTop = 0
|
||||
document.body.scrollLeft = 0
|
||||
// 获取滚动条的位置并赋值为0,因为是el-dialog弹框,并且内容较多出现了纵向的滚动条,截图出来的效果只能截取到视图窗口显示的部分,超出窗口部分则无法生成。所以先将滚动条置顶
|
||||
const A4_WIDTH = 592.28
|
||||
const A4_HEIGHT = 841.89
|
||||
|
||||
const imageWrapper = ele // 获取DOM
|
||||
const pageHeight = imageWrapper.scrollWidth / A4_WIDTH * A4_HEIGHT + 80
|
||||
// const lableListID = imageWrapper.querySelectorAll('.main-form-area')
|
||||
const lableListID = imageWrapper.querySelectorAll('.answer-item')
|
||||
// 进行分割操作,当dom内容已超出a4的高度,则将该dom前插入一个空dom,把他挤下去,分割
|
||||
for (let i = 0; i < lableListID.length; i++) {
|
||||
// console.log(lableListID[i])
|
||||
const multiple = Math.ceil((lableListID[i].offsetTop + lableListID[i].offsetHeight) / pageHeight)
|
||||
if (isSplit(lableListID, i, multiple * pageHeight)) {
|
||||
// console.log(lableListID[i])
|
||||
const divParent = lableListID[i].parentNode // 获取该div的父节点
|
||||
const newNode = document.createElement('div')
|
||||
newNode.className = 'emptyDiv'
|
||||
newNode.style.background = '#ffffff'
|
||||
const _H = multiple * pageHeight - (lableListID[i].offsetTop + lableListID[i].offsetHeight)
|
||||
// 留白
|
||||
newNode.style.height = _H + 'px'
|
||||
newNode.style.width = '100%'
|
||||
const next = lableListID[i].nextSibling // 获取div的下一个兄弟节点
|
||||
// 判断兄弟节点是否存在
|
||||
if (next) {
|
||||
// 存在则将新节点插入到div的下一个兄弟节点之前,即div之后
|
||||
divParent.insertBefore(newNode, next)
|
||||
} else {
|
||||
// 不存在则直接添加到最后,appendChild默认添加到divParent的最后
|
||||
divParent.appendChild(newNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
window.Vue.prototype.$html2Pdf = async function (id, exportInfo) {
|
||||
const ele = document.getElementById(id)
|
||||
formatPdf(ele)
|
||||
// a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
|
||||
|
||||
html2canvas(ele, {
|
||||
// allowTaint: true,
|
||||
// // taintTest: false,
|
||||
// x: ele.getBoundingClientRect().left + 13, // 绘制的dom元素相对于视口的位置
|
||||
// y: ele.getBoundingClientRect().top,
|
||||
useCORS: true,
|
||||
backgroundColor: '#FFF',
|
||||
width: ele.offsetWidth - 15, // 因为多出的需要剪裁掉,
|
||||
height: ele.offsetHeight,
|
||||
// async: true,
|
||||
scale: 3, // 放大倍数
|
||||
dpi: 350 // 精度
|
||||
// scrollY: ele.top, // 关键代码
|
||||
// height: ele.height + 50 // 加高度,避免截取不全
|
||||
}).then(canvas => {
|
||||
const pdf = new jsPDF('p', 'mm', 'a4') // A4纸,纵向
|
||||
const ctx = canvas.getContext('2d')
|
||||
const a4w = 190; const a4h = 277 // A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277
|
||||
const imgHeight = Math.floor(a4h * canvas.width / a4w) // 按A4显示比例换算一页图像的像素高度
|
||||
let renderedHeight = 0
|
||||
|
||||
while (renderedHeight < canvas.height) {
|
||||
const page = document.createElement('canvas')
|
||||
page.width = canvas.width
|
||||
page.height = Math.min(imgHeight, canvas.height - renderedHeight)// 可能内容不足一页
|
||||
// 用getImageData剪裁指定区域,并画到前面创建的canvas对象中
|
||||
if (renderedHeight === 0) {
|
||||
page.getContext('2d').putImageData(ctx.getImageData(0, renderedHeight, canvas.width, (Math.min(imgHeight, canvas.height - renderedHeight) + 20)), 0, 0)
|
||||
pdf.addImage(page.toDataURL('image/jpeg', 0.2), 'JPEG', 10, 0, a4w, Math.min(a4h, a4w * page.height / page.width)) // 添加图像到页面,保留10mm边距
|
||||
} else {
|
||||
page.getContext('2d').putImageData(ctx.getImageData(0, renderedHeight, canvas.width, Math.min(imgHeight, canvas.height - renderedHeight)), 0, 0)
|
||||
pdf.addImage(page.toDataURL('image/jpeg', 0.2), 'JPEG', 10, 10, a4w, Math.min(a4h, a4w * page.height / page.width)) // 添加图像到页面,保留10mm边距
|
||||
}
|
||||
renderedHeight += imgHeight
|
||||
if (renderedHeight < canvas.height) { pdf.addPage() }// 如果后面还有内容,添加一个空页
|
||||
}
|
||||
const pageCount = pdf.internal.getNumberOfPages()
|
||||
pdf.addFileToVFS('blobs', font)
|
||||
|
||||
pdf.addFont('blobs', 'font_blobs', 'normal')
|
||||
// 通过字体名使用字体
|
||||
pdf.setFont('font_blobs')
|
||||
pdf.setFontSize(8)
|
||||
for (let i = 1; i <= pageCount; i++) {
|
||||
pdf.setPage(i)
|
||||
pdf.text('第 ' + String(i) + ' 页 共 ' + String(pageCount) + ' 页', pdf.internal.pageSize.width / 2, 287 + 5, {
|
||||
align: 'center'
|
||||
})
|
||||
}
|
||||
console.log(exportInfo, 11111111111111)
|
||||
if (exportInfo) {
|
||||
pdf.save(exportInfo.title + '.pdf')
|
||||
} else {
|
||||
pdf.autoPrint()
|
||||
window.open(pdf.output('bloburl'), '_blank')
|
||||
const emptyDivs = ele.querySelectorAll('.emptyDiv')
|
||||
emptyDivs.forEach(item => {
|
||||
item.parentNode.removeChild(item)
|
||||
console.log(item)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
window.Vue.prototype.$elementFullscreen = async function (ele, callback, todo) {
|
||||
console.log(typeof ele)
|
||||
if (!(ele instanceof HTMLElement)) {
|
||||
ele = ele.$el
|
||||
}
|
||||
const div = document.createElement('div')
|
||||
await ele.requestFullscreen()
|
||||
window.Vue.prototype.$store.isFullscreen = true
|
||||
console.log(todo)
|
||||
todo && todo()
|
||||
|
||||
setTimeout(() => {
|
||||
const fun = () => {
|
||||
ele.removeEventListener('fullscreenchange', fun)
|
||||
window.Vue.prototype.$store.isFullscreen = false
|
||||
callback()
|
||||
}
|
||||
ele.addEventListener('fullscreenchange', fun)
|
||||
}, 100)
|
||||
console.log(ele)
|
||||
}
|
||||
51
front/src/utils/qiankun-init.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import { registerMicroApps, start } from 'qiankun'
|
||||
import store from '../store'
|
||||
// 子应用配置
|
||||
export const apps = [
|
||||
{
|
||||
name: '子应用1',
|
||||
entry: '//localhost:9099',
|
||||
id: 'app1',
|
||||
container: '#app1',
|
||||
activeRule: '/app1',
|
||||
// 测试传参,并将store传递至子应用进行消息通信!
|
||||
props: { test: 1111, store }
|
||||
},
|
||||
{
|
||||
name: '子应用2',
|
||||
entry: '//localhost:8888',
|
||||
id: 'app2',
|
||||
type: 'qiankun',
|
||||
container: '#app2',
|
||||
activeRule: '/app2',
|
||||
// 测试传参,并将store传递至子应用进行消息通信!
|
||||
props: { test: 1111, store }
|
||||
}
|
||||
]
|
||||
// 乾坤初始化方法
|
||||
export default function qianKunInit () {
|
||||
window.__POWERED_BY_QIANKUN_PARENT__ = true
|
||||
registerMicroApps(apps, {
|
||||
beforeLoad: [
|
||||
(app) => {
|
||||
console.log(app, '111111111111111111')
|
||||
}],
|
||||
destoryed: [
|
||||
(app) => {
|
||||
}
|
||||
]
|
||||
})
|
||||
const mainEle = document.getElementById('qiankun-main')
|
||||
apps.forEach(item => {
|
||||
const child = document.createElement('div')
|
||||
child.id = item.id
|
||||
mainEle.appendChild(child)
|
||||
item.container = child
|
||||
console.log(child)
|
||||
})
|
||||
start({
|
||||
prefetch: false, // 可选,是否开启预加载,默认为 true。 不开启
|
||||
sandbox: true, // 可选,是否开启沙箱,默认为 true。// 从而确保微应用的样式不会对全局造成影响。
|
||||
singular: true // 可选,是否为单实例场景,单实例指的是同一时间只会渲染一个微应用。默认为 true。
|
||||
})
|
||||
}
|
||||
7
front/src/utils/simhei-normal.js
Normal file
52
front/src/utils/storage.js
Normal file
@@ -0,0 +1,52 @@
|
||||
export default (storage) => {
|
||||
if (!storage) return
|
||||
|
||||
const skey = (key) => '' + key
|
||||
|
||||
return {
|
||||
/**
|
||||
* 获取
|
||||
* @param {string} key key
|
||||
* @param {any} defaultValue defaultValue
|
||||
*/
|
||||
get (key, defaultValue) {
|
||||
try {
|
||||
return JSON.parse(storage.getItem(skey(key))) || defaultValue
|
||||
} catch (error) {
|
||||
return defaultValue
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 保存
|
||||
* @param {string} key key
|
||||
* @param {any} val 值
|
||||
*/
|
||||
save (key, val) {
|
||||
if (val === undefined || val === null) return this.remove(key)
|
||||
storage.setItem(skey(key), JSON.stringify(val))
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取 所有key
|
||||
*/
|
||||
keys () {
|
||||
return Object.keys(storage)
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param {string} key key
|
||||
*/
|
||||
remove (key) {
|
||||
storage.removeItem(skey(key))
|
||||
},
|
||||
|
||||
/**
|
||||
* 清空
|
||||
*/
|
||||
clear () {
|
||||
storage.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||