dev-tmp1
tangjie 2026-08-21 18:18:30 +08:00
parent 8dfff0c4a1
commit 03332b3d05
6 changed files with 217 additions and 20 deletions

View File

@ -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" }
}
}
}
}
}

View File

@ -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;
}

View File

@ -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": "响应数据" }
}
}
}
}
}

View File

@ -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;
}

View File

@ -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",
);

View File

@ -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 }}