feat: admin course list
This commit is contained in:
parent
9ffd6abac7
commit
aab88ebc0f
|
@ -9,9 +9,12 @@ export interface IGetVodeResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ICourseBasic {
|
export interface ICourseBasic {
|
||||||
course_title: string;
|
course_id?: string;
|
||||||
course_cover_url: string;
|
course_title?: string;
|
||||||
course_summary: string;
|
course_cover_url?: string;
|
||||||
|
course_summary?: string;
|
||||||
|
course_createtime?: string;
|
||||||
|
valid?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ICreateCourseRequest extends ICourseBasic {
|
export interface ICreateCourseRequest extends ICourseBasic {
|
||||||
|
@ -28,3 +31,19 @@ export interface IXcode {
|
||||||
expiretime: string;
|
expiretime: string;
|
||||||
code: string;
|
code: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ISelectCourse {
|
||||||
|
all?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IChapter {
|
||||||
|
chapter_course_id?: string;
|
||||||
|
chapter_file_id?: string;
|
||||||
|
chapter_id?: string;
|
||||||
|
chapter_level?: string;
|
||||||
|
chapter_title?: string;
|
||||||
|
media_cover_url?: string;
|
||||||
|
media_time?: string;
|
||||||
|
media_url?: string;
|
||||||
|
order?: number;
|
||||||
|
}
|
||||||
|
|
|
@ -2,8 +2,11 @@ import R from "./request";
|
||||||
import P from "./process";
|
import P from "./process";
|
||||||
import {
|
import {
|
||||||
IAdminLogin,
|
IAdminLogin,
|
||||||
|
IChapter,
|
||||||
|
ICourseBasic,
|
||||||
ICreateCourseRequest,
|
ICreateCourseRequest,
|
||||||
IgetVodRequest,
|
IgetVodRequest,
|
||||||
|
ISelectCourse,
|
||||||
IXcode,
|
IXcode,
|
||||||
} from "./dto";
|
} from "./dto";
|
||||||
|
|
||||||
|
@ -23,3 +26,27 @@ export const createXCode = (codeList: IXcode[]) =>
|
||||||
R.post("/api/xcode/admin/create", codeList);
|
R.post("/api/xcode/admin/create", codeList);
|
||||||
|
|
||||||
export const selectXCodeList = () => R.post("/api/xcode/admin/select/all");
|
export const selectXCodeList = () => R.post("/api/xcode/admin/select/all");
|
||||||
|
|
||||||
|
export const selectCourseList = (p: ISelectCourse) =>
|
||||||
|
R.post("/api/course/select/all", { ...p });
|
||||||
|
|
||||||
|
export const updateCourse = (course: ICourseBasic) =>
|
||||||
|
R.post("/api/course/update", course);
|
||||||
|
|
||||||
|
export const selectChapterList = ({
|
||||||
|
chapter_course_id,
|
||||||
|
}: {
|
||||||
|
chapter_course_id: string;
|
||||||
|
}) => R.post("/api/course/chapter/select", { chapter_course_id });
|
||||||
|
|
||||||
|
export const updateChapter = (chapter: any) =>
|
||||||
|
R.post("/api/course/chapter/update", chapter);
|
||||||
|
|
||||||
|
export const removeCourse = (course: ICourseBasic) =>
|
||||||
|
R.post("/api/course/remove", course);
|
||||||
|
|
||||||
|
export const createChapter = (chapterList: IChapter[]) =>
|
||||||
|
R.post("/api/course/chapter/create", chapterList);
|
||||||
|
|
||||||
|
export const removeChapter = (chapter: IChapter) =>
|
||||||
|
R.post("/api/course/chapter/remove", chapter);
|
||||||
|
|
|
@ -31,7 +31,7 @@ instance.interceptors.response.use(
|
||||||
break;
|
break;
|
||||||
case 40000:
|
case 40000:
|
||||||
message.error(msg);
|
message.error(msg);
|
||||||
window.location.href = "/";
|
// window.location.href = "/";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// TODO ...
|
// TODO ...
|
||||||
|
|
|
@ -22,6 +22,10 @@ const sideMenus: MenuProps["items"] = [
|
||||||
key: "create",
|
key: "create",
|
||||||
label: "创建",
|
label: "创建",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "list",
|
||||||
|
label: "课程列表",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "library",
|
key: "library",
|
||||||
label: "视频库",
|
label: "视频库",
|
||||||
|
|
|
@ -28,6 +28,11 @@ export const sideMenuRoutes: IRoute[] = [
|
||||||
element: lazy(() => import("../view/Course/Create")),
|
element: lazy(() => import("../view/Course/Create")),
|
||||||
name: "创建课程",
|
name: "创建课程",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/course/list",
|
||||||
|
element: lazy(() => import("../view/Course/List")),
|
||||||
|
name: "课程列表",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/course/library",
|
path: "/course/library",
|
||||||
element: lazy(() => import("../view/Course/Library")),
|
element: lazy(() => import("../view/Course/Library")),
|
||||||
|
|
|
@ -7,11 +7,8 @@ import {
|
||||||
Space,
|
Space,
|
||||||
Table,
|
Table,
|
||||||
Input,
|
Input,
|
||||||
Segmented,
|
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Image,
|
|
||||||
Typography,
|
Typography,
|
||||||
Modal,
|
|
||||||
Tag,
|
Tag,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
@ -65,7 +62,7 @@ const Library = () => {
|
||||||
key: "key",
|
key: "key",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "m3u8 大小",
|
title: "hls",
|
||||||
dataIndex: "m3u8Size",
|
dataIndex: "m3u8Size",
|
||||||
key: "m3u8Size",
|
key: "m3u8Size",
|
||||||
},
|
},
|
||||||
|
@ -86,7 +83,7 @@ const Library = () => {
|
||||||
key: "m3u8",
|
key: "m3u8",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "视频封面图",
|
title: "封面图",
|
||||||
dataIndex: "cover",
|
dataIndex: "cover",
|
||||||
key: "cover",
|
key: "cover",
|
||||||
},
|
},
|
||||||
|
|
3
apps/admin/src/view/Course/List/index.less
Normal file
3
apps/admin/src/view/Course/List/index.less
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.list {
|
||||||
|
padding: 24px 0;
|
||||||
|
}
|
499
apps/admin/src/view/Course/List/index.tsx
Normal file
499
apps/admin/src/view/Course/List/index.tsx
Normal file
|
@ -0,0 +1,499 @@
|
||||||
|
import {
|
||||||
|
DeleteOutlined,
|
||||||
|
EditFilled,
|
||||||
|
EditOutlined,
|
||||||
|
FieldTimeOutlined,
|
||||||
|
FileAddOutlined,
|
||||||
|
InfoCircleOutlined,
|
||||||
|
} from "@ant-design/icons";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
DatePicker,
|
||||||
|
Image,
|
||||||
|
Input,
|
||||||
|
Popconfirm,
|
||||||
|
Space,
|
||||||
|
Switch,
|
||||||
|
Table,
|
||||||
|
Modal,
|
||||||
|
Tooltip,
|
||||||
|
Form,
|
||||||
|
} from "antd";
|
||||||
|
import dayjs, { Dayjs } from "dayjs";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import {
|
||||||
|
removeCourse,
|
||||||
|
selectChapterList,
|
||||||
|
selectCourseList,
|
||||||
|
updateChapter,
|
||||||
|
updateCourse,
|
||||||
|
createChapter,
|
||||||
|
removeChapter,
|
||||||
|
} from "../../../api";
|
||||||
|
import { IChapter } from "../../../api/dto";
|
||||||
|
import { useMount } from "../../../hooks";
|
||||||
|
import "./index.less";
|
||||||
|
|
||||||
|
interface IEditItem {
|
||||||
|
key: string;
|
||||||
|
value: string | boolean | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultEditItem = { key: "", value: "" };
|
||||||
|
|
||||||
|
export default function List() {
|
||||||
|
const [courseList, setCourseList] = useState<any>([]);
|
||||||
|
const [chapterList, setChapterList] = useState([]);
|
||||||
|
const [editChapterItem, setEditChapterItem] =
|
||||||
|
useState<IEditItem>(defaultEditItem);
|
||||||
|
const [editCourseItem, setEditCourseItem] =
|
||||||
|
useState<IEditItem>(defaultEditItem);
|
||||||
|
const [addChapterForm] = Form.useForm();
|
||||||
|
|
||||||
|
const onConfirmEditCourseItem = (record: any) => {
|
||||||
|
const { key, value } = editCourseItem;
|
||||||
|
updateCourse({ ...record, [key]: value }).then((res: any) => {
|
||||||
|
if (res?.code === 10000) {
|
||||||
|
renderCourseTable();
|
||||||
|
setEditCourseItem(defaultEditItem);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onConfirmEditChapterItem = (record: any) => {
|
||||||
|
const { key, value } = editChapterItem;
|
||||||
|
updateChapter({ ...record, [key]: value }).then((res: any) => {
|
||||||
|
if (res?.code === 10000) {
|
||||||
|
const { chapter_course_id } = record;
|
||||||
|
renderChapterList(chapter_course_id);
|
||||||
|
setEditChapterItem(defaultEditItem);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onConfirmDeleteCourseItem = (record: any) => {
|
||||||
|
const { course_id } = record;
|
||||||
|
removeCourse({ course_id }).then((res: any) => {
|
||||||
|
if (res?.code === 10000) renderCourseTable();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAddChapter = (record: any) => {
|
||||||
|
const { course_id: chapter_course_id } = record;
|
||||||
|
Modal.info({
|
||||||
|
title: "添加章节",
|
||||||
|
icon: <InfoCircleOutlined />,
|
||||||
|
content: (
|
||||||
|
<Form form={addChapterForm}>
|
||||||
|
<Form.Item name="chapterList">
|
||||||
|
<Input.TextArea
|
||||||
|
placeholder="级别 | 章节名 | 文件FileID"
|
||||||
|
autoSize={{ minRows: 12, maxRows: 20 }}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
),
|
||||||
|
onOk: async () => {
|
||||||
|
const origin = addChapterForm.getFieldValue("chapterList");
|
||||||
|
const chapterList = origin
|
||||||
|
.split("\n")
|
||||||
|
.filter((i: string) => i.replace(/\s/, "").length > 0)
|
||||||
|
.map((row: string) => {
|
||||||
|
const [chapter_level, chapter_title, chapter_file_id] =
|
||||||
|
row.split("|");
|
||||||
|
return !chapter_file_id
|
||||||
|
? { chapter_level, chapter_title, chapter_course_id }
|
||||||
|
: {
|
||||||
|
chapter_level,
|
||||||
|
chapter_title,
|
||||||
|
chapter_file_id,
|
||||||
|
chapter_course_id,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
createChapter(chapterList).then((res: any) => {
|
||||||
|
if (res?.code === 10000) renderChapterList(chapter_course_id);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
okText: "确认",
|
||||||
|
cancelText: "取消",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onConfirmDeleteChapterItem = (record: any) => {
|
||||||
|
const { chapter_id, chapter_course_id } = record;
|
||||||
|
removeChapter({ chapter_id }).then((res: any) => {
|
||||||
|
if (res?.code === 10000) renderChapterList(chapter_course_id);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: "课程",
|
||||||
|
dataIndex: "course_title",
|
||||||
|
key: "course_title",
|
||||||
|
render: (_: any, record: any) => {
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
<span>{record.course_title}</span>
|
||||||
|
<Popconfirm
|
||||||
|
placement="top"
|
||||||
|
title="修改课程标题"
|
||||||
|
description={
|
||||||
|
<Input
|
||||||
|
defaultValue={record.course_title}
|
||||||
|
onChange={(e: any) =>
|
||||||
|
setEditCourseItem({
|
||||||
|
key: "course_title",
|
||||||
|
value: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onConfirm={() => onConfirmEditCourseItem(record)}
|
||||||
|
okText="确定"
|
||||||
|
cancelText="取消"
|
||||||
|
>
|
||||||
|
<Button type="text" icon={<EditFilled />} />
|
||||||
|
</Popconfirm>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "摘要",
|
||||||
|
dataIndex: "course_summary",
|
||||||
|
key: "course_summary",
|
||||||
|
render: (_: any, record: any) => {
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
<span>{record.course_summary}</span>
|
||||||
|
<Popconfirm
|
||||||
|
placement="top"
|
||||||
|
title="修改摘要"
|
||||||
|
description={
|
||||||
|
<Input
|
||||||
|
defaultValue={record.course_summary}
|
||||||
|
onChange={(e: any) =>
|
||||||
|
setEditCourseItem({
|
||||||
|
key: "course_summary",
|
||||||
|
value: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onConfirm={() => onConfirmEditCourseItem(record)}
|
||||||
|
okText="确定"
|
||||||
|
cancelText="取消"
|
||||||
|
>
|
||||||
|
<Button type="text" icon={<EditFilled />} />
|
||||||
|
</Popconfirm>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "创建时间",
|
||||||
|
dataIndex: "course_createtime",
|
||||||
|
key: "course_createtime",
|
||||||
|
render: (_: any, record: any) => {
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
<span>
|
||||||
|
{dayjs(+record.course_createtime).format("YYYY-MM-DD HH:mm:ss")}
|
||||||
|
</span>
|
||||||
|
<Popconfirm
|
||||||
|
placement="top"
|
||||||
|
title="时间"
|
||||||
|
description={
|
||||||
|
<DatePicker
|
||||||
|
onChange={(date: any) => {
|
||||||
|
setEditCourseItem({
|
||||||
|
key: "course_createtime",
|
||||||
|
value: "" + new Date(date).getTime(),
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
showTime={{ defaultValue: dayjs("00:00:00", "HH:mm:ss") }}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onConfirm={() => onConfirmEditCourseItem(record)}
|
||||||
|
okText="确定"
|
||||||
|
cancelText="取消"
|
||||||
|
>
|
||||||
|
<Button type="text" icon={<FieldTimeOutlined />} />
|
||||||
|
</Popconfirm>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "封面",
|
||||||
|
dataIndex: "course_cover_url",
|
||||||
|
key: "course_cover_url",
|
||||||
|
render: (_: any, record: any) => {
|
||||||
|
return <Image width={120} src={record.course_cover_url} />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "可见",
|
||||||
|
dataIndex: "visible",
|
||||||
|
key: "visible",
|
||||||
|
render: (_: any, record: any) => {
|
||||||
|
return (
|
||||||
|
<Switch
|
||||||
|
checkedChildren="开启"
|
||||||
|
unCheckedChildren="关闭"
|
||||||
|
defaultChecked={record.valid}
|
||||||
|
onChange={(value: boolean) =>
|
||||||
|
updateCourse({ ...record, valid: value }).then((res: any) => {
|
||||||
|
if (res?.code === 10000) {
|
||||||
|
renderCourseTable();
|
||||||
|
setEditCourseItem(defaultEditItem);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "操作",
|
||||||
|
dataIndex: "operation",
|
||||||
|
key: "operation",
|
||||||
|
render: (_: any, record: any) => {
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
<Popconfirm
|
||||||
|
placement="top"
|
||||||
|
title="确定删除课程嘛"
|
||||||
|
onConfirm={() => onConfirmDeleteCourseItem(record)}
|
||||||
|
okText="确定"
|
||||||
|
cancelText="取消"
|
||||||
|
>
|
||||||
|
<Button type="dashed" danger icon={<DeleteOutlined />}></Button>
|
||||||
|
</Popconfirm>
|
||||||
|
<Tooltip title="添加章节">
|
||||||
|
<Button
|
||||||
|
type="default"
|
||||||
|
onClick={() => onAddChapter(record)}
|
||||||
|
icon={<FileAddOutlined />}
|
||||||
|
></Button>
|
||||||
|
</Tooltip>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
useEffect(() => {}, [courseList]);
|
||||||
|
|
||||||
|
const renderCourseTable = () => {
|
||||||
|
selectCourseList({ all: true }).then((res) => {
|
||||||
|
const { data } = res;
|
||||||
|
setCourseList(data.map((i: any) => ({ ...i, key: i.course_id })));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useMount(() => {
|
||||||
|
renderCourseTable();
|
||||||
|
});
|
||||||
|
|
||||||
|
const expandedRowRender = () => {
|
||||||
|
const col: any = [
|
||||||
|
{
|
||||||
|
title: "级别",
|
||||||
|
dataIndex: "chapter_level",
|
||||||
|
key: "chapter_level",
|
||||||
|
render: (_: any, record: any) => {
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
<span>{record.chapter_level}</span>
|
||||||
|
<Popconfirm
|
||||||
|
placement="top"
|
||||||
|
title="修改级别"
|
||||||
|
description={
|
||||||
|
<Input
|
||||||
|
defaultValue={record.chapter_level}
|
||||||
|
onChange={(e: any) =>
|
||||||
|
setEditChapterItem({
|
||||||
|
key: "chapter_level",
|
||||||
|
value: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onConfirm={() => onConfirmEditChapterItem(record)}
|
||||||
|
okText="确定"
|
||||||
|
cancelText="取消"
|
||||||
|
>
|
||||||
|
<Button type="text" icon={<EditOutlined />} />
|
||||||
|
</Popconfirm>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: "章节",
|
||||||
|
dataIndex: "chapter_title",
|
||||||
|
key: "chapter_title",
|
||||||
|
render: (_: any, record: any) => {
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
<span>{record.chapter_title}</span>
|
||||||
|
<Popconfirm
|
||||||
|
placement="top"
|
||||||
|
title="修改章节标题名"
|
||||||
|
description={
|
||||||
|
<Input
|
||||||
|
defaultValue={record.chapter_title}
|
||||||
|
onChange={(e: any) =>
|
||||||
|
setEditChapterItem({
|
||||||
|
key: "chapter_title",
|
||||||
|
value: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onConfirm={() => onConfirmEditChapterItem(record)}
|
||||||
|
okText="确定"
|
||||||
|
cancelText="取消"
|
||||||
|
>
|
||||||
|
<Button type="text" icon={<EditOutlined />} />
|
||||||
|
</Popconfirm>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "FileID",
|
||||||
|
dataIndex: "chapter_file_id",
|
||||||
|
key: "chapter_file_id",
|
||||||
|
render: (_: any, record: any) => {
|
||||||
|
return +record.chapter_level === 2 ? (
|
||||||
|
<Space>
|
||||||
|
<span>{record.chapter_file_id}</span>
|
||||||
|
<Popconfirm
|
||||||
|
placement="top"
|
||||||
|
title="修改FileID"
|
||||||
|
description={
|
||||||
|
<Input
|
||||||
|
defaultValue={record.chapter_file_id}
|
||||||
|
onChange={(e: any) =>
|
||||||
|
setEditChapterItem({
|
||||||
|
key: "chapter_file_id",
|
||||||
|
value: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onConfirm={() => onConfirmEditChapterItem(record)}
|
||||||
|
okText="确定"
|
||||||
|
cancelText="取消"
|
||||||
|
>
|
||||||
|
<Button type="text" icon={<EditOutlined />} />
|
||||||
|
</Popconfirm>
|
||||||
|
</Space>
|
||||||
|
) : (
|
||||||
|
"-"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "顺序",
|
||||||
|
dataIndex: "order",
|
||||||
|
key: "order",
|
||||||
|
render: (_: any, record: any) => {
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
<span>{record.order}</span>
|
||||||
|
<Popconfirm
|
||||||
|
placement="top"
|
||||||
|
title="修改顺序"
|
||||||
|
description={
|
||||||
|
<Input
|
||||||
|
defaultValue={record.order}
|
||||||
|
onChange={(e: any) =>
|
||||||
|
setEditChapterItem({
|
||||||
|
key: "order",
|
||||||
|
value: +e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onConfirm={() => onConfirmEditChapterItem(record)}
|
||||||
|
okText="确定"
|
||||||
|
cancelText="取消"
|
||||||
|
>
|
||||||
|
<Button type="text" icon={<EditOutlined />} />
|
||||||
|
</Popconfirm>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "时长",
|
||||||
|
dataIndex: "media_time",
|
||||||
|
key: "media_time",
|
||||||
|
render: (_: any, record: any) => {
|
||||||
|
return +record.chapter_level === 2 ? (
|
||||||
|
<Space>
|
||||||
|
<span>{record.media_time}</span>
|
||||||
|
</Space>
|
||||||
|
) : (
|
||||||
|
"-"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "操作",
|
||||||
|
dataIndex: "operation_chapter",
|
||||||
|
key: "operation_chapter",
|
||||||
|
render: (_: any, record: any) => {
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
<Popconfirm
|
||||||
|
placement="top"
|
||||||
|
title="确定删除章节嘛"
|
||||||
|
onConfirm={() => onConfirmDeleteChapterItem(record)}
|
||||||
|
okText="确定"
|
||||||
|
cancelText="取消"
|
||||||
|
>
|
||||||
|
<Button type="dashed" danger icon={<DeleteOutlined />}></Button>
|
||||||
|
</Popconfirm>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return <Table columns={col} dataSource={chapterList} pagination={false} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderChapterList = (chapter_course_id: string) => {
|
||||||
|
selectChapterList({ chapter_course_id }).then((res: any) => {
|
||||||
|
const { data = [] } = res;
|
||||||
|
setChapterList(data.map((i: any) => ({ ...i, key: i.chapter_id })));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onExpand = (expand: boolean, record: any) => {
|
||||||
|
if (expand) {
|
||||||
|
const { course_id: chapter_course_id } = record;
|
||||||
|
renderChapterList(chapter_course_id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="list">
|
||||||
|
<Card>
|
||||||
|
<Table
|
||||||
|
dataSource={courseList}
|
||||||
|
columns={columns}
|
||||||
|
expandable={{ expandedRowRender }}
|
||||||
|
onExpand={onExpand}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -3,6 +3,8 @@ import { Context } from '@midwayjs/koa';
|
||||||
import { BizCode } from '../biz/code';
|
import { BizCode } from '../biz/code';
|
||||||
import { WEB } from '../config/base.config';
|
import { WEB } from '../config/base.config';
|
||||||
import { CourseCreateDTO } from '../dto/course.dto';
|
import { CourseCreateDTO } from '../dto/course.dto';
|
||||||
|
import { Chapter } from '../entity/chapter.entity';
|
||||||
|
import { Course } from '../entity/course.entity';
|
||||||
import { ChapterService } from '../service/chapter.service';
|
import { ChapterService } from '../service/chapter.service';
|
||||||
import { CourseService } from '../service/course.service';
|
import { CourseService } from '../service/course.service';
|
||||||
import { GuideService } from '../service/guide.service';
|
import { GuideService } from '../service/guide.service';
|
||||||
|
@ -52,8 +54,9 @@ export class CourseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('/select/all')
|
@Post('/select/all')
|
||||||
async selectAll() {
|
async selectAll(@Body() params) {
|
||||||
const courseList = await this.courseService.selectAll();
|
const { all = false } = params;
|
||||||
|
const courseList = await this.courseService.selectAll(all);
|
||||||
return { code: BizCode.OK, data: courseList };
|
return { code: BizCode.OK, data: courseList };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +75,75 @@ export class CourseController {
|
||||||
const guide = await this.guideService.select(course_id);
|
const guide = await this.guideService.select(course_id);
|
||||||
return { code: BizCode.OK, data: { chapterList, guide, course } };
|
return { code: BizCode.OK, data: { chapterList, guide, course } };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
this.ctx.logger.error(error);
|
||||||
return { code: BizCode.ERROR, msg: '[error] /chapter/select error' };
|
return { code: BizCode.ERROR, msg: '[error] /chapter/select error' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('/update')
|
||||||
|
async updateCourse(@Body() course: Course) {
|
||||||
|
try {
|
||||||
|
await this.courseService.update(course);
|
||||||
|
return { code: BizCode.OK };
|
||||||
|
} catch (error) {
|
||||||
|
this.ctx.logger.error(error);
|
||||||
|
return { code: BizCode.ERROR, msg: error };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/chapter/select')
|
||||||
|
async selectChapterList(@Body() chapter: Chapter) {
|
||||||
|
const { chapter_course_id } = chapter;
|
||||||
|
const chapterList = await this.chapterService.select(chapter_course_id);
|
||||||
|
return { code: BizCode.OK, data: chapterList };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/chapter/update')
|
||||||
|
async updateChapter(@Body() chapter: Chapter) {
|
||||||
|
try {
|
||||||
|
await this.chapterService.update(chapter);
|
||||||
|
return { code: BizCode.OK };
|
||||||
|
} catch (error) {
|
||||||
|
this.ctx.logger.error(error);
|
||||||
|
return { code: BizCode.ERROR, msg: error };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/remove')
|
||||||
|
async remove(@Body() course: Course) {
|
||||||
|
const { course_id } = course;
|
||||||
|
try {
|
||||||
|
await this.courseService.remove(course_id);
|
||||||
|
await this.chapterService.removeByCourseId(course_id);
|
||||||
|
await this.guideService.removeByCourseId(course_id);
|
||||||
|
return { code: BizCode.OK };
|
||||||
|
} catch (error) {
|
||||||
|
this.ctx.logger.error(error);
|
||||||
|
return { code: BizCode.ERROR, msg: error };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/chapter/create')
|
||||||
|
async createChapter(@Body() chapterList: Chapter[]) {
|
||||||
|
try {
|
||||||
|
// order默认为-1
|
||||||
|
await this.chapterService.create(chapterList);
|
||||||
|
return { code: BizCode.OK };
|
||||||
|
} catch (error) {
|
||||||
|
this.ctx.logger.error(error);
|
||||||
|
return { code: BizCode.ERROR, msg: error };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/chapter/remove')
|
||||||
|
async removeChapter(@Body() chapter: Chapter) {
|
||||||
|
try {
|
||||||
|
const { chapter_id } = chapter;
|
||||||
|
await this.chapterService.remove(chapter_id);
|
||||||
|
return { code: BizCode.OK };
|
||||||
|
} catch (error) {
|
||||||
|
this.ctx.logger.error(error);
|
||||||
|
return { code: BizCode.ERROR, msg: error };
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,18 +6,18 @@ export class Chapter {
|
||||||
chapter_id?: string;
|
chapter_id?: string;
|
||||||
|
|
||||||
@Column({ type: 'text' })
|
@Column({ type: 'text' })
|
||||||
chapter_title: string;
|
chapter_title?: string;
|
||||||
|
|
||||||
@Column()
|
@Column()
|
||||||
chapter_level: '1' | '2';
|
chapter_level?: '1' | '2';
|
||||||
|
|
||||||
@Column({ default: '' })
|
@Column({ default: '' })
|
||||||
chapter_file_id?: string;
|
chapter_file_id?: string;
|
||||||
|
|
||||||
@Column()
|
@Column()
|
||||||
chapter_course_id: string;
|
chapter_course_id?: string;
|
||||||
|
|
||||||
@Column()
|
@Column({ default: -1 })
|
||||||
order?: number;
|
order?: number;
|
||||||
|
|
||||||
@Column({ default: '' })
|
@Column({ default: '' })
|
||||||
|
@ -26,6 +26,6 @@ export class Chapter {
|
||||||
@Column({ default: '' })
|
@Column({ default: '' })
|
||||||
media_url?: string;
|
media_url?: string;
|
||||||
|
|
||||||
@Column({ default: ''})
|
@Column({ default: '' })
|
||||||
media_cover_url?: string;
|
media_cover_url?: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ export class AuthMiddleware implements IMiddleware<Context, NextFunction> {
|
||||||
}
|
}
|
||||||
await next();
|
await next();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
ctx.logger.error(error);
|
||||||
return { code: BizCode.AUTH, msg: '身份验证错误' };
|
return { code: BizCode.AUTH, msg: '身份验证错误' };
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -36,4 +36,16 @@ export class ChapterService {
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async update(chapter: Chapter) {
|
||||||
|
await this.chapterModel.save(chapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeByCourseId(course_id: string) {
|
||||||
|
await this.chapterModel.delete({ chapter_course_id: course_id });
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(chapter_id: string) {
|
||||||
|
await this.chapterModel.delete({ chapter_id });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,12 +24,21 @@ export class CourseService {
|
||||||
return courseCreateRes.course_id;
|
return courseCreateRes.course_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
async selectAll() {
|
async selectAll(all) {
|
||||||
return await this.courseModel.find({ where: { valid: true } });
|
if (!all) return await this.courseModel.find({ where: { valid: true } });
|
||||||
|
else return await this.courseModel.find();
|
||||||
}
|
}
|
||||||
|
|
||||||
async select(course: Course) {
|
async select(course: Course) {
|
||||||
const { course_id } = course;
|
const { course_id } = course;
|
||||||
return await this.courseModel.findOne({ where: { course_id } });
|
return await this.courseModel.findOne({ where: { course_id } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async update(course: Course) {
|
||||||
|
return await this.courseModel.save(course);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(course_id: string) {
|
||||||
|
await this.courseModel.delete({ course_id });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,4 +21,8 @@ export class GuideService {
|
||||||
where: { guide_course_id: course_id },
|
where: { guide_course_id: course_id },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async removeByCourseId(course_id: string) {
|
||||||
|
await this.guideModel.delete({ guide_course_id: course_id });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
grid-row-gap: 10px;
|
grid-row-gap: 10px;
|
||||||
|
|
||||||
.course-card {
|
.course-card {
|
||||||
|
cursor: pointer;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
.cover {
|
.cover {
|
||||||
height: 120px;
|
height: 120px;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user