📋 主要变更: - ✅ 补充完整的7个业务模块表命名规范 - ✅ 新增后勤管理模块:hq_ 前缀 - ✅ 明确系统公共模块:common_ 前缀 - ✅ 提供完整的命名示例和实体类映射规则 - ✅ 更新变更记录文档 🎯 覆盖模块: - 后勤管理(hq_)、人事管理(rs_)、财务管理(cw_) - 学工管理(xg_)、招生就业(zsjy_)、安全保卫(aqbw_) - 系统公共(common_) 📚 文件变更: - .cursor/cursorrules-backend.md - 数据库表命名规范 - docs/变更管理/code-adjust-record-2025-01-23-wuzilong.md - 变更记录 - 各业务模块工程文件和前端结构文件 Co-authored-by: Cursor AI <cursor@cursor.sh>
1015 lines
29 KiB
Markdown
1015 lines
29 KiB
Markdown
# Cursor Rules - Web 前端开发规范
|
||
|
||
## 适用范围
|
||
|
||
- **项目目录**: `web/` (基于 Vue 3 + TypeScript + Element Plus)
|
||
- **开发环境**: Node.js 20.15.x+, npm 10.7.x+
|
||
- **前端访问**: http://localhost (默认 80 端口)
|
||
|
||
---
|
||
|
||
# Web 前端开发规范 (web/)
|
||
|
||
## 1. 技术栈规范
|
||
|
||
### 核心技术
|
||
|
||
- **框架**: Vue 3.5.13 + TypeScript 5.8.3
|
||
- **UI 组件库**: Element Plus 2.9.8
|
||
- **构建工具**: Vite 6.3.2
|
||
- **状态管理**: Pinia 3.0.2
|
||
- **路由管理**: Vue Router 4.5.0
|
||
- **HTTP 客户端**: Axios 1.8.4
|
||
- **代码风格**: ESLint 9.21.0 + Prettier 3.5.2
|
||
- **样式预处理**: SCSS (Sass 1.87.0)
|
||
- **原子化 CSS**: UnoCSS 66.0.0
|
||
- **Node.js**: 20.15.x+ (推荐 20.15.0)
|
||
- **npm**: 10.7.x+
|
||
|
||
### 环境要求
|
||
|
||
- **开发环境**: 支持本地环境或阿里云测试环境
|
||
- **数据库**: MySQL/Redis 支持本机安装或使用阿里云环境
|
||
- **前端访问**: http://localhost (默认 80 端口)
|
||
- **后端服务**: http://localhost:8080
|
||
|
||
## 🔴 前端模块管理规范(强制性,2025-01-23新增)
|
||
|
||
### 📦 前端模块文件夹规范
|
||
|
||
**⚠️ 基于后端模块工程统一规划,前端必须建立对应的模块文件夹结构!**
|
||
|
||
#### 🏗️ 模块命名和位置规范
|
||
|
||
**界面文件夹位置**: `web/src/views/{模块简称}/`
|
||
**API文件夹位置**: `web/src/api/{模块简称}/`
|
||
|
||
**已规划模块列表**:
|
||
- ✅ `hq` - 后勤管理模块 (已完成)
|
||
- ✅ `rs` - 人事管理模块
|
||
- ✅ `cw` - 财务管理模块
|
||
- ✅ `aqbw` - 安全保卫管理模块
|
||
- ✅ `zsjy` - 招生就业管理模块
|
||
- ✅ `xg` - 学工管理模块
|
||
|
||
#### 📁 标准目录结构
|
||
|
||
**界面文件夹结构** (web/src/views/{模块简称}/):
|
||
```
|
||
web/src/views/{模块简称}/
|
||
├── README.md # 模块说明文档
|
||
├── {功能模块1}/ # 按功能模块细分
|
||
│ ├── index.vue # 列表页面
|
||
│ ├── add.vue # 新增页面
|
||
│ ├── edit.vue # 编辑页面
|
||
│ └── detail.vue # 详情页面
|
||
├── {功能模块2}/
|
||
│ └── ...
|
||
└── components/ # 模块内公共组件
|
||
└── ...
|
||
```
|
||
|
||
**API文件夹结构** (web/src/api/{模块简称}/):
|
||
```
|
||
web/src/api/{模块简称}/
|
||
├── index.ts # API入口文件
|
||
├── README.md # API接口说明文档
|
||
├── types.ts # 类型定义文件
|
||
├── {功能模块1}/ # 按功能模块细分
|
||
│ └── index.ts # 功能模块API接口
|
||
├── {功能模块2}/
|
||
│ └── index.ts
|
||
└── ...
|
||
```
|
||
|
||
#### 🎯 文件命名规范
|
||
|
||
**Vue组件文件**:
|
||
- 页面组件: PascalCase (如: `UserList.vue`)
|
||
- 组件文件: kebab-case (如: `user-form.vue`)
|
||
|
||
**TypeScript文件**:
|
||
- API文件: kebab-case (如: `user-api.ts`)
|
||
- 类型文件: kebab-case (如: `user-types.ts`)
|
||
|
||
**路由命名**:
|
||
- 路由路径: kebab-case (如: `/rs/employee-list`)
|
||
- 路由名称: camelCase (如: `rsEmployeeList`)
|
||
|
||
#### 🔐 权限命名规范
|
||
|
||
**前端权限指令格式**: `v-hasPermi="'{模块简称}:{功能}:{操作}'"`
|
||
|
||
**示例**:
|
||
```vue
|
||
<!-- 人事模块员工新增权限 -->
|
||
<el-button v-hasPermi="'rs:employee:add'" type="primary">新增员工</el-button>
|
||
|
||
<!-- 财务模块预算查看权限 -->
|
||
<el-button v-hasPermi="'cw:budget:view'" type="primary">查看预算</el-button>
|
||
|
||
<!-- 学工模块学生编辑权限 -->
|
||
<el-button v-hasPermi="'xg:student:edit'" link type="primary">编辑</el-button>
|
||
```
|
||
|
||
#### 📋 路由配置规范
|
||
|
||
**路由路径格式**: `/{模块简称}/{功能模块}`
|
||
|
||
**router配置示例**:
|
||
```typescript
|
||
// router/modules/rs.ts - 人事管理模块路由
|
||
export default {
|
||
path: '/rs',
|
||
name: 'Rs',
|
||
component: () => import('@/layout/index.vue'),
|
||
redirect: '/rs/employee',
|
||
meta: {
|
||
title: '人事管理',
|
||
icon: 'peoples',
|
||
rank: 3
|
||
},
|
||
children: [
|
||
{
|
||
path: '/rs/employee',
|
||
name: 'RsEmployee',
|
||
component: () => import('@/views/rs/employee/index.vue'),
|
||
meta: {
|
||
title: '员工管理',
|
||
keepAlive: true
|
||
}
|
||
},
|
||
{
|
||
path: '/rs/department',
|
||
name: 'RsDepartment',
|
||
component: () => import('@/views/rs/department/index.vue'),
|
||
meta: {
|
||
title: '部门管理',
|
||
keepAlive: true
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
#### 🚨 前端模块开发检查清单
|
||
|
||
**创建新模块前必须检查**:
|
||
- [ ] 模块文件夹是否按规范命名?(`web/src/views/{简称}/`)
|
||
- [ ] API文件夹是否对应创建?(`web/src/api/{简称}/`)
|
||
- [ ] 是否创建了模块说明文档?(README.md)
|
||
- [ ] 路由配置是否符合规范?
|
||
- [ ] 权限指令是否使用正确的编码格式?
|
||
- [ ] 是否遵循前端开发规范和UI设计标准?
|
||
- [ ] 组件和文件命名是否符合规范?
|
||
- [ ] API接口类型定义是否与后端VO保持一致?
|
||
|
||
#### 💡 模块开发最佳实践
|
||
|
||
**1. 组件复用**:
|
||
- 模块内公共组件放在`components/`目录
|
||
- 跨模块公共组件放在`@/components/`目录
|
||
- 使用组合式API提高代码复用性
|
||
|
||
**2. 状态管理**:
|
||
- 模块相关状态使用Pinia store管理
|
||
- Store文件命名:`@/stores/{模块简称}.ts`
|
||
- 避免不同模块间的状态耦合
|
||
|
||
**3. 样式管理**:
|
||
- 使用SCSS变量和mixin提高样式复用
|
||
- 遵循Art Design Pro UI设计规范
|
||
- 统一使用轮廓式按钮设计
|
||
|
||
**4. 类型安全**:
|
||
- 所有API接口必须定义TypeScript类型
|
||
- 类型定义与后端VO对象保持一致
|
||
- 使用泛型提高类型复用性
|
||
|
||
#### 📝 注意事项
|
||
|
||
**模块间解耦**:
|
||
- 避免模块间直接依赖,通过事件总线或状态管理通信
|
||
- API接口按模块严格分离
|
||
- 路由按模块独立配置,避免交叉引用
|
||
|
||
**性能优化**:
|
||
- 页面组件使用懒加载
|
||
- 合理使用keep-alive缓存页面状态
|
||
- 图片资源优化和懒加载
|
||
|
||
**用户体验**:
|
||
- 统一的加载状态和错误处理
|
||
- 合理的页面过渡动画
|
||
- 响应式设计支持多设备访问
|
||
|
||
## 2. Art Design Pro UI 规范
|
||
|
||
### 设计理念
|
||
|
||
基于 Art Design Pro 的设计语言,追求现代化、精致、用户友好的界面体验。注重细节、动效和用户交互的流畅性。
|
||
|
||
### 颜色系统 🎨
|
||
|
||
#### 主色调 (Primary Colors) - 2025 年最新版本
|
||
|
||
```css
|
||
/* 亮色主题 */
|
||
:root {
|
||
--art-primary: 93, 135, 255; /* 主品牌色 - 亮蓝色 #5d87ff */
|
||
--art-secondary: 73, 190, 255; /* 辅助色 - 天蓝色 #49beff */
|
||
--art-success: 0, 167, 58; /* 成功色 - 绿色 #00a73a */
|
||
--art-warning: 142, 91, 0; /* 警告色 - 橙色 #8e5b00 */
|
||
--art-error: 250, 137, 107; /* 错误色 - 红色 #fa896b */
|
||
--art-info: 0, 75, 178; /* 信息色 - 蓝色 #004bb2 */
|
||
--art-danger: 255, 77, 79; /* 危险色 - 红色 #ff4d4f */
|
||
}
|
||
|
||
/* 暗色主题 */
|
||
html.dark {
|
||
--art-primary: 93, 135, 255; /* 主品牌色 - 亮蓝色 #5d87ff */
|
||
--art-secondary: 73, 190, 255; /* 辅助色 - 天蓝色 #49beff */
|
||
--art-success: 19, 222, 185; /* 成功色 - 绿色 #13deb9 */
|
||
--art-warning: 255, 174, 31; /* 警告色 - 橙色 #ffae1f */
|
||
--art-error: 250, 137, 107; /* 错误色 - 红色 #fa896b */
|
||
--art-info: 83, 155, 255; /* 信息色 - 蓝色 #539bff */
|
||
--art-danger: 255, 77, 79; /* 危险色 - 红色 #ff4d4f */
|
||
}
|
||
```
|
||
|
||
#### 背景色系统
|
||
|
||
```css
|
||
/* 浅色主题 */
|
||
:root {
|
||
--art-bg-color: #fafbfc; /* 页面底色 */
|
||
--art-main-bg-color: #ffffff; /* 主要内容背景 */
|
||
--art-bg-primary: 236, 242, 255; /* 主色背景 */
|
||
--art-bg-secondary: 232, 247, 255; /* 辅助背景 */
|
||
--art-bg-success: 230, 255, 250; /* 成功背景 */
|
||
--art-bg-warning: 254, 245, 229; /* 警告背景 */
|
||
--art-bg-error: 253, 237, 232; /* 错误背景 */
|
||
}
|
||
|
||
/* 暗色主题 */
|
||
html.dark {
|
||
--art-bg-color: #070707; /* 页面底色 */
|
||
--art-main-bg-color: #161618; /* 主要内容背景 */
|
||
}
|
||
```
|
||
|
||
#### 灰色系统
|
||
|
||
```css
|
||
:root {
|
||
--art-gray-100: #f9f9f9; /* 浅灰 */
|
||
--art-gray-200: #f1f1f4; /* 较浅灰 */
|
||
--art-gray-300: #dbdfe9; /* 中浅灰 */
|
||
--art-gray-400: #c4cada; /* 中灰 */
|
||
--art-gray-500: #99a1b7; /* 中深灰 */
|
||
--art-gray-600: #78829d; /* 深灰 */
|
||
--art-gray-700: #4b5675; /* 较深灰 */
|
||
--art-gray-800: #252f4a; /* 很深灰 */
|
||
--art-gray-900: #071437; /* 最深灰 */
|
||
}
|
||
```
|
||
|
||
### 轮廓式按钮设计系统 🔘
|
||
|
||
#### 🎨 设计理念
|
||
|
||
全面采用轮廓式(Outline)按钮设计,提供更加优雅、现代和一致的用户界面体验。
|
||
|
||
#### 核心规范
|
||
|
||
- **Primary 按钮**: 轮廓式主色设计,主色边框和文字,5%透明度主色背景
|
||
- **Success 按钮**: 轮廓式成功色设计,成功色边框和文字,5%透明度成功色背景
|
||
- **Warning 按钮**: 轮廓式警告色设计,警告色边框和文字,5%透明度警告色背景
|
||
- **Danger 按钮**: 轮廓式危险色设计,危险色边框和文字,5%透明度危险色背景
|
||
- **Info 按钮**: 轮廓式信息色设计,信息色边框和文字,5%透明度信息色背景
|
||
- **Default 按钮**: 白色背景,灰色边框,灰色文字
|
||
|
||
#### 交互效果
|
||
|
||
- **悬浮状态**: 上移 1px + 背景透明度变为 10% + 边框透明度变为 80% + 彩色阴影
|
||
- **点击状态**: 98%缩放 + 0.1s 快速过渡 + 背景透明度变为 15%
|
||
- **禁用状态**: 50%透明度,禁用所有交互和变换
|
||
|
||
### 🔘 列表页面按钮使用规范(2025 年最新标准)
|
||
|
||
#### 工具栏按钮规范
|
||
|
||
```vue
|
||
<!-- 标准工具栏按钮配置 -->
|
||
<template #header>
|
||
<el-row :gutter="10" class="mb8">
|
||
<el-col :span="1.5">
|
||
<!-- ✅ 第一个新增按钮:必须使用 type="primary" -->
|
||
<el-button type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<!-- ✅ 其他按钮:不添加type,使用默认样式 -->
|
||
<el-button icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<el-button icon="Delete" :disabled="multiple" @click="handleDelete()">删除</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<el-button icon="Download" @click="handleExport">导出</el-button>
|
||
</el-col>
|
||
</el-row>
|
||
</template>
|
||
```
|
||
|
||
#### 操作列按钮规范
|
||
|
||
```vue
|
||
<!-- 表格操作列按钮配置 -->
|
||
<el-table-column label="操作" align="center" width="160">
|
||
<template #default="scope">
|
||
<el-tooltip content="修改" placement="top">
|
||
<!-- ✅ 操作列按钮:统一使用 link type="primary" -->
|
||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
|
||
</el-tooltip>
|
||
<el-tooltip content="删除" placement="top">
|
||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
|
||
</el-tooltip>
|
||
</template>
|
||
</el-table-column>
|
||
```
|
||
|
||
#### 按钮使用规范总结
|
||
|
||
| 位置 | 按钮类型 | 样式规范 | 示例 |
|
||
| -------------------- | ---------------- | ---------------------- | -------------------------------------------------------------- |
|
||
| 工具栏第一个新增按钮 | 新增 | `type="primary" plain` | `<el-button type="primary" plain icon="Plus">新增</el-button>` |
|
||
| 工具栏其他按钮 | 修改/删除/导出等 | 无 type(默认) | `<el-button icon="Edit">修改</el-button>` |
|
||
| 表格操作列 | 所有操作按钮 | `link type="primary"` | `<el-button link type="primary" icon="Edit"></el-button>` |
|
||
| 弹窗底部 | 确认按钮 | `type="primary"` | `<el-button type="primary">确定</el-button>` |
|
||
| 弹窗底部 | 取消按钮 | 无 type(默认) | `<el-button>取消</el-button>` |
|
||
|
||
### 圆角系统
|
||
|
||
```css
|
||
:root {
|
||
--border-radius-xs: 4px; /* 超小元素 (checkbox等) */
|
||
--border-radius-sm: 6px; /* 小元素 (下拉选项、关闭按钮等) */
|
||
--border-radius-md: 8px; /* 表单控件 (input、select、button等) */
|
||
--border-radius-lg: 12px; /* 容器组件 (card、table、dialog等) */
|
||
--border-radius-xl: 12px;
|
||
--border-radius-2xl: 16px;
|
||
--border-radius-full: 50%;
|
||
}
|
||
```
|
||
|
||
### 间距系统
|
||
|
||
```css
|
||
:root {
|
||
--space-xs: 4px;
|
||
--space-sm: 8px;
|
||
--space-md: 12px;
|
||
--space-lg: 16px;
|
||
--space-xl: 20px;
|
||
--space-2xl: 24px;
|
||
--space-3xl: 32px;
|
||
--space-4xl: 48px;
|
||
--space-5xl: 64px;
|
||
}
|
||
```
|
||
|
||
## 3. 组件开发规范
|
||
|
||
### Vue 组件结构标准
|
||
|
||
```vue
|
||
<template>
|
||
<div class="component-container">
|
||
<!-- 组件内容 -->
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts" name="ComponentName">
|
||
// 1. 引入依赖
|
||
import { ref, reactive, computed, onMounted } from 'vue';
|
||
import type { ComponentProps, ComponentEmits } from './types';
|
||
|
||
// 2. 定义Props和Emits
|
||
const props = defineProps<ComponentProps>();
|
||
const emits = defineEmits<ComponentEmits>();
|
||
|
||
// 3. 响应式数据
|
||
const loading = ref(false);
|
||
const formData = reactive({});
|
||
|
||
// 4. 计算属性
|
||
const computedValue = computed(() => {
|
||
return /* 计算逻辑 */;
|
||
});
|
||
|
||
// 5. 方法定义
|
||
const handleClick = () => {
|
||
// 方法实现
|
||
};
|
||
|
||
// 6. 生命周期
|
||
onMounted(() => {
|
||
// 组件挂载后的逻辑
|
||
});
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.component-container {
|
||
// 样式实现
|
||
}
|
||
</style>
|
||
```
|
||
|
||
### 页面开发规范
|
||
|
||
#### 列表页面标准模板
|
||
|
||
```vue
|
||
<template>
|
||
<div class="p-2 page-container">
|
||
<!-- 搜索区域 -->
|
||
<transition
|
||
:enter-active-class="proxy?.animate.searchAnimate.enter"
|
||
:leave-active-class="proxy?.animate.searchAnimate.leave"
|
||
>
|
||
<div v-show="showSearch" class="mb-[10px]">
|
||
<el-card shadow="hover">
|
||
<el-form
|
||
ref="queryFormRef"
|
||
:model="queryParams"
|
||
:inline="true"
|
||
label-width="100px"
|
||
>
|
||
<!-- 搜索表单项 -->
|
||
<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>
|
||
</el-card>
|
||
</div>
|
||
</transition>
|
||
|
||
<!-- 表格区域 -->
|
||
<el-card shadow="never" class="button-card table-card">
|
||
<template #header>
|
||
<el-row :gutter="10" class="mb8">
|
||
<el-col :span="1.5">
|
||
<!-- ✅ 第一个新增按钮:type="primary" plain -->
|
||
<el-button type="primary" plain icon="Plus" @click="handleAdd">
|
||
新增
|
||
</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<!-- ✅ 其他按钮:无type,使用默认样式 -->
|
||
<el-button icon="Edit" :disabled="single" @click="handleUpdate()">
|
||
修改
|
||
</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<el-button icon="Delete" :disabled="multiple" @click="handleDelete()">
|
||
删除
|
||
</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<el-button icon="Download" @click="handleExport">导出</el-button>
|
||
</el-col>
|
||
<right-toolbar
|
||
v-model:showSearch="showSearch"
|
||
@queryTable="getList"
|
||
></right-toolbar>
|
||
</el-row>
|
||
</template>
|
||
|
||
<!-- 数据表格 -->
|
||
<el-table v-loading="loading" border :data="dataList" :height="tableHeight">
|
||
<el-table-column type="selection" width="55" align="center" />
|
||
<!-- 数据列 -->
|
||
<el-table-column label="操作" align="center" width="160">
|
||
<template #default="scope">
|
||
<el-tooltip content="修改" placement="top">
|
||
<!-- ✅ 操作列按钮:link type="primary" -->
|
||
<el-button
|
||
link
|
||
type="primary"
|
||
icon="Edit"
|
||
@click="handleUpdate(scope.row)"
|
||
></el-button>
|
||
</el-tooltip>
|
||
<el-tooltip content="删除" placement="top">
|
||
<el-button
|
||
link
|
||
type="primary"
|
||
icon="Delete"
|
||
@click="handleDelete(scope.row)"
|
||
></el-button>
|
||
</el-tooltip>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<!-- 分页组件 -->
|
||
<pagination
|
||
v-show="total > 0"
|
||
:total="total"
|
||
v-model:page="queryParams.pageNum"
|
||
v-model:limit="queryParams.pageSize"
|
||
@pagination="getList"
|
||
/>
|
||
</el-card>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
#### 表单页面标准模板
|
||
|
||
```vue
|
||
<template>
|
||
<div class="app-container">
|
||
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
|
||
<el-card shadow="never">
|
||
<template #header>
|
||
<span>基本信息</span>
|
||
</template>
|
||
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="名称" prop="name">
|
||
<el-input v-model="form.name" placeholder="请输入名称" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="状态" prop="status">
|
||
<el-select v-model="form.status" placeholder="请选择状态">
|
||
<el-option label="启用" value="0" />
|
||
<el-option label="禁用" value="1" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-form-item>
|
||
<!-- ✅ 提交按钮:type="primary" -->
|
||
<el-button type="primary" @click="submitForm">提交</el-button>
|
||
<!-- ✅ 重置按钮:无type,使用默认样式 -->
|
||
<el-button @click="resetForm">重置</el-button>
|
||
</el-form-item>
|
||
</el-card>
|
||
</el-form>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, reactive } from 'vue';
|
||
import type { FormInstance, FormRules } from 'element-plus';
|
||
|
||
// 表单引用
|
||
const formRef = ref<FormInstance>();
|
||
|
||
// 表单数据
|
||
const form = reactive({
|
||
name: '',
|
||
status: '0',
|
||
});
|
||
|
||
// 验证规则
|
||
const rules = reactive<FormRules>({
|
||
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
|
||
status: [{ required: true, message: '请选择状态', trigger: 'change' }],
|
||
});
|
||
|
||
// 提交表单
|
||
const submitForm = async () => {
|
||
if (!formRef.value) return;
|
||
await formRef.value.validate((valid) => {
|
||
if (valid) {
|
||
// 提交逻辑
|
||
console.log('提交表单:', form);
|
||
}
|
||
});
|
||
};
|
||
|
||
// 重置表单
|
||
const resetForm = () => {
|
||
if (!formRef.value) return;
|
||
formRef.value.resetFields();
|
||
};
|
||
</script>
|
||
```
|
||
|
||
## 4. API 开发规范
|
||
|
||
### API 接口定义标准
|
||
|
||
```typescript
|
||
// api/[module]/index.ts
|
||
import request from '@/utils/request';
|
||
import type { AxiosPromise } from 'axios';
|
||
import type { DataVO, DataForm, DataQuery } from './types';
|
||
|
||
/**
|
||
* 查询数据列表
|
||
* @param query 查询参数
|
||
*/
|
||
export const listData = (query?: DataQuery): AxiosPromise<DataVO[]> => {
|
||
return request({
|
||
url: '/api/data/list',
|
||
method: 'get',
|
||
params: query,
|
||
});
|
||
};
|
||
|
||
/**
|
||
* 查询数据详情
|
||
* @param id 数据ID
|
||
*/
|
||
export const getData = (id: string | number): AxiosPromise<DataVO> => {
|
||
return request({
|
||
url: `/api/data/${id}`,
|
||
method: 'get',
|
||
});
|
||
};
|
||
|
||
/**
|
||
* 新增数据
|
||
* @param data 数据信息
|
||
*/
|
||
export const addData = (data: DataForm): AxiosPromise<void> => {
|
||
return request({
|
||
url: '/api/data',
|
||
method: 'post',
|
||
data: data,
|
||
});
|
||
};
|
||
|
||
/**
|
||
* 修改数据
|
||
* @param data 数据信息
|
||
*/
|
||
export const updateData = (data: DataForm): AxiosPromise<void> => {
|
||
return request({
|
||
url: '/api/data',
|
||
method: 'put',
|
||
data: data,
|
||
});
|
||
};
|
||
|
||
/**
|
||
* 删除数据
|
||
* @param ids 数据ID数组
|
||
*/
|
||
export const delData = (ids: Array<string | number>): AxiosPromise<void> => {
|
||
return request({
|
||
url: `/api/data/${ids}`,
|
||
method: 'delete',
|
||
});
|
||
};
|
||
```
|
||
|
||
### TypeScript 类型定义
|
||
|
||
```typescript
|
||
// api/[module]/types.ts
|
||
|
||
// 查询参数
|
||
export interface DataQuery extends PageQuery {
|
||
name?: string;
|
||
status?: string;
|
||
createTime?: [DateModelType, DateModelType];
|
||
}
|
||
|
||
// 数据表单
|
||
export interface DataForm {
|
||
id?: string | number;
|
||
name: string;
|
||
status: string;
|
||
remark?: string;
|
||
}
|
||
|
||
// 数据视图对象
|
||
export interface DataVO {
|
||
id: string | number;
|
||
name: string;
|
||
status: string;
|
||
createTime: string;
|
||
updateTime: string;
|
||
remark?: string;
|
||
}
|
||
```
|
||
|
||
## 5. 状态管理规范
|
||
|
||
### Pinia Store 结构
|
||
|
||
```typescript
|
||
// stores/modules/[module].ts
|
||
import { defineStore } from 'pinia';
|
||
import { ref, computed } from 'vue';
|
||
import type { DataVO, DataQuery } from '@/api/[module]/types';
|
||
import { listData, getData } from '@/api/[module]';
|
||
|
||
export const useDataStore = defineStore('[module]', () => {
|
||
// 状态
|
||
const dataList = ref<DataVO[]>([]);
|
||
const loading = ref(false);
|
||
const total = ref(0);
|
||
|
||
// 计算属性
|
||
const hasData = computed(() => dataList.value.length > 0);
|
||
|
||
// 方法
|
||
const getList = async (query: DataQuery) => {
|
||
loading.value = true;
|
||
try {
|
||
const { data } = await listData(query);
|
||
dataList.value = data.rows;
|
||
total.value = data.total;
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
const getInfo = async (id: string | number) => {
|
||
const { data } = await getData(id);
|
||
return data;
|
||
};
|
||
|
||
const reset = () => {
|
||
dataList.value = [];
|
||
total.value = 0;
|
||
};
|
||
|
||
return {
|
||
// 状态
|
||
dataList,
|
||
loading,
|
||
total,
|
||
// 计算属性
|
||
hasData,
|
||
// 方法
|
||
getList,
|
||
getInfo,
|
||
reset,
|
||
};
|
||
});
|
||
```
|
||
|
||
## 6. 路由管理规范
|
||
|
||
### 路由定义标准
|
||
|
||
```typescript
|
||
// router/modules/[module].ts
|
||
import type { RouteRecordRaw } from 'vue-router';
|
||
import Layout from '@/layout/index.vue';
|
||
|
||
const moduleRouter: RouteRecordRaw = {
|
||
path: '/[module]',
|
||
component: Layout,
|
||
redirect: '/[module]/list',
|
||
name: 'Module',
|
||
meta: {
|
||
title: '模块管理',
|
||
icon: 'example',
|
||
alwaysShow: true,
|
||
},
|
||
children: [
|
||
{
|
||
path: 'list',
|
||
component: () => import('@/views/[module]/index.vue'),
|
||
name: 'ModuleList',
|
||
meta: {
|
||
title: '列表管理',
|
||
activeMenu: '/[module]/list',
|
||
},
|
||
},
|
||
{
|
||
path: 'add',
|
||
component: () => import('@/views/[module]/form.vue'),
|
||
name: 'ModuleAdd',
|
||
meta: {
|
||
title: '新增数据',
|
||
activeMenu: '/[module]/list',
|
||
},
|
||
hidden: true,
|
||
},
|
||
{
|
||
path: 'edit/:id(\\d+)',
|
||
component: () => import('@/views/[module]/form.vue'),
|
||
name: 'ModuleEdit',
|
||
meta: {
|
||
title: '编辑数据',
|
||
activeMenu: '/[module]/list',
|
||
},
|
||
hidden: true,
|
||
},
|
||
],
|
||
};
|
||
|
||
export default moduleRouter;
|
||
```
|
||
|
||
## 7. 项目架构规范
|
||
|
||
### 目录结构
|
||
|
||
```
|
||
web/src/
|
||
├── api/ # API接口定义
|
||
│ ├── [module]/
|
||
│ │ ├── index.ts # 接口方法
|
||
│ │ └── types.ts # 类型定义
|
||
├── assets/ # 静态资源
|
||
│ ├── images/ # 图片资源
|
||
│ └── styles/ # 样式文件
|
||
│ ├── art-variables.scss # Art Design Pro 变量
|
||
│ ├── art-element-ui.scss # Element Plus 样式定制
|
||
│ ├── art-global.scss # 全局样式
|
||
│ ├── art-dark.scss # 暗色主题
|
||
│ └── index.scss # 样式入口
|
||
├── components/ # 全局组件
|
||
├── hooks/ # 组合式函数
|
||
├── layout/ # 布局组件
|
||
├── plugins/ # 插件配置
|
||
├── router/ # 路由配置
|
||
├── stores/ # 状态管理
|
||
├── types/ # 全局类型定义
|
||
├── utils/ # 工具函数
|
||
├── views/ # 页面组件
|
||
└── main.ts # 应用入口
|
||
```
|
||
|
||
### 文件命名规范
|
||
|
||
- **组件文件**: PascalCase,如 `UserForm.vue`
|
||
- **页面文件**: kebab-case,如 `user-list.vue`
|
||
- **工具文件**: camelCase,如 `formatUtils.ts`
|
||
- **类型文件**: PascalCase,如 `UserTypes.ts`
|
||
|
||
## 8. 样式开发规范
|
||
|
||
### SCSS 变量使用
|
||
|
||
```scss
|
||
// 使用 Art Design Pro 变量系统
|
||
.custom-component {
|
||
background-color: var(--art-main-bg-color);
|
||
border: 1px solid var(--art-border-color);
|
||
border-radius: var(--border-radius-lg);
|
||
padding: var(--space-lg);
|
||
|
||
.title {
|
||
color: var(--art-text-gray-900);
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.content {
|
||
color: var(--art-text-gray-700);
|
||
margin-top: var(--space-md);
|
||
}
|
||
}
|
||
```
|
||
|
||
### UnoCSS 工具类
|
||
|
||
```vue
|
||
<template>
|
||
<!-- 使用原子化CSS类 -->
|
||
<div class="p-4 bg-white rounded-lg shadow-md">
|
||
<h3 class="text-lg font-medium text-gray-900 mb-4">标题</h3>
|
||
<p class="text-gray-700">内容</p>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
## 9. 性能优化规范
|
||
|
||
### 组件懒加载
|
||
|
||
```typescript
|
||
// 路由级别的懒加载
|
||
const UserList = () => import('@/views/user/index.vue');
|
||
|
||
// 组件级别的懒加载
|
||
const AsyncComponent = defineAsyncComponent(() => import('@/components/HeavyComponent.vue'));
|
||
```
|
||
|
||
### 图片优化
|
||
|
||
```vue
|
||
<template>
|
||
<!-- 使用适当的图片格式和尺寸 -->
|
||
<img :src="imageSrc" :alt="imageAlt" loading="lazy" width="200" height="150" />
|
||
</template>
|
||
```
|
||
|
||
## 10. 开发最佳实践
|
||
|
||
### 代码规范
|
||
|
||
1. **组件命名**: 使用 PascalCase
|
||
2. **方法命名**: 使用 camelCase,语义化命名
|
||
3. **常量命名**: 使用 UPPER_SNAKE_CASE
|
||
4. **注释规范**: 使用 JSDoc 格式
|
||
|
||
### 错误处理
|
||
|
||
```typescript
|
||
// API错误处理
|
||
const handleSubmit = async () => {
|
||
try {
|
||
loading.value = true;
|
||
await submitData(formData);
|
||
ElMessage.success('操作成功');
|
||
} catch (error) {
|
||
console.error('提交失败:', error);
|
||
ElMessage.error('操作失败');
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
```
|
||
|
||
### 类型安全
|
||
|
||
```typescript
|
||
// 严格的类型定义
|
||
interface UserForm {
|
||
name: string;
|
||
email: string;
|
||
status: 0 | 1;
|
||
}
|
||
|
||
// 类型断言
|
||
const user = data as UserForm;
|
||
```
|
||
|
||
## 11. 测试规范
|
||
|
||
### 单元测试
|
||
|
||
```typescript
|
||
// 使用 Vitest 进行单元测试
|
||
import { describe, it, expect } from 'vitest';
|
||
import { mount } from '@vue/test-utils';
|
||
import UserForm from '@/components/UserForm.vue';
|
||
|
||
describe('UserForm', () => {
|
||
it('should render correctly', () => {
|
||
const wrapper = mount(UserForm);
|
||
expect(wrapper.exists()).toBe(true);
|
||
});
|
||
});
|
||
```
|
||
|
||
## 12. 构建与部署
|
||
|
||
### 环境配置
|
||
|
||
```typescript
|
||
// .env.development
|
||
VITE_APP_BASE_API = '/dev-api';
|
||
VITE_APP_PORT = 80;
|
||
|
||
// .env.production
|
||
VITE_APP_BASE_API = '/prod-api';
|
||
VITE_APP_PORT = 80;
|
||
```
|
||
|
||
### 构建优化
|
||
|
||
```typescript
|
||
// vite.config.ts
|
||
export default defineConfig({
|
||
build: {
|
||
rollupOptions: {
|
||
output: {
|
||
chunkFileNames: 'js/[name]-[hash].js',
|
||
entryFileNames: 'js/[name]-[hash].js',
|
||
assetFileNames: '[ext]/[name]-[hash].[ext]',
|
||
},
|
||
},
|
||
},
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
本规范文档涵盖了杭州第一技师学院智慧校园管理系统前端开发的所有核心规范,包括最新的品牌色配置和统一的按钮使用规范。开发者应严格遵循这些规范,确保代码质量和用户体验的一致性。
|
||
|
||
**🔴 重要提醒**:
|
||
|
||
1. **按钮规范**: 工具栏第一个新增按钮必须使用 `type="primary" plain`,其他按钮使用默认样式
|
||
2. **颜色系统**: 使用最新的 Art Design Pro 颜色变量
|
||
3. **文档同步**: 代码变更后必须同步更新相关文档
|