feat:修改文件上传,白板关闭不影响其他用户等

This commit is contained in:
leilei
2025-11-10 17:55:52 +08:00
parent df359d01cc
commit 6fbe53009c
23 changed files with 17128 additions and 1623 deletions

BIN
dist.zip

Binary file not shown.

View File

@@ -121,3 +121,11 @@ export function getConvertStatusApi(taskId,roomId) {
}) })
} }
//获取当前会议中所有参与者信息
export function getParticipantsApi(roomId) {
return request({
url: `/api/v1/meeting/${ roomId }/participants`,
method: 'get',
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@@ -135,14 +135,14 @@ service.interceptors.response.use(
return Promise.resolve(responseData); return Promise.resolve(responseData);
case 401: case 401:
// return Promise.resolve(responseData); // return Promise.resolve(responseData);
const currentPath = router.currentRoute.value.name; // const currentPath = router.currentRoute.value.name;
if(currentPath == 'ConferencingRoom'){ // if(currentPath == 'ConferencingRoom'){
return Promise.resolve(responseData); // return Promise.resolve(responseData);
}else{ // }else{
return handleUnauthorized().then(() => { return handleUnauthorized().then(() => {
return Promise.reject({ code: 401, message: '未授权' }); return Promise.reject({ code: 401, message: '未授权' });
}); });
} // }
case 500: case 500:
const serverErrorMsg = responseData.meta?.message || '服务器内部错误'; const serverErrorMsg = responseData.meta?.message || '服务器内部错误';
ElMessage({ message: serverErrorMsg, type: 'error' }); ElMessage({ message: serverErrorMsg, type: 'error' });
@@ -211,8 +211,6 @@ function handleUnauthorized() {
import('vue').then(({ nextTick }) => { import('vue').then(({ nextTick }) => {
nextTick(() => { nextTick(() => {
const currentPath = router.currentRoute.value.fullPath; const currentPath = router.currentRoute.value.fullPath;
console.log('当前路由:', currentPath);
if (router.currentRoute.value.path !== '/login') { if (router.currentRoute.value.path !== '/login') {
router.push({ router.push({
path: '/login', path: '/login',

View File

@@ -25,7 +25,6 @@ function getLocalUserData() {
export const WhiteboardSync = { export const WhiteboardSync = {
async init(canvas, roomUid) { async init(canvas, roomUid) {
if (!canvas || !roomUid) return; if (!canvas || !roomUid) return;
console.log('初始化多人同步:', roomUid);
canvasInstance = canvas; canvasInstance = canvas;
const localUser = getLocalUserData(); const localUser = getLocalUserData();
@@ -33,8 +32,6 @@ export const WhiteboardSync = {
try { try {
// 先连接 MQTT // 先连接 MQTT
await mqttClient.connect(meterStore.getSudid()); await mqttClient.connect(meterStore.getSudid());
console.log("✅ MQTT 已连接");
// 获取历史数据 // 获取历史数据
const res = await getWhiteboardHistory({ after_timestamp: 0 }, roomUid); const res = await getWhiteboardHistory({ after_timestamp: 0 }, roomUid);
if (res.meta.code === 200 && res.data.shapes.length > 0) { if (res.meta.code === 200 && res.data.shapes.length > 0) {
@@ -48,7 +45,6 @@ export const WhiteboardSync = {
mqttClient.subscribe(topic, async (shapeData) => { mqttClient.subscribe(topic, async (shapeData) => {
const shapeDataNew = JSON.parse(shapeData.toString()) const shapeDataNew = JSON.parse(shapeData.toString())
// const shapeDataNew = decode(message); // const shapeDataNew = decode(message);
// console.log(shapeDataNew, '格式解码')
try { try {
isRemote = true; isRemote = true;
// 如果 shape 来自本地用户,则跳过 // 如果 shape 来自本地用户,则跳过
@@ -66,8 +62,6 @@ export const WhiteboardSync = {
isRemote = false; isRemote = false;
} }
}); });
console.log("✅ 已订阅:", topic);
} catch (err) { } catch (err) {
console.log("初始化多人同步失败:", err) console.log("初始化多人同步失败:", err)
// console.error("❌ 连接或订阅失败:", err); // console.error("❌ 连接或订阅失败:", err);

View File

@@ -43,11 +43,11 @@ export function getDeviceName(devices, deviceId) {
} }
export function handleVideoLoaded(identity, type) { export function handleVideoLoaded(identity, type) {
console.log(`视频加载完成: ${identity}${type}视频`); // console.log(`视频加载完成: ${identity}的${type}视频`);
} }
export function handleConnectionStateChanged(state) { export function handleConnectionStateChanged(state) {
console.log('连接状态改变:', state); // console.log('连接状态改变:', state);
} }
// 平滑曲线绘制函数 // 平滑曲线绘制函数
@@ -100,3 +100,8 @@ export async function calculateFileSHA1(file) {
export function simpleDeepEqual(obj1, obj2) { export function simpleDeepEqual(obj1, obj2) {
return JSON.stringify(obj1) === JSON.stringify(obj2); return JSON.stringify(obj1) === JSON.stringify(obj2);
} }
// 生成唯一ID
export function generateElementId() {
return `laser_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}

View File

@@ -3,7 +3,6 @@
<template #header> <template #header>
<h4>请选择要加入房间的人员</h4> <h4>请选择要加入房间的人员</h4>
</template> </template>
<div class="drawer-content"> <div class="drawer-content">
<!-- 搜索框 --> <!-- 搜索框 -->
<div class="search-section"> <div class="search-section">
@@ -36,7 +35,7 @@
</div> </div>
<!-- 树形结构 --> <!-- 树形结构 -->
<el-scrollbar class="left-list-scrollbar" height="calc(100vh - 240px)"> <el-scrollbar v-loading="leftListLoading" class="left-list-scrollbar" height="calc(100vh - 240px)">
<el-tree <el-tree
ref="treeRef" ref="treeRef"
lazy lazy
@@ -58,6 +57,7 @@
<span v-else class="dept-icon">📁</span> <span v-else class="dept-icon">📁</span>
<span class="tree-item-text">{{ data.name }}</span> <span class="tree-item-text">{{ data.name }}</span>
<span v-if="data.users_count" class="user-count">({{ data.users_count }})</span> <span v-if="data.users_count" class="user-count">({{ data.users_count }})</span>
<!-- <span v-if="isNodeDisabled(data)" class="already-joined-tag">已在房间中</span> -->
</div> </div>
</div> </div>
</template> </template>
@@ -81,6 +81,7 @@ import {
getDirectories, getDirectories,
getDirectoriesUsers getDirectoriesUsers
} from '@/api/coordinate.js' } from '@/api/coordinate.js'
import { getParticipantsApi } from '@/api/conferencingRoom.js'
import { nextTick, reactive, toRefs, watch, onMounted, ref } from "vue"; import { nextTick, reactive, toRefs, watch, onMounted, ref } from "vue";
// 定义 emit // 定义 emit
@@ -100,27 +101,48 @@ import {
const searchKeyword = ref(''); const searchKeyword = ref('');
const selectedUsers = ref([]); const selectedUsers = ref([]);
const checkedNodes = ref([]); const checkedNodes = ref([]);
const leftListLoading = ref(false)
const joinedUserIds = ref([]); // 存储已在房间中的用户ID
// 树形配置 // 树形配置
const treeProps = reactive({ const treeProps = reactive({
children: 'users', children: 'users',
label: 'name', label: 'name',
value: 'uid', value: 'uid',
disabled: (data) => {
// 只有人员节点有uid且该用户已在房间中时才禁用
return data.uid && joinedUserIds.value.includes(data.uid);
},
isLeaf: (node) => { isLeaf: (node) => {
return !!node.uid; // 有 uid 的为叶子节点(人员) return !!node.uid; // 有 uid 的为叶子节点(人员)
}, },
}); });
const isNodeDisabled = (data) => {
// 只有人员节点有uid且该用户已在房间中时才禁用
return data.uid && joinedUserIds.value.includes(data.uid);
};
// 显示抽屉 // 显示抽屉
function show() { async function show(roomId) {
drawerVisible.value = true; drawerVisible.value = true;
// 重置选择状态 // 重置选择状态
selectedUsers.value = []; selectedUsers.value = [];
checkedNodes.value = []; checkedNodes.value = [];
searchKeyword.value = ''; searchKeyword.value = '';
// 获取已在房间中的用户
try {
const res = await getParticipantsApi(roomId);
// 提取状态为1在房间中的用户ID
joinedUserIds.value = res.data
.filter(item => item.status == 1)
.map(item => item.user_uid);
} catch (error) {
console.error('获取房间参与者失败:', error);
joinedUserIds.value = [];
}
// 延迟加载树形数据,确保 DOM 已渲染 // 延迟加载树形数据,确保 DOM 已渲染
nextTick(() => { nextTick(() => {
if (treeRef.value) { if (treeRef.value) {
@@ -128,6 +150,11 @@ import {
} }
}); });
} }
async function getJoinUsers(){
const res = await getParticipantsApi(roomId)
const joinUsers = res.data.filter(item => item.status == 1).map(item => item.user_uid)
return joinUsers;
}
// 确认选择 // 确认选择
function confirmClick() { function confirmClick() {
@@ -144,30 +171,54 @@ import {
// 加载树节点 // 加载树节点
const handleLoadNode = async (node, resolve) => { const handleLoadNode = async (node, resolve) => {
if (node?.level === 0) { if(node?.level === 0){
loadNode(resolve); loadNode(resolve,'',node?.level)
} else if (node?.level === 1) { }else if(node?.level > 0){
loadNode(resolve, node.data.directory_uid); if(node.data.directory_uid){
loadUserNode(resolve,node.data.directory_uid,node?.level)
}else{
resolve(resolve)
}
} }
} }
// 加载节点数据 // 加载节点数据
const loadNode = async (resolve, id) => { const loadNode = async (resolve, id) => {
try { try {
if (!id) { leftListLoading.value = true
let res = await getDirectories({ level: 1 }); let res = await getDirectories({level:1})
if (res.meta.code == 200) { if(res.meta.code == 200){
resolve(res.data); resolve(res.data)
}
} else {
let res = await getDirectoriesUsers(id, { directory_uuid: id });
resolve(res.data);
} }
leftListLoading.value = false
} catch (error) { } catch (error) {
console.log(error); console.log(error)
leftListLoading.value = false
} }
} }
const loadUserNode = async(resolve,id,level)=>{
try {
leftListLoading.value = true
let userData = []
let orgData = []
const resOrg = await getDirectories({level: 1,parent_uuid:id})
if(resOrg?.data){
orgData = resOrg.data
}
if(id){
const res = await getDirectoriesUsers(id,{directory_uuid:id})
userData = res.data
}
resolve([...orgData, ...userData])
leftListLoading.value = false
} catch (error) {
console.log(error)
leftListLoading.value = false
}
}
// 处理复选框选择变化 // 处理复选框选择变化
const handleCheckChange = (checkedNode, checkedNodesInfo) => { const handleCheckChange = (checkedNode, checkedNodesInfo) => {
// 获取所有选中的节点 // 获取所有选中的节点
@@ -177,8 +228,10 @@ import {
// 获取所有选中节点数据 // 获取所有选中节点数据
const allCheckedNodes = treeRef.value.getCheckedNodes(false, true); const allCheckedNodes = treeRef.value.getCheckedNodes(false, true);
// 筛选出人员节点(有 uid 的节点) // 筛选出人员节点(有 uid 的节点)且不在已加入用户列表中的
const userNodes = allCheckedNodes.filter(node => node.uid); const userNodes = allCheckedNodes.filter(node =>
node.uid && !joinedUserIds.value.includes(node.uid)
);
// 更新已选用户列表 // 更新已选用户列表
selectedUsers.value = userNodes; selectedUsers.value = userNodes;

View File

@@ -156,6 +156,22 @@
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
<el-dialog
v-model="dialogFileVisible"
title="文件转换"
width="50%"
class="file-preview-dialog"
>
<div class="preview-container">
<div class="loading-container">
<el-icon class="is-loading" size="48">
<Loading />
</el-icon>
<p>正在下载文件请稍候...</p>
</div>
</div>
</el-dialog>
</div> </div>
</template> </template>
@@ -208,6 +224,8 @@ const fileName = ref('')
const textContent = ref('') const textContent = ref('')
const currentFileData = ref(null) const currentFileData = ref(null)
const convertTaskId = ref('') const convertTaskId = ref('')
const dialogFileVisible = ref(false)
// PDF相关状态 // PDF相关状态
const pdfSource = ref('') const pdfSource = ref('')
@@ -261,7 +279,12 @@ onMounted(async () => {
emitter.on('subscribeToFileConversionStatusTopic',subscribeToFileConversionStatusTopic) emitter.on('subscribeToFileConversionStatusTopic',subscribeToFileConversionStatusTopic)
emitter.on('fileUploadStatus',fileUploadStatus) emitter.on('fileUploadStatus',fileUploadStatus)
emitter.on('subscribeToFilePreviewTopic',subscribeToFilePreviewTopic) emitter.on('subscribeToFilePreviewTopic',subscribeToFilePreviewTopic)
emitter.on('fileSuccess',fileSuccess)
function fileSuccess(){
dialogFileVisible.value = true
}
//
function subscribeToFileConversionStatusTopic(data){ function subscribeToFileConversionStatusTopic(data){
try { try {
const userId = JSON.parse(sessionStorage.getItem('userData'))?.uid const userId = JSON.parse(sessionStorage.getItem('userData'))?.uid
@@ -286,12 +309,9 @@ function subscribeToFilePreviewTopic(data){
} }
} }
//提交转换任务,但文件暂未转换完成
function fileUploadStatus(data){ function fileUploadStatus(data){
if(!dialogFormVisible.value){ // console.log('文件上传成功mqtt消息')
}
} }
// 初始化MQTT连接 pdf转换成功 // 初始化MQTT连接 pdf转换成功
@@ -328,8 +348,13 @@ function handleFileUploadMessage(payload, topic){
try { try {
const messageStr = payload.toString() const messageStr = payload.toString()
const data = JSON.parse(messageStr) const data = JSON.parse(messageStr)
emitter.emit('fileUploadStatus') // emitter.emit('fileUploadStatus')
if(dialogFormVisible.value){ console.log(data,'data文件转换完成预览通知')
const userId = JSON.parse(sessionStorage.getItem('userData'))?.uid
if(dialogFileVisible.value){
dialogFileVisible.value = false
}
if(dialogFormVisible.value && userId != data.user_uid){
// 显示确认对话框 // 显示确认对话框
ElMessageBox.confirm( ElMessageBox.confirm(
`用户${data.user_uid}上传了${data.file_name}文件,是否立即预览?`, `用户${data.user_uid}上传了${data.file_name}文件,是否立即预览?`,

View File

@@ -78,7 +78,6 @@ const props = defineProps({
emitter.on('fileUploadStatus',async ()=>{ emitter.on('fileUploadStatus',async ()=>{
if(drawerVisible.value){ if(drawerVisible.value){
console.log('更新啦')
await getFileList() await getFileList()
} }
}) })
@@ -161,7 +160,7 @@ async function getFileList(){
} }
fileList.value = res.data.files || []; fileList.value = res.data.files || [];
} catch (error) { } catch (error) {
console.error('获取文件列表失败:', error); // console.error('获取文件列表失败:', error);
ElMessage.error('获取文件列表失败'); ElMessage.error('获取文件列表失败');
} finally { } finally {
loading.value = false; loading.value = false;

View File

@@ -419,7 +419,7 @@
} }
const handlePdfRendered = () => { const handlePdfRendered = () => {
console.log('PDF页面渲染完成') // console.log('PDF页面渲染完成')
} }
const handlePdfError = (error) => { const handlePdfError = (error) => {

View File

@@ -203,6 +203,8 @@ const handleHttpRequest = async (file) => {
// 上传成功取消loading // 上传成功取消loading
uploadLoading.value = false uploadLoading.value = false
dialogFormVisible.value = false dialogFormVisible.value = false
emitter.emit('fileSuccess')
return true return true
} catch (error) { } catch (error) {
console.error('上传文件失败:', error) console.error('上传文件失败:', error)
@@ -234,7 +236,7 @@ function publishFileUploadData(fileData) {
}; };
mqttClient.publish(`xSynergy/File/Upload/${roomId.value}`, message); mqttClient.publish(`xSynergy/File/Upload/${roomId.value}`, message);
} catch (error) { } catch (error) {
console.error('发布激光笔数据失败:', error); console.error('发布文件数据失败:', error);
} }
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -235,7 +235,7 @@ const searchList = () => {
state.dataList = [] state.dataList = []
getList() getList()
} else { } else {
console.log('treeRef.filter',state.treeRef) // console.log('treeRef.filter',state.treeRef)
state.treeRef.filter(state.queryFrom.nickName) state.treeRef.filter(state.queryFrom.nickName)
} }
} }
@@ -306,24 +306,45 @@ const getList = async () => {
*/ */
const HandleLoadNode = async (node, resolve) => { const HandleLoadNode = async (node, resolve) => {
if(node?.level === 0){ if(node?.level === 0){
loadNode(resolve) loadNode(resolve,'',node?.level)
}else if(node?.level === 1){ }else if(node?.level > 0){
loadNode(resolve,node.data.directory_uid) if(node.data.directory_uid){
loadUserNode(resolve,node.data.directory_uid,node?.level)
}else{
resolve(resolve)
}
} }
} }
const loadNode = async(resolve,id)=>{ const loadNode = async(resolve,id,level)=>{
try { try {
state.leftListLoading = true state.leftListLoading = true
if(!id){
let res = await getDirectories({level:1}) let res = await getDirectories({level:1})
if(res.meta.code == 200){ if(res.meta.code == 200){
resolve(res.data) resolve(res.data)
} }
}else{ state.leftListLoading = false
let res = await getDirectoriesUsers(id,{directory_uuid:id}) } catch (error) {
resolve(res.data) console.log(error)
state.leftListLoading = false
} }
}
const loadUserNode = async(resolve,id,level)=>{
try {
state.leftListLoading = true
let userData = []
let orgData = []
const resOrg = await getDirectories({level: 1,parent_uuid:id})
if(resOrg?.data){
orgData = resOrg.data
}
if(id){
const res = await getDirectoriesUsers(id,{directory_uuid:id})
userData = res.data
}
resolve([...orgData, ...userData])
state.leftListLoading = false state.leftListLoading = false
} catch (error) { } catch (error) {
console.log(error) console.log(error)

View File

@@ -447,7 +447,7 @@ onMounted(async () => {
const res = await userStore.getInfo() const res = await userStore.getInfo()
const topic = `xSynergy/ROOM/+/rooms/${res.uid}`; const topic = `xSynergy/ROOM/+/rooms/${res.uid}`;
mqttClient.subscribe(topic, async (shapeData) => { mqttClient.subscribe(topic, async (shapeData) => {
console.log(shapeData.toString(),'shapeData发送邀请') // console.log(shapeData.toString(),'shapeData发送邀请')
processingSocket(shapeData.toString()) processingSocket(shapeData.toString())
}); });
}) })

View File

@@ -54,7 +54,7 @@ async function joinWhiteboard() {
try { try {
const clientId = `whiteboard-${uuidv4()}`; const clientId = `whiteboard-${uuidv4()}`;
await mqttClient.connect(clientId); await mqttClient.connect(clientId);
console.log("✅ 已连接 MQTT:", clientId); // console.log("✅ 已连接 MQTT:", clientId);
hasJoined.value = true; hasJoined.value = true;

View File

@@ -50,10 +50,9 @@ const redirect = ref(undefined);
const loginView = ref(true) const loginView = ref(true)
// 监听路由变化,获取重定向参数 // 监听路由变化,获取重定向参数
// watch(() => route, (newRoute) => { watch(() => route, (newRoute) => {
// redirect.value = newRoute.query && newRoute.query.redirect; redirect.value = newRoute.query && newRoute.query.redirect;
// console.log('重定向路径:', redirect.value); }, { immediate: true });
// }, { immediate: true });
const loginForm = ref({ const loginForm = ref({
username: '', username: '',
@@ -110,7 +109,6 @@ const loading = ref(false)
try { try {
// 如果有重定向路径且不是登录页,则跳转到重定向页面 // 如果有重定向路径且不是登录页,则跳转到重定向页面
if (redirect.value && redirect.value !== '/login') { if (redirect.value && redirect.value !== '/login') {
console.log('跳转到重定向页面:', redirect.value);
// 确保路由存在,如果不存在则跳转到默认页面 // 确保路由存在,如果不存在则跳转到默认页面
try { try {