refactor: course detail
This commit is contained in:
parent
630c8ca1b5
commit
5b398e9c7e
|
@ -54,3 +54,8 @@ export const removeChapter = (chapter: IChapter) =>
|
||||||
export const selectUserList = () => R.post("/api/user/admin/select/all");
|
export const selectUserList = () => R.post("/api/user/admin/select/all");
|
||||||
|
|
||||||
export const updateUser = (user: any) => R.post("/api/user/admin/update", user);
|
export const updateUser = (user: any) => R.post("/api/user/admin/update", user);
|
||||||
|
|
||||||
|
export const selectGuide = (p: { guide_course_id: string }) =>
|
||||||
|
R.post("/api/course/guide/select", p);
|
||||||
|
|
||||||
|
export const updateGuide = (p: any) => R.post("/api/course/guide/update", p);
|
||||||
|
|
|
@ -46,8 +46,7 @@
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
.view {
|
.view {
|
||||||
width: 1120px;
|
padding: 0 24px;
|
||||||
margin: 0 auto;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import "vditor/dist/index.css";
|
import "vditor/dist/index.css";
|
||||||
import Vditor from "vditor";
|
import Vditor from "vditor";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import "./index.less";
|
import "./index.less";
|
||||||
import { message } from "antd";
|
import { message } from "antd";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
onChange?: Function;
|
onChange?: Function;
|
||||||
styles?: React.CSSProperties;
|
styles?: React.CSSProperties;
|
||||||
|
id: string;
|
||||||
|
defaultValue?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const emoji = {
|
const emoji = {
|
||||||
|
@ -66,7 +68,7 @@ const toolbar = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const Guide = (props: IProps) => {
|
export default function Guide(props: IProps) {
|
||||||
const vditorRef = useRef<Vditor>();
|
const vditorRef = useRef<Vditor>();
|
||||||
|
|
||||||
const submitTool = {
|
const submitTool = {
|
||||||
|
@ -83,37 +85,40 @@ const Guide = (props: IProps) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
vditorRef.current = new Vditor("vditor", {
|
if (props.defaultValue)
|
||||||
height: 600,
|
vditorRef.current = new Vditor("vditor", {
|
||||||
toolbar: [...toolbar, "|", submitTool],
|
height: 600,
|
||||||
hint: { delay: 200, emoji },
|
cache: {
|
||||||
counter: { enable: true },
|
id: props.id,
|
||||||
preview: { actions: ["desktop", "mobile"] },
|
enable: false,
|
||||||
after: () => console.log("[info] vditor init success..."),
|
|
||||||
upload: {
|
|
||||||
accept: "image/*",
|
|
||||||
url: "/api/vod/oss/image/upload",
|
|
||||||
multiple: false,
|
|
||||||
success(_, res) {
|
|
||||||
const { code, data, msg } = JSON.parse(res);
|
|
||||||
console.log(code, data, msg);
|
|
||||||
if (code === 10000) {
|
|
||||||
message.success("上传成功");
|
|
||||||
const { name, url } = data;
|
|
||||||
vditorRef.current?.insertValue(``);
|
|
||||||
} else {
|
|
||||||
message.error(msg);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
toolbar: [...toolbar, "|", submitTool],
|
||||||
});
|
value: props.defaultValue ?? "",
|
||||||
}, []);
|
hint: { delay: 200, emoji },
|
||||||
|
counter: { enable: true },
|
||||||
|
preview: { actions: ["desktop", "mobile"] },
|
||||||
|
after: () => console.log("[info] vditor init success..."),
|
||||||
|
upload: {
|
||||||
|
accept: "image/*",
|
||||||
|
url: "/api/vod/oss/image/upload",
|
||||||
|
multiple: false,
|
||||||
|
success(_, res) {
|
||||||
|
const { code, data, msg } = JSON.parse(res);
|
||||||
|
if (code === 10000) {
|
||||||
|
message.success("上传成功");
|
||||||
|
const { name, url } = data;
|
||||||
|
vditorRef.current?.insertValue(``);
|
||||||
|
} else {
|
||||||
|
message.error(msg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, [props.defaultValue]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ ...props.styles }}>
|
<div style={{ ...props.styles }}>
|
||||||
<div id="vditor" className="vditor" />
|
<div id="vditor" className="vditor" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default Guide;
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
.create-course {
|
.create-course {
|
||||||
|
width: 1120px;
|
||||||
|
margin: 0 auto;
|
||||||
padding: 24px 0;
|
padding: 24px 0;
|
||||||
.content {
|
.content {
|
||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
|
|
|
@ -102,6 +102,7 @@ const CourseCreate = () => {
|
||||||
styles={{ display: current === 1 ? "block" : "none" }}
|
styles={{ display: current === 1 ? "block" : "none" }}
|
||||||
/>
|
/>
|
||||||
<Guide
|
<Guide
|
||||||
|
id="createCourseEditor"
|
||||||
onChange={onGuideChange}
|
onChange={onGuideChange}
|
||||||
styles={{ display: current === 2 ? "block" : "none" }}
|
styles={{ display: current === 2 ? "block" : "none" }}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
CompassOutlined,
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
EditFilled,
|
EditFilled,
|
||||||
EditOutlined,
|
EditOutlined,
|
||||||
|
@ -19,8 +20,9 @@ import {
|
||||||
Modal,
|
Modal,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Form,
|
Form,
|
||||||
|
Drawer,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import dayjs, { Dayjs } from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
removeCourse,
|
removeCourse,
|
||||||
|
@ -30,8 +32,10 @@ import {
|
||||||
updateCourse,
|
updateCourse,
|
||||||
createChapter,
|
createChapter,
|
||||||
removeChapter,
|
removeChapter,
|
||||||
|
selectGuide,
|
||||||
|
updateGuide,
|
||||||
} from "../../../api";
|
} from "../../../api";
|
||||||
import { IChapter } from "../../../api/dto";
|
import Guide from "../Create/Guide";
|
||||||
import { useMount } from "../../../hooks";
|
import { useMount } from "../../../hooks";
|
||||||
import "./index.less";
|
import "./index.less";
|
||||||
|
|
||||||
|
@ -42,6 +46,14 @@ interface IEditItem {
|
||||||
|
|
||||||
const defaultEditItem = { key: "", value: "" };
|
const defaultEditItem = { key: "", value: "" };
|
||||||
|
|
||||||
|
const defaultEditGuide = {
|
||||||
|
drawerVisible: false,
|
||||||
|
course_title: "",
|
||||||
|
guide_id: "",
|
||||||
|
guide_value: "",
|
||||||
|
guide_html: "",
|
||||||
|
};
|
||||||
|
|
||||||
export default function List() {
|
export default function List() {
|
||||||
const [courseList, setCourseList] = useState<any>([]);
|
const [courseList, setCourseList] = useState<any>([]);
|
||||||
const [chapterList, setChapterList] = useState([]);
|
const [chapterList, setChapterList] = useState([]);
|
||||||
|
@ -50,6 +62,7 @@ export default function List() {
|
||||||
const [editCourseItem, setEditCourseItem] =
|
const [editCourseItem, setEditCourseItem] =
|
||||||
useState<IEditItem>(defaultEditItem);
|
useState<IEditItem>(defaultEditItem);
|
||||||
const [addChapterForm] = Form.useForm();
|
const [addChapterForm] = Form.useForm();
|
||||||
|
const [editGuide, setEditGuide] = useState(defaultEditGuide);
|
||||||
|
|
||||||
const onConfirmEditCourseItem = (record: any) => {
|
const onConfirmEditCourseItem = (record: any) => {
|
||||||
const { key, value } = editCourseItem;
|
const { key, value } = editCourseItem;
|
||||||
|
@ -127,6 +140,36 @@ export default function List() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onEditGuide = (record: any) => {
|
||||||
|
const { course_title, course_id: guide_course_id } = record;
|
||||||
|
selectGuide({ guide_course_id }).then((res: any) => {
|
||||||
|
const { data } = res;
|
||||||
|
const { guide_value = "", guide_html = "", guide_id } = data;
|
||||||
|
setEditGuide({
|
||||||
|
drawerVisible: true,
|
||||||
|
course_title,
|
||||||
|
guide_id,
|
||||||
|
guide_value,
|
||||||
|
guide_html,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onGuideChange = ({ value, html }: { value: string; html: string }) => {
|
||||||
|
setEditGuide((p) => ({ ...p, guide_value: value, guide_html: html }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const onConfirmEditGuide = () => {
|
||||||
|
const { course_title, drawerVisible, ...rest } = editGuide;
|
||||||
|
console.log(editGuide);
|
||||||
|
updateGuide(rest).then((res: any) => {
|
||||||
|
if (res?.code === 10000) {
|
||||||
|
setEditGuide(defaultEditGuide);
|
||||||
|
renderCourseTable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: "课程",
|
title: "课程",
|
||||||
|
@ -280,6 +323,13 @@ export default function List() {
|
||||||
icon={<FileAddOutlined />}
|
icon={<FileAddOutlined />}
|
||||||
></Button>
|
></Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
<Tooltip title="修改导读">
|
||||||
|
<Button
|
||||||
|
type="default"
|
||||||
|
onClick={() => onEditGuide(record)}
|
||||||
|
icon={<CompassOutlined />}
|
||||||
|
></Button>
|
||||||
|
</Tooltip>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -494,6 +544,27 @@ export default function List() {
|
||||||
onExpand={onExpand}
|
onExpand={onExpand}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
|
<Drawer
|
||||||
|
title={editGuide.course_title}
|
||||||
|
placement="bottom"
|
||||||
|
height="95%"
|
||||||
|
open={editGuide.drawerVisible}
|
||||||
|
onClose={() => setEditGuide((p) => ({ ...p, drawerVisible: false }))}
|
||||||
|
extra={
|
||||||
|
<Space>
|
||||||
|
<Button type="primary" onClick={onConfirmEditGuide}>
|
||||||
|
保存
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Guide
|
||||||
|
onChange={onGuideChange}
|
||||||
|
styles={{ display: "block" }}
|
||||||
|
id="editGuideEditor"
|
||||||
|
defaultValue={editGuide.guide_value}
|
||||||
|
/>
|
||||||
|
</Drawer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ 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 { Chapter } from '../entity/chapter.entity';
|
||||||
import { Course } from '../entity/course.entity';
|
import { Course } from '../entity/course.entity';
|
||||||
|
import { Guide } from '../entity/guide.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';
|
||||||
|
@ -148,4 +149,22 @@ export class CourseController {
|
||||||
return { code: BizCode.ERROR, msg: error };
|
return { code: BizCode.ERROR, msg: error };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('/guide/select')
|
||||||
|
async selectGuide(@Body() guide: Guide) {
|
||||||
|
const { guide_course_id: course_id } = guide;
|
||||||
|
const data = await this.guideService.select(course_id);
|
||||||
|
return { code: BizCode.OK, data };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/guide/update')
|
||||||
|
async updateGuide(@Body() guide: Guide) {
|
||||||
|
try {
|
||||||
|
await this.guideService.update(guide);
|
||||||
|
return { code: BizCode.OK };
|
||||||
|
} catch (error) {
|
||||||
|
this.ctx.logger.error(error);
|
||||||
|
return { code: BizCode.ERROR, msg: error };
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,4 +25,8 @@ export class GuideService {
|
||||||
async removeByCourseId(course_id: string) {
|
async removeByCourseId(course_id: string) {
|
||||||
await this.guideModel.delete({ guide_course_id: course_id });
|
await this.guideModel.delete({ guide_course_id: course_id });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async update(guide: Guide) {
|
||||||
|
await this.guideModel.save(guide);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,3 +3,15 @@ export interface ILoginRequest {
|
||||||
user_pass: string;
|
user_pass: string;
|
||||||
// xcode: string;
|
// xcode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,6 +2,7 @@ import "./index.less";
|
||||||
import FlashOff24Regular from "@ricons/fluent/FlashOff24Regular";
|
import FlashOff24Regular from "@ricons/fluent/FlashOff24Regular";
|
||||||
import { Icon } from "@ricons/utils";
|
import { Icon } from "@ricons/utils";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
code: 403 | 404 | 405;
|
code: 403 | 404 | 405;
|
||||||
|
@ -9,6 +10,7 @@ interface IProps {
|
||||||
|
|
||||||
export default function Result(props: IProps) {
|
export default function Result(props: IProps) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const [view, setView] = useState<JSX.Element>(<span></span>);
|
||||||
const table = {
|
const table = {
|
||||||
403: (
|
403: (
|
||||||
<div className="container result">
|
<div className="container result">
|
||||||
|
@ -48,5 +50,11 @@ export default function Result(props: IProps) {
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
return <>{table[props.code]}</>;
|
useEffect(() => {
|
||||||
|
if (props.code) {
|
||||||
|
setView(table[props.code]);
|
||||||
|
}
|
||||||
|
}, [props.code]);
|
||||||
|
|
||||||
|
return <>{view}</>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import "./index.less";
|
import "./index.less";
|
||||||
import Guide from "./components/Guide";
|
import Guide from "./components/Guide";
|
||||||
import { useMount } from "../../hook";
|
import { useMount } from "../../hook";
|
||||||
|
@ -11,90 +11,113 @@ import PlayCircle20Regular from "@ricons/fluent/PlayCircle20Regular";
|
||||||
import DocumentChevronDouble20Regular from "@ricons/fluent/DocumentChevronDouble20Regular";
|
import DocumentChevronDouble20Regular from "@ricons/fluent/DocumentChevronDouble20Regular";
|
||||||
import { Icon } from "@ricons/utils";
|
import { Icon } from "@ricons/utils";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
import { IChapter } from "../../api/dto";
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
toc: any[];
|
||||||
|
course: {
|
||||||
|
course_title: string;
|
||||||
|
course_createtime: string;
|
||||||
|
};
|
||||||
|
view: JSX.Element | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultState = {
|
||||||
|
toc: [],
|
||||||
|
course: {
|
||||||
|
course_title: "",
|
||||||
|
course_createtime: "",
|
||||||
|
},
|
||||||
|
view: null,
|
||||||
|
};
|
||||||
|
|
||||||
function CourseDetail() {
|
function CourseDetail() {
|
||||||
const { id: course_id = "" } = useParams();
|
const { id: course_id = "" } = useParams();
|
||||||
const [toc, setToc] = useState([]);
|
const [state, setState] = useState<IState>(defaultState);
|
||||||
const [course, setCourse] = useState<any>({});
|
|
||||||
const [view, setView] = useState<any>(null);
|
const chapters2toc = (chapterList: IChapter[]) => {
|
||||||
|
return chapterList.map((item) => {
|
||||||
|
return {
|
||||||
|
title: item.chapter_title,
|
||||||
|
level: +item.chapter_level!,
|
||||||
|
time: ms2Time(+item.media_time!),
|
||||||
|
icon: !!item.media_url ? (
|
||||||
|
<Icon size={20}>
|
||||||
|
<PlayCircle20Regular />
|
||||||
|
</Icon>
|
||||||
|
) : null,
|
||||||
|
active: false,
|
||||||
|
view: (
|
||||||
|
<Player
|
||||||
|
video={{ url: item.media_url!, pic: item.media_cover_url! }}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const computeYMD = (ts: number) => dayjs(ts).format("YYYY-MM-DD");
|
||||||
|
|
||||||
useMount(() => {
|
useMount(() => {
|
||||||
if (!!course_id)
|
if (!!course_id)
|
||||||
getCourseDetailById(course_id).then((res: any) => {
|
getCourseDetailById(course_id).then((res: any) => {
|
||||||
const { data, code } = res;
|
const { data = [], code } = res;
|
||||||
if (!data) setToc([]);
|
switch (code) {
|
||||||
if (code === 10000) {
|
case 50000:
|
||||||
const processToc = data?.chapterList.map((item: any) => {
|
setState((p) => ({ ...p, view: <Result code={403} /> }));
|
||||||
return {
|
break;
|
||||||
title: item.chapter_title,
|
case 10000:
|
||||||
level: +item.chapter_level,
|
const { chapterList = [], course, guide } = data;
|
||||||
time: ms2Time(+item.media_time),
|
const course_createtime = computeYMD(+course.course_createtime);
|
||||||
icon: !!item.media_url ? (
|
setState({
|
||||||
<Icon size={20}>
|
course: { ...course, course_createtime },
|
||||||
<PlayCircle20Regular />
|
toc: [
|
||||||
</Icon>
|
{
|
||||||
) : null,
|
title: "导读",
|
||||||
active: false,
|
level: 1,
|
||||||
view: (
|
time: "",
|
||||||
<Player
|
},
|
||||||
video={{ url: item.media_url, pic: item.media_cover_url }}
|
{
|
||||||
/>
|
title: "介绍 / 下载",
|
||||||
),
|
level: 2,
|
||||||
};
|
time: "",
|
||||||
});
|
active: true,
|
||||||
const composeToc = [
|
icon: (
|
||||||
{
|
<Icon size={20}>
|
||||||
title: "导读",
|
<DocumentChevronDouble20Regular />
|
||||||
level: 1,
|
</Icon>
|
||||||
time: "",
|
),
|
||||||
},
|
view: <Guide mdText={guide.guide_html} />,
|
||||||
{
|
},
|
||||||
title: "介绍 / 下载",
|
...chapters2toc(chapterList),
|
||||||
level: 2,
|
],
|
||||||
time: "",
|
view: <Guide mdText={data?.guide.guide_value} />,
|
||||||
active: true,
|
});
|
||||||
icon: (
|
default:
|
||||||
<Icon size={20}>
|
break;
|
||||||
<DocumentChevronDouble20Regular />
|
|
||||||
</Icon>
|
|
||||||
),
|
|
||||||
view: <Guide mdText={data?.guide.guide_html} />,
|
|
||||||
},
|
|
||||||
...processToc,
|
|
||||||
] as any;
|
|
||||||
const { course } = data;
|
|
||||||
setCourse({
|
|
||||||
...course,
|
|
||||||
course_createtime: dayjs(+course.course_createtime).format(
|
|
||||||
"YYYY-MM-DD"
|
|
||||||
),
|
|
||||||
});
|
|
||||||
setToc(composeToc);
|
|
||||||
setView(<Guide mdText={data?.guide.guide_value} />);
|
|
||||||
} else if (code === 40000) {
|
|
||||||
setView(<Result code={403} />);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const onclickItem = (i: any) => {
|
const onclickItem = (i: any) => {
|
||||||
setToc((t: any) =>
|
setState((p) => ({
|
||||||
t.map((p: any) => ({ ...p, active: i.title === p.title }))
|
...p,
|
||||||
);
|
toc: p.toc.map((p: any) => ({ ...p, active: i.title === p.title })),
|
||||||
setView(i.view ?? <Result code={404} />);
|
view: i.view ?? <Result code={404} />,
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="course-detail">
|
<div className="course-detail">
|
||||||
{toc.length > 0 ? (
|
{state.toc.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
<aside className="table-of-content">
|
<aside className="table-of-content">
|
||||||
<h3>{course.course_title}</h3>
|
<h3>{state.course.course_title}</h3>
|
||||||
<div style={{ color: "var(--color-text-3)", fontSize: 13 }}>
|
<div style={{ color: "var(--color-text-3)", fontSize: 13 }}>
|
||||||
<span>{course.course_createtime}</span>
|
<span>{state.course.course_createtime}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="toc">
|
<div className="toc">
|
||||||
{toc.map((i: any) => {
|
{state.toc.map((i: any) => {
|
||||||
if (i.level === 1) {
|
if (i.level === 1) {
|
||||||
return (
|
return (
|
||||||
<div className="level-1" key={i.title}>
|
<div className="level-1" key={i.title}>
|
||||||
|
@ -121,8 +144,11 @@ function CourseDetail() {
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
<div className="content" style={{ left: 300 }}>
|
<div
|
||||||
{view}
|
className="content"
|
||||||
|
style={{ left: state.toc.length > 0 ? 300 : "0" }}
|
||||||
|
>
|
||||||
|
{state.view}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|
Loading…
Reference in New Issue
Block a user