This commit is contained in:
yj 2024-07-23 14:01:03 +08:00
parent d3977ea0ae
commit 704d79b0f7
27 changed files with 277 additions and 217 deletions

View File

@ -98,7 +98,7 @@ function createNotification(user) {
const notification = new Notification({ const notification = new Notification({
title: `${user.name} 邀请你加入`, title: `${user.name} 邀请你加入`,
body: user.body, body: user.body,
icon: path.join(`${__dirname}/src/assets/avatar.png`) // icon: path.join(`${__dirname}/src/assets/avatar.png`)
}); });
notification.show(); notification.show();
mainWindow.focus(); mainWindow.focus();

11
package-lock.json generated
View File

@ -12,6 +12,7 @@
"@microsoft/signalr": "^8.0.0", "@microsoft/signalr": "^8.0.0",
"@types/node": "^20.14.9", "@types/node": "^20.14.9",
"agora-electron-sdk": "^4.3.2", "agora-electron-sdk": "^4.3.2",
"animate.css": "^4.1.1",
"antd": "^5.18.2", "antd": "^5.18.2",
"axios": "^1.7.2", "axios": "^1.7.2",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
@ -2686,6 +2687,11 @@
"ajv": "^6.9.1" "ajv": "^6.9.1"
} }
}, },
"node_modules/animate.css": {
"version": "4.1.1",
"resolved": "https://registry.npmmirror.com/animate.css/-/animate.css-4.1.1.tgz",
"integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ=="
},
"node_modules/ansi-colors": { "node_modules/ansi-colors": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmmirror.com/ansi-colors/-/ansi-colors-1.1.0.tgz", "resolved": "https://registry.npmmirror.com/ansi-colors/-/ansi-colors-1.1.0.tgz",
@ -13827,6 +13833,11 @@
"dev": true, "dev": true,
"requires": {} "requires": {}
}, },
"animate.css": {
"version": "4.1.1",
"resolved": "https://registry.npmmirror.com/animate.css/-/animate.css-4.1.1.tgz",
"integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ=="
},
"ansi-colors": { "ansi-colors": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmmirror.com/ansi-colors/-/ansi-colors-1.1.0.tgz", "resolved": "https://registry.npmmirror.com/ansi-colors/-/ansi-colors-1.1.0.tgz",

View File

@ -27,6 +27,7 @@
"@microsoft/signalr": "^8.0.0", "@microsoft/signalr": "^8.0.0",
"@types/node": "^20.14.9", "@types/node": "^20.14.9",
"agora-electron-sdk": "^4.3.2", "agora-electron-sdk": "^4.3.2",
"animate.css": "^4.1.1",
"antd": "^5.18.2", "antd": "^5.18.2",
"axios": "^1.7.2", "axios": "^1.7.2",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
@ -81,4 +82,4 @@
}, },
"extraResources": [] "extraResources": []
} }
} }

View File

