413 lines
15 KiB
TypeScript
413 lines
15 KiB
TypeScript
import styles from '@/page/Home/Index/index.module.scss'
|
|
import { useEffect, useState, useRef } from "react";
|
|
import Operation from '@/components/Operation';
|
|
import { Button, Input, Modal, Pagination, Empty, message, Popover, Popconfirm, DatePicker } from "antd";
|
|
import { GetRoom, PostRoom, GetCheckoutRoomNum, GetRoomRtcToken, DeleteRoom, GetRecord } from '@/api/Home/Index';
|
|
import ImageUrl from '@/utils/package/imageUrl'
|
|
import { ExclamationCircleFilled, ReloadOutlined } from '@ant-design/icons';
|
|
import JoinSetting from '@/components/JoinSetting';
|
|
import { storage } from '@/utils';
|
|
import { PostRefresh } from '@/api/Login';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { role } from '@/config/role';
|
|
import dayjs from 'dayjs';
|
|
import StupWizard from '@/components/StupWizard';
|
|
const fs = require('fs').promises;
|
|
const { exec } = require('child_process');
|
|
const { RangePicker } = DatePicker;
|
|
const { confirm } = Modal;
|
|
const Index: React.FC = () => {
|
|
const navigate = useNavigate();
|
|
const [list, setList] = useState({
|
|
data: [],
|
|
total: 0,
|
|
pageIndex: 1,
|
|
pageSize: 12,
|
|
})
|
|
const [createRoomModal, setCreateRoomModal] = useState(false)
|
|
const [timeSelectModal, setTimeSelectModal] = useState(false)
|
|
const [createRoomFrom, setCreateRoomFrom] = useState<{ roomName: string, roomNum: string }>({
|
|
roomName: "",
|
|
roomNum: ""
|
|
})
|
|
const joinSettingRef = useRef<any>();
|
|
const stupWizardRef = useRef<any>();
|
|
const [user, setUser] = useState<any>({});
|
|
const [currentRoomInfo, setCurrentRoomInfo] = useState<any>({});
|
|
const userInfo = JSON.parse(storage.getItem('user') as string)
|
|
useEffect(() => {
|
|
setUser(userInfo)
|
|
}, [])
|
|
useEffect(() => {
|
|
let time = null as any
|
|
if (time) {
|
|
clearInterval(time)
|
|
} else {
|
|
time = setInterval(() => {
|
|
getRoomList()
|
|
}, 1000 * 30)
|
|
}
|
|
getRoomList()
|
|
return () => {
|
|
clearInterval(time)
|
|
}
|
|
}, [list.pageIndex]);
|
|
const getRoomList = async (): Promise<void> => {
|
|
await GetRoom({
|
|
pageIndex: list.pageIndex,
|
|
pageSize: list.pageSize,
|
|
}).then(res => {
|
|
if (res.code === 200) {
|
|
setList({
|
|
...list,
|
|
total: res.data.total,
|
|
data: res.data.items,
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
const copyRoomNum = (roomNum: string): void => {
|
|
window.electron.setWriteText(roomNum)
|
|
message.success('复制成功')
|
|
}
|
|
|
|
const isGetCheckoutRoomNum = async (roomNum: string, callBack: Function): Promise<void> => {
|
|
await GetCheckoutRoomNum(roomNum).then(res => {
|
|
if (res.code === 200) {
|
|
callBack(res.data)
|
|
}
|
|
})
|
|
}
|
|
const getRoomRtcToken = async (roomNum: string, callBack: Function): Promise<void> => {
|
|
Promise.all([GetRoomRtcToken(roomNum), GetRoomRtcToken(roomNum + 'a')]).then(res => {
|
|
if (res[0].code === 200 && res[1].code === 200) {
|
|
callBack({
|
|
token: res[0].data,
|
|
tokenA: res[1].data,
|
|
})
|
|
}
|
|
})
|
|
}
|
|
const postRefresh = async (callBack: Function): Promise<void> => {
|
|
await PostRefresh(user.refresh_token).then(res => {
|
|
if (res.code === 200) {
|
|
storage.setItem('user', JSON.stringify(res.data))
|
|
storage.setItem('userLogin', true)
|
|
callBack(res.data)
|
|
}
|
|
})
|
|
}
|
|
|
|
const changeOpen = (index: number, bool: boolean): void => {
|
|
const newList = [...list.data] as any;
|
|
newList[index].open = bool
|
|
setList({
|
|
...list,
|
|
data: newList
|
|
})
|
|
}
|
|
|
|
const fileUpLoad = async (data: { url: string, content: string, fileName: string }): Promise<void> => {
|
|
const setting = await JSON.parse(storage.getItem('setting') as string)
|
|
try {
|
|
const response = await fetch(data.url);
|
|
const arrayBuffer = await response.arrayBuffer();
|
|
const buffer = Buffer.from(arrayBuffer);
|
|
await fs.writeFile(`${setting.shareFilesPath}\\${data.fileName}`, buffer, {});
|
|
confirm({
|
|
title: '提示',
|
|
icon: <ExclamationCircleFilled />,
|
|
content: data.content,
|
|
centered: true,
|
|
okText: '打开文件夹',
|
|
cancelText: '关闭',
|
|
async onOk() {
|
|
await fs.access(setting.shareFilesPath, fs.constants.F_OK);
|
|
if (process.platform === 'win32') {
|
|
exec(`explorer "${setting.shareFilesPath}"`);
|
|
} else if (process.platform === 'darwin') {
|
|
exec(`open "${setting.shareFilesPath}"`);
|
|
}
|
|
},
|
|
onCancel() {
|
|
}
|
|
})
|
|
} catch (error: any) {
|
|
if (error.code === 'ENOENT') {
|
|
message.error({
|
|
content: <div>文件夹不存在 <span style={{ color: '#606fc7', cursor: 'pointer' }} onClick={() => {
|
|
stupWizardRef.current.changeModal(4)
|
|
}}>前往设置</span></div>
|
|
})
|
|
return
|
|
} else {
|
|
message.error(error)
|
|
}
|
|
}
|
|
}
|
|
return (
|
|
<>
|
|
<div className={styles.index}>
|
|
<div className={styles.indexOperation}>
|
|
<Operation></Operation>
|
|
</div>
|
|
<div className={styles.indexBtns}>
|
|
{user?.roleId === '1' ? <Button type="primary"
|
|
icon={<img src={ImageUrl.icon8} alt="" />}
|
|
className='m-ant-btn drag'
|
|
onClick={() => {
|
|
setCreateRoomFrom({
|
|
roomName: "",
|
|
roomNum: ""
|
|
})
|
|
setCreateRoomModal(true)
|
|
}}
|
|
style={{ marginRight: '22px' }}
|
|
>
|
|
新建会议室
|
|
</Button> : null}
|
|
<Button type="primary" onClick={() => {
|
|
joinSettingRef.current.changeModal()
|
|
}}
|
|
icon={<img src={ImageUrl.icon8} alt="" />}
|
|
className={`${styles.indexBtnsJoin} drag`}>
|
|
加入会议
|
|
</Button>
|
|
</div>
|
|
<div className={styles.indexContent}>
|
|
<div className={`drag ${styles.indexContentTitle}`}>
|
|
<span>会议室列表</span>
|
|
<ReloadOutlined
|
|
title='刷新'
|
|
style={{
|
|
cursor: 'pointer',
|
|
}}
|
|
onClick={() => {
|
|
message.success('刷新成功')
|
|
getRoomList()
|
|
}}
|
|
/>
|
|
</div>
|
|
{list.data.length ? <div className={`drag ${styles.indexContentList}`}>
|
|
{list.data.map((item: any, index: number) => {
|
|
return (
|
|
<div className={`${styles.indexContentListItem}`} key={index}>
|
|
<div>
|
|
<div>{item.roomName}</div>
|
|
<div>
|
|
<img src={ImageUrl.icon11} alt="" />
|
|
<span>{item.onlineUserCount}人</span>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div onClick={() => copyRoomNum(item.roomNum)} title='复制房间号'>
|
|
<span>{item.roomNum}</span>
|
|
<img src={ImageUrl.icon10} alt="" />
|
|
</div>
|
|
<div>
|
|
{role.ID.includes(userInfo.roleId) ? <Popover
|
|
content={
|
|
<div className='meetingContentFooterPopover'>
|
|
{userInfo.roleId === '1' ? <Popconfirm
|
|
title="提示"
|
|
description={`确定删除该会议吗`}
|
|
onConfirm={async () => {
|
|
DeleteRoom(item.id).then((res) => {
|
|
if (res.code === 200) {
|
|
message.success('删除成功')
|
|
changeOpen(index, false)
|
|
getRoomList()
|
|
}
|
|
})
|
|
}}
|
|
onCancel={() => {
|
|
changeOpen(index, false)
|
|
}}
|
|
okText="确定"
|
|
cancelText="取消"
|
|
>
|
|
<div>删除会议室</div>
|
|
</Popconfirm> : null}
|
|
<div onClick={() => {
|
|
changeOpen(index, false)
|
|
setTimeSelectModal(true)
|
|
}}>导出参会记录</div>
|
|
<div onClick={() => {
|
|
changeOpen(index, false)
|
|
}}>取消</div>
|
|
</div>
|
|
}
|
|
title=""
|
|
trigger="click"
|
|
open={item.open}
|
|
onOpenChange={() => {
|
|
setCurrentRoomInfo(list.data[index])
|
|
changeOpen(index, true)
|
|
}}
|
|
>
|
|
<Button type="primary" danger>更多</Button>
|
|
</Popover> : null}
|
|
<Button type="primary"
|
|
iconPosition={'end'}
|
|
onClick={async () => {
|
|
if (role.ID.includes(userInfo.roleId)) {
|
|
joinSettingRef.current.changeModal(item.roomNum)
|
|
} else {
|
|
postRefresh(() => {
|
|
getRoomRtcToken(item.roomNum, (options: any) => {
|
|
if (options) {
|
|
navigate(`/meeting`, {
|
|
state: {
|
|
channelId: item.roomNum,
|
|
token: options.token,
|
|
tokenA: options.tokenA,
|
|
roomId: item.id,
|
|
roomName: item.roomName,
|
|
enableMicr: false,
|
|
enableCamera: false,
|
|
}
|
|
})
|
|
}
|
|
})
|
|
})
|
|
}
|
|
}}
|
|
icon={<img src={ImageUrl.icon9} alt="" />}
|
|
className='m-ant-btn'>
|
|
进入
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
})}
|
|
<div style={{ visibility: 'hidden', margin: 0, padding: 0 }} className={`${styles.indexContentListItem} drag`}></div>
|
|
<div style={{ visibility: 'hidden', margin: 0, padding: 0 }} className={`${styles.indexContentListItem} drag`}></div>
|
|
</div> :
|
|
<div className={styles.indexContentEmpty}>
|
|
<Empty />
|
|
</div>
|
|
}
|
|
<div className={styles.indexContentPagination}>
|
|
<Pagination size="small" total={list.total} onChange={(e) => {
|
|
setList({
|
|
...list,
|
|
pageIndex: e
|
|
})
|
|
}} pageSize={list.pageSize} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<Modal title="新建会议室" open={createRoomModal} footer={null} closable={false} centered width={'400px'}>
|
|
<div>
|
|
<div>
|
|
<Input
|
|
placeholder="请输入房间号"
|
|
style={{ marginBottom: '14px' }}
|
|
className={styles.letterSpacing}
|
|
showCount
|
|
maxLength={8}
|
|
value={createRoomFrom.roomNum}
|
|
onChange={(e) => {
|
|
const regex = /^[0-9]*$/;
|
|
if (regex.test(e.target.value)) {
|
|
setCreateRoomFrom({
|
|
...createRoomFrom,
|
|
roomNum: e.target.value
|
|
})
|
|
}
|
|
}}
|
|
suffix={
|
|
<span
|
|
style={{ color: '#47D3D0', cursor: 'pointer' }}
|
|
onClick={() => {
|
|
function generateTimestampWithRandom(): string {
|
|
const timestamp = new Date().getTime();
|
|
const lastSixDigits = timestamp.toString().slice(-6);
|
|
const randomTwoDigits = ('0' + Math.floor(Math.random() * 100)).slice(-2);
|
|
return lastSixDigits + randomTwoDigits;
|
|
}
|
|
setCreateRoomFrom({
|
|
...createRoomFrom,
|
|
roomNum: generateTimestampWithRandom(),
|
|
})
|
|
}}
|
|
>获取随机房间号
|
|
</span>
|
|
}
|
|
/>
|
|
<Input.TextArea
|
|
placeholder="请输入房间名字"
|
|
style={{ marginBottom: '14px' }}
|
|
showCount
|
|
maxLength={30}
|
|
value={createRoomFrom.roomName}
|
|
onChange={(e) => {
|
|
setCreateRoomFrom({
|
|
...createRoomFrom,
|
|
roomName: e.target.value
|
|
})
|
|
}}
|
|
autoSize />
|
|
</div>
|
|
<div style={{
|
|
display: 'flex', justifyContent: 'center'
|
|
}}>
|
|
<Button type="primary" style={{ backgroundColor: '#31353A', marginRight: '14px' }} onClick={() => setCreateRoomModal(false)}>取消</Button>
|
|
<Button type="primary" className='m-ant-btn' onClick={() => {
|
|
if (!createRoomFrom.roomName) {
|
|
return message.error('请输入房间名字!')
|
|
}
|
|
if (!createRoomFrom.roomNum) {
|
|
return message.error('请输入房间号!')
|
|
}
|
|
isGetCheckoutRoomNum(createRoomFrom.roomNum, (bool: boolean) => {
|
|
if (bool) {
|
|
message.error('房间号已存在!')
|
|
} else {
|
|
PostRoom(createRoomFrom).then(res => {
|
|
if (res.code === 200) {
|
|
message.success('创建成功!')
|
|
setCreateRoomModal(false)
|
|
getRoomList()
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}}>创建</Button>
|
|
</div>
|
|
</div>
|
|
</Modal>
|
|
<Modal title="选择时间段" destroyOnClose={true} open={timeSelectModal} footer={null} onCancel={() => setTimeSelectModal(false)} centered width={'400px'}>
|
|
<div>
|
|
<RangePicker
|
|
showTime={{ format: 'YYYY-MM-DD HH:mm:ss' }}
|
|
format="YYYY-MM-DD HH:mm:ss"
|
|
onChange={(_value, dateString) => {
|
|
const setting = JSON.parse(storage.getItem('setting') as string)
|
|
if (dateString.length === 2) {
|
|
GetRecord(dayjs(dateString[0]).unix(), dayjs(dateString[1]).unix(), currentRoomInfo.roomNum).then(res => {
|
|
if (res.code === 200) {
|
|
const fileName = res.data.split('/').pop().split('?')[0];
|
|
fileUpLoad({
|
|
url: res.data,
|
|
content: `下载参会记录成功!文件已保存至:${setting.shareFilesPath}`,
|
|
fileName
|
|
})
|
|
}
|
|
setTimeSelectModal(false)
|
|
})
|
|
}
|
|
}}
|
|
/>
|
|
</div>
|
|
</Modal>
|
|
<JoinSetting ref={joinSettingRef} />
|
|
<StupWizard ref={stupWizardRef} />
|
|
</>
|
|
)
|
|
}
|
|
|
|
export default Index
|