pangu-user-platform/docs/05-前端UI规范/前端UI规范文档_v1.0.md

553 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 盘古用户平台 - 前端UI规范文档 v1.0
> 适用于本项目所有前端页面开发
> 作者:湖北新华业务中台研发团队
> 创建时间2026-01-31
---
## 一、设计原则
### 1.1 统一性原则
- 保持全系统视觉风格一致
- 相同功能使用相同交互模式
- 统一的间距、颜色、字体规范
### 1.2 简洁性原则
- 页面布局清晰,信息层次分明
- 避免过度设计,突出核心功能
- 表格列宽合理,无大面积空白
### 1.3 易用性原则
- 操作直观,减少用户学习成本
- 反馈及时,状态清晰可见
- 错误提示友好,引导用户纠正
---
## 二、色彩规范
### 2.1 主题色
| 类型 | 色值 | 用途 |
|------|------|------|
| 主色 | `#409EFF` | 按钮、链接、选中态 |
| 成功色 | `#67C23A` | 正常状态、成功提示 |
| 警告色 | `#E6A23C` | 警告提示 |
| 危险色 | `#F56C6C` | 停用状态、删除操作、错误提示 |
| 信息色 | `#909399` | 辅助信息 |
### 2.2 中性色
| 类型 | 色值 | 用途 |
|------|------|------|
| 主文本 | `#303133` | 标题、正文 |
| 常规文本 | `#606266` | 表格、描述 |
| 次要文本 | `#909399` | 提示、禁用态 |
| 占位文本 | `#C0C4CC` | placeholder |
| 边框色 | `#DCDFE6` | 边框、分割线 |
| 背景色 | `#F5F7FA` | 表头、背景 |
### 2.3 侧边栏配色
| 类型 | 色值 |
|------|------|
| 背景色 | `#304156` |
| 选中背景 | `#409EFF` |
| 文字颜色 | `#FFFFFF` |
---
## 三、布局规范
### 3.1 页面结构
```vue
<template>
<div class="app-container">
<!-- 搜索区域 -->
<el-card shadow="never" class="search-wrapper">
<el-form>...</el-form>
</el-card>
<!-- 表格区域 -->
<el-card shadow="never" style="margin-top: 12px;">
<!-- 操作按钮 -->
<el-row :gutter="10" style="margin-bottom: 12px;">
<el-col :span="1.5">
<el-button type="primary" :icon="Plus">新增</el-button>
</el-col>
</el-row>
<!-- 表格 -->
<el-table>...</el-table>
<!-- 分页 -->
<el-pagination />
</el-card>
<!-- 弹窗 -->
<el-dialog>...</el-dialog>
</div>
</template>
<style scoped>
.app-container {
padding: 16px;
}
.search-wrapper {
margin-bottom: 0;
}
</style>
```
### 3.2 左右分栏布局
适用于:学校管理、学生管理等需要左侧树形筛选的页面
```vue
<el-row :gutter="16">
<!-- 左侧树形区域 -->
<el-col :span="6">
<el-card shadow="never">
<template #header>区域筛选</template>
<el-input v-model="filterText" placeholder="输入关键字过滤" />
<el-tree :data="treeData" :filter-node-method="filterNode" />
</el-card>
</el-col>
<!-- 右侧列表区域 -->
<el-col :span="18">
<!-- 搜索表单 + 表格 -->
</el-col>
</el-row>
```
### 3.3 间距规范
| 场景 | 间距值 |
|------|--------|
| 页面内边距 | 16px |
| 卡片间距 | 12px |
| 表单项间距 | 默认16px |
| 按钮组间距 | 10px |
| 表格与分页间距 | 16px |
---
## 四、表格规范
### 4.1 基础表格属性
```vue
<el-table
:data="tableData"
border
stripe
v-loading="loading"
:header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
style="width: 100%"
>
```
**必须设置的属性:**
- `border`:显示边框
- `stripe`:斑马纹
- `v-loading`:加载状态
- `header-cell-style`:表头样式
- `style="width: 100%"`:全宽显示
### 4.2 列宽规范
| 字段类型 | 推荐宽度 | 说明 |
|---------|---------|------|
| ID/编号 | `width="80-140"` | 固定宽度 |
| 名称/标题 | `min-width="120-200"` | 弹性宽度使用min-width |
| 编码 | `width="100-120"` | 固定宽度 |
| 状态/类型 | `width="80-100"` | 固定宽度,居中 |
| 日期时间 | `width="160"` | 固定宽度 |
| 手机号 | `width="120"` | 固定宽度 |
| 操作列 | `width="120-200"` | 根据按钮数量,固定右侧 |
### 4.3 列定义示例
```vue
<!-- 名称列弹性宽度 + 溢出提示 -->
<el-table-column prop="name" label="名称" min-width="150" show-overflow-tooltip />
<!-- 状态列固定宽度 + 居中 + Tag -->
<el-table-column prop="status" label="状态" width="80" align="center">
<template #default="{ row }">
<el-tag :type="row.status === '0' ? 'success' : 'danger'">
{{ row.status === '0' ? '正常' : '停用' }}
</el-tag>
</template>
</el-table-column>
<!-- 操作列固定右侧 -->
<el-table-column label="操作" width="180" fixed="right" align="center">
<template #default="{ row }">
<el-button link type="primary" :icon="Edit" @click="handleEdit(row)">编辑</el-button>
<el-button link type="danger" :icon="Delete" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
```
### 4.4 空数据处理
```vue
<el-table :data="tableData">
<template #empty>
<el-empty description="暂无数据" />
</template>
</el-table>
```
### 4.5 树形表格
```vue
<el-table
:data="tableData"
row-key="id"
border
:default-expand-all="isExpand"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
```
**注意事项:**
- `row-key` 必须指定为唯一标识字段
- 确保数据结构中的字段名与 `prop` 一致
---
## 五、表单规范
### 5.1 搜索表单
```vue
<el-form :model="queryParams" :inline="true" class="search-form">
<el-form-item label="名称" prop="name">
<el-input
v-model="queryParams.name"
placeholder="请输入名称"
clearable
style="width: 200px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="全部" clearable style="width: 100px">
<el-option label="正常" value="0" />
<el-option label="停用" value="1" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
```
### 5.2 弹窗表单
```vue
<el-dialog
v-model="dialogVisible"
:title="dialogTitle"
width="600px"
:close-on-click-modal="false"
destroy-on-close
>
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="100px"
>
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" placeholder="请输入名称" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-switch v-model="form.status" active-value="0" inactive-value="1" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">确定</el-button>
</template>
</el-dialog>
```
### 5.3 输入框宽度规范
| 场景 | 推荐宽度 |
|------|---------|
| 搜索表单-短输入 | 100-150px |
| 搜索表单-中输入 | 200px |
| 搜索表单-日期范围 | 240px |
| 弹窗表单 | 100%(默认) |
---
## 六、分页规范
```vue
<el-pagination
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
style="margin-top: 16px; justify-content: flex-end"
@size-change="handleQuery"
@current-change="handleQuery"
/>
```
**必须设置:**
- `layout="total, sizes, prev, pager, next, jumper"`:完整布局
- `style="justify-content: flex-end"`:右对齐
- `:page-sizes="[10, 20, 50, 100]"`:页码选项
---
## 七、状态Tag规范
### 7.1 状态类型映射
| 状态 | Tag类型 | 示例 |
|------|---------|------|
| 正常/启用 | `success` | 正常 |
| 停用/禁用 | `danger` | 停用 |
| 家长 | `primary`(默认) | 家长 |
| 教师 | `success` | 教师 |
| 男 | `primary`(默认) | 男 |
| 女 | `danger` | 女 |
| 未知 | `info` | 未知 |
### 7.2 代码示例
```vue
<!-- 状态Tag -->
<el-tag :type="row.status === '0' ? 'success' : 'danger'">
{{ row.status === '0' ? '正常' : '停用' }}
</el-tag>
<!-- 身份类型Tag -->
<el-tag :type="row.identityType === '1' ? '' : 'success'">
{{ row.identityType === '1' ? '家长' : '教师' }}
</el-tag>
<!-- 性别Tag -->
<el-tag :type="row.gender === '1' ? '' : row.gender === '2' ? 'danger' : 'info'">
{{ { '0': '未知', '1': '男', '2': '女' }[row.gender] }}
</el-tag>
```
---
## 八、交互规范
### 8.1 删除确认
```javascript
const handleDelete = (row) => {
ElMessageBox.confirm(`确定要删除"${row.name}"吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteApi(row.id).then(() => {
ElMessage.success('删除成功')
handleQuery()
})
}).catch(() => {})
}
```
### 8.2 消息提示
| 场景 | 类型 | 示例 |
|------|------|------|
| 操作成功 | `success` | 新增成功、修改成功、删除成功 |
| 操作失败 | `error` | 操作失败,请重试 |
| 警告提示 | `warning` | 请先选择数据 |
| 信息提示 | `info` | 已复制到剪贴板 |
```javascript
ElMessage.success('操作成功')
ElMessage.error('操作失败')
ElMessage.warning('请先选择数据')
ElMessage.info('已复制到剪贴板')
```
### 8.3 Loading状态
- 表格加载:使用 `v-loading` 指令
- 按钮提交:使用 `:loading` 属性
- 页面加载使用全局Loading
---
## 九、Mock数据规范
### 9.1 响应格式
```javascript
// 成功响应
{ code: 200, msg: '操作成功', data: { ... } }
// 分页响应
{ code: 200, msg: '查询成功', total: 100, rows: [ ... ] }
// 错误响应
{ code: 500, msg: '操作失败' }
```
### 9.2 Mock文件规范
```javascript
/**
* XXX模块Mock数据
* @author 湖北新华业务中台研发团队
*/
import Mock from 'mockjs'
// 预置数据定义
const dataList = [...]
// GET请求用RegExp匹配URL支持query参数
Mock.mock(/\/api\/xxx\/list/, 'get', (options) => {
return { code: 200, total: 100, rows: [...] }
})
// POST/PUT/DELETE用字符串或正则匹配
Mock.mock('/api/xxx', 'post', { code: 200, msg: '新增成功' })
```
### 9.3 字段命名规范
**重要Mock数据中的字段名必须与视图中 `prop` 属性保持一致!**
```javascript
// ✅ 正确字段名与视图prop一致
{ regionName: '湖北省', regionCode: 'HB' }
// 对应视图:<el-table-column prop="regionName" />
// ❌ 错误字段名与视图prop不一致
{ name: '湖北省', code: 'HB' }
// 对应视图:<el-table-column prop="regionName" /> -- 会导致数据不显示
```
---
## 十、文件命名规范
### 10.1 视图文件
| 类型 | 位置 | 命名 |
|------|------|------|
| 主页面 | `views/xxx/index.vue` | index.vue |
| 弹窗组件 | `views/xxx/components/XxxDialog.vue` | 大驼峰+Dialog |
### 10.2 API文件
| 位置 | 命名 | 示例 |
|------|------|------|
| `src/api/` | 小驼峰.js | `school.js`, `member.js` |
### 10.3 Mock文件
| 位置 | 命名 | 示例 |
|------|------|------|
| `src/mock/` | 小驼峰.js | `school.js`, `member.js` |
| 入口文件 | `src/mock/index.js` | 汇总所有mock |
---
## 十一、已知问题清单
### 11.1 区域管理页面
**问题类型:** Mock数据字段不匹配
**严重程度:**
**问题描述:** 区域名称列显示为空
**原因分析:**
- 视图使用 `prop="regionName"`但Mock返回 `name`
- 视图使用 `prop="regionCode"`但Mock返回 `code`
**修复方案:**
```javascript
// 修改 src/mock/region.js将字段名改为
{ regionName: '湖北省', regionCode: 'HB', ... }
// 或修改视图 prop 为:
<el-table-column prop="name" label="区域名称" />
```
### 11.2 应用管理页面
**问题类型:** Mock数据字段不匹配
**严重程度:**
**问题描述:** 授权接口列为空
**原因分析:**
- 视图使用 `row.apis`但Mock返回 `row.apiAuth`
**修复方案:**
```javascript
// 修改 src/views/application/index.vue 第38行
// 将 row.apis 改为 row.apiAuth
<el-tag v-for="api in (row.apiAuth || []).slice(0, 3)" ...>
```
### 11.3 年级管理页面
**问题类型:** Mock接口未正确匹配
**严重程度:**
**问题描述:** 表格显示"No Data"
**原因分析:** API调用路径与Mock匹配可能有问题
**修复方案:** 检查API调用路径是否与Mock正则匹配
### 11.4 班级管理、学科管理页面
**问题类型:** 同年级管理
**需确认:** 同样可能存在Mock匹配问题
### 11.5 学校管理页面
**问题类型:** 样式问题
**严重程度:**
**问题描述:**
- 操作列按钮显示为多行,较拥挤
- 创建时间列可能被截断
**修复方案:**
- 增加操作列宽度到 220px 或 240px
- 减少操作按钮文字,如"新增年级"改为"加年级"
### 11.6 Mock数据时间问题
**问题类型:** 数据质量
**严重程度:**
**问题描述:** 部分Mock生成的时间数据不合理如1970年、1971年
**修复方案:** 限制时间范围为近5年
---
## 十二、修复优先级
| 优先级 | 页面 | 问题 | 影响 |
|--------|------|------|------|
| P0 | 区域管理 | 字段不匹配导致数据不显示 | 页面不可用 |
| P0 | 年级/班级/学科管理 | Mock未返回数据 | 页面不可用 |
| P1 | 应用管理 | 授权接口列为空 | 功能缺失 |
| P2 | 学校管理 | 操作列过窄 | 体验不佳 |
| P3 | 全局 | Mock时间数据不合理 | 影响展示 |
---
*文档版本v1.0*
*创建时间2026-01-31*
*维护团队:湖北新华业务中台研发团队*