mirror of
https://github.com/hpd840321/craftlabs-authorization-sdk.git
synced 2026-06-10 02:20:28 +08:00
docs: add frontend framework design (10 sections: architecture, modules, layout, components, templates, dataflow, conventions)
This commit is contained in:
@@ -0,0 +1,665 @@
|
|||||||
|
# CraftLabs 前端框架设计 v1.0
|
||||||
|
|
||||||
|
> 基于设计体系 `2026-05-19-design-system.md` 提炼的前端工程框架规范
|
||||||
|
> 目标:统一架构、约束风格、加速开发、降低交接成本
|
||||||
|
> 日期:2026-05-19
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 目录
|
||||||
|
|
||||||
|
1. [框架概述](#1-框架概述)
|
||||||
|
2. [技术栈](#2-技术栈)
|
||||||
|
3. [工程结构](#3-工程结构)
|
||||||
|
4. [核心模块](#4-核心模块)
|
||||||
|
5. [布局系统](#5-布局系统)
|
||||||
|
6. [组件体系](#6-组件体系)
|
||||||
|
7. [页面模板](#7-页面模板)
|
||||||
|
8. [数据流](#8-数据流)
|
||||||
|
9. [开发规范](#9-开发规范)
|
||||||
|
10. [构建与部署](#10-构建与部署)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 框架概述
|
||||||
|
|
||||||
|
### 1.1 定位
|
||||||
|
|
||||||
|
CraftLabs 前端框架是基于 Vue 3 + Element Plus 的企业级 B2B 授权运营平台前端方案。在 Element Plus 之上注入品牌设计 Token,提供开箱即用的布局、组件、页面模板和开发约束。
|
||||||
|
|
||||||
|
### 1.2 核心原则
|
||||||
|
|
||||||
|
| 原则 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **约定优于配置** | 路由、鉴权、API 封装、错误处理均有默认行为,开发者只需关注业务 |
|
||||||
|
| **Token 驱动** | 所有视觉属性通过 CSS 变量控制,换肤只需改 `theme.css` |
|
||||||
|
| **组件隔离** | 每个 `.vue` 文件自包含模板+逻辑+样式,`<script setup>` 统一风格 |
|
||||||
|
| **渐进增强** | 在 Element Plus 基础上叠加品牌样式,不 fork 组件库 |
|
||||||
|
| **安全默认** | JWT 存储 localStorage、401 自动登出、路由级鉴权 |
|
||||||
|
|
||||||
|
### 1.3 层级架构
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────┐
|
||||||
|
│ Views (页面层) │
|
||||||
|
│ LicenseList CustomersView ContractsView │
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ Components (组件层) │
|
||||||
|
│ StatsCard SearchBar DataTable ... │
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ Layout System (布局系统) │
|
||||||
|
│ MainLayout (Header+Sidebar+Breadcrumb) │
|
||||||
|
├──────────────┬──────────────┬───────────────┤
|
||||||
|
│ Router │ Store │ API │
|
||||||
|
│ (鉴权/懒加载) │ (Pinia JWT) │ (axios封装) │
|
||||||
|
├──────────────┴──────────────┴───────────────┤
|
||||||
|
│ Element Plus + Vue 3 │
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ Theme System (Token层) │
|
||||||
|
│ theme.css · CSS Variables │
|
||||||
|
└─────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 技术栈
|
||||||
|
|
||||||
|
| 层 | 选型 | 版本 | 说明 |
|
||||||
|
|----|------|:--:|------|
|
||||||
|
| 框架 | Vue 3 | ^3.5 | Composition API + `<script setup>` |
|
||||||
|
| 构建 | Vite | ^6.0 | 极速 HMR + 开箱即用 |
|
||||||
|
| UI 库 | Element Plus | ^2.9 | 组件库本体,CSS 变量覆盖换肤 |
|
||||||
|
| 路由 | vue-router | ^4.5 | 懒加载 + meta 鉴权 |
|
||||||
|
| 状态 | Pinia | ^2.3 | 轻量状态管理 |
|
||||||
|
| HTTP | axios | ^1.7 | 拦截器 + JWT 注入 |
|
||||||
|
| 语言 | JavaScript (ESM) | — | 暂用 JS,必要时渐进引入 TS |
|
||||||
|
|
||||||
|
### 2.1 不引入的依赖
|
||||||
|
|
||||||
|
| 库 | 原因 |
|
||||||
|
|----|------|
|
||||||
|
| Tailwind CSS | Element Plus 已覆盖组件样式,避免双体系 |
|
||||||
|
| Vuex | Pinia 更轻量,官方推荐 |
|
||||||
|
| Lodash | 项目规模不需要工具函数库 |
|
||||||
|
| Moment.js | 日期格式化用原生 `Intl` 或字符串裁剪 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 工程结构
|
||||||
|
|
||||||
|
```
|
||||||
|
web/delivery-platform-ui/
|
||||||
|
├── public/
|
||||||
|
│ ├── design-demo.html # 设计 Demo(独立 HTML,无依赖)
|
||||||
|
│ └── design-system.html # 设计体系可视化
|
||||||
|
├── src/
|
||||||
|
│ ├── main.js # 入口:挂载 Vue + Pinia + Router + 401拦截器
|
||||||
|
│ ├── App.vue # 根组件:纯 <router-view>
|
||||||
|
│ ├── theme.css # ★ 全局主题 Token(CSS 变量)
|
||||||
|
│ │
|
||||||
|
│ ├── layout/
|
||||||
|
│ │ └── MainLayout.vue # ★ 主布局:Header + Sidebar + Breadcrumb
|
||||||
|
│ │
|
||||||
|
│ ├── router/
|
||||||
|
│ │ └── index.js # ★ 路由表 + 鉴权守卫
|
||||||
|
│ │
|
||||||
|
│ ├── stores/
|
||||||
|
│ │ └── auth.js # ★ 认证状态:JWT + login/logout/roles
|
||||||
|
│ │
|
||||||
|
│ ├── api/
|
||||||
|
│ │ └── platform.js # ★ API 封装:按模块导出 axios 请求函数
|
||||||
|
│ │
|
||||||
|
│ ├── utils/
|
||||||
|
│ │ ├── apiErrorMessage.js # 统一错误消息提取
|
||||||
|
│ │ └── redactPayload.js # Callback payload 脱敏
|
||||||
|
│ │
|
||||||
|
│ └── views/ # ★ 页面组件(按模块命名)
|
||||||
|
│ ├── HomeView.vue # 工作台/Dashboard
|
||||||
|
│ ├── LoginView.vue # 登录页
|
||||||
|
│ ├── ForbiddenView.vue # 403
|
||||||
|
│ ├── NotFoundView.vue # 404
|
||||||
|
│ ├── CustomersView.vue # 客户管理
|
||||||
|
│ ├── ProjectsView.vue # 项目管理
|
||||||
|
│ ├── ContractsView.vue # 合同列表
|
||||||
|
│ ├── ContractWizardView.vue # 合同创建向导
|
||||||
|
│ ├── ContractDetailView.vue # 合同详情
|
||||||
|
│ ├── DeliveriesView.vue # 交付列表
|
||||||
|
│ ├── DeliveryBatchWizardView.vue
|
||||||
|
│ ├── DeliveryBatchDetailView.vue
|
||||||
|
│ ├── LicenseSnListView.vue # 许可SN列表
|
||||||
|
│ ├── LicenseSnWizardView.vue
|
||||||
|
│ ├── LicenseSnDetailView.vue
|
||||||
|
│ ├── LicenseList.vue # ★ 许可证管理(自研SDK核心页)
|
||||||
|
│ ├── CallbackInboxView.vue # Callback 收件箱
|
||||||
|
│ ├── CallbackInboxDetailView.vue
|
||||||
|
│ ├── IntegrationEnvironmentsView.vue
|
||||||
|
│ ├── IntegrationProductLinesView.vue
|
||||||
|
│ └── LayoutCompareView.vue # 设计审核工具(内部)
|
||||||
|
│
|
||||||
|
├── package.json
|
||||||
|
├── vite.config.js
|
||||||
|
├── Dockerfile
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.1 文件命名规范
|
||||||
|
|
||||||
|
| 类型 | 规范 | 示例 |
|
||||||
|
|------|------|------|
|
||||||
|
| 页面视图 | `PascalCaseView.vue` | `CustomersView.vue` |
|
||||||
|
| 布局组件 | `PascalCase.vue` | `MainLayout.vue` |
|
||||||
|
| 路由/Store/API | `kebab-case.js` | `platform.js` |
|
||||||
|
| 工具函数 | `camelCase.js` | `apiErrorMessage.js` |
|
||||||
|
| 样式文件 | `kebab-case.css` | `theme.css` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 核心模块
|
||||||
|
|
||||||
|
### 4.1 主题系统 (`theme.css`)
|
||||||
|
|
||||||
|
所有视觉 Token 集中在一个 CSS 文件中,通过覆盖 Element Plus 变量实现换肤。
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* 核心 3 行即可改变全站主色调 */
|
||||||
|
:root {
|
||||||
|
--el-color-primary: #2C3E6B;
|
||||||
|
--el-bg-color-page: #EAEFFA;
|
||||||
|
--el-border-radius-base: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 完整 Token 见 docs/superpowers/specs/2026-05-19-design-system.md */
|
||||||
|
```
|
||||||
|
|
||||||
|
**设计意图**:
|
||||||
|
- 不修改 Element Plus 源码,只覆盖 CSS 变量
|
||||||
|
- 更换品牌色只需改 `--el-color-primary` 一个值
|
||||||
|
- 新页面自动继承主题,无需额外引入
|
||||||
|
|
||||||
|
### 4.2 路由系统 (`router/index.js`)
|
||||||
|
|
||||||
|
**路由表结构**:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const routes = [
|
||||||
|
// 公开路由(无鉴权)
|
||||||
|
{ path: "/login", component: LoginView },
|
||||||
|
|
||||||
|
// 受保护路由(需要登录)
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
component: MainLayout, // 统一布局壳
|
||||||
|
meta: { requiresAuth: true },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "licenses", // 子路由路径
|
||||||
|
name: "licenses", // 命名路由(用于编程跳转)
|
||||||
|
component: () => import("..."), // 懒加载
|
||||||
|
meta: { roles: ["SYS_ADMIN", "DEVELOPER"] } // 角色鉴权
|
||||||
|
},
|
||||||
|
// ... 更多子路由
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// 兜底路由
|
||||||
|
{ path: "/403", component: ForbiddenView },
|
||||||
|
{ path: "/:pathMatch(.*)*", component: NotFoundView },
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
**鉴权守卫逻辑**:
|
||||||
|
|
||||||
|
```
|
||||||
|
用户访问页面
|
||||||
|
│
|
||||||
|
├─ meta.requiresAuth && 无 token → 重定向 /login?redirect=原路径
|
||||||
|
│
|
||||||
|
├─ meta.roles && 用户角色不匹配 → 重定向 /403
|
||||||
|
│
|
||||||
|
└─ 通过 → 正常渲染
|
||||||
|
```
|
||||||
|
|
||||||
|
**新增页面只需**:
|
||||||
|
1. 在 `children` 数组中加一条路由
|
||||||
|
2. 设置 `meta.roles` 控制可见角色
|
||||||
|
3. `component: () => import(...)` 懒加载
|
||||||
|
|
||||||
|
### 4.3 状态管理 (`stores/auth.js`)
|
||||||
|
|
||||||
|
```js
|
||||||
|
export const useAuthStore = defineStore("auth", {
|
||||||
|
state: () => ({
|
||||||
|
token: localStorage.getItem("craftlabs_platform_token") || "",
|
||||||
|
displayName: "",
|
||||||
|
roles: [],
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
async login(username, password) {
|
||||||
|
const { data } = await axios.post("/api/v1/auth/login", { username, password })
|
||||||
|
this.token = data.token
|
||||||
|
this.roles = data.roles
|
||||||
|
localStorage.setItem("craftlabs_platform_token", this.token)
|
||||||
|
axios.defaults.headers.common.Authorization = `Bearer ${this.token}`
|
||||||
|
},
|
||||||
|
|
||||||
|
logout() {
|
||||||
|
this.token = ""
|
||||||
|
localStorage.removeItem("craftlabs_platform_token")
|
||||||
|
delete axios.defaults.headers.common.Authorization
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
**使用方式**:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { useAuthStore } from "../stores/auth"
|
||||||
|
const auth = useAuthStore()
|
||||||
|
|
||||||
|
// 模板中
|
||||||
|
auth.hasAnyRole(["SYS_ADMIN", "DEVELOPER"]) // 角色判断
|
||||||
|
auth.displayName // 用户名
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.4 API 层 (`api/platform.js`)
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 每个后端端点导出为一个具名函数
|
||||||
|
export function listCustomers(params) {
|
||||||
|
return axios.get("/api/v1/customers", { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCustomer(body) {
|
||||||
|
return axios.post("/api/v1/customers", body)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateCustomer(id, body) {
|
||||||
|
return axios.put(`/api/v1/customers/${id}`, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteCustomer(id) {
|
||||||
|
return axios.delete(`/api/v1/customers/${id}`)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**约定**:
|
||||||
|
- 函数名 = HTTP 动词 + 资源名:`listXxx` / `createXxx` / `updateXxx` / `deleteXxx` / `getXxx`
|
||||||
|
- 状态变更用语义化函数名:`patchContractStatus()`、`addLine()`
|
||||||
|
- 所有函数返回 axios Promise,由调用方 `await` + try/catch
|
||||||
|
- 查询参数用 `{ params }` 传递(axios 自动序列化)
|
||||||
|
|
||||||
|
### 4.5 全局错误处理 (`main.js`)
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 401 自动登出拦截器
|
||||||
|
axios.interceptors.response.use(
|
||||||
|
(r) => r,
|
||||||
|
(err) => {
|
||||||
|
if (err.response?.status === 401) {
|
||||||
|
const auth = useAuthStore()
|
||||||
|
auth.logout()
|
||||||
|
router.push({ name: "login", query: { redirect: router.currentRoute.value.fullPath } })
|
||||||
|
}
|
||||||
|
return Promise.reject(err)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 布局系统
|
||||||
|
|
||||||
|
### 5.1 MainLayout 结构
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────────────┐
|
||||||
|
│ <header> 60px │
|
||||||
|
│ Logo │ 导航菜单 │ 全局搜索 │ 🔔 │ ⚙️ │ 👤用户名 │
|
||||||
|
├────────┬─────────────────────────────────────────────────┤
|
||||||
|
│<aside> │ <div class="breadcrumb"> 46px │
|
||||||
|
│ 232px │ 授权运营 › 当前页面 │
|
||||||
|
│ ├─────────────────────────────────────────────────┤
|
||||||
|
│ 菜单 │ │
|
||||||
|
│ 分组 │ <router-view /> │
|
||||||
|
│ │ (各页面通过 slot 注入到此区域) │
|
||||||
|
│ 11项 │ │
|
||||||
|
│ │ │
|
||||||
|
└────────┴─────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 布局尺寸契约
|
||||||
|
|
||||||
|
| 元素 | 尺寸 | CSS |
|
||||||
|
|------|:--:|------|
|
||||||
|
| Header | 60px | `height: 60px; flex-shrink: 0` |
|
||||||
|
| Sidebar | 232px | `width: 232px; flex-shrink: 0` |
|
||||||
|
| Breadcrumb | 46px | `height: 46px; flex-shrink: 0` |
|
||||||
|
| Content | 自适应 | `flex: 1; padding: 16px 20px` |
|
||||||
|
|
||||||
|
### 5.3 布局变体
|
||||||
|
|
||||||
|
| 变体 | 触发条件 | 说明 |
|
||||||
|
|------|---------|------|
|
||||||
|
| 标准布局 | 默认 | Header + Sidebar + Breadcrumb + 全宽 Content |
|
||||||
|
| Tree 布局 | `activeModule === 'licenses'` | 额外 280px Tree 面板在 Content 左侧 |
|
||||||
|
| 全屏布局 | 无 Header/Sidebar | 登录页、403/404 页 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 组件体系
|
||||||
|
|
||||||
|
### 6.1 组件分类
|
||||||
|
|
||||||
|
```
|
||||||
|
组件层级:
|
||||||
|
Element Plus 基础组件 (el-button, el-table, el-dialog, ...)
|
||||||
|
│
|
||||||
|
├── 直接使用(无需封装)
|
||||||
|
│ el-button el-input el-select el-table el-pagination
|
||||||
|
│ el-tag el-badge el-tree el-dialog el-card
|
||||||
|
│
|
||||||
|
├── 组合组件(页面内组合)
|
||||||
|
│ 搜索栏 + 表格 + 分页 → 列表页模板
|
||||||
|
│ 统计卡片行 → Dashboard 模板
|
||||||
|
│
|
||||||
|
└── 业务组件(跨页面复用,按需抽取)
|
||||||
|
StatsCard SearchFilterBar StatusTag ConfirmDialog
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 组件开发原则
|
||||||
|
|
||||||
|
- **优先直接用 Element Plus**:90% 场景不需要封装
|
||||||
|
- **抽取时机**:同一模式在 3+ 个页面出现时才抽取为独立组件
|
||||||
|
- **Props 优先于全局状态**:组件通过 props 接收数据,不直接读 store
|
||||||
|
- **Events 向上**:组件通过 `$emit` 通知父组件,不直接改父组件状态
|
||||||
|
|
||||||
|
### 6.3 CSS 约定
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* ✅ 推荐:scoped + CSS 变量 */
|
||||||
|
<style scoped>
|
||||||
|
.my-page { background: var(--el-bg-color-page); }
|
||||||
|
.card { border-radius: var(--el-border-radius-base); }
|
||||||
|
.btn-primary { background: var(--el-color-primary); }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
/* ❌ 避免:硬编码色值 */
|
||||||
|
<style scoped>
|
||||||
|
.card { background: #EAEFFA; } /* 应使用 CSS 变量 */
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 页面模板
|
||||||
|
|
||||||
|
### 7.1 标准列表页
|
||||||
|
|
||||||
|
适用于:客户管理、合同管理、交付管理、许可SN
|
||||||
|
|
||||||
|
```
|
||||||
|
结构:
|
||||||
|
<统计卡片行> (可选)
|
||||||
|
<筛选栏>
|
||||||
|
[搜索框] [状态下拉] [查询按钮] [+ 新建按钮]
|
||||||
|
</筛选栏>
|
||||||
|
<数据表格>
|
||||||
|
<el-table> + <el-pagination>
|
||||||
|
</数据表格>
|
||||||
|
<新建/编辑 Dialog>
|
||||||
|
```
|
||||||
|
|
||||||
|
**样板代码(约 200 行)**:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from "vue"
|
||||||
|
import { ElMessage, ElMessageBox } from "element-plus"
|
||||||
|
import { useAuthStore } from "../stores/auth"
|
||||||
|
import { listXxx, createXxx, updateXxx, deleteXxx } from "../api/platform"
|
||||||
|
import { apiErrorMessage } from "../utils/apiErrorMessage"
|
||||||
|
|
||||||
|
const auth = useAuthStore()
|
||||||
|
const loading = ref(false), saving = ref(false)
|
||||||
|
const rows = ref([]), total = ref(0), page = ref(1), pageSize = ref(10)
|
||||||
|
const keyword = ref(""), filterStatus = ref("")
|
||||||
|
const dialogVisible = ref(false), editingId = ref(null)
|
||||||
|
const formRef = ref(null)
|
||||||
|
const form = reactive({ name: "", ... })
|
||||||
|
|
||||||
|
onMounted(() => { auth.restoreAxiosAuth(); load() })
|
||||||
|
|
||||||
|
async function load() {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const { data } = await listXxx({ page: page.value-1, size: pageSize.value, keyword: keyword.value?.trim() })
|
||||||
|
rows.value = data.content ?? []
|
||||||
|
total.value = data.totalElements ?? 0
|
||||||
|
} catch(e) {
|
||||||
|
ElMessage.error(apiErrorMessage(e))
|
||||||
|
} finally { loading.value = false }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submit() {
|
||||||
|
await formRef.value.validate()
|
||||||
|
saving.value = true
|
||||||
|
try {
|
||||||
|
const payload = { name: form.name.trim(), ... }
|
||||||
|
editingId.value ? await updateXxx(editingId.value, payload) : await createXxx(payload)
|
||||||
|
dialogVisible.value = false
|
||||||
|
await load()
|
||||||
|
} catch(e) {
|
||||||
|
ElMessage.error(apiErrorMessage(e))
|
||||||
|
} finally { saving.value = false }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onDelete(row) {
|
||||||
|
await ElMessageBox.confirm(`确定删除「${row.name}」?`, "提示", { type: "warning" })
|
||||||
|
await deleteXxx(row.id)
|
||||||
|
await load()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 Tree + 列表页
|
||||||
|
|
||||||
|
适用于:许可证管理
|
||||||
|
|
||||||
|
```
|
||||||
|
结构:
|
||||||
|
<div class="license-page"> ← display: flex
|
||||||
|
<Tree Panel 280px> ← 左侧固定宽度
|
||||||
|
<Main Panel> ← flex: 1
|
||||||
|
<统计卡片行>
|
||||||
|
<筛选栏 + 签发按钮>
|
||||||
|
<数据表格>
|
||||||
|
</Main Panel>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.3 工作台 Dashboard
|
||||||
|
|
||||||
|
适用于:首页
|
||||||
|
|
||||||
|
```
|
||||||
|
结构:
|
||||||
|
<统计卡片行 4列>
|
||||||
|
<趋势图 + 待办列表 2列>
|
||||||
|
<快捷入口>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.4 详情页
|
||||||
|
|
||||||
|
适用于:合同详情、交付详情、SN详情
|
||||||
|
|
||||||
|
```
|
||||||
|
结构:
|
||||||
|
<返回按钮 + 页面标题>
|
||||||
|
<详情卡片>
|
||||||
|
<el-descriptions> 或 自定义 KV 布局
|
||||||
|
</详情卡片>
|
||||||
|
<关联数据卡片> (可选:审计事件、Callback关联)
|
||||||
|
<操作按钮行>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 数据流
|
||||||
|
|
||||||
|
### 8.1 请求生命周期
|
||||||
|
|
||||||
|
```
|
||||||
|
用户操作(点击查询/新建/保存)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
loading.value = true ← 显示加载态
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
await apiFunction(payload) ← axios 自动注入 JWT Header
|
||||||
|
│
|
||||||
|
├─ 200: data → 更新响应式状态
|
||||||
|
│ ElMessage.success("操作成功")
|
||||||
|
│
|
||||||
|
├─ 4xx: err → ElMessage.error(apiErrorMessage(e))
|
||||||
|
│ 401 → 自动登出
|
||||||
|
│ 403 → 提示无权限
|
||||||
|
│
|
||||||
|
└─ 5xx / Network Error → ElMessage.error("服务器错误")
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
loading.value = false ← 恢复交互
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.2 状态分类
|
||||||
|
|
||||||
|
| 状态类型 | 存储位置 | 示例 |
|
||||||
|
|---------|---------|------|
|
||||||
|
| 页面级临时状态 | 组件内 `ref()` / `reactive()` | 表格数据、表单数据、loading |
|
||||||
|
| 跨页面持久状态 | Pinia `auth.js` | token、用户名、角色 |
|
||||||
|
| UI 状态 | 组件内 | dialogVisible、activeTab |
|
||||||
|
| URL 状态 | `route.query` / `route.params` | 分页页码、详情页 ID |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. 开发规范
|
||||||
|
|
||||||
|
### 9.1 新增页面检查清单
|
||||||
|
|
||||||
|
```
|
||||||
|
□ 文件命名:PascalCaseView.vue
|
||||||
|
□ 使用 <script setup> + Composition API
|
||||||
|
□ 所有 API 调用有 try/catch + apiErrorMessage
|
||||||
|
□ 列表有 v-loading / 提交按钮有 :loading
|
||||||
|
□ 路由 meta 包含 roles 鉴权
|
||||||
|
□ 表格表头使用 #F2F5FC 背景(继承 theme.css)
|
||||||
|
□ 状态标签使用 .tag-active / .tag-revoked 等语义类
|
||||||
|
□ CTA 按钮使用 .btn-cta 带阴影
|
||||||
|
□ 色值使用 CSS 变量,不硬编码
|
||||||
|
□ 弹框使用 <el-dialog>(自动继承 8px 圆角)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.2 禁止事项
|
||||||
|
|
||||||
|
| 禁止 | 原因 |
|
||||||
|
|------|------|
|
||||||
|
| 硬编码色值 (`#EAEFFA`) | 应使用 CSS 变量,便于换肤 |
|
||||||
|
| 直接操作 DOM (`document.querySelector`) | 使用 Vue 响应式 + ref |
|
||||||
|
| 在 `setup` 外定义响应式数据 | 非 `.vue` 文件需使用 `defineStore` |
|
||||||
|
| `v-if` + `v-for` 同时使用 | Vue 性能警告 |
|
||||||
|
| 忽略 try/catch | 未捕获异常导致白屏 |
|
||||||
|
| 提交 `console.log` | 使用 `ElMessage` 或移除 |
|
||||||
|
|
||||||
|
### 9.3 Git 提交规范
|
||||||
|
|
||||||
|
```
|
||||||
|
feat(web): 新增许可证管理页面
|
||||||
|
fix(web): 修复合同列表分页重置bug
|
||||||
|
refactor(web): 抽取 StatsCard 为独立组件
|
||||||
|
style(web): 统一表格表头样式
|
||||||
|
docs: 更新前端框架设计文档
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. 构建与部署
|
||||||
|
|
||||||
|
### 10.1 开发环境
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd web/delivery-platform-ui
|
||||||
|
npm install
|
||||||
|
npm run dev # → http://localhost:5173
|
||||||
|
# /api → proxy → http://127.0.0.1:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.2 生产构建
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 指定后端地址
|
||||||
|
VITE_API_BASE=https://api.craftlabs.cn npm run build
|
||||||
|
|
||||||
|
# 产物:dist/
|
||||||
|
# 部署:Nginx 静态托管 + 反代 /api 到后端
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.3 CI/CD
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# .github/workflows/ci-platform.yml (前端部分)
|
||||||
|
- name: Build Frontend
|
||||||
|
run: |
|
||||||
|
cd web/delivery-platform-ui
|
||||||
|
npm ci
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
### 10.4 Nginx 配置模板
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name platform.craftlabs.cn;
|
||||||
|
root /var/www/delivery-platform-ui/dist;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://127.0.0.1:8080;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html; # SPA fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 附录 A:文件职责速查
|
||||||
|
|
||||||
|
| 文件 | 职责 | 谁需要修改 |
|
||||||
|
|------|------|-----------|
|
||||||
|
| `theme.css` | 全局色彩/圆角/阴影 Token | 设计师/UED |
|
||||||
|
| `MainLayout.vue` | 页面壳:Header/Sidebar/Breadcrumb | 架构师 |
|
||||||
|
| `router/index.js` | 路由表 + 鉴权守卫 | 全栈/前端 |
|
||||||
|
| `stores/auth.js` | JWT 认证状态 | 全栈/前端 |
|
||||||
|
| `api/platform.js` | 后端 API 封装 | 前端(新增接口时) |
|
||||||
|
| `utils/*.js` | 通用工具函数 | 前端 |
|
||||||
|
| `views/*View.vue` | 业务页面 | 前端 |
|
||||||
|
|
||||||
|
## 附录 B:快速上手
|
||||||
|
|
||||||
|
**创建一个新页面(以「审计日志」为例)**:
|
||||||
|
|
||||||
|
1. 创建 `src/views/AuditLogView.vue`,使用标准列表页模板
|
||||||
|
2. 在 `api/platform.js` 添加 `listAuditEvents()` 函数
|
||||||
|
3. 在 `router/index.js` 添加路由,设置 `meta.roles`
|
||||||
|
4. 在 `MainLayout.vue` 的 `menuItems` 数组添加菜单项
|
||||||
|
5. `npm run dev` 验证
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*修订记录:2026-05-19 初版,基于设计体系 v1.0*
|
||||||
Reference in New Issue
Block a user