feat
parent
8dfff0c4a1
commit
03332b3d05
|
|
@ -0,0 +1,63 @@
|
||||||
|
{
|
||||||
|
"openapi": "3.0.1",
|
||||||
|
"info": {
|
||||||
|
"title": "批量新增班级课程",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "同步自 Apifox 项目(api-505127646),班级课程"
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"/safetyEval/classCourse/batchSave": {
|
||||||
|
"post": {
|
||||||
|
"summary": "批量新增班级课程",
|
||||||
|
"description": "批量新增班级课程",
|
||||||
|
"tags": ["班级课程"],
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "$ref": "#/components/schemas/ClassCourseDTO" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": { "$ref": "#/components/schemas/SingleResponseBoolean" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"ClassCourseDTO": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "string", "description": "主键(后端 Long;前端按字符串保证精度)" },
|
||||||
|
"classId": { "type": "string", "description": "所属班级ID(后端 Long;前端按字符串保证精度)" },
|
||||||
|
"courseName": { "type": "string", "description": "课程名称" },
|
||||||
|
"courseId": { "type": "string", "description": "课程id(后端 Long;前端按字符串保证精度)" },
|
||||||
|
"requiredTotalHours": { "type": "number", "description": "要求完成总学时(小时)" },
|
||||||
|
"videoDuration": { "type": "number", "description": "视频累计时长(小时)" },
|
||||||
|
"coursewareId": { "type": "array", "items": { "type": "string" }, "description": "课件id集合" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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,34 @@
|
||||||
|
/**
|
||||||
|
* POST /safetyEval/classCourse/batchSave —— 批量新增班级课程
|
||||||
|
* 契约见同目录 spec.json
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** 班级课程 DTO(请求体为数组) */
|
||||||
|
export interface ClassCourseDTO {
|
||||||
|
/** 主键(后端 Long;前端按字符串保证精度) */
|
||||||
|
id?: string;
|
||||||
|
/** 所属班级ID(后端 Long;前端按字符串保证精度) */
|
||||||
|
classId?: string;
|
||||||
|
/** 课程名称 */
|
||||||
|
courseName?: string;
|
||||||
|
/** 课程id(后端 Long;前端按字符串保证精度) */
|
||||||
|
courseId?: string;
|
||||||
|
/** 要求完成总学时(小时) */
|
||||||
|
requiredTotalHours?: number;
|
||||||
|
/** 视频累计时长(小时) */
|
||||||
|
videoDuration?: number;
|
||||||
|
/** 课件id集合 */
|
||||||
|
coursewareId?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 请求体:ClassCourseDTO 数组 */
|
||||||
|
export type ClassCourseBatchSaveBody = ClassCourseDTO[];
|
||||||
|
|
||||||
|
/** 响应体 */
|
||||||
|
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-503642492),班级课程"
|
||||||
|
},
|
||||||
|
"paths": {
|
||||||
|
"/safetyEval/classCourse/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/classCourse/delete —— 删除班级课程
|
||||||
|
* 契约见同目录 spec.json
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** 请求体:{ data: 班级课程主键ID }(后端 Long;前端按字符串传递保证精度) */
|
||||||
|
export interface ClassCourseDeleteBody {
|
||||||
|
data: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 响应体 */
|
||||||
|
export interface SingleResponseVoid {
|
||||||
|
success?: boolean;
|
||||||
|
code?: string;
|
||||||
|
message?: string;
|
||||||
|
errMessage?: string;
|
||||||
|
data?: null;
|
||||||
|
}
|
||||||
|
|
@ -102,3 +102,15 @@ export const classCoursePage = declareRequest(
|
||||||
"Get > /safetyEval/classCourse/page",
|
"Get > /safetyEval/classCourse/page",
|
||||||
"classCourseList: [] | res.data || [] & classCourseTotal: 0 | res.total || 0",
|
"classCourseList: [] | res.data || [] & classCourseTotal: 0 | res.total || 0",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/** 批量新增班级课程(请求体为 ClassCourseDTO 数组) */
|
||||||
|
export const classCourseBatchSave = declareRequest(
|
||||||
|
"classCourseConfirmLoading",
|
||||||
|
"Post > @/safetyEval/classCourse/batchSave",
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 删除班级课程(请求体:{ data: 班级课程主键id }) */
|
||||||
|
export const classCourseRemove = declareRequest(
|
||||||
|
"classCourseLoading",
|
||||||
|
"Post > @/safetyEval/classCourse/delete",
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -20,17 +20,12 @@ function ClassCourseTab(props) {
|
||||||
const [searchForm] = Form.useForm();
|
const [searchForm] = Form.useForm();
|
||||||
const [query, setQuery] = useState({ current: 1, size: 10 });
|
const [query, setQuery] = useState({ current: 1, size: 10 });
|
||||||
const [selectOpen, setSelectOpen] = useState(false);
|
const [selectOpen, setSelectOpen] = useState(false);
|
||||||
// 本地暂存:接口未就绪,确认选择的课程先追加到表格展示
|
|
||||||
const [pendingCourses, setPendingCourses] = useState([]);
|
|
||||||
const {
|
const {
|
||||||
classCourseList: dataSource,
|
classCourseList: dataSource,
|
||||||
classCourseTotal: total,
|
classCourseTotal: total,
|
||||||
classCourseLoading: loading,
|
classCourseLoading: loading,
|
||||||
} = props.courseware || {};
|
} = props.courseware || {};
|
||||||
const tableSource = Array.isArray(dataSource) ? dataSource : [];
|
const tableSource = Array.isArray(dataSource) ? dataSource : [];
|
||||||
// 本地暂存行仅在第一页合并展示,避免翻页重复
|
|
||||||
const mergedSource =
|
|
||||||
query.current === 1 ? [...pendingCourses, ...tableSource] : tableSource;
|
|
||||||
|
|
||||||
const handleSearch = (params = query) => {
|
const handleSearch = (params = query) => {
|
||||||
props.classCoursePage({ ...params, classId });
|
props.classCoursePage({ ...params, classId });
|
||||||
|
|
@ -83,7 +78,7 @@ function ClassCourseTab(props) {
|
||||||
title: "操作",
|
title: "操作",
|
||||||
width: 140,
|
width: 140,
|
||||||
fixed: "right",
|
fixed: "right",
|
||||||
render: () => (
|
render: (_, record) => (
|
||||||
<TableAction>
|
<TableAction>
|
||||||
<Button
|
<Button
|
||||||
type="link"
|
type="link"
|
||||||
|
|
@ -96,7 +91,23 @@ function ClassCourseTab(props) {
|
||||||
danger
|
danger
|
||||||
type="link"
|
type="link"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => message.info("删除课程接口待接入")}
|
onClick={() =>
|
||||||
|
Modal.confirm({
|
||||||
|
title: "确认删除该课程?",
|
||||||
|
content: `删除后不可恢复,确认删除课程「${
|
||||||
|
record.courseName || ""
|
||||||
|
}」吗?`,
|
||||||
|
onOk: async () => {
|
||||||
|
const res = await props.classCourseRemove({
|
||||||
|
data: record.id,
|
||||||
|
});
|
||||||
|
if (res?.success !== false) {
|
||||||
|
message.success("删除成功");
|
||||||
|
handleSearch();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
>
|
>
|
||||||
删除
|
删除
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -144,11 +155,11 @@ function ClassCourseTab(props) {
|
||||||
<Table
|
<Table
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
columns={columns}
|
columns={columns}
|
||||||
dataSource={mergedSource}
|
dataSource={tableSource}
|
||||||
scroll={{ x: 1000 }}
|
scroll={{ x: 1000 }}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
pagination={{
|
pagination={{
|
||||||
total: total + pendingCourses.length,
|
total,
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
showQuickJumper: true,
|
showQuickJumper: true,
|
||||||
showTotal: (t) => `共 ${t} 条`,
|
showTotal: (t) => `共 ${t} 条`,
|
||||||
|
|
@ -165,25 +176,25 @@ function ClassCourseTab(props) {
|
||||||
<CourseSelectModal
|
<CourseSelectModal
|
||||||
open={selectOpen}
|
open={selectOpen}
|
||||||
loading={props.courseware?.courseLoading}
|
loading={props.courseware?.courseLoading}
|
||||||
|
confirmLoading={props.courseware?.classCourseConfirmLoading}
|
||||||
dataSource={props.courseware?.courseList}
|
dataSource={props.courseware?.courseList}
|
||||||
total={props.courseware?.courseTotal}
|
total={props.courseware?.courseTotal}
|
||||||
requestPage={props.coursePage}
|
requestPage={props.coursePage}
|
||||||
selectedIds={[
|
selectedIds={tableSource.map((item) => item.courseId)}
|
||||||
...tableSource.map((item) => item.courseId),
|
onOk={async (rows) => {
|
||||||
...pendingCourses.map((item) => item.courseId),
|
// 批量新增班级课程
|
||||||
]}
|
const res = await props.classCourseBatchSave(
|
||||||
onOk={(rows) => {
|
rows.map(({ id, courseName, classHourDuration }) => ({
|
||||||
// 班级添加课程接口待接入:先本地暂存并填充表格,接入后改为提交请求 + handleSearch() 刷新
|
classId,
|
||||||
setPendingCourses((prev) => [
|
|
||||||
...prev,
|
|
||||||
...rows.map(({ id, courseName, classHourDuration }) => ({
|
|
||||||
id,
|
|
||||||
courseId: id,
|
courseId: id,
|
||||||
courseName,
|
courseName,
|
||||||
requiredTotalHours: classHourDuration,
|
requiredTotalHours: classHourDuration,
|
||||||
})),
|
})),
|
||||||
]);
|
);
|
||||||
|
if (res?.success === false) return;
|
||||||
|
message.success("添加成功");
|
||||||
setSelectOpen(false);
|
setSelectOpen(false);
|
||||||
|
handleSearch();
|
||||||
}}
|
}}
|
||||||
onCancel={() => setSelectOpen(false)}
|
onCancel={() => setSelectOpen(false)}
|
||||||
/>
|
/>
|
||||||
|
|
@ -195,6 +206,7 @@ function ClassCourseTab(props) {
|
||||||
function CourseSelectModal({
|
function CourseSelectModal({
|
||||||
open,
|
open,
|
||||||
loading,
|
loading,
|
||||||
|
confirmLoading,
|
||||||
dataSource,
|
dataSource,
|
||||||
total,
|
total,
|
||||||
requestPage,
|
requestPage,
|
||||||
|
|
@ -225,6 +237,7 @@ function CourseSelectModal({
|
||||||
title="选择课程"
|
title="选择课程"
|
||||||
width={860}
|
width={860}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
|
confirmLoading={confirmLoading}
|
||||||
destroyOnHidden
|
destroyOnHidden
|
||||||
okText={`确定${selectedRows.length ? `(已选 ${selectedRows.length})` : ""}`}
|
okText={`确定${selectedRows.length ? `(已选 ${selectedRows.length})` : ""}`}
|
||||||
okButtonProps={{ disabled: !selectedRows.length }}
|
okButtonProps={{ disabled: !selectedRows.length }}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue