From 3b51963fa18f7aa8116e3e68b7391da4539cf342 Mon Sep 17 00:00:00 2001 From: tangjie <122778500@qq.com> Date: Fri, 21 Aug 2026 18:52:24 +0800 Subject: [PATCH] feat --- .../spec.json | 69 ++++++++ .../type.d.ts | 46 +++++ .../spec.json | 57 ++++++ .../type.d.ts | 18 ++ .../spec.json | 62 +++++++ .../type.d.ts | 40 +++++ src/api/courseware/index.js | 19 ++ .../EduTraining/ClassManage/Add/StudentTab.js | 164 +++++++++++++++++- .../EduTraining/ClassManage/List/index.js | 15 +- 9 files changed, 471 insertions(+), 19 deletions(-) create mode 100644 .api/safetyEval-classStudent-batchSave-post/spec.json create mode 100644 .api/safetyEval-classStudent-batchSave-post/type.d.ts create mode 100644 .api/safetyEval-classStudent-delete-post/spec.json create mode 100644 .api/safetyEval-classStudent-delete-post/type.d.ts create mode 100644 .api/safetyEval-org-personnel-page-get/spec.json create mode 100644 .api/safetyEval-org-personnel-page-get/type.d.ts diff --git a/.api/safetyEval-classStudent-batchSave-post/spec.json b/.api/safetyEval-classStudent-batchSave-post/spec.json new file mode 100644 index 0000000..dd53971 --- /dev/null +++ b/.api/safetyEval-classStudent-batchSave-post/spec.json @@ -0,0 +1,69 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "批量新增班级学员", + "version": "1.0.0", + "description": "同步自 Apifox 项目(api-505094943),班级学员" + }, + "paths": { + "/safetyEval/classStudent/batchSave": { + "post": { + "summary": "批量新增班级学员", + "description": "批量新增班级学员", + "tags": ["班级学员"], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { "$ref": "#/components/schemas/ClassStudentDTO" } + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/SingleResponseBoolean" } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "ClassStudentDTO": { + "type": "object", + "properties": { + "id": { "type": "string", "description": "主键(后端 Long;前端按字符串保证精度)" }, + "classId": { "type": "string", "description": "关联班级ID(后端 Long;前端按字符串保证精度)" }, + "studentName": { "type": "string", "description": "姓名" }, + "unitName": { "type": "string", "description": "参训单位" }, + "department": { "type": "string", "description": "部门" }, + "phone": { "type": "string", "description": "手机号" }, + "fileNumber": { "type": "string", "description": "档案编号" }, + "faceAuth": { "type": "integer", "description": "人脸认证(0-未认证,1-已认证)" }, + "requiredHours": { "type": "number", "description": "要求学时(小时)" }, + "completedHours": { "type": "number", "description": "已完成学时(小时)" }, + "studyStatus": { "type": "integer", "description": "学习状态:0-未学习,1-学习中,2-已学完,3-已完成" }, + "examStatus": { "type": "integer", "description": "考试状态:0-不考试,1-待考试,2-考试未通过,3-考试通过,4-未参加" }, + "entryTime": { "type": "string", "description": "入班时间" } + } + }, + "SingleResponseBoolean": { + "type": "object", + "properties": { + "success": { "type": "boolean" }, + "code": { "type": "string", "description": "响应码" }, + "message": { "type": "string", "description": "响应消息" }, + "errMessage": { "type": "string", "description": "错误响应消息" }, + "data": { "type": "boolean" } + } + } + } + } +} diff --git a/.api/safetyEval-classStudent-batchSave-post/type.d.ts b/.api/safetyEval-classStudent-batchSave-post/type.d.ts new file mode 100644 index 0000000..8b09e2a --- /dev/null +++ b/.api/safetyEval-classStudent-batchSave-post/type.d.ts @@ -0,0 +1,46 @@ +/** + * POST /safetyEval/classStudent/batchSave —— 批量新增班级学员 + * 契约见同目录 spec.json + */ + +/** 班级学员 DTO(请求体为数组) */ +export interface ClassStudentDTO { + /** 主键(后端 Long;前端按字符串保证精度) */ + id?: string; + /** 关联班级ID(后端 Long;前端按字符串保证精度) */ + classId?: string; + /** 姓名 */ + studentName?: string; + /** 参训单位 */ + unitName?: string; + /** 部门 */ + department?: string; + /** 手机号 */ + phone?: string; + /** 档案编号 */ + fileNumber?: string; + /** 人脸认证(0-未认证,1-已认证) */ + faceAuth?: 0 | 1; + /** 要求学时(小时) */ + requiredHours?: number; + /** 已完成学时(小时) */ + completedHours?: number; + /** 学习状态:0-未学习,1-学习中,2-已学完,3-已完成 */ + studyStatus?: 0 | 1 | 2 | 3; + /** 考试状态:0-不考试,1-待考试,2-考试未通过,3-考试通过,4-未参加 */ + examStatus?: 0 | 1 | 2 | 3 | 4; + /** 入班时间 */ + entryTime?: string; +} + +/** 请求体:ClassStudentDTO 数组 */ +export type ClassStudentBatchSaveBody = ClassStudentDTO[]; + +/** 响应体 */ +export interface SingleResponseBoolean { + success?: boolean; + code?: string; + message?: string; + errMessage?: string; + data?: boolean; +} diff --git a/.api/safetyEval-classStudent-delete-post/spec.json b/.api/safetyEval-classStudent-delete-post/spec.json new file mode 100644 index 0000000..26f74d7 --- /dev/null +++ b/.api/safetyEval-classStudent-delete-post/spec.json @@ -0,0 +1,57 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "删除班级学员", + "version": "1.0.0", + "description": "同步自 Apifox 项目(api-503642502),班级学员" + }, + "paths": { + "/safetyEval/classStudent/delete": { + "post": { + "summary": "删除班级学员", + "tags": ["班级学员"], + "requestBody": { + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ReqLong" } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/SingleResponseVoid" } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "ReqLong": { + "type": "object", + "properties": { + "data": { + "type": "string", + "description": "班级学员主键ID(后端 Long / Apifox 显示 int64;前端按字符串保证精度)" + } + }, + "required": ["data"] + }, + "SingleResponseVoid": { + "type": "object", + "properties": { + "success": { "type": "boolean", "description": "是否成功" }, + "code": { "type": "string", "description": "响应码" }, + "message": { "type": "string", "description": "响应消息" }, + "errMessage": { "type": "string", "description": "错误响应消息" }, + "data": { "type": "null", "description": "响应数据" } + } + } + } + } +} diff --git a/.api/safetyEval-classStudent-delete-post/type.d.ts b/.api/safetyEval-classStudent-delete-post/type.d.ts new file mode 100644 index 0000000..2f02b64 --- /dev/null +++ b/.api/safetyEval-classStudent-delete-post/type.d.ts @@ -0,0 +1,18 @@ +/** + * POST /safetyEval/classStudent/delete —— 删除班级学员 + * 契约见同目录 spec.json + */ + +/** 请求体:{ data: 班级学员主键ID }(后端 Long;前端按字符串传递保证精度) */ +export interface ClassStudentDeleteBody { + data: string; +} + +/** 响应体 */ +export interface SingleResponseVoid { + success?: boolean; + code?: string; + message?: string; + errMessage?: string; + data?: null; +} diff --git a/.api/safetyEval-org-personnel-page-get/spec.json b/.api/safetyEval-org-personnel-page-get/spec.json new file mode 100644 index 0000000..e7dc7a6 --- /dev/null +++ b/.api/safetyEval-org-personnel-page-get/spec.json @@ -0,0 +1,62 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "分页查询人员信息", + "version": "1.0.0", + "description": "同步自 Apifox 项目(api-483274085),人员信息管理;此处仅收录选择学员弹窗所需字段" + }, + "paths": { + "/safetyEval/org-personnel/page": { + "get": { + "summary": "分页查询人员信息", + "tags": ["人员信息管理"], + "parameters": [ + { "name": "current", "in": "query", "description": "当前页(默认1)", "required": false, "schema": { "type": "integer" } }, + { "name": "size", "in": "query", "description": "每页条数(默认10)", "required": false, "schema": { "type": "integer" } }, + { "name": "keyword", "in": "query", "description": "keyword", "required": false, "schema": { "type": "string" } }, + { "name": "orgId", "in": "query", "description": "单位id(后端 Long;前端按字符串传递保证精度)", "required": false, "schema": { "type": "string" } }, + { "name": "deptId", "in": "query", "description": "部门id(后端 Long;前端按字符串传递保证精度)", "required": false, "schema": { "type": "string" } }, + { "name": "userName", "in": "query", "description": "用户名称/姓名", "required": false, "schema": { "type": "string" } }, + { "name": "account", "in": "query", "description": "账号", "required": false, "schema": { "type": "string" } }, + { "name": "employmentStatusCode", "in": "query", "description": "就职状态编码(1在职2离职)", "required": false, "schema": { "type": "integer" } } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/PageResponseOrgPersonnelCO" } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "PageResponseOrgPersonnelCO": { + "type": "object", + "properties": { + "success": { "type": "boolean", "description": "是否成功" }, + "code": { "type": "string", "description": "响应码" }, + "message": { "type": "string", "description": "响应消息" }, + "errMessage": { "type": "string", "description": "错误响应消息" }, + "data": { "type": "array", "items": { "$ref": "#/components/schemas/OrgPersonnelCO" }, "description": "响应数据" }, + "total": { "type": "integer", "description": "总条数" } + } + }, + "OrgPersonnelCO": { + "type": "object", + "description": "人员信息CO(选择学员弹窗仅使用下列字段)", + "properties": { + "id": { "type": "string", "description": "主键ID(后端 Long;前端按字符串保证精度)" }, + "userName": { "type": "string", "description": "用户姓名" }, + "account": { "type": "string", "description": "账号(即手机号)" }, + "deptName": { "type": "string", "description": "部门名称(列表展示,非持久化)" }, + "orgName": { "type": "string", "description": "公司名称(Spec 未声明该字段,前端按存在处理,缺失显示 -)" } + } + } + } + } +} diff --git a/.api/safetyEval-org-personnel-page-get/type.d.ts b/.api/safetyEval-org-personnel-page-get/type.d.ts new file mode 100644 index 0000000..473ade2 --- /dev/null +++ b/.api/safetyEval-org-personnel-page-get/type.d.ts @@ -0,0 +1,40 @@ +/** + * GET /safetyEval/org-personnel/page —— 分页查询人员信息 + * 契约见同目录 spec.json(仅收录选择学员弹窗所需字段) + */ + +/** Query 参数(仅收录弹窗使用项) */ +export interface OrgPersonnelPageQuery { + /** 当前页(默认1) */ + current?: number; + /** 每页条数(默认10) */ + size?: number; + /** 用户名称/姓名 */ + userName?: string; + /** 账号 */ + account?: string; +} + +/** 人员信息 CO(弹窗使用字段) */ +export interface OrgPersonnelCO { + /** 主键ID(后端 Long;前端按字符串保证精度) */ + id?: string; + /** 用户姓名 */ + userName?: string; + /** 账号(即手机号) */ + account?: string; + /** 部门名称 */ + deptName?: string; + /** 公司名称(Spec 未声明,按存在处理) */ + orgName?: string; +} + +/** 响应体 */ +export interface PageResponseOrgPersonnelCO { + success?: boolean; + code?: string; + message?: string; + errMessage?: string; + data?: OrgPersonnelCO[]; + total?: number; +} diff --git a/src/api/courseware/index.js b/src/api/courseware/index.js index 8185714..9d48b39 100644 --- a/src/api/courseware/index.js +++ b/src/api/courseware/index.js @@ -114,3 +114,22 @@ export const classCourseRemove = declareRequest( "classCourseLoading", "Post > @/safetyEval/classCourse/delete", ); + +/** 分页查询人员信息(选择学员弹窗数据源,复用人员信息管理接口) */ +export const orgPersonnelPage = declareRequest( + "orgPersonnelLoading", + "Get > /safetyEval/org-personnel/page", + "orgPersonnelList: [] | res.data || [] & orgPersonnelTotal: 0 | res.total || 0", +); + +/** 批量新增班级学员(请求体为 ClassStudentDTO 数组) */ +export const classStudentBatchSave = declareRequest( + "classStudentConfirmLoading", + "Post > @/safetyEval/classStudent/batchSave", +); + +/** 删除班级学员(请求体:{ data: 班级学员主键id }) */ +export const classStudentRemove = declareRequest( + "classStudentLoading", + "Post > @/safetyEval/classStudent/delete", +); diff --git a/src/pages/Container/EduTraining/ClassManage/Add/StudentTab.js b/src/pages/Container/EduTraining/ClassManage/Add/StudentTab.js index 69552fd..41cdfa8 100644 --- a/src/pages/Container/EduTraining/ClassManage/Add/StudentTab.js +++ b/src/pages/Container/EduTraining/ClassManage/Add/StudentTab.js @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { Badge, Button, Form, Table, message } from "antd"; +import { Badge, Button, Form, Input, Modal, Table, message } from "antd"; import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper"; import SearchForm from "~/components/SearchForm"; import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction"; @@ -34,11 +34,13 @@ function ClassStudentTab(props) { const { classId } = props; const [searchForm] = Form.useForm(); const [query, setQuery] = useState({ current: 1, size: 10 }); + const [selectOpen, setSelectOpen] = useState(false); const { classStudentList: dataSource, classStudentTotal: total, classStudentLoading: loading, } = props.courseware || {}; + const tableSource = Array.isArray(dataSource) ? dataSource : []; const handleSearch = (params = query) => { props.classStudentPage({ ...params, classId }); @@ -110,7 +112,7 @@ function ClassStudentTab(props) { title: "操作", width: 160, fixed: "right", - render: () => ( + render: (_, record) => ( @@ -184,7 +200,7 @@ function ClassStudentTab(props) { @@ -192,7 +208,7 @@ function ClassStudentTab(props) { + + item.phone)} + onOk={async (rows) => { + // 批量新增班级学员 + const res = await props.classStudentBatchSave( + rows.map(({ userName, deptName, account }) => ({ + classId, + studentName: userName, + department: deptName, + phone: account, + })), + ); + if (res?.success === false) return; + message.success("添加成功"); + setSelectOpen(false); + handleSearch(); + }} + onCancel={() => setSelectOpen(false)} + /> ); } +/** 选择学员弹窗:数据源为人员信息列表 */ +function StudentSelectModal({ + open, + loading, + confirmLoading, + dataSource, + total, + requestPage, + selectedPhones, + onOk, + onCancel, +}) { + const [query, setQuery] = useState({ current: 1, size: 10 }); + const [keyword, setKeyword] = useState(""); + const [selectedRows, setSelectedRows] = useState([]); + + const handleSearch = (params) => { + setQuery(params); + requestPage({ + ...params, + employmentStatusCode: 1, + }); + }; + + useEffect(() => { + if (open) { + setKeyword(""); + setSelectedRows([]); + handleSearch({ current: 1, size: 10 }); + } + }, [open]); + + return ( + onOk(selectedRows)} + onCancel={onCancel} + > + setKeyword(e.target.value)} + onSearch={(value) => + handleSearch({ current: 1, size: query.size, userName: value }) + } + /> +
+ (query.current - 1) * query.size + index + 1, + }, + { + title: "公司名称", + dataIndex: "orgName", + ellipsis: true, + render: (v) => v || "-", + }, + { + title: "部门", + dataIndex: "deptName", + width: 140, + render: (v) => v || "-", + }, + { title: "学员姓名", dataIndex: "userName", width: 100 }, + { + title: "手机号", + dataIndex: "account", + width: 130, + render: (v) => v || "-", + }, + ]} + dataSource={Array.isArray(dataSource) ? dataSource : []} + rowSelection={{ + preserveSelectedRowKeys: true, + selectedRowKeys: selectedRows.map((r) => r.id), + getCheckboxProps: (record) => ({ + disabled: selectedPhones.includes(record.account), + }), + onChange: (_, rows) => setSelectedRows(rows), + }} + pagination={{ + total, + size: "small", + showTotal: (t) => `共 ${t} 条`, + current: Number(query.current) || 1, + pageSize: Number(query.size) || 10, + onChange: (page, pageSize) => + handleSearch({ + current: page, + size: pageSize, + userName: query.userName, + }), + }} + /> + + ); +} + export default Connect([NS_COURSEWARE], true)(ClassStudentTab); diff --git a/src/pages/Container/EduTraining/ClassManage/List/index.js b/src/pages/Container/EduTraining/ClassManage/List/index.js index 1edfce8..58e4f46 100644 --- a/src/pages/Container/EduTraining/ClassManage/List/index.js +++ b/src/pages/Container/EduTraining/ClassManage/List/index.js @@ -155,20 +155,7 @@ function ClassManagePage(props) { > 编辑 - - + {record.status === 2 && (