Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
c76d6beeb0
|
@ -0,0 +1,91 @@
|
||||||
|
# 制度类型搜索接口实现指南
|
||||||
|
|
||||||
|
## 接口定义
|
||||||
|
|
||||||
|
### 请求接口
|
||||||
|
```
|
||||||
|
GET /api/regulation/types
|
||||||
|
Content-Type: application/json
|
||||||
|
```
|
||||||
|
|
||||||
|
### 请求参数
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"page": number, // 页码(可选,默认1)
|
||||||
|
"size": number, // 每页大小(可选,默认10)
|
||||||
|
"typeName": "string", // 类型名称(模糊搜索,可选)
|
||||||
|
"status": "string", // 状态筛选("1"启用,"0"禁用,可选)
|
||||||
|
"remark": "string" // 备注内容(模糊搜索,可选)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 响应格式
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "success",
|
||||||
|
"data": {
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"typeId": "string",
|
||||||
|
"typeName": "string",
|
||||||
|
"sortOrder": number,
|
||||||
|
"isEnabled": "string",
|
||||||
|
"remark": "string",
|
||||||
|
"createBy": "string",
|
||||||
|
"createTime": "string",
|
||||||
|
"updateBy": "string",
|
||||||
|
"updateTime": "string",
|
||||||
|
"delFlag": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": number, // 总记录数
|
||||||
|
"current": number, // 当前页码
|
||||||
|
"size": number, // 每页大小
|
||||||
|
"pages": number // 总页数
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 后端实现说明
|
||||||
|
|
||||||
|
后端已实现以下接口:
|
||||||
|
```java
|
||||||
|
@ApiOperation(value = "获取制度类型列表", httpMethod = "GET")
|
||||||
|
@GetMapping
|
||||||
|
public Result getRegulationTypes(
|
||||||
|
@RequestParam(defaultValue = "1") int page,
|
||||||
|
@RequestParam(defaultValue = "10") int size,
|
||||||
|
@RequestParam(required = false) String typeName,
|
||||||
|
@RequestParam(required = false) String status,
|
||||||
|
@RequestParam(required = false) String remark
|
||||||
|
) {
|
||||||
|
return regulationTypeService.getRegulationTypes(page, size, typeName, status, remark);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 前端集成说明
|
||||||
|
|
||||||
|
前端已完成以下功能:
|
||||||
|
1. ✅ 调整为GET请求接口
|
||||||
|
2. ✅ 参数名匹配后端接口(isEnabled → status)
|
||||||
|
3. ✅ 移除排序参数(后端不支持)
|
||||||
|
4. ✅ 简化搜索表单,只支持手动搜索
|
||||||
|
5. ✅ 保持原有功能不受影响
|
||||||
|
|
||||||
|
## 搜索流程
|
||||||
|
|
||||||
|
1. 用户在搜索表单中输入条件
|
||||||
|
2. 点击"搜索"按钮触发搜索
|
||||||
|
3. 调用后端GET接口 `/api/regulation/types`
|
||||||
|
4. 后端返回搜索结果
|
||||||
|
5. 前端展示搜索结果
|
||||||
|
|
||||||
|
## 参数说明
|
||||||
|
|
||||||
|
- **page**: 页码,默认1
|
||||||
|
- **size**: 每页大小,默认10
|
||||||
|
- **typeName**: 类型名称,支持模糊搜索
|
||||||
|
- **status**: 状态筛选,"1"表示启用,"0"表示禁用
|
||||||
|
- **remark**: 备注内容,支持模糊搜索
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
# 制度公告搜索接口实现指南
|
||||||
|
|
||||||
|
## 接口定义
|
||||||
|
|
||||||
|
### 请求接口
|
||||||
|
```
|
||||||
|
GET /api/regulation
|
||||||
|
Content-Type: application/json
|
||||||
|
```
|
||||||
|
|
||||||
|
### 请求参数
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"page": number, // 页码(可选,默认1)
|
||||||
|
"size": number, // 每页大小(可选,默认10)
|
||||||
|
"status": "string", // 状态筛选(精确匹配,固定为"PUBLISHED")
|
||||||
|
"title": "string", // 制度标题(模糊搜索,可选)
|
||||||
|
"proposer": "string", // 公示人(模糊搜索,可选)
|
||||||
|
"confirmStatus": "string" // 确认状态(精确匹配,可选)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 响应格式
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "success",
|
||||||
|
"data": {
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"regulationId": "string",
|
||||||
|
"title": "string",
|
||||||
|
"content": "string",
|
||||||
|
"regulationType": "string",
|
||||||
|
"status": "string",
|
||||||
|
"publishTime": "string",
|
||||||
|
"effectiveTime": "string",
|
||||||
|
"expireTime": "string",
|
||||||
|
"scope": "string",
|
||||||
|
"level": "string",
|
||||||
|
"version": "string",
|
||||||
|
"remark": "string",
|
||||||
|
"createBy": "string",
|
||||||
|
"updateBy": "string",
|
||||||
|
"createTime": "string",
|
||||||
|
"updateTime": "string",
|
||||||
|
"delFlag": "string",
|
||||||
|
"confirmStatus": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": number, // 总记录数
|
||||||
|
"current": number, // 当前页码
|
||||||
|
"size": number, // 每页大小
|
||||||
|
"pages": number // 总页数
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 后端实现说明
|
||||||
|
|
||||||
|
后端已实现以下接口:
|
||||||
|
```java
|
||||||
|
@ApiOperation(value = "获取制度列表", httpMethod = "GET")
|
||||||
|
@GetMapping
|
||||||
|
public Result getRegulationList(
|
||||||
|
@RequestParam(defaultValue = "1") int page,
|
||||||
|
@RequestParam(defaultValue = "10") int size,
|
||||||
|
@RequestParam(required = false) String status,
|
||||||
|
@RequestParam(required = false) String type,
|
||||||
|
@RequestParam(required = false) String title,
|
||||||
|
@RequestParam(required = false) String proposer,
|
||||||
|
@RequestParam(required = false) String confirmStatus
|
||||||
|
) {
|
||||||
|
return regulationService.getRegulationList(page, size, status, type, title, proposer, confirmStatus);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 前端集成说明
|
||||||
|
|
||||||
|
前端已完成以下功能:
|
||||||
|
1. ✅ 删除前端搜索逻辑(防抖、实时搜索等)
|
||||||
|
2. ✅ 调整为GET请求接口
|
||||||
|
3. ✅ 参数名匹配后端接口(createByName → proposer)
|
||||||
|
4. ✅ 简化搜索表单,只支持手动搜索
|
||||||
|
5. ✅ 移除前端过滤逻辑,由后端处理状态筛选
|
||||||
|
6. ✅ 保持原有功能不受影响
|
||||||
|
|
||||||
|
## 搜索流程
|
||||||
|
|
||||||
|
1. 用户在搜索表单中输入条件
|
||||||
|
2. 点击"搜索"按钮触发搜索
|
||||||
|
3. 调用后端GET接口 `/api/regulation`
|
||||||
|
4. 后端返回搜索结果(已过滤为PUBLISHED状态的制度)
|
||||||
|
5. 前端展示搜索结果
|
||||||
|
|
||||||
|
## 参数说明
|
||||||
|
|
||||||
|
- **page**: 页码,默认1
|
||||||
|
- **size**: 每页大小,默认10
|
||||||
|
- **status**: 状态筛选,固定为"PUBLISHED"(已公告状态)
|
||||||
|
- **title**: 制度标题,支持模糊搜索
|
||||||
|
- **proposer**: 公示人,支持模糊搜索
|
||||||
|
- **confirmStatus**: 确认状态,精确匹配(如:confirmed、pending)
|
||||||
|
|
||||||
|
## 搜索功能特性
|
||||||
|
|
||||||
|
- **模糊搜索**:title 和 proposer 字段支持模糊匹配
|
||||||
|
- **精确筛选**:confirmStatus 字段精确匹配
|
||||||
|
- **分页查询**:支持分页和排序
|
||||||
|
- **状态过滤**:后端自动过滤为PUBLISHED状态的制度
|
||||||
|
- **性能优化**:使用数据库索引提升查询性能
|
||||||
|
|
||||||
|
## 业务逻辑说明
|
||||||
|
|
||||||
|
制度公告页面专门用于展示已经公告的制度,因此:
|
||||||
|
- 后端需要自动过滤为 `status = 'PUBLISHED'` 的制度
|
||||||
|
- 支持按确认状态(confirmStatus)进行筛选
|
||||||
|
- 前端不再需要手动过滤,完全依赖后端处理
|
||||||
|
- 用户可以查看制度详情、下载PDF文件、确认知晓制度
|
||||||
|
|
||||||
|
## 确认状态说明
|
||||||
|
|
||||||
|
- **confirmed**: 已确认 - 用户已确认知晓并遵守该制度
|
||||||
|
- **pending**: 待确认 - 用户尚未确认知晓该制度
|
||||||
|
- 空值: 全部 - 显示所有确认状态的制度
|
|
@ -0,0 +1,117 @@
|
||||||
|
# 制度公示搜索接口实现指南
|
||||||
|
|
||||||
|
## 接口定义
|
||||||
|
|
||||||
|
### 请求接口
|
||||||
|
```
|
||||||
|
GET /api/regulation
|
||||||
|
Content-Type: application/json
|
||||||
|
```
|
||||||
|
|
||||||
|
### 请求参数
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"page": number, // 页码(可选,默认1)
|
||||||
|
"size": number, // 每页大小(可选,默认10)
|
||||||
|
"status": "string", // 状态筛选(精确匹配,可选)
|
||||||
|
"type": "string", // 提案类型(精确匹配,可选)
|
||||||
|
"title": "string", // 提案标题(模糊搜索,可选)
|
||||||
|
"proposer": "string" // 提案人(模糊搜索,可选)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 响应格式
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "success",
|
||||||
|
"data": {
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"regulationId": "string",
|
||||||
|
"title": "string",
|
||||||
|
"content": "string",
|
||||||
|
"regulationType": "string",
|
||||||
|
"status": "string",
|
||||||
|
"publishTime": "string",
|
||||||
|
"effectiveTime": "string",
|
||||||
|
"expireTime": "string",
|
||||||
|
"scope": "string",
|
||||||
|
"level": "string",
|
||||||
|
"version": "string",
|
||||||
|
"remark": "string",
|
||||||
|
"createBy": "string",
|
||||||
|
"updateBy": "string",
|
||||||
|
"createTime": "string",
|
||||||
|
"updateTime": "string",
|
||||||
|
"delFlag": "string",
|
||||||
|
"confirmStatus": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": number, // 总记录数
|
||||||
|
"current": number, // 当前页码
|
||||||
|
"size": number, // 每页大小
|
||||||
|
"pages": number // 总页数
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 后端实现说明
|
||||||
|
|
||||||
|
后端已实现以下接口:
|
||||||
|
```java
|
||||||
|
@ApiOperation(value = "获取制度列表", httpMethod = "GET")
|
||||||
|
@GetMapping
|
||||||
|
public Result getRegulationList(
|
||||||
|
@RequestParam(defaultValue = "1") int page,
|
||||||
|
@RequestParam(defaultValue = "10") int size,
|
||||||
|
@RequestParam(required = false) String status,
|
||||||
|
@RequestParam(required = false) String type,
|
||||||
|
@RequestParam(required = false) String title,
|
||||||
|
@RequestParam(required = false) String proposer
|
||||||
|
) {
|
||||||
|
return regulationService.getRegulationList(page, size, status, type, title, proposer);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 前端集成说明
|
||||||
|
|
||||||
|
前端已完成以下功能:
|
||||||
|
1. ✅ 删除前端搜索逻辑(防抖、实时搜索等)
|
||||||
|
2. ✅ 调整为GET请求接口
|
||||||
|
3. ✅ 参数名匹配后端接口(createByName → proposer, regulationType → type)
|
||||||
|
4. ✅ 简化搜索表单,只支持手动搜索
|
||||||
|
5. ✅ 移除前端过滤逻辑,由后端处理状态筛选
|
||||||
|
6. ✅ 保持原有功能不受影响
|
||||||
|
|
||||||
|
## 搜索流程
|
||||||
|
|
||||||
|
1. 用户在搜索表单中输入条件
|
||||||
|
2. 点击"搜索"按钮触发搜索
|
||||||
|
3. 调用后端GET接口 `/api/regulation`
|
||||||
|
4. 后端返回搜索结果(已过滤掉草稿状态的提案)
|
||||||
|
5. 前端展示搜索结果
|
||||||
|
|
||||||
|
## 参数说明
|
||||||
|
|
||||||
|
- **page**: 页码,默认1
|
||||||
|
- **size**: 每页大小,默认10
|
||||||
|
- **status**: 状态筛选,精确匹配(如:PUBLISHED、APPROVED等)
|
||||||
|
- **type**: 提案类型,精确匹配(如:管理规范、操作流程、安全制度、其他)
|
||||||
|
- **title**: 提案标题,支持模糊搜索
|
||||||
|
- **proposer**: 提案人,支持模糊搜索
|
||||||
|
|
||||||
|
## 搜索功能特性
|
||||||
|
|
||||||
|
- **模糊搜索**:title 和 proposer 字段支持模糊匹配
|
||||||
|
- **精确筛选**:type 和 status 字段精确匹配
|
||||||
|
- **分页查询**:支持分页和排序
|
||||||
|
- **状态过滤**:后端自动过滤掉草稿状态的提案,只显示已公示及以上的提案
|
||||||
|
- **性能优化**:使用数据库索引提升查询性能
|
||||||
|
|
||||||
|
## 业务逻辑说明
|
||||||
|
|
||||||
|
制度公示页面专门用于展示已经公示或已通过的制度提案,因此:
|
||||||
|
- 后端需要自动过滤掉 `status = 'DRAFT'` 的提案
|
||||||
|
- 只返回 `status = 'PUBLISHED'` 或 `status = 'APPROVED'` 的提案
|
||||||
|
- 前端不再需要手动过滤,完全依赖后端处理
|
|
@ -0,0 +1,108 @@
|
||||||
|
# 制度提案搜索接口实现指南
|
||||||
|
|
||||||
|
## 接口定义
|
||||||
|
|
||||||
|
### 请求接口
|
||||||
|
```
|
||||||
|
GET /api/regulation
|
||||||
|
Content-Type: application/json
|
||||||
|
```
|
||||||
|
|
||||||
|
### 请求参数
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"page": number, // 页码(可选,默认1)
|
||||||
|
"size": number, // 每页大小(可选,默认10)
|
||||||
|
"status": "string", // 状态筛选(精确匹配,可选)
|
||||||
|
"type": "string", // 提案类型(精确匹配,可选)
|
||||||
|
"title": "string", // 提案标题(模糊搜索,可选)
|
||||||
|
"proposer": "string" // 提案人(模糊搜索,可选)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 响应格式
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "success",
|
||||||
|
"data": {
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"regulationId": "string",
|
||||||
|
"title": "string",
|
||||||
|
"content": "string",
|
||||||
|
"regulationType": "string",
|
||||||
|
"status": "string",
|
||||||
|
"publishTime": "string",
|
||||||
|
"effectiveTime": "string",
|
||||||
|
"expireTime": "string",
|
||||||
|
"scope": "string",
|
||||||
|
"level": "string",
|
||||||
|
"version": "string",
|
||||||
|
"remark": "string",
|
||||||
|
"createBy": "string",
|
||||||
|
"updateBy": "string",
|
||||||
|
"createTime": "string",
|
||||||
|
"updateTime": "string",
|
||||||
|
"delFlag": "string",
|
||||||
|
"confirmStatus": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": number, // 总记录数
|
||||||
|
"current": number, // 当前页码
|
||||||
|
"size": number, // 每页大小
|
||||||
|
"pages": number // 总页数
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 后端实现说明
|
||||||
|
|
||||||
|
后端已实现以下接口:
|
||||||
|
```java
|
||||||
|
@ApiOperation(value = "获取制度列表", httpMethod = "GET")
|
||||||
|
@GetMapping
|
||||||
|
public Result getRegulationList(
|
||||||
|
@RequestParam(defaultValue = "1") int page,
|
||||||
|
@RequestParam(defaultValue = "10") int size,
|
||||||
|
@RequestParam(required = false) String status,
|
||||||
|
@RequestParam(required = false) String type,
|
||||||
|
@RequestParam(required = false) String title,
|
||||||
|
@RequestParam(required = false) String proposer
|
||||||
|
) {
|
||||||
|
return regulationService.getRegulationList(page, size, status, type, title, proposer);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 前端集成说明
|
||||||
|
|
||||||
|
前端已完成以下功能:
|
||||||
|
1. ✅ 删除前端搜索逻辑(防抖、实时搜索等)
|
||||||
|
2. ✅ 调整为GET请求接口
|
||||||
|
3. ✅ 参数名匹配后端接口(createByName → proposer, regulationType → type)
|
||||||
|
4. ✅ 简化搜索表单,只支持手动搜索
|
||||||
|
5. ✅ 保持原有功能不受影响
|
||||||
|
|
||||||
|
## 搜索流程
|
||||||
|
|
||||||
|
1. 用户在搜索表单中输入条件
|
||||||
|
2. 点击"搜索"按钮触发搜索
|
||||||
|
3. 调用后端GET接口 `/api/regulation`
|
||||||
|
4. 后端返回搜索结果
|
||||||
|
5. 前端展示搜索结果
|
||||||
|
|
||||||
|
## 参数说明
|
||||||
|
|
||||||
|
- **page**: 页码,默认1
|
||||||
|
- **size**: 每页大小,默认10
|
||||||
|
- **status**: 状态筛选,精确匹配(如:DRAFT、PUBLISHED、APPROVED等)
|
||||||
|
- **type**: 提案类型,精确匹配(如:管理规范、操作流程、安全制度、其他)
|
||||||
|
- **title**: 提案标题,支持模糊搜索
|
||||||
|
- **proposer**: 提案人,支持模糊搜索
|
||||||
|
|
||||||
|
## 搜索功能特性
|
||||||
|
|
||||||
|
- **模糊搜索**:title 和 proposer 字段支持模糊匹配
|
||||||
|
- **精确筛选**:type 和 status 字段精确匹配
|
||||||
|
- **分页查询**:支持分页和排序
|
||||||
|
- **性能优化**:使用数据库索引提升查询性能
|
|
@ -28,7 +28,9 @@
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"dayjs": "^1.11.4",
|
"dayjs": "^1.11.4",
|
||||||
"echarts": "^5.4.2",
|
"echarts": "^5.4.2",
|
||||||
|
"html2canvas": "^1.4.1",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
|
"jspdf": "^3.0.1",
|
||||||
"lint-staged": "^15.2.10",
|
"lint-staged": "^15.2.10",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mitt": "^3.0.0",
|
"mitt": "^3.0.0",
|
||||||
|
@ -3278,6 +3280,13 @@
|
||||||
"query-string": "*"
|
"query-string": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/raf": {
|
||||||
|
"version": "3.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz",
|
||||||
|
"integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/@types/sortablejs": {
|
"node_modules/@types/sortablejs": {
|
||||||
"version": "1.15.8",
|
"version": "1.15.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.15.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.15.8.tgz",
|
||||||
|
@ -3295,6 +3304,13 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/trusted-types": {
|
||||||
|
"version": "2.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||||
|
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/@types/unist": {
|
"node_modules/@types/unist": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
||||||
|
@ -4869,7 +4885,6 @@
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
|
||||||
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
|
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
|
||||||
"dev": true,
|
|
||||||
"license": "(MIT OR Apache-2.0)",
|
"license": "(MIT OR Apache-2.0)",
|
||||||
"bin": {
|
"bin": {
|
||||||
"atob": "bin/atob.js"
|
"atob": "bin/atob.js"
|
||||||
|
@ -4952,6 +4967,15 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/base64-arraybuffer": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/big.js": {
|
"node_modules/big.js": {
|
||||||
"version": "5.2.2",
|
"version": "5.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
||||||
|
@ -5075,6 +5099,18 @@
|
||||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/btoa": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==",
|
||||||
|
"license": "(MIT OR Apache-2.0)",
|
||||||
|
"bin": {
|
||||||
|
"btoa": "bin/btoa.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/buffer-from": {
|
"node_modules/buffer-from": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||||
|
@ -5244,6 +5280,26 @@
|
||||||
],
|
],
|
||||||
"license": "CC-BY-4.0"
|
"license": "CC-BY-4.0"
|
||||||
},
|
},
|
||||||
|
"node_modules/canvg": {
|
||||||
|
"version": "3.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz",
|
||||||
|
"integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.12.5",
|
||||||
|
"@types/raf": "^3.4.0",
|
||||||
|
"core-js": "^3.8.3",
|
||||||
|
"raf": "^3.4.1",
|
||||||
|
"regenerator-runtime": "^0.13.7",
|
||||||
|
"rgbcolor": "^1.0.1",
|
||||||
|
"stackblur-canvas": "^2.0.0",
|
||||||
|
"svg-pathdata": "^6.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/capital-case": {
|
"node_modules/capital-case": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz",
|
||||||
|
@ -5972,6 +6028,15 @@
|
||||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/css-line-break": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"utrie": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/css-select": {
|
"node_modules/css-select": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
|
||||||
|
@ -6423,6 +6488,16 @@
|
||||||
"domelementtype": "1"
|
"domelementtype": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dompurify": {
|
||||||
|
"version": "3.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz",
|
||||||
|
"integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==",
|
||||||
|
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||||
|
"optional": true,
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@types/trusted-types": "^2.0.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/domutils": {
|
"node_modules/domutils": {
|
||||||
"version": "1.7.0",
|
"version": "1.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
|
||||||
|
@ -8296,6 +8371,12 @@
|
||||||
"integrity": "sha512-V1PWovkspxQfssq/NnxoEyQo1DV+MRK/laPuPblIZmSjMN8P5u46OhlFQznSr9p/t0Sp8Uc6SbM3yCMfr0KU8g==",
|
"integrity": "sha512-V1PWovkspxQfssq/NnxoEyQo1DV+MRK/laPuPblIZmSjMN8P5u46OhlFQznSr9p/t0Sp8Uc6SbM3yCMfr0KU8g==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/fflate": {
|
||||||
|
"version": "0.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
|
||||||
|
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/figures": {
|
"node_modules/figures": {
|
||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz",
|
||||||
|
@ -9054,6 +9135,19 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/html2canvas": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"css-line-break": "^2.1.0",
|
||||||
|
"text-segmentation": "^1.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/htmlparser2": {
|
"node_modules/htmlparser2": {
|
||||||
"version": "3.10.1",
|
"version": "3.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
|
||||||
|
@ -10079,6 +10173,24 @@
|
||||||
"graceful-fs": "^4.1.6"
|
"graceful-fs": "^4.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jspdf": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-qaGIxqxetdoNnFQQXxTKUD9/Z7AloLaw94fFsOiJMxbfYdBbrBuhWmbzI8TVjrw7s3jBY1PFHofBKMV/wZPapg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.26.7",
|
||||||
|
"atob": "^2.1.2",
|
||||||
|
"btoa": "^1.2.1",
|
||||||
|
"fflate": "^0.8.1"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"canvg": "^3.0.11",
|
||||||
|
"core-js": "^3.6.0",
|
||||||
|
"dompurify": "^3.2.4",
|
||||||
|
"html2canvas": "^1.0.0-rc.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/keyv": {
|
"node_modules/keyv": {
|
||||||
"version": "4.5.4",
|
"version": "4.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||||
|
@ -11669,6 +11781,13 @@
|
||||||
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
|
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/performance-now": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
|
@ -12225,6 +12344,16 @@
|
||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/raf": {
|
||||||
|
"version": "3.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
|
||||||
|
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"performance-now": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/randombytes": {
|
"node_modules/randombytes": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||||
|
@ -12411,6 +12540,13 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/regenerator-runtime": {
|
||||||
|
"version": "0.13.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||||
|
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node_modules/regex-not": {
|
"node_modules/regex-not": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
|
||||||
|
@ -12679,6 +12815,16 @@
|
||||||
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
|
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/rgbcolor": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==",
|
||||||
|
"license": "MIT OR SEE LICENSE IN FEEL-FREE.md",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.15"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.46.1",
|
"version": "4.46.1",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.1.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.1.tgz",
|
||||||
|
@ -13629,6 +13775,16 @@
|
||||||
"node": ">=12.0.0"
|
"node": ">=12.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/stackblur-canvas": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.1.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/static-extend": {
|
"node_modules/static-extend": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
|
||||||
|
@ -14216,6 +14372,16 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/svg-pathdata": {
|
||||||
|
"version": "6.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz",
|
||||||
|
"integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/svgo": {
|
"node_modules/svgo": {
|
||||||
"version": "2.8.0",
|
"version": "2.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz",
|
||||||
|
@ -14338,6 +14504,15 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"node_modules/text-segmentation": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"utrie": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tinyexec": {
|
"node_modules/tinyexec": {
|
||||||
"version": "0.3.2",
|
"version": "0.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
|
||||||
|
@ -15284,6 +15459,15 @@
|
||||||
"node": ">= 0.4.0"
|
"node": ">= 0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/utrie": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-arraybuffer": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/v-viewer": {
|
"node_modules/v-viewer": {
|
||||||
"version": "3.0.22",
|
"version": "3.0.22",
|
||||||
"resolved": "https://registry.npmjs.org/v-viewer/-/v-viewer-3.0.22.tgz",
|
"resolved": "https://registry.npmjs.org/v-viewer/-/v-viewer-3.0.22.tgz",
|
||||||
|
|
|
@ -34,7 +34,9 @@
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"dayjs": "^1.11.4",
|
"dayjs": "^1.11.4",
|
||||||
"echarts": "^5.4.2",
|
"echarts": "^5.4.2",
|
||||||
|
"html2canvas": "^1.4.1",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
|
"jspdf": "^3.0.1",
|
||||||
"lint-staged": "^15.2.10",
|
"lint-staged": "^15.2.10",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mitt": "^3.0.0",
|
"mitt": "^3.0.0",
|
||||||
|
|
|
@ -24,7 +24,7 @@ export function batchAddAttachment(businessType: string, formData: FormData) {
|
||||||
* @param businessType 业务类型
|
* @param businessType 业务类型
|
||||||
* @param formData 表单数据
|
* @param formData 表单数据
|
||||||
*/
|
*/
|
||||||
export function addAttachment(formData: FormData) {
|
export function addAttachment(formData: FormData) {
|
||||||
return request<AttachInfoData>({
|
return request<AttachInfoData>({
|
||||||
url: `/attach-info/model`,
|
url: `/attach-info/model`,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
|
@ -39,7 +39,7 @@ export function addAttachment(formData: FormData) {
|
||||||
* @param businessType 业务类型
|
* @param businessType 业务类型
|
||||||
* @param formData 表单数据
|
* @param formData 表单数据
|
||||||
*/
|
*/
|
||||||
export function addAttachmentByDefectMarkPic(formData: FormData) {
|
export function addAttachmentByDefectMarkPic(formData: FormData) {
|
||||||
return request<AttachInfoData>({
|
return request<AttachInfoData>({
|
||||||
url: `/attach-info/defect_mark_pic`,
|
url: `/attach-info/defect_mark_pic`,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
|
@ -54,7 +54,7 @@ export function addAttachmentByDefectMarkPic(formData: FormData) {
|
||||||
* @param businessType 业务类型
|
* @param businessType 业务类型
|
||||||
* @param formData 表单数据
|
* @param formData 表单数据
|
||||||
*/
|
*/
|
||||||
export function addAttachInsurance(formData: FormData) {
|
export function addAttachInsurance(formData: FormData) {
|
||||||
return request<AttachInfoData>({
|
return request<AttachInfoData>({
|
||||||
url: `/attach-info/insurance_file`,
|
url: `/attach-info/insurance_file`,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
|
|
|
@ -1,12 +1,22 @@
|
||||||
import http from '@/utils/http'
|
import http from '@/utils/http'
|
||||||
|
import {
|
||||||
|
type RegulationTypeSearchRequest,
|
||||||
|
type RegulationTypeSearchResponse,
|
||||||
|
type RegulationProposalSearchRequest,
|
||||||
|
type RegulationProposalSearchResponse
|
||||||
|
} from './type'
|
||||||
|
|
||||||
// 制度管理API接口
|
// 制度管理API接口
|
||||||
export const regulationApi = {
|
export const regulationApi = {
|
||||||
// 获取制度列表
|
// 获取制度列表
|
||||||
getRegulationList: (params: {
|
getRegulationList: (params: {
|
||||||
page: number
|
page?: number
|
||||||
size: number
|
size?: number
|
||||||
}) => {
|
status?: string
|
||||||
|
type?: string
|
||||||
|
title?: string
|
||||||
|
proposer?: string
|
||||||
|
}): Promise<RegulationProposalSearchResponse> => {
|
||||||
return http.get('/regulation', params)
|
return http.get('/regulation', params)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -23,6 +33,7 @@ export const regulationApi = {
|
||||||
scope: string
|
scope: string
|
||||||
level: string
|
level: string
|
||||||
remark?: string
|
remark?: string
|
||||||
|
createBy?: string
|
||||||
}) => {
|
}) => {
|
||||||
return http.post('/regulation/proposal', data)
|
return http.post('/regulation/proposal', data)
|
||||||
},
|
},
|
||||||
|
@ -32,17 +43,22 @@ export const regulationApi = {
|
||||||
return http.put(`/regulation/proposal/${regulationId}`, data)
|
return http.put(`/regulation/proposal/${regulationId}`, data)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 公示提案
|
||||||
|
publishProposal: (regulationId: string) => {
|
||||||
|
return http.post(`/regulation/proposal/${regulationId}/publish`)
|
||||||
|
},
|
||||||
|
|
||||||
// 删除制度提案
|
// 删除制度提案
|
||||||
deleteProposal: (regulationId: string) => {
|
deleteProposal: (regulationId: string) => {
|
||||||
return http.del(`/regulation/proposal/${regulationId}`)
|
return http.del(`/regulation/proposal/${regulationId}`)
|
||||||
},
|
},
|
||||||
|
|
||||||
// 发布制度
|
// 公示制度
|
||||||
publishRegulation: (regulationId: string) => {
|
publishRegulation: (regulationId: string) => {
|
||||||
return http.post(`/regulation/${regulationId}/publish`)
|
return http.post(`/regulation/${regulationId}/approve`)
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取已发布制度列表
|
// 获取已公示制度列表
|
||||||
getPublishedRegulationList: (params: {
|
getPublishedRegulationList: (params: {
|
||||||
page: number
|
page: number
|
||||||
size: number
|
size: number
|
||||||
|
@ -54,5 +70,39 @@ export const regulationApi = {
|
||||||
// 确认制度知晓
|
// 确认制度知晓
|
||||||
confirmRegulation: (regulationId: string) => {
|
confirmRegulation: (regulationId: string) => {
|
||||||
return http.post(`/regulation/${regulationId}/confirm`)
|
return http.post(`/regulation/${regulationId}/confirm`)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 搜索制度类型(后端搜索接口)
|
||||||
|
searchRegulationTypes: (params: {
|
||||||
|
page?: number
|
||||||
|
size?: number
|
||||||
|
typeName?: string
|
||||||
|
status?: string
|
||||||
|
remark?: string
|
||||||
|
}) => {
|
||||||
|
return http.get('/regulation/types', params)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 创建制度类型
|
||||||
|
createRegulationType: (data: {
|
||||||
|
typeName: string
|
||||||
|
sortOrder?: number
|
||||||
|
isEnabled?: string
|
||||||
|
}) => {
|
||||||
|
return http.post('/regulation/types', data)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 更新制度类型
|
||||||
|
updateRegulationType: (typeId: string, data: {
|
||||||
|
typeName: string
|
||||||
|
sortOrder?: number
|
||||||
|
isEnabled?: string
|
||||||
|
}) => {
|
||||||
|
return http.put(`/regulation/types/${typeId}`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除制度类型
|
||||||
|
deleteRegulationType: (typeId: string) => {
|
||||||
|
return http.del(`/regulation/types/${typeId}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,11 +1,8 @@
|
||||||
// 制度状态枚举
|
// 制度状态枚举
|
||||||
export enum RegulationStatus {
|
export enum RegulationStatus {
|
||||||
DRAFT = 'DRAFT', // 草稿
|
DRAFT = 'DRAFT', // 草稿
|
||||||
VOTING = 'VOTING', // 投票中
|
|
||||||
APPROVED = 'APPROVED', // 已通过
|
APPROVED = 'APPROVED', // 已通过
|
||||||
REJECTED = 'REJECTED', // 已否决
|
PUBLISHED = 'PUBLISHED', // 已公示
|
||||||
PUBLISHED = 'PUBLISHED', // 已发布
|
|
||||||
ARCHIVED = 'ARCHIVED' // 已归档
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 制度级别枚举
|
// 制度级别枚举
|
||||||
|
@ -22,9 +19,7 @@ export interface Regulation {
|
||||||
content: string
|
content: string
|
||||||
regulationType: string
|
regulationType: string
|
||||||
status: RegulationStatus
|
status: RegulationStatus
|
||||||
publisherId: string
|
publishTime: string // 公示时间
|
||||||
publisherName: string
|
|
||||||
publishTime: string
|
|
||||||
effectiveTime: string
|
effectiveTime: string
|
||||||
expireTime: string
|
expireTime: string
|
||||||
scope: string
|
scope: string
|
||||||
|
@ -38,6 +33,7 @@ export interface Regulation {
|
||||||
page: number
|
page: number
|
||||||
pageSize: number
|
pageSize: number
|
||||||
delFlag: string
|
delFlag: string
|
||||||
|
confirmStatus?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建提案请求接口
|
// 创建提案请求接口
|
||||||
|
@ -48,10 +44,78 @@ export interface CreateProposalRequest {
|
||||||
scope: string
|
scope: string
|
||||||
level: RegulationLevel
|
level: RegulationLevel
|
||||||
remark?: string
|
remark?: string
|
||||||
|
createBy?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分页参数接口
|
// 分页参数接口
|
||||||
export interface PaginationParams {
|
export interface PaginationParams {
|
||||||
page: number
|
page: number
|
||||||
size: number
|
size: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 制度类型接口
|
||||||
|
export interface RegulationType {
|
||||||
|
typeId: string
|
||||||
|
typeName: string
|
||||||
|
sortOrder: number
|
||||||
|
isEnabled: string
|
||||||
|
remark?: string
|
||||||
|
createBy: string
|
||||||
|
createTime: string
|
||||||
|
updateBy: string
|
||||||
|
updateTime: string
|
||||||
|
delFlag: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建制度类型请求接口
|
||||||
|
export interface CreateRegulationTypeRequest {
|
||||||
|
typeName: string
|
||||||
|
sortOrder?: number
|
||||||
|
isEnabled?: string
|
||||||
|
remark?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新制度类型请求接口
|
||||||
|
export interface UpdateRegulationTypeRequest {
|
||||||
|
typeName: string
|
||||||
|
sortOrder?: number
|
||||||
|
isEnabled?: string
|
||||||
|
remark?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 制度类型搜索请求接口
|
||||||
|
export interface RegulationTypeSearchRequest {
|
||||||
|
page?: number
|
||||||
|
size?: number
|
||||||
|
typeName?: string
|
||||||
|
status?: string
|
||||||
|
remark?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 制度类型搜索响应接口
|
||||||
|
export interface RegulationTypeSearchResponse {
|
||||||
|
records: RegulationType[]
|
||||||
|
total: number
|
||||||
|
current: number
|
||||||
|
size: number
|
||||||
|
pages: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 制度提案搜索请求接口
|
||||||
|
export interface RegulationProposalSearchRequest {
|
||||||
|
page?: number
|
||||||
|
size?: number
|
||||||
|
status?: string
|
||||||
|
type?: string
|
||||||
|
title?: string
|
||||||
|
proposer?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 制度提案搜索响应接口
|
||||||
|
export interface RegulationProposalSearchResponse {
|
||||||
|
records: Regulation[]
|
||||||
|
total: number
|
||||||
|
current: number
|
||||||
|
size: number
|
||||||
|
pages: number
|
||||||
}
|
}
|
|
@ -37,14 +37,27 @@ export const systemRoutes: RouteRecordRaw[] = [
|
||||||
path: '/regulation/system-regulation',
|
path: '/regulation/system-regulation',
|
||||||
name: 'SystemRegulation',
|
name: 'SystemRegulation',
|
||||||
component: () => import('@/views/regulation/repository.vue'),
|
component: () => import('@/views/regulation/repository.vue'),
|
||||||
meta: { title: '制度确认', icon: 'file-text', hidden: false },
|
meta: { title: '制度公告', icon: 'file-text', hidden: false },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/regulation/process-management',
|
path: '/regulation/process-management',
|
||||||
name: 'ProcessManagement',
|
name: 'ProcessManagement',
|
||||||
component: () => import('@/views/regulation/confirm.vue'),
|
component: () => import('@/views/regulation/confirm.vue'),
|
||||||
meta: { title: '流程管理', icon: 'workflow', hidden: false },
|
meta: { title: '制度公示', icon: 'workflow', hidden: false },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/regulation/proposal',
|
||||||
|
name: 'RegulationProposal',
|
||||||
|
component: () => import('@/views/regulation/proposal/index.vue'),
|
||||||
|
meta: { title: '制度提案', icon: 'edit', hidden: false },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/regulation/type',
|
||||||
|
name: 'RegulationType',
|
||||||
|
component: () => import('@/views/regulation/type/index.vue'),
|
||||||
|
meta: { title: '制度类型', icon: 'tag', hidden: false },
|
||||||
|
},
|
||||||
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -545,9 +558,9 @@ export const systemRoutes: RouteRecordRaw[] = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'project-management/project-template/information-retrieval',
|
path: '/project-management/project-template/information-retrieval',
|
||||||
name: 'InformationRetrieval',
|
name: 'InformationRetrieval',
|
||||||
component: () => import ('@/views/default/error/404.vue'),
|
component: () => import ('@/views/project-management/bidding/information-retrieval/index.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: '信息检索(N)',
|
title: '信息检索(N)',
|
||||||
icon: 'trophy',
|
icon: 'trophy',
|
||||||
|
@ -796,11 +809,11 @@ export const systemRoutes: RouteRecordRaw[] = [
|
||||||
{
|
{
|
||||||
path: '/construction-operation-platform/implementation-workflow/data-processing/data-storage/attachment',
|
path: '/construction-operation-platform/implementation-workflow/data-processing/data-storage/attachment',
|
||||||
name: 'AttachmentManagement',
|
name: 'AttachmentManagement',
|
||||||
component: () => import('@/views/operation-platform/data-processing/data-storage/index.vue'),
|
component: () => import('@/views/construction-operation-platform/implementation-workflow/data-processing/data-storage/index.vue'),
|
||||||
meta: { title: '附件管理', icon: 'attachment', hidden: false },
|
meta: { title: '附件管理', icon: 'attachment', hidden: false },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/construction-operation-platform/implementation-workflow/data-processing/data-storage/model-config',
|
path: '/construction-operation-platform/implementation-workflow/data-processing/model-config',
|
||||||
name: 'ModelConfig',
|
name: 'ModelConfig',
|
||||||
component: () => import('@/views/construction-operation-platform/implementation-workflow/data-processing/model-config/index.vue'),
|
component: () => import('@/views/construction-operation-platform/implementation-workflow/data-processing/model-config/index.vue'),
|
||||||
meta: { title: '模型配置', icon: 'robot', hidden: false },
|
meta: { title: '模型配置', icon: 'robot', hidden: false },
|
||||||
|
@ -811,8 +824,23 @@ export const systemRoutes: RouteRecordRaw[] = [
|
||||||
{
|
{
|
||||||
path: '/construction-operation-platform/implementation-workflow/data-processing/data-storage/preprocessed-data',
|
path: '/construction-operation-platform/implementation-workflow/data-processing/data-storage/preprocessed-data',
|
||||||
name: 'PreprocessedData',
|
name: 'PreprocessedData',
|
||||||
component: () => import('@/views/construction-operation-platform/implementation-workflow/data-processing/data-preprocessing/index.vue'),
|
component: () => import('@/components/ParentView/index.vue'),
|
||||||
|
redirect: '/construction-operation-platform/implementation-workflow/data-processing/data-storage',
|
||||||
meta: { title: '数据预处理', icon: 'filter', hidden: false },
|
meta: { title: '数据预处理', icon: 'filter', hidden: false },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/construction-operation-platform/implementation-workflow/data-processing/data-storage/preprocessed-data/ImageBatchUpload',
|
||||||
|
name: 'ImageBatchUpload',
|
||||||
|
component: () => import('@/views/construction-operation-platform/implementation-workflow/data-processing/data-preprocessing/index.vue'),
|
||||||
|
meta: { title: '批量上传', icon: 'file', hidden: false },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/construction-operation-platform/implementation-workflow/data-processing/data-storage/preprocessed-data/ImageSorting',
|
||||||
|
name: 'ImageSorting',
|
||||||
|
component: () => import('@/views/construction-operation-platform/implementation-workflow/data-processing/image-sorting/index.vue'),
|
||||||
|
meta: { title: '图像分拣', icon: 'attachment', hidden: false },
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/construction-operation-platform/implementation-workflow/data-processing/intelligent-inspection',
|
path: '/construction-operation-platform/implementation-workflow/data-processing/intelligent-inspection',
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
import jsPDF from 'jspdf'
|
||||||
|
import html2canvas from 'html2canvas'
|
||||||
|
|
||||||
|
// PDF生成器类
|
||||||
|
export class PDFGenerator {
|
||||||
|
// 生成制度PDF
|
||||||
|
static async generateRegulationPDF(regulation: any): Promise<Blob> {
|
||||||
|
// 创建临时HTML元素
|
||||||
|
const tempDiv = document.createElement('div')
|
||||||
|
tempDiv.style.position = 'absolute'
|
||||||
|
tempDiv.style.left = '-9999px'
|
||||||
|
tempDiv.style.top = '-9999px'
|
||||||
|
tempDiv.style.width = '800px'
|
||||||
|
tempDiv.style.padding = '40px'
|
||||||
|
tempDiv.style.backgroundColor = 'white'
|
||||||
|
tempDiv.style.fontFamily = 'Arial, sans-serif'
|
||||||
|
tempDiv.style.fontSize = '14px'
|
||||||
|
tempDiv.style.lineHeight = '1.6'
|
||||||
|
tempDiv.style.color = '#333'
|
||||||
|
|
||||||
|
// 添加水印样式
|
||||||
|
tempDiv.style.position = 'relative'
|
||||||
|
tempDiv.style.overflow = 'hidden'
|
||||||
|
|
||||||
|
// 生成HTML内容
|
||||||
|
tempDiv.innerHTML = `
|
||||||
|
<div style="position: relative; z-index: 1;">
|
||||||
|
<!-- 水印 -->
|
||||||
|
<div style="
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%) rotate(-45deg);
|
||||||
|
font-size: 48px;
|
||||||
|
color: rgba(200, 200, 200, 0.3);
|
||||||
|
font-weight: bold;
|
||||||
|
z-index: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
">迪特聚能科技</div>
|
||||||
|
|
||||||
|
<!-- 标题 -->
|
||||||
|
<h1 style="
|
||||||
|
text-align: center;
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
color: #333;
|
||||||
|
">${regulation.title || '制度文档'}</h1>
|
||||||
|
|
||||||
|
<!-- 基本信息 -->
|
||||||
|
<div style="margin-bottom: 30px;">
|
||||||
|
<table style="width: 100%; border-collapse: collapse;">
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 8px 0; border-bottom: 1px solid #eee;"><strong>制度类型:</strong>${regulation.regulationType || '-'}</td>
|
||||||
|
<td style="padding: 8px 0; border-bottom: 1px solid #eee;"><strong>发布人:</strong>${regulation.createByName || regulation.createBy || '-'}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 8px 0; border-bottom: 1px solid #eee;"><strong>发布时间:</strong>${regulation.publishTime || '-'}</td>
|
||||||
|
<td style="padding: 8px 0; border-bottom: 1px solid #eee;"><strong>生效日期:</strong>${regulation.effectiveTime || '-'}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 8px 0; border-bottom: 1px solid #eee;"><strong>适用范围:</strong>${regulation.scope || '-'}</td>
|
||||||
|
<td style="padding: 8px 0; border-bottom: 1px solid #eee;"><strong>制度级别:</strong>${regulation.level || '-'}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 8px 0; border-bottom: 1px solid #eee;"><strong>版本:</strong>${regulation.version || '1.0'}</td>
|
||||||
|
<td style="padding: 8px 0; border-bottom: 1px solid #eee;"></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 分隔线 -->
|
||||||
|
<hr style="border: none; border-top: 2px solid #ddd; margin: 30px 0;">
|
||||||
|
|
||||||
|
<!-- 制度内容 -->
|
||||||
|
<div style="margin-bottom: 30px;">
|
||||||
|
<h2 style="font-size: 18px; font-weight: bold; margin-bottom: 15px; color: #333;">制度内容:</h2>
|
||||||
|
<div style="
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
line-height: 1.8;
|
||||||
|
text-align: justify;
|
||||||
|
">${regulation.content || ''}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${regulation.remark ? `
|
||||||
|
<!-- 备注信息 -->
|
||||||
|
<div style="margin-bottom: 30px;">
|
||||||
|
<h2 style="font-size: 18px; font-weight: bold; margin-bottom: 15px; color: #333;">备注:</h2>
|
||||||
|
<div style="
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
line-height: 1.8;
|
||||||
|
text-align: justify;
|
||||||
|
">${regulation.remark}</div>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
<!-- 页脚 -->
|
||||||
|
<div style="
|
||||||
|
margin-top: 50px;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
">
|
||||||
|
生成时间:${new Date().toLocaleString('zh-CN')} | 制度ID:${regulation.regulationId || '-'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
|
||||||
|
// 添加到DOM
|
||||||
|
document.body.appendChild(tempDiv)
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用html2canvas转换为图片
|
||||||
|
const canvas = await html2canvas(tempDiv, {
|
||||||
|
scale: 2, // 提高清晰度
|
||||||
|
useCORS: true,
|
||||||
|
allowTaint: true,
|
||||||
|
backgroundColor: '#ffffff',
|
||||||
|
width: 800,
|
||||||
|
height: tempDiv.scrollHeight
|
||||||
|
})
|
||||||
|
|
||||||
|
// 创建PDF
|
||||||
|
const imgData = canvas.toDataURL('image/png')
|
||||||
|
const pdf = new jsPDF('p', 'mm', 'a4')
|
||||||
|
|
||||||
|
const imgWidth = 210 // A4宽度
|
||||||
|
const pageHeight = 295 // A4高度
|
||||||
|
const imgHeight = (canvas.height * imgWidth) / canvas.width
|
||||||
|
let heightLeft = imgHeight
|
||||||
|
let position = 0
|
||||||
|
|
||||||
|
// 添加第一页
|
||||||
|
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight)
|
||||||
|
heightLeft -= pageHeight
|
||||||
|
|
||||||
|
// 添加后续页面
|
||||||
|
while (heightLeft >= 0) {
|
||||||
|
position = heightLeft - imgHeight
|
||||||
|
pdf.addPage()
|
||||||
|
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight)
|
||||||
|
heightLeft -= pageHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回PDF blob
|
||||||
|
return pdf.output('blob')
|
||||||
|
} finally {
|
||||||
|
// 清理临时元素
|
||||||
|
document.body.removeChild(tempDiv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载PDF文件
|
||||||
|
static downloadPDF(blob: Blob, filename: string) {
|
||||||
|
const url = URL.createObjectURL(blob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.download = filename
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -105,8 +105,16 @@ const formData = reactive({
|
||||||
const fetchBusinessTypes = async () => {
|
const fetchBusinessTypes = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await getAttachBusinessTypes()
|
const res = await getAttachBusinessTypes()
|
||||||
|
console.log("res:",res);
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
businessTypes.value = res.data
|
res.data.forEach(item => {
|
||||||
|
const key = Object.keys(item)[0];
|
||||||
|
const value = item[key];
|
||||||
|
businessTypes.value.push({
|
||||||
|
name: value,
|
||||||
|
code:key
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取业务类型失败:', error)
|
console.error('获取业务类型失败:', error)
|
||||||
|
|
|
@ -0,0 +1,603 @@
|
||||||
|
<template>
|
||||||
|
<GiPageLayout>
|
||||||
|
<div class="data-preprocessing-container">
|
||||||
|
<!-- 上传进度框 -->
|
||||||
|
<div v-if="uploadState.visible" class="upload-progress-fixed">
|
||||||
|
<a-card :title="`上传进度 (${uploadState.percent}%)`" :bordered="false">
|
||||||
|
<a-progress
|
||||||
|
:percent="uploadState.percent"
|
||||||
|
:status="uploadState.status"
|
||||||
|
:stroke-width="16"
|
||||||
|
/>
|
||||||
|
<div class="progress-details">
|
||||||
|
<p><icon-file /> {{ uploadState.currentFile || '准备中...' }}</p>
|
||||||
|
<p><icon-check-circle /> 已完成 {{ uploadState.uploadedCount }}/{{ uploadState.totalCount }}</p>
|
||||||
|
<p><icon-clock-circle /> 状态: {{ getStatusText(uploadState.status) }}</p>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="step-panel">
|
||||||
|
<div class="step-header">
|
||||||
|
<h3>批量上传图片</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="data-selection">
|
||||||
|
<a-form :model="form" layout="vertical">
|
||||||
|
<!-- 项目选择 -->
|
||||||
|
<a-form-item label="所属项目" required>
|
||||||
|
<a-select
|
||||||
|
v-model="form.projectId"
|
||||||
|
placeholder="请选择项目"
|
||||||
|
allow-search
|
||||||
|
:filter-option="filterProjectOption"
|
||||||
|
>
|
||||||
|
<a-option
|
||||||
|
v-for="project in projectList"
|
||||||
|
:key="project.id"
|
||||||
|
:value="project.id"
|
||||||
|
:label="project.name"
|
||||||
|
/>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<!-- 图片来源选择 -->
|
||||||
|
<a-form-item label="图片来源" required>
|
||||||
|
<a-select
|
||||||
|
v-model="form.imageSource"
|
||||||
|
placeholder="请选择图片来源"
|
||||||
|
allow-search
|
||||||
|
:filter-option="filterSourceOption"
|
||||||
|
>
|
||||||
|
<a-option
|
||||||
|
v-for="source in imageSources"
|
||||||
|
:key="source.value"
|
||||||
|
:value="source.value"
|
||||||
|
:label="source.label"
|
||||||
|
/>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<!-- 文件夹操作 -->
|
||||||
|
<a-form-item label="文件操作">
|
||||||
|
<div class="folder-actions">
|
||||||
|
<a-upload
|
||||||
|
ref="uploadRef"
|
||||||
|
directory
|
||||||
|
:multiple="true"
|
||||||
|
:show-file-list="false"
|
||||||
|
accept="image/*"
|
||||||
|
:key="uploadKey"
|
||||||
|
@change="handleFolderSelect"
|
||||||
|
>
|
||||||
|
<template #upload-button>
|
||||||
|
<a-button type="outline">
|
||||||
|
<template #icon>
|
||||||
|
<icon-folder />
|
||||||
|
</template>
|
||||||
|
选择文件夹
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</a-upload>
|
||||||
|
<a-button
|
||||||
|
type="outline"
|
||||||
|
status="warning"
|
||||||
|
@click="clearFileList"
|
||||||
|
:disabled="selectedFiles.length === 0"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<icon-delete />
|
||||||
|
</template>
|
||||||
|
清空列表
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
:loading="uploading"
|
||||||
|
:disabled="!canUpload"
|
||||||
|
@click="handleUpload"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<icon-upload />
|
||||||
|
</template>
|
||||||
|
开始上传
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<!-- 文件列表 -->
|
||||||
|
<a-form-item v-if="selectedFiles.length > 0">
|
||||||
|
<div class="file-list-container">
|
||||||
|
<div class="file-list-header">
|
||||||
|
<span>已选择 {{ selectedFiles.length }} 个文件(选中 {{ checkedFiles.length }} 个)</span>
|
||||||
|
<a-checkbox
|
||||||
|
v-model="selectAll"
|
||||||
|
:indeterminate="indeterminate"
|
||||||
|
@change="handleSelectAllChange"
|
||||||
|
>
|
||||||
|
全选
|
||||||
|
</a-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="file-table-wrapper">
|
||||||
|
<a-table
|
||||||
|
:data="selectedFiles"
|
||||||
|
:columns="fileColumns"
|
||||||
|
:row-selection="rowSelection"
|
||||||
|
:pagination="false"
|
||||||
|
row-key="uid"
|
||||||
|
size="small"
|
||||||
|
bordered
|
||||||
|
>
|
||||||
|
<!-- 表格列模板 -->
|
||||||
|
<template #thumbnail="{ record }">
|
||||||
|
<div class="thumbnail-cell">
|
||||||
|
<img
|
||||||
|
v-if="isImage(record.type)"
|
||||||
|
:src="record.preview"
|
||||||
|
class="thumbnail-image"
|
||||||
|
alt="预览"
|
||||||
|
/>
|
||||||
|
<div v-else class="file-icon">
|
||||||
|
<icon-file />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #fileType="{ record }">
|
||||||
|
<a-tag :color="getFileTypeColor(record.type)" size="small">
|
||||||
|
{{ record.type }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
<template #fileSize="{ record }">
|
||||||
|
{{ formatFileSize(record.size) }}
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</GiPageLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, computed, nextTick } from 'vue'
|
||||||
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
import type { UploadItem } from '@arco-design/web-vue/es/upload'
|
||||||
|
import type { SelectOptionData, TableColumnData, TableRowSelection } from '@arco-design/web-vue'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
import {
|
||||||
|
getProjectList,
|
||||||
|
getImageSources
|
||||||
|
} from '@/apis/industrial-image'
|
||||||
|
|
||||||
|
// 类型定义
|
||||||
|
interface FileItem {
|
||||||
|
uid: string
|
||||||
|
name: string
|
||||||
|
type: string
|
||||||
|
size: number
|
||||||
|
file: File
|
||||||
|
preview?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type UploadStatus = 'waiting' | 'uploading' | 'success' | 'error'
|
||||||
|
|
||||||
|
// 数据状态
|
||||||
|
const projectList = ref([])
|
||||||
|
|
||||||
|
const imageSources = ref([])
|
||||||
|
|
||||||
|
// 获取项目列表
|
||||||
|
const fetchProjectList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getProjectList({ page: 1, pageSize: 1000 });
|
||||||
|
projectList.value = res.data.map(item => ({
|
||||||
|
name: item.projectName,
|
||||||
|
id: item.projectId
|
||||||
|
}))
|
||||||
|
} catch (error) {
|
||||||
|
Message.error('获取项目列表失败')
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取图片来源
|
||||||
|
const fetchImageSourceList = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getImageSources();
|
||||||
|
res.data.forEach(item => {
|
||||||
|
const key = Object.keys(item)[0];
|
||||||
|
const value = item[key];
|
||||||
|
imageSources.value.push({
|
||||||
|
label: value,
|
||||||
|
value:key
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
Message.error('获取项目列表失败')
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
projectId: undefined as number | undefined,
|
||||||
|
imageSource: undefined as string | undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
const selectedFiles = ref<FileItem[]>([])
|
||||||
|
const checkedFiles = ref<string[]>([])
|
||||||
|
const uploading = ref(false)
|
||||||
|
const uploadRef = ref()
|
||||||
|
const uploadKey = ref(0)
|
||||||
|
|
||||||
|
const uploadState = reactive({
|
||||||
|
visible: false,
|
||||||
|
percent: 0,
|
||||||
|
status: 'waiting' as UploadStatus,
|
||||||
|
currentFile: '',
|
||||||
|
uploadedCount: 0,
|
||||||
|
totalCount: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算属性
|
||||||
|
const canUpload = computed(() => {
|
||||||
|
return checkedFiles.value.length > 0
|
||||||
|
&& !!form.projectId
|
||||||
|
&& !!form.imageSource
|
||||||
|
&& !uploading.value
|
||||||
|
})
|
||||||
|
|
||||||
|
const selectAll = ref(false)
|
||||||
|
const indeterminate = computed(() => {
|
||||||
|
return checkedFiles.value.length > 0 &&
|
||||||
|
checkedFiles.value.length < selectedFiles.value.length
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表格列配置
|
||||||
|
const fileColumns: TableColumnData[] = [
|
||||||
|
{
|
||||||
|
title: '选择',
|
||||||
|
dataIndex: 'selection',
|
||||||
|
type: 'selection',
|
||||||
|
width: 60,
|
||||||
|
align: 'center'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '预览',
|
||||||
|
dataIndex: 'thumbnail',
|
||||||
|
slotName: 'thumbnail',
|
||||||
|
width: 100,
|
||||||
|
align: 'center'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '文件名',
|
||||||
|
dataIndex: 'name',
|
||||||
|
ellipsis: true,
|
||||||
|
tooltip: true,
|
||||||
|
width: 300
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '类型',
|
||||||
|
dataIndex: 'type',
|
||||||
|
slotName: 'fileType',
|
||||||
|
width: 100,
|
||||||
|
align: 'center'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '大小',
|
||||||
|
dataIndex: 'size',
|
||||||
|
slotName: 'fileSize',
|
||||||
|
width: 100,
|
||||||
|
align: 'center'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// 行选择配置
|
||||||
|
const rowSelection = reactive<TableRowSelection>({
|
||||||
|
type: 'checkbox',
|
||||||
|
showCheckedAll: false,
|
||||||
|
selectedRowKeys: checkedFiles,
|
||||||
|
onChange: (rowKeys: string[]) => {
|
||||||
|
checkedFiles.value = rowKeys
|
||||||
|
selectAll.value = rowKeys.length === selectedFiles.value.length
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 搜索过滤函数
|
||||||
|
const filterProjectOption = (inputValue: string, option: SelectOptionData) => {
|
||||||
|
return option.label.toLowerCase().includes(inputValue.toLowerCase())
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterSourceOption = (inputValue: string, option: SelectOptionData) => {
|
||||||
|
return option.label.toLowerCase().includes(inputValue.toLowerCase())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件选择处理
|
||||||
|
const handleFolderSelect = async (fileList: UploadItem[]) => {
|
||||||
|
// 1. 清空现有状态
|
||||||
|
clearFileList()
|
||||||
|
|
||||||
|
// 2. 处理新文件
|
||||||
|
const newFiles: FileItem[] = []
|
||||||
|
for (const item of fileList) {
|
||||||
|
const file = item.file
|
||||||
|
if (!file) continue
|
||||||
|
|
||||||
|
const fileType = file.type.split('/')[0] || 'unknown'
|
||||||
|
const preview = fileType === 'image' ? URL.createObjectURL(file) : undefined
|
||||||
|
|
||||||
|
newFiles.push({
|
||||||
|
uid: item.uid,
|
||||||
|
name: file.name,
|
||||||
|
type: fileType,
|
||||||
|
size: file.size,
|
||||||
|
file: file,
|
||||||
|
preview: preview
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 更新状态
|
||||||
|
selectedFiles.value = newFiles
|
||||||
|
checkedFiles.value = newFiles.map(f => f.uid)
|
||||||
|
selectAll.value = true
|
||||||
|
|
||||||
|
// 4. 重置上传组件(通过改变key强制重新创建组件)
|
||||||
|
uploadKey.value++
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空文件列表
|
||||||
|
const clearFileList = () => {
|
||||||
|
// 释放预览URL
|
||||||
|
selectedFiles.value.forEach(file => {
|
||||||
|
if (file.preview) URL.revokeObjectURL(file.preview)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 重置状态
|
||||||
|
selectedFiles.value = []
|
||||||
|
checkedFiles.value = []
|
||||||
|
selectAll.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理全选变化
|
||||||
|
const handleSelectAllChange = (checked: boolean) => {
|
||||||
|
checkedFiles.value = checked ? selectedFiles.value.map(f => f.uid) : []
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传处理
|
||||||
|
const handleUpload = async () => {
|
||||||
|
if (!canUpload.value) {
|
||||||
|
Message.error('请完成所有必填项并选择文件')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置上传状态
|
||||||
|
Object.assign(uploadState, {
|
||||||
|
visible: true,
|
||||||
|
percent: 0,
|
||||||
|
status: 'uploading',
|
||||||
|
currentFile: '',
|
||||||
|
uploadedCount: 0,
|
||||||
|
totalCount: checkedFiles.value.length
|
||||||
|
})
|
||||||
|
|
||||||
|
uploading.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const filesToUpload = selectedFiles.value.filter(f => checkedFiles.value.includes(f.uid))
|
||||||
|
|
||||||
|
const formData = new FormData()
|
||||||
|
|
||||||
|
// 添加所有图片文件到FormData
|
||||||
|
filesToUpload.forEach(file => {
|
||||||
|
formData.append('files', file);
|
||||||
|
});
|
||||||
|
|
||||||
|
let url =`http://pms.dtyx.net:9158/image/${form.projectId}/${form.imageSource}/upload-batch`;
|
||||||
|
let res = await axios.post(
|
||||||
|
url,
|
||||||
|
formData,
|
||||||
|
{
|
||||||
|
onUploadProgress: (progressEvent) => {
|
||||||
|
if (progressEvent.total) {
|
||||||
|
uploadedBytes = progressEvent.loaded
|
||||||
|
const elapsedTime = (Date.now() - startTime) / 1000
|
||||||
|
const speed = uploadedBytes / elapsedTime
|
||||||
|
uploadState.speed = `${formatFileSize(speed)}/s`
|
||||||
|
|
||||||
|
const remainingBytes = totalBytes - uploadedBytes
|
||||||
|
uploadState.remainingTime = formatTime(remainingBytes / speed)
|
||||||
|
|
||||||
|
uploadState.percent = Math.round((uploadedBytes / totalBytes) * 100)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
console.log("res:",res);
|
||||||
|
uploadState.status = 'success'
|
||||||
|
Message.success(`成功上传 ${filesToUpload.length} 个文件`)
|
||||||
|
} catch (error) {
|
||||||
|
uploadState.status = 'error'
|
||||||
|
Message.error('上传失败: ' + (error as Error).message)
|
||||||
|
} finally {
|
||||||
|
uploading.value = false
|
||||||
|
// 5秒后自动隐藏进度框
|
||||||
|
setTimeout(() => uploadState.visible = false, 5000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助函数
|
||||||
|
const getStatusText = (status: UploadStatus) => {
|
||||||
|
const statusMap = {
|
||||||
|
waiting: '等待上传',
|
||||||
|
uploading: '上传中',
|
||||||
|
success: '上传成功',
|
||||||
|
error: '上传失败'
|
||||||
|
}
|
||||||
|
return statusMap[status] || status
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFileTypeColor = (type: string) => {
|
||||||
|
const colors: Record<string, string> = {
|
||||||
|
image: 'arcoblue',
|
||||||
|
video: 'green',
|
||||||
|
audio: 'orange',
|
||||||
|
document: 'purple'
|
||||||
|
}
|
||||||
|
return colors[type.toLowerCase()] || 'gray'
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatFileSize = (bytes: number) => {
|
||||||
|
if (bytes === 0) return '0 B'
|
||||||
|
const k = 1024
|
||||||
|
const sizes = ['B', 'KB', 'MB', 'GB']
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||||
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
const isImage = (type: string) => type === 'image'
|
||||||
|
|
||||||
|
// 在组件挂载时获取项目列表
|
||||||
|
onMounted(() => {
|
||||||
|
fetchProjectList()
|
||||||
|
fetchImageSourceList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.data-preprocessing-container {
|
||||||
|
position: relative;
|
||||||
|
padding: 20px;
|
||||||
|
min-height: calc(100vh - 40px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-panel {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 24px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-header {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
padding-bottom: 16px;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.folder-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-list-container {
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 12px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-list-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-table-wrapper {
|
||||||
|
max-height: 500px;
|
||||||
|
overflow-y: auto;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-table {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
:deep(.arco-table-th) {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1;
|
||||||
|
background-color: var(--color-fill-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail-cell {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 60px;
|
||||||
|
|
||||||
|
.thumbnail-image {
|
||||||
|
max-height: 60px;
|
||||||
|
max-width: 80px;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-icon {
|
||||||
|
font-size: 24px;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 上传进度框样式 */
|
||||||
|
.upload-progress-fixed {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 24px;
|
||||||
|
right: 24px;
|
||||||
|
width: 360px;
|
||||||
|
z-index: 1000;
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
|
||||||
|
border-radius: 8px;
|
||||||
|
animation: fadeIn 0.3s ease;
|
||||||
|
|
||||||
|
:deep(.arco-card-header) {
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
padding-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-progress-text) {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-details {
|
||||||
|
margin-top: 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--color-text-2);
|
||||||
|
|
||||||
|
p {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 6px 0;
|
||||||
|
|
||||||
|
.arco-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; transform: translateY(10px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -106,12 +106,14 @@ const getAudioUrl = (filePath: string): string => {
|
||||||
const openPreview = (item: PreviewItem) => {
|
const openPreview = (item: PreviewItem) => {
|
||||||
currentPreviewItem.value = item
|
currentPreviewItem.value = item
|
||||||
audioList.value = []
|
audioList.value = []
|
||||||
for (const audio of item.audios) {
|
if(item.audios){
|
||||||
let temp={
|
for (const audio of item.audios) {
|
||||||
audioId:audio.audioId,
|
let temp={
|
||||||
url:getAudioUrl(audio.filePath)
|
audioId:audio.audioId,
|
||||||
|
url:getAudioUrl(audio.filePath)
|
||||||
|
}
|
||||||
|
audioList.value.push(temp)
|
||||||
}
|
}
|
||||||
audioList.value.push(temp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
previewModalVisible.value = true
|
previewModalVisible.value = true
|
||||||
|
|
|
@ -251,15 +251,22 @@ const fetchPartList = async (projectId: string, turbineId: string) => {
|
||||||
|
|
||||||
// 处理筛选变化,获取图像列表
|
// 处理筛选变化,获取图像列表
|
||||||
const handleFilterChange = async () => {
|
const handleFilterChange = async () => {
|
||||||
if (!filterParams.unit) return
|
// if (!filterParams.project) return
|
||||||
|
|
||||||
loading.image = true
|
loading.image = true
|
||||||
try {
|
try {
|
||||||
let params = {
|
let params = {
|
||||||
turbineId: filterParams.unit
|
projectId: filterParams.project
|
||||||
|
}
|
||||||
|
if(filterParams.unit){
|
||||||
|
params = {
|
||||||
|
projectId: filterParams.project,
|
||||||
|
turbineId: filterParams.unit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(filterParams.component){
|
if(filterParams.component){
|
||||||
params = {
|
params = {
|
||||||
|
projectId: filterParams.project,
|
||||||
turbineId: filterParams.unit,
|
turbineId: filterParams.unit,
|
||||||
partId: filterParams.component
|
partId: filterParams.component
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
:visible="visible"
|
||||||
|
title="招标详情"
|
||||||
|
width="800px"
|
||||||
|
:footer="false"
|
||||||
|
:mask-closable="false"
|
||||||
|
@update:visible="(val) => $emit('update:visible', val)"
|
||||||
|
>
|
||||||
|
<a-descriptions
|
||||||
|
:column="2"
|
||||||
|
bordered
|
||||||
|
:label-style="{ width: '120px', fontWeight: 'bold' }"
|
||||||
|
>
|
||||||
|
<a-descriptions-item label="项目名称">{{ detail.projectName }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="招标单位">{{ detail.biddingUnit }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="预算金额">{{ detail.budgetAmount }}万元</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="截止时间">{{ detail.deadline }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="爬取时间">{{ detail.crawlingTime }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="项目地点">{{ detail.projectLocation }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="项目周期">{{ detail.projectDuration }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="招标范围">{{ detail.biddingScope }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="资质要求">{{ detail.qualificationRequirements }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="来源平台">
|
||||||
|
<a
|
||||||
|
:href="getPlatformUrl(detail.sourcePlatform)"
|
||||||
|
target="_blank"
|
||||||
|
class="platform-link"
|
||||||
|
>
|
||||||
|
{{ detail.sourcePlatform }}
|
||||||
|
</a>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="招标文件">
|
||||||
|
<div class="file-display">
|
||||||
|
<a-link
|
||||||
|
v-if="detail.biddingDocuments"
|
||||||
|
:href="detail.biddingDocuments"
|
||||||
|
target="_blank"
|
||||||
|
class="file-link"
|
||||||
|
>
|
||||||
|
{{ displayedFileName }}
|
||||||
|
</a-link>
|
||||||
|
<span v-else class="no-file">暂无文件</span>
|
||||||
|
|
||||||
|
<a-upload
|
||||||
|
:show-file-list="false"
|
||||||
|
:before-upload="beforeUpload"
|
||||||
|
accept=".pdf,.doc,.docx"
|
||||||
|
style="margin-left: 10px"
|
||||||
|
>
|
||||||
|
<a-button
|
||||||
|
type="outline"
|
||||||
|
size="mini"
|
||||||
|
:loading="uploading"
|
||||||
|
>
|
||||||
|
<template #icon><icon-upload /></template>
|
||||||
|
{{ detail.biddingDocuments ? '重新上传' : '上传文件' }}
|
||||||
|
</a-button>
|
||||||
|
</a-upload>
|
||||||
|
</div>
|
||||||
|
</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
|
||||||
|
<div class="content-section">
|
||||||
|
<h3>招标内容</h3>
|
||||||
|
<div class="content-text">{{ detail.biddingContent }}</div>
|
||||||
|
<ul v-if="detail.contentItems && detail.contentItems.length">
|
||||||
|
<li v-for="(item, index) in detail.contentItems" :key="index">{{ item }}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import type { BiddingDetail } from './types'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
visible: boolean
|
||||||
|
detail: BiddingDetail
|
||||||
|
uploading?: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:visible', 'upload'])
|
||||||
|
|
||||||
|
const displayedFileName = computed(() => {
|
||||||
|
if (!props.detail?.biddingDocuments) return ''
|
||||||
|
const url = props.detail.biddingDocuments
|
||||||
|
return url.split('/').pop() || '招标文件'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 平台URL映射
|
||||||
|
const platformUrls: Record<string, string> = {
|
||||||
|
'中国招标投标网': 'https://www.cebpubservice.com/',
|
||||||
|
'国能e招': 'https://www.negc.cn/',
|
||||||
|
'中国节能': 'https://www.cecec.cn/',
|
||||||
|
'三峡招标': 'https://epp.ctg.com.cn/'
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPlatformUrl = (platformName: string): string => {
|
||||||
|
return platformUrls[platformName] || '#'
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUpload = () => {
|
||||||
|
emit('upload')
|
||||||
|
}
|
||||||
|
|
||||||
|
const beforeUpload = (file: File) => {
|
||||||
|
// 检查文件类型
|
||||||
|
const isValidType = [
|
||||||
|
'application/pdf',
|
||||||
|
'application/msword',
|
||||||
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
||||||
|
].includes(file.type)
|
||||||
|
|
||||||
|
// 检查文件大小 (限制10MB)
|
||||||
|
const isLt10M = file.size / 1024 / 1024 < 10
|
||||||
|
|
||||||
|
if (!isValidType) {
|
||||||
|
Message.error('只能上传PDF或Word文件!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isLt10M) {
|
||||||
|
Message.error('文件大小不能超过10MB!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 触发上传事件
|
||||||
|
emit('upload', file)
|
||||||
|
|
||||||
|
// 返回false阻止默认上传行为,由父组件处理
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.file-display {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-link {
|
||||||
|
color: #1890ff;
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
max-width: 200px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #40a9ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-file {
|
||||||
|
color: var(--color-text-3);
|
||||||
|
}
|
||||||
|
.platform-link {
|
||||||
|
color: #1890ff; /* 蓝色 */
|
||||||
|
text-decoration: underline; /* 下划线 */
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #40a9ff; /* 悬停时变浅蓝色 */
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content-section {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 16px;
|
||||||
|
background-color: var(--color-fill-2);
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-text {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: var(--color-text-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding-left: 20px;
|
||||||
|
color: var(--color-text-2);
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,164 @@
|
||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
:visible="visible"
|
||||||
|
title="爬虫设置"
|
||||||
|
width="800px"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
@ok="handleOk"
|
||||||
|
:mask-closable="false"
|
||||||
|
>
|
||||||
|
<a-form :model="form" layout="vertical">
|
||||||
|
<a-form-item label="爬取频率">
|
||||||
|
<a-select v-model="form.frequency">
|
||||||
|
<a-option value="hourly">每小时</a-option>
|
||||||
|
<a-option value="daily">每天</a-option>
|
||||||
|
<a-option value="weekly">每周</a-option>
|
||||||
|
<a-option value="monthly">每月</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="关键词过滤">
|
||||||
|
<a-input-tag
|
||||||
|
v-model="form.keywords"
|
||||||
|
placeholder="输入关键词后回车"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="来源平台">
|
||||||
|
<a-checkbox-group v-model="form.platforms">
|
||||||
|
<a-row :gutter="[16, 16]">
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-checkbox value="国能e招">国能e招</a-checkbox>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-checkbox value="中国节能">中国节能</a-checkbox>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-checkbox value="科幻集团">科幻集团</a-checkbox>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-checkbox value="三峡招标">三峡招标</a-checkbox>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-checkbox value="三峡采购">三峡采购</a-checkbox>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-checkbox value="北京京能">北京京能</a-checkbox>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-checkbox value="华润守正">华润守正</a-checkbox>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-checkbox-group>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="自动通知">
|
||||||
|
<a-switch v-model="form.autoNotify" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<template v-if="form.autoNotify">
|
||||||
|
<a-form-item label="通知方式">
|
||||||
|
<a-checkbox-group v-model="form.notifyMethods">
|
||||||
|
<a-checkbox value="inApp">站内消息</a-checkbox>
|
||||||
|
<a-checkbox value="email">电子邮件</a-checkbox>
|
||||||
|
</a-checkbox-group>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item v-if="form.notifyMethods.includes('email')" label="通知邮箱">
|
||||||
|
<a-input v-model="form.notifyEmail" placeholder="请输入接收通知的邮箱" />
|
||||||
|
</a-form-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<a-divider />
|
||||||
|
|
||||||
|
<a-form-item label="爬虫日志">
|
||||||
|
<div class="log-container">
|
||||||
|
<div v-for="(log, index) in logs" :key="index" class="log-item">
|
||||||
|
[{{ log.time }}] {{ log.message }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<a-button @click="handleCancel">取消</a-button>
|
||||||
|
<a-button type="primary" @click="handleOk">保存设置</a-button>
|
||||||
|
</template>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, watch } from 'vue'
|
||||||
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
|
||||||
|
interface LogEntry {
|
||||||
|
time: string
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:visible', 'save'])
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
frequency: 'daily',
|
||||||
|
keywords: ['风电', '叶片', '检查', '运维'],
|
||||||
|
platforms: ['国能e招', '中国节能', '三峡招标'],
|
||||||
|
autoNotify: true,
|
||||||
|
notifyMethods: ['inApp', 'email'],
|
||||||
|
notifyEmail: 'user@example.com'
|
||||||
|
})
|
||||||
|
|
||||||
|
const logs = ref<LogEntry[]>([
|
||||||
|
{ time: '2025-07-30 22:00', message: '爬虫任务已启动' },
|
||||||
|
{ time: '2025-07-30 21:00', message: '爬虫任务已启动' },
|
||||||
|
{ time: '2023-11-01 09:30', message: '成功爬取中国招标投标网数据' },
|
||||||
|
{ time: '2023-11-01 09:32', message: '成功爬取某省公共资源交易中心数据' },
|
||||||
|
{ time: '2023-11-02 10:15', message: '爬取企业自有招标平台失败:连接超时' }
|
||||||
|
])
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
emit('update:visible', false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOk = () => {
|
||||||
|
Message.success('设置保存成功')
|
||||||
|
emit('save', form)
|
||||||
|
emit('update:visible', false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch for email notification toggle
|
||||||
|
watch(() => form.notifyMethods, (newVal) => {
|
||||||
|
if (!newVal.includes('email')) {
|
||||||
|
form.notifyEmail = ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.log-container {
|
||||||
|
height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid var(--color-border-2);
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: var(--color-fill-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-item {
|
||||||
|
padding: 4px 0;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--color-text-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-item:not(:last-child) {
|
||||||
|
border-bottom: 1px dashed var(--color-border-2);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,14 +1,69 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="process-management">
|
<div class="process-management">
|
||||||
<a-card title="制度提案管理" :bordered="false">
|
<a-card title="制度公示管理" :bordered="false">
|
||||||
<template #extra>
|
<!-- 搜索区域 -->
|
||||||
<a-button type="primary" @click="handleAdd">
|
<div class="search-container">
|
||||||
<template #icon>
|
<a-form layout="inline" :model="searchForm" class="search-form">
|
||||||
<GiSvgIcon name="plus" />
|
<a-form-item label="提案标题">
|
||||||
</template>
|
<a-input
|
||||||
提交制度提案
|
v-model="searchForm.title"
|
||||||
</a-button>
|
placeholder="请输入提案标题"
|
||||||
</template>
|
allow-clear
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="提案人">
|
||||||
|
<a-input
|
||||||
|
v-model="searchForm.proposer"
|
||||||
|
placeholder="请输入提案人"
|
||||||
|
allow-clear
|
||||||
|
style="width: 150px"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="提案类型">
|
||||||
|
<a-select
|
||||||
|
v-model="searchForm.type"
|
||||||
|
placeholder="请选择类型"
|
||||||
|
allow-clear
|
||||||
|
style="width: 150px"
|
||||||
|
>
|
||||||
|
<a-option value="">全部</a-option>
|
||||||
|
<a-option value="管理规范">管理规范</a-option>
|
||||||
|
<a-option value="操作流程">操作流程</a-option>
|
||||||
|
<a-option value="安全制度">安全制度</a-option>
|
||||||
|
<a-option value="其他">其他</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="状态">
|
||||||
|
<a-select
|
||||||
|
v-model="searchForm.status"
|
||||||
|
placeholder="请选择状态"
|
||||||
|
allow-clear
|
||||||
|
style="width: 150px"
|
||||||
|
>
|
||||||
|
<a-option value="">全部</a-option>
|
||||||
|
<a-option value="PUBLISHED">已公示</a-option>
|
||||||
|
<a-option value="APPROVED">已通过</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item>
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" @click="search">
|
||||||
|
<template #icon><icon-search /></template>
|
||||||
|
搜索
|
||||||
|
</a-button>
|
||||||
|
<a-button @click="reset">
|
||||||
|
<template #icon><icon-refresh /></template>
|
||||||
|
重置
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a-table
|
<a-table
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
|
@ -18,6 +73,11 @@
|
||||||
@page-change="handlePageChange"
|
@page-change="handlePageChange"
|
||||||
@page-size-change="handlePageSizeChange"
|
@page-size-change="handlePageSizeChange"
|
||||||
>
|
>
|
||||||
|
<template #toolbar-left>
|
||||||
|
<span class="search-result-info">
|
||||||
|
共找到 <strong>{{ pagination.total }}</strong> 条记录
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
<template #status="{ record }">
|
<template #status="{ record }">
|
||||||
<a-tag :color="getStatusColor(record.status)">
|
<a-tag :color="getStatusColor(record.status)">
|
||||||
{{ getStatusText(record.status) }}
|
{{ getStatusText(record.status) }}
|
||||||
|
@ -41,103 +101,13 @@
|
||||||
size="small"
|
size="small"
|
||||||
disabled
|
disabled
|
||||||
>
|
>
|
||||||
已发布
|
已公告
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button
|
|
||||||
v-if="record.publisherId === currentUser && record.status === RegulationStatus.DRAFT"
|
|
||||||
type="text"
|
|
||||||
size="small"
|
|
||||||
@click="handleEdit(record)"
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</a-button>
|
|
||||||
<a-popconfirm
|
|
||||||
v-if="record.publisherId === currentUser && record.status === RegulationStatus.DRAFT"
|
|
||||||
content="确定要删除这个提案吗?"
|
|
||||||
@ok="handleDelete(record)"
|
|
||||||
>
|
|
||||||
<a-button type="text" size="small" status="danger">
|
|
||||||
删除
|
|
||||||
</a-button>
|
|
||||||
</a-popconfirm>
|
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
|
||||||
<!-- 提案表单弹窗 -->
|
|
||||||
<a-modal
|
|
||||||
v-model:visible="modalVisible"
|
|
||||||
:title="modalTitle"
|
|
||||||
width="800px"
|
|
||||||
@ok="handleSubmit"
|
|
||||||
@cancel="handleCancel"
|
|
||||||
>
|
|
||||||
<a-form
|
|
||||||
ref="formRef"
|
|
||||||
:model="formData"
|
|
||||||
:rules="rules"
|
|
||||||
layout="vertical"
|
|
||||||
>
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="提案标题" field="title">
|
|
||||||
<a-input v-model="formData.title" placeholder="请输入提案标题" />
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="提案类型" field="regulationType">
|
|
||||||
<a-select v-model="formData.regulationType" placeholder="请选择提案类型">
|
|
||||||
<a-option value="人事制度">人事制度</a-option>
|
|
||||||
<a-option value="财务制度">财务制度</a-option>
|
|
||||||
<a-option value="安全制度">安全制度</a-option>
|
|
||||||
<a-option value="设备制度">设备制度</a-option>
|
|
||||||
<a-option value="工作流程">工作流程</a-option>
|
|
||||||
<a-option value="其他制度">其他制度</a-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="适用范围" field="scope">
|
|
||||||
<a-input v-model="formData.scope" placeholder="请输入适用范围" />
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="制度级别" field="level">
|
|
||||||
<a-select v-model="formData.level" placeholder="请选择制度级别">
|
|
||||||
<a-option :value="RegulationLevel.LOW">低</a-option>
|
|
||||||
<a-option :value="RegulationLevel.MEDIUM">中</a-option>
|
|
||||||
<a-option :value="RegulationLevel.HIGH">高</a-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-form-item label="提案内容" field="content">
|
|
||||||
<a-textarea
|
|
||||||
v-model="formData.content"
|
|
||||||
placeholder="请详细描述提案的具体内容"
|
|
||||||
:rows="6"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item label="备注" field="remark">
|
|
||||||
<a-textarea
|
|
||||||
v-model="formData.remark"
|
|
||||||
placeholder="请输入其他补充说明"
|
|
||||||
:rows="2"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-form>
|
|
||||||
</a-modal>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- 提案详情弹窗 -->
|
<!-- 提案详情弹窗 -->
|
||||||
<a-modal
|
<a-modal
|
||||||
v-model:visible="detailModalVisible"
|
v-model:visible="detailModalVisible"
|
||||||
|
@ -149,7 +119,7 @@
|
||||||
<div class="detail-header">
|
<div class="detail-header">
|
||||||
<h3>{{ currentProposal.title }}</h3>
|
<h3>{{ currentProposal.title }}</h3>
|
||||||
<div class="detail-meta">
|
<div class="detail-meta">
|
||||||
<span>提案人: {{ currentProposal.publisherName }}</span>
|
<span>提案人: {{ currentProposal.createByName }}</span>
|
||||||
<span>提案类型: {{ currentProposal.regulationType }}</span>
|
<span>提案类型: {{ currentProposal.regulationType }}</span>
|
||||||
<span>适用范围: {{ currentProposal.scope }}</span>
|
<span>适用范围: {{ currentProposal.scope }}</span>
|
||||||
<span>级别: <a-tag :color="getLevelColor(currentProposal.level)">{{ getLevelText(currentProposal.level) }}</a-tag></span>
|
<span>级别: <a-tag :color="getLevelColor(currentProposal.level)">{{ getLevelText(currentProposal.level) }}</a-tag></span>
|
||||||
|
@ -175,7 +145,7 @@
|
||||||
v-if="currentProposal.status === RegulationStatus.PUBLISHED"
|
v-if="currentProposal.status === RegulationStatus.PUBLISHED"
|
||||||
disabled
|
disabled
|
||||||
>
|
>
|
||||||
已自动发布
|
已自动公示
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -186,27 +156,37 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, onMounted } from 'vue'
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
import { Message } from '@arco-design/web-vue'
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
import { IconSearch, IconRefresh } from '@arco-design/web-vue/es/icon'
|
||||||
import { useRegulationStore } from '@/stores/modules/regulation'
|
import { useRegulationStore } from '@/stores/modules/regulation'
|
||||||
import { regulationApi } from '@/apis/regulation'
|
import { regulationApi } from '@/apis/regulation'
|
||||||
import {
|
import {
|
||||||
RegulationStatus,
|
RegulationStatus,
|
||||||
RegulationLevel,
|
RegulationLevel,
|
||||||
type Regulation
|
type Regulation,
|
||||||
|
type RegulationType
|
||||||
} from '@/apis/regulation/type'
|
} from '@/apis/regulation/type'
|
||||||
|
|
||||||
defineOptions({ name: 'ProcessManagement' })
|
defineOptions({ name: 'ProcessManagement' })
|
||||||
|
|
||||||
// 表格列定义
|
// 表格列定义
|
||||||
const columns = [
|
const columns = [
|
||||||
{ title: '提案标题', dataIndex: 'title', key: 'title' },
|
{ title: '提案标题', dataIndex: 'title', key: 'title', width: 200 },
|
||||||
{ title: '提案人', dataIndex: 'publisherName', key: 'publisherName' },
|
{ title: '提案人', dataIndex: 'createByName', key: 'createByName', width: 120 },
|
||||||
{ title: '提案类型', dataIndex: 'regulationType', key: 'regulationType' },
|
{ title: '提案类型', dataIndex: 'regulationType', key: 'regulationType', width: 120 },
|
||||||
{ title: '状态', dataIndex: 'status', key: 'status', slotName: 'status' },
|
{ title: '状态', dataIndex: 'status', key: 'status', slotName: 'status', width: 100 },
|
||||||
{ title: '级别', dataIndex: 'level', key: 'level', slotName: 'level' },
|
{ title: '级别', dataIndex: 'level', key: 'level', slotName: 'level', width: 80 },
|
||||||
{ title: '创建时间', dataIndex: 'createTime', key: 'createTime' },
|
{ title: '创建时间', dataIndex: 'createTime', key: 'createTime', width: 180 },
|
||||||
{ title: '操作', key: 'operations', slotName: 'operations', width: 280 }
|
{ title: '操作', key: 'operations', slotName: 'operations', width: 150 }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// 搜索表单
|
||||||
|
const searchForm = reactive({
|
||||||
|
title: '',
|
||||||
|
proposer: '',
|
||||||
|
type: '',
|
||||||
|
status: ''
|
||||||
|
})
|
||||||
|
|
||||||
// 表格数据
|
// 表格数据
|
||||||
const tableData = ref<Regulation[]>([])
|
const tableData = ref<Regulation[]>([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
@ -219,48 +199,23 @@ const pagination = reactive({
|
||||||
showPageSize: true
|
showPageSize: true
|
||||||
})
|
})
|
||||||
|
|
||||||
// 弹窗相关
|
|
||||||
const modalVisible = ref(false)
|
|
||||||
const modalTitle = ref('提交制度提案')
|
|
||||||
const formRef = ref()
|
|
||||||
const formData = reactive({
|
|
||||||
regulationId: '',
|
|
||||||
title: '',
|
|
||||||
regulationType: '',
|
|
||||||
content: '',
|
|
||||||
scope: '',
|
|
||||||
level: RegulationLevel.MEDIUM,
|
|
||||||
remark: ''
|
|
||||||
})
|
|
||||||
|
|
||||||
const currentProposal = ref<Regulation | null>(null)
|
const currentProposal = ref<Regulation | null>(null)
|
||||||
|
|
||||||
// 详情相关
|
// 详情相关
|
||||||
const detailModalVisible = ref(false)
|
const detailModalVisible = ref(false)
|
||||||
|
|
||||||
// 当前用户 - 应该从用户认证系统获取
|
// 当前用户
|
||||||
const currentUser = ref('') // TODO: 从用户认证系统获取当前用户ID
|
const currentUser = ref('管理者') // 从用户认证系统获取当前用户ID
|
||||||
|
|
||||||
// 制度管理store
|
// 制度管理store
|
||||||
const regulationStore = useRegulationStore()
|
const regulationStore = useRegulationStore()
|
||||||
|
|
||||||
// 表单验证规则
|
|
||||||
const rules = {
|
|
||||||
title: [{ required: true, message: '请输入提案标题' }],
|
|
||||||
regulationType: [{ required: true, message: '请选择提案类型' }],
|
|
||||||
content: [{ required: true, message: '请输入提案内容' }],
|
|
||||||
scope: [{ required: true, message: '请输入适用范围' }]
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取状态颜色
|
// 获取状态颜色
|
||||||
const getStatusColor = (status: RegulationStatus) => {
|
const getStatusColor = (status: RegulationStatus) => {
|
||||||
const colors = {
|
const colors = {
|
||||||
[RegulationStatus.DRAFT]: 'blue',
|
[RegulationStatus.DRAFT]: 'blue',
|
||||||
[RegulationStatus.VOTING]: 'orange',
|
|
||||||
[RegulationStatus.REJECTED]: 'red',
|
|
||||||
[RegulationStatus.PUBLISHED]: 'purple',
|
[RegulationStatus.PUBLISHED]: 'purple',
|
||||||
[RegulationStatus.APPROVED]: 'green',
|
[RegulationStatus.APPROVED]: 'green',
|
||||||
[RegulationStatus.ARCHIVED]: 'gray'
|
|
||||||
}
|
}
|
||||||
return colors[status] || 'blue'
|
return colors[status] || 'blue'
|
||||||
}
|
}
|
||||||
|
@ -269,11 +224,9 @@ const getStatusColor = (status: RegulationStatus) => {
|
||||||
const getStatusText = (status: RegulationStatus) => {
|
const getStatusText = (status: RegulationStatus) => {
|
||||||
const texts = {
|
const texts = {
|
||||||
[RegulationStatus.DRAFT]: '草稿',
|
[RegulationStatus.DRAFT]: '草稿',
|
||||||
[RegulationStatus.VOTING]: '投票中',
|
[RegulationStatus.PUBLISHED]: '已公告',
|
||||||
[RegulationStatus.REJECTED]: '已否决',
|
[RegulationStatus.APPROVED]: '已公示',
|
||||||
[RegulationStatus.PUBLISHED]: '已发布',
|
|
||||||
[RegulationStatus.APPROVED]: '已通过',
|
|
||||||
[RegulationStatus.ARCHIVED]: '已归档'
|
|
||||||
}
|
}
|
||||||
return texts[status] || '草稿'
|
return texts[status] || '草稿'
|
||||||
}
|
}
|
||||||
|
@ -311,13 +264,17 @@ const getLevelText = (level: RegulationLevel) => {
|
||||||
return texts[level] || '中'
|
return texts[level] || '中'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取表格数据
|
// 获取表格数据 - 使用后端搜索接口
|
||||||
const getTableData = async () => {
|
const getTableData = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const response = await regulationApi.getRegulationList({
|
const response = await regulationApi.getRegulationList({
|
||||||
page: pagination.current,
|
page: pagination.current,
|
||||||
size: pagination.pageSize
|
size: pagination.pageSize,
|
||||||
|
title: searchForm.title || undefined,
|
||||||
|
proposer: searchForm.proposer || undefined,
|
||||||
|
type: searchForm.type || undefined,
|
||||||
|
status: searchForm.status || undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
|
@ -325,36 +282,32 @@ const getTableData = async () => {
|
||||||
pagination.total = response.data.total
|
pagination.total = response.data.total
|
||||||
pagination.current = response.data.current
|
pagination.current = response.data.current
|
||||||
} else {
|
} else {
|
||||||
Message.error('获取数据失败')
|
Message.error('搜索失败')
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取制度列表失败:', error)
|
console.error('搜索制度公示失败:', error)
|
||||||
Message.error('获取数据失败')
|
Message.error('搜索失败')
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增
|
// 搜索
|
||||||
const handleAdd = () => {
|
const search = () => {
|
||||||
modalTitle.value = '提交制度提案'
|
pagination.current = 1
|
||||||
modalVisible.value = true
|
getTableData()
|
||||||
resetForm()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 编辑
|
// 重置
|
||||||
const handleEdit = (record: Regulation) => {
|
const reset = () => {
|
||||||
modalTitle.value = '编辑制度提案'
|
Object.assign(searchForm, {
|
||||||
modalVisible.value = true
|
title: '',
|
||||||
Object.assign(formData, {
|
proposer: '',
|
||||||
regulationId: record.regulationId,
|
type: '',
|
||||||
title: record.title,
|
status: ''
|
||||||
regulationType: record.regulationType,
|
|
||||||
content: record.content,
|
|
||||||
scope: record.scope,
|
|
||||||
level: record.level,
|
|
||||||
remark: record.remark
|
|
||||||
})
|
})
|
||||||
|
pagination.current = 1
|
||||||
|
getTableData()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查看
|
// 查看
|
||||||
|
@ -363,79 +316,6 @@ const handleView = (record: Regulation) => {
|
||||||
detailModalVisible.value = true
|
detailModalVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 删除
|
|
||||||
const handleDelete = async (record: Regulation) => {
|
|
||||||
try {
|
|
||||||
await regulationApi.deleteProposal(record.regulationId)
|
|
||||||
Message.success('删除成功')
|
|
||||||
getTableData()
|
|
||||||
} catch (error) {
|
|
||||||
console.error('删除失败:', error)
|
|
||||||
Message.error('删除失败')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 提交表单
|
|
||||||
const handleSubmit = async () => {
|
|
||||||
try {
|
|
||||||
await formRef.value.validate()
|
|
||||||
|
|
||||||
if (formData.regulationId) {
|
|
||||||
// 更新
|
|
||||||
await regulationApi.updateProposal(formData.regulationId, {
|
|
||||||
title: formData.title,
|
|
||||||
regulationType: formData.regulationType,
|
|
||||||
content: formData.content,
|
|
||||||
scope: formData.scope,
|
|
||||||
level: formData.level,
|
|
||||||
remark: formData.remark
|
|
||||||
})
|
|
||||||
Message.success('更新成功')
|
|
||||||
} else {
|
|
||||||
// 新增
|
|
||||||
await regulationApi.createProposal({
|
|
||||||
title: formData.title,
|
|
||||||
regulationType: formData.regulationType,
|
|
||||||
content: formData.content,
|
|
||||||
scope: formData.scope,
|
|
||||||
level: formData.level,
|
|
||||||
remark: formData.remark
|
|
||||||
})
|
|
||||||
Message.success('提案提交成功')
|
|
||||||
}
|
|
||||||
|
|
||||||
modalVisible.value = false
|
|
||||||
getTableData()
|
|
||||||
} catch (error) {
|
|
||||||
console.error('操作失败:', error)
|
|
||||||
Message.error('操作失败')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 取消
|
|
||||||
const handleCancel = () => {
|
|
||||||
modalVisible.value = false
|
|
||||||
resetForm()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重置表单
|
|
||||||
const resetForm = () => {
|
|
||||||
Object.assign(formData, {
|
|
||||||
regulationId: '',
|
|
||||||
title: '',
|
|
||||||
regulationType: '',
|
|
||||||
content: '',
|
|
||||||
scope: '',
|
|
||||||
level: RegulationLevel.MEDIUM,
|
|
||||||
remark: ''
|
|
||||||
})
|
|
||||||
formRef.value?.resetFields()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 分页事件
|
// 分页事件
|
||||||
const handlePageChange = (page: number) => {
|
const handlePageChange = (page: number) => {
|
||||||
pagination.current = page
|
pagination.current = page
|
||||||
|
@ -460,7 +340,44 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-container {
|
||||||
|
padding: 16px;
|
||||||
|
background: var(--color-bg-2);
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
.arco-form-item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-right: 16px;
|
||||||
|
|
||||||
|
.arco-form-item-label {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-input,
|
||||||
|
.arco-select {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-btn {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-result-info {
|
||||||
|
color: var(--color-text-2);
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
color: var(--color-text-1);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.proposal-detail {
|
.proposal-detail {
|
||||||
.detail-header {
|
.detail-header {
|
||||||
|
@ -497,8 +414,6 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.detail-footer {
|
.detail-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
|
|
@ -36,17 +36,22 @@
|
||||||
|
|
||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import SystemRegulation from './system-regulation/index.vue'
|
import SystemRegulation from './repository.vue'
|
||||||
import ProcessManagement from './process-management/index.vue'
|
import RegulationProposal from './proposal/index.vue'
|
||||||
|
import RegulationType from './type/index.vue'
|
||||||
|
import ProcessManagement from './confirm.vue'
|
||||||
import { useDevice } from '@/hooks'
|
import { useDevice } from '@/hooks'
|
||||||
|
|
||||||
defineOptions({ name: 'ZhiduManagement' })
|
defineOptions({ name: 'RegulationManagement' })
|
||||||
|
|
||||||
const { isDesktop } = useDevice()
|
const { isDesktop } = useDevice()
|
||||||
|
|
||||||
const data = [
|
const data = [
|
||||||
{ name: '制度规范', key: 'system-regulation', icon: 'file-text', value: SystemRegulation, path: '/zhidu/system-regulation' },
|
{ name: '制度公告', key: 'system-regulation', icon: 'file-text', value: SystemRegulation, path: '/regulation/system-regulation' },
|
||||||
{ name: '流程管理', key: 'process-management', icon: 'workflow', value: ProcessManagement, path: '/zhidu/process-management' },
|
{ name: '制度提案', key: 'proposal', icon: 'edit', value: RegulationProposal, path: '/regulation/proposal' },
|
||||||
|
{ name: '制度公示', key: 'process-management', icon: 'workflow', value: ProcessManagement, path: '/regulation/process-management' },
|
||||||
|
{ name: '制度类型', key: 'type', icon: 'tag', value: RegulationType, path: '/regulation/type' },
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
const menuList = computed(() => {
|
const menuList = computed(() => {
|
||||||
|
|
|
@ -0,0 +1,925 @@
|
||||||
|
<template>
|
||||||
|
<div class="regulation-proposal">
|
||||||
|
<a-card title="制度提案管理" :bordered="false">
|
||||||
|
<template #extra>
|
||||||
|
<a-button type="primary" @click="handleAdd">
|
||||||
|
<template #icon>
|
||||||
|
<GiSvgIcon name="plus" />
|
||||||
|
</template>
|
||||||
|
新增制度提案
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 搜索区域 -->
|
||||||
|
<div class="search-container">
|
||||||
|
<a-form layout="inline" :model="searchForm" class="search-form">
|
||||||
|
<a-form-item label="提案标题">
|
||||||
|
<a-input
|
||||||
|
v-model="searchForm.title"
|
||||||
|
placeholder="请输入提案标题"
|
||||||
|
allow-clear
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="提案人">
|
||||||
|
<a-input
|
||||||
|
v-model="searchForm.proposer"
|
||||||
|
placeholder="请输入提案人"
|
||||||
|
allow-clear
|
||||||
|
style="width: 150px"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="提案类型">
|
||||||
|
<a-select
|
||||||
|
v-model="searchForm.type"
|
||||||
|
placeholder="请选择类型"
|
||||||
|
allow-clear
|
||||||
|
style="width: 150px"
|
||||||
|
>
|
||||||
|
<a-option value="">全部</a-option>
|
||||||
|
<a-option value="管理规范">管理规范</a-option>
|
||||||
|
<a-option value="操作流程">操作流程</a-option>
|
||||||
|
<a-option value="安全制度">安全制度</a-option>
|
||||||
|
<a-option value="其他">其他</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="状态">
|
||||||
|
<a-select
|
||||||
|
v-model="searchForm.status"
|
||||||
|
placeholder="请选择状态"
|
||||||
|
allow-clear
|
||||||
|
style="width: 150px"
|
||||||
|
>
|
||||||
|
<a-option value="">全部</a-option>
|
||||||
|
<a-option value="DRAFT">草稿</a-option>
|
||||||
|
<a-option value="PUBLISHED">已公告</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item>
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" @click="search">
|
||||||
|
<template #icon><icon-search /></template>
|
||||||
|
搜索
|
||||||
|
</a-button>
|
||||||
|
<a-button @click="reset">
|
||||||
|
<template #icon><icon-refresh /></template>
|
||||||
|
重置
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-table
|
||||||
|
:columns="columns"
|
||||||
|
:data="tableData"
|
||||||
|
:loading="loading"
|
||||||
|
:pagination="pagination"
|
||||||
|
@page-change="handlePageChange"
|
||||||
|
@page-size-change="handlePageSizeChange"
|
||||||
|
>
|
||||||
|
<template #toolbar-left>
|
||||||
|
<span class="search-result-info">
|
||||||
|
共找到 <strong>{{ pagination.total }}</strong> 条记录
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template #status="{ record }">
|
||||||
|
<a-tag :color="getStatusColor(record.status)">
|
||||||
|
{{ getStatusText(record.status) }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #level="{ record }">
|
||||||
|
<a-tag :color="getLevelColor(record.level)">
|
||||||
|
{{ getLevelText(record.level) }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #operations="{ record }">
|
||||||
|
<a-space>
|
||||||
|
<a-button type="text" size="small" @click="handleView(record)">
|
||||||
|
查看详情
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
v-if="record.status === RegulationStatus.DRAFT"
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
@click="handleEdit(record)"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
v-if="record.status === RegulationStatus.DRAFT"
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="handleConfirm(record)"
|
||||||
|
>
|
||||||
|
确认公示
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
v-if="record.status !== RegulationStatus.DRAFT"
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
已公示
|
||||||
|
</a-button>
|
||||||
|
<a-popconfirm
|
||||||
|
v-if="record.status === RegulationStatus.DRAFT"
|
||||||
|
content="确定要删除这个提案吗?"
|
||||||
|
@ok="handleDelete(record)"
|
||||||
|
>
|
||||||
|
<a-button type="text" size="small" status="danger">
|
||||||
|
删除
|
||||||
|
</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 提案表单弹窗 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="modalVisible"
|
||||||
|
:title="modalTitle"
|
||||||
|
width="900px"
|
||||||
|
@ok="handleSubmit"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
class="proposal-form-modal"
|
||||||
|
>
|
||||||
|
<a-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formData"
|
||||||
|
:rules="rules"
|
||||||
|
layout="vertical"
|
||||||
|
class="proposal-form"
|
||||||
|
>
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="提案标题" field="title" class="form-item">
|
||||||
|
<a-input
|
||||||
|
v-model="formData.title"
|
||||||
|
placeholder="请输入提案标题"
|
||||||
|
class="form-input"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="提案类型" field="regulationType" class="form-item">
|
||||||
|
<a-select
|
||||||
|
v-model="formData.regulationType"
|
||||||
|
placeholder="请选择提案类型"
|
||||||
|
class="form-select"
|
||||||
|
allow-clear
|
||||||
|
>
|
||||||
|
<a-option
|
||||||
|
v-for="type in regulationTypes"
|
||||||
|
:key="type.typeId"
|
||||||
|
:value="type.typeName"
|
||||||
|
:disabled="type.isEnabled === '0'"
|
||||||
|
>
|
||||||
|
{{ type.typeName }}
|
||||||
|
</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="适用范围" field="scope" class="form-item">
|
||||||
|
<a-input
|
||||||
|
v-model="formData.scope"
|
||||||
|
placeholder="请输入适用范围"
|
||||||
|
class="form-input"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="制度级别" field="level" class="form-item">
|
||||||
|
<a-select
|
||||||
|
v-model="formData.level"
|
||||||
|
placeholder="请选择制度级别"
|
||||||
|
class="form-select"
|
||||||
|
allow-clear
|
||||||
|
>
|
||||||
|
<a-option :value="RegulationLevel.LOW">低</a-option>
|
||||||
|
<a-option :value="RegulationLevel.MEDIUM">中</a-option>
|
||||||
|
<a-option :value="RegulationLevel.HIGH">高</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-form-item label="提案内容" field="content" class="form-item">
|
||||||
|
<a-textarea
|
||||||
|
v-model="formData.content"
|
||||||
|
placeholder="请详细描述提案的具体内容,包括制度的目的、适用范围、具体规定等"
|
||||||
|
:rows="8"
|
||||||
|
class="form-textarea"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="备注" field="remark" class="form-item">
|
||||||
|
<a-textarea
|
||||||
|
v-model="formData.remark"
|
||||||
|
placeholder="请输入其他补充说明(可选)"
|
||||||
|
:rows="3"
|
||||||
|
class="form-textarea"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
|
|
||||||
|
<!-- 提案详情弹窗 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="detailModalVisible"
|
||||||
|
title="提案详情"
|
||||||
|
width="800px"
|
||||||
|
:footer="false"
|
||||||
|
>
|
||||||
|
<div class="proposal-detail" v-if="currentProposal">
|
||||||
|
<div class="detail-header">
|
||||||
|
<h3>{{ currentProposal.title }}</h3>
|
||||||
|
<div class="detail-meta">
|
||||||
|
<span>提案人: {{ currentProposal.createByName }}</span>
|
||||||
|
<span>提案类型: {{ currentProposal.regulationType }}</span>
|
||||||
|
<span>适用范围: {{ currentProposal.scope }}</span>
|
||||||
|
<span>级别: <a-tag :color="getLevelColor(currentProposal.level)">{{ getLevelText(currentProposal.level) }}</a-tag></span>
|
||||||
|
<span>创建时间: {{ formatDate(currentProposal.createTime) }}</span>
|
||||||
|
<span>状态: <a-tag :color="getStatusColor(currentProposal.status)">{{ getStatusText(currentProposal.status) }}</a-tag></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-divider />
|
||||||
|
|
||||||
|
<div class="detail-content">
|
||||||
|
<h4>提案内容</h4>
|
||||||
|
<div class="content-text">{{ currentProposal.content }}</div>
|
||||||
|
|
||||||
|
<h4>备注</h4>
|
||||||
|
<div class="content-text">{{ currentProposal.remark || '暂无备注' }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-divider />
|
||||||
|
|
||||||
|
<div class="detail-footer">
|
||||||
|
<a-button
|
||||||
|
v-if="currentProposal.status === RegulationStatus.DRAFT && currentProposal.createByName === currentUser"
|
||||||
|
type="primary"
|
||||||
|
@click="handleConfirm(currentProposal)"
|
||||||
|
>
|
||||||
|
确认公示
|
||||||
|
</a-button>
|
||||||
|
<a-button @click="detailModalVisible = false">
|
||||||
|
关闭
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
|
||||||
|
<!-- 确认公示弹窗 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="confirmModalVisible"
|
||||||
|
title="确认公示提案"
|
||||||
|
width="600px"
|
||||||
|
@ok="submitConfirm"
|
||||||
|
@cancel="confirmModalVisible = false"
|
||||||
|
>
|
||||||
|
<div class="confirm-content">
|
||||||
|
<div class="confirm-header">
|
||||||
|
<GiSvgIcon name="exclamation-circle" style="color: #faad14; font-size: 24px; margin-right: 8px;" />
|
||||||
|
<span style="font-weight: 500; font-size: 16px;">公示确认</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="confirm-body">
|
||||||
|
<p>您即将公示提案《<strong>{{ currentProposal?.title }}</strong>》</p>
|
||||||
|
<p>公示后:</p>
|
||||||
|
<ul>
|
||||||
|
<li>该提案将进入制度公示页面</li>
|
||||||
|
<li>其他用户可以在制度公示页面查看此提案</li>
|
||||||
|
<li>提案将进入正式的公示流程</li>
|
||||||
|
<li>您将无法再编辑此提案</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="confirm-footer">
|
||||||
|
<a-checkbox v-model="agreeDisplay">
|
||||||
|
我已仔细阅读并确认公示此提案
|
||||||
|
</a-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
import { IconSearch, IconRefresh } from '@arco-design/web-vue/es/icon'
|
||||||
|
import { useRegulationStore } from '@/stores/modules/regulation'
|
||||||
|
import { regulationApi } from '@/apis/regulation'
|
||||||
|
import {
|
||||||
|
RegulationStatus,
|
||||||
|
RegulationLevel,
|
||||||
|
type Regulation,
|
||||||
|
type RegulationType,
|
||||||
|
type RegulationProposalSearchRequest
|
||||||
|
} from '@/apis/regulation/type'
|
||||||
|
|
||||||
|
defineOptions({ name: 'RegulationProposal' })
|
||||||
|
|
||||||
|
// 表格列定义
|
||||||
|
const columns = [
|
||||||
|
{ title: '提案标题', dataIndex: 'title', key: 'title', width: 220 },
|
||||||
|
{ title: '提案人', dataIndex: 'createByName', key: 'createByName', width: 130 },
|
||||||
|
{ title: '提案类型', dataIndex: 'regulationType', key: 'regulationType', width: 130 },
|
||||||
|
{ title: '状态', dataIndex: 'status', key: 'status', slotName: 'status', width: 110 },
|
||||||
|
{ title: '级别', dataIndex: 'level', key: 'level', slotName: 'level', width: 90 },
|
||||||
|
{ title: '创建时间', dataIndex: 'createTime', key: 'createTime', width: 200 },
|
||||||
|
{ title: '操作', key: 'operations', slotName: 'operations', width: 320 }
|
||||||
|
]
|
||||||
|
|
||||||
|
// 搜索表单
|
||||||
|
const searchForm = reactive({
|
||||||
|
title: '',
|
||||||
|
proposer: '',
|
||||||
|
type: '',
|
||||||
|
status: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表格数据
|
||||||
|
const tableData = ref<Regulation[]>([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const pagination = reactive({
|
||||||
|
current: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
showTotal: true,
|
||||||
|
showJumper: true,
|
||||||
|
showPageSize: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 弹窗相关
|
||||||
|
const modalVisible = ref(false)
|
||||||
|
const modalTitle = ref('新增制度提案')
|
||||||
|
const formRef = ref()
|
||||||
|
const formData = reactive({
|
||||||
|
regulationId: '',
|
||||||
|
title: '',
|
||||||
|
regulationType: '',
|
||||||
|
content: '',
|
||||||
|
scope: '',
|
||||||
|
level: RegulationLevel.MEDIUM,
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const currentProposal = ref<Regulation | null>(null)
|
||||||
|
const detailModalVisible = ref(false)
|
||||||
|
const confirmModalVisible = ref(false)
|
||||||
|
const agreeDisplay = ref(false)
|
||||||
|
|
||||||
|
// 当前用户
|
||||||
|
const currentUser = ref('管理者') // 从用户认证系统获取当前用户ID
|
||||||
|
|
||||||
|
// 制度管理store
|
||||||
|
const regulationStore = useRegulationStore()
|
||||||
|
|
||||||
|
// 制度类型列表
|
||||||
|
const regulationTypes = ref<RegulationType[]>([])
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const rules = {
|
||||||
|
title: [{ required: true, message: '请输入提案标题' }],
|
||||||
|
regulationType: [{ required: true, message: '请选择提案类型' }],
|
||||||
|
content: [{ required: true, message: '请输入提案内容' }],
|
||||||
|
scope: [{ required: true, message: '请输入适用范围' }]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取状态颜色
|
||||||
|
const getStatusColor = (status: RegulationStatus) => {
|
||||||
|
const colors = {
|
||||||
|
[RegulationStatus.DRAFT]: 'blue',
|
||||||
|
[RegulationStatus.VOTING]: 'orange',
|
||||||
|
[RegulationStatus.REJECTED]: 'red',
|
||||||
|
[RegulationStatus.PUBLISHED]: 'purple',
|
||||||
|
[RegulationStatus.APPROVED]: 'green',
|
||||||
|
[RegulationStatus.ARCHIVED]: 'gray'
|
||||||
|
}
|
||||||
|
return colors[status] || 'blue'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取状态文本
|
||||||
|
const getStatusText = (status: RegulationStatus) => {
|
||||||
|
const texts = {
|
||||||
|
[RegulationStatus.DRAFT]: '草稿',
|
||||||
|
[RegulationStatus.VOTING]: '投票中',
|
||||||
|
[RegulationStatus.REJECTED]: '已否决',
|
||||||
|
[RegulationStatus.PUBLISHED]: '已公告',
|
||||||
|
[RegulationStatus.APPROVED]: '已公示',
|
||||||
|
[RegulationStatus.ARCHIVED]: '已归档'
|
||||||
|
}
|
||||||
|
return texts[status] || '草稿'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化日期
|
||||||
|
const formatDate = (dateString: string) => {
|
||||||
|
if (!dateString) return '-'
|
||||||
|
const date = new Date(dateString)
|
||||||
|
return date.toLocaleDateString('zh-CN', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取级别颜色
|
||||||
|
const getLevelColor = (level: RegulationLevel) => {
|
||||||
|
const colors = {
|
||||||
|
[RegulationLevel.LOW]: 'blue',
|
||||||
|
[RegulationLevel.MEDIUM]: 'orange',
|
||||||
|
[RegulationLevel.HIGH]: 'red'
|
||||||
|
}
|
||||||
|
return colors[level] || 'blue'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取级别文本
|
||||||
|
const getLevelText = (level: RegulationLevel) => {
|
||||||
|
const texts = {
|
||||||
|
[RegulationLevel.LOW]: '低',
|
||||||
|
[RegulationLevel.MEDIUM]: '中',
|
||||||
|
[RegulationLevel.HIGH]: '高'
|
||||||
|
}
|
||||||
|
return texts[level] || '中'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取制度类型列表
|
||||||
|
const getRegulationTypes = async () => {
|
||||||
|
try {
|
||||||
|
const response = await regulationApi.getRegulationTypes()
|
||||||
|
if (response.status === 200) {
|
||||||
|
regulationTypes.value = response.data.records || response.data
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取制度类型列表失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取表格数据 - 使用后端搜索接口
|
||||||
|
const getTableData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const response = await regulationApi.getRegulationList({
|
||||||
|
page: pagination.current,
|
||||||
|
size: pagination.pageSize,
|
||||||
|
title: searchForm.title || undefined,
|
||||||
|
proposer: searchForm.proposer || undefined,
|
||||||
|
type: searchForm.type || undefined,
|
||||||
|
status: searchForm.status || undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.status === 200) {
|
||||||
|
tableData.value = response.data.records
|
||||||
|
pagination.total = response.data.total
|
||||||
|
pagination.current = response.data.current
|
||||||
|
} else {
|
||||||
|
Message.error('搜索失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('搜索制度提案失败:', error)
|
||||||
|
Message.error('搜索失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const search = () => {
|
||||||
|
pagination.current = 1
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const reset = () => {
|
||||||
|
Object.assign(searchForm, {
|
||||||
|
title: '',
|
||||||
|
proposer: '',
|
||||||
|
type: '',
|
||||||
|
status: ''
|
||||||
|
})
|
||||||
|
pagination.current = 1
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
const handleAdd = () => {
|
||||||
|
modalTitle.value = '新增制度提案'
|
||||||
|
modalVisible.value = true
|
||||||
|
resetForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
const handleEdit = (record: Regulation) => {
|
||||||
|
|
||||||
|
modalTitle.value = '编辑制度提案'
|
||||||
|
modalVisible.value = true
|
||||||
|
Object.assign(formData, {
|
||||||
|
regulationId: record.regulationId,
|
||||||
|
title: record.title,
|
||||||
|
regulationType: record.regulationType,
|
||||||
|
content: record.content,
|
||||||
|
scope: record.scope,
|
||||||
|
level: record.level,
|
||||||
|
remark: record.remark
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看
|
||||||
|
const handleView = (record: Regulation) => {
|
||||||
|
currentProposal.value = record
|
||||||
|
detailModalVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认公示
|
||||||
|
const handleConfirm = (record: Regulation) => {
|
||||||
|
currentProposal.value = record
|
||||||
|
confirmModalVisible.value = true
|
||||||
|
agreeDisplay.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
const handleDelete = async (record: Regulation) => {
|
||||||
|
try {
|
||||||
|
await regulationApi.deleteProposal(record.regulationId)
|
||||||
|
Message.success('删除成功')
|
||||||
|
getTableData()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('删除失败:', error)
|
||||||
|
Message.error('删除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交确认公示
|
||||||
|
const submitConfirm = async () => {
|
||||||
|
if (!agreeDisplay.value) {
|
||||||
|
Message.warning('请先确认公示此提案')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (currentProposal.value) {
|
||||||
|
console.log('准备公示提案:', currentProposal.value.regulationId)
|
||||||
|
// 使用公示制度API
|
||||||
|
const response = await regulationApi.publishRegulation(currentProposal.value.regulationId)
|
||||||
|
console.log('公示响应:', response)
|
||||||
|
|
||||||
|
Message.success('提案公示成功,已进入公示流程')
|
||||||
|
confirmModalVisible.value = false
|
||||||
|
getTableData() // 刷新列表
|
||||||
|
|
||||||
|
// 如果是从详情弹窗发起的确认,也关闭详情弹窗
|
||||||
|
if (detailModalVisible.value) {
|
||||||
|
detailModalVisible.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('公示失败:', error)
|
||||||
|
console.error('错误详情:', error.response || error)
|
||||||
|
Message.error('公示失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
await formRef.value.validate()
|
||||||
|
|
||||||
|
if (formData.regulationId) {
|
||||||
|
// 更新
|
||||||
|
await regulationApi.updateProposal(formData.regulationId, {
|
||||||
|
title: formData.title,
|
||||||
|
regulationType: formData.regulationType,
|
||||||
|
content: formData.content,
|
||||||
|
scope: formData.scope,
|
||||||
|
level: formData.level,
|
||||||
|
remark: formData.remark
|
||||||
|
})
|
||||||
|
Message.success('更新成功')
|
||||||
|
} else {
|
||||||
|
// 新增
|
||||||
|
const createData = {
|
||||||
|
title: formData.title,
|
||||||
|
regulationType: formData.regulationType,
|
||||||
|
content: formData.content,
|
||||||
|
scope: formData.scope,
|
||||||
|
level: formData.level,
|
||||||
|
remark: formData.remark,
|
||||||
|
createByName: currentUser.value
|
||||||
|
}
|
||||||
|
console.log('新增提案数据:', createData)
|
||||||
|
const response = await regulationApi.createProposal(createData)
|
||||||
|
console.log('新增提案响应:', response)
|
||||||
|
Message.success('提案提交成功')
|
||||||
|
}
|
||||||
|
|
||||||
|
modalVisible.value = false
|
||||||
|
getTableData()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('操作失败:', error)
|
||||||
|
Message.error('操作失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消
|
||||||
|
const handleCancel = () => {
|
||||||
|
modalVisible.value = false
|
||||||
|
resetForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置表单
|
||||||
|
const resetForm = () => {
|
||||||
|
Object.assign(formData, {
|
||||||
|
regulationId: '',
|
||||||
|
title: '',
|
||||||
|
regulationType: '',
|
||||||
|
content: '',
|
||||||
|
scope: '',
|
||||||
|
level: RegulationLevel.MEDIUM,
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
formRef.value?.resetFields()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页事件
|
||||||
|
const handlePageChange = (page: number) => {
|
||||||
|
pagination.current = page
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePageSizeChange = (pageSize: number) => {
|
||||||
|
pagination.pageSize = pageSize
|
||||||
|
pagination.current = 1
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getTableData()
|
||||||
|
getRegulationTypes()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.regulation-proposal {
|
||||||
|
.arco-card {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container {
|
||||||
|
padding: 16px;
|
||||||
|
background: var(--color-bg-2);
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
.arco-form-item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-right: 16px;
|
||||||
|
|
||||||
|
.arco-form-item-label {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-input,
|
||||||
|
.arco-select {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-btn {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-result-info {
|
||||||
|
color: var(--color-text-2);
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
color: var(--color-text-1);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提案表单美化样式
|
||||||
|
.proposal-form-modal {
|
||||||
|
.arco-modal-body {
|
||||||
|
padding: 24px 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-modal-header {
|
||||||
|
padding: 20px 32px 16px;
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
|
||||||
|
.arco-modal-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-modal-footer {
|
||||||
|
padding: 16px 32px 24px;
|
||||||
|
border-top: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-form {
|
||||||
|
.form-item {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
.arco-form-item-label {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
color: #ff4d4f;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-form-item-error {
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input,
|
||||||
|
.form-select {
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--color-primary-light-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus,
|
||||||
|
&.arco-input-focus,
|
||||||
|
&.arco-select-focus {
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
box-shadow: 0 0 0 2px rgba(var(--primary-6), 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-input-inner {
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-textarea {
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--color-primary-light-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus,
|
||||||
|
&.arco-textarea-focus {
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
box-shadow: 0 0 0 2px rgba(var(--primary-6), 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-textarea-inner {
|
||||||
|
padding: 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择框样式优化
|
||||||
|
.arco-select {
|
||||||
|
.arco-select-view {
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-select-arrow {
|
||||||
|
color: var(--color-text-3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 占位符样式
|
||||||
|
.arco-input-inner::placeholder,
|
||||||
|
.arco-textarea-inner::placeholder {
|
||||||
|
color: var(--color-text-3);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空按钮样式
|
||||||
|
.arco-input-clear-btn,
|
||||||
|
.arco-textarea-clear-btn {
|
||||||
|
color: var(--color-text-3);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-text-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-detail {
|
||||||
|
.detail-header {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-meta {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
font-size: 14px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-content {
|
||||||
|
h4 {
|
||||||
|
margin: 16px 0 8px 0;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-text {
|
||||||
|
color: var(--color-text-2);
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 12px;
|
||||||
|
background: var(--color-fill-1);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-footer {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-content {
|
||||||
|
.confirm-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-body {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin: 12px 0;
|
||||||
|
padding-left: 20px;
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: var(--color-text-2);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-footer {
|
||||||
|
padding-top: 16px;
|
||||||
|
border-top: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,7 +1,54 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="system-regulation">
|
<div class="system-regulation">
|
||||||
<a-card title="已发布制度确认" :bordered="false">
|
<a-card title="已公告制度确认" :bordered="false">
|
||||||
|
<!-- 搜索区域 -->
|
||||||
|
<div class="search-container">
|
||||||
|
<a-form layout="inline" :model="searchForm" class="search-form">
|
||||||
|
<a-form-item label="制度标题">
|
||||||
|
<a-input
|
||||||
|
v-model="searchForm.title"
|
||||||
|
placeholder="请输入制度标题"
|
||||||
|
allow-clear
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="公示人">
|
||||||
|
<a-input
|
||||||
|
v-model="searchForm.proposer"
|
||||||
|
placeholder="请输入公示人"
|
||||||
|
allow-clear
|
||||||
|
style="width: 150px"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="确认状态">
|
||||||
|
<a-select
|
||||||
|
v-model="searchForm.confirmStatus"
|
||||||
|
placeholder="请选择状态"
|
||||||
|
allow-clear
|
||||||
|
style="width: 150px"
|
||||||
|
>
|
||||||
|
<a-option value="">全部</a-option>
|
||||||
|
<a-option value="confirmed">已确认</a-option>
|
||||||
|
<a-option value="pending">待确认</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item>
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" @click="search">
|
||||||
|
<template #icon><icon-search /></template>
|
||||||
|
搜索
|
||||||
|
</a-button>
|
||||||
|
<a-button @click="reset">
|
||||||
|
<template #icon><icon-refresh /></template>
|
||||||
|
重置
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a-table
|
<a-table
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
|
@ -11,6 +58,11 @@
|
||||||
@page-change="onPageChange"
|
@page-change="onPageChange"
|
||||||
@page-size-change="onPageSizeChange"
|
@page-size-change="onPageSizeChange"
|
||||||
>
|
>
|
||||||
|
<template #toolbar-left>
|
||||||
|
<span class="search-result-info">
|
||||||
|
共找到 <strong>{{ pagination.total }}</strong> 条记录
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
<template #confirmStatus="{ record }">
|
<template #confirmStatus="{ record }">
|
||||||
<a-tag :color="record.confirmStatus === 'confirmed' ? 'green' : 'orange'">
|
<a-tag :color="record.confirmStatus === 'confirmed' ? 'green' : 'orange'">
|
||||||
{{ record.confirmStatus === 'confirmed' ? '已确认' : '待确认' }}
|
{{ record.confirmStatus === 'confirmed' ? '已确认' : '待确认' }}
|
||||||
|
@ -22,14 +74,6 @@
|
||||||
<a-button type="text" size="small" @click="handleView(record)">
|
<a-button type="text" size="small" @click="handleView(record)">
|
||||||
查看详情
|
查看详情
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button
|
|
||||||
v-if="record.confirmStatus !== 'confirmed'"
|
|
||||||
type="text"
|
|
||||||
size="small"
|
|
||||||
@click="handleConfirm(record)"
|
|
||||||
>
|
|
||||||
确认知晓
|
|
||||||
</a-button>
|
|
||||||
<a-button type="text" size="small" @click="handleDownload(record)">
|
<a-button type="text" size="small" @click="handleDownload(record)">
|
||||||
下载
|
下载
|
||||||
</a-button>
|
</a-button>
|
||||||
|
@ -49,8 +93,8 @@
|
||||||
<div class="detail-header">
|
<div class="detail-header">
|
||||||
<h3>{{ currentRegulation.title }}</h3>
|
<h3>{{ currentRegulation.title }}</h3>
|
||||||
<div class="detail-meta">
|
<div class="detail-meta">
|
||||||
<span>发布人: {{ currentRegulation.publisherName }}</span>
|
<span>公示人: {{ currentRegulation.createByName }}</span>
|
||||||
<span>发布时间: {{ currentRegulation.publishTime }}</span>
|
<span>公示时间: {{ currentRegulation.publishTime }}</span>
|
||||||
<span>生效日期: {{ currentRegulation.effectiveTime }}</span>
|
<span>生效日期: {{ currentRegulation.effectiveTime }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -112,8 +156,10 @@
|
||||||
import { ref, reactive, onMounted } from 'vue'
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
import type { Regulation } from '@/apis/regulation/type'
|
import type { Regulation } from '@/apis/regulation/type'
|
||||||
import { Message } from '@arco-design/web-vue'
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
import { IconSearch, IconRefresh } from '@arco-design/web-vue/es/icon'
|
||||||
import { useRegulationStore } from '@/stores/modules/regulation'
|
import { useRegulationStore } from '@/stores/modules/regulation'
|
||||||
import { regulationApi } from '@/apis/regulation'
|
import { regulationApi } from '@/apis/regulation'
|
||||||
|
import { PDFGenerator } from '@/utils/pdfGenerator'
|
||||||
|
|
||||||
defineOptions({ name: 'SystemRegulation' })
|
defineOptions({ name: 'SystemRegulation' })
|
||||||
|
|
||||||
|
@ -121,8 +167,8 @@ defineOptions({ name: 'SystemRegulation' })
|
||||||
const columns = [
|
const columns = [
|
||||||
{ title: '制度名称', dataIndex: 'title', key: 'title' },
|
{ title: '制度名称', dataIndex: 'title', key: 'title' },
|
||||||
{ title: '制度类型', dataIndex: 'regulationType', key: 'regulationType' },
|
{ title: '制度类型', dataIndex: 'regulationType', key: 'regulationType' },
|
||||||
{ title: '发布人', dataIndex: 'publisherName', key: 'publisherName' },
|
{ title: '公示人', dataIndex: 'createByName', key: 'createByName' },
|
||||||
{ title: '发布时间', dataIndex: 'publishTime', key: 'publishTime' },
|
{ title: '公示时间', dataIndex: 'publishTime', key: 'publishTime' },
|
||||||
{ title: '生效日期', dataIndex: 'effectiveTime', key: 'effectiveTime' },
|
{ title: '生效日期', dataIndex: 'effectiveTime', key: 'effectiveTime' },
|
||||||
{ title: '确认状态', dataIndex: 'confirmStatus', key: 'confirmStatus', slotName: 'confirmStatus' },
|
{ title: '确认状态', dataIndex: 'confirmStatus', key: 'confirmStatus', slotName: 'confirmStatus' },
|
||||||
{ title: '操作', key: 'operations', slotName: 'operations', width: 250 }
|
{ title: '操作', key: 'operations', slotName: 'operations', width: 250 }
|
||||||
|
@ -140,6 +186,13 @@ const pagination = reactive({
|
||||||
showPageSize: true
|
showPageSize: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 搜索表单
|
||||||
|
const searchForm = reactive({
|
||||||
|
title: '',
|
||||||
|
proposer: '',
|
||||||
|
confirmStatus: ''
|
||||||
|
})
|
||||||
|
|
||||||
// 弹窗相关
|
// 弹窗相关
|
||||||
const detailModalVisible = ref(false)
|
const detailModalVisible = ref(false)
|
||||||
const confirmModalVisible = ref(false)
|
const confirmModalVisible = ref(false)
|
||||||
|
@ -149,14 +202,17 @@ const agreeTerms = ref(false)
|
||||||
// 制度管理store
|
// 制度管理store
|
||||||
const regulationStore = useRegulationStore()
|
const regulationStore = useRegulationStore()
|
||||||
|
|
||||||
// 获取表格数据
|
// 获取表格数据 - 使用后端搜索接口
|
||||||
const getTableData = async () => {
|
const getTableData = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const response = await regulationApi.getPublishedRegulationList({
|
const response = await regulationApi.getRegulationList({
|
||||||
page: pagination.current,
|
page: pagination.current,
|
||||||
size: pagination.pageSize,
|
size: pagination.pageSize,
|
||||||
status: "PUBLISHED"
|
status: "PUBLISHED",
|
||||||
|
title: searchForm.title || undefined,
|
||||||
|
proposer: searchForm.proposer || undefined,
|
||||||
|
confirmStatus: searchForm.confirmStatus || undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
|
@ -165,16 +221,33 @@ const getTableData = async () => {
|
||||||
pagination.current = response.data.current
|
pagination.current = response.data.current
|
||||||
pagination.total = response.data.total
|
pagination.total = response.data.total
|
||||||
} else {
|
} else {
|
||||||
Message.error('获取数据失败')
|
Message.error('搜索失败')
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取已发布制度列表失败:', error)
|
console.error('搜索已公告制度失败:', error)
|
||||||
Message.error('获取数据失败')
|
Message.error('搜索失败')
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const search = () => {
|
||||||
|
pagination.current = 1
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const reset = () => {
|
||||||
|
Object.assign(searchForm, {
|
||||||
|
title: '',
|
||||||
|
proposer: '',
|
||||||
|
confirmStatus: ''
|
||||||
|
})
|
||||||
|
pagination.current = 1
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
// 分页事件
|
// 分页事件
|
||||||
const onPageChange = (page: number) => {
|
const onPageChange = (page: number) => {
|
||||||
pagination.current = page
|
pagination.current = page
|
||||||
|
@ -201,55 +274,29 @@ const handleConfirm = (record: any) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 下载
|
// 下载
|
||||||
const handleDownload = (record: any) => {
|
const handleDownload = async (record: any) => {
|
||||||
try {
|
try {
|
||||||
// 创建制度文档内容
|
const loadingMessage = Message.loading('正在生成PDF文件...', 0)
|
||||||
const content = `
|
|
||||||
制度名称:${record.title}
|
// 生成PDF
|
||||||
制度类型:${record.regulationType}
|
const pdfBlob = await PDFGenerator.generateRegulationPDF(record)
|
||||||
发布人:${record.publisherName}
|
|
||||||
发布时间:${record.publishTime}
|
// 关闭加载提示
|
||||||
生效日期:${record.effectiveTime}
|
loadingMessage.close()
|
||||||
|
|
||||||
制度内容:
|
// 生成文件名
|
||||||
${record.content}
|
const filename = `${record.title}_${new Date().toISOString().split('T')[0]}.pdf`
|
||||||
|
|
||||||
适用范围:
|
// 下载PDF
|
||||||
${record.scope}
|
PDFGenerator.downloadPDF(pdfBlob, filename)
|
||||||
|
|
||||||
实施要求:
|
Message.success('PDF文件下载成功')
|
||||||
${record.requirements || '请按照制度要求执行'}
|
|
||||||
|
|
||||||
注意事项:
|
|
||||||
${record.notes || '请严格遵守制度规定'}
|
|
||||||
`.trim()
|
|
||||||
|
|
||||||
// 创建Blob对象
|
|
||||||
const blob = new Blob([content], { type: 'text/plain;charset=utf-8' })
|
|
||||||
|
|
||||||
// 创建下载链接
|
|
||||||
const url = window.URL.createObjectURL(blob)
|
|
||||||
const link = document.createElement('a')
|
|
||||||
link.href = url
|
|
||||||
link.download = `${record.title}.txt`
|
|
||||||
|
|
||||||
// 触发下载
|
|
||||||
document.body.appendChild(link)
|
|
||||||
link.click()
|
|
||||||
|
|
||||||
// 清理
|
|
||||||
document.body.removeChild(link)
|
|
||||||
window.URL.revokeObjectURL(url)
|
|
||||||
|
|
||||||
Message.success('制度文件下载成功')
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Message.error('下载失败')
|
Message.error('PDF生成失败')
|
||||||
console.error('下载错误:', error)
|
console.error('PDF生成错误:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 提交确认
|
// 提交确认
|
||||||
const submitConfirm = async () => {
|
const submitConfirm = async () => {
|
||||||
if (!agreeTerms.value) {
|
if (!agreeTerms.value) {
|
||||||
|
@ -288,6 +335,45 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-container {
|
||||||
|
padding: 16px;
|
||||||
|
background: var(--color-bg-2);
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
.arco-form-item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-right: 16px;
|
||||||
|
|
||||||
|
.arco-form-item-label {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-input,
|
||||||
|
.arco-select {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-btn {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-result-info {
|
||||||
|
color: var(--color-text-2);
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
color: var(--color-text-1);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.regulation-detail {
|
.regulation-detail {
|
||||||
.detail-header {
|
.detail-header {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|
|
@ -0,0 +1,404 @@
|
||||||
|
<template>
|
||||||
|
<GiPageLayout>
|
||||||
|
<GiTable
|
||||||
|
row-key="typeId"
|
||||||
|
title="制度类型管理"
|
||||||
|
:data="dataList"
|
||||||
|
:columns="tableColumns"
|
||||||
|
:loading="loading"
|
||||||
|
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
||||||
|
:pagination="pagination"
|
||||||
|
@page-change="onPageChange"
|
||||||
|
@page-size-change="onPageSizeChange"
|
||||||
|
@refresh="search"
|
||||||
|
>
|
||||||
|
<template #top>
|
||||||
|
<div class="search-container">
|
||||||
|
<a-form layout="inline" :model="searchForm" class="search-form">
|
||||||
|
<a-form-item label="类型名称">
|
||||||
|
<a-input
|
||||||
|
v-model="searchForm.typeName"
|
||||||
|
placeholder="请输入类型名称"
|
||||||
|
allow-clear
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="状态">
|
||||||
|
<a-select
|
||||||
|
v-model="searchForm.status"
|
||||||
|
placeholder="请选择状态"
|
||||||
|
allow-clear
|
||||||
|
style="width: 150px"
|
||||||
|
>
|
||||||
|
<a-option value="">全部</a-option>
|
||||||
|
<a-option value="1">启用</a-option>
|
||||||
|
<a-option value="0">禁用</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="备注">
|
||||||
|
<a-input
|
||||||
|
v-model="searchForm.remark"
|
||||||
|
placeholder="请输入备注内容"
|
||||||
|
allow-clear
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item>
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" @click="search">
|
||||||
|
<template #icon><icon-search /></template>
|
||||||
|
搜索
|
||||||
|
</a-button>
|
||||||
|
<a-button @click="reset">
|
||||||
|
<template #icon><icon-refresh /></template>
|
||||||
|
重置
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #toolbar-left>
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" @click="openAddModal">
|
||||||
|
<template #icon><icon-plus /></template>
|
||||||
|
<template #default>新增类型</template>
|
||||||
|
</a-button>
|
||||||
|
<span class="search-result-info">
|
||||||
|
共找到 <strong>{{ pagination.total }}</strong> 条记录
|
||||||
|
</span>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 启用状态 -->
|
||||||
|
<template #isEnabled="{ record }">
|
||||||
|
<a-tag :color="record.isEnabled === '1' ? 'green' : 'red'">
|
||||||
|
{{ record.isEnabled === '1' ? '启用' : '禁用' }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 操作列 -->
|
||||||
|
<template #action="{ record }">
|
||||||
|
<a-space>
|
||||||
|
<a-link @click="editRecord(record)">编辑</a-link>
|
||||||
|
<a-popconfirm
|
||||||
|
content="确定要删除这个制度类型吗?"
|
||||||
|
@ok="deleteRecord(record)"
|
||||||
|
>
|
||||||
|
<a-link status="danger">删除</a-link>
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</GiTable>
|
||||||
|
|
||||||
|
<!-- 新增/编辑弹窗 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="modalVisible"
|
||||||
|
:title="modalTitle"
|
||||||
|
width="600px"
|
||||||
|
@ok="handleSubmit"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
>
|
||||||
|
<a-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formData"
|
||||||
|
:rules="rules"
|
||||||
|
layout="vertical"
|
||||||
|
>
|
||||||
|
<a-form-item label="类型名称" field="typeName">
|
||||||
|
<a-input v-model="formData.typeName" placeholder="请输入制度类型名称" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="排序" field="sortOrder">
|
||||||
|
<a-input-number
|
||||||
|
v-model="formData.sortOrder"
|
||||||
|
placeholder="请输入排序值"
|
||||||
|
:min="0"
|
||||||
|
:max="999"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="状态" field="isEnabled">
|
||||||
|
<a-radio-group v-model="formData.isEnabled">
|
||||||
|
<a-radio value="1">启用</a-radio>
|
||||||
|
<a-radio value="0">禁用</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="备注" field="remark">
|
||||||
|
<a-textarea
|
||||||
|
v-model="formData.remark"
|
||||||
|
placeholder="请输入备注信息"
|
||||||
|
:rows="3"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
|
</GiPageLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted, watch } from 'vue'
|
||||||
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
import { IconSearch, IconRefresh } from '@arco-design/web-vue/es/icon'
|
||||||
|
import { regulationApi } from '@/apis/regulation'
|
||||||
|
import {
|
||||||
|
type RegulationType,
|
||||||
|
type CreateRegulationTypeRequest,
|
||||||
|
type UpdateRegulationTypeRequest,
|
||||||
|
type RegulationTypeSearchRequest
|
||||||
|
} from '@/apis/regulation/type'
|
||||||
|
|
||||||
|
defineOptions({ name: 'RegulationType' })
|
||||||
|
|
||||||
|
// 表格列定义
|
||||||
|
const tableColumns = [
|
||||||
|
{ title: '类型名称', dataIndex: 'typeName', key: 'typeName', width: 150 },
|
||||||
|
{ title: '备注', dataIndex: 'remark', key: 'remark', width: 200 },
|
||||||
|
{ title: '状态', dataIndex: 'isEnabled', key: 'isEnabled', slotName: 'isEnabled', width: 100 },
|
||||||
|
{ title: '创建人', dataIndex: 'createrName', key: 'createrName', width: 120 },
|
||||||
|
{ title: '创建时间', dataIndex: 'createTime', key: 'createTime', width: 180 },
|
||||||
|
{ title: '排序', dataIndex: 'sortOrder', key: 'sortOrder', width: 100 },
|
||||||
|
{ title: '操作', key: 'action', slotName: 'action', width: 150, fixed: 'right' }
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 表格数据
|
||||||
|
const dataList = ref<RegulationType[]>([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const pagination = reactive({
|
||||||
|
current: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
showTotal: true,
|
||||||
|
showJumper: true,
|
||||||
|
showPageSize: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 查询表单
|
||||||
|
const searchForm = reactive({
|
||||||
|
typeName: '',
|
||||||
|
status: '',
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 弹窗相关
|
||||||
|
const modalVisible = ref(false)
|
||||||
|
const modalTitle = ref('新增制度类型')
|
||||||
|
const formRef = ref()
|
||||||
|
const formData = reactive({
|
||||||
|
typeId: '',
|
||||||
|
typeName: '',
|
||||||
|
sortOrder: 0,
|
||||||
|
isEnabled: '1',
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const rules = {
|
||||||
|
typeName: [{ required: true, message: '请输入制度类型名称' }],
|
||||||
|
sortOrder: [{ required: true, message: '请输入排序值' }],
|
||||||
|
isEnabled: [{ required: true, message: '请选择状态' }]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取表格数据
|
||||||
|
const getTableData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
// 使用后端搜索接口
|
||||||
|
const response = await regulationApi.searchRegulationTypes({
|
||||||
|
page: pagination.current,
|
||||||
|
size: pagination.pageSize,
|
||||||
|
typeName: searchForm.typeName || undefined,
|
||||||
|
status: searchForm.status || undefined,
|
||||||
|
remark: searchForm.remark || undefined,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.status === 200) {
|
||||||
|
dataList.value = response.data.records || response.data
|
||||||
|
pagination.total = response.data.total || response.data.length
|
||||||
|
pagination.current = response.data.current || 1
|
||||||
|
} else {
|
||||||
|
Message.error('搜索失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('搜索制度类型失败:', error)
|
||||||
|
Message.error('搜索失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const search = () => {
|
||||||
|
pagination.current = 1
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const reset = () => {
|
||||||
|
Object.assign(searchForm, {
|
||||||
|
typeName: '',
|
||||||
|
status: '',
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
pagination.current = 1
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页事件
|
||||||
|
const onPageChange = (page: number) => {
|
||||||
|
pagination.current = page
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onPageSizeChange = (pageSize: number) => {
|
||||||
|
pagination.pageSize = pageSize
|
||||||
|
pagination.current = 1
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
const openAddModal = () => {
|
||||||
|
modalTitle.value = '新增制度类型'
|
||||||
|
modalVisible.value = true
|
||||||
|
resetForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
const editRecord = (record: RegulationType) => {
|
||||||
|
modalTitle.value = '编辑制度类型'
|
||||||
|
modalVisible.value = true
|
||||||
|
Object.assign(formData, {
|
||||||
|
typeId: record.typeId,
|
||||||
|
typeName: record.typeName,
|
||||||
|
sortOrder: record.sortOrder,
|
||||||
|
isEnabled: record.isEnabled,
|
||||||
|
remark: record.remark || ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
const deleteRecord = async (record: RegulationType) => {
|
||||||
|
try {
|
||||||
|
await regulationApi.deleteRegulationType(record.typeId)
|
||||||
|
Message.success('删除成功')
|
||||||
|
getTableData()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('删除失败:', error)
|
||||||
|
Message.error('删除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
await formRef.value.validate()
|
||||||
|
|
||||||
|
if (formData.typeId) {
|
||||||
|
// 更新
|
||||||
|
const updateData: UpdateRegulationTypeRequest = {
|
||||||
|
typeName: formData.typeName,
|
||||||
|
sortOrder: formData.sortOrder,
|
||||||
|
isEnabled: formData.isEnabled,
|
||||||
|
remark: formData.remark
|
||||||
|
}
|
||||||
|
await regulationApi.updateRegulationType(formData.typeId, updateData)
|
||||||
|
Message.success('更新成功')
|
||||||
|
} else {
|
||||||
|
// 新增
|
||||||
|
const createData: CreateRegulationTypeRequest = {
|
||||||
|
typeName: formData.typeName,
|
||||||
|
sortOrder: formData.sortOrder,
|
||||||
|
isEnabled: formData.isEnabled,
|
||||||
|
remark: formData.remark
|
||||||
|
}
|
||||||
|
await regulationApi.createRegulationType(createData)
|
||||||
|
Message.success('新增成功')
|
||||||
|
}
|
||||||
|
|
||||||
|
modalVisible.value = false
|
||||||
|
getTableData()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('操作失败:', error)
|
||||||
|
Message.error('操作失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消
|
||||||
|
const handleCancel = () => {
|
||||||
|
modalVisible.value = false
|
||||||
|
resetForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置表单
|
||||||
|
const resetForm = () => {
|
||||||
|
Object.assign(formData, {
|
||||||
|
typeId: '',
|
||||||
|
typeName: '',
|
||||||
|
sortOrder: 0,
|
||||||
|
isEnabled: '1',
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
formRef.value?.resetFields()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getTableData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.regulation-type {
|
||||||
|
.arco-card {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container {
|
||||||
|
padding: 16px;
|
||||||
|
background: var(--color-bg-2);
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
.arco-form-item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-right: 16px;
|
||||||
|
|
||||||
|
.arco-form-item-label {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-input,
|
||||||
|
.arco-select {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arco-btn {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-result-info {
|
||||||
|
color: var(--color-text-2);
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
color: var(--color-text-1);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue