初始化
This commit is contained in:
9
auts_new_mobile.client/.editorconfig
Normal file
9
auts_new_mobile.client/.editorconfig
Normal file
@@ -0,0 +1,9 @@
|
||||
[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}]
|
||||
charset = utf-8
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
end_of_line = lf
|
||||
max_line_length = 100
|
||||
1
auts_new_mobile.client/.gitattributes
vendored
Normal file
1
auts_new_mobile.client/.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
* text=auto eol=lf
|
||||
30
auts_new_mobile.client/.gitignore
vendored
Normal file
30
auts_new_mobile.client/.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
||||
7
auts_new_mobile.client/.vscode/extensions.json
vendored
Normal file
7
auts_new_mobile.client/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"Vue.volar",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"EditorConfig.EditorConfig"
|
||||
]
|
||||
}
|
||||
12
auts_new_mobile.client/CHANGELOG.md
Normal file
12
auts_new_mobile.client/CHANGELOG.md
Normal file
@@ -0,0 +1,12 @@
|
||||
This file explains how Visual Studio created the project.
|
||||
|
||||
The following tools were used to generate this project:
|
||||
- create-vite
|
||||
|
||||
The following steps were used to generate this project:
|
||||
- Create vue project with create-vite: `npm init --yes vue@latest auts_new_mobile.client -- --eslint `.
|
||||
- Updating `vite.config.js` with port.
|
||||
- Create project file (`auts_new_mobile.client.esproj`).
|
||||
- Create `launch.json` to enable debugging.
|
||||
- Add project to solution.
|
||||
- Write this file.
|
||||
35
auts_new_mobile.client/README.md
Normal file
35
auts_new_mobile.client/README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# auts_new_mobile.client
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vite.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lint with [ESLint](https://eslint.org/)
|
||||
|
||||
```sh
|
||||
npm run lint
|
||||
```
|
||||
14
auts_new_mobile.client/auts_new_mobile.client.esproj
Normal file
14
auts_new_mobile.client/auts_new_mobile.client.esproj
Normal file
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.2191419">
|
||||
<PropertyGroup>
|
||||
<StartupCommand>npm run dev</StartupCommand>
|
||||
<JavaScriptTestRoot>.\</JavaScriptTestRoot>
|
||||
<JavaScriptTestFramework>Vitest</JavaScriptTestFramework>
|
||||
<!-- Allows the build (or compile) script located on package.json to run on Build -->
|
||||
<ShouldRunBuildScript>false</ShouldRunBuildScript>
|
||||
<!-- Folder where production build objects will be placed -->
|
||||
<BuildOutputFolder>$(MSBuildProjectDirectory)\dist</BuildOutputFolder>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove=".gitignore" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
26
auts_new_mobile.client/eslint.config.js
Normal file
26
auts_new_mobile.client/eslint.config.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import js from '@eslint/js'
|
||||
import pluginVue from 'eslint-plugin-vue'
|
||||
import globals from 'globals'
|
||||
|
||||
export default [
|
||||
{
|
||||
name: 'app/files-to-lint',
|
||||
files: ['**/*.{js,mjs,jsx,vue}'],
|
||||
},
|
||||
|
||||
{
|
||||
name: 'app/files-to-ignore',
|
||||
ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**'],
|
||||
},
|
||||
|
||||
{
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
js.configs.recommended,
|
||||
...pluginVue.configs['flat/essential'],
|
||||
]
|
||||
13
auts_new_mobile.client/index.html
Normal file
13
auts_new_mobile.client/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/app.svg" type="image/svg+xml">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ATUS</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
8
auts_new_mobile.client/jsconfig.json
Normal file
8
auts_new_mobile.client/jsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
5526
auts_new_mobile.client/package-lock.json
generated
Normal file
5526
auts_new_mobile.client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
37
auts_new_mobile.client/package.json
Normal file
37
auts_new_mobile.client/package.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "auts_new_mobile.client",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint . --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@vueuse/core": "^13.0.0",
|
||||
"axios": "^1.8.3",
|
||||
"dayjs": "^1.11.13",
|
||||
"element-plus": "^2.9.6",
|
||||
"qs": "^6.14.0",
|
||||
"tdesign-icons-vue-next": "^0.3.5",
|
||||
"tdesign-mobile-vue": "^1.8.2",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0",
|
||||
"vue-simple-verify": "^1.1.0",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.21.0",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"eslint": "^9.21.0",
|
||||
"eslint-plugin-vue": "~10.0.0",
|
||||
"globals": "^16.0.0",
|
||||
"unplugin-auto-import": "^19.1.1",
|
||||
"unplugin-vue-components": "^28.4.1",
|
||||
"vite": "^6.2.1",
|
||||
"vite-plugin-vue-devtools": "^7.7.2"
|
||||
}
|
||||
}
|
||||
1
auts_new_mobile.client/public/app.svg
Normal file
1
auts_new_mobile.client/public/app.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1742891293672" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8226" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M714.410667 374.528c-3.84-23.509333-15.146667-33.834667-38.997334-35.797333a208.768 208.768 0 0 0-34.261333 0.042666c-76.330667 6.357333-130.517333-89.130667-86.314667-150.357333 6.997333-9.770667 12.842667-20.522667 18.005334-31.402667 4.266667-9.002667 8.064-18.901333 2.517333-28.970666-16.256-29.354667-43.776-43.306667-74.666667-48.938667-27.008-4.949333-35.541333 18.816-46.378666 37.12-5.461333 9.258667-10.666667 18.688-17.024 27.349333-11.52 15.573333-26.496 26.154667-45.568 31.232-53.034667 14.208-93.482667-4.096-121.898667-55.850666-25.856-46.976-43.605333-51.669333-89.514667-23.466667a159.573333 159.573333 0 0 0-15.018666 10.154667c-21.973333 16.981333-25.6 33.578667-12.501334 57.898666 7.637333 14.165333 16.896 27.562667 23.338667 42.24 24.192 55.04-16.341333 125.056-75.989333 131.328-17.28 1.834667-34.858667 0.682667-52.266667 1.493334-16.554667 0.725333-30.805333 6.485333-36.266667 23.594666a123.648 123.648 0 0 0-0.213333 75.306667 31.317333 31.317333 0 0 0 29.738667 23.338667c9.344 0.554667 18.730667 0.085333 28.16 0.085333 32.64-0.725333 62.762667 4.181333 85.930666 30.72 34.218667 39.168 37.418667 77.738667 9.344 125.568-29.952 51.242667-24.661333 68.48 29.312 95.104 6.016 2.816 12.288 5.333333 18.56 7.594667 17.621333 6.485333 32 1.066667 42.368-13.568 8.021333-11.562667 15.232-23.850667 21.504-36.352 32.341333-64.426667 137.258667-64.512 169.770667-0.512 6.357333 12.586667 13.269333 25.088 21.546667 36.352 13.909333 18.901333 28.928 22.229333 53.333333 9.898666 25.088-12.586667 55.637333-22.016 56.234667-58.154666 0.085333-9.813333-6.229333-18.901333-11.434667-27.648-6.4-11.008-13.44-21.76-18.688-33.322667-24.533333-54.229333 12.586667-122.752 71.338667-133.034667 18.688-3.285333 37.504-0.682667 56.234666-1.962666 24.021333-1.664 35.498667-11.178667 39.68-34.858667 3.029333-17.28 2.944-34.944 0.085334-52.224z m-351.658667 166.997333c-79.146667 0.298667-142.677333-62.293333-142.890667-140.8a140.629333 140.629333 0 0 1 141.994667-141.44 140.202667 140.202667 0 0 1 140.714667 140.501334c0.170667 78.08-62.250667 141.397333-139.818667 141.738666z m655.317333 112.554667a92.416 92.416 0 0 0-9.685333-32.768c-7.168-14.421333-17.92-20.650667-34.517333-16.170667-14.165333 3.925333-27.733333 10.325333-41.984 9.685334-45.098667 0.896-76.330667-44.330667-63.744-90.88 9.514667-34.944 3.584-46.165333-28.928-54.698667-32.981333-8.661333-45.184-1.109333-52.565334 32.426667l-1.834666 7.850666c-13.482667 53.12-73.173333 70.869333-113.749334 33.749334-6.4-5.845333-12.672-11.989333-19.157333-17.792-9.173333-8.149333-19.157333-8.32-29.909333-3.029334-38.101333 18.901333-43.648 49.92-13.397334 78.506667 17.493333 16.682667 28.416 34.986667 24.746667 60.416-5.162667 34.602667-25.429333 52.565333-58.24 60.842667-33.066667 8.405333-38.869333 20.352-29.269333 52.821333 9.685333 32.256 21.333333 38.912 53.504 28.330667 27.52-9.002667 51.84-8.661333 74.581333 12.586666 24.064 22.485333 25.685333 48.341333 17.578667 77.482667-3.157333 11.349333-9.344 24.405333 1.066666 34.346667 15.829333 15.146667 36.096 20.181333 57.514667 17.578666 19.072-2.432 20.096-19.072 23.765333-33.237333 1.066667-4.565333 2.176-9.173333 3.498667-13.653333a67.328 67.328 0 0 1 109.994667-31.402667c7.082667 6.186667 13.397333 13.269333 20.565333 19.328 9.002667 7.68 18.901333 9.258667 29.994667 4.010667 38.912-18.688 44.501333-50.517333 13.482666-79.744l-2.901333-2.730667c-39.338667-38.485333-24.32-99.157333 28.416-114.730667 6.4-1.92 12.842667-3.925333 19.242667-5.674666 17.536-4.693333 23.509333-16.597333 21.930666-33.450667z m-237.482666 149.418667c-52.736 0.896-97.493333-43.562667-97.578667-96.981334-0.085333-53.589333 42.837333-96.768 97.237333-97.664 52.437333-0.896 97.237333 43.648 97.664 97.152 0.512 53.248-42.752 96.426667-97.322666 97.493334z" fill="#008B00" p-id="8227"></path></svg>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
8
auts_new_mobile.client/public/config.js
Normal file
8
auts_new_mobile.client/public/config.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const config = {
|
||||
// http访问后端接口 | prod | 生产
|
||||
httpApi: "http://new.uts-data.com:6688/api/",
|
||||
// http访问后端接口 | dev | 开发环境
|
||||
//httpApi: "http://localhost:23553/api/",
|
||||
}
|
||||
|
||||
export default config
|
||||
137
auts_new_mobile.client/src/App.vue
Normal file
137
auts_new_mobile.client/src/App.vue
Normal file
@@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<layout v-if="alreadyLogin" style="z-index: 999999; position: relative;"></layout>
|
||||
<div v-if="alreadyLogin" style="height:44px; " />
|
||||
<router-view style="min-height: 85vh;"></router-view>
|
||||
<t-footer text="Copyright © 2025 AUTS All Rights Reserved --Version0.0.3" />
|
||||
</template>
|
||||
<script>
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<script setup>
|
||||
import layout from '../src/pages/layout/index.vue'
|
||||
import { ref, onMounted, onBeforeUnmount, watch, provide, watchEffect } from 'vue'
|
||||
import { Toast } from 'tdesign-mobile-vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
setTimeout(() => {
|
||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
webToDark(true);
|
||||
} else {
|
||||
webToDark(false);
|
||||
}
|
||||
}, 0);
|
||||
// 切换主题
|
||||
const isDarkMode = ref(false)
|
||||
const webToDark = (dark) => {
|
||||
if (dark) {
|
||||
document.documentElement.setAttribute('theme-mode', 'dark')
|
||||
isDarkMode.value = true
|
||||
} else {
|
||||
document.documentElement.removeAttribute('theme-mode')
|
||||
isDarkMode.value = true
|
||||
}
|
||||
}
|
||||
const toggleTheme = () => {
|
||||
isDarkMode.value = !isDarkMode.value
|
||||
let root = document.documentElement;
|
||||
|
||||
if (!isDarkMode.value) {
|
||||
// 如果当前是暗色主题,切换到亮色主题
|
||||
root.style.setProperty('--color-background', '#ffffff');
|
||||
root.style.setProperty('--color-background-soft', '#f8f8f8');
|
||||
root.style.setProperty('--color-background-mute', '#f2f2f2');
|
||||
root.style.setProperty('--color-border', 'rgba(60, 60, 60, 0.12)');
|
||||
root.style.setProperty('--color-border-hover', 'rgba(60, 60, 60, 0.29)');
|
||||
root.style.setProperty('--color-heading', '#2c3e50');
|
||||
root.style.setProperty('--color-text', '#2c3e50');
|
||||
root.setAttribute('data-theme', 'light');
|
||||
setTimeout(() => {
|
||||
root.removeAttribute('theme-mode');
|
||||
}, 20);
|
||||
} else {
|
||||
// 如果当前是亮色主题,切换到暗色主题
|
||||
root.style.setProperty('--color-background', '#242424');
|
||||
root.style.setProperty('--color-background-soft', '#222222');
|
||||
root.style.setProperty('--color-background-mute', '#282828');
|
||||
root.style.setProperty('--color-border', 'rgba(84, 84, 84, 0.48)');
|
||||
root.style.setProperty('--color-border-hover', 'rgba(84, 84, 84, 0.65)');
|
||||
root.style.setProperty('--color-heading', '#ffffff');
|
||||
root.style.setProperty('--color-text', 'rgba(235, 235, 235, 0.64)');
|
||||
root.setAttribute('data-theme', 'dark');
|
||||
setTimeout(() => {
|
||||
root.setAttribute('theme-mode', 'dark');
|
||||
}, 20);
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
// 初始检查登录状态
|
||||
checkLoginStatus()
|
||||
|
||||
// 主题初始化
|
||||
const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
const handleColorSchemeChange = (e) => {
|
||||
webToDark(e.matches)
|
||||
}
|
||||
|
||||
// 初始设置
|
||||
webToDark(darkModeQuery.matches)
|
||||
|
||||
// 监听系统主题变化
|
||||
darkModeQuery.addEventListener('change', handleColorSchemeChange)
|
||||
|
||||
// 清理监听器
|
||||
return () => {
|
||||
darkModeQuery.removeEventListener('change', handleColorSchemeChange)
|
||||
}
|
||||
})
|
||||
const alreadyLogin = ref(false)
|
||||
// 初始化检查登录状态
|
||||
const checkLoginStatus = () => {
|
||||
alreadyLogin.value = localStorage.getItem('login') == 'true'
|
||||
setTimeout(() => {
|
||||
if (alreadyLogin.value) {
|
||||
//router.push('/home')
|
||||
} else {
|
||||
router.push('/login')
|
||||
}
|
||||
}, 6);
|
||||
}
|
||||
provide('checkLoginStatus', checkLoginStatus)
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
/* 按钮样式优化 */
|
||||
.t-button {
|
||||
margin-top: 5px;
|
||||
height: 40px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.t-checkbox--block {
|
||||
padding: 0;
|
||||
}
|
||||
/* 主题切换按钮 */
|
||||
.theme-toggle {
|
||||
position: fixed;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
padding: 8px 16px;
|
||||
background: var(--toggle-bg);
|
||||
color: var(--toggle-text);
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
|
||||
/* :deep .t-footer__text {
|
||||
bottom: 2px;
|
||||
position: relative;
|
||||
}*/
|
||||
</style>
|
||||
86
auts_new_mobile.client/src/assets/base.css
Normal file
86
auts_new_mobile.client/src/assets/base.css
Normal file
@@ -0,0 +1,86 @@
|
||||
/* color palette from <https://github.com/vuejs/theme> */
|
||||
:root {
|
||||
--vt-c-white: #ffffff;
|
||||
--vt-c-white-soft: #f8f8f8;
|
||||
--vt-c-white-mute: #f2f2f2;
|
||||
|
||||
--vt-c-black: #242424;
|
||||
--vt-c-black-soft: #222222;
|
||||
--vt-c-black-mute: #282828;
|
||||
|
||||
--vt-c-indigo: #2c3e50;
|
||||
|
||||
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
|
||||
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
|
||||
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
|
||||
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
|
||||
|
||||
--vt-c-text-light-1: var(--vt-c-indigo);
|
||||
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
|
||||
--vt-c-text-dark-1: var(--vt-c-white);
|
||||
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
|
||||
}
|
||||
|
||||
/* semantic color variables for this project */
|
||||
:root {
|
||||
--color-background: var(--vt-c-white);
|
||||
--color-background-soft: var(--vt-c-white-soft);
|
||||
--color-background-mute: var(--vt-c-white-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-light-2);
|
||||
--color-border-hover: var(--vt-c-divider-light-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-light-1);
|
||||
--color-text: var(--vt-c-text-light-1);
|
||||
|
||||
--section-gap: 160px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-background: var(--vt-c-black);
|
||||
--color-background-soft: var(--vt-c-black-soft);
|
||||
--color-background-mute: var(--vt-c-black-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-dark-2);
|
||||
--color-border-hover: var(--vt-c-divider-dark-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-dark-1);
|
||||
--color-text: var(--vt-c-text-dark-2);
|
||||
}
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
color: var(--color-text);
|
||||
background: var(--color-background);
|
||||
transition:
|
||||
color 0.5s,
|
||||
background-color 0.5s;
|
||||
line-height: 1.6;
|
||||
font-family:
|
||||
Inter,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
'Fira Sans',
|
||||
'Droid Sans',
|
||||
'Helvetica Neue',
|
||||
sans-serif;
|
||||
font-size: 15px;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
1
auts_new_mobile.client/src/assets/logo.svg
Normal file
1
auts_new_mobile.client/src/assets/logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
||||
|
After Width: | Height: | Size: 276 B |
34
auts_new_mobile.client/src/assets/main.css
Normal file
34
auts_new_mobile.client/src/assets/main.css
Normal file
@@ -0,0 +1,34 @@
|
||||
@import './base.css';
|
||||
|
||||
#app {
|
||||
margin: 0 auto;
|
||||
padding: 5px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
a,
|
||||
.green {
|
||||
text-decoration: none;
|
||||
color: hsla(160, 100%, 37%, 1);
|
||||
transition: 0.4s;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
a:hover {
|
||||
background-color: hsla(160, 100%, 37%, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
/*@media (min-width: 1024px) {
|
||||
body {*/
|
||||
/*display: flex;*/
|
||||
/*place-items: center;
|
||||
}
|
||||
|
||||
#app {
|
||||
display: grid;*/
|
||||
/*grid-template-columns: 1fr 1fr;*/
|
||||
/*padding: 0 5px;
|
||||
}
|
||||
}*/
|
||||
129
auts_new_mobile.client/src/axios.js
Normal file
129
auts_new_mobile.client/src/axios.js
Normal file
@@ -0,0 +1,129 @@
|
||||
import axios from 'axios';
|
||||
import config from '../public/config.js';
|
||||
import { Toast } from 'tdesign-mobile-vue'
|
||||
|
||||
const instance = axios.create({
|
||||
baseURL: config.httpApi, // 设置基础URL
|
||||
timeout: 60000
|
||||
});
|
||||
|
||||
// 定义Token刷新状态变量
|
||||
let isRefreshing = false;
|
||||
let requestQueue = [];
|
||||
|
||||
// 添加请求拦截器
|
||||
instance.interceptors.request.use(config => {
|
||||
config.headers['Content-Type'] = 'application/json';
|
||||
|
||||
if (config.data && typeof config.data === 'object') {
|
||||
config.data = JSON.stringify(config.data);
|
||||
}
|
||||
|
||||
// 获取URL最后一段
|
||||
const url = window.location.href;
|
||||
const lastSegment = url.substring(url.lastIndexOf('/'));
|
||||
|
||||
// 如果不是登录请求
|
||||
if (config.url !== 'Login/Login') {
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
config.headers['Authorization'] = `Bearer ${token}`;
|
||||
} else if (lastSegment != '/login') {
|
||||
handleTokenExpiration();
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}, error => {
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
// 添加响应拦截器
|
||||
instance.interceptors.response.use(response => {
|
||||
return response;
|
||||
}, async error => {
|
||||
const originalRequest = error.config;
|
||||
|
||||
// 检测到Token过期(401错误)
|
||||
if (error.response && error.response.status === 401 && !originalRequest._retry) {
|
||||
|
||||
// 防止重复刷新
|
||||
if (!isRefreshing) {
|
||||
isRefreshing = true;
|
||||
originalRequest._retry = true;
|
||||
|
||||
try {
|
||||
// 尝试刷新Token
|
||||
const newToken = await refreshToken();
|
||||
|
||||
// 用新Token重试原始请求
|
||||
originalRequest.headers['Authorization'] = `Bearer ${newToken}`;
|
||||
|
||||
// 处理队列中等待的请求
|
||||
requestQueue.forEach(subscriber => subscriber(newToken));
|
||||
requestQueue = [];
|
||||
|
||||
return instance(originalRequest);
|
||||
} catch (refreshError) {
|
||||
// 刷新失败处理
|
||||
handleTokenExpiration();
|
||||
return Promise.reject(refreshError);
|
||||
} finally {
|
||||
isRefreshing = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果正在刷新Token,将请求加入队列
|
||||
return new Promise((resolve, reject) => {
|
||||
requestQueue.push((token) => {
|
||||
originalRequest.headers['Authorization'] = `Bearer ${token}`;
|
||||
resolve(instance(originalRequest));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
export default instance;
|
||||
|
||||
// Token刷新方法 (优化版)
|
||||
async function refreshToken() {
|
||||
try {
|
||||
const username = localStorage.getItem('rememberedUsername');
|
||||
const password = localStorage.getItem('rememberedPassword');
|
||||
|
||||
if (!username || !password) {
|
||||
throw new Error('登录凭证已过期');
|
||||
}
|
||||
|
||||
const response = await instance.post('Login/Login', {
|
||||
username,
|
||||
password
|
||||
});
|
||||
|
||||
if (response.data.isok) {
|
||||
const newToken = response.data.response.accessToken;
|
||||
localStorage.setItem('token', newToken);
|
||||
return newToken;
|
||||
} else {
|
||||
throw new Error('Token更新失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Token刷新失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Token过期处理
|
||||
function handleTokenExpiration() {
|
||||
Toast({
|
||||
theme: 'error',
|
||||
direction: 'column',
|
||||
message: '登录已过期,请重新登录',
|
||||
});
|
||||
setTimeout(() => {
|
||||
localStorage.removeItem('token');
|
||||
window.location.href = '/login';
|
||||
}, 1000);
|
||||
}
|
||||
65
auts_new_mobile.client/src/components/SliderCaptcha.vue
Normal file
65
auts_new_mobile.client/src/components/SliderCaptcha.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div class="slider-captcha">
|
||||
<div class="slider-bg"></div>
|
||||
<div class="slider-icon" :style="{ left: x + 'px' }" @mousedown="onDragStart"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
x: 0, // 滑块的x坐标
|
||||
startX: 0, // 鼠标按下时的x坐标
|
||||
isDragging: false // 是否正在拖动滑块
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onDragStart(e) {
|
||||
this.startX = e.clientX;
|
||||
this.isDragging = true;
|
||||
document.addEventListener('mousemove', this.onDragging);
|
||||
document.addEventListener('mouseup', this.onDragEnd);
|
||||
},
|
||||
onDragging(e) {
|
||||
if (this.isDragging) {
|
||||
const offsetX = e.clientX - this.startX;
|
||||
this.x = Math.max(0, Math.min(200, offsetX));
|
||||
}
|
||||
},
|
||||
onDragEnd() {
|
||||
if (this.isDragging) {
|
||||
this.isDragging = false;
|
||||
document.removeEventListener('mousemove', this.onDragging);
|
||||
document.removeEventListener('mouseup', this.onDragEnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.slider-captcha {
|
||||
position: relative;
|
||||
width: 200px;
|
||||
height: 50px;
|
||||
border: 1px solid #ccc;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.slider-bg {
|
||||
width: 200px;
|
||||
height: 50px;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.slider-icon {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background-color: #3498db;
|
||||
cursor: move;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
|
||||
<path
|
||||
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
19
auts_new_mobile.client/src/components/icons/IconTooling.vue
Normal file
19
auts_new_mobile.client/src/components/icons/IconTooling.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
class="iconify iconify--mdi"
|
||||
width="24"
|
||||
height="24"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
29
auts_new_mobile.client/src/main.js
Normal file
29
auts_new_mobile.client/src/main.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import config from '../public/config.js';
|
||||
import axios from './axios' // 导入配置好的实例
|
||||
import TDesign from 'tdesign-mobile-vue'
|
||||
import './assets/main.css'
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
import 'element-plus/theme-chalk/dark/css-vars.css'
|
||||
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
|
||||
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||
|
||||
const app = createApp(App)
|
||||
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||||
app.component(key, component)
|
||||
}
|
||||
// 挂载到全局属性
|
||||
app.config.globalProperties.$http = axios
|
||||
|
||||
// 提供依赖注入
|
||||
app.provide('config', config)
|
||||
app.provide('$http', axios)
|
||||
|
||||
app.use(router)
|
||||
app.use(TDesign)
|
||||
app.use(ElementPlus, { locale: zhCn })
|
||||
app.use(ElementPlus)
|
||||
app.mount('#app')
|
||||
556
auts_new_mobile.client/src/pages/dbmanage/index.vue
Normal file
556
auts_new_mobile.client/src/pages/dbmanage/index.vue
Normal file
@@ -0,0 +1,556 @@
|
||||
<template>
|
||||
<div style="padding-bottom:5px">
|
||||
<el-tag type="info">可编辑 / 总字段数</el-tag>
|
||||
<span> </span>
|
||||
<el-tag type="success">{{ tableNames.length }} / {{ allTableCount }}</el-tag>
|
||||
<span> </span>
|
||||
<el-select v-model="dbradio"
|
||||
placeholder="选择数据表"
|
||||
size="small"
|
||||
@change="dbchange"
|
||||
style="width: 120px">
|
||||
<el-option v-for="item in dblist"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value" />
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="container">
|
||||
<el-table :data="tableNames" border style="width: 100%; z-index: 0;" empty-text="暂无可变更字段">
|
||||
<el-table-column label="操作" width="75">
|
||||
<template #default="scope">
|
||||
<div style="display: flex; align-items: center">
|
||||
<t-button variant="text"
|
||||
shape="square"
|
||||
size="extra-small"
|
||||
v-if="!scope.row.del"
|
||||
@click="openUpdateDialog(scope.row)">
|
||||
<template #icon>
|
||||
<t-icon size="20px" name="task-checked-1" v-if="scope.row.edit" />
|
||||
<t-icon size="20px" name="edit" v-else />
|
||||
</template>
|
||||
</t-button>
|
||||
<span v-if="!scope.row.del" style="font-size:10px">|</span>
|
||||
<t-button variant="text"
|
||||
shape="square"
|
||||
size="extra-small"
|
||||
v-if="!scope.row.del"
|
||||
@click="openDelDialog(scope.row)">
|
||||
<template #icon>
|
||||
<t-icon size="20px" name="delete-1" />
|
||||
</template>
|
||||
</t-button>
|
||||
<t-button variant="text"
|
||||
shape="square"
|
||||
size="extra-small"
|
||||
v-if="scope.row.del"
|
||||
@click="retrunDelDialog(scope.row)">
|
||||
<template #icon>
|
||||
<t-icon size="20px" name="rollback" />
|
||||
</template>
|
||||
</t-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="字段名" width="123">
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.edit">
|
||||
<t-input placeholder="字段名" v-model="scope.row.name" style="padding:1px;height:33px" borderless />
|
||||
</div>
|
||||
<div v-else-if="!scope.row.id" style="color:green"><strong>{{scope.row.name}}</strong></div>
|
||||
<div v-else-if="scope.row.del" style="color:gray"><del>{{scope.row.name}}</del></div>
|
||||
<div v-else-if="scope.row.old" style="color:blue"><u>{{scope.row.name}}</u></div>
|
||||
<div v-else>{{scope.row.name}}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" width="140">
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.edit">
|
||||
<t-input placeholder="备注" v-model="scope.row.notes" style="padding:1px;height:33px" borderless />
|
||||
</div>
|
||||
<div v-else>
|
||||
{{scope.row.notes}}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型" width="100">
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.edit">
|
||||
<!--<t-input placeholder="类型" v-model="scope.row.type" style="padding:1px;height:40px" borderless />-->
|
||||
<el-select v-model="scope.row.type">
|
||||
<el-option v-for="item in typeList" :key="item" :label="item" :value="item" placeholder="类型" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div v-else>
|
||||
{{scope.row.type}}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="长度" width="75">
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.edit">
|
||||
<div v-if="scope.row.type == 'varchar'">
|
||||
<t-input placeholder="长度" v-model="scope.row.long" style="padding:1px;height:33px" borderless type="number" />
|
||||
</div>
|
||||
<div v-else>\</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span v-if="scope.row.long">{{scope.row.long }}</span>
|
||||
<span v-else>\</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
</div>
|
||||
<!-- 新增 悬浮按钮 -->
|
||||
<t-fab :icon="iconFuncAdd"
|
||||
draggable="true"
|
||||
style="bottom: 100px;"
|
||||
@click="openCreateDialog"
|
||||
class="fab-button" />
|
||||
<!-- <t-fab :icon="iconFuncSave"
|
||||
draggable="true"
|
||||
style="bottom: 45px;"
|
||||
@click="saveDialog"
|
||||
class="fab-button" />-->
|
||||
<!-- 确认删除对话框 -->
|
||||
<t-dialog v-model:visible="delDialogVisible"
|
||||
cancel-btn="取消"
|
||||
:confirm-btn="{ content: '确认删除', theme: 'danger' }"
|
||||
@confirm="delRow">
|
||||
<!--<div v-if="currentTable.id">是否确认加入删除清单?(可撤回)</div>
|
||||
<div v-if="!currentTable.id">是否确认永久删除?(不可撤销)</div>-->
|
||||
<div>是否确认永久删除?(不可撤销)</div>
|
||||
</t-dialog>
|
||||
<t-dialog v-model:visible="newDialogVisible"
|
||||
cancel-btn="取消"
|
||||
:confirm-btn="{ content: '确认保存', theme: 'danger' }"
|
||||
@confirm="saveDialog">
|
||||
<!--<div v-if="currentTable.id">是否确认加入删除清单?(可撤回)</div>
|
||||
<div v-if="!currentTable.id">是否确认永久删除?(不可撤销)</div>-->
|
||||
<div>是否确认保存修改?(不可撤销)</div>
|
||||
</t-dialog>
|
||||
<!-- 保存确认 -->
|
||||
<t-dialog v-model:visible="editDialogVisible"
|
||||
cancel-btn="取消"
|
||||
:confirm-btn="{ content: '确认提交', theme: 'danger' }"
|
||||
@confirm="saveChanges">
|
||||
<div style="color:red;font-size:18px">操作不可逆, 是否确定提交?</div>
|
||||
<br />
|
||||
<!-- 新增 -->
|
||||
<div>
|
||||
<div style="color:blue" v-if="addList.length > 0">新增字段</div>
|
||||
<!--<div style="color:blue" v-else>无新增字段</div>-->
|
||||
<span v-for="item in addList" style="white-space: nowrap;">{{ item.name }} </span>
|
||||
</div>
|
||||
|
||||
<!-- 修改 -->
|
||||
<div>
|
||||
<div style="color:green" v-if="updateList.length > 0">修改字段</div>
|
||||
<!--<div style="color:green" v-else>无修改字段</div>-->
|
||||
<span v-for="item in updateList" style="white-space: nowrap;">{{ item.name }} </span>
|
||||
</div>
|
||||
|
||||
<!-- 删除 -->
|
||||
<div>
|
||||
<div style="color:gray" v-if="delList.length > 0">删除字段</div>
|
||||
<!--<div style="color:gray" v-else>无删除字段</div>-->
|
||||
<span v-for="item in delList" style="white-space: nowrap;">{{ item.name }} </span>
|
||||
</div>
|
||||
|
||||
</t-dialog>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, inject, h } from 'vue';
|
||||
import { Toast } from 'tdesign-mobile-vue';
|
||||
import { AddIcon, SaveIcon } from 'tdesign-icons-vue-next';
|
||||
|
||||
const $http = inject('$http')
|
||||
// 初始化加载数据
|
||||
onMounted(() => {
|
||||
getTable()
|
||||
});
|
||||
const allTableCount = ref(0)
|
||||
|
||||
const dbradio = ref('tbl_snlist');
|
||||
const dblist = ref([
|
||||
{
|
||||
value: "tbl_snlist",
|
||||
label: "snlist"
|
||||
},
|
||||
{
|
||||
value: "tbl_importinfo",
|
||||
label: "importinfo"
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
const dbchange = (val) => {
|
||||
dbradio.value = val
|
||||
getTable()
|
||||
}
|
||||
|
||||
const typeList = ref([
|
||||
"varchar",
|
||||
"int",
|
||||
"tinyint",
|
||||
"bigint",
|
||||
"double",
|
||||
"datetime",
|
||||
"text",
|
||||
])
|
||||
const iconFuncAdd = () => h(AddIcon, { size: '24px' });
|
||||
const iconFuncSave = () => h(SaveIcon, { size: '24px' });
|
||||
const tableNames = ref([]);
|
||||
var currentTable = reactive({
|
||||
id: null,
|
||||
name: '',
|
||||
notes: '',
|
||||
type: '',
|
||||
long: null,
|
||||
old: null,
|
||||
del: false,
|
||||
edit: false,
|
||||
})
|
||||
|
||||
|
||||
// 获取数据库表
|
||||
const getTable = () => {
|
||||
let requestBody = {
|
||||
key: "getTable",
|
||||
CmdType: "sys",
|
||||
DBQueryData: [
|
||||
{
|
||||
key: "@DBName",
|
||||
value: localStorage.getItem('selectdb'),
|
||||
valueType: "string",
|
||||
},
|
||||
{
|
||||
key: "@TBName",
|
||||
value: dbradio.value,
|
||||
valueType: "string",
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const dbtb = localStorage.getItem('selectdb')
|
||||
//console.log(requestBody)
|
||||
$http.post('DataHandle/R_InfoCommon', requestBody)
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
allTableCount.value = rs.data.response.length
|
||||
tableNames.value = []
|
||||
let tbexclude = 0
|
||||
if (dbradio.value == 'tbl_snlist') {
|
||||
tbexclude = 34
|
||||
} else if (dbradio.value == 'tbl_importinfo') {
|
||||
tbexclude = 9
|
||||
}
|
||||
rs.data.response.forEach((item, index) => {
|
||||
if (index >= tbexclude) {
|
||||
tableNames.value.push({
|
||||
id: index + 1,
|
||||
name: item.name,
|
||||
type: item.type,
|
||||
notes: item.notes,
|
||||
long: Object.prototype.toString.call(item.long) === '[object Number]' ? item.long : null,
|
||||
edit: false,
|
||||
old: null,
|
||||
del: false,
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
//console.log(tableNames.value)
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
// 确认保存
|
||||
const saveDialog = () => {
|
||||
addList.value = []
|
||||
delList.value = []
|
||||
updateList.value = []
|
||||
tableNames.value.forEach((item, index) => {
|
||||
if (item.id == null) { // 新增
|
||||
addList.value.push(item)
|
||||
//addUts(item)
|
||||
} else if (item.del) { // 删除
|
||||
delList.value.push(item)
|
||||
//delUts(item)
|
||||
} else if (item.old) { // 更新
|
||||
updateList.value.push(item)
|
||||
//updateUts(item)
|
||||
}
|
||||
});
|
||||
editDialogVisible.value = true
|
||||
}
|
||||
|
||||
const editDialogVisible = ref(false);
|
||||
// 新增行
|
||||
const openCreateDialog = () => {
|
||||
tableNames.value.push({
|
||||
id: null,
|
||||
name: '',
|
||||
type: 'varchar',
|
||||
notes: '',
|
||||
long: 255,
|
||||
edit: true,
|
||||
old: null,
|
||||
del: false,
|
||||
})
|
||||
window.scrollTo({
|
||||
top: document.body.scrollHeight,
|
||||
behavior: 'smooth' // 可选,用于平滑滚动
|
||||
});
|
||||
setTimeout(() => {
|
||||
window.scrollTo({
|
||||
top: document.body.scrollHeight,
|
||||
behavior: 'smooth' // 可选,用于平滑滚动
|
||||
});
|
||||
}, 233)
|
||||
};
|
||||
// 打开修改
|
||||
const openUpdateDialog = (row) => {
|
||||
//console.log(row)
|
||||
if (row.id != null) {
|
||||
if (row.edit) {
|
||||
if (row.old.name == row.name && row.old.type == row.type && row.old.notes == row.notes && row.old.long == row.long) {
|
||||
row.old = null
|
||||
}
|
||||
} else {
|
||||
row.old = JSON.parse(JSON.stringify(row))
|
||||
}
|
||||
}
|
||||
if (row.edit) {
|
||||
newDialogVisible.value = true
|
||||
}
|
||||
// 切换编辑模式
|
||||
row.edit = !row.edit;
|
||||
};
|
||||
|
||||
const delDialogVisible = ref(false);
|
||||
const newDialogVisible = ref(false);
|
||||
// 打开删除确认
|
||||
const openDelDialog = (row) => {
|
||||
currentTable = {}
|
||||
//console.log(row)
|
||||
currentTable = row
|
||||
delDialogVisible.value = true
|
||||
row.edit = false
|
||||
}
|
||||
// 确认删除行
|
||||
const delRow = () => {
|
||||
currentTable.del = true
|
||||
let delindex = -1
|
||||
tableNames.value.forEach((item, index) => {
|
||||
if (currentTable == item && currentTable.id == null) {
|
||||
delindex = index
|
||||
}
|
||||
});
|
||||
if (delindex != -1) {
|
||||
tableNames.value.splice(delindex, 1); // 使用 splice 来删除元素
|
||||
}
|
||||
saveDialog()
|
||||
}
|
||||
|
||||
// 撤回删除行
|
||||
const retrunDelDialog = (row) => {
|
||||
row.del = false
|
||||
}
|
||||
|
||||
const updateList = ref([])
|
||||
const addList = ref([])
|
||||
const delList = ref([])
|
||||
|
||||
// 保存修改
|
||||
const saveChanges = (row) => {
|
||||
// 这里实现保存逻辑,发送请求到服务器
|
||||
addList.value.forEach(item => {
|
||||
addUts(item)
|
||||
})
|
||||
delList.value.forEach(item => {
|
||||
delUts(item)
|
||||
})
|
||||
updateList.value.forEach(item => {
|
||||
updateUts(item)
|
||||
})
|
||||
Toast('操作完成!')
|
||||
tableNames.value = []
|
||||
setTimeout(() => {
|
||||
getTable()
|
||||
}, 555)
|
||||
|
||||
}
|
||||
|
||||
// 新增
|
||||
const addUts = (item) => {
|
||||
//console.log(item)
|
||||
let requestBody = {
|
||||
key: "addTable",
|
||||
CmdType: "sys",
|
||||
DBQueryData: [{
|
||||
key: "@dbName",
|
||||
value: localStorage.getItem('selectdb'),
|
||||
valueType: "string",
|
||||
}, {
|
||||
key: "@TBName",
|
||||
value: dbradio.value,
|
||||
valueType: "string",
|
||||
}, {
|
||||
key: "@name",
|
||||
value: item.name,
|
||||
valueType: "string",
|
||||
}, {
|
||||
key: "@type",
|
||||
value: item.type,
|
||||
valueType: "string",
|
||||
}, {
|
||||
key: "@long",
|
||||
value: item.long ? "(" + item.long + ")" : "",
|
||||
valueType: "string",
|
||||
}, {
|
||||
key: "@notes",
|
||||
value: item.notes,
|
||||
valueType: "string",
|
||||
},]
|
||||
}
|
||||
$http.post('DataHandle/CUD_InfoCommon', requestBody)
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
}
|
||||
// 删除
|
||||
const delUts = (item) => {
|
||||
let requestBody = {
|
||||
key: "deleteTable",
|
||||
CmdType: "sys",
|
||||
DBQueryData: [{
|
||||
key: "@dbName",
|
||||
value: localStorage.getItem('selectdb'),
|
||||
valueType: "string",
|
||||
}, {
|
||||
key: "@TBName",
|
||||
value: dbradio.value,
|
||||
valueType: "string",
|
||||
}, {
|
||||
key: "@name",
|
||||
value: item.name,
|
||||
valueType: "string",
|
||||
}]
|
||||
}
|
||||
$http.post('DataHandle/CUD_InfoCommon', requestBody)
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
}
|
||||
// 更新
|
||||
const updateUts = (item) => {
|
||||
let requestBody = {
|
||||
key: "updateTable",
|
||||
CmdType: "sys",
|
||||
DBQueryData: [{
|
||||
key: "@dbName",
|
||||
value: localStorage.getItem('selectdb'),
|
||||
valueType: "string",
|
||||
}, {
|
||||
key: "@TBName",
|
||||
value: dbradio.value,
|
||||
valueType: "string",
|
||||
}, {
|
||||
key: "@name",
|
||||
value: item.name,
|
||||
valueType: "string",
|
||||
}, {
|
||||
key: "@oldName",
|
||||
value: item.old.name,
|
||||
valueType: "string",
|
||||
}, {
|
||||
key: "@type",
|
||||
value: item.type,
|
||||
valueType: "string",
|
||||
}, {
|
||||
key: "@long",
|
||||
value: item.type == 'varchar' ? (item.long ? '(' + item.long + ')' : '(255)') : '',
|
||||
valueType: "string",
|
||||
}, {
|
||||
key: "@notes",
|
||||
value: item.notes,
|
||||
valueType: "string",
|
||||
},]
|
||||
}
|
||||
$http.post('DataHandle/CUD_InfoCommon', requestBody)
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
min-height: 85vh;
|
||||
}
|
||||
|
||||
.fab-button {
|
||||
position: fixed;
|
||||
right: 16px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* PC端样式 */
|
||||
@media (min-width: 1024px) {
|
||||
.container {
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
41
auts_new_mobile.client/src/pages/home/index.vue
Normal file
41
auts_new_mobile.client/src/pages/home/index.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div>Home</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, inject, onMounted } from 'vue';
|
||||
const $http = inject('$http')
|
||||
// 注入方法
|
||||
const checkLoginStatus = inject('checkLoginStatus');
|
||||
checkLoginStatus();
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
getUserInfo();
|
||||
})
|
||||
|
||||
// 获取用户登录信息并保存日志
|
||||
const getUserInfo = async () => {
|
||||
|
||||
let requestBody = {
|
||||
"UserName": localStorage.getItem('username'),
|
||||
"Database": localStorage.getItem('selectdb'),
|
||||
}
|
||||
$http.post('Login/LogRecord', requestBody)
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
|
||||
} else {
|
||||
console.log(rs.data.message)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
172
auts_new_mobile.client/src/pages/inputimportantinfo/index.vue
Normal file
172
auts_new_mobile.client/src/pages/inputimportantinfo/index.vue
Normal file
@@ -0,0 +1,172 @@
|
||||
<template>
|
||||
<div class="bodyclass">
|
||||
<el-select v-model="nowModel"
|
||||
placeholder="选择机型"
|
||||
style="width: 150px;">
|
||||
<el-option v-for="item in models"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value" />
|
||||
</el-select>
|
||||
<span> </span>
|
||||
<el-button type="info" @click="handleImportClick" v-show="nowModel">导入Excel</el-button>
|
||||
<input type="file" ref="fileInput" @change="handleFileChange" style="display: none;" accept=".xlsx, .xls" />
|
||||
<span> </span>
|
||||
<el-tag type="success" closable @close="tagClose(tag)" v-if="fileName" size="large">
|
||||
{{ fileName }}
|
||||
</el-tag>
|
||||
<span> </span>
|
||||
<el-button type="danger" @click="" v-show="fileDate.length > 0">保存到数据库</el-button>
|
||||
</div>
|
||||
<div style="min-height: 85vh; margin-top: 3px;">
|
||||
<el-table :data="fileDate" style="width: 100%">
|
||||
<el-table-column v-for="header in headers"
|
||||
:key="header"
|
||||
:prop="header"
|
||||
:label="header"
|
||||
width="180">
|
||||
<template #header>
|
||||
<span :style="{ color: !tableNames.includes(header) ? 'red' : 'green' }">
|
||||
{{ header }}
|
||||
</span>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<span :style="{ color: !tableNames.includes(header) ? 'red' : 'green' }">
|
||||
{{ scope.row[header] }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, inject, onMounted } from 'vue';
|
||||
import { Toast } from 'tdesign-mobile-vue';
|
||||
import * as XLSX from 'xlsx';
|
||||
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||
const $http = inject('$http')
|
||||
|
||||
const value = ref('')
|
||||
const models = ref([
|
||||
{
|
||||
value: 'models1',
|
||||
label: 'models1',
|
||||
},
|
||||
{
|
||||
value: 'models2',
|
||||
label: 'models2',
|
||||
},
|
||||
{
|
||||
value: 'models3',
|
||||
label: 'models3',
|
||||
}
|
||||
])
|
||||
|
||||
const nowModel = ref('')
|
||||
const fileInput = ref(null)
|
||||
const headers = ref([])
|
||||
const fileDate = ref([])
|
||||
const fileName = ref('')
|
||||
|
||||
const handleImportClick = () => {
|
||||
fileInput.value.click()
|
||||
}
|
||||
|
||||
const handleFileChange = async (e) => {
|
||||
const file = e.target.files[0]
|
||||
if (!file) return
|
||||
|
||||
try {
|
||||
// 读取Excel文件
|
||||
const data = await file.arrayBuffer()
|
||||
const workbook = XLSX.read(data, { type: 'array' })
|
||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]]
|
||||
const jsonData = XLSX.utils.sheet_to_json(worksheet)
|
||||
headers.value = Object.keys(jsonData[0])
|
||||
fileDate.value = jsonData
|
||||
// 这里可以处理解析后的数据,例如:
|
||||
// 1. 发送到服务器
|
||||
//await $http.post('/api/upload', jsonData)
|
||||
|
||||
// 2. 或者更新models数据(示例)
|
||||
// models.value = jsonData.map(item => ({
|
||||
// value: item.模型ID,
|
||||
// label: item.模型名称
|
||||
// }))
|
||||
|
||||
fileName.value = file.name
|
||||
console.log('导入成功:', jsonData)
|
||||
Toast('文件导入成功')
|
||||
} catch (error) {
|
||||
console.error('导入失败:', error)
|
||||
Toast('文件导入失败')
|
||||
} finally {
|
||||
e.target.value = '' // 清空选择允许重复上传
|
||||
}
|
||||
}
|
||||
|
||||
const tableNames = ref([])
|
||||
|
||||
const tagClose = () => {
|
||||
fileDate.value = []
|
||||
fileName.value = ''
|
||||
}
|
||||
|
||||
|
||||
// 获取数据库表
|
||||
const getTable = () => {
|
||||
let requestBody = {
|
||||
key: "getTable",
|
||||
CmdType: "sys",
|
||||
DBQueryData: [
|
||||
{
|
||||
key: "@DBName",
|
||||
value: localStorage.getItem('selectdb'),
|
||||
valueType: "string",
|
||||
},
|
||||
{
|
||||
key: "@TBName",
|
||||
value: "tbl_importinfo",
|
||||
valueType: "string",
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
$http.post('DataHandle/R_InfoCommon', requestBody)
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
tableNames.value = []
|
||||
rs.data.response.forEach((item, index) => {
|
||||
tableNames.value.push(item.name)
|
||||
});
|
||||
|
||||
//console.log(tableNames.value)
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
// 初始化加载数据
|
||||
onMounted(() => {
|
||||
getTable()
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@media (min-width: 1024px) {
|
||||
.bodyclass {
|
||||
margin-top: 5px
|
||||
}
|
||||
}
|
||||
</style>
|
||||
457
auts_new_mobile.client/src/pages/layout/index.vue
Normal file
457
auts_new_mobile.client/src/pages/layout/index.vue
Normal file
@@ -0,0 +1,457 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<!-- 顶部导航栏 -->
|
||||
<t-navbar class="custom-navbar">
|
||||
<!-- 左侧区域 -->
|
||||
<template #left>
|
||||
<t-button variant="text"
|
||||
shape="square"
|
||||
@click="handleLeftClick('home')">
|
||||
<template #icon>
|
||||
<t-icon size="23px" name="home" />
|
||||
</template>
|
||||
</t-button>
|
||||
<!-- 控制页面路由菜单 -->
|
||||
<t-dropdown-menu ref="parentRef" style="height:32px;border:none">
|
||||
<t-dropdown-item :value="currentTitle.routes" :label="currentTitle.label">
|
||||
<div v-for="(group,i) in menuValue" :key="i" >
|
||||
<div class="demo-dropdown-item_label" v-if="group.label != '管理' || (username == 'MomoWen' || username == 'Admin')">
|
||||
{{ group.label }}
|
||||
</div>
|
||||
<t-radio-group @change="menuChange" :key="i" v-if="group.label != '管理' || (username == 'MomoWen' || username == 'Admin')">
|
||||
<view v-for="(item, index) in group.options" :key="item.value" :class="`card ${currentTitle.routes == item.value ? 'card--active' : ''}`">
|
||||
<icon v-if="currentTitle.routes == item.value" name="check" class="card__icon" />
|
||||
<t-radio :value="item" :label="item.label" icon="none" borderless />
|
||||
</view>
|
||||
</t-radio-group>
|
||||
</div>
|
||||
</t-dropdown-item>
|
||||
</t-dropdown-menu>
|
||||
<!-- 数据库控制菜单 -->
|
||||
<t-dropdown-menu style="height:32px;border:none">
|
||||
<t-dropdown-item :options="product.options" :value="product.value" @change="databaseChanges" :disabled="disDbDrop" />
|
||||
</t-dropdown-menu>
|
||||
</template>
|
||||
|
||||
<!-- 右侧区域 -->
|
||||
<template #right>
|
||||
<t-popover v-model="showPopover"
|
||||
trigger="click"
|
||||
placement="bottom-right"
|
||||
:overlay-style="{ width: '120px' }">
|
||||
<t-avatar class="avatar-example--small external-class-content" shape="circle" size="33px">{{ username[0] }}</t-avatar>
|
||||
|
||||
<template #content>
|
||||
<t-button size="large" theme="primary" variant="text" @click="handleAction('profile')">个人中心</t-button>
|
||||
<t-button size="large" theme="danger" variant="text" @click="handleAction('logout')">退出登录</t-button>
|
||||
|
||||
</template>
|
||||
</t-popover>
|
||||
<t-button class="theme-toggle" @click="toggleTheme" size="33px">
|
||||
{{ isDarkMode ? '🌞' : '🌙' }}
|
||||
</t-button>
|
||||
</template>
|
||||
</t-navbar>
|
||||
|
||||
<!-- 导航栏 -->
|
||||
<!-- 页面内容 -->
|
||||
<!--<router-view></router-view>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, reactive, inject } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { Toast } from 'tdesign-mobile-vue'
|
||||
import { useDark, useToggle } from '@vueuse/core'
|
||||
|
||||
const isDark = useDark()
|
||||
const toggleDark = useToggle(isDark)
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const showSidebar = ref(false)
|
||||
|
||||
const $http = inject('$http')
|
||||
const username = localStorage.getItem('username')
|
||||
|
||||
|
||||
const product = reactive({
|
||||
value: '',
|
||||
options: [],
|
||||
})
|
||||
|
||||
// 菜单值
|
||||
const menuValue = reactive([
|
||||
{
|
||||
label: "控制台",
|
||||
options: [
|
||||
{
|
||||
label: '主页',
|
||||
value: '/home',
|
||||
disabled: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "数据",
|
||||
options: [
|
||||
{
|
||||
label: '测试数据',
|
||||
value: '/testlog',
|
||||
disabled: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "管理",
|
||||
options: [
|
||||
{
|
||||
label: '导入数据',
|
||||
value: '/inputcolorbox',
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
label: '用户管理',
|
||||
value: '/usermanage',
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
label: '数据库管理',
|
||||
value: '/dbmanage',
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
label: '用户日志',
|
||||
value: '/loginlog',
|
||||
disabled: true,
|
||||
}
|
||||
],
|
||||
},
|
||||
])
|
||||
const currentTitle = reactive({
|
||||
label: '主页',
|
||||
routes: '/home',
|
||||
}) // 当前页面标题
|
||||
|
||||
const showPopover = ref(false) // 气泡菜单显示状态
|
||||
|
||||
|
||||
// 左侧按钮点击处理
|
||||
const handleLeftClick = (type) => {
|
||||
// type 可能的值: 'home' 按钮
|
||||
switch (type) {
|
||||
case 'home':
|
||||
let hm = {
|
||||
"label": "主页",
|
||||
"value": "/home",
|
||||
"disabled": true
|
||||
}
|
||||
menuChange(hm)
|
||||
router.push('/home')
|
||||
break;
|
||||
default:
|
||||
console.log('跳转错误!');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 菜单操作处理
|
||||
const handleAction = (action) => {
|
||||
// action 可能的值: 'profile' 个人中心 / 'logout' 退出登录
|
||||
showPopover.value = false
|
||||
switch (action) {
|
||||
case 'profile':
|
||||
disDbDrop.value = true
|
||||
router.push('/user')
|
||||
break;
|
||||
case 'logout':
|
||||
router.push('/login')
|
||||
break;
|
||||
default:
|
||||
console.log('跳转错误!');
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
const disDbDrop = ref(true)
|
||||
|
||||
|
||||
// 数据库变更处理
|
||||
const databaseChanges = (type) => {
|
||||
if (type != localStorage.getItem('selectdb')) {
|
||||
Toast({
|
||||
className: 'toast-root--success',
|
||||
theme: 'success',
|
||||
direction: 'column',
|
||||
message: '数据库切换为:' + type,
|
||||
});
|
||||
localStorage.setItem('selectdb', type)
|
||||
setTimeout(() => {
|
||||
location.reload(true);
|
||||
}, 666);
|
||||
}
|
||||
}
|
||||
const parentRef = ref(null)
|
||||
// 菜单处理
|
||||
const menuChange = (val) => {
|
||||
currentTitle.label = val.label;
|
||||
currentTitle.routes = val.value;
|
||||
//console.log(val)
|
||||
if (val.value != '/home') {
|
||||
disDbDrop.value = true
|
||||
} else {
|
||||
disDbDrop.value = false
|
||||
}
|
||||
parentRef.value.collapseMenu();
|
||||
router.push(val.value)
|
||||
}
|
||||
|
||||
// 判断是否管理员
|
||||
const isAdmin = (page) => {
|
||||
return (page.label == "管理" && (username == 'MomoWen' || username == 'Admin'))
|
||||
|
||||
}
|
||||
// 控制深色模式
|
||||
const isDarkMode = ref(false)
|
||||
const webToDark = (dark) => {
|
||||
// 获取 <html> 元素的类名
|
||||
let ishtmldark = null;
|
||||
if (document.documentElement.className.indexOf('dark') !== -1) {
|
||||
ishtmldark = false
|
||||
} else {
|
||||
ishtmldark = true
|
||||
}
|
||||
if (dark) {
|
||||
document.documentElement.setAttribute('theme-mode', 'dark')
|
||||
isDarkMode.value = true
|
||||
if (ishtmldark) {
|
||||
toggleDark()
|
||||
}
|
||||
} else {
|
||||
document.documentElement.removeAttribute('theme-mode')
|
||||
isDarkMode.value = false
|
||||
if (!ishtmldark) {
|
||||
toggleDark()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
const toggleTheme = () => {
|
||||
|
||||
isDarkMode.value = !isDarkMode.value
|
||||
let root = document.documentElement;
|
||||
if (!isDarkMode.value) {
|
||||
// 如果当前是暗色主题,切换到亮色主题
|
||||
root.style.setProperty('--color-background', '#ffffff');
|
||||
root.style.setProperty('--color-background-soft', '#f8f8f8');
|
||||
root.style.setProperty('--color-background-mute', '#f2f2f2');
|
||||
root.style.setProperty('--color-border', 'rgba(60, 60, 60, 0.12)');
|
||||
root.style.setProperty('--color-border-hover', 'rgba(60, 60, 60, 0.29)');
|
||||
root.style.setProperty('--color-heading', '#2c3e50');
|
||||
root.style.setProperty('--color-text', '#2c3e50');
|
||||
root.setAttribute('data-theme', 'light');
|
||||
if (root.className.indexOf('dark') !== -1) {
|
||||
toggleDark()
|
||||
}
|
||||
setTimeout(() => {
|
||||
root.removeAttribute('theme-mode');
|
||||
}, 20);
|
||||
} else {
|
||||
// 如果当前是亮色主题,切换到暗色主题
|
||||
root.style.setProperty('--color-background', '#242424');
|
||||
root.style.setProperty('--color-background-soft', '#222222');
|
||||
root.style.setProperty('--color-background-mute', '#282828');
|
||||
root.style.setProperty('--color-border', 'rgba(84, 84, 84, 0.48)');
|
||||
root.style.setProperty('--color-border-hover', 'rgba(84, 84, 84, 0.65)');
|
||||
root.style.setProperty('--color-heading', '#ffffff');
|
||||
root.style.setProperty('--color-text', 'rgba(235, 235, 235, 0.64)');
|
||||
root.setAttribute('data-theme', 'dark');
|
||||
if (!(root.className.indexOf('dark') !== -1)) {
|
||||
toggleDark()
|
||||
}
|
||||
setTimeout(() => {
|
||||
root.setAttribute('theme-mode', 'dark');
|
||||
}, 20);
|
||||
}
|
||||
}
|
||||
var alreadyLogin = ref(false)
|
||||
const checkLoginStatus = () => {
|
||||
alreadyLogin.value = localStorage.getItem('login') == 'true'
|
||||
|
||||
}
|
||||
|
||||
// 获取服务器信息
|
||||
const onDbLogout = () => {
|
||||
let requestBody = {
|
||||
key: "getDbName",
|
||||
CmdType: "sys",
|
||||
DBQueryData: []
|
||||
}
|
||||
$http.post('DataHandle/R_InfoCommon', requestBody)
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
product.options = []
|
||||
rs.data.response.forEach(item => {
|
||||
if (item.Database.slice(0, 3) == "uts" && item.Database != "uts_manage") {
|
||||
let dbname = {
|
||||
label: item.Database,
|
||||
value: item.Database
|
||||
}
|
||||
product.options.push(dbname)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
// 初始检查登录状态
|
||||
checkLoginStatus()
|
||||
|
||||
// 初始化数据库列表
|
||||
onDbLogout()
|
||||
|
||||
if (localStorage.getItem('selectdb')) {
|
||||
product.value = localStorage.getItem('selectdb')
|
||||
} else {
|
||||
product.value = "uts_blw"
|
||||
localStorage.setItem('selectdb', product.value)
|
||||
}
|
||||
|
||||
findLabelByValue();
|
||||
// 主题初始化
|
||||
const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
const handleColorSchemeChange = (e) => {
|
||||
webToDark(e.matches)
|
||||
}
|
||||
|
||||
const url = window.location.href;
|
||||
// 找到最后一个 '/' 的位置
|
||||
const lastSlashIndex = url.lastIndexOf('/');
|
||||
// 获取最后一个 '/' 后面的内容
|
||||
const lastSegment = url.substring(lastSlashIndex);
|
||||
if (lastSegment == '/home') {
|
||||
disDbDrop.value = false
|
||||
}
|
||||
|
||||
// 初始设置
|
||||
webToDark(darkModeQuery.matches)
|
||||
|
||||
// 监听系统主题变化
|
||||
darkModeQuery.addEventListener('change', handleColorSchemeChange)
|
||||
|
||||
// 清理监听器
|
||||
return () => {
|
||||
darkModeQuery.removeEventListener('change', handleColorSchemeChange)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// 确定当前地址
|
||||
const findLabelByValue = () => {
|
||||
const url = window.location.href;
|
||||
// 找到最后一个 '/' 的位置
|
||||
const lastSlashIndex = url.lastIndexOf('/');
|
||||
// 获取最后一个 '/' 后面的内容
|
||||
const lastSegment = url.substring(lastSlashIndex);
|
||||
for (const menu of menuValue) {
|
||||
for (const option of menu.options) {
|
||||
if (option.value == lastSegment) {
|
||||
currentTitle.routes = lastSegment
|
||||
currentTitle.label = option.label
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 主题切换按钮 */
|
||||
.theme-toggle {
|
||||
margin-left: 8px;
|
||||
padding: 8px 12px;
|
||||
background: var(--toggle-bg);
|
||||
color: var(--toggle-text);
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
|
||||
/* 横向布局 */
|
||||
.t-radio--block {
|
||||
padding: 3px 10px 3px 10px;
|
||||
}
|
||||
|
||||
.horizontal-box {
|
||||
width: calc(100% - 32px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 16px;
|
||||
}
|
||||
|
||||
.horizontal-box .card {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.horizontal-box .card + .card {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.demo-desc {
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.card {
|
||||
display: block;
|
||||
position: relative;
|
||||
margin: 8px;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
border: 1.5px solid #fff;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
.card--active {
|
||||
border-color: #0052d9;
|
||||
}
|
||||
|
||||
.card--active::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 0;
|
||||
border: 8px solid #0052d9;
|
||||
border-bottom-color: transparent;
|
||||
border-right-color: transparent;
|
||||
}
|
||||
|
||||
.card__icon {
|
||||
display: block;
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
left: 1.5px;
|
||||
top: 1.5px;
|
||||
z-index: 10;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
427
auts_new_mobile.client/src/pages/login/index.vue
Normal file
427
auts_new_mobile.client/src/pages/login/index.vue
Normal file
@@ -0,0 +1,427 @@
|
||||
<template>
|
||||
<h1 style="margin:100px 0px 100px 0px; text-align:center">登录AUTS</h1>
|
||||
<div class="login-container">
|
||||
<t-form @submit="handleSubmit">
|
||||
<t-input v-model="form.username"
|
||||
label="账号"
|
||||
placeholder="请输入账号"
|
||||
@focus="MoveToEnd"
|
||||
clearable />
|
||||
<t-input v-model="form.password"
|
||||
label="密码"
|
||||
placeholder="请输入密码"
|
||||
@focus="MoveToEnd"
|
||||
type="password"
|
||||
clearable />
|
||||
<t-input v-model="form.code"
|
||||
maxlength="4"
|
||||
:placeholder="captchaSrc ? '请输入验证码' : '点击获取验证码'"
|
||||
label="验证码"
|
||||
@focus="getCaptchaSrc"
|
||||
clearable>
|
||||
<template #suffix>
|
||||
<div class="suffix" v-if="captchaSrc">
|
||||
<t-image class="image"
|
||||
:src="captchaSrc"
|
||||
@click="generateCaptcha"
|
||||
mode="heightFix"
|
||||
aria-role="img"
|
||||
aria-label="验证码" />
|
||||
</div>
|
||||
</template>
|
||||
</t-input>
|
||||
<t-checkbox v-model="form.remember">记住密码</t-checkbox>
|
||||
<t-button v-if="!isLocked"
|
||||
theme="primary"
|
||||
type="submit"
|
||||
block
|
||||
:loading="loading">登录</t-button>
|
||||
<t-button v-if="isLocked"
|
||||
theme="danger"
|
||||
type="submit"
|
||||
ghost
|
||||
disabled>密码错误,账号已锁定</t-button>
|
||||
</t-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { inject, ref, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Toast } from 'tdesign-mobile-vue'
|
||||
|
||||
const router = useRouter()
|
||||
// 存储数据
|
||||
//sessionStorage.setItem('test', 'a123');
|
||||
//sessionStorage.getItem('username');
|
||||
//sessionStorage.removeItem('username');
|
||||
const successMethod = () => {
|
||||
console.log('验证成功')
|
||||
}
|
||||
|
||||
const form = ref({
|
||||
username: '',
|
||||
password: '',
|
||||
remember: false,
|
||||
code: ''
|
||||
})
|
||||
|
||||
// 状态定义
|
||||
const isDarkMode = ref(false)
|
||||
const loading = ref(false)
|
||||
const submitDisabled = ref(false) // 提交禁用状态
|
||||
const isLocked = ref(false) // 账号锁定状态
|
||||
const errorAttempts = ref(0) // 错误尝试次数
|
||||
const $http = inject('$http')
|
||||
const LOCK_STORAGE_KEY = 'loginLockUntil' // 锁定时间存储键
|
||||
const ATTEMPTS_STORAGE_KEY = 'loginErrorAttempts'
|
||||
const captchaSrc = ref('')
|
||||
const captchaValue = ref('')
|
||||
/*localStorage.removeItem('token')*/
|
||||
|
||||
const checkLoginStatus = inject('checkLoginStatus');
|
||||
checkLoginStatus();
|
||||
|
||||
// 初始化认证状态
|
||||
const initAuthState = () => {
|
||||
const storedLockUntil = localStorage.getItem(LOCK_STORAGE_KEY)
|
||||
const storedAttempts = localStorage.getItem(ATTEMPTS_STORAGE_KEY)
|
||||
|
||||
errorAttempts.value = parseInt(storedAttempts) || 0
|
||||
|
||||
if (storedLockUntil) {
|
||||
const remainingTime = parseInt(storedLockUntil) - Date.now()
|
||||
|
||||
if (remainingTime > 0) {
|
||||
isLocked.value = true
|
||||
setupUnlockTimer(remainingTime)
|
||||
} else {
|
||||
// 清理过期数据
|
||||
localStorage.removeItem(LOCK_STORAGE_KEY)
|
||||
localStorage.removeItem(ATTEMPTS_STORAGE_KEY)
|
||||
errorAttempts.value = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置解锁定时器
|
||||
const setupUnlockTimer = (duration) => {
|
||||
isLocked.value = true
|
||||
const timer = setTimeout(() => {
|
||||
isLocked.value = false
|
||||
errorAttempts.value = 0
|
||||
localStorage.removeItem(LOCK_STORAGE_KEY)
|
||||
localStorage.removeItem(ATTEMPTS_STORAGE_KEY)
|
||||
clearTimeout(timer)
|
||||
}, duration)
|
||||
}
|
||||
|
||||
// 初始化认证状态
|
||||
onMounted(() => {
|
||||
//mountedToken()
|
||||
initAuthState()
|
||||
localStorage.setItem('login', false)
|
||||
getCaptchaSrc()
|
||||
});
|
||||
|
||||
const mountedToken = () => {
|
||||
$http.post('Login/Helloooo', '')
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
router.push('/home')
|
||||
/*Toast({
|
||||
className: 'toast-root--success',
|
||||
theme: 'success',
|
||||
direction: 'column',
|
||||
message: '登录成功!',
|
||||
});*/
|
||||
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
})
|
||||
}
|
||||
// 新增深色模式状态
|
||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
isDarkMode.value = true;
|
||||
} else {
|
||||
isDarkMode.value = false;
|
||||
}
|
||||
|
||||
|
||||
const clearUsername = () => {
|
||||
form.value.username = ''
|
||||
}
|
||||
|
||||
|
||||
// 滚动页面
|
||||
const MoveToEnd = () => {
|
||||
// 滚动到页面底部
|
||||
window.scrollTo({
|
||||
top: document.body.scrollHeight,
|
||||
behavior: 'smooth' // 可选,用于平滑滚动
|
||||
});
|
||||
}
|
||||
|
||||
const getCaptchaSrc = () => {
|
||||
if (captchaValue.value == "") {
|
||||
generateCaptcha()
|
||||
}
|
||||
MoveToEnd()
|
||||
}
|
||||
// 生成验证码
|
||||
const generateCaptcha = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const chars = '0123456789';
|
||||
let captcha = '';
|
||||
canvas.width = 86;
|
||||
canvas.height = 30;
|
||||
|
||||
// 生成验证码字符串
|
||||
for (let i = 0; i < 4; i++) {
|
||||
captcha += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
}
|
||||
captchaValue.value = captcha; // 存储验证码值用于验证
|
||||
|
||||
// 绘制背景
|
||||
ctx.fillStyle = isDarkMode.value ? '#242424' : '#FFFFFF';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// 绘制噪点
|
||||
ctx.fillStyle = isDarkMode.value ? '#FFFFFF' : '#000000';
|
||||
for (let i = 0; i < 33; i++) {
|
||||
const x = Math.random() * canvas.width;
|
||||
const y = Math.random() * canvas.height;
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, 1, 0, Math.PI * 2, false);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
// 绘制验证码
|
||||
ctx.font = '24px Arial';
|
||||
let xnd = 10;
|
||||
for (let i = 0; i < captcha.length; i++) {
|
||||
const angle = (Math.random() * 30) - 15; // -10度到+10度
|
||||
|
||||
// 保存当前状态
|
||||
ctx.save();
|
||||
|
||||
// 旋转并绘制文字
|
||||
ctx.translate(xnd, 20);
|
||||
ctx.rotate(angle * Math.PI / 180);
|
||||
ctx.fillText(captcha[i], 0, 0);
|
||||
|
||||
// 恢复状态
|
||||
ctx.restore();
|
||||
|
||||
// 更新下一个字符的x位置
|
||||
xnd += 15 + Math.abs(angle) / 2; // 根据角度调整字符间距
|
||||
}
|
||||
|
||||
// 添加5-10条干扰条纹
|
||||
ctx.strokeStyle = '#6F4A2F';
|
||||
const bug = Math.floor(Math.random() * 6) + 3
|
||||
for (let i = 0; i < bug; i++) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(Math.random() * canvas.width, Math.random() * canvas.height);
|
||||
ctx.lineTo(Math.random() * canvas.width, Math.random() * canvas.height);
|
||||
ctx.stroke();
|
||||
}
|
||||
// 将canvas转换为图片URL
|
||||
captchaSrc.value = canvas.toDataURL('image/png');
|
||||
}
|
||||
// 验证验证码
|
||||
const verifyCaptcha = (inputValue) => {
|
||||
// 验证输入的验证码是否正确
|
||||
return inputValue === captchaValue.value;
|
||||
}
|
||||
|
||||
// 登录
|
||||
const handleSubmit = () => {
|
||||
if (isLocked.value || submitDisabled.value) return
|
||||
if (!form.value.username) {
|
||||
Toast('请输入账号!')
|
||||
return
|
||||
}
|
||||
if (!form.value.password) {
|
||||
Toast('请输入密码!')
|
||||
return
|
||||
}
|
||||
if (!form.value.code) {
|
||||
Toast('请输入验证码!')
|
||||
return
|
||||
}
|
||||
if (!verifyCaptcha(form.value.code)) {
|
||||
Toast('验证码错误!')
|
||||
generateCaptcha()
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
submitDisabled.value = true
|
||||
|
||||
const formdata = {
|
||||
username: form.value.username,
|
||||
password: form.value.password
|
||||
}
|
||||
|
||||
$http.post('Login/Login', formdata)
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
// 登录成功处理...
|
||||
errorAttempts.value = 0
|
||||
localStorage.removeItem(ATTEMPTS_STORAGE_KEY)
|
||||
localStorage.removeItem(LOCK_STORAGE_KEY)
|
||||
// 存储token
|
||||
localStorage.setItem('token', rs.data.response.accessToken)
|
||||
localStorage.setItem('username', rs.data.response.userName)
|
||||
localStorage.setItem('login', true)
|
||||
localStorage.setItem('uid', rs.data.response.id)
|
||||
|
||||
// 记住账号和密码
|
||||
if (form.value.remember) {
|
||||
localStorage.setItem('rememberedUsername', form.value.username)
|
||||
localStorage.setItem('rememberedPassword', form.value.password)
|
||||
} else {
|
||||
localStorage.removeItem('rememberedPassword')
|
||||
}
|
||||
Toast({
|
||||
className: 'toast-root--success',
|
||||
theme: 'success',
|
||||
direction: 'column',
|
||||
message: '登录成功,欢迎:' + rs.data.response.userName + '!',
|
||||
});
|
||||
router.push('/home')
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
errorAttempts.value++
|
||||
localStorage.setItem(ATTEMPTS_STORAGE_KEY, errorAttempts.value)
|
||||
|
||||
if (errorAttempts.value >= 5) {
|
||||
const lockUntil = Date.now() + 3600000
|
||||
localStorage.setItem(LOCK_STORAGE_KEY, lockUntil)
|
||||
setupUnlockTimer(3600000)
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 300)
|
||||
setTimeout(() => {
|
||||
submitDisabled.value = false
|
||||
}, 30000)
|
||||
})
|
||||
}
|
||||
|
||||
// 初始化记住的账号和密码
|
||||
form.value.username = localStorage.getItem('rememberedUsername') || ''
|
||||
form.value.password = localStorage.getItem('rememberedPassword') || ''
|
||||
form.value.remember = form.value.username !== '' && form.value.password !== ''
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.login-container {
|
||||
padding: 16px;
|
||||
}
|
||||
/* 基础样式 */
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
padding: 40px 16px;
|
||||
transition: background-color 0.3s, color 0.3s;
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.title {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
font-size: 24px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.login-container {
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
padding: 24px;
|
||||
background: var(--card-bg);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 输入框间距优化 */
|
||||
.t-form {
|
||||
display: flex;
|
||||
margin: 18px 10px 10px 10px;
|
||||
flex-direction: column;
|
||||
gap: 25px;
|
||||
}
|
||||
|
||||
/* 按钮样式优化 */
|
||||
.t-button {
|
||||
margin-top: 5px;
|
||||
height: 40px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.t-checkbox--block {
|
||||
padding: 0;
|
||||
}
|
||||
/* 主题切换按钮 */
|
||||
.theme-toggle {
|
||||
position: fixed;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
padding: 8px 16px;
|
||||
background: var(--toggle-bg);
|
||||
color: var(--toggle-text);
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 480px) {
|
||||
.container {
|
||||
padding: 24px 12px;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
padding: 5px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 20px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.login-container {
|
||||
padding: 5px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 28px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
164
auts_new_mobile.client/src/pages/loginlog/index.vue
Normal file
164
auts_new_mobile.client/src/pages/loginlog/index.vue
Normal file
@@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<div class="tagclass" style="display:inline;">
|
||||
<el-tag type="info">总日志数</el-tag>
|
||||
<span> </span>
|
||||
<el-tag type="success">{{ allLogsCount }}</el-tag>
|
||||
<span> </span>
|
||||
<el-input-number v-model="nowPage" min="1" :max="maxPage" size="small"
|
||||
step-strictly="true"
|
||||
@change="onChangeStepper">
|
||||
<template #suffix>
|
||||
<span>/{{ maxPage }}</span>
|
||||
</template>
|
||||
</el-input-number>
|
||||
</div>
|
||||
<t-list>
|
||||
<div v-for="user in logs" :key="user.ID" class="user-list-div"
|
||||
style="position: relative; width: auto; padding: 3px; height:48px; border-radius: 3px; margin: 2px;">
|
||||
|
||||
<span class="user-name" style="position: absolute; top: 3px; left: 4px; font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||
{{ user.ID }}:
|
||||
<span style="font-size: 14px; font-weight: 500">{{ user.UserName }} | {{ user.Database }}</span>
|
||||
</span>
|
||||
|
||||
<span class="user-ip" style="position: absolute; top: 25px; left: 4px; font-size: 12px; "></span>
|
||||
|
||||
<span class="user-time" style="position: absolute; bottom: 3px; left: 4px; font-size: 11px; ">{{ user.Ip }} | {{ user.Location }}</span>
|
||||
|
||||
<span class="user-location" style="position: absolute; top: 3px; right: 4px; font-size: 11px; ">
|
||||
<span>
|
||||
<icon name="logo-android-filled" v-if="user.Device == 'Android'" />
|
||||
<icon name="logo-windows-filled" v-else-if="user.Device == 'windows'" />
|
||||
<icon name="logo-apple-filled" v-else-if="user.Device == 'ios' || user.Device == 'Mac'" />
|
||||
<icon name="help-circle-filled" v-else />
|
||||
<!-- {{user.Device}}-->
|
||||
</span>
|
||||
| {{ user.Browser }}
|
||||
</span>
|
||||
|
||||
<span class="user-location" style="position: absolute; bottom: 3px; right: 4px; font-size: 11px; ">
|
||||
{{ user.CreationTime }}
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
</t-list>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, inject } from 'vue';
|
||||
import { Toast } from 'tdesign-mobile-vue';
|
||||
import { Icon } from 'tdesign-icons-vue-next';
|
||||
|
||||
const $http = inject('$http')
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
getPage();
|
||||
loading("");
|
||||
})
|
||||
const maxPage = ref(1)
|
||||
const nowPage = ref(1)
|
||||
const allLogsCount = ref(null)
|
||||
const logs = ref([])
|
||||
const lastId = ref(0)
|
||||
// 翻页
|
||||
const onChangeStepper = (value) => {
|
||||
if (value == 1) {
|
||||
nowPage.value = 1
|
||||
lastId.value = 0
|
||||
loading('')
|
||||
} else {
|
||||
nowPage.value = value
|
||||
loading(lastId.value)
|
||||
}
|
||||
}
|
||||
const getPage = () => {
|
||||
let requestBody = {
|
||||
key: "getLoginLogCount",
|
||||
CmdType: "sys",
|
||||
DBQueryData: [],
|
||||
}
|
||||
$http.post('DataHandle/R_InfoCommon', requestBody)
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
allLogsCount.value = rs.data.response[0].LogCount
|
||||
maxPage.value = Math.ceil(rs.data.response[0].LogCount / 25)
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
}
|
||||
// 加载页面
|
||||
const loading = (last_id) => {
|
||||
let requestBody = {
|
||||
key: "getLoginLog",
|
||||
CmdType: "",
|
||||
DBQueryData: [],
|
||||
isFunction: true,
|
||||
FunParameters: [last_id == 0 ? "" : "" + last_id]
|
||||
}
|
||||
//console.log(JSON.stringify(requestBody))
|
||||
$http.post('DataHandle/R_InfoCommon', JSON.stringify(requestBody))
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
logs.value = rs.data.response.map(item => {
|
||||
return {
|
||||
...item,
|
||||
CreationTime: formatDateTime(item.CreationTime)
|
||||
}
|
||||
})
|
||||
if (rs.data.response.length == 25) {
|
||||
lastId.value = rs.data.response[24].ID
|
||||
}
|
||||
//console.log(lastId.value)
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
}
|
||||
const formatDateTime = (isoString) => {
|
||||
const date = new Date(isoString);
|
||||
|
||||
// 使用padStart确保时间部分是两位数
|
||||
const year = date.getFullYear();
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0'); // 月份是从0开始的
|
||||
const day = date.getDate().toString().padStart(2, '0');
|
||||
const hours = date.getHours().toString().padStart(2, '0');
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||
const seconds = date.getSeconds().toString().padStart(2, '0');
|
||||
|
||||
// 拼接成指定的格式
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.user-list-div {
|
||||
border: 1px solid #E9E9E960;
|
||||
background-color: #80808030;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.user-list-div:hover {
|
||||
border-color: #409eff;
|
||||
background-color: #80808010;
|
||||
/* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); 可选阴影 */
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.tagclass {
|
||||
margin-top: 5px
|
||||
}
|
||||
}
|
||||
</style>
|
||||
566
auts_new_mobile.client/src/pages/stationlog/index.vue
Normal file
566
auts_new_mobile.client/src/pages/stationlog/index.vue
Normal file
@@ -0,0 +1,566 @@
|
||||
<template>
|
||||
<div class="query-container">
|
||||
<!-- 查询条件区域 -->
|
||||
<el-collapse v-model="activeCollapse" class="collapse-panel">
|
||||
<el-collapse-item title="基础条件" name="queryConditions">
|
||||
<div class="condition-section">
|
||||
<el-form :model="queryForm" class="condition-form" size="small">
|
||||
<div class="condition-row">
|
||||
<!-- 机型下拉框 -->
|
||||
<el-form-item label="机型" class="condition-item">
|
||||
<el-select v-model="queryForm.model"
|
||||
placeholder="全部机型"
|
||||
style="width:211px;"
|
||||
clearable>
|
||||
<el-option v-for="item in machineModels"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 当机型不为空时显示其他条件 -->
|
||||
<template v-if="queryForm.model">
|
||||
<!-- 条形码输入 -->
|
||||
<el-form-item label="条码" class="condition-item">
|
||||
<el-input v-model="queryForm.barcode"
|
||||
placeholder="请输入条形码"
|
||||
style="width:211px;"
|
||||
clearable />
|
||||
</el-form-item>
|
||||
<!-- 日期范围选择 -->
|
||||
<el-form-item label="日期" class="condition-item">
|
||||
<div class="date-range">
|
||||
<el-date-picker v-model="queryForm.startDate"
|
||||
type="date"
|
||||
style="width:120px;"
|
||||
placeholder="开始日期"
|
||||
value-format="YYYY-MM-DD" />
|
||||
<span> - </span>
|
||||
<el-date-picker v-model="queryForm.endDate"
|
||||
type="date"
|
||||
style="width:120px;"
|
||||
placeholder="结束日期"
|
||||
value-format="YYYY-MM-DD" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<!-- 查询条件选择 -->
|
||||
<el-form-item label="条件" class="condition-item">
|
||||
<el-select v-model="queryForm.queryType"
|
||||
style="width:211px;"
|
||||
placeholder="请选择">
|
||||
<el-option label="BARCODE" value="BARCODE" />
|
||||
<!-- 其他选项根据实际接口返回添加 -->
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 当条件选择不为空时显示模糊匹配和数值范围 -->
|
||||
<template v-if="queryForm.queryType">
|
||||
<!-- 模糊匹配 - 仅在数值范围没有值时显示 -->
|
||||
<el-form-item v-if="!hasNumberRangeValue"
|
||||
label="模糊"
|
||||
class="condition-item">
|
||||
<el-input v-model="queryForm.keyword"
|
||||
style="width:211px;"
|
||||
placeholder="输入关键词" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 数值范围 - 仅在模糊匹配没有值时显示 -->
|
||||
<el-form-item v-if="!hasKeywordValue"
|
||||
label="数值"
|
||||
class="condition-item">
|
||||
<div class="number-range">
|
||||
<el-input v-model="queryForm.minValue"
|
||||
style="width:100px;"
|
||||
placeholder="最小值" />
|
||||
<span> - </span>
|
||||
<el-input v-model="queryForm.maxValue"
|
||||
style="width:100px;"
|
||||
placeholder="最大值" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
<!-- 勾选参数区域 -->
|
||||
<el-collapse v-model="activeCollapse" class="collapse-panel">
|
||||
<el-collapse-item title="选择参数" name="selectParams">
|
||||
<div class="param-section">
|
||||
<div class="param-header">
|
||||
<el-checkbox v-model="allParamsSelected" size="small"
|
||||
@change="handleSelectAll">
|
||||
全选
|
||||
</el-checkbox>
|
||||
<el-checkbox v-model="resParamsSelected" size="small"
|
||||
@change="handleSelectAll">
|
||||
选择Results
|
||||
</el-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="param-list">
|
||||
<el-checkbox-group v-model="selectedParams" size="small">
|
||||
<div class="param-grid">
|
||||
<el-checkbox v-for="param in availableParams"
|
||||
:key="param.value"
|
||||
:label="param.value"
|
||||
class="param-item">
|
||||
{{ param.label }}
|
||||
</el-checkbox>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
<!-- 操作按钮 - 主要修改点:优化移动端按钮布局 -->
|
||||
<div class="action-buttons">
|
||||
<el-button type="primary"
|
||||
icon="Search" size="small"
|
||||
@click="handleSearch"
|
||||
class="action-btn">
|
||||
查询
|
||||
</el-button>
|
||||
<el-button icon="Refresh" size="small"
|
||||
@click="handleReset"
|
||||
class="action-btn">重置</el-button>
|
||||
<!-- 导出按钮 -->
|
||||
<el-button type="success"
|
||||
icon="Download"
|
||||
size="small"
|
||||
@click="handleExportExcel"
|
||||
class="action-btn">
|
||||
导出Excel
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 分页器 -->
|
||||
<div class="pagination">
|
||||
<el-pagination v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
size="small"
|
||||
:page-sizes="[50, 100, 200, 300]"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:total="total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange" />
|
||||
</div>
|
||||
<!-- 数据表格 -->
|
||||
<div class="data-section">
|
||||
<el-table :data="tableData"
|
||||
stripe
|
||||
size="small"
|
||||
border
|
||||
style="width: 100%">
|
||||
<el-table-column v-for="col in tableColumns"
|
||||
:key="col.prop"
|
||||
:prop="col.prop"
|
||||
:label="col.label"
|
||||
min-width="120" />
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, inject, computed } from 'vue';
|
||||
import { Toast } from 'tdesign-mobile-vue';
|
||||
import { AddIcon, SaveIcon } from 'tdesign-icons-vue-next';
|
||||
import { ElMessage, ElMessageBox, ElCollapse, ElCollapseItem } from 'element-plus'
|
||||
import { RefreshRight, Refresh, Search } from '@element-plus/icons-vue'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const $http = inject('$http')
|
||||
|
||||
// ---------- 数据定义 ----------
|
||||
const queryForm = reactive({
|
||||
model: null, // 机型
|
||||
barcode: null, // 条形码
|
||||
startDate: '', // 开始日期
|
||||
endDate: '', // 结束日期
|
||||
queryType: null, // 查询条件
|
||||
keyword: null, // 字符模糊匹配
|
||||
minValue: null, // 最小值
|
||||
maxValue: null // 最大值
|
||||
});
|
||||
|
||||
// 机型选项
|
||||
const machineModels = ref([]);
|
||||
const activeCollapse = ref(['queryConditions', 'selectParams']); // 默认展开
|
||||
// 可用参数列表
|
||||
const availableParams = ref([
|
||||
{ label: 'COLORBOX_BARCODE', value: 'COLORBOX_BARCODE' },
|
||||
{ label: 'OUTBOXCODE', value: 'OUTBOXCODE' },
|
||||
{ label: 'MACCODE', value: 'MACCODE' },
|
||||
{ label: 'UNITWEIGTH', value: 'UNITWEIGTH' },
|
||||
{ label: 'LABEL_PRINT_CNT', value: 'LABEL_PRINT_CNT' },
|
||||
{ label: 'S1', value: 'S1' },
|
||||
{ label: 'S4', value: 'S4' },
|
||||
{ label: 'S5', value: 'S5' },
|
||||
{ label: 'S6', value: 'S6' },
|
||||
{ label: 'S7', value: 'S7' },
|
||||
{ label: 'S10', value: 'S10' },
|
||||
{ label: 'RESULT1', value: 'RESULT1' },
|
||||
{ label: 'RESULT4', value: 'RESULT4' },
|
||||
{ label: 'RESULT7', value: 'RESULT7' },
|
||||
{ label: 'RESULT10', value: 'RESULT10' },
|
||||
{ label: 'MO', value: 'MO' },
|
||||
{ label: 'SHIPPINGLOADER', value: 'SHIPPINGLOADER' },
|
||||
{ label: 'SHIPPINGDATETIME', value: 'SHIPPINGDATETIME' },
|
||||
{ label: '589_LCM_SN', value: '589_LCM_SN' },
|
||||
{ label: '589_KEYBOARD_SN', value: '589_KEYBOARD_SN' },
|
||||
{ label: 'RESULT2', value: 'RESULT2' },
|
||||
{ label: 'RESULT3', value: 'RESULT3' },
|
||||
{ label: 'RESULT6', value: 'RESULT6' },
|
||||
{ label: 'RESULT12', value: 'RESULT12' },
|
||||
{ label: 'SHIPPINGCODE', value: 'SHIPPINGCODE' },
|
||||
{ label: '589_LCM_SN', value: '589_LCM_SN' },
|
||||
{ label: '589_MODULEBOARD_SN', value: '589_MODULEBOARD_SN' },
|
||||
{ label: '189_MAINBOARD_SN', value: '189_MAINBOARD_SN' }
|
||||
]);
|
||||
|
||||
// 选中的参数
|
||||
const selectedParams = ref([]);
|
||||
|
||||
// 表格数据
|
||||
const tableData = ref([]);
|
||||
const tableColumns = ref([
|
||||
{ prop: 'id', label: 'ID' },
|
||||
{ prop: 'barcode', label: '条形码' },
|
||||
{ prop: 'result', label: '结果' }
|
||||
]);
|
||||
|
||||
// 分页数据
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(50);
|
||||
const total = ref(0);
|
||||
|
||||
// ---------- 计算属性 ----------
|
||||
// 检查是否有数值范围值
|
||||
const hasNumberRangeValue = computed(() => {
|
||||
return queryForm.minValue || queryForm.maxValue;
|
||||
});
|
||||
|
||||
// 检查是否有模糊匹配值
|
||||
const hasKeywordValue = computed(() => {
|
||||
return queryForm.keyword;
|
||||
});
|
||||
// 全选参数控制
|
||||
const allParamsSelected = computed({
|
||||
get: () => availableParams.value.length > 0 &&
|
||||
selectedParams.value.length === availableParams.value.length,
|
||||
set: (value) => {
|
||||
selectedParams.value = value
|
||||
? availableParams.value.map(p => p.value)
|
||||
: [];
|
||||
}
|
||||
});
|
||||
const resParamsSelected = computed({
|
||||
get: () => availableParams.value.length > 0 &&
|
||||
selectedParams.value.length === availableParams.value.length,
|
||||
set: (value) => {
|
||||
selectedParams.value = value
|
||||
? availableParams.value.map(p => p.value)
|
||||
: [];
|
||||
}
|
||||
});
|
||||
// ---------- 生命周期 ----------
|
||||
onMounted(() => {
|
||||
fetchMachineModels();
|
||||
fetchAvailableParams();
|
||||
});
|
||||
|
||||
// ---------- 方法定义 ----------
|
||||
// 获取机型列表
|
||||
const fetchMachineModels = async () => {
|
||||
try {
|
||||
// 实际接口调用:
|
||||
// const response = await $http.get('/api/models');
|
||||
// machineModels.value = response.data.map(item => ({
|
||||
// value: item.id,
|
||||
// label: item.name
|
||||
// }));
|
||||
|
||||
// 模拟数据
|
||||
machineModels.value = [
|
||||
{ value: 'model1', label: '机型1' },
|
||||
{ value: 'model2', label: '机型2' },
|
||||
{ value: 'model3', label: '机型3' }
|
||||
];
|
||||
} catch (error) {
|
||||
console.error('获取机型失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取可用参数列表
|
||||
const fetchAvailableParams = async () => {
|
||||
try {
|
||||
// 实际接口调用:
|
||||
// const response = await $http.get('/api/parameters');
|
||||
// availableParams.value = response.data.map(item => ({
|
||||
// value: item.id,
|
||||
// label: item.name
|
||||
// }));
|
||||
|
||||
// 模拟数据 - 默认使用上面定义的初始数据
|
||||
} catch (error) {
|
||||
console.error('获取参数列表失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 全选/取消全选
|
||||
const handleSelectAll = (val) => {
|
||||
selectedParams.value = val
|
||||
? availableParams.value.map(p => p.value)
|
||||
: [];
|
||||
};
|
||||
|
||||
// 查询数据
|
||||
const handleSearch = async () => {
|
||||
try {
|
||||
currentPage.value = 1;
|
||||
await fetchTableData();
|
||||
} catch (error) {
|
||||
console.error('查询失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 重置查询
|
||||
const handleReset = () => {
|
||||
Object.keys(queryForm).forEach(key => {
|
||||
// 日期字段需要空字符串而非null
|
||||
if (key.includes('Date')) {
|
||||
queryForm[key] = '';
|
||||
} else {
|
||||
queryForm[key] = null;
|
||||
}
|
||||
});
|
||||
selectedParams.value = [];
|
||||
tableData.value = [];
|
||||
currentPage.value = 1;
|
||||
pageSize.value = 50;
|
||||
};
|
||||
// 获取表格数据
|
||||
const fetchTableData = async () => {
|
||||
try {
|
||||
// 实际查询接口调用:
|
||||
// const params = {
|
||||
// ...queryForm,
|
||||
// selectedParams: selectedParams.value.join(','),
|
||||
// page: currentPage.value,
|
||||
// size: pageSize.value
|
||||
// };
|
||||
// const response = await $http.get('/api/query', { params });
|
||||
// tableData.value = response.data.list;
|
||||
// total.value = response.data.total;
|
||||
|
||||
// 模拟数据
|
||||
console.log('调用查询接口:', {
|
||||
...queryForm,
|
||||
selectedParams: selectedParams.value,
|
||||
page: currentPage.value,
|
||||
size: pageSize.value
|
||||
});
|
||||
|
||||
// 模拟响应
|
||||
tableData.value = [
|
||||
{ id: 1, barcode: 'SN123456', result: '通过' },
|
||||
{ id: 2, barcode: 'SN789012', result: '失败' },
|
||||
{ id: 3, barcode: 'SN345678', result: '通过' },
|
||||
{ id: 4, barcode: 'SN901234', result: '通过' },
|
||||
{ id: 5, barcode: 'SN567890', result: '失败' }
|
||||
];
|
||||
total.value = 5;
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取表格数据失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 分页大小变化
|
||||
const handleSizeChange = (newSize) => {
|
||||
pageSize.value = newSize;
|
||||
fetchTableData();
|
||||
};
|
||||
|
||||
// 当前页变化
|
||||
const handleCurrentChange = (newPage) => {
|
||||
currentPage.value = newPage;
|
||||
fetchTableData();
|
||||
};
|
||||
|
||||
// 导出Excel
|
||||
const handleExportExcel = async () => {
|
||||
try {
|
||||
console.log('导出Excel参数:', {
|
||||
...queryForm,
|
||||
selectedParams: selectedParams.value
|
||||
});
|
||||
|
||||
// 实际导出接口调用:
|
||||
// const params = {
|
||||
// ...queryForm,
|
||||
// selectedParams: selectedParams.value.join(',')
|
||||
// };
|
||||
// window.location.href = `/api/export?${new URLSearchParams(params)}`;
|
||||
|
||||
} catch (error) {
|
||||
console.error('导出失败:', error);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.query-container {
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 查询条件区域样式 */
|
||||
.condition-section {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.condition-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.condition-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.condition-item {
|
||||
flex: 1;
|
||||
min-width: 250px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.date-range,
|
||||
.number-range {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.date-separator {
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
/* 参数区域样式 */
|
||||
.param-section {
|
||||
margin-bottom: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
/* 参数列表网格布局 */
|
||||
.param-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.param-item {
|
||||
margin-right: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.param-header {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
margin-bottom: 6px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 按钮区域 - 主要优化点:确保移动端按钮在一行显示 */
|
||||
.action-buttons {
|
||||
margin: 5px 0;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
flex-shrink: 1; /* 允许按钮宽度缩小 */
|
||||
min-width: auto; /* 去除最小宽度限制 */
|
||||
}
|
||||
|
||||
/* 表格区域 */
|
||||
.data-section {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin: 2px 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.collapse-panel {
|
||||
margin-bottom: 1px;
|
||||
border: none;
|
||||
border-radius: 1px;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
:deep(.el-collapse-item__header) {
|
||||
font-weight: bold;
|
||||
padding: 0 2px;
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
:deep(.el-collapse-item__content) {
|
||||
padding: 2px;
|
||||
}
|
||||
:deep(.el-collapse) {
|
||||
--el-collapse-header-height: 25px;
|
||||
}
|
||||
|
||||
/* 移动端适配 */
|
||||
@media (max-width: 768px) {
|
||||
.condition-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.condition-item {
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.param-list {
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.param-item {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
/* 参数列表网格布局 */
|
||||
:deep(.param-grid) {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(123px, 1fr)) !important;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
/* 移动端按钮优化 - 保持一行显示 */
|
||||
.action-buttons {
|
||||
flex-wrap: nowrap; /* 防止按钮换行 */
|
||||
overflow-x: auto; /* 如果按钮过多允许水平滚动 */
|
||||
justify-content: flex-start; /* 从左对齐 */
|
||||
gap: 1px; /* 减小按钮间距 */
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
flex-shrink: 0; /* 不收缩按钮 */
|
||||
padding: 4px 6px; /* 减小按钮内边距 */
|
||||
font-size: 12px; /* 缩小字体 */
|
||||
white-space: nowrap; /* 确保文本不换行 */
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1322
auts_new_mobile.client/src/pages/testlog/index.vue
Normal file
1322
auts_new_mobile.client/src/pages/testlog/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
347
auts_new_mobile.client/src/pages/user/index.vue
Normal file
347
auts_new_mobile.client/src/pages/user/index.vue
Normal file
@@ -0,0 +1,347 @@
|
||||
<template>
|
||||
<div class="form-container">
|
||||
<div>
|
||||
<h2 style="text-align:center">个人中心</h2>
|
||||
<div class="header">
|
||||
<el-button v-if="isEditing"
|
||||
type="danger" round
|
||||
@click="handleEditSave">保存</el-button>
|
||||
<el-button v-if="!isEditing"
|
||||
type="success" round
|
||||
@click="handleEditSave">编辑</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-form ref="formRef"
|
||||
:model="userInfo"
|
||||
:rules="formRules"
|
||||
label-width="83px"
|
||||
:disabled="!isEditing">
|
||||
<!-- 用户名 -->
|
||||
<el-form-item label="用户名" prop="userName">
|
||||
<!--<el-input v-model="userInfo.userName" readonly disabled />-->
|
||||
<el-tag size="large" type="primary">{{ userInfo.userName }}</el-tag>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 公司 -->
|
||||
<el-form-item label="所属公司" prop="userName">
|
||||
<!--<el-input v-model="userInfo.customerName" readonly disabled />-->
|
||||
<el-tag size="large" type="success">{{ userInfo.customerName }}</el-tag>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 微信 -->
|
||||
<el-form-item label="微信" prop="weiXin">
|
||||
<el-input v-model="userInfo.weiXin" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 邮箱 -->
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="userInfo.email" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 手机号 -->
|
||||
<el-form-item label="手机号" prop="mobile">
|
||||
<el-input v-model="userInfo.mobile" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 密码 -->
|
||||
<el-form-item label="修改密码" prop="password">
|
||||
<el-input v-model="userInfo.password"
|
||||
type="password"
|
||||
show-password clearable
|
||||
placeholder="不修改密码则留空" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 确认密码(动态显示) -->
|
||||
<el-form-item v-if="userInfo.password"
|
||||
label="确认密码"
|
||||
prop="confPassword">
|
||||
<el-input v-model="userInfo.confPassword"
|
||||
type="password"
|
||||
show-password clearable
|
||||
placeholder="请确认密码" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 旧密码(动态显示) -->
|
||||
<el-form-item v-if="userInfo.password"
|
||||
label="旧密码"
|
||||
prop="oldPassword">
|
||||
<el-input v-model="userInfo.oldPassword"
|
||||
type="password"
|
||||
show-password clearable
|
||||
placeholder="请输入旧密码" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, inject, computed } from 'vue';
|
||||
import { Toast } from 'tdesign-mobile-vue';
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
|
||||
// 初始化加载数据
|
||||
onMounted(() => {
|
||||
loadingUser()
|
||||
});
|
||||
|
||||
const $http = inject('$http')
|
||||
const router = useRouter()
|
||||
|
||||
|
||||
const userInfo = ref({
|
||||
userName: '',
|
||||
companyId: '',
|
||||
customerName: '',
|
||||
weiXin: '',
|
||||
email: '',
|
||||
mobile: '',
|
||||
password: '',
|
||||
confPassword: '',
|
||||
oldPassword: '',
|
||||
currentPassword: ''
|
||||
})
|
||||
const companies = ref([])
|
||||
// 初始化用户信息
|
||||
const loadingUser = () => {
|
||||
|
||||
$http.post('Company/GetCompanyInfo', {
|
||||
IsAll: true,
|
||||
ID: 0
|
||||
})
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
companies.value = rs.data.response
|
||||
companies.value.forEach(item => {
|
||||
item.label = item.customerName
|
||||
item.value = item.id
|
||||
/* product.options.push({
|
||||
label: item.customerName,
|
||||
value: item.customerName
|
||||
})*/
|
||||
});
|
||||
var formdata = {
|
||||
IsAll: false,
|
||||
ID: localStorage.getItem('uid')
|
||||
}
|
||||
// 调用API获取用户信息
|
||||
$http.post('Users/GetUserInfo', formdata)
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
userInfo.value = rs.data.response
|
||||
const companyMap = new Map(companies.value.map(company => [company.id, company.customerName]));
|
||||
userInfo.value.customerName = companyMap.get(userInfo.value.companyId);
|
||||
//console.log(userInfo.value)
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
})
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
// TODO 头像修改(未做)
|
||||
const openAvatarDialog = (user) => {
|
||||
console.log(user)
|
||||
}
|
||||
|
||||
|
||||
// 重置密码
|
||||
const handleResetPassword = (user) => {
|
||||
// TODO: 调用重置密码API
|
||||
var us = {
|
||||
id: user.id
|
||||
}
|
||||
$http.post('Users/NewPassWord', us)
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
|
||||
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
};
|
||||
|
||||
// 表单状态控制
|
||||
const isEditing = ref(false)
|
||||
const formRef = ref(null)
|
||||
|
||||
|
||||
// 验证规则
|
||||
const validatePassword = (rule, value, callback) => {
|
||||
if (value) {
|
||||
const reg = /^[\w!@#$%^&*()]{6,20}$/
|
||||
if (!reg.test(value)) {
|
||||
callback(new Error('6-20位英文/数字/特殊字符'))
|
||||
} else if (userInfo.value.confPassword && value != userInfo.value.confPassword) {
|
||||
callback(new Error('两次输入密码不一致'))
|
||||
}
|
||||
}
|
||||
callback()
|
||||
}
|
||||
|
||||
const formRules = reactive({
|
||||
email: [
|
||||
{ type: 'email', message: '请输入正确的邮箱格式', trigger: 'blur' }
|
||||
],
|
||||
mobile: [
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
|
||||
],
|
||||
password: [
|
||||
{ validator: validatePassword, trigger: 'blur' }
|
||||
],
|
||||
confPassword: [
|
||||
{ required: true, message: '请再次输入密码', trigger: 'blur' },
|
||||
{
|
||||
validator: (_, value, cb) => {
|
||||
if (value !== userInfo.value.password) {
|
||||
cb(new Error('两次输入密码不一致'))
|
||||
}
|
||||
cb()
|
||||
}, trigger: 'blur'
|
||||
}
|
||||
],
|
||||
oldPassword: [
|
||||
{ required: true, message: '请输入旧密码', trigger: 'blur' }
|
||||
]
|
||||
})
|
||||
|
||||
// 处理编辑/保存
|
||||
const handleEditSave = () => {
|
||||
if (!isEditing.value) {
|
||||
isEditing.value = true
|
||||
return
|
||||
}
|
||||
let form = {
|
||||
Id: userInfo.value.id,
|
||||
UserName: userInfo.value.userName,
|
||||
CompanyId: userInfo.value.companyId,
|
||||
Mobile: userInfo.value.mobile,
|
||||
WeiXin: userInfo.value.weiXin,
|
||||
Email: userInfo.value.email,
|
||||
}
|
||||
//Toast("保存成功!")
|
||||
|
||||
//return
|
||||
if (userInfo.value.password) {
|
||||
$http.post('Login/Login', {
|
||||
username: userInfo.value.userName,
|
||||
password: userInfo.value.oldPassword
|
||||
})
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
$http.post('Users/EditUser', form)
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
let psw = {
|
||||
Id: userInfo.value.id,
|
||||
PlaintextPwd: userInfo.value.confPassword
|
||||
}
|
||||
if (rs.data.isok) {
|
||||
$http.post('Users/ResetPassWord', psw)
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
isEditing.value = false
|
||||
Toast("保存成功!")
|
||||
setTimeout(() => {
|
||||
router.push('/login')
|
||||
}, 233)
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
} else {
|
||||
|
||||
$http.post('Users/EditUser', form)
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
isEditing.value = false
|
||||
Toast("保存成功!")
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.form-container {
|
||||
max-width: 600px;
|
||||
padding: 20px;
|
||||
/*margin: 20px;*/
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 20px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* PC端样式 */
|
||||
@media (min-width: 1024) {
|
||||
|
||||
}
|
||||
</style>
|
||||
722
auts_new_mobile.client/src/pages/usermanage/index.vue
Normal file
722
auts_new_mobile.client/src/pages/usermanage/index.vue
Normal file
@@ -0,0 +1,722 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<!-- 用户列表 -->
|
||||
<!-- <div>
|
||||
<div><t-input label="搜索" placeholder="搜索格式:公司 用户名" @change="SearchUsers" /></div>
|
||||
|
||||
<div>
|
||||
<t-dropdown-menu style="height:32px;border:none">
|
||||
<t-dropdown-item :options="product.options" :value="product.value" @change="databaseChanges" :disabled="disDbDrop" />
|
||||
</t-dropdown-menu>
|
||||
</div>
|
||||
</div>-->
|
||||
<div style="display: flex; align-items: center; gap: 10px">
|
||||
<!-- 输入框部分 - 自适应剩余宽度 -->
|
||||
<div style="flex: 1">
|
||||
<t-input label="搜索"
|
||||
placeholder="公司 用户名"
|
||||
clearable
|
||||
@change="SearchUsers" />
|
||||
</div>
|
||||
|
||||
<!-- 下拉菜单部分 - 固定宽度 -->
|
||||
<div style="width: 90px">
|
||||
<t-dropdown-menu style="height:32px; border:none; width:100%">
|
||||
<t-dropdown-item :options="product.options"
|
||||
:value="product.value"
|
||||
@change="databaseChanges" />
|
||||
</t-dropdown-menu>
|
||||
</div>
|
||||
</div>
|
||||
<t-list :async-loading="loading">
|
||||
<div v-for="user in nowUsers" :key="user.id" class="user-list-div" @click="openEditDialog(user)"
|
||||
style="position: relative; width: auto; padding: 5px; height: 55px; border-radius: 3px; margin: 2px;">
|
||||
<!-- 头像 -->
|
||||
<t-avatar class="avatar-example--small external-class-content" shape="circle" size="44px" style="margin-right: 10px;" @click.stop="openAvatarDialog(user)">{{ user.userName[0] + user.userName[1] }}</t-avatar>
|
||||
<!-- 主体 -->
|
||||
<span class="user-name" style="position: absolute; top: 3px; left: 55px; font-size: 12px; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">用户:<span style="font-size: 14px; font-weight: 500">{{ user.userName }}</span></span>
|
||||
<span class="user-company" style="position: absolute; bottom: 3px; left: 55px; font-size: 12px; ">公司:<span style="font-size:15px;font-weight:bold">{{ user.customerName }}</span></span>
|
||||
<!-- 操作 -->
|
||||
<span class="user-actions" style="position: absolute; bottom: 3px; right: 0px;">
|
||||
<t-button theme="danger"
|
||||
@click.stop="openDelDialog(user)"
|
||||
variant="outline"
|
||||
:disabled="!((user.userName != 'MomoWen') && (user.userName != 'Admin'))"
|
||||
size="extra-small">删除</t-button>
|
||||
<span> </span>
|
||||
<!-- <t-button theme="primary"
|
||||
@click="openEditDialog(user)"
|
||||
variant="outline"
|
||||
size="extra-small">修改</t-button>-->
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</t-list>
|
||||
<!-- 删除对话框 -->
|
||||
<t-dialog v-model:visible="isDelDialogVis"
|
||||
close-on-overlay-click
|
||||
content="操作不可逆,确认删除?"
|
||||
cancel-btn="取消"
|
||||
:confirm-btn="{ content: '确认删除', theme: 'danger' }"
|
||||
@confirm="isDelUser"
|
||||
@cancel="isDelUserCancel"
|
||||
@close="isDelUserCancel">
|
||||
</t-dialog>
|
||||
<!-- 编辑/新增对话框 -->
|
||||
<t-dialog v-model:visible="editDialogVisible"
|
||||
:header="currentUser.id ? '编辑用户' : '新增用户'"
|
||||
cancel-btn="取消"
|
||||
confirm-btn="保存"
|
||||
@confirm="handleSave">
|
||||
<t-form>
|
||||
<t-form-item label="用户名">
|
||||
<t-input v-model="currentUser.userName" :disabled="!!currentUser.id" />
|
||||
</t-form-item>
|
||||
<t-form-item label="所属公司">
|
||||
<!--<t-input v-model="currentUser.company" />-->
|
||||
<t-dropdown-menu>
|
||||
<t-dropdown-item :options="companies"
|
||||
:value="currentUser.companyId"
|
||||
zIndex="19999"
|
||||
@change="currentUserComChange" />
|
||||
</t-dropdown-menu>
|
||||
</t-form-item>
|
||||
<t-form-item label="手机号">
|
||||
<t-input v-model="currentUser.mobile" />
|
||||
</t-form-item>
|
||||
<t-form-item label="微信号">
|
||||
<t-input v-model="currentUser.weiXin" />
|
||||
</t-form-item>
|
||||
<t-form-item label="Email">
|
||||
<t-input v-model="currentUser.email" />
|
||||
</t-form-item>
|
||||
|
||||
<t-form-item label="权限管理">
|
||||
<div class="permission-group-btn">
|
||||
<t-button size="extra-small"
|
||||
theme="primary"
|
||||
variant="outline"
|
||||
:disabled="!((currentUser.userName != 'MomoWen') && (currentUser.userName != 'Admin'))"
|
||||
@click="permissionBtn('pages')">展开权限菜单</t-button>
|
||||
</div>
|
||||
|
||||
</t-form-item>
|
||||
<t-checkbox :checked="currentUser.resetpsw"
|
||||
:label="currentUser.id ? '重置密码' : '初始化密码'"
|
||||
:disabled="currentUser.id ? false : true"
|
||||
icon="rectangle"/>
|
||||
</t-form>
|
||||
</t-dialog>
|
||||
|
||||
<!-- 页面权限弹出层 -->
|
||||
<t-popup v-model:visible="popupVisible.pages"
|
||||
placement="bottom"
|
||||
:style="{ height: '92%' }"
|
||||
header="权限设置">
|
||||
<!-- 数据库权限 -->
|
||||
<div style="padding:10px">
|
||||
<t-checkbox :block="false" v-model="allSelected" label="全选" @change="checkboxGroupChangeAll" />
|
||||
<t-checkbox-group class="box" borderless @change="checkboxGroupChange" v-model="selectedCheckboxes">
|
||||
<span v-for="(item, index) in dbOptions" :key="index">
|
||||
<t-checkbox :block="false" :value="item.label" :label="item.label" />
|
||||
<span> </span>
|
||||
</span>
|
||||
</t-checkbox-group>
|
||||
|
||||
</div>
|
||||
<!-- 表权限 -->
|
||||
<div style="padding:10px">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="popup-footer">
|
||||
<t-button theme="default" size="extra-small" @click="popupVisible.pages = false"> 取 消 </t-button>
|
||||
<div style="display: inline-block; width:10px"></div>
|
||||
<t-button theme="primary" size="extra-small" @click="handleFormConfirm">确 认</t-button>
|
||||
</div>
|
||||
</t-popup>
|
||||
|
||||
|
||||
<!-- 新增 悬浮按钮 -->
|
||||
<t-fab icon="+"
|
||||
draggable="true"
|
||||
style="font-size:25px"
|
||||
@click="openCreateDialog"
|
||||
class="fab-button" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, watch, inject, computed } from 'vue';
|
||||
import { Toast } from 'tdesign-mobile-vue';
|
||||
import { ElTree } from 'element-plus'
|
||||
|
||||
|
||||
// 初始化加载数据
|
||||
onMounted(() => {
|
||||
loading.value = true;
|
||||
loadingUsers()
|
||||
loading.value = false;
|
||||
});
|
||||
// 用户数据
|
||||
const users = ref([]);
|
||||
const loading = ref(true);
|
||||
const tableData = reactive([]);
|
||||
|
||||
// 对话框控制
|
||||
const editDialogVisible = ref(false);
|
||||
const currentUser = reactive({
|
||||
id: 0,
|
||||
userName: '',
|
||||
companyId: null,
|
||||
companyName: '',
|
||||
mobile: '',
|
||||
weiXin: '',
|
||||
email: '',
|
||||
resetpsw: true,
|
||||
permissions: []
|
||||
});
|
||||
const companies = ref([]);
|
||||
|
||||
const $http = inject('$http')
|
||||
|
||||
|
||||
const dbNames = ref([])
|
||||
// 初始化用户列表
|
||||
const loadingUsers = () => {
|
||||
var formdata = {
|
||||
IsAll: true,
|
||||
ID: 0
|
||||
}
|
||||
|
||||
// 调用API获取用户列表
|
||||
$http.post('Company/GetCompanyInfo', formdata)
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
companies.value = rs.data.response
|
||||
product.options = [{ value: '全选', label: '全选' }]
|
||||
companies.value.forEach(item => {
|
||||
item.label = item.customerName
|
||||
item.value = item.id
|
||||
product.options.push({
|
||||
label: item.customerName,
|
||||
value: item.customerName
|
||||
})
|
||||
});
|
||||
//console.log(product)
|
||||
$http.post('Users/GetUserInfo', formdata)
|
||||
.then(rsu => {
|
||||
if (rsu.data.isok) {
|
||||
const companyMap = new Map(companies.value.map(company => [company.id, company.customerName]));
|
||||
users.value = rsu.data.response.sort((a, b) => {
|
||||
const nameA = a.userName.toUpperCase(); // 转换为大写以进行不区分大小写的比较
|
||||
const nameB = b.userName.toUpperCase(); // 转换为大写以进行不区分大小写的比较
|
||||
if (nameA < nameB) {
|
||||
return -1;
|
||||
}
|
||||
if (nameA > nameB) {
|
||||
return 1;
|
||||
}
|
||||
// 如果userName相同,则保持原始顺序(稳定排序)
|
||||
return 0;
|
||||
});
|
||||
//console.log(users.value)
|
||||
users.value.forEach(user => {
|
||||
// 检查companyId是否存在于公司映射中
|
||||
if (companyMap.has(user.companyId)) {
|
||||
// 如果存在,将公司名称添加到用户对象中
|
||||
user.customerName = companyMap.get(user.companyId);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (search.value) {
|
||||
SearchUsers(search.value)
|
||||
} else {
|
||||
nowUsers.value = JSON.parse(JSON.stringify(users.value))
|
||||
}
|
||||
|
||||
} else {
|
||||
Toast(rsu.data.message)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
})
|
||||
let requestBody = {
|
||||
key: "getDbName",
|
||||
CmdType: "sys",
|
||||
DBQueryData: []
|
||||
}
|
||||
$http.post('DataHandle/R_InfoCommon', requestBody)
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
dbNames.value = [];
|
||||
rs.data.response.forEach(item => {
|
||||
if (item.Database.slice(0, 3) == "uts" && item.Database != "uts_manage") {
|
||||
dbNames.value.push(item.Database)
|
||||
//console.log(dbNames.value)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
const product = reactive({
|
||||
value: '全选',
|
||||
options: [
|
||||
{
|
||||
value: '全选',
|
||||
label: '全选'
|
||||
}
|
||||
],
|
||||
})
|
||||
// 搜索
|
||||
const databaseChanges = (db) => {
|
||||
if (db == "全选") {
|
||||
SearchUsers("")
|
||||
} else {
|
||||
SearchUsers(db)
|
||||
}
|
||||
|
||||
}
|
||||
const search = ref('')
|
||||
const nowUsers = ref([]);
|
||||
const SearchUsers = (s) => {
|
||||
search.value = s
|
||||
if (s) {
|
||||
//nowUsers.value = users.value.filter(user => user.userName.includes(s));
|
||||
const keywords = s.trim().split(/\s+/); // 按空格分割关键词
|
||||
nowUsers.value = users.value.filter(user =>
|
||||
keywords.every(key =>
|
||||
(user.userName || '').toLowerCase().includes(key.toLowerCase()) ||
|
||||
(user.customerName || '').toLowerCase().includes(key.toLowerCase())
|
||||
)
|
||||
);
|
||||
} else {
|
||||
nowUsers.value = JSON.parse(JSON.stringify(users.value))
|
||||
}
|
||||
}
|
||||
|
||||
// 打开编辑公司对话框
|
||||
const currentUserComChange = (cid) => {
|
||||
//console.log(cid)
|
||||
const usc = companies.value.forEach(item => {
|
||||
if (cid == item.id) {
|
||||
// 将公司名称和id添加到用户对象中
|
||||
currentUser.companyId = item.id
|
||||
currentUser.companyName = item.companyName
|
||||
}
|
||||
})
|
||||
};
|
||||
// 打开编辑对话框
|
||||
const openEditDialog = (user) => {
|
||||
Object.assign(currentUser, user);
|
||||
currentUser.resetpsw = false;
|
||||
editDialogVisible.value = true;
|
||||
};
|
||||
|
||||
// TODO 头像修改(未做)
|
||||
const openAvatarDialog = (user) => {
|
||||
//console.log(user)
|
||||
}
|
||||
|
||||
const isDelDialogVis = ref(false)
|
||||
// 打开删除用户确认框
|
||||
const openDelDialog = (user) => {
|
||||
isDelDialogVis.value = true;
|
||||
Object.assign(currentUser, user);
|
||||
};
|
||||
// 取消删除
|
||||
const isDelUserCancel = () => {
|
||||
isDelDialogVis.value = false;
|
||||
}
|
||||
// 确认删除
|
||||
const isDelUser = () => {
|
||||
// TODO 删除用户API
|
||||
var us = {
|
||||
id: currentUser.id
|
||||
}
|
||||
$http.post('Users/DelUser', us)
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
Toast({
|
||||
className: 'toast-root--success',
|
||||
theme: 'success',
|
||||
direction: 'column',
|
||||
message: '删除成功!',
|
||||
});
|
||||
loadingUsers()
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// 打开新增对话框
|
||||
const openCreateDialog = () => {
|
||||
Object.assign(currentUser, {
|
||||
id: 0,
|
||||
userName: '',
|
||||
companyId: null,
|
||||
companyName: '',
|
||||
mobile: '',
|
||||
weiXin: '',
|
||||
email: '',
|
||||
resetpsw: true,
|
||||
permissions: []
|
||||
});
|
||||
editDialogVisible.value = true;
|
||||
};
|
||||
|
||||
const selectedCheckboxes = ref([]);
|
||||
|
||||
const allSelected = computed({
|
||||
get() {
|
||||
return selectedCheckboxes.value.includes('all');
|
||||
},
|
||||
set(isChecked) {
|
||||
if (isChecked) {
|
||||
selectedCheckboxes.value = ['all', ...dbOptions.value.map(opt => opt.label)];
|
||||
} else {
|
||||
selectedCheckboxes.value = [];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const checkboxGroupChange = (check) => {
|
||||
selectedCheckboxes.value = check;
|
||||
if (check.length === dbOptions.value.length && !check.includes('all')) {
|
||||
selectedCheckboxes.value = ['all', ...check];
|
||||
} else {
|
||||
selectedCheckboxes.value = check.filter(item => item !== 'all');
|
||||
}
|
||||
};
|
||||
|
||||
const checkboxGroupChangeAll = (isChecked) => {
|
||||
allSelected.value = isChecked;
|
||||
};
|
||||
/* const checkboxGroupChange = (check) => {
|
||||
const selected = new Set(check); // 使用Set便于判断存在性
|
||||
const hasAll = selected.has('all');
|
||||
const labels = dbOptions.value.map(opt => opt.label);
|
||||
const filtered = check.filter(item => item !== 'all');
|
||||
|
||||
// 情况1:选中了"all"(可能单独或与其他一起)
|
||||
if (hasAll) {
|
||||
// 如果当前选中仅"all",则全选
|
||||
if (check.length === 1) {
|
||||
selectedCheckboxes.value = [...labels, 'all'];
|
||||
}
|
||||
// 如果已全选且包含"all",则维持
|
||||
else if (filtered.length === labels.length) {
|
||||
selectedCheckboxes.value = [...labels, 'all'];
|
||||
}
|
||||
// 如果选中了"all"但未全选,移除"all"
|
||||
else {
|
||||
selectedCheckboxes.value = filtered;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 情况2:未选中"all",但勾选了全部选项
|
||||
if (filtered.length === labels.length) {
|
||||
selectedCheckboxes.value = [...labels, 'all']; // 自动补全"all"
|
||||
}
|
||||
// 情况3:正常选择
|
||||
else {
|
||||
selectedCheckboxes.value = filtered;
|
||||
}
|
||||
}*/
|
||||
// 保存处理
|
||||
const handleSave = () => {
|
||||
|
||||
if (currentUser.id != 0) {
|
||||
let us = {
|
||||
Id: currentUser.id,
|
||||
UserName: currentUser.userName,
|
||||
CompanyId: currentUser.companyId,
|
||||
Mobile: currentUser.mobile,
|
||||
WeiXin: currentUser.weiXin,
|
||||
Email: currentUser.email,
|
||||
IsValid: true
|
||||
}
|
||||
// TODO: 调用更新用户API
|
||||
$http.post('Users/EditUser', us)
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
|
||||
if (currentUser.resetpsw == true) {
|
||||
handleResetPassword(currentUser)
|
||||
}
|
||||
Toast({
|
||||
className: 'toast-root--success',
|
||||
theme: 'success',
|
||||
direction: 'column',
|
||||
message: '修改成功!',
|
||||
});
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
.finally(() => {
|
||||
loadingUsers()
|
||||
})
|
||||
|
||||
} else {
|
||||
// TODO: 调用新增用户API
|
||||
let us = {
|
||||
Id: currentUser.id,
|
||||
UserName: currentUser.userName,
|
||||
CompanyId: currentUser.companyId,
|
||||
Mobile: currentUser.mobile,
|
||||
WeiXin: currentUser.weiXin,
|
||||
Email: currentUser.email
|
||||
}
|
||||
$http.post('Users/AddUser', us)
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
|
||||
Toast({
|
||||
className: 'toast-root--success',
|
||||
theme: 'success',
|
||||
direction: 'column',
|
||||
message: '新增成功!',
|
||||
});
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
.finally(() => {
|
||||
loadingUsers()
|
||||
})
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// 重置密码
|
||||
const handleResetPassword = (user) => {
|
||||
// TODO: 调用重置密码API
|
||||
var us = {
|
||||
id: user.id
|
||||
}
|
||||
$http.post('Users/NewPassWord', us)
|
||||
.then(rs => {
|
||||
//console.log(rs)
|
||||
if (rs.data.isok) {
|
||||
|
||||
|
||||
} else {
|
||||
Toast(rs.data.message)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// 处理错误情况
|
||||
console.error(error);
|
||||
})
|
||||
};
|
||||
|
||||
// 弹出层控制
|
||||
const popupVisible = reactive({
|
||||
pages: false,
|
||||
model: false,
|
||||
db: false,
|
||||
});
|
||||
|
||||
// 页面权限表单数据
|
||||
const formItems = ref([
|
||||
{ label: '/home', isOpen: true },
|
||||
{ label: '/user', isOpen: true },
|
||||
{ label: '/usermanage', isOpen: true },
|
||||
{ label: '/scopemanage', isOpen: true },
|
||||
/* { label: '/', isOpen: true },
|
||||
{ label: '/', isOpen: true },
|
||||
{ label: '/', isOpen: true },*/
|
||||
]);
|
||||
|
||||
// 数据库权限多选数据
|
||||
const dbOptions = ref([]);
|
||||
|
||||
|
||||
// 按钮点击处理
|
||||
const permissionBtn = (type) => {
|
||||
switch (type) {
|
||||
case 'pages':
|
||||
// TODO: 这里可以添加初始化表单数据的接口调用
|
||||
dbOptions.value = []
|
||||
dbNames.value.forEach(db => {
|
||||
let dbv = { label: db, isOpen: false }
|
||||
dbOptions.value.push(dbv)
|
||||
})
|
||||
popupVisible.pages = true;
|
||||
break;
|
||||
case 'db':
|
||||
// TODO: 这里可以添加初始化多选数据的接口调用
|
||||
dbOptions.value = []
|
||||
dbNames.value.forEach(db => {
|
||||
let dbv = { label: db, isOpen: false }
|
||||
dbOptions.value.push(dbv)
|
||||
})
|
||||
popupVisible.db = true;
|
||||
break;
|
||||
case 'model':
|
||||
// TODO: 这里可以添加初始化多选数据的接口调用
|
||||
popupVisible.model = true;
|
||||
break;
|
||||
default:
|
||||
console.error('未知操作类型');
|
||||
}
|
||||
};
|
||||
// 全选或取消全选
|
||||
const isAllOpenPages = (tof) => {
|
||||
if (tof) {
|
||||
formItems.value.forEach(item => {
|
||||
item.isOpen = true;
|
||||
});
|
||||
} else {
|
||||
formItems.value.forEach(item => {
|
||||
item.isOpen = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
// 全选或取消全选
|
||||
const isAllOpenDB = (tof) => {
|
||||
//console.log(tof)
|
||||
if (tof) {
|
||||
dbOptions.value.forEach(item => {
|
||||
item.isOpen = true;
|
||||
});
|
||||
} else {
|
||||
dbOptions.value.forEach(item => {
|
||||
item.isOpen = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
// 表单确认处理
|
||||
const handleFormConfirm = () => {
|
||||
// TODO: 调用接口保存 formItems.value
|
||||
|
||||
//console.log('提交表单数据:', formItems.value);
|
||||
popupVisible.pages = false;
|
||||
};
|
||||
|
||||
// 机型权限确认处理
|
||||
const handleModelConfirm = () => {
|
||||
// TODO: 调用接口保存 selectedModels.value
|
||||
//console.log('选中机型:', selectedModels.value);
|
||||
popupVisible.model = false;
|
||||
};
|
||||
// 数据库权限确认处理
|
||||
const handleDBConfirm = () => {
|
||||
// TODO: 调用接口保存 selectedDBs.value
|
||||
//console.log('选中数据库:', selectedDBs.value);
|
||||
popupVisible.db = false;
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.fab-button {
|
||||
position: fixed;
|
||||
bottom: 32px;
|
||||
right: 16px;
|
||||
}
|
||||
|
||||
/* .t-dialog__body {
|
||||
overflow-y: auto !important;
|
||||
}
|
||||
*/
|
||||
|
||||
.t-list {
|
||||
z-index: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* 底部操作栏 */
|
||||
.popup-footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 10px;
|
||||
display: block;
|
||||
box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* 多选组样式 */
|
||||
/*.checkbox-group {
|
||||
padding: 16px;
|
||||
height: calc(100% - 60px);
|
||||
overflow-y: auto;
|
||||
}*/
|
||||
.t-dropdown-item {
|
||||
top: 145px !important
|
||||
}
|
||||
|
||||
.user-list-div {
|
||||
border: 1px solid #E9E9E960;
|
||||
background-color: #80808030;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.user-list-div:hover {
|
||||
border-color: #409eff;
|
||||
background-color: #80808010;
|
||||
/* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); 可选阴影 */
|
||||
}
|
||||
|
||||
|
||||
/* PC端样式 */
|
||||
@media (min-width: 768px) {
|
||||
}
|
||||
</style>
|
||||
100
auts_new_mobile.client/src/router/index.js
Normal file
100
auts_new_mobile.client/src/router/index.js
Normal file
@@ -0,0 +1,100 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
import Home from '../pages/home/index.vue';
|
||||
import Login from '../pages/login/index.vue';
|
||||
import User from '../pages/user/index.vue';
|
||||
import UserManage from '../pages/userManage/index.vue';
|
||||
import LoginLog from '../pages/loginlog/index.vue';
|
||||
import DbManage from '../pages/dbmanage/index.vue';
|
||||
import InputColorbox from '../pages/inputimportantinfo/index.vue';
|
||||
import StationLog from '../pages/stationlog/index.vue';
|
||||
import TestLog from '../pages/testlog/index.vue';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
// 动态重定向到登录页或主页
|
||||
redirect: () => {
|
||||
const isAuthenticated = localStorage.getItem('token');
|
||||
return isAuthenticated ? '/home' : '/login';
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: '登录',
|
||||
component: Login
|
||||
},
|
||||
{
|
||||
path: '/home',
|
||||
name: '主页',
|
||||
component: Home,
|
||||
meta: { requiresAuth: true } // 需要认证的路由
|
||||
},
|
||||
{
|
||||
path: '/user',
|
||||
name: '个人中心',
|
||||
component: User,
|
||||
meta: { requiresAuth: true } // 需要认证的路由
|
||||
},
|
||||
{
|
||||
path: '/usermanage',
|
||||
name: '用户管理',
|
||||
component: UserManage,
|
||||
meta: { requiresAuth: true } // 需要认证的路由
|
||||
},
|
||||
{
|
||||
path: '/loginlog',
|
||||
name: '用户日志',
|
||||
component: LoginLog,
|
||||
meta: { requiresAuth: true } // 需要认证的路由
|
||||
},
|
||||
{
|
||||
path: '/dbmanage',
|
||||
name: '数据库管理',
|
||||
component: DbManage,
|
||||
meta: { requiresAuth: true } // 需要认证的路由
|
||||
},
|
||||
{
|
||||
path: '/inputcolorbox',
|
||||
name: '数据导入',
|
||||
component: InputColorbox,
|
||||
meta: { requiresAuth: true } // 需要认证的路由
|
||||
},
|
||||
{
|
||||
path: '/stationlog',
|
||||
name: '数据导入',
|
||||
component: StationLog,
|
||||
meta: { requiresAuth: true } // 需要认证的路由
|
||||
},
|
||||
{
|
||||
path: '/testlog',
|
||||
name: '测试数据',
|
||||
component: TestLog,
|
||||
meta: { requiresAuth: true } // 需要认证的路由
|
||||
},
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes
|
||||
});
|
||||
|
||||
|
||||
// 路由守卫
|
||||
router.beforeEach((to, from, next) => {
|
||||
const isAuthenticated = localStorage.getItem('token') // 用localStorage存储token
|
||||
const username = localStorage.getItem('username') // 获取用户名
|
||||
|
||||
if (to.meta.requiresAuth && !isAuthenticated) {
|
||||
// 如果需要认证且没有token,则跳转到登录页面
|
||||
next('/login')
|
||||
} else if ((to.path == '/usermanage' || to.path == '/scopemanage') && (username != 'Admin' && username != 'MomoWen')) {
|
||||
// 如果访问的是用户管理页面但用户名不是Admin,则跳转到主页
|
||||
next('/home')
|
||||
} else {
|
||||
// 其他情况正常放行
|
||||
sessionStorage.setItem('currentRoute', to.fullPath)
|
||||
next()
|
||||
}
|
||||
})
|
||||
|
||||
export default router;
|
||||
10
auts_new_mobile.client/vite.config.js
Normal file
10
auts_new_mobile.client/vite.config.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import plugin from '@vitejs/plugin-vue';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [plugin()],
|
||||
server: {
|
||||
port: 8809,
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user