diff --git a/README.md b/README.md index df22502..63e0ac0 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,8 @@ Gin + Vue3 + SQLite 的多协议 VPN 专线 Web 管理平台:**单二进制交 ./run.sh stop # 停止服务 ``` +管理后台的视觉和交互规范见 [管理端 UI 实施说明](docs/specs/SDD-008-web-ui-guide-alignment.md)。前端沿用 Vue 3 + Element Plus:路由元数据统一生成侧栏与页标题,颜色和间距集中在 `frontend/src/styles/tokens.css`,列表与图表明确展示加载、空数据和失败重试。单独预览前端可在 `frontend/` 运行 `npm run dev`;默认代理 `/api` 到本机 `8080`,需要启动后端才能测试登录及真实数据。 + ### Linux 服务器部署 现有服务器的构建、升级、备份与 OIDC 入口配置参见 [部署手册](docs/deploy.md)。 diff --git a/docs/specs/SDD-008-web-ui-guide-alignment.md b/docs/specs/SDD-008-web-ui-guide-alignment.md new file mode 100644 index 0000000..6aee399 --- /dev/null +++ b/docs/specs/SDD-008-web-ui-guide-alignment.md @@ -0,0 +1,27 @@ +# SDD-008:管理端 UI 指南落地 + +| 字段 | 内容 | +|---|---| +| 级别 | S3(导航、会话错误处理与日志导出凭据传递方式) | +| 状态 | 本轮已实施,真实浏览器业务链路待联调 | +| 日期 | 2026-10-22 | + +## 现状与目标 + +当前 Vue 3 / Element Plus 后台已有基本响应式布局与 CSS 变量,但菜单与路由双份维护、局部样式仍有散落数值,图表无数据时使用假的占位切片,部分请求失败只留下空白或抛出未处理错误。按 `~/Documents/WEB-UI-Development-Guide` 的 02/04/05/06/07/08 章,以现有 Vue 栈完善全站视觉和交互,不更换框架、不修改后台授权模型与业务 API。 + +## 设计与兼容 + +- 路由 meta 提供菜单分组、顺序、图标与子页父路径;布局按 meta 渲染。保留已有 URL、管理员显隐规则和路由守卫;不能把客户端导航权限误认为服务器授权。 +- 统一品牌、表面、文本、边框、间距、侧栏和动效 CSS 变量,沿用 Element Plus;页面卡片统一描边。移动端侧栏覆盖主内容,表格保持横向滚动,表单弹窗不越界。 +- 概览、专线列表、拨号用户、设备列表、在线会话和流量图明确首屏加载、空数据、请求失败与重试;轮询失败保留已有可信数据,禁止绘制虚构切片。统计卡片使用可键盘聚焦的导航链接,图表按需注册。 +- 登录不预填账号或展示默认口令;仅真实会话失效时统一清除本地登录态并跳转,不把登录表单的 401 当作过期会话,更不从 401 处理器递归注销。 +- 日志 CSV 使用现有 Axios Authorization 请求并从 Blob 下载;筛选参数与原请求保持一致,不把 token 放入 URL。失败时保留原日志并明确显示错误,下载完成后释放 Blob URL。后端现有导出接口不变。 + +## 风险与验收 + +风险:菜单 meta 遗漏导致入口消失;颜色对比、窄屏表格或图表实例切换出错;登录错误处理误跳转;大文件 CSV 进入浏览器内存。验收:所有原有入口均可从对应角色看到,子页可返回专线;中文/英文页面可构建;桌面与小屏下菜单和操作按钮可点;登录 401 留在登录表单,业务 401 只触发一次跳转;接口错误时有重试且不伪造数据;日志导出请求 URL 不含 token,HTTP Authorization 携带凭据并成功下载 CSV。旧版后端接口保持兼容。提交前执行前端构建与 `git diff --check`,生产发布需另行确认可访问性与当前在线 VPN 连接;回退只需切回上一版前端嵌入二进制。 + +## 后续拆分点 + +`Settings.vue`、`Tunnels.vue`、`VPNUsers.vue` 目前超过指南的 400 行视图上限。下一轮按业务边界分别将身份/网关设置、专线详情/日志、拨号用户的 MFA 弹层移至页面私有组件和 composable。避免本次视觉统一同时改变这些敏感业务表单的状态及 API 调用顺序。 diff --git a/frontend/src/api.js b/frontend/src/api.js index 2dbc141..70bec7d 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -23,8 +23,7 @@ export const auth = reactive({ }, isAdmin() { return this.role === 'admin' }, canManage() { return this.role === 'admin' || this.role === 'operator' }, - logout() { - if (this.token) api.post('/logout').catch(() => {}) + clearSession() { this.token = '' this.username = '' this.role = 'viewer' @@ -33,13 +32,19 @@ export const auth = reactive({ localStorage.removeItem('vpn_user') localStorage.removeItem('vpn_role') localStorage.removeItem('vpn_must_change') + }, + logout() { + const token = this.token + this.clearSession() + if (token) api.post('/logout', null, { headers: { Authorization: `Bearer ${token}` }, skipAuth: true, skipUnauthorized: true }).catch(() => {}) } }) const api = axios.create({ baseURL: '/api', timeout: 30000 }) +let redirectingToLogin = false api.interceptors.request.use(cfg => { - if (auth.token) cfg.headers.Authorization = `Bearer ${auth.token}` + if (auth.token && !cfg.skipAuth) cfg.headers.Authorization = `Bearer ${auth.token}` return cfg }) @@ -47,9 +52,12 @@ api.interceptors.response.use( res => res, err => { const status = err.response?.status - if (status === 401) { - auth.logout() - if (location.pathname !== '/login') location.href = '/login' + if (status === 401 && !err.config?.skipUnauthorized && !['/login', '/mfa/verify'].includes(err.config?.url)) { + auth.clearSession() + if (!redirectingToLogin && location.pathname !== '/login') { + redirectingToLogin = true + location.replace('/login') + } } else if (status === 403 && err.response?.data?.must_change_password) { auth.mustChange = true localStorage.setItem('vpn_must_change', '1') diff --git a/frontend/src/components/TrafficChart.vue b/frontend/src/components/TrafficChart.vue index 128dfe5..24591f5 100644 --- a/frontend/src/components/TrafficChart.vue +++ b/frontend/src/components/TrafficChart.vue @@ -9,24 +9,31 @@ 7d -
- + {{ $t('common.refresh') }} +
+ + + diff --git a/frontend/src/views/Dashboard.vue b/frontend/src/views/Dashboard.vue index 9db0747..98197ef 100644 --- a/frontend/src/views/Dashboard.vue +++ b/frontend/src/views/Dashboard.vue @@ -1,7 +1,11 @@ - + + @@ -115,8 +120,12 @@ + + diff --git a/frontend/src/views/Settings.vue b/frontend/src/views/Settings.vue index 799bf71..d7db5d4 100644 --- a/frontend/src/views/Settings.vue +++ b/frontend/src/views/Settings.vue @@ -1,7 +1,7 @@