dev-v2
Joe 8 months ago
parent d35a19adbc
commit c6959ddb3e

@ -54,6 +54,7 @@
"@dnd-kit/sortable": "^8.0.0", "@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2", "@dnd-kit/utilities": "^3.2.2",
"@umijs/route-utils": "^2.2.2", "@umijs/route-utils": "^2.2.2",
"aliyun-aliplayer": "^2.29.1",
"antd": "^5.2.2", "antd": "^5.2.2",
"antd-img-crop": "^4.23.0", "antd-img-crop": "^4.23.0",
"braft-editor": "^2.3.9", "braft-editor": "^2.3.9",

@ -0,0 +1,126 @@
import React, { useEffect, useRef, useState } from 'react';
import { Button, Slider, Select, Space, Card } from 'antd';
import { PauseCircleOutlined, PlayCircleOutlined, ExpandOutlined } from '@ant-design/icons';
interface AliPlayerProps {
vid: string;
playAuth: string;
width?: number | string;
height?: number | string;
}
const AliPlayer: React.FC<AliPlayerProps> = ({
vid,
playAuth,
width = '100%',
height = 500
}) => {
const playerRef = useRef<any>(null);
const containerRef = useRef<HTMLDivElement>(null);
const [playing, setPlaying] = useState(false);
const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0);
const [playbackRate, setPlaybackRate] = useState(1);
// 初始化播放器
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://g.alicdn.com/apsara-media-box/imp-web-player/2.25.1/aliplayer-min.js';
script.onload = () => {
if (containerRef.current) {
playerRef.current = new (window as any).Aliplayer({
id: containerRef.current,
vid: vid,
playauth: playAuth,
width: width,
height: height,
autoplay: false,
encryptType: 1,
}, () => {
// 播放器准备就绪
setDuration(playerRef.current.getDuration());
// 绑定事件
playerRef.current.on('play', () => setPlaying(true));
playerRef.current.on('pause', () => setPlaying(false));
playerRef.current.on('ended', () => setPlaying(false));
playerRef.current.on('timeupdate', (currentTime: number) => {
setCurrentTime(currentTime);
});
});
}
};
document.head.appendChild(script);
return () => {
if (playerRef.current) {
playerRef.current.dispose();
}
document.head.removeChild(script);
};
}, [vid, playAuth]);
// 播放/暂停控制
const togglePlay = () => {
if (playing) {
playerRef.current.pause();
} else {
playerRef.current.play();
}
};
// 进度条跳转
const handleSeek = (value: number) => {
playerRef.current.seek(value);
};
// 倍速播放
const handleSpeedChange = (value: number) => {
playerRef.current.setPlaybackRate(value);
setPlaybackRate(value);
};
// 全屏
const handleFullscreen = () => {
playerRef.current.requestFullscreen();
};
return (
<Card>
<div ref={containerRef} style={{ width, height }} />
<Space style={{ marginTop: 16 }}>
<Button
icon={playing ? <PauseCircleOutlined /> : <PlayCircleOutlined />}
onClick={togglePlay}
/>
<Slider
min={0}
max={duration}
value={currentTime}
onChange={handleSeek}
style={{ width: 300 }}
/>
<Select
value={playbackRate}
onChange={handleSpeedChange}
options={[
{ value: 0.5, label: '0.5x' },
{ value: 1, label: '1.0x' },
{ value: 1.5, label: '1.5x' },
{ value: 2, label: '2.0x' },
]}
/>
<Button
icon={<ExpandOutlined />}
onClick={handleFullscreen}
/>
</Space>
</Card>
);
};
export default AliPlayer;

