diff --git a/dist/index.html b/dist/index.html index 4666506..63cbf0f 100644 --- a/dist/index.html +++ b/dist/index.html @@ -5,11 +5,11 @@ 设备管理系统 - + - - + +
diff --git a/src/api/alert.js b/src/api/alert.js new file mode 100644 index 0000000..87dcbbc --- /dev/null +++ b/src/api/alert.js @@ -0,0 +1,116 @@ +import request from '@/utils/request' + +// 后端路由前缀(AlertMessageController / AlertNotifyController) +const msgBase = '/inspection/alert-message' +const notifyBase = '/inspection/alert-notify' + +// ============ 枚举常量(与后端 Model.Entity.Inspection.AlertEnums 一一对应) ============ + +// 告警级别(AlertLevelEnum) +export const levelOptions = [ + { value: 1, label: '信息' }, + { value: 2, label: '警告' }, + { value: 3, label: '紧急' } +] +export const levelLabel = (v) => levelOptions.find(i => i.value === v)?.label || '-' +export const levelTag = (v) => ({ 3: 'danger', 2: 'warning' }[v] || 'info') + +// 告警状态(AlertStatusEnum) +export const statusOptions = [ + { value: 1, label: '未确认' }, + { value: 2, label: '已确认' }, + { value: 3, label: '已处理' }, + { value: 4, label: '已关闭' }, + { value: 5, label: '已忽略' }, + { value: 6, label: '已转工单' } +] +export const statusLabel = (v) => statusOptions.find(i => i.value === v)?.label || '-' +export const statusTag = (v) => ({ 1: 'danger', 2: 'warning', 3: 'success', 4: 'info', 5: 'info', 6: 'info' }[v] || 'info') + +// 告警类型(AlertTypeEnum) +export const typeOptions = [ + { value: 1, label: '阈值超限' }, + { value: 2, label: '设备离线' }, + { value: 3, label: '通讯故障' }, + { value: 4, label: '巡检异常' }, + { value: 99, label: '自定义' } +] +export const typeLabel = (v) => typeOptions.find(i => i.value === v)?.label || '-' + +// 告警来源(AlertSourceEnum) +export const sourceOptions = [ + { value: 1, label: '规则引擎' }, + { value: 2, label: '自动巡检' }, + { value: 3, label: '数据看板' }, + { value: 4, label: '数据采集' }, + { value: 5, label: '手动上报' }, + { value: 99, label: '其它' } +] +export const sourceLabel = (v) => sourceOptions.find(i => i.value === v)?.label || '-' + +// 通知渠道类型(NotifyChannelTypeEnum) +export const channelTypeOptions = [ + { value: 1, label: '飞书' }, + { value: 2, label: '钉钉' }, + { value: 3, label: '企业微信' } +] +export const channelLabel = (v) => channelTypeOptions.find(i => i.value === v)?.label || '-' + +// 推送模式(NotifyPushModeEnum) +export const pushModeOptions = [ + { value: 1, label: '直连推送' }, + { value: 2, label: 'Outbox 中转' } +] +export const pushModeLabel = (v) => pushModeOptions.find(i => i.value === v)?.label || '-' + +// ============ 告警消息 ============ + +// 告警列表(分页;level/status/type/source 筛选 + 时间段) +export const getAlertList = (params) => request.get(`${msgBase}/list`, { params }) + +// 告警详情 +export const getAlertDetail = (id) => request.get(`${msgBase}/${id}`) + +// 告警操作(确认/处理/关闭/忽略):body = { Operator, Remark } +export const ackAlert = (id, data) => request.post(`${msgBase}/${id}/ack`, data) +export const handleAlert = (id, data) => request.post(`${msgBase}/${id}/handle`, data) +export const closeAlert = (id, data) => request.post(`${msgBase}/${id}/close`, data) +export const ignoreAlert = (id, data) => request.post(`${msgBase}/${id}/ignore`, data) + +// 告警概览(未确认数/今日新增/今日紧急/近7日总数) +export const getAlertOverview = () => request.get(`${msgBase}/statistics/overview`) + +// 告警趋势(granularity: day/week/month) +export const getAlertTrend = (params) => request.get(`${msgBase}/statistics/trend`, { params }) + +// 告警分布(dimension: device/type/level/source) +export const getAlertDistribution = (params) => request.get(`${msgBase}/statistics/distribution`, { params }) + +// 高频告警 TOP +export const getAlertTop = (params) => request.get(`${msgBase}/statistics/top`, { params }) + +// 处理及时率 +export const getAlertTimeliness = (params) => request.get(`${msgBase}/statistics/timeliness`, { params }) + +// ============ 通知渠道 ============ +export const getChannels = () => request.get(`${notifyBase}/channel/list`) +export const addChannel = (data) => request.post(`${notifyBase}/channel`, data) +export const updateChannel = (id, data) => request.put(`${notifyBase}/channel/${id}`, data) +export const deleteChannel = (id) => request.delete(`${notifyBase}/channel/${id}`) +export const testChannel = (id) => request.post(`${notifyBase}/channel/${id}/test`) + +// ============ 通知规则 ============ +export const getNotifyRules = () => request.get(`${notifyBase}/rule/list`) +export const addNotifyRule = (data) => request.post(`${notifyBase}/rule`, data) +export const updateNotifyRule = (id, data) => request.put(`${notifyBase}/rule/${id}`, data) +export const deleteNotifyRule = (id) => request.delete(`${notifyBase}/rule/${id}`) +export const setRuleEnabled = (id, enabled) => request.put(`${notifyBase}/rule/${id}/enabled`, null, { params: { enabled } }) + +// ============ 通知日志 ============ +export const getNotifyLogs = (params) => request.get(`${notifyBase}/log/list`, { params }) + +// ============ 值班排班 ============ +export const getDuties = (params) => request.get(`${notifyBase}/duty/list`, { params }) +export const addDuty = (data) => request.post(`${notifyBase}/duty`, data) +export const updateDuty = (id, data) => request.put(`${notifyBase}/duty/${id}`, data) +export const deleteDuty = (id) => request.delete(`${notifyBase}/duty/${id}`) diff --git a/src/api/audit.js b/src/api/audit.js new file mode 100644 index 0000000..527c5ed --- /dev/null +++ b/src/api/audit.js @@ -0,0 +1,7 @@ +import request from '@/utils/request' + +// 审计日志接口(AuditController: api/system/audit,需 user:manage 权限) +const base = '/system/audit' + +// 审计日志列表(分页;筛选:关键字/操作类型/操作对象/时间段;X-Total-Count) +export const getAuditLogList = (params) => request.get(`${base}/list`, { params }) diff --git a/src/api/auth.js b/src/api/auth.js new file mode 100644 index 0000000..8785755 --- /dev/null +++ b/src/api/auth.js @@ -0,0 +1,16 @@ +import request from '@/utils/request' + +// 认证接口(AuthController: api/system/auth) +const base = '/system/auth' + +// 登录:{ UserName, Password } → { Token, User, Permissions } +export const login = (data) => request.post(`${base}/login`, data) + +// 退出登录(记录审计日志) +export const logout = () => request.post(`${base}/logout`) + +// 当前登录用户信息 + 权限列表 +export const getMe = () => request.get(`${base}/me`) + +// 修改自己的密码:{ OldPassword, NewPassword } +export const changePassword = (data) => request.post(`${base}/change-password`, data) diff --git a/src/api/org.js b/src/api/org.js new file mode 100644 index 0000000..fadfbe5 --- /dev/null +++ b/src/api/org.js @@ -0,0 +1,42 @@ +import request from '@/utils/request' + +// 组织架构接口(OrganizationController: api/system/organization,需 user:manage 权限) +const base = '/system/organization' + +// 组织类型(OrgType) +export const ORG_TYPES = { + 1: { label: '公司', tag: 'danger', icon: 'OfficeBuilding' }, + 2: { label: '实验室', tag: 'warning', icon: 'School' }, + 3: { label: '部门', tag: 'primary', icon: 'Files' }, + 4: { label: '班组', tag: 'success', icon: 'UserFilled' } +} +export const orgTypeLabel = (t) => ORG_TYPES[t]?.label || '-' +export const orgTypeTag = (t) => ORG_TYPES[t]?.tag || 'info' +export const orgTypeOptions = Object.entries(ORG_TYPES).map(([value, o]) => ({ value: Number(value), label: o.label })) + +// 班组班次(ShiftType) +export const SHIFT_TYPES = { + 0: '非班组', + 1: '白班', + 2: '夜班' +} +export const shiftLabel = (s) => SHIFT_TYPES[s] || '-' +export const shiftOptions = [ + { value: 1, label: '白班' }, + { value: 2, label: '夜班' } +] + +// 完整组织树(嵌套子级) +export const getOrgTree = () => request.get(`${base}/tree`) + +// 组织下拉选项(平铺) +export const getOrgOptions = () => request.get(`${base}/options`) + +// 新增组织节点(ParentId 传父级 Id,根节点传 "0") +export const addOrg = (data) => request.post(base, data) + +// 修改组织节点 +export const updateOrg = (data) => request.put(base, data) + +// 删除组织节点(有子级或挂靠用户时不可删) +export const deleteOrg = (id) => request.delete(`${base}/${id}`) diff --git a/src/api/role.js b/src/api/role.js new file mode 100644 index 0000000..426e243 --- /dev/null +++ b/src/api/role.js @@ -0,0 +1,36 @@ +import request from '@/utils/request' + +// 角色权限管理接口(RoleController: api/system/role,需 user:manage 权限) +const base = '/system/role' + +// 角色列表(分页) +export const getRoleList = (params) => request.get(`${base}/list`, { params }) + +// 角色下拉选项 +export const getRoleOptions = () => request.get(`${base}/options`) + +// 全部权限列表(分配权限用) +export const getPermissionList = () => request.get(`${base}/permissions`) + +// 角色详情(含权限Id列表) +export const getRoleDetail = (id) => request.get(`${base}/${id}`) + +// 新增角色 +export const addRole = (data) => request.post(base, data) + +// 修改角色 +export const updateRole = (data) => request.put(base, data) + +// 删除角色(系统内置角色不可删) +export const deleteRole = (id) => request.delete(`${base}/${id}`) + +// 分配权限(全量覆盖) +export const assignRolePermissions = (id, permissionIds) => request.post(`${base}/${id}/permissions`, permissionIds) + +// 数据范围常量(DataScope:1=全部, 2=本组织及下级) +export const DATA_SCOPES = { + 1: { label: '全部', tag: 'danger' }, + 2: { label: '本组织及下级', tag: 'warning' } +} +export const dataScopeLabel = (s) => DATA_SCOPES[s]?.label || '-' +export const dataScopeTag = (s) => DATA_SCOPES[s]?.tag || 'info' diff --git a/src/api/user.js b/src/api/user.js new file mode 100644 index 0000000..8d9b1b0 --- /dev/null +++ b/src/api/user.js @@ -0,0 +1,31 @@ +import request from '@/utils/request' + +// 用户管理接口(UserController: api/system/user,需 user:manage 权限) +const base = '/system/user' + +// 用户列表(分页;关键字/角色筛选;X-Total-Count) +export const getUserList = (params) => request.get(`${base}/list`, { params }) + +// 用户下拉选项(启用中的用户) +export const getUserOptions = () => request.get(`${base}/options`) + +// 用户详情 +export const getUserDetail = (id) => request.get(`${base}/${id}`) + +// 新增用户:{ UserName, RealName, InitialPassword, RoleIds, ... } +export const addUser = (data) => request.post(base, data) + +// 修改用户(RoleIds=null 不改角色) +export const updateUser = (data) => request.put(base, data) + +// 删除用户(软删除) +export const deleteUser = (id) => request.delete(`${base}/${id}`) + +// 分配角色(全量覆盖) +export const assignUserRoles = (id, roleIds) => request.post(`${base}/${id}/roles`, roleIds) + +// 重置密码 +export const resetUserPassword = (id, newPassword) => request.post(`${base}/${id}/reset-password`, { NewPassword: newPassword }) + +// 启用/禁用 +export const setUserEnabled = (id, enabled) => request.post(`${base}/${id}/enabled`, { Enabled: enabled }) diff --git a/src/directives/permission.js b/src/directives/permission.js new file mode 100644 index 0000000..2b2b666 --- /dev/null +++ b/src/directives/permission.js @@ -0,0 +1,20 @@ +import { useUserStore } from '@/store/user' + +/** + * 按钮级权限指令:v-permission="'device:edit'" 或 v-permission="['device:edit','device:control']" + * 无权限时移除元素(与 v-if 语义一致) + */ +export const permission = { + mounted(el, binding) { + const userStore = useUserStore() + const value = binding.value + const required = Array.isArray(value) ? value : [value] + if (!userStore.hasAnyPermission(required)) { + el.parentNode?.removeChild(el) + } + } +} + +export function setupDirectives(app) { + app.directive('permission', permission) +} diff --git a/src/layout/Header.vue b/src/layout/Header.vue index b47e620..8dd91c3 100644 --- a/src/layout/Header.vue +++ b/src/layout/Header.vue @@ -13,34 +13,113 @@
- +
+ + + + + + + + + + + + + + + + diff --git a/src/layout/Sidebar.vue b/src/layout/Sidebar.vue index cd2aa30..8d37936 100644 --- a/src/layout/Sidebar.vue +++ b/src/layout/Sidebar.vue @@ -39,8 +39,7 @@ - - + @@ -49,20 +48,31 @@ import { computed } from 'vue' import { useRoute, useRouter } from 'vue-router' import { useAppStore } from '@/store/app' +import { useUserStore } from '@/store/user' const route = useRoute() const router = useRouter() const appStore = useAppStore() +const userStore = useUserStore() const activeMenu = computed(() => route.path) +// 按权限过滤菜单:meta.permissions 任一满足才显示;无权限标记的菜单登录即可见 +function hasPerm(meta) { + const required = meta?.permissions + if (!required || required.length === 0) return true + return userStore.hasAnyPermission(required) +} + const menuRoutes = computed(() => { return router.options.routes - .filter(r => r.component && r.path !== '/:pathMatch(.*)*') + .filter(r => r.component && r.path !== '/:pathMatch(.*)*' && !r.meta?.public) .map(r => ({ ...r, - children: (r.children || []).filter(c => !c.path.includes(':')) + children: (r.children || []).filter(c => !c.path.includes(':') && hasPerm(c.meta)) })) + // 父级自身也要过权限;子菜单全被过滤掉的分组隐藏 + .filter(r => hasPerm(r.meta) && (!r.children || r.children.length > 0)) }) function resolvePath(basePath, childPath) { diff --git a/src/main.js b/src/main.js index 93f1d36..cdc4f13 100644 --- a/src/main.js +++ b/src/main.js @@ -5,6 +5,7 @@ import 'element-plus/dist/index.css' import * as ElementPlusIconsVue from '@element-plus/icons-vue' import router from './router' import App from './App.vue' +import { setupDirectives } from './directives/permission' const app = createApp(App) @@ -14,6 +15,7 @@ for (const [key, component] of Object.entries(ElementPlusIconsVue)) { } app.use(createPinia()) +setupDirectives(app) app.use(router) app.use(ElementPlus) app.mount('#app') diff --git a/src/router/index.js b/src/router/index.js index c895b33..f3947aa 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -2,6 +2,12 @@ import { createRouter, createWebHistory } from 'vue-router' import Layout from '@/layout/Layout.vue' const routes = [ + { + path: '/login', + name: 'Login', + component: () => import('@/views/login/Index.vue'), + meta: { title: '登录', public: true } + }, { path: '/', component: Layout, @@ -143,7 +149,7 @@ const routes = [ path: 'patrol', name: 'PatrolManage', component: () => import('@/views/inspection/patrol/Index.vue'), - meta: { title: '巡检管理', icon: 'Guide' } + meta: { title: '巡检管理', icon: 'Guide', permissions: ['inspection:manage'] } }, { path: 'patrol/template', @@ -200,25 +206,25 @@ const routes = [ path: 'product', name: 'ProductManage', component: () => import('@/views/config/product/Index.vue'), - meta: { title: '产品管理', icon: 'Goods' } + meta: { title: '产品管理', icon: 'Goods', permissions: ['device:view'] } }, { path: 'product/category', name: 'ProductCategory', component: () => import('@/views/config/product/Category.vue'), - meta: { title: '产品分类', icon: 'Menu' } + meta: { title: '产品分类', icon: 'Menu', permissions: ['device:view'] } }, { path: 'device', name: 'DeviceManage', component: () => import('@/views/config/device/Index.vue'), - meta: { title: '设备管理', icon: 'Cpu' } + meta: { title: '设备管理', icon: 'Cpu', permissions: ['device:view'] } }, { path: 'gateway', name: 'GatewayManage', component: () => import('@/views/config/gateway/Index.vue'), - meta: { title: '网关管理', icon: 'Connection' } + meta: { title: '网关管理', icon: 'Connection', permissions: ['device:view'] } }, { path: 'protocol/certificate', @@ -287,13 +293,13 @@ const routes = [ path: 'role', name: 'RoleManage', component: () => import('@/views/system/role/Index.vue'), - meta: { title: '角色权限管理', icon: 'Lock' } + meta: { title: '角色权限管理', icon: 'Lock', permissions: ['user:manage'] } }, { path: 'audit', name: 'AuditLog', component: () => import('@/views/system/audit/Index.vue'), - meta: { title: '操作审计日志', icon: 'Notebook' } + meta: { title: '操作审计日志', icon: 'Notebook', permissions: ['user:manage'] } }, { path: 'organization', @@ -305,7 +311,7 @@ const routes = [ path: 'user', name: 'UserManage', component: () => import('@/views/system/user/Index.vue'), - meta: { title: '用户管理', icon: 'User' } + meta: { title: '用户管理', icon: 'User', permissions: ['user:manage'] } }, { path: 'dict', @@ -352,4 +358,42 @@ const router = createRouter({ routes }) +// ---------- 全局守卫:登录校验 + 权限校验 ---------- +import { ElMessage } from 'element-plus' +import { useUserStore } from '@/store/user' + +router.beforeEach(async (to) => { + const userStore = useUserStore() + + // 公开页面(登录页)直接放行;已登录访问登录页则回首页 + if (to.meta.public) { + if (userStore.isLoggedIn && to.path === '/login') return '/' + return true + } + + // 未登录 → 跳登录页(带上回跳地址) + if (!userStore.isLoggedIn) { + return { path: '/login', query: { redirect: to.fullPath } } + } + + // 已登录但权限未加载(页面刷新场景)→ 拉取当前用户 + if (userStore.permissions.length === 0 && !userStore.userInfo) { + try { + await userStore.fetchMe() + } catch { + userStore.reset() + return { path: '/login', query: { redirect: to.fullPath } } + } + } + + // 路由级权限校验(meta.permissions 任一满足即放行) + const required = to.meta.permissions + if (required && required.length > 0 && !userStore.hasAnyPermission(required)) { + ElMessage.warning('没有访问该页面的权限') + return '/' + } + + return true +}) + export default router diff --git a/src/store/user.js b/src/store/user.js new file mode 100644 index 0000000..2d35709 --- /dev/null +++ b/src/store/user.js @@ -0,0 +1,57 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' +import { login as loginApi, logout as logoutApi, getMe } from '@/api/auth' + +/** + * 认证状态:token / 用户信息 / 权限列表(登录成功后由后端签发,权限随 JWT Claims 校验) + */ +export const useUserStore = defineStore('user', () => { + const token = ref(localStorage.getItem('token') || '') + const userInfo = ref(null) + const permissions = ref([]) + + const isLoggedIn = computed(() => !!token.value) + const displayName = computed(() => { + const u = userInfo.value + return u?.RealName || u?.UserName || '未登录' + }) + const roleNames = computed(() => userInfo.value?.RoleNames || '') + + function hasPermission(code) { + return permissions.value.includes(code) || permissions.value.includes('*') + } + function hasAnyPermission(codes) { + if (!codes || codes.length === 0) return true + return codes.some((c) => hasPermission(c)) + } + + async function login(form) { + const res = await loginApi(form) + token.value = res.Token + userInfo.value = res.User + permissions.value = res.Permissions || [] + localStorage.setItem('token', res.Token) + return res + } + + async function fetchMe() { + const res = await getMe() + userInfo.value = res.User + permissions.value = res.Permissions || [] + return res + } + + async function logout() { + try { await logoutApi() } catch { /* 审计失败不影响退出 */ } + reset() + } + + function reset() { + token.value = '' + userInfo.value = null + permissions.value = [] + localStorage.removeItem('token') + } + + return { token, userInfo, permissions, isLoggedIn, displayName, roleNames, hasPermission, hasAnyPermission, login, fetchMe, logout, reset } +}) diff --git a/src/utils/format.js b/src/utils/format.js index 5a8169d..b3de670 100644 --- a/src/utils/format.js +++ b/src/utils/format.js @@ -1,60 +1,42 @@ /** - * 通用格式化工具(后端返回的日期为 ISO 字符串,如 "2026-08-28T10:30:00") + * 通用格式化工具(asset 台账视图与告警页共用) */ -// 补零:5 → "05" -const pad = n => String(n).padStart(2, '0') - -/** - * 格式化为日期:2026-08-28 - * @param {string|Date} value 日期/时间值 - * @returns {string} 空值返回 '' - */ +// 日期:YYYY-MM-DD(兼容 ISO 字符串 / Date) export function fmtDate(value) { - if (!value) return '' + if (!value) return '-' const d = new Date(value) - if (isNaN(d.getTime())) return String(value).slice(0, 10) + if (Number.isNaN(d.getTime())) return String(value) + const pad = (n) => String(n).padStart(2, '0') return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}` } -/** - * 格式化为日期时间:2026-08-28 10:30:00 - * @param {string|Date} value 日期/时间值 - * @returns {string} 空值返回 '' - */ +// 日期时间:YYYY-MM-DD HH:mm:ss export function fmtDateTime(value) { - if (!value) return '' + if (!value) return '-' const d = new Date(value) - if (isNaN(d.getTime())) return String(value).replace('T', ' ').slice(0, 19) + if (Number.isNaN(d.getTime())) return String(value) + const pad = (n) => String(n).padStart(2, '0') return `${fmtDate(d)} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}` } -/** - * 格式化文件大小:1024 → "1.00 KB" - * @param {number} bytes 字节数 - * @returns {string} 空值返回 '' - */ +// 文件大小(字节 → 可读单位) export function fmtFileSize(bytes) { - if (bytes === null || bytes === undefined || isNaN(bytes)) return '' - if (bytes < 1024) return `${bytes} B` - const units = ['KB', 'MB', 'GB', 'TB'] - let size = bytes - let i = -1 - do { - size /= 1024 - i++ - } while (size >= 1024 && i < units.length - 1) - return `${size.toFixed(2)} ${units[i]}` + if (bytes == null || bytes === '' || Number.isNaN(Number(bytes))) return '-' + const n = Number(bytes) + if (n < 1024) return `${n} B` + if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB` + if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)} MB` + return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB` } -/** - * 判断日期是否已超期(小于当前日期) - * @param {string|Date} value 日期值 - * @returns {boolean} - */ +// 是否已逾期(日期早于今天且非空) export function isOverdue(value) { if (!value) return false const d = new Date(value) - if (isNaN(d.getTime())) return false - return d < new Date() + if (Number.isNaN(d.getTime())) return false + const today = new Date() + today.setHours(0, 0, 0, 0) + d.setHours(0, 0, 0, 0) + return d.getTime() < today.getTime() } diff --git a/src/utils/request.js b/src/utils/request.js index ade014d..289d9bf 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -34,7 +34,25 @@ request.interceptors.response.use( return data }, error => { - ElMessage.error(error.message || '网络错误') + const status = error.response?.status + if (status === 401) { + // 未登录 / Token 失效:清凭证并跳登录页(避免死循环用 sessionStorage 标记只提示一次) + if (!sessionStorage.getItem('authRedirecting')) { + sessionStorage.setItem('authRedirecting', '1') + ElMessage.error('登录已失效,请重新登录') + localStorage.removeItem('token') + const redirect = window.location.pathname + window.location.search + window.location.href = `/login?redirect=${encodeURIComponent(redirect)}` + } + return Promise.reject(error) + } + if (status === 403) { + ElMessage.error(error.response?.data?.Msg || '没有操作权限') + return Promise.reject(error) + } + // 后端统一返回 { Code, Msg, Data },HTTP 200 但业务失败时错误体也带 Msg + const msg = error.response?.data?.Msg || error.message || '网络错误' + ElMessage.error(msg) return Promise.reject(error) } ) diff --git a/src/views/config/device/Index.vue b/src/views/config/device/Index.vue index 8580d6d..3efb2d7 100644 --- a/src/views/config/device/Index.vue +++ b/src/views/config/device/Index.vue @@ -70,8 +70,8 @@ + + diff --git a/src/views/system/organization/Index.vue b/src/views/system/organization/Index.vue index 740711b..c0a97f0 100644 --- a/src/views/system/organization/Index.vue +++ b/src/views/system/organization/Index.vue @@ -2,13 +2,235 @@
- + + + +
+ + + + 刷新 +
+ + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + {{ s.label }} + + + + + + + + + + + + + + + + + + + + + + +
+ + diff --git a/src/views/system/role/Index.vue b/src/views/system/role/Index.vue index a55b7a2..c2be63a 100644 --- a/src/views/system/role/Index.vue +++ b/src/views/system/role/Index.vue @@ -2,13 +2,231 @@
- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
{{ group.name }}
+ {{ p.Name }}({{ p.Code }}) +
+
+ +
+ + diff --git a/src/views/system/user/Index.vue b/src/views/system/user/Index.vue index 538e059..e2de2d1 100644 --- a/src/views/system/user/Index.vue +++ b/src/views/system/user/Index.vue @@ -2,13 +2,270 @@
- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +