feat: 完善项目开发规范和测试文档体系

📝 主要改进:
• Cursor配置模块化:拆分为前端、后端、移动端、文档、API测试5个专门规范文件
• API文档重新组织:按业务模块拆分到docs/开发文档/API/目录下
• 新增维修分类功能完整测试方案和自动化测试脚本
• 后端热启动脚本完善,支持Linux/Windows双平台
• 强化文档同步更新规范,确保代码与文档一致性

🔧 技术改进:
• 优化Cursor AI工作流程和代码生成质量
• 完善API测试规范(AES+RSA双重加密登录方法)
• 提升开发效率和代码质量标准化程度

�� 测试覆盖:
• 维修分类管理功能端到端测试验证通过
• 包含页面加载、CRUD操作、搜索功能等核心测试用例

更新时间:2025-07-01
This commit is contained in:
zlwu committed 2025-07-01 17:53:05 +08:00
1 parent 7a5b9d1bea
commit 249b1dfeb4
44 files changed
+14027 -2852

No files matched your search

+714
View File
@@ -0,0 +1,714 @@
# Cursor Rules - 移动端开发规范
## 适用范围
- **项目目录**: `mobile/` (基于 uniapp + uView UI)
- **开发语言**: JavaScript/TypeScript
- **平台支持**: H5、小程序、APP
---
# 移动端开发规范 (mobile/)
## 1. 技术栈规范
### 核心技术
- **框架**: uni-app
- **UI 组件库**: uView UI
- **开发语言**: JavaScript/TypeScript
- **状态管理**: Vuex
- **HTTP 客户端**: uni.request 封装
## 2. 项目结构规范
### 移动端目录结构
```
mobile/
├── src/
│ ├── api/ # API接口定义
│ ├── components/ # 通用组件
│ ├── pages/ # 页面文件
│ │ ├── index/ # 首页
│ │ ├── login/ # 登录页
│ │ ├── mine/ # 个人中心
│ │ └── workbench/ # 工作台
│ ├── static/ # 静态资源
│ ├── utils/ # 工具函数
│ ├── App.vue # 应用入口
│ ├── config.js # 配置文件
│ └── main.js # 主入口文件
├── pages.json # 页面配置
├── manifest.json # 应用配置
└── uni.scss # 全局样式
```
## 3. 开发规范
### 页面开发规范
- **页面命名**: 使用小写字母和连字符 (如: user-list)
- **组件命名**: 使用 PascalCase (如: UserCard)
- **API 调用**: 统一使用封装的 request 工具
- **样式规范**: 使用 uView UI 组件和样式变量
### 页面模板
```vue
<template>
<view class="page-container">
<u-navbar title="页面标题" @leftClick="handleBack"></u-navbar>
<!-- 页面内容 -->
</view>
</template>
<script>
export default {
data() {
return {
// 页面数据
};
},
onLoad() {
// 页面加载逻辑
},
methods: {
handleBack() {
uni.navigateBack();
},
},
};
</script>
<style lang="scss" scoped>
.page-container {
// 页面样式
}
</style>
```
### 组件开发规范
```vue
<template>
<view class="component-wrapper">
<!-- 组件内容 -->
</view>
</template>
<script>
export default {
name: 'ComponentName',
props: {
// 组件属性
},
data() {
return {
// 组件数据
};
},
methods: {
// 组件方法
},
};
</script>
<style lang="scss" scoped>
.component-wrapper {
// 组件样式
}
</style>
```
## 4. 与后端 API 对接规范
### 请求封装
```javascript
// utils/request.js
const request = (options) => {
return new Promise((resolve, reject) => {
uni.request({
url: getApp().globalData.baseUrl + options.url,
method: options.method || 'GET',
data: options.data || {},
header: {
Authorization: uni.getStorageSync('token'),
'Content-Type': 'application/json',
},
success: (res) => {
if (res.data.code === 200) {
resolve(res.data);
} else {
reject(res.data);
}
},
fail: (err) => {
reject(err);
},
});
});
};
export default request;
```
### API 接口定义
```javascript
// api/user.js
import request from '@/utils/request';
export const userApi = {
// 用户登录
login(data) {
return request({
url: '/auth/login',
method: 'POST',
data,
});
},
// 获取用户信息
getUserInfo() {
return request({
url: '/system/user/getInfo',
method: 'GET',
});
},
// 用户列表
getUserList(params) {
return request({
url: '/system/user/list',
method: 'GET',
data: params,
});
},
};
```
## 5. 状态管理规范
### Vuex Store 结构
```javascript
// store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
import user from './modules/user';
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
user,
},
});
```
### 用户模块示例
```javascript
// store/modules/user.js
const state = {
token: uni.getStorageSync('token') || '',
userInfo: {},
};
const mutations = {
SET_TOKEN(state, token) {
state.token = token;
uni.setStorageSync('token', token);
},
SET_USER_INFO(state, userInfo) {
state.userInfo = userInfo;
},
};
const actions = {
// 用户登录
login({ commit }, loginForm) {
return new Promise((resolve, reject) => {
userApi
.login(loginForm)
.then((res) => {
commit('SET_TOKEN', res.data.access_token);
resolve(res);
})
.catch((error) => {
reject(error);
});
});
},
// 获取用户信息
getUserInfo({ commit }) {
return new Promise((resolve, reject) => {
userApi
.getUserInfo()
.then((res) => {
commit('SET_USER_INFO', res.data);
resolve(res);
})
.catch((error) => {
reject(error);
});
});
},
};
export default {
namespaced: true,
state,
mutations,
actions,
};
```
## 6. 页面配置规范
### pages.json 配置
```json
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页"
}
},
{
"path": "pages/login/login",
"style": {
"navigationBarTitleText": "登录",
"navigationStyle": "custom"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "校园管理",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#3cc51f",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [
{
"pagePath": "pages/index/index",
"iconPath": "static/tabbar/home.png",
"selectedIconPath": "static/tabbar/home-active.png",
"text": "首页"
},
{
"pagePath": "pages/workbench/workbench",
"iconPath": "static/tabbar/workbench.png",
"selectedIconPath": "static/tabbar/workbench-active.png",
"text": "工作台"
},
{
"pagePath": "pages/mine/mine",
"iconPath": "static/tabbar/mine.png",
"selectedIconPath": "static/tabbar/mine-active.png",
"text": "我的"
}
]
}
}
```
## 7. 样式规范
### 全局样式
```scss
// uni.scss
$primary-color: #007aff;
$success-color: #4cd964;
$warning-color: #f0ad4e;
$error-color: #dd524d;
$text-color: #333333;
$text-color-light: #666666;
$text-color-lighter: #999999;
$border-color: #e5e5e5;
$background-color: #f5f5f5;
// 通用样式
.container {
padding: 20rpx;
}
.flex {
display: flex;
}
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
.text-center {
text-align: center;
}
.text-right {
text-align: right;
}
```
### 组件样式规范
```scss
// 组件样式示例
.user-card {
background: #fff;
border-radius: 10rpx;
padding: 20rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
.avatar {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
}
.info {
flex: 1;
margin-left: 20rpx;
.name {
font-size: 32rpx;
color: $text-color;
font-weight: bold;
}
.desc {
font-size: 28rpx;
color: $text-color-light;
margin-top: 10rpx;
}
}
}
```
## 8. 工具函数规范
### 通用工具函数
```javascript
// utils/index.js
// 格式化日期
export function formatDate(date, format = 'YYYY-MM-DD HH:mm:ss') {
// 实现日期格式化逻辑
}
// 防抖函数
export function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// 节流函数
export function throttle(func, limit) {
let inThrottle;
return function () {
const args = arguments;
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
}
// 深拷贝
export function deepClone(obj) {
if (obj === null) return null;
if (typeof obj !== 'object') return obj;
if (obj.constructor === Date) return new Date(obj);
if (obj.constructor === RegExp) return new RegExp(obj);
var newObj = new obj.constructor();
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepClone(obj[key]);
}
}
return newObj;
}
```
### 存储工具
```javascript
// utils/storage.js
// 同步存储
export function setStorageSync(key, data) {
try {
uni.setStorageSync(key, data);
} catch (e) {
console.error('存储数据失败', e);
}
}
export function getStorageSync(key) {
try {
return uni.getStorageSync(key);
} catch (e) {
console.error('获取存储数据失败', e);
return null;
}
}
export function removeStorageSync(key) {
try {
uni.removeStorageSync(key);
} catch (e) {
console.error('删除存储数据失败', e);
}
}
// 异步存储
export function setStorage(key, data) {
return new Promise((resolve, reject) => {
uni.setStorage({
key,
data,
success: resolve,
fail: reject,
});
});
}
export function getStorage(key) {
return new Promise((resolve, reject) => {
uni.getStorage({
key,
success: (res) => resolve(res.data),
fail: reject,
});
});
}
```
## 9. 权限管理
### 登录状态检查
```javascript
// utils/auth.js
// 检查登录状态
export function checkLogin() {
const token = uni.getStorageSync('token');
return !!token;
}
// 跳转到登录页
export function toLogin() {
uni.navigateTo({
url: '/pages/login/login',
});
}
// 页面权限检查中间件
export function requireAuth() {
if (!checkLogin()) {
toLogin();
return false;
}
return true;
}
```
### 路由守卫
```javascript
// main.js
import { requireAuth } from '@/utils/auth';
// 页面跳转拦截器
const originalNavigateTo = uni.navigateTo;
uni.navigateTo = function (options) {
// 需要登录的页面列表
const authPages = ['/pages/workbench/workbench', '/pages/mine/mine'];
if (authPages.includes(options.url) && !requireAuth()) {
return;
}
return originalNavigateTo.call(this, options);
};
```
## 10. 错误处理
### 全局错误处理
```javascript
// utils/errorHandler.js
// 统一错误处理
export function handleError(error) {
console.error('Error:', error);
let message = '系统异常,请稍后重试';
if (error.code) {
switch (error.code) {
case 401:
message = '登录已过期,请重新登录';
// 跳转到登录页
toLogin();
break;
case 403:
message = '没有权限访问';
break;
case 404:
message = '请求的资源不存在';
break;
case 500:
message = '服务器内部错误';
break;
default:
message = error.msg || message;
}
}
uni.showToast({
title: message,
icon: 'none',
duration: 2000,
});
}
// 网络错误处理
export function handleNetworkError() {
uni.showToast({
title: '网络连接异常',
icon: 'none',
duration: 2000,
});
}
```
## 11. 性能优化
### 图片优化
```javascript
// 图片懒加载
export function lazyLoadImage(selector) {
const query = uni.createSelectorQuery();
query
.selectAll(selector)
.boundingClientRect((data) => {
data.forEach((item) => {
if (item.top < uni.getSystemInfoSync().windowHeight) {
// 加载图片
}
});
})
.exec();
}
// 图片压缩
export function compressImage(src, quality = 0.8) {
return new Promise((resolve, reject) => {
uni.compressImage({
src,
quality,
success: resolve,
fail: reject,
});
});
}
```
### 数据缓存
```javascript
// 数据缓存管理
class DataCache {
constructor() {
this.cache = new Map();
this.timeout = 5 * 60 * 1000; // 5分钟
}
set(key, data) {
this.cache.set(key, {
data,
timestamp: Date.now(),
});
}
get(key) {
const item = this.cache.get(key);
if (!item) return null;
if (Date.now() - item.timestamp > this.timeout) {
this.cache.delete(key);
return null;
}
return item.data;
}
clear() {
this.cache.clear();
}
}
export const dataCache = new DataCache();
```
## 12. 测试规范
### 单元测试
```javascript
// 使用Jest进行单元测试
describe('utils/index', () => {
test('formatDate should format date correctly', () => {
const date = new Date('2023-01-01 12:00:00');
const result = formatDate(date, 'YYYY-MM-DD');
expect(result).toBe('2023-01-01');
});
});
```
### 页面测试
```javascript
// 页面功能测试
describe('Login Page', () => {
test('should show error when username is empty', async () => {
// 模拟登录表单提交
const wrapper = mount(LoginPage);
await wrapper.find('.login-btn').trigger('tap');
expect(wrapper.find('.error-message').text()).toBe('请输入用户名');
});
});
```