Compare commits

...

15 Commits

18 changed files with 509 additions and 203 deletions

25
main.js
View File

@ -17,6 +17,7 @@ const { autoUpdater, CancellationToken } = require('electron-updater');
const cancellationToken = new CancellationToken() const cancellationToken = new CancellationToken()
app.allowRendererProcessReuse = false; app.allowRendererProcessReuse = false;
let mainWindow = null; let mainWindow = null;
let childWindow = {}
let isMaximized = false; let isMaximized = false;
let env; let env;
let regKey; let regKey;
@ -299,6 +300,30 @@ app.on('ready', () => {
}) })
}); });
}); });
// 创建子窗口
ipcMain.handle('createChildWindow', (event, config) => {
const child = new BrowserWindow({
parent: mainWindow,
webPreferences: {
contextIsolation: false,
nodeIntegration: true,
enableRemoteModule: true,
nodeIntegrationInWorker: true,
allowMediaDevices: true,
preload: path.join(__dirname, 'preload.js')
},
show: false,
frame: false,
backgroundColor: '#00000000',
transparent: true,
})
child.loadURL(config.url)
childWindow[config.key] = child
child.once('ready-to-show', () => {
child.show()
child.setSize(config.width, config.height)
})
});
} }
}); });
// 检测更新在你想要检查更新的时候执行renderer事件触发后的操作自行编写 // 检测更新在你想要检查更新的时候执行renderer事件触发后的操作自行编写

View File

@ -73,4 +73,8 @@ window.electron = {
setRegistry: (uuid) => { setRegistry: (uuid) => {
ipcRenderer.invoke('setRegistry', uuid) ipcRenderer.invoke('setRegistry', uuid)
}, },
// 创建子窗口
createChildWindow: (config) => {
ipcRenderer.invoke('createChildWindow', config)
},
} }

View File

