共享文件单独提出

This commit is contained in:
yj 2024-07-26 13:56:29 +08:00
parent 0de1122908
commit 7d0b53cf3e
4 changed files with 326 additions and 302 deletions

View File

@ -0,0 +1,27 @@
.sharedFilesModel {
>div:nth-child(1) {
display: flex;
align-items: center;
justify-content: space-between;
>span {
color: #EEEEEE;
font-size: 16px;
}
>div {
display: flex;
align-items: center;
>span {
margin-right: 20px;
cursor: pointer;
font-size: 16px;
}
}
}
>div:nth-child(2) {
margin: 20px 0;
}
}

View File

@ -0,0 +1,239 @@
import styles from '@/components/SharedFilesModel/index.module.scss'
import { DeleteOutlined, ProfileOutlined, ReloadOutlined, SearchOutlined, VerticalAlignBottomOutlined } from '@ant-design/icons';
import { Button, Input, Modal, Pagination, Table, message } from 'antd';
import { useState, useImperativeHandle, forwardRef, useEffect } from "react";
import { PostRoomFile, DeleteRoomFile, GetRoomUpFileurl, GetRoomFileDwUrl, GetRoomFile } from '@/api/Meeting';
import axios from 'axios';
import { useLocation } from 'react-router-dom';
import { storage } from '@/utils';
const { Column } = Table
const SharedFilesModel = forwardRef((props: any, ref: any) => {
useImperativeHandle(ref, () => ({
getData: () => {
setIsSharedFilesModel(true)
}
}))
const { state } = useLocation();
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const [showRowSelection, setShowRowSelection] = useState(false);
const [isSharedFilesModel, setIsSharedFilesModel] = useState(false);
const [user, setUser] = useState<any>({});
const [fileList, setFileList] = useState({
data: [],
keyword: '',
total: 0,
pageIndex: 1,
pageSize: 10,
})
useEffect(() => {
let userInfo = JSON.parse(storage.getItem('user') as string)
setUser(userInfo)
getRoomFile()
}, [fileList.pageIndex]);
// 获取共享文件列表
const getRoomFile = async (): Promise<void> => {
await GetRoomFile({
pageIndex: fileList.pageIndex,
pageSize: fileList.pageSize,
keyword: fileList.keyword,
roomId: state.roomId
}).then(res => {
if (res.code === 200) {
setFileList({
...fileList,
data: res.data.items.map((item: any) => {
return {
...item,
key: item.id,
}
}),
total: res.data.total
})
}
})
}
return (
<>
<Modal
title="共享文件"
open={isSharedFilesModel}
footer={null}
centered
width={'800px'}
onCancel={() => setIsSharedFilesModel(false)}
maskClosable
>
<div>
<div className={styles.sharedFilesModel}>
<div>
<span>{fileList.total}</span>
<div style={{ color: 'white' }}>
<Input
placeholder="搜索"
style={{ width: '200px' }}
prefix={<SearchOutlined style={{ color: 'white' }} />}
onChange={(e) => {
setFileList({
...fileList,
keyword: e.target.value
})
}}
onPressEnter={() => {
if (fileList.pageIndex === 1) {
getRoomFile()
} else {
setFileList({
...fileList,
pageIndex: 1
})
}
}}
onBlur={() => {
if (fileList.pageIndex === 1) {
getRoomFile()
} else {
setFileList({
...fileList,
pageIndex: 1
})
}
}}
/>
<ReloadOutlined title='刷新' onClick={() => {
if (fileList.pageIndex === 1) {
getRoomFile()
} else {
setFileList({
...fileList,
pageIndex: 1
})
}
}} />
<ProfileOutlined title={showRowSelection ? '取消框选' : '显示框选'} onClick={() => {
setShowRowSelection(!showRowSelection)
}} style={{ color: showRowSelection ? '#5575F2' : 'white' }} />
{showRowSelection ? <DeleteOutlined title='删除' onClick={() => {
if (selectedRowKeys.length) {
DeleteRoomFile(selectedRowKeys).then(res => {
if (res.code === 200) {
message.success('删除成功!')
getRoomFile()
}
})
} else {
message.error('请选择文件!')
}
}} /> : null}
<Button type="primary" style={{ backgroundColor: '#31353A' }}
onClick={() => {
const file = document.createElement("input") as any;
file.accept = "image/*,.doc,.docx,.ppt,.pptx,.xls,.xlsx,application/pdf";
file.type = "file";
file.onchange = async () => {
const fileInfo = file.files[0];
const maxSize = 100 * 1024 * 1024; // 100MB in bytes
if (fileInfo.size > maxSize) {
message.error('文件太大请上传小于100MB的文件。')
// 清除文件输入框的值,以便用户可以选择其他文件
return
}
const fileType = fileInfo.name.split('.');
const fileTypeName = fileType[fileType.length - 1];
await GetRoomUpFileurl(state.channelId, fileTypeName).then(async res => {
const formData = new FormData();
formData.append("name", fileInfo.name);
formData.append("OSSAccessKeyId", res.data.ossAccessKeyId);
formData.append("key", res.data.key);
formData.append("policy", res.data.policy);
formData.append("signature", res.data.signature);
formData.append("success_action_status", res.data.success_action_status);
formData.append("file", fileInfo);
await axios.post(res.data.host, formData, {
headers: {
"Content-Type": "multipart/form-data",
"Authorization": `Bearer ${user.token}`
},
withCredentials: false
})
await PostRoomFile({
fileUrl: res.data.key,
size: fileInfo.size,
fileName: fileInfo.name,
roomId: state.roomId
})
getRoomFile()
})
};
file.click();
}}
></Button>
</div>
</div>
<div>
<Table
size={'small'}
rowSelection={showRowSelection ? {
selectedRowKeys,
onChange: (newSelectedRowKeys: React.Key[]) => {
setSelectedRowKeys(newSelectedRowKeys);
}
} : undefined}
dataSource={fileList.data}
pagination={false}
scroll={{ y: '40vh' }}
style={{ width: '100%' }}
>
<Column title="文件" dataIndex="fileName" key="fileName" width={140} />
<Column title="更新时间" dataIndex="modifyTime" key="modifyTime" width={200} />
<Column title="大小" render={(item) => (
<>
<span>{item.size / 1024 > 1000 ? (item.size / (1024 * 1024)).toFixed(2) + 'MB' : (item.size / 1024).toFixed(2) + 'KB'}</span>
</>
)} />
<Column title="上传者" dataIndex="userName" key="userName" />
<Column title="下载次数"
render={(item) => (
<>
<span>{item.downloadCount}</span>
</>
)}
/>
<Column title="操作" render={(item) => (
<>
<VerticalAlignBottomOutlined title='下载' style={{ color: '#5575F2', cursor: 'pointer' }} onClick={() => {
GetRoomFileDwUrl(item.fileUrl, item.id).then(res => {
if (res.code === 200) {
const downloadLink = document.createElement("a");
downloadLink.href = res.data;
downloadLink.download = item.fileName;
downloadLink.click();
getRoomFile()
}
})
}} />
{/* <FolderOutlined title='文件' style={{ color: '#FFA000', cursor: 'pointer' }} /> */}
</>
)} />
</Table>
<div style={{ display: 'flex', justifyContent: 'center', marginTop: '10px' }}>
<Pagination size="small" total={fileList.total} onChange={(e) => {
setFileList({
...fileList,
pageIndex: e
})
}} pageSize={fileList.pageSize} current={fileList.pageIndex} hideOnSinglePage={true} />
</div>
</div>
</div>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end' }}>
</div>
</div>
</Modal>
</>
)
})
export default SharedFilesModel

