6.8 管理端前端开发

前端开发(admin-webui)

dwz-server 的管理后台是一个独立的前端仓库 admin-webui,基于 Vben Admin v5 构建,最终产物会被嵌入到 Go 后端的二进制中(通过 //go:embed static/admin/)。

技术栈

类别 选型
框架 Vue 3.5 + TypeScript 5.8
构建 Vite 7 + Turbo + pnpm workspace
UI 库 Ant Design Vue 4.2
状态管理 Pinia 3(含 persistedstate 插件)
路由 Vue Router 4(hash 模式)
HTTP @vben/request(axios 封装)
表单校验 Vee-validate + Zod
样式 Tailwind CSS 3
包管理 必须使用 pnpm 10+
Node 22.1.0+

仓库地址

建议把 admin-webuidwz-server 克隆到同一父目录,便于打包脚本找到前端产物:

workspace/
├── dwz-server/       # Go 后端
└── admin-webui/      # Vue 前端

Monorepo 结构

admin-webui 是 Turbo + pnpm workspace 组织的 monorepo:

admin-webui/
├── apps/
│   └── web-antd/           # 主应用:dwz 管理后台(Ant Design Vue 版本)
├── packages/
│   ├── @core/              # 共享 Vue 组件、设计系统、基础工具
│   ├── effects/            # icons、locales、stores 等副作用模块
│   └── business/           # 业务组件
├── internal/               # 构建配置包:@vben/vite-config、@vben/tsconfig 等
├── scripts/                # 部署与工具脚本
├── docs/                   # 文档(含 API.md)
├── pnpm-workspace.yaml     # workspace 声明 + catalog 版本目录
├── turbo.json              # Turbo 任务流水线
└── package.json

只有 apps/web-antd 一个应用,其余 packages 都是被它引用的内部库。

开发命令

所有命令在 admin-webui/ 根目录执行:

命令 说明
pnpm install 安装依赖(首次必跑)
pnpm dev:antd 启动开发服务器(默认 http://localhost:5666
pnpm build:antd 打包生产版本到 apps/web-antd/dist/
pnpm build:analyze 带包体积分析的构建
pnpm lint ESLint + Prettier + StyleLint
pnpm format 自动格式化
pnpm typecheck 全量 TypeScript 检查(vue-tsc)
pnpm test:unit Vitest 单元测试
pnpm clean 清理 node_modules 与产物

首次启动

git clone https://cnb.cool/mliev/open/dwz-admin-webui.git admin-webui
cd admin-webui

# 启用 pnpm(如未安装)
corepack enable
corepack prepare pnpm@latest --activate

pnpm install
pnpm dev:antd

浏览器访问 http://localhost:5666/admin/,开发服务器会自动把 /api/* 代理到后端。

后端对接

开发期代理

Vite 配置在 apps/web-antd/vite.config.mts 内设置了 dev 代理:默认把 /api/* 转发到后端。请先启动 Go 后端 (go run main.go),再启动前端 dev server,否则 API 调用会失败。

若后端不是默认 8080 端口,可通过环境变量覆盖:

VITE_GLOB_API_URL=http://127.0.0.1:9090 pnpm dev:antd

生产环境 API 地址

生产环境通常把前端产物跟后端同源部署(由 Go 后端直接托管 /admin/),此时 VITE_GLOB_API_URL 留空,前端使用相对路径 /api/v1/... 请求本域。

环境变量(Vite)

位于 apps/web-antd/ 下:

文件 用途
.env 通用默认值
.env.development 开发覆盖:API 指向本地后端、devtools 开启
.env.production 生产覆盖:API 留空、hash 路由

常用变量:

  • VITE_BASE — 应用基路径,固定 /admin/
  • VITE_GLOB_API_URL — 后端 API 根地址
  • VITE_ROUTER_HISTORY — 路由模式,hash 方便部署到任意路径
  • VITE_APP_NAMESPACE / VITE_APP_VERSION — 用于 store 隔离

Vite 环境变量仅在 构建时 注入,不支持运行时替换。若需要根据部署环境动态切换 API 地址,建议走反向代理同源 + 相对路径方案。

关键目录:apps/web-antd/src

目录 职责
api/ API 客户端层,按业务模块拆分的 TypeScript 接口
api/core/auth.ts 登录、刷新 Token、登出
api/shortlink.tsab-test.tsuser-management.ts 业务接口
api/request.ts axios 封装:鉴权拦截器、统一错误处理、Token 刷新
store/ Pinia stores(auth.ts 管理登录态和用户信息)
router/routes/modules/ 路由模块:dashboard / shortlink / system
router/access.ts 基于权限的路由访问控制
router/guard.ts 路由守卫:登录校验、权限判定
views/ 页面组件,按业务分组(dashboard / shortlink / system / profile)
components/ 可复用 UI 组件(如 QRCodeGenerator)
layouts/ 应用外壳(侧边栏、顶栏、底部)
locales/langs/ i18n 翻译(中文 / 英文)
adapter/ UI 库适配层,统一注册组件
types/ TypeScript 类型定义

API 调用规范

所有后端请求必须通过 src/api/*.ts 封装的函数发起,不要在 Vue 组件里直接调用 axios。

调用示例

import { loginApi, getShortLinkList } from '#/api';

// 登录
const { token, user } = await loginApi({ username, password });

// 分页查询
const { list, total } = await getShortLinkList({
  page: 1,
  page_size: 10,
});

响应格式

统一期望后端返回:

{ "code": 0, "data": { ... }, "message": "success" }

code !== 0 时拦截器会自动弹出全局错误提示,业务层只需处理 data

新增接口

  1. src/api/ 下新建或扩展对应模块文件(如 src/api/tag.ts
  2. 定义请求参数与响应类型
  3. 导出函数:
import { requestClient } from '#/api/request';

export interface CreateTagParams {
  name: string;
  color?: string;
}

export function createTagApi(params: CreateTagParams) {
  return requestClient.post<{ id: number }>('/api/v1/tags', params);
}
  1. 在页面中直接 import 使用

登录与鉴权流程

登录态由 Pinia auth store(src/store/auth.ts)管理:

  1. 用户提交表单 → loginApi() 返回 token + 用户信息
  2. accessStore.accessToken 存储 Token,自动持久化(localStorage)
  3. 后续所有请求由拦截器自动注入 Authorization: Bearer <token>
  4. 收到 401 时尝试调用刷新接口
  5. 收到 429(限频锁定)时提示剩余次数并停留登录页
  6. 登出清空所有 store,跳转登录页

路由守卫 src/router/guard.ts 会在进入受保护页面前检查登录态与权限。

构建产物与后端集成

构建输出

apps/web-antd/dist/

Vite 配置 base: '/admin/',所有静态资源引用路径都是 /admin/*

嵌入后端二进制

后端 main.go 通过 //go:embed static/admin/** 把前端产物打进 Go 二进制。因此每次前端改完后,需要把 dist/ 的内容复制到后端的 static/admin/ 再构建后端:

# 1. 构建前端
cd admin-webui
pnpm install
pnpm build:antd

# 2. 拷贝到后端
cd ..
rm -rf dwz-server/static/admin
mkdir -p dwz-server/static/admin
cp -r admin-webui/apps/web-antd/dist/* dwz-server/static/admin/

# 3. 构建后端
cd dwz-server
CGO_ENABLED=0 go build -ldflags="-s -w" -o dwz-server main.go

Docker 部署

admin-webui 自带 Dockerfile,独立发布前端镜像(Nginx 托管):

  • 第一阶段:Node 22 镜像跑 pnpm build:antd
  • 第二阶段:把 dist/ 拷到 nginx 容器的 /usr/share/nginx/html/admin
  • 监听 80 端口

dwz-server 推荐的部署模式 是把前端嵌入后端二进制,不单独跑前端容器;nginx 仅在需要 CDN/多实例缓存时才单独部署。

常见问题

Q1: 启动 dev server 报 "ERR_PNPM_UNSUPPORTED_ENGINE"

Node 版本低于 22.1.0。用 nvm use 22fnm use 22 切换。

Q2: 接口全部 401 / CORS 报错

  • 确认后端 go run main.go 已启动
  • 确认已在后端登录获取 Token(或前端调用 loginApi
  • 确认 VITE_GLOB_API_URL 指向正确后端地址
  • 开发期若走跨域,请检查后端 cors 配置是否允许 http://localhost:5666

Q3: 页面空白,控制台报 "Failed to fetch dynamically imported module"

一般是构建产物与浏览器缓存不一致。清浏览器缓存或改配 base 后重新 pnpm build:antd

Q4: 改了后端路由前端没反应

前端的 API 封装在 src/api/,需要手工同步修改;TypeScript 类型也要更新,否则编辑器会报错。

Q5: 想本地跑一次完整的"打包 + 嵌入后端"流程

参考上面「嵌入后端二进制」的三步脚本;也可以直接参考仓库根目录 dwz-server/docs/manual-build.md

文档与参考

  • Vben Admin 官方文档https://doc.vben.pro/ — 组件、布局、权限、主题等通用用法
  • Ant Design Vuehttps://www.antdv.com/ — UI 组件 API
  • API 文档admin-webui/docs/API.md(OpenAPI 风格的后端接口速查)
  • API 客户端说明admin-webui/apps/web-antd/src/api/README.md