大屏项目初始化

This commit is contained in:
2026-03-05 22:32:39 +08:00
parent 6cd69428af
commit 3ee4fbfd8c
26 changed files with 198 additions and 153 deletions

View File

@@ -12,6 +12,7 @@
"echarts": "^6.0.0",
"element-plus": "^2.13.2",
"file-saver": "^2.0.5",
"pinia": "^3.0.4",
"v-scale-screen": "^2.3.0",
"vue": "^3.5.28",
"vue-router": "^5.0.3",
@@ -57,7 +58,6 @@
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@@ -1404,7 +1404,6 @@
"resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz",
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/lodash": "*"
}
@@ -1898,7 +1897,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -2639,15 +2637,13 @@
"version": "4.17.23",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.23.tgz",
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/lodash-es": {
"version": "4.17.23",
"resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.23.tgz",
"integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/lodash-unified": {
"version": "1.0.3",
@@ -2875,6 +2871,66 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pinia": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz",
"integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
"license": "MIT",
"dependencies": {
"@vue/devtools-api": "^7.7.7"
},
"funding": {
"url": "https://github.com/sponsors/posva"
},
"peerDependencies": {
"typescript": ">=4.5.0",
"vue": "^3.5.11"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/pinia/node_modules/@vue/devtools-api": {
"version": "7.7.9",
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz",
"integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==",
"license": "MIT",
"dependencies": {
"@vue/devtools-kit": "^7.7.9"
}
},
"node_modules/pinia/node_modules/@vue/devtools-kit": {
"version": "7.7.9",
"resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz",
"integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==",
"license": "MIT",
"dependencies": {
"@vue/devtools-shared": "^7.7.9",
"birpc": "^2.3.0",
"hookable": "^5.5.3",
"mitt": "^3.0.1",
"perfect-debounce": "^1.0.0",
"speakingurl": "^14.0.1",
"superjson": "^2.2.2"
}
},
"node_modules/pinia/node_modules/@vue/devtools-shared": {
"version": "7.7.9",
"resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz",
"integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==",
"license": "MIT",
"dependencies": {
"rfdc": "^1.4.1"
}
},
"node_modules/pinia/node_modules/perfect-debounce": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
"license": "MIT"
},
"node_modules/pkg-types": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.3.0.tgz",
@@ -3203,7 +3259,6 @@
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.27.0",
"fdir": "^6.5.0",
@@ -3382,7 +3437,6 @@
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.28.tgz",
"integrity": "sha512-BRdrNfeoccSoIZeIhyPBfvWSLFP4q8J3u8Ju8Ug5vu3LdD+yTM13Sg4sKtljxozbnuMu1NB1X5HBHRYUzFocKg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@vue/compiler-dom": "3.5.28",
"@vue/compiler-sfc": "3.5.28",

View File

@@ -13,6 +13,7 @@
"echarts": "^6.0.0",
"element-plus": "^2.13.2",
"file-saver": "^2.0.5",
"pinia": "^3.0.4",
"v-scale-screen": "^2.3.0",
"vue": "^3.5.28",
"vue-router": "^5.0.3",

View File

