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

View File

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

View File

@@ -43,11 +43,11 @@ export function getDeviceName(devices, deviceId) {
}
export function handleVideoLoaded(identity, type) {
console.log(`视频加载完成: ${identity}${type}视频`);
// console.log(`视频加载完成: ${identity}的${type}视频`);
}
export function handleConnectionStateChanged(state) {
console.log('连接状态改变:', state);
// console.log('连接状态改变:', state);
}
// 平滑曲线绘制函数
@@ -100,3 +100,8 @@ export async function calculateFileSHA1(file) {
export function simpleDeepEqual(obj1, 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

@@ -1,11 +1,10 @@
<template>
<el-drawer v-model="drawerVisible" direction="rtl" title="请选择要加入房间的人员" size="40%">
<template #header>
<h4>请选择要加入房间的人员</h4>
<h4>请选择要加入房间的人员</h4>
</template>
<div class="drawer-content">
<!-- 搜索框 -->
<!-- 搜索框 -->
<div class="search-section">
<el-input
v-model="searchKeyword"
@@ -36,7 +35,7 @@
</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
ref="treeRef"
lazy
@@ -58,12 +57,13 @@
<span v-else class="dept-icon">📁</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="isNodeDisabled(data)" class="already-joined-tag">已在房间中</span> -->
</div>
</div>
</template>
</el-tree>
</el-scrollbar>
</div>
</div>
<template #footer>
<div class="footer-actions">
@@ -81,6 +81,7 @@ import {
getDirectories,
getDirectoriesUsers
} from '@/api/coordinate.js'
import { getParticipantsApi } from '@/api/conferencingRoom.js'
import { nextTick, reactive, toRefs, watch, onMounted, ref } from "vue";
// 定义 emit
@@ -100,27 +101,48 @@ import {
const searchKeyword = ref('');
const selectedUsers = ref([]);
const checkedNodes = ref([]);
const leftListLoading = ref(false)
const joinedUserIds = ref([]); // 存储已在房间中的用户ID
// 树形配置
const treeProps = reactive({
children: 'users',
label: 'name',
value: 'uid',
disabled: (data) => {
// 只有人员节点有uid且该用户已在房间中时才禁用
return data.uid && joinedUserIds.value.includes(data.uid);
},
isLeaf: (node) => {
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;
// 重置选择状态
selectedUsers.value = [];
checkedNodes.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 已渲染
nextTick(() => {
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() {
@@ -144,30 +171,54 @@ import {
// 加载树节点
const handleLoadNode = async (node, resolve) => {
if (node?.level === 0) {
loadNode(resolve);
} else if (node?.level === 1) {
loadNode(resolve, node.data.directory_uid);
if(node?.level === 0){
loadNode(resolve,'',node?.level)
}else if(node?.level > 0){
if(node.data.directory_uid){
loadUserNode(resolve,node.data.directory_uid,node?.level)
}else{
resolve(resolve)
}
}
}
// 加载节点数据
const loadNode = async (resolve, id) => {
try {
if (!id) {
let res = await getDirectories({ level: 1 });
if (res.meta.code == 200) {
resolve(res.data);
leftListLoading.value = true
let res = await getDirectories({level:1})
if(res.meta.code == 200){
resolve(res.data)
}
} else {
let res = await getDirectoriesUsers(id, { directory_uuid: id });
resolve(res.data);
}
leftListLoading.value = false
} 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) => {
// 获取所有选中的节点
@@ -177,8 +228,10 @@ import {
// 获取所有选中节点数据
const allCheckedNodes = treeRef.value.getCheckedNodes(false, true);
// 筛选出人员节点(有 uid 的节点)
const userNodes = allCheckedNodes.filter(node => node.uid);
// 筛选出人员节点(有 uid 的节点)且不在已加入用户列表中的
const userNodes = allCheckedNodes.filter(node =>
node.uid && !joinedUserIds.value.includes(node.uid)
);
// 更新已选用户列表
selectedUsers.value = userNodes;

View File

@@ -156,6 +156,22 @@
</div>
</template>
</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>
</template>
@@ -208,6 +224,8 @@ const fileName = ref('')
const textContent = ref('')
const currentFileData = ref(null)
const convertTaskId = ref('')
const dialogFileVisible = ref(false)
// PDF相关状态
const pdfSource = ref('')
@@ -261,7 +279,12 @@ onMounted(async () => {
emitter.on('subscribeToFileConversionStatusTopic',subscribeToFileConversionStatusTopic)
emitter.on('fileUploadStatus',fileUploadStatus)
emitter.on('subscribeToFilePreviewTopic',subscribeToFilePreviewTopic)
emitter.on('fileSuccess',fileSuccess)
function fileSuccess(){
dialogFileVisible.value = true
}
//
function subscribeToFileConversionStatusTopic(data){
try {
const userId = JSON.parse(sessionStorage.getItem('userData'))?.uid
@@ -286,12 +309,9 @@ function subscribeToFilePreviewTopic(data){
}
}
//提交转换任务,但文件暂未转换完成
function fileUploadStatus(data){
if(!dialogFormVisible.value){
}
// console.log('文件上传成功mqtt消息')
}
// 初始化MQTT连接 pdf转换成功
@@ -328,8 +348,13 @@ function handleFileUploadMessage(payload, topic){
try {
const messageStr = payload.toString()
const data = JSON.parse(messageStr)
emitter.emit('fileUploadStatus')
if(dialogFormVisible.value){
// emitter.emit('fileUploadStatus')
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(
`用户${data.user_uid}上传了${data.file_name}文件,是否立即预览?`,

View File

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

View File

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

View File

@@ -203,6 +203,8 @@ const handleHttpRequest = async (file) => {
// 上传成功取消loading
uploadLoading.value = false
dialogFormVisible.value = false
emitter.emit('fileSuccess')
return true
} catch (error) {
console.error('上传文件失败:', error)
@@ -234,7 +236,7 @@ function publishFileUploadData(fileData) {
};
mqttClient.publish(`xSynergy/File/Upload/${roomId.value}`, message);
} 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 = []
getList()
} else {
console.log('treeRef.filter',state.treeRef)
// console.log('treeRef.filter',state.treeRef)
state.treeRef.filter(state.queryFrom.nickName)
}
}
@@ -306,22 +306,22 @@ const getList = async () => {
*/
const HandleLoadNode = async (node, resolve) => {
if(node?.level === 0){
loadNode(resolve)
}else if(node?.level === 1){
loadNode(resolve,node.data.directory_uid)
loadNode(resolve,'',node?.level)
}else if(node?.level > 0){
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 {
state.leftListLoading = true
if(!id){
let res = await getDirectories({level:1})
if(res.meta.code == 200){
resolve(res.data)
}
}else{
let res = await getDirectoriesUsers(id,{directory_uuid:id})
let res = await getDirectories({level:1})
if(res.meta.code == 200){
resolve(res.data)
}
state.leftListLoading = false
@@ -331,6 +331,27 @@ const loadNode = async(resolve,id)=>{
}
}
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
} catch (error) {
console.log(error)
state.leftListLoading = false
}
}
// try {
// if (userList.data.sub_units?.length) {
// state.dataList = userList.data.sub_units

View File

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

View File

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

View File

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