删除不需要的文件及提交移动端的代码
No files matched your search
@@ -1,3 +1,2 @@
|
||||
# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv)
|
||||
temp/
|
||||
test/
|
||||
|
||||
@@ -161,4 +161,11 @@ coverage/
|
||||
*.crt
|
||||
*.p12
|
||||
*.keystore/xgxt/
|
||||
excel/
|
||||
excel/
|
||||
|
||||
# 吴自龙注释
|
||||
test/
|
||||
script/
|
||||
stgl-api-test.js
|
||||
update/
|
||||
本地临时文件目录/
|
||||
@@ -1,2 +0,0 @@
|
||||
-- 添加批次ID字段
|
||||
ALTER TABLE xljk_consultation_schedule ADD COLUMN batch_id VARCHAR(50) DEFAULT NULL COMMENT '批次ID,用于标识同一批次创建的预约时段';
|
||||
@@ -128,7 +128,7 @@ spring.data:
|
||||
# 端口,默认为6379
|
||||
port: 16379
|
||||
# 数据库索引
|
||||
database: 0
|
||||
database: 8
|
||||
# redis 密码必须配置
|
||||
password: Hzdyjs@123456
|
||||
# 连接超时时间
|
||||
|
||||
@@ -1,506 +1,216 @@
|
||||
# 代码调整记录 - 2025-01-07 - wuzilong
|
||||
# 移动端首页界面重构 - 2025-01-07
|
||||
|
||||
## 2025-01-07 20:15:00 - 学生信息详细对话框改为完全禁用模式
|
||||
## 📋 变更概述
|
||||
|
||||
### 📋 变更描述
|
||||
将学生信息详细对话框(stuMsgDetailDialog.vue)中所有输入控件从 `readonly` 状态改为 `disabled` 状态,实现完全禁用效果。
|
||||
**时间**: 2025-01-07
|
||||
**开发者**: wuzilong
|
||||
**变更类型**: 界面重构、权限控制
|
||||
|
||||
### 🎯 变更目标
|
||||
- 统一所有输入控件的禁用状态
|
||||
- 从 `readonly`(可读但不可编辑)改为 `disabled`(完全禁用)
|
||||
- 修复 linter 错误
|
||||
## 🎯 变更目标
|
||||
|
||||
### 🔧 具体修改内容
|
||||
完全重构移动端首页界面,实现以下目标:
|
||||
1. 简化界面结构,移除底部导航,只保留单页面
|
||||
2. 重新设计头部,包含用户信息和退出登录功能
|
||||
3. 采用权限控制的服务卡片布局
|
||||
4. 将所有emoji图标替换为u-icon组件
|
||||
5. 确保界面风格与web端保持一致
|
||||
|
||||
#### 1. 导入错误修复
|
||||
## 🔄 主要变更
|
||||
|
||||
### 1. 创建权限管理工具 (`mobile/src/utils/permission.js`)
|
||||
|
||||
**功能**: 移动端权限校验工具,参考web端实现
|
||||
|
||||
**核心功能**:
|
||||
- `checkPermi()`: 字符权限校验
|
||||
- `checkRole()`: 角色权限校验
|
||||
- `hasPermission()`: 权限指令校验函数
|
||||
- `hasPermi()`: Vue过滤器权限校验
|
||||
- `hasRole()`: 用户角色校验
|
||||
- `permissionMixin`: 权限控制混入
|
||||
|
||||
**使用方式**:
|
||||
```javascript
|
||||
// ElMessage导入修复
|
||||
// 尝试过的方式:import ElMessage from 'element-plus/es/components/message';
|
||||
// 最终正确方式:
|
||||
import { ElMessage } from 'element-plus';
|
||||
// 在组件中使用
|
||||
import { hasPermi, permissionMixin } from '@/utils/permission'
|
||||
|
||||
// Request导入类型错误修复
|
||||
// @ts-ignore
|
||||
import request from '@/utils/request';
|
||||
// 混入方式
|
||||
mixins: [permissionMixin]
|
||||
|
||||
// 模板中使用
|
||||
v-if="hasPermi(['system:user:list'])"
|
||||
```
|
||||
|
||||
#### 2. 输入控件状态统一
|
||||
将所有 `el-input` 的 `readonly` 属性改为 `disabled` 属性:
|
||||
### 2. 重构首页界面 (`mobile/src/pages/index/index.vue`)
|
||||
|
||||
**涉及的输入控件**:
|
||||
- 基础信息:英文姓名、曾用名、手机号码、邮政编码、户籍所在地、家庭地址、现住址等
|
||||
- 扩展信息:宗教信仰、毕业院校、电子邮箱、QQ、微信、紧急联系人信息、助学贷款金额等
|
||||
- 扩展文本域:特长爱好、获奖情况、违纪记录、社会活动等
|
||||
- 家庭信息:父母姓名、电话、工作单位、监护人信息、家庭收入等
|
||||
- 健康信息:身高、体重、健康状况、残疾等级、疾病史等
|
||||
- 银行卡信息:银行卡号、持卡人姓名、开户银行、开户支行等
|
||||
**变更前**:
|
||||
- 复杂的仪表板界面,包含欢迎横幅、统计数据、待办任务、新闻动态等
|
||||
- 使用emoji图标
|
||||
- 底部tabbar导航
|
||||
|
||||
#### 3. 状态对比
|
||||
- **readonly**:字段可被选中和复制,但不可编辑
|
||||
- **disabled**:字段完全禁用,不可选中、复制或编辑,呈现灰色状态
|
||||
**变更后**:
|
||||
- **头部区域**: 用户头像+昵称+部门信息 + 退出登录按钮
|
||||
- **内容区域**: 按业务模块分组的服务卡片
|
||||
- **权限控制**: 所有服务卡片支持权限控制
|
||||
- **图标系统**: 全部使用u-icon组件
|
||||
|
||||
### 📁 影响文件
|
||||
- `web/src/views/stuMsgSupply/stuMsgDetailDialog.vue`
|
||||
**头部功能**:
|
||||
- 点击用户头像: 跳转到个人中心页面
|
||||
- 点击退出登录: 确认后退出并清理本地数据
|
||||
|
||||
### ✅ 验证结果
|
||||
- [x] 修复了 ElMessage 导入错误
|
||||
- [x] 所有文本输入框改为 disabled 状态
|
||||
- [x] 所有文本域改为 disabled 状态
|
||||
- [x] select、date-picker、tree-select 保持原有 disabled 状态
|
||||
- [x] 界面呈现完全禁用的灰色效果
|
||||
- [x] 无 linter 错误
|
||||
**服务模块分组**:
|
||||
- 学工管理 (6个服务): 学生信息、宿舍管理、学生请假、心理健康、社团管理、心理咨询
|
||||
- 后勤管理 (6个服务): 物业报修、场地预约、报告厅预约、满意度调查、食堂管理、物业管理
|
||||
- 招生就业 (3个服务): 学生管理、毕业生信息、就业信息
|
||||
- 人事管理 (3个服务): 员工管理、出国申请、培训管理
|
||||
- 财务管理 (3个服务): 预算管理、费用报销、发票管理
|
||||
- 系统管理 (6个服务): 用户管理、角色管理、菜单管理、部门管理、参数设置、在线用户
|
||||
- 工作流 (5个服务): 流程定义、我发起的、我的待办、我的已办、抄送我的
|
||||
|
||||
### 🎯 用户体验
|
||||
- 更明显的视觉禁用效果(灰色显示)
|
||||
- 防止用户尝试选中或复制内容
|
||||
- 统一的禁用状态,界面更加一致
|
||||
**权限控制示例**:
|
||||
```vue
|
||||
<ServiceItem
|
||||
v-if="hasPermi(['student:info:list'])"
|
||||
title="学生信息"
|
||||
@click="handleServiceClick"
|
||||
>
|
||||
```
|
||||
|
||||
---
|
||||
### 3. 更新ServiceItem组件 (`mobile/src/components/ServiceItem.vue`)
|
||||
|
||||
## 2025-01-07 19:50:00 - 学生信息详细对话框改为只读模式
|
||||
**变更内容**:
|
||||
- 支持u-icon组件通过slot传入
|
||||
- 添加深度选择器样式支持u-icon
|
||||
- 保持向后兼容emoji图标
|
||||
|
||||
### 📋 变更描述
|
||||
将学生信息详细对话框(stuMsgDetailDialog.vue)从编辑模式改为只读查看模式,所有输入控件不可编辑。
|
||||
**使用方式**:
|
||||
```vue
|
||||
<ServiceItem title="学生信息" @click="handleServiceClick">
|
||||
<template #icon>
|
||||
<u-icon name="account" size="24" color="inherit"></u-icon>
|
||||
</template>
|
||||
</ServiceItem>
|
||||
```
|
||||
|
||||
### 🎯 变更目标
|
||||
- 将界面从信息完善对话框改为信息查看对话框
|
||||
- 移除编辑和提交功能,仅保留查看功能
|
||||
## 🎨 样式设计
|
||||
|
||||
### 🔧 具体修改内容
|
||||
### 头部设计
|
||||
- 渐变背景: `linear-gradient(135deg, $uni-color-primary, $uni-color-primary-dark)`
|
||||
- 用户头像: 圆形,48px × 48px,带白色边框
|
||||
- 退出按钮: 半透明圆形背景,hover效果
|
||||
|
||||
#### 1. 界面标题修改
|
||||
- 默认标题:`学生信息完善` → `学生信息详情查看`
|
||||
- Props默认值:`title: '学生信息完善'` → `title: '学生信息详情查看'`
|
||||
### 服务卡片
|
||||
- 每行3个卡片的自适应布局
|
||||
- 卡片悬停效果: 向上移动3px + 阴影变化
|
||||
- 图标容器: 圆形背景,主题色
|
||||
|
||||
#### 2. 底部按钮修改
|
||||
- 移除:确定按钮(提交功能)
|
||||
- 保留:关闭按钮(`取消` → `关闭`)
|
||||
### 响应式适配
|
||||
- 小屏设备(<375px): 调整padding和图标尺寸
|
||||
- 保持良好的触摸交互体验
|
||||
|
||||
#### 3. 输入控件只读化
|
||||
**基础信息Tab**:
|
||||
- `el-input`:添加 `readonly` 属性
|
||||
- `el-select`:添加 `disabled` 属性
|
||||
- `el-date-picker`:添加 `disabled` 属性
|
||||
- `el-tree-select`:添加 `disabled` 属性
|
||||
- 照片上传:改为 `<div class="photo-display">` 纯显示
|
||||
## 🔐 权限集成
|
||||
|
||||
**扩展信息Tab**:
|
||||
- 所有文本输入框:添加 `readonly` 属性
|
||||
- 所有下拉选择器:添加 `disabled` 属性
|
||||
- 所有文本域:添加 `readonly` 属性
|
||||
|
||||
**家庭信息Tab**:
|
||||
- 父母信息输入框:添加 `readonly` 属性
|
||||
- 监护人信息输入框:添加 `readonly` 属性
|
||||
- 经济状况选择器:添加 `disabled` 属性
|
||||
|
||||
**健康信息Tab**:
|
||||
- 身高体重输入框:添加 `readonly` 属性
|
||||
- 血型选择器:添加 `disabled` 属性
|
||||
- 是否残疾等选择器:添加 `disabled` 属性
|
||||
- 健康相关文本域:添加 `readonly` 属性
|
||||
|
||||
**学籍信息Tab**:
|
||||
- 培养方式选择器:添加 `disabled` 属性
|
||||
- 录取类别选择器:添加 `disabled` 属性
|
||||
- 学籍状态选择器:添加 `disabled` 属性
|
||||
- 入学日期选择器:添加 `disabled` 属性
|
||||
|
||||
**银行卡信息Tab**:
|
||||
- 所有银行卡相关输入框:添加 `readonly` 属性
|
||||
|
||||
#### 4. 功能函数移除
|
||||
- 移除:`submitForm()` 提交表单函数
|
||||
- 移除:`beforePhotoUpload()` 照片上传校验函数
|
||||
- 移除:`handlePhotoUpload()` 照片上传处理函数
|
||||
|
||||
#### 5. 样式优化
|
||||
添加照片显示容器样式:
|
||||
```css
|
||||
.photo-display {
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
width: 120px;
|
||||
height: 150px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
### 权限检查计算属性
|
||||
```javascript
|
||||
computed: {
|
||||
hasStudentServices() {
|
||||
return this.hasPermi(['student:info:list']) ||
|
||||
this.hasPermi(['student:dorm:list']) ||
|
||||
// ... 其他权限
|
||||
},
|
||||
// ... 其他模块权限检查
|
||||
}
|
||||
```
|
||||
|
||||
### 📁 影响文件
|
||||
- `web/src/views/stuMsgSupply/stuMsgDetailDialog.vue`
|
||||
### 模块级权限控制
|
||||
- 如果用户没有某模块的任何权限,整个模块卡片组不显示
|
||||
- 单个服务卡片根据具体权限显示/隐藏
|
||||
- 工作流模块始终显示基础功能
|
||||
|
||||
### ✅ 验证结果
|
||||
- [x] 所有Tab页的输入控件均为只读状态
|
||||
- [x] 照片显示正常,无法上传新照片
|
||||
- [x] 底部只有关闭按钮,无提交功能
|
||||
- [x] 历史记录Tab正常显示
|
||||
- [x] 界面标题正确显示为"学生信息详情查看"
|
||||
- [x] 样式保持一致,用户体验良好
|
||||
## 📱 用户体验优化
|
||||
|
||||
### 🎯 使用场景
|
||||
该只读对话框适用于:
|
||||
- 查看学生已提交的信息详情
|
||||
- 历史记录查看
|
||||
- 信息确认和核实
|
||||
- 管理员查看学生信息
|
||||
### 交互优化
|
||||
- 退出登录: 二次确认 + Loading状态
|
||||
- 服务点击: 震动反馈
|
||||
- 页面进入: 渐显动画效果
|
||||
- 卡片交互: 平滑过渡动画
|
||||
|
||||
### 📝 后续计划
|
||||
- 如需要编辑功能,可创建单独的编辑对话框组件
|
||||
- 可根据权限控制显示编辑按钮
|
||||
- 可添加打印或导出功能
|
||||
### 无障碍支持
|
||||
- aria-label标签
|
||||
- 合适的焦点管理
|
||||
- 键盘导航支持
|
||||
|
||||
---
|
||||
## 🔧 技术实现
|
||||
|
||||
## 2025-01-07 18:30:00 - 学生信息批次管理收集范围功能完善
|
||||
### 核心依赖
|
||||
- Vue 3组件式开发
|
||||
- uview-plus图标组件
|
||||
- 权限校验混入
|
||||
- 用户状态管理
|
||||
|
||||
## 历史记录Tab页样式优化及箭头逻辑修正
|
||||
|
||||
### 变更时间
|
||||
2025-01-07 16:45:00(初次优化)
|
||||
2025-01-07 17:00:00(箭头逻辑修正)
|
||||
|
||||
### 变更内容
|
||||
优化了学生信息完善弹窗中历史记录Tab页的显示样式,统一卡片大小,添加内容省略和tooltip效果,调整箭头显示逻辑。
|
||||
|
||||
### 修改文件
|
||||
- `web/src/views/stuMsgSupply/stuMsgDialog.vue`
|
||||
|
||||
### 具体变更
|
||||
1. **卡片样式统一化**
|
||||
- 设置固定卡片尺寸:宽度200px,高度120px
|
||||
- 使用flex布局:`flex-direction: column` 和 `justify-content: space-between`
|
||||
- 确保所有卡片大小一致,提升视觉效果
|
||||
|
||||
2. **操作内容省略优化**
|
||||
- 添加 `el-tooltip` 包装操作内容文本
|
||||
- 设置省略规则:最多显示2行,超出显示省略号
|
||||
- 添加条件tooltip:内容长度>20字符时才显示tooltip
|
||||
- CSS样式:使用 `-webkit-line-clamp: 2` 实现多行省略
|
||||
|
||||
3. **箭头显示逻辑调整**
|
||||
- **取消第四个卡片的向下箭头**:条件从 `(index + 1) % 4 === 0` 改为 `(index + 1) % 4 === 0 && index !== 3`
|
||||
- **新增每行第一个卡片前的向右箭头**:条件为 `index % 4 === 0 && index >= 4`
|
||||
- **修正箭头重复问题**:避免第二行第二个卡片出现两个箭头,只在每行第一个卡片前显示向右箭头
|
||||
- 添加 `row-start-arrow` 和 `right-arrow` 样式类
|
||||
|
||||
4. **响应式布局优化**
|
||||
- 在所有断点(1200px、900px、600px)下隐藏新的向右箭头
|
||||
- 确保小屏幕下的显示效果不受影响
|
||||
- 保持原有的列数布局逻辑
|
||||
|
||||
### 样式变更详情
|
||||
```css
|
||||
/* 卡片固定尺寸 */
|
||||
.history-card {
|
||||
width: 200px;
|
||||
height: 120px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* 内容省略效果 */
|
||||
.operate-content {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-height: 2.8em;
|
||||
}
|
||||
|
||||
/* 新的向右箭头 */
|
||||
.row-start-arrow {
|
||||
position: absolute;
|
||||
left: -25px;
|
||||
z-index: 10;
|
||||
}
|
||||
### 代码结构
|
||||
```
|
||||
index.vue (534行)
|
||||
├── 模板部分 (204行) - 头部 + 内容区域
|
||||
├── 脚本部分 (234行) - 权限控制 + 用户管理
|
||||
└── 样式部分 (96行) - 响应式设计
|
||||
```
|
||||
|
||||
### 用户体验改进
|
||||
- **视觉一致性**:所有卡片大小统一,界面更整洁
|
||||
- **内容可读性**:长文本显示省略号,悬停查看完整内容
|
||||
- **流程清晰度**:箭头逻辑更合理,每行第一个卡片前显示向右箭头表示换行,避免箭头重复
|
||||
- **适配性**:在不同屏幕尺寸下都有良好的显示效果
|
||||
|
||||
### 技术要点
|
||||
- 使用CSS Grid/Flexbox实现响应式布局
|
||||
- -webkit-line-clamp实现多行文本省略
|
||||
- Element Plus tooltip组件增强交互体验
|
||||
- 媒体查询确保移动端适配
|
||||
|
||||
---
|
||||
|
||||
## 学生信息完善弹窗蒙层加载效果优化
|
||||
|
||||
### 变更时间
|
||||
2025-01-07 15:30:00
|
||||
|
||||
### 变更内容
|
||||
优化了学生信息完善弹窗的蒙层加载效果,解决用户看不到加载状态的问题。
|
||||
|
||||
### 修改文件
|
||||
- `web/src/views/stuMsgSupply/stuMsgDialog.vue`
|
||||
|
||||
### 具体变更
|
||||
1. **添加对话框级别的蒙层**
|
||||
- 在 `el-dialog` 上添加 `v-loading="dialogLoading"` 和 `element-loading-text="正在加载数据,请稍候..."`
|
||||
- 新增 `dialogLoading` 响应式变量控制加载状态
|
||||
|
||||
2. **优化加载逻辑**
|
||||
- 立即显示蒙层,防止闪烁
|
||||
- 分阶段加载数据:第一阶段加载基础配置,第二阶段加载业务数据
|
||||
- 增加最小显示时间到1000ms,确保用户能看到加载过程
|
||||
- 增强日志输出,使用emoji提升可读性
|
||||
|
||||
3. **修复导入错误**
|
||||
- 将 `import service from '@/utils/request'` 修正为 `import request from '@/utils/request'`
|
||||
- 更新所有相关的API调用从 `service` 改为 `request`
|
||||
|
||||
4. **异常处理优化**
|
||||
- 在 try-catch-finally 结构中确保蒙层能够正确隐藏
|
||||
- 对话框关闭时重置加载状态
|
||||
|
||||
### 技术要点
|
||||
- 使用Element Plus的v-loading指令实现蒙层效果
|
||||
- 分阶段加载策略避免多API并发调用的时序问题
|
||||
- 最小显示时间机制确保用户体验
|
||||
|
||||
### 预期效果
|
||||
- 弹窗打开时显示明显的蒙层加载效果
|
||||
- 防止用户在数据加载期间误操作
|
||||
- 提供更好的用户体验和视觉反馈
|
||||
|
||||
---
|
||||
|
||||
## 变更概述
|
||||
新增学生信息批次收集模块中的学生提交完善信息接口
|
||||
|
||||
## 变更时间
|
||||
2025-01-07 14:30:00
|
||||
|
||||
## 变更人员
|
||||
wuzilong
|
||||
|
||||
---
|
||||
|
||||
## 🔄 变更记录(按时间倒序)
|
||||
|
||||
### 2025-01-07 14:30:00 - 新增学生提交信息完善接口
|
||||
|
||||
#### 📋 变更说明
|
||||
在学生信息批次收集模块中新增接口,用于存储学生提交的完善信息。
|
||||
|
||||
#### 🎯 业务需求
|
||||
- 学生在信息完善界面提交数据时,需要先逻辑删除历史数据,再插入新数据
|
||||
- 确保每个学生在同一批次中只有一条有效记录
|
||||
- 支持使用前台传递的ID进行数据插入
|
||||
|
||||
#### 🔧 技术实现
|
||||
|
||||
**1. Controller层变更**
|
||||
- 文件:`backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentMsgBatchController.java`
|
||||
- 新增接口:`POST /studentMsgBatch/studentMsgBatch/submitStudentMsg`
|
||||
- 权限要求:`studentMsgBatch:studentMsgBatch:add`
|
||||
- 入参:`XgStudentMsgBatchBo`
|
||||
- 返回:`R<Void>`
|
||||
|
||||
**2. Service层变更**
|
||||
- 接口文件:`backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgStudentMsgBatchService.java`
|
||||
- 新增方法:`Boolean submitStudentMsg(XgStudentMsgBatchBo bo)`
|
||||
- 实现文件:`backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentMsgBatchServiceImpl.java`
|
||||
- 实现业务逻辑:逻辑删除 + 数据插入
|
||||
|
||||
**3. Mapper层变更**
|
||||
- 接口文件:`backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgStudentMsgBatchMapper.java`
|
||||
- 新增方法:`int logicalDeleteByUserId(@Param("userId") Long userId)`
|
||||
- XML文件:`backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/studentMsgBatch/XgStudentMsgBatchMapper.xml`
|
||||
- 新增SQL:逻辑删除操作(只更新deleted字段,不更新update_by和update_time)
|
||||
|
||||
#### 📝 核心业务逻辑
|
||||
```java
|
||||
// 第一步:逻辑删除当前登录人的历史数据
|
||||
UPDATE xg_student_msg_batch
|
||||
SET deleted = 1
|
||||
WHERE id = #{userId} AND deleted = 0
|
||||
|
||||
// 第二步:插入新的学生信息数据(使用前台传递的id)
|
||||
XgStudentMsgBatch add = MapstructUtils.convert(bo, XgStudentMsgBatch.class);
|
||||
if (bo.getId() != null) {
|
||||
add.setId(bo.getId());
|
||||
}
|
||||
baseMapper.insert(add);
|
||||
```
|
||||
|
||||
#### ✅ 测试验证
|
||||
- [ ] 接口功能测试
|
||||
- [ ] 权限验证测试
|
||||
- [ ] 数据完整性测试
|
||||
- [ ] 并发操作测试
|
||||
|
||||
#### 📚 文档更新
|
||||
- [x] 变更记录文档
|
||||
- [ ] API文档更新(待补充到学工管理API文档中)
|
||||
|
||||
#### 🏷️ 关联标签
|
||||
- 模块:学工管理
|
||||
- 功能:学生信息批次收集
|
||||
- 类型:新增接口
|
||||
- 影响范围:后端API
|
||||
|
||||
---
|
||||
|
||||
## 📋 待办事项
|
||||
1. 补充API文档到相应的学工管理API文档中
|
||||
2. 编写完整的接口测试用例
|
||||
3. 前端对接测试
|
||||
|
||||
## 🔗 相关文件
|
||||
- Controller: `backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentMsgBatchController.java`
|
||||
- Service: `backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/`
|
||||
- Mapper: `backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgStudentMsgBatchMapper.java`
|
||||
- XML: `backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/studentMsgBatch/XgStudentMsgBatchMapper.xml`
|
||||
|
||||
---
|
||||
|
||||
# 代码调整记录 - 2025-01-07
|
||||
|
||||
**调整时间**: 2025-01-07
|
||||
**调整人员**: wuzilong
|
||||
**调整范围**: 学生信息收集批次管理 - 历史记录功能
|
||||
|
||||
---
|
||||
|
||||
## 📝 本次调整概述
|
||||
|
||||
在学生信息完善对话框(`stuMsgDialog.vue`)中增加历史记录Tab页功能,展示用户的历史提交记录。
|
||||
|
||||
---
|
||||
|
||||
## 🔧 具体调整内容
|
||||
|
||||
### 1. 新增历史记录Tab页
|
||||
- **位置**: 在基础信息Tab前面增加"历史记录"Tab页
|
||||
- **默认显示**: 打开对话框时默认显示历史记录Tab
|
||||
- **数据来源**: 通过 `getStudentMsgHistory` 方法获取后端数据
|
||||
|
||||
### 2. 历史记录数据管理
|
||||
- **新增变量**: `historyLoading`、`historyRecords`
|
||||
- **接口调用**: `/studentMsgBatch/studentMsgBatch/history/{batchId}`
|
||||
- **数据提取**: 从返回数据的 `historyMsg` 字段获取历史记录列表
|
||||
|
||||
### 3. 历史记录展示逻辑
|
||||
- **空数据处理**: 当 `historyMsg` 为空时显示"暂无历史记录"
|
||||
- **卡片展示**: 每条记录以卡片形式展示
|
||||
- **布局设计**: 每行4个卡片,使用flex布局
|
||||
- **连接线**: 卡片间用箭头连接,行尾显示向下箭头
|
||||
|
||||
### 4. 操作内容显示规则
|
||||
- **infoStatus = 3**: 显示"信息填写提交"
|
||||
- **infoStatus = 4 或 5**: 显示 `remark` 字段内容
|
||||
- **其他状态**: 显示"未知操作"
|
||||
|
||||
### 5. 时间格式化
|
||||
- **方法**: `formatDateTime`
|
||||
- **格式**: `YYYY-MM-DD HH:mm:ss` 中文本地化格式
|
||||
|
||||
---
|
||||
|
||||
## 🎨 界面优化
|
||||
|
||||
### 卡片设计
|
||||
- **卡片结构**: 头部(操作人+时间)+ 内容(操作内容)
|
||||
- **悬停效果**: 阴影加深,轻微上移
|
||||
- **边框圆角**: 8px 圆角设计
|
||||
- **间距**: 16px 内边距,20px 底部外边距
|
||||
|
||||
### 连接线设计
|
||||
- **水平箭头**: 卡片间显示 → 符号
|
||||
- **换行箭头**: 每行末尾显示 ↓ 符号
|
||||
- **颜色**: 主题蓝色 (#409eff)
|
||||
- **字体**: 18px 粗体
|
||||
|
||||
### 响应式布局
|
||||
- **>1200px**: 每行4列
|
||||
- **900-1200px**: 每行3列
|
||||
- **600-900px**: 每行2列,隐藏连接线
|
||||
- **<600px**: 每行1列,隐藏连接线
|
||||
|
||||
---
|
||||
|
||||
## 🔄 数据流程
|
||||
|
||||
### 对话框打开流程
|
||||
1. 设置默认Tab为"history"
|
||||
2. 并行加载:行政区划、宿舍楼栋、历史记录、历史数据
|
||||
3. 历史记录通过 `getStudentMsgHistory` 方法获取
|
||||
4. 数据存储在 `historyRecords` 响应式变量中
|
||||
|
||||
### 关闭重置流程
|
||||
1. 重置Tab为"history"
|
||||
2. 清空 `historyRecords` 数组
|
||||
3. 重置表单数据到默认值
|
||||
|
||||
---
|
||||
|
||||
## 📁 涉及文件
|
||||
|
||||
### 前端文件
|
||||
- **主要文件**: `web/src/views/stuMsgSupply/stuMsgDialog.vue`
|
||||
- **修改类型**: 新增功能、样式优化
|
||||
|
||||
### 后端接口
|
||||
- **控制器**: `XgStudentMsgBatchController.java`
|
||||
- **方法**: `getStudentMsgHistory(@PathVariable Long batchId)`
|
||||
- **返回**: `Map<String, Object>` 包含 `historyMsg` 字段
|
||||
|
||||
---
|
||||
|
||||
## ✅ 测试要点
|
||||
## ✅ 测试验证
|
||||
|
||||
### 功能测试
|
||||
- [ ] 历史记录Tab页正常显示
|
||||
- [ ] 无历史记录时显示空状态
|
||||
- [ ] 有历史记录时正确显示卡片
|
||||
- [ ] 操作内容根据状态正确显示
|
||||
- [ ] 时间格式化正确
|
||||
- [x] 用户头像点击跳转个人中心
|
||||
- [x] 退出登录流程完整性
|
||||
- [x] 权限控制正确性
|
||||
- [x] 服务卡片交互
|
||||
- [x] 响应式布局适配
|
||||
|
||||
### 界面测试
|
||||
- [ ] 卡片布局每行4个
|
||||
- [ ] 连接线和箭头正确显示
|
||||
- [ ] 响应式布局在不同屏幕下正常
|
||||
- [ ] 悬停效果正常
|
||||
- [ ] 加载状态正常显示
|
||||
### 兼容性测试
|
||||
- [x] 保持ServiceItem组件向后兼容
|
||||
- [x] 权限校验工具正确性
|
||||
- [x] u-icon组件显示正常
|
||||
|
||||
### 数据测试
|
||||
- [ ] 接口调用正常
|
||||
- [ ] 数据解析正确
|
||||
- [ ] 错误处理正常
|
||||
- [ ] 空数据处理正常
|
||||
## 📊 性能影响
|
||||
|
||||
### 优化项
|
||||
- 移除了复杂的仪表板数据
|
||||
- 简化了页面结构和状态管理
|
||||
- 减少了不必要的API调用
|
||||
|
||||
### 文件大小
|
||||
- index.vue: 压缩为534行 (原1323行)
|
||||
- 新增permission.js: 123行
|
||||
- 更新ServiceItem.vue: +12行样式
|
||||
|
||||
## 🚀 部署注意事项
|
||||
|
||||
### 权限配置
|
||||
- 确保后端已配置相应的权限标识
|
||||
- 验证权限数据返回格式正确
|
||||
- 检查超级管理员权限(`*:*:*`)
|
||||
|
||||
### 图标资源
|
||||
- 确保uview-plus组件库正确安装
|
||||
- 验证所有u-icon名称有效性
|
||||
- 测试图标在不同设备上的显示效果
|
||||
|
||||
## 📝 后续优化建议
|
||||
|
||||
1. **服务路由**: 为每个服务卡片配置具体的跳转路由
|
||||
2. **图标主题**: 考虑为不同模块使用不同颜色的图标
|
||||
3. **加载状态**: 添加权限数据加载时的骨架屏
|
||||
4. **缓存优化**: 权限数据的本地缓存策略
|
||||
5. **搜索功能**: 添加服务快速搜索功能
|
||||
|
||||
## 🔄 相关文档更新
|
||||
|
||||
- [x] 移动端开发规范 (cursorrules-mobile.md)
|
||||
- [x] 权限管理文档
|
||||
- [x] API文档同步更新
|
||||
- [x] 组件使用说明
|
||||
|
||||
---
|
||||
|
||||
## 🎯 预期效果
|
||||
|
||||
1. **用户体验**: 用户可以直观查看历史提交记录
|
||||
2. **界面美观**: 卡片式布局清晰美观,连接线体现时间流程
|
||||
3. **响应式**: 在不同设备上都有良好的显示效果
|
||||
4. **性能**: 历史记录和表单数据并行加载,提升加载速度
|
||||
|
||||
---
|
||||
|
||||
## 📋 后续优化建议
|
||||
|
||||
1. **分页加载**: 如果历史记录过多,考虑分页显示
|
||||
2. **筛选功能**: 可按时间范围或操作类型筛选记录
|
||||
3. **详情查看**: 点击卡片可查看更详细的历史信息
|
||||
4. **导出功能**: 支持导出历史记录为Excel
|
||||
|
||||
---
|
||||
|
||||
**变更状态**: ✅ 已完成
|
||||
**测试状态**: 🔄 待测试
|
||||
**上线状态**: 📋 待上线
|
||||
**变更影响评估**: 🟢 低风险
|
||||
**回滚方案**: Git版本回滚
|
||||
**测试状态**: ✅ 已完成基础测试
|
||||
@@ -0,0 +1,156 @@
|
||||
# 🔄 代码调整记录 - 2025-01-07 - 移动端图标系统
|
||||
|
||||
## 📋 变更总览
|
||||
|
||||
**变更日期**: 2025-01-07 16:45:23
|
||||
**变更负责人**: Cursor AI
|
||||
**变更类型**: 移动端图标系统增强
|
||||
**影响范围**: 移动端首页登出功能
|
||||
|
||||
## 🎯 变更内容
|
||||
|
||||
### 1. 新增iconfont图标
|
||||
|
||||
**变更文件**: `mobile/src/static/fonts/iconfont.css`
|
||||
|
||||
**新增内容**:
|
||||
```css
|
||||
/* 登出图标 */
|
||||
.icon-logout {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.59L17 17l5-5-5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z'/%3E%3C/svg%3E");
|
||||
}
|
||||
```
|
||||
|
||||
**图标特点**:
|
||||
- ✅ 采用SVG data URI格式,无需额外字体文件
|
||||
- ✅ 支持currentColor,可通过CSS修改颜色
|
||||
- ✅ 响应式适配,支持所有平台
|
||||
- ✅ 语义化设计,清晰表达"登出"含义
|
||||
|
||||
### 2. 更新移动端首页
|
||||
|
||||
**变更文件**: `mobile/src/pages/index/index.vue`
|
||||
|
||||
**修改内容**:
|
||||
```vue
|
||||
<!-- 变更前 -->
|
||||
<text class="iconfont icon-close logout-icon"></text>
|
||||
|
||||
<!-- 变更后 -->
|
||||
<text class="iconfont icon-logout logout-icon"></text>
|
||||
```
|
||||
|
||||
**变更原因**:
|
||||
- ❌ `icon-close` 语义不清,容易与"关闭"操作混淆
|
||||
- ✅ `icon-logout` 语义明确,用户体验更佳
|
||||
|
||||
## 🔧 技术细节
|
||||
|
||||
### SVG图标设计
|
||||
|
||||
**原始SVG代码**:
|
||||
```svg
|
||||
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'>
|
||||
<path d='M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.59L17 17l5-5-5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z'/>
|
||||
</svg>
|
||||
```
|
||||
|
||||
**data URI编码过程**:
|
||||
1. 转换特殊字符
|
||||
2. URL编码处理
|
||||
3. 添加data URI前缀
|
||||
|
||||
### 图标系统优势
|
||||
|
||||
根据 `mobile/docs/图标管理系统使用指南.md`:
|
||||
|
||||
- **轻量级**: 无需下载字体文件
|
||||
- **兼容性**: 支持所有uni-app平台
|
||||
- **可定制**: 支持颜色和尺寸调整
|
||||
- **性能优**: 内联SVG,加载快速
|
||||
|
||||
## ✅ 测试验证
|
||||
|
||||
### 功能测试
|
||||
- [x] 图标正常显示
|
||||
- [x] 点击事件响应正常
|
||||
- [x] 多平台兼容性验证
|
||||
- [x] 响应式适配检查
|
||||
|
||||
### 视觉测试
|
||||
- [x] 图标清晰度符合要求
|
||||
- [x] 与UI风格保持一致
|
||||
- [x] 颜色继承正常工作
|
||||
- [x] 不同尺寸下显示正常
|
||||
|
||||
## 📝 代码质量
|
||||
|
||||
### 符合规范
|
||||
- ✅ 遵循项目图标命名规范
|
||||
- ✅ 使用推荐的SVG data URI方式
|
||||
- ✅ 符合移动端开发规范
|
||||
- ✅ 注释清晰,便于维护
|
||||
|
||||
### 维护性
|
||||
- 📁 图标统一管理在CSS文件中
|
||||
- 📖 完整的使用文档支持
|
||||
- 🔄 易于后续扩展和修改
|
||||
|
||||
## 🚀 后续建议
|
||||
|
||||
### 1. 图标库完善
|
||||
考虑添加更多常用图标:
|
||||
- `icon-power` - 电源/开关图标
|
||||
- `icon-exit` - 退出图标
|
||||
- `icon-sign-out` - 注销图标
|
||||
|
||||
### 2. 图标管理工具
|
||||
可以开发简单的图标管理脚本:
|
||||
```javascript
|
||||
// 批量生成图标CSS
|
||||
function generateIconCSS(iconName, svgPath) {
|
||||
return `.icon-${iconName} {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,${encodeSVG(svgPath)}");
|
||||
}`;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 设计规范
|
||||
建议制定图标设计规范:
|
||||
- 统一的视觉风格
|
||||
- 标准的尺寸规格
|
||||
- 一致的线条粗细
|
||||
|
||||
## 📊 影响评估
|
||||
|
||||
### 用户体验改善
|
||||
- ✅ 图标语义更清晰
|
||||
- ✅ 操作意图更明确
|
||||
- ✅ 减少用户困惑
|
||||
|
||||
### 技术债务
|
||||
- ✅ 无新增技术债务
|
||||
- ✅ 遵循最佳实践
|
||||
- ✅ 便于后续维护
|
||||
|
||||
### 性能影响
|
||||
- ✅ 无性能负面影响
|
||||
- ✅ 图标内联,加载更快
|
||||
- ✅ 减少HTTP请求
|
||||
|
||||
## 🔗 相关文档
|
||||
|
||||
- **图标系统文档**: `mobile/docs/图标管理系统使用指南.md`
|
||||
- **移动端开发规范**: `.cursor/cursorrules-mobile.md`
|
||||
- **项目规范总览**: `.cursor/rules.md`
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
如需扩展图标系统或遇到问题,请参考:
|
||||
1. 图标管理系统使用指南
|
||||
2. 在线SVG编辑工具
|
||||
3. data URI转换工具
|
||||
|
||||
---
|
||||
|
||||
**✨ 总结**: 成功创建并部署了语义化的logout图标,提升了移动端用户体验,遵循了项目最佳实践。图标采用SVG data URI格式,具备良好的兼容性和性能表现。
|
||||
@@ -0,0 +1,61 @@
|
||||
# 移动端控制台错误修复记录
|
||||
|
||||
**日期**: 2025-01-17
|
||||
**操作人**: Cursor AI
|
||||
**时间**: 2025-01-17 14:30:00
|
||||
|
||||
## 🚨 修复的问题
|
||||
|
||||
### 1. 修复 "Refused to set unsafe header 'referer'" 错误
|
||||
|
||||
**问题描述**: 移动端H5环境下,浏览器禁止JavaScript手动设置referer头,导致控制台报错。
|
||||
|
||||
**修复位置**: `mobile/src/utils/request.js`
|
||||
- 第80行:注释掉统一请求方法中的referer设置
|
||||
- 第382行:注释掉加密登录方法中的referer设置
|
||||
|
||||
**修复内容**:
|
||||
```javascript
|
||||
// 修复前
|
||||
referer: options.referer || REQUEST_CONFIG.referer,
|
||||
|
||||
// 修复后(注释掉)
|
||||
// 注意:在H5环境中,浏览器会自动设置referer,手动设置会报错
|
||||
// referer: options.referer || REQUEST_CONFIG.referer,
|
||||
```
|
||||
|
||||
### 2. 修复 UInput组件border属性类型错误
|
||||
|
||||
**问题描述**: uview-plus的u-input组件期望border属性为字符串类型,但传入了布尔值导致类型警告。
|
||||
|
||||
**修复位置**: `mobile/src/pages/login/index.vue`
|
||||
- 第22行:账号输入框的border属性
|
||||
- 第37行:密码输入框的border属性
|
||||
|
||||
**修复内容**:
|
||||
```vue
|
||||
<!-- 修复前 -->
|
||||
:border="true"
|
||||
|
||||
<!-- 修复后 -->
|
||||
border="true"
|
||||
```
|
||||
|
||||
## ✅ 修复结果
|
||||
|
||||
- ❌ 移除了所有会导致"unsafe header"错误的referer设置
|
||||
- ❌ 修复了UInput组件的属性类型问题
|
||||
- ✅ 保持了功能完整性(浏览器会自动处理referer)
|
||||
- ✅ 消除了控制台错误和警告
|
||||
|
||||
## 📝 技术说明
|
||||
|
||||
1. **referer处理**: H5环境下浏览器自动设置referer,无需手动指定
|
||||
2. **组件属性**: uview-plus组件要求严格的属性类型匹配
|
||||
3. **向后兼容**: 修复不影响小程序等其他端的功能
|
||||
|
||||
## 🔄 影响范围
|
||||
|
||||
- **前端影响**: 移动端H5登录页面控制台错误消除
|
||||
- **后端影响**: 无,referer信息仍然正常传递
|
||||
- **其他端影响**: 无,小程序端不受影响
|
||||
@@ -0,0 +1,130 @@
|
||||
# 移动端加密密钥配置修复记录
|
||||
|
||||
**变更时间**: 2025-01-25 11:30:00
|
||||
**变更人员**: wuzilong
|
||||
**变更类型**: 安全配置修复
|
||||
**影响范围**: 移动端加密模块
|
||||
|
||||
## 🎯 变更目标
|
||||
|
||||
修复移动端与web端加密密钥配置不一致的问题,确保前后端加密通信正常。
|
||||
|
||||
## 🔍 问题发现
|
||||
|
||||
在检查移动端和web端的加密密钥配置时发现:
|
||||
|
||||
### 原始问题
|
||||
1. **移动端RSA私钥缺失**: `mobile/src/utils/crypto.js` 中 `RSA_PRIVATE_KEY` 配置为空字符串
|
||||
2. **解密功能异常**: 由于私钥缺失,响应解密功能无法正常工作
|
||||
3. **配置不一致**: 移动端和web端配置文档中的密钥配置格式不统一
|
||||
|
||||
### 配置对比结果
|
||||
| 组件 | RSA公钥 | RSA私钥 | 状态 |
|
||||
|------|---------|---------|------|
|
||||
| web端配置文档 | ✅ 已配置 | ✅ 已配置 | 正常 |
|
||||
| 移动端原配置 | ✅ 已配置 | ❌ 空字符串 | 异常 |
|
||||
| 移动端修复后 | ✅ 已配置 | ✅ 已配置 | 正常 |
|
||||
|
||||
## 🔧 修复方案
|
||||
|
||||
### 1. 补充RSA私钥配置
|
||||
|
||||
**文件**: `mobile/src/utils/crypto.js`
|
||||
**修改内容**:
|
||||
```javascript
|
||||
// 修复前
|
||||
RSA_PRIVATE_KEY: '',
|
||||
|
||||
// 修复后
|
||||
RSA_PRIVATE_KEY: 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE=',
|
||||
```
|
||||
|
||||
### 2. 增强解密函数日志
|
||||
|
||||
**文件**: `mobile/src/utils/crypto.js`
|
||||
**修改内容**: 为 `decrypt` 函数添加详细的日志记录,便于调试和问题定位
|
||||
|
||||
### 3. 新增密钥验证功能
|
||||
|
||||
**文件**: `mobile/src/utils/crypto.js`
|
||||
**新增函数**:
|
||||
- `validateKeyPair()`: 验证RSA密钥对的有效性
|
||||
- `getConfigSummary()`: 获取当前加密配置摘要
|
||||
|
||||
### 4. 更新请求模块导出
|
||||
|
||||
**文件**: `mobile/src/utils/request.js`
|
||||
**新增导出**:
|
||||
- `validateCryptoKeys`: 密钥对验证方法
|
||||
- `getCryptoConfigSummary`: 获取加密配置摘要
|
||||
|
||||
## ✅ 验证结果
|
||||
|
||||
### 密钥配置一致性检查
|
||||
- ✅ RSA公钥: 移动端与web端配置文档完全一致
|
||||
- ✅ RSA私钥: 已补充完整,与web端配置文档一致
|
||||
- ✅ 客户端ID: `e5cd7e4891bf95d1d19206ce24a7b32e`
|
||||
- ✅ 加密头字段: `encrypt-key`
|
||||
|
||||
### 功能验证
|
||||
- ✅ RSA加密功能: 正常工作
|
||||
- ✅ RSA解密功能: 已修复,可正常解密
|
||||
- ✅ AES加密解密: 正常工作
|
||||
- ✅ 请求响应加密流程: 完整可用
|
||||
|
||||
## 📋 后续建议
|
||||
|
||||
### 1. 安全注意事项
|
||||
- 🔐 私钥配置应考虑从环境变量或安全配置文件读取
|
||||
- 🔐 生产环境建议定期更换密钥对
|
||||
- 🔐 考虑实现密钥轮换机制
|
||||
|
||||
### 2. 监控和维护
|
||||
- 📊 添加加密解密成功率监控
|
||||
- 📊 记录加密相关错误统计
|
||||
- 📊 定期验证密钥对有效性
|
||||
|
||||
### 3. 文档更新
|
||||
- 📚 更新API文档中的加密说明
|
||||
- 📚 完善移动端开发规范中的安全配置部分
|
||||
|
||||
## 🎯 变更影响
|
||||
|
||||
### 正面影响
|
||||
- ✅ 修复了移动端响应解密功能
|
||||
- ✅ 统一了前后端密钥配置
|
||||
- ✅ 增强了密钥配置的可维护性
|
||||
- ✅ 提供了密钥验证工具
|
||||
|
||||
### 风险评估
|
||||
- ⚠️ 无向后兼容性问题
|
||||
- ⚠️ 无数据丢失风险
|
||||
- ⚠️ 配置修复后需要重新部署移动端应用
|
||||
|
||||
## 📝 测试验证
|
||||
|
||||
### 建议测试项目
|
||||
1. **登录加密测试**: 验证移动端登录加密流程
|
||||
2. **响应解密测试**: 验证加密响应的正确解密
|
||||
3. **密钥对验证测试**: 运行 `validateCryptoKeys()` 函数
|
||||
4. **跨端兼容性测试**: 确保与web端和后端的加密兼容性
|
||||
|
||||
### 测试命令
|
||||
```javascript
|
||||
// 在移动端控制台执行
|
||||
import { validateCryptoKeys, getCryptoConfigSummary } from '@/utils/request.js';
|
||||
|
||||
// 验证密钥对
|
||||
console.log('密钥对验证结果:', validateCryptoKeys());
|
||||
|
||||
// 查看配置摘要
|
||||
console.log('加密配置摘要:', getCryptoConfigSummary());
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**变更完成**: ✅
|
||||
**验证状态**: ✅
|
||||
**文档更新**: ✅
|
||||
|
||||
**备注**: 此次修复确保了移动端与web端加密密钥配置的完全一致,解决了响应解密异常问题,为移动端安全通信提供了可靠保障。
|
||||
@@ -0,0 +1,73 @@
|
||||
# 代码调整记录 - 2025-01-27 - 移动端底部导航栏移除
|
||||
|
||||
## 修改概述
|
||||
移除移动端底部导航栏,只保留首页,删除办公中心和我的页面。
|
||||
|
||||
## 修改内容
|
||||
|
||||
### 1. 配置文件修改
|
||||
- **文件**: `mobile/src/pages.json`
|
||||
- **修改类型**: 删除tabBar配置和相关页面配置
|
||||
- **具体变更**:
|
||||
- 删除完整的tabBar配置块
|
||||
- 移除`pages/workbench/index`页面配置
|
||||
- 移除`pages/profile/index`页面配置
|
||||
|
||||
### 2. 页面文件删除
|
||||
- **删除文件**:
|
||||
- `mobile/src/pages/workbench/index.vue` - 办公中心页面
|
||||
- `mobile/src/pages/profile/index.vue` - 我的页面
|
||||
|
||||
### 3. 静态资源清理
|
||||
- **删除文件**:
|
||||
- `mobile/src/static/tabbar/workbench.svg` - 办公中心图标
|
||||
- `mobile/src/static/tabbar/workbench-active.svg` - 办公中心激活图标
|
||||
- `mobile/src/static/tabbar/profile.svg` - 我的页面图标
|
||||
- `mobile/src/static/tabbar/profile-active.svg` - 我的页面激活图标
|
||||
|
||||
### 4. 工具类文件更新
|
||||
- **文件**: `mobile/src/utils/gestures.js`
|
||||
- **修改内容**:
|
||||
- 更新PageSwipeGesture类,移除workbench和profile页面引用
|
||||
- 简化页面滑动功能,适配单页面模式
|
||||
|
||||
- **文件**: `mobile/src/utils/user.js`
|
||||
- **修改内容**: 从公共页面列表中移除profile页面
|
||||
|
||||
- **文件**: `mobile/src/utils/accessibility.js`
|
||||
- **修改内容**: 移除workbench和profile页面的标题配置
|
||||
|
||||
### 5. 首页功能调整
|
||||
- **文件**: `mobile/src/pages/index/index.vue`
|
||||
- **修改内容**:
|
||||
- 修改用户头像点击事件,从跳转profile页面改为显示toast提示
|
||||
- 保持退出登录功能正常
|
||||
|
||||
## 技术影响
|
||||
|
||||
### 正面影响
|
||||
1. **应用结构简化**: 移除了底部导航栏,界面更加简洁
|
||||
2. **用户体验优化**: 专注于单一首页功能,减少页面跳转复杂度
|
||||
3. **代码维护**: 减少了页面维护工作量
|
||||
|
||||
### 需要关注的点
|
||||
1. **用户信息查看**: 原profile页面功能需要通过其他方式实现(如头部用户信息显示)
|
||||
2. **办公功能**: 原workbench页面的功能已整合到首页各模块中
|
||||
3. **导航模式**: 从tabBar模式改为普通页面导航模式
|
||||
|
||||
## 测试建议
|
||||
1. 验证首页各功能模块正常显示和权限控制
|
||||
2. 确认用户登录状态和退出功能正常
|
||||
3. 检查页面跳转和路由功能
|
||||
4. 验证各业务模块的访问入口
|
||||
|
||||
## 后续优化建议
|
||||
1. 可考虑在首页头部增加更多用户信息展示
|
||||
2. 优化首页布局,突出常用功能
|
||||
3. 考虑添加搜索功能来快速定位服务
|
||||
|
||||
---
|
||||
**修改时间**: 2025-01-27 23:45:00
|
||||
**修改人员**: Cursor AI
|
||||
**测试状态**: 已完成基础验证
|
||||
**上线状态**: 待测试
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
# 接口加密功能开关(如需关闭 后端也必须对应关闭)
|
||||
VITE_APP_ENCRYPT = true
|
||||
# 接口加密传输 RSA 公钥与后端解密私钥对应 如更换需前后端一同更换
|
||||
VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
|
||||
# 接口响应解密 RSA 私钥与后端加密公钥对应 如更换需前后端一同更换
|
||||
VITE_APP_RSA_PRIVATE_KEY = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE='
|
||||
@@ -0,0 +1,348 @@
|
||||
# 📚 图标管理系统使用指南
|
||||
|
||||
## 🎯 概述
|
||||
|
||||
项目采用了 **双图标系统**:
|
||||
- **iconfont 自定义图标** (推荐) - 基于 SVG data URI 的轻量级图标系统
|
||||
- **uview-plus 图标** (辅助) - UI库自带图标,部分图标存在显示问题
|
||||
|
||||
## 📁 文件结构
|
||||
|
||||
```
|
||||
mobile/src/static/fonts/
|
||||
├── iconfont.css # 主图标文件(SVG data URI)
|
||||
├── iconfont.ttf # 字体文件(可选)
|
||||
└── iconfont.woff # 字体文件(可选)
|
||||
|
||||
mobile/src/
|
||||
├── main.js # 引入图标CSS
|
||||
├── pages/iconfont-demo/ # 图标演示页面
|
||||
└── pages/uview-icon-test/ # uview图标问题测试页面
|
||||
```
|
||||
|
||||
## 🚀 添加新图标 - 超级简单!
|
||||
|
||||
### 方法一:SVG Data URI(推荐)
|
||||
|
||||
**优势**:
|
||||
- ✅ 无需下载字体文件
|
||||
- ✅ 支持彩色图标
|
||||
- ✅ 完美适配所有平台
|
||||
- ✅ 加载速度最快
|
||||
|
||||
**操作步骤**:
|
||||
|
||||
#### 1. 从 iconfont.cn 下载 SVG
|
||||
1. 访问 [iconfont.cn](https://www.iconfont.cn/)
|
||||
2. 搜索需要的图标
|
||||
3. 下载 SVG 格式
|
||||
|
||||
#### 2. 转换为 data URI
|
||||
使用在线工具或以下代码:
|
||||
|
||||
```javascript
|
||||
// SVG 转 data URI 工具函数
|
||||
function svgToDataUri(svgContent) {
|
||||
const encoded = encodeURIComponent(svgContent)
|
||||
.replace(/'/g, '%27')
|
||||
.replace(/"/g, '%22');
|
||||
return `data:image/svg+xml;charset=utf8,${encoded}`;
|
||||
}
|
||||
|
||||
// 示例
|
||||
const svgContent = `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'>
|
||||
<path d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z'/>
|
||||
</svg>`;
|
||||
```
|
||||
|
||||
#### 3. 添加到 CSS 文件
|
||||
在 `mobile/src/static/fonts/iconfont.css` 中添加:
|
||||
|
||||
```css
|
||||
/* 新增图标示例 */
|
||||
.icon-add-circle {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z'/%3E%3C/svg%3E");
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. 在页面中使用
|
||||
```vue
|
||||
<template>
|
||||
<text class="iconfont icon-add-circle" style="font-size: 24px; color: #1890ff;"></text>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 方法二:iconfont.cn 项目同步(适合大量图标)
|
||||
|
||||
#### 1. 创建 iconfont 项目
|
||||
1. 在 iconfont.cn 注册账号
|
||||
2. 创建新项目
|
||||
3. 添加需要的图标到项目
|
||||
|
||||
#### 2. 下载代码
|
||||
1. 点击"下载至本地"
|
||||
2. 选择 "Font class" 或 "Unicode" 方式
|
||||
3. 下载解压得到文件
|
||||
|
||||
#### 3. 替换项目文件
|
||||
```bash
|
||||
# 将下载的文件复制到项目
|
||||
cp iconfont.css mobile/src/static/fonts/
|
||||
cp iconfont.ttf mobile/src/static/fonts/
|
||||
cp iconfont.woff mobile/src/static/fonts/
|
||||
```
|
||||
|
||||
#### 4. 更新字体引用(如需要)
|
||||
在 `uni.scss` 或相关文件中更新字体路径:
|
||||
|
||||
```scss
|
||||
@font-face {
|
||||
font-family: 'iconfont';
|
||||
src: url('./static/fonts/iconfont.woff') format('woff'),
|
||||
url('./static/fonts/iconfont.ttf') format('truetype');
|
||||
}
|
||||
```
|
||||
|
||||
## 🎨 使用规范
|
||||
|
||||
### 基础用法
|
||||
```vue
|
||||
<template>
|
||||
<!-- 方式1:直接使用class -->
|
||||
<text class="iconfont icon-user"></text>
|
||||
|
||||
<!-- 方式2:自定义样式 -->
|
||||
<text class="iconfont icon-setting custom-icon"></text>
|
||||
|
||||
<!-- 方式3:动态样式 -->
|
||||
<text
|
||||
class="iconfont icon-home"
|
||||
:style="{
|
||||
fontSize: '24px',
|
||||
color: iconColor
|
||||
}"
|
||||
></text>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.custom-icon {
|
||||
font-size: 20px;
|
||||
color: #52c41a;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
### 响应式尺寸
|
||||
```vue
|
||||
<template>
|
||||
<!-- 小图标 -->
|
||||
<text class="iconfont icon-user icon-sm"></text>
|
||||
|
||||
<!-- 中等图标 -->
|
||||
<text class="iconfont icon-user icon-md"></text>
|
||||
|
||||
<!-- 大图标 -->
|
||||
<text class="iconfont icon-user icon-lg"></text>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.icon-sm { width: 16px; height: 16px; font-size: 16px; }
|
||||
.icon-md { width: 24px; height: 24px; font-size: 24px; }
|
||||
.icon-lg { width: 32px; height: 32px; font-size: 32px; }
|
||||
</style>
|
||||
```
|
||||
|
||||
## 📋 当前可用图标列表
|
||||
|
||||
### 基础图标
|
||||
- `icon-user` - 用户
|
||||
- `icon-lock` - 锁定
|
||||
- `icon-eye` - 显示
|
||||
- `icon-eye-close` - 隐藏
|
||||
- `icon-home` - 首页
|
||||
- `icon-setting` - 设置
|
||||
|
||||
### 操作图标
|
||||
- `icon-add` - 添加
|
||||
- `icon-close` - 关闭
|
||||
- `icon-check` - 确认
|
||||
- `icon-search` - 搜索
|
||||
- `icon-arrow-right` - 右箭头
|
||||
|
||||
### 功能图标
|
||||
- `icon-message` - 消息
|
||||
- `icon-phone` - 电话
|
||||
- `icon-mail` - 邮件
|
||||
- `icon-login` - 登录
|
||||
|
||||
## 🔧 管理工具
|
||||
|
||||
### 在线 SVG 编辑器
|
||||
- [SVG Editor](https://svg-edit.github.io/svgedit/)
|
||||
- [Boxy SVG](https://boxy-svg.com/)
|
||||
|
||||
### Data URI 转换工具
|
||||
```javascript
|
||||
// 工具函数:SVG 转 data URI
|
||||
function createSvgDataUri(svgPath, viewBox = "0 0 24 24") {
|
||||
const svg = `<svg xmlns='http://www.w3.org/2000/svg' viewBox='${viewBox}' fill='currentColor'><path d='${svgPath}'/></svg>`;
|
||||
return `url("data:image/svg+xml;charset=utf8,${encodeURIComponent(svg)}")`;
|
||||
}
|
||||
|
||||
// 使用示例
|
||||
const newIconCSS = `
|
||||
.icon-new-feature {
|
||||
background-image: ${createSvgDataUri("M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10...")};
|
||||
}
|
||||
`;
|
||||
```
|
||||
|
||||
### 批量添加脚本
|
||||
```javascript
|
||||
// 批量生成图标CSS的脚本
|
||||
const icons = [
|
||||
{ name: 'star', path: 'M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z' },
|
||||
{ name: 'heart', path: 'M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z' }
|
||||
];
|
||||
|
||||
function generateIconCSS(icons) {
|
||||
return icons.map(icon => `
|
||||
.icon-${icon.name} {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='${icon.path}'/%3E%3C/svg%3E");
|
||||
}`).join('\n');
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 最佳实践
|
||||
|
||||
### 1. 命名规范
|
||||
```css
|
||||
/* ✅ 推荐命名 */
|
||||
.icon-user /* 功能性图标 */
|
||||
.icon-user-add /* 带操作的图标 */
|
||||
.icon-arrow-right /* 方向性图标 */
|
||||
|
||||
/* ❌ 避免命名 */
|
||||
.icon-1 /* 无意义编号 */
|
||||
.icon-temp /* 临时命名 */
|
||||
```
|
||||
|
||||
### 2. 颜色管理
|
||||
```scss
|
||||
// 使用CSS变量管理图标颜色
|
||||
:root {
|
||||
--icon-primary: #1890ff;
|
||||
--icon-success: #52c41a;
|
||||
--icon-warning: #faad14;
|
||||
--icon-error: #ff4d4f;
|
||||
}
|
||||
|
||||
.icon-primary { color: var(--icon-primary); }
|
||||
.icon-success { color: var(--icon-success); }
|
||||
.icon-warning { color: var(--icon-warning); }
|
||||
.icon-error { color: var(--icon-error); }
|
||||
```
|
||||
|
||||
### 3. 尺寸规范
|
||||
```scss
|
||||
// 标准尺寸系统
|
||||
.icon-xs { width: 12px; height: 12px; } // 超小
|
||||
.icon-sm { width: 16px; height: 16px; } // 小
|
||||
.icon-md { width: 24px; height: 24px; } // 中(默认)
|
||||
.icon-lg { width: 32px; height: 32px; } // 大
|
||||
.icon-xl { width: 48px; height: 48px; } // 超大
|
||||
```
|
||||
|
||||
## 🔄 迁移指南
|
||||
|
||||
### 从 uview 图标迁移到 iconfont
|
||||
|
||||
```vue
|
||||
<!-- 迁移前 -->
|
||||
<u-icon name="logout" size="20" color="#fff"></u-icon>
|
||||
|
||||
<!-- 迁移后 -->
|
||||
<text class="iconfont icon-close" style="font-size: 20px; color: #fff;"></text>
|
||||
```
|
||||
|
||||
### 批量替换工具
|
||||
```bash
|
||||
# 使用正则表达式批量替换
|
||||
# 将 <u-icon name="logout" 替换为 <text class="iconfont icon-close"
|
||||
find ./src -name "*.vue" -exec sed -i 's/<u-icon name="logout"/<text class="iconfont icon-close"/g' {} \;
|
||||
```
|
||||
|
||||
## 🚀 性能优化
|
||||
|
||||
### 1. 按需加载
|
||||
```javascript
|
||||
// 动态导入图标CSS(大型应用)
|
||||
const loadIconFont = async () => {
|
||||
if (!document.querySelector('#iconfont-css')) {
|
||||
const link = document.createElement('link');
|
||||
link.id = 'iconfont-css';
|
||||
link.rel = 'stylesheet';
|
||||
link.href = './static/fonts/iconfont.css';
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 2. CDN 优化
|
||||
```css
|
||||
/* 使用CDN加载字体文件 */
|
||||
@font-face {
|
||||
font-family: 'iconfont';
|
||||
src: url('https://cdn.example.com/fonts/iconfont.woff') format('woff');
|
||||
}
|
||||
```
|
||||
|
||||
## 🛠️ 开发工具
|
||||
|
||||
### VS Code 插件推荐
|
||||
- **SVG Viewer** - 预览SVG文件
|
||||
- **CSS Peek** - 快速查看CSS定义
|
||||
- **IconFont Helper** - iconfont图标提示
|
||||
|
||||
### 图标搜索工具
|
||||
- [Iconify](https://iconify.design/) - 100,000+ 开源图标
|
||||
- [Feather Icons](https://feathericons.com/) - 简洁线性图标
|
||||
- [Heroicons](https://heroicons.com/) - Tailwind CSS图标
|
||||
|
||||
## ❓ 常见问题
|
||||
|
||||
### Q: 图标不显示怎么办?
|
||||
A: 检查以下几点:
|
||||
1. CSS文件是否正确引入
|
||||
2. 类名是否拼写正确
|
||||
3. 字体文件路径是否正确
|
||||
4. 浏览器是否支持data URI
|
||||
|
||||
### Q: 如何修改图标颜色?
|
||||
A: 使用 `color` 或 `fill` 属性:
|
||||
```css
|
||||
.my-icon {
|
||||
color: #1890ff; /* 对于SVG data URI */
|
||||
fill: #1890ff; /* 对于字体图标 */
|
||||
}
|
||||
```
|
||||
|
||||
### Q: 图标模糊怎么解决?
|
||||
A: 确保图标尺寸为偶数像素:
|
||||
```css
|
||||
.icon-clear {
|
||||
width: 24px; /* ✅ 偶数 */
|
||||
height: 24px; /* ✅ 偶数 */
|
||||
}
|
||||
```
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
- **项目位置**: `mobile/src/static/fonts/`
|
||||
- **演示页面**: `/pages/iconfont-demo/index`
|
||||
- **测试页面**: `/pages/uview-icon-test/index`
|
||||
- **文档更新**: 2025-01-07
|
||||
|
||||
---
|
||||
|
||||
**✨ 总结**: iconfont 图标系统让您轻松管理图标,添加新图标只需要复制粘贴几行CSS代码!推荐使用 SVG data URI 方式,兼容性最好,性能最优。
|
||||
@@ -25,9 +25,9 @@ module.exports = [
|
||||
'no-console': 'warn',
|
||||
'no-debugger': 'warn',
|
||||
'no-unused-vars': 'warn',
|
||||
indent: ['error', 2],
|
||||
// indent: ['error', 2],
|
||||
quotes: ['error', 'single'],
|
||||
semi: ['error', 'never'],
|
||||
// semi: ['error', 'never'],
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -7,8 +7,9 @@
|
||||
tabindex="0"
|
||||
>
|
||||
<view class="service-icon">
|
||||
<text class="icon" v-if="icon">{{ icon }}</text>
|
||||
<slot v-else name="icon"></slot>
|
||||
<slot name="icon">
|
||||
<text class="icon" v-if="icon">{{ icon }}</text>
|
||||
</slot>
|
||||
</view>
|
||||
<text class="service-title">{{ title }}</text>
|
||||
<view v-if="badge" class="service-badge">
|
||||
@@ -82,12 +83,33 @@ export default {
|
||||
.icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
// 支持u-icon组件
|
||||
:deep(.u-icon) {
|
||||
font-size: 24px !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
// 支持iconfont图标
|
||||
.iconfont {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font-size: 24px;
|
||||
color: inherit;
|
||||
filter: brightness(0) saturate(100%) invert(49%) sepia(98%) saturate(3332%)
|
||||
hue-rotate(196deg) brightness(104%) contrast(101%);
|
||||
}
|
||||
}
|
||||
|
||||
.service-item:hover .service-icon {
|
||||
background: $uni-color-primary;
|
||||
color: $uni-text-color-inverse;
|
||||
transform: scale(1.1);
|
||||
|
||||
.iconfont {
|
||||
filter: brightness(0) saturate(100%) invert(100%) sepia(0%) saturate(0%) hue-rotate(0deg)
|
||||
brightness(100%) contrast(100%);
|
||||
}
|
||||
}
|
||||
|
||||
.service-title {
|
||||
@@ -160,6 +182,18 @@ export default {
|
||||
.icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
// 响应式u-icon支持
|
||||
:deep(.u-icon) {
|
||||
font-size: 20px !important;
|
||||
}
|
||||
|
||||
// 响应式iconfont支持
|
||||
.iconfont {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.service-title {
|
||||
|
||||
@@ -24,20 +24,6 @@
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/workbench/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "办公中心",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/profile/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/iconfont-demo/index",
|
||||
"style": {
|
||||
@@ -66,6 +52,14 @@
|
||||
"navigationBarTitleText": "我发起的流程",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/uview-icon-test/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "uview图标测试",
|
||||
"navigationBarBackgroundColor": "#5d87ff",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
@@ -73,35 +67,5 @@
|
||||
"navigationBarTitleText": "智慧校园",
|
||||
"navigationBarBackgroundColor": "rgb(93, 135, 255)",
|
||||
"backgroundColor": "#f8f9fa"
|
||||
},
|
||||
"tabBar": {
|
||||
"color": "#999999",
|
||||
"selectedColor": "rgb(93, 135, 255)",
|
||||
"borderStyle": "white",
|
||||
"backgroundColor": "#ffffff",
|
||||
"height": "60px",
|
||||
"fontSize": "12px",
|
||||
"iconWidth": "22px",
|
||||
"iconHeight": "22px",
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/index/index",
|
||||
"text": "首页",
|
||||
"iconPath": "static/tabbar/home.svg",
|
||||
"selectedIconPath": "static/tabbar/home-active.svg"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/workbench/index",
|
||||
"text": "办公中心",
|
||||
"iconPath": "static/tabbar/workbench.svg",
|
||||
"selectedIconPath": "static/tabbar/workbench-active.svg"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/profile/index",
|
||||
"text": "我的",
|
||||
"iconPath": "static/tabbar/profile.svg",
|
||||
"selectedIconPath": "static/tabbar/profile-active.svg"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -38,9 +38,19 @@
|
||||
<text class="section-title">操作图标</text>
|
||||
<view class="icon-grid">
|
||||
<view class="icon-item">
|
||||
<text class="iconfont icon-login"></text>
|
||||
<text class="icon-name">登录</text>
|
||||
<text class="icon-code">icon-login</text>
|
||||
<text class="iconfont icon-add"></text>
|
||||
<text class="icon-name">添加</text>
|
||||
<text class="icon-code">icon-add</text>
|
||||
</view>
|
||||
<view class="icon-item">
|
||||
<text class="iconfont icon-edit"></text>
|
||||
<text class="icon-name">编辑</text>
|
||||
<text class="icon-code">icon-edit</text>
|
||||
</view>
|
||||
<view class="icon-item">
|
||||
<text class="iconfont icon-delete"></text>
|
||||
<text class="icon-name">删除</text>
|
||||
<text class="icon-code">icon-delete</text>
|
||||
</view>
|
||||
<view class="icon-item">
|
||||
<text class="iconfont icon-search"></text>
|
||||
@@ -48,9 +58,9 @@
|
||||
<text class="icon-code">icon-search</text>
|
||||
</view>
|
||||
<view class="icon-item">
|
||||
<text class="iconfont icon-add"></text>
|
||||
<text class="icon-name">添加</text>
|
||||
<text class="icon-code">icon-add</text>
|
||||
<text class="iconfont icon-refresh"></text>
|
||||
<text class="icon-name">刷新</text>
|
||||
<text class="icon-code">icon-refresh</text>
|
||||
</view>
|
||||
<view class="icon-item">
|
||||
<text class="iconfont icon-close"></text>
|
||||
@@ -60,6 +70,82 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 文件图标 -->
|
||||
<view class="icon-section">
|
||||
<text class="section-title">文件图标</text>
|
||||
<view class="icon-grid">
|
||||
<view class="icon-item">
|
||||
<text class="iconfont icon-download"></text>
|
||||
<text class="icon-name">下载</text>
|
||||
<text class="icon-code">icon-download</text>
|
||||
</view>
|
||||
<view class="icon-item">
|
||||
<text class="iconfont icon-upload"></text>
|
||||
<text class="icon-name">上传</text>
|
||||
<text class="icon-code">icon-upload</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 状态图标 -->
|
||||
<view class="icon-section">
|
||||
<text class="section-title">状态图标</text>
|
||||
<view class="icon-grid">
|
||||
<view class="icon-item">
|
||||
<text class="iconfont icon-success" style="color: #52c41a"></text>
|
||||
<text class="icon-name">成功</text>
|
||||
<text class="icon-code">icon-success</text>
|
||||
</view>
|
||||
<view class="icon-item">
|
||||
<text class="iconfont icon-warning" style="color: #faad14"></text>
|
||||
<text class="icon-name">警告</text>
|
||||
<text class="icon-code">icon-warning</text>
|
||||
</view>
|
||||
<view class="icon-item">
|
||||
<text class="iconfont icon-info" style="color: #1890ff"></text>
|
||||
<text class="icon-name">信息</text>
|
||||
<text class="icon-code">icon-info</text>
|
||||
</view>
|
||||
<view class="icon-item">
|
||||
<text class="iconfont icon-check" style="color: #52c41a"></text>
|
||||
<text class="icon-name">确认</text>
|
||||
<text class="icon-code">icon-check</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 社交图标 -->
|
||||
<view class="icon-section">
|
||||
<text class="section-title">社交图标</text>
|
||||
<view class="icon-grid">
|
||||
<view class="icon-item">
|
||||
<text class="iconfont icon-star" style="color: #faad14"></text>
|
||||
<text class="icon-name">星星</text>
|
||||
<text class="icon-code">icon-star</text>
|
||||
</view>
|
||||
<view class="icon-item">
|
||||
<text class="iconfont icon-heart" style="color: #ff4d4f"></text>
|
||||
<text class="icon-name">喜欢</text>
|
||||
<text class="icon-code">icon-heart</text>
|
||||
</view>
|
||||
<view class="icon-item">
|
||||
<text class="iconfont icon-message"></text>
|
||||
<text class="icon-name">消息</text>
|
||||
<text class="icon-code">icon-message</text>
|
||||
</view>
|
||||
<view class="icon-item">
|
||||
<text class="iconfont icon-phone"></text>
|
||||
<text class="icon-name">电话</text>
|
||||
<text class="icon-code">icon-phone</text>
|
||||
</view>
|
||||
<view class="icon-item">
|
||||
<text class="iconfont icon-mail"></text>
|
||||
<text class="icon-name">邮件</text>
|
||||
<text class="icon-code">icon-mail</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 导航图标 -->
|
||||
<view class="icon-section">
|
||||
<text class="section-title">导航图标</text>
|
||||
@@ -0,0 +1,209 @@
|
||||
<template>
|
||||
<view class="test-page">
|
||||
<view class="test-header">
|
||||
<text class="test-title">uview-plus 图标问题分析与解决</text>
|
||||
</view>
|
||||
|
||||
<!-- 问题演示 -->
|
||||
<view class="test-section">
|
||||
<text class="section-title">❌ 问题演示 - uview-plus图标异常</text>
|
||||
<view class="test-items">
|
||||
<view class="test-item">
|
||||
<u-icon name="logout" size="24" color="#ff4d4f"></u-icon>
|
||||
<text class="test-desc">logout图标 (可能显示文字)</text>
|
||||
</view>
|
||||
<view class="test-item">
|
||||
<u-icon name="power" size="24" color="#ff4d4f"></u-icon>
|
||||
<text class="test-desc">power图标 (可能显示文字)</text>
|
||||
</view>
|
||||
<view class="test-item">
|
||||
<u-icon name="exit" size="24" color="#ff4d4f"></u-icon>
|
||||
<text class="test-desc">exit图标 (可能显示文字)</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 正常工作的uview图标 -->
|
||||
<view class="test-section">
|
||||
<text class="section-title">✅ 正常工作 - uview-plus图标</text>
|
||||
<view class="test-items">
|
||||
<view class="test-item">
|
||||
<u-icon name="account" size="24" color="#52c41a"></u-icon>
|
||||
<text class="test-desc">account图标 (正常)</text>
|
||||
</view>
|
||||
<view class="test-item">
|
||||
<u-icon name="home" size="24" color="#52c41a"></u-icon>
|
||||
<text class="test-desc">home图标 (正常)</text>
|
||||
</view>
|
||||
<view class="test-item">
|
||||
<u-icon name="setting" size="24" color="#52c41a"></u-icon>
|
||||
<text class="test-desc">setting图标 (正常)</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 解决方案 - 使用iconfont -->
|
||||
<view class="test-section">
|
||||
<text class="section-title">🎯 解决方案 - 使用iconfont自定义图标</text>
|
||||
<view class="test-items">
|
||||
<view class="test-item">
|
||||
<text class="iconfont icon-close solution-icon"></text>
|
||||
<text class="test-desc">关闭图标 (退出登录)</text>
|
||||
</view>
|
||||
<view class="test-item">
|
||||
<text class="iconfont icon-user solution-icon"></text>
|
||||
<text class="test-desc">用户图标</text>
|
||||
</view>
|
||||
<view class="test-item">
|
||||
<text class="iconfont icon-setting solution-icon"></text>
|
||||
<text class="test-desc">设置图标</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Unicode字符方案 -->
|
||||
<view class="test-section">
|
||||
<text class="section-title">🔤 备选方案 - Unicode字符</text>
|
||||
<view class="test-items">
|
||||
<view class="test-item">
|
||||
<text class="unicode-icon">⏻</text>
|
||||
<text class="test-desc">电源符号 (退出登录)</text>
|
||||
</view>
|
||||
<view class="test-item">
|
||||
<text class="unicode-icon">❌</text>
|
||||
<text class="test-desc">关闭符号</text>
|
||||
</view>
|
||||
<view class="test-item">
|
||||
<text class="unicode-icon">⚙️</text>
|
||||
<text class="test-desc">设置符号</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 解决方案说明 -->
|
||||
<view class="solution-section">
|
||||
<text class="section-title">💡 解决方案说明</text>
|
||||
<view class="solution-content">
|
||||
<text class="solution-text">
|
||||
1. **根本原因**: uview-plus某些图标名称在当前版本字体文件中不存在
|
||||
</text>
|
||||
<text class="solution-text">
|
||||
2. **推荐方案**: 使用项目中的iconfont自定义图标系统
|
||||
</text>
|
||||
<text class="solution-text">
|
||||
3. **备选方案**: 使用Unicode字符(⏻ ❌ ⚙️),兼容性最好
|
||||
</text>
|
||||
<text class="solution-text">
|
||||
4. **长期方案**: 更新uview-plus字体文件或使用其他图标库
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'UviewIconTest',
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.test-page {
|
||||
min-height: 100vh;
|
||||
background: #f8f9fa;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.test-header {
|
||||
text-align: center;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.test-title {
|
||||
display: block;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.test-section {
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.test-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.test-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px;
|
||||
background: #fafbfc;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.test-desc {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.solution-icon {
|
||||
font-size: 24px;
|
||||
color: #1890ff;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.unicode-icon {
|
||||
font-size: 24px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.solution-section {
|
||||
background: #fff7e6;
|
||||
border: 1px solid #ffd591;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.solution-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.solution-text {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
@@ -19,7 +19,7 @@
|
||||
v-model="username"
|
||||
placeholder="账号"
|
||||
type="text"
|
||||
:border="true"
|
||||
border="true"
|
||||
:custom-style="inputStyle"
|
||||
placeholder-style="color: #999; font-size: 16px"
|
||||
>
|
||||
@@ -34,7 +34,7 @@
|
||||
v-model="password"
|
||||
placeholder="密码"
|
||||
type="password"
|
||||
:border="true"
|
||||
border="true"
|
||||
:custom-style="inputStyle"
|
||||
placeholder-style="color: #999; font-size: 16px"
|
||||
@confirm="onPasswordConfirm"
|
||||
@@ -187,7 +187,7 @@ export default {
|
||||
|
||||
// 调用登录API
|
||||
const loginResult = await api.login(loginData);
|
||||
|
||||
console.log('登录结果:', loginResult);
|
||||
if (loginResult.code === 200) {
|
||||
// 登录成功,先保存Token
|
||||
console.log('✅ 登录成功,先保存Token');
|
||||
|
||||
@@ -1,674 +0,0 @@
|
||||
<template>
|
||||
<view class="page-container">
|
||||
<!-- 自定义导航栏 -->
|
||||
<NavBar title="我的" :show-settings="true" @settings="handleSettings" />
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<scroll-view class="main-content" scroll-y enhanced :show-scrollbar="false">
|
||||
<!-- 用户信息卡片 -->
|
||||
<view class="profile-card">
|
||||
<view class="profile-info">
|
||||
<view class="profile-avatar">
|
||||
<text class="avatar-icon">👤</text>
|
||||
</view>
|
||||
<view class="profile-details">
|
||||
<text class="user-name">{{ userInfo.name }}</text>
|
||||
<text class="user-major">{{ userInfo.major }} · {{ userInfo.grade }}</text>
|
||||
<text class="user-id">学号:{{ userInfo.studentId }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="profile-stats">
|
||||
<view class="stat-item">
|
||||
<text class="stat-number">{{ userInfo.gpa }}</text>
|
||||
<text class="stat-label">GPA</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-number">{{ userInfo.credits }}</text>
|
||||
<text class="stat-label">学分</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-number">{{ userInfo.grade }}</text>
|
||||
<text class="stat-label">年级</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-number">{{ userInfo.ranking }}</text>
|
||||
<text class="stat-label">排名</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 学习状态 -->
|
||||
<Card>
|
||||
<template #header>
|
||||
<view class="section-header">
|
||||
<text class="section-icon">📊</text>
|
||||
<text class="card-title">学习状态</text>
|
||||
</view>
|
||||
</template>
|
||||
<GridLayout :columns="2" gap="12px">
|
||||
<StatCard
|
||||
v-for="stat in learningStats"
|
||||
:key="stat.label"
|
||||
:number="stat.number"
|
||||
:label="stat.label"
|
||||
@click="handleStatClick"
|
||||
/>
|
||||
</GridLayout>
|
||||
</Card>
|
||||
|
||||
<!-- 我的服务 -->
|
||||
<view class="menu-section">
|
||||
<view
|
||||
v-for="menu in myServiceMenus"
|
||||
:key="menu.title"
|
||||
class="menu-item touchable"
|
||||
@click="handleMenuClick(menu)"
|
||||
>
|
||||
<view class="menu-icon">
|
||||
<text class="icon">{{ menu.icon }}</text>
|
||||
</view>
|
||||
<view class="menu-content">
|
||||
<text class="menu-title">{{ menu.title }}</text>
|
||||
<text class="menu-desc">{{ menu.description }}</text>
|
||||
</view>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section-divider"></view>
|
||||
|
||||
<!-- 校园生活 -->
|
||||
<view class="menu-section">
|
||||
<view
|
||||
v-for="menu in campusLifeMenus"
|
||||
:key="menu.title"
|
||||
class="menu-item touchable"
|
||||
@click="handleMenuClick(menu)"
|
||||
>
|
||||
<view class="menu-icon">
|
||||
<text class="icon">{{ menu.icon }}</text>
|
||||
</view>
|
||||
<view class="menu-content">
|
||||
<text class="menu-title">{{ menu.title }}</text>
|
||||
<text class="menu-desc">{{ menu.description }}</text>
|
||||
</view>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section-divider"></view>
|
||||
|
||||
<!-- 应用设置 -->
|
||||
<view class="menu-section">
|
||||
<view
|
||||
v-for="menu in settingsMenus"
|
||||
:key="menu.title"
|
||||
class="menu-item touchable"
|
||||
@click="handleMenuClick(menu)"
|
||||
>
|
||||
<view class="menu-icon">
|
||||
<text class="icon">{{ menu.icon }}</text>
|
||||
</view>
|
||||
<view class="menu-content">
|
||||
<text class="menu-title">{{ menu.title }}</text>
|
||||
<text class="menu-desc">{{ menu.description }}</text>
|
||||
</view>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section-divider"></view>
|
||||
|
||||
<!-- 帮助与反馈 -->
|
||||
<view class="menu-section">
|
||||
<view
|
||||
v-for="menu in helpMenus"
|
||||
:key="menu.title"
|
||||
class="menu-item touchable"
|
||||
@click="handleMenuClick(menu)"
|
||||
>
|
||||
<view class="menu-icon">
|
||||
<text class="icon">{{ menu.icon }}</text>
|
||||
</view>
|
||||
<view class="menu-content">
|
||||
<text class="menu-title">{{ menu.title }}</text>
|
||||
<text class="menu-desc">{{ menu.description }}</text>
|
||||
</view>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 版权信息 -->
|
||||
<view class="footer-section">
|
||||
<text class="app-version">智慧校园 v2.1.0</text>
|
||||
<text class="copyright">© 2024 University. All rights reserved.</text>
|
||||
</view>
|
||||
|
||||
<!-- 底部间距 -->
|
||||
<view class="bottom-spacer"></view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NavBar from '@/components/NavBar.vue';
|
||||
import Card from '@/components/Card.vue';
|
||||
import StatCard from '@/components/StatCard.vue';
|
||||
import GridLayout from '@/components/GridLayout.vue';
|
||||
import { userStorage } from '@/utils/request';
|
||||
import { userManager, userState } from '@/utils/user';
|
||||
import { checkPageAccess } from '@/utils/route-guard';
|
||||
|
||||
export default {
|
||||
name: 'ProfilePage',
|
||||
components: {
|
||||
NavBar,
|
||||
Card,
|
||||
StatCard,
|
||||
GridLayout,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
userInfo: {
|
||||
name: '用户',
|
||||
major: '未设置',
|
||||
grade: '未设置',
|
||||
studentId: '未设置',
|
||||
gpa: 0,
|
||||
credits: 0,
|
||||
ranking: 0,
|
||||
},
|
||||
learningStats: [
|
||||
{ number: 5, label: '本周课程' },
|
||||
{ number: 3, label: '待提交作业' },
|
||||
{ number: 2, label: '即将考试' },
|
||||
{ number: 8, label: '借阅图书' },
|
||||
],
|
||||
myServiceMenus: [
|
||||
{
|
||||
title: '我的课表',
|
||||
description: '查看本学期课程安排',
|
||||
icon: '🎓',
|
||||
action: 'schedule',
|
||||
},
|
||||
{
|
||||
title: '成绩单',
|
||||
description: '查看历史成绩记录',
|
||||
icon: '📊',
|
||||
action: 'grades',
|
||||
},
|
||||
{
|
||||
title: '我的待办',
|
||||
description: '管理学习任务和事项',
|
||||
icon: '✅',
|
||||
action: 'todos',
|
||||
},
|
||||
{
|
||||
title: '校园卡',
|
||||
description: '余额:¥128.50',
|
||||
icon: '💳',
|
||||
action: 'card',
|
||||
},
|
||||
],
|
||||
campusLifeMenus: [
|
||||
{
|
||||
title: '图书借阅',
|
||||
description: '当前借阅8本,2本即将到期',
|
||||
icon: '📚',
|
||||
action: 'library',
|
||||
},
|
||||
{
|
||||
title: '宿舍信息',
|
||||
description: '1号楼 308室',
|
||||
icon: '🛏️',
|
||||
action: 'dormitory',
|
||||
},
|
||||
{
|
||||
title: '社团活动',
|
||||
description: '计算机协会、摄影社',
|
||||
icon: '👥',
|
||||
action: 'clubs',
|
||||
},
|
||||
{
|
||||
title: '活动报名',
|
||||
description: '查看可参与的活动',
|
||||
icon: '📅',
|
||||
action: 'events',
|
||||
},
|
||||
],
|
||||
settingsMenus: [
|
||||
{
|
||||
title: '个人信息',
|
||||
description: '编辑个人资料',
|
||||
icon: '👤',
|
||||
action: 'profile',
|
||||
},
|
||||
{
|
||||
title: '消息通知',
|
||||
description: '管理通知设置',
|
||||
icon: '🔔',
|
||||
action: 'notifications',
|
||||
},
|
||||
{
|
||||
title: '账号安全',
|
||||
description: '密码、绑定手机等',
|
||||
icon: '🛡️',
|
||||
action: 'security',
|
||||
},
|
||||
{
|
||||
title: '主题设置',
|
||||
description: '切换应用主题',
|
||||
icon: '🎨',
|
||||
action: 'theme',
|
||||
},
|
||||
],
|
||||
helpMenus: [
|
||||
{
|
||||
title: '帮助中心',
|
||||
description: '常见问题解答',
|
||||
icon: '❓',
|
||||
action: 'help',
|
||||
},
|
||||
{
|
||||
title: '意见反馈',
|
||||
description: '提交建议和问题',
|
||||
icon: '💬',
|
||||
action: 'feedback',
|
||||
},
|
||||
{
|
||||
title: '关于应用',
|
||||
description: '版本 v2.1.0',
|
||||
icon: 'ℹ️',
|
||||
action: 'about',
|
||||
},
|
||||
{
|
||||
title: '退出登录',
|
||||
description: '安全退出当前账号',
|
||||
icon: '🚪',
|
||||
action: 'logout',
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
// 检查登录状态
|
||||
checkLoginStatus() {
|
||||
return userState.autoLoginCheck();
|
||||
},
|
||||
|
||||
// 加载用户信息
|
||||
loadUserInfo() {
|
||||
const displayUser = userManager.formatUserForDisplay();
|
||||
if (displayUser) {
|
||||
this.userInfo.name = displayUser.name;
|
||||
this.userInfo.avatar = displayUser.avatar;
|
||||
this.userInfo.department = displayUser.department;
|
||||
this.userInfo.phone = displayUser.phone;
|
||||
this.userInfo.email = displayUser.email;
|
||||
this.userInfo.studentId = userManager.getCurrentUser()?.userId || '未设置';
|
||||
}
|
||||
},
|
||||
|
||||
handleSettings() {
|
||||
uni.showToast({ title: '设置', icon: 'none' });
|
||||
},
|
||||
handleStatClick(stat) {
|
||||
uni.showToast({ title: `查看${stat.label}详情`, icon: 'none' });
|
||||
},
|
||||
handleMenuClick(menu) {
|
||||
uni.vibrateShort();
|
||||
|
||||
switch (menu.action) {
|
||||
case 'schedule':
|
||||
this.openSchedule();
|
||||
break;
|
||||
case 'grades':
|
||||
this.openGrades();
|
||||
break;
|
||||
case 'todos':
|
||||
this.openTodos();
|
||||
break;
|
||||
case 'card':
|
||||
this.openCampusCard();
|
||||
break;
|
||||
case 'library':
|
||||
this.openLibrary();
|
||||
break;
|
||||
case 'logout':
|
||||
this.handleLogout();
|
||||
break;
|
||||
default:
|
||||
uni.showToast({
|
||||
title: `${menu.title}功能开发中`,
|
||||
icon: 'none',
|
||||
});
|
||||
}
|
||||
},
|
||||
openSchedule() {
|
||||
uni.showToast({ title: '打开课程表', icon: 'success' });
|
||||
},
|
||||
openGrades() {
|
||||
uni.showToast({ title: '打开成绩单', icon: 'success' });
|
||||
},
|
||||
openTodos() {
|
||||
uni.showToast({ title: '打开待办事项', icon: 'success' });
|
||||
},
|
||||
openCampusCard() {
|
||||
uni.showToast({ title: '打开校园卡', icon: 'success' });
|
||||
},
|
||||
openLibrary() {
|
||||
uni.showToast({ title: '打开图书借阅', icon: 'success' });
|
||||
},
|
||||
handleLogout() {
|
||||
uni.showModal({
|
||||
title: '确认退出',
|
||||
content: '确定要退出登录吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showLoading({ title: '正在退出...' });
|
||||
|
||||
// 清除登录信息
|
||||
userStorage.clearLoginInfo();
|
||||
|
||||
setTimeout(() => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '已退出登录',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
// 跳转到登录页面
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({
|
||||
url: '/pages/login/index'
|
||||
});
|
||||
}, 500);
|
||||
}, 1000);
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
onLoad() {
|
||||
// 检查页面访问权限
|
||||
if (!checkPageAccess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.checkLoginStatus()) {
|
||||
this.loadUserInfo();
|
||||
}
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 页面显示时再次检查权限
|
||||
if (!checkPageAccess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 刷新用户信息
|
||||
this.loadUserInfo();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-container {
|
||||
min-height: 100vh;
|
||||
background-color: $uni-bg-color-grey;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding-bottom: calc(50px + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
/* 用户信息卡片 */
|
||||
.profile-card {
|
||||
background: linear-gradient(135deg, $uni-color-primary, $uni-color-primary-dark);
|
||||
color: $uni-text-color-inverse;
|
||||
margin: $uni-spacing-col-lg;
|
||||
border-radius: $uni-border-radius-lg;
|
||||
padding: 24px 20px;
|
||||
box-shadow: $uni-shadow;
|
||||
}
|
||||
|
||||
.profile-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: $uni-spacing-col-lg;
|
||||
}
|
||||
|
||||
.profile-avatar {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: $uni-border-radius-circle;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: $uni-spacing-row-lg;
|
||||
}
|
||||
|
||||
.avatar-icon {
|
||||
font-size: 24px;
|
||||
color: $uni-text-color-inverse;
|
||||
}
|
||||
|
||||
.profile-details {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: $uni-font-size-xl;
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.user-major {
|
||||
font-size: $uni-font-size-base;
|
||||
opacity: 0.9;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.user-id {
|
||||
font-size: $uni-font-size-sm;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.profile-stats {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: $uni-spacing-col-lg;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: $uni-font-size-xxl;
|
||||
font-weight: 600;
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: $uni-font-size-sm;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* 分组标题 */
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.section-icon {
|
||||
font-size: $uni-font-size-lg;
|
||||
color: $uni-color-primary;
|
||||
}
|
||||
|
||||
/* 菜单列表 */
|
||||
.menu-section {
|
||||
background: $uni-bg-color;
|
||||
border-radius: $uni-border-radius-lg;
|
||||
margin: $uni-spacing-col-lg;
|
||||
box-shadow: $uni-shadow;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: $uni-spacing-col-lg $uni-spacing-row-xl;
|
||||
transition: $uni-transition;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $uni-color-primary-light;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: $uni-color-primary-light;
|
||||
border-radius: $uni-border-radius-circle;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: $uni-spacing-row-lg;
|
||||
|
||||
.icon {
|
||||
font-size: 18px;
|
||||
color: $uni-color-primary;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
font-size: $uni-font-size-lg;
|
||||
font-weight: 500;
|
||||
color: $uni-text-color;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.menu-desc {
|
||||
font-size: $uni-font-size-sm;
|
||||
color: $uni-text-color-secondary;
|
||||
}
|
||||
|
||||
.menu-arrow {
|
||||
color: $uni-text-color-grey;
|
||||
font-size: $uni-font-size-xl;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
/* 分隔器 */
|
||||
.section-divider {
|
||||
height: 8px;
|
||||
background: $uni-bg-color-grey;
|
||||
margin: 0 $uni-spacing-row-lg;
|
||||
}
|
||||
|
||||
/* 版权信息 */
|
||||
.footer-section {
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
color: $uni-text-color-grey;
|
||||
}
|
||||
|
||||
.app-version {
|
||||
font-size: $uni-font-size-sm;
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* 底部间距 */
|
||||
.bottom-spacer {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
/* 菜单项进入动画 */
|
||||
.menu-item {
|
||||
animation: slideInRight 0.6s ease-out backwards;
|
||||
}
|
||||
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 页面进入动画 */
|
||||
.page-container {
|
||||
animation: fadeInUp 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 375px) {
|
||||
.profile-card,
|
||||
.menu-section {
|
||||
margin: $uni-spacing-col-base;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
padding: $uni-spacing-col-base $uni-spacing-row-lg;
|
||||
}
|
||||
|
||||
.profile-avatar {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
|
||||
.avatar-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: $uni-font-size-lg;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: $uni-font-size-xl;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,499 +0,0 @@
|
||||
<template>
|
||||
<view class="page-container">
|
||||
<!-- 自定义导航栏 -->
|
||||
<NavBar title="办公中心" :show-search="true" @search="handleSearch" />
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<scroll-view class="main-content" scroll-y enhanced :show-scrollbar="false">
|
||||
<!-- 搜索栏 -->
|
||||
<view class="search-section">
|
||||
<view class="search-bar" @click="focusSearch">
|
||||
<text class="search-icon">🔍</text>
|
||||
<input
|
||||
class="search-input"
|
||||
placeholder="搜索服务..."
|
||||
v-model="searchQuery"
|
||||
@input="handleSearchInput"
|
||||
@confirm="handleSearchConfirm"
|
||||
:focus="searchFocused"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 热门服务 -->
|
||||
<Card>
|
||||
<template #header>
|
||||
<view class="section-header">
|
||||
<text class="section-icon">🔥</text>
|
||||
<text class="card-title">热门服务</text>
|
||||
</view>
|
||||
</template>
|
||||
<GridLayout :columns="4" gap="12px">
|
||||
<ServiceItem
|
||||
v-for="service in hotServices"
|
||||
:key="service.title"
|
||||
:title="service.title"
|
||||
:icon="service.icon"
|
||||
:badge="service.badge"
|
||||
@click="handleServiceClick"
|
||||
/>
|
||||
</GridLayout>
|
||||
</Card>
|
||||
|
||||
<!-- 学习教务 -->
|
||||
<Card>
|
||||
<template #header>
|
||||
<view class="section-header">
|
||||
<text class="section-icon">🎓</text>
|
||||
<text class="card-title">学习教务</text>
|
||||
</view>
|
||||
</template>
|
||||
<GridLayout :columns="3" gap="12px">
|
||||
<ServiceItem
|
||||
v-for="service in academicServices"
|
||||
:key="service.title"
|
||||
:title="service.title"
|
||||
:icon="service.icon"
|
||||
@click="handleServiceClick"
|
||||
/>
|
||||
</GridLayout>
|
||||
</Card>
|
||||
|
||||
<!-- 生活服务 -->
|
||||
<Card>
|
||||
<template #header>
|
||||
<view class="section-header">
|
||||
<text class="section-icon">🏠</text>
|
||||
<text class="card-title">生活服务</text>
|
||||
</view>
|
||||
</template>
|
||||
<GridLayout :columns="3" gap="12px">
|
||||
<ServiceItem
|
||||
v-for="service in lifeServices"
|
||||
:key="service.title"
|
||||
:title="service.title"
|
||||
:icon="service.icon"
|
||||
@click="handleServiceClick"
|
||||
/>
|
||||
</GridLayout>
|
||||
</Card>
|
||||
|
||||
<!-- 校园资源 -->
|
||||
<Card>
|
||||
<template #header>
|
||||
<view class="section-header">
|
||||
<text class="section-icon">🏢</text>
|
||||
<text class="card-title">校园资源</text>
|
||||
</view>
|
||||
</template>
|
||||
<GridLayout :columns="3" gap="12px">
|
||||
<ServiceItem
|
||||
v-for="service in resourceServices"
|
||||
:key="service.title"
|
||||
:title="service.title"
|
||||
:icon="service.icon"
|
||||
@click="handleServiceClick"
|
||||
/>
|
||||
</GridLayout>
|
||||
</Card>
|
||||
|
||||
<!-- 办事服务 -->
|
||||
<Card>
|
||||
<template #header>
|
||||
<view class="section-header">
|
||||
<text class="section-icon">📋</text>
|
||||
<text class="card-title">办事服务</text>
|
||||
</view>
|
||||
</template>
|
||||
<GridLayout :columns="3" gap="12px">
|
||||
<ServiceItem
|
||||
v-for="service in adminServices"
|
||||
:key="service.title"
|
||||
:title="service.title"
|
||||
:icon="service.icon"
|
||||
@click="handleServiceClick"
|
||||
/>
|
||||
</GridLayout>
|
||||
</Card>
|
||||
|
||||
<!-- 信息查询 -->
|
||||
<Card>
|
||||
<template #header>
|
||||
<view class="section-header">
|
||||
<text class="section-icon">🔍</text>
|
||||
<text class="card-title">信息查询</text>
|
||||
</view>
|
||||
</template>
|
||||
<GridLayout :columns="3" gap="12px">
|
||||
<ServiceItem
|
||||
v-for="service in queryServices"
|
||||
:key="service.title"
|
||||
:title="service.title"
|
||||
:icon="service.icon"
|
||||
@click="handleServiceClick"
|
||||
/>
|
||||
</GridLayout>
|
||||
</Card>
|
||||
|
||||
<!-- 快捷工具 -->
|
||||
<Card>
|
||||
<template #header>
|
||||
<view class="section-header">
|
||||
<text class="section-icon">⚡</text>
|
||||
<text class="card-title">快捷工具</text>
|
||||
</view>
|
||||
</template>
|
||||
<GridLayout :columns="3" gap="12px">
|
||||
<ServiceItem
|
||||
v-for="service in toolServices"
|
||||
:key="service.title"
|
||||
:title="service.title"
|
||||
:icon="service.icon"
|
||||
@click="handleServiceClick"
|
||||
/>
|
||||
</GridLayout>
|
||||
</Card>
|
||||
|
||||
<!-- 底部间距 -->
|
||||
<view class="bottom-spacer"></view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NavBar from '@/components/NavBar.vue';
|
||||
import Card from '@/components/Card.vue';
|
||||
import ServiceItem from '@/components/ServiceItem.vue';
|
||||
import GridLayout from '@/components/GridLayout.vue';
|
||||
import { userStorage } from '@/utils/request';
|
||||
import { checkPageAccess } from '@/utils/route-guard';
|
||||
|
||||
export default {
|
||||
name: 'WorkbenchPage',
|
||||
components: {
|
||||
NavBar,
|
||||
Card,
|
||||
ServiceItem,
|
||||
GridLayout,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
searchQuery: '',
|
||||
searchFocused: false,
|
||||
hotServices: [
|
||||
{ title: '课程表', icon: '🎓' },
|
||||
{ title: '成绩查询', icon: '📊' },
|
||||
{ title: '校园卡', icon: '💳' },
|
||||
{ title: '图书馆', icon: '📚' },
|
||||
],
|
||||
academicServices: [
|
||||
{ title: '我的课表', icon: '🎓' },
|
||||
{ title: '成绩查询', icon: '📊' },
|
||||
{ title: '选课系统', icon: '👨🎓' },
|
||||
{ title: '考试安排', icon: '📝' },
|
||||
{ title: '课程评教', icon: '📋' },
|
||||
{ title: '学分查询', icon: '🏆' },
|
||||
{ title: '作业提交', icon: '📄' },
|
||||
{ title: '竞赛报名', icon: '🏅' },
|
||||
{ title: '教学评价', icon: '👨🏫' },
|
||||
],
|
||||
lifeServices: [
|
||||
{ title: '食堂菜单', icon: '🍽️' },
|
||||
{ title: '校园卡', icon: '💳' },
|
||||
{ title: '宿舍管理', icon: '🛏️' },
|
||||
{ title: '停车服务', icon: '🚗' },
|
||||
{ title: '校车时刻', icon: '🚌' },
|
||||
{ title: '校园购物', icon: '🛒' },
|
||||
{ title: '医疗服务', icon: '🏥' },
|
||||
{ title: '报修服务', icon: '🔧' },
|
||||
{ title: '空调控制', icon: '🌡️' },
|
||||
],
|
||||
resourceServices: [
|
||||
{ title: '图书馆', icon: '📚' },
|
||||
{ title: '机房预约', icon: '💻' },
|
||||
{ title: '会议室', icon: '🎤' },
|
||||
{ title: '体育馆', icon: '🏃♂️' },
|
||||
{ title: '艺术中心', icon: '🎭' },
|
||||
{ title: '实验室', icon: '🧪' },
|
||||
{ title: '打印服务', icon: '🖨️' },
|
||||
{ title: '网络服务', icon: '📶' },
|
||||
{ title: '设备租借', icon: '🔧' },
|
||||
],
|
||||
adminServices: [
|
||||
{ title: '学费缴费', icon: '💰' },
|
||||
{ title: '证明开具', icon: '📄' },
|
||||
{ title: '学生证', icon: '🆔' },
|
||||
{ title: '学籍管理', icon: '👤' },
|
||||
{ title: '转专业', icon: '🔄' },
|
||||
{ title: '请假申请', icon: '✈️' },
|
||||
{ title: '毕业申请', icon: '🎓' },
|
||||
{ title: '就业指导', icon: '💼' },
|
||||
{ title: '国际交流', icon: '🌍' },
|
||||
],
|
||||
queryServices: [
|
||||
{ title: '校历查询', icon: '📅' },
|
||||
{ title: '校园通讯录', icon: '📞' },
|
||||
{ title: '校园地图', icon: '🗺️' },
|
||||
{ title: '通知公告', icon: '📢' },
|
||||
{ title: '常见问题', icon: '❓' },
|
||||
{ title: '意见反馈', icon: '💬' },
|
||||
{ title: '在线客服', icon: '🎧' },
|
||||
{ title: '数据统计', icon: '📈' },
|
||||
{ title: '校园新闻', icon: '📰' },
|
||||
],
|
||||
toolServices: [
|
||||
{ title: '扫一扫', icon: '📱' },
|
||||
{ title: '计算器', icon: '🧮' },
|
||||
{ title: '作息时间', icon: '⏰' },
|
||||
{ title: '天气查询', icon: '☁️' },
|
||||
{ title: '翻译工具', icon: '🔤' },
|
||||
{ title: '分享', icon: '📤' },
|
||||
],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleSearch() {
|
||||
this.focusSearch();
|
||||
},
|
||||
focusSearch() {
|
||||
this.searchFocused = true;
|
||||
},
|
||||
handleSearchInput(e) {
|
||||
this.searchQuery = e.detail.value;
|
||||
// 实现搜索建议逻辑
|
||||
this.showSearchSuggestions();
|
||||
},
|
||||
handleSearchConfirm() {
|
||||
if (this.searchQuery.trim()) {
|
||||
this.performSearch(this.searchQuery);
|
||||
}
|
||||
},
|
||||
showSearchSuggestions() {
|
||||
// 根据输入显示搜索建议
|
||||
if (this.searchQuery.length > 0) {
|
||||
// 这里可以实现搜索建议逻辑
|
||||
}
|
||||
},
|
||||
performSearch(query) {
|
||||
uni.showToast({
|
||||
title: `搜索: ${query}`,
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
});
|
||||
this.searchFocused = false;
|
||||
},
|
||||
handleServiceClick(service) {
|
||||
uni.vibrateShort();
|
||||
|
||||
// 根据不同服务类型进行路由跳转或功能调用
|
||||
switch (service.title) {
|
||||
case '课程表':
|
||||
case '我的课表':
|
||||
this.openCourseSchedule();
|
||||
break;
|
||||
case '成绩查询':
|
||||
this.openGradeQuery();
|
||||
break;
|
||||
case '校园卡':
|
||||
this.openCampusCard();
|
||||
break;
|
||||
case '图书馆':
|
||||
this.openLibrary();
|
||||
break;
|
||||
case '扫一扫':
|
||||
this.openScanner();
|
||||
break;
|
||||
default:
|
||||
uni.showToast({
|
||||
title: `${service.title}功能开发中`,
|
||||
icon: 'none',
|
||||
});
|
||||
}
|
||||
},
|
||||
openCourseSchedule() {
|
||||
uni.showToast({ title: '打开课程表', icon: 'success' });
|
||||
// 这里可以跳转到课程表页面
|
||||
},
|
||||
openGradeQuery() {
|
||||
uni.showToast({ title: '打开成绩查询', icon: 'success' });
|
||||
// 这里可以跳转到成绩查询页面
|
||||
},
|
||||
openCampusCard() {
|
||||
uni.showToast({ title: '打开校园卡', icon: 'success' });
|
||||
// 这里可以跳转到校园卡页面
|
||||
},
|
||||
openLibrary() {
|
||||
uni.showToast({ title: '打开图书馆', icon: 'success' });
|
||||
// 这里可以跳转到图书馆页面
|
||||
},
|
||||
openScanner() {
|
||||
uni.scanCode({
|
||||
success: (res) => {
|
||||
uni.showToast({
|
||||
title: `扫描结果: ${res.result}`,
|
||||
icon: 'none',
|
||||
});
|
||||
},
|
||||
fail: () => {
|
||||
uni.showToast({
|
||||
title: '扫描失败',
|
||||
icon: 'error',
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
// 检查登录状态
|
||||
checkLoginStatus() {
|
||||
if (!userStorage.isLoggedIn()) {
|
||||
uni.reLaunch({
|
||||
url: '/pages/login/index',
|
||||
});
|
||||
return;
|
||||
}
|
||||
},
|
||||
},
|
||||
onLoad() {
|
||||
// 检查页面访问权限
|
||||
if (!checkPageAccess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.checkLoginStatus();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 页面显示时再次检查权限
|
||||
if (!checkPageAccess()) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-container {
|
||||
min-height: 100vh;
|
||||
background-color: $uni-bg-color-grey;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding-bottom: calc(50px + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
/* 搜索栏 */
|
||||
.search-section {
|
||||
padding: $uni-spacing-col-lg;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
position: relative;
|
||||
background: $uni-bg-color;
|
||||
border-radius: $uni-border-radius-lg;
|
||||
box-shadow: $uni-shadow;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 $uni-spacing-row-lg;
|
||||
transition: $uni-transition;
|
||||
|
||||
&:hover {
|
||||
box-shadow: $uni-shadow-hover;
|
||||
}
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
font-size: 16px;
|
||||
color: $uni-text-color-grey;
|
||||
margin-right: $uni-spacing-row-base;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
padding: $uni-spacing-col-base 0;
|
||||
font-size: $uni-font-size-lg;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
color: $uni-text-color;
|
||||
|
||||
&::placeholder {
|
||||
color: $uni-text-color-placeholder;
|
||||
}
|
||||
}
|
||||
|
||||
/* 分组标题 */
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.section-icon {
|
||||
font-size: $uni-font-size-lg;
|
||||
}
|
||||
|
||||
/* 服务网格动画 */
|
||||
.service-item {
|
||||
animation-delay: calc(var(--item-index, 0) * 0.1s);
|
||||
animation: slideInUp 0.6s ease-out backwards;
|
||||
}
|
||||
|
||||
@keyframes slideInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 搜索焦点效果 */
|
||||
.search-bar:focus-within {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: $uni-shadow-hover;
|
||||
|
||||
.search-icon {
|
||||
color: $uni-color-primary;
|
||||
}
|
||||
}
|
||||
|
||||
/* 底部间距 */
|
||||
.bottom-spacer {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
/* 页面进入动画 */
|
||||
.page-container {
|
||||
animation: fadeInUp 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 375px) {
|
||||
.search-section {
|
||||
padding: $uni-spacing-col-base;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
padding: 0 $uni-spacing-row-base;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
font-size: $uni-font-size-base;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -73,3 +73,180 @@
|
||||
.icon-mail {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 新增常用图标 - 2025-01-07 */
|
||||
|
||||
/* 星星图标 */
|
||||
.icon-star {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 心形图标 */
|
||||
.icon-heart {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 日历图标 */
|
||||
.icon-calendar {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 聊天图标 */
|
||||
.icon-chat {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h4l4 4 4-4h4c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 工具图标 */
|
||||
.icon-tools {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M22.7 19l-9.1-9.1c.9-2.3.4-5-1.5-6.9-2-2-5-2.4-7.4-1.3L9 6 6 9 1.6 4.7C.4 7.1.9 10.1 2.9 12.1c1.9 1.9 4.6 2.4 6.9 1.5l9.1 9.1c.4.4 1 .4 1.4 0l2.3-2.3c.5-.4.5-1.1.1-1.4z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 地图图标 */
|
||||
.icon-map {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M20.5 3l-.16.03L15 5.1 9 3 3.36 4.9c-.21.07-.36.25-.36.48V20.5c0 .28.22.5.5.5l.16-.03L9 18.9l6 2.1 5.64-1.9c.21-.07.36-.25.36-.48V3.5c0-.28-.22-.5-.5-.5zM15 19l-6-2.11V5l6 2.11V19z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 播放圆圈图标 */
|
||||
.icon-play-circle {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 14.5v-9l6 4.5-6 4.5z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 列表图标 */
|
||||
.icon-list {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 人民币圆圈图标 */
|
||||
.icon-rmb-circle {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1.5 6L12 10.5 10.5 8H9.5l1.75 2.5H9v1h3v1H9v1h3V15h1v-1.5h3v-1h-3v-1h3v-1h-2.25L14.5 8h-1z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 商店图标 */
|
||||
.icon-shop {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M12 2l3.09 6.26L22 9.27 16.18 14l1.64 7L12 17.77 6.18 21l1.64-7L2 9.27l6.91-1.01L12 2zm0 0L4 8v12h16V8l-8-6z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 奖杯图标 */
|
||||
.icon-trophy {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M7 4V2c0-1.1.9-2 2-2h6c1.1 0 2 .9 2 2v2h4c1.1 0 2 .9 2 2v4c0 2.21-1.79 4-4 4h-1.28c-.44 2.83-2.82 5-5.72 5s-5.28-2.17-5.72-5H5c-2.21 0-4-1.79-4-4V6c0-1.1.9-2 2-2h4zM9 4h6V2H9v2zM5 6v4c0 1.1.9 2 2 2h1.28c.42-1.82 1.89-3.26 3.72-3.26s3.3 1.44 3.72 3.26H17c1.1 0 2-.9 2-2V6H5zm7 14c1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3 1.34 3 3 3z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 朋友图标 */
|
||||
.icon-friends {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M16 4c0-1.11.89-2 2-2s2 .89 2 2-.89 2-2 2-2-.89-2-2zm4 18v-6h2.5l-2.54-7.63A1.495 1.495 0 0 0 18.5 7.5h-3c-.57 0-1.08.32-1.34.82L11.5 15h2.5v7h6zM12.5 11.5c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5S11 9.17 11 10s.67 1.5 1.5 1.5zM5.5 6c1.11 0 2-.89 2-2s-.89-2-2-2-2 .89-2 2 .89 2 2 2zm2 16v-7H9l-1.46-3.12A1.495 1.495 0 0 0 6.12 11h-1.5c-.57 0-1.08.32-1.34.82L1 17h2.5v7h4z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 飞机图标 */
|
||||
.icon-plane {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M21 16v-2l-8-5V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5V9l-8 5v2l8-2.5V19l-2 1.5V22l3.5-1 3.5 1v-1.5L13 19v-5.5l8 2.5z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 书签图标 */
|
||||
.icon-bookmark {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 人民币圆圈填充图标 */
|
||||
.icon-rmb-circle-fill {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1.5 6L12 10.5 10.5 8H9.5l1.75 2.5H9v1h3v1H9v1h3V15h1v-1.5h3v-1h-3v-1h3v-1h-2.25L14.5 8h-1z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 金钱圆圈图标 */
|
||||
.icon-money-circle {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M11.8 10.9c-2.27-.59-3-1.2-3-2.15 0-1.09 1.01-1.85 2.7-1.85 1.78 0 2.44.85 2.5 2.1h2.21c-.07-1.72-1.12-3.3-3.21-3.81V3h-3v2.16c-1.94.42-3.5 1.68-3.5 3.61 0 2.31 1.91 3.46 4.7 4.13 2.5.6 3 1.48 3 2.41 0 .69-.49 1.79-2.7 1.79-2.06 0-2.87-.92-2.98-2.1h-2.2c.12 2.19 1.76 3.42 3.68 3.83V21h3v-2.15c1.95-.37 3.5-1.5 3.5-3.55 0-2.84-2.43-3.81-4.7-4.4z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 文件文本图标 */
|
||||
.icon-file-text {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 订单图标 */
|
||||
.icon-order {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-5 14H7v-2h7v2zm3-4H7v-2h10v2zm0-4H7V7h10v2z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 设置填充图标 */
|
||||
.icon-setting-fill {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm8.94 3A8.994 8.994 0 0 0 13 3.06V1h-2v2.06A8.994 8.994 0 0 0 3.06 11H1v2h2.06A8.994 8.994 0 0 0 11 20.94V23h2v-2.06A8.994 8.994 0 0 0 20.94 13H23v-2h-2.06zM12 19c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 用户圆圈填充图标 */
|
||||
.icon-account-circle-fill {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 列表点图标 */
|
||||
.icon-list-dot {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M4 10.5c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zm0-6c-.83 0-1.5.67-1.5 1.5S3.17 7.5 4 7.5 5.5 6.83 5.5 6 4.83 4.5 4 4.5zm0 12c-.83 0-1.5.67-1.5 1.5s.67 1.5 1.5 1.5 1.5-.67 1.5-1.5-.67-1.5-1.5-1.5zM7 19h14v-2H7v2zm0-6h14v-2H7v2zm0-8v2h14V5H7z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 首页填充图标 */
|
||||
.icon-home-fill {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* WiFi图标 */
|
||||
.icon-wifi {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M1 9l2 2c4.97-4.97 13.03-4.97 18 0l2-2C16.93 2.93 7.07 2.93 1 9zm8 8l3 3 3-3c-1.65-1.66-4.34-1.66-6 0zm-4-4l2 2c2.76-2.76 7.24-2.76 10 0l2-2C15.14 9.14 8.87 9.14 5 13z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 分享图标 */
|
||||
.icon-share {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 分享方形图标 */
|
||||
.icon-share-square {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M18 2h-3c-1.1 0-2 .9-2 2v3H7c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2v-6h3c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-8 15H7v-3h3v3zm0-5H7v-2h3v2zm0-4H7V6h3v2zm5 9h-3v-3h3v3zm0-5h-3v-2h3v2zm0-4h-3V6h3v2zm5-3h-3V4h3v1z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 编辑笔图标 */
|
||||
.icon-edit-pen {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 时钟图标 */
|
||||
.icon-clock {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z'/%3E%3C/path%3E%3Cpath d='m12.5 7-1 1v4.25l3.5 2.08.75-1.23-2.75-1.63V8z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 确认圆圈图标 */
|
||||
.icon-checkmark-circle {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 复制图标 */
|
||||
.icon-copy {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 用户圆圈图标 */
|
||||
.icon-account-circle {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 用户填充图标 */
|
||||
.icon-account-fill {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 星星填充图标 */
|
||||
.icon-star-fill {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 包图标 */
|
||||
.icon-bag {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M17 6h-2V3c0-.55-.45-1-1-1h-4c-.55 0-1 .45-1 1v3H7c-1.1 0-2 .9-2 2v11c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zM9.5 3h5v3h-5V3zM9 12v-2h2v2h2v-2h2v2c0 1.1-.9 2-2 2h-2c-1.1 0-2-.9-2-2z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 学工管理填充图标 */
|
||||
.icon-account-fill {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
/* 登出图标 */
|
||||
.icon-logout {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.59L17 17l5-5-5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z'/%3E%3C/svg%3E");
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="12" cy="7" r="4" fill="rgb(93, 135, 255)"/>
|
||||
<path d="M12 14c-4.42 0-8 3.58-8 8h16c0-4.42-3.58-8-8-8z" fill="rgb(93, 135, 255)"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 256 B |
@@ -1,5 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="12" cy="7" r="4" fill="#999999"/>
|
||||
<path d="M12 14c-4.42 0-8 3.58-8 8h16c0-4.42-3.58-8-8-8z" fill="#999999"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 236 B |
@@ -1,5 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 4h16v2H4V4zm0 4h16v2H4V8zm0 4h16v2H4v-2zm0 4h10v2H4v-2z" fill="rgb(93, 135, 255)"/>
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" stroke="rgb(93, 135, 255)" stroke-width="1.5" fill="none"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 319 B |
@@ -1,5 +0,0 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 4h16v2H4V4zm0 4h16v2H4V8zm0 4h16v2H4v-2zm0 4h10v2H4v-2z" fill="#999999"/>
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" stroke="#999999" stroke-width="1.5" fill="none"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 299 B |
@@ -0,0 +1,378 @@
|
||||
# 移动端工具库使用说明
|
||||
|
||||
本文档说明移动端 `utils` 目录下各个工具的使用方法,特别是升级后的请求工具。
|
||||
|
||||
## 📁 文件结构
|
||||
|
||||
```
|
||||
mobile/src/utils/
|
||||
├── request.js # 统一请求工具(已升级)
|
||||
├── crypto.js # 加密解密工具(新增)
|
||||
├── config.js # 配置管理工具(新增)
|
||||
├── encrypt-pure.js # 登录专用加密工具(保留)
|
||||
├── encrypt-*.js # 其他加密实现(保留)
|
||||
└── README.md # 本文档
|
||||
```
|
||||
|
||||
## 🚀 核心升级功能
|
||||
|
||||
### 1. 统一的加密解密支持
|
||||
|
||||
移动端现在与 Web 端保持一致,支持:
|
||||
- ✅ **入参加密**:通过 `isEncrypt: 'true'` 头部控制
|
||||
- ✅ **出参解密**:自动处理后端返回的加密数据
|
||||
- ✅ **AES + RSA 双重加密**:与后端完全兼容
|
||||
- ✅ **防重复提交**:避免用户误操作
|
||||
- ✅ **错误处理优化**:404静默处理、401自动跳转
|
||||
|
||||
### 2. 可配置的参数管理
|
||||
|
||||
所有关键参数都可以通过配置文件统一管理,方便调试和部署。
|
||||
|
||||
## 📖 使用方法
|
||||
|
||||
### 基础请求
|
||||
|
||||
```javascript
|
||||
import { request } from '@/utils/request.js';
|
||||
|
||||
// 普通GET请求
|
||||
const response = await request({
|
||||
url: '/api/user/list',
|
||||
method: 'GET'
|
||||
});
|
||||
|
||||
// 普通POST请求
|
||||
const response = await request({
|
||||
url: '/api/user/create',
|
||||
method: 'POST',
|
||||
data: {
|
||||
name: '张三',
|
||||
age: 25
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 加密请求
|
||||
|
||||
```javascript
|
||||
import { request } from '@/utils/request.js';
|
||||
|
||||
// 启用加密的POST请求
|
||||
const response = await request({
|
||||
url: '/api/sensitive/data',
|
||||
method: 'POST',
|
||||
data: {
|
||||
sensitiveInfo: '敏感数据'
|
||||
},
|
||||
header: {
|
||||
isEncrypt: 'true' // 关键:启用加密
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 免Token请求
|
||||
|
||||
```javascript
|
||||
import { request } from '@/utils/request.js';
|
||||
|
||||
// 不需要Token的请求(如获取租户列表)
|
||||
const response = await request({
|
||||
url: '/auth/tenant/list',
|
||||
method: 'GET',
|
||||
header: {
|
||||
isToken: false // 关键:不携带Token
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 允许重复提交
|
||||
|
||||
```javascript
|
||||
import { request } from '@/utils/request.js';
|
||||
|
||||
// 允许重复提交的请求
|
||||
const response = await request({
|
||||
url: '/api/data/sync',
|
||||
method: 'POST',
|
||||
data: { syncData: 'xxx' },
|
||||
header: {
|
||||
repeatSubmit: false // 关键:允许重复提交
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## ⚙️ 配置管理
|
||||
|
||||
### 基础配置更新
|
||||
|
||||
```javascript
|
||||
import { updateConfig } from '@/utils/config.js';
|
||||
|
||||
// 更新API基础URL
|
||||
updateConfig({
|
||||
request: {
|
||||
baseURL: {
|
||||
production: 'https://new-api-domain.com'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 更新客户端ID
|
||||
updateConfig({
|
||||
app: {
|
||||
clientId: 'new-client-id-here'
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 加密配置更新
|
||||
|
||||
```javascript
|
||||
import { updateConfig } from '@/utils/config.js';
|
||||
|
||||
// 更新RSA公钥
|
||||
updateConfig({
|
||||
encryption: {
|
||||
rsaPublicKey: 'new-rsa-public-key-here'
|
||||
}
|
||||
});
|
||||
|
||||
// 临时禁用加密(调试用)
|
||||
updateConfig({
|
||||
encryption: {
|
||||
enabled: false
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 预设配置方案
|
||||
|
||||
```javascript
|
||||
import { applyPreset } from '@/utils/config.js';
|
||||
|
||||
// 应用生产环境配置
|
||||
applyPreset('production');
|
||||
|
||||
// 应用测试环境配置
|
||||
applyPreset('test');
|
||||
|
||||
// 应用开发环境配置
|
||||
applyPreset('development');
|
||||
```
|
||||
|
||||
### 单个配置项更新
|
||||
|
||||
```javascript
|
||||
import { setConfigItem, getConfigItem } from '@/utils/config.js';
|
||||
|
||||
// 设置超时时间
|
||||
setConfigItem('request.timeout', 600000); // 10分钟
|
||||
|
||||
// 获取当前客户端ID
|
||||
const clientId = getConfigItem('app.clientId');
|
||||
|
||||
// 检查是否启用加密
|
||||
const isEncrypted = getConfigItem('encryption.enabled');
|
||||
```
|
||||
|
||||
## 🔐 加密工具直接使用
|
||||
|
||||
```javascript
|
||||
import {
|
||||
encryptWithAES,
|
||||
decryptWithAES,
|
||||
generateRandomAESKey,
|
||||
encrypt,
|
||||
decrypt
|
||||
} from '@/utils/crypto.js';
|
||||
|
||||
// AES加密解密
|
||||
const aesKey = generateRandomAESKey();
|
||||
const encryptedData = encryptWithAES('敏感数据', aesKey);
|
||||
const decryptedData = decryptWithAES(encryptedData, aesKey);
|
||||
|
||||
// RSA加密解密
|
||||
const encryptedKey = encrypt('AES密钥');
|
||||
const decryptedKey = decrypt(encryptedKey);
|
||||
```
|
||||
|
||||
## 📦 API方法封装
|
||||
|
||||
### 登录
|
||||
|
||||
```javascript
|
||||
import { api } from '@/utils/request.js';
|
||||
|
||||
// 用户登录(自动加密)
|
||||
const loginResult = await api.login({
|
||||
username: 'admin',
|
||||
password: 'password123',
|
||||
tenantId: '000000'
|
||||
});
|
||||
```
|
||||
|
||||
### 获取用户信息
|
||||
|
||||
```javascript
|
||||
import { api } from '@/utils/request.js';
|
||||
|
||||
// 获取当前用户信息
|
||||
const userInfo = await api.getUserInfo();
|
||||
```
|
||||
|
||||
### 注销登录
|
||||
|
||||
```javascript
|
||||
import { api } from '@/utils/request.js';
|
||||
|
||||
// 用户注销
|
||||
await api.logout();
|
||||
```
|
||||
|
||||
## 💾 缓存工具
|
||||
|
||||
### 用户信息缓存
|
||||
|
||||
```javascript
|
||||
import { userStorage } from '@/utils/request.js';
|
||||
|
||||
// 保存登录信息
|
||||
userStorage.saveLoginInfo(loginResult, userInfo);
|
||||
|
||||
// 获取用户信息
|
||||
const user = userStorage.getUserInfo();
|
||||
|
||||
// 检查登录状态
|
||||
const isLoggedIn = userStorage.isLoggedIn();
|
||||
|
||||
// 清除登录信息
|
||||
userStorage.clearLoginInfo();
|
||||
```
|
||||
|
||||
### 通用缓存
|
||||
|
||||
```javascript
|
||||
import { storage } from '@/utils/request.js';
|
||||
|
||||
// 设置缓存(30分钟过期)
|
||||
storage.set('cacheKey', { data: 'value' }, 1800);
|
||||
|
||||
// 获取缓存
|
||||
const data = storage.get('cacheKey');
|
||||
|
||||
// 删除缓存
|
||||
storage.remove('cacheKey');
|
||||
|
||||
// 清空所有缓存
|
||||
storage.clear();
|
||||
```
|
||||
|
||||
## 📥 文件下载
|
||||
|
||||
```javascript
|
||||
import { download } from '@/utils/request.js';
|
||||
|
||||
// 下载文件
|
||||
await download('/api/export/users', {
|
||||
filters: { status: 'active' }
|
||||
}, 'users.xlsx');
|
||||
```
|
||||
|
||||
## 🔧 调试和日志
|
||||
|
||||
### 开启详细日志
|
||||
|
||||
```javascript
|
||||
import { updateConfig } from '@/utils/config.js';
|
||||
|
||||
// 开启所有调试日志
|
||||
updateConfig({
|
||||
debug: {
|
||||
enableVerboseLog: true,
|
||||
showRequestLog: true,
|
||||
showResponseLog: true,
|
||||
showCryptoLog: true
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 错误处理配置
|
||||
|
||||
```javascript
|
||||
import { updateConfig } from '@/utils/config.js';
|
||||
|
||||
// 自定义错误处理
|
||||
updateConfig({
|
||||
error: {
|
||||
silentCodes: [404, 403], // 静默处理的状态码
|
||||
toastDuration: 3000, // 错误提示显示时间
|
||||
loginRedirectDelay: 1000 // 跳转登录延迟
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## 🚨 注意事项
|
||||
|
||||
### 1. 加密兼容性
|
||||
- 移动端加密算法与 Web 端完全一致
|
||||
- RSA 公钥必须与后端保持同步
|
||||
- 加密只对 POST/PUT 请求生效
|
||||
|
||||
### 2. 配置优先级
|
||||
- 运行时配置 > 环境预设 > 默认配置
|
||||
- 配置更新会立即生效,无需重启应用
|
||||
|
||||
### 3. Token 管理
|
||||
- Token 自动从本地存储获取
|
||||
- 401 错误会自动清除 Token 并跳转登录
|
||||
- 支持 Token 过期时间管理
|
||||
|
||||
### 4. 错误处理
|
||||
- 404 错误默认静默处理
|
||||
- 403 错误会显示提示但不跳转
|
||||
- 网络错误会显示友好提示
|
||||
|
||||
## 📊 性能优化
|
||||
|
||||
### 1. 防重复提交
|
||||
- 默认 500ms 间隔防重复提交
|
||||
- 可通过配置调整间隔时间
|
||||
- 支持单个请求禁用防重复
|
||||
|
||||
### 2. 请求超时
|
||||
- 普通请求:5分钟超时
|
||||
- 下载请求:20分钟超时
|
||||
- 可通过配置调整超时时间
|
||||
|
||||
### 3. 缓存策略
|
||||
- 会话级缓存用于防重复提交
|
||||
- 持久化缓存支持过期时间
|
||||
- 登录信息缓存支持时间戳
|
||||
|
||||
## 🔗 与 Web 端对比
|
||||
|
||||
| 功能 | Web 端 | 移动端 | 说明 |
|
||||
|------|--------|--------|------|
|
||||
| 入参加密 | ✅ | ✅ | 完全一致 |
|
||||
| 出参解密 | ✅ | ✅ | 完全一致 |
|
||||
| 防重复提交 | ✅ | ✅ | 完全一致 |
|
||||
| 401处理 | ✅ | ✅ | 跳转方式略有不同 |
|
||||
| 404静默 | ✅ | ✅ | 完全一致 |
|
||||
| 下载支持 | ✅ | ✅ | 平台实现不同 |
|
||||
| 配置管理 | 环境变量 | 配置文件 | 更灵活的配置方式 |
|
||||
|
||||
## 🎯 最佳实践
|
||||
|
||||
1. **生产环境**:关闭调试日志,启用加密
|
||||
2. **开发环境**:开启详细日志,保持加密测试
|
||||
3. **配置管理**:使用预设配置,避免硬编码
|
||||
4. **错误处理**:合理设置静默错误码
|
||||
5. **性能优化**:适当设置超时和缓存策略
|
||||
|
||||
---
|
||||
|
||||
**📝 更新日志**
|
||||
- 2025-01-07:完成移动端请求工具升级,与 Web 端功能对齐
|
||||
- 支持入参加密、出参解密、防重复提交等完整功能
|
||||
- 新增配置管理系统,提升可维护性
|
||||
@@ -373,8 +373,6 @@ class AccessibilityUtils {
|
||||
uni.switchTab = (options) => {
|
||||
const pageTitles = {
|
||||
'/pages/index/index': '智慧校园 - 首页',
|
||||
'/pages/workbench/index': '智慧校园 - 办公中心',
|
||||
'/pages/profile/index': '智慧校园 - 我的',
|
||||
};
|
||||
|
||||
const title = pageTitles[options.url];
|
||||
|
||||
@@ -0,0 +1,353 @@
|
||||
// 移动端配置文件 - 集中管理所有可配置参数
|
||||
// 使用方法:import { updateConfig } from '@/utils/config.js' 然后调用 updateConfig(newConfig)
|
||||
|
||||
// 默认配置
|
||||
const DEFAULT_CONFIG = {
|
||||
// =========================
|
||||
// 加密相关配置
|
||||
// =========================
|
||||
encryption: {
|
||||
// 是否启用加密
|
||||
enabled: true,
|
||||
|
||||
// RSA公钥 - 与后端保持一致
|
||||
rsaPublicKey:
|
||||
'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==',
|
||||
|
||||
// RSA私钥 - 前端不建议存放,仅用于测试环境
|
||||
rsaPrivateKey: '',
|
||||
|
||||
// 加密请求头名称
|
||||
encryptHeader: 'encrypt-key',
|
||||
|
||||
// AES密钥长度
|
||||
aesKeyLength: 32,
|
||||
},
|
||||
|
||||
// =========================
|
||||
// 请求相关配置
|
||||
// =========================
|
||||
request: {
|
||||
// API基础URL
|
||||
baseURL: {
|
||||
development: '/api',
|
||||
production: 'https://your-api-domain.com',
|
||||
},
|
||||
|
||||
// 请求超时时间(毫秒)
|
||||
timeout: 300000, // 5分钟
|
||||
|
||||
// 下载超时时间(毫秒)
|
||||
downloadTimeout: 1200000, // 20分钟
|
||||
|
||||
// 防重复提交间隔时间(毫秒)
|
||||
repeatSubmitInterval: 500,
|
||||
|
||||
// 默认请求头
|
||||
defaultHeaders: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
|
||||
// =========================
|
||||
// 应用相关配置
|
||||
// =========================
|
||||
app: {
|
||||
// 客户端ID
|
||||
clientId: 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
||||
|
||||
// 租户ID
|
||||
tenantId: '000000',
|
||||
|
||||
// 默认referer
|
||||
referer: 'http://localhost/login?redirect=/index',
|
||||
|
||||
// 登录页面路径
|
||||
loginPath: '/pages/login/index',
|
||||
},
|
||||
|
||||
// =========================
|
||||
// 缓存相关配置
|
||||
// =========================
|
||||
cache: {
|
||||
// 默认缓存过期时间(秒)
|
||||
defaultExpire: 3600, // 1小时
|
||||
|
||||
// Token存储键名
|
||||
tokenKey: 'access_token',
|
||||
|
||||
// 用户信息存储键名
|
||||
userInfoKey: 'userInfo',
|
||||
|
||||
// 登录状态存储键名
|
||||
loginStatusKey: 'isLoggedIn',
|
||||
|
||||
// 会话缓存前缀
|
||||
sessionPrefix: 'session_',
|
||||
},
|
||||
|
||||
// =========================
|
||||
// 错误处理配置
|
||||
// =========================
|
||||
error: {
|
||||
// 静默处理的HTTP状态码
|
||||
silentCodes: [404],
|
||||
|
||||
// 自动跳转登录的状态码
|
||||
loginRedirectCodes: [401],
|
||||
|
||||
// 错误提示显示时间(毫秒)
|
||||
toastDuration: 2000,
|
||||
|
||||
// 跳转登录延迟时间(毫秒)
|
||||
loginRedirectDelay: 500,
|
||||
},
|
||||
|
||||
// =========================
|
||||
// 调试相关配置
|
||||
// =========================
|
||||
debug: {
|
||||
// 是否启用详细日志
|
||||
enableVerboseLog: process.env.NODE_ENV === 'development',
|
||||
|
||||
// 是否显示请求日志
|
||||
showRequestLog: true,
|
||||
|
||||
// 是否显示响应日志
|
||||
showResponseLog: true,
|
||||
|
||||
// 是否显示加密解密日志
|
||||
showCryptoLog: true,
|
||||
},
|
||||
};
|
||||
|
||||
// 当前配置 - 可被动态修改
|
||||
let currentConfig = JSON.parse(JSON.stringify(DEFAULT_CONFIG));
|
||||
|
||||
// =========================
|
||||
// 配置管理方法
|
||||
// =========================
|
||||
|
||||
/**
|
||||
* 获取完整配置
|
||||
* @returns {Object} 当前配置对象
|
||||
*/
|
||||
export const getConfig = () => {
|
||||
return currentConfig;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取特定配置项
|
||||
* @param {string} path 配置路径,支持嵌套 如: 'encryption.enabled' 或 'request.timeout'
|
||||
* @returns {any} 配置值
|
||||
*/
|
||||
export const getConfigItem = (path) => {
|
||||
const keys = path.split('.');
|
||||
let value = currentConfig;
|
||||
|
||||
for (const key of keys) {
|
||||
if (value && typeof value === 'object' && key in value) {
|
||||
value = value[key];
|
||||
} else {
|
||||
console.warn(`配置项 ${path} 不存在`);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新配置
|
||||
* @param {Object} newConfig 新配置对象(部分更新)
|
||||
* @param {boolean} deep 是否深度合并,默认true
|
||||
*/
|
||||
export const updateConfig = (newConfig, deep = true) => {
|
||||
if (deep) {
|
||||
currentConfig = deepMerge(currentConfig, newConfig);
|
||||
} else {
|
||||
currentConfig = { ...currentConfig, ...newConfig };
|
||||
}
|
||||
|
||||
console.log('🔧 配置已更新:', currentConfig);
|
||||
|
||||
// 通知其他模块配置已更新
|
||||
notifyConfigUpdate();
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置特定配置项
|
||||
* @param {string} path 配置路径
|
||||
* @param {any} value 配置值
|
||||
*/
|
||||
export const setConfigItem = (path, value) => {
|
||||
const keys = path.split('.');
|
||||
let target = currentConfig;
|
||||
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
const key = keys[i];
|
||||
if (!target[key] || typeof target[key] !== 'object') {
|
||||
target[key] = {};
|
||||
}
|
||||
target = target[key];
|
||||
}
|
||||
|
||||
const lastKey = keys[keys.length - 1];
|
||||
target[lastKey] = value;
|
||||
|
||||
console.log(`🔧 配置项 ${path} 已更新为:`, value);
|
||||
|
||||
// 通知其他模块配置已更新
|
||||
notifyConfigUpdate();
|
||||
};
|
||||
|
||||
/**
|
||||
* 重置配置为默认值
|
||||
*/
|
||||
export const resetConfig = () => {
|
||||
currentConfig = JSON.parse(JSON.stringify(DEFAULT_CONFIG));
|
||||
console.log('🔄 配置已重置为默认值');
|
||||
|
||||
// 通知其他模块配置已更新
|
||||
notifyConfigUpdate();
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前环境的API基础URL
|
||||
* @returns {string} API基础URL
|
||||
*/
|
||||
export const getBaseURL = () => {
|
||||
const env = process.env.NODE_ENV || 'development';
|
||||
return currentConfig.request.baseURL[env] || currentConfig.request.baseURL.development;
|
||||
};
|
||||
|
||||
// =========================
|
||||
// 配置预设方案
|
||||
// =========================
|
||||
|
||||
/**
|
||||
* 开发环境配置
|
||||
*/
|
||||
export const developmentConfig = {
|
||||
encryption: {
|
||||
enabled: true, // 开发环境也启用加密测试
|
||||
},
|
||||
debug: {
|
||||
enableVerboseLog: true,
|
||||
showRequestLog: true,
|
||||
showResponseLog: true,
|
||||
showCryptoLog: true,
|
||||
},
|
||||
error: {
|
||||
toastDuration: 3000, // 开发环境错误提示显示久一点
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 生产环境配置
|
||||
*/
|
||||
export const productionConfig = {
|
||||
encryption: {
|
||||
enabled: true,
|
||||
},
|
||||
debug: {
|
||||
enableVerboseLog: false,
|
||||
showRequestLog: false,
|
||||
showResponseLog: false,
|
||||
showCryptoLog: false,
|
||||
},
|
||||
request: {
|
||||
timeout: 300000, // 生产环境保持较长超时
|
||||
repeatSubmitInterval: 1000, // 生产环境防重复提交间隔更长
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 测试环境配置
|
||||
*/
|
||||
export const testConfig = {
|
||||
encryption: {
|
||||
enabled: true,
|
||||
},
|
||||
debug: {
|
||||
enableVerboseLog: true,
|
||||
showRequestLog: true,
|
||||
showResponseLog: false,
|
||||
showCryptoLog: true,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 应用预设配置
|
||||
* @param {string} preset 预设名称: 'development' | 'production' | 'test'
|
||||
*/
|
||||
export const applyPreset = (preset) => {
|
||||
const presets = {
|
||||
development: developmentConfig,
|
||||
production: productionConfig,
|
||||
test: testConfig,
|
||||
};
|
||||
|
||||
if (presets[preset]) {
|
||||
updateConfig(presets[preset]);
|
||||
console.log(`✅ 已应用 ${preset} 环境配置预设`);
|
||||
} else {
|
||||
console.error(`❌ 未知的配置预设: ${preset}`);
|
||||
}
|
||||
};
|
||||
|
||||
// =========================
|
||||
// 工具方法
|
||||
// =========================
|
||||
|
||||
/**
|
||||
* 深度合并对象
|
||||
* @param {Object} target 目标对象
|
||||
* @param {Object} source 源对象
|
||||
* @returns {Object} 合并后的对象
|
||||
*/
|
||||
const deepMerge = (target, source) => {
|
||||
const result = { ...target };
|
||||
|
||||
for (const key in source) {
|
||||
if (source.hasOwnProperty(key)) {
|
||||
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
||||
result[key] = deepMerge(result[key] || {}, source[key]);
|
||||
} else {
|
||||
result[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* 通知其他模块配置已更新
|
||||
*/
|
||||
const notifyConfigUpdate = () => {
|
||||
// 这里可以实现事件通知机制
|
||||
// 例如使用 uni.$emit('configUpdated', currentConfig)
|
||||
if (typeof uni !== 'undefined' && uni.$emit) {
|
||||
uni.$emit('configUpdated', currentConfig);
|
||||
}
|
||||
};
|
||||
|
||||
// =========================
|
||||
// 初始化配置
|
||||
// =========================
|
||||
|
||||
// 根据环境自动应用配置预设
|
||||
const env = process.env.NODE_ENV || 'development';
|
||||
if (env !== 'development') {
|
||||
applyPreset(env);
|
||||
}
|
||||
|
||||
// 导出常用配置项的快捷访问方法
|
||||
export const isEncryptionEnabled = () => getConfigItem('encryption.enabled');
|
||||
export const getClientId = () => getConfigItem('app.clientId');
|
||||
export const getTenantId = () => getConfigItem('app.tenantId');
|
||||
export const getTimeout = () => getConfigItem('request.timeout');
|
||||
export const isDebugMode = () => getConfigItem('debug.enableVerboseLog');
|
||||
|
||||
console.log('📋 移动端配置初始化完成,当前环境:', env);
|
||||
@@ -0,0 +1,291 @@
|
||||
// 移动端加密解密工具 - 与web端保持一致
|
||||
import CryptoJS from 'crypto-js';
|
||||
import JSEncrypt from 'jsencrypt';
|
||||
|
||||
// 加密配置 - 可配置
|
||||
export const ENCRYPT_CONFIG = {
|
||||
// RSA公钥 - 与后端保持一致
|
||||
RSA_PUBLIC_KEY:
|
||||
'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==',
|
||||
// RSA私钥 - 与web端配置文档保持一致
|
||||
RSA_PRIVATE_KEY:
|
||||
'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE=',
|
||||
// 客户端ID
|
||||
CLIENT_ID: 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
||||
// 租户ID
|
||||
TENANT_ID: '000000',
|
||||
// 加密请求头
|
||||
ENCRYPT_HEADER: 'encrypt-key',
|
||||
// 是否启用加密
|
||||
ENCRYPT_ENABLED: true,
|
||||
};
|
||||
|
||||
/**
|
||||
* 随机生成32位的字符串
|
||||
* @returns {string}
|
||||
*/
|
||||
const generateRandomString = () => {
|
||||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let result = '';
|
||||
const charactersLength = characters.length;
|
||||
for (let i = 0; i < 32; i++) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* 随机生成AES密钥
|
||||
* @returns {CryptoJS.lib.WordArray}
|
||||
*/
|
||||
export const generateAesKey = () => {
|
||||
return CryptoJS.enc.Utf8.parse(generateRandomString());
|
||||
};
|
||||
|
||||
/**
|
||||
* 加密base64
|
||||
* @param {CryptoJS.lib.WordArray} str
|
||||
* @returns {string}
|
||||
*/
|
||||
export const encryptBase64 = (str) => {
|
||||
return CryptoJS.enc.Base64.stringify(str);
|
||||
};
|
||||
|
||||
/**
|
||||
* 解密base64
|
||||
* @param {string} str
|
||||
* @returns {CryptoJS.lib.WordArray}
|
||||
*/
|
||||
export const decryptBase64 = (str) => {
|
||||
return CryptoJS.enc.Base64.parse(str);
|
||||
};
|
||||
|
||||
/**
|
||||
* 使用密钥对数据进行AES加密
|
||||
* @param {string} message 待加密的数据
|
||||
* @param {CryptoJS.lib.WordArray} aesKey AES密钥
|
||||
* @returns {string} 加密后的数据
|
||||
*/
|
||||
export const encryptWithAes = (message, aesKey) => {
|
||||
const encrypted = CryptoJS.AES.encrypt(message, aesKey, {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
});
|
||||
return encrypted.toString();
|
||||
};
|
||||
|
||||
/**
|
||||
* 使用密钥对数据进行AES解密
|
||||
* @param {string} message 待解密的数据
|
||||
* @param {CryptoJS.lib.WordArray} aesKey AES密钥
|
||||
* @returns {string} 解密后的数据
|
||||
*/
|
||||
export const decryptWithAes = (message, aesKey) => {
|
||||
const decrypted = CryptoJS.AES.decrypt(message, aesKey, {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
});
|
||||
return decrypted.toString(CryptoJS.enc.Utf8);
|
||||
};
|
||||
|
||||
/**
|
||||
* RSA加密
|
||||
* @param {string} txt 待加密的文本
|
||||
* @returns {string|false} 加密后的数据
|
||||
*/
|
||||
export const encrypt = (txt) => {
|
||||
try {
|
||||
console.log('🔐 开始RSA加密,输入文本长度:', txt ? txt.length : 'null');
|
||||
console.log('🔐 使用的RSA公钥:', ENCRYPT_CONFIG.RSA_PUBLIC_KEY.substring(0, 20) + '...');
|
||||
|
||||
const encryptor = new JSEncrypt();
|
||||
|
||||
// 构造完整的PEM格式RSA公钥
|
||||
const publicKey = `-----BEGIN PUBLIC KEY-----
|
||||
${ENCRYPT_CONFIG.RSA_PUBLIC_KEY}
|
||||
-----END PUBLIC KEY-----`;
|
||||
|
||||
console.log('🔐 构造的完整公钥:', publicKey.substring(0, 50) + '...');
|
||||
|
||||
encryptor.setPublicKey(publicKey);
|
||||
console.log('🔐 公钥设置完成');
|
||||
|
||||
const result = encryptor.encrypt(txt);
|
||||
|
||||
if (result) {
|
||||
console.log('✅ RSA加密成功,结果长度:', result.length);
|
||||
console.log('✅ 加密结果前50位:', result.substring(0, 50) + '...');
|
||||
} else {
|
||||
console.error('❌ RSA加密返回空结果');
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('❌ RSA加密失败:', error);
|
||||
console.error('❌ 错误堆栈:', error.stack);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* RSA解密
|
||||
* @param {string} txt 待解密的文本
|
||||
* @returns {string|false} 解密后的数据
|
||||
*/
|
||||
export const decrypt = (txt) => {
|
||||
try {
|
||||
console.log('🔓 开始RSA解密,输入文本长度:', txt ? txt.length : 'null');
|
||||
console.log('🔓 使用的RSA私钥:', ENCRYPT_CONFIG.RSA_PRIVATE_KEY.substring(0, 20) + '...');
|
||||
|
||||
if (!ENCRYPT_CONFIG.RSA_PRIVATE_KEY) {
|
||||
console.warn('❌ RSA私钥未配置,无法解密');
|
||||
return false;
|
||||
}
|
||||
|
||||
const encryptor = new JSEncrypt();
|
||||
|
||||
// 构造完整的PEM格式RSA私钥
|
||||
const privateKey = `-----BEGIN PRIVATE KEY-----
|
||||
${ENCRYPT_CONFIG.RSA_PRIVATE_KEY}
|
||||
-----END PRIVATE KEY-----`;
|
||||
|
||||
console.log('🔓 构造的完整私钥:', privateKey.substring(0, 50) + '...');
|
||||
|
||||
encryptor.setPrivateKey(privateKey);
|
||||
console.log('🔓 私钥设置完成');
|
||||
|
||||
const result = encryptor.decrypt(txt);
|
||||
|
||||
if (result) {
|
||||
console.log('✅ RSA解密成功,结果长度:', result.length);
|
||||
console.log('✅ 解密结果前50位:', result.substring(0, 50) + '...');
|
||||
} else {
|
||||
console.error('❌ RSA解密返回空结果');
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('❌ RSA解密失败:', error);
|
||||
console.error('❌ 错误堆栈:', error.stack);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 生成随机字符串AES密钥(字符串格式)
|
||||
* @param {number} length 密钥长度,默认32位
|
||||
* @returns {string} AES密钥字符串
|
||||
*/
|
||||
export const generateRandomAESKey = (length = 32) => {
|
||||
return generateRandomString();
|
||||
};
|
||||
|
||||
/**
|
||||
* 使用字符串密钥进行AES加密(兼容原有函数)
|
||||
* @param {string} data 待加密的数据
|
||||
* @param {string} key AES密钥字符串
|
||||
* @returns {string} 加密后的数据
|
||||
*/
|
||||
export const encryptWithAES = (data, key) => {
|
||||
const keyUtf8 = CryptoJS.enc.Utf8.parse(key);
|
||||
const encrypted = CryptoJS.AES.encrypt(data, keyUtf8, {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
});
|
||||
return encrypted.toString();
|
||||
};
|
||||
|
||||
/**
|
||||
* 使用字符串密钥进行AES解密(兼容原有函数)
|
||||
* @param {string} data 待解密的数据
|
||||
* @param {string} key AES密钥字符串
|
||||
* @returns {string} 解密后的数据
|
||||
*/
|
||||
export const decryptWithAES = (data, key) => {
|
||||
const keyUtf8 = CryptoJS.enc.Utf8.parse(key);
|
||||
const decrypted = CryptoJS.AES.decrypt(data, keyUtf8, {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
});
|
||||
return decrypted.toString(CryptoJS.enc.Utf8);
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新加密配置
|
||||
* @param {Object} config 新的配置对象
|
||||
*/
|
||||
export const updateEncryptConfig = (config) => {
|
||||
Object.assign(ENCRYPT_CONFIG, config);
|
||||
console.log('🔧 加密配置已更新:', ENCRYPT_CONFIG);
|
||||
};
|
||||
|
||||
/**
|
||||
* 验证RSA密钥对配置
|
||||
* @returns {boolean} 验证结果
|
||||
*/
|
||||
export const validateKeyPair = () => {
|
||||
try {
|
||||
console.log('🔍 开始验证RSA密钥对配置...');
|
||||
|
||||
// 检查公钥和私钥是否存在
|
||||
if (!ENCRYPT_CONFIG.RSA_PUBLIC_KEY) {
|
||||
console.error('❌ RSA公钥未配置');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ENCRYPT_CONFIG.RSA_PRIVATE_KEY) {
|
||||
console.error('❌ RSA私钥未配置');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 测试加密解密功能
|
||||
const testMessage = 'RSA密钥对验证测试消息';
|
||||
console.log('🧪 测试消息:', testMessage);
|
||||
|
||||
// 使用公钥加密
|
||||
const encrypted = encrypt(testMessage);
|
||||
if (!encrypted) {
|
||||
console.error('❌ RSA加密测试失败');
|
||||
return false;
|
||||
}
|
||||
console.log('✅ RSA加密测试成功');
|
||||
|
||||
// 使用私钥解密
|
||||
const decrypted = decrypt(encrypted);
|
||||
if (!decrypted || decrypted !== testMessage) {
|
||||
console.error('❌ RSA解密测试失败');
|
||||
console.error('原始消息:', testMessage);
|
||||
console.error('解密结果:', decrypted);
|
||||
return false;
|
||||
}
|
||||
console.log('✅ RSA解密测试成功');
|
||||
|
||||
console.log('🎉 RSA密钥对配置验证通过!');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('❌ RSA密钥对验证过程中出现错误:', error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前加密配置摘要
|
||||
* @returns {Object} 配置摘要
|
||||
*/
|
||||
export const getConfigSummary = () => {
|
||||
return {
|
||||
hasPublicKey: !!ENCRYPT_CONFIG.RSA_PUBLIC_KEY,
|
||||
hasPrivateKey: !!ENCRYPT_CONFIG.RSA_PRIVATE_KEY,
|
||||
publicKeyLength: ENCRYPT_CONFIG.RSA_PUBLIC_KEY ? ENCRYPT_CONFIG.RSA_PUBLIC_KEY.length : 0,
|
||||
privateKeyLength: ENCRYPT_CONFIG.RSA_PRIVATE_KEY
|
||||
? ENCRYPT_CONFIG.RSA_PRIVATE_KEY.length
|
||||
: 0,
|
||||
clientId: ENCRYPT_CONFIG.CLIENT_ID,
|
||||
tenantId: ENCRYPT_CONFIG.TENANT_ID,
|
||||
encryptHeader: ENCRYPT_CONFIG.ENCRYPT_HEADER,
|
||||
encryptEnabled: ENCRYPT_CONFIG.ENCRYPT_ENABLED,
|
||||
};
|
||||
};
|
||||
|
||||
// 导出配置以便外部修改
|
||||
export { ENCRYPT_CONFIG as config };
|
||||
@@ -375,7 +375,7 @@ class GestureUtils {
|
||||
class PageSwipeGesture {
|
||||
constructor() {
|
||||
this.currentPageIndex = 0;
|
||||
this.pages = ['home', 'workbench', 'profile'];
|
||||
this.pages = ['home'];
|
||||
this.isAnimating = false;
|
||||
}
|
||||
|
||||
@@ -387,29 +387,18 @@ class PageSwipeGesture {
|
||||
enableDoubleTap: false,
|
||||
});
|
||||
|
||||
gesture.on('swipeleft', () => {
|
||||
this.nextPage();
|
||||
});
|
||||
|
||||
gesture.on('swiperight', () => {
|
||||
this.prevPage();
|
||||
});
|
||||
|
||||
// 只有一个页面,禁用滑动切换
|
||||
return gesture;
|
||||
}
|
||||
|
||||
nextPage() {
|
||||
if (this.isAnimating) return;
|
||||
|
||||
this.currentPageIndex = (this.currentPageIndex + 1) % this.pages.length;
|
||||
this.switchPage(this.pages[this.currentPageIndex]);
|
||||
// 只有一个页面,不执行切换
|
||||
return;
|
||||
}
|
||||
|
||||
prevPage() {
|
||||
if (this.isAnimating) return;
|
||||
|
||||
this.currentPageIndex = (this.currentPageIndex - 1 + this.pages.length) % this.pages.length;
|
||||
this.switchPage(this.pages[this.currentPageIndex]);
|
||||
// 只有一个页面,不执行切换
|
||||
return;
|
||||
}
|
||||
|
||||
switchPage(pageName) {
|
||||
@@ -417,12 +406,10 @@ class PageSwipeGesture {
|
||||
|
||||
const pageUrls = {
|
||||
home: '/pages/index/index',
|
||||
workbench: '/pages/workbench/index',
|
||||
profile: '/pages/profile/index',
|
||||
};
|
||||
|
||||
if (pageUrls[pageName]) {
|
||||
uni.switchTab({
|
||||
uni.navigateTo({
|
||||
url: pageUrls[pageName],
|
||||
success: () => {
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
/**
|
||||
* iconfont 图标管理工具
|
||||
* 帮助开发者快速添加和管理自定义图标
|
||||
*/
|
||||
|
||||
/**
|
||||
* SVG Path 转换为 iconfont CSS 代码
|
||||
* @param {string} iconName - 图标名称 (如: star, heart)
|
||||
* @param {string} svgPath - SVG path 数据
|
||||
* @param {string} viewBox - SVG viewBox, 默认 "0 0 24 24"
|
||||
* @returns {string} CSS 代码
|
||||
*/
|
||||
export function generateIconCSS(iconName, svgPath, viewBox = '0 0 24 24') {
|
||||
const svg = `<svg xmlns='http://www.w3.org/2000/svg' viewBox='${viewBox}' fill='currentColor'><path d='${svgPath}'/></svg>`;
|
||||
const encodedSvg = encodeURIComponent(svg).replace(/'/g, '%27').replace(/"/g, '%22');
|
||||
|
||||
return `/* ${iconName}图标 */
|
||||
.icon-${iconName} {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,${encodedSvg}");
|
||||
}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量生成图标 CSS
|
||||
* @param {Array} icons - 图标数组 [{name, path, viewBox}]
|
||||
* @returns {string} 完整的 CSS 代码
|
||||
*/
|
||||
export function generateBatchIconCSS(icons) {
|
||||
return icons.map((icon) => generateIconCSS(icon.name, icon.path, icon.viewBox)).join('\n\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* 从完整SVG生成CSS
|
||||
* @param {string} iconName - 图标名称
|
||||
* @param {string} svgContent - 完整的SVG内容
|
||||
* @returns {string} CSS 代码
|
||||
*/
|
||||
export function generateIconCSSFromSVG(iconName, svgContent) {
|
||||
const encodedSvg = encodeURIComponent(svgContent).replace(/'/g, '%27').replace(/"/g, '%22');
|
||||
|
||||
return `/* ${iconName}图标 */
|
||||
.icon-${iconName} {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,${encodedSvg}");
|
||||
}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预定义的常用图标路径
|
||||
* 来源:Material Design Icons
|
||||
*/
|
||||
export const COMMON_ICONS = {
|
||||
// 基础图标
|
||||
user: 'M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z',
|
||||
lock: 'M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zM9 6c0-1.66 1.34-3 3-3s3 1.34 3 3v2H9V6z',
|
||||
home: 'M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z',
|
||||
setting:
|
||||
'M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z',
|
||||
|
||||
// 操作图标
|
||||
add: 'M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z',
|
||||
close: 'M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z',
|
||||
edit: 'M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z',
|
||||
delete: 'M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z',
|
||||
search: 'M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z',
|
||||
check: 'M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z',
|
||||
|
||||
// 状态图标
|
||||
success:
|
||||
'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z',
|
||||
warning: 'M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z',
|
||||
info: 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z',
|
||||
error: 'M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z',
|
||||
|
||||
// 文件图标
|
||||
download: 'M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z',
|
||||
upload: 'M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z',
|
||||
|
||||
// 导航图标
|
||||
arrowLeft: 'M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z',
|
||||
arrowRight: 'M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z',
|
||||
arrowUp: 'M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z',
|
||||
arrowDown: 'M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6z',
|
||||
|
||||
// 社交图标
|
||||
star: 'M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z',
|
||||
heart: 'M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z',
|
||||
message:
|
||||
'M20 2H4c-1.1 0-1.99.9-1.99 2L2 22l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z',
|
||||
phone: 'M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z',
|
||||
mail: 'M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z',
|
||||
};
|
||||
|
||||
/**
|
||||
* 快速生成常用图标的CSS
|
||||
* @param {string[]} iconNames - 图标名称数组
|
||||
* @returns {string} CSS 代码
|
||||
*/
|
||||
export function generateCommonIconsCSS(iconNames) {
|
||||
const selectedIcons = iconNames
|
||||
.map((name) => ({
|
||||
name,
|
||||
path: COMMON_ICONS[name],
|
||||
}))
|
||||
.filter((icon) => icon.path);
|
||||
|
||||
return generateBatchIconCSS(selectedIcons);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查图标名称是否已存在
|
||||
* @param {string} iconName - 图标名称
|
||||
* @param {string} cssContent - 当前CSS文件内容
|
||||
* @returns {boolean} 是否已存在
|
||||
*/
|
||||
export function checkIconExists(iconName, cssContent) {
|
||||
return cssContent.includes(`.icon-${iconName}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证SVG内容
|
||||
* @param {string} svgContent - SVG内容
|
||||
* @returns {boolean} 是否有效
|
||||
*/
|
||||
export function validateSVG(svgContent) {
|
||||
try {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(svgContent, 'image/svg+xml');
|
||||
return !doc.querySelector('parsererror');
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用示例
|
||||
*/
|
||||
export const USAGE_EXAMPLES = {
|
||||
// 单个图标生成
|
||||
singleIcon: {
|
||||
code: `import { generateIconCSS } from '@/utils/iconHelper';
|
||||
|
||||
const css = generateIconCSS(
|
||||
'calendar',
|
||||
'M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z'
|
||||
);
|
||||
console.log(css);`,
|
||||
output: `/* calendar图标 */
|
||||
.icon-calendar {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,...");
|
||||
}`,
|
||||
},
|
||||
|
||||
// 批量图标生成
|
||||
batchIcons: {
|
||||
code: `import { generateCommonIconsCSS } from '@/utils/iconHelper';
|
||||
|
||||
const css = generateCommonIconsCSS(['star', 'heart', 'message']);
|
||||
console.log(css);`,
|
||||
output: `/* star图标 */
|
||||
.icon-star { ... }
|
||||
|
||||
/* heart图标 */
|
||||
.icon-heart { ... }
|
||||
|
||||
/* message图标 */
|
||||
.icon-message { ... }`,
|
||||
},
|
||||
|
||||
// 从SVG生成
|
||||
fromSVG: {
|
||||
code: `import { generateIconCSSFromSVG } from '@/utils/iconHelper';
|
||||
|
||||
const svgContent = \`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z"/>
|
||||
</svg>\`;
|
||||
|
||||
const css = generateIconCSSFromSVG('circle', svgContent);
|
||||
console.log(css);`,
|
||||
},
|
||||
};
|
||||
|
||||
// 导出所有工具函数
|
||||
export default {
|
||||
generateIconCSS,
|
||||
generateBatchIconCSS,
|
||||
generateIconCSSFromSVG,
|
||||
generateCommonIconsCSS,
|
||||
checkIconExists,
|
||||
validateSVG,
|
||||
COMMON_ICONS,
|
||||
USAGE_EXAMPLES,
|
||||
};
|
||||
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* 移动端权限校验工具
|
||||
* 参考web端的permission.ts实现
|
||||
*/
|
||||
|
||||
import { userManager } from '@/utils/user'
|
||||
|
||||
/**
|
||||
* 字符权限校验
|
||||
* @param {Array} value 校验值
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export const checkPermi = (value) => {
|
||||
if (value && value instanceof Array && value.length > 0) {
|
||||
const userInfo = userManager.getCurrentUser()
|
||||
if (!userInfo || !userInfo.permissions) {
|
||||
return false
|
||||
}
|
||||
|
||||
const permissions = userInfo.permissions
|
||||
const permissionDatas = value
|
||||
const all_permission = '*:*:*'
|
||||
|
||||
const hasPermission = permissions.some((permission) => {
|
||||
return all_permission === permission || permissionDatas.includes(permission)
|
||||
})
|
||||
|
||||
if (!hasPermission) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
console.error('need roles! Like checkPermi="[\'system:user:add\',\'system:user:edit\']"')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色权限校验
|
||||
* @param {Array} value 校验值
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export const checkRole = (value) => {
|
||||
if (value && value instanceof Array && value.length > 0) {
|
||||
const userInfo = userManager.getCurrentUser()
|
||||
if (!userInfo || !userInfo.roles) {
|
||||
return false
|
||||
}
|
||||
|
||||
const roles = userInfo.roles
|
||||
const permissionRoles = value
|
||||
const super_admin = 'admin'
|
||||
|
||||
const hasRole = roles.some((role) => {
|
||||
return super_admin === role || permissionRoles.includes(role)
|
||||
})
|
||||
|
||||
if (!hasRole) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
console.error('need roles! Like checkRole="[\'admin\',\'editor\']"')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限指令校验函数
|
||||
* 用于在模板中进行权限判断
|
||||
* @param {Array|String} permissions 权限标识数组或单个权限标识
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export const hasPermission = (permissions) => {
|
||||
if (!permissions) return true
|
||||
|
||||
// 如果传入的是字符串,转换为数组
|
||||
const perms = Array.isArray(permissions) ? permissions : [permissions]
|
||||
return checkPermi(perms)
|
||||
}
|
||||
|
||||
/**
|
||||
* Vue过滤器:权限校验
|
||||
* 在模板中使用:v-if="hasPermi(['system:user:add'])"
|
||||
*/
|
||||
export const hasPermi = hasPermission
|
||||
|
||||
/**
|
||||
* 用户角色校验
|
||||
* @param {Array|String} roles 角色标识数组或单个角色标识
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export const hasRole = (roles) => {
|
||||
if (!roles) return true
|
||||
|
||||
// 如果传入的是字符串,转换为数组
|
||||
const roleList = Array.isArray(roles) ? roles : [roles]
|
||||
return checkRole(roleList)
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限控制混入
|
||||
* 在页面或组件中使用
|
||||
*/
|
||||
export const permissionMixin = {
|
||||
methods: {
|
||||
// 权限校验方法
|
||||
hasPermi(permissions) {
|
||||
return hasPermission(permissions)
|
||||
},
|
||||
|
||||
// 角色校验方法
|
||||
hasRole(roles) {
|
||||
return hasRole(roles)
|
||||
},
|
||||
|
||||
// 检查单个权限
|
||||
checkPermission(permission) {
|
||||
return hasPermission([permission])
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,10 +1,66 @@
|
||||
import { encryptLoginData } from './encrypt-pure.js';
|
||||
import {
|
||||
generateAesKey,
|
||||
encryptBase64,
|
||||
decryptBase64,
|
||||
encryptWithAes,
|
||||
decryptWithAes,
|
||||
encrypt,
|
||||
decrypt,
|
||||
ENCRYPT_CONFIG,
|
||||
updateEncryptConfig,
|
||||
validateKeyPair,
|
||||
getConfigSummary,
|
||||
} from './crypto.js';
|
||||
|
||||
// API请求配置
|
||||
const baseURL =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? '/api' // 开发环境使用代理
|
||||
: 'https://your-api-domain.com';
|
||||
// API请求配置 - 可配置
|
||||
const REQUEST_CONFIG = {
|
||||
// 基础URL配置
|
||||
baseURL: process.env.NODE_ENV === 'development' ? '/api' : 'https://your-api-domain.com',
|
||||
|
||||
// 请求超时时间
|
||||
timeout: 300000, // 5分钟,适合大数据量导出
|
||||
|
||||
// 客户端ID
|
||||
clientId: 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
||||
|
||||
// 默认referer
|
||||
referer: 'http://localhost/login?redirect=/index',
|
||||
|
||||
// 是否启用加密
|
||||
encryptEnabled: true,
|
||||
|
||||
// 防重复提交间隔时间(ms)
|
||||
repeatSubmitInterval: 500,
|
||||
};
|
||||
|
||||
// 会话缓存工具
|
||||
const sessionCache = {
|
||||
set: (key, value) => {
|
||||
try {
|
||||
uni.setStorageSync(`session_${key}`, JSON.stringify(value));
|
||||
} catch (e) {
|
||||
console.error('设置会话缓存失败:', e);
|
||||
}
|
||||
},
|
||||
|
||||
get: (key) => {
|
||||
try {
|
||||
const data = uni.getStorageSync(`session_${key}`);
|
||||
return data ? JSON.parse(data) : null;
|
||||
} catch (e) {
|
||||
console.error('获取会话缓存失败:', e);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
remove: (key) => {
|
||||
try {
|
||||
uni.removeStorageSync(`session_${key}`);
|
||||
} catch (e) {
|
||||
console.error('删除会话缓存失败:', e);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// 统一请求方法
|
||||
export const request = (options) => {
|
||||
@@ -12,18 +68,157 @@ export const request = (options) => {
|
||||
// 获取token
|
||||
const token = getToken();
|
||||
|
||||
uni.request({
|
||||
url: baseURL + options.url,
|
||||
// 处理请求配置
|
||||
const config = {
|
||||
url: REQUEST_CONFIG.baseURL + options.url,
|
||||
method: options.method || 'GET',
|
||||
data: options.data || {},
|
||||
timeout: options.timeout || REQUEST_CONFIG.timeout,
|
||||
header: {
|
||||
'Content-Type': 'application/json',
|
||||
authorization: token,
|
||||
clientid: 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
||||
referer: 'http://localhost/login?redirect=/index',
|
||||
clientid: options.clientId || REQUEST_CONFIG.clientId,
|
||||
// 注意:在H5环境中,浏览器会自动设置referer,手动设置会报错
|
||||
// referer: options.referer || REQUEST_CONFIG.referer,
|
||||
...options.header,
|
||||
},
|
||||
};
|
||||
|
||||
// 检查特殊header配置
|
||||
const isToken = options.header?.isToken === false;
|
||||
const isRepeatSubmit = options.header?.repeatSubmit === false;
|
||||
const isEncrypt = options.header?.isEncrypt === 'true';
|
||||
|
||||
// 如果不需要token,移除authorization
|
||||
if (isToken) {
|
||||
delete config.header.authorization;
|
||||
}
|
||||
|
||||
// 防重复提交检查
|
||||
if (!isRepeatSubmit && (config.method === 'POST' || config.method === 'PUT')) {
|
||||
const requestObj = {
|
||||
url: config.url,
|
||||
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
|
||||
time: new Date().getTime(),
|
||||
};
|
||||
|
||||
const sessionObj = sessionCache.get('sessionObj');
|
||||
if (sessionObj) {
|
||||
const s_url = sessionObj.url;
|
||||
const s_data = sessionObj.data;
|
||||
const s_time = sessionObj.time;
|
||||
const interval = REQUEST_CONFIG.repeatSubmitInterval;
|
||||
|
||||
if (
|
||||
s_data === requestObj.data &&
|
||||
requestObj.time - s_time < interval &&
|
||||
s_url === requestObj.url
|
||||
) {
|
||||
const message = '数据正在处理,请勿重复提交';
|
||||
console.warn(`[${s_url}]: ` + message);
|
||||
uni.showToast({
|
||||
title: message,
|
||||
icon: 'none',
|
||||
});
|
||||
return reject(new Error(message));
|
||||
}
|
||||
}
|
||||
sessionCache.set('sessionObj', requestObj);
|
||||
}
|
||||
|
||||
// 加密处理
|
||||
if (
|
||||
REQUEST_CONFIG.encryptEnabled &&
|
||||
isEncrypt &&
|
||||
(config.method === 'POST' || config.method === 'PUT')
|
||||
) {
|
||||
try {
|
||||
console.log('🔐 开始请求加密处理...');
|
||||
|
||||
// 生成AES密钥
|
||||
const aesKey = generateAesKey();
|
||||
|
||||
// 使用RSA加密AES密钥
|
||||
const encryptedKey = encrypt(encryptBase64(aesKey));
|
||||
if (!encryptedKey) {
|
||||
throw new Error('RSA加密AES密钥失败');
|
||||
}
|
||||
|
||||
// 设置加密头
|
||||
config.header[ENCRYPT_CONFIG.ENCRYPT_HEADER] = encryptedKey;
|
||||
|
||||
// 加密请求数据
|
||||
const dataToEncrypt =
|
||||
typeof config.data === 'object' ? JSON.stringify(config.data) : config.data;
|
||||
config.data = encryptWithAes(dataToEncrypt, aesKey);
|
||||
|
||||
console.log('✅ 请求数据加密完成');
|
||||
} catch (error) {
|
||||
console.error('❌ 请求加密失败:', error);
|
||||
uni.showToast({
|
||||
title: '请求加密失败',
|
||||
icon: 'none',
|
||||
});
|
||||
return reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
// FormData处理
|
||||
if (config.data instanceof globalThis.FormData) {
|
||||
delete config.header['Content-Type'];
|
||||
}
|
||||
|
||||
console.log('📤 发送请求:', {
|
||||
url: config.url,
|
||||
method: config.method,
|
||||
hasToken: !!token,
|
||||
isEncrypt: isEncrypt,
|
||||
});
|
||||
|
||||
// 发送请求
|
||||
uni.request({
|
||||
...config,
|
||||
success: (res) => {
|
||||
console.log('📥 收到响应:', {
|
||||
statusCode: res.statusCode,
|
||||
url: config.url,
|
||||
hasEncryptHeader: !!res.header[ENCRYPT_CONFIG.ENCRYPT_HEADER],
|
||||
});
|
||||
|
||||
// 解密处理
|
||||
if (REQUEST_CONFIG.encryptEnabled && res.header[ENCRYPT_CONFIG.ENCRYPT_HEADER]) {
|
||||
try {
|
||||
console.log('🔓 开始响应解密处理...');
|
||||
|
||||
// 获取加密的AES密钥
|
||||
const keyStr = res.header[ENCRYPT_CONFIG.ENCRYPT_HEADER];
|
||||
|
||||
// RSA解密获取AES密钥
|
||||
const base64Str = decrypt(keyStr);
|
||||
if (!base64Str) {
|
||||
throw new Error('RSA解密AES密钥失败');
|
||||
}
|
||||
|
||||
// Base64解码获取AES密钥
|
||||
const aesKey = decryptBase64(base64Str);
|
||||
|
||||
// AES解密响应数据
|
||||
const decryptedData = decryptWithAes(res.data, aesKey);
|
||||
|
||||
// 解析JSON
|
||||
res.data = JSON.parse(decryptedData);
|
||||
|
||||
console.log('✅ 响应数据解密完成');
|
||||
} catch (error) {
|
||||
console.error('❌ 响应解密失败:', error);
|
||||
uni.showToast({
|
||||
title: '响应解密失败',
|
||||
icon: 'none',
|
||||
});
|
||||
return reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理响应
|
||||
if (res.statusCode === 200) {
|
||||
// 检查业务状态码
|
||||
@@ -37,10 +232,14 @@ export const request = (options) => {
|
||||
reject(res.data);
|
||||
} else {
|
||||
// 其他业务错误
|
||||
uni.showToast({
|
||||
title: res.data.msg || '请求失败',
|
||||
icon: 'none',
|
||||
});
|
||||
const errorMsg = res.data.msg || '请求失败';
|
||||
if (res.data.code !== 404) {
|
||||
// 404错误静默处理
|
||||
uni.showToast({
|
||||
title: errorMsg,
|
||||
icon: 'none',
|
||||
});
|
||||
}
|
||||
reject(res.data);
|
||||
}
|
||||
} else {
|
||||
@@ -51,18 +250,34 @@ export const request = (options) => {
|
||||
console.log('🔒 HTTP 401未授权,自动跳转到登录页');
|
||||
handleUnauthorized('登录已失效,请重新登录');
|
||||
reject(res);
|
||||
} else if (res.statusCode === 404) {
|
||||
// 404错误静默处理
|
||||
console.log('404错误被静默处理,不显示用户提示');
|
||||
reject(res);
|
||||
} else {
|
||||
// 其他HTTP状态码错误
|
||||
const errorMsg = `请求失败: ${res.statusCode}`;
|
||||
uni.showToast({
|
||||
title: `请求失败: ${res.statusCode}`,
|
||||
title: errorMsg,
|
||||
icon: 'none',
|
||||
});
|
||||
reject(res);
|
||||
}
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('❌ 网络请求失败:', error);
|
||||
let message = '网络请求失败';
|
||||
|
||||
if (error.errMsg) {
|
||||
if (error.errMsg.includes('timeout')) {
|
||||
message = '请求超时,请检查网络连接';
|
||||
} else if (error.errMsg.includes('fail')) {
|
||||
message = '网络连接失败,请检查网络';
|
||||
}
|
||||
}
|
||||
|
||||
uni.showToast({
|
||||
title: '网络请求失败',
|
||||
title: message,
|
||||
icon: 'none',
|
||||
});
|
||||
reject(error);
|
||||
@@ -133,87 +348,25 @@ function handleUnauthorized(message = '登录已失效,请重新登录') {
|
||||
|
||||
// API方法封装
|
||||
export const api = {
|
||||
// 用户登录
|
||||
login: async (data) => {
|
||||
try {
|
||||
console.log('🔐 开始加密登录流程...');
|
||||
// 用户登录 - 使用统一的request方法和加密拦截器
|
||||
login: (data) => {
|
||||
console.log('🔐 开始登录流程...');
|
||||
|
||||
// 加密登录数据
|
||||
const encryptResult = encryptLoginData({
|
||||
return request({
|
||||
url: '/auth/login',
|
||||
method: 'POST',
|
||||
data: {
|
||||
username: data.username,
|
||||
password: data.password,
|
||||
tenantId: data.tenantId || '000000',
|
||||
clientId: data.clientId || 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
||||
clientId: data.clientId || REQUEST_CONFIG.clientId,
|
||||
grantType: data.grantType || 'password',
|
||||
});
|
||||
|
||||
// 如果encryptLoginData返回的是Promise,需要await
|
||||
const actualEncryptResult = encryptResult.then ? await encryptResult : encryptResult;
|
||||
|
||||
if (!actualEncryptResult.success) {
|
||||
console.error('❌ 登录数据加密失败:', actualEncryptResult.error);
|
||||
throw new Error(actualEncryptResult.error);
|
||||
}
|
||||
|
||||
console.log('✅ 登录数据加密成功,发送加密请求...');
|
||||
|
||||
// 使用加密数据和自定义header发送请求
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: baseURL + '/auth/login',
|
||||
method: 'POST',
|
||||
data: actualEncryptResult.encryptedData, // 发送加密字符串,不是JSON对象
|
||||
header: {
|
||||
...actualEncryptResult.headers,
|
||||
referer: 'http://localhost/login?redirect=/index', // 添加正确格式的referer
|
||||
},
|
||||
success: (res) => {
|
||||
console.log('🔐 加密登录响应:', res);
|
||||
if (res.statusCode === 200) {
|
||||
if (res.data && res.data.code !== undefined) {
|
||||
if (res.data.code === 200) {
|
||||
console.log('✅ 加密登录成功');
|
||||
resolve(res.data);
|
||||
} else {
|
||||
console.log('❌ 登录失败:', res.data.msg);
|
||||
uni.showToast({
|
||||
title: res.data.msg || '登录失败',
|
||||
icon: 'none',
|
||||
});
|
||||
reject(res.data);
|
||||
}
|
||||
} else {
|
||||
resolve(res.data);
|
||||
}
|
||||
} else {
|
||||
console.log('❌ HTTP状态错误:', res.statusCode);
|
||||
uni.showToast({
|
||||
title: `请求失败: ${res.statusCode}`,
|
||||
icon: 'none',
|
||||
});
|
||||
reject(res);
|
||||
}
|
||||
},
|
||||
fail: (error) => {
|
||||
console.log('❌ 登录请求失败:', error);
|
||||
uni.showToast({
|
||||
title: '网络请求失败',
|
||||
icon: 'none',
|
||||
});
|
||||
reject(error);
|
||||
},
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('❌ 登录加密处理失败:', error);
|
||||
console.error('❌ 错误详情:', error.message);
|
||||
console.error('❌ 错误堆栈:', error.stack);
|
||||
uni.showToast({
|
||||
title: '登录处理失败: ' + error.message,
|
||||
icon: 'none',
|
||||
});
|
||||
return Promise.reject(error);
|
||||
}
|
||||
},
|
||||
header: {
|
||||
isToken: false, // 登录接口不需要token
|
||||
isEncrypt: 'true', // 启用加密
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// 获取用户信息
|
||||
@@ -227,11 +380,6 @@ export const api = {
|
||||
return request({
|
||||
url: '/system/user/getInfo',
|
||||
method: 'GET',
|
||||
header: {
|
||||
authorization: token,
|
||||
clientid: 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
||||
referer: 'http://localhost/login?redirect=/index',
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
@@ -265,7 +413,7 @@ export const userStorage = {
|
||||
if (userInfo) {
|
||||
const enhancedUserInfo = {
|
||||
...userInfo,
|
||||
loginTime: new Date().toISOString(), // 记录登录时间
|
||||
loginTime: new Date().toISOString(),
|
||||
tokenExpireTime: loginResult.expires_in
|
||||
? new Date(Date.now() + loginResult.expires_in * 1000).toISOString()
|
||||
: null,
|
||||
@@ -333,6 +481,26 @@ export const userStorage = {
|
||||
console.error('清除登录信息失败:', e);
|
||||
}
|
||||
},
|
||||
|
||||
// 清除所有用户相关数据(包括refresh_token等)
|
||||
clearAll: () => {
|
||||
try {
|
||||
// 清除所有登录相关的存储
|
||||
uni.removeStorageSync('access_token');
|
||||
uni.removeStorageSync('userInfo');
|
||||
uni.removeStorageSync('isLoggedIn');
|
||||
uni.removeStorageSync('refresh_token');
|
||||
|
||||
// 清除可能存在的其他用户相关数据
|
||||
uni.removeStorageSync('userPermissions');
|
||||
uni.removeStorageSync('userRoles');
|
||||
uni.removeStorageSync('tenantInfo');
|
||||
|
||||
console.log('所有用户相关数据已清除');
|
||||
} catch (e) {
|
||||
console.error('清除所有用户数据失败:', e);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// 数据缓存工具
|
||||
@@ -390,3 +558,58 @@ export const storage = {
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// 通用下载方法
|
||||
export const download = (url, params, fileName) => {
|
||||
// 显示加载提示
|
||||
uni.showLoading({
|
||||
title: '正在下载数据,请稍候',
|
||||
mask: true,
|
||||
});
|
||||
|
||||
return request({
|
||||
url: url,
|
||||
method: 'POST',
|
||||
data: params,
|
||||
header: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
timeout: 1200000, // 20分钟超时
|
||||
responseType: 'arraybuffer',
|
||||
})
|
||||
.then((resp) => {
|
||||
// 处理下载响应
|
||||
// 注意:uni-app中文件下载需要特殊处理
|
||||
console.log('文件下载完成:', fileName);
|
||||
uni.hideLoading();
|
||||
|
||||
// 这里需要根据平台进行不同的文件保存处理
|
||||
// 具体实现可能需要平台特定的代码
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('下载失败:', error);
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '下载文件出现错误,请联系管理员!',
|
||||
icon: 'none',
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 配置更新方法
|
||||
export const updateRequestConfig = (config) => {
|
||||
Object.assign(REQUEST_CONFIG, config);
|
||||
console.log('🔧 请求配置已更新:', REQUEST_CONFIG);
|
||||
};
|
||||
|
||||
// 加密配置更新方法
|
||||
export const updateCryptoConfig = updateEncryptConfig;
|
||||
|
||||
// 密钥对验证方法
|
||||
export const validateCryptoKeys = validateKeyPair;
|
||||
|
||||
// 获取加密配置摘要
|
||||
export const getCryptoConfigSummary = getConfigSummary;
|
||||
|
||||
// 导出配置以便外部修改
|
||||
export { REQUEST_CONFIG as config, ENCRYPT_CONFIG as cryptoConfig };
|
||||
@@ -176,7 +176,7 @@ export const permissionUtils = {
|
||||
*/
|
||||
checkPagePermission(pagePath) {
|
||||
// 公开页面,无需权限检查
|
||||
const publicPages = ['/pages/login/index', '/pages/index/index', '/pages/profile/index'];
|
||||
const publicPages = ['/pages/login/index', '/pages/index/index'];
|
||||
|
||||
if (publicPages.includes(pagePath)) {
|
||||
return true;
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
INSERT INTO sys_menu (menu_id, menu_name, path, parent_id, order_num, component, is_frame, menu_type, visible, status, perms, icon) VALUES (1948304542593663001, '企业信息', '/zsjy/enterprise/', 1948304542593662977, 1, 'zsjy/enterprise/index', 1, 'C', '0', '0', NULL, 'company');
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, menu_type, visible, status, perms, icon) VALUES (1948304542593663002, '新增企业信息', 1948304542593663001, 1, 'F', '0', '0', 'zsjy:enterprise:add', '#');
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, menu_type, visible, status, perms, icon) VALUES (1948304542593663003, '编辑企业信息', 1948304542593663001, 2, 'F', '0', '0', 'zsjy:enterprise:edit', '#');
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, menu_type, visible, status, perms, icon) VALUES (1948304542593663004, '删除企业信息', 1948304542593663001, 3, 'F', '0', '0', 'zsjy:enterprise:remove', '#');
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, menu_type, visible, status, perms, icon) VALUES (1948304542593663005, '导出企业信息', 1948304542593663001, 4, 'F', '0', '0', 'zsjy:enterprise:export', '#');
|
||||
INSERT INTO sys_menu (menu_id, menu_name, path, parent_id, order_num, component, is_frame, menu_type, visible, status, perms, icon) VALUES (1948304542593663011, '招聘岗位', '/zsjy/jobPosition/', 1948304542593662977, 2, 'zsjy/jobPosition/index', 1, 'C', '0', '0', NULL, 'user');
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, menu_type, visible, status, perms, icon) VALUES (1948304542593663012, '新增招聘岗位', 1948304542593663011, 1, 'F', '0', '0', 'zsjy:jobPosition:add', '#');
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, menu_type, visible, status, perms, icon) VALUES (1948304542593663013, '编辑招聘岗位', 1948304542593663011, 2, 'F', '0', '0', 'zsjy:jobPosition:edit', '#');
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, menu_type, visible, status, perms, icon) VALUES (1948304542593663014, '删除招聘岗位', 1948304542593663011, 3, 'F', '0', '0', 'zsjy:jobPosition:remove', '#');
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, menu_type, visible, status, perms, icon) VALUES (1948304542593663015, '导出招聘岗位', 1948304542593663011, 4, 'F', '0', '0', 'zsjy:jobPosition:export', '#');
|
||||
INSERT INTO sys_menu (menu_id, menu_name, path, parent_id, order_num, component, is_frame, menu_type, visible, status, perms, icon) VALUES (1948304542593663021, '招聘海报', '/zsjy/poster', 1948304542593662977, 3, 'zsjy/poster/index', 1, 'C', '0', '0', NULL, 'file');
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, menu_type, visible, status, perms, icon) VALUES (1948304542593663022, '新增招聘海报', 1948304542593663021, 1, 'F', '0', '0', 'zsjy:poster:add', '#');
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, menu_type, visible, status, perms, icon) VALUES (1948304542593663023, '编辑招聘海报', 1948304542593663021, 2, 'F', '0', '0', 'zsjy:poster:edit', '#');
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, menu_type, visible, status, perms, icon) VALUES (1948304542593663024, '删除招聘海报', 1948304542593663021, 3, 'F', '0', '0', 'zsjy:poster:remove', '#');
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, menu_type, visible, status, perms, icon) VALUES (1948304542593663025, '导出招聘海报', 1948304542593663021, 4, 'F', '0', '0', 'zsjy:poster:export', '#');
|
||||
@@ -1,321 +0,0 @@
|
||||
# 项目申报表单集成步骤指南
|
||||
|
||||
## 🎯 目标
|
||||
完成项目申报表单的绩效指标组件集成和金额字段修复
|
||||
|
||||
## 📋 前置条件
|
||||
- ✅ 已创建 `PerformanceIndicators.vue` 组件
|
||||
- ✅ 已创建 `performanceIndicator.ts` API接口
|
||||
- ✅ 已修复表单数据结构
|
||||
|
||||
## 🔧 集成步骤
|
||||
|
||||
### 步骤1: 修复form.vue的模板部分
|
||||
|
||||
需要在 `web/src/views/cw/application/form.vue` 中添加完整的金额字段表单:
|
||||
|
||||
```vue
|
||||
<!-- 第三步:资金预算 -->
|
||||
<el-card shadow="never" class="form-card" v-show="currentStep === 2">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<el-icon><Money /></el-icon>
|
||||
<span>资金预算安排</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-form ref="budgetFormRef" :model="form" :rules="rules" label-width="140px">
|
||||
<!-- 项目总投资 -->
|
||||
<div class="budget-section">
|
||||
<h4 class="section-title">项目总投资(万元)</h4>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="合计" prop="totalInvestment">
|
||||
<el-input-number v-model="form.totalInvestment" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="市财政资金" prop="totalMunicipalFiscal">
|
||||
<el-input-number v-model="form.totalMunicipalFiscal" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="单位自筹" prop="totalSelfFunding">
|
||||
<el-input-number v-model="form.totalSelfFunding" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="其他资金" prop="totalOtherFunding">
|
||||
<el-input-number v-model="form.totalOtherFunding" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- 年度预计执行数 -->
|
||||
<div class="budget-section">
|
||||
<h4 class="section-title">年度预计执行数(万元)</h4>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="合计" prop="currentYearTotal">
|
||||
<el-input-number v-model="form.currentYearTotal" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="市财政资金" prop="currentYearMunicipal">
|
||||
<el-input-number v-model="form.currentYearMunicipal" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="单位自筹" prop="currentYearSelf">
|
||||
<el-input-number v-model="form.currentYearSelf" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="其他资金" prop="currentYearOther">
|
||||
<el-input-number v-model="form.currentYearOther" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- 下年度申请资金 -->
|
||||
<div class="budget-section">
|
||||
<h4 class="section-title">下年度申请资金(万元)</h4>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="合计" prop="nextYearTotal">
|
||||
<el-input-number v-model="form.nextYearTotal" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="市财政资金" prop="nextYearMunicipal">
|
||||
<el-input-number v-model="form.nextYearMunicipal" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="单位自筹" prop="nextYearSelf">
|
||||
<el-input-number v-model="form.nextYearSelf" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="其他资金" prop="nextYearOther">
|
||||
<el-input-number v-model="form.nextYearOther" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- 以后年度安排 -->
|
||||
<div class="budget-section">
|
||||
<h4 class="section-title">以后年度安排(万元)</h4>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="合计" prop="futureYearTotal">
|
||||
<el-input-number v-model="form.futureYearTotal" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="市财政资金" prop="futureYearMunicipal">
|
||||
<el-input-number v-model="form.futureYearMunicipal" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="单位自筹" prop="futureYearSelf">
|
||||
<el-input-number v-model="form.futureYearSelf" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="其他资金" prop="futureYearOther">
|
||||
<el-input-number v-model="form.futureYearOther" :min="0" :precision="2" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-form>
|
||||
</el-card>
|
||||
```
|
||||
|
||||
### 步骤2: 注册PerformanceIndicators组件
|
||||
|
||||
在 `web/src/views/cw/application/form.vue` 的script部分添加:
|
||||
|
||||
```javascript
|
||||
// 注册组件
|
||||
import PerformanceIndicators from './components/PerformanceIndicators.vue'
|
||||
|
||||
// 在组件定义中添加
|
||||
export default {
|
||||
components: {
|
||||
PerformanceIndicators
|
||||
},
|
||||
// ... 其他配置
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤3: 修复API导入错误
|
||||
|
||||
确保 `web/src/api/cw/projectApplication.ts` 文件存在并包含正确的API方法:
|
||||
|
||||
```typescript
|
||||
// 项目申报相关API
|
||||
export const addProjectApplication = (data: any) => {
|
||||
return request({
|
||||
url: '/cw/application',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
export const updateProjectApplication = (data: any) => {
|
||||
return request({
|
||||
url: '/cw/application',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
export const getProjectApplication = (id: string | number) => {
|
||||
return request({
|
||||
url: '/cw/application/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
export const submitApplication = (id: string | number) => {
|
||||
return request({
|
||||
url: '/cw/application/submit/' + id,
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
### 步骤4: 更新保存逻辑
|
||||
|
||||
修复 `handleSaveDraft` 和 `handleSubmit` 方法:
|
||||
|
||||
```javascript
|
||||
import { batchSavePerformanceIndicator } from '@/api/cw/performanceIndicator'
|
||||
|
||||
// 保存草稿
|
||||
const handleSaveDraft = async () => {
|
||||
try {
|
||||
savingDraft.value = true
|
||||
|
||||
// 1. 保存申报基本信息
|
||||
let result
|
||||
if (form.id) {
|
||||
result = await updateProjectApplication(form)
|
||||
} else {
|
||||
result = await addProjectApplication(form)
|
||||
if (result && result.data) {
|
||||
form.id = result.data.id
|
||||
form.applicationNo = result.data.applicationNo
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 保存绩效指标
|
||||
if (form.id && form.performanceIndicators.length > 0) {
|
||||
await batchSavePerformanceIndicator(form.id, form.applicationNo, form.performanceIndicators)
|
||||
}
|
||||
|
||||
ElMessage.success('草稿保存成功')
|
||||
router.push('/cw/application')
|
||||
} catch (error) {
|
||||
ElMessage.error('保存失败:' + error.message)
|
||||
} finally {
|
||||
savingDraft.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 提交申报
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
// 1. 验证绩效指标
|
||||
const performanceValidation = performanceFormRef.value?.validate()
|
||||
if (performanceValidation && !performanceValidation.valid) {
|
||||
ElMessage.error(performanceValidation.message)
|
||||
return
|
||||
}
|
||||
|
||||
submitting.value = true
|
||||
|
||||
// 2. 先保存草稿
|
||||
await handleSaveDraft()
|
||||
|
||||
// 3. 提交审批
|
||||
if (form.id) {
|
||||
await submitApplication(form.id)
|
||||
ElMessage.success('提交成功,已进入审批流程')
|
||||
router.push('/cw/application')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('提交失败:' + error.message)
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤5: 添加表单验证
|
||||
|
||||
更新验证规则:
|
||||
|
||||
```javascript
|
||||
const rules = {
|
||||
projectName: [
|
||||
{ required: true, message: '请输入项目名称', trigger: 'blur' }
|
||||
],
|
||||
principal: [
|
||||
{ required: true, message: '请输入项目负责人', trigger: 'blur' }
|
||||
],
|
||||
totalInvestment: [
|
||||
{ required: true, message: '请填写项目总投资', trigger: 'blur' },
|
||||
{ type: 'number', min: 0.01, message: '投资金额必须大于0', trigger: 'blur' }
|
||||
],
|
||||
// ... 其他验证规则
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤6: 测试功能
|
||||
|
||||
1. **新增申报测试**:
|
||||
- 填写基本信息
|
||||
- 填写项目内容
|
||||
- 填写完整的资金预算(所有金额字段)
|
||||
- 添加绩效指标(确保分值总和为100%)
|
||||
- 保存草稿,检查数据库是否正确保存
|
||||
|
||||
2. **修改申报测试**:
|
||||
- 打开已保存的申报
|
||||
- 修改金额和绩效指标
|
||||
- 重新保存,验证数据更新
|
||||
|
||||
3. **提交流程测试**:
|
||||
- 完成申报填写
|
||||
- 点击提交,确认审批流程创建成功
|
||||
|
||||
## 🚨 常见问题解决
|
||||
|
||||
### 问题1: API导入错误
|
||||
**解决方案**: 确保API文件路径正确,检查 `@/api/cw/` 目录下的文件是否存在
|
||||
|
||||
### 问题2: 绩效指标组件不显示
|
||||
**解决方案**: 检查组件导入路径,确保 `PerformanceIndicators.vue` 文件存在
|
||||
|
||||
### 问题3: 金额字段保存失败
|
||||
**解决方案**: 检查前端字段名与后端BO类字段名是否严格匹配
|
||||
|
||||
### 问题4: 分值验证错误
|
||||
**解决方案**: 检查绩效指标组件的 `validate()` 方法是否正确调用
|
||||
|
||||
## ✅ 验收标准
|
||||
|
||||
- [ ] 所有金额字段能正确显示和编辑
|
||||
- [ ] 绩效指标分类显示正确(产出指标、效益指标、满意度指标)
|
||||
- [ ] 绩效指标验证规则正常工作(必填项、分值总和100%)
|
||||
- [ ] 保存草稿功能正常
|
||||
- [ ] 提交申报功能正常
|
||||
- [ ] 数据库中申报信息和绩效指标数据完整
|
||||
|
||||
完成这些步骤后,项目申报功能将完全满足业务需求。
|
||||
@@ -1,201 +0,0 @@
|
||||
-- 修复审批记录数据脚本
|
||||
-- 问题:审批人设置错误(使用申报人作为审批人)
|
||||
-- 解决:删除错误记录,为submitted和approving状态的申报重新创建审批记录
|
||||
|
||||
-- ============================================
|
||||
-- 1. 备份现有错误数据(可选)
|
||||
-- ============================================
|
||||
/*
|
||||
CREATE TABLE cw_approval_record_backup_20250801 AS
|
||||
SELECT * FROM cw_approval_record WHERE 1=1;
|
||||
*/
|
||||
|
||||
-- ============================================
|
||||
-- 2. 删除错误的审批记录
|
||||
-- ============================================
|
||||
-- 删除审批人是申报人自己的错误记录
|
||||
DELETE ar FROM cw_approval_record ar
|
||||
INNER JOIN cw_project_application pa ON ar.application_id = pa.id
|
||||
WHERE ar.approver_id = pa.create_by;
|
||||
|
||||
SELECT '删除错误审批记录完成' as status;
|
||||
|
||||
-- ============================================
|
||||
-- 3. 检查需要重新创建审批记录的申报
|
||||
-- ============================================
|
||||
SELECT
|
||||
id,
|
||||
application_no,
|
||||
project_name,
|
||||
approval_status,
|
||||
department_id,
|
||||
'需要重新创建审批记录' as note
|
||||
FROM cw_project_application
|
||||
WHERE approval_status IN ('submitted', 'approving')
|
||||
AND deleted = 0
|
||||
ORDER BY submit_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 4. 为教务处申报创建正确的审批记录(jwc001作为部门审批人)
|
||||
-- ============================================
|
||||
|
||||
-- 获取需要处理的申报ID
|
||||
SET @dept_id = 1938484570826956801; -- 教务处
|
||||
SET @dept_approver_id = 2101; -- jwc001 (教务处处长江建)
|
||||
SET @finance_approver_id = 1; -- admin (临时财务审批人)
|
||||
SET @leader_approver_id = 1; -- admin (临时领导审批人)
|
||||
|
||||
-- 为每个submitted状态的申报创建审批记录
|
||||
INSERT INTO cw_approval_record (
|
||||
id, application_id, application_no, node_code, node_name, node_order,
|
||||
approver_id, approver_name, is_delegate, original_approver_id, original_approver_name,
|
||||
node_status, approve_action, approve_opinion, start_time, end_time, expect_finish_time,
|
||||
is_overtime, config_id, config_snapshot, create_by, create_time, update_by, update_time,
|
||||
create_dept, tenant_id
|
||||
)
|
||||
SELECT
|
||||
-- 为部门审批记录生成ID
|
||||
CONCAT(pa.id, '01') as id,
|
||||
pa.id as application_id,
|
||||
pa.application_no,
|
||||
'dept_approve' as node_code,
|
||||
'部门审批' as node_name,
|
||||
1 as node_order,
|
||||
@dept_approver_id as approver_id,
|
||||
'教务处处长江建' as approver_name,
|
||||
0 as is_delegate,
|
||||
NULL as original_approver_id,
|
||||
NULL as original_approver_name,
|
||||
'waiting' as node_status,
|
||||
NULL as approve_action,
|
||||
NULL as approve_opinion,
|
||||
NOW() as start_time,
|
||||
NULL as end_time,
|
||||
NULL as expect_finish_time,
|
||||
0 as is_overtime,
|
||||
NULL as config_id,
|
||||
NULL as config_snapshot,
|
||||
pa.create_by as create_by,
|
||||
NOW() as create_time,
|
||||
pa.create_by as update_by,
|
||||
NOW() as update_time,
|
||||
pa.create_dept as create_dept,
|
||||
'000000' as tenant_id
|
||||
FROM cw_project_application pa
|
||||
WHERE pa.department_id = @dept_id
|
||||
AND pa.approval_status IN ('submitted', 'approving')
|
||||
AND pa.deleted = 0;
|
||||
|
||||
-- 为每个申报创建财务审批记录(pending状态)
|
||||
INSERT INTO cw_approval_record (
|
||||
id, application_id, application_no, node_code, node_name, node_order,
|
||||
approver_id, approver_name, is_delegate, original_approver_id, original_approver_name,
|
||||
node_status, approve_action, approve_opinion, start_time, end_time, expect_finish_time,
|
||||
is_overtime, config_id, config_snapshot, create_by, create_time, update_by, update_time,
|
||||
create_dept, tenant_id
|
||||
)
|
||||
SELECT
|
||||
-- 为财务审批记录生成ID
|
||||
CONCAT(pa.id, '02') as id,
|
||||
pa.id as application_id,
|
||||
pa.application_no,
|
||||
'finance_approve' as node_code,
|
||||
'财务审批' as node_name,
|
||||
2 as node_order,
|
||||
@finance_approver_id as approver_id,
|
||||
'系统管理员' as approver_name,
|
||||
0 as is_delegate,
|
||||
NULL as original_approver_id,
|
||||
NULL as original_approver_name,
|
||||
'pending' as node_status,
|
||||
NULL as approve_action,
|
||||
NULL as approve_opinion,
|
||||
NOW() as start_time,
|
||||
NULL as end_time,
|
||||
NULL as expect_finish_time,
|
||||
0 as is_overtime,
|
||||
NULL as config_id,
|
||||
NULL as config_snapshot,
|
||||
pa.create_by as create_by,
|
||||
NOW() as create_time,
|
||||
pa.create_by as update_by,
|
||||
NOW() as update_time,
|
||||
pa.create_dept as create_dept,
|
||||
'000000' as tenant_id
|
||||
FROM cw_project_application pa
|
||||
WHERE pa.department_id = @dept_id
|
||||
AND pa.approval_status IN ('submitted', 'approving')
|
||||
AND pa.deleted = 0;
|
||||
|
||||
-- 为每个申报创建领导审批记录(pending状态)
|
||||
INSERT INTO cw_approval_record (
|
||||
id, application_id, application_no, node_code, node_name, node_order,
|
||||
approver_id, approver_name, is_delegate, original_approver_id, original_approver_name,
|
||||
node_status, approve_action, approve_opinion, start_time, end_time, expect_finish_time,
|
||||
is_overtime, config_id, config_snapshot, create_by, create_time, update_by, update_time,
|
||||
create_dept, tenant_id
|
||||
)
|
||||
SELECT
|
||||
-- 为领导审批记录生成ID
|
||||
CONCAT(pa.id, '03') as id,
|
||||
pa.id as application_id,
|
||||
pa.application_no,
|
||||
'leader_approve' as node_code,
|
||||
'领导审批' as node_name,
|
||||
3 as node_order,
|
||||
@leader_approver_id as approver_id,
|
||||
'系统管理员' as approver_name,
|
||||
0 as is_delegate,
|
||||
NULL as original_approver_id,
|
||||
NULL as original_approver_name,
|
||||
'pending' as node_status,
|
||||
NULL as approve_action,
|
||||
NULL as approve_opinion,
|
||||
NOW() as start_time,
|
||||
NULL as end_time,
|
||||
NULL as expect_finish_time,
|
||||
0 as is_overtime,
|
||||
NULL as config_id,
|
||||
NULL as config_snapshot,
|
||||
pa.create_by as create_by,
|
||||
NOW() as create_time,
|
||||
pa.create_by as update_by,
|
||||
NOW() as update_time,
|
||||
pa.create_dept as create_dept,
|
||||
'000000' as tenant_id
|
||||
FROM cw_project_application pa
|
||||
WHERE pa.department_id = @dept_id
|
||||
AND pa.approval_status IN ('submitted', 'approving')
|
||||
AND pa.deleted = 0;
|
||||
|
||||
-- ============================================
|
||||
-- 5. 验证修复结果
|
||||
-- ============================================
|
||||
|
||||
-- 检查审批记录是否正确创建
|
||||
SELECT
|
||||
ar.application_id,
|
||||
ar.application_no,
|
||||
ar.node_code,
|
||||
ar.node_name,
|
||||
ar.node_status,
|
||||
ar.approver_id,
|
||||
ar.approver_name,
|
||||
pa.approval_status
|
||||
FROM cw_approval_record ar
|
||||
INNER JOIN cw_project_application pa ON ar.application_id = pa.id
|
||||
WHERE pa.department_id = @dept_id
|
||||
AND pa.approval_status IN ('submitted', 'approving')
|
||||
ORDER BY ar.application_id, ar.node_order;
|
||||
|
||||
-- 统计修复结果
|
||||
SELECT
|
||||
'修复完成' as status,
|
||||
COUNT(DISTINCT ar.application_id) as fixed_applications,
|
||||
COUNT(*) as total_approval_records
|
||||
FROM cw_approval_record ar
|
||||
INNER JOIN cw_project_application pa ON ar.application_id = pa.id
|
||||
WHERE pa.department_id = @dept_id
|
||||
AND pa.approval_status IN ('submitted', 'approving');
|
||||
|
||||
SELECT '数据修复脚本执行完成' as result;
|
||||
@@ -1,155 +0,0 @@
|
||||
-- 财务项目申报审批状态显示问题排查和修复脚本
|
||||
-- 日期: 2025-01-26
|
||||
-- 说明: 解决审批状态字段显示数字而非中文描述的问题
|
||||
|
||||
-- ==========================================
|
||||
-- 1. 问题排查:检查当前数据状态
|
||||
-- ==========================================
|
||||
|
||||
-- 1.1 查看当前申报记录的状态值分布
|
||||
SELECT
|
||||
approval_status,
|
||||
COUNT(*) as record_count,
|
||||
GROUP_CONCAT(DISTINCT project_name SEPARATOR ', ') as sample_projects
|
||||
FROM cw_project_application
|
||||
GROUP BY approval_status
|
||||
ORDER BY record_count DESC;
|
||||
|
||||
-- 1.2 查看字典类型配置是否存在
|
||||
SELECT * FROM sys_dict_type WHERE dict_type = 'cw_approval_status';
|
||||
|
||||
-- 1.3 查看字典数据配置是否完整
|
||||
SELECT
|
||||
dict_sort,
|
||||
dict_label,
|
||||
dict_value,
|
||||
css_class,
|
||||
status
|
||||
FROM sys_dict_data
|
||||
WHERE dict_type = 'cw_approval_status'
|
||||
ORDER BY dict_sort;
|
||||
|
||||
-- 1.4 检查是否有数字格式的状态值(问题数据)
|
||||
SELECT
|
||||
id,
|
||||
application_no,
|
||||
project_name,
|
||||
approval_status,
|
||||
create_time
|
||||
FROM cw_project_application
|
||||
WHERE approval_status IN ('1', '2', '3', '4', '5', '6', '7')
|
||||
ORDER BY create_time DESC
|
||||
LIMIT 10;
|
||||
|
||||
-- ==========================================
|
||||
-- 2. 问题修复:数据状态值迁移
|
||||
-- ==========================================
|
||||
|
||||
-- 2.1 备份当前数据(可选,建议执行)
|
||||
-- CREATE TABLE cw_project_application_backup_20250126 AS
|
||||
-- SELECT * FROM cw_project_application;
|
||||
|
||||
-- 2.2 修复数字状态值为标准字典值
|
||||
UPDATE cw_project_application
|
||||
SET approval_status = CASE
|
||||
WHEN approval_status = '1' THEN 'draft'
|
||||
WHEN approval_status = '2' THEN 'submitted'
|
||||
WHEN approval_status = '3' THEN 'approving'
|
||||
WHEN approval_status = '4' THEN 'approved'
|
||||
WHEN approval_status = '5' THEN 'rejected'
|
||||
WHEN approval_status = '6' THEN 'returned'
|
||||
WHEN approval_status = '7' THEN 'cancelled'
|
||||
ELSE approval_status
|
||||
END
|
||||
WHERE approval_status IN ('1', '2', '3', '4', '5', '6', '7');
|
||||
|
||||
-- 2.3 验证修复结果
|
||||
SELECT
|
||||
approval_status,
|
||||
COUNT(*) as record_count
|
||||
FROM cw_project_application
|
||||
GROUP BY approval_status
|
||||
ORDER BY record_count DESC;
|
||||
|
||||
-- ==========================================
|
||||
-- 3. 字典配置检查和修复
|
||||
-- ==========================================
|
||||
|
||||
-- 3.1 确保字典类型存在
|
||||
INSERT IGNORE INTO sys_dict_type VALUES
|
||||
(3000003, '审批状态', 'cw_approval_status', '0', NULL, '财务预算-审批状态', now(), NULL, NULL, '审批状态分类');
|
||||
|
||||
-- 3.2 确保字典数据完整
|
||||
INSERT IGNORE INTO sys_dict_data VALUES
|
||||
(3000031, 0, '草稿', 'draft', 'cw_approval_status', '', '', 'N', '0', '0', '', '草稿状态', now(), NULL, NULL, NULL),
|
||||
(3000032, 1, '已提交', 'submitted', 'cw_approval_status', '', '', 'N', '0', '0', '', '已提交审批', now(), NULL, NULL, NULL),
|
||||
(3000033, 2, '审批中', 'approving', 'cw_approval_status', '', 'blue', 'N', '0', '0', '', '审批中', now(), NULL, NULL, NULL),
|
||||
(3000034, 3, '已通过', 'approved', 'cw_approval_status', '', 'green', 'N', '0', '0', '', '审批通过', now(), NULL, NULL, NULL),
|
||||
(3000035, 4, '已拒绝', 'rejected', 'cw_approval_status', '', 'red', 'N', '0', '0', '', '审批拒绝', now(), NULL, NULL, NULL),
|
||||
(3000036, 5, '已退回', 'returned', 'cw_approval_status', '', 'orange', 'N', '0', '0', '', '退回修改', now(), NULL, NULL, NULL),
|
||||
(3000037, 6, '已取消', 'cancelled', 'cw_approval_status', '', 'gray', 'N', '0', '0', '', '取消申报', now(), NULL, NULL, NULL);
|
||||
|
||||
-- ==========================================
|
||||
-- 4. 清理和重置缓存(应用层面)
|
||||
-- ==========================================
|
||||
|
||||
-- 4.1 更新字典缓存标记(如果系统支持)
|
||||
-- UPDATE sys_config SET config_value = now() WHERE config_key = 'sys.dict.cache.refresh';
|
||||
|
||||
-- ==========================================
|
||||
-- 5. 验证最终结果
|
||||
-- ==========================================
|
||||
|
||||
-- 5.1 验证所有申报记录的状态值都是标准格式
|
||||
SELECT
|
||||
'修复后状态分布' as check_item,
|
||||
approval_status,
|
||||
COUNT(*) as record_count
|
||||
FROM cw_project_application
|
||||
GROUP BY approval_status
|
||||
ORDER BY record_count DESC;
|
||||
|
||||
-- 5.2 验证字典配置完整性
|
||||
SELECT
|
||||
'字典配置验证' as check_item,
|
||||
dict_label,
|
||||
dict_value
|
||||
FROM sys_dict_data
|
||||
WHERE dict_type = 'cw_approval_status'
|
||||
AND status = '0'
|
||||
ORDER BY dict_sort;
|
||||
|
||||
-- 5.3 检查是否还有问题数据
|
||||
SELECT
|
||||
'问题数据检查' as check_item,
|
||||
COUNT(*) as problem_count
|
||||
FROM cw_project_application
|
||||
WHERE approval_status IN ('1', '2', '3', '4', '5', '6', '7');
|
||||
|
||||
-- ==========================================
|
||||
-- 使用说明
|
||||
-- ==========================================
|
||||
/*
|
||||
执行步骤:
|
||||
|
||||
1. 先执行排查SQL(第1部分),查看当前数据状态
|
||||
- 如果approval_status列显示的是数字(1,2,7等),说明需要数据迁移
|
||||
- 如果已经是英文(draft,submitted等),说明数据正确
|
||||
|
||||
2. 如果需要修复,执行第2部分的UPDATE语句
|
||||
- 建议先执行备份语句
|
||||
- 然后执行状态值迁移
|
||||
|
||||
3. 执行第3部分确保字典配置完整
|
||||
|
||||
4. 执行第5部分验证修复结果
|
||||
|
||||
5. 重启后端服务和清理前端缓存
|
||||
- cd backend && mvn spring-boot:run -pl ruoyi-admin
|
||||
- cd web && npm run dev
|
||||
|
||||
注意事项:
|
||||
- 执行前请先备份数据库
|
||||
- 如果有正在进行的审批流程,请谨慎操作
|
||||
- 修复后需要重启服务以清理缓存
|
||||
*/
|
||||
@@ -1,181 +0,0 @@
|
||||
-- 修复jwc001用户审批权限问题
|
||||
-- 创建时间: 2025-01-27
|
||||
-- 问题: jwc001用户无法访问"部门审批管理"功能,API调用时提示权限不足
|
||||
|
||||
-- =====================================================
|
||||
-- 问题分析
|
||||
-- =====================================================
|
||||
-- jwc001用户点击"部门审批管理"页面时,前端调用以下API接口:
|
||||
-- 1. /cw/approval/list (需要 cw:approval:list 权限) ✅ 已有
|
||||
-- 2. /cw/approval/dept/pending (需要 cw:approval:dept 权限) ❌ 缺失
|
||||
-- 3. /cw/approval/my (需要 cw:approval:list 权限) ✅ 已有
|
||||
--
|
||||
-- 根本原因:jwc001用户的角色缺少 cw:approval:dept 权限
|
||||
|
||||
-- =====================================================
|
||||
-- 第一步:确认当前状态
|
||||
-- =====================================================
|
||||
-- 查询jwc001用户信息
|
||||
SELECT
|
||||
u.user_id,
|
||||
u.user_name,
|
||||
u.nick_name,
|
||||
u.dept_id,
|
||||
d.dept_name
|
||||
FROM sys_user u
|
||||
LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
|
||||
WHERE u.user_name = 'jwc001';
|
||||
|
||||
-- 查询jwc001用户当前角色
|
||||
SELECT
|
||||
u.user_name,
|
||||
r.role_id,
|
||||
r.role_name,
|
||||
r.role_key
|
||||
FROM sys_user u
|
||||
JOIN sys_user_role ur ON u.user_id = ur.user_id
|
||||
JOIN sys_role r ON ur.role_id = r.role_id
|
||||
WHERE u.user_name = 'jwc001';
|
||||
|
||||
-- 查询当前角色拥有的财务审批相关权限
|
||||
SELECT DISTINCT
|
||||
r.role_name,
|
||||
m.menu_name,
|
||||
m.perms
|
||||
FROM sys_user u
|
||||
JOIN sys_user_role ur ON u.user_id = ur.user_id
|
||||
JOIN sys_role r ON ur.role_id = r.role_id
|
||||
JOIN sys_role_menu rm ON r.role_id = rm.role_id
|
||||
JOIN sys_menu m ON rm.menu_id = m.menu_id
|
||||
WHERE u.user_name = 'jwc001'
|
||||
AND m.perms LIKE 'cw:approval:%'
|
||||
ORDER BY m.perms;
|
||||
|
||||
-- =====================================================
|
||||
-- 第二步:查找缺失的权限
|
||||
-- =====================================================
|
||||
-- 查找所有财务审批相关的菜单权限
|
||||
SELECT
|
||||
menu_id,
|
||||
menu_name,
|
||||
perms,
|
||||
parent_id
|
||||
FROM sys_menu
|
||||
WHERE perms LIKE 'cw:approval:%'
|
||||
ORDER BY menu_id;
|
||||
|
||||
-- 特别检查部门审批权限菜单
|
||||
SELECT
|
||||
menu_id,
|
||||
menu_name,
|
||||
perms,
|
||||
component
|
||||
FROM sys_menu
|
||||
WHERE perms = 'cw:approval:dept';
|
||||
|
||||
-- =====================================================
|
||||
-- 第三步:修复权限配置
|
||||
-- =====================================================
|
||||
-- 方案1:如果部门审批员角色已存在,直接分配权限
|
||||
-- 查找部门审批员角色
|
||||
SELECT role_id, role_name, role_key
|
||||
FROM sys_role
|
||||
WHERE role_key = 'dept_approver' OR role_name LIKE '%部门审批%';
|
||||
|
||||
-- 如果角色存在,为该角色添加缺失的权限
|
||||
-- (假设部门审批员角色ID为100,需要根据实际情况调整)
|
||||
INSERT IGNORE INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT r.role_id, m.menu_id
|
||||
FROM sys_role r, sys_menu m
|
||||
WHERE r.role_key = 'dept_approver'
|
||||
AND m.perms = 'cw:approval:dept'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM sys_role_menu rm
|
||||
WHERE rm.role_id = r.role_id AND rm.menu_id = m.menu_id
|
||||
);
|
||||
|
||||
-- 方案2:如果部门审批员角色不存在,创建角色并分配权限
|
||||
-- 创建部门审批员角色(如果不存在)
|
||||
INSERT INTO sys_role (
|
||||
role_id, tenant_id, role_name, role_key, role_sort,
|
||||
data_scope, menu_check_strictly, dept_check_strictly,
|
||||
status, del_flag, create_dept, create_by, create_time,
|
||||
update_by, update_time, remark
|
||||
)
|
||||
SELECT
|
||||
100, '000000', '部门审批员', 'dept_approver', 4,
|
||||
'3', 1, 1, '0', '0', 1938484570826956801, 1, NOW(),
|
||||
1, NOW(), '负责本部门的财务项目申报审批工作'
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM sys_role WHERE role_key = 'dept_approver'
|
||||
);
|
||||
|
||||
-- 为部门审批员角色分配必要的菜单权限
|
||||
INSERT IGNORE INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT 100, menu_id FROM sys_menu WHERE perms IN (
|
||||
'cw:approval:list', -- 查询审批记录列表
|
||||
'cw:approval:query', -- 查询审批记录详情
|
||||
'cw:approval:dept', -- 部门审批权限
|
||||
'cw:approval:export' -- 导出审批记录
|
||||
);
|
||||
|
||||
-- 为部门审批员角色分配财务管理相关的父级菜单权限
|
||||
INSERT IGNORE INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT 100, menu_id FROM sys_menu WHERE menu_id IN (
|
||||
6000, -- 财务管理(如果存在)
|
||||
6100, -- 财务项目申报管理(如果存在)
|
||||
6200 -- 部门审批管理(如果存在)
|
||||
);
|
||||
|
||||
-- 确保jwc001用户拥有部门审批员角色
|
||||
INSERT IGNORE INTO sys_user_role (user_id, role_id)
|
||||
SELECT u.user_id, 100
|
||||
FROM sys_user u
|
||||
WHERE u.user_name = 'jwc001'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM sys_user_role ur
|
||||
WHERE ur.user_id = u.user_id AND ur.role_id = 100
|
||||
);
|
||||
|
||||
-- =====================================================
|
||||
-- 第四步:验证修复结果
|
||||
-- =====================================================
|
||||
-- 验证jwc001用户最终的权限配置
|
||||
SELECT
|
||||
u.user_name AS '用户名',
|
||||
r.role_name AS '角色名',
|
||||
m.menu_name AS '菜单名',
|
||||
m.perms AS '权限码'
|
||||
FROM sys_user u
|
||||
JOIN sys_user_role ur ON u.user_id = ur.user_id
|
||||
JOIN sys_role r ON ur.role_id = r.role_id
|
||||
JOIN sys_role_menu rm ON r.role_id = rm.role_id
|
||||
JOIN sys_menu m ON rm.menu_id = m.menu_id
|
||||
WHERE u.user_name = 'jwc001'
|
||||
AND m.perms LIKE 'cw:approval:%'
|
||||
ORDER BY m.perms;
|
||||
|
||||
-- 特别验证部门审批权限
|
||||
SELECT
|
||||
COUNT(*) AS '是否有部门审批权限',
|
||||
CASE
|
||||
WHEN COUNT(*) > 0 THEN '✅ 权限配置正确'
|
||||
ELSE '❌ 权限配置缺失'
|
||||
END AS '配置状态'
|
||||
FROM sys_user u
|
||||
JOIN sys_user_role ur ON u.user_id = ur.user_id
|
||||
JOIN sys_role r ON ur.role_id = r.role_id
|
||||
JOIN sys_role_menu rm ON r.role_id = rm.role_id
|
||||
JOIN sys_menu m ON rm.menu_id = m.menu_id
|
||||
WHERE u.user_name = 'jwc001'
|
||||
AND m.perms = 'cw:approval:dept';
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- =====================================================
|
||||
-- 预期结果
|
||||
-- =====================================================
|
||||
-- 1. jwc001用户获得完整的部门审批权限
|
||||
-- 2. 能够正常访问 /cw/approval/dept/pending 接口
|
||||
-- 3. "部门审批管理"页面加载正常,不再报权限错误
|
||||
-- 4. 可以查看待审批和已审批记录
|
||||
@@ -1,16 +0,0 @@
|
||||
-- ==================================================
|
||||
-- 修复招生就业模块表,补充缺失的基础字段
|
||||
-- 创建日期: 2025-01-26
|
||||
-- ==================================================
|
||||
|
||||
-- 1. 为 `zsjy_job_position` 表添加 `create_dept` 字段
|
||||
ALTER TABLE `zsjy_job_position` ADD COLUMN `create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门';
|
||||
|
||||
-- 2. 为 `zsjy_recruitment_poster` 表添加 `create_dept` 字段
|
||||
ALTER TABLE `zsjy_recruitment_poster` ADD COLUMN `create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门';
|
||||
|
||||
-- ==================================================
|
||||
-- 检查语句
|
||||
-- ==================================================
|
||||
DESC `zsjy_job_position`;
|
||||
DESC `zsjy_recruitment_poster`;
|
||||
@@ -1,55 +0,0 @@
|
||||
-- 学生请假审批状态字典配置脚本
|
||||
-- 日期: 2025-01-27
|
||||
-- 说明: 创建学生请假审批状态字典类型和数据
|
||||
|
||||
-- 1. 创建字典类型
|
||||
INSERT IGNORE INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_by, create_time, remark)
|
||||
VALUES (2000000000000000001, '000000', '学生请假审批状态', 'student_leave_approval_status', 1, now(), '学生请假申请审批状态字典');
|
||||
|
||||
-- 2. 创建字典数据
|
||||
-- 已撤销
|
||||
INSERT IGNORE INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (2000000000000001001, '000000', 1, '已撤销', 'cancel', 'student_leave_approval_status', '', 'info', 'N', 1, now(), '已撤销');
|
||||
|
||||
-- 草稿
|
||||
INSERT IGNORE INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (2000000000000001002, '000000', 2, '草稿', 'draft', 'student_leave_approval_status', '', 'info', 'N', 1, now(), '草稿状态');
|
||||
|
||||
-- 待审核
|
||||
INSERT IGNORE INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (2000000000000001003, '000000', 3, '待审核', 'waiting', 'student_leave_approval_status', '', 'warning', 'N', 1, now(), '待审核状态');
|
||||
|
||||
-- 已完成
|
||||
INSERT IGNORE INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (2000000000000001004, '000000', 4, '已完成', 'finish', 'student_leave_approval_status', '', 'success', 'N', 1, now(), '已完成状态');
|
||||
|
||||
-- 已作废
|
||||
INSERT IGNORE INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (2000000000000001005, '000000', 5, '已作废', 'invalid', 'student_leave_approval_status', '', 'danger', 'N', 1, now(), '已作废状态');
|
||||
|
||||
-- 已退回
|
||||
INSERT IGNORE INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (2000000000000001006, '000000', 6, '已退回', 'back', 'student_leave_approval_status', '', 'warning', 'N', 1, now(), '已退回状态');
|
||||
|
||||
-- 已终止
|
||||
INSERT IGNORE INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (2000000000000001007, '000000', 7, '已终止', 'termination', 'student_leave_approval_status', '', 'danger', 'N', 1, now(), '已终止状态');
|
||||
|
||||
-- 3. 验证创建结果
|
||||
SELECT
|
||||
'字典类型' as check_item,
|
||||
dict_name,
|
||||
dict_type,
|
||||
create_time
|
||||
FROM sys_dict_type
|
||||
WHERE dict_type = 'student_leave_approval_status';
|
||||
|
||||
SELECT
|
||||
'字典数据' as check_item,
|
||||
dict_sort,
|
||||
dict_label,
|
||||
dict_value,
|
||||
list_class
|
||||
FROM sys_dict_data
|
||||
WHERE dict_type = 'student_leave_approval_status'
|
||||
ORDER BY dict_sort;
|
||||
@@ -1,15 +0,0 @@
|
||||
-- 调整财务项目预算审批菜单结构
|
||||
-- 目标:去掉"财务管理"一级目录,让"项目预算审批"直接作为一级目录
|
||||
|
||||
-- 1. 将"项目预算审批"菜单设置为一级菜单(parent_id改为0)
|
||||
UPDATE sys_menu SET parent_id = 0, order_num = 8 WHERE menu_id = 5100;
|
||||
|
||||
-- 2. 删除"财务管理"一级目录(如果没有其他子菜单的话)
|
||||
DELETE FROM sys_menu WHERE menu_id = 5000;
|
||||
|
||||
-- 3. 确认菜单结构调整后的完整性
|
||||
-- 查看调整后的菜单结构
|
||||
SELECT menu_id, parent_id, menu_name, menu_type, order_num, path
|
||||
FROM sys_menu
|
||||
WHERE menu_id >= 5100 AND menu_id <= 5200
|
||||
ORDER BY parent_id, order_num;
|
||||
@@ -1,46 +0,0 @@
|
||||
-- 财务项目申报审批功能字典数据创建脚本
|
||||
-- 日期: 2025-01-26
|
||||
-- 说明: 创建审批动作和审批节点状态字典数据
|
||||
|
||||
-- 1. 审批动作字典数据
|
||||
-- 审批动作字典类型
|
||||
INSERT INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_by, create_time, remark)
|
||||
VALUES (1950000000000000001, '000000', '审批动作', 'cw_approve_action', 1, now(), '财务项目申报审批动作字典');
|
||||
|
||||
-- 审批动作字典数据
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (1950000000000001001, '000000', 1, '通过', 'approve', 'cw_approve_action', '', 'success', 'N', 1, now(), '审批通过');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (1950000000000001002, '000000', 2, '拒绝', 'reject', 'cw_approve_action', '', 'danger', 'N', 1, now(), '审批拒绝');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (1950000000000001003, '000000', 3, '退回', 'return', 'cw_approve_action', '', 'warning', 'N', 1, now(), '退回修改');
|
||||
|
||||
-- 2. 审批节点状态字典数据
|
||||
-- 审批节点状态字典类型
|
||||
INSERT INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_by, create_time, remark)
|
||||
VALUES (1950000000000000002, '000000', '审批节点状态', 'cw_node_status', 1, now(), '财务项目申报审批节点状态字典');
|
||||
|
||||
-- 审批节点状态字典数据
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (1950000000000002001, '000000', 1, '等待审批', 'waiting', 'cw_node_status', '', 'info', 'N', 1, now(), '等待审批');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (1950000000000002002, '000000', 2, '审批中', 'approving', 'cw_node_status', '', 'primary', 'N', 1, now(), '正在审批');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (1950000000000002003, '000000', 3, '已通过', 'approved', 'cw_node_status', '', 'success', 'N', 1, now(), '审批通过');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (1950000000000002004, '000000', 4, '已拒绝', 'rejected', 'cw_node_status', '', 'danger', 'N', 1, now(), '审批拒绝');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (1950000000000002005, '000000', 5, '已退回', 'returned', 'cw_node_status', '', 'warning', 'N', 1, now(), '已退回');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (1950000000000002006, '000000', 6, '未开始', 'pending', 'cw_node_status', '', 'info', 'N', 1, now(), '未开始');
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- 字典数据创建完成
|
||||
@@ -1,84 +0,0 @@
|
||||
-- 财务项目申报部门审批功能菜单创建脚本(仅创建菜单,不赋权)
|
||||
-- 日期: 2025-01-26
|
||||
-- 说明: 在"项目申报管理"(6000)下创建部门审批功能菜单
|
||||
|
||||
-- 1. 部门审批管理菜单 (在项目申报管理下新增子菜单)
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (6200, '部门审批管理', 6000, 2, 'deptApproval', 'cw/deptApproval/index', '', 1, 0, 'C', '0', '0', 'cw:approval:dept', 'ele-Checked', 1, now(), 1, now(), '财务项目申报部门审批管理');
|
||||
|
||||
-- 2. 部门审批管理功能按钮
|
||||
-- 查询
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (6201, '部门审批查询', 6200, 1, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:dept', '#', 1, now(), 1, now(), '');
|
||||
|
||||
-- 审批操作
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (6202, '部门审批操作', 6200, 2, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:process', '#', 1, now(), 1, now(), '');
|
||||
|
||||
-- 审批详情查看
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (6203, '审批详情查看', 6200, 3, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:query', '#', 1, now(), 1, now(), '');
|
||||
|
||||
-- 3. 审批记录管理菜单 (给管理员查看所有审批记录)
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (6210, '审批记录管理', 6000, 3, 'approvalRecord', 'cw/approvalRecord/index', '', 1, 0, 'C', '0', '0', 'cw:approval:list', 'ele-List', 1, now(), 1, now(), '财务项目申报审批记录管理');
|
||||
|
||||
-- 4. 审批记录管理功能按钮
|
||||
-- 查询
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (6211, '审批记录查询', 6210, 1, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:list', '#', 1, now(), 1, now(), '');
|
||||
|
||||
-- 导出
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (6212, '审批记录导出', 6210, 2, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:export', '#', 1, now(), 1, now(), '');
|
||||
|
||||
-- 详情
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (6213, '审批记录详情', 6210, 3, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:query', '#', 1, now(), 1, now(), '');
|
||||
|
||||
-- 5. 创建审批流程权限 (系统管理员)
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (6214, '创建审批流程', 6210, 4, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:create', '#', 1, now(), 1, now(), '');
|
||||
|
||||
-- 6. 审批动作字典数据
|
||||
-- 审批动作字典类型
|
||||
INSERT INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_by, create_time, remark)
|
||||
VALUES (1950000000000000001, '000000', '审批动作', 'cw_approve_action', 1, now(), '财务项目申报审批动作字典');
|
||||
|
||||
-- 审批动作字典数据
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (1950000000000001001, '000000', 1, '通过', 'approve', 'cw_approve_action', '', 'success', 'N', 1, now(), '审批通过');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (1950000000000001002, '000000', 2, '拒绝', 'reject', 'cw_approve_action', '', 'danger', 'N', 1, now(), '审批拒绝');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (1950000000000001003, '000000', 3, '退回', 'return', 'cw_approve_action', '', 'warning', 'N', 1, now(), '退回修改');
|
||||
|
||||
-- 7. 审批节点状态字典数据
|
||||
-- 审批节点状态字典类型
|
||||
INSERT INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_by, create_time, remark)
|
||||
VALUES (1950000000000000002, '000000', '审批节点状态', 'cw_node_status', 1, now(), '财务项目申报审批节点状态字典');
|
||||
|
||||
-- 审批节点状态字典数据
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (1950000000000002001, '000000', 1, '等待审批', 'waiting', 'cw_node_status', '', 'info', 'N', 1, now(), '等待审批');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (1950000000000002002, '000000', 2, '审批中', 'approving', 'cw_node_status', '', 'primary', 'N', 1, now(), '正在审批');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (1950000000000002003, '000000', 3, '已通过', 'approved', 'cw_node_status', '', 'success', 'N', 1, now(), '审批通过');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (1950000000000002004, '000000', 4, '已拒绝', 'rejected', 'cw_node_status', '', 'danger', 'N', 1, now(), '审批拒绝');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (1950000000000002005, '000000', 5, '已退回', 'returned', 'cw_node_status', '', 'warning', 'N', 1, now(), '已退回');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (1950000000000002006, '000000', 6, '未开始', 'pending', 'cw_node_status', '', 'info', 'N', 1, now(), '未开始');
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- 菜单创建完成,等待后续手动赋权操作
|
||||
@@ -1,94 +0,0 @@
|
||||
-- 财务项目申报部门审批功能菜单权限配置
|
||||
-- 日期: 2025-01-26
|
||||
|
||||
-- 1. 部门审批管理菜单 (在财务项目申报管理下新增子菜单)
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark, tenant_id)
|
||||
VALUES (6200, '部门审批管理', 6100, 2, 'deptApproval', 'cw/deptApproval/index', '', 1, 0, 'C', '0', '0', 'cw:approval:dept', 'ele-Checked', 1, sysdate(), 1, sysdate(), '财务项目申报部门审批管理', '000000');
|
||||
|
||||
-- 2. 部门审批管理功能按钮
|
||||
-- 查询
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark, tenant_id)
|
||||
VALUES (6201, '部门审批查询', 6200, 1, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:dept', '#', 1, sysdate(), 1, sysdate(), '', '000000');
|
||||
|
||||
-- 审批操作
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark, tenant_id)
|
||||
VALUES (6202, '部门审批操作', 6200, 2, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:process', '#', 1, sysdate(), 1, sysdate(), '', '000000');
|
||||
|
||||
-- 审批详情查看
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark, tenant_id)
|
||||
VALUES (6203, '审批详情查看', 6200, 3, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:query', '#', 1, sysdate(), 1, sysdate(), '', '000000');
|
||||
|
||||
-- 3. 审批记录管理菜单 (给管理员查看所有审批记录)
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark, tenant_id)
|
||||
VALUES (6210, '审批记录管理', 6100, 3, 'approvalRecord', 'cw/approvalRecord/index', '', 1, 0, 'C', '0', '0', 'cw:approval:list', 'ele-List', 1, sysdate(), 1, sysdate(), '财务项目申报审批记录管理', '000000');
|
||||
|
||||
-- 4. 审批记录管理功能按钮
|
||||
-- 查询
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark, tenant_id)
|
||||
VALUES (6211, '审批记录查询', 6210, 1, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:list', '#', 1, sysdate(), 1, sysdate(), '', '000000');
|
||||
|
||||
-- 导出
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark, tenant_id)
|
||||
VALUES (6212, '审批记录导出', 6210, 2, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:export', '#', 1, sysdate(), 1, sysdate(), '', '000000');
|
||||
|
||||
-- 详情
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark, tenant_id)
|
||||
VALUES (6213, '审批记录详情', 6210, 3, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:query', '#', 1, sysdate(), 1, sysdate(), '', '000000');
|
||||
|
||||
-- 5. 创建审批流程权限 (系统管理员)
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark, tenant_id)
|
||||
VALUES (6214, '创建审批流程', 6210, 4, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:create', '#', 1, sysdate(), 1, sysdate(), '', '000000');
|
||||
|
||||
-- 6. 将新菜单分配给超级管理员角色 (role_id = 1)
|
||||
INSERT INTO sys_role_menu(role_id, menu_id) VALUES (1, 6200);
|
||||
INSERT INTO sys_role_menu(role_id, menu_id) VALUES (1, 6201);
|
||||
INSERT INTO sys_role_menu(role_id, menu_id) VALUES (1, 6202);
|
||||
INSERT INTO sys_role_menu(role_id, menu_id) VALUES (1, 6203);
|
||||
INSERT INTO sys_role_menu(role_id, menu_id) VALUES (1, 6210);
|
||||
INSERT INTO sys_role_menu(role_id, menu_id) VALUES (1, 6211);
|
||||
INSERT INTO sys_role_menu(role_id, menu_id) VALUES (1, 6212);
|
||||
INSERT INTO sys_role_menu(role_id, menu_id) VALUES (1, 6213);
|
||||
INSERT INTO sys_role_menu(role_id, menu_id) VALUES (1, 6214);
|
||||
|
||||
-- 7. 审批动作字典数据
|
||||
-- 检查是否已存在审批动作字典类型
|
||||
INSERT INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_by, create_time, remark)
|
||||
VALUES (nextval('sys_dict_type_seq'), '000000', '审批动作', 'cw_approve_action', 1, sysdate(), '财务项目申报审批动作字典')
|
||||
ON DUPLICATE KEY UPDATE dict_name = VALUES(dict_name);
|
||||
|
||||
-- 审批动作字典数据
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (nextval('sys_dict_data_seq'), '000000', 1, '通过', 'approve', 'cw_approve_action', '', 'success', 'N', 1, sysdate(), '审批通过');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (nextval('sys_dict_data_seq'), '000000', 2, '拒绝', 'reject', 'cw_approve_action', '', 'danger', 'N', 1, sysdate(), '审批拒绝');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (nextval('sys_dict_data_seq'), '000000', 3, '退回', 'return', 'cw_approve_action', '', 'warning', 'N', 1, sysdate(), '退回修改');
|
||||
|
||||
-- 8. 审批节点状态字典数据
|
||||
-- 检查是否已存在审批节点状态字典类型
|
||||
INSERT INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_by, create_time, remark)
|
||||
VALUES (nextval('sys_dict_type_seq'), '000000', '审批节点状态', 'cw_node_status', 1, sysdate(), '财务项目申报审批节点状态字典')
|
||||
ON DUPLICATE KEY UPDATE dict_name = VALUES(dict_name);
|
||||
|
||||
-- 审批节点状态字典数据
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (nextval('sys_dict_data_seq'), '000000', 1, '等待审批', 'waiting', 'cw_node_status', '', 'info', 'N', 1, sysdate(), '等待审批');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (nextval('sys_dict_data_seq'), '000000', 2, '审批中', 'approving', 'cw_node_status', '', 'primary', 'N', 1, sysdate(), '正在审批');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (nextval('sys_dict_data_seq'), '000000', 3, '已通过', 'approved', 'cw_node_status', '', 'success', 'N', 1, sysdate(), '审批通过');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (nextval('sys_dict_data_seq'), '000000', 4, '已拒绝', 'rejected', 'cw_node_status', '', 'danger', 'N', 1, sysdate(), '审批拒绝');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (nextval('sys_dict_data_seq'), '000000', 5, '已退回', 'returned', 'cw_node_status', '', 'warning', 'N', 1, sysdate(), '已退回');
|
||||
|
||||
INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (nextval('sys_dict_data_seq'), '000000', 6, '未开始', 'pending', 'cw_node_status', '', 'info', 'N', 1, sysdate(), '未开始');
|
||||
|
||||
COMMIT;
|
||||
@@ -1,447 +0,0 @@
|
||||
-- ==========================================
|
||||
-- 财务项目预算审批系统 - 数据库创建脚本
|
||||
-- 版本: 1.0
|
||||
-- 创建时间: 2025-01-26
|
||||
-- 表名前缀: cw_ (财务模块)
|
||||
-- ==========================================
|
||||
|
||||
-- 设置字符集
|
||||
SET NAMES utf8mb4;
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
-- ==========================================
|
||||
-- 1. 创建核心数据表
|
||||
-- ==========================================
|
||||
|
||||
-- 1.1 项目预算申报表
|
||||
DROP TABLE IF EXISTS `cw_project_application`;
|
||||
CREATE TABLE `cw_project_application` (
|
||||
`id` bigint(20) NOT NULL COMMENT '主键ID(雪花ID)',
|
||||
`application_no` varchar(50) NOT NULL COMMENT '申报编号(自动生成,格式:CW-YYYY-MMDD-XXXX)',
|
||||
|
||||
-- 项目基本信息
|
||||
`project_name` varchar(200) NOT NULL COMMENT '项目名称',
|
||||
`principal` varchar(50) NOT NULL COMMENT '项目负责人',
|
||||
`contact_phone` varchar(20) COMMENT '联系电话',
|
||||
`start_date` date COMMENT '项目实施开始时间',
|
||||
`end_date` date COMMENT '项目实施结束时间',
|
||||
|
||||
-- 项目特性
|
||||
`target_sme` tinyint(1) DEFAULT 0 COMMENT '是否面向中小企业(0-否 1-是)',
|
||||
`target_micro_sme` tinyint(1) DEFAULT 0 COMMENT '是否面向小微企业(0-否 1-是)',
|
||||
`import_equipment` tinyint(1) DEFAULT 0 COMMENT '是否采购进口设备(0-否 1-是)',
|
||||
|
||||
-- 项目详细内容
|
||||
`application_reason` text COMMENT '项目申请原因(包括设立依据、可行性及必要性)',
|
||||
`construction_content` text COMMENT '项目建设内容',
|
||||
`total_performance_goal` text COMMENT '绩效总目标',
|
||||
`current_year_goal` text COMMENT '当年绩效目标',
|
||||
|
||||
-- 资金来源汇总(万元)
|
||||
-- 项目总投资
|
||||
`total_investment` decimal(15,2) DEFAULT 0.00 COMMENT '项目总投资-合计(万元)',
|
||||
`total_municipal_fiscal` decimal(15,2) DEFAULT 0.00 COMMENT '项目总投资-市财政资金(万元)',
|
||||
`total_self_funding` decimal(15,2) DEFAULT 0.00 COMMENT '项目总投资-单位自筹(万元)',
|
||||
`total_other_funding` decimal(15,2) DEFAULT 0.00 COMMENT '项目总投资-其他(万元)',
|
||||
|
||||
-- 年度预计执行数
|
||||
`current_year_total` decimal(15,2) DEFAULT 0.00 COMMENT '年度预计执行数-合计(万元)',
|
||||
`current_year_municipal` decimal(15,2) DEFAULT 0.00 COMMENT '年度预计执行数-市财政资金(万元)',
|
||||
`current_year_self` decimal(15,2) DEFAULT 0.00 COMMENT '年度预计执行数-单位自筹(万元)',
|
||||
`current_year_other` decimal(15,2) DEFAULT 0.00 COMMENT '年度预计执行数-其他(万元)',
|
||||
|
||||
-- 下年度申请资金
|
||||
`next_year_total` decimal(15,2) DEFAULT 0.00 COMMENT '下年度申请资金-合计(万元)',
|
||||
`next_year_municipal` decimal(15,2) DEFAULT 0.00 COMMENT '下年度申请资金-市财政资金(万元)',
|
||||
`next_year_self` decimal(15,2) DEFAULT 0.00 COMMENT '下年度申请资金-单位自筹(万元)',
|
||||
`next_year_other` decimal(15,2) DEFAULT 0.00 COMMENT '下年度申请资金-其他(万元)',
|
||||
|
||||
-- 以后年度安排
|
||||
`future_year_total` decimal(15,2) DEFAULT 0.00 COMMENT '以后年度安排-合计(万元)',
|
||||
`future_year_municipal` decimal(15,2) DEFAULT 0.00 COMMENT '以后年度安排-市财政资金(万元)',
|
||||
`future_year_self` decimal(15,2) DEFAULT 0.00 COMMENT '以后年度安排-单位自筹(万元)',
|
||||
`future_year_other` decimal(15,2) DEFAULT 0.00 COMMENT '以后年度安排-其他(万元)',
|
||||
|
||||
-- 申请人信息
|
||||
`applicant_id` bigint(20) NOT NULL COMMENT '申请人ID',
|
||||
`applicant_name` varchar(50) NOT NULL COMMENT '申请人姓名',
|
||||
`department_id` bigint(20) NOT NULL COMMENT '所属部门ID',
|
||||
`department_name` varchar(100) NOT NULL COMMENT '所属部门名称',
|
||||
|
||||
-- 审批相关
|
||||
`approval_status` varchar(20) DEFAULT 'draft' COMMENT '审批状态(draft-草稿 submitted-已提交 approving-审批中 approved-已通过 rejected-已拒绝 returned-已退回 cancelled-已取消)',
|
||||
`submit_time` datetime COMMENT '提交时间',
|
||||
`approve_time` datetime COMMENT '审批完成时间',
|
||||
`year` int(11) NOT NULL COMMENT '预算年度',
|
||||
|
||||
-- 系统字段
|
||||
`remark` varchar(500) COMMENT '备注',
|
||||
`create_by` bigint(20) COMMENT '创建者',
|
||||
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` bigint(20) COMMENT '更新者',
|
||||
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`create_dept` bigint(20) COMMENT '创建部门',
|
||||
`deleted` tinyint(1) DEFAULT 0 COMMENT '删除标志(0-正常 1-删除)',
|
||||
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户ID',
|
||||
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_application_no` (`application_no`, `tenant_id`, `deleted`),
|
||||
KEY `idx_applicant_id` (`applicant_id`),
|
||||
KEY `idx_department_id` (`department_id`),
|
||||
KEY `idx_status_year` (`approval_status`, `year`),
|
||||
KEY `idx_create_time` (`create_time`),
|
||||
KEY `idx_submit_time` (`submit_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='项目预算申报表';
|
||||
|
||||
-- 1.2 审批人配置表
|
||||
DROP TABLE IF EXISTS `cw_approver_config`;
|
||||
CREATE TABLE `cw_approver_config` (
|
||||
`id` bigint(20) NOT NULL COMMENT '主键ID',
|
||||
`config_name` varchar(100) NOT NULL COMMENT '配置名称',
|
||||
`config_type` varchar(20) NOT NULL COMMENT '配置类型(global-全局配置 dept-按部门配置 amount-按金额配置)',
|
||||
|
||||
-- 配置条件
|
||||
`dept_id` bigint(20) COMMENT '部门ID(按部门配置时使用)',
|
||||
`dept_name` varchar(100) COMMENT '部门名称(按部门配置时使用)',
|
||||
`min_amount` decimal(15,2) COMMENT '最小金额(按金额配置时使用,万元)',
|
||||
`max_amount` decimal(15,2) COMMENT '最大金额(按金额配置时使用,万元)',
|
||||
|
||||
-- 3个固定审批节点的审批人
|
||||
-- 部门审批(第1节点)
|
||||
`dept_approver_id` bigint(20) COMMENT '部门审批人ID',
|
||||
`dept_approver_name` varchar(50) COMMENT '部门审批人姓名',
|
||||
`dept_delegate_id` bigint(20) COMMENT '部门代理审批人ID',
|
||||
`dept_delegate_name` varchar(50) COMMENT '部门代理审批人姓名',
|
||||
`dept_delegate_start_time` datetime COMMENT '部门代理开始时间',
|
||||
`dept_delegate_end_time` datetime COMMENT '部门代理结束时间',
|
||||
|
||||
-- 财务审批(第2节点)
|
||||
`finance_approver_id` bigint(20) COMMENT '财务审批人ID',
|
||||
`finance_approver_name` varchar(50) COMMENT '财务审批人姓名',
|
||||
`finance_delegate_id` bigint(20) COMMENT '财务代理审批人ID',
|
||||
`finance_delegate_name` varchar(50) COMMENT '财务代理审批人姓名',
|
||||
`finance_delegate_start_time` datetime COMMENT '财务代理开始时间',
|
||||
`finance_delegate_end_time` datetime COMMENT '财务代理结束时间',
|
||||
|
||||
-- 领导审批(第3节点)
|
||||
`leader_approver_id` bigint(20) COMMENT '领导审批人ID',
|
||||
`leader_approver_name` varchar(50) COMMENT '领导审批人姓名',
|
||||
`leader_delegate_id` bigint(20) COMMENT '领导代理审批人ID',
|
||||
`leader_delegate_name` varchar(50) COMMENT '领导代理审批人姓名',
|
||||
`leader_delegate_start_time` datetime COMMENT '领导代理开始时间',
|
||||
`leader_delegate_end_time` datetime COMMENT '领导代理结束时间',
|
||||
|
||||
-- 配置状态
|
||||
`priority` int(11) DEFAULT 0 COMMENT '优先级(数字越大优先级越高,用于多个配置冲突时的选择)',
|
||||
`status` char(1) DEFAULT '1' COMMENT '状态(0-禁用 1-启用)',
|
||||
`effective_start_time` datetime COMMENT '生效开始时间',
|
||||
`effective_end_time` datetime COMMENT '生效结束时间',
|
||||
|
||||
-- 系统字段
|
||||
`remark` varchar(500) COMMENT '备注',
|
||||
`create_by` bigint(20) COMMENT '创建者',
|
||||
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` bigint(20) COMMENT '更新者',
|
||||
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`create_dept` bigint(20) COMMENT '创建部门',
|
||||
`deleted` tinyint(1) DEFAULT 0 COMMENT '删除标志(0-正常 1-删除)',
|
||||
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户ID',
|
||||
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_config_type` (`config_type`),
|
||||
KEY `idx_dept_id` (`dept_id`),
|
||||
KEY `idx_amount_range` (`min_amount`, `max_amount`),
|
||||
KEY `idx_status_priority` (`status`, `priority`),
|
||||
KEY `idx_effective_time` (`effective_start_time`, `effective_end_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='审批人配置表';
|
||||
|
||||
-- 1.3 审批记录表
|
||||
DROP TABLE IF EXISTS `cw_approval_record`;
|
||||
CREATE TABLE `cw_approval_record` (
|
||||
`id` bigint(20) NOT NULL COMMENT '主键ID',
|
||||
`application_id` bigint(20) NOT NULL COMMENT '申报ID',
|
||||
`application_no` varchar(50) NOT NULL COMMENT '申报编号',
|
||||
|
||||
-- 节点信息
|
||||
`node_code` varchar(20) NOT NULL COMMENT '节点编码(dept_approve-部门审批 finance_approve-财务审批 leader_approve-领导审批)',
|
||||
`node_name` varchar(50) NOT NULL COMMENT '节点名称',
|
||||
`node_order` int(11) NOT NULL COMMENT '节点顺序(1-部门审批 2-财务审批 3-领导审批)',
|
||||
|
||||
-- 审批人信息
|
||||
`approver_id` bigint(20) NOT NULL COMMENT '审批人ID',
|
||||
`approver_name` varchar(50) NOT NULL COMMENT '审批人姓名',
|
||||
`is_delegate` tinyint(1) DEFAULT 0 COMMENT '是否代理审批(0-否 1-是)',
|
||||
`original_approver_id` bigint(20) COMMENT '原审批人ID(代理时使用)',
|
||||
`original_approver_name` varchar(50) COMMENT '原审批人姓名(代理时使用)',
|
||||
|
||||
-- 审批状态和结果
|
||||
`node_status` varchar(20) NOT NULL COMMENT '节点状态(waiting-等待审批 approving-审批中 approved-已通过 rejected-已拒绝 returned-已退回)',
|
||||
`approve_action` varchar(20) COMMENT '审批动作(approve-通过 reject-拒绝 return-退回)',
|
||||
`approve_opinion` text COMMENT '审批意见',
|
||||
|
||||
-- 时间记录
|
||||
`start_time` datetime NOT NULL COMMENT '节点开始时间',
|
||||
`end_time` datetime COMMENT '节点结束时间',
|
||||
`expect_finish_time` datetime COMMENT '期望完成时间',
|
||||
`is_overtime` tinyint(1) DEFAULT 0 COMMENT '是否超时(0-否 1-是)',
|
||||
|
||||
-- 审批配置记录
|
||||
`config_id` bigint(20) COMMENT '使用的审批配置ID',
|
||||
`config_snapshot` json COMMENT '审批配置快照(JSON格式,记录当时的配置)',
|
||||
|
||||
-- 系统字段
|
||||
`create_by` bigint(20) COMMENT '创建者',
|
||||
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` bigint(20) COMMENT '更新者',
|
||||
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`create_dept` bigint(20) COMMENT '创建部门',
|
||||
`deleted` tinyint(1) DEFAULT 0 COMMENT '删除标志(0-正常 1-删除)',
|
||||
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户ID',
|
||||
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_application_id` (`application_id`),
|
||||
KEY `idx_application_no` (`application_no`),
|
||||
KEY `idx_approver_id` (`approver_id`),
|
||||
KEY `idx_node_status` (`node_status`),
|
||||
KEY `idx_node_order` (`node_order`),
|
||||
KEY `idx_start_time` (`start_time`),
|
||||
KEY `idx_expect_finish_time` (`expect_finish_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='审批记录表';
|
||||
|
||||
-- 1.4 项目支出明细表
|
||||
DROP TABLE IF EXISTS `cw_expenditure_detail`;
|
||||
CREATE TABLE `cw_expenditure_detail` (
|
||||
`id` bigint(20) NOT NULL COMMENT '主键ID',
|
||||
`application_id` bigint(20) NOT NULL COMMENT '申报ID',
|
||||
`application_no` varchar(50) NOT NULL COMMENT '申报编号',
|
||||
|
||||
-- 支出明细信息
|
||||
`category_code` varchar(50) COMMENT '支出科目编码(关联字典)',
|
||||
`category` varchar(100) NOT NULL COMMENT '支出科目名称',
|
||||
`amount` decimal(15,2) NOT NULL COMMENT '金额(万元)',
|
||||
`calculation` text COMMENT '测算依据',
|
||||
`description` varchar(500) COMMENT '详细说明',
|
||||
|
||||
-- 时间分配
|
||||
`current_year_amount` decimal(15,2) DEFAULT 0.00 COMMENT '当年执行金额(万元)',
|
||||
`next_year_amount` decimal(15,2) DEFAULT 0.00 COMMENT '下年度金额(万元)',
|
||||
`future_year_amount` decimal(15,2) DEFAULT 0.00 COMMENT '以后年度金额(万元)',
|
||||
|
||||
-- 资金来源分配
|
||||
`municipal_fiscal_amount` decimal(15,2) DEFAULT 0.00 COMMENT '市财政资金(万元)',
|
||||
`self_funding_amount` decimal(15,2) DEFAULT 0.00 COMMENT '单位自筹(万元)',
|
||||
`other_funding_amount` decimal(15,2) DEFAULT 0.00 COMMENT '其他资金(万元)',
|
||||
|
||||
-- 排序和状态
|
||||
`sort_order` int(11) DEFAULT 0 COMMENT '排序',
|
||||
`status` char(1) DEFAULT '1' COMMENT '状态(0-删除 1-正常)',
|
||||
|
||||
-- 系统字段
|
||||
`create_by` bigint(20) COMMENT '创建者',
|
||||
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` bigint(20) COMMENT '更新者',
|
||||
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`create_dept` bigint(20) COMMENT '创建部门',
|
||||
`deleted` tinyint(1) DEFAULT 0 COMMENT '删除标志(0-正常 1-删除)',
|
||||
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户ID',
|
||||
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_application_id` (`application_id`),
|
||||
KEY `idx_application_no` (`application_no`),
|
||||
KEY `idx_category_code` (`category_code`),
|
||||
KEY `idx_sort_order` (`sort_order`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='项目支出明细表';
|
||||
|
||||
-- 1.5 绩效指标表
|
||||
DROP TABLE IF EXISTS `cw_performance_indicator`;
|
||||
CREATE TABLE `cw_performance_indicator` (
|
||||
`id` bigint(20) NOT NULL COMMENT '主键ID',
|
||||
`application_id` bigint(20) NOT NULL COMMENT '申报ID',
|
||||
`application_no` varchar(50) NOT NULL COMMENT '申报编号',
|
||||
|
||||
-- 指标信息
|
||||
`indicator_type` varchar(20) NOT NULL COMMENT '指标类型(quantity-数量指标 quality-质量指标 timeliness-时效指标 cost-成本指标 economic-经济效益指标 social-社会效益指标 environmental-环境效益指标 satisfaction-满意度指标)',
|
||||
`indicator_type_name` varchar(50) NOT NULL COMMENT '指标类型名称',
|
||||
`goal_name` varchar(200) NOT NULL COMMENT '绩效目标名称',
|
||||
`target_value` varchar(100) NOT NULL COMMENT '目标值',
|
||||
`weight_percentage` decimal(5,2) NOT NULL COMMENT '分值占比(%)',
|
||||
`description` varchar(500) COMMENT '详细说明',
|
||||
|
||||
-- 指标属性
|
||||
`unit` varchar(20) COMMENT '计量单位',
|
||||
`measurement_method` varchar(200) COMMENT '测量方法',
|
||||
`data_source` varchar(200) COMMENT '数据来源',
|
||||
`is_quantitative` tinyint(1) DEFAULT 1 COMMENT '是否定量指标(0-定性 1-定量)',
|
||||
|
||||
-- 排序和状态
|
||||
`sort_order` int(11) DEFAULT 0 COMMENT '排序',
|
||||
`status` char(1) DEFAULT '1' COMMENT '状态(0-删除 1-正常)',
|
||||
|
||||
-- 系统字段
|
||||
`create_by` bigint(20) COMMENT '创建者',
|
||||
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` bigint(20) COMMENT '更新者',
|
||||
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`create_dept` bigint(20) COMMENT '创建部门',
|
||||
`deleted` tinyint(1) DEFAULT 0 COMMENT '删除标志(0-正常 1-删除)',
|
||||
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户ID',
|
||||
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_application_id` (`application_id`),
|
||||
KEY `idx_application_no` (`application_no`),
|
||||
KEY `idx_indicator_type` (`indicator_type`),
|
||||
KEY `idx_sort_order` (`sort_order`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='绩效指标表';
|
||||
|
||||
-- 1.6 申报附件表
|
||||
DROP TABLE IF EXISTS `cw_application_attachment`;
|
||||
CREATE TABLE `cw_application_attachment` (
|
||||
`id` bigint(20) NOT NULL COMMENT '主键ID',
|
||||
`application_id` bigint(20) NOT NULL COMMENT '申报ID',
|
||||
`application_no` varchar(50) NOT NULL COMMENT '申报编号',
|
||||
|
||||
-- 文件信息
|
||||
`file_name` varchar(255) NOT NULL COMMENT '文件名称',
|
||||
`file_original_name` varchar(255) NOT NULL COMMENT '原始文件名',
|
||||
`file_type` varchar(20) COMMENT '文件类型(pdf,doc,docx,xls,xlsx,jpg,png等)',
|
||||
`file_size` bigint(20) COMMENT '文件大小(字节)',
|
||||
`file_path` varchar(500) COMMENT '文件存储路径',
|
||||
`file_url` varchar(500) COMMENT '文件访问URL',
|
||||
|
||||
-- 附件分类
|
||||
`attachment_type` varchar(50) COMMENT '附件类型(feasibility_report-可行性研究报告 implementation_plan-项目实施方案 budget_calculation-资金测算明细表 other_materials-其他相关证明材料)',
|
||||
`attachment_type_name` varchar(100) COMMENT '附件类型名称',
|
||||
`description` varchar(500) COMMENT '文件说明',
|
||||
|
||||
-- 上传信息
|
||||
`upload_user_id` bigint(20) NOT NULL COMMENT '上传人ID',
|
||||
`upload_user_name` varchar(50) NOT NULL COMMENT '上传人姓名',
|
||||
`upload_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '上传时间',
|
||||
|
||||
-- 排序和状态
|
||||
`sort_order` int(11) DEFAULT 0 COMMENT '排序',
|
||||
`status` char(1) DEFAULT '1' COMMENT '状态(0-删除 1-正常)',
|
||||
|
||||
-- 系统字段
|
||||
`create_by` bigint(20) COMMENT '创建者',
|
||||
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_by` bigint(20) COMMENT '更新者',
|
||||
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`create_dept` bigint(20) COMMENT '创建部门',
|
||||
`deleted` tinyint(1) DEFAULT 0 COMMENT '删除标志(0-正常 1-删除)',
|
||||
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户ID',
|
||||
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_application_id` (`application_id`),
|
||||
KEY `idx_application_no` (`application_no`),
|
||||
KEY `idx_attachment_type` (`attachment_type`),
|
||||
KEY `idx_upload_time` (`upload_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='申报附件表';
|
||||
|
||||
-- ==========================================
|
||||
-- 2. 插入字典数据
|
||||
-- ==========================================
|
||||
|
||||
-- 2.1 字典类型
|
||||
INSERT INTO `sys_dict_type` VALUES
|
||||
(3000001, '项目特性选项', 'cw_project_feature', '0', NULL, '财务预算-项目特性选项', now(), NULL, NULL, '项目特性相关选项'),
|
||||
(3000002, '绩效指标类型', 'cw_performance_type', '0', NULL, '财务预算-绩效指标类型', now(), NULL, NULL, '8大绩效指标类型'),
|
||||
(3000003, '审批状态', 'cw_approval_status', '0', NULL, '财务预算-审批状态', now(), NULL, NULL, '审批状态分类'),
|
||||
(3000004, '审批人配置类型', 'cw_approver_config_type', '0', NULL, '财务预算-审批人配置类型', now(), NULL, NULL, '审批人配置类型分类'),
|
||||
(3000005, '审批节点', 'cw_approval_node', '0', NULL, '财务预算-审批节点', now(), NULL, NULL, '固定3节点审批流程'),
|
||||
(3000006, '资金来源类型', 'cw_funding_source', '0', NULL, '财务预算-资金来源类型', now(), NULL, NULL, '资金来源分类'),
|
||||
(3000007, '支出科目', 'cw_expenditure_category', '0', NULL, '财务预算-支出科目', now(), NULL, NULL, '项目支出科目分类'),
|
||||
(3000008, '附件类型', 'cw_attachment_type', '0', NULL, '财务预算-附件类型', now(), NULL, NULL, '申报附件类型分类');
|
||||
|
||||
-- 2.2 字典数据
|
||||
INSERT INTO `sys_dict_data` VALUES
|
||||
-- 项目特性选项
|
||||
(3000011, 0, '是', '1', 'cw_project_feature', '', '', 'N', '0', '0', '', '是', now(), NULL, NULL, NULL),
|
||||
(3000012, 1, '否', '0', 'cw_project_feature', '', '', 'N', '0', '0', '', '否', now(), NULL, NULL, NULL),
|
||||
|
||||
-- 绩效指标类型(8大维度)
|
||||
(3000021, 0, '数量指标', 'quantity', 'cw_performance_type', '', '', 'N', '0', '0', '', '数量指标', now(), NULL, NULL, NULL),
|
||||
(3000022, 1, '质量指标', 'quality', 'cw_performance_type', '', '', 'N', '0', '0', '', '质量指标', now(), NULL, NULL, NULL),
|
||||
(3000023, 2, '时效指标', 'timeliness', 'cw_performance_type', '', '', 'N', '0', '0', '', '时效指标', now(), NULL, NULL, NULL),
|
||||
(3000024, 3, '成本指标', 'cost', 'cw_performance_type', '', '', 'N', '0', '0', '', '成本指标', now(), NULL, NULL, NULL),
|
||||
(3000025, 4, '经济效益指标', 'economic', 'cw_performance_type', '', '', 'N', '0', '0', '', '经济效益指标', now(), NULL, NULL, NULL),
|
||||
(3000026, 5, '社会效益指标', 'social', 'cw_performance_type', '', '', 'N', '0', '0', '', '社会效益指标', now(), NULL, NULL, NULL),
|
||||
(3000027, 6, '环境效益指标', 'environmental', 'cw_performance_type', '', '', 'N', '0', '0', '', '环境效益指标', now(), NULL, NULL, NULL),
|
||||
(3000028, 7, '满意度指标', 'satisfaction', 'cw_performance_type', '', '', 'N', '0', '0', '', '满意度指标', now(), NULL, NULL, NULL),
|
||||
|
||||
-- 审批状态
|
||||
(3000031, 0, '草稿', 'draft', 'cw_approval_status', '', '', 'N', '0', '0', '', '草稿状态', now(), NULL, NULL, NULL),
|
||||
(3000032, 1, '已提交', 'submitted', 'cw_approval_status', '', '', 'N', '0', '0', '', '已提交审批', now(), NULL, NULL, NULL),
|
||||
(3000033, 2, '审批中', 'approving', 'cw_approval_status', '', 'blue', 'N', '0', '0', '', '审批中', now(), NULL, NULL, NULL),
|
||||
(3000034, 3, '已通过', 'approved', 'cw_approval_status', '', 'green', 'N', '0', '0', '', '审批通过', now(), NULL, NULL, NULL),
|
||||
(3000035, 4, '已拒绝', 'rejected', 'cw_approval_status', '', 'red', 'N', '0', '0', '', '审批拒绝', now(), NULL, NULL, NULL),
|
||||
(3000036, 5, '已退回', 'returned', 'cw_approval_status', '', 'orange', 'N', '0', '0', '', '退回修改', now(), NULL, NULL, NULL),
|
||||
(3000037, 6, '已取消', 'cancelled', 'cw_approval_status', '', 'gray', 'N', '0', '0', '', '取消申报', now(), NULL, NULL, NULL),
|
||||
|
||||
-- 审批人配置类型
|
||||
(3000041, 0, '全局配置', 'global', 'cw_approver_config_type', '', '', 'N', '0', '0', '', '全局统一配置', now(), NULL, NULL, NULL),
|
||||
(3000042, 1, '部门配置', 'dept', 'cw_approver_config_type', '', '', 'N', '0', '0', '', '按部门配置', now(), NULL, NULL, NULL),
|
||||
(3000043, 2, '金额配置', 'amount', 'cw_approver_config_type', '', '', 'N', '0', '0', '', '按金额阈值配置', now(), NULL, NULL, NULL),
|
||||
|
||||
-- 审批节点(固定3节点)
|
||||
(3000051, 1, '部门审批', 'dept_approve', 'cw_approval_node', '', '', 'N', '0', '0', '', '部门负责人审批', now(), NULL, NULL, NULL),
|
||||
(3000052, 2, '财务审批', 'finance_approve', 'cw_approval_node', '', '', 'N', '0', '0', '', '财务部门审批', now(), NULL, NULL, NULL),
|
||||
(3000053, 3, '领导审批', 'leader_approve', 'cw_approval_node', '', '', 'N', '0', '0', '', '校领导审批', now(), NULL, NULL, NULL),
|
||||
|
||||
-- 资金来源类型
|
||||
(3000061, 0, '市财政资金', 'municipal_fiscal', 'cw_funding_source', '', '', 'N', '0', '0', '', '市财政资金', now(), NULL, NULL, NULL),
|
||||
(3000062, 1, '单位自筹', 'self_funding', 'cw_funding_source', '', '', 'N', '0', '0', '', '单位自筹资金', now(), NULL, NULL, NULL),
|
||||
(3000063, 2, '其他资金', 'other_funding', 'cw_funding_source', '', '', 'N', '0', '0', '', '其他来源资金', now(), NULL, NULL, NULL),
|
||||
|
||||
-- 支出科目
|
||||
(3000071, 0, '人员经费', 'personnel_expense', 'cw_expenditure_category', '', '', 'N', '0', '0', '', '人员相关费用', now(), NULL, NULL, NULL),
|
||||
(3000072, 1, '设备费', 'equipment_expense', 'cw_expenditure_category', '', '', 'N', '0', '0', '', '设备采购费用', now(), NULL, NULL, NULL),
|
||||
(3000073, 2, '材料费', 'material_expense', 'cw_expenditure_category', '', '', 'N', '0', '0', '', '材料采购费用', now(), NULL, NULL, NULL),
|
||||
(3000074, 3, '差旅费', 'travel_expense', 'cw_expenditure_category', '', '', 'N', '0', '0', '', '差旅相关费用', now(), NULL, NULL, NULL),
|
||||
(3000075, 4, '培训费', 'training_expense', 'cw_expenditure_category', '', '', 'N', '0', '0', '', '培训相关费用', now(), NULL, NULL, NULL),
|
||||
(3000076, 5, '咨询费', 'consulting_expense', 'cw_expenditure_category', '', '', 'N', '0', '0', '', '咨询服务费用', now(), NULL, NULL, NULL),
|
||||
(3000077, 6, '运行维护费', 'maintenance_expense', 'cw_expenditure_category', '', '', 'N', '0', '0', '', '运行维护费用', now(), NULL, NULL, NULL),
|
||||
(3000078, 7, '其他费用', 'other_expense', 'cw_expenditure_category', '', '', 'N', '0', '0', '', '其他类型费用', now(), NULL, NULL, NULL),
|
||||
|
||||
-- 附件类型
|
||||
(3000081, 0, '可行性研究报告', 'feasibility_report', 'cw_attachment_type', '', '', 'N', '0', '0', '', '项目可行性研究报告', now(), NULL, NULL, NULL),
|
||||
(3000082, 1, '项目实施方案', 'implementation_plan', 'cw_attachment_type', '', '', 'N', '0', '0', '', '项目实施方案文档', now(), NULL, NULL, NULL),
|
||||
(3000083, 2, '资金测算明细表', 'budget_calculation', 'cw_attachment_type', '', '', 'N', '0', '0', '', '资金测算明细表', now(), NULL, NULL, NULL),
|
||||
(3000084, 3, '其他相关证明材料', 'other_materials', 'cw_attachment_type', '', '', 'N', '0', '0', '', '其他相关证明材料', now(), NULL, NULL, NULL);
|
||||
|
||||
-- ==========================================
|
||||
-- 3. 创建复合索引优化
|
||||
-- ==========================================
|
||||
|
||||
-- 针对常用查询的复合索引
|
||||
ALTER TABLE `cw_project_application`
|
||||
ADD INDEX `idx_dept_status_year` (`department_id`, `approval_status`, `year`);
|
||||
|
||||
ALTER TABLE `cw_approval_record`
|
||||
ADD INDEX `idx_approver_status_order` (`approver_id`, `node_status`, `node_order`);
|
||||
|
||||
ALTER TABLE `cw_approval_record`
|
||||
ADD INDEX `idx_app_node_status` (`application_id`, `node_code`, `node_status`);
|
||||
|
||||
-- ==========================================
|
||||
-- 4. 插入初始化数据
|
||||
-- ==========================================
|
||||
|
||||
-- 插入默认全局审批人配置
|
||||
INSERT INTO `cw_approver_config` (
|
||||
`id`, `config_name`, `config_type`, `dept_approver_id`, `dept_approver_name`,
|
||||
`finance_approver_id`, `finance_approver_name`, `leader_approver_id`, `leader_approver_name`,
|
||||
`priority`, `status`, `create_by`, `create_time`, `tenant_id`
|
||||
) VALUES (
|
||||
1, '默认全局审批配置', 'global', 1, '系统管理员',
|
||||
1, '系统管理员', 1, '系统管理员',
|
||||
100, '1', 1, now(), '000000'
|
||||
);
|
||||
|
||||
-- 恢复外键检查
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
|
||||
-- ==========================================
|
||||
-- 脚本执行完成
|
||||
-- ==========================================
|
||||
SELECT '财务项目预算审批系统数据库创建完成!' AS result;
|
||||
@@ -1,131 +0,0 @@
|
||||
-- ==========================================
|
||||
-- 财务项目预算审批系统 - 菜单配置脚本
|
||||
-- 版本: 1.0
|
||||
-- 创建时间: 2025-01-26
|
||||
-- 模块前缀: cw_ (财务模块)
|
||||
-- 菜单ID范围: 5000-5999
|
||||
-- ==========================================
|
||||
|
||||
-- ==========================================
|
||||
-- 菜单结构设计:
|
||||
-- 5000 - 财务管理 (一级菜单)
|
||||
-- 5100 - 项目预算审批 (二级菜单)
|
||||
-- 5110 - 预算申报管理 (三级菜单)
|
||||
-- 5111-5117 - 功能按钮
|
||||
-- 5120 - 审批人配置 (三级菜单)
|
||||
-- 5121-5125 - 功能按钮
|
||||
-- 5130 - 审批记录查询 (三级菜单)
|
||||
-- 5131-5135 - 功能按钮
|
||||
-- 5140 - 支出明细管理 (三级菜单)
|
||||
-- 5141-5145 - 功能按钮
|
||||
-- 5150 - 绩效指标管理 (三级菜单)
|
||||
-- 5151-5155 - 功能按钮
|
||||
-- 5160 - 附件管理 (三级菜单)
|
||||
-- 5161-5165 - 功能按钮
|
||||
-- ==========================================
|
||||
|
||||
-- 1. 一级菜单:财务管理
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES
|
||||
(5000, '财务管理', 0, 10, 'finance', NULL, '', 1, 0, 'M', '0', '0', '', 'money', 103, 1, now(), NULL, NULL, '财务管理根菜单', '000000');
|
||||
|
||||
-- 2. 二级菜单:项目预算审批
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES
|
||||
(5100, '项目预算审批', 5000, 1, 'budget-approval', NULL, '', 1, 0, 'M', '0', '0', '', 'file-text', 103, 1, now(), NULL, NULL, '项目预算审批管理', '000000');
|
||||
|
||||
-- ==========================================
|
||||
-- 3. 三级菜单:预算申报管理
|
||||
-- ==========================================
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES
|
||||
(5110, '预算申报管理', 5100, 1, 'application', 'cw/budgetApproval/application/index', '', 1, 0, 'C', '0', '0', 'cw:application:list', 'form', 103, 1, now(), NULL, NULL, '预算申报管理页面', '000000');
|
||||
|
||||
-- 预算申报管理 - 功能按钮
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES
|
||||
(5111, '预算申报查询', 5110, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:query', '#', 103, 1, now(), NULL, NULL, '预算申报查询权限', '000000'),
|
||||
(5112, '预算申报详情', 5110, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:detail', '#', 103, 1, now(), NULL, NULL, '预算申报详情权限', '000000'),
|
||||
(5113, '预算申报新增', 5110, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:add', '#', 103, 1, now(), NULL, NULL, '预算申报新增权限', '000000'),
|
||||
(5114, '预算申报修改', 5110, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:edit', '#', 103, 1, now(), NULL, NULL, '预算申报修改权限', '000000'),
|
||||
(5115, '预算申报删除', 5110, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:remove', '#', 103, 1, now(), NULL, NULL, '预算申报删除权限', '000000'),
|
||||
(5116, '预算申报导出', 5110, 6, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:export', '#', 103, 1, now(), NULL, NULL, '预算申报导出权限', '000000'),
|
||||
(5117, '预算申报提交', 5110, 7, '', '', '', 1, 0, 'F', '0', '0', 'cw:application:submit', '#', 103, 1, now(), NULL, NULL, '预算申报提交审批权限', '000000');
|
||||
|
||||
-- ==========================================
|
||||
-- 4. 三级菜单:审批人配置
|
||||
-- ==========================================
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES
|
||||
(5120, '审批人配置', 5100, 2, 'approver-config', 'cw/budgetApproval/approverConfig/index', '', 1, 0, 'C', '0', '0', 'cw:approverConfig:list', 'peoples', 103, 1, now(), NULL, NULL, '审批人配置页面', '000000');
|
||||
|
||||
-- 审批人配置 - 功能按钮
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES
|
||||
(5121, '审批人查询', 5120, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:approverConfig:query', '#', 103, 1, now(), NULL, NULL, '审批人配置查询权限', '000000'),
|
||||
(5122, '审批人详情', 5120, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:approverConfig:detail', '#', 103, 1, now(), NULL, NULL, '审批人配置详情权限', '000000'),
|
||||
(5123, '审批人新增', 5120, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:approverConfig:add', '#', 103, 1, now(), NULL, NULL, '审批人配置新增权限', '000000'),
|
||||
(5124, '审批人修改', 5120, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:approverConfig:edit', '#', 103, 1, now(), NULL, NULL, '审批人配置修改权限', '000000'),
|
||||
(5125, '审批人删除', 5120, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:approverConfig:remove', '#', 103, 1, now(), NULL, NULL, '审批人配置删除权限', '000000');
|
||||
|
||||
-- ==========================================
|
||||
-- 5. 三级菜单:审批记录查询
|
||||
-- ==========================================
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES
|
||||
(5130, '审批记录查询', 5100, 3, 'approval-record', 'cw/budgetApproval/approvalRecord/index', '', 1, 0, 'C', '0', '0', 'cw:approvalRecord:list', 'documentation', 103, 1, now(), NULL, NULL, '审批记录查询页面', '000000');
|
||||
|
||||
-- 审批记录查询 - 功能按钮
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES
|
||||
(5131, '审批记录查询', 5130, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:approvalRecord:query', '#', 103, 1, now(), NULL, NULL, '审批记录查询权限', '000000'),
|
||||
(5132, '审批记录详情', 5130, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:approvalRecord:detail', '#', 103, 1, now(), NULL, NULL, '审批记录详情权限', '000000'),
|
||||
(5133, '审批操作', 5130, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:approvalRecord:approve', '#', 103, 1, now(), NULL, NULL, '审批操作权限', '000000'),
|
||||
(5134, '退回操作', 5130, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:approvalRecord:return', '#', 103, 1, now(), NULL, NULL, '退回操作权限', '000000'),
|
||||
(5135, '审批记录导出', 5130, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:approvalRecord:export', '#', 103, 1, now(), NULL, NULL, '审批记录导出权限', '000000');
|
||||
|
||||
-- ==========================================
|
||||
-- 6. 三级菜单:支出明细管理
|
||||
-- ==========================================
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES
|
||||
(5140, '支出明细管理', 5100, 4, 'expenditure-detail', 'cw/budgetApproval/expenditureDetail/index', '', 1, 0, 'C', '0', '0', 'cw:expenditureDetail:list', 'money', 103, 1, now(), NULL, NULL, '支出明细管理页面', '000000');
|
||||
|
||||
-- 支出明细管理 - 功能按钮
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES
|
||||
(5141, '支出明细查询', 5140, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:expenditureDetail:query', '#', 103, 1, now(), NULL, NULL, '支出明细查询权限', '000000'),
|
||||
(5142, '支出明细详情', 5140, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:expenditureDetail:detail', '#', 103, 1, now(), NULL, NULL, '支出明细详情权限', '000000'),
|
||||
(5143, '支出明细新增', 5140, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:expenditureDetail:add', '#', 103, 1, now(), NULL, NULL, '支出明细新增权限', '000000'),
|
||||
(5144, '支出明细修改', 5140, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:expenditureDetail:edit', '#', 103, 1, now(), NULL, NULL, '支出明细修改权限', '000000'),
|
||||
(5145, '支出明细删除', 5140, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:expenditureDetail:remove', '#', 103, 1, now(), NULL, NULL, '支出明细删除权限', '000000');
|
||||
|
||||
-- ==========================================
|
||||
-- 7. 三级菜单:绩效指标管理
|
||||
-- ==========================================
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES
|
||||
(5150, '绩效指标管理', 5100, 5, 'performance-indicator', 'cw/budgetApproval/performanceIndicator/index', '', 1, 0, 'C', '0', '0', 'cw:performanceIndicator:list', 'chart', 103, 1, now(), NULL, NULL, '绩效指标管理页面', '000000');
|
||||
|
||||
-- 绩效指标管理 - 功能按钮
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES
|
||||
(5151, '绩效指标查询', 5150, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:performanceIndicator:query', '#', 103, 1, now(), NULL, NULL, '绩效指标查询权限', '000000'),
|
||||
(5152, '绩效指标详情', 5150, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:performanceIndicator:detail', '#', 103, 1, now(), NULL, NULL, '绩效指标详情权限', '000000'),
|
||||
(5153, '绩效指标新增', 5150, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:performanceIndicator:add', '#', 103, 1, now(), NULL, NULL, '绩效指标新增权限', '000000'),
|
||||
(5154, '绩效指标修改', 5150, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:performanceIndicator:edit', '#', 103, 1, now(), NULL, NULL, '绩效指标修改权限', '000000'),
|
||||
(5155, '绩效指标删除', 5150, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:performanceIndicator:remove', '#', 103, 1, now(), NULL, NULL, '绩效指标删除权限', '000000');
|
||||
|
||||
-- ==========================================
|
||||
-- 8. 三级菜单:附件管理
|
||||
-- ==========================================
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES
|
||||
(5160, '附件管理', 5100, 6, 'attachment', 'cw/budgetApproval/attachment/index', '', 1, 0, 'C', '0', '0', 'cw:attachment:list', 'upload', 103, 1, now(), NULL, NULL, '附件管理页面', '000000');
|
||||
|
||||
-- 附件管理 - 功能按钮
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark, tenant_id) VALUES
|
||||
(5161, '附件查询', 5160, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:attachment:query', '#', 103, 1, now(), NULL, NULL, '附件查询权限', '000000'),
|
||||
(5162, '附件详情', 5160, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:attachment:detail', '#', 103, 1, now(), NULL, NULL, '附件详情权限', '000000'),
|
||||
(5163, '附件上传', 5160, 3, '', '', '', 1, 0, 'F', '0', '0', 'cw:attachment:upload', '#', 103, 1, now(), NULL, NULL, '附件上传权限', '000000'),
|
||||
(5164, '附件下载', 5160, 4, '', '', '', 1, 0, 'F', '0', '0', 'cw:attachment:download', '#', 103, 1, now(), NULL, NULL, '附件下载权限', '000000'),
|
||||
(5165, '附件删除', 5160, 5, '', '', '', 1, 0, 'F', '0', '0', 'cw:attachment:remove', '#', 103, 1, now(), NULL, NULL, '附件删除权限', '000000');
|
||||
|
||||
-- ==========================================
|
||||
-- 菜单创建完成,显示创建结果
|
||||
-- ==========================================
|
||||
SELECT '财务项目预算审批系统菜单配置完成!' AS result;
|
||||
|
||||
-- 验证菜单创建情况
|
||||
SELECT '=== 菜单创建验证 ===' as verification;
|
||||
SELECT menu_id, menu_name, menu_type, parent_id, order_num, perms
|
||||
FROM sys_menu
|
||||
WHERE menu_id BETWEEN 5000 AND 5999
|
||||
ORDER BY menu_id;
|
||||
@@ -1,34 +0,0 @@
|
||||
-- 财务审批管理菜单配置
|
||||
-- 执行顺序:在现有财务管理模块下添加
|
||||
-- 菜单ID范围:6400-6404
|
||||
|
||||
-- 1. 财务审批管理菜单
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (6400, '财务审批', 6000, 5, 'financeApproval', 'cw/financeApproval/index', '', 1, 0, 'C', '0', '0', 'cw:approval:finance', 'ele-Money', 103, 1, now(), 1, now(), '财务项目申报审批管理');
|
||||
|
||||
-- 2. 财务审批功能权限
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES
|
||||
(6401, '财务审批查询', 6400, 1, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:finance', '#', 103, 1, now(), 1, now(), '财务审批查询权限'),
|
||||
(6402, '财务审批操作', 6400, 2, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:finance', '#', 103, 1, now(), 1, now(), '财务审批操作权限'),
|
||||
(6403, '财务审批详情', 6400, 3, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:query', '#', 103, 1, now(), 1, now(), '财务审批详情权限'),
|
||||
(6404, '财务审批导出', 6400, 4, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:export', '#', 103, 1, now(), 1, now(), '财务审批导出权限');
|
||||
|
||||
-- 3. 如果需要给特定角色分配权限,请执行以下SQL(可选)
|
||||
-- 注意:请根据实际角色ID调整,以下使用示例角色ID 2(普通角色)
|
||||
|
||||
-- 插入角色菜单关联(示例:给角色ID为2的角色分配财务审批权限)
|
||||
-- INSERT INTO sys_role_menu (role_id, menu_id) VALUES
|
||||
-- (2, 6400), -- 财务审批主菜单
|
||||
-- (2, 6401), -- 查询权限
|
||||
-- (2, 6402), -- 操作权限
|
||||
-- (2, 6403), -- 详情权限
|
||||
-- (2, 6404); -- 导出权限
|
||||
|
||||
-- 4. 添加字典数据(如果需要新的审批状态字典)
|
||||
-- 注意:以下字典数据可能已存在,如果已存在则跳过
|
||||
INSERT IGNORE INTO sys_dict_data (dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES
|
||||
(null, '000000', 8, '审批完成', 'approved', 'cw_approval_status', '', 'success', 'N', 103, 1, now(), 1, now(), '财务审批通过,整个流程完成');
|
||||
|
||||
-- 执行完成后,建议重新登录系统以刷新菜单权限
|
||||
@@ -1,32 +0,0 @@
|
||||
-- 分管领导审批管理菜单配置(最终修正版本)
|
||||
-- 执行顺序:在现有财务管理模块下添加
|
||||
|
||||
-- 1. 分管领导审批管理菜单
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (6300, '分管领导审批', 6000, 4, 'leaderApproval', 'cw/leaderApproval/index', '', 1, 0, 'C', '0', '0', 'cw:approval:leader', 'ele-User', 103, 1, now(), 1, now(), '财务项目申报分管领导审批管理');
|
||||
|
||||
-- 2. 分管领导审批功能权限
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES
|
||||
(6301, '分管领导审批查询', 6300, 1, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:leader', '#', 103, 1, now(), 1, now(), '分管领导审批查询权限'),
|
||||
(6302, '分管领导审批操作', 6300, 2, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:leader', '#', 103, 1, now(), 1, now(), '分管领导审批操作权限'),
|
||||
(6303, '分管领导审批详情', 6300, 3, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:query', '#', 103, 1, now(), 1, now(), '分管领导审批详情权限'),
|
||||
(6304, '分管领导审批导出', 6300, 4, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:export', '#', 103, 1, now(), 1, now(), '分管领导审批导出权限');
|
||||
|
||||
-- 3. 如果需要给特定角色分配权限,请执行以下SQL(可选)
|
||||
-- 注意:请根据实际角色ID调整,以下使用示例角色ID 2(普通角色)
|
||||
|
||||
-- 插入角色菜单关联(示例:给角色ID为2的角色分配分管领导审批权限)
|
||||
-- INSERT INTO sys_role_menu (role_id, menu_id) VALUES
|
||||
-- (2, 6300), -- 分管领导审批主菜单
|
||||
-- (2, 6301), -- 查询权限
|
||||
-- (2, 6302), -- 操作权限
|
||||
-- (2, 6303), -- 详情权限
|
||||
-- (2, 6304); -- 导出权限
|
||||
|
||||
-- 4. 添加字典数据(如果需要新的审批状态字典)
|
||||
INSERT INTO sys_dict_data (dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES
|
||||
(null, '000000', 7, '财务审批中', 'finance_approving', 'cw_approval_status', '', 'warning', 'N', 103, 1, now(), 1, now(), '分管领导审批通过,财务审批中');
|
||||
|
||||
-- 执行完成后,建议重新登录系统以刷新菜单权限
|
||||
@@ -1,32 +0,0 @@
|
||||
-- 分管领导审批管理菜单配置(修正版本)
|
||||
-- 执行顺序:在现有财务管理模块下添加
|
||||
|
||||
-- 1. 分管领导审批管理菜单
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (6300, '分管领导审批', 6000, 4, 'leaderApproval', 'cw/leaderApproval/index', '', 1, 0, 'C', '0', '0', 'cw:approval:leader', 'ele-User', 103, 1, now(), 1, now(), '财务项目申报分管领导审批管理');
|
||||
|
||||
-- 2. 分管领导审批功能权限
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES
|
||||
(6301, '分管领导审批查询', 6300, 1, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:leader', '#', 103, 1, now(), 1, now(), '分管领导审批查询权限'),
|
||||
(6302, '分管领导审批操作', 6300, 2, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:leader', '#', 103, 1, now(), 1, now(), '分管领导审批操作权限'),
|
||||
(6303, '分管领导审批详情', 6300, 3, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:query', '#', 103, 1, now(), 1, now(), '分管领导审批详情权限'),
|
||||
(6304, '分管领导审批导出', 6300, 4, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:export', '#', 103, 1, now(), 1, now(), '分管领导审批导出权限');
|
||||
|
||||
-- 3. 如果需要给特定角色分配权限,请执行以下SQL(可选)
|
||||
-- 注意:请根据实际角色ID调整,以下使用示例角色ID 2(普通角色)
|
||||
|
||||
-- 插入角色菜单关联(示例:给角色ID为2的角色分配分管领导审批权限)
|
||||
-- INSERT INTO sys_role_menu (role_id, menu_id) VALUES
|
||||
-- (2, 6300), -- 分管领导审批主菜单
|
||||
-- (2, 6301), -- 查询权限
|
||||
-- (2, 6302), -- 操作权限
|
||||
-- (2, 6303), -- 详情权限
|
||||
-- (2, 6304); -- 导出权限
|
||||
|
||||
-- 4. 添加字典数据(如果需要新的审批状态字典)
|
||||
INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES
|
||||
(null, 7, '财务审批中', 'finance_approving', 'cw_approval_status', '', 'warning', 'N', '0', 1, now(), 1, now(), '分管领导审批通过,财务审批中');
|
||||
|
||||
-- 执行完成后,建议重新登录系统以刷新菜单权限
|
||||
@@ -1,32 +0,0 @@
|
||||
-- 分管领导审批管理菜单配置
|
||||
-- 执行顺序:在现有财务管理模块下添加
|
||||
|
||||
-- 1. 分管领导审批管理菜单
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark, tenant_id)
|
||||
VALUES (6300, '分管领导审批', 6000, 4, 'leaderApproval', 'cw/leaderApproval/index', '', 1, 0, 'C', '0', '0', 'cw:approval:leader', 'ele-User', 1, now(), 1, now(), '财务项目申报分管领导审批管理', '000000');
|
||||
|
||||
-- 2. 分管领导审批功能权限
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark, tenant_id)
|
||||
VALUES
|
||||
(6301, '分管领导审批查询', 6300, 1, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:leader', '#', 1, now(), 1, now(), '分管领导审批查询权限', '000000'),
|
||||
(6302, '分管领导审批操作', 6300, 2, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:leader', '#', 1, now(), 1, now(), '分管领导审批操作权限', '000000'),
|
||||
(6303, '分管领导审批详情', 6300, 3, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:query', '#', 1, now(), 1, now(), '分管领导审批详情权限', '000000'),
|
||||
(6304, '分管领导审批导出', 6300, 4, '#', '', '', 1, 0, 'F', '0', '0', 'cw:approval:export', '#', 1, now(), 1, now(), '分管领导审批导出权限', '000000');
|
||||
|
||||
-- 3. 如果需要给特定角色分配权限,请执行以下SQL(可选)
|
||||
-- 注意:请根据实际角色ID调整,以下使用示例角色ID 2(普通角色)
|
||||
|
||||
-- 插入角色菜单关联(示例:给角色ID为2的角色分配分管领导审批权限)
|
||||
-- INSERT INTO sys_role_menu (role_id, menu_id) VALUES
|
||||
-- (2, 6300), -- 分管领导审批主菜单
|
||||
-- (2, 6301), -- 查询权限
|
||||
-- (2, 6302), -- 操作权限
|
||||
-- (2, 6303), -- 详情权限
|
||||
-- (2, 6304); -- 导出权限
|
||||
|
||||
-- 4. 添加字典数据(如果需要新的审批状态字典)
|
||||
INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, update_by, update_time, remark, tenant_id)
|
||||
VALUES
|
||||
(null, 7, '财务审批中', 'finance_approving', 'cw_approval_status', '', 'warning', 'N', '0', 1, now(), 1, now(), '分管领导审批通过,财务审批中', '000000');
|
||||
|
||||
-- 执行完成后,建议重新登录系统以刷新菜单权限
|
||||
@@ -1,60 +0,0 @@
|
||||
-- 项目预算数据统计菜单配置
|
||||
-- 作者:Cursor AI
|
||||
-- 创建时间:2025-01-27
|
||||
|
||||
-- 1. 添加项目预算数据统计菜单
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (7100, '项目预算统计', 6200, 4, 'statistics', 'cw/statistics/index', '', 1, 0, 'C', '0', '0', 'cw:statistics:list', 'chart', 103, 1, NOW(), 1, NOW(), '项目预算数据统计菜单');
|
||||
|
||||
-- 2. 添加统计相关按钮权限
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (7101, '项目预算统计查询', 7100, 1, '', '', '', 1, 0, 'F', '0', '0', 'cw:statistics:list', '#', 103, 1, NOW(), 1, NOW(), '');
|
||||
|
||||
INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query_param, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (7102, '项目预算统计导出', 7100, 2, '', '', '', 1, 0, 'F', '0', '0', 'cw:statistics:export', '#', 103, 1, NOW(), 1, NOW(), '');
|
||||
|
||||
-- 3. 为财务管理员角色分配统计菜单权限
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT r.role_id, 7100
|
||||
FROM sys_role r
|
||||
WHERE r.role_key = 'cw_admin' AND r.status = '0';
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT r.role_id, 7101
|
||||
FROM sys_role r
|
||||
WHERE r.role_key = 'cw_admin' AND r.status = '0';
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT r.role_id, 7102
|
||||
FROM sys_role r
|
||||
WHERE r.role_key = 'cw_admin' AND r.status = '0';
|
||||
|
||||
-- 4. 为系统管理员角色分配统计菜单权限
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT r.role_id, 7100
|
||||
FROM sys_role r
|
||||
WHERE r.role_key = 'admin' AND r.status = '0';
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT r.role_id, 7101
|
||||
FROM sys_role r
|
||||
WHERE r.role_key = 'admin' AND r.status = '0';
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT r.role_id, 7102
|
||||
FROM sys_role r
|
||||
WHERE r.role_key = 'admin' AND r.status = '0';
|
||||
|
||||
-- 5. 确认菜单创建结果
|
||||
SELECT
|
||||
m.menu_id,
|
||||
m.menu_name,
|
||||
m.parent_id,
|
||||
m.path,
|
||||
m.component,
|
||||
m.perms,
|
||||
m.menu_type,
|
||||
m.status
|
||||
FROM sys_menu m
|
||||
WHERE m.menu_id IN (7100, 7101, 7102)
|
||||
ORDER BY m.menu_id;
|
||||
@@ -1,88 +0,0 @@
|
||||
-- ==========================================
|
||||
-- 修复项目申报中申请人ID为空的问题
|
||||
-- 创建时间: 2025-08-01
|
||||
-- 描述: 检查并修复cw_project_application表中applicant_id为NULL的记录
|
||||
-- ==========================================
|
||||
|
||||
-- 1. 检查问题记录
|
||||
SELECT
|
||||
id,
|
||||
application_no,
|
||||
project_name,
|
||||
applicant_id,
|
||||
applicant_name,
|
||||
department_id,
|
||||
department_name,
|
||||
approval_status,
|
||||
create_by,
|
||||
create_time
|
||||
FROM cw_project_application
|
||||
WHERE applicant_id IS NULL OR applicant_id = 0;
|
||||
|
||||
-- 2. 修复方案1:将申请人ID设置为创建者ID(推荐)
|
||||
UPDATE cw_project_application
|
||||
SET
|
||||
applicant_id = create_by,
|
||||
applicant_name = (
|
||||
SELECT nick_name
|
||||
FROM sys_user
|
||||
WHERE user_id = cw_project_application.create_by
|
||||
AND deleted = 0
|
||||
LIMIT 1
|
||||
)
|
||||
WHERE (applicant_id IS NULL OR applicant_id = 0)
|
||||
AND create_by IS NOT NULL
|
||||
AND create_by > 0;
|
||||
|
||||
-- 3. 修复方案2:如果创建者也为空,设置为系统管理员(ID=1)
|
||||
UPDATE cw_project_application
|
||||
SET
|
||||
applicant_id = 1,
|
||||
applicant_name = '系统管理员'
|
||||
WHERE (applicant_id IS NULL OR applicant_id = 0)
|
||||
AND (create_by IS NULL OR create_by = 0);
|
||||
|
||||
-- 4. 修复部门信息为空的记录
|
||||
UPDATE cw_project_application
|
||||
SET
|
||||
department_id = (
|
||||
SELECT dept_id
|
||||
FROM sys_user
|
||||
WHERE user_id = cw_project_application.applicant_id
|
||||
AND deleted = 0
|
||||
LIMIT 1
|
||||
),
|
||||
department_name = (
|
||||
SELECT d.dept_name
|
||||
FROM sys_user u
|
||||
LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
|
||||
WHERE u.user_id = cw_project_application.applicant_id
|
||||
AND u.deleted = 0
|
||||
AND d.deleted = 0
|
||||
LIMIT 1
|
||||
)
|
||||
WHERE (department_id IS NULL OR department_id = 0)
|
||||
AND applicant_id IS NOT NULL
|
||||
AND applicant_id > 0;
|
||||
|
||||
-- 5. 验证修复结果
|
||||
SELECT
|
||||
'修复后检查' as check_type,
|
||||
COUNT(*) as total_records,
|
||||
SUM(CASE WHEN applicant_id IS NULL OR applicant_id = 0 THEN 1 ELSE 0 END) as null_applicant_count,
|
||||
SUM(CASE WHEN department_id IS NULL OR department_id = 0 THEN 1 ELSE 0 END) as null_department_count
|
||||
FROM cw_project_application;
|
||||
|
||||
-- 6. 显示修复后的记录
|
||||
SELECT
|
||||
id,
|
||||
application_no,
|
||||
project_name,
|
||||
applicant_id,
|
||||
applicant_name,
|
||||
department_id,
|
||||
department_name,
|
||||
approval_status
|
||||
FROM cw_project_application
|
||||
ORDER BY create_time DESC
|
||||
LIMIT 10;
|
||||
@@ -1,85 +0,0 @@
|
||||
-- 修复项目申报表中申请人ID为空的问题
|
||||
-- 日期: 2025-08-01
|
||||
-- 说明: 检查并修复cw_project_application表中applicant_id和department_id为空的记录
|
||||
|
||||
-- 1. 查看当前有问题的记录
|
||||
SELECT
|
||||
id,
|
||||
application_no,
|
||||
project_name,
|
||||
applicant_id,
|
||||
applicant_name,
|
||||
department_id,
|
||||
department_name,
|
||||
create_by,
|
||||
approval_status
|
||||
FROM cw_project_application
|
||||
WHERE applicant_id IS NULL
|
||||
OR applicant_id = 0
|
||||
OR department_id IS NULL
|
||||
OR department_id = 0
|
||||
ORDER BY create_time DESC;
|
||||
|
||||
-- 2. 修复applicant_id为空的记录(使用create_by作为申请人ID)
|
||||
UPDATE cw_project_application
|
||||
SET applicant_id = create_by
|
||||
WHERE (applicant_id IS NULL OR applicant_id = 0)
|
||||
AND create_by IS NOT NULL
|
||||
AND create_by > 0;
|
||||
|
||||
-- 3. 修复applicant_name为空的记录(从sys_user表获取用户名)
|
||||
UPDATE cw_project_application cpa
|
||||
INNER JOIN sys_user su ON cpa.applicant_id = su.user_id
|
||||
SET cpa.applicant_name = su.user_name
|
||||
WHERE (cpa.applicant_name IS NULL OR cpa.applicant_name = '')
|
||||
AND cpa.applicant_id IS NOT NULL;
|
||||
|
||||
-- 4. 修复department_id为空的记录(从sys_user表获取部门ID)
|
||||
UPDATE cw_project_application cpa
|
||||
INNER JOIN sys_user su ON cpa.applicant_id = su.user_id
|
||||
SET cpa.department_id = su.dept_id
|
||||
WHERE (cpa.department_id IS NULL OR cpa.department_id = 0)
|
||||
AND cpa.applicant_id IS NOT NULL
|
||||
AND su.dept_id IS NOT NULL;
|
||||
|
||||
-- 5. 修复department_name为空的记录(从sys_dept表获取部门名称)
|
||||
UPDATE cw_project_application cpa
|
||||
INNER JOIN sys_dept sd ON cpa.department_id = sd.dept_id
|
||||
SET cpa.department_name = sd.dept_name
|
||||
WHERE (cpa.department_name IS NULL OR cpa.department_name = '')
|
||||
AND cpa.department_id IS NOT NULL;
|
||||
|
||||
-- 6. 对于仍然无法修复的记录,设置为系统管理员(ID=1)
|
||||
UPDATE cw_project_application
|
||||
SET applicant_id = 1,
|
||||
applicant_name = 'admin',
|
||||
department_id = 103,
|
||||
department_name = '研发部门'
|
||||
WHERE applicant_id IS NULL
|
||||
OR applicant_id = 0
|
||||
OR department_id IS NULL
|
||||
OR department_id = 0;
|
||||
|
||||
-- 7. 验证修复结果
|
||||
SELECT
|
||||
COUNT(*) as total_records,
|
||||
COUNT(CASE WHEN applicant_id IS NULL OR applicant_id = 0 THEN 1 END) as null_applicant_id,
|
||||
COUNT(CASE WHEN department_id IS NULL OR department_id = 0 THEN 1 END) as null_department_id,
|
||||
COUNT(CASE WHEN applicant_name IS NULL OR applicant_name = '' THEN 1 END) as null_applicant_name,
|
||||
COUNT(CASE WHEN department_name IS NULL OR department_name = '' THEN 1 END) as null_department_name
|
||||
FROM cw_project_application;
|
||||
|
||||
-- 8. 显示修复后的记录
|
||||
SELECT
|
||||
id,
|
||||
application_no,
|
||||
project_name,
|
||||
applicant_id,
|
||||
applicant_name,
|
||||
department_id,
|
||||
department_name,
|
||||
approval_status,
|
||||
create_time
|
||||
FROM cw_project_application
|
||||
ORDER BY create_time DESC
|
||||
LIMIT 10;
|
||||
@@ -1,105 +0,0 @@
|
||||
-- 修复项目申报表中缺失字段的问题
|
||||
-- 日期: 2025-08-01
|
||||
-- 说明: 修复cw_project_application表中可能为空的必填字段
|
||||
|
||||
-- 1. 检查当前有问题的记录
|
||||
SELECT
|
||||
id,
|
||||
application_no,
|
||||
project_name,
|
||||
application_reason,
|
||||
construction_content,
|
||||
total_performance_goal,
|
||||
current_year_goal,
|
||||
department_name,
|
||||
approval_status
|
||||
FROM cw_project_application
|
||||
WHERE application_reason IS NULL
|
||||
OR application_reason = ''
|
||||
OR construction_content IS NULL
|
||||
OR construction_content = ''
|
||||
OR total_performance_goal IS NULL
|
||||
OR total_performance_goal = ''
|
||||
OR current_year_goal IS NULL
|
||||
OR current_year_goal = ''
|
||||
OR department_name IS NULL
|
||||
OR department_name = ''
|
||||
ORDER BY create_time DESC;
|
||||
|
||||
-- 2. 修复application_reason为空的记录
|
||||
UPDATE cw_project_application
|
||||
SET application_reason = CONCAT('项目申请原因:', project_name, '项目的设立基于实际业务需求,具有可行性和必要性。')
|
||||
WHERE (application_reason IS NULL OR application_reason = '')
|
||||
AND project_name IS NOT NULL;
|
||||
|
||||
-- 3. 修复construction_content为空的记录
|
||||
UPDATE cw_project_application
|
||||
SET construction_content = CONCAT('项目建设内容:', project_name, '项目的主要建设内容包括相关软硬件建设、人员培训等。')
|
||||
WHERE (construction_content IS NULL OR construction_content = '')
|
||||
AND project_name IS NOT NULL;
|
||||
|
||||
-- 4. 修复total_performance_goal为空的记录(基于绩效指标自动生成)
|
||||
UPDATE cw_project_application cpa
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
application_id,
|
||||
COUNT(CASE WHEN indicator_type IN ('quantity', 'quality', 'timeliness', 'cost') THEN 1 END) as output_count,
|
||||
COUNT(CASE WHEN indicator_type IN ('economic', 'social', 'environmental') THEN 1 END) as effect_count,
|
||||
COUNT(CASE WHEN indicator_type = 'satisfaction' THEN 1 END) as satisfaction_count
|
||||
FROM cw_performance_indicator
|
||||
WHERE deleted = 0
|
||||
GROUP BY application_id
|
||||
) pi ON cpa.id = pi.application_id
|
||||
SET cpa.total_performance_goal = CASE
|
||||
WHEN pi.application_id IS NOT NULL THEN
|
||||
CONCAT('通过本项目的实施,预期实现以下绩效目标:产出指标', IFNULL(pi.output_count, 0), '项,效益指标', IFNULL(pi.effect_count, 0), '项,满意度指标', IFNULL(pi.satisfaction_count, 0), '项,确保项目按期完成并达到预期效果。')
|
||||
ELSE
|
||||
CONCAT('通过', cpa.project_name, '项目的实施,预期实现既定的绩效目标,确保项目按期完成并达到预期效果。')
|
||||
END
|
||||
WHERE (cpa.total_performance_goal IS NULL OR cpa.total_performance_goal = '')
|
||||
AND cpa.project_name IS NOT NULL;
|
||||
|
||||
-- 5. 修复current_year_goal为空的记录
|
||||
UPDATE cw_project_application
|
||||
SET current_year_goal = CONCAT(YEAR(CURDATE()), '年度绩效目标:确保项目关键节点按计划推进,各项指标达到设定标准,为项目整体目标的实现奠定坚实基础。')
|
||||
WHERE (current_year_goal IS NULL OR current_year_goal = '')
|
||||
AND project_name IS NOT NULL;
|
||||
|
||||
-- 6. 修复department_name为空的记录(从sys_dept表获取)
|
||||
UPDATE cw_project_application cpa
|
||||
INNER JOIN sys_dept sd ON cpa.department_id = sd.dept_id
|
||||
SET cpa.department_name = sd.dept_name
|
||||
WHERE (cpa.department_name IS NULL OR cpa.department_name = '')
|
||||
AND cpa.department_id IS NOT NULL;
|
||||
|
||||
-- 7. 对于仍然缺失department_name的记录,设置默认值
|
||||
UPDATE cw_project_application
|
||||
SET department_name = '未指定部门'
|
||||
WHERE (department_name IS NULL OR department_name = '')
|
||||
AND applicant_id IS NOT NULL;
|
||||
|
||||
-- 8. 验证修复结果
|
||||
SELECT
|
||||
COUNT(*) as total_records,
|
||||
COUNT(CASE WHEN application_reason IS NULL OR application_reason = '' THEN 1 END) as null_application_reason,
|
||||
COUNT(CASE WHEN construction_content IS NULL OR construction_content = '' THEN 1 END) as null_construction_content,
|
||||
COUNT(CASE WHEN total_performance_goal IS NULL OR total_performance_goal = '' THEN 1 END) as null_total_performance_goal,
|
||||
COUNT(CASE WHEN current_year_goal IS NULL OR current_year_goal = '' THEN 1 END) as null_current_year_goal,
|
||||
COUNT(CASE WHEN department_name IS NULL OR department_name = '' THEN 1 END) as null_department_name
|
||||
FROM cw_project_application;
|
||||
|
||||
-- 9. 显示修复后的记录样例
|
||||
SELECT
|
||||
id,
|
||||
application_no,
|
||||
project_name,
|
||||
SUBSTRING(application_reason, 1, 50) as application_reason_preview,
|
||||
SUBSTRING(construction_content, 1, 50) as construction_content_preview,
|
||||
SUBSTRING(total_performance_goal, 1, 50) as total_performance_goal_preview,
|
||||
SUBSTRING(current_year_goal, 1, 50) as current_year_goal_preview,
|
||||
department_name,
|
||||
approval_status,
|
||||
create_time
|
||||
FROM cw_project_application
|
||||
ORDER BY create_time DESC
|
||||
LIMIT 10;
|
||||
@@ -1,31 +0,0 @@
|
||||
-- 补充学生请假审批状态字典数据脚本
|
||||
-- 日期: 2025-01-27
|
||||
-- 说明: 补充缺失的学生请假审批状态字典数据
|
||||
|
||||
-- 补充缺失的字典数据
|
||||
-- 已撤销
|
||||
INSERT IGNORE INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (2000000000000009032, '000000', 1, '已撤销', 'cancel', 'student_leave_approval_status', '', 'info', 'N', 1, now(), '已撤销');
|
||||
|
||||
-- 草稿
|
||||
INSERT IGNORE INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (2000000000000009033, '000000', 2, '草稿', 'draft', 'student_leave_approval_status', '', 'info', 'N', 1, now(), '草稿状态');
|
||||
|
||||
-- 待审核
|
||||
INSERT IGNORE INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (2000000000000009034, '000000', 3, '待审核', 'waiting', 'student_leave_approval_status', '', 'warning', 'N', 1, now(), '待审核状态');
|
||||
|
||||
-- 已完成
|
||||
INSERT IGNORE INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_by, create_time, remark)
|
||||
VALUES (2000000000000009035, '000000', 4, '已完成', 'finish', 'student_leave_approval_status', '', 'success', 'N', 1, now(), '已完成状态');
|
||||
|
||||
-- 验证补充结果
|
||||
SELECT
|
||||
'补充后完整字典数据' as check_item,
|
||||
dict_sort,
|
||||
dict_label,
|
||||
dict_value,
|
||||
list_class
|
||||
FROM sys_dict_data
|
||||
WHERE dict_type = 'student_leave_approval_status'
|
||||
ORDER BY dict_sort;
|
||||
@@ -1,2 +0,0 @@
|
||||
-- 为xljk_consultation_appointment表添加appointment_detail_time字段
|
||||
ALTER TABLE xljk_consultation_appointment ADD COLUMN appointment_detail_time VARCHAR(20) DEFAULT NULL COMMENT '具体预约时间段,如"08:30-09:00"';
|
||||
@@ -1,76 +0,0 @@
|
||||
-- 测试审批流程SQL脚本
|
||||
-- 用于验证 start_time 字段修复是否有效
|
||||
|
||||
-- 1. 清理测试数据
|
||||
DELETE FROM cw_approval_record WHERE application_no LIKE 'TEST%';
|
||||
DELETE FROM cw_project_application WHERE application_no LIKE 'TEST%';
|
||||
|
||||
-- 2. 创建测试项目申报
|
||||
INSERT INTO cw_project_application (
|
||||
id, application_no, project_name, principal, contact_phone,
|
||||
department_id, department_name, total_investment, budget_year,
|
||||
start_date, end_date, project_description, expected_benefits,
|
||||
approval_status, create_by, create_time, update_by, update_time,
|
||||
create_dept, tenant_id, deleted
|
||||
) VALUES (
|
||||
9999999999999999, 'TEST20250801001', '测试项目申报', '测试用户', '13800000000',
|
||||
103, '教务处', 100000.00, '2025',
|
||||
'2025-01-01', '2025-12-31', '这是一个测试项目', '预期效益很好',
|
||||
'submitted', 1, NOW(), 1, NOW(),
|
||||
103, '000000', 0
|
||||
);
|
||||
|
||||
-- 3. 手动创建审批记录(模拟我们的批量插入逻辑)
|
||||
INSERT INTO cw_approval_record (
|
||||
id, application_id, application_no, node_code, node_name, node_order,
|
||||
approver_id, approver_name, is_delegate, original_approver_id, original_approver_name,
|
||||
node_status, approve_action, approve_opinion, start_time, end_time, expect_finish_time,
|
||||
is_overtime, config_id, config_snapshot, create_by, create_time, update_by, update_time,
|
||||
create_dept, tenant_id
|
||||
) VALUES
|
||||
(9999999999999991, 9999999999999999, 'TEST20250801001', 'dept_approve', '部门审批', 1,
|
||||
1, 'admin_部门审批', 0, NULL, NULL,
|
||||
'waiting', NULL, NULL, NOW(), NULL, NULL,
|
||||
0, NULL, NULL, 1, NOW(), 1, NOW(),
|
||||
103, '000000'),
|
||||
(9999999999999992, 9999999999999999, 'TEST20250801001', 'finance_approve', '财务审批', 2,
|
||||
1, 'admin_财务审批', 0, NULL, NULL,
|
||||
'pending', NULL, NULL, NOW(), NULL, NULL,
|
||||
0, NULL, NULL, 1, NOW(), 1, NOW(),
|
||||
103, '000000'),
|
||||
(9999999999999993, 9999999999999999, 'TEST20250801001', 'leader_approve', '领导审批', 3,
|
||||
1, 'admin_领导审批', 0, NULL, NULL,
|
||||
'pending', NULL, NULL, NOW(), NULL, NULL,
|
||||
0, NULL, NULL, 1, NOW(), 1, NOW(),
|
||||
103, '000000');
|
||||
|
||||
-- 4. 验证插入结果
|
||||
SELECT
|
||||
'=== 测试结果验证 ===' as title,
|
||||
COUNT(*) as total_records,
|
||||
COUNT(CASE WHEN start_time IS NOT NULL THEN 1 END) as records_with_start_time,
|
||||
COUNT(CASE WHEN start_time IS NULL THEN 1 END) as records_with_null_start_time
|
||||
FROM cw_approval_record
|
||||
WHERE application_no = 'TEST20250801001';
|
||||
|
||||
-- 5. 显示详细记录
|
||||
SELECT
|
||||
node_code,
|
||||
node_name,
|
||||
node_status,
|
||||
approver_name,
|
||||
start_time,
|
||||
CASE
|
||||
WHEN start_time IS NULL THEN '❌ NULL (问题未修复)'
|
||||
ELSE '✅ 有值 (修复成功)'
|
||||
END as start_time_status
|
||||
FROM cw_approval_record
|
||||
WHERE application_no = 'TEST20250801001'
|
||||
ORDER BY node_order;
|
||||
|
||||
-- 6. 清理测试数据
|
||||
-- DELETE FROM cw_approval_record WHERE application_no = 'TEST20250801001';
|
||||
-- DELETE FROM cw_project_application WHERE application_no = 'TEST20250801001';
|
||||
|
||||
-- 如果上述测试成功(所有记录都有start_time值),说明我们的修复有效
|
||||
-- 如果测试失败,请检查是否还有其他问题
|
||||
@@ -1,90 +0,0 @@
|
||||
-- ==================================================
|
||||
-- 招生就业模块 - 招聘信息管理菜单配置
|
||||
-- 创建日期: 2025-01-26
|
||||
-- 说明: 参考企业信息管理菜单配置
|
||||
-- ==================================================
|
||||
|
||||
-- 1. 招聘信息管理父菜单(招聘管理)
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (8710, '招聘管理', 8000, 6, 'recruitment', null, null, 1, 0, 'M', '0', '0', '', 'post', 103, 1, NOW(), null, null, '招聘管理菜单');
|
||||
|
||||
-- 2. 招聘信息子菜单(招聘岗位)
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (8711, '招聘岗位', 8710, 1, 'position', 'zsjy/jobPosition/index', null, 1, 0, 'C', '0', '0', 'zsjy:position:list', 'job', 103, 1, NOW(), null, null, '招聘岗位菜单');
|
||||
|
||||
-- 3. 招聘岗位按钮权限
|
||||
-- 3.1 查询权限
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (8712, '招聘岗位查询', 8711, 1, '', '', null, 1, 0, 'F', '0', '0', 'zsjy:position:query', '#', 103, 1, NOW(), null, null, '');
|
||||
|
||||
-- 3.2 新增权限
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (8713, '招聘岗位新增', 8711, 2, '', '', null, 1, 0, 'F', '0', '0', 'zsjy:position:add', '#', 103, 1, NOW(), null, null, '');
|
||||
|
||||
-- 3.3 修改权限
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (8714, '招聘岗位修改', 8711, 3, '', '', null, 1, 0, 'F', '0', '0', 'zsjy:position:edit', '#', 103, 1, NOW(), null, null, '');
|
||||
|
||||
-- 3.4 删除权限
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (8715, '招聘岗位删除', 8711, 4, '', '', null, 1, 0, 'F', '0', '0', 'zsjy:position:remove', '#', 103, 1, NOW(), null, null, '');
|
||||
|
||||
-- 3.5 导出权限
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (8716, '招聘岗位导出', 8711, 5, '', '', null, 1, 0, 'F', '0', '0', 'zsjy:position:export', '#', 103, 1, NOW(), null, null, '');
|
||||
|
||||
-- 4. 招聘海报子菜单
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (8720, '招聘海报', 8710, 2, 'poster', 'zsjy/recruitmentPoster/index', null, 1, 0, 'C', '0', '0', 'zsjy:poster:list', 'picture', 103, 1, NOW(), null, null, '招聘海报菜单');
|
||||
|
||||
-- 5. 招聘海报按钮权限
|
||||
-- 5.1 查询权限
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (8721, '招聘海报查询', 8720, 1, '', '', null, 1, 0, 'F', '0', '0', 'zsjy:poster:query', '#', 103, 1, NOW(), null, null, '');
|
||||
|
||||
-- 5.2 新增权限
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (8722, '招聘海报新增', 8720, 2, '', '', null, 1, 0, 'F', '0', '0', 'zsjy:poster:add', '#', 103, 1, NOW(), null, null, '');
|
||||
|
||||
-- 5.3 修改权限
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (8723, '招聘海报修改', 8720, 3, '', '', null, 1, 0, 'F', '0', '0', 'zsjy:poster:edit', '#', 103, 1, NOW(), null, null, '');
|
||||
|
||||
-- 5.4 删除权限
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (8724, '招聘海报删除', 8720, 4, '', '', null, 1, 0, 'F', '0', '0', 'zsjy:poster:remove', '#', 103, 1, NOW(), null, null, '');
|
||||
|
||||
-- 5.5 导出权限
|
||||
INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
VALUES (8725, '招聘海报导出', 8720, 5, '', '', null, 1, 0, 'F', '0', '0', 'zsjy:poster:export', '#', 103, 1, NOW(), null, null, '');
|
||||
|
||||
-- ==================================================
|
||||
-- 为管理员角色分配招聘管理相关权限
|
||||
-- ==================================================
|
||||
|
||||
-- 为管理员角色(role_id=1)分配招聘管理菜单权限
|
||||
INSERT INTO sys_role_menu (role_id, menu_id) VALUES (1, 8710); -- 招聘管理
|
||||
INSERT INTO sys_role_menu (role_id, menu_id) VALUES (1, 8711); -- 招聘岗位
|
||||
INSERT INTO sys_role_menu (role_id, menu_id) VALUES (1, 8712); -- 招聘岗位查询
|
||||
INSERT INTO sys_role_menu (role_id, menu_id) VALUES (1, 8713); -- 招聘岗位新增
|
||||
INSERT INTO sys_role_menu (role_id, menu_id) VALUES (1, 8714); -- 招聘岗位修改
|
||||
INSERT INTO sys_role_menu (role_id, menu_id) VALUES (1, 8715); -- 招聘岗位删除
|
||||
INSERT INTO sys_role_menu (role_id, menu_id) VALUES (1, 8716); -- 招聘岗位导出
|
||||
INSERT INTO sys_role_menu (role_id, menu_id) VALUES (1, 8720); -- 招聘海报
|
||||
INSERT INTO sys_role_menu (role_id, menu_id) VALUES (1, 8721); -- 招聘海报查询
|
||||
INSERT INTO sys_role_menu (role_id, menu_id) VALUES (1, 8722); -- 招聘海报新增
|
||||
INSERT INTO sys_role_menu (role_id, menu_id) VALUES (1, 8723); -- 招聘海报修改
|
||||
INSERT INTO sys_role_menu (role_id, menu_id) VALUES (1, 8724); -- 招聘海报删除
|
||||
INSERT INTO sys_role_menu (role_id, menu_id) VALUES (1, 8725); -- 招聘海报导出
|
||||
|
||||
-- ==================================================
|
||||
-- 执行前检查
|
||||
-- ==================================================
|
||||
-- SELECT * FROM sys_menu WHERE menu_id BETWEEN 8710 AND 8725;
|
||||
-- SELECT * FROM sys_role_menu WHERE role_id = 1 AND menu_id BETWEEN 8710 AND 8725;
|
||||
|
||||
-- ==================================================
|
||||
-- 回滚语句
|
||||
-- ==================================================
|
||||
-- DELETE FROM sys_role_menu WHERE role_id = 1 AND menu_id BETWEEN 8710 AND 8725;
|
||||
-- DELETE FROM sys_menu WHERE menu_id BETWEEN 8710 AND 8725;
|
||||
@@ -1,550 +0,0 @@
|
||||
/**
|
||||
* 社团管理系统API测试脚本
|
||||
*
|
||||
* 功能说明:
|
||||
* 1. 社团信息管理 - 增删改查、状态管理、权限验证
|
||||
* 2. 社团成员管理 - 成员添加、角色管理、退出处理
|
||||
* 3. 社团报名大厅 - 浏览社团、报名加入、我的社团
|
||||
*
|
||||
* 测试环境配置:
|
||||
* - 服务器地址:http://localhost:8080
|
||||
* - 加密登录:AES+RSA双重加密
|
||||
* - 权限验证:Sa-Token
|
||||
*
|
||||
* 运行方法:
|
||||
* npm install axios crypto
|
||||
* node stgl-api-test.js
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-15
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
const axios = require('axios');
|
||||
const crypto = require('crypto');
|
||||
|
||||
// ============================== 配置信息 ==============================
|
||||
const CONFIG = {
|
||||
BASE_URL: 'http://localhost:8080',
|
||||
TIMEOUT: 30000,
|
||||
|
||||
// 测试用户配置
|
||||
TEST_USER: {
|
||||
username: 'admin',
|
||||
password: 'admin123',
|
||||
clientid: 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
||||
referer: 'http://localhost'
|
||||
},
|
||||
|
||||
// AES加密密钥(16位)
|
||||
AES_SECRET_KEY: '1234567890123456',
|
||||
|
||||
// RSA公钥
|
||||
RSA_PUBLIC_KEY: `-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7zL3w8s0LnG7yMrp9fGU+ZOwS
|
||||
Wd3g0CqX8nfF5qSZQ7GNcN8L3HS9N3jQKLdFy/9bTQJb8KcV3hxGJEGZ5cV3zqJf
|
||||
oX5x7N8sGZHtQzN5pG4qqGt8M2QlGbFQNQXxZ8HwJj8yGjQl7Z7gN3Z7yKd1J3M2
|
||||
5fX3Q2mFtGzP8KGc8QIDAQAB
|
||||
-----END PUBLIC KEY-----`
|
||||
};
|
||||
|
||||
// ============================== 工具函数 ==============================
|
||||
|
||||
// AES加密函数
|
||||
function aesEncrypt(text, key) {
|
||||
const cipher = crypto.createCipher('aes-128-ecb', key);
|
||||
let encrypted = cipher.update(text, 'utf8', 'hex');
|
||||
encrypted += cipher.final('hex');
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
// RSA加密函数
|
||||
function rsaEncrypt(text, publicKey) {
|
||||
const buffer = Buffer.from(text, 'utf8');
|
||||
const encrypted = crypto.publicEncrypt({
|
||||
key: publicKey,
|
||||
padding: crypto.constants.RSA_PKCS1_PADDING
|
||||
}, buffer);
|
||||
return encrypted.toString('base64');
|
||||
}
|
||||
|
||||
// HTTP请求封装
|
||||
const request = axios.create({
|
||||
baseURL: CONFIG.BASE_URL,
|
||||
timeout: CONFIG.TIMEOUT,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'clientid': CONFIG.TEST_USER.clientid,
|
||||
'referer': CONFIG.TEST_USER.referer
|
||||
}
|
||||
});
|
||||
|
||||
// 全局变量
|
||||
let authToken = '';
|
||||
let testData = {
|
||||
clubId: null,
|
||||
memberId: null,
|
||||
userIds: []
|
||||
};
|
||||
|
||||
// ============================== 认证相关 ==============================
|
||||
|
||||
/**
|
||||
* 用户登录(AES+RSA双重加密)
|
||||
*/
|
||||
async function login() {
|
||||
console.log('\n🔐 开始用户登录...');
|
||||
|
||||
try {
|
||||
// 第一步:AES加密用户名和密码
|
||||
const aesUsername = aesEncrypt(CONFIG.TEST_USER.username, CONFIG.AES_SECRET_KEY);
|
||||
const aesPassword = aesEncrypt(CONFIG.TEST_USER.password, CONFIG.AES_SECRET_KEY);
|
||||
|
||||
// 第二步:RSA加密AES密钥
|
||||
const rsaKey = rsaEncrypt(CONFIG.AES_SECRET_KEY, CONFIG.RSA_PUBLIC_KEY);
|
||||
|
||||
// 第三步:发送登录请求
|
||||
const response = await request.post('/auth/login', {
|
||||
username: aesUsername,
|
||||
password: aesPassword,
|
||||
key: rsaKey,
|
||||
captcha: '',
|
||||
uuid: ''
|
||||
});
|
||||
|
||||
if (response.data.code === 200) {
|
||||
authToken = response.data.data.access_token;
|
||||
request.defaults.headers['Authorization'] = `Bearer ${authToken}`;
|
||||
console.log('✅ 登录成功');
|
||||
console.log(`🔑 Token: ${authToken.substring(0, 50)}...`);
|
||||
return true;
|
||||
} else {
|
||||
console.error('❌ 登录失败:', response.data.msg);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ 登录请求失败:', error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== 社团信息管理API测试 ==============================
|
||||
|
||||
/**
|
||||
* 测试社团信息管理功能
|
||||
*/
|
||||
async function testClubManagement() {
|
||||
console.log('\n📋 ========== 社团信息管理API测试 ==========');
|
||||
|
||||
// 1. 查询社团列表
|
||||
await testAPI('GET', '/stgl/club/list', {
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
}, '查询社团列表');
|
||||
|
||||
// 2. 新增社团
|
||||
const clubData = {
|
||||
clubName: '智能科技社团',
|
||||
clubType: '学术科技类',
|
||||
establishDate: '2025-01-15',
|
||||
description: '专注于人工智能、机器学习等前沿科技领域的学术研究和实践交流社团',
|
||||
email: 'aitech@example.com',
|
||||
phone: '028-12345678',
|
||||
presidentName: '张三',
|
||||
presidentStudentNo: '20250001',
|
||||
presidentDept: '计算机学院',
|
||||
presidentMajor: '软件工程',
|
||||
presidentClass: '软工2501班',
|
||||
presidentPhone: '13800138001',
|
||||
advisorName: '李教授',
|
||||
advisorDept: '计算机学院',
|
||||
memberLimit: 50,
|
||||
recruitCycle: '每学期',
|
||||
recruitTime: '9月、3月',
|
||||
activityFrequency: '每周1-2次',
|
||||
fundingSources: '学校拨款,社团自筹',
|
||||
status: 1
|
||||
};
|
||||
|
||||
const createResult = await testAPI('POST', '/stgl/club', clubData, '新增社团');
|
||||
if (createResult) {
|
||||
// 获取创建的社团ID
|
||||
const listResult = await testAPI('GET', '/stgl/club/list', {
|
||||
clubName: clubData.clubName
|
||||
}, '查询新建社团ID');
|
||||
|
||||
if (listResult && listResult.data.rows.length > 0) {
|
||||
testData.clubId = listResult.data.rows[0].id;
|
||||
console.log(`🆔 获取到社团ID: ${testData.clubId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 查询社团详情
|
||||
if (testData.clubId) {
|
||||
await testAPI('GET', `/stgl/club/${testData.clubId}`, {}, '查询社团详情');
|
||||
}
|
||||
|
||||
// 4. 修改社团信息
|
||||
if (testData.clubId) {
|
||||
await testAPI('PUT', '/stgl/club', {
|
||||
id: testData.clubId,
|
||||
clubName: '智能科技社团(更新版)',
|
||||
memberLimit: 60
|
||||
}, '修改社团信息');
|
||||
}
|
||||
|
||||
// 5. 查询可用社团选项
|
||||
await testAPI('GET', '/stgl/club/options', {}, '查询可用社团选项');
|
||||
|
||||
// 6. 检查社团名称唯一性
|
||||
await testAPI('GET', '/stgl/club/checkClubNameUnique', {
|
||||
clubName: '不存在的社团名称'
|
||||
}, '检查社团名称唯一性');
|
||||
|
||||
// 7. 查询我管理的社团
|
||||
await testAPI('GET', '/stgl/club/myClubs', {}, '查询我管理的社团');
|
||||
|
||||
// 8. 启用/禁用社团
|
||||
if (testData.clubId) {
|
||||
await testAPI('PUT', '/stgl/club/changeStatus', {}, '启用/禁用社团', {
|
||||
id: testData.clubId,
|
||||
status: 0
|
||||
});
|
||||
|
||||
await testAPI('PUT', '/stgl/club/changeStatus', {}, '重新启用社团', {
|
||||
id: testData.clubId,
|
||||
status: 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== 社团成员管理API测试 ==============================
|
||||
|
||||
/**
|
||||
* 测试社团成员管理功能
|
||||
*/
|
||||
async function testMemberManagement() {
|
||||
console.log('\n👥 ========== 社团成员管理API测试 ==========');
|
||||
|
||||
// 1. 查询成员列表
|
||||
await testAPI('GET', '/stgl/member/list', {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
memberType: 1
|
||||
}, '查询成员列表');
|
||||
|
||||
// 2. 新增成员
|
||||
if (testData.clubId) {
|
||||
const memberData = {
|
||||
memberType: 1,
|
||||
relationId: testData.clubId,
|
||||
userId: 1001,
|
||||
studentNo: '20250002',
|
||||
studentName: '李四',
|
||||
roleType: 3,
|
||||
status: 1
|
||||
};
|
||||
|
||||
const createResult = await testAPI('POST', '/stgl/member', memberData, '新增成员');
|
||||
if (createResult) {
|
||||
// 获取创建的成员ID
|
||||
const listResult = await testAPI('GET', '/stgl/member/list', {
|
||||
studentNo: memberData.studentNo
|
||||
}, '查询新建成员ID');
|
||||
|
||||
if (listResult && listResult.data.rows.length > 0) {
|
||||
testData.memberId = listResult.data.rows[0].id;
|
||||
console.log(`🆔 获取到成员ID: ${testData.memberId}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 查询成员详情
|
||||
if (testData.memberId) {
|
||||
await testAPI('GET', `/stgl/member/${testData.memberId}`, {}, '查询成员详情');
|
||||
}
|
||||
|
||||
// 4. 修改成员信息
|
||||
if (testData.memberId) {
|
||||
await testAPI('PUT', '/stgl/member', {
|
||||
id: testData.memberId,
|
||||
studentName: '李四(更新)',
|
||||
roleType: 2,
|
||||
remark: '表现优秀,晋升为副社长'
|
||||
}, '修改成员信息');
|
||||
}
|
||||
|
||||
// 5. 根据社团ID查询成员
|
||||
if (testData.clubId) {
|
||||
await testAPI('GET', `/stgl/member/club/${testData.clubId}`, {}, '根据社团ID查询成员');
|
||||
}
|
||||
|
||||
// 6. 批量添加成员
|
||||
if (testData.clubId) {
|
||||
await testAPI('POST', '/stgl/member/batchAdd', {}, '批量添加成员', {
|
||||
memberType: 1,
|
||||
relationId: testData.clubId,
|
||||
userIds: [1002, 1003, 1004],
|
||||
roleType: 3
|
||||
});
|
||||
}
|
||||
|
||||
// 7. 更新成员角色
|
||||
if (testData.memberId) {
|
||||
await testAPI('PUT', '/stgl/member/updateRole', {}, '更新成员角色', {
|
||||
id: testData.memberId,
|
||||
roleType: 3
|
||||
});
|
||||
}
|
||||
|
||||
// 8. 检查用户是否已是成员
|
||||
if (testData.clubId) {
|
||||
await testAPI('GET', '/stgl/member/checkMemberExists', {
|
||||
memberType: 1,
|
||||
relationId: testData.clubId,
|
||||
userId: 1001
|
||||
}, '检查用户是否已是成员');
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== 社团报名大厅API测试 ==============================
|
||||
|
||||
/**
|
||||
* 测试社团报名大厅功能
|
||||
*/
|
||||
async function testClubRegister() {
|
||||
console.log('\n🎯 ========== 社团报名大厅API测试 ==========');
|
||||
|
||||
// 1. 查询社团报名大厅列表
|
||||
await testAPI('GET', '/stgl/register/list', {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
clubType: '学术科技类'
|
||||
}, '查询社团报名大厅列表');
|
||||
|
||||
// 2. 查询社团报名大厅列表(不分页)
|
||||
await testAPI('GET', '/stgl/register/listAll', {
|
||||
clubType: '学术科技类'
|
||||
}, '查询社团报名大厅列表(不分页)');
|
||||
|
||||
// 3. 查询社团详细信息
|
||||
if (testData.clubId) {
|
||||
await testAPI('GET', `/stgl/register/${testData.clubId}`, {}, '查询社团详细信息');
|
||||
}
|
||||
|
||||
// 4. 报名加入社团
|
||||
if (testData.clubId) {
|
||||
await testAPI('POST', `/stgl/register/join/${testData.clubId}`, {}, '报名加入社团');
|
||||
}
|
||||
|
||||
// 5. 检查报名状态
|
||||
if (testData.clubId) {
|
||||
await testAPI('GET', `/stgl/register/checkRegistered/${testData.clubId}`, {}, '检查报名状态');
|
||||
}
|
||||
|
||||
// 6. 查询我参与的社团
|
||||
await testAPI('GET', '/stgl/register/myClubs', {}, '查询我参与的社团');
|
||||
|
||||
// 7. 查询推荐社团
|
||||
await testAPI('GET', '/stgl/register/recommend', {
|
||||
clubType: '学术科技类',
|
||||
limit: 5
|
||||
}, '查询推荐社团');
|
||||
|
||||
// 8. 退出社团
|
||||
if (testData.clubId) {
|
||||
await testAPI('POST', `/stgl/register/quit/${testData.clubId}`, {}, '退出社团');
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== 通用API测试函数 ==============================
|
||||
|
||||
/**
|
||||
* 通用API测试函数
|
||||
* @param {string} method HTTP方法
|
||||
* @param {string} url 请求URL
|
||||
* @param {object} data 请求数据
|
||||
* @param {string} description 测试描述
|
||||
* @param {object} params 查询参数
|
||||
*/
|
||||
async function testAPI(method, url, data = {}, description = '', params = {}) {
|
||||
console.log(`\n${getMethodIcon(method)} 测试${description}...`);
|
||||
|
||||
try {
|
||||
let response;
|
||||
const config = {
|
||||
params: Object.keys(params).length > 0 ? params : (method === 'GET' ? data : {})
|
||||
};
|
||||
|
||||
switch (method.toUpperCase()) {
|
||||
case 'GET':
|
||||
response = await request.get(url, config);
|
||||
break;
|
||||
case 'POST':
|
||||
response = await request.post(url, data, config);
|
||||
break;
|
||||
case 'PUT':
|
||||
response = await request.put(url, data, config);
|
||||
break;
|
||||
case 'DELETE':
|
||||
response = await request.delete(url, config);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`不支持的HTTP方法: ${method}`);
|
||||
}
|
||||
|
||||
if (response.data.code === 200) {
|
||||
console.log(`✅ ${description}成功`);
|
||||
|
||||
// 根据响应类型输出不同信息
|
||||
if (response.data.data) {
|
||||
if (Array.isArray(response.data.data)) {
|
||||
console.log(`📊 返回数据数量: ${response.data.data.length}`);
|
||||
} else if (response.data.data.rows) {
|
||||
console.log(`📊 总记录数: ${response.data.data.total}`);
|
||||
console.log(`📄 当前页数据: ${response.data.data.rows.length} 条`);
|
||||
} else if (typeof response.data.data === 'object') {
|
||||
console.log(`📝 返回数据类型: ${Object.keys(response.data.data).join(', ')}`);
|
||||
} else {
|
||||
console.log(`📝 返回数据: ${response.data.data}`);
|
||||
}
|
||||
}
|
||||
|
||||
return response.data;
|
||||
} else {
|
||||
console.error(`❌ ${description}失败:`, response.data.msg);
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`❌ ${description}请求失败:`, error.response?.data?.msg || error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取HTTP方法对应的图标
|
||||
*/
|
||||
function getMethodIcon(method) {
|
||||
const icons = {
|
||||
'GET': '🔍',
|
||||
'POST': '➕',
|
||||
'PUT': '✏️',
|
||||
'DELETE': '🗑️'
|
||||
};
|
||||
return icons[method.toUpperCase()] || '📡';
|
||||
}
|
||||
|
||||
// ============================== 清理测试数据 ==============================
|
||||
|
||||
/**
|
||||
* 清理测试数据
|
||||
*/
|
||||
async function cleanupTestData() {
|
||||
console.log('\n🧹 ========== 清理测试数据 ==========');
|
||||
|
||||
// 删除测试社团(会级联删除相关成员)
|
||||
if (testData.clubId) {
|
||||
await testAPI('DELETE', `/stgl/club/${testData.clubId}`, {}, '删除测试社团');
|
||||
}
|
||||
|
||||
console.log('🎯 测试数据清理完成');
|
||||
}
|
||||
|
||||
// ============================== 主测试流程 ==============================
|
||||
|
||||
/**
|
||||
* 主测试函数
|
||||
*/
|
||||
async function runTests() {
|
||||
console.log('🚀 ========== 社团管理系统API测试开始 ==========');
|
||||
console.log(`📅 测试时间: ${new Date().toLocaleString()}`);
|
||||
console.log(`🌐 服务器地址: ${CONFIG.BASE_URL}`);
|
||||
console.log(`👤 测试用户: ${CONFIG.TEST_USER.username}`);
|
||||
|
||||
const startTime = Date.now();
|
||||
let successCount = 0;
|
||||
let totalCount = 0;
|
||||
|
||||
try {
|
||||
// 1. 用户认证
|
||||
console.log('\n🔐 ========== 用户认证测试 ==========');
|
||||
const loginSuccess = await login();
|
||||
if (!loginSuccess) {
|
||||
console.log('❌ 登录失败,退出测试');
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
const userInfo = await testAPI('GET', '/system/user/getInfo', {}, '获取用户信息');
|
||||
if (userInfo && userInfo.data.user) {
|
||||
console.log(`👤 当前用户: ${userInfo.data.user.userName} (${userInfo.data.user.nickName})`);
|
||||
}
|
||||
|
||||
// 2. 社团信息管理测试
|
||||
await testClubManagement();
|
||||
|
||||
// 3. 社团成员管理测试
|
||||
await testMemberManagement();
|
||||
|
||||
// 4. 社团报名大厅测试
|
||||
await testClubRegister();
|
||||
|
||||
// 5. 清理测试数据
|
||||
await cleanupTestData();
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ 测试过程中发生错误:', error.message);
|
||||
}
|
||||
|
||||
const endTime = Date.now();
|
||||
const duration = (endTime - startTime) / 1000;
|
||||
|
||||
console.log('\n🎯 ========== 社团管理系统API测试完成 ==========');
|
||||
console.log(`⏱️ 测试耗时: ${duration.toFixed(2)} 秒`);
|
||||
console.log('📊 测试结果总结:');
|
||||
console.log(' ✅ 社团信息管理: 8个API接口');
|
||||
console.log(' ✅ 社团成员管理: 8个API接口');
|
||||
console.log(' ✅ 社团报名大厅: 8个API接口');
|
||||
console.log(' 📝 总计: 24个API接口测试完成');
|
||||
console.log('\n📋 API接口列表:');
|
||||
console.log('【社团信息管理】');
|
||||
console.log(' - GET /stgl/club/list 查询社团列表');
|
||||
console.log(' - POST /stgl/club 新增社团');
|
||||
console.log(' - GET /stgl/club/{id} 查询社团详情');
|
||||
console.log(' - PUT /stgl/club 修改社团信息');
|
||||
console.log(' - GET /stgl/club/options 查询可用社团选项');
|
||||
console.log(' - GET /stgl/club/checkClubNameUnique 检查社团名称唯一性');
|
||||
console.log(' - GET /stgl/club/myClubs 查询我管理的社团');
|
||||
console.log(' - PUT /stgl/club/changeStatus 启用/禁用社团');
|
||||
console.log('\n【社团成员管理】');
|
||||
console.log(' - GET /stgl/member/list 查询成员列表');
|
||||
console.log(' - POST /stgl/member 新增成员');
|
||||
console.log(' - GET /stgl/member/{id} 查询成员详情');
|
||||
console.log(' - PUT /stgl/member 修改成员信息');
|
||||
console.log(' - GET /stgl/member/club/{clubId} 根据社团ID查询成员');
|
||||
console.log(' - POST /stgl/member/batchAdd 批量添加成员');
|
||||
console.log(' - PUT /stgl/member/updateRole 更新成员角色');
|
||||
console.log(' - GET /stgl/member/checkMemberExists 检查用户是否已是成员');
|
||||
console.log('\n【社团报名大厅】');
|
||||
console.log(' - GET /stgl/register/list 查询社团报名大厅列表');
|
||||
console.log(' - GET /stgl/register/listAll 查询社团报名大厅列表(不分页)');
|
||||
console.log(' - GET /stgl/register/{clubId} 查询社团详细信息');
|
||||
console.log(' - POST /stgl/register/join/{clubId} 报名加入社团');
|
||||
console.log(' - GET /stgl/register/checkRegistered/{clubId} 检查报名状态');
|
||||
console.log(' - GET /stgl/register/myClubs 查询我参与的社团');
|
||||
console.log(' - GET /stgl/register/recommend 查询推荐社团');
|
||||
console.log(' - POST /stgl/register/quit/{clubId} 退出社团');
|
||||
}
|
||||
|
||||
// 导出模块
|
||||
module.exports = {
|
||||
runTests,
|
||||
CONFIG,
|
||||
testAPI,
|
||||
login
|
||||
};
|
||||
|
||||
// 如果直接运行此文件,则执行测试
|
||||
if (require.main === module) {
|
||||
runTests().catch(console.error);
|
||||
}
|
||||
@@ -1,411 +0,0 @@
|
||||
const axios = require('axios');
|
||||
const colors = require('colors');
|
||||
|
||||
// 测试配置
|
||||
const CONFIG = {
|
||||
baseURL: 'http://localhost/dev-api',
|
||||
orderNo: 'RO20250126008',
|
||||
username: 'zwc007',
|
||||
password: 'admin123', // 需要根据实际密码调整
|
||||
timeout: 10000,
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取Token的通用函数
|
||||
*/
|
||||
async function getToken(username, password) {
|
||||
const { getToken } = require('./quick-token-getter');
|
||||
return await getToken(username, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试API调用的通用函数
|
||||
*/
|
||||
async function testAPI(endpoint, data, options = {}) {
|
||||
try {
|
||||
const response = await axios({
|
||||
method: options.method || 'GET',
|
||||
url: `${CONFIG.baseURL}${endpoint}`,
|
||||
data: data,
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=utf-8',
|
||||
clientid: 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
||||
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
||||
referer: 'http://localhost/login?redirect=%252Findex',
|
||||
Authorization: options.token ? `Bearer ${options.token}` : undefined,
|
||||
...options.headers,
|
||||
},
|
||||
timeout: CONFIG.timeout,
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: response.data,
|
||||
status: response.status,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data || error.message,
|
||||
status: error.response?.status,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试工单RO20250126008的更新进度按钮
|
||||
*/
|
||||
async function testRO20250126008UpdateProgressButton() {
|
||||
console.log('\n' + '='.repeat(80));
|
||||
console.log(colors.cyan.bold(' 工单RO20250126008更新进度按钮测试'));
|
||||
console.log('='.repeat(80));
|
||||
console.log(colors.yellow(`测试时间: ${new Date().toLocaleString()}`));
|
||||
console.log(colors.yellow(`目标工单: ${CONFIG.orderNo}`));
|
||||
console.log(colors.yellow(`测试用户: ${CONFIG.username}`));
|
||||
console.log('='.repeat(80));
|
||||
|
||||
const results = {
|
||||
login: null,
|
||||
userInfo: null,
|
||||
targetOrder: null,
|
||||
permissionCheck: null,
|
||||
buttonDisplayConditions: null,
|
||||
apiTest: null,
|
||||
};
|
||||
|
||||
try {
|
||||
// 步骤1:用户登录
|
||||
console.log(colors.blue('\n【步骤1】用户登录验证'));
|
||||
console.log('----------------------------------------');
|
||||
|
||||
try {
|
||||
const token = await getToken(CONFIG.username, CONFIG.password);
|
||||
results.login = { success: true, token: token.substring(0, 50) + '...' };
|
||||
console.log(colors.green('✓ 登录成功'));
|
||||
console.log(`Token: ${token.substring(0, 50)}...`);
|
||||
} catch (error) {
|
||||
results.login = { success: false, error: error.message };
|
||||
console.log(colors.red('✗ 登录失败: ' + error.message));
|
||||
return results;
|
||||
}
|
||||
|
||||
// 步骤2:获取用户信息和权限
|
||||
console.log(colors.blue('\n【步骤2】用户权限检查'));
|
||||
console.log('----------------------------------------');
|
||||
|
||||
const userInfoResult = await testAPI('/system/user/getInfo', null, {
|
||||
method: 'GET',
|
||||
token: await getToken(CONFIG.username, CONFIG.password),
|
||||
});
|
||||
|
||||
if (userInfoResult.success) {
|
||||
const userInfo = userInfoResult.data.data || userInfoResult.data;
|
||||
results.userInfo = {
|
||||
userId: userInfo.user?.userId,
|
||||
userName: userInfo.user?.userName,
|
||||
nickName: userInfo.user?.nickName,
|
||||
roles: userInfo.roles?.map((r) => r.roleKey) || [],
|
||||
permissions: userInfo.permissions || [],
|
||||
};
|
||||
|
||||
console.log(colors.green('✓ 用户信息获取成功'));
|
||||
console.log(`用户ID: ${results.userInfo.userId}`);
|
||||
console.log(`用户名: ${results.userInfo.userName}`);
|
||||
console.log(`昵称: ${results.userInfo.nickName}`);
|
||||
|
||||
// 检查关键权限
|
||||
const hasMaintenanceRole = results.userInfo.roles.includes('maintenance_worker');
|
||||
const hasUpdateProgressPerm = results.userInfo.permissions.includes(
|
||||
'pm:repairOrder:updateProgress',
|
||||
);
|
||||
|
||||
console.log(
|
||||
`维修人员角色: ${
|
||||
hasMaintenanceRole ? colors.green('✓ 已配置') : colors.red('✗ 缺少')
|
||||
}`,
|
||||
);
|
||||
console.log(
|
||||
`更新进度权限: ${
|
||||
hasUpdateProgressPerm ? colors.green('✓ 已配置') : colors.red('✗ 缺少')
|
||||
}`,
|
||||
);
|
||||
|
||||
results.permissionCheck = {
|
||||
hasMaintenanceRole,
|
||||
hasUpdateProgressPerm,
|
||||
};
|
||||
} else {
|
||||
console.log(colors.red('✗ 用户信息获取失败: ' + JSON.stringify(userInfoResult.error)));
|
||||
}
|
||||
|
||||
// 步骤3:查找目标工单
|
||||
console.log(colors.blue('\n【步骤3】查找目标工单RO20250126008'));
|
||||
console.log('----------------------------------------');
|
||||
|
||||
const orderListResult = await testAPI('/pm/repairOrder/list', null, {
|
||||
method: 'GET',
|
||||
token: await getToken(CONFIG.username, CONFIG.password),
|
||||
});
|
||||
|
||||
if (orderListResult.success) {
|
||||
const orderData = orderListResult.data;
|
||||
const orders = orderData.rows || orderData.data || [];
|
||||
|
||||
console.log(colors.green('✓ 工单列表获取成功'));
|
||||
console.log(`可见工单总数: ${orderData.total || orders.length}`);
|
||||
|
||||
// 查找目标工单
|
||||
const targetOrder = orders.find((order) => order.orderNo === CONFIG.orderNo);
|
||||
|
||||
if (targetOrder) {
|
||||
results.targetOrder = {
|
||||
id: targetOrder.id,
|
||||
orderNo: targetOrder.orderNo,
|
||||
title: targetOrder.title,
|
||||
orderStatus: targetOrder.orderStatus,
|
||||
assigneeId: targetOrder.assigneeId,
|
||||
assigneeName: targetOrder.assigneeName,
|
||||
repairLocation: targetOrder.repairLocation,
|
||||
createTime: targetOrder.createTime,
|
||||
};
|
||||
|
||||
console.log(colors.green(`✓ 找到目标工单: ${CONFIG.orderNo}`));
|
||||
console.log(`工单标题: ${targetOrder.title}`);
|
||||
console.log(`工单状态: ${getStatusText(targetOrder.orderStatus)}`);
|
||||
console.log(
|
||||
`维修负责人: ${targetOrder.assigneeName || '未分派'} (ID: ${
|
||||
targetOrder.assigneeId || 'null'
|
||||
})`,
|
||||
);
|
||||
console.log(`报修地点: ${targetOrder.repairLocation}`);
|
||||
|
||||
// 检查按钮显示条件
|
||||
const isStatus2 = targetOrder.orderStatus === '2' || targetOrder.orderStatus === 2;
|
||||
const isAssignedToZwc007 = targetOrder.assigneeId === results.userInfo?.userId;
|
||||
|
||||
console.log('\n按钮显示条件检查:');
|
||||
console.log(
|
||||
`工单状态为已分派(2): ${
|
||||
isStatus2 ? colors.green('✓ 满足') : colors.red('✗ 不满足')
|
||||
} (当前: ${targetOrder.orderStatus})`,
|
||||
);
|
||||
console.log(
|
||||
`zwc007是负责人: ${
|
||||
isAssignedToZwc007 ? colors.green('✓ 满足') : colors.red('✗ 不满足')
|
||||
} (负责人ID: ${targetOrder.assigneeId})`,
|
||||
);
|
||||
console.log(
|
||||
`维修人员角色: ${
|
||||
results.permissionCheck?.hasMaintenanceRole
|
||||
? colors.green('✓ 满足')
|
||||
: colors.red('✗ 不满足')
|
||||
}`,
|
||||
);
|
||||
console.log(
|
||||
`更新进度权限: ${
|
||||
results.permissionCheck?.hasUpdateProgressPerm
|
||||
? colors.green('✓ 满足')
|
||||
: colors.red('✗ 不满足')
|
||||
}`,
|
||||
);
|
||||
|
||||
const allConditionsMet =
|
||||
isStatus2 &&
|
||||
isAssignedToZwc007 &&
|
||||
results.permissionCheck?.hasMaintenanceRole &&
|
||||
results.permissionCheck?.hasUpdateProgressPerm;
|
||||
|
||||
console.log(
|
||||
`\n更新进度按钮应该显示: ${
|
||||
allConditionsMet ? colors.green('是 🎉') : colors.red('否 ❌')
|
||||
}`,
|
||||
);
|
||||
|
||||
results.buttonDisplayConditions = {
|
||||
isStatus2,
|
||||
isAssignedToZwc007,
|
||||
allConditionsMet,
|
||||
};
|
||||
} else {
|
||||
console.log(colors.red(`✗ 未找到工单${CONFIG.orderNo}`));
|
||||
console.log(colors.yellow('可能原因:'));
|
||||
console.log(colors.yellow('1. 工单不存在'));
|
||||
console.log(colors.yellow('2. zwc007没有查看该工单的权限'));
|
||||
console.log(colors.yellow('3. 数据权限过滤导致工单不可见'));
|
||||
|
||||
results.targetOrder = { found: false };
|
||||
}
|
||||
} else {
|
||||
console.log(colors.red('✗ 工单列表获取失败: ' + JSON.stringify(orderListResult.error)));
|
||||
}
|
||||
|
||||
// 步骤4:测试更新进度API(如果工单存在且条件满足)
|
||||
console.log(colors.blue('\n【步骤4】更新进度API测试'));
|
||||
console.log('----------------------------------------');
|
||||
|
||||
if (results.targetOrder?.found !== false && results.targetOrder?.id) {
|
||||
console.log(`测试工单: ${results.targetOrder.orderNo} (ID: ${results.targetOrder.id})`);
|
||||
|
||||
// 构造测试用的进度数据
|
||||
const progressData = {
|
||||
orderId: results.targetOrder.id,
|
||||
progressType: '5', // 维修进度
|
||||
progressStatus: '测试进度状态',
|
||||
progressDetail: `工单${
|
||||
CONFIG.orderNo
|
||||
}进度更新测试 - ${new Date().toLocaleString()}`,
|
||||
progressTime: new Date().toISOString().slice(0, 19).replace('T', ' '),
|
||||
locationInfo: results.targetOrder.repairLocation || '测试位置',
|
||||
estimatedDuration: 60,
|
||||
nextStep: '继续测试验证',
|
||||
remark: 'API权限验证测试',
|
||||
};
|
||||
|
||||
const apiResult = await testAPI('/pm/repairOrder/updateProgress', progressData, {
|
||||
method: 'POST',
|
||||
token: await getToken(CONFIG.username, CONFIG.password),
|
||||
});
|
||||
|
||||
if (apiResult.success) {
|
||||
console.log(colors.green('✓ 更新进度API调用成功'));
|
||||
console.log('响应数据:', JSON.stringify(apiResult.data, null, 2));
|
||||
|
||||
results.apiTest = {
|
||||
success: true,
|
||||
response: apiResult.data,
|
||||
};
|
||||
} else {
|
||||
console.log(colors.red('✗ 更新进度API调用失败'));
|
||||
console.log('错误信息:', JSON.stringify(apiResult.error, null, 2));
|
||||
|
||||
results.apiTest = {
|
||||
success: false,
|
||||
error: apiResult.error,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
console.log(colors.yellow('⚠ 跳过API测试:工单不存在或条件不满足'));
|
||||
results.apiTest = { skipped: true, reason: '工单不存在或条件不满足' };
|
||||
}
|
||||
|
||||
// 步骤5:生成测试总结
|
||||
console.log(colors.blue('\n【步骤5】测试结果总结'));
|
||||
console.log('----------------------------------------');
|
||||
|
||||
console.log(colors.cyan('测试结果汇总:'));
|
||||
console.log(
|
||||
`1. 用户登录: ${
|
||||
results.login?.success ? colors.green('✓ 成功') : colors.red('✗ 失败')
|
||||
}`,
|
||||
);
|
||||
console.log(
|
||||
`2. 权限配置: ${
|
||||
results.permissionCheck?.hasMaintenanceRole &&
|
||||
results.permissionCheck?.hasUpdateProgressPerm
|
||||
? colors.green('✓ 完整')
|
||||
: colors.red('✗ 缺失')
|
||||
}`,
|
||||
);
|
||||
console.log(
|
||||
`3. 目标工单: ${
|
||||
results.targetOrder?.found !== false
|
||||
? colors.green('✓ 找到')
|
||||
: colors.red('✗ 未找到')
|
||||
}`,
|
||||
);
|
||||
console.log(
|
||||
`4. 按钮显示: ${
|
||||
results.buttonDisplayConditions?.allConditionsMet
|
||||
? colors.green('✓ 应显示')
|
||||
: colors.red('✗ 不显示')
|
||||
}`,
|
||||
);
|
||||
console.log(
|
||||
`5. API权限: ${
|
||||
results.apiTest?.success
|
||||
? colors.green('✓ 正常')
|
||||
: results.apiTest?.skipped
|
||||
? colors.yellow('⚠ 跳过')
|
||||
: colors.red('✗ 失败')
|
||||
}`,
|
||||
);
|
||||
|
||||
// 提供修复建议
|
||||
if (!results.buttonDisplayConditions?.allConditionsMet) {
|
||||
console.log(colors.yellow('\n修复建议:'));
|
||||
|
||||
if (!results.permissionCheck?.hasMaintenanceRole) {
|
||||
console.log(colors.yellow('- 需要为zwc007分配maintenance_worker角色'));
|
||||
}
|
||||
if (!results.permissionCheck?.hasUpdateProgressPerm) {
|
||||
console.log(
|
||||
colors.yellow('- 需要为维修人员角色分配pm:repairOrder:updateProgress权限'),
|
||||
);
|
||||
}
|
||||
if (!results.buttonDisplayConditions?.isStatus2) {
|
||||
console.log(colors.yellow(`- 需要将工单${CONFIG.orderNo}状态设置为已分派(2)`));
|
||||
}
|
||||
if (!results.buttonDisplayConditions?.isAssignedToZwc007) {
|
||||
console.log(colors.yellow(`- 需要将工单${CONFIG.orderNo}分派给zwc007`));
|
||||
}
|
||||
|
||||
console.log(
|
||||
colors.yellow('- 执行 test/RO20250126008-update-progress-fix.sql 脚本进行修复'),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(colors.red('测试过程中发生错误: ' + error.message));
|
||||
results.error = error.message;
|
||||
}
|
||||
|
||||
console.log('\n' + '='.repeat(80));
|
||||
console.log(colors.cyan.bold(' 测试完成'));
|
||||
console.log('='.repeat(80));
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态文本
|
||||
*/
|
||||
function getStatusText(status) {
|
||||
const statusMap = {
|
||||
1: '待分派',
|
||||
2: '已分派',
|
||||
3: '进行中',
|
||||
4: '已完成',
|
||||
5: '已评价',
|
||||
6: '已归档',
|
||||
9: '已取消',
|
||||
};
|
||||
return statusMap[status] || `状态${status}`;
|
||||
}
|
||||
|
||||
// 执行测试
|
||||
if (require.main === module) {
|
||||
testRO20250126008UpdateProgressButton()
|
||||
.then((results) => {
|
||||
console.log('\n测试结果已保存');
|
||||
|
||||
// 输出最终结论
|
||||
const success =
|
||||
results.buttonDisplayConditions?.allConditionsMet && results.apiTest?.success;
|
||||
|
||||
if (success) {
|
||||
console.log(
|
||||
colors.green.bold(
|
||||
'\n🎉 测试通过!工单RO20250126008的更新进度按钮应该可以正常显示和使用。',
|
||||
),
|
||||
);
|
||||
} else {
|
||||
console.log(colors.red.bold('\n❌ 测试未通过!需要执行修复脚本解决问题。'));
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(colors.red('测试执行失败:'), error);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
testRO20250126008UpdateProgressButton,
|
||||
};
|
||||
@@ -1,276 +0,0 @@
|
||||
-- ==========================================
|
||||
-- 工单RO20250126008更新进度按钮修复脚本
|
||||
-- 功能:针对具体工单RO20250126008和维修人员zwc007的权限问题修复
|
||||
-- 时间:2025-06-30 18:45:00
|
||||
-- 说明:精准定位和修复特定工单的显示问题
|
||||
-- ==========================================
|
||||
|
||||
-- 第一部分:工单RO20250126008诊断查询
|
||||
-- ==========================================
|
||||
|
||||
-- 1. 检查工单RO20250126008的基本信息和当前状态
|
||||
SELECT '=== 工单RO20250126008基本信息 ===' as message;
|
||||
SELECT
|
||||
ro.id,
|
||||
ro.order_no,
|
||||
ro.title,
|
||||
ro.order_status,
|
||||
CASE ro.order_status
|
||||
WHEN '1' THEN '待分派'
|
||||
WHEN '2' THEN '已分派 ✓ (应显示更新进度按钮)'
|
||||
WHEN '3' THEN '进行中'
|
||||
WHEN '4' THEN '已完成'
|
||||
WHEN '5' THEN '已评价'
|
||||
WHEN '6' THEN '已归档'
|
||||
WHEN '9' THEN '已取消'
|
||||
ELSE CONCAT('未知状态: ', ro.order_status)
|
||||
END as status_description,
|
||||
ro.assignee_id,
|
||||
u_assignee.user_name as assignee_name,
|
||||
u_assignee.nick_name as assignee_nick_name,
|
||||
ro.assign_time,
|
||||
ro.create_time,
|
||||
ro.repair_location,
|
||||
ro.priority_level
|
||||
FROM pm_repair_order ro
|
||||
LEFT JOIN sys_user u_assignee ON ro.assignee_id = u_assignee.user_id
|
||||
WHERE ro.order_no = 'RO20250126008';
|
||||
|
||||
-- 2. 检查zwc007用户信息
|
||||
SELECT '=== 用户zwc007信息检查 ===' as message;
|
||||
SELECT
|
||||
u.user_id,
|
||||
u.user_name,
|
||||
u.nick_name,
|
||||
u.status as user_status,
|
||||
CASE u.status WHEN '0' THEN '正常' WHEN '1' THEN '停用' ELSE '未知' END as status_text
|
||||
FROM sys_user u
|
||||
WHERE u.user_name = 'zwc007';
|
||||
|
||||
-- 3. 检查当前分派情况匹配性
|
||||
SELECT '=== 工单分派匹配性检查 ===' as message;
|
||||
SELECT
|
||||
ro.order_no,
|
||||
ro.assignee_id as current_assignee_id,
|
||||
u_current.user_name as current_assignee_name,
|
||||
u_zwc.user_id as zwc007_user_id,
|
||||
u_zwc.user_name as zwc007_name,
|
||||
CASE
|
||||
WHEN ro.assignee_id = u_zwc.user_id THEN '✓ zwc007是负责人'
|
||||
WHEN ro.assignee_id IS NULL THEN '✗ 工单未分派'
|
||||
ELSE CONCAT('✗ 工单分派给其他人: ', u_current.user_name)
|
||||
END as assignment_status,
|
||||
CASE
|
||||
WHEN ro.order_status = '2' AND ro.assignee_id = u_zwc.user_id THEN '✅ 应显示更新进度按钮'
|
||||
WHEN ro.order_status != '2' THEN CONCAT('❌ 工单状态不是已分派: ', ro.order_status)
|
||||
WHEN ro.assignee_id != u_zwc.user_id THEN '❌ zwc007不是负责人'
|
||||
ELSE '❌ 不满足显示条件'
|
||||
END as button_display_status
|
||||
FROM pm_repair_order ro
|
||||
LEFT JOIN sys_user u_current ON ro.assignee_id = u_current.user_id
|
||||
CROSS JOIN (SELECT user_id, user_name FROM sys_user WHERE user_name = 'zwc007') u_zwc
|
||||
WHERE ro.order_no = 'RO20250126008';
|
||||
|
||||
-- 4. 检查zwc007的角色和权限
|
||||
SELECT '=== zwc007角色和权限检查 ===' as message;
|
||||
SELECT
|
||||
u.user_name,
|
||||
r.role_name,
|
||||
r.role_key,
|
||||
CASE WHEN r.role_key = 'maintenance_worker' THEN '✓ 维修人员角色' ELSE '其他角色' END as role_type
|
||||
FROM sys_user u
|
||||
JOIN sys_user_role ur ON u.user_id = ur.user_id
|
||||
JOIN sys_role r ON ur.role_id = r.role_id
|
||||
WHERE u.user_name = 'zwc007'
|
||||
ORDER BY r.role_key;
|
||||
|
||||
-- 检查关键权限
|
||||
SELECT '=== zwc007更新进度权限检查 ===' as message;
|
||||
SELECT
|
||||
m.menu_name,
|
||||
m.perms,
|
||||
m.menu_type,
|
||||
CASE WHEN m.perms = 'pm:repairOrder:updateProgress' THEN '*** 关键权限 ***' ELSE '' END as importance
|
||||
FROM sys_user u
|
||||
JOIN sys_user_role ur ON u.user_id = ur.user_id
|
||||
JOIN sys_role r ON ur.role_id = r.role_id
|
||||
JOIN sys_role_menu rm ON r.role_id = rm.role_id
|
||||
JOIN sys_menu m ON rm.menu_id = m.menu_id
|
||||
WHERE u.user_name = 'zwc007'
|
||||
AND m.perms = 'pm:repairOrder:updateProgress';
|
||||
|
||||
-- 第二部分:问题修复操作
|
||||
-- ==========================================
|
||||
|
||||
-- 修复1:确保zwc007有维修人员角色(如果没有)
|
||||
INSERT IGNORE INTO sys_user_role (user_id, role_id)
|
||||
SELECT u.user_id, r.role_id
|
||||
FROM sys_user u, sys_role r
|
||||
WHERE u.user_name = 'zwc007'
|
||||
AND r.role_key = 'maintenance_worker'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM sys_user_role ur2
|
||||
WHERE ur2.user_id = u.user_id AND ur2.role_id = r.role_id
|
||||
);
|
||||
|
||||
-- 修复2:确保updateProgress权限存在
|
||||
INSERT INTO sys_menu (
|
||||
menu_id, menu_name, parent_id, order_num, path, component,
|
||||
query_param, is_frame, is_cache, menu_type, visible, status,
|
||||
perms, icon, create_by, create_time, update_by, update_time, remark
|
||||
)
|
||||
SELECT
|
||||
2117, '更新进度', 2110, 6, '', '',
|
||||
'', 1, 0, 'F', '0', '0',
|
||||
'pm:repairOrder:updateProgress', '#', 1, now(), 1, now(), '更新工单进度权限'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'pm:repairOrder:updateProgress');
|
||||
|
||||
-- 修复3:为维修人员角色分配权限
|
||||
INSERT IGNORE INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT r.role_id, m.menu_id
|
||||
FROM sys_role r, sys_menu m
|
||||
WHERE r.role_key = 'maintenance_worker'
|
||||
AND m.perms = 'pm:repairOrder:updateProgress';
|
||||
|
||||
-- 修复4:将工单RO20250126008分派给zwc007(关键修复)
|
||||
UPDATE pm_repair_order
|
||||
SET
|
||||
assignee_id = (SELECT user_id FROM sys_user WHERE user_name = 'zwc007'),
|
||||
assign_time = CASE WHEN assign_time IS NULL THEN now() ELSE assign_time END,
|
||||
order_status = '2', -- 设置为已分派状态
|
||||
update_time = now(),
|
||||
update_by = 1
|
||||
WHERE order_no = 'RO20250126008'
|
||||
AND EXISTS (SELECT 1 FROM sys_user WHERE user_name = 'zwc007');
|
||||
|
||||
-- 修复5:记录分派操作到进度表
|
||||
INSERT INTO pm_repair_progress (
|
||||
order_id, progress_type, progress_status, progress_detail,
|
||||
progress_time, location_info, remark,
|
||||
tenant_id, create_dept, create_by, create_time, update_by, update_time, deleted
|
||||
)
|
||||
SELECT
|
||||
ro.id,
|
||||
'2', -- 分派类型
|
||||
'已分派给维修人员',
|
||||
CONCAT('工单已分派给维修人员zwc007,负责处理报修问题'),
|
||||
now(),
|
||||
ro.repair_location,
|
||||
'系统自动分派 - 修复更新进度按钮显示问题',
|
||||
'000000', 103, 1, now(), 1, now(), 0
|
||||
FROM pm_repair_order ro
|
||||
WHERE ro.order_no = 'RO20250126008'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM pm_repair_progress pp
|
||||
WHERE pp.order_id = ro.id
|
||||
AND pp.progress_type = '2'
|
||||
AND pp.progress_detail LIKE '%zwc007%'
|
||||
);
|
||||
|
||||
-- 第三部分:修复后验证
|
||||
-- ==========================================
|
||||
|
||||
-- 验证1:确认工单状态和分派
|
||||
SELECT '=== 修复后:工单状态验证 ===' as message;
|
||||
SELECT
|
||||
ro.order_no,
|
||||
ro.title,
|
||||
ro.order_status,
|
||||
CASE ro.order_status
|
||||
WHEN '2' THEN '✅ 已分派状态 - 应显示更新进度按钮'
|
||||
ELSE CONCAT('❌ 状态异常: ', ro.order_status)
|
||||
END as status_check,
|
||||
ro.assignee_id,
|
||||
u.user_name as assignee_name,
|
||||
u.nick_name as assignee_nick_name,
|
||||
CASE
|
||||
WHEN u.user_name = 'zwc007' THEN '✅ zwc007是负责人'
|
||||
ELSE '❌ 负责人不是zwc007'
|
||||
END as assignee_check,
|
||||
ro.assign_time
|
||||
FROM pm_repair_order ro
|
||||
LEFT JOIN sys_user u ON ro.assignee_id = u.user_id
|
||||
WHERE ro.order_no = 'RO20250126008';
|
||||
|
||||
-- 验证2:确认zwc007权限配置
|
||||
SELECT '=== 修复后:zwc007权限验证 ===' as message;
|
||||
SELECT
|
||||
'维修人员角色' as permission_type,
|
||||
CASE
|
||||
WHEN COUNT(*) > 0 THEN '✅ 已配置'
|
||||
ELSE '❌ 未配置'
|
||||
END as status
|
||||
FROM sys_user u
|
||||
JOIN sys_user_role ur ON u.user_id = ur.user_id
|
||||
JOIN sys_role r ON ur.role_id = r.role_id
|
||||
WHERE u.user_name = 'zwc007' AND r.role_key = 'maintenance_worker'
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
'更新进度权限' as permission_type,
|
||||
CASE
|
||||
WHEN COUNT(*) > 0 THEN '✅ 已配置'
|
||||
ELSE '❌ 未配置'
|
||||
END as status
|
||||
FROM sys_user u
|
||||
JOIN sys_user_role ur ON u.user_id = ur.user_id
|
||||
JOIN sys_role r ON ur.role_id = r.role_id
|
||||
JOIN sys_role_menu rm ON r.role_id = rm.role_id
|
||||
JOIN sys_menu m ON rm.menu_id = m.menu_id
|
||||
WHERE u.user_name = 'zwc007'
|
||||
AND m.perms = 'pm:repairOrder:updateProgress';
|
||||
|
||||
-- 验证3:确认按钮显示条件
|
||||
SELECT '=== 修复后:按钮显示条件验证 ===' as message;
|
||||
SELECT
|
||||
ro.order_no,
|
||||
'工单状态 = 2 (已分派)' as condition_1,
|
||||
CASE WHEN ro.order_status = '2' THEN '✅ 满足' ELSE '❌ 不满足' END as check_1,
|
||||
'用户是维修负责人' as condition_2,
|
||||
CASE WHEN ro.assignee_id = u.user_id THEN '✅ 满足' ELSE '❌ 不满足' END as check_2,
|
||||
'用户有maintenance_worker角色' as condition_3,
|
||||
CASE WHEN role_count.cnt > 0 THEN '✅ 满足' ELSE '❌ 不满足' END as check_3,
|
||||
'用户有updateProgress权限' as condition_4,
|
||||
CASE WHEN perm_count.cnt > 0 THEN '✅ 满足' ELSE '❌ 不满足' END as check_4,
|
||||
CASE
|
||||
WHEN ro.order_status = '2'
|
||||
AND ro.assignee_id = u.user_id
|
||||
AND role_count.cnt > 0
|
||||
AND perm_count.cnt > 0
|
||||
THEN '🎉 所有条件满足 - 应显示更新进度按钮'
|
||||
ELSE '❌ 条件不满足 - 按钮不会显示'
|
||||
END as final_result
|
||||
FROM pm_repair_order ro
|
||||
JOIN sys_user u ON u.user_name = 'zwc007'
|
||||
LEFT JOIN (
|
||||
SELECT ur.user_id, COUNT(*) as cnt
|
||||
FROM sys_user_role ur
|
||||
JOIN sys_role r ON ur.role_id = r.role_id
|
||||
WHERE r.role_key = 'maintenance_worker'
|
||||
GROUP BY ur.user_id
|
||||
) role_count ON role_count.user_id = u.user_id
|
||||
LEFT JOIN (
|
||||
SELECT ur.user_id, COUNT(*) as cnt
|
||||
FROM sys_user_role ur
|
||||
JOIN sys_role_menu rm ON ur.role_id = rm.role_id
|
||||
JOIN sys_menu m ON rm.menu_id = m.menu_id
|
||||
WHERE m.perms = 'pm:repairOrder:updateProgress'
|
||||
GROUP BY ur.user_id
|
||||
) perm_count ON perm_count.user_id = u.user_id
|
||||
WHERE ro.order_no = 'RO20250126008';
|
||||
|
||||
-- 第四部分:操作指导和确认
|
||||
-- ==========================================
|
||||
SELECT '=== 修复完成提示 ===' as message,
|
||||
'1. 工单RO20250126008已分派给zwc007' as step1,
|
||||
'2. 工单状态已设置为已分派(状态码2)' as step2,
|
||||
'3. zwc007已配置维修人员角色和权限' as step3,
|
||||
'4. 请重启前后端服务清除权限缓存' as step4,
|
||||
'5. 使用zwc007账号登录查看该工单' as step5,
|
||||
'6. 确认更新进度按钮是否显示' as step6;
|
||||
|
||||
-- 显示需要重启服务的提醒
|
||||
SELECT '⚠️ 重要提醒' as alert_title,
|
||||
'权限配置修改后必须重启前后端服务' as alert_message,
|
||||
'用户需要重新登录以刷新权限缓存' as additional_note;
|
||||
@@ -1,251 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
学生信息管理SQL修复最终验证脚本
|
||||
测试修复后的XML映射文件是否能正确生成SQL
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
# 配置
|
||||
BASE_URL = "http://localhost:8080"
|
||||
LOGIN_URL = f"{BASE_URL}/auth/login"
|
||||
STUDENT_LIST_URL = f"{BASE_URL}/xg-xx/studentInfo/list"
|
||||
|
||||
# 测试数据
|
||||
LOGIN_DATA = {
|
||||
"clientId": "e5cd7e4891bf95d1d19206ce24a7b32e",
|
||||
"username": "admin",
|
||||
"password": "admin123",
|
||||
"grant_type": "password",
|
||||
"tenantId": "000000"
|
||||
}
|
||||
|
||||
def test_server_connection():
|
||||
"""测试服务器连接"""
|
||||
print("🔍 测试服务器连接...")
|
||||
try:
|
||||
response = requests.get(f"{BASE_URL}/health", timeout=5)
|
||||
if response.status_code == 200:
|
||||
print("✅ 服务器连接正常")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ 服务器响应异常: {response.status_code}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ 服务器连接失败: {e}")
|
||||
return False
|
||||
|
||||
def get_auth_token():
|
||||
"""获取认证Token"""
|
||||
print("🔐 获取认证Token...")
|
||||
try:
|
||||
response = requests.post(LOGIN_URL, json=LOGIN_DATA, timeout=10)
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get('code') == 200:
|
||||
token = result['data']['access_token']
|
||||
print("✅ Token获取成功")
|
||||
return token
|
||||
else:
|
||||
print(f"❌ 登录失败: {result.get('msg', '未知错误')}")
|
||||
return None
|
||||
else:
|
||||
print(f"❌ 登录请求失败: {response.status_code}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"❌ 登录异常: {e}")
|
||||
return None
|
||||
|
||||
def test_student_list_without_params(token):
|
||||
"""测试不带参数的学生列表查询"""
|
||||
print("📋 测试不带参数的学生列表查询...")
|
||||
|
||||
headers = {
|
||||
'Authorization': f'Bearer {token}',
|
||||
'Content-Type': 'application/json',
|
||||
'clientid': 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
||||
'referer': 'http://localhost',
|
||||
'tenantId': '000000'
|
||||
}
|
||||
|
||||
params = {
|
||||
'pageNum': 1,
|
||||
'pageSize': 10
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.get(STUDENT_LIST_URL, params=params, headers=headers, timeout=10)
|
||||
print(f"响应状态码: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get('code') == 200:
|
||||
print("✅ 基础查询成功")
|
||||
data = result.get('data', {})
|
||||
total = data.get('total', 0)
|
||||
rows = data.get('rows', [])
|
||||
print(f"📊 查询结果: 总数={total}, 当前页记录数={len(rows)}")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ 查询失败: {result.get('msg', '未知错误')}")
|
||||
return False
|
||||
else:
|
||||
print(f"❌ 请求失败: {response.status_code}")
|
||||
print(f"响应内容: {response.text}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ 查询异常: {e}")
|
||||
return False
|
||||
|
||||
def test_student_list_with_gender_filter(token):
|
||||
"""测试带性别过滤的学生列表查询(这是之前报错的场景)"""
|
||||
print("🔍 测试带性别过滤的学生列表查询...")
|
||||
|
||||
headers = {
|
||||
'Authorization': f'Bearer {token}',
|
||||
'Content-Type': 'application/json',
|
||||
'clientid': 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
||||
'referer': 'http://localhost',
|
||||
'tenantId': '000000'
|
||||
}
|
||||
|
||||
params = {
|
||||
'pageNum': 1,
|
||||
'pageSize': 10,
|
||||
'gender': '1' # 这是之前导致SQL重复WHERE的参数
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.get(STUDENT_LIST_URL, params=params, headers=headers, timeout=10)
|
||||
print(f"响应状态码: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get('code') == 200:
|
||||
print("✅ 性别过滤查询成功 - SQL重复WHERE问题已修复!")
|
||||
data = result.get('data', {})
|
||||
total = data.get('total', 0)
|
||||
rows = data.get('rows', [])
|
||||
print(f"📊 查询结果: 总数={total}, 当前页记录数={len(rows)}")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ 查询失败: {result.get('msg', '未知错误')}")
|
||||
return False
|
||||
else:
|
||||
print(f"❌ 请求失败: {response.status_code}")
|
||||
print(f"响应内容: {response.text}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ 查询异常: {e}")
|
||||
return False
|
||||
|
||||
def test_student_list_with_multiple_filters(token):
|
||||
"""测试多条件过滤查询"""
|
||||
print("🔍 测试多条件过滤查询...")
|
||||
|
||||
headers = {
|
||||
'Authorization': f'Bearer {token}',
|
||||
'Content-Type': 'application/json',
|
||||
'clientid': 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
||||
'referer': 'http://localhost',
|
||||
'tenantId': '000000'
|
||||
}
|
||||
|
||||
params = {
|
||||
'pageNum': 1,
|
||||
'pageSize': 10,
|
||||
'gender': '1',
|
||||
'studentStatus': '1',
|
||||
'name': '张'
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.get(STUDENT_LIST_URL, params=params, headers=headers, timeout=10)
|
||||
print(f"响应状态码: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get('code') == 200:
|
||||
print("✅ 多条件过滤查询成功")
|
||||
data = result.get('data', {})
|
||||
total = data.get('total', 0)
|
||||
rows = data.get('rows', [])
|
||||
print(f"📊 查询结果: 总数={total}, 当前页记录数={len(rows)}")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ 查询失败: {result.get('msg', '未知错误')}")
|
||||
return False
|
||||
else:
|
||||
print(f"❌ 请求失败: {response.status_code}")
|
||||
print(f"响应内容: {response.text}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ 查询异常: {e}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("="*80)
|
||||
print("🎯 学生信息管理SQL修复最终验证")
|
||||
print("="*80)
|
||||
print(f"⏰ 测试时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print()
|
||||
|
||||
# 1. 测试服务器连接
|
||||
if not test_server_connection():
|
||||
print("❌ 服务器未启动,请先启动后端服务")
|
||||
return
|
||||
|
||||
print()
|
||||
|
||||
# 2. 获取认证Token
|
||||
token = get_auth_token()
|
||||
if not token:
|
||||
print("❌ 无法获取认证Token,测试中止")
|
||||
return
|
||||
|
||||
print()
|
||||
|
||||
# 3. 测试基础查询
|
||||
if not test_student_list_without_params(token):
|
||||
print("❌ 基础查询失败")
|
||||
return
|
||||
|
||||
print()
|
||||
|
||||
# 4. 测试性别过滤查询(关键测试)
|
||||
if not test_student_list_with_gender_filter(token):
|
||||
print("❌ 性别过滤查询失败 - SQL重复WHERE问题可能仍存在")
|
||||
return
|
||||
|
||||
print()
|
||||
|
||||
# 5. 测试多条件过滤查询
|
||||
if not test_student_list_with_multiple_filters(token):
|
||||
print("❌ 多条件过滤查询失败")
|
||||
return
|
||||
|
||||
print()
|
||||
print("="*80)
|
||||
print("🎉 所有测试通过!SQL重复WHERE问题已完全修复!")
|
||||
print("="*80)
|
||||
print("📋 修复总结:")
|
||||
print(" 1. ✅ 修复了XML映射文件中的SQL语句结构")
|
||||
print(" 2. ✅ 使用条件判断避免重复WHERE子句")
|
||||
print(" 3. ✅ 验证了单条件和多条件查询都正常工作")
|
||||
print(" 4. ✅ 确认MyBatis-Plus的customSqlSegment正确集成")
|
||||
print()
|
||||
print("🔧 技术细节:")
|
||||
print(" - 修改前: WHERE deleted = 0 AND tenant_id = '000000' ${ew.customSqlSegment}")
|
||||
print(" - 修改后: WHERE deleted = 0 AND tenant_id = '000000'")
|
||||
print(" <if test=\"ew != null and ew.sqlSegment != null\">")
|
||||
print(" AND ${ew.sqlSegment}")
|
||||
print(" </if>")
|
||||
print()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,195 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
SQL修复验证测试
|
||||
直接测试学生信息管理SQL语句是否修复了重复WHERE子句问题
|
||||
"""
|
||||
|
||||
import time
|
||||
import requests
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
# 配置
|
||||
BASE_URL = "http://localhost:8080"
|
||||
HEADERS = {
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
|
||||
}
|
||||
|
||||
def test_service_health():
|
||||
"""测试服务健康状态"""
|
||||
print("=== 测试服务健康状态 ===")
|
||||
|
||||
try:
|
||||
response = requests.get(f"{BASE_URL}/", headers=HEADERS, timeout=5)
|
||||
print(f"服务状态码: {response.status_code}")
|
||||
print(f"服务响应: {response.text[:100]}...")
|
||||
|
||||
if response.status_code == 200:
|
||||
print("✅ 服务正常运行")
|
||||
return True
|
||||
else:
|
||||
print(f"⚠️ 服务状态异常: {response.status_code}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ 服务连接失败: {e}")
|
||||
return False
|
||||
|
||||
def test_sql_error_pattern():
|
||||
"""测试SQL错误模式"""
|
||||
print("\n=== 测试SQL错误模式 ===")
|
||||
|
||||
# 测试会触发SQL错误的请求
|
||||
search_params = {
|
||||
'pageNum': 1,
|
||||
'pageSize': 10,
|
||||
'gender': '男', # 这个参数会触发QueryWrapper生成WHERE子句
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.get(f"{BASE_URL}/xg/studentInfo/list",
|
||||
params=search_params,
|
||||
headers=HEADERS,
|
||||
timeout=10)
|
||||
print(f"请求状态码: {response.status_code}")
|
||||
print(f"响应内容: {response.text[:400]}...")
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
if data.get('code') == 401:
|
||||
print("✅ 正常返回401认证错误(预期行为)")
|
||||
return True
|
||||
elif data.get('code') == 500:
|
||||
error_msg = data.get('msg', '')
|
||||
if "WHERE deleted = 0 AND tenant_id = '000000' WHERE" in error_msg:
|
||||
print("❌ 仍然存在重复WHERE子句错误")
|
||||
print("🔧 XML映射文件修复可能没有生效")
|
||||
return False
|
||||
else:
|
||||
print("❌ 其他500错误")
|
||||
print(f"错误信息: {error_msg}")
|
||||
return False
|
||||
else:
|
||||
print(f"⚠️ 意外的响应码: {data.get('code')}")
|
||||
return False
|
||||
elif response.status_code == 500:
|
||||
# 直接500错误
|
||||
response_text = response.text
|
||||
if "WHERE deleted = 0 AND tenant_id = '000000' WHERE" in response_text:
|
||||
print("❌ 仍然存在重复WHERE子句错误")
|
||||
print("🔧 XML映射文件修复可能没有生效")
|
||||
return False
|
||||
else:
|
||||
print("❌ 其他500错误")
|
||||
print(f"错误详情: {response_text[:200]}...")
|
||||
return False
|
||||
else:
|
||||
print(f"⚠️ 预期200状态码,实际: {response.status_code}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ 请求异常: {e}")
|
||||
return False
|
||||
|
||||
def test_xml_file_content():
|
||||
"""验证XML文件内容"""
|
||||
print("\n=== 验证XML文件内容 ===")
|
||||
|
||||
xml_file_path = "../backend/ruoyi-modules/ruoyi-xg-xx/src/main/resources/mapper/xg/XgStudentInfoMapper.xml"
|
||||
|
||||
try:
|
||||
with open(xml_file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# 检查是否包含正确的SQL结构
|
||||
if "WHERE deleted = 0 AND tenant_id = '000000'\n ${ew.customSqlSegment}" in content:
|
||||
print("✅ XML文件包含正确的SQL结构")
|
||||
return True
|
||||
elif "<where>" in content and "${ew.customSqlSegment}" in content:
|
||||
print("❌ XML文件仍然使用<where>标签包裹customSqlSegment")
|
||||
print("🔧 需要手动修复XML文件")
|
||||
return False
|
||||
else:
|
||||
print("⚠️ XML文件结构不明确")
|
||||
print("🔍 让我检查具体内容...")
|
||||
|
||||
# 查找selectVoPage方法
|
||||
if "selectVoPage" in content:
|
||||
lines = content.split('\n')
|
||||
for i, line in enumerate(lines):
|
||||
if "selectVoPage" in line:
|
||||
print(f"找到selectVoPage方法,周围内容:")
|
||||
start = max(0, i-2)
|
||||
end = min(len(lines), i+10)
|
||||
for j in range(start, end):
|
||||
print(f"{j+1:3d}: {lines[j]}")
|
||||
break
|
||||
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ 读取XML文件失败: {e}")
|
||||
return False
|
||||
|
||||
def show_correct_vs_incorrect():
|
||||
"""显示正确与错误的SQL结构对比"""
|
||||
print("\n=== SQL结构对比 ===")
|
||||
|
||||
print("✅ 正确的XML结构:")
|
||||
print("""
|
||||
<select id="selectVoPage" resultMap="XgStudentInfoResult">
|
||||
SELECT ... FROM xg_student_info
|
||||
WHERE deleted = 0 AND tenant_id = '000000'
|
||||
${ew.customSqlSegment}
|
||||
</select>
|
||||
""")
|
||||
|
||||
print("❌ 错误的XML结构:")
|
||||
print("""
|
||||
<select id="selectVoPage" resultMap="XgStudentInfoResult">
|
||||
SELECT ... FROM xg_student_info
|
||||
<where>
|
||||
deleted = 0 AND tenant_id = '000000'
|
||||
${ew.customSqlSegment}
|
||||
</where>
|
||||
</select>
|
||||
""")
|
||||
|
||||
print("🔍 错误原因:")
|
||||
print("- MyBatis-Plus的${ew.customSqlSegment}可能已经包含WHERE关键字")
|
||||
print("- 使用<where>标签包裹会导致重复的WHERE子句")
|
||||
print("- 正确做法是直接在SQL中写WHERE条件,然后跟${ew.customSqlSegment}")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("🚀 SQL修复验证测试")
|
||||
print("=" * 50)
|
||||
|
||||
# 测试服务健康状态
|
||||
if not test_service_health():
|
||||
print("❌ 服务未正常运行,无法进行测试")
|
||||
return
|
||||
|
||||
# 验证XML文件内容
|
||||
xml_ok = test_xml_file_content()
|
||||
|
||||
# 测试SQL错误模式
|
||||
sql_ok = test_sql_error_pattern()
|
||||
|
||||
# 显示结构对比
|
||||
show_correct_vs_incorrect()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print("📊 测试结果总结:")
|
||||
print(f"XML文件结构: {'✅ 正确' if xml_ok else '❌ 错误'}")
|
||||
print(f"SQL执行结果: {'✅ 正常' if sql_ok else '❌ 异常'}")
|
||||
|
||||
if xml_ok and sql_ok:
|
||||
print("🎉 SQL修复验证通过!")
|
||||
elif xml_ok and not sql_ok:
|
||||
print("⚠️ XML文件已修复,但可能需要重启服务")
|
||||
else:
|
||||
print("❌ 需要检查XML文件修复")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,198 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
学生信息管理SQL语法错误修复验证脚本
|
||||
验证修复后的XML映射文件是否能正确处理查询请求
|
||||
|
||||
日期: 2025-01-15
|
||||
修复内容: 解决 WHERE...AND ORDER BY 语法错误
|
||||
第二次修复: 正确区分WHERE条件和ORDER BY子句的处理
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
# 配置信息
|
||||
BASE_URL = "http://localhost:8080"
|
||||
LOGIN_URL = f"{BASE_URL}/auth/login"
|
||||
STUDENT_LIST_URL = f"{BASE_URL}/xg-xx/studentInfo/list"
|
||||
|
||||
# 登录配置
|
||||
LOGIN_DATA = {
|
||||
"username": "admin",
|
||||
"password": "admin123",
|
||||
"clientId": "e5cd7e4891bf95d1d19206ce24a7b32e",
|
||||
"grantType": "password"
|
||||
}
|
||||
|
||||
# 请求头配置
|
||||
HEADERS = {
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
||||
"referer": "http://localhost",
|
||||
"clientid": "e5cd7e4891bf95d1d19206ce24a7b32e"
|
||||
}
|
||||
|
||||
def print_test_header(title):
|
||||
"""打印测试标题"""
|
||||
print(f"\n{'='*60}")
|
||||
print(f"🧪 {title}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
def print_result(success, message, details=None):
|
||||
"""打印测试结果"""
|
||||
status = "✅ 成功" if success else "❌ 失败"
|
||||
print(f"{status}: {message}")
|
||||
if details:
|
||||
print(f"详情: {details}")
|
||||
|
||||
def login():
|
||||
"""登录获取token"""
|
||||
print_test_header("步骤1: 用户登录")
|
||||
|
||||
try:
|
||||
response = requests.post(LOGIN_URL, json=LOGIN_DATA, headers=HEADERS, timeout=10)
|
||||
print(f"登录请求状态码: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
if data.get("code") == 200:
|
||||
token = data.get("data", {}).get("access_token")
|
||||
if token:
|
||||
print_result(True, "登录成功", f"Token: {token[:20]}...")
|
||||
return token
|
||||
else:
|
||||
print_result(False, "登录响应中没有token", data)
|
||||
else:
|
||||
print_result(False, "登录失败", data.get("msg", "未知错误"))
|
||||
else:
|
||||
print_result(False, f"登录请求失败", f"状态码: {response.status_code}")
|
||||
|
||||
except Exception as e:
|
||||
print_result(False, "登录异常", str(e))
|
||||
|
||||
return None
|
||||
|
||||
def test_student_list_query(token, test_name, params=None):
|
||||
"""测试学生列表查询"""
|
||||
print_test_header(f"步骤2: {test_name}")
|
||||
|
||||
# 设置认证头
|
||||
test_headers = HEADERS.copy()
|
||||
test_headers["Authorization"] = f"Bearer {token}"
|
||||
|
||||
try:
|
||||
# 发送查询请求
|
||||
response = requests.get(
|
||||
STUDENT_LIST_URL,
|
||||
params=params or {},
|
||||
headers=test_headers,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
print(f"查询请求状态码: {response.status_code}")
|
||||
print(f"查询参数: {params or '无'}")
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
if data.get("code") == 200:
|
||||
rows = data.get("rows", [])
|
||||
total = data.get("total", 0)
|
||||
print_result(True, f"查询成功", f"返回 {len(rows)} 条记录,总计 {total} 条")
|
||||
return True, data
|
||||
else:
|
||||
print_result(False, "查询失败", data.get("msg", "未知错误"))
|
||||
return False, data
|
||||
else:
|
||||
print_result(False, f"查询请求失败", f"状态码: {response.status_code}")
|
||||
try:
|
||||
error_data = response.json()
|
||||
print(f"错误详情: {error_data}")
|
||||
except:
|
||||
print(f"响应内容: {response.text[:200]}...")
|
||||
return False, None
|
||||
|
||||
except Exception as e:
|
||||
print_result(False, "查询异常", str(e))
|
||||
return False, None
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("🔍 学生信息管理SQL语法错误修复验证")
|
||||
print(f"时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print(f"目标: 验证 WHERE...AND ORDER BY 语法错误修复")
|
||||
print(f"修复版本: 第二次修复 - 正确区分WHERE条件和ORDER BY子句")
|
||||
|
||||
# 1. 登录获取token
|
||||
token = login()
|
||||
if not token:
|
||||
print("\n❌ 登录失败,无法继续测试")
|
||||
sys.exit(1)
|
||||
|
||||
# 2. 测试无查询条件的列表查询(最容易触发SQL语法错误)
|
||||
success1, data1 = test_student_list_query(
|
||||
token,
|
||||
"无查询条件的列表查询(最容易触发SQL语法错误)",
|
||||
{"pageNum": 1, "pageSize": 10}
|
||||
)
|
||||
|
||||
# 3. 测试有查询条件的搜索
|
||||
success2, data2 = test_student_list_query(
|
||||
token,
|
||||
"有查询条件的搜索",
|
||||
{"pageNum": 1, "pageSize": 10, "name": "测试"}
|
||||
)
|
||||
|
||||
# 4. 测试排序功能
|
||||
success3, data3 = test_student_list_query(
|
||||
token,
|
||||
"排序功能测试",
|
||||
{"pageNum": 1, "pageSize": 10, "orderByColumn": "create_time", "isAsc": "desc"}
|
||||
)
|
||||
|
||||
# 5. 测试复合查询
|
||||
success4, data4 = test_student_list_query(
|
||||
token,
|
||||
"复合查询测试",
|
||||
{
|
||||
"pageNum": 1,
|
||||
"pageSize": 10,
|
||||
"gender": "1",
|
||||
"orderByColumn": "student_no",
|
||||
"isAsc": "asc"
|
||||
}
|
||||
)
|
||||
|
||||
# 总结测试结果
|
||||
print_test_header("测试结果总结")
|
||||
|
||||
tests = [
|
||||
("无查询条件列表查询", success1),
|
||||
("有查询条件搜索", success2),
|
||||
("排序功能测试", success3),
|
||||
("复合查询测试", success4)
|
||||
]
|
||||
|
||||
passed = sum(1 for _, success in tests if success)
|
||||
total = len(tests)
|
||||
|
||||
print(f"📊 测试统计: {passed}/{total} 通过")
|
||||
|
||||
for test_name, success in tests:
|
||||
status = "✅" if success else "❌"
|
||||
print(f"{status} {test_name}")
|
||||
|
||||
if passed == total:
|
||||
print(f"\n🎉 所有测试通过!SQL语法错误修复成功!")
|
||||
print("✅ WHERE...AND ORDER BY 语法错误已解决")
|
||||
print("✅ 学生信息管理页面搜索功能恢复正常")
|
||||
else:
|
||||
print(f"\n⚠️ 部分测试失败,需要进一步排查问题")
|
||||
|
||||
return passed == total
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = main()
|
||||
sys.exit(0 if success else 1)
|
||||
@@ -1,331 +0,0 @@
|
||||
const axios = require('axios');
|
||||
const colors = require('colors');
|
||||
|
||||
// API格式验证测试脚本
|
||||
// 用于验证所有接口是否符合统一响应格式规范
|
||||
|
||||
// 获取Token
|
||||
async function getToken() {
|
||||
try {
|
||||
const { getToken } = require('./quick-token-getter');
|
||||
return await getToken();
|
||||
} catch (error) {
|
||||
console.log('❌ 获取Token失败'.red);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 验证响应格式
|
||||
function validateResponseFormat(response, apiName, expectedType) {
|
||||
const issues = [];
|
||||
|
||||
// 检查基础字段
|
||||
if (typeof response.code !== 'number') {
|
||||
issues.push('❌ code字段缺失或类型错误(应为number)');
|
||||
}
|
||||
|
||||
if (typeof response.msg !== 'string') {
|
||||
issues.push('❌ msg字段缺失或类型错误(应为string)');
|
||||
}
|
||||
|
||||
if (!response.hasOwnProperty('data')) {
|
||||
issues.push('❌ data字段缺失');
|
||||
}
|
||||
|
||||
// 检查成功状态码
|
||||
if (response.code === 200) {
|
||||
// 根据期望类型验证字段
|
||||
switch (expectedType) {
|
||||
case 'paginated':
|
||||
// 分页接口直接在顶层包含total和rows字段
|
||||
if (typeof response.total !== 'number') {
|
||||
issues.push('❌ 分页接口缺少total字段(应为number)');
|
||||
}
|
||||
if (!Array.isArray(response.rows)) {
|
||||
issues.push('❌ 分页接口缺少rows字段(应为array)');
|
||||
}
|
||||
break;
|
||||
case 'array':
|
||||
if (!Array.isArray(response.data)) {
|
||||
issues.push('❌ 列表接口data应为数组');
|
||||
}
|
||||
break;
|
||||
case 'object':
|
||||
if (
|
||||
response.data === null ||
|
||||
typeof response.data !== 'object' ||
|
||||
Array.isArray(response.data)
|
||||
) {
|
||||
issues.push('❌ 单条数据接口data应为对象(非null)');
|
||||
}
|
||||
break;
|
||||
case 'null':
|
||||
if (response.data !== null) {
|
||||
issues.push('❌ 操作类接口data应为null');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
// API测试用例
|
||||
const apiTestCases = [
|
||||
{
|
||||
name: '报修工单列表(分页)',
|
||||
url: '/pm/repairOrder/list',
|
||||
method: 'GET',
|
||||
data: { pageNum: 1, pageSize: 10 },
|
||||
expectedType: 'paginated',
|
||||
permission: 'pm:repairOrder:list',
|
||||
},
|
||||
{
|
||||
name: '维修分类下拉选项',
|
||||
url: '/pm/repairCategory/options',
|
||||
method: 'GET',
|
||||
data: null,
|
||||
expectedType: 'array',
|
||||
permission: 'pm:category:options',
|
||||
},
|
||||
{
|
||||
name: '报修统计数据',
|
||||
url: '/pm/repairOrder/statistics',
|
||||
method: 'GET',
|
||||
data: null,
|
||||
expectedType: 'object',
|
||||
permission: 'pm:repairOrder:statistics',
|
||||
},
|
||||
{
|
||||
name: '系统用户信息',
|
||||
url: '/system/user/getInfo',
|
||||
method: 'GET',
|
||||
data: null,
|
||||
expectedType: 'object',
|
||||
permission: 'system:user:query',
|
||||
},
|
||||
{
|
||||
name: '系统字典数据',
|
||||
url: '/system/dict/data/list',
|
||||
method: 'GET',
|
||||
data: { pageNum: 1, pageSize: 10 },
|
||||
expectedType: 'paginated',
|
||||
permission: 'system:dict:list',
|
||||
},
|
||||
];
|
||||
|
||||
// 执行API格式验证测试
|
||||
async function runApiFormatValidation() {
|
||||
console.log('🔍 API格式验证测试'.rainbow.bold);
|
||||
console.log('='.repeat(80).rainbow);
|
||||
console.log('📋 验证规范:统一响应格式 {code, msg, data}');
|
||||
console.log('🎯 测试目标:确保所有接口符合新的格式规范');
|
||||
console.log('');
|
||||
|
||||
// 获取Token
|
||||
console.log('🔐 获取认证Token...'.magenta);
|
||||
const token = await getToken();
|
||||
if (!token) {
|
||||
console.log('❌ 无法获取Token,测试终止'.red);
|
||||
return;
|
||||
}
|
||||
console.log('✅ Token获取成功'.green);
|
||||
console.log('');
|
||||
|
||||
const results = [];
|
||||
let totalTests = 0;
|
||||
let passedTests = 0;
|
||||
|
||||
// 逐个测试API
|
||||
for (const testCase of apiTestCases) {
|
||||
totalTests++;
|
||||
console.log(`📝 测试 ${totalTests}: ${testCase.name}`.cyan.bold);
|
||||
console.log(` 📍 接口: ${testCase.method} ${testCase.url}`);
|
||||
console.log(` 🔑 权限: ${testCase.permission}`);
|
||||
console.log(` 📊 期望格式: ${testCase.expectedType}`);
|
||||
|
||||
try {
|
||||
// 构建请求配置
|
||||
const config = {
|
||||
method: testCase.method.toLowerCase(),
|
||||
url: `http://localhost/dev-api${testCase.url}`,
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=utf-8',
|
||||
clientid: 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
||||
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
||||
referer: 'http://localhost/login?redirect=%252Findex',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
timeout: 10000,
|
||||
};
|
||||
|
||||
if (testCase.data) {
|
||||
if (testCase.method === 'GET') {
|
||||
config.params = testCase.data;
|
||||
} else {
|
||||
config.data = testCase.data;
|
||||
}
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
const response = await axios(config);
|
||||
|
||||
// 验证响应格式
|
||||
const issues = validateResponseFormat(
|
||||
response.data,
|
||||
testCase.name,
|
||||
testCase.expectedType,
|
||||
);
|
||||
|
||||
if (issues.length === 0) {
|
||||
console.log(' ✅ 格式验证通过'.green);
|
||||
passedTests++;
|
||||
|
||||
// 显示响应数据统计
|
||||
if (response.data.code === 200) {
|
||||
if (testCase.expectedType === 'paginated') {
|
||||
console.log(
|
||||
` 📊 数据统计: total=${response.data.total}, rows=${
|
||||
response.data.rows?.length || 0
|
||||
}`,
|
||||
);
|
||||
} else if (
|
||||
testCase.expectedType === 'array' &&
|
||||
Array.isArray(response.data.data)
|
||||
) {
|
||||
console.log(` 📊 数据统计: 数组长度=${response.data.data.length}`);
|
||||
} else if (testCase.expectedType === 'object' && response.data.data) {
|
||||
const keys = Object.keys(response.data.data);
|
||||
console.log(` 📊 数据统计: 对象字段=${keys.length}个`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(' ❌ 格式验证失败'.red);
|
||||
issues.forEach((issue) => {
|
||||
console.log(` ${issue}`.red);
|
||||
});
|
||||
}
|
||||
|
||||
results.push({
|
||||
name: testCase.name,
|
||||
url: testCase.url,
|
||||
method: testCase.method,
|
||||
success: issues.length === 0,
|
||||
issues: issues,
|
||||
response: {
|
||||
code: response.data.code,
|
||||
msg: response.data.msg,
|
||||
hasData: response.data.hasOwnProperty('data'),
|
||||
dataType:
|
||||
testCase.expectedType === 'paginated'
|
||||
? 'paginated'
|
||||
: Array.isArray(response.data.data)
|
||||
? 'array'
|
||||
: response.data.data === null
|
||||
? 'null'
|
||||
: typeof response.data.data,
|
||||
total: response.data.total,
|
||||
rowsCount: response.data.rows?.length,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(' ❌ 请求失败'.red);
|
||||
console.log(` 错误信息: ${error.response?.data?.msg || error.message}`.red);
|
||||
|
||||
results.push({
|
||||
name: testCase.name,
|
||||
url: testCase.url,
|
||||
method: testCase.method,
|
||||
success: false,
|
||||
issues: [`请求失败: ${error.response?.data?.msg || error.message}`],
|
||||
response: null,
|
||||
});
|
||||
}
|
||||
|
||||
console.log('');
|
||||
}
|
||||
|
||||
// 输出测试总结
|
||||
console.log('📊 测试结果汇总'.rainbow.bold);
|
||||
console.log('='.repeat(80).rainbow);
|
||||
console.log(`🎯 总测试数: ${totalTests}`);
|
||||
console.log(`✅ 通过测试: ${passedTests}`.green);
|
||||
console.log(`❌ 失败测试: ${totalTests - passedTests}`.red);
|
||||
console.log(`📈 通过率: ${((passedTests / totalTests) * 100).toFixed(1)}%`);
|
||||
console.log('');
|
||||
|
||||
// 详细结果表格
|
||||
console.log('📋 详细测试结果'.blue.bold);
|
||||
console.log('─'.repeat(100));
|
||||
results.forEach((result, index) => {
|
||||
const status = result.success ? '✅ 通过'.green : '❌ 失败'.red;
|
||||
console.log(
|
||||
`${(index + 1).toString().padStart(2)}. ${result.name.padEnd(
|
||||
20,
|
||||
)} ${result.method.padEnd(6)} ${status}`,
|
||||
);
|
||||
|
||||
if (!result.success && result.issues.length > 0) {
|
||||
result.issues.forEach((issue) => {
|
||||
console.log(` ${issue}`.red);
|
||||
});
|
||||
}
|
||||
|
||||
if (result.response) {
|
||||
console.log(
|
||||
` 响应: code=${result.response.code}, msg="${result.response.msg}", data类型=${result.response.dataType}`,
|
||||
);
|
||||
}
|
||||
console.log('');
|
||||
});
|
||||
|
||||
// 规范建议
|
||||
if (passedTests < totalTests) {
|
||||
console.log('💡 规范建议'.yellow.bold);
|
||||
console.log('─'.repeat(50));
|
||||
console.log('1. 检查请求头是否包含Bearer Token格式的Authorization');
|
||||
console.log('2. 分页接口必须直接返回TableDataInfo格式(total、rows、code、msg)');
|
||||
console.log('3. 非分页接口必须使用R<T>类型包装返回结果');
|
||||
console.log('4. 确保包含clientid、Content-Type等必要请求头');
|
||||
console.log('5. 验证响应格式是否符合新的统一规范');
|
||||
console.log('');
|
||||
}
|
||||
|
||||
// 成功提示
|
||||
if (passedTests === totalTests) {
|
||||
console.log('🎉 恭喜!所有接口都符合统一响应格式规范!'.green.bold);
|
||||
}
|
||||
|
||||
return {
|
||||
total: totalTests,
|
||||
passed: passedTests,
|
||||
failed: totalTests - passedTests,
|
||||
successRate: ((passedTests / totalTests) * 100).toFixed(1),
|
||||
};
|
||||
}
|
||||
|
||||
// 主函数
|
||||
async function main() {
|
||||
try {
|
||||
const result = await runApiFormatValidation();
|
||||
console.log('\n🏁 API格式验证测试完成'.rainbow.bold);
|
||||
|
||||
// 返回测试结果供其他脚本使用
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.log('❌ 测试执行失败:'.red, error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果直接运行此脚本
|
||||
if (require.main === module) {
|
||||
main().then((result) => {
|
||||
if (result.failed > 0) {
|
||||
process.exit(1); // 有失败用例时退出码为1
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { runApiFormatValidation };
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
# 出国审批页面问题诊断指南
|
||||
|
||||
## 🔍 问题排查步骤
|
||||
|
||||
### 1. 检查浏览器控制台错误
|
||||
|
||||
**请按以下步骤操作:**
|
||||
|
||||
1. 打开浏览器访问:`http://localhost/rs/overseasApproval`
|
||||
2. 按 **F12** 打开开发者工具
|
||||
3. 点击 **Console** 标签
|
||||
4. 刷新页面,查看是否有红色错误信息
|
||||
5. 点击 **Network** 标签,查看API请求状态
|
||||
|
||||
### 2. 检查API请求状态
|
||||
|
||||
在Network标签中查找以下请求:
|
||||
- `pending/list` - 待审批申请列表API
|
||||
- `my/list` - 我的审批记录API
|
||||
|
||||
**状态码说明:**
|
||||
- `200` - 正常
|
||||
- `401` - 未登录或token过期
|
||||
- `403` - 权限不足
|
||||
- `404` - 接口不存在
|
||||
- `500` - 服务器错误
|
||||
|
||||
### 3. 常见问题及解决方案
|
||||
|
||||
#### 问题1: API返回401未授权
|
||||
**原因**: 用户未登录或登录状态过期
|
||||
**解决**: 重新登录系统
|
||||
|
||||
#### 问题2: API返回403权限不足
|
||||
**原因**: 当前用户没有审批相关权限
|
||||
**解决**: 联系管理员分配审批权限
|
||||
|
||||
#### 问题3: 页面完全空白
|
||||
**原因**: Vue组件加载失败或API调用异常
|
||||
**解决**: 查看控制台具体错误信息
|
||||
|
||||
#### 问题4: API返回空数据
|
||||
**原因**: 数据库中没有待审批的数据
|
||||
**解决**: 这是正常情况,数据库已包含测试数据
|
||||
|
||||
### 4. 快速验证方法
|
||||
|
||||
**打开浏览器控制台,输入以下代码进行测试:**
|
||||
|
||||
```javascript
|
||||
// 测试API连通性
|
||||
fetch('/rs/overseas/application/pending/list?pageNum=1&pageSize=10')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log('待审批API响应:', data);
|
||||
if (data.code === 200) {
|
||||
console.log('✅ API正常,数据条数:', data.total);
|
||||
} else {
|
||||
console.log('❌ API异常,错误:', data.msg);
|
||||
}
|
||||
})
|
||||
.catch(error => console.log('🔴 请求失败:', error));
|
||||
|
||||
fetch('/rs/overseas/approval/my/list?pageNum=1&pageSize=10')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log('审批记录API响应:', data);
|
||||
if (data.code === 200) {
|
||||
console.log('✅ API正常,数据条数:', data.total);
|
||||
} else {
|
||||
console.log('❌ API异常,错误:', data.msg);
|
||||
}
|
||||
})
|
||||
.catch(error => console.log('🔴 请求失败:', error));
|
||||
```
|
||||
|
||||
### 5. 数据验证
|
||||
|
||||
**预期数据:**
|
||||
- 待审批申请:应该显示2条记录(张测试、李测试)
|
||||
- 审批记录:应该显示5条历史审批记录
|
||||
|
||||
## 📋 请提供的信息
|
||||
|
||||
为了进一步协助诊断,请提供以下信息:
|
||||
|
||||
1. **浏览器控制台的完整错误信息**
|
||||
2. **Network标签中API请求的状态码和响应**
|
||||
3. **页面显示的具体情况**(完全空白 or 有加载动画 or 显示错误信息)
|
||||
|
||||
## 🚀 临时解决方案
|
||||
|
||||
如果问题仍然存在,可以尝试:
|
||||
|
||||
1. **清除浏览器缓存和Cookie**
|
||||
2. **使用无痕模式重新登录**
|
||||
3. **检查是否有浏览器插件干扰**
|
||||
4. **尝试不同的浏览器**
|
||||
|
||||
---
|
||||
|
||||
**创建时间**: 2025-01-28 14:30:00
|
||||
**状态**: 等待用户反馈具体错误信息
|
||||
@@ -1,83 +0,0 @@
|
||||
-- ==========================================
|
||||
-- 维修角色权限分配脚本
|
||||
-- 功能:为维修人员和物业维修管理员分配适当的功能权限
|
||||
-- 时间:2025-06-30 13:50:00
|
||||
-- 说明:基于业务需求分配差异化权限
|
||||
-- ==========================================
|
||||
|
||||
-- 第一步:为维修人员(role_id: 5)分配权限
|
||||
-- 维修人员需要:查看工单、处理自己的维修任务、查看维修分类
|
||||
INSERT IGNORE INTO sys_role_menu (role_id, menu_id) VALUES
|
||||
-- 报修管理基础权限
|
||||
(5, 2110), -- 报修管理页面
|
||||
(5, 2111), -- 报修查询
|
||||
(5, 2117), -- 开始维修
|
||||
(5, 2118), -- 完成维修
|
||||
(5, 2121), -- 生成工单号
|
||||
-- 维修分类查看权限
|
||||
(5, 2130), -- 维修分类页面
|
||||
(5, 2131), -- 分类查询
|
||||
(5, 2136), -- 分类树查询
|
||||
(5, 2137); -- 分类选项
|
||||
|
||||
-- 第二步:为物业维修管理员(role_id: 6)分配权限
|
||||
-- 物业维修管理员需要:管理所有工单、分配任务、查看统计、管理分类
|
||||
INSERT IGNORE INTO sys_role_menu (role_id, menu_id) VALUES
|
||||
-- 报修管理全权限(除删除外)
|
||||
(6, 2110), -- 报修管理页面
|
||||
(6, 2111), -- 报修查询
|
||||
(6, 2112), -- 报修新增
|
||||
(6, 2113), -- 报修修改
|
||||
(6, 2115), -- 报修导出
|
||||
(6, 2116), -- 工单分派
|
||||
(6, 2117), -- 开始维修
|
||||
(6, 2118), -- 完成维修
|
||||
(6, 2119), -- 取消工单
|
||||
(6, 2120), -- 统计数据
|
||||
(6, 2121), -- 生成工单号
|
||||
-- 维修分类管理权限
|
||||
(6, 2130), -- 维修分类页面
|
||||
(6, 2131), -- 分类查询
|
||||
(6, 2132), -- 分类新增
|
||||
(6, 2133), -- 分类修改
|
||||
(6, 2135), -- 分类导出
|
||||
(6, 2136), -- 分类树查询
|
||||
(6, 2137); -- 分类选项
|
||||
|
||||
-- 第三步:为两个角色分配物业管理主菜单权限(如果存在)
|
||||
-- 确保用户能看到物业管理菜单入口
|
||||
INSERT IGNORE INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT 5, menu_id FROM sys_menu WHERE menu_name = '物业管理' AND menu_type = 'M'
|
||||
UNION ALL
|
||||
SELECT 6, menu_id FROM sys_menu WHERE menu_name = '物业管理' AND menu_type = 'M';
|
||||
|
||||
-- 第四步:验证权限分配结果
|
||||
SELECT '权限分配验证' as message,
|
||||
'维修人员权限数' as role_type,
|
||||
(SELECT COUNT(*) FROM sys_role_menu WHERE role_id = 5) as permission_count
|
||||
UNION ALL
|
||||
SELECT '权限分配验证' as message,
|
||||
'物业维修管理员权限数' as role_type,
|
||||
(SELECT COUNT(*) FROM sys_role_menu WHERE role_id = 6) as permission_count;
|
||||
|
||||
-- 详细权限分配情况
|
||||
SELECT
|
||||
r.role_name as '角色名称',
|
||||
m.menu_name as '菜单/功能名称',
|
||||
m.perms as '权限标识',
|
||||
CASE m.menu_type
|
||||
WHEN 'M' THEN '目录'
|
||||
WHEN 'C' THEN '菜单'
|
||||
WHEN 'F' THEN '按钮'
|
||||
END as '权限类型'
|
||||
FROM sys_role_menu rm
|
||||
JOIN sys_role r ON rm.role_id = r.role_id
|
||||
JOIN sys_menu m ON rm.menu_id = m.menu_id
|
||||
WHERE rm.role_id IN (5, 6)
|
||||
AND m.perms LIKE 'pm:%'
|
||||
ORDER BY r.role_id, m.menu_id;
|
||||
|
||||
-- 第五步:权限差异化说明
|
||||
SELECT '权限设计说明' as info_type, '维修人员:只能处理维修任务,无管理权限' as description
|
||||
UNION ALL
|
||||
SELECT '权限设计说明' as info_type, '物业维修管理员:可管理工单、分配任务、查看统计' as description;
|
||||
@@ -1,466 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
批量创建学生信息脚本
|
||||
为每个班级创建30个学生的测试数据
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import random
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class SimpleDataGenerator:
|
||||
"""简单的数据生成器,替代faker"""
|
||||
|
||||
def __init__(self):
|
||||
# 姓氏
|
||||
self.surnames = ['李', '王', '张', '刘', '陈', '杨', '黄', '赵', '周', '吴',
|
||||
'徐', '孙', '朱', '马', '胡', '郭', '林', '何', '高', '梁']
|
||||
|
||||
# 男性名字
|
||||
self.male_names = ['伟', '强', '磊', '军', '勇', '涛', '明', '超', '辉', '华',
|
||||
'建', '文', '健', '云', '峰', '浩', '鹏', '飞', '宇', '杰']
|
||||
|
||||
# 女性名字
|
||||
self.female_names = ['丽', '娜', '敏', '静', '佳', '雅', '莉', '萍', '红', '梅',
|
||||
'芳', '燕', '玲', '霞', '琳', '晶', '欣', '宁', '蕾', '洁']
|
||||
|
||||
# 省份
|
||||
self.provinces = ['北京市', '上海市', '天津市', '重庆市', '河北省', '山西省', '辽宁省',
|
||||
'吉林省', '黑龙江省', '江苏省', '浙江省', '安徽省', '福建省', '江西省',
|
||||
'山东省', '河南省', '湖北省', '湖南省', '广东省', '海南省', '四川省',
|
||||
'贵州省', '云南省', '陕西省', '甘肃省', '青海省', '台湾省', '内蒙古自治区',
|
||||
'广西壮族自治区', '西藏自治区', '宁夏回族自治区', '新疆维吾尔自治区']
|
||||
|
||||
def generate_name(self, gender):
|
||||
"""生成姓名"""
|
||||
surname = random.choice(self.surnames)
|
||||
if gender == '0': # 男性
|
||||
given_name = random.choice(self.male_names)
|
||||
else: # 女性
|
||||
given_name = random.choice(self.female_names)
|
||||
|
||||
# 有时候生成双字名
|
||||
if random.random() < 0.3:
|
||||
if gender == '0':
|
||||
given_name += random.choice(self.male_names)
|
||||
else:
|
||||
given_name += random.choice(self.female_names)
|
||||
|
||||
return surname + given_name
|
||||
|
||||
def generate_phone(self):
|
||||
"""生成手机号"""
|
||||
prefixes = ['130', '131', '132', '133', '134', '135', '136', '137', '138', '139',
|
||||
'150', '151', '152', '153', '155', '156', '157', '158', '159',
|
||||
'180', '181', '182', '183', '184', '185', '186', '187', '188', '189']
|
||||
prefix = random.choice(prefixes)
|
||||
suffix = ''.join([str(random.randint(0, 9)) for _ in range(8)])
|
||||
return prefix + suffix
|
||||
|
||||
def generate_id_card(self, birth_date):
|
||||
"""生成身份证号"""
|
||||
# 地区代码(简化版)
|
||||
area_codes = ['110101', '110102', '110105', '110106', '110107', '110108',
|
||||
'310101', '310104', '310105', '310106', '310107', '310109',
|
||||
'440101', '440103', '440104', '440105', '440106', '440111']
|
||||
|
||||
area_code = random.choice(area_codes)
|
||||
|
||||
# 出生日期(YYYYMMDD格式)
|
||||
birth_str = birth_date.replace('-', '')
|
||||
|
||||
# 顺序码(3位)+ 校验码(1位)
|
||||
sequence = str(random.randint(100, 999))
|
||||
check_digit = str(random.randint(0, 9))
|
||||
|
||||
return area_code + birth_str + sequence + check_digit
|
||||
|
||||
def generate_address(self):
|
||||
"""生成地址"""
|
||||
province = random.choice(self.provinces)
|
||||
cities = ['市', '县']
|
||||
city = f"某{random.choice(cities)}"
|
||||
district = f"某区某街道{random.randint(1, 999)}号"
|
||||
return province + city + district
|
||||
|
||||
def generate_province(self):
|
||||
"""生成省份"""
|
||||
return random.choice(self.provinces)
|
||||
|
||||
class StudentBatchCreator:
|
||||
def __init__(self, base_url="http://localhost:8080", token=None):
|
||||
"""
|
||||
初始化学生批量创建器
|
||||
|
||||
Args:
|
||||
base_url: 后端API基础URL
|
||||
token: 认证token
|
||||
"""
|
||||
self.base_url = base_url
|
||||
self.token = token
|
||||
self.session = requests.Session()
|
||||
self.data_gen = SimpleDataGenerator()
|
||||
|
||||
# 设置请求头
|
||||
if token:
|
||||
self.session.headers.update({
|
||||
'Authorization': f'Bearer {token}',
|
||||
'Content-Type': 'application/json'
|
||||
})
|
||||
|
||||
def set_token(self, token):
|
||||
"""设置认证token"""
|
||||
self.token = token
|
||||
self.session.headers.update({
|
||||
'Authorization': f'Bearer {token}'
|
||||
})
|
||||
|
||||
def get_class_list(self):
|
||||
"""获取班级列表"""
|
||||
try:
|
||||
url = f"{self.base_url}/classMsg/classMsg/list"
|
||||
params = {
|
||||
'pageNum': 1,
|
||||
'pageSize': 1000 # 获取所有班级
|
||||
}
|
||||
|
||||
response = self.session.get(url, params=params)
|
||||
response.raise_for_status()
|
||||
|
||||
data = response.json()
|
||||
if data.get('code') == 200:
|
||||
classes = data.get('rows', [])
|
||||
logger.info(f"获取到 {len(classes)} 个班级")
|
||||
return classes
|
||||
else:
|
||||
logger.error(f"获取班级列表失败: {data.get('msg')}")
|
||||
return []
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"获取班级列表异常: {str(e)}")
|
||||
return []
|
||||
|
||||
def generate_birth_date(self):
|
||||
"""生成出生日期(18-22岁)"""
|
||||
today = datetime.now()
|
||||
min_age = 18
|
||||
max_age = 22
|
||||
|
||||
# 计算出生年份范围
|
||||
birth_year = today.year - random.randint(min_age, max_age)
|
||||
birth_month = random.randint(1, 12)
|
||||
birth_day = random.randint(1, 28) # 使用28避免月份天数问题
|
||||
|
||||
return f"{birth_year:04d}-{birth_month:02d}-{birth_day:02d}"
|
||||
|
||||
def generate_student_data(self, class_info, student_index):
|
||||
"""
|
||||
生成单个学生的测试数据
|
||||
|
||||
Args:
|
||||
class_info: 班级信息
|
||||
student_index: 学生编号(1-30)
|
||||
|
||||
Returns:
|
||||
dict: 学生数据
|
||||
"""
|
||||
# 生成基础信息
|
||||
gender = random.choice(['0', '1']) # 0-男,1-女
|
||||
name = self.data_gen.generate_name(gender)
|
||||
|
||||
# 生成学号:班级代码 + 学生编号
|
||||
class_code = class_info.get('classCode', class_info.get('class_code', ''))
|
||||
if not class_code:
|
||||
# 如果没有班级代码,使用班级ID
|
||||
class_code = f"CLS{class_info.get('id', '')}"
|
||||
|
||||
student_no = f"{class_code}{student_index:02d}"
|
||||
|
||||
# 生成出生日期
|
||||
birth_date = self.generate_birth_date()
|
||||
|
||||
# 生成身份证号
|
||||
id_card = self.data_gen.generate_id_card(birth_date)
|
||||
|
||||
# 生成手机号
|
||||
phone = self.data_gen.generate_phone()
|
||||
|
||||
# 民族选择
|
||||
nations = ['01', '02', '03', '04', '05'] # 汉族、蒙古族、回族、藏族、维吾尔族等
|
||||
nationality = random.choice(nations)
|
||||
|
||||
# 政治面貌选择
|
||||
political_statuses = ['01', '02', '03', '13'] # 群众、团员、党员等
|
||||
political_status = random.choice(political_statuses)
|
||||
|
||||
# 构建学生数据
|
||||
student_data = {
|
||||
# 基础信息
|
||||
"studentNo": student_no,
|
||||
"idCard": id_card,
|
||||
"name": name,
|
||||
"gender": gender,
|
||||
"birthDate": birth_date,
|
||||
"nationality": nationality,
|
||||
"politicalStatus": political_status,
|
||||
"phone": phone,
|
||||
"email": f"{student_no}@student.edu.cn",
|
||||
|
||||
# 学籍信息(从班级信息继承)
|
||||
"classId": class_info.get('id'),
|
||||
"className": class_info.get('className', class_info.get('class_name', '')),
|
||||
"majorId": class_info.get('majorId', class_info.get('major_id')),
|
||||
"majorName": class_info.get('majorName', class_info.get('major_name', '')),
|
||||
"collegeId": class_info.get('collegeId', class_info.get('college_id')),
|
||||
"collegeName": class_info.get('collegeName', class_info.get('college_name', '')),
|
||||
"grade": class_info.get('grade', ''),
|
||||
"enrollmentYear": class_info.get('enrollmentYear', class_info.get('enrollment_year')),
|
||||
"academicLevel": class_info.get('classType', class_info.get('class_type', 1)),
|
||||
"classTeacherId": class_info.get('classTeacherId', class_info.get('class_teacher_id')),
|
||||
"classTeacherName": class_info.get('classTeacherName', class_info.get('class_teacher_name', '')),
|
||||
"schoolSystem": class_info.get('schoolSystem', class_info.get('school_system', 3)),
|
||||
|
||||
# 默认状态
|
||||
"studentStatus": "1", # 在籍
|
||||
"trainingType": "全日制",
|
||||
"admissionCategory": "统招",
|
||||
"householdRegisterType": "非农业",
|
||||
"educationLevel": "2", # 高中
|
||||
"maritalStatus": "1", # 未婚
|
||||
|
||||
# 地址信息
|
||||
"nativePlace": self.data_gen.generate_province(),
|
||||
"sourceRegion": self.data_gen.generate_province(),
|
||||
"homeAddress": self.data_gen.generate_address(),
|
||||
"currentAddress": "学校宿舍",
|
||||
|
||||
# 家庭信息
|
||||
"fatherName": self.data_gen.generate_name('0'),
|
||||
"fatherPhone": self.data_gen.generate_phone(),
|
||||
"motherName": self.data_gen.generate_name('1'),
|
||||
"motherPhone": self.data_gen.generate_phone(),
|
||||
|
||||
# 健康信息
|
||||
"height": random.randint(155, 185),
|
||||
"weight": random.randint(45, 80),
|
||||
"bloodType": random.choice(['A', 'B', 'AB', 'O']),
|
||||
"healthStatus": "良好",
|
||||
|
||||
# 布尔字段
|
||||
"isBoarder": 1, # 住校
|
||||
"isPoorStudent": random.randint(0, 1),
|
||||
"isMinority": 1 if nationality != '01' else 0, # 非汉族为少数民族
|
||||
"isDisabled": 0,
|
||||
"isOrphan": 0,
|
||||
"isLeftBehindChild": 0,
|
||||
"isSingleChild": random.randint(0, 1),
|
||||
"isLowIncome": 0,
|
||||
"hasLoan": random.randint(0, 1),
|
||||
|
||||
# 其他字段
|
||||
"remark": f"批量生成的测试学生数据 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
||||
}
|
||||
|
||||
return student_data
|
||||
|
||||
def create_student(self, student_data):
|
||||
"""
|
||||
创建单个学生
|
||||
|
||||
Args:
|
||||
student_data: 学生数据
|
||||
|
||||
Returns:
|
||||
tuple: (success, result)
|
||||
"""
|
||||
try:
|
||||
url = f"{self.base_url}/studentMsg/studentMsg"
|
||||
|
||||
response = self.session.post(url, json=student_data)
|
||||
response.raise_for_status()
|
||||
|
||||
result = response.json()
|
||||
if result.get('code') == 200:
|
||||
logger.info(f"成功创建学生: {student_data['name']} ({student_data['studentNo']})")
|
||||
return True, result
|
||||
else:
|
||||
logger.error(f"创建学生失败: {student_data['name']} - {result.get('msg')}")
|
||||
return False, result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"创建学生异常: {student_data['name']} - {str(e)}")
|
||||
return False, str(e)
|
||||
|
||||
def batch_create_students_for_class(self, class_info, student_count=30):
|
||||
"""
|
||||
为指定班级批量创建学生
|
||||
|
||||
Args:
|
||||
class_info: 班级信息
|
||||
student_count: 要创建的学生数量
|
||||
|
||||
Returns:
|
||||
dict: 创建结果统计
|
||||
"""
|
||||
class_name = class_info.get('className', class_info.get('class_name', f"班级{class_info.get('id')}"))
|
||||
logger.info(f"开始为班级 [{class_name}] 创建 {student_count} 个学生...")
|
||||
|
||||
success_count = 0
|
||||
failed_count = 0
|
||||
failed_students = []
|
||||
|
||||
for i in range(1, student_count + 1):
|
||||
try:
|
||||
# 生成学生数据
|
||||
student_data = self.generate_student_data(class_info, i)
|
||||
|
||||
# 创建学生
|
||||
success, result = self.create_student(student_data)
|
||||
|
||||
if success:
|
||||
success_count += 1
|
||||
else:
|
||||
failed_count += 1
|
||||
failed_students.append({
|
||||
'student_no': student_data['studentNo'],
|
||||
'name': student_data['name'],
|
||||
'error': result
|
||||
})
|
||||
|
||||
# 添加短暂延迟,避免请求过于频繁
|
||||
time.sleep(0.1)
|
||||
|
||||
except Exception as e:
|
||||
failed_count += 1
|
||||
logger.error(f"处理第 {i} 个学生时发生异常: {str(e)}")
|
||||
failed_students.append({
|
||||
'student_no': f"未知_{i}",
|
||||
'name': '未知',
|
||||
'error': str(e)
|
||||
})
|
||||
|
||||
result = {
|
||||
'class_name': class_name,
|
||||
'total': student_count,
|
||||
'success': success_count,
|
||||
'failed': failed_count,
|
||||
'failed_students': failed_students
|
||||
}
|
||||
|
||||
logger.info(f"班级 [{class_name}] 学生创建完成: 成功 {success_count},失败 {failed_count}")
|
||||
return result
|
||||
|
||||
def batch_create_students_for_all_classes(self, student_count_per_class=30):
|
||||
"""
|
||||
为所有班级批量创建学生
|
||||
|
||||
Args:
|
||||
student_count_per_class: 每个班级创建的学生数量
|
||||
|
||||
Returns:
|
||||
list: 所有班级的创建结果
|
||||
"""
|
||||
# 获取班级列表
|
||||
classes = self.get_class_list()
|
||||
if not classes:
|
||||
logger.error("没有获取到班级信息,无法创建学生")
|
||||
return []
|
||||
|
||||
logger.info(f"开始为 {len(classes)} 个班级批量创建学生,每个班级 {student_count_per_class} 个学生")
|
||||
|
||||
all_results = []
|
||||
total_success = 0
|
||||
total_failed = 0
|
||||
|
||||
for class_info in classes:
|
||||
try:
|
||||
result = self.batch_create_students_for_class(class_info, student_count_per_class)
|
||||
all_results.append(result)
|
||||
total_success += result['success']
|
||||
total_failed += result['failed']
|
||||
|
||||
# 班级间添加延迟
|
||||
time.sleep(0.5)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"处理班级 {class_info} 时发生异常: {str(e)}")
|
||||
all_results.append({
|
||||
'class_name': f"班级{class_info.get('id', '未知')}",
|
||||
'total': student_count_per_class,
|
||||
'success': 0,
|
||||
'failed': student_count_per_class,
|
||||
'error': str(e)
|
||||
})
|
||||
total_failed += student_count_per_class
|
||||
|
||||
# 输出总结
|
||||
logger.info("="*50)
|
||||
logger.info("批量创建学生完成!")
|
||||
logger.info(f"总班级数: {len(classes)}")
|
||||
logger.info(f"总学生数: {len(classes) * student_count_per_class}")
|
||||
logger.info(f"成功创建: {total_success}")
|
||||
logger.info(f"创建失败: {total_failed}")
|
||||
logger.info("="*50)
|
||||
|
||||
return all_results
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("学生批量创建工具")
|
||||
print("="*50)
|
||||
|
||||
# 配置参数
|
||||
base_url = input("请输入后端API地址 (默认: http://localhost:8080): ").strip()
|
||||
if not base_url:
|
||||
base_url = "http://localhost:8080"
|
||||
|
||||
token = input("请输入认证Token (可为空): ").strip()
|
||||
|
||||
student_count = input("请输入每个班级创建的学生数量 (默认: 30): ").strip()
|
||||
if not student_count:
|
||||
student_count = 30
|
||||
else:
|
||||
try:
|
||||
student_count = int(student_count)
|
||||
except ValueError:
|
||||
print("输入的学生数量无效,使用默认值 30")
|
||||
student_count = 30
|
||||
|
||||
# 创建批量创建器
|
||||
creator = StudentBatchCreator(base_url, token)
|
||||
|
||||
# 确认操作
|
||||
print(f"\n配置信息:")
|
||||
print(f"API地址: {base_url}")
|
||||
print(f"每个班级学生数: {student_count}")
|
||||
|
||||
confirm = input("\n确认开始批量创建学生?(y/N): ").strip().lower()
|
||||
if confirm not in ['y', 'yes']:
|
||||
print("操作已取消")
|
||||
return
|
||||
|
||||
# 开始批量创建
|
||||
start_time = time.time()
|
||||
results = creator.batch_create_students_for_all_classes(student_count)
|
||||
end_time = time.time()
|
||||
|
||||
# 保存结果到文件
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
result_file = f"student_creation_result_{timestamp}.json"
|
||||
|
||||
with open(result_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(results, f, ensure_ascii=False, indent=2)
|
||||
|
||||
print(f"\n创建结果已保存到: {result_file}")
|
||||
print(f"总耗时: {end_time - start_time:.2f} 秒")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,66 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
检查系统状态和可用的API端点
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
def check_system_status():
|
||||
"""检查系统状态"""
|
||||
base_urls = [
|
||||
"http://localhost:8080",
|
||||
"http://localhost/dev-api",
|
||||
"http://localhost"
|
||||
]
|
||||
|
||||
print("🔍 检查系统状态...")
|
||||
|
||||
for base_url in base_urls:
|
||||
print(f"\n🌐 测试地址: {base_url}")
|
||||
|
||||
# 测试基本连接
|
||||
try:
|
||||
response = requests.get(f"{base_url}/", timeout=5)
|
||||
print(f" ✅ 基础连接: {response.status_code}")
|
||||
except Exception as e:
|
||||
print(f" ❌ 基础连接失败: {str(e)}")
|
||||
continue
|
||||
|
||||
# 测试登录端点
|
||||
try:
|
||||
login_endpoints = ["/auth/login", "/login"]
|
||||
for endpoint in login_endpoints:
|
||||
try:
|
||||
response = requests.post(f"{base_url}{endpoint}", json={}, timeout=5)
|
||||
print(f" 🔑 登录端点 {endpoint}: {response.status_code}")
|
||||
if response.status_code in [200, 400, 401, 403]:
|
||||
print(f" 响应: {response.text[:200]}...")
|
||||
except Exception as e:
|
||||
print(f" ❌ 登录端点 {endpoint} 失败: {str(e)}")
|
||||
except Exception as e:
|
||||
print(f" ❌ 登录测试失败: {str(e)}")
|
||||
|
||||
# 测试API端点
|
||||
try:
|
||||
api_endpoints = [
|
||||
"/classMsg/classMsg/list",
|
||||
"/studentMsg/studentMsg/list",
|
||||
"/api/classMsg/classMsg/list"
|
||||
]
|
||||
for endpoint in api_endpoints:
|
||||
try:
|
||||
response = requests.get(f"{base_url}{endpoint}", timeout=5)
|
||||
print(f" 📊 API端点 {endpoint}: {response.status_code}")
|
||||
if response.text:
|
||||
data = response.json()
|
||||
if data.get('code'):
|
||||
print(f" 响应码: {data.get('code')}, 消息: {data.get('msg', '')}")
|
||||
except Exception as e:
|
||||
print(f" ❌ API端点 {endpoint} 失败: {str(e)}")
|
||||
except Exception as e:
|
||||
print(f" ❌ API测试失败: {str(e)}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
check_system_status()
|
||||
@@ -1,102 +0,0 @@
|
||||
-- ==========================================
|
||||
-- 物业报修管理完整权限配置脚本(修复版)
|
||||
-- 功能:为报修管理模块添加完整的权限配置到sys_menu表
|
||||
-- 时间:2025-06-30
|
||||
-- 说明:解决报修管理接口权限访问问题
|
||||
-- ==========================================
|
||||
|
||||
-- 清理可能存在的旧数据(谨慎使用)
|
||||
-- DELETE FROM sys_role_menu WHERE menu_id BETWEEN 2100 AND 2199;
|
||||
-- DELETE FROM sys_menu WHERE menu_id BETWEEN 2100 AND 2199;
|
||||
|
||||
-- ==========================================
|
||||
-- 第一部分:主菜单 - 物业管理
|
||||
-- ==========================================
|
||||
|
||||
INSERT INTO sys_menu (
|
||||
menu_id, menu_name, parent_id, order_num, path, component,
|
||||
query_param, is_frame, is_cache, menu_type, visible, status,
|
||||
perms, icon, create_by, create_time, update_by, update_time, remark
|
||||
) VALUES (
|
||||
2100, '物业管理', 0, 6, 'pm', NULL,
|
||||
'', 1, 0, 'M', '0', '0',
|
||||
'', 'tool', 1, now(), 1, now(), '物业管理模块'
|
||||
);
|
||||
|
||||
-- ==========================================
|
||||
-- 第二部分:报修管理菜单和权限
|
||||
-- ==========================================
|
||||
|
||||
-- 报修管理主菜单
|
||||
INSERT INTO sys_menu (
|
||||
menu_id, menu_name, parent_id, order_num, path, component,
|
||||
query_param, is_frame, is_cache, menu_type, visible, status,
|
||||
perms, icon, create_by, create_time, update_by, update_time, remark
|
||||
) VALUES (
|
||||
2110, '报修管理', 2100, 1, 'repairOrder', 'pm/repairOrder/index',
|
||||
'', 1, 0, 'C', '0', '0',
|
||||
'pm:repairOrder:list', 'form', 1, now(), 1, now(), '报修工单管理'
|
||||
);
|
||||
|
||||
-- 报修管理功能权限按钮
|
||||
INSERT INTO sys_menu (
|
||||
menu_id, menu_name, parent_id, order_num, path, component,
|
||||
query_param, is_frame, is_cache, menu_type, visible, status,
|
||||
perms, icon, create_by, create_time, update_by, update_time, remark
|
||||
) VALUES
|
||||
-- 基础CRUD权限
|
||||
(2111, '报修查询', 2110, 1, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:query', '#', 1, now(), 1, now(), '查询报修工单详情'),
|
||||
(2112, '报修新增', 2110, 2, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:add', '#', 1, now(), 1, now(), '新增报修工单'),
|
||||
(2113, '报修修改', 2110, 3, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:edit', '#', 1, now(), 1, now(), '修改报修工单'),
|
||||
(2114, '报修删除', 2110, 4, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:remove', '#', 1, now(), 1, now(), '删除报修工单'),
|
||||
(2115, '报修导出', 2110, 5, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:export', '#', 1, now(), 1, now(), '导出报修工单'),
|
||||
|
||||
-- 业务流程权限
|
||||
(2116, '工单分派', 2110, 6, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:assign', '#', 1, now(), 1, now(), '分派维修工单'),
|
||||
(2117, '开始维修', 2110, 7, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:start', '#', 1, now(), 1, now(), '开始维修工单'),
|
||||
(2118, '完成维修', 2110, 8, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:finish', '#', 1, now(), 1, now(), '完成维修工单'),
|
||||
(2119, '取消工单', 2110, 9, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:cancel', '#', 1, now(), 1, now(), '取消维修工单'),
|
||||
|
||||
-- 统计和工具权限
|
||||
(2120, '统计数据', 2110, 10, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:statistics', '#', 1, now(), 1, now(), '获取报修统计数据'),
|
||||
(2121, '生成工单号', 2110, 11, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:generateOrderNo', '#', 1, now(), 1, now(), '生成工单编号');
|
||||
|
||||
-- ==========================================
|
||||
-- 第三部分:维修分类菜单和权限
|
||||
-- ==========================================
|
||||
|
||||
-- 维修分类主菜单
|
||||
INSERT INTO sys_menu (
|
||||
menu_id, menu_name, parent_id, order_num, path, component,
|
||||
query_param, is_frame, is_cache, menu_type, visible, status,
|
||||
perms, icon, create_by, create_time, update_by, update_time, remark
|
||||
) VALUES (
|
||||
2130, '维修分类', 2100, 2, 'repairCategory', 'pm/repairCategory/index',
|
||||
'', 1, 0, 'C', '0', '0',
|
||||
'pm:category:list', 'tree', 1, now(), 1, now(), '维修分类管理'
|
||||
);
|
||||
|
||||
-- 维修分类功能权限按钮
|
||||
INSERT INTO sys_menu (
|
||||
menu_id, menu_name, parent_id, order_num, path, component,
|
||||
query_param, is_frame, is_cache, menu_type, visible, status,
|
||||
perms, icon, create_by, create_time, update_by, update_time, remark
|
||||
) VALUES
|
||||
-- 基础CRUD权限
|
||||
(2131, '分类查询', 2130, 1, '', '', '', 1, 0, 'F', '0', '0', 'pm:category:query', '#', 1, now(), 1, now(), '查询维修分类详情'),
|
||||
(2132, '分类新增', 2130, 2, '', '', '', 1, 0, 'F', '0', '0', 'pm:category:add', '#', 1, now(), 1, now(), '新增维修分类'),
|
||||
(2133, '分类修改', 2130, 3, '', '', '', 1, 0, 'F', '0', '0', 'pm:category:edit', '#', 1, now(), 1, now(), '修改维修分类'),
|
||||
(2134, '分类删除', 2130, 4, '', '', '', 1, 0, 'F', '0', '0', 'pm:category:remove', '#', 1, now(), 1, now(), '删除维修分类'),
|
||||
(2135, '分类导出', 2130, 5, '', '', '', 1, 0, 'F', '0', '0', 'pm:category:export', '#', 1, now(), 1, now(), '导出维修分类'),
|
||||
|
||||
-- 特殊功能权限
|
||||
(2136, '分类树查询', 2130, 6, '', '', '', 1, 0, 'F', '0', '0', 'pm:category:tree', '#', 1, now(), 1, now(), '获取维修分类树形结构'),
|
||||
(2137, '分类选项', 2130, 7, '', '', '', 1, 0, 'F', '0', '0', 'pm:category:options', '#', 1, now(), 1, now(), '获取维修分类下拉选项');
|
||||
|
||||
-- ==========================================
|
||||
-- 第四部分:为admin角色分配权限
|
||||
-- ==========================================
|
||||
|
||||
-- 为admin角色(role_id=1)分配所有物业管理权限
|
||||
INSERT IGNORE INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT 1, menu_id FROM sys_menu WHERE menu_id BETWEEN 2100 AND 2199;
|
||||
@@ -1,161 +0,0 @@
|
||||
-- ==========================================
|
||||
-- 物业报修管理完整权限配置脚本
|
||||
-- 功能:为报修管理模块添加完整的权限配置到sys_menu表
|
||||
-- 时间:2025-06-30
|
||||
-- 说明:解决报修管理接口权限访问问题
|
||||
-- ==========================================
|
||||
|
||||
-- 清理可能存在的旧数据(谨慎使用)
|
||||
-- DELETE FROM sys_role_menu WHERE menu_id BETWEEN 2100 AND 2199;
|
||||
-- DELETE FROM sys_menu WHERE menu_id BETWEEN 2100 AND 2199;
|
||||
|
||||
-- ==========================================
|
||||
-- 第一部分:主菜单 - 物业管理
|
||||
-- ==========================================
|
||||
|
||||
INSERT INTO sys_menu (
|
||||
menu_id, menu_name, parent_id, order_num, path, component,
|
||||
query_param, is_frame, is_cache, menu_type, visible, status,
|
||||
perms, icon, create_by, create_time, update_by, update_time, remark
|
||||
) VALUES (
|
||||
2100, '物业管理', 0, 6, 'pm', NULL,
|
||||
'', 1, 0, 'M', '0', '0',
|
||||
'', 'tool', 1, sysdate(), 1, sysdate(), '物业管理模块'
|
||||
);
|
||||
|
||||
-- ==========================================
|
||||
-- 第二部分:报修管理菜单和权限
|
||||
-- ==========================================
|
||||
|
||||
-- 报修管理主菜单
|
||||
INSERT INTO sys_menu (
|
||||
menu_id, menu_name, parent_id, order_num, path, component,
|
||||
query_param, is_frame, is_cache, menu_type, visible, status,
|
||||
perms, icon, create_by, create_time, update_by, update_time, remark
|
||||
) VALUES (
|
||||
2110, '报修管理', 2100, 1, 'repairOrder', 'pm/repairOrder/index',
|
||||
'', 1, 0, 'C', '0', '0',
|
||||
'pm:repairOrder:list', 'form', 1, sysdate(), 1, sysdate(), '报修工单管理'
|
||||
);
|
||||
|
||||
-- 报修管理功能权限按钮
|
||||
INSERT INTO sys_menu (
|
||||
menu_id, menu_name, parent_id, order_num, path, component,
|
||||
query_param, is_frame, is_cache, menu_type, visible, status,
|
||||
perms, icon, create_by, create_time, update_by, update_time, remark
|
||||
) VALUES
|
||||
-- 基础CRUD权限
|
||||
(2111, '报修查询', 2110, 1, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:query', '#', 1, sysdate(), 1, sysdate(), '查询报修工单详情'),
|
||||
(2112, '报修新增', 2110, 2, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:add', '#', 1, sysdate(), 1, sysdate(), '新增报修工单'),
|
||||
(2113, '报修修改', 2110, 3, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:edit', '#', 1, sysdate(), 1, sysdate(), '修改报修工单'),
|
||||
(2114, '报修删除', 2110, 4, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:remove', '#', 1, sysdate(), 1, sysdate(), '删除报修工单'),
|
||||
(2115, '报修导出', 2110, 5, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:export', '#', 1, sysdate(), 1, sysdate(), '导出报修工单'),
|
||||
|
||||
-- 业务流程权限
|
||||
(2116, '工单分派', 2110, 6, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:assign', '#', 1, sysdate(), 1, sysdate(), '分派维修工单'),
|
||||
(2117, '开始维修', 2110, 7, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:start', '#', 1, sysdate(), 1, sysdate(), '开始维修工单'),
|
||||
(2118, '完成维修', 2110, 8, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:finish', '#', 1, sysdate(), 1, sysdate(), '完成维修工单'),
|
||||
(2119, '取消工单', 2110, 9, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:cancel', '#', 1, sysdate(), 1, sysdate(), '取消维修工单'),
|
||||
|
||||
-- 统计和工具权限
|
||||
(2120, '统计数据', 2110, 10, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:statistics', '#', 1, sysdate(), 1, sysdate(), '获取报修统计数据'),
|
||||
(2121, '生成工单号', 2110, 11, '', '', '', 1, 0, 'F', '0', '0', 'pm:repairOrder:generateOrderNo', '#', 1, sysdate(), 1, sysdate(), '生成工单编号');
|
||||
|
||||
-- ==========================================
|
||||
-- 第三部分:维修分类菜单和权限
|
||||
-- ==========================================
|
||||
|
||||
-- 维修分类主菜单
|
||||
INSERT INTO sys_menu (
|
||||
menu_id, menu_name, parent_id, order_num, path, component,
|
||||
query_param, is_frame, is_cache, menu_type, visible, status,
|
||||
perms, icon, create_by, create_time, update_by, update_time, remark
|
||||
) VALUES (
|
||||
2130, '维修分类', 2100, 2, 'repairCategory', 'pm/repairCategory/index',
|
||||
'', 1, 0, 'C', '0', '0',
|
||||
'pm:category:list', 'tree', 1, sysdate(), 1, sysdate(), '维修分类管理'
|
||||
);
|
||||
|
||||
-- 维修分类功能权限按钮
|
||||
INSERT INTO sys_menu (
|
||||
menu_id, menu_name, parent_id, order_num, path, component,
|
||||
query_param, is_frame, is_cache, menu_type, visible, status,
|
||||
perms, icon, create_by, create_time, update_by, update_time, remark
|
||||
) VALUES
|
||||
-- 基础CRUD权限
|
||||
(2131, '分类查询', 2130, 1, '', '', '', 1, 0, 'F', '0', '0', 'pm:category:query', '#', 1, sysdate(), 1, sysdate(), '查询维修分类详情'),
|
||||
(2132, '分类新增', 2130, 2, '', '', '', 1, 0, 'F', '0', '0', 'pm:category:add', '#', 1, sysdate(), 1, sysdate(), '新增维修分类'),
|
||||
(2133, '分类修改', 2130, 3, '', '', '', 1, 0, 'F', '0', '0', 'pm:category:edit', '#', 1, sysdate(), 1, sysdate(), '修改维修分类'),
|
||||
(2134, '分类删除', 2130, 4, '', '', '', 1, 0, 'F', '0', '0', 'pm:category:remove', '#', 1, sysdate(), 1, sysdate(), '删除维修分类'),
|
||||
(2135, '分类导出', 2130, 5, '', '', '', 1, 0, 'F', '0', '0', 'pm:category:export', '#', 1, sysdate(), 1, sysdate(), '导出维修分类'),
|
||||
|
||||
-- 特殊功能权限
|
||||
(2136, '分类树查询', 2130, 6, '', '', '', 1, 0, 'F', '0', '0', 'pm:category:tree', '#', 1, sysdate(), 1, sysdate(), '获取维修分类树形结构'),
|
||||
(2137, '分类选项', 2130, 7, '', '', '', 1, 0, 'F', '0', '0', 'pm:category:options', '#', 1, sysdate(), 1, sysdate(), '获取维修分类下拉选项');
|
||||
|
||||
-- ==========================================
|
||||
-- 第四部分:为admin角色分配权限
|
||||
-- ==========================================
|
||||
|
||||
-- 为admin角色(role_id=1)分配所有物业管理权限
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT 1, menu_id FROM sys_menu WHERE menu_id BETWEEN 2100 AND 2199
|
||||
ON DUPLICATE KEY UPDATE role_id = role_id; -- 避免重复插入
|
||||
|
||||
-- ==========================================
|
||||
-- 第五部分:验证和清理
|
||||
-- ==========================================
|
||||
|
||||
-- 验证权限配置是否正确
|
||||
SELECT
|
||||
'=== 菜单权限配置验证 ===' AS section,
|
||||
NULL AS menu_id, NULL AS menu_name, NULL AS menu_type, NULL AS perms, NULL AS has_role_permission
|
||||
UNION ALL
|
||||
SELECT
|
||||
CASE
|
||||
WHEN m.menu_type = 'M' THEN '📁 主菜单'
|
||||
WHEN m.menu_type = 'C' THEN '📄 页面菜单'
|
||||
WHEN m.menu_type = 'F' THEN '🔘 功能按钮'
|
||||
ELSE '❓ 未知类型'
|
||||
END AS section,
|
||||
m.menu_id,
|
||||
m.menu_name,
|
||||
m.menu_type,
|
||||
m.perms,
|
||||
CASE WHEN rm.role_id IS NOT NULL THEN '✅ 已分配' ELSE '❌ 未分配' END AS has_role_permission
|
||||
FROM sys_menu m
|
||||
LEFT JOIN sys_role_menu rm ON m.menu_id = rm.menu_id AND rm.role_id = 1
|
||||
WHERE m.menu_id BETWEEN 2100 AND 2199
|
||||
ORDER BY m.menu_id;
|
||||
|
||||
-- 统计权限配置结果
|
||||
SELECT
|
||||
'权限配置统计' AS summary,
|
||||
COUNT(*) AS total_menus,
|
||||
SUM(CASE WHEN menu_type = 'M' THEN 1 ELSE 0 END) AS main_menus,
|
||||
SUM(CASE WHEN menu_type = 'C' THEN 1 ELSE 0 END) AS page_menus,
|
||||
SUM(CASE WHEN menu_type = 'F' THEN 1 ELSE 0 END) AS function_buttons,
|
||||
SUM(CASE WHEN perms IS NOT NULL AND perms != '' THEN 1 ELSE 0 END) AS with_permissions
|
||||
FROM sys_menu
|
||||
WHERE menu_id BETWEEN 2100 AND 2199;
|
||||
|
||||
-- 检查admin角色权限分配
|
||||
SELECT
|
||||
'权限分配统计' AS summary,
|
||||
COUNT(rm.menu_id) AS assigned_permissions,
|
||||
(SELECT COUNT(*) FROM sys_menu WHERE menu_id BETWEEN 2100 AND 2199) AS total_permissions,
|
||||
CONCAT(
|
||||
ROUND(COUNT(rm.menu_id) * 100.0 / (SELECT COUNT(*) FROM sys_menu WHERE menu_id BETWEEN 2100 AND 2199), 2),
|
||||
'%'
|
||||
) AS assignment_rate
|
||||
FROM sys_role_menu rm
|
||||
WHERE rm.role_id = 1 AND rm.menu_id BETWEEN 2100 AND 2199;
|
||||
|
||||
-- 重要提示信息
|
||||
SELECT
|
||||
'🎉 物业报修管理权限配置完成!' AS message,
|
||||
'请执行以下步骤完成配置:' AS next_steps,
|
||||
'1. 重新登录系统刷新权限缓存' AS step1,
|
||||
'2. 修改Controller权限注解' AS step2,
|
||||
'3. 重启后端服务' AS step3,
|
||||
'4. 测试API接口访问' AS step4;
|
||||
@@ -1,257 +0,0 @@
|
||||
"""
|
||||
cooperation经费来源显示问题调试脚本
|
||||
|
||||
问题:修复后出国记录的查看详情中的经费来源显示的还是cooperation
|
||||
|
||||
调试步骤:
|
||||
1. 登录系统获取token
|
||||
2. 获取出国记录列表,找到cooperation经费来源的记录
|
||||
3. 查看记录详情,检查返回的数据结构
|
||||
4. 分析budgetSource和budgetSourceText字段的值
|
||||
5. 确定问题的具体原因
|
||||
|
||||
执行时间: 2025-01-07 22:50:00
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
from typing import Dict, Any, Optional, List
|
||||
|
||||
class CooperationBudgetSourceDebugger:
|
||||
def __init__(self):
|
||||
self.base_url = "http://localhost:8080"
|
||||
self.token = None
|
||||
self.headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'clientid': 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
||||
'referer': 'http://localhost'
|
||||
}
|
||||
|
||||
def login(self) -> bool:
|
||||
"""登录获取token"""
|
||||
try:
|
||||
login_url = f"{self.base_url}/login"
|
||||
login_data = {
|
||||
"username": "admin",
|
||||
"password": "admin123",
|
||||
"code": "1234",
|
||||
"uuid": "test-uuid"
|
||||
}
|
||||
|
||||
response = requests.post(login_url, json=login_data, headers=self.headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get('code') == 200:
|
||||
self.token = result['data']['access_token']
|
||||
self.headers['Authorization'] = f'Bearer {self.token}'
|
||||
print("✅ 登录成功")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ 登录失败: {result.get('msg', '未知错误')}")
|
||||
return False
|
||||
else:
|
||||
print(f"❌ 登录请求失败: HTTP {response.status_code}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 登录异常: {str(e)}")
|
||||
return False
|
||||
|
||||
def get_record_list(self) -> Optional[List[Dict[str, Any]]]:
|
||||
"""获取出国记录列表"""
|
||||
try:
|
||||
url = f"{self.base_url}/rs/overseas/record/list"
|
||||
params = {
|
||||
'pageNum': 1,
|
||||
'pageSize': 20
|
||||
}
|
||||
|
||||
response = requests.get(url, params=params, headers=self.headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get('code') == 200:
|
||||
records = result['data']['rows']
|
||||
print(f"✅ 获取到 {len(records)} 条出国记录")
|
||||
return records
|
||||
else:
|
||||
print(f"❌ 获取记录列表失败: {result.get('msg')}")
|
||||
return None
|
||||
else:
|
||||
print(f"❌ 请求失败: HTTP {response.status_code}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 获取记录列表异常: {str(e)}")
|
||||
return None
|
||||
|
||||
def get_record_detail(self, record_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""获取出国记录详情"""
|
||||
try:
|
||||
url = f"{self.base_url}/rs/overseas/record/{record_id}"
|
||||
|
||||
response = requests.get(url, headers=self.headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
if result.get('code') == 200:
|
||||
record_detail = result['data']
|
||||
print(f"✅ 获取到记录详情: {record_id}")
|
||||
return record_detail
|
||||
else:
|
||||
print(f"❌ 获取记录详情失败: {result.get('msg')}")
|
||||
return None
|
||||
else:
|
||||
print(f"❌ 请求失败: HTTP {response.status_code}")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 获取记录详情异常: {str(e)}")
|
||||
return None
|
||||
|
||||
def debug_cooperation_records(self):
|
||||
"""调试cooperation经费来源记录"""
|
||||
print("🔍 开始调试cooperation经费来源显示问题...")
|
||||
|
||||
# 获取记录列表
|
||||
records = self.get_record_list()
|
||||
if not records:
|
||||
print("⚠️ 没有找到出国记录数据")
|
||||
return
|
||||
|
||||
print("\n📋 分析记录列表中的经费来源:")
|
||||
cooperation_records = []
|
||||
|
||||
for i, record in enumerate(records):
|
||||
record_id = record.get('recordId')
|
||||
budget_source = record.get('budgetSource')
|
||||
budget_source_text = record.get('budgetSourceText')
|
||||
|
||||
print(f" 记录 {i+1} (ID: {record_id}): budgetSource='{budget_source}', budgetSourceText='{budget_source_text}'")
|
||||
|
||||
if budget_source == 'cooperation':
|
||||
cooperation_records.append({
|
||||
'record_id': record_id,
|
||||
'budget_source': budget_source,
|
||||
'budget_source_text': budget_source_text
|
||||
})
|
||||
|
||||
if not cooperation_records:
|
||||
print("\n⚠️ 列表中没有找到经费来源为cooperation的记录")
|
||||
# 检查是否有其他经费来源
|
||||
all_sources = set(record.get('budgetSource') for record in records if record.get('budgetSource'))
|
||||
print(f" 当前存在的经费来源: {list(all_sources)}")
|
||||
return
|
||||
|
||||
print(f"\n🎯 找到 {len(cooperation_records)} 条cooperation经费来源记录")
|
||||
|
||||
# 详细分析每条cooperation记录
|
||||
for coop_record in cooperation_records:
|
||||
record_id = coop_record['record_id']
|
||||
print(f"\n📊 详细分析记录 ID: {record_id}")
|
||||
print(f" 列表中显示 - budgetSource: '{coop_record['budget_source']}'")
|
||||
print(f" 列表中显示 - budgetSourceText: '{coop_record['budget_source_text']}'")
|
||||
|
||||
# 获取详情页面数据
|
||||
record_detail = self.get_record_detail(str(record_id))
|
||||
if record_detail:
|
||||
detail_budget_source = record_detail.get('budgetSource')
|
||||
detail_budget_source_text = record_detail.get('budgetSourceText')
|
||||
|
||||
print(f" 详情页显示 - budgetSource: '{detail_budget_source}'")
|
||||
print(f" 详情页显示 - budgetSourceText: '{detail_budget_source_text}'")
|
||||
|
||||
# 分析问题
|
||||
print(f" 🔍 问题分析:")
|
||||
if detail_budget_source_text == 'cooperation':
|
||||
print(f" ❌ 问题确认: 详情页budgetSourceText显示为'{detail_budget_source_text}'")
|
||||
print(f" 🔧 应该显示: '合作单位'")
|
||||
|
||||
# 检查可能的原因
|
||||
if detail_budget_source == 'cooperation':
|
||||
print(f" 💡 分析: budgetSource值正确为'cooperation'")
|
||||
print(f" 💡 问题: getBudgetSourceText()方法可能未生效")
|
||||
else:
|
||||
print(f" 💡 问题: budgetSource值异常为'{detail_budget_source}'")
|
||||
|
||||
elif detail_budget_source_text == '合作单位':
|
||||
print(f" ✅ 正常: 详情页budgetSourceText正确显示为'{detail_budget_source_text}'")
|
||||
else:
|
||||
print(f" ⚠️ 异常: 详情页budgetSourceText显示为'{detail_budget_source_text}'")
|
||||
|
||||
# 显示完整的详情数据结构(用于调试)
|
||||
print(f" 📋 完整详情数据结构:")
|
||||
self.print_relevant_fields(record_detail)
|
||||
else:
|
||||
print(f" ❌ 无法获取记录详情")
|
||||
|
||||
def print_relevant_fields(self, record: Dict[str, Any]):
|
||||
"""打印相关字段用于调试"""
|
||||
relevant_fields = [
|
||||
'recordId', 'budgetSource', 'budgetSourceText',
|
||||
'purpose', 'purposeText', 'completionStatus', 'completionStatusText'
|
||||
]
|
||||
|
||||
for field in relevant_fields:
|
||||
value = record.get(field)
|
||||
print(f" {field}: '{value}'")
|
||||
|
||||
def test_backend_restart_needed(self):
|
||||
"""检查是否需要重启后端服务"""
|
||||
print("\n🔄 检查后端服务状态:")
|
||||
print(" 💡 如果代码修改后问题仍然存在,可能需要:")
|
||||
print(" 1. 重启后端服务 (Spring Boot)")
|
||||
print(" 2. 清除应用缓存")
|
||||
print(" 3. 确认代码编译生效")
|
||||
|
||||
try:
|
||||
# 简单的健康检查
|
||||
health_url = f"{self.base_url}/actuator/health"
|
||||
response = requests.get(health_url, headers=self.headers, timeout=5)
|
||||
if response.status_code == 200:
|
||||
print(" ✅ 后端服务运行正常")
|
||||
else:
|
||||
print(" ⚠️ 后端服务状态异常")
|
||||
except:
|
||||
print(" ⚠️ 无法访问后端健康检查接口")
|
||||
|
||||
def suggest_solutions(self):
|
||||
"""建议解决方案"""
|
||||
print("\n💡 可能的解决方案:")
|
||||
print(" 1. 🔄 重启后端服务:")
|
||||
print(" cd backend && mvn spring-boot:run")
|
||||
print(" 2. 🔧 检查代码编译:")
|
||||
print(" 确认RsOverseasRecordVo.java的修改已编译生效")
|
||||
print(" 3. 🧹 清除缓存:")
|
||||
print(" 检查是否有Redis或其他缓存影响")
|
||||
print(" 4. 📊 检查数据库:")
|
||||
print(" 确认数据库中budgetSource字段的实际值")
|
||||
print(" 5. 🔍 调试模式:")
|
||||
print(" 在getBudgetSourceText()方法中添加日志输出")
|
||||
|
||||
def run_debug(self):
|
||||
"""运行完整调试流程"""
|
||||
print("🚀 开始cooperation经费来源显示问题调试")
|
||||
print("="*60)
|
||||
|
||||
# 登录
|
||||
if not self.login():
|
||||
return
|
||||
|
||||
# 调试cooperation记录
|
||||
self.debug_cooperation_records()
|
||||
|
||||
# 检查后端服务状态
|
||||
self.test_backend_restart_needed()
|
||||
|
||||
# 建议解决方案
|
||||
self.suggest_solutions()
|
||||
|
||||
print("="*60)
|
||||
print("🎯 调试完成")
|
||||
print("💡 请根据上述分析结果采取相应的解决措施")
|
||||
|
||||
if __name__ == "__main__":
|
||||
debugger = CooperationBudgetSourceDebugger()
|
||||
debugger.run_debug()
|
||||
@@ -1,66 +0,0 @@
|
||||
-- ==========================================
|
||||
-- 维修角色创建和用户分配脚本
|
||||
-- 功能:创建维修人员和物业维修管理员角色,并为总务处用户分配角色
|
||||
-- 时间:2025-06-30 13:45:00
|
||||
-- 说明:使用UPSERT逻辑,支持重复执行
|
||||
-- ==========================================
|
||||
|
||||
-- 第一步:创建维修人员角色
|
||||
INSERT INTO sys_role (
|
||||
role_id, role_name, role_key, role_sort, data_scope,
|
||||
menu_check_strictly, dept_check_strictly, status,
|
||||
del_flag, create_by, create_time, update_by, update_time, remark
|
||||
)
|
||||
SELECT
|
||||
5, '维修人员', 'maintenance_worker', 5, '5',
|
||||
1, 1, '0',
|
||||
'0', 1, now(), 1, now(), '负责具体维修工作的人员,可以查看和处理分配给自己的维修工单'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_role WHERE role_id = 5);
|
||||
|
||||
-- 第二步:创建物业维修管理员角色
|
||||
INSERT INTO sys_role (
|
||||
role_id, role_name, role_key, role_sort, data_scope,
|
||||
menu_check_strictly, dept_check_strictly, status,
|
||||
del_flag, create_by, create_time, update_by, update_time, remark
|
||||
)
|
||||
SELECT
|
||||
6, '物业维修管理员', 'property_maintenance_admin', 6, '3',
|
||||
1, 1, '0',
|
||||
'0', 1, now(), 1, now(), '负责物业维修管理工作,可以管理维修工单、分配维修任务、查看维修统计'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_role WHERE role_id = 6);
|
||||
|
||||
-- 第三步:为总务处用户分配维修人员角色
|
||||
-- 选择用户:zwc005(陆雄) 和 zwc007(邹凯)
|
||||
INSERT IGNORE INTO sys_user_role (user_id, role_id) VALUES
|
||||
(2125, 5), -- zwc005(陆雄) -> 维修人员
|
||||
(2127, 5); -- zwc007(邹凯) -> 维修人员
|
||||
|
||||
-- 第四步:为总务处用户分配物业维修管理员角色
|
||||
-- 选择用户:zwc003(舒刚) 和 zwc004(晏茹)
|
||||
INSERT IGNORE INTO sys_user_role (user_id, role_id) VALUES
|
||||
(2123, 6), -- zwc003(舒刚) -> 物业维修管理员
|
||||
(2124, 6); -- zwc004(晏茹) -> 物业维修管理员
|
||||
|
||||
-- 第五步:验证创建结果
|
||||
SELECT '角色创建验证' as message,
|
||||
(SELECT COUNT(*) FROM sys_role WHERE role_id IN (5, 6)) as created_roles,
|
||||
(SELECT COUNT(*) FROM sys_user_role WHERE role_id IN (5, 6)) as assigned_users;
|
||||
|
||||
-- 详细验证信息
|
||||
SELECT
|
||||
'维修角色分配验证' as verification_type,
|
||||
r.role_name as '角色名称',
|
||||
u.user_name as '用户名',
|
||||
u.nick_name as '昵称',
|
||||
d.dept_name as '部门'
|
||||
FROM sys_user_role ur
|
||||
JOIN sys_role r ON ur.role_id = r.role_id
|
||||
JOIN sys_user u ON ur.user_id = u.user_id
|
||||
JOIN sys_dept d ON u.dept_id = d.dept_id
|
||||
WHERE ur.role_id IN (5, 6)
|
||||
ORDER BY r.role_id, u.user_id;
|
||||
|
||||
-- 第六步:为这两个角色分配基础权限(可根据需要调整)
|
||||
-- 注意:这里暂时不分配具体权限,后续可以通过管理界面或额外脚本配置
|
||||
SELECT '权限配置提醒' as message,
|
||||
'新创建的角色需要在系统管理->角色管理中配置具体的菜单权限' as reminder;
|
||||
@@ -1,66 +0,0 @@
|
||||
-- ==========================================
|
||||
-- 物业报修管理测试数据脚本
|
||||
-- 功能:插入测试数据到数据库
|
||||
-- 时间:2025-06-30
|
||||
-- 说明:为测试报修管理功能创建必要的数据
|
||||
-- ==========================================
|
||||
|
||||
-- 1. 插入维修分类数据(如果不存在)
|
||||
INSERT IGNORE INTO `pm_repair_category` (
|
||||
`id`, `tenant_id`, `category_name`, `category_code`, `category_description`,
|
||||
`parent_id`, `level_depth`, `required_skills`, `skill_level`,
|
||||
`estimated_duration`, `icon_url`, `color_code`, `sort_order`, `status`,
|
||||
`create_time`, `create_by`
|
||||
) VALUES
|
||||
(1, '000000', '电路维修', 'ELECTRIC', '电路、照明、开关、插座等电气设备维修', 0, 1, '电工技能', '2', 60, '/icons/electric.svg', '#ff6b35', 1, '1', NOW(), 1),
|
||||
(2, '000000', '水暖维修', 'PLUMBING', '水管、暖气、卫生间、厨房设备维修', 0, 1, '水暖技能', '2', 90, '/icons/plumbing.svg', '#1890ff', 2, '1', NOW(), 1),
|
||||
(3, '000000', '土建维修', 'CONSTRUCTION', '墙面、地面、天花板、门窗等土建维修', 0, 1, '泥工技能,木工技能', '2', 120, '/icons/construction.svg', '#52c41a', 3, '1', NOW(), 1),
|
||||
(4, '000000', '设备维修', 'EQUIPMENT', '空调、电梯、监控等设备维修', 0, 1, '设备维修技能', '3', 90, '/icons/equipment.svg', '#722ed1', 4, '1', NOW(), 1),
|
||||
(5, '000000', '园林维修', 'GARDEN', '绿化、草坪、树木修剪等园林维护', 0, 1, '园林技能', '1', 45, '/icons/garden.svg', '#13c2c2', 5, '1', NOW(), 1),
|
||||
(6, '000000', '清洁维护', 'CLEANING', '日常清洁、垃圾处理、环境维护', 0, 1, '清洁技能', '1', 30, '/icons/cleaning.svg', '#fa8c16', 6, '1', NOW(), 1);
|
||||
|
||||
-- 2. 插入测试报修工单数据
|
||||
INSERT IGNORE INTO `pm_repair_order` (
|
||||
`id`, `tenant_id`, `order_no`, `title`, `description`, `repair_location`,
|
||||
`category_id`, `priority_level`, `expected_finish_time`,
|
||||
`reporter_id`, `reporter_name`, `reporter_phone`, `reporter_dept`,
|
||||
`order_status`, `create_time`, `create_by`
|
||||
) VALUES
|
||||
(1001, '000000', 'RO20250630001', '办公室空调不制冷', '302办公室空调制冷效果差,温度显示异常,需要检查维修', '行政楼3楼302办公室', 4, 2, DATE_ADD(NOW(), INTERVAL 1 DAY), 1, '张三', '13900001001', '行政办公室', '1', NOW(), 1),
|
||||
(1002, '000000', 'RO20250630002', '教室投影仪故障', '多媒体教室投影仪无法正常显示,有色彩偏差', '教学楼A座201教室', 4, 3, DATE_ADD(NOW(), INTERVAL 2 DAY), 1, '李四', '13900001002', '教务处', '1', NOW(), 1),
|
||||
(1003, '000000', 'RO20250630003', '宿舍楼水管漏水', '学生宿舍3楼卫生间水管接头漏水,影响正常使用', '学生宿舍楼3楼卫生间', 2, 1, DATE_ADD(NOW(), INTERVAL 3 HOUR), 1, '王五', '13900001003', '后勤处', '2', NOW(), 1),
|
||||
(1004, '000000', 'RO20250630004', '图书馆照明灯具损坏', '图书馆二楼阅览区多个LED灯管损坏,照明不足', '图书馆二楼阅览区', 1, 3, DATE_ADD(NOW(), INTERVAL 1 WEEK), 1, '赵六', '13900001004', '图书馆', '1', NOW(), 1),
|
||||
(1005, '000000', 'RO20250630005', '食堂地面瓷砖松动', '第二食堂入口处地面瓷砖松动,存在安全隐患', '第二食堂入口', 3, 2, DATE_ADD(NOW(), INTERVAL 2 DAY), 1, '孙七', '13900001005', '餐饮服务中心', '3', NOW(), 1);
|
||||
|
||||
-- 3. 为已分派的工单添加分派信息
|
||||
UPDATE `pm_repair_order` SET
|
||||
`assign_time` = NOW(),
|
||||
`assignee_id` = 2,
|
||||
`assignee_name` = '维修工A',
|
||||
`assign_remark` = '紧急维修,请尽快处理'
|
||||
WHERE `id` = 1003;
|
||||
|
||||
-- 4. 为进行中的工单添加开始时间
|
||||
UPDATE `pm_repair_order` SET
|
||||
`start_time` = DATE_SUB(NOW(), INTERVAL 30 MINUTE)
|
||||
WHERE `id` = 1005;
|
||||
|
||||
-- 5. 验证插入的数据
|
||||
SELECT
|
||||
'数据插入完成' AS message,
|
||||
(SELECT COUNT(*) FROM pm_repair_category WHERE tenant_id = '000000') AS category_count,
|
||||
(SELECT COUNT(*) FROM pm_repair_order WHERE tenant_id = '000000' AND id >= 1001) AS order_count;
|
||||
|
||||
-- 6. 显示插入的报修工单数据
|
||||
SELECT
|
||||
id,
|
||||
order_no,
|
||||
title,
|
||||
repair_location,
|
||||
priority_level,
|
||||
order_status,
|
||||
reporter_name,
|
||||
create_time
|
||||
FROM pm_repair_order
|
||||
WHERE tenant_id = '000000' AND id >= 1001
|
||||
ORDER BY create_time DESC;
|
||||
@@ -1,119 +0,0 @@
|
||||
-- ==========================================
|
||||
-- 为每个部门新增10个用户脚本
|
||||
-- 创建时间:2025-06-30 13:30:00
|
||||
-- 说明:为系统中的每个部门添加10个测试用户
|
||||
-- ==========================================
|
||||
|
||||
-- 智能控制学院 (dept_id: 103) - 10个用户
|
||||
INSERT INTO `sys_user` (`user_id`, `tenant_id`, `dept_id`, `user_name`, `nick_name`, `user_type`, `email`, `phonenumber`, `sex`, `avatar`, `password`, `status`, `del_flag`, `login_ip`, `login_date`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES
|
||||
(2001, '000000', 103, 'zkxy001', '智控学院张三', 'sys_user', 'zkxy001@hzdyjs.edu.cn', '13800138001', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '智能控制学院教师'),
|
||||
(2002, '000000', 103, 'zkxy002', '智控学院李四', 'sys_user', 'zkxy002@hzdyjs.edu.cn', '13800138002', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '智能控制学院教师'),
|
||||
(2003, '000000', 103, 'zkxy003', '智控学院王五', 'sys_user', 'zkxy003@hzdyjs.edu.cn', '13800138003', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '智能控制学院教师'),
|
||||
(2004, '000000', 103, 'zkxy004', '智控学院赵六', 'sys_user', 'zkxy004@hzdyjs.edu.cn', '13800138004', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '智能控制学院教师'),
|
||||
(2005, '000000', 103, 'zkxy005', '智控学院孙七', 'sys_user', 'zkxy005@hzdyjs.edu.cn', '13800138005', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '智能控制学院教师'),
|
||||
(2006, '000000', 103, 'zkxy006', '智控学院周八', 'sys_user', 'zkxy006@hzdyjs.edu.cn', '13800138006', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '智能控制学院教师'),
|
||||
(2007, '000000', 103, 'zkxy007', '智控学院吴九', 'sys_user', 'zkxy007@hzdyjs.edu.cn', '13800138007', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '智能控制学院教师'),
|
||||
(2008, '000000', 103, 'zkxy008', '智控学院郑十', 'sys_user', 'zkxy008@hzdyjs.edu.cn', '13800138008', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '智能控制学院教师'),
|
||||
(2009, '000000', 103, 'zkxy009', '智控学院王小明', 'sys_user', 'zkxy009@hzdyjs.edu.cn', '13800138009', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '智能控制学院教师'),
|
||||
(2010, '000000', 103, 'zkxy010', '智控学院李小红', 'sys_user', 'zkxy010@hzdyjs.edu.cn', '13800138010', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '智能控制学院教师');
|
||||
|
||||
-- 智能制造学院 (dept_id: 104) - 10个用户
|
||||
INSERT INTO `sys_user` (`user_id`, `tenant_id`, `dept_id`, `user_name`, `nick_name`, `user_type`, `email`, `phonenumber`, `sex`, `avatar`, `password`, `status`, `del_flag`, `login_ip`, `login_date`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES
|
||||
(2011, '000000', 104, 'znzz001', '智造学院陈刚', 'sys_user', 'znzz001@hzdyjs.edu.cn', '13800138011', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '智能制造学院教师'),
|
||||
(2012, '000000', 104, 'znzz002', '智造学院刘敏', 'sys_user', 'znzz002@hzdyjs.edu.cn', '13800138012', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '智能制造学院教师'),
|
||||
(2013, '000000', 104, 'znzz003', '智造学院黄伟', 'sys_user', 'znzz003@hzdyjs.edu.cn', '13800138013', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '智能制造学院教师'),
|
||||
(2014, '000000', 104, 'znzz004', '智造学院张娟', 'sys_user', 'znzz004@hzdyjs.edu.cn', '13800138014', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '智能制造学院教师'),
|
||||
(2015, '000000', 104, 'znzz005', '智造学院李强', 'sys_user', 'znzz005@hzdyjs.edu.cn', '13800138015', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '智能制造学院教师'),
|
||||
(2016, '000000', 104, 'znzz006', '智造学院王丽', 'sys_user', 'znzz006@hzdyjs.edu.cn', '13800138016', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '智能制造学院教师'),
|
||||
(2017, '000000', 104, 'znzz007', '智造学院赵军', 'sys_user', 'znzz007@hzdyjs.edu.cn', '13800138017', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '智能制造学院教师'),
|
||||
(2018, '000000', 104, 'znzz008', '智造学院孙燕', 'sys_user', 'znzz008@hzdyjs.edu.cn', '13800138018', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '智能制造学院教师'),
|
||||
(2019, '000000', 104, 'znzz009', '智造学院周涛', 'sys_user', 'znzz009@hzdyjs.edu.cn', '13800138019', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '智能制造学院教师'),
|
||||
(2020, '000000', 104, 'znzz010', '智造学院吴静', 'sys_user', 'znzz010@hzdyjs.edu.cn', '13800138020', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '智能制造学院教师');
|
||||
|
||||
-- 医药健康学院 (dept_id: 105) - 10个用户
|
||||
INSERT INTO `sys_user` (`user_id`, `tenant_id`, `dept_id`, `user_name`, `nick_name`, `user_type`, `email`, `phonenumber`, `sex`, `avatar`, `password`, `status`, `del_flag`, `login_ip`, `login_date`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES
|
||||
(2021, '000000', 105, 'yyjk001', '医药学院郑华', 'sys_user', 'yyjk001@hzdyjs.edu.cn', '13800138021', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '医药健康学院教师'),
|
||||
(2022, '000000', 105, 'yyjk002', '医药学院冯芳', 'sys_user', 'yyjk002@hzdyjs.edu.cn', '13800138022', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '医药健康学院教师'),
|
||||
(2023, '000000', 105, 'yyjk003', '医药学院卫峰', 'sys_user', 'yyjk003@hzdyjs.edu.cn', '13800138023', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '医药健康学院教师'),
|
||||
(2024, '000000', 105, 'yyjk004', '医药学院褚宁', 'sys_user', 'yyjk004@hzdyjs.edu.cn', '13800138024', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '医药健康学院教师'),
|
||||
(2025, '000000', 105, 'yyjk005', '医药学院蒋凯', 'sys_user', 'yyjk005@hzdyjs.edu.cn', '13800138025', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '医药健康学院教师'),
|
||||
(2026, '000000', 105, 'yyjk006', '医药学院蒋雪', 'sys_user', 'yyjk006@hzdyjs.edu.cn', '13800138026', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '医药健康学院教师'),
|
||||
(2027, '000000', 105, 'yyjk007', '医药学院韩彬', 'sys_user', 'yyjk007@hzdyjs.edu.cn', '13800138027', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '医药健康学院教师'),
|
||||
(2028, '000000', 105, 'yyjk008', '医药学院朱琴', 'sys_user', 'yyjk008@hzdyjs.edu.cn', '13800138028', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '医药健康学院教师'),
|
||||
(2029, '000000', 105, 'yyjk009', '医药学院秦亮', 'sys_user', 'yyjk009@hzdyjs.edu.cn', '13800138029', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '医药健康学院教师'),
|
||||
(2030, '000000', 105, 'yyjk010', '医药学院尤娜', 'sys_user', 'yyjk010@hzdyjs.edu.cn', '13800138030', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '医药健康学院教师');
|
||||
|
||||
-- 餐饮休闲学院 (dept_id: 106) - 10个用户
|
||||
INSERT INTO `sys_user` (`user_id`, `tenant_id`, `dept_id`, `user_name`, `nick_name`, `user_type`, `email`, `phonenumber`, `sex`, `avatar`, `password`, `status`, `del_flag`, `login_ip`, `login_date`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES
|
||||
(2031, '000000', 106, 'cyxx001', '餐饮学院许冰', 'sys_user', 'cyxx001@hzdyjs.edu.cn', '13800138031', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '餐饮休闲学院教师'),
|
||||
(2032, '000000', 106, 'cyxx002', '餐饮学院葛玲', 'sys_user', 'cyxx002@hzdyjs.edu.cn', '13800138032', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '餐饮休闲学院教师'),
|
||||
(2033, '000000', 106, 'cyxx003', '餐饮学院戴锋', 'sys_user', 'cyxx003@hzdyjs.edu.cn', '13800138033', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '餐饮休闲学院教师'),
|
||||
(2034, '000000', 106, 'cyxx004', '餐饮学院薛婷', 'sys_user', 'cyxx004@hzdyjs.edu.cn', '13800138034', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '餐饮休闲学院教师'),
|
||||
(2035, '000000', 106, 'cyxx005', '餐饮学院谭勇', 'sys_user', 'cyxx005@hzdyjs.edu.cn', '13800138035', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '餐饮休闲学院教师'),
|
||||
(2036, '000000', 106, 'cyxx006', '餐饮学院吕萍', 'sys_user', 'cyxx006@hzdyjs.edu.cn', '13800138036', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '餐饮休闲学院教师'),
|
||||
(2037, '000000', 106, 'cyxx007', '餐饮学院范超', 'sys_user', 'cyxx007@hzdyjs.edu.cn', '13800138037', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '餐饮休闲学院教师'),
|
||||
(2038, '000000', 106, 'cyxx008', '餐饮学院史瑶', 'sys_user', 'cyxx008@hzdyjs.edu.cn', '13800138038', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '餐饮休闲学院教师'),
|
||||
(2039, '000000', 106, 'cyxx009', '餐饮学院伍龙', 'sys_user', 'cyxx009@hzdyjs.edu.cn', '13800138039', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '餐饮休闲学院教师'),
|
||||
(2040, '000000', 106, 'cyxx010', '餐饮学院叶惠', 'sys_user', 'cyxx010@hzdyjs.edu.cn', '13800138040', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '餐饮休闲学院教师');
|
||||
|
||||
-- 数字信息学院 (dept_id: 107) - 10个用户
|
||||
INSERT INTO `sys_user` (`user_id`, `tenant_id`, `dept_id`, `user_name`, `nick_name`, `user_type`, `email`, `phonenumber`, `sex`, `avatar`, `password`, `status`, `del_flag`, `login_ip`, `login_date`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES
|
||||
(2041, '000000', 107, 'szxx001', '数信学院廖洋', 'sys_user', 'szxx001@hzdyjs.edu.cn', '13800138041', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '数字信息学院教师'),
|
||||
(2042, '000000', 107, 'szxx002', '数信学院马悦', 'sys_user', 'szxx002@hzdyjs.edu.cn', '13800138042', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '数字信息学院教师'),
|
||||
(2043, '000000', 107, 'szxx003', '数信学院常杰', 'sys_user', 'szxx003@hzdyjs.edu.cn', '13800138043', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '数字信息学院教师'),
|
||||
(2044, '000000', 107, 'szxx004', '数信学院乐希', 'sys_user', 'szxx004@hzdyjs.edu.cn', '13800138044', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '数字信息学院教师'),
|
||||
(2045, '000000', 107, 'szxx005', '数信学院金浩', 'sys_user', 'szxx005@hzdyjs.edu.cn', '13800138045', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '数字信息学院教师'),
|
||||
(2046, '000000', 107, 'szxx006', '数信学院夏蕾', 'sys_user', 'szxx006@hzdyjs.edu.cn', '13800138046', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '数字信息学院教师'),
|
||||
(2047, '000000', 107, 'szxx007', '数信学院侯斌', 'sys_user', 'szxx007@hzdyjs.edu.cn', '13800138047', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '数字信息学院教师'),
|
||||
(2048, '000000', 107, 'szxx008', '数信学院潘莎', 'sys_user', 'szxx008@hzdyjs.edu.cn', '13800138048', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '数字信息学院教师'),
|
||||
(2049, '000000', 107, 'szxx009', '数信学院万兵', 'sys_user', 'szxx009@hzdyjs.edu.cn', '13800138049', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '数字信息学院教师'),
|
||||
(2050, '000000', 107, 'szxx010', '数信学院梅曼', 'sys_user', 'szxx010@hzdyjs.edu.cn', '13800138050', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '数字信息学院教师');
|
||||
|
||||
-- 院领导 (dept_id: 108) - 10个用户
|
||||
INSERT INTO `sys_user` (`user_id`, `tenant_id`, `dept_id`, `user_name`, `nick_name`, `user_type`, `email`, `phonenumber`, `sex`, `avatar`, `password`, `status`, `del_flag`, `login_ip`, `login_date`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES
|
||||
(2051, '000000', 108, 'yuanld001', '院长王建国', 'sys_user', 'yuanld001@hzdyjs.edu.cn', '13800138051', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '院长'),
|
||||
(2052, '000000', 108, 'yuanld002', '副院长李红梅', 'sys_user', 'yuanld002@hzdyjs.edu.cn', '13800138052', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '副院长'),
|
||||
(2053, '000000', 108, 'yuanld003', '副院长张志强', 'sys_user', 'yuanld003@hzdyjs.edu.cn', '13800138053', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '副院长'),
|
||||
(2054, '000000', 108, 'yuanld004', '副院长赵文静', 'sys_user', 'yuanld004@hzdyjs.edu.cn', '13800138054', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '副院长'),
|
||||
(2055, '000000', 108, 'yuanld005', '纪委书记孙建华', 'sys_user', 'yuanld005@hzdyjs.edu.cn', '13800138055', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '纪委书记'),
|
||||
(2056, '000000', 108, 'yuanld006', '工会主席周雅丽', 'sys_user', 'yuanld006@hzdyjs.edu.cn', '13800138056', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '工会主席'),
|
||||
(2057, '000000', 108, 'yuanld007', '顾问吴德成', 'sys_user', 'yuanld007@hzdyjs.edu.cn', '13800138057', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '顾问'),
|
||||
(2058, '000000', 108, 'yuanld008', '顾问郑爱华', 'sys_user', 'yuanld008@hzdyjs.edu.cn', '13800138058', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '顾问'),
|
||||
(2059, '000000', 108, 'yuanld009', '助理冯小明', 'sys_user', 'yuanld009@hzdyjs.edu.cn', '13800138059', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '院长助理'),
|
||||
(2060, '000000', 108, 'yuanld010', '助理卫小娟', 'sys_user', 'yuanld010@hzdyjs.edu.cn', '13800138060', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '院长助理');
|
||||
|
||||
-- 办公室 (dept_id: 109) - 10个用户
|
||||
INSERT INTO `sys_user` (`user_id`, `tenant_id`, `dept_id`, `user_name`, `nick_name`, `user_type`, `email`, `phonenumber`, `sex`, `avatar`, `password`, `status`, `del_flag`, `login_ip`, `login_date`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES
|
||||
(2061, '000000', 109, 'bangong001', '办公室主任褚宁华', 'sys_user', 'bangong001@hzdyjs.edu.cn', '13800138061', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '办公室主任'),
|
||||
(2062, '000000', 109, 'bangong002', '办公室蒋小雪', 'sys_user', 'bangong002@hzdyjs.edu.cn', '13800138062', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '办公室工作人员'),
|
||||
(2063, '000000', 109, 'bangong003', '办公室韩小彬', 'sys_user', 'bangong003@hzdyjs.edu.cn', '13800138063', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '办公室工作人员'),
|
||||
(2064, '000000', 109, 'bangong004', '办公室朱小琴', 'sys_user', 'bangong004@hzdyjs.edu.cn', '13800138064', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '办公室工作人员'),
|
||||
(2065, '000000', 109, 'bangong005', '办公室秦小亮', 'sys_user', 'bangong005@hzdyjs.edu.cn', '13800138065', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '办公室工作人员'),
|
||||
(2066, '000000', 109, 'bangong006', '办公室尤小娜', 'sys_user', 'bangong006@hzdyjs.edu.cn', '13800138066', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '办公室工作人员'),
|
||||
(2067, '000000', 109, 'bangong007', '办公室许小冰', 'sys_user', 'bangong007@hzdyjs.edu.cn', '13800138067', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '办公室工作人员'),
|
||||
(2068, '000000', 109, 'bangong008', '办公室葛小玲', 'sys_user', 'bangong008@hzdyjs.edu.cn', '13800138068', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '办公室工作人员'),
|
||||
(2069, '000000', 109, 'bangong009', '办公室戴小锋', 'sys_user', 'bangong009@hzdyjs.edu.cn', '13800138069', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '办公室工作人员'),
|
||||
(2070, '000000', 109, 'bangong010', '办公室薛小婷', 'sys_user', 'bangong010@hzdyjs.edu.cn', '13800138070', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '办公室工作人员');
|
||||
|
||||
-- 为所有新增用户分配基础角色(role_id: 3 - 普通用户角色)
|
||||
INSERT INTO `sys_user_role` (`user_id`, `role_id`) VALUES
|
||||
(2001, 3), (2002, 3), (2003, 3), (2004, 3), (2005, 3), (2006, 3), (2007, 3), (2008, 3), (2009, 3), (2010, 3),
|
||||
(2011, 3), (2012, 3), (2013, 3), (2014, 3), (2015, 3), (2016, 3), (2017, 3), (2018, 3), (2019, 3), (2020, 3),
|
||||
(2021, 3), (2022, 3), (2023, 3), (2024, 3), (2025, 3), (2026, 3), (2027, 3), (2028, 3), (2029, 3), (2030, 3),
|
||||
(2031, 3), (2032, 3), (2033, 3), (2034, 3), (2035, 3), (2036, 3), (2037, 3), (2038, 3), (2039, 3), (2040, 3),
|
||||
(2041, 3), (2042, 3), (2043, 3), (2044, 3), (2045, 3), (2046, 3), (2047, 3), (2048, 3), (2049, 3), (2050, 3),
|
||||
(2051, 3), (2052, 3), (2053, 3), (2054, 3), (2055, 3), (2056, 3), (2057, 3), (2058, 3), (2059, 3), (2060, 3),
|
||||
(2061, 3), (2062, 3), (2063, 3), (2064, 3), (2065, 3), (2066, 3), (2067, 3), (2068, 3), (2069, 3), (2070, 3);
|
||||
|
||||
-- 为管理岗位用户分配额外权限(role_id: 4 - 学院管理员)
|
||||
INSERT INTO `sys_user_role` (`user_id`, `role_id`) VALUES
|
||||
(2051, 1), -- 院长分配超级管理员权限
|
||||
(2052, 4), (2053, 4), (2054, 4), -- 副院长分配学院管理员权限
|
||||
(2055, 4), (2056, 4), -- 纪委书记和工会主席分配学院管理员权限
|
||||
(2061, 4); -- 办公室主任分配学院管理员权限
|
||||
|
||||
-- 记录执行结果
|
||||
SELECT
|
||||
'用户创建完成' AS message,
|
||||
(SELECT COUNT(*) FROM sys_user WHERE user_id >= 2001 AND user_id <= 2070) AS created_users,
|
||||
(SELECT COUNT(*) FROM sys_user_role WHERE user_id >= 2001 AND user_id <= 2070) AS assigned_roles;
|
||||
@@ -1,109 +0,0 @@
|
||||
-- ==========================================
|
||||
-- 为剩余部门新增10个用户脚本 (补充部分)
|
||||
-- 创建时间:2025-06-30 13:35:00
|
||||
-- 说明:为人文学院、生态园林学院和各行政部门新增用户
|
||||
-- ==========================================
|
||||
|
||||
-- 人文学院 (dept_id: 1938483954134245378) - 10个用户
|
||||
INSERT INTO `sys_user` (`user_id`, `tenant_id`, `dept_id`, `user_name`, `nick_name`, `user_type`, `email`, `phonenumber`, `sex`, `avatar`, `password`, `status`, `del_flag`, `login_ip`, `login_date`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES
|
||||
(2071, '000000', 1938483954134245378, 'rwxy001', '人文学院谭老师', 'sys_user', 'rwxy001@hzdyjs.edu.cn', '13800138071', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '人文学院教师'),
|
||||
(2072, '000000', 1938483954134245378, 'rwxy002', '人文学院吕老师', 'sys_user', 'rwxy002@hzdyjs.edu.cn', '13800138072', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '人文学院教师'),
|
||||
(2073, '000000', 1938483954134245378, 'rwxy003', '人文学院范老师', 'sys_user', 'rwxy003@hzdyjs.edu.cn', '13800138073', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '人文学院教师'),
|
||||
(2074, '000000', 1938483954134245378, 'rwxy004', '人文学院史老师', 'sys_user', 'rwxy004@hzdyjs.edu.cn', '13800138074', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '人文学院教师'),
|
||||
(2075, '000000', 1938483954134245378, 'rwxy005', '人文学院伍老师', 'sys_user', 'rwxy005@hzdyjs.edu.cn', '13800138075', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '人文学院教师'),
|
||||
(2076, '000000', 1938483954134245378, 'rwxy006', '人文学院叶老师', 'sys_user', 'rwxy006@hzdyjs.edu.cn', '13800138076', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '人文学院教师'),
|
||||
(2077, '000000', 1938483954134245378, 'rwxy007', '人文学院廖老师', 'sys_user', 'rwxy007@hzdyjs.edu.cn', '13800138077', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '人文学院教师'),
|
||||
(2078, '000000', 1938483954134245378, 'rwxy008', '人文学院马老师', 'sys_user', 'rwxy008@hzdyjs.edu.cn', '13800138078', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '人文学院教师'),
|
||||
(2079, '000000', 1938483954134245378, 'rwxy009', '人文学院常老师', 'sys_user', 'rwxy009@hzdyjs.edu.cn', '13800138079', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '人文学院教师'),
|
||||
(2080, '000000', 1938483954134245378, 'rwxy010', '人文学院乐老师', 'sys_user', 'rwxy010@hzdyjs.edu.cn', '13800138080', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '人文学院教师');
|
||||
|
||||
-- 生态园林学院 (dept_id: 1938484049621770242) - 10个用户
|
||||
INSERT INTO `sys_user` (`user_id`, `tenant_id`, `dept_id`, `user_name`, `nick_name`, `user_type`, `email`, `phonenumber`, `sex`, `avatar`, `password`, `status`, `del_flag`, `login_ip`, `login_date`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES
|
||||
(2081, '000000', 1938484049621770242, 'styy001', '生态园林金老师', 'sys_user', 'styy001@hzdyjs.edu.cn', '13800138081', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '生态园林学院教师'),
|
||||
(2082, '000000', 1938484049621770242, 'styy002', '生态园林夏老师', 'sys_user', 'styy002@hzdyjs.edu.cn', '13800138082', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '生态园林学院教师'),
|
||||
(2083, '000000', 1938484049621770242, 'styy003', '生态园林侯老师', 'sys_user', 'styy003@hzdyjs.edu.cn', '13800138083', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '生态园林学院教师'),
|
||||
(2084, '000000', 1938484049621770242, 'styy004', '生态园林潘老师', 'sys_user', 'styy004@hzdyjs.edu.cn', '13800138084', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '生态园林学院教师'),
|
||||
(2085, '000000', 1938484049621770242, 'styy005', '生态园林万老师', 'sys_user', 'styy005@hzdyjs.edu.cn', '13800138085', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '生态园林学院教师'),
|
||||
(2086, '000000', 1938484049621770242, 'styy006', '生态园林梅老师', 'sys_user', 'styy006@hzdyjs.edu.cn', '13800138086', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '生态园林学院教师'),
|
||||
(2087, '000000', 1938484049621770242, 'styy007', '生态园林任老师', 'sys_user', 'styy007@hzdyjs.edu.cn', '13800138087', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '生态园林学院教师'),
|
||||
(2088, '000000', 1938484049621770242, 'styy008', '生态园林柳老师', 'sys_user', 'styy008@hzdyjs.edu.cn', '13800138088', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '生态园林学院教师'),
|
||||
(2089, '000000', 1938484049621770242, 'styy009', '生态园林花老师', 'sys_user', 'styy009@hzdyjs.edu.cn', '13800138089', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '生态园林学院教师'),
|
||||
(2090, '000000', 1938484049621770242, 'styy010', '生态园林草老师', 'sys_user', 'styy010@hzdyjs.edu.cn', '13800138090', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '生态园林学院教师');
|
||||
|
||||
-- 人事处 (dept_id: 1938484510047297538) - 10个用户
|
||||
INSERT INTO `sys_user` (`user_id`, `tenant_id`, `dept_id`, `user_name`, `nick_name`, `user_type`, `email`, `phonenumber`, `sex`, `avatar`, `password`, `status`, `del_flag`, `login_ip`, `login_date`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES
|
||||
(2091, '000000', 1938484510047297538, 'rsc001', '人事处处长林明', 'sys_user', 'rsc001@hzdyjs.edu.cn', '13800138091', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '人事处处长'),
|
||||
(2092, '000000', 1938484510047297538, 'rsc002', '人事处副处长何琳', 'sys_user', 'rsc002@hzdyjs.edu.cn', '13800138092', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '人事处副处长'),
|
||||
(2093, '000000', 1938484510047297538, 'rsc003', '人事处科长方刚', 'sys_user', 'rsc003@hzdyjs.edu.cn', '13800138093', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '人事处科长'),
|
||||
(2094, '000000', 1938484510047297538, 'rsc004', '人事处干事邓妮', 'sys_user', 'rsc004@hzdyjs.edu.cn', '13800138094', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '人事处干事'),
|
||||
(2095, '000000', 1938484510047297538, 'rsc005', '人事处干事曾磊', 'sys_user', 'rsc005@hzdyjs.edu.cn', '13800138095', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '人事处干事'),
|
||||
(2096, '000000', 1938484510047297538, 'rsc006', '人事处干事彭蕊', 'sys_user', 'rsc006@hzdyjs.edu.cn', '13800138096', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '人事处干事'),
|
||||
(2097, '000000', 1938484510047297538, 'rsc007', '人事处干事白帆', 'sys_user', 'rsc007@hzdyjs.edu.cn', '13800138097', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '人事处干事'),
|
||||
(2098, '000000', 1938484510047297538, 'rsc008', '人事处干事邬菊', 'sys_user', 'rsc008@hzdyjs.edu.cn', '13800138098', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '人事处干事'),
|
||||
(2099, '000000', 1938484510047297538, 'rsc009', '人事处干事贺威', 'sys_user', 'rsc009@hzdyjs.edu.cn', '13800138099', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '人事处干事'),
|
||||
(2100, '000000', 1938484510047297538, 'rsc010', '人事处干事齐艳', 'sys_user', 'rsc010@hzdyjs.edu.cn', '13800138100', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '人事处干事');
|
||||
|
||||
-- 教务处 (dept_id: 1938484570826956801) - 10个用户
|
||||
INSERT INTO `sys_user` (`user_id`, `tenant_id`, `dept_id`, `user_name`, `nick_name`, `user_type`, `email`, `phonenumber`, `sex`, `avatar`, `password`, `status`, `del_flag`, `login_ip`, `login_date`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES
|
||||
(2101, '000000', 1938484570826956801, 'jwc001', '教务处处长江建', 'sys_user', 'jwc001@hzdyjs.edu.cn', '13800138101', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '教务处处长'),
|
||||
(2102, '000000', 1938484570826956801, 'jwc002', '教务处副处长易霞', 'sys_user', 'jwc002@hzdyjs.edu.cn', '13800138102', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '教务处副处长'),
|
||||
(2103, '000000', 1938484570826956801, 'jwc003', '教务处科长时健', 'sys_user', 'jwc003@hzdyjs.edu.cn', '13800138103', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '教务处科长'),
|
||||
(2104, '000000', 1938484570826956801, 'jwc004', '教务处干事查瑜', 'sys_user', 'jwc004@hzdyjs.edu.cn', '13800138104', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '教务处干事'),
|
||||
(2105, '000000', 1938484570826956801, 'jwc005', '教务处干事宫强', 'sys_user', 'jwc005@hzdyjs.edu.cn', '13800138105', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '教务处干事'),
|
||||
(2106, '000000', 1938484570826956801, 'jwc006', '教务处干事宁兰', 'sys_user', 'jwc006@hzdyjs.edu.cn', '13800138106', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '教务处干事'),
|
||||
(2107, '000000', 1938484570826956801, 'jwc007', '教务处干事米超', 'sys_user', 'jwc007@hzdyjs.edu.cn', '13800138107', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '教务处干事'),
|
||||
(2108, '000000', 1938484570826956801, 'jwc008', '教务处干事习莉', 'sys_user', 'jwc008@hzdyjs.edu.cn', '13800138108', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '教务处干事'),
|
||||
(2109, '000000', 1938484570826956801, 'jwc009', '教务处干事欧翔', 'sys_user', 'jwc009@hzdyjs.edu.cn', '13800138109', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '教务处干事'),
|
||||
(2110, '000000', 1938484570826956801, 'jwc010', '教务处干事练芬', 'sys_user', 'jwc010@hzdyjs.edu.cn', '13800138110', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '教务处干事');
|
||||
|
||||
-- 学生处 (dept_id: 1938484642188845057) - 10个用户
|
||||
INSERT INTO `sys_user` (`user_id`, `tenant_id`, `dept_id`, `user_name`, `nick_name`, `user_type`, `email`, `phonenumber`, `sex`, `avatar`, `password`, `status`, `del_flag`, `login_ip`, `login_date`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES
|
||||
(2111, '000000', 1938484642188845057, 'xsc001', '学生处处长游新', 'sys_user', 'xsc001@hzdyjs.edu.cn', '13800138111', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '学生处处长'),
|
||||
(2112, '000000', 1938484642188845057, 'xsc002', '学生处副处长廉美', 'sys_user', 'xsc002@hzdyjs.edu.cn', '13800138112', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '学生处副处长'),
|
||||
(2113, '000000', 1938484642188845057, 'xsc003', '学生处科长郝坚', 'sys_user', 'xsc003@hzdyjs.edu.cn', '13800138113', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '学生处科长'),
|
||||
(2114, '000000', 1938484642188845057, 'xsc004', '学生处干事殷慧', 'sys_user', 'xsc004@hzdyjs.edu.cn', '13800138114', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '学生处干事'),
|
||||
(2115, '000000', 1938484642188845057, 'xsc005', '学生处干事连涛', 'sys_user', 'xsc005@hzdyjs.edu.cn', '13800138115', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '学生处干事'),
|
||||
(2116, '000000', 1938484642188845057, 'xsc006', '学生处干事罗霞', 'sys_user', 'xsc006@hzdyjs.edu.cn', '13800138116', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '学生处干事'),
|
||||
(2117, '000000', 1938484642188845057, 'xsc007', '学生处干事苏刚', 'sys_user', 'xsc007@hzdyjs.edu.cn', '13800138117', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '学生处干事'),
|
||||
(2118, '000000', 1938484642188845057, 'xsc008', '学生处干事苗晨', 'sys_user', 'xsc008@hzdyjs.edu.cn', '13800138118', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '学生处干事'),
|
||||
(2119, '000000', 1938484642188845057, 'xsc009', '学生处干事汤波', 'sys_user', 'xsc009@hzdyjs.edu.cn', '13800138119', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '学生处干事'),
|
||||
(2120, '000000', 1938484642188845057, 'xsc010', '学生处干事牟爽', 'sys_user', 'xsc010@hzdyjs.edu.cn', '13800138120', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '学生处干事');
|
||||
|
||||
-- 总务处 (dept_id: 1938484793632579586) - 10个用户
|
||||
INSERT INTO `sys_user` (`user_id`, `tenant_id`, `dept_id`, `user_name`, `nick_name`, `user_type`, `email`, `phonenumber`, `sex`, `avatar`, `password`, `status`, `del_flag`, `login_ip`, `login_date`, `create_dept`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES
|
||||
(2121, '000000', 1938484793632579586, 'zwc001', '总务处处长曲建', 'sys_user', 'zwc001@hzdyjs.edu.cn', '13800138121', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '总务处处长'),
|
||||
(2122, '000000', 1938484793632579586, 'zwc002', '总务处副处长温丽', 'sys_user', 'zwc002@hzdyjs.edu.cn', '13800138122', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '总务处副处长'),
|
||||
(2123, '000000', 1938484793632579586, 'zwc003', '总务处科长舒刚', 'sys_user', 'zwc003@hzdyjs.edu.cn', '13800138123', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '总务处科长'),
|
||||
(2124, '000000', 1938484793632579586, 'zwc004', '总务处干事晏茹', 'sys_user', 'zwc004@hzdyjs.edu.cn', '13800138124', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '总务处干事'),
|
||||
(2125, '000000', 1938484793632579586, 'zwc005', '总务处干事陆雄', 'sys_user', 'zwc005@hzdyjs.edu.cn', '13800138125', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '总务处干事'),
|
||||
(2126, '000000', 1938484793632579586, 'zwc006', '总务处干事湛红', 'sys_user', 'zwc006@hzdyjs.edu.cn', '13800138126', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '总务处干事'),
|
||||
(2127, '000000', 1938484793632579586, 'zwc007', '总务处干事邹凯', 'sys_user', 'zwc007@hzdyjs.edu.cn', '13800138127', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '总务处干事'),
|
||||
(2128, '000000', 1938484793632579586, 'zwc008', '总务处干事农玲', 'sys_user', 'zwc008@hzdyjs.edu.cn', '13800138128', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '总务处干事'),
|
||||
(2129, '000000', 1938484793632579586, 'zwc009', '总务处干事包龙', 'sys_user', 'zwc009@hzdyjs.edu.cn', '13800138129', '0', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '总务处干事'),
|
||||
(2130, '000000', 1938484793632579586, 'zwc010', '总务处干事步芸', 'sys_user', 'zwc010@hzdyjs.edu.cn', '13800138130', '1', NULL, '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '', NULL, 103, 1, now(), 1, now(), '总务处干事');
|
||||
|
||||
-- 为所有新增用户分配基础角色(role_id: 3 - 普通用户角色)
|
||||
INSERT INTO `sys_user_role` (`user_id`, `role_id`) VALUES
|
||||
(2071, 3), (2072, 3), (2073, 3), (2074, 3), (2075, 3), (2076, 3), (2077, 3), (2078, 3), (2079, 3), (2080, 3),
|
||||
(2081, 3), (2082, 3), (2083, 3), (2084, 3), (2085, 3), (2086, 3), (2087, 3), (2088, 3), (2089, 3), (2090, 3),
|
||||
(2091, 3), (2092, 3), (2093, 3), (2094, 3), (2095, 3), (2096, 3), (2097, 3), (2098, 3), (2099, 3), (2100, 3),
|
||||
(2101, 3), (2102, 3), (2103, 3), (2104, 3), (2105, 3), (2106, 3), (2107, 3), (2108, 3), (2109, 3), (2110, 3),
|
||||
(2111, 3), (2112, 3), (2113, 3), (2114, 3), (2115, 3), (2116, 3), (2117, 3), (2118, 3), (2119, 3), (2120, 3),
|
||||
(2121, 3), (2122, 3), (2123, 3), (2124, 3), (2125, 3), (2126, 3), (2127, 3), (2128, 3), (2129, 3), (2130, 3);
|
||||
|
||||
-- 为管理岗位用户分配额外权限(role_id: 4 - 学院管理员)
|
||||
INSERT INTO `sys_user_role` (`user_id`, `role_id`) VALUES
|
||||
(2091, 4), -- 人事处处长
|
||||
(2092, 4), -- 人事处副处长
|
||||
(2101, 4), -- 教务处处长
|
||||
(2102, 4), -- 教务处副处长
|
||||
(2111, 4), -- 学生处处长
|
||||
(2112, 4), -- 学生处副处长
|
||||
(2121, 4), -- 总务处处长
|
||||
(2122, 4); -- 总务处副处长
|
||||
|
||||
-- 记录执行结果
|
||||
SELECT
|
||||
'补充用户创建完成' AS message,
|
||||
(SELECT COUNT(*) FROM sys_user WHERE user_id >= 2071 AND user_id <= 2130) AS created_users,
|
||||
(SELECT COUNT(*) FROM sys_user_role WHERE user_id >= 2071 AND user_id <= 2130) AS assigned_roles;
|
||||
@@ -1,344 +0,0 @@
|
||||
const axios = require('axios');
|
||||
const CryptoJS = require('crypto-js');
|
||||
const NodeRSA = require('node-rsa');
|
||||
|
||||
// 配置信息
|
||||
const CONFIG = {
|
||||
BASE_URL: 'http://localhost/dev-api',
|
||||
LOGIN_ENDPOINT: '/auth/login',
|
||||
CLIENT_ID: 'e5cd7e4891bf95d1d19206ce24a7b32e',
|
||||
TENANT_ID: '000000',
|
||||
REFERER: 'http://localhost/login?redirect=%252Findex',
|
||||
// RSA公钥
|
||||
RSA_PUBLIC_KEY:
|
||||
'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==',
|
||||
};
|
||||
|
||||
// 默认登录凭据
|
||||
const CREDENTIALS = {
|
||||
username: 'admin',
|
||||
password: 'admin123',
|
||||
};
|
||||
|
||||
/**
|
||||
* 生成随机AES密钥
|
||||
*/
|
||||
function generateRandomAESKey(length = 32) {
|
||||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let result = '';
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += characters.charAt(Math.floor(Math.random() * characters.length));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* AES加密数据
|
||||
*/
|
||||
function encryptWithAES(data, key) {
|
||||
const keyUtf8 = CryptoJS.enc.Utf8.parse(key);
|
||||
const encrypted = CryptoJS.AES.encrypt(data, keyUtf8, {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
});
|
||||
return encrypted.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* RSA加密AES密钥
|
||||
*/
|
||||
function encryptAESKeyWithRSA(aesKey) {
|
||||
try {
|
||||
const pemKey = `-----BEGIN PUBLIC KEY-----\n${CONFIG.RSA_PUBLIC_KEY}\n-----END PUBLIC KEY-----`;
|
||||
const rsaKey = new NodeRSA(pemKey);
|
||||
rsaKey.setOptions({ encryptionScheme: 'pkcs1' });
|
||||
|
||||
// 将AES密钥转为Base64
|
||||
const aesKeyBase64 = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(aesKey));
|
||||
|
||||
// RSA加密
|
||||
return rsaKey.encrypt(aesKeyBase64, 'base64');
|
||||
} catch (error) {
|
||||
console.error('RSA加密失败:', error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义Header登录 - 按照用户要求
|
||||
*/
|
||||
async function customHeaderLogin(credentials = CREDENTIALS) {
|
||||
console.log('🔐 开始自定义Header登录...');
|
||||
console.log('📍 接口地址:', CONFIG.BASE_URL + CONFIG.LOGIN_ENDPOINT);
|
||||
|
||||
try {
|
||||
// 1. 生成AES密钥
|
||||
const aesKey = generateRandomAESKey(32);
|
||||
console.log('🔑 生成AES密钥:', aesKey);
|
||||
|
||||
// 2. 准备登录数据
|
||||
const loginData = {
|
||||
tenantId: CONFIG.TENANT_ID,
|
||||
username: credentials.username,
|
||||
password: credentials.password,
|
||||
clientId: CONFIG.CLIENT_ID,
|
||||
grantType: 'password',
|
||||
};
|
||||
|
||||
console.log('📋 登录数据:', JSON.stringify(loginData, null, 2));
|
||||
|
||||
// 3. AES加密登录数据
|
||||
const encryptedData = encryptWithAES(JSON.stringify(loginData), aesKey);
|
||||
console.log('🔐 AES加密后数据长度:', encryptedData.length);
|
||||
|
||||
// 4. RSA加密AES密钥
|
||||
const encryptedKey = encryptAESKeyWithRSA(aesKey);
|
||||
if (!encryptedKey) {
|
||||
throw new Error('RSA加密AES密钥失败');
|
||||
}
|
||||
console.log('🔑 RSA加密后密钥长度:', encryptedKey.length);
|
||||
|
||||
// 5. 按照用户要求设置Header
|
||||
const headers = {
|
||||
'Content-Type': 'application/json;charset=utf-8',
|
||||
clientid: CONFIG.CLIENT_ID, // 用户要求的clientid
|
||||
'encrypt-key': encryptedKey, // 用户要求的encrypt-key
|
||||
referer: CONFIG.REFERER, // 用户要求的referer
|
||||
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
||||
isToken: 'false',
|
||||
isEncrypt: 'true',
|
||||
repeatSubmit: 'false',
|
||||
};
|
||||
|
||||
console.log('📤 请求头设置:');
|
||||
console.log(' - clientid:', headers.clientid);
|
||||
console.log(' - encrypt-key:', encryptedKey.substring(0, 50) + '...');
|
||||
console.log(' - referer:', headers.referer);
|
||||
console.log(' - Content-Type:', headers['Content-Type']);
|
||||
|
||||
// 6. 发送登录请求
|
||||
console.log('\n🚀 发送登录请求...');
|
||||
const response = await axios.post(CONFIG.BASE_URL + CONFIG.LOGIN_ENDPOINT, encryptedData, {
|
||||
headers: headers,
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
// 7. 处理响应
|
||||
console.log('✅ 请求成功!');
|
||||
console.log('📊 响应状态:', response.status);
|
||||
console.log('📄 响应数据:', JSON.stringify(response.data, null, 2));
|
||||
|
||||
// 8. 提取Token
|
||||
if (response.data.code === 200 && response.data.data?.access_token) {
|
||||
const token = response.data.data.access_token;
|
||||
console.log('\n🎯 成功获取Token!');
|
||||
console.log('🎫 Token:', token);
|
||||
console.log('🎫 Token长度:', token.length);
|
||||
console.log('🎫 Token前50字符:', token.substring(0, 50) + '...');
|
||||
|
||||
// 测试Token有效性
|
||||
await testTokenValidity(token);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
token: token,
|
||||
data: response.data,
|
||||
};
|
||||
} else {
|
||||
console.log('⚠️ 登录请求成功,但未获取到Token');
|
||||
console.log('❌ 错误信息:', response.data.msg);
|
||||
return {
|
||||
success: false,
|
||||
error: response.data.msg || 'Unknown error',
|
||||
data: response.data,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('❌ 登录失败!');
|
||||
|
||||
if (error.response) {
|
||||
console.log('📊 错误状态码:', error.response.status);
|
||||
console.log('📄 错误响应:', JSON.stringify(error.response.data, null, 2));
|
||||
|
||||
// 特殊错误处理
|
||||
if (error.response.status === 500) {
|
||||
console.log('💡 提示: 这可能是服务器端加密处理问题');
|
||||
} else if (error.response.status === 403) {
|
||||
console.log('💡 提示: 这是权限配置问题,需要联系管理员');
|
||||
}
|
||||
} else if (error.request) {
|
||||
console.log('🌐 网络错误,请检查服务器是否启动');
|
||||
console.log('📡 请求详情:', error.message);
|
||||
} else {
|
||||
console.log('⚙️ 请求配置错误:', error.message);
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data || error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试Token有效性
|
||||
*/
|
||||
async function testTokenValidity(token) {
|
||||
console.log('\n🧪 测试Token有效性...');
|
||||
|
||||
try {
|
||||
const response = await axios.get(CONFIG.BASE_URL + '/system/user/getInfo', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
clientid: CONFIG.CLIENT_ID,
|
||||
referer: CONFIG.REFERER,
|
||||
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
||||
},
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
console.log('✅ Token有效! 用户信息获取成功');
|
||||
console.log('👤 用户信息:', JSON.stringify(response.data, null, 2));
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log('❌ Token测试失败');
|
||||
if (error.response) {
|
||||
console.log('📊 错误状态码:', error.response.status);
|
||||
console.log('📄 错误信息:', error.response.data);
|
||||
} else {
|
||||
console.log('🌐 网络错误:', error.message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 标准非加密登录 (备用方案)
|
||||
*/
|
||||
async function standardLogin(credentials = CREDENTIALS) {
|
||||
console.log('\n🔓 尝试标准非加密登录 (备用方案)...');
|
||||
|
||||
const loginData = {
|
||||
tenantId: CONFIG.TENANT_ID,
|
||||
username: credentials.username,
|
||||
password: credentials.password,
|
||||
clientId: CONFIG.CLIENT_ID,
|
||||
grantType: 'password',
|
||||
};
|
||||
|
||||
const headers = {
|
||||
'Content-Type': 'application/json;charset=utf-8',
|
||||
clientid: CONFIG.CLIENT_ID,
|
||||
referer: CONFIG.REFERER,
|
||||
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await axios.post(CONFIG.BASE_URL + CONFIG.LOGIN_ENDPOINT, loginData, {
|
||||
headers,
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
console.log('✅ 标准登录请求成功');
|
||||
console.log('📄 响应数据:', JSON.stringify(response.data, null, 2));
|
||||
|
||||
if (response.data.code === 200 && response.data.data?.access_token) {
|
||||
const token = response.data.data.access_token;
|
||||
console.log('🎫 获取到Token:', token.substring(0, 50) + '...');
|
||||
return { success: true, token: token, data: response.data };
|
||||
} else {
|
||||
return { success: false, error: response.data.msg, data: response.data };
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('❌ 标准登录失败');
|
||||
if (error.response) {
|
||||
console.log('📊 状态码:', error.response.status);
|
||||
console.log('📄 错误信息:', error.response.data);
|
||||
}
|
||||
return { success: false, error: error.response?.data || error.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 主函数 - 智能登录策略
|
||||
*/
|
||||
async function smartLogin(credentials) {
|
||||
console.log('🌟'.repeat(25));
|
||||
console.log('🚀 智能登录脚本 - 自定义Header版本');
|
||||
console.log('🌟'.repeat(25));
|
||||
|
||||
let result = null;
|
||||
|
||||
// 策略1: 首先尝试加密登录
|
||||
console.log('\n📋 策略1: 尝试加密登录 (用户指定Header格式)');
|
||||
result = await customHeaderLogin(credentials);
|
||||
|
||||
if (result.success) {
|
||||
console.log('\n🎉 加密登录成功!');
|
||||
return result;
|
||||
}
|
||||
|
||||
// 策略2: 如果加密登录失败,尝试标准登录
|
||||
console.log('\n📋 策略2: 尝试标准非加密登录 (备用方案)');
|
||||
result = await standardLogin(credentials);
|
||||
|
||||
if (result.success) {
|
||||
console.log('\n🎉 标准登录成功!');
|
||||
return result;
|
||||
}
|
||||
|
||||
// 所有策略都失败
|
||||
console.log('\n❌ 所有登录策略都失败了');
|
||||
console.log('🔧 建议检查:');
|
||||
console.log(' 1. 服务器是否正常运行');
|
||||
console.log(' 2. 用户权限是否正确配置');
|
||||
console.log(' 3. 网络连接是否正常');
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 导出函数
|
||||
module.exports = {
|
||||
customHeaderLogin,
|
||||
standardLogin,
|
||||
testTokenValidity,
|
||||
smartLogin,
|
||||
CONFIG,
|
||||
CREDENTIALS,
|
||||
};
|
||||
|
||||
// 直接运行脚本
|
||||
if (require.main === module) {
|
||||
console.log('🔥 检查依赖包...');
|
||||
|
||||
// 检查依赖
|
||||
try {
|
||||
require('axios');
|
||||
require('crypto-js');
|
||||
require('node-rsa');
|
||||
console.log('✅ 所有依赖包检查完成');
|
||||
} catch (error) {
|
||||
console.error('❌ 缺少依赖包,请运行: npm install axios crypto-js node-rsa');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 运行智能登录
|
||||
smartLogin(CREDENTIALS)
|
||||
.then((result) => {
|
||||
console.log('\n' + '🌟'.repeat(25));
|
||||
console.log('📊 最终结果:');
|
||||
if (result.success) {
|
||||
console.log('✅ 登录成功!');
|
||||
console.log('🎫 Token:', result.token);
|
||||
console.log('💡 您可以使用这个Token进行后续API调用');
|
||||
} else {
|
||||
console.log('❌ 登录失败!');
|
||||
console.log('📄 错误信息:', result.error);
|
||||
}
|
||||
console.log('🌟'.repeat(25));
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('💥 脚本运行出错:', error.message);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>出国审批页面API调试</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
.error { color: red; }
|
||||
.success { color: green; }
|
||||
.info { color: blue; }
|
||||
pre { background: #f5f5f5; padding: 10px; overflow-x: auto; }
|
||||
button { margin: 5px; padding: 8px 16px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>出国审批页面API调试工具</h1>
|
||||
|
||||
<div id="status">正在检查...</div>
|
||||
|
||||
<h2>测试结果</h2>
|
||||
<div id="results"></div>
|
||||
|
||||
<h2>控制台日志</h2>
|
||||
<pre id="console-log"></pre>
|
||||
|
||||
<script>
|
||||
const baseURL = 'http://localhost:8080';
|
||||
const results = document.getElementById('results');
|
||||
const consoleLog = document.getElementById('console-log');
|
||||
const status = document.getElementById('status');
|
||||
|
||||
// 劫持console.log来显示在页面上
|
||||
const originalLog = console.log;
|
||||
const originalError = console.error;
|
||||
|
||||
console.log = function(...args) {
|
||||
originalLog.apply(console, args);
|
||||
consoleLog.innerHTML += '[LOG] ' + args.join(' ') + '\n';
|
||||
};
|
||||
|
||||
console.error = function(...args) {
|
||||
originalError.apply(console, args);
|
||||
consoleLog.innerHTML += '[ERROR] ' + args.join(' ') + '\n';
|
||||
};
|
||||
|
||||
async function testAPI() {
|
||||
status.innerHTML = '<span class="info">开始测试API...</span>';
|
||||
|
||||
// 测试1: 检查基础连接
|
||||
try {
|
||||
console.log('测试1: 检查后端服务连接...');
|
||||
const response = await fetch(`${baseURL}/actuator/health`);
|
||||
const data = await response.text();
|
||||
addResult('✅ 后端服务连接', 'success', `状态码: ${response.status}, 响应: ${data.substring(0, 100)}`);
|
||||
} catch (error) {
|
||||
addResult('❌ 后端服务连接失败', 'error', error.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// 测试2: 检查待审批申请API(需要token)
|
||||
try {
|
||||
console.log('测试2: 检查待审批申请API...');
|
||||
const response = await fetch(`${baseURL}/rs/overseas/application/pending/list?pageNum=1&pageSize=10`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.status === 401) {
|
||||
addResult('⚠️ 待审批申请API需要认证', 'info', `状态码: ${response.status}, 消息: ${data.msg}`);
|
||||
} else if (response.ok) {
|
||||
addResult('✅ 待审批申请API正常', 'success', `返回数据: ${JSON.stringify(data, null, 2)}`);
|
||||
} else {
|
||||
addResult('❌ 待审批申请API异常', 'error', `状态码: ${response.status}, 响应: ${JSON.stringify(data, null, 2)}`);
|
||||
}
|
||||
} catch (error) {
|
||||
addResult('❌ 待审批申请API请求失败', 'error', error.message);
|
||||
}
|
||||
|
||||
// 测试3: 检查审批记录API(需要token)
|
||||
try {
|
||||
console.log('测试3: 检查审批记录API...');
|
||||
const response = await fetch(`${baseURL}/rs/overseas/approval/my/list?pageNum=1&pageSize=10`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.status === 401) {
|
||||
addResult('⚠️ 审批记录API需要认证', 'info', `状态码: ${response.status}, 消息: ${data.msg}`);
|
||||
} else if (response.ok) {
|
||||
addResult('✅ 审批记录API正常', 'success', `返回数据: ${JSON.stringify(data, null, 2)}`);
|
||||
} else {
|
||||
addResult('❌ 审批记录API异常', 'error', `状态码: ${response.status}, 响应: ${JSON.stringify(data, null, 2)}`);
|
||||
}
|
||||
} catch (error) {
|
||||
addResult('❌ 审批记录API请求失败', 'error', error.message);
|
||||
}
|
||||
|
||||
status.innerHTML = '<span class="success">测试完成!</span>';
|
||||
}
|
||||
|
||||
function addResult(title, type, detail) {
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = `<h3 class="${type}">${title}</h3><pre>${detail}</pre>`;
|
||||
results.appendChild(div);
|
||||
}
|
||||
|
||||
// 页面加载后自动执行测试
|
||||
window.onload = function() {
|
||||
testAPI();
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,115 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>字典数据调试</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
.container { max-width: 800px; margin: 0 auto; }
|
||||
.test-section { margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }
|
||||
.success { color: green; }
|
||||
.error { color: red; }
|
||||
.warning { color: orange; }
|
||||
pre { background: #f5f5f5; padding: 10px; border-radius: 3px; overflow-x: auto; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>字典数据调试页面</h1>
|
||||
|
||||
<div class="test-section">
|
||||
<h3>测试说明</h3>
|
||||
<p>此页面用于调试学生信息管理页面的字典数据问题</p>
|
||||
<p>请确保:</p>
|
||||
<ul>
|
||||
<li>后端服务正在运行在 http://localhost:8080</li>
|
||||
<li>已经登录管理后台</li>
|
||||
<li>打开浏览器开发者工具查看详细日志</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h3>测试结果</h3>
|
||||
<div id="results"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h3>字典数据详情</h3>
|
||||
<div id="dictDetails"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const results = document.getElementById('results');
|
||||
const dictDetails = document.getElementById('dictDetails');
|
||||
|
||||
function log(message, type = 'info') {
|
||||
const div = document.createElement('div');
|
||||
div.className = type;
|
||||
div.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
|
||||
results.appendChild(div);
|
||||
console.log(`[DICT-DEBUG] ${message}`);
|
||||
}
|
||||
|
||||
function showDictData(dictType, data) {
|
||||
const section = document.createElement('div');
|
||||
section.innerHTML = `
|
||||
<h4>${dictType} (${data.length} 条记录)</h4>
|
||||
<pre>${JSON.stringify(data, null, 2)}</pre>
|
||||
`;
|
||||
dictDetails.appendChild(section);
|
||||
}
|
||||
|
||||
async function testDictAPI() {
|
||||
log('开始测试字典API...');
|
||||
|
||||
const dictTypes = ['xg_gender', 'xg_student_status', 'xg_info_status'];
|
||||
|
||||
for (const dictType of dictTypes) {
|
||||
try {
|
||||
log(`正在测试字典类型: ${dictType}`);
|
||||
|
||||
const response = await fetch(`http://localhost:8080/system/dict/data/type/${dictType}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
credentials: 'include' // 包含cookies
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.code === 200) {
|
||||
log(`✅ ${dictType}: 成功获取 ${result.data.length} 条数据`, 'success');
|
||||
showDictData(dictType, result.data);
|
||||
} else if (result.code === 401) {
|
||||
log(`❌ ${dictType}: 认证失败,请先登录管理后台`, 'error');
|
||||
} else {
|
||||
log(`❌ ${dictType}: ${result.msg}`, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
log(`❌ ${dictType}: 网络错误 - ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载完成后执行测试
|
||||
window.addEventListener('load', () => {
|
||||
log('页面加载完成,开始测试...');
|
||||
testDictAPI();
|
||||
});
|
||||
|
||||
// 添加手动重试按钮
|
||||
const retryButton = document.createElement('button');
|
||||
retryButton.textContent = '重新测试';
|
||||
retryButton.onclick = () => {
|
||||
results.innerHTML = '';
|
||||
dictDetails.innerHTML = '';
|
||||
testDictAPI();
|
||||
};
|
||||
document.querySelector('.container').appendChild(retryButton);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,172 +0,0 @@
|
||||
-- ==========================================
|
||||
-- 维修人员权限修复脚本
|
||||
-- 功能:确保维修人员角色正确配置,能正确过滤只查看自己相关的订单
|
||||
-- 时间:2025-06-30 18:45:00
|
||||
-- 说明:使用UPSERT逻辑,支持重复执行
|
||||
-- ==========================================
|
||||
|
||||
-- 第一步:创建维修人员和管理员角色(如果不存在)
|
||||
INSERT INTO sys_role (
|
||||
role_id, role_name, role_key, role_sort, data_scope,
|
||||
menu_check_strictly, dept_check_strictly, status,
|
||||
del_flag, create_by, create_time, update_by, update_time, remark
|
||||
)
|
||||
SELECT
|
||||
5, '维修人员', 'maintenance_worker', 5, '5',
|
||||
1, 1, '0',
|
||||
'0', 1, now(), 1, now(), '负责具体维修工作的人员,只能查看和处理分配给自己的维修工单或自己创建的工单'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_role WHERE role_id = 5);
|
||||
|
||||
INSERT INTO sys_role (
|
||||
role_id, role_name, role_key, role_sort, data_scope,
|
||||
menu_check_strictly, dept_check_strictly, status,
|
||||
del_flag, create_by, create_time, update_by, update_time, remark
|
||||
)
|
||||
SELECT
|
||||
6, '物业维修管理员', 'property_maintenance_admin', 6, '3',
|
||||
1, 1, '0',
|
||||
'0', 1, now(), 1, now(), '负责物业维修管理工作,可以管理所有维修工单、分配维修任务、查看维修统计'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_role WHERE role_id = 6);
|
||||
|
||||
-- 第二步:确保维修人员角色有基础菜单权限
|
||||
-- 为维修人员分配物业管理主菜单权限
|
||||
INSERT IGNORE INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT 5, menu_id FROM sys_menu WHERE menu_id = 2100; -- 物业管理主菜单
|
||||
|
||||
-- 为维修人员分配报修管理子菜单权限
|
||||
INSERT IGNORE INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT 5, menu_id FROM sys_menu WHERE menu_id = 2110; -- 报修管理菜单
|
||||
|
||||
-- 为维修人员分配基础功能权限
|
||||
INSERT IGNORE INTO sys_role_menu (role_id, menu_id) VALUES
|
||||
(5, 2111), -- 查询权限
|
||||
(5, 2113), -- 修改权限
|
||||
(5, 2117), -- 开始维修权限
|
||||
(5, 2118), -- 完成维修权限
|
||||
(5, 2120); -- 生成工单编号权限
|
||||
|
||||
-- 第三步:为物业维修管理员分配完整权限
|
||||
INSERT IGNORE INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT 6, menu_id FROM sys_menu WHERE menu_id BETWEEN 2100 AND 2199;
|
||||
|
||||
-- 第四步:创建测试用户(如果不存在)
|
||||
-- 创建一个专门的维修人员测试账号
|
||||
INSERT INTO sys_user (
|
||||
user_id, user_name, nick_name, email, phone, sex, avatar, password, status,
|
||||
del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark
|
||||
)
|
||||
SELECT
|
||||
999, 'maintenance_test', '测试维修员', 'maintenance@test.com', '13800000001', '0', '',
|
||||
'$2a$10$7JB720yubVSOnlhHOY5aDuAEaG9MXOAG7Ap3CfvzDuR8YYnQbmLqK', -- 密码:admin123
|
||||
'0', '0', '', now(), 1, now(), 1, now(), '维修人员权限测试账号'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_user WHERE user_id = 999);
|
||||
|
||||
-- 创建一个物业管理员测试账号
|
||||
INSERT INTO sys_user (
|
||||
user_id, user_name, nick_name, email, phone, sex, avatar, password, status,
|
||||
del_flag, login_ip, login_date, create_by, create_time, update_by, update_time, remark
|
||||
)
|
||||
SELECT
|
||||
998, 'property_admin_test', '测试物业管理员', 'property@test.com', '13800000002', '0', '',
|
||||
'$2a$10$7JB720yubVSOnlhHOY5aDuAEaG9MXOAG7Ap3CfvzDuR8YYnQbmLqK', -- 密码:admin123
|
||||
'0', '0', '', now(), 1, now(), 1, now(), '物业管理员权限测试账号'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_user WHERE user_id = 998);
|
||||
|
||||
-- 第五步:为测试用户分配角色
|
||||
INSERT IGNORE INTO sys_user_role (user_id, role_id) VALUES
|
||||
(999, 5), -- 测试维修员 -> 维修人员角色
|
||||
(998, 6); -- 测试物业管理员 -> 物业维修管理员角色
|
||||
|
||||
-- 第六步:为现有总务处用户分配维修人员角色(如果用户存在)
|
||||
INSERT IGNORE INTO sys_user_role (user_id, role_id)
|
||||
SELECT user_id, 5 FROM sys_user
|
||||
WHERE user_name IN ('zwc005', 'zwc007') AND user_id IS NOT NULL;
|
||||
|
||||
-- 为现有总务处用户分配物业管理员角色(如果用户存在)
|
||||
INSERT IGNORE INTO sys_user_role (user_id, role_id)
|
||||
SELECT user_id, 6 FROM sys_user
|
||||
WHERE user_name IN ('zwc003', 'zwc004') AND user_id IS NOT NULL;
|
||||
|
||||
-- 第七步:创建测试数据 - 创建一些报修订单用于测试权限
|
||||
-- 为测试维修员创建的报修订单
|
||||
INSERT INTO pm_repair_order (
|
||||
id, order_no, title, description, repair_location, category_id, priority_level,
|
||||
reporter_id, reporter_name, reporter_phone, order_status, assignee_id, assignee_name,
|
||||
attachment_count, progress_count, create_by, create_time, update_by, update_time,
|
||||
tenant_id, create_dept, deleted
|
||||
)
|
||||
SELECT
|
||||
9901, 'RO20251230001', '测试报修-维修员自己创建', '这是维修员自己创建的报修订单,用于测试权限',
|
||||
'测试楼101室', 1, 2, 999, '测试维修员', '13800000001', '1', NULL, NULL,
|
||||
0, 0, 999, now(), 999, now(), '000000', 103, 0
|
||||
WHERE NOT EXISTS (SELECT 1 FROM pm_repair_order WHERE id = 9901);
|
||||
|
||||
-- 分配给测试维修员的报修订单
|
||||
INSERT INTO pm_repair_order (
|
||||
id, order_no, title, description, repair_location, category_id, priority_level,
|
||||
reporter_id, reporter_name, reporter_phone, order_status, assignee_id, assignee_name,
|
||||
attachment_count, progress_count, create_by, create_time, update_by, update_time,
|
||||
tenant_id, create_dept, deleted
|
||||
)
|
||||
SELECT
|
||||
9902, 'RO20251230002', '测试报修-分配给维修员', '这是分配给维修员的报修订单,用于测试权限',
|
||||
'测试楼102室', 1, 3, 1, '管理员', '13900000000', '2', 999, '测试维修员',
|
||||
0, 0, 1, now(), 1, now(), '000000', 103, 0
|
||||
WHERE NOT EXISTS (SELECT 1 FROM pm_repair_order WHERE id = 9902);
|
||||
|
||||
-- 其他用户创建的报修订单(维修员不应该看到)
|
||||
INSERT INTO pm_repair_order (
|
||||
id, order_no, title, description, repair_location, category_id, priority_level,
|
||||
reporter_id, reporter_name, reporter_phone, order_status, assignee_id, assignee_name,
|
||||
attachment_count, progress_count, create_by, create_time, update_by, update_time,
|
||||
tenant_id, create_dept, deleted
|
||||
)
|
||||
SELECT
|
||||
9903, 'RO20251230003', '测试报修-其他用户创建', '这是其他用户创建的报修订单,维修员不应该看到',
|
||||
'测试楼103室', 1, 1, 998, '测试物业管理员', '13800000002', '1', NULL, NULL,
|
||||
0, 0, 998, now(), 998, now(), '000000', 103, 0
|
||||
WHERE NOT EXISTS (SELECT 1 FROM pm_repair_order WHERE id = 9903);
|
||||
|
||||
-- 第八步:验证配置结果
|
||||
SELECT '=== 角色配置验证 ===' as section;
|
||||
SELECT
|
||||
role_id, role_name, role_key, status, remark
|
||||
FROM sys_role
|
||||
WHERE role_id IN (5, 6)
|
||||
ORDER BY role_id;
|
||||
|
||||
SELECT '=== 用户角色分配验证 ===' as section;
|
||||
SELECT
|
||||
u.user_id, u.user_name, u.nick_name, r.role_name, r.role_key
|
||||
FROM sys_user_role ur
|
||||
JOIN sys_user u ON ur.user_id = u.user_id
|
||||
JOIN sys_role r ON ur.role_id = r.role_id
|
||||
WHERE ur.role_id IN (5, 6)
|
||||
ORDER BY ur.role_id, u.user_id;
|
||||
|
||||
SELECT '=== 维修人员权限验证 ===' as section;
|
||||
SELECT
|
||||
COUNT(*) as menu_permission_count
|
||||
FROM sys_role_menu
|
||||
WHERE role_id = 5;
|
||||
|
||||
SELECT '=== 测试数据验证 ===' as section;
|
||||
SELECT
|
||||
id, order_no, title, reporter_id, reporter_name, assignee_id, assignee_name, order_status
|
||||
FROM pm_repair_order
|
||||
WHERE id BETWEEN 9901 AND 9903
|
||||
ORDER BY id;
|
||||
|
||||
-- 第九步:权限测试说明
|
||||
SELECT '=== 权限测试说明 ===' as section;
|
||||
SELECT
|
||||
'1. 使用maintenance_test账号登录,应该只能看到自己创建(9901)和分配给自己(9902)的工单' as test_case_1,
|
||||
'2. 使用property_admin_test账号登录,应该能看到所有工单(9901,9902,9903)' as test_case_2,
|
||||
'3. 使用admin账号登录,作为超级管理员应该能看到所有工单' as test_case_3;
|
||||
|
||||
-- 第十步:完成提示
|
||||
SELECT
|
||||
'权限修复完成' as status,
|
||||
'maintenance_test / admin123 - 维修人员测试账号' as test_account_1,
|
||||
'property_admin_test / admin123 - 物业管理员测试账号' as test_account_2,
|
||||
'请使用这些账号测试权限是否正确' as next_step;
|
||||
@@ -1,93 +0,0 @@
|
||||
-- ==========================================
|
||||
-- 统计权限修复脚本
|
||||
-- 功能:确保只有维护管理员和超级管理员才能查看统计卡片
|
||||
-- 时间:2025-06-30 18:58:00
|
||||
-- 说明:修复统计权限分配,移除维修人员的统计权限
|
||||
-- ==========================================
|
||||
|
||||
-- 第一步:确保统计权限项存在
|
||||
INSERT INTO sys_menu (
|
||||
menu_id, menu_name, parent_id, order_num, path, component,
|
||||
query_param, is_frame, is_cache, menu_type, visible, status,
|
||||
perms, icon, create_by, create_time, update_by, update_time, remark
|
||||
)
|
||||
SELECT
|
||||
2120, '统计数据', 2110, 10, '', '',
|
||||
'', 1, 0, 'F', '0', '0',
|
||||
'pm:repairOrder:statistics', '#', 1, now(), 1, now(), '获取报修统计数据'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_id = 2120);
|
||||
|
||||
-- 第二步:移除维修人员的统计权限(如果存在)
|
||||
DELETE FROM sys_role_menu
|
||||
WHERE role_id = 5 AND menu_id = 2120;
|
||||
|
||||
-- 第三步:确保超级管理员有统计权限
|
||||
INSERT IGNORE INTO sys_role_menu (role_id, menu_id) VALUES
|
||||
(1, 2120); -- admin角色
|
||||
|
||||
-- 第四步:确保物业维修管理员有统计权限
|
||||
INSERT IGNORE INTO sys_role_menu (role_id, menu_id) VALUES
|
||||
(6, 2120); -- property_maintenance_admin角色
|
||||
|
||||
-- 第五步:验证权限分配结果
|
||||
SELECT '=== 统计权限分配验证 ===' as section;
|
||||
|
||||
-- 检查统计权限项是否存在
|
||||
SELECT
|
||||
menu_id, menu_name, perms, menu_type, status
|
||||
FROM sys_menu
|
||||
WHERE menu_id = 2120;
|
||||
|
||||
-- 检查哪些角色有统计权限
|
||||
SELECT
|
||||
'当前拥有统计权限的角色' as info_type,
|
||||
rm.role_id,
|
||||
r.role_name,
|
||||
r.role_key,
|
||||
r.status as role_status
|
||||
FROM sys_role_menu rm
|
||||
JOIN sys_role r ON rm.role_id = r.role_id
|
||||
WHERE rm.menu_id = 2120
|
||||
ORDER BY rm.role_id;
|
||||
|
||||
-- 检查维修人员是否还有统计权限(应该为空)
|
||||
SELECT
|
||||
'维修人员统计权限检查' as info_type,
|
||||
CASE
|
||||
WHEN COUNT(*) = 0 THEN '✓ 正确:维修人员没有统计权限'
|
||||
ELSE '✗ 错误:维修人员仍有统计权限'
|
||||
END as result
|
||||
FROM sys_role_menu
|
||||
WHERE role_id = 5 AND menu_id = 2120;
|
||||
|
||||
-- 检查物业维修管理员是否有统计权限
|
||||
SELECT
|
||||
'物业维修管理员统计权限检查' as info_type,
|
||||
CASE
|
||||
WHEN COUNT(*) > 0 THEN '✓ 正确:物业维修管理员有统计权限'
|
||||
ELSE '✗ 错误:物业维修管理员缺少统计权限'
|
||||
END as result
|
||||
FROM sys_role_menu
|
||||
WHERE role_id = 6 AND menu_id = 2120;
|
||||
|
||||
-- 第六步:权限分配总结
|
||||
SELECT '=== 权限分配总结 ===' as section;
|
||||
SELECT
|
||||
'统计权限分配已修复' as status,
|
||||
'只有超级管理员和物业维修管理员可以查看统计卡片' as permission_rule,
|
||||
'维修人员和其他角色无法查看统计功能' as restriction;
|
||||
|
||||
-- 第七步:测试指导
|
||||
SELECT '=== 测试指导 ===' as section;
|
||||
SELECT
|
||||
'使用以下账号测试权限是否正确' as test_guide,
|
||||
'admin/admin123 - 应该能看到统计卡片' as test_case_1,
|
||||
'property_admin_test/admin123 - 应该能看到统计卡片' as test_case_2,
|
||||
'maintenance_test/admin123 - 不应该看到统计卡片' as test_case_3,
|
||||
'前端会根据用户角色自动隐藏/显示统计区域' as frontend_behavior;
|
||||
|
||||
-- 第八步:完成提示
|
||||
SELECT
|
||||
'统计权限修复完成' as completion_status,
|
||||
'需要重启前端和后端服务以确保权限生效' as next_step,
|
||||
'运行 test/statistics-permission-test.js 验证权限配置' as verification;
|
||||
|
Before Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 320 KiB |
|
Before Width: | Height: | Size: 953 KiB |
|
Before Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 248 KiB |
|
Before Width: | Height: | Size: 189 KiB |
|
Before Width: | Height: | Size: 139 KiB |