@@ -14,22 +14,11 @@ export function getHomeUserList(params) {
/**
* 保存用户信息列表
*/
export function getHomeUserSave(params) {
export function getHomeUserSave(data) {
return request({
url: '/biz/homeUser/save',
method: 'post',
params: params
})
}
/**
* 修改用户信息列表
*/
export function getHomeUserUpdate(params) {
return request({
url: '/biz/homeUser/update',
method: 'post',
params: params
data
})
}

View File

@@ -19,7 +19,9 @@ import { ref, reactive, onUnmounted, nextTick } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { updatePasswd } from '@/api/user'
const LoginUser = ref(JSON.parse(localStorage.getItem("loginUser")) || {});
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
const emit = defineEmits(['success'])
const pwdFormRef = ref(null)
@@ -104,7 +106,7 @@ const submitForm = async () => {
const reqParams = {
oldPasswd :pwdForm.oldPassword,
password : pwdForm.newPassword,
userId : LoginUser.value?.userId || '',
userId : userStore.loginUser?.userId || '',
}
const res = await updatePasswd(reqParams);
await new Promise(res => setTimeout(res, 800))

View File

@@ -19,7 +19,7 @@
<el-dropdown trigger="click" @command="handleCommand">
<div class="user-info">
<el-avatar bg-color="#409eff"><el-icon><User /></el-icon></el-avatar>
<span class="user-name">{{ LoginUser?.uname }}</span>
<span class="user-name">{{ userStore.loginUser?.uname }}</span>
<el-icon><ArrowDown /></el-icon>
</div>
<template #dropdown>
@@ -91,7 +91,7 @@
</div>
</el-aside>
<el-container class="main-container">
<el-main class="main-container">
<div class="tabs-container">
<div class="tabs-wrapper">
<div
@@ -131,12 +131,12 @@
</div>
</div>
<el-main class="content-container">
<div class="content-container">
<div class="content-wrapper">
<router-view />
</div>
</el-main>
</el-container>
</div>
</el-main>
</el-container>
<el-dialog
@@ -166,10 +166,12 @@ import { ElMessage, ElMessageBox } from 'element-plus'
import { getUserMenuList } from '@/api/bizMenu'
import LogoImg from '@/assets/logo.png'
import EditPswd from './components/editPswd.vue'
import { useUserStore } from '@/stores/user'
const isMounted = ref(true)
const userStore = useUserStore()
const isMounted = ref(false)
const systemTitle = ref("myPro管理系统")
const LoginUser = ref(JSON.parse(localStorage.getItem("loginUser")) || {});
const router = useRouter()
const route = useRoute()
@@ -308,7 +310,7 @@ const handleFullScreen = () => {
ElMessage.success('已退出全屏模式');
}
} catch (error) {
ElMessage.error(error);
ElMessage.error('全屏操作失败,请检查浏览器权限');
}
}
@@ -327,8 +329,7 @@ const handleCommand = (cmd) => {
showClose: false
})
.then(() => {
localStorage.removeItem('loginUser');
localStorage.removeItem('token');
userStore.logout();
ElMessage.success('退出成功');
router.push('/login');
})
@@ -377,8 +378,7 @@ const handlePwdModifySuccess = () => {
if (!isMounted.value) return
closeEditPwdDialog();
ElMessage.success('密码修改成功,请重新登录');
localStorage.removeItem('loginUser');
localStorage.removeItem('token');
userStore.logout();
router.push('/login');
}
@@ -387,6 +387,7 @@ onUnmounted(() => {
})
onMounted(() => {
isMounted.value = true
getMenuList();
watch(() => route.path, (newPath) => {
if (!isMounted.value) return
@@ -583,11 +584,12 @@ onMounted(() => {
}
.main-container {
height: calc(100vh - 60px);
height: calc(100vh - 60px) !important;
display: flex;
flex-direction: column;
background: rgba(248, 249, 250, 0.9) !important;
overflow: hidden;
padding: 0 !important;
}
.tabs-container {

View File

@@ -1,18 +1,20 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import VScaleScreen from 'v-scale-screen'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import 'element-plus/dist/index.css'
import * as echarts from 'echarts'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import "@/assets/font/pangshizidao.ttf";
const app = createApp(App)
// 注册路由
app.use(createPinia())
app.use(router)
app.use(ElementPlus, {
locale: zhCn,

View File

@@ -4,6 +4,8 @@ import Page404 from '@/views/error/404.vue'
import Layout from '@/components/Layout/index.vue'
import Dashboard from '@/views/desktop/index.vue'
import bigScreen from '@/views/screen/index.vue'
import { useUserStore } from '@/stores/user'
import { ElMessage } from 'element-plus'
const modules = import.meta.glob('../views/**/{index,list}.vue', {
eager: false,
@@ -90,22 +92,18 @@ const router = createRouter({
}
})
// 优化路由守卫适配hash模式增强容错
router.beforeEach((to, from, next) => {
// 1. 公开页面直接放行(登录页)
if (to.meta.isPublic) {
next()
return
}
// 2. 非公开页面校验token兼容hash模式
const userStore = useUserStore()
try {
const token = localStorage.getItem('token')
const isValidToken = token.trim().length > 0
if (isValidToken) {
if (userStore.isLoggedIn()) {
next()
} else {
ElMessage.warning('请先登录后再操作!')
next({
path: '/login',
query: { redirect: to.fullPath },
@@ -114,6 +112,7 @@ router.beforeEach((to, from, next) => {
}
} catch (error) {
console.error('路由守卫校验失败:', error)
ElMessage.error('登录状态校验失败,请重新登录!')
next({
path: '/login',
replace: true

View File

@@ -0,0 +1,32 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useUserStore = defineStore('user', () => {
const token = ref(localStorage.getItem('token') || '')
const loginUser = ref(JSON.parse(localStorage.getItem('loginUser')) || null)
const login = (userInfo) => {
token.value = userInfo.token
loginUser.value = userInfo.loginUser
localStorage.setItem('token', token.value)
localStorage.setItem('loginUser', JSON.stringify(loginUser.value))
}
const logout = () => {
token.value = ''
loginUser.value = null
localStorage.removeItem('token')
localStorage.removeItem('loginUser')
}
const isLoggedIn = () => {
return !!token.value
}
return {
token,
loginUser,
login,
logout,
isLoggedIn
}
})

View File

@@ -22,7 +22,7 @@
type="password"
placeholder="请输入密码"
size="large"
show-password
show-password
prefix-icon="Lock"
></el-input>
</el-form-item>
@@ -33,7 +33,7 @@
class="login-btn"
size="small"
:loading="isLoading"
round
round
>
登录
</el-button>
@@ -47,10 +47,14 @@
<script setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { useRouter } from 'vue-router'
import { useRouter, useRoute } from 'vue-router'
import { login } from '@/api/user'
import { useUserStore } from '@/stores/user'
const router = useRouter()
const route = useRoute()
const userStore = useUserStore()
const loginFormRef = ref(null)
const loginForm = ref({
username: '',
@@ -67,16 +71,16 @@ const handleLogin = async () => {
await loginFormRef.value.validate();
isLoading.value = true;
const res = await login(loginForm.value);
localStorage.setItem('token', res.token);
localStorage.setItem('loginUser', JSON.stringify(res.loginUser));
ElMessage.success('登录成功!');
userStore.login(res);
ElMessage.success('登录成功!');
const redirect = route.query.redirect || '/dashboard';
setTimeout(() => {
router.push('/dashboard');
router.push(redirect);
}, 300);
} catch (error) {
console.log(error);
}finally{
isLoading.value = false;
} finally {
isLoading.value = false;
}
}
</script>

View File

@@ -36,9 +36,9 @@
placeholder="请选择性别"
clearable
>
<el-option label="男" value="" />
<el-option label="女" value="" />
<el-option label="未知" value="未知" />
<el-option label="男" value="0" />
<el-option label="女" value="1" />
<el-option label="未知" value="9" />
</el-select>
</el-form-item>
</div>
@@ -193,7 +193,6 @@ onMounted(() => {
box-sizing: border-box;
}
/* 布局样式 */
.form-row {
display: flex;
flex-wrap: wrap;

View File

@@ -98,9 +98,9 @@
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Download, Edit, Delete } from '@element-plus/icons-vue'
import { getHomeUserList } from '@/api/bizUser'
import { getHomeUserList, getHomeUserSave, getHomeUserDelete } from '@/api/bizUser'
import CSearch from '@/components/Search/proSearch.vue'
import STable from '@/components/Table/proTable.vue'
@@ -124,9 +124,10 @@ const pagination = reactive({
const tableData = ref([])
const dialogVisible = ref(false)
const isEdit = ref(false)
const formData = ref({})
const currentRow = ref({})
const dialogVisible = ref(false)
async function getDataList() {
loading.value = true
@@ -174,12 +175,14 @@ const handleReset = () => {
const handleAdd = () => {
isEdit.value = false
formData.value = {}
currentRow.value = {}
dialogVisible.value = true
}
const handleEdit = (row) => {
isEdit.value = true
formData.value = { ...row }
currentRow.value = { ...row }
dialogVisible.value = true
}
@@ -187,8 +190,22 @@ const handleExport = () => {
ElMessage.success('开始导出数据...')
}
const handleDelete = (row) => {
ElMessage.warning(`删除ID为 ${row.id} 的数据`)
const handleDelete = async (row) => {
try {
await ElMessageBox.confirm('确定要删除该条数据吗?', '删除确认', {
type: 'warning',
closeOnClickModal: false,
showClose: false
})
const reqParams = {
userId: row.userId
}
const res = await getHomeUserDelete(reqParams);
ElMessage.success('删除成功');
getDataList();
} catch (error) {
ElMessage.error('删除数据失败,请重试' ,error);
}
}
const handleSizeChange = (val) => {
@@ -204,6 +221,7 @@ const handleCurrentChange = (val) => {
const handleDialogClose = () => {
formData.value = {}
isEdit.value = false
currentRow.value = {}
dialogVisible.value = false
}
@@ -223,11 +241,19 @@ const handleSave = async () => {
}
}
setTimeout(() => {
ElMessage.success(isEdit.value ? '编辑成功' : '新增成功')
dialogVisible.value = false
getDataList()
}, 500)
try {
const reqParams = {
...formData.value,
isEdit: isEdit.value,
userId: currentRow.value?.userId
}
const res = await getHomeUserSave(reqParams);
dialogVisible.value = false;
getDataList();
ElMessage.success(res.msg)
} catch (error) {
console.log(error);
}
}
onMounted(() => {