dev-v2
Joe 8 months ago
parent d35a19adbc
commit c6959ddb3e

@ -54,6 +54,7 @@
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@umijs/route-utils": "^2.2.2",
"aliyun-aliplayer": "^2.29.1",
"antd": "^5.2.2",
"antd-img-crop": "^4.23.0",
"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,
ProFormDigit,
ProFormInstance,
ProFormRadio,
ProFormSelect,
ProFormText,
ProFormUploadButton,
ProTable,
} from '@ant-design/pro-components';
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 React, { useRef, useState } from 'react';
import { Access, history, useAccess, useParams, useSearchParams } from '@umijs/max';
import { getClassesTypeListForAdminUsingPost } from '@/services/pop-b2b2c/pbcTrainingClassesTypeController';
import { addOrUpdateClassUsingPost, classDetailForAdminUsingGet } from '@/services/pop-b2b2c/pbcTrainingClassesController';
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 formRef1 = useRef<ProFormInstance>();
const [info, setInfo] = useState<API.PbcTrainingClassesPageDTO>()
const [playAuth, setPlayAuth] = useState<string>('')
const [videoId, setVideoId] = useState<string>('')
const getInfo = () => {
if (params.id) {
@ -134,6 +138,8 @@ const Detail: React.FC<any> = () => {
const [updateModalVisible, handleUpdateModalVisible] = useState<boolean>(false);
const [stepFormValues1, setStepFormValues1] = useState<API.PbcTrainingClassesVideo_>({});
const [updateModalVisible1, handleUpdateModalVisible1] = useState<boolean>(false);
const [showVideo, handleShowVideo] = useState<boolean>(false);
const [fileType, setFileType] = useState<string>('1');
const columns: ProColumns<API.PbcTrainingClassesChapter_>[] = [
{
@ -179,7 +185,7 @@ const Detail: React.FC<any> = () => {
}
}}
>
</Button>
</Access>
<Access key="remove" accessible={access.productCategoryDelete}>
@ -207,7 +213,21 @@ const Detail: React.FC<any> = () => {
rowKey="pbcId"
columns={[
{ 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: '操作',
@ -229,7 +249,7 @@ const Detail: React.FC<any> = () => {
</Access>
<Access key="remove" accessible={access.productCategoryDelete}>
<Popconfirm
title={`确定需要删除该视频?`}
title={`确定需要删除该课程?`}
onConfirm={async () => {
const success = await handleRemoveVideo(record.pbcId);
if (success) getInfo();
@ -403,7 +423,7 @@ const Detail: React.FC<any> = () => {
</ProForm>
<ProCard title="课程详情">
<Access key="add" accessible={access.trainingClassesSave && isEdit}>
<Button type="primary" onClick={() => {
<Button type="primary" style={{ marginBottom: 15 }} onClick={() => {
if (params.id) {
handleUpdateModalVisible(true);
setStepFormValues({
@ -470,7 +490,7 @@ const Detail: React.FC<any> = () => {
/>
</ModalForm>
<ModalForm
title={stepFormValues1.pbcId ? '编辑视频' : '新增视频'}
title={stepFormValues1.pbcId ? '编辑文件' : '新增文件'}
open={updateModalVisible1}
modalProps={{
destroyOnClose: true,
@ -480,13 +500,34 @@ const Detail: React.FC<any> = () => {
width={500}
initialValues={{
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}
onFinish={async (value: any) => {
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({
...stepFormValues1,
pbcVideoName: value.pbcVideoName,
pbcFileType: value.pbcFileType,
pbcVideoAddress
}).then((res) => {
if (res.retcode) {
message.success('保存成功');
@ -511,13 +552,32 @@ const Detail: React.FC<any> = () => {
width="lg"
name="pbcVideoName"
/>
<ProFormRadio.Group
name="pbcFileType"
label="文件类型"
fieldProps={{
onChange(e) {
setFileType(e.target.value)
},
}}
options={[
{
label: '视频',
value: '1',
},
{
label: '文件',
value: '2',
},
]}
/>
<ProFormUploadButton
label="上传视频"
label={fileType === '1' ? '上传视频' : '上传文件'}
name="pbcVideoAddress"
max={1}
fieldProps={{
name: 'file',
accept: 'video/mp4',
accept: fileType === '1' ? 'video/mp4' : 'image/*,.pdf',
multiple: true,
headers: {
authorization: localStorage.getItem('token') ?? '',
@ -528,19 +588,45 @@ const Detail: React.FC<any> = () => {
if (info.file.response.retcode === 0) {
message.error(info.file.response.retmsg);
formRef1.current?.setFieldValue('pbcVideoAddress', [])
} else {
const { data } = info.file.response
if (fileType === '1') {
setPlayAuth(data.playAuth)
setVideoId(data.videoId)
}
}
break;
default:
break;
}
},
action: process.env.BASE_URL + '/b2b2c/pbcTrainingClassesVideo/uploadVideoAndGetInfo',
action: process.env.BASE_URL + (fileType === '1' ? '/b2b2c/pbcTrainingClassesVideo/uploadVideoAndGetInfo' : '/oss/imgUpload'),
onPreview: async (file) => {
if (file.uid === '-1') {
window.open(file.url);
}
if (file.response && file.response.retcode) {
window.open(file.response.data);
if (fileType === '1') {
if (file.uid === '-1' && stepFormValues1.pbcId) {
getVideoAuthByIdUsingGet({pbcId: stepFormValues1.pbcId}).then(res => {
if (res.retcode && res.data && file.url) {
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: {
@ -553,10 +639,19 @@ const Detail: React.FC<any> = () => {
},
}}
rules={[
{ required: true, message: '请上传视频' },
{ required: true, message: fileType === '1' ? '请上传视频' : '请上传文件' },
]}
/>
</ModalForm>
<Modal
title={'预览视频'}
open={showVideo}
onCancel={() => handleShowVideo(false)}
width={800}
>
<AliPlayer playAuth={playAuth} vid={videoId} />
</Modal>
</PageContainer>
);
};

Loading…
Cancel
Save