feat: 课程创建

This commit is contained in:
mozzie 2023-03-07 22:08:00 +08:00
parent 15537d5c13
commit ddc823cf9e
12 changed files with 200 additions and 155 deletions

View File

@ -24,3 +24,16 @@ ul {
font-family: "bs"; font-family: "bs";
src: url("./backset.woff"); src: url("./backset.woff");
} }
.bs-scrollbar {
&::-webkit-scrollbar {
width: 14px;
height: 4px;
}
&::-webkit-scrollbar-thumb {
border: 4px solid transparent;
background-clip: padding-box;
border-radius: 7px;
background-color: #d2d2d2;
}
}

View File

@ -1,17 +1,50 @@
.logo { .container {
float: left; height: 100%;
width: 120px; background: #f1f1f1;
height: 31px; header {
margin: 16px 24px 16px 0; padding: 0 24px;
display: flex; display: flex;
align-items: center; align-items: center;
color: #fff; position: fixed;
svg { top: 0;
width: 22px; left: 0;
right: 0;
height: 46px;
background: #001529;
z-index: 19;
.logo {
width: 320px;
display: flex;
align-items: center;
color: #fff;
svg {
width: 22px;
}
span {
padding-left: 10px;
font-family: "bs";
font-size: 16px;
}
}
} }
span { aside {
padding-left: 10px; position: fixed;
font-family: "bs"; left: 0;
font-size: 16px; top: 46px;
bottom: 0;
width: 200px;
}
main {
position: fixed;
left: 200px;
right: 0;
top: 46px;
bottom: 0;
overflow-y: auto;
.view {
width: 1120px;
margin: 0 auto;
}
} }
} }

View File

@ -50,8 +50,8 @@ const Index: React.FC = () => {
}; };
return ( return (
<Layout style={{ height: "100%" }}> <div className="container">
<Header className="header"> <header>
<div className="logo"> <div className="logo">
<svg <svg
fill="currentColor" fill="currentColor"
@ -71,8 +71,8 @@ const Index: React.FC = () => {
items={navMenus} items={navMenus}
onClick={onClickNavMenuItem} onClick={onClickNavMenuItem}
/> />
</Header> </header>
<Layout> <aside>
<Sider width={200} style={{ background: colorBgContainer }}> <Sider width={200} style={{ background: colorBgContainer }}>
<Menu <Menu
mode="inline" mode="inline"
@ -83,33 +83,26 @@ const Index: React.FC = () => {
onClick={onClickSideMenuItem} onClick={onClickSideMenuItem}
/> />
</Sider> </Sider>
<Layout className="site-layout"> </aside>
<Content <main className="bs-scrollbar">
style={{ <div className="view">
margin: "24px 16px", <Routes>
padding: 24, {[...navRoutes, ...sideMenuRoutes].map((router) => (
minHeight: 280, <Route
background: colorBgContainer, key={router.path}
}} path={router.path}
> element={
<Routes> <Suspense fallback={<Spin />}>
{[...navRoutes, ...sideMenuRoutes].map((router) => ( <Guard>{<router.element />}</Guard>
<Route </Suspense>
key={router.path} }
path={router.path} />
element={ ))}
<Suspense fallback={<Spin />}> <Route path="*" element={<span>404</span>} />
<Guard>{<router.element />}</Guard> </Routes>
</Suspense> </div>
} </main>
/> </div>
))}
<Route path="*" element={<span>404</span>} />
</Routes>
</Content>
</Layout>
</Layout>
</Layout>
); );
}; };

View File

@ -1,5 +0,0 @@
const Appendix = () => {
return <div></div>;
};
export default Appendix;

View File

@ -0,0 +1,10 @@
interface IProps {
onChange?: Function;
styles?: React.CSSProperties;
}
const Appendix = (props: IProps) => {
return <div style={{ ...props.styles }}></div>;
};
export default Appendix;

View File

@ -3,7 +3,7 @@
background-size: cover; background-size: cover;
background-position: center; background-position: center;
border-radius: 6px; border-radius: 6px;
height: 100%; height: 360px;
.mask { .mask {
position: absolute; position: absolute;
left: 50%; left: 50%;
@ -14,16 +14,19 @@
color: #fff; color: #fff;
text-align: center; text-align: center;
margin: 0; margin: 0;
letter-spacing: 2px;
&.title { &.title {
background: rgba(0, 0, 0, 0.7); background: rgba(0, 0, 0, 0.7);
font-size: 22px; font-size: 22px;
padding: 10px; padding: 10px;
line-height: 1.5; line-height: 1.5;
border-radius: 6px 6px 0 0;
} }
&.summary { &.summary {
background: rgba(0, 0, 0, 0.3); background: rgba(0, 0, 0, 0.3);
padding: 6px; padding: 6px;
line-height: 1.4; line-height: 1.4;
border-radius: 0 0 6px 6px;
} }
} }
} }

View File

@ -1,34 +1,24 @@
import { InboxOutlined } from "@ant-design/icons"; import { InboxOutlined } from "@ant-design/icons";
import { import { Col, Form, Input, message, Row, Upload, UploadProps } from "antd";
Card, import { useEffect, useState } from "react";
Col,
Form,
Input,
message,
Row,
Upload,
UploadProps,
} from "antd";
import { useState } from "react";
import "./index.less"; import "./index.less";
const { Dragger } = Upload; const { Dragger } = Upload;
const { Meta } = Card;
interface IProps { interface IProps {
onChange: Function; onChange: Function;
styles?: React.CSSProperties;
} }
const BasicForm = (props: IProps) => { const BasicForm = (props: IProps) => {
const [preview, setPreivew] = useState({ const [preview, setPreivew] = useState({
coverUrl: "", coverUrl: "",
title: "标题", title: "",
summary: "摘要", summary: "",
}); });
const [form] = Form.useForm(); const [form] = Form.useForm();
const onValuesChange = (_: any, all: any) => { const onValuesChange = (_: any, all: any) =>
setPreivew((p) => ({ ...p, ...all })); setPreivew((p) => ({ ...p, ...all }));
};
const coverDragger: UploadProps = { const coverDragger: UploadProps = {
name: "file", name: "file",
@ -53,36 +43,12 @@ const BasicForm = (props: IProps) => {
}, },
}; };
useEffect(() => props.onChange(preview), [preview]);
return ( return (
<div> <div style={{ ...props.styles }}>
<Row gutter={24}> <Row style={{ marginBottom: 24 }}>
<Col span={12}> <Col span={24}>
<Form form={form} onValuesChange={onValuesChange}>
<Form.Item>
<Dragger {...coverDragger} maxCount={1}>
<p className="ant-upload-drag-icon">
<InboxOutlined />
</p>
<p className="ant-upload-text"></p>
<p className="ant-upload-hint"></p>
</Dragger>
</Form.Item>
<Form.Item name="title" rules={[{ required: true }]}>
<Input type="text" placeholder="标题" />
</Form.Item>
<Form.Item
name="summary"
rules={[{ required: true }]}
style={{ marginBottom: 0 }}
>
<Input.TextArea
placeholder="摘要"
style={{ height: 120, resize: "none" }}
/>
</Form.Item>
</Form>
</Col>
<Col span={12}>
<div <div
className="preview-course" className="preview-course"
style={{ style={{
@ -99,12 +65,42 @@ const BasicForm = (props: IProps) => {
}} }}
> >
<div className="mask"> <div className="mask">
<p className="title">{preview.title}</p> <p className="title">{!preview.title ? "标题" : preview.title}</p>
<p className="summary">{preview.summary} </p> <p className="summary">
{!preview.summary ? "摘要" : preview.summary}{" "}
</p>
</div> </div>
</div> </div>
</Col> </Col>
</Row> </Row>
<Row gutter={24}>
<Col span={12}>
<Dragger {...coverDragger} maxCount={1}>
<p className="ant-upload-drag-icon">
<InboxOutlined />
</p>
<p className="ant-upload-text"></p>
<p className="ant-upload-hint"></p>
</Dragger>
</Col>
<Col span={12}>
<Form form={form} onValuesChange={onValuesChange}>
<Form.Item name="title" rules={[{ required: true }]}>
<Input size="large" type="text" placeholder="标题" />
</Form.Item>
<Form.Item
name="summary"
rules={[{ required: true }]}
style={{ marginBottom: 0 }}
>
<Input.TextArea
placeholder="摘要"
style={{ height: 130, resize: "none" }}
/>
</Form.Item>
</Form>
</Col>
</Row>
</div> </div>
); );
}; };

View File

@ -0,0 +1,10 @@
interface IProps {
onChange?: Function;
styles?: React.CSSProperties;
}
const Chatpter = (props: IProps) => {
return <div style={{ ...props.styles }}>123</div>;
};
export default Chatpter;

View File

@ -1,5 +0,0 @@
const MediaBind = () => {
return <div>123</div>;
};
export default MediaBind;

View File

@ -1,6 +1,5 @@
.create-course { .create-course {
display: flex; padding: 24px 0;
flex-direction: column;
.content { .content {
padding: 20px 0; padding: 20px 0;
flex: 1; flex: 1;

View File

@ -1,5 +1,7 @@
import { import {
Button, Button,
Card,
Col,
Form, Form,
Input, Input,
InputNumber, InputNumber,
@ -11,28 +13,27 @@ import {
import { useState } from "react"; import { useState } from "react";
import Appendix from "./Appendix"; import Appendix from "./Appendix";
import BasicForm from "./BasicForm"; import BasicForm from "./BasicForm";
import Chatpter from "./Chatpter";
import "./index.less"; import "./index.less";
import MediaBind from "./MediaBind";
const CourseCreate = () => { const CourseCreate = () => {
const [current, setCurrent] = useState(0); const [current, setCurrent] = useState(0);
const [course, setCourse] = useState({
basicInfo: {},
});
const onBasicFormChange = (form: any) => { const onBasicFormChange = (form: any) =>
console.log(form); setCourse((p) => ({ ...p, basicInfo: form }));
};
const steps = [ const steps = [
{ {
title: "基本信息", title: "基本信息",
content: <BasicForm onChange={onBasicFormChange} />,
}, },
{ {
title: "媒体资源", title: "章节",
content: <MediaBind />,
}, },
{ {
title: "附件", title: "附件",
content: <Appendix />,
}, },
]; ];
@ -40,45 +41,40 @@ const CourseCreate = () => {
return ( return (
<div className="create-course"> <div className="create-course">
<Steps current={current} items={items} /> <Card>
<div className="content">{steps[current].content}</div> <Steps current={current} items={items} />
<div style={{ textAlign: "right" }}> <div className="content">
<Button <BasicForm
style={{ onChange={onBasicFormChange}
margin: "0 8px", styles={{ display: current === 0 ? "block" : "none" }}
visibility: current > 0 ? "visible" : "hidden", />
}} <Chatpter styles={{ display: current === 1 ? "block" : "none" }} />
onClick={() => setCurrent(current - 1)} <Appendix styles={{ display: current === 2 ? "block" : "none" }} />
> </div>
<div style={{ textAlign: "right", marginTop: "40px" }}>
</Button> {current > 0 && (
{current < steps.length - 1 && ( <Button
<Button type="primary" onClick={() => setCurrent(current + 1)}> style={{ marginRight: "12px" }}
onClick={() => setCurrent(current - 1)}
</Button> >
)}
{current === steps.length - 1 && ( </Button>
<Button )}
type="primary" {current < steps.length - 1 && (
onClick={() => message.success("Processing complete!")} <Button type="primary" onClick={() => setCurrent(current + 1)}>
>
</Button>
</Button> )}
)} {current === steps.length - 1 && (
</div> <Button
{/* <Form> type="primary"
<Form.Item onClick={() => message.success("Processing complete!")}
wrapperCol={{ span: 24 }} >
name="title"
rules={[{ required: true, message: "Please input your username!" }]} </Button>
> )}
<Input placeholder="标题" /> </div>
</Form.Item> </Card>
<Form.Item><InputNumber placeholder="售价" /></Form.Item>
<Form.Item>
<Button type="primary"></Button>
</Form.Item>
</Form> */}
</div> </div>
); );
}; };

View File

@ -48,7 +48,9 @@ const Library = () => {
<Row>{record.name}</Row> <Row>{record.name}</Row>
<Row style={{ paddingTop: "5px" }}> <Row style={{ paddingTop: "5px" }}>
{record.m3u8SubStreamList.map((item: any, index: number) => ( {record.m3u8SubStreamList.map((item: any, index: number) => (
<Tag color={colors[index]}>{item}</Tag> <Tag key={item} color={colors[index]}>
{item}
</Tag>
))} ))}
</Row> </Row>
<Row style={{ paddingTop: "5px" }}> <Row style={{ paddingTop: "5px" }}>
@ -160,7 +162,7 @@ const Library = () => {
}); });
return ( return (
<div> <Card style={{ marginTop: 24 }}>
<Row> <Row>
<Col span={14}> <Col span={14}>
<Space> <Space>
@ -197,7 +199,7 @@ const Library = () => {
/> />
</Col> </Col>
</Row> </Row>
</div> </Card>
); );
}; };