View File

@ -706,33 +706,4 @@
justify-content: space-between;
margin-top: 20px;
}
}
// 共享文件
.sharedFilesModel {
>div:nth-child(1) {
display: flex;
align-items: center;
justify-content: space-between;
>span {
color: #EEEEEE;
font-size: 16px;
}
>div {
display: flex;
align-items: center;
>span {
margin-right: 20px;
cursor: pointer;
font-size: 16px;
}
}
}
>div:nth-child(2) {
margin: 20px 0;
}
}

View File

@ -3,13 +3,12 @@ import { useEffect, useRef, useState } from "react";
import Operation from '@/components/Operation';
import SpeakerModeModal from '@/components/SpeakerModeModal';
import InvitingPersonnelModal from '@/components/InvitingPersonnelModal';
import { Button, Input, Popover, Modal, Checkbox, message, Table, Pagination } from "antd";
import { DeleteOutlined, ProfileOutlined, ReloadOutlined, SearchOutlined, VerticalAlignBottomOutlined } from '@ant-design/icons';
import { Button, Input, Popover, Modal, Checkbox, message } from "antd";
import { SearchOutlined } from '@ant-design/icons';
import { useLocation, useNavigate } from 'react-router-dom';
import { thumbImageBufferToBase64 } from '@/utils/package/base64'
import { storage } from '@/utils';
import { GetRoomFile, PostRoomFile, DeleteRoomFile, GetRoomUpFileurl, GetRoomFileDwUrl, GetRoomUser, PostOpenMicr, PostOpenCamera, PostRoomManager, DeleteRoomManager, GetRoomKickout } from '@/api/Meeting';
import axios from 'axios';
import { GetRoomUser, PostOpenMicr, PostOpenCamera, PostRoomManager, DeleteRoomManager, GetRoomKickout } from '@/api/Meeting';
import ImageUrl from '@/utils/package/ImageUrl'
import agora from '@/utils/package/agora'
import { onInvoke, onSignalr, offSignalr, onStart } from '@/utils/package/signalr';
@ -18,23 +17,20 @@ import durationPlugin from 'dayjs/plugin/duration';
import { VideoSourceType } from 'agora-electron-sdk';
import { GetUserList } from '@/api/Home/User';
import Avatar from '@/components/Avatar';
import SharedFilesModel from '@/components/SharedFilesModel';
dayjs.extend(durationPlugin);
const { Column } = Table
const Meeting: React.FC = () => {
const navigate = useNavigate();
const { state } = useLocation();
const speakerModeModalRef = useRef<any>();
const sharedFilesModelRef = useRef<any>();
const invitingPersonnelRef = useRef<any>();
const [statusList, setStatusList] = useState({
userList: false,
userChatList: false,
})
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const [isSharedScreenModal, setIsSharedScreenModal] = useState(false);
const [isInit, setIsInit] = useState(true);
const [user, setUser] = useState<any>({});
const [showRowSelection, setShowRowSelection] = useState(false);
const [isSharedFilesModel, setIsSharedFilesModel] = useState(false);
const [sharedScreenList, setSharedScreenList] = useState<any>([]);
const [sharedScreenItem, setSharedScreenItem] = useState<any>('');
const [textMsg, setTextMsg] = useState('');
@ -103,13 +99,6 @@ const Meeting: React.FC = () => {
itemIndex: 0,
rowIndex: 0,
});
const [fileList, setFileList] = useState({
data: [],
keyword: '',
total: 0,
pageIndex: 1,
pageSize: 10,
})
const [roomUserList, setRoomUserList] = useState<any>([])
const [allUserList, setAllUserList] = useState<any>([])
const [chatList, setChatList] = useState<any>([])
@ -122,73 +111,69 @@ const Meeting: React.FC = () => {
const [noViewChatList, setNoViewChatList] = useState(0)
useEffect(() => {
let time = null as any;
if (isInit) {
let userInfo = JSON.parse(storage.getItem('user') as string)
setMeetingMode('StandardMode');
agora.init()
agora.registerEventHandler({
onJoinChannelSuccess: async (info: any, _elapsed: any) => {
await onInvoke('joinChannel', {
roomNum: info.channelId,
enableMicr: true,
enableCamera: true
let userInfo = JSON.parse(storage.getItem('user') as string)
setUser(userInfo)
setMeetingMode('StandardMode');
agora.init()
agora.registerEventHandler({
onJoinChannelSuccess: async (info: any, _elapsed: any) => {
await onInvoke('joinChannel', {
roomNum: info.channelId,
enableMicr: true,
enableCamera: true
})
await getRoomUser()
setTimeout(() => {
agora.setupLocalVideo({
account: Number(info.localUid),
view: document.getElementById(`video-${info.localUid}`) as HTMLElement,
channelId: info.channelId,
})
await getRoomUser()
setTimeout(() => {
agora.setupLocalVideo({
account: Number(info.localUid),
view: document.getElementById(`video-${info.localUid}`) as HTMLElement,
channelId: info.channelId,
})
}, 1000);
},
onUserJoined: async (info: any, remoteUid: any, _elapsed: any) => {
await getRoomUser()
setTimeout(() => {
agora.setupRemoteVideoJoin({
account: Number(remoteUid),
view: document.getElementById(`video-${remoteUid}`) as HTMLElement,
channelId: info.channelId,
})
}, 1000);
},
onUserOffline: async (info: any, remoteUid: any, _reason: any) => {
await agora.setupRemoteVideo({
}, 1000);
},
onUserJoined: async (info: any, remoteUid: any, _elapsed: any) => {
await getRoomUser()
setTimeout(() => {
agora.setupRemoteVideoJoin({
account: Number(remoteUid),
view: document.getElementById(`video-${remoteUid}`) as HTMLElement,
channelId: info.channelId,
})
setTimeout(() => {
getRoomUser()
}, 1000);
}
})
agora.setCameraCapture(VideoSourceType.VideoSourceCameraPrimary)
agora.setJoinChannel({
channelId: state.channelId,
userid: userInfo.account,
token: state.token,
})
setCurrentVideoId(userInfo.account)
setUser(userInfo)
setIsInit(false)
storage.setItem('noViewChatList', 0)
window.addEventListener('customStorageChange', handleCustomStorageChange);
window.addEventListener('online', handleNetworkChange);
window.addEventListener('offline', handleNetworkChange);
time = setInterval(() => {
setCurrentSeconds(currentSeconds++)
}, 1000)
} else {
getRoomFile()
}
}, 1000);
},
onUserOffline: async (info: any, remoteUid: any, _reason: any) => {
await agora.setupRemoteVideo({
account: Number(remoteUid),
view: document.getElementById(`video-${remoteUid}`) as HTMLElement,
channelId: info.channelId,
})
setTimeout(() => {
getRoomUser()
}, 1000);
}
})
agora.setCameraCapture(VideoSourceType.VideoSourceCameraPrimary)
agora.setJoinChannel({
channelId: state.channelId,
userid: userInfo.account,
token: state.token,
})
setCurrentVideoId(userInfo.account)
storage.setItem('noViewChatList', 0)
window.addEventListener('customStorageChange', handleCustomStorageChange);
window.addEventListener('online', handleNetworkChange);
window.addEventListener('offline', handleNetworkChange);
time = setInterval(() => {
setCurrentSeconds(currentSeconds++)
}, 1000)
return () => {
window.removeEventListener('customStorageChange', handleCustomStorageChange);
window.removeEventListener('online', handleNetworkChange);
window.removeEventListener('offline', handleNetworkChange);
clearInterval(time)
};
}, [fileList.pageIndex]);
}, []);
useEffect(() => {
roomUserList.forEach((item: any) => {
@ -364,8 +349,7 @@ const Meeting: React.FC = () => {
agora.stopRecording()
break;
case '共享文件':
await getRoomFile()
setIsSharedFilesModel(true)
sharedFilesModelRef.current.getData()
break;
}
}
@ -407,28 +391,7 @@ const Meeting: React.FC = () => {
}
})
};
// 获取共享文件列表
const getRoomFile = async (): Promise<void> => {
await GetRoomFile({
pageIndex: fileList.pageIndex,
pageSize: fileList.pageSize,
keyword: fileList.keyword,
roomId: state.roomId
}).then(res => {
if (res.code === 200) {
setFileList({
...fileList,
data: res.data.items.map((item: any) => {
return {
...item,
key: item.id,
}
}),
total: res.data.total
})
}
})
}
// 获取房间用户
const getRoomUser = async (): Promise<void> => {
Promise.all([
@ -848,183 +811,7 @@ const Meeting: React.FC = () => {
</div>
</div>
</Modal>
<Modal
title="共享文件"
open={isSharedFilesModel}
footer={null}
centered
width={'800px'}
onCancel={() => setIsSharedFilesModel(false)}
maskClosable
>
<div>
<div className={styles.sharedFilesModel}>
<div>
<span>{fileList.total}</span>
<div style={{ color: 'white' }}>
<Input
placeholder="搜索"
style={{ width: '200px' }}
prefix={<SearchOutlined style={{ color: 'white' }} />}
onChange={(e) => {
setFileList({
...fileList,
keyword: e.target.value
})
}}
onPressEnter={() => {
if (fileList.pageIndex === 1) {
getRoomFile()
} else {
setFileList({
...fileList,
pageIndex: 1
})
}
}}
onBlur={() => {
if (fileList.pageIndex === 1) {
getRoomFile()
} else {
setFileList({
...fileList,
pageIndex: 1
})
}
}}
/>
<ReloadOutlined title='刷新' onClick={() => {
if (fileList.pageIndex === 1) {
getRoomFile()
} else {
setFileList({
...fileList,
pageIndex: 1
})
}
}} />
<ProfileOutlined title={showRowSelection ? '取消框选' : '显示框选'} onClick={() => {
setShowRowSelection(!showRowSelection)
}} style={{ color: showRowSelection ? '#5575F2' : 'white' }} />
{showRowSelection ? <DeleteOutlined title='删除' onClick={() => {
if (selectedRowKeys.length) {
DeleteRoomFile(selectedRowKeys).then(res => {
if (res.code === 200) {
message.success('删除成功!')
getRoomFile()
}
})
} else {
message.error('请选择文件!')
}
}} /> : null}
<Button type="primary" style={{ backgroundColor: '#31353A' }}
onClick={() => {
const file = document.createElement("input") as any;
file.accept = "image/*,.doc,.docx,.ppt,.pptx,.xls,.xlsx,application/pdf";
file.type = "file";
file.onchange = async () => {
const fileInfo = file.files[0];
const maxSize = 100 * 1024 * 1024; // 100MB in bytes
if (fileInfo.size > maxSize) {
message.error('文件太大请上传小于100MB的文件。')
// 清除文件输入框的值,以便用户可以选择其他文件
return
}
const fileType = fileInfo.name.split('.');
const fileTypeName = fileType[fileType.length - 1];
await GetRoomUpFileurl(state.channelId, fileTypeName).then(async res => {
const formData = new FormData();
formData.append("name", fileInfo.name);
formData.append("OSSAccessKeyId", res.data.ossAccessKeyId);
formData.append("key", res.data.key);
formData.append("policy", res.data.policy);
formData.append("signature", res.data.signature);
formData.append("success_action_status", res.data.success_action_status);
formData.append("file", fileInfo);
await axios.post(res.data.host, formData, {
headers: {
"Content-Type": "multipart/form-data",
"Authorization": `Bearer ${user.token}`
},
withCredentials: false
})
await PostRoomFile({
fileUrl: res.data.key,
size: fileInfo.size,
fileName: fileInfo.name,
roomId: state.roomId
})
getRoomFile()
})
};
file.click();
}}
></Button>
</div>
</div>
<div>
<Table
size={'small'}
rowSelection={showRowSelection ? {
selectedRowKeys,
onChange: (newSelectedRowKeys: React.Key[]) => {
setSelectedRowKeys(newSelectedRowKeys);
}
} : undefined}
dataSource={fileList.data}
pagination={false}
scroll={{ y: '40vh' }}
style={{ width: '100%' }}
>
<Column title="文件" dataIndex="fileName" key="fileName" width={140} />
<Column title="更新时间" dataIndex="modifyTime" key="modifyTime" width={200} />
<Column title="大小" render={(item) => (
<>
<span>{item.size / 1024 > 1000 ? (item.size / (1024 * 1024)).toFixed(2) + 'MB' : (item.size / 1024).toFixed(2) + 'KB'}</span>
</>
)} />
<Column title="上传者" dataIndex="userName" key="userName" />
<Column title="下载次数"
render={(item) => (
<>
<span>{item.downloadCount}</span>
</>
)}
/>
<Column title="操作" render={(item) => (
<>
<VerticalAlignBottomOutlined title='下载' style={{ color: '#5575F2', cursor: 'pointer' }} onClick={() => {
GetRoomFileDwUrl(item.fileUrl, item.id).then(res => {
if (res.code === 200) {
const downloadLink = document.createElement("a");
downloadLink.href = res.data;
downloadLink.download = item.fileName;
downloadLink.click();
getRoomFile()
}
})
}} />
{/* <FolderOutlined title='文件' style={{ color: '#FFA000', cursor: 'pointer' }} /> */}
</>
)} />
</Table>
<div style={{ display: 'flex', justifyContent: 'center', marginTop: '10px' }}>
<Pagination size="small" total={fileList.total} onChange={(e) => {
setFileList({
...fileList,
pageIndex: e
})
}} pageSize={fileList.pageSize} current={fileList.pageIndex} hideOnSinglePage={true} />
</div>
</div>
</div>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end' }}>
</div>
</div>
</Modal>
<SharedFilesModel ref={sharedFilesModelRef} />
<SpeakerModeModal ref={speakerModeModalRef} />
<InvitingPersonnelModal ref={invitingPersonnelRef} />
</>