@ -12,6 +12,8 @@ import { storage } from '@/utils'
import { Spin } from "antd"; import { Spin } from "antd";
import { onInvitation } from "@/utils/package/signalr"; import { onInvitation } from "@/utils/package/signalr";
import JoinMeetingModal from "./components/JoinMeetingModal"; import JoinMeetingModal from "./components/JoinMeetingModal";
import * as CryptoJS from 'crypto-js';
import { PostLogin } from "@/api/Login";
const App: React.FC = () => { const App: React.FC = () => {
const navigate = useNavigate(); const navigate = useNavigate();
@ -22,28 +24,47 @@ const App: React.FC = () => {
}); });
const [spinning, setSpinning] = useState(false); const [spinning, setSpinning] = useState(false);
useEffect(() => { useEffect(() => {
if (storage.getItem('user')) { function toLogin() {
try {
window.electron.setMainWindowSize({
width: 1200,
height: 800,
})
} catch {
}
navigate('/home')
} else {
try { try {
window.electron.setMainWindowSize({ window.electron.setMainWindowSize({
width: 752, width: 752,
height: 520, height: 520,
key: 'login' key: 'login'
}) })
} catch { } catch { }
storage.removeItem('user')
}
navigate('/login') navigate('/login')
} }
function toHome() {
try {
window.electron.setMainWindowSize({
width: 1200,
height: 800,
})
} catch { }
navigate('/home')
}
let userInfo = JSON.parse(storage.getItem('user') as string)
let loginInfo = JSON.parse(storage.getItem('login') as string)
if (userInfo) {
if (loginInfo && loginInfo.isAutoLogin) {
PostLogin({
account: loginInfo.account,
pwd: CryptoJS.MD5(loginInfo.password).toString(CryptoJS.enc.Hex)
}).then(res => {
if (res.code === 200) {
storage.setItem('user', JSON.stringify(res.data))
toHome()
} else {
toLogin()
}
})
} else {
toLogin()
}
} else {
toLogin()
}
if (import.meta.env.VITE_ENV !== 'development') { if (import.meta.env.VITE_ENV !== 'development') {
document.addEventListener('keydown', (event) => { document.addEventListener('keydown', (event) => {
if ((event.ctrlKey || event.metaKey) && event.key === 'r') { if ((event.ctrlKey || event.metaKey) && event.key === 'r') {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 414 B

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 312 B

After

Width:  |  Height:  |  Size: 245 B

View File

@ -0,0 +1,11 @@
.avatar {
background-color: #3F51B5;
height: 36px;
width: 36px;
border-radius: 50%;
overflow: hidden;
color: white;
font-size: 14px;
line-height: 36px;
text-align: center;
}

View File

@ -0,0 +1,18 @@
import styles from '@/components/Avatar/index.module.scss'
import { useState, useImperativeHandle, forwardRef } from "react";
const Avatar = forwardRef((props: any, ref: any) => {
useImperativeHandle(ref, () => ({
getData: () => {
}
}))
return (
<>
<div className={styles.avatar}>
{props.name ? props.name.slice(-2) : '访客'}
</div>
</>
)
})
export default Avatar

View File

@ -29,17 +29,7 @@
align-items: center; align-items: center;
>div:nth-child(2) { >div:nth-child(2) {
width: 36px;
height: 36px;
overflow: hidden;
border-radius: 50%;
margin: 0 10px; margin: 0 10px;
>img {
width: 100%;
height: 100%;
object-fit: cover;
}
} }
>span { >span {
@ -86,17 +76,7 @@
margin-bottom: 10px; margin-bottom: 10px;
>div { >div {
width: 36px;
height: 36px;
overflow: hidden;
border-radius: 50%;
margin: 0 10px; margin: 0 10px;
>img {
width: 100%;
height: 100%;
object-fit: cover;
}
} }
>span { >span {

View File

@ -1,15 +1,17 @@
import styles from '@/components/InvitingPersonnelModal/index.module.scss' import styles from '@/components/InvitingPersonnelModal/index.module.scss'
import { Button, Checkbox, Input, Modal, Pagination, message } from 'antd'; import { Button, Checkbox, Input, Modal, Pagination, message } from 'antd';
import { useState, useImperativeHandle, forwardRef, useEffect } from "react"; import { useState, useImperativeHandle, forwardRef, useEffect } from "react";
import ImageUrl from '@/utils/package/imageUrl';
import { SearchOutlined } from '@ant-design/icons'; import { SearchOutlined } from '@ant-design/icons';
import { GetUserList } from '@/api/Home/User'; import { GetUserList } from '@/api/Home/User';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { PostRoomInvite } from '@/api/Meeting'; import { PostRoomInvite } from '@/api/Meeting';
import { storage } from '@/utils'; import { storage } from '@/utils';
import Avatar from '@/components/Avatar';
const InvitingPersonnelModal = forwardRef((props: any, ref: any) => { const InvitingPersonnelModal = forwardRef((props: any, ref: any) => {
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
changeInvitingPersonnelModal: () => { changeInvitingPersonnelModal: () => {
let userInfo = JSON.parse(storage.getItem('user') as string)
setUser(userInfo)
getUserList() getUserList()
setIsInvitingPersonnelModal(true) setIsInvitingPersonnelModal(true)
} }
@ -17,6 +19,7 @@ const InvitingPersonnelModal = forwardRef((props: any, ref: any) => {
const { state } = useLocation(); const { state } = useLocation();
const [isInvitingPersonnelModal, setIsInvitingPersonnelModal] = useState(false); const [isInvitingPersonnelModal, setIsInvitingPersonnelModal] = useState(false);
const [isFirstRender, setIsFirstRender] = useState(true); const [isFirstRender, setIsFirstRender] = useState(true);
const [user, setUser] = useState<any>({});
const [operation, setOperation] = useState<{ const [operation, setOperation] = useState<{
options: { label: string; value: number }[]; options: { label: string; value: number }[];
optionsValue: number[]; optionsValue: number[];
@ -96,7 +99,7 @@ const InvitingPersonnelModal = forwardRef((props: any, ref: any) => {
footer={null} footer={null}
onCancel={() => setIsInvitingPersonnelModal(false)} onCancel={() => setIsInvitingPersonnelModal(false)}
centered centered
width={'560px'} width={'700px'}
> >
<div className={styles.invitingPersonnelModal}> <div className={styles.invitingPersonnelModal}>
<div className={styles.invitingPersonnelModalContent}> <div className={styles.invitingPersonnelModalContent}>
@ -147,8 +150,8 @@ const InvitingPersonnelModal = forwardRef((props: any, ref: any) => {
return checkedList return checkedList
} }
}) })
}} defaultChecked={item.checked}></Checkbox> }} defaultChecked={item.checked} disabled={!item.isOnline || item.account === user.account}></Checkbox>
<div><img src={ImageUrl.avatar} alt="" /></div> <div><Avatar name={item.userName} /></div>
<span>{item.userName}</span> <span>{item.userName}</span>
</div> </div>
<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>
@ -159,13 +162,13 @@ const InvitingPersonnelModal = forwardRef((props: any, ref: any) => {
...list, ...list,
pageIndex: e pageIndex: e
}) })
}} pageSize={list.pageSize} current={list.pageIndex} hideOnSinglePage={true} /> }} pageSize={list.pageSize} current={list.pageIndex} hideOnSinglePage={true} showLessItems={true}/>
</div> </div>
<div className={styles.invitingPersonnelModalContentRight}> <div className={styles.invitingPersonnelModalContentRight}>
<span></span> <span></span>
<div> <div>
{checkedList.map((item: any, index: number) => <div key={item.id + index}> {checkedList.map((item: any, index: number) => <div key={item.id + index}>
<div><img src={ImageUrl.avatar} alt="" /></div> <div><Avatar name={item.userName} /></div>
<span>{item.userName}</span> <span>{item.userName}</span>
</div>)} </div>)}
</div> </div>
@ -175,8 +178,7 @@ const InvitingPersonnelModal = forwardRef((props: any, ref: any) => {
<Button type="primary" onClick={() => { setIsInvitingPersonnelModal(false) }} style={{ backgroundColor: '#31353A', marginRight: '14px' }}></Button> <Button type="primary" onClick={() => { setIsInvitingPersonnelModal(false) }} style={{ backgroundColor: '#31353A', marginRight: '14px' }}></Button>
<Button type="primary" className='m-ant-btn' onClick={() => { <Button type="primary" className='m-ant-btn' onClick={() => {
if (checkedList.length) { if (checkedList.length) {
let userInfo = JSON.parse(storage.getItem('user') as string) let me = checkedList.find((item: any) => item.id === user.uid)
let me = checkedList.find((item: any) => item.id === userInfo.uid)
if (me) { if (me) {
message.error('您不能邀请自己') message.error('您不能邀请自己')
} else { } else {

View File

@ -3,18 +3,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
>div:nth-child(1) { >div:nth-child(1) {}
width: 44px;
height: 40px;
border-radius: 50%;
overflow: hidden;
>img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
>div:nth-child(2) { >div:nth-child(2) {
margin-left: 10px; margin-left: 10px;

View File

@ -4,6 +4,7 @@ import ImageUrl from '@/utils/package/imageUrl';
import { Modal, message } from 'antd'; import { Modal, message } from 'antd';
import { useState, useImperativeHandle, forwardRef } from "react"; import { useState, useImperativeHandle, forwardRef } from "react";
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import Avatar from '@/components/Avatar';
const JoinMeetingModal = forwardRef((props: any, ref: any) => { const JoinMeetingModal = forwardRef((props: any, ref: any) => {
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
changeModal: (item: any) => { changeModal: (item: any) => {
@ -40,7 +41,7 @@ const JoinMeetingModal = forwardRef((props: any, ref: any) => {
> >
<div className={styles.joinMeetingModal}> <div className={styles.joinMeetingModal}>
<div> <div>
<div><img src={ImageUrl.avatar} alt="" /></div> <div><Avatar name={info.InviterName} /></div>
<div> <div>
<span>{info.InviterName} </span> <span>{info.InviterName} </span>
<span>{info.roomName}</span> <span>{info.roomName}</span>

View File

@ -19,6 +19,7 @@
width: 144px; width: 144px;
} }
>span { >span {
color: white; color: white;
font-size: 16px; font-size: 16px;
@ -26,6 +27,10 @@
} }
} }
.active {
border: 2px #4096ff solid;
}
.freedomMode { .freedomMode {
>div { >div {
@ -104,6 +109,11 @@
box-sizing: border-box; box-sizing: border-box;
background-color: #101317; background-color: #101317;
} }
.active {
border: 2px #4096ff solid;
}
} }
.dualScreenMode { .dualScreenMode {

View File

@ -6,6 +6,7 @@ import { useLocation } from 'react-router-dom';
import { GetSyncView } from '@/api/Meeting'; import { GetSyncView } from '@/api/Meeting';
interface Props { interface Props {
onClick: () => void; onClick: () => void;
meetingMode: string;
} }
const SpeakerModeModal = forwardRef((props: any, ref: any) => { const SpeakerModeModal = forwardRef((props: any, ref: any) => {
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
@ -13,6 +14,7 @@ const SpeakerModeModal = forwardRef((props: any, ref: any) => {
setIsSpeakerModeModal(true) setIsSpeakerModeModal(true)
} }
})) }))
const [meetingMode, setMeetingMode] = useState('StandardMode')
const { state } = useLocation(); const { state } = useLocation();
const [isSpeakerModeModal, setIsSpeakerModeModal] = useState(false); const [isSpeakerModeModal, setIsSpeakerModeModal] = useState(false);
const [isView, setIsView] = useState(false); const [isView, setIsView] = useState(false);
@ -21,6 +23,7 @@ const SpeakerModeModal = forwardRef((props: any, ref: any) => {
await GetSyncView(state.channelId, mode) await GetSyncView(state.channelId, mode)
} }
storage.setItem('meetingMode', mode) storage.setItem('meetingMode', mode)
setMeetingMode(mode);
setIsSpeakerModeModal(false) setIsSpeakerModeModal(false)
} }
return ( return (
@ -34,27 +37,27 @@ const SpeakerModeModal = forwardRef((props: any, ref: any) => {
width={'560px'} width={'560px'}
> >
<div className={styles.speakerModeModal}> <div className={styles.speakerModeModal}>
<FreedomMode onClick={() => setMode('FreedomMode')} /> <FreedomMode onClick={() => setMode('FreedomMode')} meetingMode={meetingMode} />
<StandardMode onClick={() => setMode('StandardMode')} /> <StandardMode onClick={() => setMode('StandardMode')} meetingMode={meetingMode} />
<SpeakerMode onClick={() => setMode('SpeakerMode')} /> <SpeakerMode onClick={() => setMode('SpeakerMode')} meetingMode={meetingMode} />
<SingleScreenMode onClick={() => setMode('SingleScreenMode')} /> <SingleScreenMode onClick={() => setMode('SingleScreenMode')} meetingMode={meetingMode} />
<DualScreenMode onClick={() => setMode('DualScreenMode')} /> <DualScreenMode onClick={() => setMode('DualScreenMode')} meetingMode={meetingMode} />
<FourScreenMode onClick={() => setMode('FourScreenMode')} /> <FourScreenMode onClick={() => setMode('FourScreenMode')} meetingMode={meetingMode} />
</div> </div>
<Checkbox onChange={(e) => { <Checkbox onChange={(e) => {
setIsView(e.target.checked) setIsView(e.target.checked)
}} value={isView}></Checkbox> }} defaultChecked={isView}></Checkbox>
</Modal> </Modal>
</> </>
) )
}) })
const FreedomMode: React.FC<Props> = ({ onClick }) => { const FreedomMode: React.FC<Props> = ({ onClick, meetingMode }) => {
// 自由者模式 // 自由者模式
return ( return (
<> <>
<div className={styles.freedomMode} onClick={onClick}> <div className={styles.freedomMode} onClick={onClick}>
<div> <div className={`${meetingMode === 'FreedomMode' ? styles.active : ''}`}>
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16].map(item => <div key={item}></div>)} {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16].map(item => <div key={item}></div>)}
</div> </div>
<span></span> <span></span>
@ -62,12 +65,12 @@ const FreedomMode: React.FC<Props> = ({ onClick }) => {
</> </>
) )
} }
const StandardMode: React.FC<Props> = ({ onClick }) => { const StandardMode: React.FC<Props> = ({ onClick, meetingMode }) => {
// 标准模式 // 标准模式
return ( return (
<> <>
<div className={styles.standardMode} onClick={onClick}> <div className={styles.standardMode} onClick={onClick}>
<div> <div className={`${meetingMode === 'StandardMode' ? styles.active : ''}`}>
<div> <div>
<div></div> <div></div>
<div></div> <div></div>
@ -81,12 +84,12 @@ const StandardMode: React.FC<Props> = ({ onClick }) => {
</> </>
) )
} }
const SpeakerMode: React.FC<Props> = ({ onClick }) => { const SpeakerMode: React.FC<Props> = ({ onClick, meetingMode }) => {
// 演讲者模式 // 演讲者模式
return ( return (
<> <>
<div className={styles.speakerMode} onClick={onClick}> <div className={styles.speakerMode} onClick={onClick}>
<div> <div className={`${meetingMode === 'SpeakerMode' ? styles.active : ''}`}>
<div> <div>
<div></div> <div></div>
<div></div> <div></div>
@ -100,12 +103,12 @@ const SpeakerMode: React.FC<Props> = ({ onClick }) => {
</> </>
) )
} }
const SingleScreenMode: React.FC<Props> = ({ onClick }) => { const SingleScreenMode: React.FC<Props> = ({ onClick, meetingMode }) => {
// 单画面模式 // 单画面模式
return ( return (
<> <>
<div className={styles.singleScreenMode} onClick={onClick}> <div className={styles.singleScreenMode} onClick={onClick}>
<div> <div className={`${meetingMode === 'SingleScreenMode' ? styles.active : ''}`}>
<div></div> <div></div>
</div> </div>
<span></span> <span></span>
@ -113,12 +116,12 @@ const SingleScreenMode: React.FC<Props> = ({ onClick }) => {
</> </>
) )
} }
const DualScreenMode: React.FC<Props> = ({ onClick }) => { const DualScreenMode: React.FC<Props> = ({ onClick, meetingMode }) => {
// 二分屏模式 // 二分屏模式
return ( return (
<> <>
<div className={styles.dualScreenMode} onClick={onClick}> <div className={styles.dualScreenMode} onClick={onClick}>
<div> <div className={`${meetingMode === 'DualScreenMode' ? styles.active : ''}`}>
<div></div> <div></div>
<div></div> <div></div>
</div> </div>
@ -127,12 +130,12 @@ const DualScreenMode: React.FC<Props> = ({ onClick }) => {
</> </>
) )
} }
const FourScreenMode: React.FC<Props> = ({ onClick }) => { const FourScreenMode: React.FC<Props> = ({ onClick, meetingMode }) => {
// 四分屏模式 // 四分屏模式
return ( return (
<> <>
<div className={styles.fourScreenMode} onClick={onClick}> <div className={styles.fourScreenMode} onClick={onClick}>
<div> <div className={`${meetingMode === 'FourScreenMode' ? styles.active : ''}`}>
<div></div> <div></div>
<div></div> <div></div>
<div></div> <div></div>

View File

@ -4,6 +4,7 @@ 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/locale/zh_CN';
import 'animate.css';
ReactDOM.createRoot(document.getElementById('root')!).render( ReactDOM.createRoot(document.getElementById('root')!).render(
<HashRouter> <HashRouter>
<ConfigProvider locale={zhCN}> <ConfigProvider locale={zhCN}>

View File

@ -16,18 +16,19 @@
border-bottom: 1px solid #2C2C2C; border-bottom: 1px solid #2C2C2C;
.indexBtnsJoin { .indexBtnsJoin {
background-color: #FFCFEB; background-color: #3A1457;
color: red; box-shadow: none;
color: white;
margin-left: 22px; margin-left: 22px;
&:hover { &:hover {
background-color: lighten(#FFCFEB, 5%) !important; background-color: lighten(#3A1457, 5%) !important;
color: red; color: white;
} }
&:active { &:active {
background-color: darken(#FFCFEB, 5%) !important; background-color: darken(#3A1457, 5%) !important;
color: red; color: white;
} }
} }
} }

View File

@ -84,7 +84,7 @@ const Index: React.FC = () => {
setJoinRoomFrom('') setJoinRoomFrom('')
setJoinRoomModal(true) setJoinRoomModal(true)
}} }}
icon={<img src={ImageUrl.icon7} alt="" />} icon={<img src={ImageUrl.icon8} alt="" />}
className={`${styles.indexBtnsJoin} drag`}> className={`${styles.indexBtnsJoin} drag`}>
</Button> </Button>
@ -157,17 +157,6 @@ const Index: React.FC = () => {
<Modal title="新建会议室" open={createRoomModal} footer={null} closable={false} centered width={'400px'}> <Modal title="新建会议室" open={createRoomModal} footer={null} closable={false} centered width={'400px'}>
<div> <div>
<div> <div>
<Input
placeholder="请输入房间名字"
style={{ marginBottom: '14px' }}
value={createRoomFrom.roomName}
onChange={(e) => {
setCreateRoomFrom({
...createRoomFrom,
roomName: e.target.value
})
}}
/>
<Input <Input
placeholder="请输入房间号" placeholder="请输入房间号"
style={{ marginBottom: '14px' }} style={{ marginBottom: '14px' }}
@ -187,15 +176,34 @@ const Index: React.FC = () => {
<span <span
style={{ color: '#47D3D0', cursor: 'pointer' }} style={{ color: '#47D3D0', cursor: 'pointer' }}
onClick={() => { 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({ setCreateRoomFrom({
...createRoomFrom, ...createRoomFrom,
roomNum: Math.floor(+new Date() / 1000).toString().slice(-8), roomNum: generateTimestampWithRandom(),
}) })
}} }}
> >
</span> </span>
} }
/> />
<Input.TextArea
placeholder="请输入房间名字"
style={{ marginBottom: '14px' }}
showCount
maxLength={30}
value={createRoomFrom.roomName}
onChange={(e) => {
setCreateRoomFrom({
...createRoomFrom,
roomName: e.target.value
})
}}
autoSize />
</div> </div>
<div style={{ <div style={{
display: 'flex', justifyContent: 'center' display: 'flex', justifyContent: 'center'

View File

@ -22,18 +22,19 @@
align-items: center; align-items: center;
.userBtnsDel { .userBtnsDel {
background-color: #FFCFEB; background-color: #3A1457;
color: red; box-shadow: none;
color: white;
margin-left: 22px; margin-left: 22px;
&:hover { &:hover {
background-color: lighten(#FFCFEB, 5%) !important; background-color: lighten(#3A1457, 5%) !important;
color: red; color: white;
} }
&:active { &:active {
background-color: darken(#FFCFEB, 5%) !important; background-color: darken(#3A1457, 5%) !important;
color: red; color: white;
} }
} }
} }

View File

@ -116,7 +116,7 @@ const User: React.FC = () => {
</div> </div>
<div className={`${styles.userBtnsRight} drag`}> <div className={`${styles.userBtnsRight} drag`}>
<Input <Input
placeholder="请输入用户名" placeholder="请输入用户名或账号"
prefix={<SearchOutlined style={{ color: 'white' }} />} prefix={<SearchOutlined style={{ color: 'white' }} />}
value={list.searchKeywod} value={list.searchKeywod}
onChange={(e) => { onChange={(e) => {

View File

@ -21,18 +21,8 @@
cursor: pointer; cursor: pointer;
>div { >div {
height: 40px;
width: 40px;
border-radius: 50%;
overflow: hidden;
margin-right: 14px; margin-right: 14px;
flex-shrink: 0; flex-shrink: 0;
>img {
width: 100%;
height: 100%;
object-fit: cover;
}
} }
>span { >span {

View File

@ -7,6 +7,7 @@ import 'dayjs/locale/zh-cn'
import { storage } from '@/utils'; import { storage } from '@/utils';
import ImageUrl from '@/utils/package/imageUrl' import ImageUrl from '@/utils/package/imageUrl'
import { startSignalr } from '@/utils/package/signalr'; import { startSignalr } from '@/utils/package/signalr';
import Avatar from '@/components/Avatar';
dayjs.locale('zh-cn'); dayjs.locale('zh-cn');
type navListType = { type navListType = {
title: string; title: string;
@ -84,9 +85,9 @@ const Home: React.FC = () => {
<div className={styles.homeLeft}> <div className={styles.homeLeft}>
<div className='drag'> <div className='drag'>
<div> <div>
<img src={ImageUrl.avatar} alt="" /> <Avatar name={userInfo.userName} />
</div> </div>
<span>{userInfo?.userName}</span> <span>{userInfo.userName}</span>
</div> </div>
<div> <div>
<img src={ImageUrl.icon14} alt="" /> <img src={ImageUrl.icon14} alt="" />

View File

@ -106,18 +106,19 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
background-color: #FFCFEB; background-color: #3A1457;
box-shadow: none;
border-radius: 10px; border-radius: 10px;
width: 56px; width: 56px;
margin-left: 4px; margin-left: 4px;
transition: 0.3s; transition: 0.3s;
&:hover { &:hover {
background-color: lighten(#FFCFEB, 5%) !important; background-color: lighten(#3A1457, 5%) !important;
} }
&:active { &:active {
background-color: darken(#FFCFEB, 5%) !important; background-color: darken(#3A1457, 5%) !important;
} }
} }
} }
@ -139,6 +140,7 @@
.ant-input { .ant-input {
height: 34px; height: 34px;
line-height: 34px; line-height: 34px;
text-align: center;
} }
} }
} }

View File

@ -77,7 +77,10 @@ const Login: React.FC = () => {
setAccountPasswordStatus(false) setAccountPasswordStatus(false)
} }
// 继续 // 继续
const continueClick = (): void => { const continueClick = (): any => {
if (!operation.account) {
return message.error('请输入账号!')
}
GetCheckUser(operation.account).then(res => { GetCheckUser(operation.account).then(res => {
if (res.code === 200) { if (res.code === 200) {
res.data ? setAccountPasswordStatus(true) : message.error('账号不存在!') res.data ? setAccountPasswordStatus(true) : message.error('账号不存在!')
@ -87,6 +90,9 @@ const Login: React.FC = () => {
// 设置勾选 // 设置勾选
const changeOptionsValue = (checkedValues: string[]): void => { const changeOptionsValue = (checkedValues: string[]): void => {
if (checkedValues.indexOf('isAutoLogin') >= 0 && checkedValues.indexOf('isRememberPassword') === -1) {
checkedValues.push('isRememberPassword')
}
setOperation({ setOperation({
...operation, ...operation,
optionsValue: checkedValues, optionsValue: checkedValues,
@ -159,22 +165,22 @@ const Login: React.FC = () => {
placeholder="请输入账号" placeholder="请输入账号"
prefix={<img src={ImageUrl.icon5} alt="" />} prefix={<img src={ImageUrl.icon5} alt="" />}
suffix={ suffix={
<span (accountPasswordStatus && operation.account ? <span
style={{ color: '#47D3D0', cursor: 'pointer' }} style={{ color: '#47D3D0', cursor: 'pointer' }}
onClick={resetClick} onClick={resetClick}
> >
</span> </span> : null)
} }
/> />
{!accountPasswordStatus ? <div style={{ marginTop: '36px' }} className='drag'> {!accountPasswordStatus ? <div style={{ marginTop: '36px' }} className={`drag`}>
<Button type="primary" <Button type="primary"
onClick={continueClick} onClick={continueClick}
style={{ width: '100%' }} style={{ width: '100%' }}
className={`${styles.loginButton} m-ant-btn`} className={`${styles.loginButton} m-ant-btn`}
></Button> ></Button>
</div> : null} </div> : null}
{accountPasswordStatus ? <div> {accountPasswordStatus ? <div className={`animate__animated animate__fadeIn`}>
<Input.Password <Input.Password
value={operation.password} value={operation.password}
onChange={e => { onChange={e => {
@ -200,11 +206,11 @@ const Login: React.FC = () => {
</div> </div>
</div> </div>
<div> <div>
<div className={styles.footer}> {/* <div className={styles.footer}>
<div></div> <div></div>
<span>or</span> <span>or</span>
<div></div> <div></div>
</div> </div> */}
{/* <div className={`${styles.code} drag`}> {/* <div className={`${styles.code} drag`}>
<div> <div>
<Input placeholder="输入会议号" className={`${styles.loginInput}`} /> <Input placeholder="输入会议号" className={`${styles.loginInput}`} />

View File

@ -340,16 +340,6 @@
>div { >div {
flex-shrink: 0; flex-shrink: 0;
width: 30px;
height: 30px;
overflow: hidden;
border-radius: 50%;
>img {
width: 100%;
height: 100%;
object-fit: cover;
}
} }
} }
@ -482,18 +472,7 @@
margin-left: 4px; margin-left: 4px;
} }
>div { >div {}
width: 30px;
height: 30px;
overflow: hidden;
border-radius: 50%;
>img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
} }
>div:nth-child(2) { >div:nth-child(2) {
@ -597,6 +576,19 @@
color: #EEEEEE; color: #EEEEEE;
font-size: 14px; font-size: 14px;
} }
&:hover {
>span {
color: #4096ff;
}
}
&:active {
>span {
color: darken(#4096ff, 10%) !important;
;
}
}
} }
} }
} }

View File

@ -17,6 +17,7 @@ import dayjs from 'dayjs';
import durationPlugin from 'dayjs/plugin/duration'; import durationPlugin from 'dayjs/plugin/duration';
import { VideoSourceType } from 'agora-electron-sdk'; import { VideoSourceType } from 'agora-electron-sdk';
import { GetUserList } from '@/api/Home/User'; import { GetUserList } from '@/api/Home/User';
import Avatar from '@/components/Avatar';
dayjs.extend(durationPlugin); dayjs.extend(durationPlugin);
const { Column } = Table const { Column } = Table
const Meeting: React.FC = () => { const Meeting: React.FC = () => {
@ -122,7 +123,7 @@ const Meeting: React.FC = () => {
let time = null as any; let time = null as any;
if (isInit) { if (isInit) {
let userInfo = JSON.parse(storage.getItem('user') as string) let userInfo = JSON.parse(storage.getItem('user') as string)
setMeetingMode(storage.getItem('meetingMode') as string || 'FreedomMode'); setMeetingMode('StandardMode');
agora.init() agora.init()
agora.registerEventHandler({ agora.registerEventHandler({
onJoinChannelSuccess: async (info: any, _elapsed: any) => { onJoinChannelSuccess: async (info: any, _elapsed: any) => {
@ -258,14 +259,14 @@ const Meeting: React.FC = () => {
switch (row.title) { switch (row.title) {
case '成员列表': case '成员列表':
setStatusList({ setStatusList({
userList: true, userList: statusList.userList ? false : true,
userChatList: false, userChatList: false,
}) })
break; break;
case '聊天': case '聊天':
setStatusList({ setStatusList({
userList: false, userList: false,
userChatList: true, userChatList: statusList.userChatList ? false : true,
}) })
break; break;
case '共享屏幕': case '共享屏幕':
@ -606,75 +607,73 @@ const Meeting: React.FC = () => {
<div className={styles.meetingUserListContent}> <div className={styles.meetingUserListContent}>
{roomUserList.map((item: any, index: number) => { {roomUserList.map((item: any, index: number) => {
return ( return (
<> item.isShow ? <div key={index + item.id} className='drag'>
{item.isShow ? <div key={index + item.id} className='drag'> <div>
<div> <div><Avatar name={item.userName} /></div>
<div><img src={ImageUrl.avatar} alt="" /></div> <span>
<span> {item.userName}
{item.userName} {item.roleId === '1' || item.isManager ?
{item.roleId === '1' || item.isManager ? <span style={{ color: '#02B188', marginLeft: '4px' }}>
<span style={{ color: '#02B188', marginLeft: '4px' }}> {item.roleId === '1' ? '主持人' : '临时主持人'}
{item.roleId === '1' ? '主持人' : '临时主持人'} </span>
</span> : null}
: null} </span>
</span> </div>
</div> <div>
<div> <img src={item.enableMicr ? ImageUrl.icon22 : ImageUrl.icon22Active} alt="" />
<img src={item.enableMicr ? ImageUrl.icon22 : ImageUrl.icon22Active} alt="" /> <img src={item.enableCamera ? ImageUrl.icon23 : ImageUrl.icon23Active} alt="" />
<img src={item.enableCamera ? ImageUrl.icon23 : ImageUrl.icon23Active} alt="" /> </div>
</div> {item.account !== user.account && user.roleId === '1' ? <div className='drag'>
{item.account !== user.account && user.roleId === '1' ? <div className='drag'> {!item.isManager ? <Button
{!item.isManager ? <Button type="primary"
type="primary" className='m-ant-btn'
className='m-ant-btn' style={{ marginBottom: '10px', width: '80%' }}
style={{ marginBottom: '10px', width: '80%' }} size={'small'}
size={'small'} onClick={() => {
onClick={() => { PostRoomManager(state.roomId, [item.id]).then(res => {
PostRoomManager(state.roomId, [item.id]).then(res => { if (res.code === 200) {
if (res.code === 200) { onInvoke('sendOper', {
onInvoke('sendOper', { roomNum: state.channelId,
roomNum: state.channelId, type: 2,
type: 2, })
}) }
} })
}) }}
}} ></Button> : <Button
></Button> : <Button type="primary"
type="primary" className='m-ant-btn'
className='m-ant-btn' style={{ marginBottom: '10px', width: '80%' }}
style={{ marginBottom: '10px', width: '80%' }} size={'small'}
size={'small'} onClick={() => {
onClick={() => { DeleteRoomManager(state.roomId, [item.id]).then(res => {
DeleteRoomManager(state.roomId, [item.id]).then(res => { if (res.code === 200) {
if (res.code === 200) { onInvoke('sendOper', {
onInvoke('sendOper', { roomNum: state.channelId,
roomNum: state.channelId, type: 2,
type: 2, })
}) }
} })
}) }}
}} ></Button>}
></Button>}
<Button <Button
type="primary" type="primary"
className='m-ant-btn' className='m-ant-btn'
style={{ width: '80%' }} style={{ width: '80%' }}
size={'small'} size={'small'}
onClick={() => { onClick={() => {
GetRoomKickout(state.channelId, item.id).then(res => { GetRoomKickout(state.channelId, item.id).then(res => {
if (res.code === 200) { if (res.code === 200) {
onInvoke('sendOper', { onInvoke('sendOper', {
roomNum: state.channelId, roomNum: state.channelId,
type: 3, type: 3,
}) })
} }
}) })
}} }}
></Button> ></Button>
</div> : null}
</div> : null} </div> : null}
</> </div> : null
) )
} }
)} )}
@ -701,7 +700,7 @@ const Meeting: React.FC = () => {
key={index} key={index}
className={`${item.uid !== state.uid ? styles.meetingUserChatContentLeft : styles.meetingUserChatContentRight} drag`}> className={`${item.uid !== state.uid ? styles.meetingUserChatContentLeft : styles.meetingUserChatContentRight} drag`}>
<div> <div>
<div><img src={ImageUrl.avatar} alt="" /></div> <div><Avatar name={item.userName} /></div>
<span>{item.userName}</span> <span>{item.userName}</span>
</div> </div>
<div>{item.message}</div> <div>{item.message}</div>

View File

@ -68,6 +68,11 @@ class Request {
if (res.code == 200) { if (res.code == 200) {
storage.setItem('user', JSON.stringify(res.data)) storage.setItem('user', JSON.stringify(res.data))
location.reload() location.reload()
} else {
setTimeout(() => {
storage.removeItem('user')
location.href = location.origin + '/#/login'
}, 3000)
} }
}) })
} else { } else {

View File

@ -40,6 +40,10 @@ $pagination-hover-background-color: #5575F2;
} }
} }
.ant-input-data-count {
color: white !important;
}
.ant-input-affix-wrapper { .ant-input-affix-wrapper {
background-color: $input-background-color !important; background-color: $input-background-color !important;
border: 1px solid $input-border-color; border: 1px solid $input-border-color;
@ -208,6 +212,9 @@ $pagination-hover-background-color: #5575F2;
color: black !important; color: black !important;
} }
} }
.ant-pagination-item-ellipsis{
color: white !important;
}
} }
// popover // popover