53 KiB
前端技术设计规范
目录
项目概述
本项目是一个企业智能运营管理系统(Smart Ops Front),采用 Vue 3 + Vite + Element Plus 技术栈构建,提供完整的权限管理、知识库管理、CRM管理、OA审批等功能模块。
设计原则
- 统一性:所有页面采用相同的布局结构和交互模式
- 简洁性:基于Element Plus组件库,避免重度定制样式
- 可维护性:使用composable模式和组件分离提高代码质量
- 可扩展性:模块化设计,支持功能快速迭代
技术栈
核心框架
- Vue 3.3.4 - 渐进式JavaScript框架
- Vite 4.4.9 - 下一代前端构建工具
- Vue Router 4.2.4 - 官方路由管理器
- Pinia 2.1.6 - Vue状态管理库
UI组件库
- Element Plus 2.10.4 - 基于Vue 3的组件库
- @element-plus/icons-vue 2.1.0 - Element Plus图标库
表格组件
- vxe-table 4.14.1 - 高性能表格组件
- xe-utils 3.7.6 - 表格工具库
编辑器组件
- @umoteam/editor 7.0.1 - 富文本编辑器
- @vueup/vue-quill 1.2.0 - Quill编辑器Vue组件
- @vue-office/ - Office文档预览组件
工具库
- @vueuse/core 13.1.0 - Vue组合式API工具集
- axios 1.5.0 - HTTP客户端
- crypto-js 4.2.0 - 加密库
- echarts 5.6.0 - 数据可视化图表库
项目架构
目录结构
src/
├── api/ # API接口层
├── assets/ # 静态资源
│ ├── images/ # 图片资源
│ └── styles/ # 样式文件
│ ├── index.scss # 主样式文件(设计令牌)
│ └── common.scss # 公共页面样式
├── components/ # 公共组件
├── composables/ # 组合式函数
├── layout/ # 布局组件
├── router/ # 路由配置
├── stores/ # 状态管理
├── utils/ # 工具函数
└── views/ # 页面组件
├── account/ # 账户管理
├── crm/ # CRM管理
├── knowledge/ # 知识库管理
├── oa-approval/ # OA审批
├── org/ # 组织管理
└── system/ # 系统管理
架构特点
- 分层架构: API层、组件层、页面层清晰分离
- 模块化设计: 按业务功能模块组织代码
- 组件化开发: 可复用的公共组件和业务组件
- 状态管理: 使用Pinia进行全局状态管理
- Composable模式: 使用composable函数实现逻辑复用
- 动态组件: 支持按需加载和动态组件管理
页面结构规范
1. 页面容器规范
必须使用 PageContainer 包裹
所有页面必须使用 PageContainer 组件作为根容器:
<template>
<PageContainer @refresh="handlePageRefresh">
<!-- 页面内容 -->
</PageContainer>
</template>
<script setup>
import PageContainer from '@/components/PageContainer.vue'
const handlePageRefresh = () => {
// 刷新页面数据逻辑
}
</script>
2. 标准页面结构
普通列表页面结构
<template>
<PageContainer @refresh="handlePageRefresh">
<!-- 页面头部 -->
<div class="page-header">
<div class="header-content">
<div class="header-left">
<h2 class="page-title">
<el-icon class="title-icon"><Document /></el-icon>
页面标题
</h2>
<p class="page-description">页面描述信息</p>
</div>
<div class="header-actions">
<el-button type="primary" @click="handleAdd" class="add-btn">
<el-icon><Plus /></el-icon>
新增
</el-button>
</div>
</div>
</div>
<!-- 主要内容区域 -->
<el-card class="main-card" shadow="never">
<!-- 搜索表单 -->
<div class="search-section">
<!-- 搜索条件 -->
</div>
<!-- 数据表格 -->
<div class="table-section">
<!-- vxe-table 组件 -->
</div>
</el-card>
</PageContainer>
</template>
一级菜单首页结构(模块Dashboard)
重要说明:一级菜单的首页(如 /email、/crm、/knowledge 等路由的根页面)必须采用特殊的模块首页布局,不能使用普通列表页面结构:
标准首页结构(带动画效果) - 推荐使用:
<template>
<PageContainer @refresh="handlePageRefresh">
<div class="module-dashboard-container">
<!-- 模块首页头部(带背景动画) -->
<div class="module-home-header">
<!-- 背景装饰 -->
<div class="header-decoration">
<!-- 流星动画效果 -->
<div class="meteor meteor-1"></div>
<div class="meteor meteor-2"></div>
<div class="meteor meteor-3"></div>
<!-- 装饰形状 -->
<div class="decoration-shape shape-1"></div>
<div class="decoration-shape shape-2"></div>
<div class="decoration-shape shape-3"></div>
</div>
<div class="header-main">
<div class="header-content">
<!-- 左侧:模块图标 -->
<div class="module-image">
<img src="@/assets/images/模块名/banner.png" alt="模块名称" />
</div>
<!-- 右侧:文字内容 -->
<div class="header-text">
<h1>模块名称</h1>
<p>模块功能的详细描述,说明模块的价值和特色功能,让用户了解模块的核心价值和使用场景。</p>
<div class="status-indicator">
<div class="status-dot"></div>
<span>系统正在为您服务</span>
</div>
</div>
</div>
</div>
</div>
<!-- 模块内容区域 -->
<div class="module-content">
<!-- 统计卡片区域 -->
<div class="stats-section">
<!-- 数据统计卡片 -->
</div>
<!-- 功能入口区域 -->
<div class="feature-section">
<!-- 功能模块卡片 -->
</div>
</div>
</div>
</PageContainer>
</template>
<style lang="scss" scoped>
@import '@/assets/styles/common.scss';
/* 模块特定样式 */
.module-content {
// 在这里添加模块特定的内容样式
}
</style>
简化首页结构(无动画效果):
<template>
<PageContainer @refresh="handlePageRefresh">
<!-- 模块首页头部(简化版) -->
<div class="page-header">
<div class="header-content">
<div class="header-left">
<h2 class="page-title">
<el-icon class="title-icon"><Management /></el-icon>
模块名称
</h2>
<p class="page-description">模块功能描述</p>
</div>
<div class="header-actions">
<!-- 模块级操作按钮 -->
</div>
</div>
</div>
<!-- 模块内容区域 -->
<div class="module-content">
<!-- 统计卡片区域 -->
<div class="stats-section">
<!-- 数据统计卡片 -->
</div>
<!-- 功能入口区域 -->
<div class="feature-section">
<!-- 功能模块卡片 -->
</div>
</div>
</PageContainer>
</template>
关键区别:
- 模块首页:使用
<div class="module-content">直接包裹内容区域 - 普通列表页:使用
<el-card class="main-card" shadow="never">包裹内容区域
样式选择:
module-home-header:华丽的动画效果,适合重要的模块首页(推荐)page-header:简洁的渐变样式,适合功能型页面或简化的首页
类名对照表:
| 用途 | 类名 | 说明 |
|---|---|---|
| 模块首页容器 | module-dashboard-container |
统一的模块首页容器 |
| 动画头部 | module-home-header |
带背景动画的头部样式 |
| 简化头部 | page-header |
简洁的渐变头部样式 |
| 模块图片 | module-image |
模块banner图片容器 |
| 文字内容 | header-text |
头部文字内容区域 |
| 状态指示器 | status-indicator |
系统状态显示 |
应用场景:
- 模块首页:如
/email、/crm、/knowledge等一级菜单的入口页面 - 普通列表页:如
/email/management、/crm/customer、/knowledge/repository等功能页面
识别规则:
- 路由层级:一级菜单的根路由页面(如
/email/index/Dashboard.vue) - 页面性质:展示模块概览、统计数据、功能入口的首页
- 文件位置:通常位于
views/模块名/index/Dashboard.vue或views/模块名/Dashboard.vue - 菜单结构:在左侧菜单中作为一级菜单的主页面
样式文件优化:
- ✅ 统一封装:所有首页样式都在
src/assets/styles/common.scss中定义 - ✅ 完整动画:包含流星漂移、浮动形状、呼吸状态指示器等动画效果
- ✅ 响应式设计:自动适配移动端和桌面端
- ✅ 模块特定样式:各模块只需要在自己的文件中添加特定样式
- ✅ 性能优化:避免重复代码,减少CSS体积
使用指南:
- 导入样式:在Vue文件中使用
@import '@/assets/styles/common.scss' - 统一结构:使用标准的模板结构和类名
- 内容区域:使用
.module-content包裹所有模块内容 - 首页布局:在
.module-content内使用.dashboard-layout进行布局 - 模块特定样式:在模块的style中添加特定的内容样式
- 图片资源:将模块banner图片放在
src/assets/images/模块名/banner.png
正确的首页布局结构:
<template>
<PageContainer @refresh="handlePageRefresh">
<div class="module-dashboard-container">
<!-- 模块首页头部(带动画效果) -->
<div class="module-home-header">
<!-- ... 头部内容 ... -->
</div>
<!-- 模块内容区域 -->
<div class="module-content">
<!-- 首页专用布局容器 -->
<div class="dashboard-layout">
<!-- 第一行:智能分析摘要 + 快捷操作 -->
<div class="dashboard-row-1">
<div class="summary-section"><!-- 智能分析摘要 --></div>
<div class="feature-section"><!-- 快捷操作 --></div>
</div>
<!-- 第二行:未读内容 + 活动记录 -->
<div class="dashboard-row-2">
<div class="unread-section"><!-- 未读内容 --></div>
<div class="activity-section"><!-- 活动记录 --></div>
</div>
</div>
</div>
</div>
</PageContainer>
</template>
层级说明:
module-dashboard-container:模块首页的根容器module-home-header:带动画效果的模块头部module-content:模块内容区域(所有模块页面的标准容器)dashboard-layout:首页专用的布局容器(只在Dashboard页面使用)dashboard-row-1/2:首页布局的行容器
动画效果:
meteor-drift:流星漂移动画,3个不同延迟的流星在XY平面内移动float:装饰形状的旋转和缩放动画(已优化:移除上下浮动)pulse:状态指示器的呼吸灯效果- 交互动画:使用阴影、亮度、边框等效果替代Y轴位移
- 所有动画都经过性能优化,使用硬件加速
设计原则:
- ❌ 禁止使用
translateY进行上下浮动动画 - ✅ 推荐使用 阴影变化、颜色过渡、缩放、旋转等效果
- ✅ 保持稳定 所有元素位置固定,避免页面跳动
- ✅ 流畅体验 使用平滑的视觉反馈替代位移动画
动画设计规范
推荐的悬停效果:
// ✅ 推荐:使用阴影和亮度变化
&:hover {
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
filter: brightness(1.05);
border-color: #primary-color;
}
// ✅ 推荐:使用缩放和旋转
&:hover {
transform: scale(1.05) rotate(2deg);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
// ❌ 禁止:使用Y轴位移
&:hover {
transform: translateY(-5px); // 不要使用这种效果
}
装饰性动画原则:
- 旋转动画:
transform: rotate(0deg to 360deg) - 缩放动画:
transform: scale(0.9 to 1.1) - 透明度变化:
opacity: 0.3 to 0.8 - 颜色渐变:
background,border-color,color过渡 - 禁止位移:避免
translateX,translateY造成的位置跳动
快捷操作组件封装
快捷操作区域已在 common.scss 中统一封装,支持自适应布局和多种样式:
<template>
<div class="quick-actions-section">
<!-- 头部标题区域 -->
<div class="quick-actions-header">
<h3 class="actions-title">
<el-icon class="title-icon"><Lightning /></el-icon>
快捷操作
</h3>
<span class="actions-extra">6 项服务</span>
</div>
<!-- 自适应网格 -->
<div class="quick-actions-grid">
<!-- 按顺序使用预设颜色 -->
<div class="quick-action-item color-0" @click="handleAction1">
<div class="action-icon">
<el-icon><Edit /></el-icon>
</div>
<div class="action-text">写邮件</div>
</div>
<div class="quick-action-item color-1" @click="handleAction2">
<div class="action-icon">
<el-icon><Folder /></el-icon>
</div>
<div class="action-text">文件管理</div>
</div>
<div class="quick-action-item color-2" @click="handleAction3">
<div class="action-icon">
<el-icon><Setting /></el-icon>
</div>
<div class="action-text">设置</div>
</div>
<div class="quick-action-item color-3" @click="handleAction4">
<div class="action-icon">
<el-icon><DataAnalysis /></el-icon>
</div>
<div class="action-text">数据分析</div>
</div>
<div class="quick-action-item color-4" @click="handleAction5">
<div class="action-icon">
<el-icon><User /></el-icon>
</div>
<div class="action-text">用户管理</div>
</div>
<div class="quick-action-item color-5" @click="handleAction6">
<div class="action-icon">
<el-icon><Bell /></el-icon>
</div>
<div class="action-text">消息通知</div>
</div>
</div>
</div>
</template>
自适应特性:
- 最小宽度控制:每个操作项最小宽度
120px - 自动换行:使用
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)) - 响应式布局:
- 桌面端:最多8列
- 平板端:自动适应
- 手机端:最少2列,最小宽度100px
预设颜色系统:
| 类名 | 颜色 | 图标渐变 | 悬停背景 |
|---|---|---|---|
color-0 |
蓝色 | #6366f1 → #4f46e5 |
紫蓝色浅色 |
color-1 |
绿色 | #10b981 → #059669 |
绿色浅色 |
color-2 |
橙色 | #f59e0b → #d97706 |
橙色浅色 |
color-3 |
红色 | #ef4444 → #dc2626 |
红色浅色 |
color-4 |
紫色 | #8b5cf6 → #7c3aed |
紫色浅色 |
color-5 |
青色 | #06b6d4 → #0891b2 |
青色浅色 |
color-6 |
粉色 | #ec4899 → #db2777 |
粉色浅色 |
color-7 |
草绿色 | #84cc16 → #65a30d |
草绿色浅色 |
color-8 |
灰色 | #6b7280 → #4b5563 |
灰色浅色 |
color-9 |
深橙色 | #f97316 → #ea580c |
深橙色浅色 |
使用规则:
- 顺序分配:按照操作项的顺序依次使用
color-0到color-9 - 循环使用:超过10个操作时,重新从
color-0开始循环 - 主要操作:仍可使用
primary-action类来突出显示最重要的操作 - 视觉一致:所有颜色都经过精心调配,确保视觉和谐
布局规则:
- 网格优先:优先显示更多列,到达最小宽度后自动换行
- 均匀分布:使用
1fr确保每项宽度相等 - 最小保证:极小屏幕下确保至少显示2列
- 最大限制:大屏幕下限制最大8列,避免过度拉伸
组件使用规范
1. 表格组件使用规范
使用场景
- 数据列表展示: 用户列表、文件列表、订单列表等
- 数据筛选排序: 需要分页、搜索、排序的数据表格
- 复杂数据操作: 批量操作、行内编辑、树形数据等
技术选型
必须使用 vxe-table 作为表格组件
<vxe-table
:data="tableData"
:loading="loading"
:pager-config="pagerConfig"
@page-change="handlePageChange"
>
<vxe-column field="name" title="名称" />
<vxe-column field="status" title="状态" />
<vxe-column title="操作" width="200">
<template #default="{ row }">
<el-button size="small" @click="handleEdit(row)">
<el-icon><Edit /></el-icon>编辑
</el-button>
</template>
</vxe-column>
</vxe-table>
表格配置规范
// 标准分页配置
const pagerConfig = {
currentPage: 1,
pageSize: 20,
total: 0,
layouts: ['PrevPage', 'JumpNumber', 'NextPage', 'FullJump', 'Sizes', 'Total']
}
2. 按钮组件使用规范
按钮类型和使用场景
<!-- 主要操作按钮 -->
<el-button type="primary" @click="handleSubmit">
<el-icon><Check /></el-icon>
确认
</el-button>
<!-- 次要操作按钮 -->
<el-button @click="handleCancel">
<el-icon><Close /></el-icon>
取消
</el-button>
<!-- 危险操作按钮 -->
<el-button type="danger" @click="handleDelete">
<el-icon><Delete /></el-icon>
删除
</el-button>
<!-- 图标按钮 -->
<el-button circle size="small" @click="handleRefresh">
<el-icon><Refresh /></el-icon>
</el-button>
<!-- 页面头部特殊按钮(使用add-btn类) -->
<el-button type="primary" @click="handleAdd" class="add-btn">
<el-icon><Plus /></el-icon>
新增
</el-button>
3. 表单组件使用规范
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="120px"
>
<el-form-item label="名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入名称" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="formData.status" placeholder="请选择状态">
<el-option label="启用" value="enabled" />
<el-option label="禁用" value="disabled" />
</el-select>
</el-form-item>
</el-form>
4. 卡片组件使用规范
<!-- 主要内容卡片 -->
<el-card class="main-card" shadow="never">
<!-- 卡片内容 -->
</el-card>
<!-- 功能模块卡片 -->
<el-card class="feature-card" shadow="hover">
<!-- 功能入口内容 -->
</el-card>
5. 统计卡片组件规范
统计卡片标准结构
项目已在 src/assets/styles/common.scss 中封装了统一的统计卡片样式,支持多种布局和主题:
<template>
<!-- 统计卡片区域 -->
<div class="stats-section">
<div class="stats-grid grid-4"> <!-- 4列布局 -->
<div class="stat-card theme-blue" @click="handleClick">
<div class="stat-icon">
<el-icon><Message /></el-icon>
</div>
<div class="stat-content">
<div class="stat-number">
<CountUp :target="statisticsData.count" />
</div>
<div class="stat-label">统计标签</div>
</div>
</div>
<!-- 更多统计卡片... -->
</div>
</div>
</template>
<script setup>
import CountUp from '@/components/CountUp.vue'
</script>
布局类型
| 类名 | 布局 | 响应式断点 | 使用场景 |
|---|---|---|---|
grid-4 |
4列布局 | 1200px→2列, 768px→1列 | 完整的统计面板 |
grid-3 |
3列布局 | 900px→2列, 768px→1列 | 知识库风格布局 |
grid-2 |
2列布局 | 768px→1列 | 简化的统计展示 |
主题颜色
| 主题类名 | 颜色系 | 渐变色 | 适用场景 |
|---|---|---|---|
theme-blue |
蓝色 | #3b82f6 → #1d4ed8 |
主要数据、消息类 |
theme-red |
红色 | #ef4444 → #dc2626 |
紧急、警告类 |
theme-orange |
橙色 | #f59e0b → #d97706 |
待处理、提醒类 |
theme-green |
绿色 | #10b981 → #059669 |
完成、成功类 |
theme-purple |
紫色 | #a855f7 → #7c3aed |
特殊、高级功能 |
theme-cyan |
青色 | #06b6d4 → #0891b2 |
活动、记录类 |
theme-pink |
粉色 | #ec4899 → #db2777 |
收藏、喜欢类 |
theme-lime |
草绿 | #84cc16 → #65a30d |
新增、增长类 |
CountUp数字动画组件
统计卡片配合使用CountUp组件实现数字滚动动画效果:
<script setup>
import CountUp from '@/components/CountUp.vue'
// CountUp组件参数
const countUpProps = {
target: 150, // 目标数字
duration: 1000, // 动画持续时间(毫秒)
decimals: 0, // 小数位数
delay: 0, // 动画延迟(毫秒)
preset: 'easeOutCubic' // 动画预设
}
</script>
<template>
<div class="stat-number">
<!-- 基础用法 -->
<CountUp :target="statistics.count" />
<!-- 带小数位 -->
<CountUp :target="storage.used" :decimals="1" />
<span class="stat-unit">GB</span>
<!-- 自定义动画 -->
<CountUp
:target="performance.score"
:duration="1500"
:delay="200"
preset="easeOutBounce"
/>
</div>
</template>
完整示例
<template>
<!-- 邮件模块统计示例 -->
<div class="stats-section">
<div class="stats-grid grid-3">
<div class="stat-card theme-blue">
<div class="stat-icon">
<el-icon><Message /></el-icon>
</div>
<div class="stat-content">
<div class="stat-number">
<CountUp :target="emailStats.unreadCount" />
</div>
<div class="stat-label">未读邮件</div>
</div>
</div>
<div class="stat-card theme-green">
<div class="stat-icon">
<el-icon><Calendar /></el-icon>
</div>
<div class="stat-content">
<div class="stat-number">
<CountUp :target="emailStats.todayCount" />
</div>
<div class="stat-label">今日最新邮件</div>
</div>
</div>
<div class="stat-card theme-orange">
<div class="stat-icon">
<el-icon><User /></el-icon>
</div>
<div class="stat-content">
<div class="stat-number">
<CountUp :target="emailStats.accountCount" />
</div>
<div class="stat-label">邮件账户数量</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { Message, Calendar, User } from '@element-plus/icons-vue'
import CountUp from '@/components/CountUp.vue'
const emailStats = ref({
unreadCount: 0,
todayCount: 0,
accountCount: 0
})
const loadStatistics = async () => {
// 加载统计数据的逻辑
}
onMounted(() => {
loadStatistics()
})
</script>
设计原则
- 视觉一致性:所有统计卡片使用相同的尺寸和样式
- 颜色语义化:根据数据类型选择合适的主题颜色
- 纯展示功能:统计卡片仅用于数据展示,不支持点击交互
- 动画增强:使用CountUp组件提升用户体验
- 响应式设计:自动适配不同屏幕尺寸
- 悬浮反馈:支持轻微的阴影变化,但不包含Y轴移动效果
业务组件规范
1. 组件文件组织结构
业务模块组件结构
views/
├── module-name/ # 业务模块
│ ├── stores/ # 模块状态管理
│ │ └── useModuleStore.js
│ ├── components/ # 模块内组件
│ │ ├── ComponentA.vue
│ │ └── ComponentB.vue
│ ├── composables/ # 业务逻辑函数
│ │ ├── useModuleData.js
│ │ └── useModuleOperations.js
│ ├── utils/ # 模块工具函数
│ │ └── moduleUtils.js
│ ├── api/ # 模块API
│ │ └── moduleApi.js
│ ├── Dashboard.vue # 模块首页
│ └── index.vue # 模块入口
2. Composable 设计模式
推荐使用 Composable 分离业务逻辑
// composables/useTableOperations.js
export function useTableOperations() {
const loading = ref(false)
const tableData = ref([])
const loadData = async () => {
loading.value = true
try {
// 数据加载逻辑
} finally {
loading.value = false
}
}
return {
loading,
tableData,
loadData
}
}
图标使用规范
必须使用 Element Plus 图标库
基础操作图标
import {
// 基础操作
Plus, // 新增
Edit, // 编辑
Delete, // 删除
Search, // 搜索
Refresh, // 刷新
View, // 查看
Setting, // 设置
// 文件操作
Download, // 下载
Upload, // 上传
Document, // 文档
Folder, // 文件夹
FolderOpened, // 打开的文件夹
// 导航和布局
Home, // 首页
ArrowLeft, // 左箭头
ArrowRight, // 右箭头
ArrowUp, // 上箭头
ArrowDown, // 下箭头
// 状态指示
Check, // 成功/确认
Close, // 关闭/失败
Warning, // 警告
Info, // 信息
Success, // 成功
Error, // 错误
// 用户和权限
User, // 用户
UserFilled, // 用户(填充)
Lock, // 锁定
Unlock, // 解锁
// 通信和分享
Message, // 消息
Phone, // 电话
Email, // 邮件
Share, // 分享
Link, // 链接
// 媒体类型
Picture, // 图片
Video, // 视频
Audio, // 音频
// 业务功能
Management, // 管理
DataBoard, // 数据看板
Grid, // 网格
List, // 列表
Calendar, // 日历
Clock, // 时钟
Location, // 位置
// 交互反馈
Star, // 星标
StarFilled, // 星标(填充)
Heart, // 喜欢
HeartFilled, // 喜欢(填充)
// 圆形图标变体
CircleCheck, // 圆形确认
CircleClose, // 圆形关闭
CirclePlus, // 圆形加号
// 更多操作
More, // 更多
MoreFilled, // 更多(填充)
// 展开收起
Expand, // 展开
Fold, // 收起
// 其他常用
Filter, // 筛选
Sort, // 排序
Export, // 导出
Import, // 导入
Copy, // 复制
Files, // 文件集合
Histogram, // 柱状图
PieChart, // 饼图
TrendCharts // 趋势图
} from '@element-plus/icons-vue'
图标使用原则
- 统一性:同一功能在不同页面必须使用相同图标
- 语义性:图标含义要与功能匹配
- 一致性:图标大小和颜色保持项目统一风格
常用图标映射
| 功能 | 图标 | 使用场景 |
|---|---|---|
| 新增 | Plus | 新建数据、添加项目 |
| 编辑 | Edit | 修改数据、编辑内容 |
| 删除 | Delete | 删除数据、移除项目 |
| 搜索 | Search | 搜索框、查询功能 |
| 刷新 | Refresh | 重新加载数据 |
| 下载 | Download | 文件下载、数据导出 |
| 上传 | Upload | 文件上传、导入数据 |
| 查看 | View | 查看详情、预览 |
| 设置 | Setting | 配置、参数设置 |
| 首页 | Home | 返回首页、主页导航 |
样式规范
1. 样式架构
项目采用分层样式架构:
src/assets/styles/index.scss- 设计令牌和全局变量src/assets/styles/common.scss- 公共页面样式
2. 间距规范
统一Padding标准
为确保界面的一致性和视觉平衡,项目采用统一的padding规范:
默认padding值:16px
// 标准间距规范
.default-padding {
padding: 16px; // 默认内边距16px
}
// 卡片组件标准间距
.dashboard-card {
:deep(.el-card__body) {
padding: 16px !important; // 卡片内容区域16px
}
.card-header {
padding: 16px 16px 12px; // 卡片头部:水平16px,底部12px
}
}
// 统计卡片间距
.stat-card {
padding: 16px; // 统计卡片16px
}
特殊情况说明
以下情况可根据设计需求调整:
| 场景 | Padding值 | 说明 |
|---|---|---|
| 密集信息展示 | 12px |
需要显示更多内容时 |
| 强调内容区域 | 20px |
需要突出显示的重要内容 |
| 移动端适配 | 12px |
小屏幕下的紧凑布局 |
| 表单输入区域 | 16px |
保持与默认值一致 |
| 列表项内容 | 12px - 16px |
根据列表密度调整 |
间距层级系统
// 间距变量定义
:root {
--padding-xs: 8px; // 极小间距
--padding-sm: 12px; // 小间距
--padding-md: 16px; // 默认间距(推荐)
--padding-lg: 20px; // 大间距
--padding-xl: 24px; // 超大间距
}
// 使用示例
.content-area {
padding: var(--padding-md); // 使用默认16px
}
.compact-list {
padding: var(--padding-sm); // 使用紧凑12px
}
3. 全局样式规范
使用 common.scss 统一样式
项目已在 src/assets/styles/common.scss 中定义了页面头部样式,所有页面应使用统一的样式类:
.page-header- 页面头部容器.header-content- 头部内容区域.header-left- 头部左侧区域.page-title- 页面标题.page-description- 页面描述.header-actions- 头部操作区域.add-btn- 特殊的新增按钮样式
CSS 变量和设计令牌
项目已在 src/assets/styles/index.scss 中定义了完整的设计令牌系统:
- 颜色系统:
--primary-color,--success-color等 - 间距系统:
--spacing-small,--spacing-medium等 - 高度系统:
--full-content-height,--card-content-height等 - 阴影系统:
--shadow-light,--shadow-medium等 - Z-Index层级:
--z-index-header,--z-index-modal等
3. 边框颜色规范
统一边框颜色使用
为确保界面的一致性和品牌统一性,项目采用统一的边框颜色规范:
标准边框颜色:var(--customer-board-color)
// 标准边框样式
.standard-border {
border: 1px solid var(--customer-board-color);
}
// 卡片边框
.card-container {
border: 1px solid var(--customer-board-color);
border-radius: 8px;
}
// 分割线边框
.divider-line {
border-bottom: 1px solid var(--customer-board-color);
}
// 输入框边框
.input-field {
border: 1px solid var(--customer-board-color);
border-radius: 4px;
}
边框使用场景
| 场景 | 边框样式 | 说明 |
|---|---|---|
| 容器边框 | 1px solid var(--customer-board-color) |
卡片、面板、弹窗等容器 |
| 分割线 | 1px solid var(--customer-board-color) |
列表项、内容区域之间的分割 |
| 输入框 | 1px solid var(--customer-board-color) |
表单输入框、选择器等 |
| 表格边框 | 1px solid var(--customer-board-color) |
表格单元格、表头等 |
| 悬浮状态 | 1px solid var(--customer-board-color) |
按钮、链接等悬浮时的边框 |
禁止使用的边框颜色
- ❌ 硬编码的颜色值:
#e4e7ed,#f1f5f9,#e5e7eb等 - ❌ 直接使用 Element Plus 默认边框色
- ❌ 使用不统一的颜色变量
正确示例
// ✅ 正确:使用统一的边框颜色变量
.email-list {
border-bottom: 1px solid var(--customer-board-color);
}
// ✅ 正确:使用统一的边框颜色变量
.sidebar-section {
border-bottom: 1px solid var(--customer-board-color);
}
// ✅ 正确:使用统一的边框颜色变量
.dialog-content {
border: 1px solid var(--customer-board-color);
}
错误示例
// ❌ 错误:使用硬编码颜色值
.email-list {
border-bottom: 1px solid #e4e7ed;
}
// ❌ 错误:使用不统一的颜色变量
.sidebar-section {
border-bottom: 1px solid #f1f5f9;
}
4. 组件样式规范
Element Plus 组件样式增强
项目已对 Element Plus 组件进行了样式增强:
- 卡片组件: 统一圆角和阴影
- 抽屉组件: 标准化头部和底部样式
- 对话框组件: 统一边框和间距
- 滚动条: 自定义滚动条样式
页面布局类
.main-card- 主要内容卡片.search-section- 搜索区域.table-section- 表格区域.stats-section- 统计区域.feature-section- 功能区域
文件预览规范
使用 FileViewer 组件
统一使用 FileViewer 组件处理文件预览
<template>
<!-- 文件预览 -->
<FileViewer :file="currentFile" @download="handleDownload" />
</template>
<script setup>
import FileViewer from '@/views/knowledge/repository/browser/FileViewer.vue'
const currentFile = ref(null)
const handleDownload = (file) => {
// 下载逻辑
}
</script>
支持的文件类型
- Office文档: DOC, DOCX, XLS, XLSX, PPT, PPTX
- PDF文档: PDF
- 图片文件: JPG, PNG, GIF, BMP, SVG
- 文本文件: TXT, MD, JSON, XML
- 视频文件: MP4, AVI, MOV
- 音频文件: MP3, WAV, OGG
- 在线文档: 富文本内容
FileViewer 组件特性
- 自动识别文件类型
- 支持在线预览
- 统一的预览界面
- 不支持预览的文件显示下载提示
路由规范
1. 路由配置规范
路由命名规范
// 路由命名遵循以下规则:
// 1. 使用 kebab-case 格式
// 2. 层级结构清晰
// 3. 名称具有语义性
const routes = [
{
path: '/crm',
name: 'Crm',
component: Layout,
meta: {
title: 'CRM',
icon: 'UserFilled',
permission: 'crm'
},
children: [
{
path: 'customer',
name: 'CrmCustomer',
component: () => import('@/views/crm/CustomerInfo.vue'),
meta: {
title: '客户信息',
icon: 'User',
permission: 'crm:customer',
cache: true
}
}
]
}
]
路由 Meta 信息规范
// Meta 字段标准定义
meta: {
title: '页面标题', // 必填:页面标题
icon: 'ElementIcon', // 可选:菜单图标
permission: 'module:action', // 可选:权限标识
cache: true, // 可选:是否缓存页面
hidden: false, // 可选:是否隐藏菜单
2. 路由守卫规范
权限检查流程
// 统一的路由守卫逻辑
router.beforeEach(async (to, from, next) => {
const userStore = useUserStore()
const permissionStore = usePermissionStore()
// 1. 公开页面直接放行
if (PUBLIC_PAGES.has(to.path)) {
next()
return
}
// 2. 登录状态检查
if (!userStore.isLoggedIn) {
userStore.redirectToLoginWithUrl(to.fullPath, router)
return
}
// 3. 权限检查
if (to.meta.permission && !permissionStore.hasPermission(to.meta.permission)) {
next('/403')
return
}
next()
})
3. 路由跳转规范
编程式导航
// 推荐:使用命名路由
router.push({ name: 'CrmCustomer', params: { id: '123' } })
// 推荐:带查询参数
router.push({
name: 'CrmCustomer',
query: { tab: 'projects', status: 'active' }
})
// 避免:硬编码路径
// router.push('/crm/customer/123')
路由参数处理
// 在组件中获取路由参数
const route = useRoute()
const router = useRouter()
// 获取路径参数
const customerId = route.params.id
// 获取查询参数
const activeTab = route.query.tab || 'basic'
// 监听路由变化
watch(() => route.params.id, (newId) => {
if (newId) {
loadCustomerData(newId)
}
})
状态管理规范
1. Store 组织结构
Store 命名和文件组织
# 全局状态管理
stores/
├── user.js # 用户状态
├── permission.js # 权限状态
├── app.js # 应用全局状态
└── utils/ # 状态工具
└── storeHelpers.js
# 业务模块状态管理(放在各自模块内)
views/
├── crm/
│ ├── stores/
│ │ └── useCrmStore.js # CRM模块状态
│ ├── components/
│ └── ...
├── knowledge/
│ ├── stores/
│ │ └── useKnowledgeStore.js # 知识库模块状态
│ ├── components/
│ └── ...
└── email/
├── stores/
│ └── useEmailStore.js # 邮件模块状态
├── components/
└── ...
2. Store 设计规范
标准 Store 结构
// views/crm/stores/useCrmStore.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import * as crmApi from '../api/crmApi'
export const useCrmStore = defineStore('crm', () => {
// ========== 状态定义 ==========
const customers = ref([])
const projects = ref([])
const loading = ref(false)
const error = ref(null)
// ========== 计算属性 ==========
const activeCustomers = computed(() =>
customers.value.filter(customer => customer.status === 'active')
)
const totalProjects = computed(() => projects.value.length)
// ========== Actions ==========
const loadCustomers = async (params = {}) => {
loading.value = true
error.value = null
try {
const response = await crmApi.getCustomerPage(params)
customers.value = response.records || []
return response
} catch (err) {
error.value = err.message
throw err
} finally {
loading.value = false
}
}
const addCustomer = async (customerData) => {
try {
const newCustomer = await crmApi.createCustomer(customerData)
customers.value.unshift(newCustomer)
return newCustomer
} catch (err) {
error.value = err.message
throw err
}
}
const updateCustomer = async (id, customerData) => {
try {
const updatedCustomer = await crmApi.updateCustomer(id, customerData)
const index = customers.value.findIndex(c => c.id === id)
if (index !== -1) {
customers.value[index] = updatedCustomer
}
return updatedCustomer
} catch (err) {
error.value = err.message
throw err
}
}
const removeCustomer = async (id) => {
try {
await crmApi.deleteCustomer(id)
customers.value = customers.value.filter(c => c.id !== id)
} catch (err) {
error.value = err.message
throw err
}
}
// ========== 工具方法 ==========
const resetState = () => {
customers.value = []
projects.value = []
loading.value = false
error.value = null
}
const findCustomerById = (id) => {
return customers.value.find(customer => customer.id === id)
}
return {
// 状态
customers,
projects,
loading,
error,
// 计算属性
activeCustomers,
totalProjects,
// 方法
loadCustomers,
addCustomer,
updateCustomer,
removeCustomer,
resetState,
findCustomerById
}
})
3. Store 使用规范
在组件中使用 Store
// 组件中的标准用法
<script setup>
import { useCrmStore } from './stores/useCrmStore'
import { storeToRefs } from 'pinia'
const crmStore = useCrmStore()
// 响应式引用状态(使用 storeToRefs)
const { customers, loading, error } = storeToRefs(crmStore)
// 直接使用方法
const { loadCustomers, addCustomer } = crmStore
// 组件挂载时加载数据
onMounted(() => {
loadCustomers()
})
// 处理错误
watch(error, (newError) => {
if (newError) {
ElMessage.error(newError)
}
})
</script>
跨 Store 通信
// 在业务模块 Store 中使用全局 Store
export const useCrmStore = defineStore('crm', () => {
const userStore = useUserStore() // 全局用户状态
const permissionStore = usePermissionStore() // 全局权限状态
const loadCustomers = async () => {
// 检查权限
if (!permissionStore.hasPermission('crm:customer:read')) {
throw new Error('没有访问权限')
}
// 使用用户信息
const params = {
userId: userStore.userId,
tenantId: userStore.currentTenant?.id
}
// API调用...
}
})
4. 状态持久化
使用 localStorage 持久化
import { defineStore } from 'pinia'
export const useAppStore = defineStore('app', () => {
// 从 localStorage 恢复状态
const sidebarCollapsed = ref(
localStorage.getItem('sidebarCollapsed') === 'true'
)
const toggleSidebar = () => {
sidebarCollapsed.value = !sidebarCollapsed.value
// 持久化到 localStorage
localStorage.setItem('sidebarCollapsed', sidebarCollapsed.value.toString())
}
return {
sidebarCollapsed,
toggleSidebar
}
})
接口处理规范
1. 接口调用规范
基于 http.js 的统一调用
项目已使用 src/utils/http.js 进行 Ajax 响应封装,所有接口调用必须使用统一的 request 方法:
// 正确:使用统一的 http 实例
import request from '@/utils/http'
export function getCustomerList(params) {
return request({
url: '/smart-ops/crm/customer/page',
method: 'post',
data: params
})
}
// 错误:直接使用 axios
// import axios from 'axios'
// axios.post('/api/customer', data)
2. 接口文件组织规范
API 文件结构
# 全局API接口
api/
├── index.js # API 统一导出
├── user.js # 用户相关接口
├── permission.js # 权限相关接口
└── wiki/ # Wiki相关接口
├── comment.js
├── favorite.js
└── version.js
# 业务模块API接口(放在各自模块内)
views/
├── crm/
│ └── api/
│ └── crmApi.js # CRM模块接口
├── email/
│ └── api/
│ └── emailApi.js # 邮件模块接口
└── knowledge/
└── api/
└── knowledgeApi.js # 知识库模块接口
接口命名规范
// 命名规范:动词 + 资源名 + 操作类型
// CRUD 操作标准命名
export function getCustomerList(params) // 获取列表
export function getCustomerPage(params) // 分页查询
export function getCustomerDetail(id) // 获取详情
export function createCustomer(data) // 创建
export function updateCustomer(id, data) // 更新
export function deleteCustomer(id) // 删除
// 特殊操作命名
export function exportCustomers(params) // 导出
export function importCustomers(file) // 导入
export function batchDeleteCustomers(ids) // 批量删除
export function enableCustomer(id) // 启用
export function disableCustomer(id) // 禁用
3. 接口文档规范
标准接口注释
/**
* 获取客户分页列表
* @param {Object} params - 查询参数
* @param {number} params.pageNo - 页码(从1开始)
* @param {number} params.pageSize - 每页大小
* @param {string} [params.keyword] - 搜索关键词
* @param {string} [params.status] - 客户状态:active|inactive
* @param {string} [params.createTimeStart] - 创建时间开始
* @param {string} [params.createTimeEnd] - 创建时间结束
* @returns {Promise<Object>} 返回分页数据
* @returns {Object[]} returns.records - 客户列表
* @returns {number} returns.total - 总数量
* @returns {number} returns.pageNo - 当前页码
* @returns {number} returns.pageSize - 每页大小
*
* @example
* const result = await getCustomerPage({
* pageNo: 1,
* pageSize: 20,
* keyword: '客户名称',
* status: 'active'
* })
*/
export function getCustomerPage(params) {
return request({
url: '/smart-ops/crm/customer/page',
method: 'post',
data: params
})
}
4. Mock 数据规范
Mock 开关管理
// views/email/api/emailApi.js - Mock模式示例
import http from '@/utils/http'
import { EMAIL_LIST, createMockResponse } from '@/mock/emailData'
// Mock模式开关
const USE_MOCK = process.env.NODE_ENV === 'development'
export function getEmailList(params) {
if (USE_MOCK) {
// Mock 数据处理逻辑
let filteredEmails = [...EMAIL_LIST]
// 搜索筛选
if (params.search) {
const query = params.search.toLowerCase()
filteredEmails = filteredEmails.filter(email =>
email.subject?.toLowerCase().includes(query)
)
}
return Promise.resolve(createMockResponse(filteredEmails))
}
// 真实接口调用
return http({
url: '/smart-ops/email/list',
method: 'post',
data: params
})
}
5. 错误处理规范
统一错误处理模式
// 在 composable 中处理接口错误
export function useCustomerOperations() {
const loading = ref(false)
const error = ref(null)
const loadCustomers = async (params) => {
loading.value = true
error.value = null
try {
const result = await getCustomerPage(params)
return result
} catch (err) {
error.value = err.message || '获取客户列表失败'
ElMessage.error(error.value)
throw err
} finally {
loading.value = false
}
}
return {
loading,
error,
loadCustomers
}
}
标准错误处理
// 统一的错误处理模式
export function createCustomer(data) {
return request({
url: '/smart-ops/crm/customer/create',
method: 'post',
data
})
}
// 在 composable 中处理错误
export function useCustomerOperations() {
const createCustomer = async (data) => {
try {
const result = await createCustomerApi(data)
ElMessage.success('创建客户成功')
return result
} catch (error) {
ElMessage.error(error.message || '创建客户失败')
throw error
}
}
return { createCustomer }
}
6. 文件上传/下载规范
文件上传规范
必须使用已封装的阿里云OSS上传组件
// 使用项目封装的OSS上传工具
import { uesOssUpload } from '@/utils/uesOssUpload'
// 在组件中使用OSS上传
export function useFileUpload() {
const uploadRequest = uesOssUpload()
const handleUpload = async (file, options = {}) => {
try {
const result = await uploadRequest.upload(file, {
onProgress: (progress) => {
console.log(`上传进度: ${progress}%`)
},
...options
})
return {
fileId: result.fileId,
fileName: result.fileName,
originalName: file.name,
fileSize: file.size,
fileType: file.type,
filePath: result.filePath
}
} catch (error) {
ElMessage.error('文件上传失败')
throw error
}
}
return { handleUpload }
}
文件下载规范
使用 FileUtils 工具类统一处理文件下载
// utils/fileUtils.js - 添加标准下载方法
import SystemConfig from '@/config/config'
import { ElMessage } from 'element-plus'
class FileUtils {
/**
* 下载文件
* @param {string} fileId - 文件ID
* @param {string} fileName - 文件名
* @returns {Promise<void>}
*/
static async downloadFile(fileId, fileName) {
if (!fileId) {
ElMessage.error('文件ID不能为空')
return
}
if (!fileName) {
ElMessage.error('文件名不能为空')
return
}
const downloadUrl = SystemConfig.FILE_DOWNLOAD_BASE_URL + `/${fileId}?fileName=${encodeURIComponent(fileName)}`
try {
const response = await fetch(downloadUrl)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
const blob = await response.blob()
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = fileName
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
ElMessage.success('开始下载文件')
} catch (error) {
console.error('下载文件失败:', error)
ElMessage.error('下载文件失败')
throw error
}
}
/**
* 下载文件(兼容对象参数)
* @param {Object} file - 文件信息对象
* @returns {Promise<void>}
*/
static async downloadFileByObject(file) {
if (!file) {
ElMessage.error('文件信息为空')
return
}
const fileId = file.fileId || file.id
const fileName = file.originalName || file.fileName || file.name || '文件'
return this.downloadFile(fileId, fileName)
}
}
export default FileUtils
在组件中使用
// 在业务组件中使用
import FileUtils from '@/utils/fileUtils'
// 方式1:直接传入fileId和fileName
const handleDownload = async () => {
try {
await FileUtils.downloadFile(fileId, fileName)
} catch (error) {
console.error('下载失败:', error)
}
}
// 方式2:传入文件对象(兼容现有代码)
const handleDownloadByObject = async (file) => {
try {
await FileUtils.downloadFileByObject(file)
} catch (error) {
console.error('下载失败:', error)
}
}
最佳实践
1. 组件设计原则
单一职责原则
- 每个组件只负责一个功能领域
- 使用 composable 函数分离业务逻辑
- 避免组件代码超过 500 行
可复用性原则
- 提取公共逻辑到 composable 函数
- 业务组件放在模块的 components 文件夹
- 通用组件放在 src/components 文件夹
Composable 模式
// 推荐:使用 composable 管理复杂逻辑
export function useFileOperations() {
const loading = ref(false)
const uploadFile = async (file) => {
loading.value = true
try {
// 上传逻辑
} finally {
loading.value = false
}
}
return {
loading,
uploadFile
}
}
2. 代码质量规范
组件文档注释
/**
* 文件预览组件
* @description 支持多种文件类型的在线预览
* @author 团队名称
* @date 2024-01-01
*/
// Props 定义
const props = defineProps({
file: {
type: Object,
required: true,
default: () => ({})
}
})
统一错误处理
// 在 composable 中处理错误
export function useApiCall() {
const loading = ref(false)
const error = ref(null)
const execute = async (apiFunction, errorMessage = '操作失败') => {
loading.value = true
error.value = null
try {
const result = await apiFunction()
return result
} catch (err) {
error.value = err
ElMessage.error(errorMessage)
throw err
} finally {
loading.value = false
}
}
return { loading, error, execute }
}
3. 性能优化建议
组件懒加载
// 推荐:使用动态组件加载器
import { useDynamicLoader } from '@/utils/dynamicComponentLoader'
const { getAsyncComponent } = useDynamicLoader()
const LazyDialog = getAsyncComponent('CreateDialog')
大数据优化
<!-- vxe-table 虚拟滚动 -->
<vxe-table
:data="tableData"
:scroll-y="{ enabled: true, gt: 100 }"
:height="var(--card-content-height)"
>
<!-- 表格配置 -->
</vxe-table>
总结
本设计规范确保了项目的统一性和可维护性,所有开发成员应严格遵循。
核心要求
- 页面结构统一: 使用 PageContainer + 标准页面结构
- 组件库优先: 基于 Element Plus,避免重度定制
- 表格统一: 必须使用 vxe-table
- 文件预览统一: 使用 FileViewer 组件
- 图标规范: 只使用 Element Plus 图标库
- 样式统一: 使用 common.scss 中的全局样式
- 路由规范: 统一路由配置和守卫机制
- 状态管理: 业务模块内管理,全局状态分离
- 接口调用: 基于 http.js 统一处理
- 业务组件: 按模块组织,自包含结构
- 代码质量: 单一职责、可复用、高性能
开发流程
- 新建页面: 复制标准页面结构模板
- 配置路由: 按照路由规范配置路由和权限
- 创建模块: 在业务模块内创建 stores 和 api 目录
- 开发 Store: 使用标准 Store 结构管理模块状态
- 开发 API: 基于 http.js 创建模块接口调用
- 添加功能: 优先使用现有组件和 composable
- 样式开发: 使用全局样式类,避免重复定义
- 图标选择: 从规范的图标列表中选择
- 测试验证: 确保功能正常和性能良好
- 代码审查: 确保符合所有规范要求
版本: v2.0
更新时间: 2024年1月
维护团队: 前端架构团队