12 KiB
12 KiB
Xboard 管理端前端 - 基础登录功能实施计划
任务类型
- 前端 (Vue3 + TypeScript + Vite + Element Plus)
- 后端 (无需改动,复用现有 API)
- 全栈
需求概述
为 Xboard 创建独立的管理端前端项目,技术栈:Vue3 + TypeScript + Vite + Element Plus。第一阶段实现基础登录功能,包括:
- 管理员登录页面(邮箱 + 密码)
- Token 认证与路由守卫
- 登录后 Dashboard 占位页
- 基础布局框架(侧边栏 + 顶栏)
技术方案
后端 API 契约(已存在,无需改动)
| 接口 | 方法 | URL | 说明 |
|---|---|---|---|
| 登录 | POST | /api/v2/passport/auth/login |
{email, password} → {auth_data, is_admin, token} |
| 管理接口前缀 | - | /api/v2/{secure_path}/... |
secure_path 从后端 admin_setting 获取 |
| 系统状态探针 | GET | /api/v2/{secure_path}/system/getSystemStatus |
登录后验证 admin 权限 |
关键发现:
- 登录接口是通用用户接口,
is_admin在响应中标识管理员身份 auth_data(格式Bearer xxx)是鉴权凭证,token是邀请码字段,不可用作认证- Admin 中间件使用 Sanctum guard,通过
Authorizationheader 传递 Bearer token secure_path是动态的,首期从 Laravel 的window.settings.secure_path获取
部署策略:独立源码 + Laravel 托管 dist
首期将 Vue3 项目构建产物放到 Laravel 的 public/ 目录下,由 Laravel 的 admin.blade.php 通过 window.settings 注入运行时配置。这样 secure_path 自举最简单,无需跨域。
前端分析交叉验证(Codex 后端视角 + 前端 UI/UX 视角)
一致观点(强信号):
- 方案 A(独立源码 + Laravel 托管 dist)是最务实的首期路线
secure_path从运行时配置获取,不在前端推导- 登录后必须校验
is_admin
前端分析补充建议(已纳入计划):
- 工程化优化:使用
unplugin-auto-import+unplugin-vue-components实现 Element Plus 按需引入,优化包体积 - 暗色/亮色主题切换:Element Plus 原生支持 dark mode,首期预留切换能力
- 前端登录频率限制提示:在登录表单增加防抖,429 时显示倒计时提示
- Token 过期自动重定向:Axios 拦截器中 401 响应自动清除 token 并跳转登录页
- 动态路径策略:
VITE_ADMIN_PATH环境变量作为secure_path的默认值,同时支持从window.settings.secure_path动态覆盖
项目结构
admin-frontend/ # 独立前端项目根目录
├── index.html
├── package.json
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
├── env.d.ts
├── .env
├── .env.development
├── .env.production
├── public/
│ └── favicon.ico
└── src/
├── main.ts # 应用入口
├── App.vue # 根组件
├── api/
│ ├── client.ts # Axios 实例与拦截器
│ ├── passport.ts # 登录相关 API
│ └── admin.ts # 管理端 API(带 secure_path)
├── stores/
│ ├── auth.ts # 认证状态(Pinia)
│ └── app.ts # 应用运行时配置
├── router/
│ ├── index.ts # 路由定义
│ └── guards.ts # 路由守卫
├── layouts/
│ └── AdminLayout.vue # 管理端主布局
├── views/
│ ├── login/
│ │ └── LoginView.vue # 登录页
│ └── dashboard/
│ └── DashboardView.vue # 仪表盘占位页
├── types/
│ ├── api.d.ts # API 响应类型
│ └── env.d.ts # 环境变量类型
├── utils/
│ ├── token.ts # Token 存储工具
│ └── runtime.ts # 运行时配置解析
└── styles/
└── index.scss # 全局样式
实施步骤
Step 1: 初始化项目骨架
预期产物: 可运行的空 Vue3 + TS 项目
- 使用
npm create vite@latest admin-frontend -- --template vue-ts创建项目 - 安装依赖:
element-plus,pinia,vue-router,axios,sass,@element-plus/icons-vue - 安装开发依赖:
unplugin-auto-import,unplugin-vue-components(Element Plus 按需引入) - 配置
vite.config.ts(代理、别名、构建输出路径、AutoImport 插件) - 配置
tsconfig.json路径别名 - 创建
.env.development和.env.production
Vite 配置要点:
// vite.config.ts
export default defineConfig({
base: '/assets/admin/',
resolve: { alias: { '@': '/src' } },
plugins: [
vue(),
AutoImport({ resolvers: [ElementPlusResolver()] }),
Components({ resolvers: [ElementPlusResolver()] }),
],
server: {
port: 5173,
proxy: {
'/api': {
target: 'http://localhost:8000',
changeOrigin: true,
}
}
},
build: {
outDir: '../public/assets/admin',
emptyOutDir: true,
}
})
环境变量:
# .env.development
VITE_API_BASE_URL=/api/v2
VITE_SECURE_PATH= # 开发时留空,由 proxy 转发
# .env.production
VITE_API_BASE_URL=/api/v2
# production 下 secure_path 从 window.settings 获取
Step 2: 基础设施层
预期产物: API client、Token 工具、运行时配置、类型定义
src/types/api.d.ts- API 响应泛型、登录接口类型src/utils/token.ts- Token 存取(sessionStorage 为默认,可选 localStorage 记住登录)src/utils/runtime.ts- 解析window.settings和环境变量,提供getSecurePath()、getApiBaseUrl()secure_path优先级:window.settings.secure_path>VITE_ADMIN_PATH环境变量 > 启动报错
src/api/client.ts- 两个 Axios 实例:passportClient:固定前缀/api/v2/passportadminClient:运行时拼接/api/v2/{secure_path}- 统一响应拦截:错误归一化、401/403 自动清除 token 跳登录
src/api/passport.ts-login(email, password)函数src/api/admin.ts-getSystemStatus()探针函数
Step 3: 状态管理
预期产物: Pinia stores
src/stores/auth.ts:- state:
authHeader,isAdmin,isLoading - actions:
login(),logout(),validateAdmin(),initFromStorage() - 登录流程:调用 API → 校验
is_admin→ 保存authHeader→ admin 探针验证 - 非 admin 用户登录后直接拒绝并提示"无管理员权限"
- state:
src/stores/app.ts:- state:
securePath,apiBaseUrl,sidebarCollapsed - actions:
initConfig()从 runtime 解析配置
- state:
Step 4: 路由与守卫
预期产物: 路由配置 + 权限守卫
src/router/index.ts:- 使用
createWebHashHistory()(因为 Laravel 无 catch-all 路由,hash 模式避免刷新 404) - 路由表:
/login → LoginView (公开) / → AdminLayout (需认证) /dashboard → DashboardView (需认证)
- 使用
src/router/guards.ts:beforeEach:无 token →/login;有 token 但未验证 → 执行 admin 探针 → 失败清 token 回/login- 已登录用户访问
/login→ 重定向/dashboard
Step 5: 登录页面
预期产物: 功能完整的登录页
src/views/login/LoginView.vue:- 简约卡片布局,深色渐变背景,居中展示
- Element Plus 组件:
ElForm,ElFormItem,ElInput,ElButton,ElMessage - 表单字段:
- 邮箱(email 类型,必填,格式校验)
- 密码(password 类型,必填,最少 8 位)
- 记住登录(可选 checkbox,控制 token 存 localStorage vs sessionStorage)
- 提交时 loading 状态,按钮禁用 + 防抖(防止重复提交)
- 错误处理:
- 400:邮箱或密码错误 → 表单级错误提示
- 429:密码错误次数过多 → 显示倒计时提示(从响应 message 解析剩余分钟数)
- 403/无权限:
is_admin=false→ 提示"该账号无管理员权限" - 网络错误:通用错误提示
- 登录成功后跳转
/dashboard - 支持暗色/亮色主题切换(Element Plus 原生 dark mode)
Step 6: 管理端布局与 Dashboard
预期产物: 基础布局框架 + Dashboard 占位页
src/layouts/AdminLayout.vue:- Element Plus
ElContainer+ElAside+ElHeader+ElMain - 左侧边栏(可折叠):Logo + 菜单项(首期仅 Dashboard)
- 顶栏:面包屑 + 右侧用户操作区(登出按钮)
- 响应式:小屏幕自动折叠侧边栏
- Element Plus
src/views/dashboard/DashboardView.vue:- 占位页面,显示欢迎信息和系统基本信息
- 后续迭代补充统计数据
Step 7: 样式与全局配置
预期产物: 统一视觉风格
src/styles/index.scss:- CSS 变量定义主题色
- Element Plus 主题覆盖(主色调、边框、背景)
- 登录页专用样式
- 全局样式重置
Step 8: 入口文件整合
预期产物: 完整可运行的应用
src/main.ts:- 注册 Element Plus(通过 AutoImport 按需引入)
- 注册 Pinia
- 注册 Router
- 初始化运行时配置(
appStore.initConfig()) - 初始化认证状态(
authStore.initFromStorage()) - 引入 Element Plus 暗色主题 CSS(
element-plus/theme-chalk/dark/css-vars.css)
src/App.vue:<RouterView />
关键文件清单
| 文件 | 操作 | 说明 |
|---|---|---|
admin-frontend/package.json |
新建 | 项目依赖与脚本 |
admin-frontend/vite.config.ts |
新建 | Vite 构建 + 代理配置 |
admin-frontend/src/main.ts |
新建 | 应用入口 |
admin-frontend/src/api/client.ts |
新建 | Axios 实例 + 拦截器 |
admin-frontend/src/api/passport.ts |
新建 | 登录 API |
admin-frontend/src/api/admin.ts |
新建 | Admin API 封装 |
admin-frontend/src/utils/token.ts |
新建 | Token 存储工具 |
admin-frontend/src/utils/runtime.ts |
新建 | 运行时配置解析 |
admin-frontend/src/stores/auth.ts |
新建 | 认证状态管理 |
admin-frontend/src/stores/app.ts |
新建 | 应用配置管理 |
admin-frontend/src/router/index.ts |
新建 | 路由定义 |
admin-frontend/src/router/guards.ts |
新建 | 路由守卫 |
admin-frontend/src/views/login/LoginView.vue |
新建 | 登录页 |
admin-frontend/src/layouts/AdminLayout.vue |
新建 | 管理端布局 |
admin-frontend/src/views/dashboard/DashboardView.vue |
新建 | 仪表盘占位 |
风险与缓解
| 风险 | 缓解措施 |
|---|---|
secure_path 在独立部署时无法从前端推导 |
首期用 Laravel 托管 dist,从 window.settings 获取;中长期可加 bootstrap API |
| 登录接口是用户通用接口,非管理员也能登录 | 登录后立即校验 is_admin,非 admin 拒绝进入管理端 |
token 字段含义混淆(实际是邀请码) |
封装层仅暴露 auth_data 作为认证凭证,token 字段忽略 |
CORS supports_credentials=false |
前端走 Bearer token 无状态认证,不依赖 cookie |
| Hash 路由不够优雅 | 首期务实选择,后续独立域名部署时可切 history 模式 |
| 无专用 admin logout API | 前端清除本地 token 即可(Sanctum token 有 1 年有效期,不影响安全性) |
SESSION_ID
- CODEX_SESSION: 019dac16-b724-73a2-a3ff-f6b2ac49e2bf
- GEMINI_SESSION: (不可用,调用失败)