| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
- import classNames from 'classnames';
- import LivePlayer from './index';
- import { Button, Dropdown, Menu, Popconfirm, Popover, Radio, Tooltip } from 'antd';
- import { createSchemaField } from '@formily/react';
- import { Form, FormItem, Input } from '@formily/antd';
- import { useFullscreen } from 'ahooks';
- import './index.less';
- import { DeleteOutlined, QuestionCircleOutlined } from '@ant-design/icons';
- import Service from './service';
- import MediaTool from '@/components/Player/mediaTool';
- import { createForm } from '@formily/core';
- import { onlyMessage } from '@/utils/util';
- import { Empty } from '@/components';
- type Player = {
- id?: string;
- url?: string;
- channelId?: string;
- key: string;
- show: boolean;
- };
- interface ScreenProps {
- url?: string;
- id?: string;
- channelId: string;
- className?: string;
- historyHandle?: (deviceId: string, channelId: string) => string;
- /**
- *
- * @param id 当前选中播发视频ID
- * @param type 当前操作动作
- */
- onMouseDown?: (deviceId: string, channelId: string, type: string) => void;
- /**
- *
- * @param id 当前选中播发视频ID
- * @param type 当前操作动作
- */
- onMouseUp?: (deviceId: string, channelId: string, type: string) => void;
- showScreen?: boolean;
- }
- const service = new Service();
- const DEFAULT_SAVE_CODE = 'screen-save';
- const useCallbackState = <T extends object>(olValue: T): [T, Function] => {
- const cbRef = useRef<Function>();
- const [players, setData] = useState<T>(olValue);
- useEffect(() => {
- if (cbRef.current) {
- cbRef.current(players);
- }
- }, [players]);
- return [
- players,
- function (value: T, callback?: Function) {
- cbRef.current = callback;
- setData(value);
- },
- ];
- };
- export default forwardRef((props: ScreenProps, ref) => {
- const [screen, setScreen] = useState(1);
- const [players, setPlayers] = useCallbackState<Player[]>([]);
- const [playerActive, setPlayerActive] = useState(0);
- const [historyList, setHistoryList] = useState<any>([]);
- const [visible, setVisible] = useState(false);
- const [loading, setLoading] = useState(false);
- const fullscreenRef = useRef(null);
- const [isFullscreen, { setFull }] = useFullscreen(fullscreenRef);
- const SchemaField = createSchemaField({
- components: {
- FormItem,
- Input,
- },
- });
- const historyForm = createForm();
- const reloadPlayer = useCallback(
- (id: string, channelId: string, url: string, index: number) => {
- const olPlayers = [...players];
- olPlayers[index] = {
- id: '',
- channelId: '',
- url: '',
- key: olPlayers[index].key,
- show: true,
- };
- const newPlayer = {
- id,
- url,
- channelId,
- key: olPlayers[index].key,
- show: true,
- };
- setPlayers([...olPlayers]);
- setTimeout(() => {
- olPlayers[index] = newPlayer;
- setPlayers(olPlayers);
- }, 1000);
- },
- [players],
- );
- const replaceVideo = useCallback(
- (id: string, channelId: string, url: string) => {
- const olPlayers = [...players];
- const newPlayer = {
- id,
- url,
- channelId,
- key: olPlayers[playerActive].key,
- show: true,
- };
- if (olPlayers[playerActive].url === url) {
- // 刷新视频
- reloadPlayer(id, channelId, url, playerActive);
- } else {
- olPlayers[playerActive] = newPlayer;
- setPlayers(olPlayers);
- }
- if (playerActive === screen - 1) {
- // 当前位置为分屏最后一位
- setPlayerActive(0);
- } else {
- setPlayerActive(playerActive + 1);
- }
- },
- [players, playerActive, screen, props.showScreen],
- );
- const handleHistory = useCallback(
- (item: any) => {
- if (props.historyHandle) {
- const log = JSON.parse(item.content || '{}');
- setScreen(log.screen);
- const oldPlayers = [...players];
- setPlayers(
- oldPlayers.map((oldPlayer, index) => {
- oldPlayer.show = false;
- if (index < log.screen) {
- const { deviceId, channelId } = log.players[index];
- return {
- ...oldPlayer,
- id: deviceId,
- channelId: deviceId,
- url: deviceId ? props.historyHandle!(deviceId, channelId) : '',
- show: true,
- };
- }
- return oldPlayer;
- }),
- );
- }
- },
- [players, props.historyHandle],
- );
- const getHistory = async () => {
- const resp = await service.history.query(DEFAULT_SAVE_CODE);
- if (resp.status === 200) {
- setHistoryList(resp.result);
- }
- };
- const deleteHistory = async (id: string) => {
- const resp = await service.history.remove(DEFAULT_SAVE_CODE, id);
- if (resp.status === 200) {
- getHistory();
- setVisible(false);
- }
- };
- const saveHistory = useCallback(async () => {
- const historyValue = await historyForm.submit<{ alias: string }>();
- const param = {
- name: historyValue.alias,
- content: JSON.stringify({
- screen: screen,
- players: players.map((item) => ({ deviceId: item.id, channelId: item.channelId })),
- }),
- };
- setLoading(true);
- const resp = await service.history.save(DEFAULT_SAVE_CODE, param);
- setLoading(false);
- if (resp.status === 200) {
- setVisible(false);
- getHistory();
- onlyMessage('保存成功!');
- } else {
- onlyMessage('保存失败', 'error');
- // message.error('保存失败');
- }
- }, [players, screen, historyForm]);
- const mediaInit = () => {
- const newArr = [];
- for (let i = 0; i < 9; i++) {
- newArr.push({
- id: '',
- channelId: '',
- url: '',
- key: 'time_' + new Date().getTime() + i,
- show: i === 0,
- });
- }
- setPlayers(newArr);
- };
- const screenChange = (index: number) => {
- setPlayers(
- players.map((item, i) => {
- return {
- id: '',
- channelId: '',
- url: '',
- updateTime: 0,
- key: item.key,
- show: i < index,
- };
- }),
- );
- setPlayerActive(0);
- setScreen(index);
- };
- useEffect(() => {
- // 查看当前 播放视频位置,如果当前视频位置有视频在播放,则替换
- if (props.url && props.id) {
- replaceVideo(props.id, props.channelId, props.url);
- }
- }, [props.url]);
- useEffect(() => {
- if (props.showScreen !== false) {
- getHistory();
- }
- mediaInit();
- }, []);
- useImperativeHandle(ref, () => ({
- replaceVideo: replaceVideo,
- }));
- const screenClass = `screen-${screen}`;
- const DropdownMenu = (
- <Menu>
- {historyList.length ? (
- historyList.map((item: any) => {
- return (
- <Menu.Item key={item.id}>
- <span
- onClick={() => {
- handleHistory(item);
- }}
- style={{ padding: '0 4px' }}
- >
- {item.name}
- </span>
- <Popconfirm
- title={'确认删除'}
- onConfirm={(e) => {
- e?.stopPropagation();
- deleteHistory(item.key);
- }}
- >
- <DeleteOutlined
- onClick={(e) => {
- e.stopPropagation();
- }}
- />
- </Popconfirm>
- </Menu.Item>
- );
- })
- ) : (
- <Empty />
- )}
- </Menu>
- );
- return (
- <div className={classNames('live-player-warp', props.className)}>
- <div className={'live-player-content'}>
- {props.showScreen !== false && (
- <div className={'player-screen-tool'}>
- <>
- <div>
- <Radio.Group
- options={[
- { label: '单屏', value: 1 },
- { label: '四分屏', value: 4 },
- { label: '九分屏', value: 9 },
- { label: '全屏', value: 0 },
- ]}
- value={screen}
- onChange={(e) => {
- if (e.target.value) {
- screenChange(e.target.value);
- } else {
- // 全屏操作
- setFull();
- }
- }}
- optionType={'button'}
- buttonStyle={'solid'}
- />
- {/*<Tooltip*/}
- {/* title={''}*/}
- {/*>*/}
- {/* <QuestionCircleOutlined />*/}
- {/*</Tooltip>*/}
- </div>
- <div
- className={'screen-tool-save'}
- style={{ display: 'flex', flexDirection: 'row-reverse', alignItems: 'center' }}
- >
- <Popover
- content={
- <Form style={{ width: '217px' }} form={historyForm}>
- <SchemaField
- schema={{
- type: 'object',
- properties: {
- alias: {
- 'x-decorator': 'FormItem',
- 'x-component': 'Input.TextArea',
- 'x-validator': [
- {
- max: 64,
- message: '最多可输入64个字符',
- },
- {
- required: true,
- message: '请输入名称',
- },
- ],
- },
- },
- }}
- />
- <Button
- type={'primary'}
- onClick={saveHistory}
- loading={loading}
- style={{ width: '100%', marginRight: 16 }}
- >
- 保存
- </Button>
- </Form>
- }
- title="分屏名称"
- trigger="click"
- visible={visible}
- onVisibleChange={(v) => {
- setVisible(v);
- }}
- >
- <Dropdown.Button
- type={'primary'}
- overlay={DropdownMenu}
- onClick={() => {
- setVisible(true);
- }}
- >
- 保存
- </Dropdown.Button>
- </Popover>
- <Tooltip title={'可保存分屏配置记录'}>
- <QuestionCircleOutlined style={{ marginLeft: 8 }} />
- </Tooltip>
- </div>
- </>
- </div>
- )}
- <div className={'player-body'}>
- <div className={classNames('player-screen', screenClass)} ref={fullscreenRef}>
- {players.map((item, index) => {
- return (
- <div
- key={item.key}
- className={classNames('player-screen-item', {
- active: props.showScreen !== false && playerActive === index && !isFullscreen,
- 'full-screen': isFullscreen,
- })}
- style={{ display: item.show ? 'block' : 'none' }}
- onClick={() => {
- setPlayerActive(index);
- }}
- >
- <div
- className={'media-btn-refresh'}
- style={{ display: item.url ? 'block' : 'none' }}
- onClick={(e) => {
- e.stopPropagation();
- if (item.url) {
- reloadPlayer(item.id!, item.channelId!, item.url!, index);
- }
- }}
- >
- 刷新
- </div>
- <LivePlayer url={item.url} />
- </div>
- );
- })}
- </div>
- </div>
- </div>
- <MediaTool
- onMouseDown={(type) => {
- const { id, channelId } = players[playerActive];
- if (id && channelId && props.onMouseDown) {
- props.onMouseDown(id, channelId, type);
- }
- }}
- onMouseUp={(type) => {
- const { id, channelId } = players[playerActive];
- if (props.onMouseUp && id && channelId) {
- props.onMouseUp(id, channelId, type);
- }
- }}
- />
- </div>
- );
- });
|