mirror of
https://github.com/PlayEdu/backend
synced 2025-06-07 23:44:10 +08:00
资源视频编辑组件、视频预览组件
This commit is contained in:
parent
7cb408a1cb
commit
87d1fc8e01
@ -8,9 +8,15 @@
|
|||||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
|
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
|
||||||
/>
|
/>
|
||||||
<title>管理后台</title>
|
<title>管理后台</title>
|
||||||
|
<script src="/js/DPlayer.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
<script
|
||||||
|
crossorigin="anonymous"
|
||||||
|
integrity="sha512-oHrfR/z2wkuRuaHrdZ9NhoT/o/1kteub+QvmQgVzOKK7NTvIKQMvnY9+/RR0+eW311o4lAE/YzzLXXmP2XUvig=="
|
||||||
|
src="https://lib.baomitu.com/hls.js/1.1.4/hls.min.js"
|
||||||
|
></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
1
public/js/DPlayer.min.js
vendored
Normal file
1
public/js/DPlayer.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/js/xg/hls.min.js
vendored
Normal file
1
public/js/xg/hls.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
21
public/js/xg/index.js
Normal file
21
public/js/xg/index.js
Normal file
File diff suppressed because one or more lines are too long
@ -62,6 +62,10 @@ export function destroyResourceMulti(ids: number[]) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function videoDetail(id: number) {
|
||||||
|
return client.get(`/backend/v1/resource/${id}`, {});
|
||||||
|
}
|
||||||
|
|
||||||
export function videoUpdate(id: number, params: any) {
|
export function videoUpdate(id: number, params: any) {
|
||||||
return client.put(`/backend/v1/resource/${id}`, params);
|
return client.put(`/backend/v1/resource/${id}`, params);
|
||||||
}
|
}
|
||||||
|
BIN
src/assets/images/commen/close.png
Normal file
BIN
src/assets/images/commen/close.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 736 B |
138
src/pages/resource/videos/compenents/update-dialog/index.tsx
Normal file
138
src/pages/resource/videos/compenents/update-dialog/index.tsx
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { Modal, Form, Input, message, TreeSelect } from "antd";
|
||||||
|
import { resource, resourceCategory } from "../../../../../api/index";
|
||||||
|
|
||||||
|
interface PropInterface {
|
||||||
|
id: number;
|
||||||
|
open: boolean;
|
||||||
|
onCancel: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const VideosUpdateDialog: React.FC<PropInterface> = ({
|
||||||
|
id,
|
||||||
|
open,
|
||||||
|
onCancel,
|
||||||
|
}) => {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
|
const [categories, setCategories] = useState<any>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (id === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (open) {
|
||||||
|
getCategory();
|
||||||
|
getDetail();
|
||||||
|
}
|
||||||
|
}, [id, open]);
|
||||||
|
|
||||||
|
const getCategory = () => {
|
||||||
|
resourceCategory.resourceCategoryList().then((res: any) => {
|
||||||
|
const categories = res.data.categories;
|
||||||
|
if (JSON.stringify(categories) !== "{}") {
|
||||||
|
const new_arr: any = checkArr(categories, 0, null);
|
||||||
|
setCategories(new_arr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDetail = () => {
|
||||||
|
resource.videoDetail(id).then((res: any) => {
|
||||||
|
let data = res.data.resources;
|
||||||
|
form.setFieldsValue({
|
||||||
|
name: data.name,
|
||||||
|
category_id: res.data.category_ids,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkArr = (departments: any[], id: number, counts: any) => {
|
||||||
|
const arr = [];
|
||||||
|
for (let i = 0; i < departments[id].length; i++) {
|
||||||
|
if (!departments[departments[id][i].id]) {
|
||||||
|
arr.push({
|
||||||
|
title: departments[id][i].name,
|
||||||
|
value: departments[id][i].id,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const new_arr: any = checkArr(
|
||||||
|
departments,
|
||||||
|
departments[id][i].id,
|
||||||
|
counts
|
||||||
|
);
|
||||||
|
arr.push({
|
||||||
|
title: departments[id][i].name,
|
||||||
|
value: departments[id][i].id,
|
||||||
|
children: new_arr,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFinish = (values: any) => {
|
||||||
|
resource.videoUpdate(id, values).then((res: any) => {
|
||||||
|
message.success("保存成功!");
|
||||||
|
onCancel();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFinishFailed = (errorInfo: any) => {
|
||||||
|
console.log("Failed:", errorInfo);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
title="编辑视频"
|
||||||
|
centered
|
||||||
|
forceRender
|
||||||
|
open={open}
|
||||||
|
width={416}
|
||||||
|
onOk={() => form.submit()}
|
||||||
|
onCancel={() => onCancel()}
|
||||||
|
maskClosable={false}
|
||||||
|
>
|
||||||
|
<div className="float-left mt-24">
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
name="videos-update"
|
||||||
|
labelCol={{ span: 8 }}
|
||||||
|
wrapperCol={{ span: 16 }}
|
||||||
|
initialValues={{ remember: true }}
|
||||||
|
onFinish={onFinish}
|
||||||
|
onFinishFailed={onFinishFailed}
|
||||||
|
autoComplete="off"
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
label="视频分类"
|
||||||
|
name="category_id"
|
||||||
|
rules={[{ required: true, message: "请选择视频分类!" }]}
|
||||||
|
>
|
||||||
|
<TreeSelect
|
||||||
|
showCheckedStrategy={TreeSelect.SHOW_ALL}
|
||||||
|
allowClear
|
||||||
|
style={{ width: 200 }}
|
||||||
|
treeData={categories}
|
||||||
|
placeholder="视频分类"
|
||||||
|
treeDefaultExpandAll
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="视频名称"
|
||||||
|
name="name"
|
||||||
|
rules={[{ required: true, message: "请输入视频名称!" }]}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
allowClear
|
||||||
|
style={{ width: 200 }}
|
||||||
|
placeholder="请输入视频名称"
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,40 @@
|
|||||||
|
.play-mask {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
position: fixed;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 200;
|
||||||
|
.play-dialog {
|
||||||
|
width: 800px;
|
||||||
|
height: 450px;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
.close-button {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
position: absolute;
|
||||||
|
top: 24px;
|
||||||
|
right: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 1000;
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.play-box {
|
||||||
|
width: 800px;
|
||||||
|
height: 450px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import styles from "./index.module.less";
|
||||||
|
import closeIcon from "../../../../../assets/images/commen/close.png";
|
||||||
|
|
||||||
|
interface PropInterface {
|
||||||
|
id: number;
|
||||||
|
url: string;
|
||||||
|
open: boolean;
|
||||||
|
onCancel: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare const window: any;
|
||||||
|
|
||||||
|
export const VideoPlayDialog: React.FC<PropInterface> = ({
|
||||||
|
id,
|
||||||
|
url,
|
||||||
|
open,
|
||||||
|
onCancel,
|
||||||
|
}) => {
|
||||||
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open && url) {
|
||||||
|
initDPlayer(url);
|
||||||
|
}
|
||||||
|
}, [id, open, url]);
|
||||||
|
|
||||||
|
const initDPlayer = (playUrl: string) => {
|
||||||
|
window.player = new window.DPlayer({
|
||||||
|
container: document.getElementById("meedu-player-container"),
|
||||||
|
autoplay: false,
|
||||||
|
video: {
|
||||||
|
url: playUrl,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
window.player.on("ended", () => {
|
||||||
|
window.player && window.player.destroy();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{open && (
|
||||||
|
<div className={styles["play-mask"]}>
|
||||||
|
<div className={styles["play-dialog"]}>
|
||||||
|
<div
|
||||||
|
className={styles["close-button"]}
|
||||||
|
onClick={() => {
|
||||||
|
window.player && window.player.destroy();
|
||||||
|
onCancel();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img src={closeIcon} />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={styles["play-box"]}
|
||||||
|
id="meedu-player-container"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -9,6 +9,8 @@ import type { ColumnsType } from "antd/es/table";
|
|||||||
import { dateFormat } from "../../../utils/index";
|
import { dateFormat } from "../../../utils/index";
|
||||||
import { TreeCategory, DurationText, PerButton } from "../../../compenents";
|
import { TreeCategory, DurationText, PerButton } from "../../../compenents";
|
||||||
import { UploadVideoButton } from "../../../compenents/upload-video-button";
|
import { UploadVideoButton } from "../../../compenents/upload-video-button";
|
||||||
|
import { VideoPlayDialog } from "./compenents/video-play-dialog";
|
||||||
|
import { VideosUpdateDialog } from "./compenents/update-dialog";
|
||||||
|
|
||||||
const { confirm } = Modal;
|
const { confirm } = Modal;
|
||||||
|
|
||||||
@ -39,6 +41,7 @@ const ResourceVideosPage = () => {
|
|||||||
const [playVisible, setPlayeVisible] = useState<boolean>(false);
|
const [playVisible, setPlayeVisible] = useState<boolean>(false);
|
||||||
const [multiConfig, setMultiConfig] = useState<boolean>(false);
|
const [multiConfig, setMultiConfig] = useState<boolean>(false);
|
||||||
const [updateId, setUpdateId] = useState(0);
|
const [updateId, setUpdateId] = useState(0);
|
||||||
|
const [playUrl, setPlayUrl] = useState<string>("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCateId(Number(result.get("cid")));
|
setCateId(Number(result.get("cid")));
|
||||||
@ -131,6 +134,8 @@ const ResourceVideosPage = () => {
|
|||||||
size="small"
|
size="small"
|
||||||
className="b-n-link c-red"
|
className="b-n-link c-red"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
setUpdateId(record.id);
|
||||||
|
setPlayUrl(record.url);
|
||||||
setPlayeVisible(true);
|
setPlayeVisible(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -332,6 +337,17 @@ const ResourceVideosPage = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<VideoPlayDialog
|
||||||
|
id={Number(updateId)}
|
||||||
|
open={playVisible}
|
||||||
|
url={playUrl}
|
||||||
|
onCancel={() => setPlayeVisible(false)}
|
||||||
|
></VideoPlayDialog>
|
||||||
|
<VideosUpdateDialog
|
||||||
|
id={Number(updateId)}
|
||||||
|
open={updateVisible}
|
||||||
|
onCancel={() => setUpdateVisible(false)}
|
||||||
|
></VideosUpdateDialog>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user