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",
"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 [query, setQuery] = useState({ current: 1, size: 10 });
const [selectOpen, setSelectOpen] = useState(false);
// 本地暂存:接口未就绪,确认选择的课程先追加到表格展示
const [pendingCourses, setPendingCourses] = useState([]);
const {
classCourseList: dataSource,
classCourseTotal: total,
classCourseLoading: loading,
} = props.courseware || {};
const tableSource = Array.isArray(dataSource) ? dataSource : [];
// 本地暂存行仅在第一页合并展示,避免翻页重复
const mergedSource =
query.current === 1 ? [...pendingCourses, ...tableSource] : tableSource;
const handleSearch = (params = query) => {
props.classCoursePage({ ...params, classId });
@ -83,7 +78,7 @@ function ClassCourseTab(props) {
title: "操作",
width: 140,
fixed: "right",
render: () => (
render: (_, record) => (
<TableAction>
<Button
type="link"
@ -96,7 +91,23 @@ function ClassCourseTab(props) {
danger
type="link"
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>
@ -144,11 +155,11 @@ function ClassCourseTab(props) {
<Table
rowKey="id"
columns={columns}
dataSource={mergedSource}
dataSource={tableSource}
scroll={{ x: 1000 }}
loading={loading}
pagination={{
total: total + pendingCourses.length,
total,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (t) => `${t}`,
@ -165,25 +176,25 @@ function ClassCourseTab(props) {
<CourseSelectModal
open={selectOpen}
loading={props.courseware?.courseLoading}
confirmLoading={props.courseware?.classCourseConfirmLoading}
dataSource={props.courseware?.courseList}
total={props.courseware?.courseTotal}
requestPage={props.coursePage}
selectedIds={[
...tableSource.map((item) => item.courseId),
...pendingCourses.map((item) => item.courseId),
]}
onOk={(rows) => {
// 班级添加课程接口待接入:先本地暂存并填充表格,接入后改为提交请求 + handleSearch() 刷新
setPendingCourses((prev) => [
...prev,
...rows.map(({ id, courseName, classHourDuration }) => ({
id,
selectedIds={tableSource.map((item) => item.courseId)}
onOk={async (rows) => {
// 批量新增班级课程
const res = await props.classCourseBatchSave(
rows.map(({ id, courseName, classHourDuration }) => ({
classId,
courseId: id,
courseName,
requiredTotalHours: classHourDuration,
})),
]);
);
if (res?.success === false) return;
message.success("添加成功");
setSelectOpen(false);
handleSearch();
}}
onCancel={() => setSelectOpen(false)}
/>
@ -195,6 +206,7 @@ function ClassCourseTab(props) {
function CourseSelectModal({
open,
loading,
confirmLoading,
dataSource,
total,
requestPage,
@ -225,6 +237,7 @@ function CourseSelectModal({
title="选择课程"
width={860}
loading={loading}
confirmLoading={confirmLoading}
destroyOnHidden
okText={`确定${selectedRows.length ? `(已选 ${selectedRows.length}` : ""}`}
okButtonProps={{ disabled: !selectedRows.length }}