All checks were successful
Build and Deploy Front / deploy (push) Successful in 17m52s
运营端顶栏布局不变;客户侧栏按常用/仓储/更多分组,余额与打印沉底,打印提示按门户区分。 Co-authored-by: Cursor <cursoragent@cursor.com>
693 lines
24 KiB
Markdown
693 lines
24 KiB
Markdown
# 中后台前端页面设计规范
|
||
|
||
> 基于 itfx-front 项目现有技术栈与实现,整理的一套通用、可直接落地的前端设计规范。
|
||
> 后续开发须遵循本规范,确保视觉与交互一致性。
|
||
|
||
---
|
||
|
||
## 一、技术栈约定
|
||
|
||
| 类别 | 技术选型 | 版本/说明 |
|
||
|------|----------|-----------|
|
||
| 框架 | Vue 3 | 使用 Composition API、`<script setup>` |
|
||
| UI 库 | Arco Design Vue | 主 UI 框架,优先使用 |
|
||
| 辅助 UI | Element Plus | 仅在特殊场景使用 |
|
||
| 表格 | vxe-table / Handsontable | 见「表格」章节 |
|
||
| 构建 | Vite | 6.x |
|
||
| 状态 | Pinia | |
|
||
| 国际化 | vue-i18n | |
|
||
| 样式 | Less + UnoCSS + 全局 CSS 变量 | |
|
||
| 路由 | vue-router | 4.x |
|
||
|
||
---
|
||
|
||
## 二、布局规范
|
||
|
||
### 2.1 整体结构
|
||
|
||
```
|
||
a-layout (垂直)
|
||
├── TopNav(仅运营端,fixed)
|
||
├── a-layout-content
|
||
│ ├── 运营端:paddingTop = 顶栏 + Tab;PageLayout
|
||
│ └── 客户门户:CustomerSideRail(全高)+ PageLayout(无顶栏、无多页签)
|
||
└── Footer(可选)
|
||
```
|
||
|
||
运营端布局 DOM/样式与改前一致;客户门户不挂载 TopNav / TabBar。
|
||
|
||
**约定:**
|
||
|
||
- 主内容背景色:`#f0f3f4`
|
||
- 页面容器使用 `[class$='-container']`,统一 `padding: 16px`
|
||
- 内容区域使用 `overflow: hidden`、`height: 100%` 保证高度正确
|
||
|
||
**客户门户 vs 运营端导航(差异约定):**
|
||
|
||
| | 运营端 | 客户门户 |
|
||
|---|---|---|
|
||
| 顶栏 | 一级模块 + hover 二级 + 右侧余额/打印/用户 | **无顶栏** |
|
||
| 页签 | TabBar 多页签 | **无多页签**(当前页仅由侧栏高亮表达) |
|
||
| 侧栏 | 无全局侧栏 | **全高可折叠左栏**(默认展开):顶 Logo;中按「常用 / 仓储 / 更多」浅分组;**资产与账户收进用户下拉**;底沉余额/打印(含下载)/消息/仓库;**语言收进用户菜单** |
|
||
| 高亮 | 路由最长路径匹配 | 侧栏最长路径匹配;账户类路由高亮底栏用户区 |
|
||
| 数据 | RBAC 菜单树原样 | 仍用同一菜单树,`flatMap` 二级即可,不改表结构 |
|
||
|
||
### 2.2 页面内容结构
|
||
|
||
单页内容推荐结构:
|
||
|
||
```html
|
||
<div class="xxx-container" style="background-color: #fff;">
|
||
<!-- 1. 页面头部操作区(可选) -->
|
||
<div class="page-header">
|
||
<a-space>...</a-space>
|
||
<a-space>...</a-space>
|
||
</div>
|
||
|
||
<!-- 2. 表格工具栏 -->
|
||
<div class="table-toolbar">
|
||
<div class="toolbar-left">主操作、批量操作</div>
|
||
<div class="toolbar-right">分页控制等</div>
|
||
</div>
|
||
|
||
<!-- 3. 表格/表单内容 -->
|
||
...
|
||
</div>
|
||
```
|
||
|
||
### 2.3 工具栏布局
|
||
|
||
- 使用 `table-toolbar` 作为表格上方工具栏
|
||
- 左侧 `toolbar-left`:新增、删除、导出等主操作
|
||
- 右侧 `toolbar-right`:分页、筛选等
|
||
- **分页位置(必选)**:所有列表的分页组件统一放在**紧邻列表上方**工具栏的**右侧**(即相对列表区域的**右上角**),见「七、7.3 分页」;不得放在表格下方或列表底部
|
||
- 使用 `a-space :size="8"` 或 `display: flex; gap: 8px` 控制按钮间距
|
||
|
||
### 2.4 响应式断点(Less)
|
||
|
||
```less
|
||
@screen-xs: 480px; // 手机
|
||
@screen-sm: 576px; // 平板
|
||
@screen-md: 768px; // 小屏
|
||
@screen-lg: 992px; // 桌面
|
||
@screen-xl: 1200px; // 大屏
|
||
@screen-xxl: 1600px; // 超大屏
|
||
```
|
||
|
||
### 2.5 页面壳、白卡片与列表区尺寸(列表 / Handsontable 类页面)
|
||
|
||
以下约定与 `default-layout` + `page-layout`、`*-container` / `*-page` 等实现一致,避免「灰底大块留白、列表高度塌缩、内外边距叠成 32px/40px」等问题。
|
||
|
||
#### 2.5.1 页面层(灰底壳)
|
||
|
||
| 项目 | 约定 |
|
||
|------|------|
|
||
| 背景色 | `#f0f3f4`(与布局主内容区一致) |
|
||
| 页面根容器 padding | **`16px 16px 24px`**:上/左/右 **16px**,**底 24px** —— 与视口底边留白,列表区随 flex 撑满后,整块内容与屏幕底边保持 **24px** 间距 |
|
||
| 参与主内容区高度 | `flex: 1; min-height: 0; display: flex; flex-direction: column;`,与 `PageLayout` 子节点 flex 链衔接 |
|
||
| 纵向滚动 | 页面根容器宜 `overflow-y: hidden`(或仅最外层允许滚动),**表格/Handsontable 在内部 `.list-wrap` / `.wtHolder` 滚动**,避免整页与表格双滚动条 |
|
||
|
||
#### 2.5.2 白卡片(白色内容块)
|
||
|
||
| 项目 | 约定 |
|
||
|------|------|
|
||
| 视觉 | 背景 `#fff`,`border-radius: 8px`,`box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08)`(可按页面微调透明度) |
|
||
| 撑满剩余高度 | `flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden` |
|
||
| 内边距(与列表区配合) | **二选一,避免与 `.list-wrap` 重复叠加**:<br>• **推荐**:白卡片 **`padding: 0`**,由 **页头/搜索行、`.table-toolbar`、`.list-wrap`** 分区控制留白(见下);<br>• **简化页**:白卡片统一 `padding: 16px`,则内部列表区不再重复加左右底 padding |
|
||
|
||
#### 2.5.3 表格工具栏(`.table-toolbar`)
|
||
|
||
| 项目 | 约定 |
|
||
|------|------|
|
||
| 与列表横向对齐 | `padding: 16px 16px 0`(上 16、左右 16,底由与列表的间距承担) |
|
||
| 与分页/标题间距 | `margin-bottom: 16px`(分页组件(`PaginationPro`)与列表区 `.list-wrap` 的固定间距) |
|
||
| 收缩 | `flex-shrink: 0` |
|
||
|
||
分页组件放置约束:`PaginationPro` 放在 `.toolbar-right` 内,并与 `toolbar-left` 保持在同一行(同一个 `flex` 行中)展示;工具栏外层使用 `padding: 16px 16px 0` 以保证分页组件距上边/右边均为 16px,且不需要在 `PaginationPro` 外再额外套一层 padding。
|
||
|
||
有**独立搜索行**(如轨迹配置页)时:搜索框可用 `margin: 16px 16px 16px`,与工具栏、列表区 **左右 16px** 对齐。
|
||
|
||
#### 2.5.4 列表区(`.list-wrap`)
|
||
|
||
用于包裹 **HandsontablePro** 或需 **固定测量高度** 的表格容器(配合 `useTableContainerHeight` 的 `tableContainerRef` **挂在本层**)。
|
||
|
||
| 项目 | 约定 |
|
||
|------|------|
|
||
| 与内容区四边距离 | **默认**:`padding: 16px`(上、右、下、左均为 **16px**,相对**白卡片内容边缘**或分区后的可用区域,勿与白卡片 padding 重复叠加)。<br>**与 `.list-toolbar` 联用**:当 `.list-toolbar` 使用 `margin-bottom: 16px` 作为“分页组件到列表”的固定间距时,为避免重复留白,建议 `.list-wrap` 使用 `padding: 0 16px 16px`(上 0、左右/下 16)。 |
|
||
| 布局 | `flex: 1; min-height: 0; width: 100%; box-sizing: border-box; overflow: hidden; display: flex; flex-direction: column` |
|
||
| 触摸/滚动冒泡 | 可选:`overscroll-behavior: contain; touch-action: pan-x pan-y` |
|
||
| 子级 Handsontable | 建议 `:deep(.handsontable-pro-container) { flex: 1; min-height: 0; display: flex; flex-direction: column; }`,保证纵向撑满 |
|
||
|
||
分页组件同一行约束(列表工具栏场景):当页面存在 `.list-toolbar` / `table-toolbar` 时,`PaginationPro` 必须放在右侧同一行的工具栏区域(如 `.toolbar-right`),不要将分页组件单独再做成一行(避免“工具栏一行 + 分页下一行”的不一致布局)。
|
||
|
||
#### 2.5.5 与布局组件的关系
|
||
|
||
- `PageLayout` 下直接子节点(路由页根)需能被主内容区赋予高度:项目中通过 `flex: 1; min-height: 0` 等与 `a-layout-content` 链式配合。
|
||
- `default-layout` 中对 `[class$='-container']` 的样式仅作用于布局模板内节点;**业务页根类名**(如 `xxx-page`、`track-order-container`)需在**页面自身样式**中按上表补齐 flex 与 padding。
|
||
|
||
---
|
||
|
||
## 三、色彩规范
|
||
|
||
### 3.1 主题色(CSS 变量)
|
||
|
||
通过 `:root` 统一管理,运行时由 `themeColor` 工具更新:
|
||
|
||
```css
|
||
:root {
|
||
--app-primary-color: #1d7281;
|
||
--app-primary-color-hover: #186170;
|
||
--app-primary-color-light: #e8f3f5;
|
||
--app-navbar-bg: #1d7281;
|
||
--app-sidebar-bg: #1d7281;
|
||
--app-button-gap: 8px; /* 相邻按钮间距 */
|
||
}
|
||
```
|
||
|
||
**约定:**
|
||
|
||
- 主色:`var(--app-primary-color)` 或 `#1d7281`
|
||
- Hover 色:`var(--app-primary-color-hover)`
|
||
- 浅底(选中背景):`var(--app-primary-color-light)`
|
||
- 页面背景:`var(--color-bg-1)`(Arco)
|
||
- 内容区背景:`#fff` 或 `#f0f3f4`
|
||
|
||
### 3.2 组件配色
|
||
|
||
- 主按钮、Switch、Tabs 指示线、菜单选中:使用主题色
|
||
- 日期选择器、Input focus:`border-color: var(--app-primary-color)`
|
||
- 链接激活态:`color: var(--app-primary-color)`
|
||
|
||
---
|
||
|
||
## 四、字体规范
|
||
|
||
### 4.1 字体栈
|
||
|
||
```css
|
||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
|
||
"Microsoft YaHei", Inter, Roboto, "Helvetica Neue", Arial, sans-serif;
|
||
```
|
||
|
||
代码字体:
|
||
|
||
```css
|
||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
|
||
"Courier New", monospace;
|
||
```
|
||
|
||
### 4.2 字号与行高
|
||
|
||
- 全局基准字号:`14px`
|
||
- 表单、按钮、输入框:`14px`
|
||
- 日期选择器、部分紧凑区域:`12px`(如 `.notice` 内)
|
||
- 菜单文字:`12px`,`font-weight: bold`
|
||
|
||
---
|
||
|
||
## 五、组件规范
|
||
|
||
### 5.1 按钮
|
||
|
||
- 高度:`32px`,`border-radius: 4px`(抽屉/卡片内软操作可用 `12px`,与亚克力面板一致)
|
||
- 图标按钮:`32px × 32px` 正方形
|
||
- **相邻按钮间距**:统一 `8px`
|
||
- 使用 `a-space :size="8"` 或 `gap: 8px` / `gap: var(--app-button-gap)`
|
||
- 适用于工具栏、表格操作列、弹窗底部按钮组
|
||
- **推荐用法**
|
||
- 主操作:`type="primary"`,或工具栏新增用 `app-btn-primary-soft`
|
||
- 查询:`app-btn-search`
|
||
- 次要/取消:默认 `a-button`(无 type)或抽屉内与面板同风格的软按钮
|
||
- 表格行内操作:优先图标按钮 / `type="text"`(见操作列规范)
|
||
- **禁止**:Arco `type="outline"` 默认描边按钮(灰硬边框、白底,与主题亚克力/软主色体系完全不搭)。新页面与改版时一律不要使用;次要操作改用上文推荐样式。
|
||
|
||
### 5.2 输入类(Input / Select / Picker)
|
||
|
||
- 统一高度:`32px`(Input)、`28px`(Picker)
|
||
- `border-radius: 4px`
|
||
- Focus / Hover 使用主题色 border 与 shadow
|
||
|
||
### 5.3 卡片 / 弹窗 / 下拉
|
||
|
||
- `border-radius: 8px`(Card、Modal、Popover、Dropdown、Drawer、Table、Tag、Alert、Message、Notification)
|
||
|
||
### 5.4 Pro 系列组件
|
||
|
||
| 组件 | 用途 |
|
||
|------|------|
|
||
| FormPro | 配置驱动表单,itemList + comp + compProps |
|
||
| TablePro | VxeGrid 封装,tableConfig + api + formConfig |
|
||
| CustomList | 自定义列 + TablePro,支持列管理 |
|
||
| HandsontablePro | 类 Excel 表格 |
|
||
| ModalPro | 弹窗,配合 useModal |
|
||
| DrawerPro | 抽屉 |
|
||
| SelectPro | 增强选择器 |
|
||
| JsonEditorPro / JsonDiffPro | JSON 编辑与对比 |
|
||
| CodeMirror | 代码编辑 |
|
||
|
||
**约定:**
|
||
|
||
- 增强型封装统一使用 `Pro` 后缀
|
||
- 优先使用 Pro 组件,减少重复造轮子
|
||
|
||
### 5.5 消息与确认
|
||
|
||
- 使用 `$baseMessage`、`$baseConfirm`(如有)
|
||
- 或 Arco 的 `Message`、`Modal.confirm`、`a-popconfirm`
|
||
|
||
---
|
||
|
||
## 六、表单规范
|
||
|
||
### 6.1 布局
|
||
|
||
- 表单类型:`horizontal`、`vertical`、`inline`
|
||
- 标签列:`label-col-props="{ span: 6 }"`(或 5)
|
||
- 内容列:`wrapper-col-props="{ span: 16 }"`(或 19)
|
||
- FormPro:`labelColProps: { span: 5 }`、`wrapperColProps: { span: 19 }`、`rowGutter: 20`、`defaultSpan: 6`
|
||
|
||
### 6.2 FormPro 配置示例
|
||
|
||
```ts
|
||
itemList: [
|
||
{
|
||
comp: 'AInput',
|
||
field: 'name',
|
||
label: '名称',
|
||
required: true,
|
||
span: 6,
|
||
rules: [{ required: true, message: '请输入名称' }],
|
||
compProps: { placeholder: '请输入' },
|
||
},
|
||
{
|
||
comp: 'ASelect',
|
||
field: 'status',
|
||
label: '状态',
|
||
compProps: { options: [...], placeholder: '请选择' },
|
||
},
|
||
]
|
||
```
|
||
|
||
### 6.3 校验
|
||
|
||
- 使用 Arco Form 的 `rules`
|
||
- 提交前调用 `formRef.validate()` 统一校验
|
||
- 必填项使用 `required: true` 并配置 `message`
|
||
|
||
---
|
||
|
||
## 七、表格规范
|
||
|
||
### 7.1 组件选择
|
||
|
||
| 场景 | 组件 | 说明 |
|
||
|------|------|------|
|
||
| 常规 CRUD 列表 | TablePro | 带搜索表单、分页、API 集成 |
|
||
| 自定义列 + 列管理 | CustomList | 包裹 TablePro |
|
||
| 类 Excel 编辑 | HandsontablePro | 单元格编辑、复制粘贴 |
|
||
| 简单列表(角色、菜单等) | a-table | Arco 原生 |
|
||
|
||
### 7.2 TablePro 配置
|
||
|
||
- `tableConfig`:columns、toolbarConfig 等
|
||
- `api`:分页接口,参数含 `pageIndex`、`pageSize`、表单值
|
||
- `formConfig`:搜索区域
|
||
- `columnConfig.useKey: true`、`resizable: true`
|
||
- `showOverflow: 'tooltip'`
|
||
|
||
### 7.3 分页
|
||
|
||
- **位置(必选)**:所有分页组件(`PaginationPro`、Arco `a-pagination`、项目内其它分页封装等)须置于**列表的右上角**——即放在**紧邻列表上方**的工具栏**右侧**(`table-toolbar` / `.list-toolbar` 的 `.toolbar-right`,与 `toolbar-left` 同一行),与列表左右对齐、视觉上位于列表区域上方靠右。禁止将分页放在表格**下方**、列表**底部**、或单独居中另起一行(与「二、2.5.3」「2.5.4」工具栏约定一致)。
|
||
- 可选 pageSize:`10 / 30 / 50 / 100`(按需裁剪)
|
||
- 默认 pageSize:`50`
|
||
- 接口参数:`pageIndex`、`pageSize`;返回:`totalCount`、`items` 或 `data.list`
|
||
|
||
### 7.4 操作列
|
||
|
||
- 编辑、删除、分配等使用 `a-button type="text" size="small"`
|
||
- 删除使用 `a-popconfirm`
|
||
- 操作按钮需权限控制(见「权限」)
|
||
|
||
### 7.5 HandsontablePro 列定义(必读)
|
||
|
||
**HandsontablePro 的 `columns` 与原生 Handsontable 不同**,必须使用项目约定的 `ColumnDef` 结构:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `key` | `string` | 对应行数据字段名(如 `jobId`、`statusLabel`) |
|
||
| `label` | `string` | 表头文案(支持 `t('...')`) |
|
||
| `width` | `number` | 列宽(像素) |
|
||
| `headerMode` | `'inline' \| 'icon' \| 'none'` | 混合表头:内嵌搜索 / 仅图标 / 纯展示;未设时由 `searchable` + `filterType` 推导 |
|
||
| `searchable` / `searchField` / `multiValue` | — | 表头搜索;`multiValue` 显示「多」批量入口 |
|
||
|
||
**正确示例:**
|
||
|
||
```ts
|
||
const hotColumns = computed(() => [
|
||
{ key: 'jobId', label: t('order.batchJobs.table.jobId'), width: 90 },
|
||
{ key: 'statusLabel', label: t('order.batchJobs.table.status'), width: 100 },
|
||
])
|
||
```
|
||
|
||
**错误示例(会导致表头、单元格均为空):**
|
||
|
||
```ts
|
||
// ❌ 勿用 Handsontable 原生 data/title
|
||
{ data: 'jobId', title: '任务 ID', width: 90 }
|
||
```
|
||
|
||
**操作列渲染器**签名固定为 `(rowData, rowIndex) => string`,勿写成 `(row, col, rowData)`。
|
||
|
||
**固定列序(组件内置,勿再传 `action-column-position` / `action-column-width`):**
|
||
|
||
| 顺序 | 列 | 说明 |
|
||
|------|----|------|
|
||
| 1 | 操作列(可选) | 有 `show-action-column` 时永远第一列;宽度按按钮内容自适应;开启 `stretch-columns` 时不被拉伸 |
|
||
| 2 | 序号列 | 永远紧随操作列(无操作时为第一列);固定宽度 `50px`;开启 stretch 时不被拉伸 |
|
||
| 3 | 复选框列(可选) | `show-checkbox-column`;固定 `60px`,stretch 时不被拉伸 |
|
||
| 4+ | 数据列 | `columns` 定义的业务列(stretch 时仅这些列被拉宽) |
|
||
|
||
`customCellRenderer(row, col, value, rowData)` 的 `col` 为**表格绝对列索引**(含操作/序号/复选框)。有操作列时数据列从 `col - 2` 起算(另有复选框再减 1)。
|
||
|
||
行数据字段名须与 `key` 一致;展示用衍生字段(如 `actionLabel`)在 `mapRow` 中提前组装。
|
||
|
||
---
|
||
|
||
## 八、交互规范
|
||
|
||
### 8.1 弹窗 / 抽屉
|
||
|
||
- 使用 `ModalPro` + `useModal()` 管理显隐与数据
|
||
- 标准流程:`openModal(true, data)` → 编辑 → `setLoading(true)` → 提交 → 成功 → `closeModal()`、刷新列表
|
||
- 弹窗宽度:常规 `600px`,复杂表单 `800px` 或更高
|
||
|
||
### 8.2 加载与空态
|
||
|
||
- 表格:`loading` 状态
|
||
- 弹窗提交:`confirm-loading`
|
||
- 空数据:使用 Arco `Empty` 或统一空态组件
|
||
|
||
### 8.3 按钮顺序
|
||
|
||
- 主操作(确定、保存)在右侧
|
||
- 取消 / 重置在左侧
|
||
- destructive 操作(删除)单独放置,并加确认
|
||
|
||
---
|
||
|
||
## 九、权限规范
|
||
|
||
### 9.1 权限格式
|
||
|
||
`Module.Resource.Action`,如:
|
||
|
||
- `Rbac.Roles.Create`
|
||
- `Rbac.Users.Update`
|
||
- `Api.Interfaces.Query`
|
||
- `Track.Orders.Query`
|
||
|
||
### 9.2 使用方式
|
||
|
||
**模板:**
|
||
|
||
```vue
|
||
<a-button
|
||
v-if="hasPermission('Rbac.Roles.Create')"
|
||
type="primary"
|
||
@click="handleAdd"
|
||
>
|
||
新增
|
||
</a-button>
|
||
```
|
||
|
||
**路由:**
|
||
|
||
```ts
|
||
{
|
||
path: '/system/roles',
|
||
meta: { permission: 'Rbac.Roles.Query' },
|
||
}
|
||
```
|
||
|
||
**工具函数:**
|
||
|
||
```ts
|
||
import { hasPermission, hasAnyPermission, hasAllPermissions, hasRole } from '@/utils/rbac-permission';
|
||
```
|
||
|
||
### 9.3 菜单
|
||
|
||
- 菜单基于 RBAC `menus` 与 `userMenuIdSet` 过滤
|
||
- 无权限的菜单项不展示
|
||
|
||
---
|
||
|
||
## 十、命名规范
|
||
|
||
### 10.1 文件与目录
|
||
|
||
| 类型 | 规范 | 示例 |
|
||
|------|------|------|
|
||
| 页面组件 | `index.vue` 置于功能目录下 | `views/api/upstreamInterface/index.vue` |
|
||
| 子组件 | PascalCase 或 kebab-case | `ServiceProviderInterfaceModal.vue`、`AccountList.vue` |
|
||
| 布局组件 | kebab-case | `top-nav`、`tab-bar`、`page-layout` |
|
||
| 共享组件 | PascalCase | `FormPro`、`TablePro`、`CustomList` |
|
||
| API 模块 | camelCase 或 PascalCase | `interface-serviceProvider`、`TrackOrder` |
|
||
| Store 模块 | kebab-case | `rbac-user`、`tab-bar`、`app` |
|
||
| 国际化 | `zh-CN.ts`、`en-US.ts` | `locale/zh-CN.ts` |
|
||
|
||
### 10.2 代码命名
|
||
|
||
| 类型 | 规范 |
|
||
|------|------|
|
||
| 变量 / 方法 | camelCase |
|
||
| 常量 | UPPER_SNAKE_CASE 或 camelCase |
|
||
| 类型 / 接口 | PascalCase |
|
||
| 组件名 | PascalCase |
|
||
|
||
### 10.3 路由
|
||
|
||
- path:camelCase,如 `/api/upstreamInterface`、`/track/kpiEarlyWarningConfig`
|
||
- name:PascalCase,如 `InterfaceDetailTest`
|
||
|
||
### 10.4 国际化 Key
|
||
|
||
- 格式:`模块.资源.键` 或 `common.xxx`
|
||
- 示例:`system.role.actions.add`、`common.search`、`common.reset`
|
||
|
||
---
|
||
|
||
## 十一、国际化规范
|
||
|
||
### 11.1 结构
|
||
|
||
- 根:`src/locale/zh-CN.ts`、`en-US.ts`
|
||
- 按页面拆分:`views/xxx/locale/zh-CN.ts`,在根 locale 中合并
|
||
|
||
### 11.2 使用
|
||
|
||
```vue
|
||
<template>{{ t('common.search') }}</template>
|
||
<script setup>
|
||
const { t } = useI18n();
|
||
</script>
|
||
```
|
||
|
||
### 11.3 必备 Key
|
||
|
||
- `common.search`、`common.reset`、`common.add`、`common.delete`、`common.editSuccess`、`common.addSuccess`、`common.saveSuccess`
|
||
- `common.selectPlaceholder`、`common.actions`、`common.viewDetail`
|
||
|
||
---
|
||
|
||
## 十二、API 与请求规范
|
||
|
||
### 12.1 分页接口
|
||
|
||
**请求:**
|
||
|
||
```ts
|
||
{
|
||
pageIndex: number;
|
||
pageSize: number;
|
||
// 其它搜索参数
|
||
}
|
||
```
|
||
|
||
**响应:**
|
||
|
||
```ts
|
||
{
|
||
totalCount: number;
|
||
items: T[]; // 或 data.list
|
||
}
|
||
```
|
||
|
||
### 12.2 错误处理
|
||
|
||
- 使用统一的 axios 拦截器(如 `rbac-error-handler`)
|
||
- 401 自动跳转登录;其它错误统一提示
|
||
|
||
---
|
||
|
||
## 十三、样式约定
|
||
|
||
### 13.1 作用域
|
||
|
||
- 组件内使用 `scoped`
|
||
- 覆盖第三方样式时使用 `:deep()` 或全局类
|
||
|
||
### 13.2 容器类
|
||
|
||
- 页面根容器:`xxx-container` / `xxx-page`(如 `role-page`、`track-order-container`),**列表类页面**推荐 `padding: 16px 16px 24px`,详见 **§2.5 页面壳、白卡片与列表区尺寸**
|
||
- 白卡片:`*-page-card`、`*-section` 等,与 `.list-wrap` 的 padding **勿重复叠加**
|
||
- 列表区:`.list-wrap`,**四边 16px**(相对分区后的内容边缘)
|
||
- 表格工具栏:`table-toolbar`、`toolbar-left`、`toolbar-right`,工具栏 **`padding: 16px 16px 0`** 与列表对齐
|
||
- Handsontable/列表分页工具栏:`.list-toolbar`(当页面没有标准 `table-toolbar` 时),与 `table-toolbar` 保持一致,使用 **`padding: 16px 16px 0`** 且 `margin-bottom: 16px`;`PaginationPro` 必须与 `toolbar-left` 同一行展示(放在 `.toolbar-right` 内,禁止分页另起行),同时 `.list-wrap` 在联用场景建议 `padding-top: 0`(例如 `padding: 0 16px 16px`),实现分页距上/右 16px、距列表 16px
|
||
|
||
### 13.3 按钮间距
|
||
|
||
- 相邻 `arco-btn` 之间:`margin-left: 8px`(全局已统一)
|
||
- 使用 `a-space :size="8"` 包裹一组按钮
|
||
|
||
---
|
||
|
||
## 十四、检查清单(新页面开发)
|
||
|
||
- [ ] 使用 `xxx-container` / `xxx-page` 或等效布局结构;列表页见 **§2.5**:灰底 **`padding: 16px 16px 24px`**、`flex: 1; min-height: 0`
|
||
- [ ] 白卡片与 **`.list-wrap`** 内边距勿重复叠加(推荐白卡片 `padding: 0` + 分区 padding)
|
||
- [ ] **`.list-wrap`**:`padding: 16px`、`flex: 1; min-height: 0`;需测高时 `ref` 挂在本层
|
||
- [ ] 工具栏使用 `table-toolbar`、`toolbar-left`、`toolbar-right`,工具栏 **`padding: 16px 16px 0`**
|
||
- [ ] 按钮、输入框符合全局样式(高度、圆角);未使用 `type="outline"` 描边按钮
|
||
- [ ] 使用主题色变量,不硬编码 `#1d7281`
|
||
- [ ] 弹窗 / 抽屉使用 ModalPro / DrawerPro + useModal
|
||
- [ ] 表单使用 FormPro 或统一 label/span
|
||
- [ ] 表格优先 TablePro / CustomList / HandsontablePro
|
||
- [ ] **HandsontablePro** 列定义使用 **`key` + `label`**,勿用 `data` + `title`(**§7.5**)
|
||
- [ ] 分页组件置于**列表上方工具栏右侧**(列表右上角),**§7.3**;分页 10/30/50/100,默认 50
|
||
- [ ] 操作按钮按权限使用 `hasPermission`
|
||
- [ ] 路由 meta 配置 `permission`
|
||
- [ ] **部署**:前端与 Admin API **分域名**(如 `ideship.com` / `api.ideship.com`);TLS 走 Traefik + cert-manager(**§十五**)
|
||
- [ ] 文案通过 i18n,不写死中文
|
||
- [ ] 命名符合本规范(文件、路由、变量、i18n key)
|
||
|
||
---
|
||
|
||
## 十五、部署与网关(Traefik / SPA)
|
||
|
||
生产已弃用站内 nginx。静态资源由前端容器(`serve -s`)提供,**HTTPS** 由 k3s Traefik `websecure` + cert-manager(Let's Encrypt)签发;流量路径:
|
||
|
||
`浏览器 → :443 Traefik(websecure) → Ingress(tls) → Service → 前端 Pod(:80)`
|
||
|
||
**约定:**
|
||
|
||
1. **前后端分域名**(方案 A):站点如 `ideship.com` / `test.ideship.com`,API 如 `api.ideship.com` / `api.test.ideship.com`。浏览器直连 API,前端容器不做 Admin 反代,也无 `/__cos__` 反代。
|
||
2. SPA 回退由 `serve -s`(或等价静态服务器)处理;刷新 `/order/batchJobs` 等路径应回 `index.html`,不依赖 nginx `try_files`。
|
||
3. 官网 **`/`** 为 Vue 路由(`src/views/site`);静态资源在 **`/site/`**;**`/9610.html`** 为 `public/9610.html`。客户登录 **`/login`**,管理端 **`/admin`**。
|
||
4. 面单/附件预览:开发可用 Vite `/__cos__`;生产优先 `file-storage/access-url` 新鲜签名直开,失败再 `stream` → blob(勿依赖过期库内签名 URL)。
|
||
5. 本地 `pnpm dev` 不受 Ingress 限制;`/` 即为官网。
|
||
|
||
---
|
||
|
||
## 附录:常用代码片段
|
||
|
||
### 页面头部 + 权限按钮
|
||
|
||
```vue
|
||
<div class="page-header">
|
||
<a-space>
|
||
<a-button
|
||
v-if="hasPermission('Module.Resource.Create')"
|
||
type="primary"
|
||
@click="handleAdd"
|
||
>
|
||
<template #icon><icon-plus /></template>
|
||
{{ t('common.add') }}
|
||
</a-button>
|
||
</a-space>
|
||
<a-space>
|
||
<a-button @click="loadData">
|
||
<template #icon><icon-refresh /></template>
|
||
</a-button>
|
||
</a-space>
|
||
</div>
|
||
```
|
||
|
||
### 表格操作列(编辑 / 删除)
|
||
|
||
```vue
|
||
<template #action="{ record }">
|
||
<div class="action-buttons">
|
||
<a-button
|
||
v-if="hasPermission('Module.Resource.Update')"
|
||
type="text"
|
||
size="small"
|
||
@click="handleEdit(record)"
|
||
>
|
||
<IconEdit />
|
||
</a-button>
|
||
<a-popconfirm
|
||
v-if="hasPermission('Module.Resource.Delete')"
|
||
:content="t('common.deleteConfirm')"
|
||
@ok="handleDelete(record.id)"
|
||
>
|
||
<a-button type="text" size="small">
|
||
<IconDelete />
|
||
</a-button>
|
||
</a-popconfirm>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
### HandsontablePro 列表页(列 + 高度)
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
import HandsontablePro from '@/components/HandsontablePro/index.vue'
|
||
import { useTableContainerHeight } from '@/composables/useTableContainerHeight'
|
||
|
||
const { tableContainerRef, tableHeight: handsontableHeight } = useTableContainerHeight()
|
||
|
||
const hotColumns = computed(() => [
|
||
{ key: 'jobId', label: t('module.table.jobId'), width: 90 },
|
||
{ key: 'statusLabel', label: t('module.table.status'), width: 100 },
|
||
])
|
||
</script>
|
||
|
||
<template>
|
||
<div class="module-page">
|
||
<div class="module-page-card">
|
||
<div class="table-toolbar">...</div>
|
||
<a-spin class="list-spin" :loading="listLoading">
|
||
<div ref="tableContainerRef" class="list-wrap">
|
||
<HandsontablePro
|
||
:columns="hotColumns"
|
||
:data="tableRows"
|
||
:height="handsontableHeight"
|
||
:stretch-columns="true"
|
||
/>
|
||
</div>
|
||
</a-spin>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
布局样式见 **§2.5**(`flex: 1; min-height: 0`,勿给 `.list-wrap` 写死 `min-height: 480px` 导致列表区塌缩或撑破)。
|
||
|
||
### FormPro 查询表单
|
||
|
||
```vue
|
||
<FormPro
|
||
ref="formProRef"
|
||
:item-list="formItemList"
|
||
:button-config="{ showBtn: true }"
|
||
@submit="handleSearch"
|
||
@reset="handleReset"
|
||
/>
|
||
```
|
||
|
||
---
|
||
|
||
*文档版本:1.2 | 更新日期:2026-03-23*
|