@ -18,6 +18,7 @@ import { agora } from "@/utils/package/agora";
import QuitTips from "@/components/QuitTips"; import QuitTips from "@/components/QuitTips";
import { GetLeave } from "@/api/Meeting"; import { GetLeave } from "@/api/Meeting";
import path from "path"; import path from "path";
import ShareScreenWindow from "./page/ShareScreenWindow";
const fs = require('fs').promises; const fs = require('fs').promises;
const { exec } = require('child_process'); const { exec } = require('child_process');
const App: React.FC = () => { const App: React.FC = () => {
@ -32,10 +33,11 @@ const App: React.FC = () => {
}); });
const [spinning, setSpinning] = useState(false); const [spinning, setSpinning] = useState(false);
const [isState, setIsState] = useState(true); const [isState, setIsState] = useState(true);
if (location.hash.indexOf('shareScreenWindow') == -1) {
useEffect(() => { useEffect(() => {
let userInfo = JSON.parse(storage.getItem('user') as string) let userInfo = JSON.parse(storage.getItem('user') as string)
let loginInfo = JSON.parse(storage.getItem('login') as string) let loginInfo = JSON.parse(storage.getItem('login') as string)
if (userInfo && Number(userInfo.perms)) { if (userInfo && !userInfo.isAnonymous) {
if (loginInfo && loginInfo.isAutoLogin) { if (loginInfo && loginInfo.isAutoLogin) {
PostLogin({ PostLogin({
account: loginInfo.account, account: loginInfo.account,
@ -148,6 +150,7 @@ const App: React.FC = () => {
onStop() onStop()
} }
}, [navigate]) }, [navigate])
}
useEffect(() => { useEffect(() => {
document.addEventListener('keydown', (event) => { document.addEventListener('keydown', (event) => {
if (event.key === 'F11') { if (event.key === 'F11') {
@ -239,6 +242,7 @@ const App: React.FC = () => {
</Route> </Route>
<Route path='/login' element={<Login />} /> <Route path='/login' element={<Login />} />
<Route path='/meeting' element={<Meeting />} /> <Route path='/meeting' element={<Meeting />} />
<Route path='/shareScreenWindow' element={<ShareScreenWindow />} />
<Route path='*' element={<NotFound />} /> <Route path='*' element={<NotFound />} />
</Routes> </Routes>
<Spin spinning={spinning} fullscreen /> <Spin spinning={spinning} fullscreen />

View File

@ -11,6 +11,12 @@ export const PostRoom = (data: any) =>
method: 'post', method: 'post',
data, data,
}) })
export const PostRoomInfo = (data: any) =>
request({
url: `/home/room-info`,
method: 'put',
data,
})
export const DeleteRoom = (roomId: string) => export const DeleteRoom = (roomId: string) =>
request({ request({
url: `/home/room?roomId=${roomId}`, url: `/home/room?roomId=${roomId}`,

View File

@ -19,6 +19,13 @@ export const PutUser = (data: any) =>
method: 'put', method: 'put',
data data
}) })
export const PutUserBth = (data: any) =>
request({
url: `/user/bth`,
method: 'put',
data
})
export const DeleteUser = (data: any) => export const DeleteUser = (data: any) =>
request({ request({
url: `/user`, url: `/user`,
@ -33,6 +40,12 @@ export const PutUserPwd = (data: { id: string, pwd: string }) =>
data data
}) })
export const GetSubDpList = () =>
request({
url: `/pub/sub-dp-list`,
method: 'get',
})
export const GetRoleDpList = () => export const GetRoleDpList = () =>
request({ request({
url: `/pub/role-dp-list`, url: `/pub/role-dp-list`,

View File

@ -158,7 +158,7 @@ const InvitingPersonnelModal = forwardRef((props: any, ref: any) => {
</div> </div>
</div>) : <span style={{ display: 'block', textAlign: 'center', color: 'white', padding: '30px 0' }}></span>} </div>) : <span style={{ display: 'block', textAlign: 'center', color: 'white', padding: '30px 0' }}></span>}
</div> </div>
<Pagination size="small" total={list.total} style={{ flexShrink: 0, margin: '10px 0 0' }} onChange={(e) => { <Pagination size="small" total={list.total} showSizeChanger={false} style={{ flexShrink: 0, margin: '10px 0 0' }} onChange={(e) => {
setList({ setList({
...list, ...list,
pageIndex: e pageIndex: e

View File

@ -341,7 +341,7 @@ const SharedFilesModel = forwardRef((props: any, ref: any) => {
...fileList, ...fileList,
pageIndex: e pageIndex: e
}) })
}} pageSize={fileList.pageSize} current={fileList.pageIndex} hideOnSinglePage={true} /> }} pageSize={fileList.pageSize} current={fileList.pageIndex} hideOnSinglePage={true} showSizeChanger={false}/>
</div> </div>
</div> </div>
</div> </div>

View File

@ -3,7 +3,7 @@ import App from './App.tsx'
import '@/utils/styles/main.css' import '@/utils/styles/main.css'
import { HashRouter } from 'react-router-dom'; import { HashRouter } from 'react-router-dom';
import { ConfigProvider } from 'antd'; import { ConfigProvider } from 'antd';
import zhCN from 'antd/locale/zh_CN'; import zhCN from 'antd/es/locale/zh_CN';
import 'animate.css'; import 'animate.css';
ReactDOM.createRoot(document.getElementById('root')!).render( ReactDOM.createRoot(document.getElementById('root')!).render(
<HashRouter> <HashRouter>

View File

@ -1,8 +1,8 @@
import styles from '@/page/Home/Index/index.module.scss' import styles from '@/page/Home/Index/index.module.scss'
import { useEffect, useState, useRef } from "react"; import { useEffect, useState, useRef } from "react";
import Operation from '@/components/Operation'; import Operation from '@/components/Operation';
import { Button, Input, Modal, Pagination, Empty, message, Popover, Popconfirm, DatePicker } from "antd"; import { Button, Input, Modal, Pagination, Empty, message, Popover, Popconfirm, DatePicker, Select } from "antd";
import { GetRoom, PostRoom, GetCheckoutRoomNum, GetRoomRtcToken, DeleteRoom, GetRecord } from '@/api/Home/Index'; import { GetRoom, PostRoom, GetCheckoutRoomNum, GetRoomRtcToken, DeleteRoom, GetRecord, PostRoomInfo } from '@/api/Home/Index';
import ImageUrl from '@/utils/package/imageUrl' import ImageUrl from '@/utils/package/imageUrl'
import { ExclamationCircleFilled, ReloadOutlined } from '@ant-design/icons'; import { ExclamationCircleFilled, ReloadOutlined } from '@ant-design/icons';
import JoinSetting from '@/components/JoinSetting'; import JoinSetting from '@/components/JoinSetting';
@ -12,6 +12,7 @@ import { useNavigate } from 'react-router-dom';
import { role } from '@/config/role'; import { role } from '@/config/role';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import StupWizard from '@/components/StupWizard'; import StupWizard from '@/components/StupWizard';
import { GetSubDpList } from '@/api/Home/User';
const fs = require('fs').promises; const fs = require('fs').promises;
const { exec } = require('child_process'); const { exec } = require('child_process');
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
@ -26,15 +27,20 @@ const Index: React.FC = () => {
}) })
const [createRoomModal, setCreateRoomModal] = useState(false) const [createRoomModal, setCreateRoomModal] = useState(false)
const [timeSelectModal, setTimeSelectModal] = useState(false) const [timeSelectModal, setTimeSelectModal] = useState(false)
const [createRoomFrom, setCreateRoomFrom] = useState<{ roomName: string, roomNum: string }>({ const [createRoomFrom, setCreateRoomFrom] = useState<{ id: string, roomName: string, roomNum: string, subject: string, year: string }>({
id: "",
roomName: "", roomName: "",
roomNum: "" roomNum: "",
subject: "",
year: ""
}) })
const joinSettingRef = useRef<any>(); const joinSettingRef = useRef<any>();
const stupWizardRef = useRef<any>(); const stupWizardRef = useRef<any>();
const [user, setUser] = useState<any>({}); const [user, setUser] = useState<any>({});
const [currentRoomInfo, setCurrentRoomInfo] = useState<any>({}); const [currentRoomInfo, setCurrentRoomInfo] = useState<any>({});
const [subjectList, setSubjectList] = useState<any>([]);
const [timeData, setTimeData] = useState<any>([]); const [timeData, setTimeData] = useState<any>([]);
const [isCreateRoom, setIsCreateRoom] = useState<boolean>(false);
const userInfo = JSON.parse(storage.getItem('user') as string) const userInfo = JSON.parse(storage.getItem('user') as string)
useEffect(() => { useEffect(() => {
setUser(userInfo) setUser(userInfo)
@ -62,7 +68,12 @@ const Index: React.FC = () => {
setList({ setList({
...list, ...list,
total: res.data.total, total: res.data.total,
data: res.data.items, data: res.data.items.map((item: any) => {
return {
...item,
open: false
}
}),
}) })
} }
}) })
@ -80,6 +91,13 @@ const Index: React.FC = () => {
} }
}) })
} }
const getSubDpList = async (): Promise<void> => {
await GetSubDpList().then(res => {
if (res.code === 200) {
setSubjectList(res.data.map((item: any) => { return { value: item.value, label: item.name } }))
}
})
}
const getRoomRtcToken = async (roomNum: string, callBack: Function): Promise<void> => { const getRoomRtcToken = async (roomNum: string, callBack: Function): Promise<void> => {
Promise.all([GetRoomRtcToken(roomNum), GetRoomRtcToken(roomNum + 'a')]).then(res => { Promise.all([GetRoomRtcToken(roomNum), GetRoomRtcToken(roomNum + 'a')]).then(res => {
if (res[0].code === 200 && res[1].code === 200) { if (res[0].code === 200 && res[1].code === 200) {
@ -166,8 +184,13 @@ const Index: React.FC = () => {
onClick={() => { onClick={() => {
setCreateRoomFrom({ setCreateRoomFrom({
roomName: "", roomName: "",
roomNum: "" roomNum: "",
subject: "",
year: "",
id: "",
}) })
getSubDpList()
setIsCreateRoom(true)
setCreateRoomModal(true) setCreateRoomModal(true)
}} }}
style={{ marginRight: '22px' }} style={{ marginRight: '22px' }}
@ -234,13 +257,26 @@ const Index: React.FC = () => {
okText="确定" okText="确定"
cancelText="取消" cancelText="取消"
> >
<div></div> <div className='meetingContentFooterPopoverDel'></div>
</Popconfirm> : null} </Popconfirm> : null}
<div onClick={() => { <div className='meetingContentFooterPopoverDefault' onClick={() => {
changeOpen(index, false) changeOpen(index, false)
setTimeSelectModal(true) setTimeSelectModal(true)
}}></div> }}></div>
<div onClick={() => { <div className='meetingContentFooterPopoverDefault' onClick={() => {
changeOpen(index, false)
setCreateRoomFrom({
roomName: item.roomName,
roomNum: item.roomNum,
subject: item.subject,
year: item.year,
id: item.id,
})
getSubDpList()
setIsCreateRoom(false)
setCreateRoomModal(true)
}}></div>
<div className='meetingContentFooterPopoverCancel' onClick={() => {
changeOpen(index, false) changeOpen(index, false)
}}></div> }}></div>
</div> </div>
@ -298,19 +334,19 @@ const Index: React.FC = () => {
</div> </div>
} }
<div className={styles.indexContentPagination}> <div className={styles.indexContentPagination}>
<Pagination size="small" total={list.total} onChange={(e) => { <Pagination size="small" total={list.total} onChange={(e: number) => {
setList({ setList({
...list, ...list,
pageIndex: e pageIndex: e
}) })
}} pageSize={list.pageSize} /> }} pageSize={list.pageSize} showSizeChanger={false} />
</div> </div>
</div> </div>
</div> </div>
<Modal title="新建会议室" open={createRoomModal} footer={null} closable={false} centered width={'400px'}> <Modal title={isCreateRoom ? '新建会议室' : '修改会议信息'} open={createRoomModal} footer={null} closable={false} centered width={'400px'}>
<div> <div>
<div> <div>
<Input {isCreateRoom ? <Input
placeholder="请输入房间号" placeholder="请输入房间号"
style={{ marginBottom: '14px' }} style={{ marginBottom: '14px' }}
className={styles.letterSpacing} className={styles.letterSpacing}
@ -344,7 +380,7 @@ const Index: React.FC = () => {
> >
</span> </span>
} }
/> /> : null}
<Input.TextArea <Input.TextArea
placeholder="请输入房间名字" placeholder="请输入房间名字"
style={{ marginBottom: '14px' }} style={{ marginBottom: '14px' }}
@ -358,6 +394,30 @@ const Index: React.FC = () => {
}) })
}} }}
autoSize /> autoSize />
<Input
placeholder="请输入届"
style={{ marginBottom: '14px' }}
value={createRoomFrom.year}
onChange={(e) => {
const regex = /^[0-9]*$/;
if (regex.test(e.target.value)) {
setCreateRoomFrom({
...createRoomFrom,
year: e.target.value
})
}
}}
/>
<Select
placeholder='请选择学科'
style={{ width: '100%', marginBottom: '14px' }}
options={subjectList}
value={createRoomFrom.subject === "" ? null : createRoomFrom.subject} onChange={(e) => {
setCreateRoomFrom({
...createRoomFrom,
subject: e
})
}} />
</div> </div>
<div style={{ <div style={{
display: 'flex', justifyContent: 'center' display: 'flex', justifyContent: 'center'
@ -370,6 +430,13 @@ const Index: React.FC = () => {
if (!createRoomFrom.roomNum) { if (!createRoomFrom.roomNum) {
return message.error('请输入房间号!') return message.error('请输入房间号!')
} }
if (!createRoomFrom.year) {
return message.error('请输入届!')
}
if (createRoomFrom.subject === "") {
return message.error('请选择学科!')
}
if (isCreateRoom) {
isGetCheckoutRoomNum(createRoomFrom.roomNum, (bool: boolean) => { isGetCheckoutRoomNum(createRoomFrom.roomNum, (bool: boolean) => {
if (bool) { if (bool) {
message.error('房间号已存在!') message.error('房间号已存在!')
@ -383,7 +450,16 @@ const Index: React.FC = () => {
}) })
} }
}) })
}}></Button> } else {
PostRoomInfo(createRoomFrom).then(res => {
if (res.code === 200) {
message.success('更新成功!')
setCreateRoomModal(false)
getRoomList()
}
})
}
}}>{isCreateRoom ? '创建' : '更新'}</Button>
</div> </div>
</div> </div>
</Modal> </Modal>

View File

@ -3,7 +3,7 @@ import { useEffect, useState, useRef } from "react";
import Operation from '@/components/Operation'; import Operation from '@/components/Operation';
import { Button, Input, Table, Pagination, Modal, message, Select } from "antd"; import { Button, Input, Table, Pagination, Modal, message, Select } from "antd";
import { ExclamationCircleFilled, SearchOutlined } from '@ant-design/icons'; import { ExclamationCircleFilled, SearchOutlined } from '@ant-design/icons';
import { GetUserList, PostUser, PutUser, DeleteUser, PutUserPwd, GetRoleDpList, PostUserImport } from '@/api/Home/User'; import { GetUserList, PostUser, PutUser, DeleteUser, PutUserPwd, GetRoleDpList, PostUserImport, GetSubDpList, PutUserBth } from '@/api/Home/User';
import * as CryptoJS from 'crypto-js'; import * as CryptoJS from 'crypto-js';
import ImageUrl from '@/utils/package/imageUrl'; import ImageUrl from '@/utils/package/imageUrl';
import { storage } from '@/utils'; import { storage } from '@/utils';
@ -15,7 +15,7 @@ const fs = require('fs').promises;
const User: React.FC = () => { const User: React.FC = () => {
const stupWizardRef = useRef<any>(); const stupWizardRef = useRef<any>();
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]); const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const [isCreateUser, setIsCreateUser] = useState(false); const [isCreateUser, setIsCreateUser] = useState<'add' | 'batch' | 'edit'>();
const [list, setList] = useState({ const [list, setList] = useState({
data: [], data: [],
searchKeywod: '', searchKeywod: '',
@ -30,7 +30,9 @@ const User: React.FC = () => {
Account: "", Account: "",
RoleId: null, RoleId: null,
Pwd: "", Pwd: "",
UserName: "" UserName: "",
subject: null,
year: "",
}) })
const [changeUserPawModal, setChangeUserPawModal] = useState(false) const [changeUserPawModal, setChangeUserPawModal] = useState(false)
const [changeImportModal, setChangeImportModal] = useState(false) const [changeImportModal, setChangeImportModal] = useState(false)
@ -39,10 +41,14 @@ const User: React.FC = () => {
newPwd: '', newPwd: '',
}) })
const [deleteUserPawModal, setDeleteUserPawModal] = useState(false) const [deleteUserPawModal, setDeleteUserPawModal] = useState(false)
const [subjectList, setSubjectList] = useState<any>([]);
useEffect(() => {
getSubDpList()
}, []);
useEffect(() => { useEffect(() => {
getUserList() getUserList()
}, [list.pageIndex]); }, [list.pageIndex, list.pageSize]);
const getUserList = async (): Promise<void> => { const getUserList = async (): Promise<void> => {
await GetUserList({ await GetUserList({
@ -61,6 +67,7 @@ const User: React.FC = () => {
} }
}), }),
}) })
setSelectedRowKeys([])
} }
}) })
} }
@ -118,7 +125,13 @@ const User: React.FC = () => {
} }
} }
} }
const getSubDpList = async (): Promise<void> => {
await GetSubDpList().then(res => {
if (res.code === 200) {
setSubjectList(res.data.map((item: any) => { return { value: item.value, label: item.name } }))
}
})
}
return ( return (
<> <>
<div className={styles.user}> <div className={styles.user}>
@ -131,13 +144,15 @@ const User: React.FC = () => {
onClick={() => { onClick={() => {
getRoleDpList((bool: boolean) => { getRoleDpList((bool: boolean) => {
if (bool) { if (bool) {
setIsCreateUser(true) setIsCreateUser('add')
setAddUserFrom({ setAddUserFrom({
Id: "", Id: "",
Account: "", Account: "",
RoleId: null, RoleId: null,
Pwd: "", Pwd: "",
UserName: "", UserName: "",
subject: null,
year: "",
}) })
setAddUserModal(true) setAddUserModal(true)
} }
@ -154,6 +169,31 @@ const User: React.FC = () => {
className='m-ant-btn'> className='m-ant-btn'>
</Button> </Button>
<Button type="primary"
onClick={() => {
if (selectedRowKeys.length) {
getRoleDpList((bool: boolean) => {
if (bool) {
setIsCreateUser('batch')
setAddUserFrom({
Id: "",
Account: "",
RoleId: null,
Pwd: "",
UserName: "",
subject: null,
year: "",
})
setAddUserModal(true)
}
})
} else {
message.error('请选择需要修改的用户!')
}
}}
className='m-ant-btn'>
</Button>
<Button type="primary" <Button type="primary"
icon={<img src={ImageUrl.icon21} alt="" />} icon={<img src={ImageUrl.icon21} alt="" />}
className={styles.userBtnsDel} className={styles.userBtnsDel}
@ -161,7 +201,7 @@ const User: React.FC = () => {
if (selectedRowKeys.length) { if (selectedRowKeys.length) {
setDeleteUserPawModal(true) setDeleteUserPawModal(true)
} else { } else {
message.error('请选择要删除的用户') message.error('请选择要删除的用户')
} }
}} }}
> >
@ -214,7 +254,13 @@ const User: React.FC = () => {
<div style={{ color: item.isOnline ? '#02B188' : 'rgb(221 11 11)' }}>{item.isOnline ? '在线' : '离线'}</div> <div style={{ color: item.isOnline ? '#02B188' : 'rgb(221 11 11)' }}>{item.isOnline ? '在线' : '离线'}</div>
</> </>
)} /> )} />
<Column title="操作" render={(item) => ( <Column title="届" dataIndex="year" key="year" />
<Column title="学科" render={(item) => (
<>
<div>{subjectList.find((subject: any) => subject.value === item.subject)?.label}</div>
</>
)} />
<Column title="操作" width={200} render={(item) => (
<> <>
<Button <Button
type="primary" type="primary"
@ -223,13 +269,15 @@ const User: React.FC = () => {
onClick={() => { onClick={() => {
getRoleDpList((bool: boolean) => { getRoleDpList((bool: boolean) => {
if (bool) { if (bool) {
setIsCreateUser(false) setIsCreateUser('edit')
setAddUserFrom({ setAddUserFrom({
...addUserFrom, ...addUserFrom,
Id: item.id, Id: item.id,
Account: item.account, Account: item.account,
RoleId: item.roleId, RoleId: item.roleId,
UserName: item.userName, UserName: item.userName,
subject: item.subject,
year: item.year,
}) })
setAddUserModal(true) setAddUserModal(true)
} }
@ -258,19 +306,20 @@ const User: React.FC = () => {
</Table> </Table>
<div className={styles.userContentPagination}> <div className={styles.userContentPagination}>
<span>{list.total}</span> <span>{list.total}</span>
<Pagination size="small" total={list.total} onChange={(e) => { <Pagination size="small" total={list.total} onChange={(page, pageSize) => {
setList({ setList({
...list, ...list,
pageIndex: e pageIndex: page,
pageSize: pageSize
}) })
}} pageSize={list.pageSize} current={list.pageIndex} hideOnSinglePage={true} /> }} showSizeChanger pageSizeOptions={[10, 14, 20, 30, 40, 50, 100]} pageSize={list.pageSize} current={list.pageIndex} />
</div> </div>
</div> </div>
</div> </div>
<Modal title={isCreateUser ? '添加用户' : '编辑用户'} open={addUserModal} footer={null} closable={false} centered width={'500px'}> <Modal title={isCreateUser === 'add' ? '添加用户' : isCreateUser === 'edit' ? '编辑用户' : '批量修改用户信息'} open={addUserModal} footer={null} closable={false} centered width={'500px'}>
<div> <div>
<div className={styles.addUserModal}> <div className={styles.addUserModal}>
<div> {isCreateUser !== 'batch' ? <div>
<span></span> <span></span>
<Input <Input
style={{ flexGrow: 1 }} style={{ flexGrow: 1 }}
@ -285,7 +334,7 @@ const User: React.FC = () => {
}); });
}} }}
/> />
</div> </div> : null}
<div> <div>
<span></span> <span></span>
<Select <Select
@ -299,7 +348,7 @@ const User: React.FC = () => {
}); });
}} />; }} />;
</div> </div>
{isCreateUser ? <div> {isCreateUser === 'add' ? <div>
<span></span> <span></span>
<Input.Password <Input.Password
placeholder="请输入密码" placeholder="请输入密码"
@ -313,7 +362,7 @@ const User: React.FC = () => {
}} }}
/> />
</div> : null} </div> : null}
<div> {isCreateUser !== 'batch' ? <div>
<span></span> <span></span>
<Input <Input
placeholder="请输入用户名称" placeholder="请输入用户名称"
@ -326,6 +375,35 @@ const User: React.FC = () => {
}); });
}} }}
/> />
</div> : null}
<div>
<span></span>
<Input
placeholder="请输入届"
value={addUserFrom.year}
onChange={(e) => {
const regex = /^[0-9]*$/;
if (regex.test(e.target.value)) {
setAddUserFrom({
...addUserFrom,
year: e.target.value
});
}
}}
/>
</div>
<div>
<span></span>
<Select
placeholder='请选择学科'
style={{ flexGrow: 1 }}
options={subjectList}
value={addUserFrom.subject} onChange={(e) => {
setAddUserFrom({
...addUserFrom,
subject: e
});
}} />
</div> </div>
</div> </div>
<div style={{ <div style={{
@ -333,43 +411,66 @@ const User: React.FC = () => {
}}> }}>
<Button type="primary" style={{ backgroundColor: '#31353A', marginRight: '14px' }} onClick={() => setAddUserModal(false)}></Button> <Button type="primary" style={{ backgroundColor: '#31353A', marginRight: '14px' }} onClick={() => setAddUserModal(false)}></Button>
<Button type="primary" className='m-ant-btn' onClick={async () => { <Button type="primary" className='m-ant-btn' onClick={async () => {
if (!addUserFrom.Account) { if (!addUserFrom.Account && isCreateUser !== 'batch') {
return message.error('请输入账号!') return message.error('请输入账号!')
} }
if (!addUserFrom.RoleId) { if (!addUserFrom.RoleId) {
return message.error('请选择角色!') return message.error('请选择角色!')
} }
if (!addUserFrom.Pwd && isCreateUser) { if (!addUserFrom.Pwd && isCreateUser === 'add') {
return message.error('请输入密码!') return message.error('请输入密码!')
} }
if (!addUserFrom.UserName) { if (!addUserFrom.UserName && isCreateUser !== 'batch') {
return message.error('请输入用户名称!') return message.error('请输入用户名称!')
} }
if (isCreateUser) { if (!addUserFrom.year) {
return message.error('请输入届!')
}
if (addUserFrom.subject === null) {
return message.error('请选择学科!')
}
if (isCreateUser === 'add') {
await PostUser({ await PostUser({
...addUserFrom, ...addUserFrom,
Pwd: CryptoJS.MD5(addUserFrom.Pwd).toString(CryptoJS.enc.Hex) Pwd: CryptoJS.MD5(addUserFrom.Pwd).toString(CryptoJS.enc.Hex)
}).then(res => { }).then(res => {
if (res.code === 200) { if (res.code === 200) {
setAddUserModal(false) setAddUserModal(false)
message.success('添加成功!') res.data ? message.success('添加成功!') : message.error('添加失败!')
} }
}) })
} else { } else if (isCreateUser === 'edit') {
await PutUser({ await PutUser({
Id: addUserFrom.Id, Id: addUserFrom.Id,
Account: addUserFrom.Account, Account: addUserFrom.Account,
RoleId: addUserFrom.RoleId, RoleId: addUserFrom.RoleId,
UserName: addUserFrom.UserName UserName: addUserFrom.UserName,
subject: addUserFrom.subject,
year: addUserFrom.year,
}).then(res => { }).then(res => {
if (res.code === 200) { if (res.code === 200) {
setAddUserModal(false) setAddUserModal(false)
message.success('修改成功!') res.data ? message.success('修改成功!') : message.error('修改失败!')
}
})
} else {
const param = selectedRowKeys.map((item: any) => {
return {
id: item,
subject: addUserFrom.subject,
year: addUserFrom.year,
RoleId: addUserFrom.RoleId,
}
})
await PutUserBth(param).then(res => {
if (res.code === 200) {
setAddUserModal(false)
res.data ? message.success('修改成功!') : message.error('修改失败!')
} }
}) })
} }
await getUserList() await getUserList()
}}>{isCreateUser ? '添加' : '修改'}</Button> }}>{isCreateUser === 'add' ? '添加' : '修改'}</Button>
</div> </div>
</div> </div>
</Modal> </Modal>
@ -442,7 +543,6 @@ const User: React.FC = () => {
DeleteUser(selectedRowKeys).then(res => { DeleteUser(selectedRowKeys).then(res => {
if (res.code === 200) { if (res.code === 200) {
setDeleteUserPawModal(false) setDeleteUserPawModal(false)
setSelectedRowKeys([])
message.success('删除成功!') message.success('删除成功!')
getUserList() getUserList()
} }

View File

@ -811,7 +811,7 @@
} }
} }
>div:nth-child(1) { .meetingContentFooterPopoverDel {
background-color: #FF5219; background-color: #FF5219;
&:hover { &:hover {
@ -823,7 +823,7 @@
} }
} }
>div:nth-child(2) { .meetingContentFooterPopoverDefault {
background-color: #31353A; background-color: #31353A;
&:hover { &:hover {
@ -835,7 +835,7 @@
} }
} }
>div:nth-child(3) { .meetingContentFooterPopoverCancel {
background-color: #101418; background-color: #101418;
&:hover { &:hover {

View File

@ -160,6 +160,7 @@ const Meeting: React.FC = () => {
const [noViewChatList, setNoViewChatList] = useState(0) const [noViewChatList, setNoViewChatList] = useState(0)
const [currentLookUserAccount, setCurrentLookUserAccount] = useState<any>('') const [currentLookUserAccount, setCurrentLookUserAccount] = useState<any>('')
const [recorder, setRecorder] = useState<any>('') const [recorder, setRecorder] = useState<any>('')
const [_currentRequestSpeakType, setCurrentRequestSpeakType] = useState<'video' | 'audio' | ''>('')
const [_mediaStream, setMediaStream] = useState<any>('') const [_mediaStream, setMediaStream] = useState<any>('')
const [isShare, setIsShare] = useState<any>(null) const [isShare, setIsShare] = useState<any>(null)
const [isSharePopConfirm, setIsSharePopConfirm] = useState<any>(false) const [isSharePopConfirm, setIsSharePopConfirm] = useState<any>(false)
@ -413,13 +414,23 @@ const Meeting: React.FC = () => {
} }
} else { } else {
if (item.user.uid === userInfo.uid) { if (item.user.uid === userInfo.uid) {
if (!item.user.isRoomManage) { if (!item.user.isRoomManager) {
await agora.allLeaveChannelEx() await agora.allLeaveChannelEx()
} }
message.success(`管理员${item.user.isRoomManager ? '设置' : '取消'}您为发言人`) message.success(`管理员${item.user.isRoomManager ? '设置' : '取消'}您为发言人`)
await agora.updateChannelMediaOptions(item.user.isRoomManager) await agora.updateChannelMediaOptions(item.user.isRoomManager)
await postOpenMicrApi(item.user.isRoomManager, userInfo.uid, false) setCurrentRequestSpeakType(res => {
await postOpenCameraApi(item.user.isRoomManager, userInfo.uid) if (res === 'video') {
postOpenCameraApi(item.user.isRoomManager, userInfo.uid)
} else if (res === 'audio') {
postOpenMicrApi(item.user.isRoomManager, userInfo.uid, false)
} else {
postOpenMicrApi(item.user.isRoomManager, userInfo.uid, false)
postOpenCameraApi(item.user.isRoomManager, userInfo.uid)
}
return ''
})
await stopScreenCapture() await stopScreenCapture()
} else { } else {
message.success(`管理员${item.user.isRoomManager ? '设置' : '取消'}${item.user.userName}为发言人`) message.success(`管理员${item.user.isRoomManager ? '设置' : '取消'}${item.user.userName}为发言人`)
@ -602,6 +613,7 @@ const Meeting: React.FC = () => {
if (isClicked) { if (isClicked) {
timer = setTimeout(() => { timer = setTimeout(() => {
setIsClicked(false); setIsClicked(false);
setCurrentRequestSpeakType('')
}, 10000); }, 10000);
} }
@ -999,6 +1011,25 @@ const Meeting: React.FC = () => {
} }
// 操作按钮 // 操作按钮
const changeStatusList = async (row: any, itemIndex: number, rowIndex: number): Promise<void> => { const changeStatusList = async (row: any, itemIndex: number, rowIndex: number): Promise<void> => {
function requestSpeak() {
confirm({
title: '提示',
icon: <ExclamationCircleFilled />,
content: `该操作需向管理员申请权限`,
centered: true,
okText: '申请',
cancelText: '取消',
async onOk() {
GetApplySpeak(state.channelId).then(res => {
if (res.code === 200) {
message.success('申请发言成功')
}
})
},
onCancel() {
}
})
}
const footerListTemplate = [...footerList] const footerListTemplate = [...footerList]
setFooterListIndex({ setFooterListIndex({
itemIndex, itemIndex,
@ -1046,13 +1077,37 @@ const Meeting: React.FC = () => {
await postOpenMicr(false, user.uid) await postOpenMicr(false, user.uid)
break; break;
case '解除静音': case '解除静音':
await getUserRoomInfo().then(async (res) => {
if (res) {
await postOpenMicr(true, user.uid) await postOpenMicr(true, user.uid)
} else {
if (!isClicked) {
setCurrentRequestSpeakType('audio')
setIsClicked(true);
requestSpeak()
} else {
message.error('申请太频繁了,请稍后重试!');
}
}
})
break; break;
case '关闭视频': case '关闭视频':
await postOpenCamera(false, user.uid) await postOpenCamera(false, user.uid)
break; break;
case '开启视频': case '开启视频':
await getUserRoomInfo().then(async (res) => {
if (res) {
await postOpenCamera(true, user.uid) await postOpenCamera(true, user.uid)
} else {
if (!isClicked) {
setCurrentRequestSpeakType('video')
setIsClicked(true);
requestSpeak()
} else {
message.error('申请太频繁了,请稍后重试!');
}
}
})
break; break;
case '设置': case '设置':
stupWizardRef.current.changeModal() stupWizardRef.current.changeModal()
@ -1141,11 +1196,7 @@ const Meeting: React.FC = () => {
case '申请发言': case '申请发言':
if (!isClicked) { if (!isClicked) {
setIsClicked(true); setIsClicked(true);
GetApplySpeak(state.channelId).then(res => { requestSpeak()
if (res.code === 200) {
message.success('申请发言成功')
}
})
} else { } else {
message.error('申请太频繁了,请稍后重试!'); message.error('申请太频繁了,请稍后重试!');
} }
@ -2177,11 +2228,11 @@ const Meeting: React.FC = () => {
okText="结束" okText="结束"
cancelText="取消" cancelText="取消"
> >
<div></div> <div className='meetingContentFooterPopoverDel'></div>
</Popconfirm> </Popconfirm>
: null} : null}
<div onClick={() => leaveChannel()}></div> <div className='meetingContentFooterPopoverDefault' onClick={() => leaveChannel()}></div>
<div onClick={() => { setOpen(false) }}></div> <div className='meetingContentFooterPopoverCancel' onClick={() => { setOpen(false) }}></div>
</div> </div>
} }
title="" title=""
@ -2200,12 +2251,12 @@ const Meeting: React.FC = () => {
</div> </div>
</Popover> </Popover>
case '申请发言': case '申请发言':
if (!role.ID.includes(user.roleId)) { // if (!role.ID.includes(user.roleId)) {
return <div className='drag' onClick={() => changeStatusList(row, itemIndex, rowIndex)} key={rowIndex}> // return <div className='drag' onClick={() => changeStatusList(row, itemIndex, rowIndex)} key={rowIndex}>
<img src={row.active ? row.iconActive : row.icon} alt="" /> // <img src={row.active ? row.iconActive : row.icon} alt="" />
<span>{row.title}</span> // <span>{row.title}</span>
</div> // </div>
} // }
return null return null
case '结束发言': case '结束发言':
if (!role.ID.includes(user.roleId)) { if (!role.ID.includes(user.roleId)) {

View File

@ -0,0 +1,7 @@
.shareScreenWindow {
background-color: red;
color: black;
height: 100%;
width: 100%;
font-size: 30px;
}

View File

@ -0,0 +1,23 @@
import styles from '@/page/ShareScreenWindow/index.module.scss'
import { useEffect } from "react";
const ShareScreenWindow: React.FC = () => {
useEffect(() => {
}, []);
// window.electron.createChildWindow({
// url: location.origin + `/#/shareScreenWindow`,
// width: 600,
// height: 40,
// key: 'shareScreenWindow',
// })
return (
<>
<div className={styles.shareScreenWindow}>
2222
</div>
</>
)
}
export default ShareScreenWindow

1
src/render.d.ts vendored
View File

@ -18,6 +18,7 @@ export interface IElectronAPI {
getVersion: () => Promise<string>; getVersion: () => Promise<string>;
setRegistry: (uuid: string) => any; setRegistry: (uuid: string) => any;
getRegistry: () => any; getRegistry: () => any;
createChildWindow: (config: any) => void;
} }
declare global { declare global {

View File

@ -115,15 +115,15 @@ export const agora = {
rtcEngine.registerEventHandler({ rtcEngine.registerEventHandler({
// 监听本地用户加入频道事件 // 监听本地用户加入频道事件
onJoinChannelSuccess: async (connection: RtcConnection, elapsed: number) => { onJoinChannelSuccess: async (connection: RtcConnection, elapsed: number) => {
await onJoinChannelSuccess(connection, elapsed) await onJoinChannelSuccess?.(connection, elapsed)
}, },
// 监听远端用户加入频道事件 // 监听远端用户加入频道事件
onUserJoined: async (connection: RtcConnection, remoteUid: number, elapsed: number) => { onUserJoined: async (connection: RtcConnection, remoteUid: number, elapsed: number) => {
await onUserJoined(connection, remoteUid, elapsed) await onUserJoined?.(connection, remoteUid, elapsed)
}, },
// 监听用户离开频道事件 // 监听用户离开频道事件
onUserOffline: async (connection: RtcConnection, remoteUid: number, reason: UserOfflineReasonType) => { onUserOffline: async (connection: RtcConnection, remoteUid: number, reason: UserOfflineReasonType) => {
await onUserOffline(connection, remoteUid, reason) await onUserOffline?.(connection, remoteUid, reason)
}, },
// // 视频发布状态改变回调 // // 视频发布状态改变回调
// onVideoPublishStateChanged: (source: any, channel: any, oldState: any, newState: any, elapseSinceLastState: any) => { // onVideoPublishStateChanged: (source: any, channel: any, oldState: any, newState: any, elapseSinceLastState: any) => {
@ -139,19 +139,19 @@ export const agora = {
// }, // },
// // 用户音量提示回调。 // // 用户音量提示回调。
onAudioVolumeIndication: async (_connection: RtcConnection, speakers: AudioVolumeInfo[], _speakerNumber: number, _totalVolume: number) => { onAudioVolumeIndication: async (_connection: RtcConnection, speakers: AudioVolumeInfo[], _speakerNumber: number, _totalVolume: number) => {
await onAudioVolumeIndication(speakers) await onAudioVolumeIndication?.(speakers)
}, },
//通话中每个用户的网络上下行 last mile 质量报告回调。 //通话中每个用户的网络上下行 last mile 质量报告回调。
onNetworkQuality: async (connection: RtcConnection, remoteUid: number, txQuality: QualityType, rxQuality: QualityType) => { onNetworkQuality: async (connection: RtcConnection, remoteUid: number, txQuality: QualityType, rxQuality: QualityType) => {
await onNetworkQuality(connection, remoteUid, txQuality, rxQuality) await onNetworkQuality?.(connection, remoteUid, txQuality, rxQuality)
}, },
//当前通话相关的统计信息回调。 //当前通话相关的统计信息回调。
onRtcStats: async (_connection: RtcConnection, stats: RtcStats) => { onRtcStats: async (_connection: RtcConnection, stats: RtcStats) => {
await onRtcStats(stats) await onRtcStats?.(stats)
}, },
// 网络连接状态已改变回调。 // 网络连接状态已改变回调。
onConnectionStateChanged: async (connection: RtcConnection, state: ConnectionStateType, reason: ConnectionChangedReasonType) => { onConnectionStateChanged: async (connection: RtcConnection, state: ConnectionStateType, reason: ConnectionChangedReasonType) => {
await onConnectionStateChanged(connection, state, reason) await onConnectionStateChanged?.(connection, state, reason)
}, },
}); });
}, },

View File

@ -189,8 +189,6 @@ $pagination-hover-background-color: #5575F2;
.ant-pagination-prev, .ant-pagination-prev,
.ant-pagination-next { .ant-pagination-next {
width: 30px !important;
height: 30px !important;
border-radius: 50%; border-radius: 50%;
background: $pagination-background-color; background: $pagination-background-color;
@ -200,9 +198,6 @@ $pagination-hover-background-color: #5575F2;
} }
.ant-pagination-item { .ant-pagination-item {
width: 30px !important;
height: 30px !important;
line-height: 30px !important;
border-radius: 50%; border-radius: 50%;
background: $pagination-background-color !important; background: $pagination-background-color !important;
margin-right: 10px !important; margin-right: 10px !important;

View File

@ -14,6 +14,7 @@ body {
img { img {
display: block; display: block;
-webkit-user-drag: none;
} }