Compare commits

..

No commits in common. "master" and "main" have entirely different histories.
master ... main

121 changed files with 19 additions and 18199 deletions

View File

@ -1,13 +0,0 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

34
.gitignore vendored
View File

@ -1,20 +1,20 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# ---> Actionscript
# Build and Release Folders
bin-debug/
bin-release/
[Oo]bj/
[Bb]in/
# dependencies
/node_modules
# Other files and folders
.settings/
# production
/dist
/demo
# Executables
*.swf
*.air
*.ipa
*.apk
# Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties`
# should NOT be excluded as they contain compiler settings and other important
# information for Eclipse / Flash Builder.
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.idea
yarn.lock
/openspec/
/.codebuddy/
/.idea/
/.playwright-cli/
/.vscode/
/.workbuddy/

1
.npmrc
View File

@ -1 +0,0 @@
shamefully-hoist=true

View File

@ -1,75 +1,3 @@
# 微应用模板说明文档
# safety-eval-service-frontend
## 在线文档
https://www.yuque.com/buhangjiecheshen-ymbtb/qc0093/gxdun1dphetcurko
## 安装依赖
项目依赖可通过 **yarn****npm** 进行安装:
```bash
# 使用 yarn
yarn
# 或使用 npm
npm i
```
## 开发服务&打包应用
```bash
# 启动开发服务
yarn serve:<env>
# 或
npm run serve:<env>
# 开发环境打包
yarn build:<env>
# 或
npm run build:<env>
```
## 路由配置&路由访问&自动化路由
所有页面必须放在`src/pages/container`目录下,启动访问页面请在浏览器地址栏输入`/<appIdentifier>/container/<你的路由页面文件名称>`
解释:
1. 所有页面组件命名为`index.js`或`index.jsx`,必须放在一个首字母大写的文件中。
2. `container`为固定路径访问格式
3. `<appIdentifier>`为应用的唯一标识符,也是应用路由的`basename`,在底座中用于区分其他应用。可在根目录 `jjb.config.js` 文件的 `appIdentifier` 节点中进行修改。
4. 自动化路由将根据`pages/container`中的路由页面文件自动生成路由树。
5. `id`匹配路由,文件夹命名`_id`
## 应用接口环境配置
应用接口环境相关配置在根目录 `jjb.config.js` 文件的 `environment` 节点中进行定义。
## 应用开发服务配置
应用开发服务相关配置在根目录 `jjb.config.js` 文件的 `server` 节点中进行定义。
## Babel 配置
应用的 `Babel` 配置在根目录 `jjb.babel.js` 文件中进行管理。
## 目录说明
1. `src/api/` 配置各个 store 模块的接口数据。
2. `src/components/` 全局公共组件。
3. `src/enumerate/` 全局各种枚举配置。
4. `src/pages/` 页面文件目录。
5. `src/main.js` 应用的入口文件。
## 核心依赖
1. `@cqsjjb/jjb-common-decorator`
1. 公共装饰器库,内部包含:
1. 按钮权限处理
2. antd/Table 控制
3. 文本重命名处理
4. 具体使用方式可参考各个模块的 `d.ts`
2. `@cqsjjb/jjb-common-lib`
1. 公共工具库,具体 API 使用请查看 `d.ts`
3. `@cqsjjb/jjb-dva-runtime`
1. 核心运行时,基于 `dvajs` 实现。
1. 应用核心依赖模块
2. 应用的自动化路由
3. `store` 模块接口数据处理
4. 均基于此依赖实现,具体使用方式请查看 `d.ts`
4. `@cqsjjb/jjb-react-admin-component`
1. 公共组件库,具体组件使用方式请查看 `d.ts`
重庆安全评价前端项目

View File

@ -1,66 +0,0 @@
/*
_ooOoo_
o8888888o
88" . "88
(| -_- |)
O\ = /O
____/`---'\____
. ' \\| |// `.
/ \\||| : |||// \
/ _||||| -:- |||||- \
| | \\\ - /// | |
| \_| ''\---/'' | |
\ .-\__ `-` ___/-. /
___`. .' /--.--\ `. . __
."" '< `.___\_<|>_/___.' >'"".
| | : `- \`.;`\ _ /`;.`/ - ` : | |
\ \ `-. \_ __\ /__ _/ .-` / /
======`-.____`-.___\_____/___.-`____.-'======
`=---='
.............................................
佛祖保佑 永无BUG
佛曰:
写字楼里写字间写字间里程序员
程序人员写程序又拿程序换酒钱
酒醒只在网上坐酒醉还来网下眠
酒醉酒醒日复日网上网下年复年
但愿老死电脑间不愿鞠躬老板前
奔驰宝马贵者趣公交自行程序员
别人笑我忒疯癫我笑自己命太贱
不见满街漂亮妹哪个归得程序员
*/
const blessedByBuddha
= "%c _ooOoo_\n"
+ " o8888888o\n"
+ " 88\" . \"88\n"
+ " (| -_- |)\n"
+ " O\\ = /O\n"
+ " ____/`---'\\____\n"
+ " . ' \\\\| |// `.\n"
+ " / \\\\||| : |||// \\\n"
+ " / _||||| -:- |||||- \\\n"
+ " | | \\\\\\ - /// | |\n"
+ " | \\_| ''\\---/'' | |\n"
+ " \\ .-\\__ `-` ___/-. /\n"
+ " ___`. .' /--.--\\ `. . __\n"
+ " .\"\" '< `.___\\_<|>_/___.' >'\"\".\n"
+ " | | : `- \\`.;`\\ _ /`;.`/ - ` : | |\n"
+ " \\ \\ `-. \\_ __\\ /__ _/ .-` / /\n"
+ " ======`-.____`-.___\\_____/___.-`____.-'======\n"
+ " `=---='\n"
+ "\n"
+ "%c .............................................\n"
+ " 佛祖保佑 永无BUG\n"
+ "\n"
+ "%c 佛曰:\n"
+ " 写字楼里写字间,写字间里程序员;\n"
+ " 程序人员写程序,又拿程序换酒钱。\n"
+ " 酒醒只在网上坐,酒醉还来网下眠;\n"
+ " 酒醉酒醒日复日,网上网下年复年。\n"
+ " 但愿老死电脑间,不愿鞠躬老板前;\n"
+ " 奔驰宝马贵者趣,公交自行程序员。\n"
+ " 别人笑我忒疯癫,我笑自己命太贱;\n"
+ " 不见满街漂亮妹,哪个归得程序员?";
console.log(blessedByBuddha, "color:#ffd700", "color:red", "color:#1e80ff");

View File

