18 KiB
中后台前端页面设计规范
基于 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(router-view + keep-alive)
└── Footer(可选)
约定:
- 主内容背景色:
#f0f3f4 - 页面容器使用
[class$='-container'],统一padding: 16px - 内容区域使用
overflow: hidden、height: 100%保证高度正确
2.2 页面内容结构
单页内容推荐结构:
<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)
@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 重复叠加:• 推荐:白卡片 padding: 0,由 页头/搜索行、.table-toolbar、.list-wrap 分区控制留白(见下);• 简化页:白卡片统一 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 重复叠加)。与 .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 工具更新:
:root {
--app-primary-color: #2D8C8C;
--app-primary-color-hover: #2A7F7F;
--app-primary-color-light: #e6f7f7;
--app-navbar-bg: #2D8C8C;
--app-sidebar-bg: #2D8C8C;
--app-button-gap: 8px; /* 相邻按钮间距 */
}
约定:
- 主色:
var(--app-primary-color)或#2D8C8C - 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 字体栈
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
"Microsoft YaHei", Inter, Roboto, "Helvetica Neue", Arial, sans-serif;
代码字体:
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 - 图标按钮:
32px × 32px正方形 - 相邻按钮间距:统一
8px- 使用
a-space :size="8"或gap: 8px/gap: var(--app-button-gap) - 适用于工具栏、表格操作列、弹窗底部按钮组
- 使用
- 主操作使用
type="primary"
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 配置示例
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: trueshowOverflow: 'tooltip'
7.3 分页
- 位置(必选):所有分页组件(
PaginationPro、Arcoa-pagination、项目内其它分页封装等)须置于列表的右上角——即放在紧邻列表上方的工具栏右侧(table-toolbar/.list-toolbar的.toolbar-right,与toolbar-left同一行),与列表左右对齐、视觉上位于列表区域上方靠右。禁止将分页放在表格下方、列表底部、或单独居中另起一行(与「二、2.5.3」「2.5.4」工具栏约定一致)。 - 可选 pageSize:
10 / 30 / 50 / 100(按需裁剪) - 默认 pageSize:
30 - 接口参数:
pageIndex、pageSize;返回:totalCount、items或data.list
7.4 操作列
- 编辑、删除、分配等使用
a-button type="text" size="small" - 删除使用
a-popconfirm - 操作按钮需权限控制(见「权限」)
八、交互规范
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.CreateRbac.Users.UpdateApi.Interfaces.QueryTrack.Orders.Query
9.2 使用方式
模板:
<a-button
v-if="hasPermission('Rbac.Roles.Create')"
type="primary"
@click="handleAdd"
>
新增
</a-button>
路由:
{
path: '/system/roles',
meta: { permission: 'Rbac.Roles.Query' },
}
工具函数:
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 使用
<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.saveSuccesscommon.selectPlaceholder、common.actions、common.viewDetail
十二、API 与请求规范
12.1 分页接口
请求:
{
pageIndex: number;
pageSize: number;
// 其它搜索参数
}
响应:
{
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 - 按钮、输入框符合全局样式(高度、圆角)
- 使用主题色变量,不硬编码
#2D8C8C - 弹窗 / 抽屉使用 ModalPro / DrawerPro + useModal
- 表单使用 FormPro 或统一 label/span
- 表格优先 TablePro / CustomList / HandsontablePro
- 分页组件置于列表上方工具栏右侧(列表右上角),§7.3;分页 10/30/50/100,默认 30
- 操作按钮按权限使用
hasPermission - 路由 meta 配置
permission - 文案通过 i18n,不写死中文
- 命名符合本规范(文件、路由、变量、i18n key)
附录:常用代码片段
页面头部 + 权限按钮
<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>
表格操作列(编辑 / 删除)
<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>
FormPro 查询表单
<FormPro
ref="formProRef"
:item-list="formItemList"
:button-config="{ showBtn: true }"
@submit="handleSearch"
@reset="handleReset"
/>
文档版本:1.1 | 更新日期:2026-03-23