feat:初始化 -融骅

This commit is contained in:
2023-10-17 09:15:30 +08:00
parent c9ff84e6a2
commit 405e152b38
1190 changed files with 138344 additions and 455 deletions

3
front/.env.development Normal file
View 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
View File

@@ -0,0 +1,3 @@
VITE_APP_BASE_URL = ''
VITE_APP_WS_URL = ''
VITE_APP_IFRAME_URL = /text-book

3
front/.eslintignore Normal file
View File

@@ -0,0 +1,3 @@
*.d.ts
dist
public

37
front/.eslintrc.cjs Normal file
View 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
View 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
View File

@@ -0,0 +1,2 @@
dist
public

6
front/.stylelintrc.js Normal file
View 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
View 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
View File

@@ -0,0 +1,3 @@
# 怀励【前端】项目
## GIT 工作流

3
front/bin/publish.sh Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

43
front/package.json Normal file
View 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
View 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

View 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";
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

2
front/public/js/axios.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
front/public/js/element-ui.min.js vendored Normal file

File diff suppressed because one or more lines are too long

7
front/public/js/socket.io.min.js vendored Normal file

File diff suppressed because one or more lines are too long

11
front/public/js/vue-router.min.js vendored Normal file

File diff suppressed because one or more lines are too long

6
front/public/js/vue.min.js vendored Normal file

File diff suppressed because one or more lines are too long

6
front/public/js/vuex.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
front/public/logo.svg Normal file
View 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

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.4 KiB

View 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' })

View 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 })

View 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' })

View 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' })

View 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' })

View 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' })

View 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 })

View 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' })

View 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 })
}

View 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 })
}

View 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' })
}

View 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 })
}

View 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 })
}

View 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 })
}

View 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] } })

View 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' })

View 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 } })

View 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' })

View 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' })

View 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 })

View 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 })

View 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'
}
})
}

View 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 })

View 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 })

View 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 })

View 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' })

View 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 })

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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
}

View 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>

View 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>

View 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>

View 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
View 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
View 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
View 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

View 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
View 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__
}
}
})

View 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
View 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;
}

View 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";

View 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)

View 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
View 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)
}
}

View 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
View 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
View 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)
}

View 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。
})
}

File diff suppressed because one or more lines are too long

View 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()
}
}
}

Some files were not shown because too many files have changed in this diff Show More