diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index ae9cde3f7..307e758c4 100755 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -1175,6 +1175,7 @@ class UsersController extends AbstractController * @apiParam {String} [name] 会话ID * @apiParam {String} [sharekey] 分享的key * @apiParam {String} [username] 用户名称 + * @apiParam {String} [userimg] 用户头像 * @apiParam {Array} [userids] 邀请成员 * * @apiSuccess {Number} ret 返回状态码(1正确、0错误) @@ -1189,6 +1190,7 @@ class UsersController extends AbstractController $userids = Request::input('userids'); $sharekey = trim(Request::input('sharekey')); $username = trim(Request::input('username')); + $userimg = trim(Request::input('userimg')) ?: Base::fillUrl('avatar/' . $username . '.png'); $user = null; if (!empty($sharekey) && $type === 'join') { if (!Meeting::getShareInfo($sharekey)) { @@ -1266,7 +1268,7 @@ class UsersController extends AbstractController // $data['appid'] = $meetingSetting['appid']; $data['uid'] = $uid; - $data['userimg'] = $sharekey ? Base::fillUrl('avatar/' . $username . '.png') : $user?->userimg; + $data['userimg'] = $sharekey ? $userimg : $user?->userimg; $data['nickname'] = $sharekey ? $username : $user?->nickname; $data['token'] = $token; $data['msgs'] = $msgs; diff --git a/resources/assets/js/App.vue b/resources/assets/js/App.vue index 3b79c11f4..684b243e1 100755 --- a/resources/assets/js/App.vue +++ b/resources/assets/js/App.vue @@ -7,6 +7,9 @@ + + + @@ -43,11 +46,13 @@ import PreviewImageState from "./components/PreviewImage/state"; import NetworkException from "./components/NetworkException"; import GuidePage from "./components/GuidePage"; import TaskOperation from "./pages/manage/components/TaskOperation"; +import MeetingManager from "./pages/manage/components/MeetingManager"; import DropdownMenu from "./components/DropdownMenu"; import {mapState} from "vuex"; export default { components: { + MeetingManager, DropdownMenu, TaskOperation, NetworkException, diff --git a/resources/assets/js/pages/manage.vue b/resources/assets/js/pages/manage.vue index 571aa0a5d..10ba122a4 100644 --- a/resources/assets/js/pages/manage.vue +++ b/resources/assets/js/pages/manage.vue @@ -326,9 +326,6 @@ - - - @@ -357,7 +354,6 @@ import TaskAdd from "./manage/components/TaskAdd"; import Report from "./manage/components/Report"; import MobileBack from "../components/Mobile/Back"; import MobileNotification from "../components/Mobile/Notification"; -import MeetingManager from "./manage/components/MeetingManager"; import longpress from "../directives/longpress"; import DialogModal from "./manage/components/DialogModal"; import TaskModal from "./manage/components/TaskModal"; @@ -366,12 +362,11 @@ import TaskExport from "./manage/components/TaskExport"; import ApproveExport from "./manage/components/ApproveExport"; import ComplaintManagement from "./manage/components/ComplaintManagement"; import MicroApps from "../components/MicroApps.vue"; -import notificationKoro from "notification-koro1"; -import {Store} from "le5le-store"; -import {MarkdownPreview} from "../store/markdown"; import UserSelect from "../components/UserSelect.vue"; import ImgUpload from "../components/ImgUpload.vue"; import ApproveDetails from "./manage/approve/details.vue"; +import notificationKoro from "notification-koro1"; +import {Store} from "le5le-store"; export default { components: { @@ -383,7 +378,6 @@ export default { ApproveExport, TaskModal, DialogModal, - MeetingManager, MobileNotification, MobileBack, MobileTabbar, diff --git a/resources/assets/js/pages/manage/components/MeetingManager.vue b/resources/assets/js/pages/manage/components/MeetingManager.vue index 2acfd173b..f5f7b6f1d 100644 --- a/resources/assets/js/pages/manage/components/MeetingManager.vue +++ b/resources/assets/js/pages/manage/components/MeetingManager.vue @@ -179,7 +179,7 @@ export default { }, computed: { - ...mapState(['meetingWindow', 'formOptions']), + ...mapState(['meetingWindow', 'formOptions', 'userToken']), }, mounted() { @@ -200,31 +200,54 @@ export default { } }, meetingWindow: { - handler(val) { - switch (val.type) { + handler(data) { + switch (data.type) { + // 创建会议 case 'add': - this.addShow = val.show; + this.addShow = data.show; this.loadIng = 0; break; + + // 加入会议(直接加入) case 'join': - this.addShow = val.show; + case 'direct': + this.addShow = data.show; this.loadIng = 0; this.addData.type = 'join'; - if(val.meetingSharekey){ - this.addData.sharekey = val.meetingSharekey; - this.addData.meetingid = val.meetingid || ''; - this.addData.meetingdisabled = val.meetingSharekey ? true : false; + if (data.meetingNickname) { + this.addData.username = data.meetingNickname; + } + if (data.meetingAvatar) { + this.addData.userimg = data.meetingAvatar; + } + if ($A.runNum(data.meetingAudio) && !this.addData.tracks.includes('audio')) { + this.addData.tracks.push('audio') + } + if ($A.runNum(data.meetingVideo) && !this.addData.tracks.includes('video')) { + this.addData.tracks.push('video') + } + if (data.meetingSharekey) { + this.addData.sharekey = data.meetingSharekey; + this.addData.meetingid = data.meetingid || ''; + this.addData.meetingdisabled = !!data.meetingSharekey; + } + if (data.type === 'direct') { + this.onOpen(true); } break; + + // 邀请加入 case 'invitation': - this.invitationShow = val.show; + this.invitationShow = data.show; this.invitationLoad = false; - this.invitationData.meetingid = val.meetingid; + this.invitationData.meetingid = data.meetingid; break; + + // 加入失败 case 'error': - this.addShow = val.show; + this.addShow = data.show; this.loadIng = 0; - this.invitationShow = val.show; + this.invitationShow = data.show; this.invitationLoad = false; $A.modalError('加入会议失败'); break; @@ -254,8 +277,9 @@ export default { } // 加上自己 if (!$A.isArray(data.userids)) { - data.userids = [this.userId] - } else if (!data.userids.includes(this.userId)) { + data.userids = [] + } + if (this.userId && !data.userids.includes(this.userId)) { data.userids.push(this.userId) } // 加上音频 @@ -269,64 +293,125 @@ export default { }, onSubmit() { + this.$refs.addForm.validate((valid) => { + if (valid) { + this.onOpen() + } + }); + }, + + onOpen(isDirect = false) { if (this.meetingShow) { $A.modalWarning("正在会议中,无法进入其他会议室"); return; } - this.$refs.addForm.validate((valid) => { - if (valid) { - this.loadIng++; - this.$store.dispatch("call", { - url: 'users/meeting/open', - data: this.addData - }).then(({data}) => { - this.$set(this.addData, 'name', data.name); - this.$set(this.addData, 'meetingid', data.meetingid); - this.$set(this.localUser, 'nickname', data.nickname); - this.$set(this.localUser, 'userimg', data.userimg); - this.$store.dispatch("saveDialogMsg", data.msgs); - this.$store.dispatch("updateDialogLastMsg", data.msgs); - delete data.name; - delete data.msgs; - // - if ($A.isEEUiApp) { - $A.eeuiAppSendMessage({ - action: 'startMeeting', - meetingParams: { - name: this.addData.name, - token: data.token, - channel: data.channel, - uuid: data.uid, - appid: data.appid, - avatar: data.userimg, - username: data.nickname, - video: this.addData.tracks.includes("video"), - audio: this.addData.tracks.includes("audio"), - meetingid: data.meetingid, - sharelink: data.sharelink, - alert: { - title: this.$L('温馨提示'), - message: this.$L('确定要离开会议吗?'), - cancel: this.$L('继续'), - confirm: this.$L('退出'), - } - } - }); - } else { - $A.loadScript('js/AgoraRTC_N-4.17.0.js').then(_ => { - this.join(data) - }).catch(_ => { - $A.modalError("会议组件加载失败!"); - }).finally(_ => { - this.loadIng--; - }) - } - }).catch(({msg}) => { + const loader = (add) => { + if (isDirect) { + if (add) { + this.$store.dispatch('showSpinner'); + } else { + this.$store.dispatch('hiddenSpinner', 600); + } + } else { + if (add) { + this.loadIng++; + } else { this.loadIng--; - $A.modalError(msg); - }); + } } - + } + loader(true); + this.$store.dispatch("call", { + url: 'users/meeting/open', + data: this.addData + }).then(({data}) => { + this.$set(this.addData, 'name', data.name); + this.$set(this.addData, 'meetingid', data.meetingid); + this.$set(this.localUser, 'nickname', data.nickname); + this.$set(this.localUser, 'userimg', data.userimg); + this.$store.dispatch("saveDialogMsg", data.msgs); + this.$store.dispatch("updateDialogLastMsg", data.msgs); + delete data.name; + delete data.msgs; + // App 直接使用新窗口打开会议 + if ($A.isEEUiApp) { + $A.eeuiAppSendMessage({ + action: 'startMeeting', + meetingParams: { + name: this.addData.name, + token: data.token, + channel: data.channel, + uuid: data.uid, + appid: data.appid, + avatar: data.userimg, + username: data.nickname, + video: this.addData.tracks.includes("video"), + audio: this.addData.tracks.includes("audio"), + meetingid: data.meetingid, + sharelink: data.sharelink, + alert: { + title: this.$L('温馨提示'), + message: this.$L('确定要离开会议吗?'), + cancel: this.$L('继续'), + confirm: this.$L('退出'), + } + } + }); + return + } + // 客户端且未获得邀请链接 获取会议链接之后使用子窗口打开会议 + if ($A.Electron && !this.addData.sharekey) { + loader(true); + this.$store.dispatch("call", { + url: 'users/meeting/link', + data: { + meetingid: data.meetingid, + }, + }).then(linkRes => { + // 使用子窗口打开会议 + const config = { + title: this.addData.name, + titleFixed: true, + parent: null, + width: Math.min(window.screen.availWidth, 1440), + height: Math.min(window.screen.availHeight, 900), + } + const meetingPath = $A.urlAddParams(linkRes.data, { + type: 'direct', + nickname: encodeURIComponent(data.nickname), + avatar: encodeURIComponent(data.userimg), + audio: this.addData.tracks.includes("audio") ? 1 : 0, + video: this.addData.tracks.includes("video") ? 1 : 0, + token: this.userToken, + }); + this.$store.dispatch('openChildWindow', { + name: `meeting-window`, + path: meetingPath, + force: false, + config + }); + // 关闭弹窗 + this.addShow = false; + }).catch(({ msg }) => { + $A.modalError(msg); + }).finally(_ => { + loader(false); + }); + return; + } + // Web 加载会议组件 + loader(true); + $A.loadScript('js/AgoraRTC_N-4.17.0.js').then(_ => { + this.join(data) + }).catch(_ => { + $A.modalError("会议组件加载失败!"); + }).finally(_ => { + loader(false); + }) + }).catch(({msg}) => { + $A.modalError(msg); + }).finally(_ => { + loader(false); }); }, @@ -348,7 +433,7 @@ export default { onInvitation(type) { if (type === 'open') { - if(this.addData.sharekey){ + if (this.addData.sharekey && !this.userId) { this.linkCopy(); return; } @@ -358,6 +443,10 @@ export default { }; this.invitationShow = true; } else if (type === 'submit') { + if (this.invitationData.userids.length === 0) { + $A.modalWarning("请选择邀请成员"); + return; + } this.invitationLoad = true; this.$store.dispatch("call", { url: 'users/meeting/invitation', @@ -383,9 +472,10 @@ export default { okText: '退出', onOk: async _ => { await this.leave() - if(this.addData.sharekey){ + if ($A.isSubElectron) { + this.$Electron.sendMessage('windowDestroy'); + } else if (this.addData.sharekey) { this.addShow = true; - this.loadIng = 0; } resolve() } @@ -393,6 +483,28 @@ export default { }) }, + linkCopy() { + this.linkCopyLoad = true; + this.$store.dispatch("call", { + url: 'users/meeting/link', + data: { + meetingid: this.addData.meetingid || this.invitationData.meetingid, + sharekey: this.addData.sharekey + }, + }).then(({ data }) => { + this.copyText({ + text: data, + success: '已复制会议邀请链接', + error: "复制失败" + }); + this.invitationShow = false; + }).catch(({ msg }) => { + $A.modalError(msg); + }).finally(_ => { + this.linkCopyLoad = false; + }); + }, + async join(options) { this.loadIng++; // 音频采集设备状态变化回调 @@ -451,8 +563,8 @@ export default { console.error(error) $A.modalError("会议组件加载失败!"); } - this.loadIng--; this.addShow = false; + this.loadIng--; }, async leave() { @@ -472,8 +584,8 @@ export default { // 离开频道 await this.agoraClient.leave(); // - this.loadIng--; this.meetingShow = false; + this.loadIng--; }, async openAudio() { @@ -513,6 +625,9 @@ export default { }, async handleUserJoined(user) { + if (user.uid == this.localUser.uid) { + return; + } const index = this.remoteUsers.findIndex(item => item.uid == user.uid) if (index > -1) { this.remoteUsers.splice(index, 1, user) @@ -541,28 +656,6 @@ export default { await this.agoraClient.unsubscribe(user, mediaType); } }, - - linkCopy() { - this.linkCopyLoad = true; - this.$store.dispatch("call", { - url: 'users/meeting/link', - data: { - meetingid: this.addData.meetingid || this.invitationData.meetingid, - sharekey: this.addData.sharekey - }, - }).then(({ data }) => { - this.copyText({ - text: data, - success: '已复制会议邀请链接', - error: "复制失败" - }); - this.invitationShow = false; - }).catch(({ msg }) => { - $A.modalError(msg); - }).finally(_ => { - this.linkCopyLoad = false; - }); - }, } } diff --git a/resources/assets/js/pages/manage/components/MeetingPlayer.vue b/resources/assets/js/pages/manage/components/MeetingPlayer.vue index 913e48f53..3995055c8 100644 --- a/resources/assets/js/pages/manage/components/MeetingPlayer.vue +++ b/resources/assets/js/pages/manage/components/MeetingPlayer.vue @@ -90,7 +90,7 @@ export default { }, playerStyle() { const user = this.cacheUserBasic.find(({userid}) => userid == this.userid); - if (user) { + if (user && user.userimg) { return { backgroundImage: `url("${user.userimg}")` } diff --git a/resources/assets/js/pages/meeting.vue b/resources/assets/js/pages/meeting.vue index 5000a7153..3e8d2f0ba 100644 --- a/resources/assets/js/pages/meeting.vue +++ b/resources/assets/js/pages/meeting.vue @@ -12,10 +12,16 @@ export default { MeetingManager, }, mounted() { + const {meetingId, sharekey} = this.$route.params; + const {nickname, avatar, audio, video, type} = this.$route.query; this.$store.dispatch("showMeetingWindow",{ - type: "join", - meetingid: this.$route.params.meetingId, - meetingSharekey: this.$route.params.sharekey, + type: ['direct', 'join'].includes(type) ? type : 'join', + meetingid: meetingId, + meetingSharekey: sharekey, + meetingNickname: nickname, + meetingAvatar: avatar, + meetingAudio: audio, + meetingVideo: video, meetingdisabled: true, }) }, diff --git a/resources/assets/js/store/actions.js b/resources/assets/js/store/actions.js index 64faf72b8..7d8571b8b 100644 --- a/resources/assets/js/store/actions.js +++ b/resources/assets/js/store/actions.js @@ -3515,8 +3515,16 @@ export default { /** * 隐藏全局浮窗加载器 * @param state + * @param dispatch + * @param delay */ - hiddenSpinner({state}) { + hiddenSpinner({state, dispatch}, delay) { + if (typeof delay === "number") { + setTimeout(_ => { + dispatch("hiddenSpinner") + }, delay) + return + } const item = state.floatSpinnerTimer.shift() if (item) { clearTimeout(item.timer) @@ -4193,19 +4201,12 @@ export default { /** * 显示会议窗口 * @param state - * @param type - * @param meetingid - * @param meetingdisabled - * @param meetingSharekey + * @param data */ - showMeetingWindow({state}, {type, meetingid, meetingdisabled, meetingSharekey}) { - state.meetingWindow = { - show: true, - type: type, - meetingid: meetingid, - meetingdisabled: meetingdisabled, - meetingSharekey: meetingSharekey - }; + showMeetingWindow({state}, data) { + state.meetingWindow = Object.assign(data, { + show: data.type !== 'direct', + }); }, /** *****************************************************************************************/ diff --git a/resources/assets/sass/pages/components/meeting-manager.scss b/resources/assets/sass/pages/components/meeting-manager.scss index 44f550e4e..d514e52e2 100644 --- a/resources/assets/sass/pages/components/meeting-manager.scss +++ b/resources/assets/sass/pages/components/meeting-manager.scss @@ -9,7 +9,7 @@ body { > ul { display: grid; justify-content: space-between; - grid-template-columns: repeat(auto-fill, 220px); + grid-template-columns: repeat(auto-fill, 210px); grid-gap: 24px; > li { list-style: none; @@ -17,8 +17,8 @@ body { .meeting-player { position: relative; .player { - width: 220px; - height: 220px;; + width: 210px; + height: 210px;; border-radius: 12px; position: relative; z-index: 1;