基于 WEB-UI-Development-Guide 标准的整体升级,并更名 LokiDeploy → opsx-loki。 前端(Gin+React+shadcn/ui): - 按 02/07 章重构:页面域化拆分(无超 400 行文件)、PageContainer/ListQuery、 反馈分级(成功静默/失败 toast/危险确认)、401 单飞刷新+队列重放 - 暗色模式(next-themes)+ 语义 Token(--primary 品牌色/success/warning/info) - 部署向导(状态推导四步:环境→存储→Loki→采集,内嵌表单闭环) - 日志查询页:LogQL 标签构建器/拖拽直方图/日志上下文/Live Tail/查询历史 - 按用户+标签策略的查询授权(服务端强制合并策略) 后端(Go+Gin+JSON DB): - 简单用户管理(仅 admin,无 RBAC)+ TokenVersion 会话吊销 + 登录 IP 限流 - LDAP 接入:配置(AES 加密存储/hasPassword 脱敏)/测试连接/批量导入/登录 - 存储配置测试连接(SigV4 零依赖)+ 桶不存在自动创建 - Loki 三模式部署修复:SSH 密钥认证、3.x 配置模板、ruler 对象存储嵌套块、 凭证解密、孤儿 deploying 状态恢复、Grafana 部署(可选默认关)+面板导入 - DaemonSet 采集纯 YAML(去 helm 依赖)+ DNS 自愈回退 ClusterIP + 集群名标签 - 日志查询代理:cluster-svc 经 kube-apiserver proxy/url 直连 + cursor 翻页 - 统一 log 命名空间;部署就绪探活;Grafana 访问地址自动记录 修复:run.sh 增量编译检测、Sanitized 脱敏补全、LokiDeployment Update 白名单、 健康检查 CreatedAt 零值等 10+ 存量 bug。 数据兼容:DB 沿用旧 lokideploy.json(新名优先);localStorage 一次性迁移; HKDF 加密前缀保留(已加密凭证不受影响)。
14 KiB
01 — 框架选型与技术栈
参考:vue-vben-admin(★★★★★ Vue 标准标杆)、ant-design-pro(★★★★★ React 标准标杆)、soybean-admin(★★★★☆ 强烈推荐)、vue-pure-admin(★★★★☆ 推荐);对照:gin-vue-admin(★★☆☆☆ Gin 全栈)
一、选型的第一性问题:单栈还是双栈
1.1 标杆项目的单栈现实
| 项目 | 框架 | 为什么不双栈 |
|---|---|---|
| ant-design-pro | React 19 | 生态深度绑定 Umi Max 运行时(约定式路由、运行时配置、插件体系) |
| gin-vue-admin | Vue 3 | Element Plus + 国内管理端生态,Go 后端配套脚手架默认 Vue(★★☆ 仅对照) |
| vue-vben-admin | Vue 3 | 用 monorepo + adapter 层支持 5 个 UI 库,但框架始终只有 Vue |
| soybean-admin | Vue 3 | 文件路由 + 生成物策略,规范优先(★★★★☆ 强烈推荐) |
| vue-pure-admin | Vue 3 | 单包直出、轻量实用,官方精简版 thin(★★★★☆ 推荐) |
结论:标杆项目全部单栈。多 UI 库共存靠的是「适配器层」(vben 的
effects/*adapter),而不是多框架。框架级双栈的成本(请求层、状态、权限、构建、测试全部 ×2)只有迁移期才值得付。参考优先级:Vue 架构看 vben、规范写法看 soybean、同栈 EP 实操看 pure、React 看 adp;gva 仅作 Gin 全栈视角补充,不作前端代码质量的首选参考。
1.2 双栈只在迁移期
┌─────────────────────────────────────┐
│ 后端 REST API(Gin / FastAPI ...) │
│ 单一契约:/api/* │
└───────────┬─────────────┬───────────┘
│ │
┌───────────▼────┐ ┌─────▼──────────────┐
│ 主端(功能源) │ │ 副端(迁移参照) │
│ 承接全部新功能 │ │ 只对齐、不抢先 │
│ 进入产物 │ │ 迁移完成即退役 │
└────────────────┘ └────────────────────┘
迁移期三条纪律(必须):
- 功能以主端为准:副端只做「对齐」同步,不抢先实现。
- 契约以两端共同名为准:token key、主题 key、模块开关键、权限码、响应格式——迁移开始前就统一命名(推荐命名空间
<app>.auth.token/<app>.theme.mode),而不是迁移后再改。 - 不共享代码,只共享约定:两端各自实现同一份「契约文档」(见 08 章),不引入 monorepo 这种为双栈兜底的重型方案。
1.3 何时值得上 monorepo(vben 模式)
vben 用 pnpm workspace + Turbo 支撑 5 个 UI 库 app + 30+ 框架包,代价是:
pnpm-workspace.yaml维护 300+ 依赖 catalog,overrides强制统一版本- 循环依赖检查(
check:circular)、依赖合规检查(check:dep)成为必跑任务 - 构建配置收敛为内部包
internal/vite-config,app 配置才能薄到 15 行
判断标准:当「框架层代码」(布局引擎、请求客户端、权限系统)需要被 ≥3 个应用复用时,才值得付 monorepo 成本。普通单产品工程(1-2 个前端)——不值得,独立 Vite 工程 + 统一 Makefile 编排即可。soybean 的「轻 workspace」(单应用 + 8 个工具包)是中间形态,适合工具链需要沉淀的团队。
1.4 何时允许偏离默认栈
必须:新项目 / 新模块使用本章默认栈(见 00 章「默认技术栈」);存量代码不因「不合推荐」而大规模重写——渐进迁移:新文件执行新标准,旧文件随功能迭代就近升级。
允许偏离的场景(需在 PR / ADR 中说明理由):组件库确缺关键能力(虚拟表格、复杂甘特等);性能热点需要更轻方案;与后端生态强绑定(如脚手架生成的默认产物)。
禁止:同一工程内并存两套 UI 库、两套状态库、两种 CSS 方案——偏离只允许发生在「整工程」粒度。
二、UI 组件库选型
2.1 三种流派对照
| 维度 | Element Plus(本标准 Vue 主推;gva 补充) | Ant Design + ProComponents(adp) | shadcn/ui(React 品牌差异化) |
|---|---|---|---|
| 形态 | 传统全量组件库 | 组件库 + 中后台增强层 | 源码复制型(Radix + Tailwind) |
| 中后台生产力 | 高(表格/表单/弹层齐全) | 极高(ProTable/ProForm 声明式 CRUD) | 中(原子件全,业务件自建) |
| 主题定制 | CSS 变量 + SCSS 双通道 | Design Token(configProvider) | 完全可控(Token 就是你的 CSS 变量) |
| 暗色模式 | dark/css-vars.css 一行接入 |
data-theme="dark" + token |
.dark class 天然支持 |
| TS 支持 | 一般(类型宽松) | 好 | 极好(TSX 源码进仓库) |
| 体积 | 全量较大,靠 unplugin 按需 | 较大 | 用多少复制多少 |
| 适合谁 | Vue 主栈、快速交付 | React 中后台重 CRUD | React、要品牌差异化 |
2.2 选型决策树
需要管理端全家桶、快速交付?
├─ Vue 栈 ──────────▶ Element Plus(+ unplugin 按需引入)
├─ React 栈、CRUD 为主 ▶ Ant Design + ProComponents
└─ React 栈、要品牌感 ─▶ shadcn/ui + Tailwind
双栈迁移期?
└─▶ 主端选全量组件库保效率(EP),
副端选 shadcn 保可读性 —— 常见组合
两个 ★★★★☆ 参考的选型启示:soybean-admin(Naive UI + UnoCSS)证明「hook 优先 + 组件薄壳」也能撑起完整中后台;vue-pure-admin 证明了 Tailwind v4 @theme 把 --el-* 变量桥接为原子色 token 是 EP 工程引入原子类的最短路径(详见 06 章)。
2.3 图标体系(易被忽视的选型)
- glob 注册模式(gva,补充):
import.meta.glob自动收集assets/icons/**/*.svg注册为全局组件,菜单存 icon 字符串即可<component :is>渲染;插件图标加插件名-前缀防冲突。适合「菜单图标由后端配置下发」的场景。 - 路由声明模式(adp):路由
icon: 'crown'字符串 + 图标映射表,与路由集中声明天然契合。 - 按需编译模式(pure/soybean):unplugin-icons 虚拟模块(
import Upload from '~icons/ri/upload-line')+ Iconify 离线/在线双模式。 - 通用规则:图标名是数据(存在菜单表里),渲染是前端映射——不要把图标组件硬编码进菜单模板。
三、构建工具与运行时
3.1 Vite 是唯一现实选择(2026)
| 能力 | 说明 | 标杆用法 |
|---|---|---|
| 按需引入 | unplugin-auto-import + unplugin-vue-components |
EP 组件零 import(vben/soybean 系均如此) |
import.meta.glob |
动态路由的组件映射基石 | gva/vben/pure 的 backend 菜单模式 |
| env 分层 | .env.development / .env.production + import.meta.env |
全部项目;vben 另分 VITE_GLOB_* 运行时可改(见 08 章) |
| proxy | dev 反代 /api 到后端端口,避免 CORS 配置漂移 |
'/api' → 后端端口(含 ws: true 供 WebSocket) |
| 构建指纹 | define 注入版本/commit hash |
adp:__APP_VERSION__ + git rev-parse 兜底 |
必须按需引入,禁止全量 app.use(组件库) + 全量 CSS(gva 全量引入是反面教材,首屏体积大)。
3.2 路由库
- Vue:
vue-router@4,createWebHistory(hash 模式是历史包袱,新工程用 history) - React:
react-router@7,用路由配置数组 +<Route>声明式或createBrowserRouter;禁止用Object.entries().map+ 三元链选页面渲染
3.3 HTTP 客户端
| 库 | 使用方 | 要点 |
|---|---|---|
| axios | vben / soybean / pure / gva | 拦截器、取消(AbortController)生态成熟 |
| umi-request / axios | adp | 错误契约挂载在 request config 上(skipErrorHandler 等) |
| fetch | — | 零依赖,但拦截器/超时/取消需自建,不推荐作为管理端基础 |
必须统一 axios:拦截器结构、错误对象形态在全员脑中保持一致,排障不用切换心智模型。
四、TypeScript 采用策略
4.1 三档策略
| 档位 | 适用 | 做法 | 标杆 |
|---|---|---|---|
| 纯 JS + JSDoc | 快速交付的存量 Vue 主端 | 关键模块(request/permission/stores)写 JSDoc 类型注释 | gva |
| 渐进 TS | 存量 JS 工程演进 | 新文件 .ts,老文件不动;allowJs: true;先建 typings/ 写 .d.ts |
soybean 路线 |
| TS strict | 新工程(默认) | strict: true,类型即文档 |
adp、vben、soybean、pure |
新项目必须 TS strict;存量工程按上表渐进,但接口函数不允许「既无类型注释也无类型定义」。
4.2 类型放哪里(adp/soybean 范式)
- 全局契约类型集中声明:
declare namespace API { interface RuleListItem {...} },服务层函数签名request<API.RuleList>(...)收口 - 页面私有类型随页面 co-location(
data.d.ts/utils/types.ts) - 路由 meta 用
vue-router.d.ts模块扩充强类型化(vben 30+ 字段全 JSDoc;soybean 17 字段同款) - soybean 补充:全局类型按 namespace 组织(
typings/下 Api/App/Storage/Env 四区,storage 与 env 均有键级类型,i18n key 递归派生)——JS 工程渐进 TS 的最低成本路径:先建typings/写.d.ts,不动业务代码
五、状态管理选型
5.1 先分类再选库
状态三类:
├─ 客户端全局状态(token、用户信息、菜单、UI 偏好)→ Pinia / Zustand
├─ 服务端状态(列表、详情、缓存失效) → TanStack Query / ProTable request / 自研 usePagedQuery
└─ URL 状态(筛选、分页、Tab) → 路由 query,永远不要复制进 store
5.2 三种落法
| 落法 | 出处 | 说明 |
|---|---|---|
| adp 三层 | initialState(useModel)+ React Query + ProTable request | 服务端状态交给 Query:queryKey 按资源命名,mutation 成功后 invalidateQueries |
| vben 分域 store | Pinia setup 风格,按域拆 modules(gva 同构,补充参考) | token/user/router/app/dictionary 各一,持久化手动分层 |
| pure StoreHook | useXxxStoreHook() 包装 store + pinia 实例 |
在 setup 外(拦截器、守卫、工具函数)也能安全取 store |
| soybean 模块化 | 每模块一目录 index.ts(状态编排)+ shared.ts(可单测纯函数) |
纯逻辑与响应式状态强制分离 |
经验值:列表页优先「组件内管理 + 请求取消」(AbortController + 版本号方案,见 03 章),只有跨页面共享的数据才进 store。不要为了「规范」把每个列表都塞进全局 store。
六、技术栈对照总表
| 维度 | ant-design-pro | vue-vben-admin | soybean-admin | vue-pure-admin | gin-vue-admin | 本标准推荐值 |
|---|---|---|---|---|---|---|
| 框架 | React 19 | Vue 3 | Vue 3.5 | Vue 3.5 | Vue 3 | Vue 3 或 React(单栈) |
| 语言 | TS strict | TS strict | TS strict(namespace 组织) | TS(strict 关闭) | JS | 新项目 TS strict;存量渐进 |
| UI 库 | antd v6 + Pro | antd-vue/EP/naive 适配 | Naive UI + UnoCSS | Element Plus + TW v4 | Element Plus | Vue: EP;React: antd 或 shadcn |
| 构建 | Umi Max(utoopack) | Vite + Turbo monorepo | Vite(轻 workspace) | Vite 8/rolldown 单包 | Vite | Vite + pnpm |
| 状态 | useModel + RQ | Pinia + 持久化插件 | Pinia(index+shared) | Pinia(StoreHook 模式) | Pinia 分域 | Pinia(Vue)/ Zustand+RQ(React) |
| 请求 | umi-request + 错误契约 | RequestClient 类 | 五钩子工厂 + flat 返回 | PureHttp 类(196 行) | axios 单例 | axios 封装(见 03 章) |
| 样式 | Tailwind v4 + antd-style | HSL Token + TW v4 | ThemeSetting 三端同步 | EP↔TW v4 变量桥接 | SCSS + UnoCSS + EP 变量 | 语义 Token 单源(见 06 章) |
| 规范 | Biome + commitlint | oxlint + eslint + lefthook | soybeanjs/eslint-config + sa CLI | eslint + stylelint + husky | ESLint flat + Prettier | ESLint flat + Prettier + commitlint |
| 测试 | Vitest + Playwright | Vitest(内核组件全覆盖) | —(未见体系化) | —(未见体系化) | 无(短板) | Vitest + Playwright(见 08 章) |
七、AI 检查清单(对比目标工程逐项核对)
P0(结构性的,立即改)
- 工程是否单栈?若双栈并存,是否有明确的「主端/副端 + 退役计划」与统一契约命名?
- 是否存在同工程两套 UI 库 / 两套状态库 / 两种 CSS 方案?
- 组件库是否按需引入(unplugin 或手动)?全量
app.use()即不合格 - 包管理器是否 pnpm?
preinstall: only-allow pnpm是否配置?
P1(质量项,排期改)
- 新文件是否 TS / 存量是否补 JSDoc?接口函数是否存在「无类型也无注释」
- 路由渲染是否声明式(禁止 entries+三元链)?
- HTTP 客户端是否统一 axios?
- 状态三分法是否被遵守(URL 状态不进 store、列表不进全局 store)?
P2(演进项)
- 是否需要
typings/目录承接全局契约类型(soybean 四区范式)? - 构建指纹(版本/commit 注入)是否配置?
下一篇:02-项目架构与目录结构 —— 目录是架构的最后陈述。