diff --git a/.env.development b/.env.development index 6d1fc1d..604cf80 100644 --- a/.env.development +++ b/.env.development @@ -7,6 +7,7 @@ VITE_APP_PORT = 80 # 应用模板管理后台/开发环境 VITE_APP_BASE_API = '/dev-api' +VITE_APP_BASE_API_livekit = '/livekit-api' # 后端实际地址(可选,用于proxy.target) VITE_API_TARGET = 'https://xsynergy.gxtech.ltd' diff --git a/package-lock.json b/package-lock.json index 42fb52f..b676d66 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "code-inspector-plugin": "^0.20.12", "element-plus": "^2.2.27", "js-cookie": "^3.0.1", + "livekit-client": "^2.7.5", "mitt": "^3.0.0", "mqtt": "^5.14.0", "pinia": "^2.0.22", @@ -94,6 +95,11 @@ "node": ">=6.9.0" } }, + "node_modules/@bufbuild/protobuf": { + "version": "1.10.1", + "resolved": "https://registry.npmmirror.com/@bufbuild/protobuf/-/protobuf-1.10.1.tgz", + "integrity": "sha512-wJ8ReQbHxsAfXhrf9ixl0aYbZorRuOWpBNzm8pL8ftmSxQx/wnJD5Eg861NwJU/czy2VXFIebCeZnZrI9rktIQ==" + }, "node_modules/@ctrl/tinycolor": { "version": "3.6.1", "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", @@ -603,6 +609,19 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@livekit/mutex": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/@livekit/mutex/-/mutex-1.0.0.tgz", + "integrity": "sha512-aiUhoThBNF9UyGTxEURFzJLhhPLIVTnQiEVMjRhPnfHNKLfo2JY9xovHKIus7B78UD5hsP6DlgpmAsjrz4U0Iw==" + }, + "node_modules/@livekit/protocol": { + "version": "1.29.4", + "resolved": "https://registry.npmmirror.com/@livekit/protocol/-/protocol-1.29.4.tgz", + "integrity": "sha512-dsqxvABHilrMA0BU5m1w8cMWSVeDjV2ZUIUDClNQZju3c30DLMfEYDHU5nmXDfaaHjNIgoRbYR7upJMozG8JJg==", + "dependencies": { + "@bufbuild/protobuf": "^1.10.0" + } + }, "node_modules/@msgpack/msgpack": { "version": "3.1.2", "resolved": "https://registry.npmmirror.com/@msgpack/msgpack/-/msgpack-3.1.2.tgz", @@ -2279,6 +2298,27 @@ "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "node_modules/livekit-client": { + "version": "2.7.5", + "resolved": "https://registry.npmmirror.com/livekit-client/-/livekit-client-2.7.5.tgz", + "integrity": "sha512-sPhHYwXvG75y1LDC50dDC9k6Z49L2vc/HcMRhzhi7yBca6ofPEebpB0bmPOry4ovrnFA+a8TL1pFR2mko1/clw==", + "dependencies": { + "@livekit/mutex": "1.0.0", + "@livekit/protocol": "1.29.4", + "events": "^3.3.0", + "loglevel": "^1.8.0", + "sdp-transform": "^2.14.1", + "ts-debounce": "^4.0.0", + "tslib": "2.7.0", + "typed-emitter": "^2.1.0", + "webrtc-adapter": "^9.0.0" + } + }, + "node_modules/livekit-client/node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" + }, "node_modules/local-pkg": { "version": "1.1.2", "resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-1.1.2.tgz", @@ -2316,6 +2356,18 @@ "lodash-es": "*" } }, + "node_modules/loglevel": { + "version": "1.9.2", + "resolved": "https://registry.npmmirror.com/loglevel/-/loglevel-1.9.2.tgz", + "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz", @@ -3021,6 +3073,15 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmmirror.com/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "optional": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -3063,6 +3124,19 @@ "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==", "dev": true }, + "node_modules/sdp": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/sdp/-/sdp-3.2.1.tgz", + "integrity": "sha512-lwsAIzOPlH8/7IIjjz3K0zYBk7aBVVcvjMwt3M4fLxpjMYyy7i3I97SLHebgn4YBjirkzfp3RvRDWSKsh/+WFw==" + }, + "node_modules/sdp-transform": { + "version": "2.15.0", + "resolved": "https://registry.npmmirror.com/sdp-transform/-/sdp-transform-2.15.0.tgz", + "integrity": "sha512-KrOH82c/W+GYQ0LHqtr3caRpM3ITglq3ljGUIb8LTki7ByacJZ9z+piSGiwZDsRyhQbYBOBJgr2k6X4BZXi3Kw==", + "bin": { + "sdp-verify": "checker.js" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3375,6 +3449,11 @@ "node": ">=8.0" } }, + "node_modules/ts-debounce": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/ts-debounce/-/ts-debounce-4.0.0.tgz", + "integrity": "sha512-+1iDGY6NmOGidq7i7xZGA4cm8DAa6fqdYcvO5Z6yBevH++Bdo9Qt/mN0TzHUgcCcKv1gmh9+W5dHqz8pMWbCbg==" + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmmirror.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -3385,6 +3464,14 @@ "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, + "node_modules/typed-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/typed-emitter/-/typed-emitter-2.1.0.tgz", + "integrity": "sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==", + "optionalDependencies": { + "rxjs": "*" + } + }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmmirror.com/typedarray/-/typedarray-0.0.6.tgz", @@ -3751,6 +3838,18 @@ "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", "dev": true }, + "node_modules/webrtc-adapter": { + "version": "9.0.3", + "resolved": "https://registry.npmmirror.com/webrtc-adapter/-/webrtc-adapter-9.0.3.tgz", + "integrity": "sha512-5fALBcroIl31OeXAdd1YUntxiZl1eHlZZWzNg3U4Fn+J9/cGL3eT80YlrsWGvj2ojuz1rZr2OXkgCzIxAZ7vRQ==", + "dependencies": { + "sdp": "^3.2.0" + }, + "engines": { + "node": ">=6.0.0", + "npm": ">=3.10.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 54fbabe..e72c4bb 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "code-inspector-plugin": "^0.20.12", "element-plus": "^2.2.27", "js-cookie": "^3.0.1", + "livekit-client": "^2.7.5", "mitt": "^3.0.0", "mqtt": "^5.14.0", "pinia": "^2.0.22", diff --git a/src/api/conferencingRoom.js b/src/api/conferencingRoom.js new file mode 100644 index 0000000..5c4bc55 --- /dev/null +++ b/src/api/conferencingRoom.js @@ -0,0 +1,28 @@ +import request from '@/views/conferencingRoom/utils/request.js' +import { tansParams } from "@/utils/ruoyi"; + +// 获取roomtoken +export function getRoomToken(data) { + return request({ + url: `/room/token`, + method: 'post', + data: tansParams(data), + }) +} + +// 创建房间 +export function createRoom(data) { + return request({ + url: `/room/`, + method: 'post', + data: tansParams(data), + }) +} + +//获取所有房间列表 +export function getRoomList() { + return request({ + url: `/room/ `, + method: 'get', + }) +} \ No newline at end of file diff --git a/src/assets/styles/element-ui.scss b/src/assets/styles/element-ui.scss index 5209f8f..178c2ba 100644 --- a/src/assets/styles/element-ui.scss +++ b/src/assets/styles/element-ui.scss @@ -71,8 +71,7 @@ align-items: center; justify-content: center; width: 100%; - height: 100%; - + height: 100%; .el-dialog { margin: 0 auto !important; diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss index d52a54e..d0394c1 100644 --- a/src/assets/styles/index.scss +++ b/src/assets/styles/index.scss @@ -7,9 +7,9 @@ @import "./ruoyi.scss"; @import 'element-plus/theme-chalk/index.css'; -@tailwind base; -@tailwind components; -@tailwind utilities; +// @tailwind base; +// @tailwind components; +// @tailwind utilities; body { height: 100%; diff --git a/src/router/index.js b/src/router/index.js index ae93e0d..8bafdb1 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -30,6 +30,18 @@ const router = createRouter({ } ] }, + { + path: "/conferencingRoom", + children: [ + { + path: '', + name: "Coordinate", + component: () => import("@/views/conferencingRoom/index.vue") + } + ] + }, + + // 错误页面路由 { path: "/:pathMatch(.*)*", diff --git a/src/utils/request.js b/src/utils/request.js index 7eab37d..578208f 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -127,9 +127,10 @@ service.interceptors.response.use( case 401: console.log('未授权', responseData) - return handleUnauthorized().then(() => { - return Promise.reject({ code: 401, message: '未授权' }); - }); + return Promise.resolve(responseData); + // return handleUnauthorized().then(() => { + // return Promise.reject({ code: 401, message: '未授权' }); + // }); case 500: const serverErrorMsg = responseData.meta?.message || '服务器内部错误'; @@ -142,18 +143,9 @@ service.interceptors.response.use( return Promise.reject({ code: businessCode, message: errorMsg }); } }, - (error) => { - console.error('请求错误:', error); + (error) => { let { message } = error; - let code = error?.response?.status || -1; - console.log(code,'code') - - if(code == 401) { - return handleUnauthorized().then(() => { - return Promise.reject({ code: 401, message: '未授权' }); - }); - } - + let code = error?.response?.status || -1; if (message == 'Network Error') { message = '后端接口连接异常'; ElMessage({ message, type: 'error', duration: 5 * 1000 }); diff --git a/src/views/conferencingRoom/index.vue b/src/views/conferencingRoom/index.vue new file mode 100644 index 0000000..1fefd4f --- /dev/null +++ b/src/views/conferencingRoom/index.vue @@ -0,0 +1,683 @@ + + + + + \ No newline at end of file diff --git a/src/views/conferencingRoom/utils/request.js b/src/views/conferencingRoom/utils/request.js new file mode 100644 index 0000000..f838558 --- /dev/null +++ b/src/views/conferencingRoom/utils/request.js @@ -0,0 +1,186 @@ +import axios from "axios"; +import { + ElNotification, + ElMessageBox, + ElMessage, +} from "element-plus"; +import { tansParams } from "@/utils/ruoyi"; +import cache from "@/plugins/cache"; +import { getToken, removeToken } from "@/utils/auth"; +import router from '@/router'; +import { useMeterStore } from '@/stores/modules/meter' + +axios.defaults.headers["Content-Type"] = "application/x-www-form-urlencoded"; +const meterStore = useMeterStore() +meterStore.initUdid() +// 创建axios实例 +const service = axios.create({ + // axios中请求配置有baseURL选项,表示请求URL公共部分 + baseURL: import.meta.env.VITE_APP_BASE_API_livekit, + // 超时 + timeout: 10000, +}); + +// request拦截器 +service.interceptors.request.use( + (config) => { + // 是否需要防止数据重复提交 + const isRepeatSubmit = (config.headers || {}).repeatSubmit === false; + if (meterStore.getSudid()) { + config.headers["X-User-Agent"] = `gxtech/web 1.0.0: c=GxTech, udid=${meterStore.getSudid()}, sv=15.4.1, app=stt`; + } + // get请求映射params参数 + if (config.method === "get" && config.params) { + let url = config.url + "?" + tansParams(config.params); + url = url.slice(0, -1); + config.params = {}; + config.url = url; + } + if ( + !isRepeatSubmit && + (config.method === "post" || config.method === "put") + ) { + const requestObj = { + url: config.url, + data: + typeof config.data === "object" + ? JSON.stringify(config.data) + : config.data, + time: new Date().getTime(), + }; + const requestSize = Object.keys(JSON.stringify(requestObj)).length; // 请求数据大小 + const limitSize = 5 * 1024 * 1024; // 限制存放数据5M + if (requestSize >= limitSize) { + console.warn( + `[${config.url}]: ` + + "请求数据大小超出允许的5M限制,无法进行防重复提交验证。" + ); + return config; + } + const sessionObj = cache.session.getJSON("sessionObj"); + if ( + sessionObj === undefined || + sessionObj === null || + sessionObj === "" + ) { + cache.session.setJSON("sessionObj", requestObj); + } else { + const s_url = sessionObj.url; // 请求地址 + const s_data = sessionObj.data; // 请求数据 + const s_time = sessionObj.time; // 请求时间 + const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交 + if ( + s_data === requestObj.data && + requestObj.time - s_time < interval && + s_url === requestObj.url + ) { + const message = "数据正在处理,请勿重复提交"; + console.warn(`[${s_url}]: ` + message); + return Promise.reject(new Error(message)); + } else { + cache.session.setJSON("sessionObj", requestObj); + } + } + } + return config; + }, + (error) => { + Promise.reject(error); + } +); + +service.interceptors.response.use( + (response) => { + // 1. 检查响应是否存在 + if (!response) { + ElMessage.error('无响应数据'); + return Promise.reject(new Error('无响应数据')); + } + // 2. 安全获取响应数据和状态码 + const responseData = response.data || {}; + const statusCode = response.status; + const businessCode = responseData.meta?.code || statusCode; + + // 3. 二进制数据直接返回 + if ( + response.request.responseType === 'blob' || + response.request.responseType === 'arraybuffer' + ) { + return responseData; + } + + // 4. 根据业务码处理不同情况 + switch (businessCode) { + case 200: + case 201: + return Promise.resolve(responseData); + + case 401: + console.log('未授权', responseData) + return Promise.resolve(responseData); + // return handleUnauthorized().then(() => { + // return Promise.reject({ code: 401, message: '未授权' }); + // }); + + case 500: + const serverErrorMsg = responseData.meta?.message || '服务器内部错误'; + ElMessage({ message: serverErrorMsg, type: 'error' }); + return Promise.reject({ code: 500, message: serverErrorMsg }); + + default: + const errorMsg = responseData.meta?.message || `业务错误 (${businessCode})`; + ElNotification.error({ title: errorMsg }); + return Promise.reject({ code: businessCode, message: errorMsg }); + } + }, + (error) => { + let { message } = error; + let code = error?.response?.status || -1; + if (message == 'Network Error') { + message = '后端接口连接异常'; + ElMessage({ message, type: 'error', duration: 5 * 1000 }); + } else if (message.includes('timeout')) { + message = '系统接口请求超时'; + ElMessage({ message, type: 'error', duration: 5 * 1000 }); + } else if (message.includes('Request failed with status code')) { + // message = '系统接口' + message.substr(message.length - 3) + '异常'; + } + + // 返回结构化错误 + return Promise.reject({ + code, + message, + raw: error // 保留原始 error + }); + } +); + +// 单独处理401未授权 +function handleUnauthorized() { + return ElMessageBox.confirm( + '认证信息已失效,您可以继续留在该页面,或者重新登录', + '系统提示', + { + confirmButtonText: '重新登录', + cancelButtonText: '取消', + type: 'warning', + } + ) + .then(() => { + removeToken() + if (router.currentRoute.path !== '/login') { + router.push({ + path: '/login', + query: { redirect: router.currentRoute.fullPath } + }); + } else { + // 如果在登录页,强制刷新以清除残留状态 + window.location.reload(); + } + }) + .catch(() => { + return Promise.reject('用户取消操作'); + }); +} + +export default service; diff --git a/src/views/coordinate/personnelList/components/leftTab/index.vue b/src/views/coordinate/personnelList/components/leftTab/index.vue index b223d74..327b773 100644 --- a/src/views/coordinate/personnelList/components/leftTab/index.vue +++ b/src/views/coordinate/personnelList/components/leftTab/index.vue @@ -1,7 +1,7 @@