@ -1,572 +0,0 @@
# 证照管理子应用 — 本地开发指南
## 目录
- [1. 项目概述](#1-项目概述)
- [2. 环境准备与启动](#2-环境准备与启动)
- [3. 路由系统](#3-路由系统)
- [4. 菜单配置](#4-菜单配置)
- [5. 页面开发流程](#5-页面开发流程)
- [6. API 接口声明](#6-api-接口声明)
- [7. 状态管理DVA](#7-状态管理dva)
- [8. 共享组件](#8-共享组件)
- [9. 本地模拟布局](#9-本地模拟布局)
- [10. 已知问题与注意事项](#10-已知问题与注意事项)
- [11. 目录结构参考](#11-目录结构参考)
---
## 1. 项目概述
本项目是基于 **qiankun 微前端** 框架的子应用,运行在 **GBS 底座平台** 内。使用以下核心技术栈:
| 技术 | 说明 |
|------|------|
| React 18 | UI 组件框架 |
| Ant Design 5.x | UI 组件库 |
| @cqsjjb/jjb-dva-runtime | 底座 DVA 状态管理运行时 |
| @cqsjjb/jjb-common-decorator | 装饰器工具(权限、模块加载等) |
| rspack | 构建打包工具 |
| pnpm | 包管理器 |
项目通过 `jjb.config.js` 中配置的 `appIdentifier: "certificate"` 注册到底座,所有页面路由前缀为 `/certificate/container/`
---
## 2. 环境准备与启动
### 2.1 安装依赖
```bash
pnpm install
```
### 2.2 启动命令
| 命令 | 说明 |
|------|------|
| `pnpm run serve:development` | **开发环境启动**(推荐) |
| `pnpm run serve:production` | 生产环境配置启动 |
| `pnpm run build:development` | 开发环境构建 |
| `pnpm run build:production` | 生产环境构建 |
### 2.3 访问地址
启动后开发服务器默认监听 `http://127.0.0.1:8081`,访问:
```
http://localhost:8081/certificate/container/
http://localhost:8081/certificate/container/Test
```
> **注意**:本地独立启动时,应用处于 qiankun 独立运行模式(`window.__POWERED_BY_QIANKUN__` 为 false
> 会使用内置的 `SimulatedLayout` 模拟侧边栏和标签页布局。
### 2.4 配置说明
`jjb.config.js` 中的关键配置项:
```js
module.exports = {
appIdentifier: "certificate", // 应用唯一标识,对应路由前缀
server: {
port: "8081", // 开发服务器端口
host: "127.0.0.1", // 服务地址
},
environment: {
development: {
API_HOST: "https://gbs-gateway.qhdsafety.com", // 开发环境接口地址
},
},
};
```
---
## 3. 路由系统
### 3.1 约定式路由
本项目使用 **文件系统约定式路由**,由 `@cqsjjb/jjb-dva-runtime` 框架自动注册。页面对应的路径由目录结构决定:
```
src/pages/Container/<目录层级>/index.js → /certificate/container/<目录层级>
```
**示例:**
| 文件路径 | 对应路由 |
|----------|----------|
| `src/pages/Container/Test/index.js` | `/certificate/container/Test` |
| `src/pages/Container/Supervision/index.js` | `/certificate/container/Supervision` |
| `src/pages/Container/Supervision/PersonnelLicense/PersonInCharge/List/index.js` | `/certificate/container/Supervision/PersonnelLicense/PersonInCharge/List` |
### 3.2 页面组件类型
项目中有两类页面组件:
1. **容器组件**(中间层级):作为 `{props.children}` 的容器,负责嵌套子页面
```js
function ContainerPage(props) {
return <div>{props.children}</div>;
}
export default ContainerPage;
```
2. **叶子页面**(终端页面):实际的业务功能页面,通过 `Connect` 连接 DVA 数据
```js
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function List(props) {
return <div>业务内容</div>;
}
export default Connect([NS_USER_CERTIFICATE], true)(List);
```
### 3.3 路由匹配规则
- 框架自动递归扫描 `src/pages/` 目录,每个 `index.js` 对应一个路由
- 路由路径是将目录路径转换为小写并用 `/` 连接
- 带参数的路由(如 `/View/:id`)需在目录中创建对应层级
---
## 4. 菜单配置
侧边栏菜单定义在 `src/pages/Container/Layout/menuConfig.js` 中。
### 4.1 菜单数据结构
```js
{
key: "/certificate/container/<实际路由>", // 必须与文件目录对应的路由完全一致
label: "菜单显示名称",
icon: <SomeIcon />, // Ant Design 图标组件
children: [...] // 可选,子菜单项
}
```
### 4.2 当前菜单结构
```
📁 监管端 (Supervision)
├── 📄 企业证照 (EnterpriseLicense)
│ ├── 企业证照管理
│ ├── 分公司统计
│ └── 干系人统计
└── 📄 人员证照 (PersonnelLicense)
├── 负责人
├── 安全管理员
├── 特种设备
├── 特种作业人员
├── 分公司人员统计
└── 干系人人员统计
📁 分公司端 (BranchCompany)
└── 📄 企业证照 (EnterpriseLicense)
├── 企业证照管理
└── 📄 人员证照 (PersonnelLicense)
├── 负责人
├── 安全管理员
├── 特种设备
└── 特种作业人员
📁 干系人端 (Stakeholder)
└── 📄 企业证照 (EnterpriseLicense)
├── 企业证照管理
└── 📄 人员证照 (PersonnelLicense)
├── 负责人
├── 安全管理员
├── 特种设备
└── 特种作业人员
📁 测试页面 (Test)
```
### 4.3 菜单配置注意事项
> **关键规则**:菜单的 `key` 必须与文件系统的实际目录层级完全一致,否则会出现"页面不存在"。
以"干系人端 → 负责人"为例:
- **文件位置**`Stakeholder/EnterpriseLicense/PersonnelLicense/PersonInCharge/List/index.js`
- **正确路径**`/certificate/container/Stakeholder/EnterpriseLicense/PersonnelLicense/PersonInCharge/List`
- **错误路径**(缺少 `EnterpriseLicense` 层级):`/certificate/container/Stakeholder/PersonnelLicense/PersonInCharge/List`
### 4.4 工具函数
`menuConfig.js` 同时导出以下辅助函数:
| 函数 | 参数 | 返回值 | 说明 |
|------|------|--------|------|
| `flattenMenu(items)` | 菜单树 | 扁平数组 | 将树形菜单拍平 |
| `findMenuPath(path)` | 当前路由 | 菜单项数组 | 从根到叶子节点的面包屑路径 |
| `getPageLabel(path)` | 当前路由 | 字符串 | 获取页面标签名 |
| `getOpenKeys(path)` | 当前路由 | 路径数组 | 默认展开的菜单项 |
| `getSelectedKeys(path)` | 当前路由 | 路径数组 | 默认选中的菜单项 |
---
## 5. 页面开发流程
### 5.1 新增业务页面
以新增"监管端 → 人员证照 → 新增功能"为例:
**步骤 1创建目录结构**
```
src/pages/Container/Supervision/PersonnelLicense/NewFeature/
├── index.js # 容器组件
├── List/
│ └── index.js # 列表页
└── View/
└── index.js # 详情页
```
**步骤 2编写容器组件**
`src/pages/Container/Supervision/PersonnelLicense/NewFeature/index.js`
```js
function NewFeature(props) {
return <div>{props.children}</div>;
}
export default NewFeature;
```
**步骤 3编写列表页面**
`src/pages/Container/Supervision/PersonnelLicense/NewFeature/List/index.js`
```js
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function List(props) {
return (
<div>
<h1>新功能列表</h1>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(List);
```
**步骤 4添加菜单配置**
`src/pages/Container/Layout/menuConfig.js` 中添加:
```js
{
key: "/certificate/container/Supervision/PersonnelLicense",
label: "人员证照",
icon: <TeamOutlined />,
children: [
// ... 已有菜单项
{
key: "/certificate/container/Supervision/PersonnelLicense/NewFeature/List",
label: "新功能",
icon: <SomeIcon />,
},
],
}
```
### 5.2 新增测试页面(独立于业务)
`src/pages/Container/` 下创建目录,并在菜单中添加即可:
```
src/pages/Container/MyDebug/
└── index.js
```
菜单:
```js
{
key: "/certificate/container/MyDebug",
label: "调试页面",
icon: <ExperimentOutlined />,
}
```
### 5.3 验证路径是否正确
启动项目后,通过以下方式验证:
1. 在浏览器直接访问完整 URL
2. 从侧边栏菜单点击导航
3. 检查开发者工具 Network 面板404 表示路径不匹配
---
## 6. API 接口声明
### 6.1 接口定义位置
API 接口声明在 `src/api/` 目录下,按模块划分:
```
src/api/
├── userCertificate/index.js # 人员证书接口
├── corpCertificate/index.js # 企业证书接口
├── courseware/index.js # 课件接口
└── global/index.js # 全局接口
```
### 6.2 declareRequest 语法
```js
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
// 格式declareRequest(loadingKey, "HttpMethod > 接口路径")
export const listApi = declareRequest(
"namespaceLoading", // loading 状态 key
"Post > @/certificate/userCertificate/list", // POST + 相对路径(带 @
);
export const getApi = declareRequest(
"namespaceLoading",
"Get > /certificate/userCertificate/getInfoById/{id}", // GET + 绝对路径
);
```
**语法规则:**
| 符号 | 说明 | 示例 |
|------|------|------|
| `@/` | 相对路径,使用 `jjb.config.js` 中配置的 `API_HOST` | `Post > @/certificate/xxx/list` |
| `/` | 绝对路径 | `Get > /config/dict-trees/list` |
| `{param}` | 路径参数 | `/certificate/xxx/delete/{id}` |
| `Post >` | POST 请求 | — |
| `Get >` | GET 请求 | — |
| `Put >` | PUT 请求 | — |
| `Delete >` | DELETE 请求 | — |
### 6.3 在组件中使用 API
API 通过 DVA 的 `dispatch``Connect` 来调用。`declareRequest` 会自动生成对应的 dva effect
```js
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function List({ dispatch, userCertificate }) {
const fetchData = () => {
dispatch({
type: `${NS_USER_CERTIFICATE}/userCertificateList`, // namespace/接口名
payload: { page: 1, size: 10 },
});
};
return <div>{/* ... */}</div>;
}
export default Connect([NS_USER_CERTIFICATE], true)(List);
```
---
## 7. 状态管理DVA
### 7.1 Namespace 定义
`src/enumerate/namespace/index.js` 中定义了所有 DVA namespace
```js
export const NS_GLOBAL = defineNamespace("global");
export const NS_PERSNONEL_CERTFICATE = defineNamespace("personnelCertificate");
export const NS_CORP_CERTIFICATE = defineNamespace("corpCertificate");
export const NS_USER_CERTIFICATE = defineNamespace("userCertificate");
export const NS_COURSEWARE = defineNamespace("courseware");
```
### 7.2 Connect 高阶组件
```js
Connect([NS_USER_CERTIFICATE], true)(MyComponent)
```
- 第一个参数:需要连接的 namespace 数组
- 第二个参数:是否在组件挂载时自动加载数据(`true` 表示自动触发 `load` effect
### 7.3 数据流向
```
dispatch(action) → DVA Effect → declareRequest → HTTP API → reducer → state 更新 → 组件重渲染
```
### 7.4 本地开发时的限制
本地独立运行时非底座环境DVA 状态管理正常可用,但 `declareRequest` 发起的 HTTP 请求会因缺少认证 Token 而返回 **401**。详见 [§10 已知问题](#10-已知问题与注意事项)。
---
## 8. 共享组件
业务组件位于 `src/components/` 目录下,目前包括:
```
src/components/
├── PersonInChargeInfo/ # 负责人详情组件
├── PersonInChargeList/ # 负责人列表组件含搜索、分页、CRUD
├── SpecialCertificateInfo/ # 特种证书详情组件
└── SpecialCertificateList/ # 特种证书列表组件
```
组件通过 `@/components/xxx` 或别名 `~/components/xxx` 引入:
```js
import PersonInChargeList from "~/components/PersonInChargeList";
```
### 8.1 组件参数传递
列表组件通过 `props` 接收业务参数:
```js
<PersonInChargeList
props={props} // dva props 透传
certificatePhotoType={161} // 证照照片类型
personnelType="zyfzr" // 人员类型标识
permissionAdd="gfd-xxx-add" // 新增权限码
permissionEdit="gfd-xxx-edit" // 编辑权限码
permissionView="gfd-xxx-info" // 查看权限码
permissionDel="gfd-xxx-del" // 删除权限码
dictionaryType="zyfzrgwmc0000" // 字典类型
/>
```
---
## 9. 本地模拟布局
### 9.1 工作原理
`src/pages/Container/index.js` 中的 `AppMiddle` 组件检测运行环境:
```js
function AppMiddle(props) {
const content = process.env.NODE_ENV === "development" && !window.__POWERED_BY_QIANKUN__
? <SimulatedLayout>{props.children}</SimulatedLayout>
: props.children;
// ...
}
```
- **本地开发**`development` + 非 qiankun使用 `SimulatedLayout` 包裹内容,提供模拟的侧边栏、面包屑和标签页
- **底座环境**:由底座平台提供真实的布局
### 9.2 SimulatedLayout 功能
`src/pages/Container/Layout/index.jsx` 提供:
| 功能 | 说明 |
|------|------|
| 侧边栏 | 使用 `menuConfig.js` 的菜单数据渲染,支持折叠 |
| 面包屑 | 根据当前路径自动计算导航路径 |
| 多标签页 | 支持打开多个页面标签,右键关闭操作 |
| 标签持久化 | 使用 sessionStorage 保存标签状态 |
| 主题适配 | 通过 `window.base?.themeConfig` 支持底座主题切换 |
---
## 10. 已知问题与注意事项
### 10.1 API 401 错误
**现象**:本地启动后,点击侧边栏菜单(除 Test 页面外),控制台报 401 错误。
**原因**:本项目是 qiankun 微前端子应用API 请求需要的认证 Token 由 GBS 底座注入。本地独立运行时没有底座,也就没有 Token所有 API 调用被拒绝。
**处理方式**
- **纯前端调试**:使用 Test 页面(`/certificate/container/Test`),该页面不发起任何 API 请求
- **完整功能调试**:在 GBS 底座环境中打开应用
### 10.2 路由路径与菜单 key 必须匹配
菜单的 `key` 值必须和文件目录层级完全一致。错误示例见 [§4.3 菜单配置注意事项](#43-菜单配置注意事项)。
### 10.3 重命名项目目录后需重新安装依赖
由于 pnpm 使用基于绝对路径的符号链接管理 `node_modules`,重命名项目根目录后需要:
```bash
Remove-Item -Recurse -Force node_modules
pnpm install
```
### 10.4 权限控制
页面使用 `@cqsjjb/jjb-common-decorator/permission``Permission` 装饰器控制访问:
```js
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));
```
权限码由底座平台管理,本地开发时权限校验可能不生效。
### 10.5 pnpm overrides
`package.json` 中通过 `pnpm.overrides` 强制统一 React 等依赖版本,避免多副本冲突:
```json
{
"pnpm": {
"overrides": {
"react": "$react",
"react-dom": "$react-dom"
}
}
}
```
---
## 11. 目录结构参考
```
safety-eval-service-frontend/
├── public/
│ └── index.html # HTML 模板
├── src/
│ ├── main.js # 应用入口setup + 生命周期导出)
│ ├── api/ # API 接口声明
│ │ ├── global/
│ │ ├── userCertificate/
│ │ ├── corpCertificate/
│ │ └── courseware/
│ ├── components/ # 共享业务组件
│ │ ├── PersonInChargeList/
│ │ ├── PersonInChargeInfo/
│ │ ├── SpecialCertificateList/
│ │ └── SpecialCertificateInfo/
│ ├── enumerate/ # 枚举/常量定义
│ │ ├── namespace/ # DVA namespace
│ │ ├── context/ # React Context
│ │ └── constant/ # 常量
│ ├── pages/
│ │ └── Container/
│ │ ├── index.js # 容器入口Antd 主题 + 布局切换)
│ │ ├── Entry/ # 底座入口页
│ │ ├── Layout/ # 本地模拟布局
│ │ │ ├── index.jsx # SimulatedLayout 组件
│ │ │ └── menuConfig.js # ★ 菜单配置
│ │ ├── Test/ # 测试页面(无 API 依赖)
│ │ ├── Supervision/ # 监管端
│ │ │ ├── EnterpriseLicense/
│ │ │ └── PersonnelLicense/
│ │ ├── BranchCompany/ # 分公司端
│ │ │ └── EnterpriseLicense/
│ │ └── Stakeholder/ # 干系人端
│ │ └── EnterpriseLicense/
│ ├── assets/ # 静态资源
│ └── utils/ # 工具函数
├── jjb.config.js # 项目配置端口、API_HOST、主题等
├── package.json
├── pnpm-lock.yaml
└── pnpm-workspace.yaml
```

View File

@ -1,47 +0,0 @@
# 证照管理子应用 — 快速启动
## 环境要求
- Node.js >= 18
- pnpm >= 8
## 启动
```bash
# 安装依赖(首次或重命名项目目录后)
pnpm install
# 启动开发服务器
pnpm run serve:development
```
## 访问
| 地址 | 说明 |
|------|------|
| `http://localhost:8081/certificate/container/` | 应用首页 |
| `http://localhost:8081/certificate/container/Test` | 测试页面(无 API 依赖,纯前端调试可用) |
## 常用命令
| 命令 | 说明 |
|------|------|
| `pnpm run serve:development` | 开发环境启动(推荐) |
| `pnpm run serve:production` | 生产环境配置启动 |
| `pnpm run build:development` | 开发环境构建 |
| `pnpm run build:production` | 生产环境构建 |
## 技术栈
- React 18 + Ant Design 5.x
- @cqsjjb/jjb-dva-runtimeGBS 底座 DVA 运行时)
- rspack 构建 / pnpm 包管理
- qiankun 微前端子应用
## 重要提示
- 本项目为 GBS 底座微前端子应用,独立启动时有内置模拟布局
- 本地独立运行时 API 调用会返回 401缺少底座注入的认证 Token
- 仅 Test 页面可在本地无底座环境下正常访问
- 重命名项目目录后需 `pnpm install` 重建 node_modules 符号链接
- 完整开发指南请参考 [dev-guide.md](./dev-guide.md)

View File

@ -1,48 +0,0 @@
import antfu from "@antfu/eslint-config";
export default antfu({
formatters: {
html: false,
css: true,
},
test: false,
typescript: true,
react: true,
vue: false,
markdown: false,
stylistic: {
semi: true,
quotes: "double",
},
overrides: {
react: {
"react/no-comment-textnodes": "off",
"react-hooks-extra/no-unnecessary-use-prefix": "off",
"react-hooks-extra/prefer-use-state-lazy-initialization": "off",
"react-hooks/exhaustive-deps": "off",
},
javascript: {
"no-console": process.env.NODE_ENV === "production" ? "error" : "warn",
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "warn",
"no-alert": process.env.NODE_ENV === "production" ? "error" : "warn",
"no-restricted-syntax": [
"error",
{
selector: "VariableDeclarator[id.name='pd']",
message: "不允许使用 pd请改用有语义化的变量名",
},
{
selector: "ObjectExpression > Property[key.name='pd']",
message: "不允许使用 pd请改用有语义化的变量名",
},
],
"no-unused-vars": ["error", { varsIgnorePattern: "^React$" }],
},
},
rules: {
"antfu/top-level-function": "off",
"node/prefer-global/process": "off",
"dot-notation": "off",
"linebreak-style": ["off", "windows"],
},
});

View File

@ -1,23 +0,0 @@
module.exports = {
compact: false,
// 插件
plugins: [
[
"@babel/plugin-proposal-decorators",
{
legacy: true,
},
],
],
// 预设
presets: [
["@babel/preset-env", {
targets: {
browsers: ["ie >= 10"],
},
}],
["@babel/preset-react", {
runtime: "automatic",
}],
],
};

View File

@ -1,78 +0,0 @@
module.exports = {
// 应用后端git地址部署上线需要
javaGit: "<git-url>",
// 应用后端仓库名称,部署上线需要
javaGitName: "<git-name>",
// 环境配置
environment: {
development: {
// 应用后端分支名称,部署上线需要
javaGitBranch: "<branch-name>",
// 接口服务地址
// API_HOST: "http://192.168.20.100:30140",
API_HOST: "https://gbs-gateway.qhdsafety.com",
},
production: {
// 应用后端分支名称,部署上线需要
javaGitBranch: "<branch-name>",
// 接口服务地址
API_HOST: "",
},
},
// 应用唯一标识符
appIdentifier: "certificate",
// 应用上下文注入全局变量
contextInject: {
// 应用Key
appKey: "",
// fileUrl: "http://192.168.20.240:9787/mnt",
// fileUrl: "https://jpfz.qhdsafety.com/gbsFileTest/",
fileUrl: "https://skqhdg.porthebei.com:9004/file/uploadFiles2/",
},
// public/index.html注入全局变量
windowInject: {
// 应用标题
title: "微应用模板",
// 注入css链接集合
links: [],
element: {
root: {
// 挂载DOM元素ID
id: "root",
},
},
// 注入js链接集合
scripts: [],
},
// 开发服务
server: {
// 监听端口号
port: "8081",
// 服务地址
host: "127.0.0.1",
// 是否自动打开浏览器
open: true,
},
// 框架
framework: {
// ant-design
antd: {
// 全局antd-class-name前缀
"ant-prefix": "micro-temp",
// 全局字体
"fontFamily": "PingFangSC-Regular",
// 全局主题色
"colorPrimary": "#1677ff",
// 全局圆角
"borderRadius": 2,
},
},
// webpack
webpackConfig: {
// 单页面插件
htmlWebpackPluginOption: {
// 自动注入编译后的文件到public/index.html中
inject: true,
},
},
};

View File

@ -1,9 +0,0 @@
{
"compilerOptions": {
"baseUrl": "src",
"paths": {
"~/*": ["*"]
}
},
"include": ["src"]
}

View File

@ -1,63 +0,0 @@
{
"name": "micro-app",
"version": "2.0.0",
"description": "建教帮微应用模板",
"author": "JJB",
"license": "MIT",
"main": "index.js",
"scripts": {
"serve": "node node_modules/@cqsjjb/scripts/rspack.dev.server.js",
"build": "node node_modules/@cqsjjb/scripts/rspack.build.js",
"push": "jjb-cmd push java production",
"clean-cache": "rimraf node_modules/.cache/webpack",
"serve:development": "cross-env NODE_ENV=development npm run serve",
"serve:production": "cross-env NODE_ENV=production npm run serve",
"build:development": "cross-env NODE_ENV=development npm run build",
"build:production": "cross-env NODE_ENV=production npm run build",
"code-optimization": "node node_modules/@cqsjjb/scripts/code-optimization.js",
"lint": "eslint --ext .js,.jsx,.tsx --fix src"
},
"dependencies": {
"@ant-design/icons": "^5.0.0",
"@ant-design/pro-components": "^2.8.10",
"@cqsjjb/jjb-common-decorator": "latest",
"@cqsjjb/jjb-common-lib": "latest",
"@cqsjjb/jjb-dva-runtime": "latest",
"@cqsjjb/jjb-react-admin-component": "latest",
"@rc-component/motion": "^1.0.0",
"@rc-component/util": "^1.11.1",
"ahooks": "^3.9.5",
"antd": "^5.11.2",
"dayjs": "^1.11.7",
"lodash-es": "^4.17.21",
"qrcode.react": "^4.2.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"zy-react-library": "^1.2.3"
},
"devDependencies": {
"@antfu/eslint-config": "^5.4.1",
"@babel/plugin-proposal-decorators": "^7.19.3",
"@cqsjjb/scripts": "latest",
"@eslint-react/eslint-plugin": "^2.2.2",
"babel-loader": "^9.1.3",
"cross-env": "^7.0.3",
"css-loader": "^6.8.1",
"eslint": "^9.37.0",
"eslint-plugin-format": "^1.0.2",
"eslint-plugin-react-hooks": "^7.0.0",
"eslint-plugin-react-refresh": "^0.4.23",
"file-loader": "^6.2.0",
"less-loader": "^11.1.3",
"style-loader": "^3.3.3",
"typescript": "^5.9.3"
},
"pnpm": {
"overrides": {
"history": "^4.10.1",
"path-to-regexp": "^1.9.0",
"react": "$react",
"react-dom": "$react-dom"
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +0,0 @@
allowBuilds:
es5-ext: false
zy-react-library: false

View File

@ -1,54 +0,0 @@
<!--BEGIN-->
<!--<% var { env: $env, process: $process, mode: $mode, builtInfo: $builtInfo, links: $links, redirectLogin: $redirectLogin, framework: $framework, scripts: $scripts, element: $element } = htmlWebpackPlugin.options %>-->
<!--<% var { appKey: $appKey, antd: $antd, basename: $basename, API_HOST: $API_HOST } = $process %>-->
<!--<% var { ['ant-prefix']: $antPrefix, fontFamily: $fontFamily, colorPrimary: $colorPrimary, borderRadius: $borderRadius } = $antd %>-->
<!--NED-->
<!DOCTYPE html>
<html lang="zh">
<head data-built-info="<%= $builtInfo %>">
<meta charset="UTF-8"/>
<meta name="renderer" content="webkit"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,Chrome=1"/>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no,viewport-fit=cover">
<% for (const item of $links) { %>
<link type="text/css" rel="stylesheet" href="<%= item %>"></link>
<% } %>
<title>--</title>
<script>
(function () {
const APP_ENV = {
antd: {
'ant-prefix': '<%= $antPrefix %>',
fontFamily: '<%= $fontFamily %>',
colorPrimary: '<%= $colorPrimary %>',
borderRadius: parseInt('<%= $borderRadius %>')
},
appKey: '<%= $appKey %>',
basename: '<%= $basename %>',
API_HOST: '<%= $API_HOST %>'
};
APP_ENV.API_HOST = sessionStorage.API_HOST || APP_ENV.API_HOST || window.location.origin;
window.process = {
env: { app: APP_ENV },
NODE_ENV: '<%= $mode %>'
};
window.__JJB_ENVIRONMENT__ = {
API_HOST: APP_ENV.API_HOST,
redirect: '<%= $redirectLogin %>',
FRAMEWORK: APP_ENV.antd
};
})();
</script>
<!-- SCRIPTS -->
<% for (const item of $scripts) { %>
<script src="<%= item %>" type="text/javascript"></script>
<% } %>
</head>
<body >
<!-- NOSCRIPT -->
<noscript>此网页需要开启JavaScript功能。</noscript>
<!-- MAIN -->
<% const { root } = $element; %>
<div id="<%= root.id %>" style="width: 100%; height: 100%; position: relative;overflow-y: auto"></div>
</body>
</html>

View File

@ -1,41 +0,0 @@
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
export const corpCertificateList = declareRequest(
"corpCertificateLoading",
"Post > @/certificate/corpCertificate/list",
);
export const corpCertificateInfo = declareRequest(
"corpCertificateLoading",
"Get > /certificate/corpCertificate/getInfoById?id={id}",
);
export const corpCertificateAdd = declareRequest(
"corpCertificateLoading",
"Post > @/certificate/corpCertificate/save",
);
export const corpCertificateEdit = declareRequest(
"corpCertificateLoading",
"Put > @/certificate/corpCertificate/edit",
);
// 股份端查看分公司-相关方证照统计信息分页
export const corpCertificateStatPage = declareRequest(
"corpCertificateLoading",
"Post > @/certificate/corpCertificate/statPage",
);
export const corpCertificateRemove = declareRequest(
"corpCertificateLoading",
"Put > @/certificate/corpCertificate/remove?id={id}",
);
// 获取字典
export const dictValuesData = declareRequest(
"corpCertificateLoading",
"Get > /config/dict-trees/list/by/dictValues",
);
export const corpCertificateIsExistCertNo = declareRequest(
"corpCertificateLoading",
"Get > /certificate/corpCertificate/isExistCertNo",
);

View File

@ -1,6 +0,0 @@
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
export const identifyPartList = declareRequest(
"coursewareLoading",
"Post > @/risk/busIdentifyPart/list",
);

View File

@ -1,11 +0,0 @@
export {};
// export const riskList = declareRequest(
// "loading",
// "Post > @/xxx",
// "dataSource: [] | res.data || [] & total: 0 | res.totalCount || 0 & pageIndex: 1 | res.pageIndex || 1 & pageSize: 10 | res.pageSize || 10",
// );
// export const riskDelete = declareRequest(
// "loading",
// "Delete > @/xxx/{id}",
// );

View File

@ -1,48 +0,0 @@
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
export const userCertificateList = declareRequest(
"userCertificateLoading",
"Post > @/certificate/userCertificate/list",
);
export const userCertificateInfo = declareRequest(
"userCertificateLoading",
"Get > /certificate/userCertificate/getInfoById/{id}",
);
export const userCertificateAdd = declareRequest(
"userCertificateLoading",
"Post > @/certificate/userCertificate/save",
);
export const userCertificateEdit = declareRequest(
"userCertificateLoading",
"Put > @/certificate/userCertificate/edit",
);
// 股份端查看分公司-相关方证照统计信息分页
export const userCertificateStatPage = declareRequest(
"userCertificateLoading",
"Post > @/certificate/userCertificate/corpCertificateStatPage",
);
export const userCertificateRemove = declareRequest(
"userCertificateLoading",
"Delete > @/certificate/userCertificate/delete/{id}",
);
// 获取字典
export const dictValuesData = declareRequest(
"userCertificateLoading",
"Get > /config/dict-trees/list/by/dictValues",
);
export const userCertificateIsExistCertNo = declareRequest(
"userCertificateLoading",
"Get > /certificate/userCertificate/isExistCertNo",
);
export const projectHasUser = declareRequest(
"userCertificateLoading",
`Get > /xgfManager/project/projectHasUser`,
);
// 获取人员 (带权限)
export const getUserlistAll = declareRequest(
"corpCertificateLoading",
"Post > @/certificate/user/listAll",
);

View File

@ -1,62 +0,0 @@
/* 打印时:隐藏所有内容 */
@media print {
body * {
visibility: hidden;
}
/* 只显示要打印的 div 及其子元素 */
#print-invoice,
#print-invoice * {
visibility: visible;
}
#print-invoice {
position: absolute;
left: 0;
top: 0;
width: 100%;
padding: 1cm;
box-sizing: border-box;
font-size: 12pt;
font-family: 'SimSun', '宋体', serif;
}
/* 隐藏弹窗中的操作按钮(双重保险) */
.no-print {
display: none !important;
}
}
/* 屏幕上正常显示 */
@media screen {
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal-content {
background: white;
padding: 20px;
border-radius: 8px;
width: 80%;
max-width: 600px;
}
.modal-actions {
margin-top: 20px;
text-align: right;
}
.modal-actions button {
margin-left: 10px;
padding: 8px 16px;
}
}

View File

@ -1,142 +0,0 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Descriptions, Divider } from "antd";
import { useEffect, useState } from "react";
import Page from "zy-react-library/components/Page";
import PreviewImg from "zy-react-library/components/PreviewImg";
import { UPLOAD_FILE_TYPE_ENUM } from "zy-react-library/enum/uploadFile/gwj";
import useGetFile from "zy-react-library/hooks/useGetFile";
import useGetUrlQuery from "zy-react-library/hooks/useGetUrlQuery";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
const JOB_STATUS = {
1: "在职",
0: "离职",
2: "信息变更中",
3: "未入职",
4: "实习生",
5: "实习结束",
6: "退休",
7: "劳务派遣",
8: "劳务派遣结束",
11: "入职待审核",
10: "离职待审核",
};
function PersonInChargeInfo({ props, certificatePhotoType }) {
const [info, setInfo] = useState({});
const queryParams = useGetUrlQuery();
const { loading: getFileLoading, getFile } = useGetFile();
useEffect(() => {
console.log(queryParams);
const fetchData = async () => {
const res = await props.userCertificateInfo({
id: queryParams["id"],
});
const licenseFile = await getFile({
eqType: UPLOAD_FILE_TYPE_ENUM[certificatePhotoType],
eqForeignKey: res.data.userCertificateId,
});
res.data.licenseFile = licenseFile;
setInfo(res.data || {});
};
fetchData();
}, []);
return (
<div>
<Page headerTitle="证书信息查看">
<div>
<Divider orientation="left">人员信息</Divider>
<Descriptions
bordered
loding={getFileLoading}
items={[
{
label: "姓名",
children: info.name,
},
{
label: "企业名称",
children: info.corpinfoName,
},
{
label: "部门名称",
children: info.departmentName,
},
{
label: "岗位名称",
children: info.userPostName,
},
{
label: "就职状态",
children: <div>{JOB_STATUS[info.employmentStatus]}</div>,
},
]}
column={2}
labelStyle={{
width: 200,
}}
contentStyle={{
width: 500,
}}
/>
<Divider orientation="left">证书信息</Divider>
<Descriptions
bordered
loding={getFileLoading}
items={[
{
label: "证书名称",
children: info.certificateName,
},
{
label: "证书编号",
children: info.certificateCode,
},
{
label: "岗位名称",
children: info.postName,
},
{
label: "发证机构",
children: info.issuingAuthority,
},
{
label: "发证日期",
children: info.dateIssue,
},
{
label: "有效期至",
children: <div>{`${info.certificateDateStart ?? ""} - ${info.certificateDateEnd ?? ""}`}</div>,
},
{
label: "证照照片",
children: <PreviewImg files={info.licenseFile} />,
},
]}
column={2}
labelStyle={{
width: 200,
}}
contentStyle={{
width: 500,
}}
/>
</div>
</Page>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(PersonInChargeInfo);

View File

@ -1,665 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Button, Form, message, Modal, Space } from "antd";
import { useEffect, useRef, useState } from "react";
import FormBuilder from "zy-react-library/components/FormBuilder";
import AddIcon from "zy-react-library/components/Icon/AddIcon";
import Page from "zy-react-library/components/Page";
import Search from "zy-react-library/components/Search";
import DictionarySelect from "zy-react-library/components/Select/Dictionary";
import PersonnelSelect from "zy-react-library/components/Select/Personnel/Gwj";
import Table from "zy-react-library/components/Table";
import TooltipPreviewImg from "zy-react-library/components/TooltipPreviewImg";
import Upload from "zy-react-library/components/Upload";
import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender";
import { UPLOAD_FILE_TYPE_ENUM } from "zy-react-library/enum/uploadFile/gwj";
import useDeleteFile from "zy-react-library/hooks/useDeleteFile";
import useGetFile from "zy-react-library/hooks/useGetFile";
import useGetUrlQuery from "zy-react-library/hooks/useGetUrlQuery";
import useGetUserInfo from "zy-react-library/hooks/useGetUserInfo";
import useTable from "zy-react-library/hooks/useTable";
import useUploadFile from "zy-react-library/hooks/useUploadFile";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
import { useDebounce } from "~/utils";
import { useLocation } from 'react-router-dom';
const JOB_STATUS = {
1: "在职",
0: "离职",
2: "信息变更中",
3: "未入职",
4: "实习生",
5: "实习结束",
6: "退休",
7: "劳务派遣",
8: "劳务派遣结束",
11: "入职待审核",
10: "离职待审核",
};
function List({ props, certificatePhotoType, displayType, personnelType, permissionAdd, permissionEdit, permissionView, permissionDel, dictionaryType }) {
const [addModalOpen, setAddModalOpen] = useState(false);
const [currentId, setCurrentId] = useState("");
const queryParams = useGetUrlQuery();
const { loading: getFileLoading, getFile } = useGetFile();
const [form] = Form.useForm();
const { tableProps, getData } = useTable(props["userCertificateList"], {
form,
transform: (formData) => {
return {
...formData,
eqCorpinfoId: queryParams["corpinfoId"],
eqType: personnelType,
};
},
});
const onDelete = async (row) => {
props["projectHasUser"]({ eqUserId: row.userId }).then((res) => {
console.log(res);
if (res.data && res.data.length > 0) {
const arr = [];
const userArr = [];
res.data.forEach((item) => {
arr.push(item.projectName);
userArr.push(item.userName);
});
Modal.confirm({
title: "提示",
content: (
<div>
<div>
{[...new Set(userArr)].join(",")}
有正在进行的项目包含
{
[...new Set(arr)].join(",")
}
确定删除吗
</div>
<div style={{ fontSize: 14, color: "red" }}>删除可能会对正在进行的项目造成异常状态请谨慎操作</div>
</div>
),
onOk: () => {
props["userCertificateRemove"]({
id: row.id,
}).then((res) => {
if (res.success) {
message.success("删除成功");
getData();
}
});
},
});
}
else {
Modal.confirm({
title: "提示",
content: "确定删除吗?",
onOk: () => {
props["userCertificateRemove"]({
id: row.id,
}).then((res) => {
if (res.success) {
message.success("删除成功");
getData();
}
});
},
});
}
});
};
const [fileCache, setFileCache] = useState({});
const [loadingKeys, setLoadingKeys] = useState(new Set());
const pendingLoadIdsRef = useRef(new Set());
const requestQueueRef = useRef([]);
const activeRequestsRef = useRef(0);
const MAX_CONCURRENT_REQUESTS = 3; // 最大并发请求数
const processQueue = () => {
while (
requestQueueRef.current.length > 0
&& activeRequestsRef.current < MAX_CONCURRENT_REQUESTS
) {
const { id, resolve, reject } = requestQueueRef.current.shift();
activeRequestsRef.current++;
getFile({
eqType: UPLOAD_FILE_TYPE_ENUM[certificatePhotoType],
eqForeignKey: id,
})
.then((res) => {
setFileCache(prev => ({
...prev,
[id]: res || [],
}));
resolve(res);
})
.catch((err) => {
console.error("图片加载失败:", err);
reject(err);
})
.finally(() => {
activeRequestsRef.current--;
setLoadingKeys((prev) => {
const newSet = new Set(prev);
newSet.delete(id);
return newSet;
});
processQueue();
});
}
};
const loadFileForRecord = (userQualificationinfoId) => {
if (!userQualificationinfoId)
return Promise.resolve();
if (fileCache[userQualificationinfoId])
return Promise.resolve();
if (loadingKeys.has(userQualificationinfoId))
return Promise.resolve();
if (pendingLoadIdsRef.current.has(userQualificationinfoId))
return Promise.resolve();
pendingLoadIdsRef.current.add(userQualificationinfoId);
setLoadingKeys(prev => new Set([...prev, userQualificationinfoId]));
return new Promise((resolve, reject) => {
requestQueueRef.current.push({ id: userQualificationinfoId, resolve, reject });
processQueue();
}).finally(() => {
pendingLoadIdsRef.current.delete(userQualificationinfoId);
});
};
// 清除伪类
useEffect(() => {
if (displayType === "View") {
const style = document.createElement("style");
style.innerHTML = `
.search-layout::after {
content: none !important;
display: none !important;
}
`;
document.head.appendChild(style);
// 清理函数,在组件卸载时移除样式
return () => {
document.head.removeChild(style);
};
}
}, []);
// 缓存数据变化时清空图片缓存
useEffect(() => {
if (tableProps.dataSource) {
const currentIds = new Set(tableProps.dataSource.map(item => item.userCertificateId).filter(Boolean));
setFileCache((prev) => {
const newCache = {};
Object.keys(prev).forEach((id) => {
if (currentIds.has(id)) {
newCache[id] = prev[id];
}
});
return newCache;
});
}
}, [tableProps.dataSource]);
// 记录已经触发过加载的 ID避免 render 重复触发
const triggeredLoadIdsRef = useRef(new Set());
useEffect(() => {
if (tableProps.dataSource) {
tableProps.dataSource.forEach((record) => {
const id = record.userCertificateId;
if (id && !triggeredLoadIdsRef.current.has(id)) {
triggeredLoadIdsRef.current.add(id);
loadFileForRecord(id);
}
});
}
// 组件卸载或数据源变化时重置
return () => {
triggeredLoadIdsRef.current.clear();
};
}, [tableProps.dataSource]);
return (
<Page isShowAllAction={false}>
<Search
form={form}
options={[
{
name: "likeUserName",
label: "姓名",
},
{
name: "likePostName",
label: "岗位名称",
},
]}
onFinish={getData}
/>
<Table
loding={getFileLoading}
options={displayType !== "View"}
toolBarRender={() => (
<>
{
(displayType !== "View" && props.permission(permissionAdd))
&& (
<Button
type="primary"
icon={<AddIcon />}
onClick={() => {
setAddModalOpen(true);
}}
>
新增
</Button>
)
}
</>
)}
columns={[
{
title: "姓名",
dataIndex: "name",
},
{
title: "证书名称",
dataIndex: "certificateName",
},
{
title: "证书编号",
dataIndex: "certificateCode",
},
{
title: "岗位名称",
dataIndex: "postName",
},
{
title: "有效期至",
dataIndex: "certificateNo",
width: 280,
render: (_, record) =>
<div>{`${record.certificateDateStart ?? ""} - ${record.certificateDateEnd ?? ""}`}</div>,
},
{
title: "就职状态",
dataIndex: "certificateNo",
render: (_, record) => (
<div>{JOB_STATUS[record.employmentStatus] || "-"}</div>
),
},
{
title: "图片",
dataIndex: "name",
render: (_, record) => {
const id = record.userCertificateId;
const files = fileCache[id] || [];
if (!files.length)
return <span></span>;
return <TooltipPreviewImg files={files} />;
},
},
{
title: "操作",
width: 200,
render: (_, record) => (
<Space>
{
props.permission(permissionView)
&& (
<Button
type="link"
onClick={() => props.history.push(`./View?id=${record.id}`)}
>
查看
</Button>
)
}
{
(displayType !== "View" && props.permission(permissionEdit))
&& (
<Button
type="link"
onClick={() => {
setAddModalOpen(true);
setCurrentId(record.id);
}}
>
编辑
</Button>
)
}
{
(displayType !== "View" && props.permission(permissionDel))
&& (
<Button
danger
type="link"
onClick={() => onDelete(record)}
>
删除
</Button>
)
}
</Space>
),
},
]}
{...tableProps}
/>
{addModalOpen && (
<AddModal
open={addModalOpen}
loding={props.userCertificate.userCertificateLoading}
getData={getData}
currentId={currentId}
requestAdd={props["userCertificateAdd"]}
requestEdit={props["userCertificateEdit"]}
requestDetails={props["userCertificateInfo"]}
userCertificateIsExistCertNo={props["userCertificateIsExistCertNo"]}
getUserlistAll={props["getUserlistAll"]}
certificatePhotoType={certificatePhotoType}
personnelType={personnelType}
dictionaryType={dictionaryType}
onCancel={() => {
setAddModalOpen(false);
setCurrentId("");
}}
onSuccess={(userQualificationinfoId) => {
// 清除该记录的图片缓存,强制下次 render 时重新加载
setFileCache((prev) => {
const newCache = { ...prev };
delete newCache[userQualificationinfoId];
return newCache;
});
}}
/>
)}
</Page>
);
}
function AddModalComponent(props) {
const [form] = Form.useForm();
const [userQualificationinfoId, setUserQualificationinfoId] = useState("");
const [userObj, setUserObj] = useState({});
const { loading: deleteFileLoading, deleteFile } = useDeleteFile();
const { loading: uploadFileLoading, uploadFile } = useUploadFile();
const { loading: getFileLoading, getFile } = useGetFile();
const { getUserInfo } = useGetUserInfo();
const [deleteCardImageFiles, setDeleteCardImageFiles] = useState([]);
const [CertificateCodeValue, setPertificateCodeValue] = useState(null);
const debouncedCertificateCodeValue = useDebounce(CertificateCodeValue, 100);
const [personnelData, setPersonnelData] = useState([]);
const [isSubmit, setIsSubmit] = useState(true);
const location = useLocation();
const getUserInfoFun = async () => {
const userInfo = await getUserInfo();
setUserObj(userInfo);
};
const getUserlistFun = async () => {
props.getUserlistAll({menuPath:"/certificate"+location.pathname}).then(res => {
if(res.data) {
res.data.forEach(item => {
item.bianma=item.id;
})
setPersonnelData(res.data)
}
})
};
useEffect(() => {
if (props.currentId) {
const fetchData = async () => {
const { data } = await props.requestDetails({
id: props.currentId,
});
const certificateImgs = await getFile({
eqType: UPLOAD_FILE_TYPE_ENUM[props.certificatePhotoType],
eqForeignKey: data.userCertificateId,
});
data.certificateDate = [data.certificateDateStart, data.certificateDateEnd];
data.certificateImgs = certificateImgs;
form.setFieldsValue(data);
setUserQualificationinfoId(data.userCertificateId);
};
fetchData();
}
getUserInfoFun();
getUserlistFun()
}, [props.currentId]);
const onCancel = () => {
form.resetFields();
props.onCancel();
};
const onSubmit = async (values) => {
if (!isSubmit) {
form.setFields([
{
name: "certificateCode",
errors: ["证书编号重复"],
},
]);
return;
}
if (values.certificateImgs.length !== 2) {
message.error("证照照片必须上传正反面两张");
return;
}
const maxSizeInBytes = 10 * 1024 * 1024;
const isOverSize = values.certificateImgs.some(file => file.size > maxSizeInBytes);
if (isOverSize) {
message.error("选择的图片不能大于10MB");
return;
}
await deleteFile({
single: false,
files: deleteCardImageFiles,
});
const { id } = await uploadFile({
single: false,
files: values.certificateImgs,
params: {
type: UPLOAD_FILE_TYPE_ENUM[props.certificatePhotoType],
foreignKey: userQualificationinfoId,
},
});
values.certificateDateStart = values.certificateDate[0];
values.certificateDateEnd = values.certificateDate[1];
values.type = props.personnelType;
const personnelTypeMap = {
tezhongzuoye: "特种作业人员",
tzsbczry: "特种设备操作人员",
zyfzr: "主要负责人",
aqscglry: "安全生产管理人员",
};
values.typeName = personnelTypeMap[props.personnelType];
if (props.currentId) {
values.id = props.currentId;
values.userCertificateId = userQualificationinfoId;
await props.requestEdit(values).then((res) => {
if (res.success) {
onCancel();
props.getData();
props.onSuccess(userQualificationinfoId);
message.success("编辑成功");
}
});
}
else {
values.userCertificateId = id;
await props.requestAdd(values).then((res) => {
if (res.success) {
onCancel();
props.getData();
props.onSuccess(userQualificationinfoId);
message.success("新增成功");
}
});
}
};
// 校验重复
useEffect(() => {
if (!debouncedCertificateCodeValue) {
form.setFields([
{
name: "certificateCode",
errors: [],
},
]);
return;
}
props["userCertificateIsExistCertNo"]({
certNo: debouncedCertificateCodeValue,
id: props.currentId ?? "",
}).then((res) => {
if (res.data) {
form.setFields([
{
name: "certificateCode",
errors: ["证书编号重复"],
},
]);
setIsSubmit(false);
}
else {
setIsSubmit(true);
}
});
}, [debouncedCertificateCodeValue]);
const onValuesChange = (changedValues) => {
if ("certificateCode" in changedValues) {
setPertificateCodeValue(changedValues.certificateCode ?? "");
}
};
return (
<Modal
open={props.open}
maskClosable={false}
title={props.currentId ? "编辑" : "新增"}
width={800}
confirmLoading={
deleteFileLoading || uploadFileLoading || getFileLoading || props.loding
}
onOk={form.submit}
onCancel={onCancel}
>
<FormBuilder
form={form}
onValuesChange={onValuesChange}
span={24}
values={{
securityFlag: 0,
}}
options={[
{
name: "userId",
label: "选择人员",
render: FORM_ITEM_RENDER_ENUM.SELECT,
items:personnelData
},
{ name: "userName", label: "人员名称", onlyForLabel: true },
{
name: "certificateName",
label: "证书名称",
},
{
name: "certificateCode",
label: "证书编号",
},
{
name: "postId",
label: "岗位名称",
render: (
<DictionarySelect
dictValue={props.dictionaryType}
onGetLabel={label => form.setFieldValue("postName", label)}
/>
),
},
{ name: "postName", label: "岗位名称", onlyForLabel: true },
{
name: "issuingAuthority",
label: "发证机构",
},
{
name: "dateIssue",
label: "发证日期",
render: FORM_ITEM_RENDER_ENUM.DATE,
},
{
name: "certificateDate",
label: "有效期",
render: FORM_ITEM_RENDER_ENUM.DATE_RANGE,
rules: [
{
validator: (_, value) => {
const allEmptyStrings = Array.isArray(value) && value.every(item => item === "");
if (allEmptyStrings) {
return Promise.reject(new Error("请选择有效期"));
}
return Promise.resolve();
},
},
],
},
{
name: "certificateImgs",
label: "证照照片",
render: (
<Upload
maxCount={2}
onGetRemoveFile={(file) => {
setDeleteCardImageFiles([...deleteCardImageFiles, file]);
}}
tipContent={(
<div
style={{
lineHeight: 1.6,
color: "red",
fontSize: 12,
}}
>
<div>
温馨提示用户要上传证照照片正反面证照照片数量是2张单张大小不超过10MB支持jpgjpegpng格式
</div>
</div>
)}
/>
),
},
]}
labelCol={{
span: 10,
}}
showActionButtons={false}
onFinish={onSubmit}
/>
</Modal>
);
}
const AddModal = AddModalComponent;
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));

View File

@ -1,151 +0,0 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Descriptions, Divider } from "antd";
import { useEffect, useState } from "react";
import Page from "zy-react-library/components/Page";
import PreviewImg from "zy-react-library/components/PreviewImg";
import { UPLOAD_FILE_TYPE_ENUM } from "zy-react-library/enum/uploadFile/gwj";
import useGetFile from "zy-react-library/hooks/useGetFile";
import useGetUrlQuery from "zy-react-library/hooks/useGetUrlQuery";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
const JOB_STATUS = {
1: "在职",
0: "离职",
2: "信息变更中",
3: "未入职",
4: "实习生",
5: "实习结束",
6: "退休",
7: "劳务派遣",
8: "劳务派遣结束",
11: "入职待审核",
10: "离职待审核",
};
function SpecialCertificateInfo({ props, certificatePhotoType }) {
const [info, setInfo] = useState({});
const queryParams = useGetUrlQuery();
const { loading: getFileLoading, getFile } = useGetFile();
useEffect(() => {
console.log(queryParams["personnelType"]);
const fetchData = async () => {
const res = await props.userCertificateInfo({
id: queryParams["id"],
});
const licenseFile = await getFile({
eqType: UPLOAD_FILE_TYPE_ENUM[certificatePhotoType],
eqForeignKey: res.data.userCertificateId,
});
res.data.licenseFile = licenseFile;
setInfo(res.data || {});
};
fetchData();
}, []);
return (
<div>
<Page headerTitle="证书信息查看">
<div>
<Divider orientation="left">人员信息</Divider>
<Descriptions
bordered
loding={getFileLoading}
items={[
{
label: "姓名",
children: info.name,
},
{
label: "企业名称",
children: info.corpinfoName,
},
{
label: "部门名称",
children: info.departmentName,
},
{
label: "岗位名称",
children: info.userPostName,
},
{
label: "就职状态",
children: <div>{JOB_STATUS[info.employmentStatus]}</div>,
},
]}
column={2}
labelStyle={{
width: 200,
}}
contentStyle={{
width: 500,
}}
/>
<Divider orientation="left">证书信息</Divider>
<Descriptions
bordered
loding={getFileLoading}
items={[
{
label: "证书名称",
children: info.certificateName,
},
{
label: "证书编号",
children: info.certificateCode,
},
{
label: "发证机构",
children: info.issuingAuthority,
},
{
label: queryParams["personnelType"] === "tzsbczry" ? "操作项目" : "行业类别",
children: queryParams["personnelType"] === "tzsbczry" ? info.assignmentOperatingItemsName : info.industryCategoryName,
},
{
label: queryParams["personnelType"] === "tzsbczry" ? "作业类别" : "操作项目",
children: queryParams["personnelType"] === "tzsbczry" ? info.assignmentCategoryName : info.industryOperatingItemsName,
},
{
label: "发证日期",
children: info.dateIssue,
},
{
label: "复审日期",
children: info.reviewDate,
},
{
label: "有效期至",
children: <div>{`${info.certificateDateStart ?? ""} - ${info.certificateDateEnd ?? ""}`}</div>,
},
{
label: "证照照片",
children: <PreviewImg files={info.licenseFile} />,
},
]}
column={2}
labelStyle={{
width: 200,
}}
contentStyle={{
width: 500,
}}
/>
</div>
</Page>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(SpecialCertificateInfo);

View File

@ -1,796 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Button, Form, message, Modal, Space } from "antd";
import { useEffect, useRef, useState } from "react";
import { useLocation } from "react-router-dom";
import FormBuilder from "zy-react-library/components/FormBuilder";
import AddIcon from "zy-react-library/components/Icon/AddIcon";
import Page from "zy-react-library/components/Page";
import Search from "zy-react-library/components/Search";
import DictionarySelect from "zy-react-library/components/Select/Dictionary";
import Table from "zy-react-library/components/Table";
import TooltipPreviewImg from "zy-react-library/components/TooltipPreviewImg";
import Upload from "zy-react-library/components/Upload";
import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender";
import { UPLOAD_FILE_TYPE_ENUM } from "zy-react-library/enum/uploadFile/gwj";
import useDeleteFile from "zy-react-library/hooks/useDeleteFile";
import useGetFile from "zy-react-library/hooks/useGetFile";
import useGetUrlQuery from "zy-react-library/hooks/useGetUrlQuery";
import useGetUserInfo from "zy-react-library/hooks/useGetUserInfo";
import useTable from "zy-react-library/hooks/useTable";
import useUploadFile from "zy-react-library/hooks/useUploadFile";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
import { useDebounce } from "~/utils";
const JOB_STATUS = {
1: "在职",
0: "离职",
2: "信息变更中",
3: "未入职",
4: "实习生",
5: "实习结束",
6: "退休",
7: "劳务派遣",
8: "劳务派遣结束",
11: "入职待审核",
10: "离职待审核",
};
function SpecialCertificateList({ props, certificatePhotoType, displayType, personnelType, permissionAdd, permissionEdit, permissionView, permissionDel, dictionaryType }) {
const [addModalOpen, setAddModalOpen] = useState(false);
const [currentId, setCurrentId] = useState("");
const [operatingProjectType, setOperatingProjectType] = useState("");
const queryParams = useGetUrlQuery();
const { loading: getFileLoading, getFile } = useGetFile();
const [form] = Form.useForm();
const { tableProps, getData } = useTable(props["userCertificateList"], {
form,
transform: (formData) => {
if (formData.eqIndustryCategoryCode) {
setOperatingProjectType(formData.eqIndustryCategoryCode);
}
if (formData.eqAssignmentOperatingItemsCode) {
setOperatingProjectType(formData.eqAssignmentOperatingItemsCode);
}
return {
...formData,
eqCorpinfoId: queryParams["corpinfoId"],
eqType: personnelType,
};
},
});
// 清除伪类
useEffect(() => {
if (displayType === "View") {
const style = document.createElement("style");
style.innerHTML = `
.search-layout::after {
content: none !important;
display: none !important;
}
`;
document.head.appendChild(style);
// 清理函数,在组件卸载时移除样式
return () => {
document.head.removeChild(style);
};
}
}, []);
const onDelete = async (row) => {
props["projectHasUser"]({ eqUserId: row.userId }).then((res) => {
if (res.data && res.data.length > 0) {
const arr = [];
const userArr = [];
res.data.forEach((item) => {
arr.push(item.projectName);
userArr.push(item.userName);
});
Modal.confirm({
title: "提示",
content: (
<div>
<div>
{[...new Set(userArr)].join(",")}
有正在进行的项目包含
{
[...new Set(arr)].join(",")
}
确定删除吗
</div>
<div style={{ fontSize: 14, color: "red" }}>删除可能会对正在进行的项目造成异常状态请谨慎操作</div>
</div>
),
onOk: () => {
props["userCertificateRemove"]({
id: row.id,
}).then((res) => {
if (res.success) {
message.success("删除成功");
getData();
}
});
},
});
}
else {
Modal.confirm({
title: "提示",
content: "确定删除吗?",
onOk: () => {
props["userCertificateRemove"]({
id: row.id,
}).then((res) => {
if (res.success) {
message.success("删除成功");
getData();
}
});
},
});
}
});
};
const [fileCache, setFileCache] = useState({});
const [loadingKeys, setLoadingKeys] = useState(new Set());
const pendingLoadIdsRef = useRef(new Set());
const requestQueueRef = useRef([]);
const activeRequestsRef = useRef(0);
const MAX_CONCURRENT_REQUESTS = 3; // 最大并发请求数
const processQueue = () => {
while (
requestQueueRef.current.length > 0
&& activeRequestsRef.current < MAX_CONCURRENT_REQUESTS
) {
const { id, resolve, reject } = requestQueueRef.current.shift();
activeRequestsRef.current++;
getFile({
eqType: UPLOAD_FILE_TYPE_ENUM[certificatePhotoType],
eqForeignKey: id,
})
.then((res) => {
setFileCache(prev => ({
...prev,
[id]: res || [],
}));
resolve(res);
})
.catch((err) => {
console.error("图片加载失败:", err);
reject(err);
})
.finally(() => {
activeRequestsRef.current--;
setLoadingKeys((prev) => {
const newSet = new Set(prev);
newSet.delete(id);
return newSet;
});
processQueue();
});
}
};
const loadFileForRecord = (userQualificationinfoId) => {
if (!userQualificationinfoId)
return Promise.resolve();
if (fileCache[userQualificationinfoId])
return Promise.resolve();
if (loadingKeys.has(userQualificationinfoId))
return Promise.resolve();
if (pendingLoadIdsRef.current.has(userQualificationinfoId))
return Promise.resolve();
pendingLoadIdsRef.current.add(userQualificationinfoId);
setLoadingKeys(prev => new Set([...prev, userQualificationinfoId]));
return new Promise((resolve, reject) => {
requestQueueRef.current.push({ id: userQualificationinfoId, resolve, reject });
processQueue();
}).finally(() => {
pendingLoadIdsRef.current.delete(userQualificationinfoId);
});
};
// 缓存数据变化时清空图片缓存
useEffect(() => {
if (tableProps.dataSource) {
const currentIds = new Set(tableProps.dataSource.map(item => item.userCertificateId).filter(Boolean));
setFileCache((prev) => {
const newCache = {};
Object.keys(prev).forEach((id) => {
if (currentIds.has(id)) {
newCache[id] = prev[id];
}
});
return newCache;
});
}
}, [tableProps.dataSource]);
// 记录已经触发过加载的 ID避免 render 重复触发
const triggeredLoadIdsRef = useRef(new Set());
useEffect(() => {
if (tableProps.dataSource) {
tableProps.dataSource.forEach((record) => {
const id = record.userCertificateId;
if (id && !triggeredLoadIdsRef.current.has(id)) {
triggeredLoadIdsRef.current.add(id);
loadFileForRecord(id);
}
});
}
// 组件卸载或数据源变化时重置
return () => {
triggeredLoadIdsRef.current.clear();
};
}, [tableProps.dataSource]);
const onValuesChange = (changedValues) => {
if ("eqIndustryCategoryCode" in changedValues) {
const newIdCard = changedValues.eqIndustryCategoryCode;
if (newIdCard) {
setOperatingProjectType(newIdCard);
form.setFieldsValue({
eqIndustryOperatingItemsCode: undefined,
});
}
}
if ("eqAssignmentOperatingItemsCode" in changedValues) {
const newIdCard = changedValues.eqAssignmentOperatingItemsCode;
if (newIdCard) {
setOperatingProjectType(newIdCard);
form.setFieldsValue({
eqAssignmentCategoryCode: undefined,
});
}
}
};
return (
<Page isShowAllAction={false}>
<Search
onValuesChange={onValuesChange}
form={form}
options={[
{
name: "likeUserName",
label: "姓名",
},
{
name: personnelType === "tzsbczry" ? "eqAssignmentOperatingItemsCode" : "eqIndustryCategoryCode",
label: personnelType === "tzsbczry" ? "操作项目" : "行业类别",
render: (
<DictionarySelect
dictValue={dictionaryType}
/>
),
},
{
name: personnelType === "tzsbczry" ? "eqAssignmentCategoryCode" : "eqIndustryOperatingItemsCode",
label: personnelType === "tzsbczry" ? "作业类别" : "操作项目",
render: (
<DictionarySelect
dictValue={operatingProjectType}
/>
),
},
]}
onFinish={getData}
/>
<Table
loding={getFileLoading}
options={displayType !== "View"}
toolBarRender={() => (
<>
{
(displayType !== "View" && props.permission(permissionAdd))
&& (
<Button
type="primary"
icon={<AddIcon />}
onClick={() => {
setAddModalOpen(true);
}}
>
新增
</Button>
)
}
</>
)}
columns={[
{
title: "姓名",
dataIndex: "name",
},
{
title: "证书名称",
dataIndex: "certificateName",
},
{
title: "证书编号",
dataIndex: "certificateCode",
},
{
title: personnelType === "tzsbczry" ? "操作项目" : "行业类别",
dataIndex: personnelType === "tzsbczry" ? "assignmentOperatingItemsName" : "industryCategoryName",
},
{
title: personnelType === "tzsbczry" ? "作业类别" : "操作项目",
dataIndex: personnelType === "tzsbczry" ? "assignmentCategoryName" : "industryOperatingItemsName",
},
{
title: "有效期至",
dataIndex: "certificateDate",
width: 280,
render: (_, record) =>
<div>{`${record.certificateDateStart ?? ""} - ${record.certificateDateEnd ?? ""}`}</div>,
},
{
title: "就职状态",
dataIndex: "employmentStatus",
render: (_, record) => (
<div>{JOB_STATUS[record.employmentStatus] || "-"}</div>
),
},
{
title: "图片",
dataIndex: "name",
render: (_, record) => {
const id = record.userCertificateId;
const files = fileCache[id] || [];
if (!files.length)
return <span></span>;
return <TooltipPreviewImg files={files} />;
},
},
{
title: "操作",
width: 200,
render: (_, record) => (
<Space>
{
props.permission(permissionView)
&& (
<Button
type="link"
onClick={() => props.history.push(`./View?id=${record.id}&personnelType=${personnelType}`)}
>
查看
</Button>
)
}
{
(displayType !== "View" && props.permission(permissionEdit))
&& (
<Button
type="link"
onClick={() => {
setAddModalOpen(true);
setCurrentId(record.id);
}}
>
编辑
</Button>
)
}
{
displayType !== "View" && props.permission(permissionDel)
&& (
<Button
danger
type="link"
onClick={() => onDelete(record)}
>
删除
</Button>
)
}
</Space>
),
},
]}
{...tableProps}
/>
{addModalOpen && (
<AddModal
open={addModalOpen}
loding={props.userCertificate.userCertificateLoading}
getData={getData}
currentId={currentId}
requestAdd={props["userCertificateAdd"]}
requestEdit={props["userCertificateEdit"]}
requestDetails={props["userCertificateInfo"]}
userCertificateIsExistCertNo={props["userCertificateIsExistCertNo"]}
certificatePhotoType={certificatePhotoType}
getUserlistAll={props["getUserlistAll"]}
personnelType={personnelType}
dictionaryType={dictionaryType}
onCancel={() => {
setAddModalOpen(false);
setCurrentId("");
}}
onSuccess={(userQualificationinfoId) => {
// 清除该记录的图片缓存,强制下次 render 时重新加载
setFileCache((prev) => {
const newCache = { ...prev };
delete newCache[userQualificationinfoId];
return newCache;
});
}}
/>
)}
</Page>
);
}
function AddModalComponent(props) {
const [form] = Form.useForm();
const [userQualificationinfoId, setUserQualificationinfoId] = useState("");
const { loading: deleteFileLoading, deleteFile } = useDeleteFile();
const { loading: uploadFileLoading, uploadFile } = useUploadFile();
const { loading: getFileLoading, getFile } = useGetFile();
const [operatingProjectType, setOperatingProjectType] = useState("");
const [deleteCardImageFiles, setDeleteCardImageFiles] = useState([]);
const [CertificateCodeValue, setPertificateCodeValue] = useState(null);
const [userObj, setUserObj] = useState({});
const debouncedCertificateCodeValue = useDebounce(CertificateCodeValue, 100);
const [isSubmit, setIsSubmit] = useState(true);
const [personnelData, setPersonnelData] = useState([]);
const { getUserInfo } = useGetUserInfo();
const location = useLocation();
const getUserInfoFun = async () => {
const userInfo = await getUserInfo();
setUserObj(userInfo);
};
// 校验重复
useEffect(() => {
if (!debouncedCertificateCodeValue) {
form.setFields([
{
name: "certificateCode",
errors: [],
},
]);
return;
}
props["userCertificateIsExistCertNo"]({
certNo: debouncedCertificateCodeValue,
id: props.currentId ?? "",
}).then((res) => {
if (res.data) {
form.setFields([
{
name: "certificateCode",
errors: ["证书编号重复"],
},
]);
setIsSubmit(false);
}
else {
setIsSubmit(true);
}
});
}, [debouncedCertificateCodeValue]);
const getUserlistFun = async () => {
props.getUserlistAll({ menuPath: `/certificate${location.pathname}` }).then((res) => {
if (res.data) {
console.log(res.data);
res.data.forEach((item) => {
item.bianma = item.id;
});
setPersonnelData(res.data);
}
});
};
useEffect(() => {
if (props.currentId) {
const fetchData = async () => {
const { data } = await props.requestDetails({
id: props.currentId,
});
setOperatingProjectType(data.industryCategoryCode ? data.industryCategoryCode : data.assignmentOperatingItemsCode);
data.certificateDate = [data.certificateDateStart, data.certificateDateEnd];
const certificateImgs = await getFile({
eqType: UPLOAD_FILE_TYPE_ENUM[props.certificatePhotoType],
eqForeignKey: data.userCertificateId,
});
data.certificateImgs = certificateImgs;
form.setFieldsValue(data);
setUserQualificationinfoId(data.userCertificateId);
};
fetchData();
}
getUserInfoFun();
getUserlistFun();
}, [props.currentId]);
const onCancel = () => {
form.resetFields();
props.onCancel();
};
const onSubmit = async (values) => {
if (!isSubmit) {
form.setFields([
{
name: "certificateCode",
errors: ["证书编号重复"],
},
]);
return;
}
if (values.certificateImgs.length !== 2) {
message.error("证照照片必须上传正反面两张");
return;
}
const maxSizeInBytes = 10 * 1024 * 1024;
const isOverSize = values.certificateImgs.some(file => file.size > maxSizeInBytes);
if (isOverSize) {
message.error("选择的图片不能大于10MB");
return;
}
await deleteFile({
single: false,
files: deleteCardImageFiles,
});
const { id } = await uploadFile({
single: false,
files: values.certificateImgs,
params: {
type: UPLOAD_FILE_TYPE_ENUM[props.certificatePhotoType],
foreignKey: userQualificationinfoId,
},
});
values.type = props.personnelType;
const personnelTypeMap = {
tezhongzuoye: "特种作业人员",
tzsbczry: "特种设备操作人员",
zyfzr: "主要负责人",
aqscglry: "安全生产管理人员",
};
values.typeName = personnelTypeMap[props.personnelType];
values.certificateDateStart = values.certificateDate[0];
values.certificateDateEnd = values.certificateDate[1];
if (props.currentId) {
values.id = props.currentId;
values.userCertificateId = userQualificationinfoId;
await props.requestEdit(values).then((res) => {
if (res.success) {
onCancel();
props.getData();
props.onSuccess(userQualificationinfoId);
message.success("编辑成功");
}
});
}
else {
values.userCertificateId = id;
await props.requestAdd(values).then((res) => {
if (res.success) {
onCancel();
props.getData();
props.onSuccess(userQualificationinfoId);
message.success("新增成功");
}
});
}
};
const onValuesChange = (changedValues) => {
if ("industryCategoryCode" in changedValues) {
const industryCategoryCode = changedValues.industryCategoryCode;
if (industryCategoryCode) {
setOperatingProjectType(industryCategoryCode);
form.setFieldsValue({
industryOperatingItemsCode: undefined,
});
}
}
if ("assignmentOperatingItemsCode" in changedValues) {
const assignmentOperatingItemsCode = changedValues.assignmentOperatingItemsCode;
if (assignmentOperatingItemsCode) {
setOperatingProjectType(assignmentOperatingItemsCode);
form.setFieldsValue({
assignmentCategoryCode: undefined,
});
}
}
if ("certificateCode" in changedValues) {
setPertificateCodeValue(changedValues.certificateCode ?? "");
}
};
return (
<Modal
open={props.open}
maskClosable={false}
title={props.currentId ? "编辑" : "新增"}
width={800}
confirmLoading={
deleteFileLoading || uploadFileLoading || getFileLoading || props.loding
}
onOk={form.submit}
onCancel={onCancel}
>
<FormBuilder
form={form}
onValuesChange={onValuesChange}
span={24}
values={{
securityFlag: 0,
}}
options={[
{
name: "userId",
label: "选择人员",
render: FORM_ITEM_RENDER_ENUM.SELECT,
items: personnelData,
},
{
name: "certificateName",
label: "证书名称",
},
{
name: "certificateCode",
label: "证书编号",
},
{
name: "issuingAuthority",
label: "发证机构",
},
{
name: "industryCategoryCode",
label: "行业类别",
hidden: !(props.personnelType === "tezhongzuoye"),
render: (
<DictionarySelect
dictValue={props.dictionaryType}
onGetLabel={label => form.setFieldValue("industryCategoryName", label)}
/>
),
},
{ name: "industryCategoryName", label: "行业类别名称", onlyForLabel: true },
{
name: "industryOperatingItemsCode",
label: "操作项目",
hidden: !(props.personnelType === "tezhongzuoye"),
render: (
<DictionarySelect
dictValue={operatingProjectType}
onGetLabel={label => form.setFieldValue("industryOperatingItemsName", label)}
/>
),
},
{ name: "industryOperatingItemsName", label: "操作项目名称", onlyForLabel: true },
{
name: "assignmentOperatingItemsCode",
label: "操作项目",
hidden: !(props.personnelType === "tzsbczry"),
render: (
<DictionarySelect
dictValue={props.dictionaryType}
onGetLabel={label => form.setFieldValue("assignmentOperatingItemsName", label)}
/>
),
},
{ name: "assignmentOperatingItemsName", label: "操作项目名称", onlyForLabel: true },
{
name: "assignmentCategoryCode",
label: "作业类别",
hidden: !(props.personnelType === "tzsbczry"),
render: (
<DictionarySelect
dictValue={operatingProjectType}
onGetLabel={label => form.setFieldValue("assignmentCategoryName", label)}
/>
),
},
{ name: "assignmentCategoryName", label: "作业类别名称", onlyForLabel: true },
{
name: "dateIssue",
label: "发证日期",
render: FORM_ITEM_RENDER_ENUM.DATE,
},
{
name: "certificateDate",
label: "有效期",
render: FORM_ITEM_RENDER_ENUM.DATE_RANGE,
rules: [
{
validator: (_, value) => {
const allEmptyStrings = Array.isArray(value) && value.every(item => item === "");
if (allEmptyStrings) {
return Promise.reject(new Error("请选择有效期"));
}
return Promise.resolve();
},
},
],
},
{
name: "reviewDate",
label: "复审日期",
render: FORM_ITEM_RENDER_ENUM.DATE,
},
{
name: "certificateImgs",
label: "证照照片",
render: (
<Upload
maxCount={2}
onGetRemoveFile={(file) => {
setDeleteCardImageFiles([...deleteCardImageFiles, file]);
}}
tipContent={(
<div
style={{
lineHeight: 1.6,
color: "red",
fontSize: 12,
}}
>
<div>
温馨提示用户要上传证照照片正反面证照照片数量是2张单张大小不超过10MB支持jpgjpegpng格式
</div>
</div>
)}
/>
),
},
]}
labelCol={{
span: 10,
}}
showActionButtons={false}
onFinish={onSubmit}
/>
</Modal>
);
}
const AddModal = AddModalComponent;
export default Connect([NS_USER_CERTIFICATE], true)(Permission(SpecialCertificateList));

View File

@ -1 +0,0 @@
export {};

View File

@ -1,5 +0,0 @@
/**
* 全局常量定义
*/
export {};

View File

@ -1,8 +0,0 @@
/**
* 全局上下文定义
*/
import React from "react";
// 获取antd全局静态方法
export const InjectContext = React.createContext({});

View File

@ -1,10 +0,0 @@
/**
* 全局数据状态管理模块定义
*/
import { defineNamespace } from "@cqsjjb/jjb-dva-runtime";
export const NS_GLOBAL = defineNamespace("global");
export const NS_PERSNONEL_CERTFICATE = defineNamespace("personnelCertificate");
export const NS_CORP_CERTIFICATE = defineNamespace("corpCertificate");
export const NS_USER_CERTIFICATE = defineNamespace("userCertificate");
export const NS_COURSEWARE = defineNamespace("courseware");

View File

@ -1,49 +0,0 @@
import { setJJBCommonAntdMessage } from "@cqsjjb/jjb-common-lib";
import { setup } from "@cqsjjb/jjb-dva-runtime";
import { message } from "antd";
import dayjs from "dayjs";
import { getFileUrlFromServer } from "zy-react-library/utils";
import "dayjs/locale/zh-cn";
import "../blessed_by_buddha";
require("antd/dist/reset.css");
require("zy-react-library/css/common.less");
dayjs.locale("zh-cn");
setJJBCommonAntdMessage(message);
const app = setup();
getFileUrlFromServer();
// 非底座环境运行
if (!window.__POWERED_BY_QIANKUN__) {
// 云组件默认依赖
window.__coreLib = {};
window.__coreLib.React = require("react");
window.__coreLib.ReactDOM = require("react-dom");
window.__coreLib.jjbCommonLib = require("@cqsjjb/jjb-common-lib");
}
/**
* @description 挂载
* @param props {{ setGlobalState: ({ rendered: boolean }) => void }}
* @returns {Promise<*>} ''
*/
export const mount = async (props) => {
// 云组件默认依赖
window.__coreLib.React = require("react");
window.__coreLib.ReactDOM = require("react-dom");
window.__coreLib.jjbCommonLib = require("@cqsjjb/jjb-common-lib");
app.mount(props);
};
/**
* @description 卸载
* @param props {object}
* @returns {Promise<*>} ''
*/
export const unmount = async props => app.unmount(props);
/**
* @description 启动
* @param props
*/
export const bootstrap = async props => app.bootstrap(props);

View File

@ -1,17 +0,0 @@
import EnterpriseLicenseList from "~/pages/Container/Supervision/EnterpriseLicense/EnterpriseLicense";
function EnterpriseLicense(props) {
return (
<div>
<EnterpriseLicenseList
props={props}
permissionAdd="qyd-qyzzgl-add"
permissionEdit="qyd-qyzzgl-edit"
permissionView="qyd-qyzzgl-info"
permissionDel="qyd-qyzzgl-del"
/>
</div>
);
}
export default EnterpriseLicense;

View File

@ -1,23 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import PersonInChargeList from "~/components/PersonInChargeList";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
// 企业主要负责人管理
function List(props) {
return (
<div>
<PersonInChargeList
props={props}
certificatePhotoType={161}
personnelType="zyfzr"
permissionAdd="qyd-zyfzrgl-add"
permissionEdit="qyd-zyfzrgl-edit"
permissionView="qyd-zyfzrgl-info"
permissionDel="qyd-zyfzrgl-del"
dictionaryType="zyfzrgwmc0000"
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));

View File

@ -1,16 +0,0 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import PersonInChargeInfo from "~/components/PersonInChargeInfo";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function View(props) {
return (
<div>
<PersonInChargeInfo
props={props}
certificatePhotoType={161}
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(View);

View File

@ -1,9 +0,0 @@
function PersonInCharge(props) {
return (
<div>
{props.children}
</div>
);
}
export default PersonInCharge;

View File

@ -1,23 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import PersonInChargeList from "~/components/PersonInChargeList";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
// 企业安全生产管理人员管理
function List(props) {
return (
<div>
<PersonInChargeList
props={props}
certificatePhotoType={162}
personnelType="aqscglry"
permissionAdd="qyd-aqscglrygl-add"
permissionEdit="qyd-aqscglrygl-edit"
permissionView="qyd-aqscglrygl-info"
permissionDel="qyd-aqscglrygl-del"
dictionaryType="aqscglrygwmc0000"
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));

View File

@ -1,16 +0,0 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import PersonInChargeInfo from "~/components/PersonInChargeInfo";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function View(props) {
return (
<div>
<PersonInChargeInfo
props={props}
certificatePhotoType={162}
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(View);

View File

@ -1,9 +0,0 @@
function SecurityAdmini(props) {
return (
<div>
{props.children}
</div>
);
}
export default SecurityAdmini;

View File

@ -1,23 +0,0 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import SpecialCertificateList from "~/components/SpecialCertificateList";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
import {Permission} from "@cqsjjb/jjb-common-decorator/permission";
// 企业特种设备操作人员管理
function List(props) {
return (
<div>
<SpecialCertificateList
props={props}
certificatePhotoType={160}
personnelType="tzsbczry"
permissionAdd="qyd-tzsbczrygl-add"
permissionEdit="qyd-tzsbczrygl-edit"
permissionView="qyd-tzsbczrygl-info"
permissionDel="qyd-tzsbczrygl-del"
dictionaryType="tzsbczryczxmzylb0000"
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));

View File

@ -1,16 +0,0 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import SpecialCertificateInfo from "~/components/SpecialCertificateInfo";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function View(props) {
return (
<div>
<SpecialCertificateInfo
props={props}
certificatePhotoType={160}
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(View);

View File

@ -1,9 +0,0 @@
function SpecialEquipment(props) {
return (
<div>
{props.children}
</div>
);
}
export default SpecialEquipment;

View File

@ -1,23 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import SpecialCertificateList from "~/components/SpecialCertificateList";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
// 企业特种作业人员管理
function List(props) {
return (
<div>
<SpecialCertificateList
props={props}
certificatePhotoType={159}
personnelType="tezhongzuoye"
permissionAdd="qyd-tzzyrugl-add"
permissionEdit="qyd-tzzyrugl-edit"
permissionView="qyd-tzzyrugl-info"
permissionDel="qyd-tzzyrugl-del"
dictionaryType="tzzyryhylb0000"
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));

View File

@ -1,16 +0,0 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import SpecialCertificateInfo from "~/components/SpecialCertificateInfo";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function View(props) {
return (
<div>
<SpecialCertificateInfo
props={props}
certificatePhotoType={159}
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(View);

View File

@ -1,9 +0,0 @@
function SpecialPersonnel(props) {
return (
<div>
{props.children}
</div>
);
}
export default SpecialPersonnel;

View File

@ -1,9 +0,0 @@
function PersonnelLicense(props) {
return (
<div>
{props.children}
</div>
);
}
export default PersonnelLicense;

View File

@ -1,9 +0,0 @@
function EnterpriseLicenseManage(props) {
return (
<div>
{props.children}
</div>
);
}
export default EnterpriseLicenseManage;

View File

@ -1,9 +0,0 @@
function BranchCompany(props) {
return (
<div>
{props.children}
</div>
);
}
export default BranchCompany;

View File

@ -1,31 +0,0 @@
import { ImportCore } from "@cqsjjb/jjb-common-decorator/module";
import React from "react";
export default class Entry extends React.Component {
state = {
Component: undefined,
};
componentDidMount() {
if (process.env.app.appKey) {
ImportCore({
name: "$",
from: "https://cdn.cqjjb.cn/jcloud/use/plugin/b31c9840a57f11ef91cf7f3cabbb7484/latest",
}).then((res) => {
if (res.status) {
this.setState({ Component: res.module?.default });
}
});
}
}
render() {
const { Component } = this.state;
return (Component && process.env.app.appKey) && (
<Component
detail={{ componentKey: process.env.app.appKey }}
appKey={process.env.app.appKey}
/>
);
}
}

View File

@ -1,434 +0,0 @@
import React, {
useState,
useCallback,
useMemo,
useEffect,
} from "react";
import { Layout, Menu, Breadcrumb, Tabs, Button, Dropdown, Space, Typography } from "antd";
import {
ReloadOutlined,
CloseOutlined,
CloseCircleOutlined,
DownOutlined,
MenuFoldOutlined,
MenuUnfoldOutlined,
} from "@ant-design/icons";
import menuItems, {
findMenuPath,
getPageLabel,
getOpenKeys,
getSelectedKeys,
} from "./menuConfig";
const { Header, Sider, Content } = Layout;
const { Text } = Typography;
const TABS_KEY = "sim_layout_tabs";
const ACTIVE_KEY = "sim_layout_active";
/* ---- sessionStorage 持久化 ---- */
function loadState() {
try {
const tabs = JSON.parse(sessionStorage.getItem(TABS_KEY) || "[]");
const activeKey = sessionStorage.getItem(ACTIVE_KEY) || "";
return { tabs, activeKey };
} catch {
return { tabs: [], activeKey: "" };
}
}
function saveState(tabs, activeKey) {
sessionStorage.setItem(TABS_KEY, JSON.stringify(tabs));
sessionStorage.setItem(ACTIVE_KEY, activeKey || "");
}
export default function SimulatedLayout({ children }) {
const [collapsed, setCollapsed] = useState(false);
const [state, setState] = useState(loadState);
const currentPath = window.location.pathname;
//
useEffect(() => {
if (currentPath && !currentPath.endsWith("/container/")) {
addTab(currentPath);
}
}, []); //
/* ---- 标签操作 ---- */
const addTab = useCallback((path) => {
setState((prev) => {
if (prev.tabs.find((t) => t.key === path)) {
// activeKey
if (prev.activeKey !== path) {
saveState(prev.tabs, path);
return { ...prev, activeKey: path };
}
return prev;
}
const newTabs = [...prev.tabs, { key: path, label: getPageLabel(path) }];
saveState(newTabs, path);
return { tabs: newTabs, activeKey: path };
});
}, []);
const navigateTo = useCallback((path) => {
if (path !== currentPath) {
window.location.href = path;
}
}, [currentPath]);
const handleCloseTab = useCallback((targetKey) => {
setState((prev) => {
const idx = prev.tabs.findIndex((t) => t.key === targetKey);
const newTabs = prev.tabs.filter((t) => t.key !== targetKey);
if (newTabs.length === 0) {
saveState([], "");
return { tabs: [], activeKey: "" };
}
let newActive = prev.activeKey;
if (targetKey === prev.activeKey) {
newActive = newTabs[Math.max(0, idx - 1)]?.key || newTabs[0]?.key;
navigateTo(newActive);
}
saveState(newTabs, newActive);
return { tabs: newTabs, activeKey: newActive };
});
}, [navigateTo]);
const handleCloseOthers = useCallback((targetKey) => {
setState((prev) => {
const target = prev.tabs.find((t) => t.key === targetKey);
const newTabs = target ? [target] : [];
saveState(newTabs, targetKey);
if (targetKey !== currentPath) navigateTo(targetKey);
return { tabs: newTabs, activeKey: targetKey };
});
}, [currentPath, navigateTo]);
const handleCloseRight = useCallback((targetKey) => {
setState((prev) => {
const idx = prev.tabs.findIndex((t) => t.key === targetKey);
if (idx === -1) return prev;
const newTabs = prev.tabs.slice(0, idx + 1);
saveState(newTabs, targetKey);
return { tabs: newTabs, activeKey: targetKey };
});
}, []);
const handleCloseAll = useCallback(() => {
saveState([], "");
setState({ tabs: [], activeKey: "" });
}, []);
const handleRefresh = useCallback(() => {
window.location.reload();
}, []);
/* ---- 菜单事件 ---- */
const handleMenuClick = useCallback(
({ key }) => {
navigateTo(key);
},
[navigateTo],
);
const handleTabChange = useCallback(
(key) => {
navigateTo(key);
},
[navigateTo],
);
const handleTabEdit = useCallback(
(targetKey, action) => {
if (action === "remove") handleCloseTab(targetKey);
},
[handleCloseTab],
);
/* ---- 面包屑 ---- */
const breadcrumbItems = useMemo(() => {
const path = findMenuPath(currentPath);
return path.map((item, idx) => ({
title:
idx === path.length - 1 ? (
item.label
) : (
<a onClick={() => handleMenuClick({ key: item.key })}>{item.label}</a>
),
key: item.key,
}));
}, [currentPath, handleMenuClick]);
/* ---- 菜单选中/展开 ---- */
const selectedKeys = useMemo(() => getSelectedKeys(currentPath), [currentPath]);
const defaultOpenKeys = useMemo(() => getOpenKeys(currentPath), [currentPath]);
/* ---- 标签页右键菜单 ---- */
const buildContextMenu = useCallback(
(tabKey) => {
const { tabs } = state;
const isOnlyOne = tabs.length <= 1;
const idx = tabs.findIndex((t) => t.key === tabKey);
const hasRight = idx >= 0 && idx < tabs.length - 1;
return {
items: [
{
key: "refresh",
icon: <ReloadOutlined />,
label: "刷新当前标签",
},
{
key: "close",
icon: <CloseOutlined />,
label: "关闭当前标签",
disabled: isOnlyOne,
},
{
key: "close-others",
icon: <CloseCircleOutlined />,
label: "关闭其他标签",
disabled: isOnlyOne,
},
{
key: "close-right",
icon: <CloseOutlined />,
label: "关闭右侧标签",
disabled: !hasRight,
},
{ type: "divider" },
{
key: "close-all",
icon: <CloseCircleOutlined />,
label: "关闭所有标签",
disabled: tabs.length === 0,
},
],
onClick: ({ key }) => {
switch (key) {
case "refresh":
handleRefresh();
break;
case "close":
handleCloseTab(tabKey);
break;
case "close-others":
handleCloseOthers(tabKey);
break;
case "close-right":
handleCloseRight(tabKey);
break;
case "close-all":
handleCloseAll();
break;
}
},
};
},
[state, handleRefresh, handleCloseTab, handleCloseOthers, handleCloseRight, handleCloseAll],
);
/* ---- 标签 label 渲染(带右键菜单) ---- */
const renderTabLabel = useCallback(
(tab) => (
<Dropdown menu={buildContextMenu(tab.key)} trigger={["contextMenu"]}>
<span style={{ display: "inline-block", padding: "0 8px", userSelect: "none" }}>
{tab.label}
</span>
</Dropdown>
),
[buildContextMenu],
);
const { tabs, activeKey } = state;
const siderWidth = collapsed ? 64 : 220;
return (
<Layout style={{ minHeight: "100vh" }}>
{/* ---- 侧边栏 ---- */}
<Sider
collapsible
collapsed={collapsed}
onCollapse={setCollapsed}
theme="dark"
width={220}
style={{
overflow: "auto",
height: "100vh",
position: "fixed",
left: 0,
top: 0,
bottom: 0,
zIndex: 10,
}}
>
<div
style={{
height: 48,
display: "flex",
alignItems: "center",
justifyContent: "center",
color: "#fff",
fontWeight: 700,
fontSize: collapsed ? 15 : 17,
borderBottom: "1px solid rgba(255,255,255,0.1)",
letterSpacing: 1,
flexShrink: 0,
}}
>
{collapsed ? "证照" : "证照管理系统"}
</div>
<Menu
theme="dark"
mode="inline"
selectedKeys={selectedKeys}
defaultOpenKeys={defaultOpenKeys}
onClick={handleMenuClick}
items={menuItems}
style={{ borderRight: 0 }}
/>
</Sider>
{/* ---- 右侧主体 ---- */}
<Layout
style={{
marginLeft: siderWidth,
transition: "margin-left 0.2s",
}}
>
{/* ---- Header + 面包屑 + 操作按钮 ---- */}
<Header
style={{
background: "#fff",
padding: "0 16px",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
borderBottom: "1px solid #f0f0f0",
height: 48,
}}
>
<Space>
<Button
type="text"
icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
onClick={() => setCollapsed(!collapsed)}
/>
<Breadcrumb items={breadcrumbItems} />
</Space>
<Space>
<Button
type="text"
icon={<ReloadOutlined />}
onClick={handleRefresh}
>
刷新
</Button>
<Button
type="text"
icon={<CloseCircleOutlined />}
onClick={handleCloseAll}
disabled={tabs.length === 0}
>
关闭所有
</Button>
<Text type="secondary" style={{ fontSize: 12 }}>
模拟布局·Dev
</Text>
</Space>
</Header>
{/* ---- 标签栏 ---- */}
<div
style={{
background: "#fff",
borderBottom: "1px solid #f0f0f0",
padding: "4px 8px 0",
}}
>
{tabs.length > 0 ? (
<Tabs
type="editable-card"
hideAdd
activeKey={activeKey}
onChange={handleTabChange}
onEdit={handleTabEdit}
size="small"
style={{ marginBottom: 0 }}
items={tabs.map((tab) => ({
key: tab.key,
label: renderTabLabel(tab),
closable: true,
}))}
tabBarExtraContent={
<Dropdown
menu={{
items: [
{
key: "refresh",
icon: <ReloadOutlined />,
label: "刷新当前标签",
},
{
key: "close-others",
icon: <CloseOutlined />,
label: "关闭其他标签",
disabled: tabs.length <= 1,
},
{
key: "close-all",
icon: <CloseCircleOutlined />,
label: "关闭所有标签",
disabled: tabs.length === 0,
},
],
onClick: ({ key }) => {
switch (key) {
case "refresh":
handleRefresh();
break;
case "close-others":
handleCloseOthers(activeKey);
break;
case "close-all":
handleCloseAll();
break;
}
},
}}
placement="bottomRight"
>
<Button type="text" size="small" icon={<DownOutlined />} />
</Dropdown>
}
/>
) : (
<div
style={{
height: 36,
display: "flex",
alignItems: "center",
paddingLeft: 8,
color: "#bbb",
fontSize: 12,
}}
>
暂无打开的标签页请从左侧菜单选择页面
</div>
)}
</div>
{/* ---- 内容区 ---- */}
<Content
style={{
margin: 0,
minHeight: 280,
position: "relative",
}}
>
{children}
</Content>
</Layout>
</Layout>
);
}

View File

@ -1,239 +0,0 @@
import React from "react";
import {
DashboardOutlined,
FileProtectOutlined,
TeamOutlined,
BankOutlined,
UserSwitchOutlined,
IdcardOutlined,
SafetyCertificateOutlined,
ToolOutlined,
UserOutlined,
BarChartOutlined,
PieChartOutlined,
ExperimentOutlined,
} from "@ant-design/icons";
const menuItems = [
{
key: "/certificate/container/Supervision",
label: "监管端",
icon: <DashboardOutlined />,
children: [
{
key: "/certificate/container/Supervision/test2",
label: "Test2 测试",
icon: <ExperimentOutlined />,
},
{
key: "/certificate/container/Supervision/EnterpriseLicense",
label: "企业证照",
icon: <FileProtectOutlined />,
children: [
{
key: "/certificate/container/Supervision/EnterpriseLicense/EnterpriseLicense",
label: "企业证照管理",
icon: <IdcardOutlined />,
},
{
key: "/certificate/container/Supervision/EnterpriseLicense/BranchStatistics/List",
label: "分公司统计",
icon: <BarChartOutlined />,
},
{
key: "/certificate/container/Supervision/EnterpriseLicense/StakeholderStatistics/List",
label: "干系人统计",
icon: <PieChartOutlined />,
},
],
},
{
key: "/certificate/container/Supervision/PersonnelLicense",
label: "人员证照",
icon: <TeamOutlined />,
children: [
{
key: "/certificate/container/Supervision/PersonnelLicense/PersonInCharge/List",
label: "负责人",
icon: <UserOutlined />,
},
{
key: "/certificate/container/Supervision/PersonnelLicense/SecurityAdmini/List",
label: "安全管理员",
icon: <SafetyCertificateOutlined />,
},
{
key: "/certificate/container/Supervision/PersonnelLicense/SpecialDevice/List",
label: "特种设备",
icon: <ToolOutlined />,
},
{
key: "/certificate/container/Supervision/PersonnelLicense/SpecialPersonnel/List",
label: "特种作业人员",
icon: <IdcardOutlined />,
},
{
key: "/certificate/container/Supervision/PersonnelLicense/BranchCompanyStat/List",
label: "分公司人员统计",
icon: <BarChartOutlined />,
},
{
key: "/certificate/container/Supervision/PersonnelLicense/StakeholderStat/List",
label: "干系人人员统计",
icon: <PieChartOutlined />,
},
],
},
],
},
{
key: "/certificate/container/BranchCompany",
label: "分公司端",
icon: <BankOutlined />,
children: [
{
key: "/certificate/container/BranchCompany/EnterpriseLicense/EnterpriseLicense",
label: "企业证照管理",
icon: <IdcardOutlined />,
},
{
key: "/certificate/container/BranchCompany/EnterpriseLicense/PersonnelLicense",
label: "人员证照",
icon: <TeamOutlined />,
children: [
{
key: "/certificate/container/BranchCompany/EnterpriseLicense/PersonnelLicense/PersonInCharge/List",
label: "负责人",
icon: <UserOutlined />,
},
{
key: "/certificate/container/BranchCompany/EnterpriseLicense/PersonnelLicense/SecurityAdmini/List",
label: "安全管理员",
icon: <SafetyCertificateOutlined />,
},
{
key: "/certificate/container/BranchCompany/EnterpriseLicense/PersonnelLicense/SpecialDevice/List",
label: "特种设备",
icon: <ToolOutlined />,
},
{
key: "/certificate/container/BranchCompany/EnterpriseLicense/PersonnelLicense/SpecialPersonnel/List",
label: "特种作业人员",
icon: <IdcardOutlined />,
},
],
},
],
},
{
key: "/certificate/container/Stakeholder",
label: "干系人端",
icon: <UserSwitchOutlined />,
children: [
{
key: "/certificate/container/Stakeholder/EnterpriseLicense/EnterpriseLicense",
label: "企业证照管理",
icon: <IdcardOutlined />,
},
{
key: "/certificate/container/Stakeholder/EnterpriseLicense/PersonnelLicense",
label: "人员证照",
icon: <TeamOutlined />,
children: [
{
key: "/certificate/container/Stakeholder/EnterpriseLicense/PersonnelLicense/PersonInCharge/List",
label: "负责人",
icon: <UserOutlined />,
},
{
key: "/certificate/container/Stakeholder/EnterpriseLicense/PersonnelLicense/SecurityAdmini/List",
label: "安全管理员",
icon: <SafetyCertificateOutlined />,
},
{
key: "/certificate/container/Stakeholder/EnterpriseLicense/PersonnelLicense/SpecialDevice/List",
label: "特种设备",
icon: <ToolOutlined />,
},
{
key: "/certificate/container/Stakeholder/EnterpriseLicense/PersonnelLicense/SpecialPersonnel/List",
label: "特种作业人员",
icon: <IdcardOutlined />,
},
],
},
],
},
{
key: "/certificate/container/Test",
label: "测试页面",
icon: <ExperimentOutlined />,
},
];
export default menuItems;
/** 扁平化菜单 */
export function flattenMenu(items) {
const result = [];
function walk(list) {
for (const item of list) {
result.push(item);
if (item.children) walk(item.children);
}
}
walk(items);
return result;
}
/**
* 根据路径查找面包屑路径
* 返回从根到叶子节点的菜单项数组
*/
export function findMenuPath(path) {
function search(items, ancestors) {
for (const item of items) {
const current = [...ancestors, item];
if (item.key === path) {
return current;
}
if (item.children) {
const found = search(item.children, current);
if (found) return found;
}
}
return null;
}
return search(menuItems, []) || [];
}
/**
* 根据路径获取页面标签名
*/
export function getPageLabel(path) {
const flat = flattenMenu(menuItems);
const item = flat.find((m) => m.key === path);
return item?.label || path.split("/").filter(Boolean).pop() || "未命名页面";
}
/**
* 根据路径获取默认展开的菜单项
*/
export function getOpenKeys(path) {
const breadcrumb = findMenuPath(path);
return breadcrumb.slice(0, -1).map((i) => i.key);
}
/**
* 根据路径获取选中的菜单项
*/
export function getSelectedKeys(path) {
const allPaths = flattenMenu(menuItems).map((m) => m.key);
let match = "";
for (const p of allPaths) {
if (path.startsWith(p) && p.length > match.length) {
match = p;
}
}
return match ? [match] : [];
}

View File

@ -1,17 +0,0 @@
import EnterpriseLicenseList from "~/pages/Container/Supervision/EnterpriseLicense/EnterpriseLicense";
function EnterpriseLicense(props) {
return (
<div>
<EnterpriseLicenseList
props={props}
permissionAdd="xgfd-qyzzgl-add"
permissionEdit="xgfd-qyzzgl-edit"
permissionView="xgfd-qyzzgl-info"
permissionDel="xgfd-qyzzgl-del"
/>
</div>
);
}
export default EnterpriseLicense;

View File

@ -1,23 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import PersonInChargeList from "~/components/PersonInChargeList";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
// 企业主要负责人管理
function List(props) {
return (
<div>
<PersonInChargeList
props={props}
certificatePhotoType={161}
personnelType="zyfzr"
permissionAdd="xgfd-zyfzrgl-add"
permissionEdit="xgfd-zyfzrgl-edit"
permissionView="xgfd-zyfzrgl-info"
permissionDel="xgfd-zyfzrgl-del"
dictionaryType="zyfzrgwmc0000"
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));

View File

@ -1,16 +0,0 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import PersonInChargeInfo from "~/components/PersonInChargeInfo";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function View(props) {
return (
<div>
<PersonInChargeInfo
props={props}
certificatePhotoType={161}
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(View);

View File

@ -1,9 +0,0 @@
function PersonInCharge(props) {
return (
<div>
{props.children}
</div>
);
}
export default PersonInCharge;

View File

@ -1,23 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import PersonInChargeList from "~/components/PersonInChargeList";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
// 企业安全生产管理人员管理
function List(props) {
return (
<div>
<PersonInChargeList
props={props}
certificatePhotoType={162}
personnelType="aqscglry"
permissionAdd="xgfd-aqscglrygl-add"
permissionEdit="xgfd-aqscglrygl-edit"
permissionView="xgfd-aqscglrygl-info"
permissionDel="xgfd-aqscglrygl-del"
dictionaryType="aqscglrygwmc0000"
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));

View File

@ -1,16 +0,0 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import PersonInChargeInfo from "~/components/PersonInChargeInfo";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function View(props) {
return (
<div>
<PersonInChargeInfo
props={props}
certificatePhotoType={162}
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(View);

View File

@ -1,9 +0,0 @@
function SecurityAdmini(props) {
return (
<div>
{props.children}
</div>
);
}
export default SecurityAdmini;

View File

@ -1,23 +0,0 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import SpecialCertificateList from "~/components/SpecialCertificateList";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
import {Permission} from "@cqsjjb/jjb-common-decorator/permission";
// 企业特种设备操作人员管理
function List(props) {
return (
<div>
<SpecialCertificateList
props={props}
certificatePhotoType={160}
personnelType="tzsbczry"
permissionAdd="xgfd-tzzzsbczrygl-add"
permissionEdit="xgfd-tzzzsbczrygl-edit"
permissionView="xgfd-tzzzsbczrygl-info"
permissionDel="xgfd-tzzzsbczrygl-del"
dictionaryType="tzsbczryczxmzylb0000"
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));

View File

@ -1,16 +0,0 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import SpecialCertificateInfo from "~/components/SpecialCertificateInfo";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function View(props) {
return (
<div>
<SpecialCertificateInfo
props={props}
certificatePhotoType={160}
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(View);

View File

@ -1,9 +0,0 @@
function SpecialEquipment(props) {
return (
<div>
{props.children}
</div>
);
}
export default SpecialEquipment;

View File

@ -1,23 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import SpecialCertificateList from "~/components/SpecialCertificateList";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
// 企业特种作业人员管理
function List(props) {
return (
<div>
<SpecialCertificateList
props={props}
certificatePhotoType={159}
personnelType="tezhongzuoye"
permissionAdd="xgfd-tzzyrugl-add"
permissionEdit="xgfd-tzzyrugl-edit"
permissionView="xgfd-tzzyrugl-info"
permissionDel="xgfd-tzzyrugl-del"
dictionaryType="tzzyryhylb0000"
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));

View File

@ -1,16 +0,0 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import SpecialCertificateInfo from "~/components/SpecialCertificateInfo";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function View(props) {
return (
<div>
<SpecialCertificateInfo
props={props}
certificatePhotoType={159}
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(View);

View File

@ -1,9 +0,0 @@
function SpecialPersonnel(props) {
return (
<div>
{props.children}
</div>
);
}
export default SpecialPersonnel;

View File

@ -1,9 +0,0 @@
function PersonnelLicense(props) {
return (
<div>
{props.children}
</div>
);
}
export default PersonnelLicense;

View File

@ -1,9 +0,0 @@
function EnterpriseLicenseManage(props) {
return (
<div>
{props.children}
</div>
);
}
export default EnterpriseLicenseManage;

View File

@ -1,9 +0,0 @@
function Stakeholder(props) {
return (
<div>
{props.children}
</div>
);
}
export default Stakeholder;

View File

@ -1,70 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Button, Form, Space } from "antd";
import Page from "zy-react-library/components/Page";
import Search from "zy-react-library/components/Search";
import Table from "zy-react-library/components/Table";
import useTable from "zy-react-library/hooks/useTable";
import { NS_CORP_CERTIFICATE } from "~/enumerate/namespace";
function List(props) {
const [form] = Form.useForm();
const { tableProps, getData } = useTable(props["corpCertificateStatPage"], {
form,
transform: (formData) => {
return {
...formData,
corpType: 0,
};
},
});
return (
<Page isShowAllAction={false}>
<Search
form={form}
options={[
{
name: "corpName",
label: "公司名称",
},
]}
onFinish={getData}
/>
<Table
columns={[
{
title: "公司名称",
dataIndex: "corpName",
},
{
title: "证书数量",
dataIndex: "certCount",
},
{
title: "操作",
width: 200,
hidden: !props.permission("gfd-zgsztj-info"),
render: (_, record) => (
<Space>
<Button
type="link"
onClick={() => props.history.push(`./View?corpinfoId=${record.corpId}`)}
>
查看
</Button>
</Space>
),
},
]}
{...tableProps}
/>
</Page>
);
}
export default Connect([NS_CORP_CERTIFICATE], true)(Permission(List));

View File

@ -1,15 +0,0 @@
import EnterpriseLicense from "../../EnterpriseLicense";
function View(props) {
return (
<div>
<EnterpriseLicense
props={props}
type="View"
permissionView="gfd-zgsztj-insideInfo"
/>
</div>
);
}
export default View;

View File

@ -1,9 +0,0 @@
function BranchStatistics(props) {
return (
<div>
{props.children}
</div>
);
}
export default BranchStatistics;

View File

@ -1,600 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Button, Descriptions, Form, message, Modal, Space } from "antd";
import { useEffect, useRef, useState } from "react";
import FormBuilder from "zy-react-library/components/FormBuilder";
import AddIcon from "zy-react-library/components/Icon/AddIcon";
import Page from "zy-react-library/components/Page";
import PreviewImg from "zy-react-library/components/PreviewImg";
import Search from "zy-react-library/components/Search";
import Table from "zy-react-library/components/Table";
import TooltipPreviewImg from "zy-react-library/components/TooltipPreviewImg";
import Upload from "zy-react-library/components/Upload";
import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender";
import { UPLOAD_FILE_TYPE_ENUM } from "zy-react-library/enum/uploadFile/gwj";
import useDeleteFile from "zy-react-library/hooks/useDeleteFile";
import useGetFile from "zy-react-library/hooks/useGetFile";
import useGetUrlQuery from "zy-react-library/hooks/useGetUrlQuery";
import useTable from "zy-react-library/hooks/useTable";
import useUploadFile from "zy-react-library/hooks/useUploadFile";
import { NS_CORP_CERTIFICATE } from "~/enumerate/namespace";
import { useDebounce } from "~/utils";
function EnterpriseLicense(props) {
const [addModalOpen, setAddModalOpen] = useState(false);
const [viewModalOpen, setViewModalOpen] = useState(false);
const [currentId, setCurrentId] = useState("");
const queryParams = useGetUrlQuery();
const permissionAdd = props.permissionAdd ? props.permissionAdd : "gfd-qyzzgl-add";
const permissionEdit = props.permissionEdit ? props.permissionEdit : "gfd-qyzzgl-edit";
const permissionView = props.permissionView ? props.permissionView : "gfd-qyzzgl-info";
const permissionDel = props.permissionDel ? props.permissionDel : "gfd-qyzzgl-del";
const { loading: getFileLoading, getFile } = useGetFile();
const [form] = Form.useForm();
const { tableProps, getData } = useTable(props["corpCertificateList"], {
form,
transform: (formData) => {
return {
...formData,
geCertificateDateStart: formData.certificateDate?.[0],
leCertificateDateEnd: formData.certificateDate?.[1],
eqCorpinfoId: queryParams["corpinfoId"],
};
},
});
const onDelete = (id) => {
Modal.confirm({
title: "提示",
content: "确定删除吗?",
onOk: () => {
props["corpCertificateRemove"]({
id,
}).then((res) => {
if (res.success) {
message.success("删除成功");
getData();
}
});
getData();
},
});
};
const [fileCache, setFileCache] = useState({});
const [loadingKeys, setLoadingKeys] = useState(new Set());
const pendingLoadIdsRef = useRef(new Set());
const requestQueueRef = useRef([]);
const activeRequestsRef = useRef(0);
const MAX_CONCURRENT_REQUESTS = 3; // 最大并发请求数
const processQueue = () => {
while (
requestQueueRef.current.length > 0
&& activeRequestsRef.current < MAX_CONCURRENT_REQUESTS
) {
const { id, resolve, reject } = requestQueueRef.current.shift();
activeRequestsRef.current++;
getFile({
eqType: UPLOAD_FILE_TYPE_ENUM["6"],
eqForeignKey: id,
})
.then((res) => {
setFileCache(prev => ({
...prev,
[id]: res || [],
}));
resolve(res);
})
.catch((err) => {
reject(err);
})
.finally(() => {
activeRequestsRef.current--;
setLoadingKeys((prev) => {
const newSet = new Set(prev);
newSet.delete(id);
return newSet;
});
processQueue();
});
}
};
const loadFileForRecord = (userQualificationinfoId) => {
if (!userQualificationinfoId)
return Promise.resolve();
if (fileCache[userQualificationinfoId])
return Promise.resolve();
if (loadingKeys.has(userQualificationinfoId))
return Promise.resolve();
if (pendingLoadIdsRef.current.has(userQualificationinfoId))
return Promise.resolve();
pendingLoadIdsRef.current.add(userQualificationinfoId);
setLoadingKeys(prev => new Set([...prev, userQualificationinfoId]));
return new Promise((resolve, reject) => {
requestQueueRef.current.push({ id: userQualificationinfoId, resolve, reject });
processQueue();
}).finally(() => {
pendingLoadIdsRef.current.delete(userQualificationinfoId);
});
};
// 缓存数据变化时清空图片缓存
useEffect(() => {
if (tableProps.dataSource) {
const currentIds = new Set(tableProps.dataSource.map(item => item.corpCertificateId).filter(Boolean));
setFileCache((prev) => {
const newCache = {};
Object.keys(prev).forEach((id) => {
if (currentIds.has(id)) {
newCache[id] = prev[id];
}
});
return newCache;
});
}
}, [tableProps.dataSource]);
// 记录已经触发过加载的 ID避免 render 重复触发
const triggeredLoadIdsRef = useRef(new Set());
useEffect(() => {
if (tableProps.dataSource) {
tableProps.dataSource.forEach((record) => {
const id = record.corpCertificateId;
if (id && !triggeredLoadIdsRef.current.has(id)) {
triggeredLoadIdsRef.current.add(id);
loadFileForRecord(id);
}
});
}
// 组件卸载或数据源变化时重置
return () => {
triggeredLoadIdsRef.current.clear();
};
}, [tableProps.dataSource]);
return (
<Page isShowAllAction={false}>
<Search
form={form}
options={[
{
name: "likeCertificateName",
label: "证书名称",
},
{
name: "certificateDate",
label: "证书有效期",
render: FORM_ITEM_RENDER_ENUM.DATE_RANGE,
},
]}
onFinish={getData}
/>
<Table
loding={getFileLoading}
toolBarRender={() => (
<>
{
(!props.type && props.permission(permissionAdd))
&& (
<Button
type="primary"
icon={<AddIcon />}
onClick={() => {
setAddModalOpen(true);
}}
>
新增
</Button>
)
}
</>
)}
columns={[
{
title: "证书名称",
dataIndex: "certificateName",
},
{
title: "证书编号",
dataIndex: "certificateCode",
},
{
title: "证书有效期",
dataIndex: "certificateNo",
width: 370,
render: (_, record) =>
<div>{`${record.certificateDateStart ?? ""} - ${record.certificateDateEnd ?? ""}`}</div>,
},
{
title: "图片",
render: (_, record) => {
const id = record.corpCertificateId;
const files = fileCache[id] || [];
if (!files.length)
return <span></span>;
return <TooltipPreviewImg files={files} />;
},
},
{
title: "操作",
width: 200,
render: (_, record) => (
<Space>
{
props.permission(permissionView)
&& (
<Button
type="link"
onClick={() => {
setViewModalOpen(true);
setCurrentId(record.id);
}}
>
查看
</Button>
)
}
{
(!props.type && props.permission(permissionEdit))
&& (
<Button
type="link"
onClick={() => {
setAddModalOpen(true);
setCurrentId(record.id);
}}
>
编辑
</Button>
)
}
{
(!props.type && props.permission(permissionDel))
&& (
<Button
danger
type="link"
onClick={() => onDelete(record.id)}
>
删除
</Button>
)
}
</Space>
),
},
]}
{...tableProps}
/>
{addModalOpen && (
<AddModal
open={addModalOpen}
loding={props.corpCertificate.corpCertificateLoading}
getData={getData}
currentId={currentId}
requestAdd={props["corpCertificateAdd"]}
requestEdit={props["corpCertificateEdit"]}
requestDetails={props["corpCertificateInfo"]}
corpCertificateIsExistCertNo={props["corpCertificateIsExistCertNo"]}
onCancel={() => {
setAddModalOpen(false);
setCurrentId("");
}}
onSuccess={(userQualificationinfoId) => {
// 清除该记录的图片缓存,强制下次 render 时重新加载
setFileCache((prev) => {
const newCache = { ...prev };
delete newCache[userQualificationinfoId];
return newCache;
});
}}
/>
)}
{viewModalOpen && (
<ViewModal
open={viewModalOpen}
loding={props.corpCertificate.corpCertificateLoading}
getData={getData}
currentId={currentId}
requestDetails={props["corpCertificateInfo"]}
onCancel={() => {
setViewModalOpen(false);
setCurrentId("");
}}
/>
)}
</Page>
);
}
function AddModalComponent(props) {
const [form] = Form.useForm();
const [userQualificationinfoId, setUserQualificationinfoId] = useState("");
const { loading: deleteFileLoading, deleteFile } = useDeleteFile();
const { loading: uploadFileLoading, uploadFile } = useUploadFile();
const { loading: getFileLoading, getFile } = useGetFile();
const [deleteCardImageFiles, setDeleteCardImageFiles] = useState([]);
const [CertificateCodeValue, setPertificateCodeValue] = useState(null);
const [isSubmit, setIsSubmit] = useState(true);
const debouncedCertificateCodeValue = useDebounce(CertificateCodeValue, 100);
useEffect(() => {
if (props.currentId) {
const fetchData = async () => {
const { data } = await props.requestDetails({
id: props.currentId,
});
data.certificateDate = [data.certificateDateStart, data.certificateDateEnd];
const certificateImgs = await getFile({
eqType: UPLOAD_FILE_TYPE_ENUM["6"],
eqForeignKey: data.corpCertificateId,
});
data.certificateImgs = certificateImgs;
form.setFieldsValue(data);
setUserQualificationinfoId(data.corpCertificateId);
};
fetchData();
}
}, [props.currentId]);
const onCancel = () => {
form.resetFields();
props.onCancel();
};
const onSubmit = async (values) => {
await deleteFile({
single: false,
files: deleteCardImageFiles,
});
values.certificateDateStart = values.certificateDate[0];
values.certificateDateEnd = values.certificateDate[1];
if (!isSubmit) {
form.setFields([
{
name: "certificateCode",
errors: ["证书编号重复"],
},
]);
return;
}
const { id } = await uploadFile({
single: false,
files: values.certificateImgs,
params: {
type: UPLOAD_FILE_TYPE_ENUM["6"],
foreignKey: userQualificationinfoId,
},
});
if (props.currentId) {
values.id = props.currentId;
values.corpCertificateId = userQualificationinfoId;
await props.requestEdit(values).then((res) => {
if (res.success) {
onCancel();
props.getData();
message.success("编辑成功");
}
});
}
else {
values.corpCertificateId = id;
await props.requestAdd(values).then((res) => {
if (res.success) {
onCancel();
props.getData();
message.success("新增成功");
}
});
}
};
// 校验重复
useEffect(() => {
if (!debouncedCertificateCodeValue) {
form.setFields([
{
name: "certificateCode",
errors: [],
},
]);
return;
}
props["corpCertificateIsExistCertNo"]({
certNo: debouncedCertificateCodeValue,
id: props.currentId ?? "",
}).then((res) => {
if (res.data) {
form.setFields([
{
name: "certificateCode",
errors: ["证书编号重复"],
},
]);
setIsSubmit(false);
}
else {
setIsSubmit(true);
}
});
}, [debouncedCertificateCodeValue]);
const onValuesChange = (changedValues) => {
if ("certificateCode" in changedValues) {
setPertificateCodeValue(changedValues.certificateCode ?? "");
}
};
return (
<Modal
open={props.open}
maskClosable={false}
title={props.currentId ? "编辑" : "新增"}
width={800}
confirmLoading={
deleteFileLoading || uploadFileLoading || getFileLoading || props.loding
}
onOk={form.submit}
onCancel={onCancel}
>
<FormBuilder
form={form}
span={24}
onValuesChange={onValuesChange}
values={{
securityFlag: 0,
}}
options={[
{
name: "certificateName",
label: "证书名称",
},
{
name: "certificateDate",
label: "证书有效期",
render: FORM_ITEM_RENDER_ENUM.DATE_RANGE,
rules: [
{
validator: (_, value) => {
const allEmptyStrings = Array.isArray(value) && value.every(item => item === "");
if (allEmptyStrings) {
return Promise.reject(new Error("请选择有效期"));
}
return Promise.resolve();
},
},
],
},
{
name: "certificateCode",
label: "证书编号",
},
{
name: "remark",
label: "备注",
required: false,
render: FORM_ITEM_RENDER_ENUM.TEXTAREA,
},
{
name: "certificateImgs",
label: "证书图片",
render: (
<Upload
maxCount={3}
onGetRemoveFile={(file) => {
setDeleteCardImageFiles([...deleteCardImageFiles, file]);
}}
/>
),
},
]}
labelCol={{
span: 10,
}}
showActionButtons={false}
onFinish={onSubmit}
/>
</Modal>
);
}
const AddModal = AddModalComponent;
function ViewModalComponent(props) {
const [form] = Form.useForm();
const [info, setInfo] = useState({});
const { loading: getFileLoading, getFile } = useGetFile();
useEffect(() => {
if (props.currentId) {
const fetchData = async () => {
const { data } = await props.requestDetails({
id: props.currentId,
});
const certificateImgs = await getFile({
eqType: UPLOAD_FILE_TYPE_ENUM["6"],
eqForeignKey: data.corpCertificateId,
});
data.certificateImgs = certificateImgs;
setInfo(data);
};
fetchData();
}
}, [props.currentId]);
const onCancel = () => {
form.resetFields();
props.onCancel();
};
return (
<Modal
open={props.open}
maskClosable={false}
title="查看"
width={800}
cancelText="关闭"
okButtonProps={{
style: {
display: "none",
},
}}
confirmLoading={
getFileLoading || props.loding
}
onCancel={onCancel}
>
<Descriptions
size="middle"
bordered
labelStyle={{ width: 200 }}
items={[
{
label: "证书名称",
children: info.certificateName,
span: 4,
},
{
label: "证书有效期",
children: <div>{`${info.certificateDateStart ?? ""} - ${info.certificateDateEnd ?? ""}`}</div>,
span: 4,
},
{
label: "证书编号",
children: info.certificateCode,
span: 4,
},
{
label: "备注",
children: info.remark,
span: 4,
},
{
label: "证书图片",
children: <PreviewImg files={info.certificateImgs} />,
span: 4,
},
]}
/>
</Modal>
);
}
const ViewModal = ViewModalComponent;
export default Connect([NS_CORP_CERTIFICATE], true)(Permission(EnterpriseLicense));

View File

@ -1,72 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Button, Form, Space } from "antd";
import Page from "zy-react-library/components/Page";
import Search from "zy-react-library/components/Search";
import Table from "zy-react-library/components/Table";
import useTable from "zy-react-library/hooks/useTable";
import { NS_CORP_CERTIFICATE } from "~/enumerate/namespace";
function List(props) {
const [form] = Form.useForm();
const { tableProps, getData } = useTable(props["corpCertificateStatPage"], {
form,
transform: (formData) => {
return {
...formData,
corpType: 1,
};
},
});
return (
<Page isShowAllAction={false}>
<Search
form={form}
options={[
{
name: "corpName",
label: "公司名称",
},
]}
onFinish={getData}
/>
<Table
columns={[
{
title: "公司名称",
dataIndex: "corpName",
},
{
title: "证书数量",
dataIndex: "certCount",
},
{
title: "操作",
width: 200,
hidden: !props.permission("gfd-xgfztj-info"),
render: (_, record) => (
<Space>
<Button
type="link"
onClick={() => props.history.push(`./View?corpinfoId=${record.corpId}`)}
>
查看
</Button>
</Space>
),
},
]}
{...tableProps}
/>
</Page>
);
}
export default Connect([NS_CORP_CERTIFICATE], true)(Permission(List));

View File

@ -1,15 +0,0 @@
import EnterpriseLicense from "../../EnterpriseLicense";
function View(props) {
return (
<div>
<EnterpriseLicense
props={props}
type="View"
permissionView="gfd-xgfztj-insideInfo"
/>
</div>
);
}
export default View;

View File

@ -1,9 +0,0 @@
function StakeholderStatistics(props) {
return (
<div>
{props.children}
</div>
);
}
export default StakeholderStatistics;

View File

@ -1,9 +0,0 @@
function EnterpriseLicenseManage(props) {
return (
<div>
{props.children}
</div>
);
}
export default EnterpriseLicenseManage;

View File

@ -1,117 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Button, Form, Space } from "antd";
import Page from "zy-react-library/components/Page";
import Search from "zy-react-library/components/Search";
import Table from "zy-react-library/components/Table";
import useTable from "zy-react-library/hooks/useTable";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function List(props) {
const [form] = Form.useForm();
const { tableProps, getData } = useTable(props["userCertificateStatPage"], {
form,
transform: (formData) => {
return {
...formData,
corpType: 0,
};
},
});
return (
<Page isShowAllAction={false}>
<Search
form={form}
options={[
{
name: "corpName",
label: "公司名称",
},
]}
onFinish={getData}
/>
<Table
columns={[
{
title: "公司名称",
dataIndex: "corpName",
},
{
title: "特种作业人员证书数",
dataIndex: "specialWorkCertCount",
render: (_, record) => (
<Space>
<Button
type="link"
onClick={() => props.history.push(`./SpecialPersonnel/List?corpinfoId=${record.corpinfoId}`)}
disabled={!props.permission("gfd-fzgsryzztj-tzzyInfo")}
>
{record.specialWorkCertCount}
</Button>
</Space>
),
},
{
title: "特种设备操作人员证书数",
dataIndex: "specialEquipmentCertCount",
render: (_, record) => (
<Space>
<Button
type="link"
onClick={() => props.history.push(`./SpecialDevice/List?corpinfoId=${record.corpinfoId}`)}
disabled={!props.permission("gfd-fzgsryzztj-tzsbInfo")}
>
{record.specialEquipmentCertCount}
</Button>
</Space>
),
},
{
title: "主要负责人证书数",
dataIndex: "principalCertCount",
render: (_, record) => (
<Space>
<Button
type="link"
onClick={() => props.history.push(`./PersonInCharge/List?corpinfoId=${record.corpinfoId}`)}
disabled={!props.permission("gfd-fzgsryzztj-zyfzrInfo")}
>
{record.principalCertCount}
</Button>
</Space>
),
},
{
title: "安全生产管理人员证书数",
dataIndex: "safetyManagerCertCount",
render: (_, record) => (
<Space>
<Button
type="link"
onClick={() => props.history.push(`./SecurityAdmini/List?corpinfoId=${record.corpinfoId}`)}
disabled={!props.permission("gfd-fzgsryzztj-aqgly-info")}
>
{record.safetyManagerCertCount}
</Button>
</Space>
),
},
]}
{...tableProps}
/>
</Page>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));

View File

@ -1,24 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import Page from "zy-react-library/components/Page";
import PersonInChargeList from "~/components/PersonInChargeList";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
// 企业主要负责人管理
function List(props) {
return (
<Page headerTitle="主要负责人证书数" isShowFooter={false}>
<div>
<PersonInChargeList
props={props}
certificatePhotoType={161}
displayType="View"
personnelType="zyfzr"
permissionView="gfd-fzgsryzztj-zyfzrInfoNb"
dictionaryType="zyfzrgwmc0000"
/>
</div>
</Page>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));

View File

@ -1,16 +0,0 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import PersonInChargeInfo from "~/components/PersonInChargeInfo";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function View(props) {
return (
<div>
<PersonInChargeInfo
props={props}
certificatePhotoType={161}
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(View);

View File

@ -1,9 +0,0 @@
function PersonInCharge(props) {
return (
<div>
{props.children}
</div>
);
}
export default PersonInCharge;

View File

@ -1,22 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import Page from "zy-react-library/components/Page";
import PersonInChargeList from "~/components/PersonInChargeList";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
// 企业安全生产管理人员管理
function List(props) {
return (
<Page headerTitle="安全生产管理人员证书数" isShowFooter={false}>
<PersonInChargeList
props={props}
certificatePhotoType={162}
displayType="View"
personnelType="aqscglry"
permissionView="gfd-fzgsryzztj-aqgly-infoNb"
dictionaryType="aqscglrygwmc0000"
/>
</Page>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));

View File

@ -1,16 +0,0 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import PersonInChargeInfo from "~/components/PersonInChargeInfo";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function View(props) {
return (
<div>
<PersonInChargeInfo
props={props}
certificatePhotoType={162}
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(View);

View File

@ -1,9 +0,0 @@
function SecurityAdmini(props) {
return (
<div>
{props.children}
</div>
);
}
export default SecurityAdmini;

View File

@ -1,22 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import Page from "zy-react-library/components/Page";
import SpecialCertificateList from "~/components/SpecialCertificateList";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
// 企业特种设备操作人员管理
function List(props) {
return (
<Page headerTitle="特种设备操作人员证书数" isShowFooter={false}>
<SpecialCertificateList
props={props}
certificatePhotoType={160}
displayType="View"
personnelType="tzsbczry"
permissionView="gfd-fzgsryzztj-tzsbInfoNb"
dictionaryType="tzsbczryczxmzylb0000"
/>
</Page>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));

View File

@ -1,16 +0,0 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import SpecialCertificateInfo from "~/components/SpecialCertificateInfo";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function View(props) {
return (
<div>
<SpecialCertificateInfo
props={props}
certificatePhotoType={160}
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(View);

View File

@ -1,9 +0,0 @@
function SpecialEquipment(props) {
return (
<div>
{props.children}
</div>
);
}
export default SpecialEquipment;

View File

@ -1,22 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import Page from "zy-react-library/components/Page";
import SpecialCertificateList from "~/components/SpecialCertificateList";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
// 企业特种作业人员管理
function List(props) {
return (
<Page headerTitle="特种作业人员证书数" isShowFooter={false}>
<SpecialCertificateList
props={props}
certificatePhotoType={159}
displayType="View"
personnelType="tezhongzuoye"
permissionView="gfd-fzgsryzztj-tzzyInfoNb"
dictionaryType="tzzyryhylb0000"
/>
</Page>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));

View File

@ -1,16 +0,0 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import SpecialCertificateInfo from "~/components/SpecialCertificateInfo";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function View(props) {
return (
<div>
<SpecialCertificateInfo
props={props}
certificatePhotoType={159}
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(View);

View File

@ -1,9 +0,0 @@
function SpecialPersonnel(props) {
return (
<div>
{props.children}
</div>
);
}
export default SpecialPersonnel;

View File

@ -1,9 +0,0 @@
function BranchCompanyStat(props) {
return (
<div>
{props.children}
</div>
);
}
export default BranchCompanyStat;

View File

@ -1,23 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import PersonInChargeList from "~/components/PersonInChargeList";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
// 企业主要负责人管理
function List(props) {
return (
<div>
<PersonInChargeList
props={props}
certificatePhotoType={161}
personnelType="zyfzr"
permissionAdd="gfd-qyzyfzrgl-add"
permissionEdit="gfd-qyzyfzrgl-edit"
permissionView="gfd-qyzyfzrgl-info"
permissionDel="gfd-qyzyfzrgl-del"
dictionaryType="zyfzrgwmc0000"
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));

View File

@ -1,16 +0,0 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import PersonInChargeInfo from "~/components/PersonInChargeInfo";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function View(props) {
return (
<div>
<PersonInChargeInfo
props={props}
certificatePhotoType={161}
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(View);

View File

@ -1,9 +0,0 @@
function PersonInCharge(props) {
return (
<div>
{props.children}
</div>
);
}
export default PersonInCharge;

View File

@ -1,23 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import PersonInChargeList from "~/components/PersonInChargeList";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
// 企业安全生产管理人员管理
function List(props) {
return (
<div>
<PersonInChargeList
props={props}
certificatePhotoType={162}
personnelType="aqscglry"
permissionAdd="gfd-qyaqscglrygl-add"
permissionEdit="gfd-qyaqscglrygl-edit"
permissionView="gfd-qyaqscglrygl-info"
permissionDel="gfd-qyaqscglrygl-del"
dictionaryType="aqscglrygwmc0000"
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));

View File

@ -1,16 +0,0 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import PersonInChargeInfo from "~/components/PersonInChargeInfo";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function View(props) {
return (
<div>
<PersonInChargeInfo
props={props}
certificatePhotoType={162}
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(View);

View File

@ -1,9 +0,0 @@
function SecurityAdmini(props) {
return (
<div>
{props.children}
</div>
);
}
export default SecurityAdmini;

View File

@ -1,23 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import SpecialCertificateList from "~/components/SpecialCertificateList";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
// 企业特种设备操作人员管理
function List(props) {
return (
<div>
<SpecialCertificateList
props={props}
certificatePhotoType={160}
personnelType="tzsbczry"
permissionAdd="gfd-tzsbczrygl-add"
permissionEdit="gfd-tzsbczrygl-edit"
permissionView="gfd-tzsbczrygl-info"
permissionDel="gfd-tzsbczrygl-del"
dictionaryType="tzsbczryczxmzylb0000"
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));

View File

@ -1,16 +0,0 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import SpecialCertificateInfo from "~/components/SpecialCertificateInfo";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function View(props) {
return (
<div>
<SpecialCertificateInfo
props={props}
certificatePhotoType={160}
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(View);

View File

@ -1,9 +0,0 @@
function SpecialEquipment(props) {
return (
<div>
{props.children}
</div>
);
}
export default SpecialEquipment;

View File

@ -1,22 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import SpecialCertificateList from "~/components/SpecialCertificateList";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
// 企业特种作业人员管理
function List(props) {
return (
<div>
<SpecialCertificateList
props={props}
certificatePhotoType={159}
personnelType="tezhongzuoye"
permissionAdd="gfd-tzzyrugl-add"
permissionEdit="gfd-tzzyrugl-edit"
permissionView="gfd-tzzyrugl-info"
permissionDel="gfd-tzzyrugl-del"
dictionaryType="tzzyryhylb0000"
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));

View File

@ -1,16 +0,0 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import SpecialCertificateInfo from "~/components/SpecialCertificateInfo";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function View(props) {
return (
<div>
<SpecialCertificateInfo
props={props}
certificatePhotoType={159}
/>
</div>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(View);

View File

@ -1,9 +0,0 @@
function SpecialPersonnel(props) {
return (
<div>
{props.children}
</div>
);
}
export default SpecialPersonnel;

View File

@ -1,119 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Button, Form, Space } from "antd";
import Page from "zy-react-library/components/Page";
import Search from "zy-react-library/components/Search";
import Table from "zy-react-library/components/Table";
import useTable from "zy-react-library/hooks/useTable";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
function List(props) {
const [form] = Form.useForm();
const { tableProps, getData } = useTable(props["userCertificateStatPage"], {
form,
transform: (formData) => {
return {
...formData,
corpType: 1,
};
},
});
return (
<Page isShowAllAction={false}>
<Search
form={form}
options={[
{
name: "corpName",
label: "公司名称",
},
]}
onFinish={getData}
/>
<Table
columns={[
{
title: "公司名称",
dataIndex: "corpName",
},
{
title: "特种作业人员证书数",
dataIndex: "specialWorkCertCount",
render: (_, record) => (
<Space>
<Button
type="link"
disabled={!props.permission("gfd-xgfryzztj-tzzyInfo")}
onClick={() => props.history.push(`./SpecialPersonnel/List?corpinfoId=${record.corpinfoId}`)}
>
{record.specialWorkCertCount}
</Button>
</Space>
),
},
{
title: "特种设备操作人员证书数",
dataIndex: "specialEquipmentCertCount",
render: (_, record) => (
<Space>
<Button
type="link"
onClick={() => props.history.push(`./SpecialDevice/List?corpinfoId=${record.corpinfoId}`)}
disabled={!props.permission("gfd-xgfryzztj-tzsbInfo")}
>
{record.specialEquipmentCertCount}
</Button>
</Space>
),
},
{
title: "主要负责人证书数",
dataIndex: "principalCertCount",
render: (_, record) => (
<Space>
<Button
type="link"
onClick={() => props.history.push(`./PersonInCharge/List?corpinfoId=${record.corpinfoId}`)}
disabled={!props.permission("gfd-xgfryzztj-zyfzrInfo")}
>
{record.principalCertCount}
</Button>
</Space>
),
},
{
title: "安全生产管理人员证书数",
dataIndex: "safetyManagerCertCount",
render: (_, record) => (
<Space>
<Button
type="link"
onClick={() => props.history.push(`./SecurityAdmini/List?corpinfoId=${record.corpinfoId}`)}
disabled={!props.permission("gfd-xgfryzztj-aqscInfo")}
>
{record.safetyManagerCertCount}
</Button>
</Space>
),
},
]}
{...tableProps}
/>
</Page>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));

View File

@ -1,22 +0,0 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import Page from "zy-react-library/components/Page";
import PersonInChargeList from "~/components/PersonInChargeList";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
// 企业主要负责人管理
function List(props) {
return (
<Page headerTitle="主要负责人证书数" isShowFooter={false}>
<PersonInChargeList
props={props}
certificatePhotoType={161}
displayType="View"
personnelType="zyfzr"
permissionView="gfd-xgfryzztj-zyfzrInfoNb"
dictionaryType="zyfzrgwmc0000"
/>
</Page>
);
}
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));

Some files were not shown because too many files have changed in this diff Show More