@ -6,20 +6,22 @@ import {
ProFormDateTimeRangePicker, ProFormDateTimeRangePicker,
ProFormDigit, ProFormDigit,
ProFormInstance, ProFormInstance,
ProFormRadio,
ProFormSelect, ProFormSelect,
ProFormText, ProFormText,
ProFormUploadButton, ProFormUploadButton,
ProTable, ProTable,
} from '@ant-design/pro-components'; } from '@ant-design/pro-components';
import { PageContainer } from '@ant-design/pro-layout'; import { PageContainer } from '@ant-design/pro-layout';
import { Button, Col, message, Popconfirm, Row, Space } from 'antd'; import { Button, Col, message, Modal, Popconfirm, Row, Space } from 'antd';
import Upload, { RcFile } from 'antd/es/upload'; import Upload, { RcFile } from 'antd/es/upload';
import React, { useRef, useState } from 'react'; import React, { useRef, useState } from 'react';
import { Access, history, useAccess, useParams, useSearchParams } from '@umijs/max'; import { Access, history, useAccess, useParams, useSearchParams } from '@umijs/max';
import { getClassesTypeListForAdminUsingPost } from '@/services/pop-b2b2c/pbcTrainingClassesTypeController'; import { getClassesTypeListForAdminUsingPost } from '@/services/pop-b2b2c/pbcTrainingClassesTypeController';
import { addOrUpdateClassUsingPost, classDetailForAdminUsingGet } from '@/services/pop-b2b2c/pbcTrainingClassesController'; import { addOrUpdateClassUsingPost, classDetailForAdminUsingGet } from '@/services/pop-b2b2c/pbcTrainingClassesController';
import { addOrUpdateChapterUsingPost, removeChapterUsingGet } from '@/services/pop-b2b2c/pbcTrainingClassesChapterController'; import { addOrUpdateChapterUsingPost, removeChapterUsingGet } from '@/services/pop-b2b2c/pbcTrainingClassesChapterController';
import { addOrUpdateVideoUsingPost, removeVideoUsingGet } from '@/services/pop-b2b2c/pbcTrainingClassesVideoController'; import { addOrUpdateVideoUsingPost, getVideoAuthByIdUsingGet, removeVideoUsingGet } from '@/services/pop-b2b2c/pbcTrainingClassesVideoController';
import AliPlayer from '@/components/AliPlayer';
/** /**
* *
@ -83,6 +85,8 @@ const Detail: React.FC<any> = () => {
const formRef = useRef<ProFormInstance>(); const formRef = useRef<ProFormInstance>();
const formRef1 = useRef<ProFormInstance>(); const formRef1 = useRef<ProFormInstance>();
const [info, setInfo] = useState<API.PbcTrainingClassesPageDTO>() const [info, setInfo] = useState<API.PbcTrainingClassesPageDTO>()
const [playAuth, setPlayAuth] = useState<string>('')
const [videoId, setVideoId] = useState<string>('')
const getInfo = () => { const getInfo = () => {
if (params.id) { if (params.id) {
@ -134,6 +138,8 @@ const Detail: React.FC<any> = () => {
const [updateModalVisible, handleUpdateModalVisible] = useState<boolean>(false); const [updateModalVisible, handleUpdateModalVisible] = useState<boolean>(false);
const [stepFormValues1, setStepFormValues1] = useState<API.PbcTrainingClassesVideo_>({}); const [stepFormValues1, setStepFormValues1] = useState<API.PbcTrainingClassesVideo_>({});
const [updateModalVisible1, handleUpdateModalVisible1] = useState<boolean>(false); const [updateModalVisible1, handleUpdateModalVisible1] = useState<boolean>(false);
const [showVideo, handleShowVideo] = useState<boolean>(false);
const [fileType, setFileType] = useState<string>('1');
const columns: ProColumns<API.PbcTrainingClassesChapter_>[] = [ const columns: ProColumns<API.PbcTrainingClassesChapter_>[] = [
{ {
@ -179,7 +185,7 @@ const Detail: React.FC<any> = () => {
} }
}} }}
> >
</Button> </Button>
</Access> </Access>
<Access key="remove" accessible={access.productCategoryDelete}> <Access key="remove" accessible={access.productCategoryDelete}>
@ -207,7 +213,21 @@ const Detail: React.FC<any> = () => {
rowKey="pbcId" rowKey="pbcId"
columns={[ columns={[
{ title: '视频名称', dataIndex: 'pbcVideoName' }, { title: '视频名称', dataIndex: 'pbcVideoName' },
{ title: '视频地址', dataIndex: 'pbcVideoAddress' }, { title: '视频地址', dataIndex: 'pbcVideoAddress', render:(text: any, record: API.PbcTrainingClassesVideo_) => {
return text ? record.pbcFileType === 1 ? <Button type="link" onClick={() => {
if (record.pbcId) {
getVideoAuthByIdUsingGet({pbcId: record.pbcId}).then(res => {
if (res.retcode && res.data) {
setPlayAuth(res.data)
setVideoId(text)
handleShowVideo(true)
} else {
message.error(res.retmsg)
}
})
}
}} ></Button> : <Button type="link" href={text} target='_blank'></Button> : null
} },
{ title: '创建时间', dataIndex: 'pbcCreateAt' }, { title: '创建时间', dataIndex: 'pbcCreateAt' },
{ {
title: '操作', title: '操作',
@ -229,7 +249,7 @@ const Detail: React.FC<any> = () => {
</Access> </Access>
<Access key="remove" accessible={access.productCategoryDelete}> <Access key="remove" accessible={access.productCategoryDelete}>
<Popconfirm <Popconfirm
title={`确定需要删除该视频?`} title={`确定需要删除该课程?`}
onConfirm={async () => { onConfirm={async () => {
const success = await handleRemoveVideo(record.pbcId); const success = await handleRemoveVideo(record.pbcId);
if (success) getInfo(); if (success) getInfo();
@ -403,7 +423,7 @@ const Detail: React.FC<any> = () => {
</ProForm> </ProForm>
<ProCard title="课程详情"> <ProCard title="课程详情">
<Access key="add" accessible={access.trainingClassesSave && isEdit}> <Access key="add" accessible={access.trainingClassesSave && isEdit}>
<Button type="primary" onClick={() => { <Button type="primary" style={{ marginBottom: 15 }} onClick={() => {
if (params.id) { if (params.id) {
handleUpdateModalVisible(true); handleUpdateModalVisible(true);
setStepFormValues({ setStepFormValues({
@ -470,7 +490,7 @@ const Detail: React.FC<any> = () => {
/> />
</ModalForm> </ModalForm>
<ModalForm <ModalForm
title={stepFormValues1.pbcId ? '编辑视频' : '新增视频'} title={stepFormValues1.pbcId ? '编辑文件' : '新增文件'}
open={updateModalVisible1} open={updateModalVisible1}
modalProps={{ modalProps={{
destroyOnClose: true, destroyOnClose: true,
@ -480,13 +500,34 @@ const Detail: React.FC<any> = () => {
width={500} width={500}
initialValues={{ initialValues={{
pbcVideoName: stepFormValues1.pbcVideoName, pbcVideoName: stepFormValues1.pbcVideoName,
pbcFileType: stepFormValues1.pbcFileType ? stepFormValues1.pbcFileType + '' : '1',
pbcVideoAddress: stepFormValues1.pbcVideoAddress ? [{
uid: '-1',
name: stepFormValues1.pbcVideoAddress.substring(stepFormValues1.pbcVideoAddress.lastIndexOf('/') + 1),
status: 'done',
url: stepFormValues1.pbcVideoAddress,
}] : []
}} }}
formRef={formRef1} formRef={formRef1}
onFinish={async (value: any) => { onFinish={async (value: any) => {
if (params.id) { if (params.id) {
let pbcVideoAddress = ""
if (value.pbcVideoAddress && value.pbcVideoAddress.length > 0) {
if (value.pbcVideoAddress[0].uid === '-1') {
pbcVideoAddress = value.pbcVideoAddress[0].url || '';
}
if (
value.pbcVideoAddress[0].response &&
value.pbcVideoAddress[0].response.retcode
) {
pbcVideoAddress = fileType === '1' ? value.pbcVideoAddress[0].response.data.videoId : value.pbcVideoAddress[0].response.data;
}
}
await addOrUpdateVideoUsingPost({ await addOrUpdateVideoUsingPost({
...stepFormValues1, ...stepFormValues1,
pbcVideoName: value.pbcVideoName, pbcVideoName: value.pbcVideoName,
pbcFileType: value.pbcFileType,
pbcVideoAddress
}).then((res) => { }).then((res) => {
if (res.retcode) { if (res.retcode) {
message.success('保存成功'); message.success('保存成功');
@ -511,13 +552,32 @@ const Detail: React.FC<any> = () => {
width="lg" width="lg"
name="pbcVideoName" name="pbcVideoName"
/> />
<ProFormRadio.Group
name="pbcFileType"
label="文件类型"
fieldProps={{
onChange(e) {
setFileType(e.target.value)
},
}}
options={[
{
label: '视频',
value: '1',
},
{
label: '文件',
value: '2',
},
]}
/>
<ProFormUploadButton <ProFormUploadButton
label="上传视频" label={fileType === '1' ? '上传视频' : '上传文件'}
name="pbcVideoAddress" name="pbcVideoAddress"
max={1} max={1}
fieldProps={{ fieldProps={{
name: 'file', name: 'file',
accept: 'video/mp4', accept: fileType === '1' ? 'video/mp4' : 'image/*,.pdf',
multiple: true, multiple: true,
headers: { headers: {
authorization: localStorage.getItem('token') ?? '', authorization: localStorage.getItem('token') ?? '',
@ -528,19 +588,45 @@ const Detail: React.FC<any> = () => {
if (info.file.response.retcode === 0) { if (info.file.response.retcode === 0) {
message.error(info.file.response.retmsg); message.error(info.file.response.retmsg);
formRef1.current?.setFieldValue('pbcVideoAddress', []) formRef1.current?.setFieldValue('pbcVideoAddress', [])
} else {
const { data } = info.file.response
if (fileType === '1') {
setPlayAuth(data.playAuth)
setVideoId(data.videoId)
}
} }
break; break;
default: default:
break; break;
} }
}, },
action: process.env.BASE_URL + '/b2b2c/pbcTrainingClassesVideo/uploadVideoAndGetInfo', action: process.env.BASE_URL + (fileType === '1' ? '/b2b2c/pbcTrainingClassesVideo/uploadVideoAndGetInfo' : '/oss/imgUpload'),
onPreview: async (file) => { onPreview: async (file) => {
if (file.uid === '-1') { if (fileType === '1') {
window.open(file.url); if (file.uid === '-1' && stepFormValues1.pbcId) {
} getVideoAuthByIdUsingGet({pbcId: stepFormValues1.pbcId}).then(res => {
if (file.response && file.response.retcode) { if (res.retcode && res.data && file.url) {
window.open(file.response.data); setPlayAuth(res.data)
setVideoId(file.url)
handleShowVideo(true)
} else {
message.error(res.retmsg)
}
})
}
if (file.response && file.response.retcode) {
const { data } = file.response
setPlayAuth(data.playAuth)
setVideoId(data.videoId)
handleShowVideo(true)
}
} else {
if (file.uid === '-1') {
window.open(file.url);
}
if (file.response && file.response.retcode) {
window.open(file.response.data);
}
} }
}, },
progress: { progress: {
@ -553,10 +639,19 @@ const Detail: React.FC<any> = () => {
}, },
}} }}
rules={[ rules={[
{ required: true, message: '请上传视频' }, { required: true, message: fileType === '1' ? '请上传视频' : '请上传文件' },
]} ]}
/> />
</ModalForm> </ModalForm>
<Modal
title={'预览视频'}
open={showVideo}
onCancel={() => handleShowVideo(false)}
width={800}
>
<AliPlayer playAuth={playAuth} vid={videoId} />
</Modal>
</PageContainer> </PageContainer>
); );
}; };

Loading…
Cancel
Save