feat
parent
03332b3d05
commit
3b51963fa1
|
|
@ -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" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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": "响应数据" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 未声明该字段,前端按存在处理,缺失显示 -)" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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",
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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) => (
|
||||
<TableAction>
|
||||
<Button
|
||||
type="link"
|
||||
|
|
@ -123,7 +125,21 @@ function ClassStudentTab(props) {
|
|||
danger
|
||||
type="link"
|
||||
size="small"
|
||||
onClick={() => message.info("移除学员接口待接入")}
|
||||
onClick={() =>
|
||||
Modal.confirm({
|
||||
title: "确认移除该学员?",
|
||||
content: `确认将学员「${record.studentName || ""}」从本班移除吗?`,
|
||||
onOk: async () => {
|
||||
const res = await props.classStudentRemove({
|
||||
data: record.id,
|
||||
});
|
||||
if (res?.success !== false) {
|
||||
message.success("移除成功");
|
||||
handleSearch();
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
>
|
||||
从本班移除
|
||||
</Button>
|
||||
|
|
@ -184,7 +200,7 @@ function ClassStudentTab(props) {
|
|||
<Button
|
||||
type="primary"
|
||||
style={{ marginBottom: 12 }}
|
||||
onClick={() => message.info("选择学员功能待接入")}
|
||||
onClick={() => setSelectOpen(true)}
|
||||
>
|
||||
选择学员
|
||||
</Button>
|
||||
|
|
@ -192,7 +208,7 @@ function ClassStudentTab(props) {
|
|||
<Table
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
dataSource={Array.isArray(dataSource) ? dataSource : []}
|
||||
dataSource={tableSource}
|
||||
scroll={{ x: 1400 }}
|
||||
loading={loading}
|
||||
pagination={{
|
||||
|
|
@ -209,8 +225,146 @@ function ClassStudentTab(props) {
|
|||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<StudentSelectModal
|
||||
open={selectOpen}
|
||||
loading={props.courseware?.orgPersonnelLoading}
|
||||
confirmLoading={props.courseware?.classStudentConfirmLoading}
|
||||
dataSource={props.courseware?.orgPersonnelList}
|
||||
total={props.courseware?.orgPersonnelTotal}
|
||||
requestPage={props.orgPersonnelPage}
|
||||
selectedPhones={tableSource.map((item) => 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 (
|
||||
<Modal
|
||||
open={open}
|
||||
title="选择学员"
|
||||
width={860}
|
||||
confirmLoading={confirmLoading}
|
||||
destroyOnHidden
|
||||
okText={`确定${selectedRows.length ? `(已选 ${selectedRows.length})` : ""}`}
|
||||
okButtonProps={{ disabled: !selectedRows.length }}
|
||||
onOk={() => onOk(selectedRows)}
|
||||
onCancel={onCancel}
|
||||
>
|
||||
<Input.Search
|
||||
placeholder="请输入学员姓名"
|
||||
allowClear
|
||||
style={{ width: 280, marginBottom: 12 }}
|
||||
value={keyword}
|
||||
onChange={(e) => setKeyword(e.target.value)}
|
||||
onSearch={(value) =>
|
||||
handleSearch({ current: 1, size: query.size, userName: value })
|
||||
}
|
||||
/>
|
||||
<Table
|
||||
rowKey="id"
|
||||
size="small"
|
||||
loading={loading}
|
||||
columns={[
|
||||
{
|
||||
title: "序号",
|
||||
width: 60,
|
||||
render: (_, __, index) =>
|
||||
(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,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default Connect([NS_COURSEWARE], true)(ClassStudentTab);
|
||||
|
|
|
|||
|
|
@ -155,20 +155,7 @@ function ClassManagePage(props) {
|
|||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
onClick={() => message.info("学员列表功能待接入")}
|
||||
>
|
||||
学员列表
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
onClick={() => message.info("课程列表功能待接入")}
|
||||
>
|
||||
课程列表
|
||||
</Button>
|
||||
|
||||
{record.status === 2 && (
|
||||
<Button
|
||||
type="link"
|
||||
|
|
|
|||
Loading…
Reference in New Issue