You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

633 lines
20 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import React, { useState, useRef } from 'react';
import { PlusOutlined, TagsOutlined, UploadOutlined } from '@ant-design/icons';
import { Button, message, Modal, Col, Row, Space, Upload } from 'antd';
import { PageContainer, FooterToolbar } from '@ant-design/pro-layout';
import type { ProColumns, ActionType } from '@ant-design/pro-table';
import ProTable from '@ant-design/pro-table';
import { BetaSchemaForm, ModalForm } from '@ant-design/pro-form';
import type { ProDescriptionsItemProps } from '@ant-design/pro-descriptions';
import ProDescriptions from '@ant-design/pro-descriptions';
import type { FormValueType } from './components/UpdateForm';
import { queryTagList, saveCourse, removeCourse, queryCourseList } from './service';
import type { TableListItem, TableListPagination } from './data';
import Tags from './components/Tags';
import type { DataItem } from '@antv/data-set/lib/transform/tag-cloud';
import { VideoJS } from './components/VideoJS';
import { v4 as uuidv4 } from 'uuid';
const uuid = uuidv4();
/**
* 添加课程
*
* @param fields
*/
const handleAdd = async (fields: TableListItem) => {
const hide = message.loading('正在添加');
try {
await saveCourse({ ...fields, attachment_filesize: 0, });
hide();
message.success('添加成功');
return true;
} catch (error) {
hide();
message.error('添加失败请重试!');
return false;
}
};
/**
* 更新课程
*
* @param fields
*/
const handleUpdate = async (fields: FormValueType, currentRow?: TableListItem) => {
const hide = message.loading('正在配置');
try {
await saveCourse({
...currentRow,
...fields,
});
hide();
message.success('配置成功');
return true;
} catch (error) {
hide();
message.error('配置失败请重试!');
return false;
}
};
/**
* 删除课程(接口不支持批量)
* 参数为记录数组
* @param selectedRows
*/
const handleRemove = async (selectedRows: TableListItem[]) => {
const hide = message.loading('正在删除');
if (!selectedRows) return true;
console.log('key', selectedRows);
try {
await removeCourse({
key: selectedRows.map((row) => row.key),
});
hide();
message.success('删除成功,即将刷新');
return true;
} catch (error) {
console.log('error', error)
hide();
message.error('删除失败,请重试');
return false;
}
};
const CourseList: React.FC = () => {
/** 新建窗口的弹窗 */
const [tagsModalVisible, handleTagsModalVisible] = useState<boolean>(false);
/** 更新窗口的弹窗 */
const [createModalVisible, handleCreateModalVisible] = useState<boolean>(false);
const [detailModalVisible, handleDetailModalVisible] = useState<boolean>(false);
const [updateModalVisible, handleUpdateModalVisible] = useState<boolean>(false);
const actionRef = useRef<ActionType>();
const playerRef = React.useRef<any>(null); // 播放器引用
const [currentRow, setCurrentRow] = useState<TableListItem>();
const [selectedRowsState, setSelectedRows] = useState<TableListItem[]>([]);
const [uploadFileName, SetUploadFileName] = useState<string>();
const [uploadFileExt, SetUploadFileExt] = useState<string>();
/** 列表项定义 */
const columns: ProColumns<TableListItem>[] = [
{
title: '序号',
key: 'index',
valueType: 'indexBorder',
render: (text: React.ReactNode, _: any, index: number) => {
if (actionRef && actionRef?.current && actionRef?.current?.pageInfo) {
return `${(actionRef?.current?.pageInfo?.current - 1) * actionRef.current.pageInfo?.pageSize + (index + 1)
}`;
} else {
return '';
}
},
width: 48,
},
{
title: '课程名称',
dataIndex: 'course_name',
valueType: 'text',
hideInTable: false,
hideInDescriptions: false,
hideInForm: false,
hideInSearch: true,
formItemProps: {
// 参照 https://ant.design/components/form-cn/#Rule
rules: [
{
required: true,
message: '此项为必填项',
},
{
max: 50,
message: '最大长度为50字符',
}
],
}, // 传递给 Form.Item 的配置
},
{
title: '主讲教师',
dataIndex: 'lecture_teacher',
valueType: 'text',
sorter: false,
hideInTable: false,
hideInForm: false,
hideInSearch: true,
width: 100,
renderText: (val: string) => `${val}`,
formItemProps: {
// 参照 https://ant.design/components/form-cn/#Rule
rules: [
{
required: true,
message: '此项为必填项',
},
{
max: 20,
message: '最大长度为20字符',
}
],
}, // 传递给 Form.Item 的配置
},
{
title: '课时',
dataIndex: 'course_minutes',
valueType: 'text',
sorter: false,
hideInTable: true,
hideInForm: false,
hideInSearch: true,
renderText: (val: string) => `${val}`,
formItemProps: {
// 参照 https://ant.design/components/form-cn/#Rule
rules: [
{
required: true,
message: '此项为必填项',
},
/*{
max: 4,
message: '最大长度为4字符',
}*/
],
}, // 传递给 Form.Item 的配置
},
{
title: '课程描述',
valueType: 'textarea',
dataIndex: 'course_describe',
sorter: false,
hideInTable: false,
hideInForm: false,
hideInSearch: true,
renderText: (val: string) => `${val}`,
formItemProps: {
// 参照 https://ant.design/components/form-cn/#Rule
rules: [
{
required: true,
message: '此项为必填项',
},
{
max: 150,
message: '最大长度为150字符',
}
],
}, // 传递给 Form.Item 的配置
},
{
title: '标签',
dataIndex: 'tag_id',
sorter: false,
valueType: 'select',
hideInForm: false,
request: async () => {
const { data: Items } = await queryTagList({});
const tags = []
for (let i = 0; i < Items.length; i++) {
tags.push({ label: Items[i].tag_name, value: Items[i].tag_id })
}
return tags;
},
formItemProps: {
// 参照 https://ant.design/components/form-cn/#Rule
rules: [
{
required: true,
message: '此项为必填项',
},
],
}, // 传递给 Form.Item 的配置
},
{
title: '视频',
dataIndex: 'attachment_json',
sorter: false,
hideInTable: true,
hideInForm: false,
hideInSearch: true,
hideInDescriptions: true,
renderFormItem: (item, { defaultRender, ...rest }, form) => (
<Upload
action='/dsideal_yy/res/plupload/'
defaultFileList={item.initialValue ? [item.initialValue] : []}
beforeUpload={(file) => {
console.log('file', file)
// 获取文件名
SetUploadFileName(file?.name);
// 获取最后一个.的位置
const index = file?.name.lastIndexOf(".");
// 获取后缀
SetUploadFileExt(file?.name.substr(index + 1));
}}
data={{
name: uploadFileName,
chunk: 0,
chunks: 1,
key: `down/Syzx/${uuid?.substr(0, 2)}/${uuid}.${uploadFileExt}`
}}
maxCount={1}
multiple={false}
>
<Button icon={<UploadOutlined />}></Button>
</Upload>
),
formItemProps: {
// 参照 https://ant.design/components/form-cn/#Rule
rules: [
{
required: true,
message: '此项为必填项',
},
],
}, // 传递给 Form.Item 的配置
//renderText: (val: string) => `${val}M`,
},
{
title: '视频大小',
dataIndex: 'attachment_filesize',
sorter: false,
hideInForm: true,
hideInSearch: true,
width: 100,
renderText: (val: string) => `${val}M`,
formItemProps: {
// 参照 https://ant.design/components/form-cn/#Rule
rules: [
{
required: true,
message: '此项为必填项',
},
],
}, // 传递给 Form.Item 的配置
},
{
title: '创建时间',
dataIndex: 'create_time',
valueType: 'dateRange',
sorter: true,
hideInTable: true,
hideInForm: true,
hideInSearch: false,
//renderText: (val: string) => `${val}`,
},
{
title: '操作',
dataIndex: 'option',
valueType: 'option',
width: 150,
render: (_dom: any, record: React.SetStateAction<TableListItem | undefined>) => [
<a
key="detail"
onClick={() => {
setCurrentRow(record);
handleDetailModalVisible(true);
}}
>
</a>,
<a
key="update"
onClick={() => {
setCurrentRow(record);
handleUpdateModalVisible(true);
}}
>
</a>,
<a
key="remove"
onClick={() => {
handleRemove([{ key: record?.course_id }]); // 调用批量删除函数如果接口不支持批量需要在service中处理
setSelectedRows([]);
actionRef.current?.reloadAndRest?.();
}}>
</a>,
],
},
];
/** 获取列数据初始值 */
const getInitialValues = (cols: any[], vals: any) => {
console.log('getInitialValues-columns', columns);
console.log('getInitialValues-values', vals);
const initialValues: any[] = [];
cols.forEach((column: { dataIndex: string }) => {
const key: any = column?.dataIndex || '';
initialValues.push({ ...column, initialValue: key ? vals[key] : '' });
});
console.log('initialValues::', initialValues);
return initialValues || [];
};
return (
<PageContainer>
<ProTable<TableListItem, TableListPagination>
headerTitle={false}
actionRef={actionRef}
rowKey="course_id"
options={false}
search={{
labelWidth: 120,
}}
toolBarRender={() => [
<Button
type="primary"
key="primary"
onClick={() => {
handleTagsModalVisible(true);
}}
>
<TagsOutlined />
</Button>,
<Button
type="primary"
key="primary"
onClick={() => {
handleCreateModalVisible(true);
}}
>
<PlusOutlined />
</Button>,
]}
request={async (value) => {
console.log('value', value)
const { create_time } = value;
if (create_time) {
value.begin_time = create_time[0]
value.end_time = create_time[1]
}
const { data } = await queryCourseList({
...value,
page_number: value?.current || 1,
page_size: value?.pageSize,
});
return {
current: data?.page_number,
data: data?.list,
pageSize: data?.page_size,
success: true,
total: data?.total_row || 0,
};
}}
// dataSource={list}
columns={columns}
rowSelection={false}
/>
{selectedRowsState?.length > 0 && (
<FooterToolbar
extra={
<div>
{' '}
<a
style={{
fontWeight: 600,
}}
>
{selectedRowsState.length}
</a>{' '}
&nbsp;&nbsp;
</div>
}
>
<Button
onClick={async () => {
await handleRemove(selectedRowsState);
setSelectedRows([]);
actionRef.current?.reloadAndRest?.();
}}
>
</Button>
</FooterToolbar>
)}
<Modal
title={currentRow?.course_name || '课程详细'}
width="50%"
visible={detailModalVisible}
onCancel={() => {
playerRef.current?.pause();
//videoRef.current.play()
console.log('playerRef:', playerRef.current)
setCurrentRow(undefined); // 设置当前行
handleDetailModalVisible(false);
}}
footer={null}
centered
>
{console.log('currentRow', currentRow)}
{console.log('columns', columns.slice(0, columns.length - 1))}
{currentRow?.course_id && (
<Row>
<Col span={14}>
<VideoJS
options={{
controls: true,
playbackRates: [0.7, 1.0, 1.5, 2.0], // 播放速度
autoplay: false, // 如果true,浏览器准备好时开始回放。
muted: false, // 默认情况下将会消除任何音频。
loop: true, // 导致视频一结束就重新开始。
preload: 'auto', // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
language: 'zh-CN',
aspectRatio: '4:3', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3"
fluid: true, // 当true时Video.js player将拥有流体大小。换句话说它将按比例缩放以适应其容器。
sources: [
{
src: (currentRow?.attachment_json?.url) || '/dsideal_yy/html/down/M3u8/2D/2D99BF1D-2F37-47FB-8A24-45112A236B8F.m3u8', // 测试地址后续请删除
type: 'application/x-mpegURL'
}
],
poster: currentRow?.attachment_json?.img || '', // 你的封面地址
width: document.documentElement.clientWidth,
notSupportedMessage: '此视频暂无法播放,请稍后再试', // 允许覆盖Video.js无法播放媒体源时显示的默认信息。
controlBar: {
timeDivider: true,
durationDisplay: true,
remainingTimeDisplay: true,
fullscreenToggle: true // 全屏按钮
}
}}
onReady={(play: any) => {
//console.log('play====', play);
playerRef.current = play
play.play();
play.on("timeupdate", function (event) {
//const _timeCurrent = Date.parse(new Date().toString()) / 1000; // 当前时间
//setTimeUpdateState(_timeCurrent); //timeUpdateState
console.log('play--', play.currentTime())
console.log('play-%-', parseInt(play.currentTime()) % 15)
if (parseInt(play.currentTime()) % 15 === 0) { // 每15秒更新进度
console.log()
}
//var currentTime = parseInt(this.currentTime()); //当前时间
//var duration = this.duration(); //视频时常
//var percent = (currentTime / duration * 100).toFixed(0) + "%";
//console.log('event',event);
//$("#current").text(this.currentTime());
//$("#duration").text(duration);
})
}}
/>
</Col>
<Col span={10}>
<ProDescriptions<TableListItem>
column={1}
/* title={currentRow?.name} */
dataSource={currentRow}
/*
request={async () => ({
data: currentRow || {},
})}*/
params={{
id: currentRow?.course_id,
}}
columns={
columns.slice(0, columns.length - 1) as ProDescriptionsItemProps<TableListItem>[]
}
style={{ padding: '0 24px' }}
/>
</Col>
</Row>
)}
</Modal>
<ModalForm
title="标签管理"
visible={tagsModalVisible}
onVisibleChange={handleTagsModalVisible}
>
<Tags />
</ModalForm>
<Modal
title="新建课程"
//
width="60%"
visible={createModalVisible}
destroyOnClose
onCancel={() => {
handleCreateModalVisible(false);
}}
footer={null}
>
<BetaSchemaForm<DataItem>
layout="horizontal"
layoutType="Form"
labelCol={{ span: 8 }}
wrapperCol={{ span: 12 }}
onFinish={async (values: any) => {
// 表单处理
//console.log('columns:', columns);
console.log('values:', values);
console.log('uploadFileName', uploadFileName)
//return false;
// values.attachment_json.response.file.response.url
//console.log('currentRow', currentRow)
await handleAdd({
...values,
//course_id: currentRow?.course_id,
attachment_json: `{"img":"", "name": "${values.attachment_json.file.name}", "url": "down/M3u8/${uuid?.substr(0, 2)}/${uuid}.m3u8"}`
});
handleCreateModalVisible(false); // 隐藏创建窗口
actionRef.current?.reloadAndRest?.();
}}
submitter={{
render: (props, doms) => (
<Row>
<Col span={12} offset={8}>
<Space>{doms}</Space>
</Col>
</Row>
),
}}
// action = ''
title="新建"
columns={columns || []}
/>
</Modal>
<Modal
title="编辑"
width="60%"
visible={updateModalVisible}
destroyOnClose
onCancel={() => {
handleUpdateModalVisible(false); // 隐藏编辑窗口
}}
footer={null}
>
{currentRow?.course_id && (
<BetaSchemaForm<DataItem>
layout="horizontal"
layoutType="Form"
labelCol={{ span: 8 }}
wrapperCol={{ span: 12 }}
onFinish={async (values) => {
alert(0)
console.log('currentRow', currentRow)
await handleUpdate({
...values,
course_id: currentRow?.course_id,
//attachment_json: `{"img":"", "name": "${values.attachment_json.file.name}", "url": "down/M3u8/${uuid?.substr(0, 2)}/${uuid}.m3u8"}`
});
handleUpdateModalVisible(false); // 隐藏编辑窗口
console.log(values);
}}
submitter={{
render: (props, doms) => (
<Row>
<Col span={12} offset={8}>
<Space>{doms}</Space>
</Col>
</Row>
),
}}
// action = ''
title="编辑"
columns={getInitialValues(columns, currentRow)}
/>
)}
</Modal>
</PageContainer>
);
};
export default CourseList;