台账init
parent
9d02295729
commit
087daf26a9
|
@ -1,2 +1,2 @@
|
|||
VITE_BASE=/
|
||||
VITE_BASE_URL=
|
||||
VITE_BASE_URL=http://192.168.0.14:8060/qa_kangzai/
|
||||
|
|
|
@ -8,12 +8,16 @@
|
|||
"name": "qa-kangzai-vue",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@icon-park/vue-next": "^1.4.2",
|
||||
"@vueuse/core": "^13.3.0",
|
||||
"animate.css": "^4.1.1",
|
||||
"autofit.js": "^3.2.8",
|
||||
"axios": "^1.9.0",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"echarts": "^5.6.0",
|
||||
"element-plus": "^2.10.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mitt": "^3.0.1",
|
||||
"normalize.css": "^8.0.1",
|
||||
|
@ -22,7 +26,8 @@
|
|||
"throttle-debounce": "^5.0.2",
|
||||
"v-viewer": "^3.0.21",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.1"
|
||||
"vue-router": "^4.5.1",
|
||||
"vue3-puzzle-vcode": "^1.1.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.19.68",
|
||||
|
@ -40,6 +45,8 @@
|
|||
"eslint-plugin-vue": "^9.32.0",
|
||||
"prettier": "^2.8.8",
|
||||
"sass": "^1.83.0",
|
||||
"unplugin-auto-import": "^19.3.0",
|
||||
"unplugin-vue-components": "^28.7.0",
|
||||
"vite": "^6.3.5",
|
||||
"vite-plugin-env-parse": "^1.0.15",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
|
@ -93,6 +100,24 @@
|
|||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ctrl/tinycolor": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
|
||||
"integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@element-plus/icons-vue": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz",
|
||||
"integrity": "sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"vue": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
|
||||
|
@ -591,6 +616,31 @@
|
|||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@floating-ui/core/-/core-1.7.1.tgz",
|
||||
"integrity": "sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/utils": "^0.2.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@floating-ui/dom/-/dom-1.7.1.tgz",
|
||||
"integrity": "sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.7.1",
|
||||
"@floating-ui/utils": "^0.2.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/utils": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@floating-ui/utils/-/utils-0.2.9.tgz",
|
||||
"integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
|
||||
|
@ -629,6 +679,19 @@
|
|||
"dev": true,
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@icon-park/vue-next": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@icon-park/vue-next/-/vue-next-1.4.2.tgz",
|
||||
"integrity": "sha512-+QklF255wkfBOabY+xw6FAI0Bwln/RhdwCunNy/9sKdKuChtaU67QZqU67KGAvZUTeeBgsL+yaHHxqfQeGZXEQ==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">= 8.0.0",
|
||||
"npm": ">= 5.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "3.x"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
|
||||
|
@ -1016,6 +1079,17 @@
|
|||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@popperjs/core": {
|
||||
"name": "@sxzz/popperjs-es",
|
||||
"version": "2.11.7",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz",
|
||||
"integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/pluginutils": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@rollup/pluginutils/-/pluginutils-4.2.1.tgz",
|
||||
|
@ -1361,6 +1435,21 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.17.17",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@types/lodash/-/lodash-4.17.17.tgz",
|
||||
"integrity": "sha512-RRVJ+J3J+WmyOTqnz3PiBLA501eKwXl2noseKOrNo/6+XEHjTAxO4xHvxQB6QuNm+s4WRbn6rSiap8+EA+ykFQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/lodash-es": {
|
||||
"version": "4.17.12",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@types/lodash-es/-/lodash-es-4.17.12.tgz",
|
||||
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.19.111",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@types/node/-/node-18.19.111.tgz",
|
||||
|
@ -1662,6 +1751,33 @@
|
|||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/anymatch": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/anymatch/-/anymatch-3.1.3.tgz",
|
||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"normalize-path": "^3.0.0",
|
||||
"picomatch": "^2.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/anymatch/node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/argparse/-/argparse-2.0.1.tgz",
|
||||
|
@ -1801,6 +1917,12 @@
|
|||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/async-validator": {
|
||||
"version": "4.2.5",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/async-validator/-/async-validator-4.2.5.tgz",
|
||||
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/asynckit/-/asynckit-0.4.0.tgz",
|
||||
|
@ -1885,6 +2007,19 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/birpc": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/birpc/-/birpc-2.3.0.tgz",
|
||||
|
@ -1918,7 +2053,6 @@
|
|||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
|
@ -2202,6 +2336,12 @@
|
|||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/crypto-js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cssesc": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/cssesc/-/cssesc-3.0.0.tgz",
|
||||
|
@ -2439,6 +2579,126 @@
|
|||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/element-plus": {
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/element-plus/-/element-plus-2.10.1.tgz",
|
||||
"integrity": "sha512-R+YM8b+s+3aQ3EeY33q0inn3ehRnunP42aDYoJxUtSZPgMPSXzYgmGEhIDP7Xg4NvY8raaSuO0/1fDLEfZ+nlA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ctrl/tinycolor": "^3.4.1",
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@floating-ui/dom": "^1.0.1",
|
||||
"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
|
||||
"@types/lodash": "^4.14.182",
|
||||
"@types/lodash-es": "^4.17.6",
|
||||
"@vueuse/core": "^9.1.0",
|
||||
"async-validator": "^4.2.5",
|
||||
"dayjs": "^1.11.13",
|
||||
"escape-html": "^1.0.3",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lodash-unified": "^1.0.2",
|
||||
"memoize-one": "^6.0.0",
|
||||
"normalize-wheel-es": "^1.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/element-plus/node_modules/@types/web-bluetooth": {
|
||||
"version": "0.0.16",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
|
||||
"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/element-plus/node_modules/@vueuse/core": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@vueuse/core/-/core-9.13.0.tgz",
|
||||
"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/web-bluetooth": "^0.0.16",
|
||||
"@vueuse/metadata": "9.13.0",
|
||||
"@vueuse/shared": "9.13.0",
|
||||
"vue-demi": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/element-plus/node_modules/@vueuse/core/node_modules/vue-demi": {
|
||||
"version": "0.14.10",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/vue-demi/-/vue-demi-0.14.10.tgz",
|
||||
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.0.0-rc.1",
|
||||
"vue": "^3.0.0-0 || ^2.6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/element-plus/node_modules/@vueuse/metadata": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@vueuse/metadata/-/metadata-9.13.0.tgz",
|
||||
"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/element-plus/node_modules/@vueuse/shared": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/@vueuse/shared/-/shared-9.13.0.tgz",
|
||||
"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"vue-demi": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/element-plus/node_modules/@vueuse/shared/node_modules/vue-demi": {
|
||||
"version": "0.14.10",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/vue-demi/-/vue-demi-0.14.10.tgz",
|
||||
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/composition-api": "^1.0.0-rc.1",
|
||||
"vue": "^3.0.0-0 || ^2.6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/composition-api": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/entities/-/entities-4.5.0.tgz",
|
||||
|
@ -2653,6 +2913,12 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/escape-string-regexp": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
|
||||
|
@ -3254,7 +3520,6 @@
|
|||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
|
@ -3816,6 +4081,19 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"binary-extensions": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-boolean-object": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
|
||||
|
@ -3987,7 +4265,6 @@
|
|||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
|
@ -4328,7 +4605,6 @@
|
|||
"version": "4.17.21",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
|
@ -4337,6 +4613,17 @@
|
|||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash-unified": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/lodash-unified/-/lodash-unified-1.0.3.tgz",
|
||||
"integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/lodash-es": "*",
|
||||
"lodash": "*",
|
||||
"lodash-es": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
|
@ -4362,6 +4649,12 @@
|
|||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/memoize-one": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/memoize-one/-/memoize-one-6.0.0.tgz",
|
||||
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/micromatch": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/micromatch/-/micromatch-4.0.8.tgz",
|
||||
|
@ -4523,6 +4816,16 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-range": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/normalize-range/-/normalize-range-0.1.2.tgz",
|
||||
|
@ -4533,6 +4836,12 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-wheel-es": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
|
||||
"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/normalize.css": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/normalize.css/-/normalize.css-8.0.1.tgz",
|
||||
|
@ -5717,7 +6026,6 @@
|
|||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
|
@ -5949,6 +6257,75 @@
|
|||
"node": ">=18.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/unplugin-auto-import": {
|
||||
"version": "19.3.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/unplugin-auto-import/-/unplugin-auto-import-19.3.0.tgz",
|
||||
"integrity": "sha512-iIi0u4Gq2uGkAOGqlPJOAMI8vocvjh1clGTfSK4SOrJKrt+tirrixo/FjgBwXQNNdS7ofcr7OxzmOb/RjWxeEQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"local-pkg": "^1.1.1",
|
||||
"magic-string": "^0.30.17",
|
||||
"picomatch": "^4.0.2",
|
||||
"unimport": "^4.2.0",
|
||||
"unplugin": "^2.3.4",
|
||||
"unplugin-utils": "^0.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nuxt/kit": "^3.2.2",
|
||||
"@vueuse/core": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@nuxt/kit": {
|
||||
"optional": true
|
||||
},
|
||||
"@vueuse/core": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/unplugin-auto-import/node_modules/estree-walker": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/estree-walker/-/estree-walker-3.0.3.tgz",
|
||||
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/unplugin-auto-import/node_modules/unimport": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/unimport/-/unimport-4.2.0.tgz",
|
||||
"integrity": "sha512-mYVtA0nmzrysnYnyb3ALMbByJ+Maosee2+WyE0puXl+Xm2bUwPorPaaeZt0ETfuroPOtG8jj1g/qeFZ6buFnag==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "^8.14.1",
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
"estree-walker": "^3.0.3",
|
||||
"local-pkg": "^1.1.1",
|
||||
"magic-string": "^0.30.17",
|
||||
"mlly": "^1.7.4",
|
||||
"pathe": "^2.0.3",
|
||||
"picomatch": "^4.0.2",
|
||||
"pkg-types": "^2.1.0",
|
||||
"scule": "^1.3.0",
|
||||
"strip-literal": "^3.0.0",
|
||||
"tinyglobby": "^0.2.12",
|
||||
"unplugin": "^2.2.2",
|
||||
"unplugin-utils": "^0.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/unplugin-utils": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/unplugin-utils/-/unplugin-utils-0.2.4.tgz",
|
||||
|
@ -5965,6 +6342,106 @@
|
|||
"url": "https://github.com/sponsors/sxzz"
|
||||
}
|
||||
},
|
||||
"node_modules/unplugin-vue-components": {
|
||||
"version": "28.7.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/unplugin-vue-components/-/unplugin-vue-components-28.7.0.tgz",
|
||||
"integrity": "sha512-3SuWAHlTjOiZckqRBGXRdN/k6IMmKyt2Ch5/+DKwYaT321H0ItdZDvW4r8/YkEKQpN9TN3F/SZ0W342gQROC3Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chokidar": "^3.6.0",
|
||||
"debug": "^4.4.1",
|
||||
"local-pkg": "^1.1.1",
|
||||
"magic-string": "^0.30.17",
|
||||
"mlly": "^1.7.4",
|
||||
"tinyglobby": "^0.2.14",
|
||||
"unplugin": "^2.3.4",
|
||||
"unplugin-utils": "^0.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/parser": "^7.15.8",
|
||||
"@nuxt/kit": "^3.2.2",
|
||||
"vue": "2 || 3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@babel/parser": {
|
||||
"optional": true
|
||||
},
|
||||
"@nuxt/kit": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/unplugin-vue-components/node_modules/chokidar": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/chokidar/-/chokidar-3.6.0.tgz",
|
||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
"glob-parent": "~5.1.2",
|
||||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
"normalize-path": "~3.0.0",
|
||||
"readdirp": "~3.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/unplugin-vue-components/node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/unplugin-vue-components/node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/unplugin-vue-components/node_modules/readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"picomatch": "^2.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/untyped": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/untyped/-/untyped-2.0.0.tgz",
|
||||
|
@ -6240,6 +6717,11 @@
|
|||
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vue3-puzzle-vcode": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/vue3-puzzle-vcode/-/vue3-puzzle-vcode-1.1.7.tgz",
|
||||
"integrity": "sha512-mW780dz7HKjrElnE60CeYSeHGidKBKHoMjTDYfqF21330rTkFOsfDK1FQKZ22MktgMtTEoS/imfpEDlM1cxY/g=="
|
||||
},
|
||||
"node_modules/webpack-virtual-modules": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://repo.huaweicloud.com/repository/npm/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
|
||||
|
|
|
@ -9,12 +9,16 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@icon-park/vue-next": "^1.4.2",
|
||||
"@vueuse/core": "^13.3.0",
|
||||
"animate.css": "^4.1.1",
|
||||
"autofit.js": "^3.2.8",
|
||||
"axios": "^1.9.0",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"echarts": "^5.6.0",
|
||||
"element-plus": "^2.10.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mitt": "^3.0.1",
|
||||
"normalize.css": "^8.0.1",
|
||||
|
@ -23,7 +27,8 @@
|
|||
"throttle-debounce": "^5.0.2",
|
||||
"v-viewer": "^3.0.21",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.1"
|
||||
"vue-router": "^4.5.1",
|
||||
"vue3-puzzle-vcode": "^1.1.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.19.68",
|
||||
|
@ -41,6 +46,8 @@
|
|||
"eslint-plugin-vue": "^9.32.0",
|
||||
"prettier": "^2.8.8",
|
||||
"sass": "^1.83.0",
|
||||
"unplugin-auto-import": "^19.3.0",
|
||||
"unplugin-vue-components": "^28.7.0",
|
||||
"vite": "^6.3.5",
|
||||
"vite-plugin-env-parse": "^1.0.15",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
|
@ -0,0 +1,25 @@
|
|||
import CryptoJS from "crypto-js";
|
||||
|
||||
const key = CryptoJS.enc.Utf8.parse("daac3ae52eff4cec"); // 16位
|
||||
|
||||
const encrypt = (word) => {
|
||||
let encrypted = "";
|
||||
if (typeof word === "string") {
|
||||
const src = CryptoJS.enc.Utf8.parse(word);
|
||||
encrypted = CryptoJS.AES.encrypt(src, key, {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
});
|
||||
} else if (typeof word === "object") {
|
||||
// 对象格式的转成json字符串
|
||||
const data = JSON.stringify(word);
|
||||
const src = CryptoJS.enc.Utf8.parse(data);
|
||||
encrypted = CryptoJS.AES.encrypt(src, key, {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
});
|
||||
}
|
||||
return encrypted.ciphertext.toString(CryptoJS.enc.Base64);
|
||||
};
|
||||
|
||||
export { encrypt };
|
|
@ -0,0 +1,17 @@
|
|||
import dayjs from "dayjs";
|
||||
import { setRefreshToken } from "@/request/api.js";
|
||||
import { useUserStore } from "@/pinia/user.js";
|
||||
import pinia from "@/pinia/index.js";
|
||||
import router from "@/router/index.js";
|
||||
|
||||
export default async function () {
|
||||
if (router.currentRoute.value.meta.isLogin) {
|
||||
const userStore = useUserStore(pinia);
|
||||
if (userStore.getTokenTime) {
|
||||
if (dayjs().diff(dayjs(userStore.getTokenTime), "minute") >= 5) {
|
||||
await userStore.setTokenTime(dayjs().format("YYYY-MM-DD HH:mm:ss"));
|
||||
await setRefreshToken();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,480 @@
|
|||
import { ElMessage } from "element-plus";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
/**
|
||||
* @description 计算序号
|
||||
* @param {Object} pagination 分页数据对象
|
||||
* @param {number | string} pagination.currentPage 当前页
|
||||
* @param {number | string} pagination.pageSize 每页条数
|
||||
* @param {number} index 当页数据的索引值
|
||||
* @return {number} 序号
|
||||
**/
|
||||
export function serialNumber(pagination, index) {
|
||||
return (pagination.currentPage - 1) * pagination.pageSize + (index + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 字符串数组转数组
|
||||
* @param {string} value 转换的字符串数组
|
||||
* @return {Array} 转换后的数组
|
||||
**/
|
||||
export function toArrayString(value) {
|
||||
// eslint-disable-next-line no-eval
|
||||
return value ? eval(value).map(String) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 判断文件后缀名是否符合
|
||||
* @param {string} name 文件名字
|
||||
* @param {string} suffix 文件后缀
|
||||
* @return {boolean} 是否符合
|
||||
**/
|
||||
export function interceptTheSuffix(name, suffix) {
|
||||
return (
|
||||
name.substring(name.lastIndexOf("."), name.length).toLowerCase() ===
|
||||
suffix.toLowerCase()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 图片转base64
|
||||
* @param {string} imgUrl 图片地址
|
||||
* @return {Promise} Promise实例,then包含base64编码
|
||||
**/
|
||||
export function image2Base64(imgUrl) {
|
||||
return new Promise((resolve) => {
|
||||
const img = new Image();
|
||||
img.src = imgUrl;
|
||||
img.crossOrigin = "Anonymous";
|
||||
img.onload = function () {
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(img, 0, 0, img.width, img.height);
|
||||
const ext = img.src.substring(img.src.lastIndexOf(".") + 1).toLowerCase();
|
||||
resolve(canvas.toDataURL("image/" + ext));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 判断图片是否可访问成功
|
||||
* @param {string} imgUrl 图片地址
|
||||
* @return {Promise} Promise实例
|
||||
**/
|
||||
export function checkImgExists(imgUrl) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const ImgObj = new Image();
|
||||
ImgObj.src = imgUrl;
|
||||
ImgObj.onload = function (res) {
|
||||
resolve(res);
|
||||
};
|
||||
ImgObj.onerror = function (err) {
|
||||
reject(err);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取数据类型
|
||||
* @param {any} data 数据
|
||||
* @return {string} 数据类型
|
||||
**/
|
||||
export function getDataType(data) {
|
||||
return Object.prototype.toString.call(data).slice(8, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 数组去重
|
||||
* @param {Array<number,string>} arr 去重的数组
|
||||
* @return {Array} 去重后的数组
|
||||
**/
|
||||
export function ArrayDeduplication(arr) {
|
||||
return [...new Set(arr)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 数组对象去重
|
||||
* @param {Array} arr 去重的数组
|
||||
* @param {string} name 去重的key
|
||||
* @return {Array} 去重后的数组
|
||||
**/
|
||||
export function arrayObjectDeduplication(arr, name) {
|
||||
const obj = {};
|
||||
arr = arr.reduce(function (previousValue, currentValue) {
|
||||
if (!obj[currentValue[name]]) {
|
||||
obj[currentValue[name]] = true;
|
||||
previousValue.push(currentValue);
|
||||
}
|
||||
return previousValue;
|
||||
}, []);
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 查找字符串中指定的值第几次出现的位置
|
||||
* @param {Array} str 查找的字符串数组
|
||||
* @param {string} char 查找的值
|
||||
* @param {number} num 第几次出现
|
||||
* @return {number} 出现的位置
|
||||
**/
|
||||
export function findCharIndex(str, char, num) {
|
||||
let index = str.indexOf(char);
|
||||
if (index === -1) return -1;
|
||||
for (let i = 0; i < num - 1; i++) {
|
||||
index = str.indexOf(char, index + 1);
|
||||
if (index === -1) return -1;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 生成指定两个值之间的随机数
|
||||
* @param {number} min 最小值
|
||||
* @param {number} max 最大值
|
||||
* @return {number} 随机数
|
||||
**/
|
||||
export function randoms(min, max) {
|
||||
return Math.random() * (max - min + 1) + min;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 千位分隔符
|
||||
* @param {number | string} num 转换的值
|
||||
* @return {string} 转换后的值
|
||||
**/
|
||||
export function numFormat(num) {
|
||||
if (num) {
|
||||
const numArr = num.toString().split(".");
|
||||
const arr = numArr[0].split("").reverse();
|
||||
let res = [];
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (i % 3 === 0 && i !== 0) {
|
||||
res.push(",");
|
||||
}
|
||||
res.push(arr[i]);
|
||||
}
|
||||
res.reverse();
|
||||
if (numArr[1]) {
|
||||
res = res.join("").concat("." + numArr[1]);
|
||||
} else {
|
||||
res = res.join("");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 验证是否为空
|
||||
* @param {any} value 验证的值
|
||||
* @return {boolean} 是否为空
|
||||
**/
|
||||
export function isEmpty(value) {
|
||||
return (
|
||||
value === undefined ||
|
||||
value === null ||
|
||||
(typeof value === "object" && Object.keys(value).length === 0) ||
|
||||
(typeof value === "string" && value.trim().length === 0)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取url参数
|
||||
* @param {string} name 获取的key
|
||||
* @return {string} 获取的值
|
||||
**/
|
||||
export function getUrlParam(name) {
|
||||
const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
|
||||
const r = window.location.search.substr(1).match(reg);
|
||||
if (r != null) return decodeURI(r[2]);
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 数据分页
|
||||
* @param {Array} list 分页的数组
|
||||
* @param {number | string} currentPage 当前页
|
||||
* @param {number | string} pageSize 每页条数
|
||||
* @return {Array} 分页后的数组
|
||||
**/
|
||||
export function paging(list, currentPage, pageSize) {
|
||||
return list.filter((item, index) => {
|
||||
return (
|
||||
index < +currentPage * +pageSize &&
|
||||
index >= (+currentPage - 1) * +pageSize
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取文件后缀
|
||||
* @param {string} name 文件名
|
||||
* @return {string} 文件后缀
|
||||
**/
|
||||
export function getFileSuffix(name) {
|
||||
return name.substring(name.lastIndexOf(".") + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取文件名称
|
||||
* @param {string} name 文件地址
|
||||
* @return {string} 文件名称
|
||||
**/
|
||||
export function getFileName(name) {
|
||||
if (!name) return "";
|
||||
return name.substring(name.lastIndexOf("/") + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 读取txt文档
|
||||
* @param {string} filePah 文档路径
|
||||
* @return {resolve,string} 读取后的内容
|
||||
**/
|
||||
export function readTxtDocument(filePah) {
|
||||
return new Promise((resolve) => {
|
||||
const FILE_URL = import.meta.env.VITE_FILE_URL;
|
||||
const file_url = FILE_URL + filePah;
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open("get", file_url, true);
|
||||
xhr.responseType = "blob";
|
||||
xhr.onload = function (event) {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(event.target.response, "GB2312");
|
||||
reader.onload = function () {
|
||||
resolve(reader.result);
|
||||
};
|
||||
};
|
||||
xhr.send();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 将秒转换成时分秒
|
||||
* @param {string,number} second 需要转换的秒数
|
||||
* @return {string} 转换后的时间
|
||||
**/
|
||||
export function secondConversion(second) {
|
||||
if (!second) return 0;
|
||||
const h = parseInt(second / 60 / 60, 10);
|
||||
const m = parseInt((second / 60) % 60, 10);
|
||||
const s = parseInt(second % 60, 10);
|
||||
if (h) {
|
||||
return h + "小时" + m + "分钟" + s + "秒";
|
||||
} else {
|
||||
if (m) {
|
||||
return m + "分钟" + s + "秒";
|
||||
} else {
|
||||
return s + "秒";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 附件添加前缀
|
||||
* @param {Array} list 附件数组
|
||||
* @return {Array} 添加完的数组
|
||||
**/
|
||||
export function addingPrefixToFile(list) {
|
||||
if (!list) return [];
|
||||
const FILE_URL = import.meta.env.VITE_FILE_URL;
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
list[i].url = FILE_URL + list[i].FILEPATH;
|
||||
if (list[i].FILENAME) {
|
||||
list[i].name = list[i].FILENAME;
|
||||
} else {
|
||||
list[i].name = getFileName(list[i].FILEPATH);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 验证重复选择
|
||||
* @param {Array} list 验证的数组
|
||||
* @param {number} index 选择的索引
|
||||
* @param {string} key 验证的字段
|
||||
* @param {string} id 验证的值
|
||||
**/
|
||||
export async function verifyDuplicateSelection(list, index, key, id) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (list.some((item) => item[key] === id)) {
|
||||
ElMessage.warning("不能重复选择");
|
||||
reject(new Error("不能重复选择"));
|
||||
} else {
|
||||
list[index][key] = id;
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 翻译状态
|
||||
* @param {number | string} status 状态
|
||||
* @param {Array} list 翻译的数组
|
||||
* @param {String} idKey
|
||||
* @param {String} nameKey
|
||||
* @return {string} 翻译后的状态
|
||||
**/
|
||||
export function translationStatus(
|
||||
status,
|
||||
list,
|
||||
idKey = "id",
|
||||
nameKey = "name"
|
||||
) {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (status === list[i][idKey]) {
|
||||
return list[i][nameKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 计算文件大小
|
||||
* @param {number | string} size 文件kb
|
||||
* @return {string} 计算后的文件大小
|
||||
**/
|
||||
export function calculateFileSize(size) {
|
||||
return size > 1024
|
||||
? (size / 1024 + "").substring(0, (size / 1024 + "").lastIndexOf(".") + 3) +
|
||||
"MB"
|
||||
: size + "KB";
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 根据身份证号获取出生日期和性别
|
||||
* @param {String} idCard 身份证号
|
||||
* @return {Object} 出生日期和性别 date sex
|
||||
**/
|
||||
export function idCardGetDateAndGender(idCard) {
|
||||
const reg =
|
||||
/^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
|
||||
let sex = "";
|
||||
let date = "";
|
||||
if (reg.test(idCard)) {
|
||||
const org_birthday = idCard.substring(6, 14);
|
||||
const org_gender = idCard.substring(16, 17);
|
||||
const birthday =
|
||||
org_birthday.substring(0, 4) +
|
||||
"-" +
|
||||
org_birthday.substring(4, 6) +
|
||||
"-" +
|
||||
org_birthday.substring(6, 8);
|
||||
const birthdays = new Date(birthday.replace(/-/g, "/"));
|
||||
const Month = birthdays.getMonth() + 1;
|
||||
let MonthDate;
|
||||
const DayDate = birthdays.getDate();
|
||||
let Day;
|
||||
if (Month < 10) MonthDate = "0" + Month;
|
||||
else MonthDate = Month;
|
||||
if (DayDate < 10) Day = "0" + DayDate;
|
||||
else Day = DayDate;
|
||||
sex = org_gender % 2 === 1 ? "1" : "0";
|
||||
date = birthdays.getFullYear() + "-" + MonthDate + "-" + Day;
|
||||
}
|
||||
return { sex, date };
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取select的label
|
||||
* @param {Array} list 获取的数组
|
||||
* @param {number | string} value 获取的值
|
||||
* @param {string?} idKey 获取的id
|
||||
* @param {string?} labelKey 获取的label
|
||||
* @return {string} 获取的label
|
||||
**/
|
||||
export function getSelectLabel(
|
||||
list,
|
||||
value,
|
||||
idKey = "bianma",
|
||||
labelKey = "name"
|
||||
) {
|
||||
const result = list.find((item) => item[idKey] === value);
|
||||
return result ? result[labelKey] : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取select中指定项组成的数组
|
||||
* @param {Array} list 获取的数组
|
||||
* @param {Array} value 获取的值
|
||||
* @param {string?} idKey 获取的id
|
||||
* @return {Array} list中指定项组成的数组
|
||||
**/
|
||||
export function getSelectAppointItemList(list, value, idKey = "id") {
|
||||
return list.filter((item) => value.includes(item[idKey]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @description dayjs改变日期格式
|
||||
* @param {date} date 传入的日期
|
||||
* @param {string} column 需要变成的日期格式
|
||||
* @return {string} 改变的日期格式
|
||||
**/
|
||||
export function formatDate(date, column) {
|
||||
if (date) {
|
||||
return dayjs(date).format(column);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description json转换为树形结构
|
||||
* @param {Array} json 需要转换的json
|
||||
* @param {string} idStr id字段
|
||||
* @param {string} pidStr 父级id字段
|
||||
* @param {string} chindrenStr 子级字段
|
||||
* @return {string} 转换完的树形结构
|
||||
**/
|
||||
export function listTransTree(json, idStr, pidStr, chindrenStr) {
|
||||
const r = [];
|
||||
const hash = {};
|
||||
const id = idStr;
|
||||
const pid = pidStr;
|
||||
const children = chindrenStr;
|
||||
let i = 0;
|
||||
let j = 0;
|
||||
const len = json.length;
|
||||
for (; i < len; i++) {
|
||||
hash[json[i][id]] = json[i];
|
||||
}
|
||||
for (; j < len; j++) {
|
||||
const aVal = json[j];
|
||||
const hashVP = hash[aVal[pid]];
|
||||
if (hashVP) {
|
||||
!hashVP[children] && (hashVP[children] = []);
|
||||
hashVP[children].push(aVal);
|
||||
} else {
|
||||
r.push(aVal);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 附件添加前缀
|
||||
* @param {Array} list 附件数组
|
||||
* @return {Array} 添加完的数组
|
||||
**/
|
||||
export function fileFeedback(list) {
|
||||
if (!list) return [];
|
||||
const FILE_URL = import.meta.env.VITE_FILE_URL;
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
list[i].url = FILE_URL + list[i].filepath;
|
||||
list[i].name = list[i].filepath || getFileName(list[i].filepath);
|
||||
list[i].imgfilesId = getFileSuffix(list[i].imgfilesId);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取文件路径前缀地址
|
||||
* @return {string} 文件路径前缀地址
|
||||
**/
|
||||
export function getFileUrl() {
|
||||
return import.meta.env.VITE_FILE_URL;
|
||||
}
|
||||
|
||||
export function getBaseUrl() {
|
||||
return import.meta.env[import.meta.env.DEV ? "VITE_PROXY" : "VITE_BASE_URL"];
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
<template>
|
||||
<div class="mi-captcha">
|
||||
<div class="mi-captcha-content">
|
||||
<!-- 没有进行验证-->
|
||||
<div
|
||||
v-if="!verificationPass"
|
||||
class="mi-captcha-radar"
|
||||
@click="verificationShow = true"
|
||||
>
|
||||
<div class="mi-captcha-radar-ready">
|
||||
<div class="mi-captcha-radar-ring" />
|
||||
<div class="mi-captcha-radar-dot" />
|
||||
</div>
|
||||
<div class="mi-captcha-radar-tip">点击按钮进行验证</div>
|
||||
</div>
|
||||
<!-- 验证通过-->
|
||||
<div
|
||||
v-if="verificationPass"
|
||||
class="mi-captcha-radar mi-captcha-radar-pass"
|
||||
>
|
||||
<div class="mi-captcha-radar-success mi-captcha-radar-success-icon">
|
||||
<span role="img" aria-label="verified">
|
||||
<svg
|
||||
focusable="false"
|
||||
class=""
|
||||
data-icon="verified"
|
||||
width="1em"
|
||||
height="1em"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
viewBox="64 64 896 896"
|
||||
>
|
||||
<path
|
||||
d="M447.8 588.8l-7.3-32.5c-.2-1-.6-1.9-1.1-2.7a7.94 7.94 0 00-11.1-2.2L405 567V411c0-4.4-3.6-8-8-8h-81c-4.4 0-8 3.6-8 8v36c0 4.4 3.6 8 8 8h37v192.4a8 8 0 0012.7 6.5l79-56.8c2.6-1.9 3.8-5.1 3.1-8.3zm-56.7-216.6l.2.2c3.2 3 8.3 2.8 11.3-.5l24.1-26.2a8.1 8.1 0 00-.3-11.2l-53.7-52.1a8 8 0 00-11.2.1l-24.7 24.7c-3.1 3.1-3.1 8.2.1 11.3l54.2 53.7z"
|
||||
/>
|
||||
<path
|
||||
d="M866.9 169.9L527.1 54.1C523 52.7 517.5 52 512 52s-11 .7-15.1 2.1L157.1 169.9c-8.3 2.8-15.1 12.4-15.1 21.2v482.4c0 8.8 5.7 20.4 12.6 25.9L499.3 968c3.5 2.7 8 4.1 12.6 4.1s9.2-1.4 12.6-4.1l344.7-268.6c6.9-5.4 12.6-17 12.6-25.9V191.1c.2-8.8-6.6-18.3-14.9-21.2zM810 654.3L512 886.5 214 654.3V226.7l298-101.6 298 101.6v427.6z"
|
||||
/>
|
||||
<path
|
||||
d="M452 297v36c0 4.4 3.6 8 8 8h108v274h-38V405c0-4.4-3.6-8-8-8h-35c-4.4 0-8 3.6-8 8v210h-31c-4.4 0-8 3.6-8 8v37c0 4.4 3.6 8 8 8h244c4.4 0 8-3.6 8-8v-37c0-4.4-3.6-8-8-8h-72V493h58c4.4 0 8-3.6 8-8v-35c0-4.4-3.6-8-8-8h-58V341h63c4.4 0 8-3.6 8-8v-36c0-4.4-3.6-8-8-8H460c-4.4 0-8 3.6-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div class="mi-captcha-radar-tip">通过验证</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<verification
|
||||
:show="verificationShow"
|
||||
@success="verificationSuccess"
|
||||
@close="verificationClose"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Verification from "vue3-puzzle-vcode";
|
||||
import { ref } from "vue";
|
||||
|
||||
defineOptions({
|
||||
name: "AppVerification",
|
||||
});
|
||||
const verificationPass = defineModel("verificationPass", {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
});
|
||||
const verificationShow = ref(false);
|
||||
const verificationClose = () => {
|
||||
verificationShow.value = false;
|
||||
};
|
||||
const verificationSuccess = () => {
|
||||
verificationPass.value = true;
|
||||
verificationClose();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.mi-captcha {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
height: 2.625rem;
|
||||
font-family: "Pingfang SC", "Microsoft YaHei", "Monospaced Number",
|
||||
"Chinese Quote", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
||||
"PingFang SC", "Hiragino Sans GB", "Helvetica Neue", Helvetica, Arial,
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
.mi-captcha-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mi-captcha-radar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
flex-direction: row;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: #1d1e23;
|
||||
background-image: linear-gradient(315deg, #a8b4d3 0%, #adc0ed 74%);
|
||||
border: 1px solid #96a4c8;
|
||||
cursor: pointer;
|
||||
min-width: 10rem;
|
||||
position: relative;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.mi-captcha-radar-ready,
|
||||
.mi-captcha-radar-success {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
position: relative;
|
||||
transition: all 0.4s ease;
|
||||
width: 1.875rem;
|
||||
height: 1.875rem;
|
||||
margin: 0.375rem;
|
||||
}
|
||||
|
||||
.mi-captcha-radar-ring,
|
||||
.mi-captcha-radar-dot {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
transform: scale(0.4);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-shadow: inset 0 0 0 1px #2c67ec;
|
||||
background-image: linear-gradient(0, rgba(0, 0, 0, 0) 50%, #fff 50%),
|
||||
linear-gradient(0, #fff 50%, rgba(0, 0, 0, 0) 50%);
|
||||
}
|
||||
|
||||
.mi-captcha-radar-dot {
|
||||
background: #2c67ec;
|
||||
}
|
||||
|
||||
.mi-captcha-radar-ring {
|
||||
animation: mi-anim-wait 1s infinite;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.mi-captcha-radar-success {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: row;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.mi-captcha-radar-success-icon {
|
||||
color: #f6ca9d;
|
||||
animation-name: mi-captcha-success;
|
||||
animation-timing-function: ease;
|
||||
animation-iteration-count: 1;
|
||||
animation-delay: 0.5s;
|
||||
animation-duration: 0.4s;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.mi-captcha-radar-tip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
flex-direction: row;
|
||||
height: 2.625rem;
|
||||
padding-left: 0.125rem;
|
||||
font-size: 0.875rem;
|
||||
color: #fff;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.mi-captcha-radar-pass .mi-captcha-radar-tip {
|
||||
color: #f6ca9d;
|
||||
}
|
||||
|
||||
@keyframes mi-captcha-success {
|
||||
25% {
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(-360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes mi-anim-wait {
|
||||
60% {
|
||||
transform: scale(0.75);
|
||||
}
|
||||
}
|
||||
|
||||
:deep {
|
||||
.vue-auth-box_ {
|
||||
background: #2e63d8 !important;
|
||||
border: 1px solid #2752b3 !important;
|
||||
}
|
||||
.mi-captcha-radar-pass .mi-captcha-radar-tip {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
.mi-captcha-radar-success-icon {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,28 @@
|
|||
import { ElMessage } from "element-plus";
|
||||
import { useTemplateRef } from "vue";
|
||||
|
||||
export default function useFormValidate() {
|
||||
const formRef = useTemplateRef("formRef");
|
||||
const validate = (message = "请补全必填项!") => {
|
||||
return new Promise((resolve, reject) => {
|
||||
formRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
resolve(valid);
|
||||
} else {
|
||||
reject(valid);
|
||||
ElMessage.warning(message);
|
||||
setTimeout(() => {
|
||||
const element = document.querySelectorAll(
|
||||
".el-form-item__error"
|
||||
)[0];
|
||||
element.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "center",
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
return { validate, formRef };
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
import { useMiscellaneousStore } from "@/pinia/miscellaneous.js";
|
||||
|
||||
export const getQueryCriteria = () => {
|
||||
const miscellaneousStore = useMiscellaneousStore();
|
||||
const key = window.location.href;
|
||||
let queryCriteria = miscellaneousStore.getQueryCriteria[key] || {};
|
||||
if (queryCriteria.tabsActiveName) {
|
||||
queryCriteria =
|
||||
miscellaneousStore.getQueryCriteria[
|
||||
key + "/" + queryCriteria.tabsActiveName
|
||||
] || {};
|
||||
}
|
||||
const pagination = queryCriteria.pagination;
|
||||
const searchForm = queryCriteria.searchForm;
|
||||
const tabsActiveName = queryCriteria.tabsActiveName;
|
||||
return {
|
||||
pagination,
|
||||
searchForm,
|
||||
tabsActiveName,
|
||||
};
|
||||
};
|
||||
export const setQueryCriteria = (data) => {
|
||||
const miscellaneousStore = useMiscellaneousStore();
|
||||
let key = window.location.href;
|
||||
if (data.tabsActiveName) {
|
||||
miscellaneousStore.setQueryCriteria({
|
||||
...miscellaneousStore.getQueryCriteria,
|
||||
[key]: {
|
||||
...miscellaneousStore.getQueryCriteria[key],
|
||||
tabsActiveName: data.tabsActiveName,
|
||||
},
|
||||
});
|
||||
key = key + "/" + data.tabsActiveName;
|
||||
}
|
||||
miscellaneousStore.setQueryCriteria({
|
||||
...miscellaneousStore.getQueryCriteria,
|
||||
[key]: {
|
||||
...miscellaneousStore.getQueryCriteria[key],
|
||||
...data,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const resetQueryCriteria = () => {
|
||||
const miscellaneousStore = useMiscellaneousStore();
|
||||
miscellaneousStore.resetQueryCriteria();
|
||||
};
|
||||
|
||||
export const getTabsActiveName = () => {
|
||||
const key = window.location.href;
|
||||
const miscellaneousStore = useMiscellaneousStore();
|
||||
const queryCriteria = miscellaneousStore.getQueryCriteria[key] || {};
|
||||
return queryCriteria.tabsActiveName;
|
||||
};
|
|
@ -0,0 +1,64 @@
|
|||
<template>
|
||||
<div
|
||||
style="display: flex; justify-content: space-between; align-items: center"
|
||||
>
|
||||
<div class="breadcrumb">
|
||||
<el-breadcrumb class="app-breadcrumb" separator=">">
|
||||
<transition-group name="breadcrumb">
|
||||
<el-breadcrumb-item
|
||||
v-for="(item, index) in breadcrumbList"
|
||||
:key="item.path"
|
||||
>
|
||||
<router-link v-if="index === 0" :to="item.path">
|
||||
{{ item.meta.title }}
|
||||
</router-link>
|
||||
<span v-else-if="index !== breadcrumbList.length - 1">
|
||||
{{ item.meta.title }}
|
||||
</span>
|
||||
<span v-else class="no-redirect">{{ item.meta.title }}</span>
|
||||
</el-breadcrumb-item>
|
||||
</transition-group>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
defineOptions({
|
||||
name: "LayoutBreadcrumb",
|
||||
});
|
||||
const route = useRoute();
|
||||
const breadcrumbList = ref([]);
|
||||
const fnGetBreadcrumb = () => {
|
||||
const matched = route.matched.filter((item) => item.meta?.title);
|
||||
if (matched[0].path === "/") matched[0].path = "/index";
|
||||
breadcrumbList.value = matched.filter(
|
||||
(item) => item.meta?.title && item.meta?.breadcrumb !== false
|
||||
);
|
||||
};
|
||||
fnGetBreadcrumb();
|
||||
watch(
|
||||
() => route,
|
||||
() => fnGetBreadcrumb(),
|
||||
{ deep: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-breadcrumb.el-breadcrumb {
|
||||
font-size: 14px;
|
||||
line-height: var(--el-header-height);
|
||||
|
||||
.no-redirect {
|
||||
color: #97a8be;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.el-breadcrumb__inner a {
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,70 @@
|
|||
<template>
|
||||
<el-dialog v-model="visible" title="修改密码" width="600px" @close="fnClose">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="旧密码" prop="password">
|
||||
<el-input v-model="form.password" type="password" />
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码" prop="newPassword">
|
||||
<el-input v-model="form.newPassword" type="password" />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码" prop="newPasswordConfirm">
|
||||
<el-input v-model="form.newPasswordConfirm" type="password" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="fnSubmit">确认修改</el-button>
|
||||
<el-button @click="fnClose">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { debounce } from "throttle-debounce";
|
||||
import useFormValidate from "@/hooks/useFormValidate.js";
|
||||
import { setChangePassword } from "@/request/api.js";
|
||||
import { ref } from "vue";
|
||||
|
||||
const emits = defineEmits(["submit"]);
|
||||
const visible = defineModel("visible", { type: Boolean, required: true });
|
||||
const form = ref({
|
||||
password: "",
|
||||
newPassword: "",
|
||||
newPasswordConfirm: "",
|
||||
});
|
||||
const { formRef, validate } = useFormValidate();
|
||||
const validatePass = (_rule, value, callback) => {
|
||||
if (value === "") {
|
||||
callback(new Error("请再次输入新密码"));
|
||||
} else if (value !== form.value.newPassword) {
|
||||
callback(new Error("两次输入密码不一致!"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
const rules = {
|
||||
password: [{ required: true, message: "请输入旧密码", trigger: "blur" }],
|
||||
newPassword: [
|
||||
{ required: true, message: "请输入新密码", trigger: "blur" },
|
||||
{ min: 6, max: 18, message: "密码长度为6-18位", trigger: "blur" },
|
||||
],
|
||||
newPasswordConfirm: [
|
||||
{ required: true, validator: validatePass, trigger: "blur" },
|
||||
],
|
||||
};
|
||||
const fnClose = () => {
|
||||
formRef.value.resetFields();
|
||||
visible.value = false;
|
||||
};
|
||||
const fnSubmit = debounce(
|
||||
1000,
|
||||
async () => {
|
||||
await validate();
|
||||
await setChangePassword({ ...form.value });
|
||||
fnClose();
|
||||
emits("submit");
|
||||
},
|
||||
{ atBegin: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
|
@ -0,0 +1,125 @@
|
|||
<template>
|
||||
<div class="header">
|
||||
<div class="logo">
|
||||
<!-- <img-->
|
||||
<!-- src="/src/assets/images/public/logo1.png"-->
|
||||
<!-- alt=""-->
|
||||
<!-- width="134"-->
|
||||
<!-- height="28"-->
|
||||
<!-- />-->
|
||||
{{ logoValue.title }}
|
||||
</div>
|
||||
<div class="breadcrumb">
|
||||
<layout-breadcrumb v-if="route.meta.isBreadcrumb !== false" />
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="user_dropdown">
|
||||
<el-dropdown
|
||||
trigger="click"
|
||||
placement="bottom-end"
|
||||
@command="fnUserDropdownCommand"
|
||||
>
|
||||
<div class="user_info">
|
||||
<el-avatar shape="circle" :size="30" fit="fill" :src="tx" />
|
||||
<span>{{ userInfo.username }}</span>
|
||||
<icon-down
|
||||
theme="filled"
|
||||
size="16"
|
||||
fill="#a2c2d3"
|
||||
:stroke-width="3"
|
||||
/>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="modifyPassword">
|
||||
修改密码
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="signOut">退出</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<change-password
|
||||
v-model:visible="passwordDialogVisible"
|
||||
@submit="fnSignOut"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { useUserStore } from "@/pinia/user.js";
|
||||
import { logout } from "@/request/api.js";
|
||||
import LayoutBreadcrumb from "@/layout/breadcrumb/index.vue";
|
||||
import tx from "@/assets/images/public/tx.png";
|
||||
import ChangePassword from "./components/change_password.vue";
|
||||
|
||||
defineOptions({
|
||||
name: "LayoutHeader",
|
||||
});
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
const userInfo = userStore.getUserInfo;
|
||||
const logoValue = ref({
|
||||
title: "",
|
||||
});
|
||||
const passwordDialogVisible = ref(false);
|
||||
const fnUserDropdownCommand = async (command) => {
|
||||
if (command === "signOut") {
|
||||
await fnSignOut();
|
||||
}
|
||||
if (command === "modifyPassword") {
|
||||
passwordDialogVisible.value = true;
|
||||
}
|
||||
};
|
||||
const fnSignOut = async () => {
|
||||
await logout();
|
||||
userStore.$reset();
|
||||
await router.replace("/login");
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-shadow: 0 1px 4px rgb(0 21 41 / 8%);
|
||||
padding-right: 20px;
|
||||
background-color: #fff;
|
||||
|
||||
.logo {
|
||||
width: 210px;
|
||||
height: 50px;
|
||||
background-color: #2b2f3a;
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
line-height: 60px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.right {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
|
||||
.user_dropdown {
|
||||
margin-top: 5px;
|
||||
cursor: pointer;
|
||||
|
||||
.el-avatar {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.user_info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,109 @@
|
|||
<template>
|
||||
<el-container>
|
||||
<el-header>
|
||||
<layout-header />
|
||||
</el-header>
|
||||
<el-container>
|
||||
<el-aside>
|
||||
<el-scrollbar style="height: calc(100vh - 50px)">
|
||||
<el-menu
|
||||
router
|
||||
unique-opened
|
||||
:default-active="route.meta.activeMenu || route.path"
|
||||
background-color="rgb(48, 65, 86)"
|
||||
text-color="#edf7ff"
|
||||
active-text-color="#409eff"
|
||||
>
|
||||
<layout-menu :menus="routes" />
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</el-aside>
|
||||
<el-main>
|
||||
<el-scrollbar style="height: calc(100vh - 50px)">
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="view" mode="out-in">
|
||||
<el-card :key="route.path">
|
||||
<el-page-header
|
||||
v-if="route.meta.isBack !== false"
|
||||
title="返回"
|
||||
@back="router.back()"
|
||||
>
|
||||
<template #content>
|
||||
{{
|
||||
route.matched.filter((item) => item.meta?.title).at(-1)
|
||||
.meta.title
|
||||
}}
|
||||
</template>
|
||||
</el-page-header>
|
||||
<component :is="Component"></component>
|
||||
</el-card>
|
||||
</transition>
|
||||
</router-view>
|
||||
</el-scrollbar>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import LayoutHeader from "@/layout/header/index.vue";
|
||||
import LayoutMenu from "@/layout/menu/index.vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
|
||||
defineOptions({
|
||||
name: "Layout",
|
||||
});
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const routes = router.options.routes
|
||||
.filter((item) => item.name === "app")[0]
|
||||
.children.filter((item) => item.name === "backend")[0].children;
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.el-header {
|
||||
--el-header-padding: 0;
|
||||
--el-header-height: 50px;
|
||||
}
|
||||
|
||||
.el-aside {
|
||||
width: 210px;
|
||||
background-color: rgb(48, 65, 86);
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
width: 210px;
|
||||
min-height: calc(100vh - 50px);
|
||||
border-right: none;
|
||||
background-color: rgb(48, 65, 86);
|
||||
|
||||
:deep {
|
||||
span {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep {
|
||||
.el-sub-menu__title *,
|
||||
.el-menu-item * {
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
|
||||
.is-always-shadow {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-main {
|
||||
--el-main-padding: 0;
|
||||
}
|
||||
|
||||
.el-card {
|
||||
margin: 20px;
|
||||
border: none !important;
|
||||
min-height: calc(100vh - 90px);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,80 @@
|
|||
<template>
|
||||
<template v-for="menu in props.menus" :key="menu.path">
|
||||
<!-- 没有二级导航-->
|
||||
<el-menu-item
|
||||
v-if="fnIsShowMenuItem(menu)"
|
||||
:index="menu.path"
|
||||
@click="fnNavigate"
|
||||
>
|
||||
<component
|
||||
:is="'icon-' + menu.meta?.icon"
|
||||
v-if="menu.meta?.icon"
|
||||
theme="filled"
|
||||
fill="#a5b2c2"
|
||||
size="18"
|
||||
:stroke-width="3"
|
||||
style="margin-right: 15px"
|
||||
/>
|
||||
<span>{{ menu.meta?.title }}</span>
|
||||
</el-menu-item>
|
||||
<!-- 有二级导航-->
|
||||
<el-sub-menu v-else-if="fnIsShowSubmenu(menu)" :index="menu.path">
|
||||
<template #title>
|
||||
<component
|
||||
:is="'icon-' + menu.meta?.icon"
|
||||
v-if="menu.meta?.icon"
|
||||
theme="filled"
|
||||
fill="#bfcbd9"
|
||||
size="18"
|
||||
:stroke-width="3"
|
||||
style="margin-right: 10px"
|
||||
/>
|
||||
<span>{{ menu.meta?.title }}</span>
|
||||
</template>
|
||||
<!-- 递归调用当前组件生成导航-->
|
||||
<layout-menu :menus="menu.children" />
|
||||
</el-sub-menu>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from "vue-router";
|
||||
import { resetQueryCriteria } from "@/hooks/useQueryCriteria.js";
|
||||
|
||||
const router = useRouter();
|
||||
defineOptions({
|
||||
name: "LayoutMenu",
|
||||
});
|
||||
const props = defineProps({
|
||||
menus: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
const fnIsShowMenuItem = (menu) => {
|
||||
if (menu.meta?.isMenu === false) {
|
||||
return false;
|
||||
}
|
||||
if (menu.meta?.isSubMenu === false) {
|
||||
return true;
|
||||
} else {
|
||||
return !menu.children || menu.children.length === 0;
|
||||
}
|
||||
};
|
||||
const fnIsShowSubmenu = (menu) => {
|
||||
if (menu.meta?.isMenu === false) {
|
||||
return false;
|
||||
}
|
||||
if (menu.meta?.isSubMenu === false) {
|
||||
return false;
|
||||
} else {
|
||||
return menu.children && menu.children.length > 0;
|
||||
}
|
||||
};
|
||||
const fnNavigate = (event) => {
|
||||
resetQueryCriteria();
|
||||
router.push(event.index);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
|
@ -9,8 +9,12 @@ import "normalize.css";
|
|||
import "animate.css";
|
||||
import "viewerjs/dist/viewer.css";
|
||||
import VueViewer from "v-viewer";
|
||||
import { install } from "@icon-park/vue-next/es/all";
|
||||
import "element-plus/es/components/loading/style/css";
|
||||
import "element-plus/es/components/message/style/css";
|
||||
|
||||
const app = createApp(App);
|
||||
install(app, "icon");
|
||||
app
|
||||
.use(pinia)
|
||||
.use(router)
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import { defineStore } from "pinia";
|
||||
|
||||
export const useMiscellaneousStore = defineStore("miscellaneousStore", {
|
||||
state: () => ({
|
||||
queryCriteria: {},
|
||||
}),
|
||||
getters: {
|
||||
getQueryCriteria() {
|
||||
return this.queryCriteria;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setQueryCriteria(data) {
|
||||
this.queryCriteria = data;
|
||||
},
|
||||
resetQueryCriteria() {
|
||||
this.queryCriteria = {};
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
storage: window.sessionStorage,
|
||||
paths: [""],
|
||||
},
|
||||
});
|
|
@ -0,0 +1,33 @@
|
|||
import { defineStore } from "pinia";
|
||||
|
||||
export const useUserStore = defineStore("userStore", {
|
||||
state: () => ({
|
||||
userInfo: {},
|
||||
token: "",
|
||||
tokenTime: "1",
|
||||
permissions: [],
|
||||
}),
|
||||
getters: {
|
||||
getUserInfo: (state) => state.userInfo,
|
||||
getToken: (state) => state.token,
|
||||
getTokenTime: (state) => state.tokenTime,
|
||||
getPermissions: (state) => state.permissions,
|
||||
},
|
||||
actions: {
|
||||
setUserInfo(userInfo) {
|
||||
this.userInfo = userInfo;
|
||||
},
|
||||
async setToken(token) {
|
||||
this.token = token;
|
||||
},
|
||||
async setTokenTime(tokenTime) {
|
||||
this.tokenTime = tokenTime;
|
||||
},
|
||||
setPermissions(permissions) {
|
||||
this.permissions = permissions;
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
storage: window.sessionStorage,
|
||||
},
|
||||
});
|
|
@ -0,0 +1,8 @@
|
|||
import { post } from "./axios";
|
||||
|
||||
export const Login = (params) => post("/sys/login", params); // 登录
|
||||
export const logout = (params) => post("/sys/logout", params); // 退出登录
|
||||
export const getUserInfo = (params) => post("/sys/user/info", params); // 获取用户信息
|
||||
export const setChangePassword = (params) => post("/sys/user/password", params); // 修改密码
|
||||
export const setRefreshToken = (params) =>
|
||||
post("/sys/refreshToken", { loading: false, ...params }); // 刷新token
|
|
@ -0,0 +1,147 @@
|
|||
import axios from "axios";
|
||||
import { ElLoading, ElMessage } from "element-plus";
|
||||
import router from "../router";
|
||||
import pinia from "../pinia";
|
||||
import { useUserStore } from "@/pinia/user.js";
|
||||
import refreshToken from "@/assets/js/refreshToken.js";
|
||||
import { getBaseUrl } from "@/assets/js/utils.js";
|
||||
|
||||
let loading = null;
|
||||
let isTipTokenFailure = false;
|
||||
function startLoading() {
|
||||
loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: "加载中...",
|
||||
background: "rgba(0, 0, 0, 0.5)",
|
||||
});
|
||||
}
|
||||
|
||||
function endLoading() {
|
||||
loading && loading.close();
|
||||
}
|
||||
|
||||
axios.defaults.baseURL = getBaseUrl();
|
||||
axios.defaults.timeout = 1000 * 60 * 10;
|
||||
// axios.defaults.withCredentials = true;
|
||||
axios.interceptors.request.use(
|
||||
async (config) => {
|
||||
const userStore = useUserStore(pinia);
|
||||
config.headers.Token = userStore.getToken;
|
||||
if (config.method === "get" || config.method === "GET") {
|
||||
if (config.params.loading !== false) startLoading();
|
||||
}
|
||||
if (config.method === "post" || config.method === "POST") {
|
||||
if (config.data.loading !== false) startLoading();
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
);
|
||||
|
||||
axios.interceptors.response.use(
|
||||
(config) => {
|
||||
if (config.config.method === "get" || config.config.method === "GET") {
|
||||
if (config.config.params.loading !== false) endLoading();
|
||||
}
|
||||
if (config.config.method === "post" || config.config.method === "POST") {
|
||||
if (config.config.headers["Content-Type"] === "multipart/form-data") {
|
||||
endLoading();
|
||||
} else {
|
||||
if (JSON.parse(config.config.data)?.loading !== false) endLoading();
|
||||
}
|
||||
}
|
||||
if (config.data.code === 401) {
|
||||
if (!isTipTokenFailure) {
|
||||
isTipTokenFailure = true;
|
||||
ElMessage.error("登录失效,请重新登录");
|
||||
router.push("/login").then();
|
||||
isTipTokenFailure = false;
|
||||
}
|
||||
return Promise.reject(config.data.msg);
|
||||
} else {
|
||||
refreshToken();
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
if (error && error.response) {
|
||||
error.message = `连接错误${error.response.status}`;
|
||||
import.meta.env.DEV &&
|
||||
ElMessage.error(`连接错误${error.response.status}`);
|
||||
} else {
|
||||
error.message = "连接到服务器失败";
|
||||
ElMessage.error("连接到服务器失败");
|
||||
}
|
||||
return Promise.reject(error.message);
|
||||
}
|
||||
);
|
||||
|
||||
export function post(url, params = {}) {
|
||||
const userStore = useUserStore(pinia);
|
||||
return new Promise((resolve, reject) => {
|
||||
axios
|
||||
.post(url, {
|
||||
corpinfoId: userStore.getUserInfo.corpinfoId,
|
||||
userId: userStore.getUserInfo.userId,
|
||||
...params,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.data.result === "success") {
|
||||
resolve(res.data);
|
||||
} else {
|
||||
ElMessage.error(res.data.msg || "系统开小差了");
|
||||
reject(res.data);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function get(url, params = {}) {
|
||||
const userStore = useUserStore(pinia);
|
||||
return new Promise((resolve, reject) => {
|
||||
axios
|
||||
.get(url, {
|
||||
params: {
|
||||
corpinfoId: userStore.getUserInfo.corpinfoId,
|
||||
userId: userStore.getUserInfo.userId,
|
||||
...params,
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.data.result === "success") {
|
||||
resolve(res.data);
|
||||
} else {
|
||||
ElMessage.error(res.data.msg || "系统开小差了");
|
||||
reject(res.data);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function upload(url, params = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
axios
|
||||
.post(url, params, {
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.data.result === "success") {
|
||||
resolve(res.data);
|
||||
} else {
|
||||
ElMessage.error(res.data.msg || "系统开小差了");
|
||||
reject(res.data);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,19 +1,25 @@
|
|||
import { createRouter, createWebHashHistory } from "vue-router";
|
||||
import children from "../components/children/index.vue";
|
||||
import loayout from "@/layout/index.vue";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: "/login",
|
||||
name: "/login",
|
||||
meta: { title: "登录", isLogin: false },
|
||||
component: () => import("@/views/login/index"),
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
name: "app",
|
||||
redirect: "/map",
|
||||
meta: { title: "首页", isLogin: true },
|
||||
meta: { isLogin: true },
|
||||
component: children,
|
||||
children: [
|
||||
{
|
||||
path: "/map/:type?",
|
||||
name: "/map",
|
||||
meta: {
|
||||
title: "首页",
|
||||
title: "地图",
|
||||
breadcrumb: false,
|
||||
isMenu: false,
|
||||
isSubMenu: false,
|
||||
|
@ -22,6 +28,29 @@ const routes = [
|
|||
},
|
||||
component: () => import("@/views/map/index"),
|
||||
},
|
||||
{
|
||||
path: "/backend",
|
||||
name: "backend",
|
||||
meta: {
|
||||
breadcrumb: false,
|
||||
isMenu: false,
|
||||
isSubMenu: false,
|
||||
isBack: false,
|
||||
},
|
||||
component: loayout,
|
||||
children: [
|
||||
{
|
||||
path: "/backend/index",
|
||||
meta: { title: "首页", isMenu: false },
|
||||
component: () => import("@/views/index/index"),
|
||||
},
|
||||
{
|
||||
path: "/backend/index111",
|
||||
meta: { title: "首页" },
|
||||
component: () => import("@/views/index/index"),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<template>
|
||||
<div>1</div>
|
||||
</template>
|
||||
|
||||
<script setup></script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
|
@ -0,0 +1,185 @@
|
|||
<template>
|
||||
<div class="login">
|
||||
<div class="form">
|
||||
<div class="title">登录</div>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
@submit.prevent="fnLogin"
|
||||
>
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
v-model="form.username"
|
||||
placeholder="请输入用户名"
|
||||
tabindex="1"
|
||||
>
|
||||
<template #prepend>
|
||||
<icon-people theme="filled" size="16" fill="#909399" />
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
v-model="form.password"
|
||||
type="password"
|
||||
placeholder="请输入密码"
|
||||
tabindex="2"
|
||||
>
|
||||
<template #prepend>
|
||||
<icon-lock
|
||||
theme="filled"
|
||||
size="16"
|
||||
fill="#909399"
|
||||
:stroke-width="3"
|
||||
/>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<verification v-model:verification-pass="verificationPass" />
|
||||
</el-form-item>
|
||||
<el-form-item class="button">
|
||||
<el-button native-type="submit">登录</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import Verification from "@/components/verification/index";
|
||||
import { useUserStore } from "@/pinia/user";
|
||||
import { getUserInfo, Login } from "@/request/api";
|
||||
import { debounce } from "throttle-debounce";
|
||||
import useFormValidate from "@/hooks/useFormValidate.js";
|
||||
import dayjs from "dayjs";
|
||||
import { encrypt } from "@/assets/js/aes_secret.js";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
const userStore = useUserStore();
|
||||
const router = useRouter();
|
||||
const { formRef, validate } = useFormValidate();
|
||||
const verificationPass = ref(false);
|
||||
const form = ref({
|
||||
username: "",
|
||||
password: "",
|
||||
});
|
||||
const rules = {
|
||||
username: [{ required: true, message: "请输入用户名", trigger: "blur" }],
|
||||
password: [{ required: true, message: "请输入密码", trigger: "blur" }],
|
||||
};
|
||||
const fnLogin = debounce(
|
||||
1000,
|
||||
() => {
|
||||
if (import.meta.env.DEV) {
|
||||
fnSubmitLogin();
|
||||
return;
|
||||
}
|
||||
if (verificationPass.value) {
|
||||
fnSubmitLogin();
|
||||
} else {
|
||||
ElMessage.warning("请进行登录验证");
|
||||
}
|
||||
},
|
||||
{ atBegin: true }
|
||||
);
|
||||
const fnSubmitLogin = async () => {
|
||||
await validate("请输入用户名密码");
|
||||
const { token } = await Login({
|
||||
username: encrypt(form.value.username),
|
||||
password: encrypt(form.value.password),
|
||||
});
|
||||
await userStore.setToken(token);
|
||||
await userStore.setTokenTime(dayjs().format("YYYY-MM-DD HH:mm:ss"));
|
||||
const { user } = await getUserInfo();
|
||||
userStore.setUserInfo({
|
||||
...userStore.getUserInfo,
|
||||
...user,
|
||||
});
|
||||
await router.replace("/backend/index");
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.login {
|
||||
width: 100%;
|
||||
max-width: 1920px;
|
||||
margin: 0 auto;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
|
||||
.form {
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 20px rgb(109 109 109 / 40%);
|
||||
position: absolute;
|
||||
right: 230px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 400px;
|
||||
height: 490px;
|
||||
padding: 40px 50px;
|
||||
background-color: #fff;
|
||||
z-index: 1;
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
left: -30px;
|
||||
width: calc(100% + 60px);
|
||||
height: calc(100% - 80px);
|
||||
border-radius: 10px;
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: -15px;
|
||||
width: calc(100% + 30px);
|
||||
height: calc(100% - 40px);
|
||||
border-radius: 10px;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
z-index: -2;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 20px;
|
||||
color: #000;
|
||||
line-height: 60px;
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
.el-input {
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
.el-button {
|
||||
background: #0a7dfe;
|
||||
height: 45px;
|
||||
width: 100%;
|
||||
color: #ffffff;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep {
|
||||
.el-input-group__prepend {
|
||||
padding-top: 5px !important;
|
||||
}
|
||||
|
||||
.el-carousel__indicators {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -3,6 +3,9 @@ import vue from "@vitejs/plugin-vue";
|
|||
import eslintPlugin from "vite-plugin-eslint";
|
||||
import removeConsole from "vite-plugin-remove-console";
|
||||
import { envParse } from "vite-plugin-env-parse";
|
||||
import AutoImport from "unplugin-auto-import/vite";
|
||||
import Components from "unplugin-vue-components/vite";
|
||||
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
|
||||
|
||||
export default ({ mode }) => {
|
||||
return defineConfig({
|
||||
|
@ -11,6 +14,12 @@ export default ({ mode }) => {
|
|||
vue(),
|
||||
envParse(),
|
||||
eslintPlugin(),
|
||||
AutoImport({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
Components({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
removeConsole({
|
||||
includes: [
|
||||
"assert",
|
||||
|
|
Loading…
Reference in New Issue