From 754591bc4c3ea0debf9df7111d822b28e2263c0e Mon Sep 17 00:00:00 2001 From: wujingling0908 <2.com> Date: Fri, 18 Sep 2026 16:44:33 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=81=8F=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/index.js | 9 +- src/utils/config.js | 22 +- src/utils/http.js | 4 +- src/utils/https.js | 686 ++++++++++++++++++++++---------------------- src/views/chat.vue | 100 ++++++- vite.config.js | 9 +- 6 files changed, 461 insertions(+), 369 deletions(-) diff --git a/src/store/index.js b/src/store/index.js index 65ada29..c27590b 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -121,7 +121,7 @@ export const useAppStore = defineStore('app', () => { const msgReply = ref(true) const hotUserData = ref(null) - function set_isBqCalcClaim(isBqCalcClaim) { + function setIsBqCalcClaim(isBqCalcClaim) { isBqCalcClaim.value = isBqCalcClaim } function set_token(token) { @@ -189,7 +189,7 @@ export const useAppStore = defineStore('app', () => { console.log(e) } } - function set_source(notitle) { + function set_notitle(notitle) { notitle.value = notitle try { localStorage.notitle = notitle @@ -570,15 +570,16 @@ export const useAppStore = defineStore('app', () => { mydFlag, msgReply, hotUserData, - set_isBqCalcClaim, + setIsBqCalcClaim, set_token, - set_sessionId, + setSessionId, set_configId, set_appentry, set_bpErrorInfo, del_token, set_userid, set_source, + set_notitle, set_planCode, set_entrance, set_elder, diff --git a/src/utils/config.js b/src/utils/config.js index fc9de11..8655df2 100644 --- a/src/utils/config.js +++ b/src/utils/config.js @@ -1,9 +1,9 @@ -let curWwwPath = window.document.location.href; -let pathName = window.document.location.pathname; +let curWwwPath = window.location.href; +let pathName = ''; // window.location.pathname let pos = curWwwPath.indexOf(pathName); let host = window.location.host; let protocol = window.location.protocol; -let localhostPaht = curWwwPath.substring(0, pos); +let localhostPath = curWwwPath // downSource.substring(0, pos); let projectName = pathName.substring(0, pathName.substr(1).indexOf('/') + 1); let isWs = protocol == 'https:' ? 'wss' : 'ws'; @@ -12,28 +12,28 @@ let WSURL = ''; let envType = ''; //开发环境 -if(localhostPaht.indexOf("localhost") >=0 || localhostPaht.indexOf("172.16") >=0){ +if(localhostPath.indexOf("localhost") >=0 || localhostPath.indexOf("172.16") >=0){ baseUrl = '/api'; WSURL = `${isWs}://${host}/api/liics_api/webSocket/`; //ws://ssp-dev.trace.com.cn/liics_api/webSocket/ envType = 'dev'; //测试环境 }else if( - localhostPaht.includes("-dev") || - localhostPaht.includes("-sit") || - localhostPaht.includes("-uat") || - localhostPaht.includes("-pt") + localhostPath.includes("-dev") || + localhostPath.includes("-sit") || + localhostPath.includes("-uat") || + localhostPath.includes("-pt") ){ - baseUrl = localhostPaht; + baseUrl = localhostPath; WSURL = `${isWs}://${host}/liics_api/webSocket/`; //ws://ssp-dev.trace.com.cn/liics_api/webSocket/ envType = 'sit'; //生产环境 }else{ - baseUrl = localhostPaht; + baseUrl = localhostPath; WSURL = `${isWs}://${host}/liics_api/webSocket/`; //ws://ssp-dev.trace.com.cn/liics_api/webSocket/ envType = 'production'; } - +console.log('baseUrl', baseUrl, localhostPath, window.location.href, window.location.pathname); // switch (process.env.NODE_ENV) { // case 'development': diff --git a/src/utils/http.js b/src/utils/http.js index bb32178..aca4a27 100644 --- a/src/utils/http.js +++ b/src/utils/http.js @@ -12,7 +12,7 @@ import { showToast, showConfirmDialog } from 'vant' // Toast.setDefaultOptions({ forbidClick: true }); const base = { - baseUrl: '', + baseUrl: 'https://ssp-sit.cpic.com.cn', timeout: 300000, } @@ -375,7 +375,7 @@ export function Get(url, params) { export function Post(url, params) { return new Promise((resolve, reject) => { url = base.baseUrl + url; - + console.log('url', url,base); const request = params?.idempotentKey ? postWithIdempotentRequest(url, params) : axios.post(url, params); diff --git a/src/utils/https.js b/src/utils/https.js index 741dda3..b738419 100644 --- a/src/utils/https.js +++ b/src/utils/https.js @@ -7,47 +7,47 @@ import router from '@/router' import config from './config' import utils from './utils' import { useAppStore } from '@/store/app.js' -import { showToast, showConfirmDialog, allowMultipleToast, setToastDefaultOptions } from 'vant'; +import { showToast, showConfirmDialog, allowMultipleToast, setToastDefaultOptions } from 'vant' // 惰性获取 Pinia store(替代原 Vuex 的 import store from "@/store/index") const appStore = () => useAppStore() -allowMultipleToast(); -setToastDefaultOptions({ forbidClick: true }); +allowMultipleToast() +setToastDefaultOptions({ forbidClick: true }) const base = { - baseUrl: config.baseUrl, - timeout: 300000, + baseUrl: config.baseUrl, + timeout: 300000, } //关闭页面 const closeWindow = () => { - var userAgent = navigator.userAgent; - if (userAgent.indexOf("Firefox") != -1 || userAgent.indexOf("Chrome") != -1) { - window.location.href = "about:blank"; - window.close(); - } else { - window.opener = null; - window.open("", "_self"); - window.close(); - } - - // 微信中关闭 - if (WeixinJSBridge) { - WeixinJSBridge.call('closeWindow'); - } + var userAgent = navigator.userAgent + if (userAgent.indexOf('Firefox') != -1 || userAgent.indexOf('Chrome') != -1) { + window.location.href = 'about:blank' + window.close() + } else { + window.opener = null + window.open('', '_self') + window.close() + } + + // 微信中关闭 + if (WeixinJSBridge) { + WeixinJSBridge.call('closeWindow') + } } /** * 提示函数 * 禁止点击蒙层、显示3秒后关闭 */ -const toast = msg => { - showToast({ - message: msg, - duration: 4000, - forbidClick: true - }); +const toast = (msg) => { + showToast({ + message: msg, + duration: 4000, + forbidClick: true, + }) } /** @@ -55,12 +55,12 @@ const toast = msg => { * 携带当前页面路由,以期在登录页面完成登录后返回当前页面 */ const toLogin = () => { - router.replace({ - path: '/liics/login', - query: { - redirect: router.currentRoute.fullPath - } - }); + router.replace({ + path: '/liics/login', + query: { + redirect: router.currentRoute.fullPath, + }, + }) } /** @@ -68,172 +68,179 @@ const toLogin = () => { * @param {Number} status 请求失败的状态码 */ const errorHandle = (status, msg) => { - // 状态码判断 - switch (status) { - //未登录状态,跳转登录页 跳转登录 - case 401: - // toLogin(); - // 清除token - toast('【401】当前会话超时,请刷新页面'); - appStore().del_token(); - sessionStorage.removeItem('token'); - break; - // 403 token过期 - // 清除token并跳转登录页 - case 403: - toast('【403】当前会话超时,请刷新页面'); - // 清除token - appStore().del_token(); - sessionStorage.removeItem('token'); - setTimeout(() => { - // toLogin(); - }, 1000); - break; - // 404请求不存在 - case 404: - toast('哎呦~404了~休息一下'); - break; - case 408: - toast('【408】请求超时'); - break; - case 500: - toast('【500】当前网络不给力,请检查你的网络设置'); - break; - case 503: - toast('【503】当前服务器不可用,请稍后重试'); - break; - // 其他错误,直接抛出错误提示 - default: - toast(`连接错误${msg}`); - } + // 状态码判断 + switch (status) { + //未登录状态,跳转登录页 跳转登录 + case 401: + // toLogin(); + // 清除token + toast('【401】当前会话超时,请刷新页面') + appStore().del_token() + sessionStorage.removeItem('token') + break + // 403 token过期 + // 清除token并跳转登录页 + case 403: + toast('【403】当前会话超时,请刷新页面') + // 清除token + appStore().del_token() + sessionStorage.removeItem('token') + setTimeout(() => { + // toLogin(); + }, 1000) + break + // 404请求不存在 + case 404: + toast('哎呦~404了~休息一下') + break + case 408: + toast('【408】请求超时') + break + case 500: + toast('【500】当前网络不给力,请检查你的网络设置') + break + case 503: + toast('【503】当前服务器不可用,请稍后重试') + break + // 其他错误,直接抛出错误提示 + default: + toast(`连接错误${msg}`) + } } // 请求超时时间 -axios.defaults.timeout = base.timeout; +axios.defaults.timeout = base.timeout //设置默认请求头 // axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'; -axios.defaults.headers.post['Content-Type'] = 'application/json'; +axios.defaults.headers.post['Content-Type'] = 'application/json' // 请求拦截器 axios.interceptors.request.use( - config => { - // 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了 - // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断 - const token = appStore().token; - // console.log("https===token:", token) - token && (config.headers.token = token); - return config; - }, - error => { - return Promise.reject(error); - }) - + (config) => { + // 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了 + // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断 + const token = appStore().token + // console.log("https===token:", token) + token && (config.headers.token = token) + return config + }, + (error) => { + return Promise.reject(error) + }, +) // 响应拦截器 axios.interceptors.response.use( - // 请求成功 - (res) => { - if (res.data.code === 10001) { - appStore().del_token(); - sessionStorage.removeItem("token"); // 删除已经失效或过期的token(不删除也可以,因为登录后覆盖) - - // 如果没有展示返回头部的话去掉弹窗按钮 - if (appStore().notitle === '1') { - toast('当前会话已结束,请刷新页面或关闭页面重新进入'); - return Promise.resolve(res); - } - - showConfirmDialog({ - title: '提示', - message: '当前会话已结束,请刷新页面或关闭页面重新进入', - confirmButtonText: '取消', - cancelButtonText: '确认', - confirmButtonColor: '#000000', - cancelButtonColor: '#000000' - }).then(() => { - // on cancel (现在是右边的取消按钮) - // utils.closeWindow(); - // router.replace({ - // path: '/404' // 到登录页重新获取token - // }) - }).catch(() => { - // on confirm (现在是左边的确认按钮) - //确认重新刷新页面获取token - // window.location.reload(); - utils.AppReloadHtml(); - }); - - return Promise.resolve(res); - } + // 请求成功 + (res) => { + if (res.data.code === 10001) { + appStore().del_token() + sessionStorage.removeItem('token') // 删除已经失效或过期的token(不删除也可以,因为登录后覆盖) + + // 如果没有展示返回头部的话去掉弹窗按钮 + if (appStore().notitle === '1') { + toast('当前会话已结束,请刷新页面或关闭页面重新进入') + return Promise.resolve(res) + } + + showConfirmDialog({ + title: '提示', + message: '当前会话已结束,请刷新页面或关闭页面重新进入', + confirmButtonText: '取消', + cancelButtonText: '确认', + confirmButtonColor: '#000000', + cancelButtonColor: '#000000', + }) + .then(() => { + // on cancel (现在是右边的取消按钮) + // utils.closeWindow(); + // router.replace({ + // path: '/404' // 到登录页重新获取token + // }) + }) + .catch(() => { + // on confirm (现在是左边的确认按钮) + //确认重新刷新页面获取token + // window.location.reload(); + utils.AppReloadHtml() + }) - res.status === 200 ? Promise.resolve(res) : Promise.reject(res); + return Promise.resolve(res) + } - return res - }, - // 服务器状态码不是200的情况 - // 请求失败 - async error => { - const { - response - } = error; - if (response) { - //过滤排队接口弹窗问题 - if (response.config.url.indexOf('HumanChat/queueNumbers') != -1 || response.config.url.indexOf('HumanChat/msgCallBack') != -1) { - return Promise.reject(response); - } - - if (response.data.code === 10001) { - appStore().del_token(); - sessionStorage.removeItem("token"); // 删除已经失效或过期的token(不删除也可以,因为登录后覆盖) - - // 如果没有展示返回头部的话去掉弹窗按钮 - if (appStore().notitle === '1') { - toast('当前会话已结束,请刷新页面或关闭页面重新进入'); - return Promise.reject(response); - } - - showConfirmDialog({ - title: '提示', - message: '当前会话已结束,请刷新页面或关闭页面重新进入', - confirmButtonText: '取消', - cancelButtonText: '确认', - confirmButtonColor: '#000000', - cancelButtonColor: '#000000' - }).then(() => { - // on cancel (现在是右边的取消按钮) - // utils.closeWindow(); - // router.replace({ - // path: '/404' // 到登录页重新获取token - // }) - }).catch(() => { - // on confirm (现在是左边的确认按钮) - //确认重新刷新页面获取token - // window.location.reload(); - utils.AppReloadHtml(); - }); - - return Promise.reject(response); - } - - // 请求已发出,但是不在2xx的范围 - errorHandle(response.status, response.statusText); - return Promise.reject(response); - } else { - // 处理断网的情况 - // eg:请求超时或断网时,更新state的network状态 - // network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏 - // 关于断网组件中的刷新重新获取数据,会在断网组件中说明 - // store.commit('changeNetwork', false); - if (error?.message?.includes('timeout')) { // 判断请求异常信息中是否含有超时timeout字符串 - toast('请求超时,稍后重试!'); - return Promise.reject(error); - } - - toast('当前网络不给力,请检查你的网络设置'); - return Promise.reject(error); + res.status === 200 ? Promise.resolve(res) : Promise.reject(res) + + return res + }, + // 服务器状态码不是200的情况 + // 请求失败 + async (error) => { + const { response } = error + if (response) { + //过滤排队接口弹窗问题 + if ( + response.config.url.indexOf('HumanChat/queueNumbers') != -1 || + response.config.url.indexOf('HumanChat/msgCallBack') != -1 + ) { + return Promise.reject(response) + } + + if (response.data.code === 10001) { + appStore().del_token() + sessionStorage.removeItem('token') // 删除已经失效或过期的token(不删除也可以,因为登录后覆盖) + + // 如果没有展示返回头部的话去掉弹窗按钮 + if (appStore().notitle === '1') { + toast('当前会话已结束,请刷新页面或关闭页面重新进入') + return Promise.reject(response) } - }); + + showConfirmDialog({ + title: '提示', + message: '当前会话已结束,请刷新页面或关闭页面重新进入', + confirmButtonText: '取消', + cancelButtonText: '确认', + confirmButtonColor: '#000000', + cancelButtonColor: '#000000', + }) + .then(() => { + // on cancel (现在是右边的取消按钮) + // utils.closeWindow(); + // router.replace({ + // path: '/404' // 到登录页重新获取token + // }) + }) + .catch(() => { + // on confirm (现在是左边的确认按钮) + //确认重新刷新页面获取token + // window.location.reload(); + utils.AppReloadHtml() + }) + + return Promise.reject(response) + } + + // 请求已发出,但是不在2xx的范围 + errorHandle(response.status, response.statusText) + return Promise.reject(response) + } else { + // 处理断网的情况 + // eg:请求超时或断网时,更新state的network状态 + // network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏 + // 关于断网组件中的刷新重新获取数据,会在断网组件中说明 + // store.commit('changeNetwork', false); + if (error?.message?.includes('timeout')) { + // 判断请求异常信息中是否含有超时timeout字符串 + toast('请求超时,稍后重试!') + return Promise.reject(error) + } + + toast('当前网络不给力,请检查你的网络设置') + return Promise.reject(error) + } + }, +) /** * get方法,对应get请求 @@ -241,17 +248,18 @@ axios.interceptors.response.use( * @param {Object} params [请求时携带的参数] */ export function Get(url, params) { - return new Promise((resolve, reject) => { - url = base.baseUrl + url; - - axios.get(url, QS.stringify(params)) - .then(res => { - resolve(res.data); - }) - .catch(err => { - reject(err.status) - }) - }); + return new Promise((resolve, reject) => { + url = base.baseUrl + url + + axios + .get(url, QS.stringify(params)) + .then((res) => { + resolve(res.data) + }) + .catch((err) => { + reject(err.status) + }) + }) } /** @@ -260,70 +268,71 @@ export function Get(url, params) { * @param {Object} params [请求时携带的参数] */ export function Post(url, params) { - return new Promise((resolve, reject) => { - url = base.baseUrl + url; - - axios.post(url, params) - .then(res => { - resolve(res.data); - }) - .catch(err => { - reject(err.status) - }) - }); + return new Promise((resolve, reject) => { + url = base.baseUrl + url + console.log('url', url, base); + axios + .post(url, params) + .then((res) => { + resolve(res.data) + }) + .catch((err) => { + reject(err.status) + }) + }) } - /** * post方法,对应post请求 * @param {String} url [请求的url地址] * @param {Object} params [请求时携带的参数] */ export function fetchPost(url, params) { - return new Promise((resolve, reject) => { - url = base.baseUrl + url; - - axios.post(url, params, { - headers: { - 'Content-Type': 'application/json', - token: appStore().token - }, - }) - .then(res => { - resolve(res.data); - }) - .catch(err => { - reject(err.status) - }) - }); + return new Promise((resolve, reject) => { + url = base.baseUrl + url + + axios + .post(url, params, { + headers: { + 'Content-Type': 'application/json', + token: appStore().token, + }, + }) + .then((res) => { + resolve(res.data) + }) + .catch((err) => { + reject(err.status) + }) + }) } - /** * post方法,对应post请求 上传图片专用 * @param {String} url [请求的url地址] * @param {Object} params [请求时携带的参数] */ export function upLoadImgHttp(url, params) { - return new Promise((resolve, reject) => { - url = base.baseUrl + url; - const instance = axios.create({ - withCredentials: true, - }) + return new Promise((resolve, reject) => { + url = base.baseUrl + url + const instance = axios.create({ + withCredentials: true, + }) - instance.post(url, params, { - headers: { - 'Content-Type': 'multipart/form-data', - token: appStore().token - } - }) - .then(res => { - resolve(res.data); - }) - .catch(err => { - reject(err.status) - }) - }); + instance + .post(url, params, { + headers: { + 'Content-Type': 'multipart/form-data', + token: appStore().token, + }, + }) + .then((res) => { + resolve(res.data) + }) + .catch((err) => { + reject(err.status) + }) + }) } /** @@ -333,58 +342,57 @@ export function upLoadImgHttp(url, params) { * @param {Object} options [额外配置选项] */ export function fetchImageData(url, params, options = {}) { - return new Promise((resolve, reject) => { - url = base.baseUrl + url; - - const { - timeout = 10000 - } = options; - - const instance = axios.create({ - timeout, - withCredentials: true, - }); - - instance.post(url, params, { - headers: { - 'Content-Type': 'application/json', - token: appStore().token - } + return new Promise((resolve, reject) => { + url = base.baseUrl + url + + const { timeout = 10000 } = options + + const instance = axios.create({ + timeout, + withCredentials: true, + }) + + instance + .post(url, params, { + headers: { + 'Content-Type': 'application/json', + token: appStore().token, + }, + }) + .then((res) => { + // 检查响应格式 + if (res.data.code !== 0) { + reject(new Error(res.data.msg || 'Request failed')) + return + } + + const imageData = res.data.data + if (!imageData || !imageData.content) { + reject(new Error('Invalid response: missing image content')) + return + } + + // 验证文件扩展名是否为图片格式 + const validImageExts = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'bmp'] + const fileExt = imageData.fileNameExt?.toLowerCase() + if (!validImageExts.includes(fileExt)) { + reject(new Error(`Unsupported image format: ${fileExt}`)) + return + } + + resolve({ + fileName: imageData.fileName, + fileCrc: imageData.fileCrc, + fileNameExt: imageData.fileNameExt, + content: imageData.content, + // 构建完整的data URL + dataUrl: `data:image/${fileExt};base64,${imageData.content}`, }) - .then(res => { - // 检查响应格式 - if (res.data.code !== 0) { - reject(new Error(res.data.msg || 'Request failed')); - return; - } - - const imageData = res.data.data; - if (!imageData || !imageData.content) { - reject(new Error('Invalid response: missing image content')); - return; - } - - // 验证文件扩展名是否为图片格式 - const validImageExts = ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'bmp']; - const fileExt = imageData.fileNameExt?.toLowerCase(); - if (!validImageExts.includes(fileExt)) { - reject(new Error(`Unsupported image format: ${fileExt}`)); - return; - } - - resolve({ - fileName: imageData.fileName, - fileCrc: imageData.fileCrc, - fileNameExt: imageData.fileNameExt, - content: imageData.content, - // 构建完整的data URL - dataUrl: `data:image/${fileExt};base64,${imageData.content}` - }); - }) - .catch(err => { - reject(err); - }); - }); + }) + .catch((err) => { + reject(err) + }) + }) } /** @@ -392,10 +400,10 @@ export function fetchImageData(url, params, options = {}) { * @deprecated 使用 fetchImageData 替代 */ export function fetchImageStream(url, params, options = {}) { - return fetchImageData(url, params, options).then(imageData => { - // 将base64转换为blob以保持兼容性 - return base64ToBlob(imageData.content, `image/${imageData.fileNameExt}`); - }); + return fetchImageData(url, params, options).then((imageData) => { + // 将base64转换为blob以保持兼容性 + return base64ToBlob(imageData.content, `image/${imageData.fileNameExt}`) + }) } /** @@ -405,20 +413,20 @@ export function fetchImageStream(url, params, options = {}) { * @returns {Blob} Blob对象 */ export function base64ToBlob(base64Data, contentType = 'image/png') { - try { - // 解码base64数据 - const byteCharacters = atob(base64Data); - const byteNumbers = new Array(byteCharacters.length); - - for (let i = 0; i < byteCharacters.length; i++) { - byteNumbers[i] = byteCharacters.charCodeAt(i); - } - - const byteArray = new Uint8Array(byteNumbers); - return new Blob([byteArray], { type: contentType }); - } catch (error) { - throw new Error(`Failed to convert base64 to blob: ${error.message}`); + try { + // 解码base64数据 + const byteCharacters = atob(base64Data) + const byteNumbers = new Array(byteCharacters.length) + + for (let i = 0; i < byteCharacters.length; i++) { + byteNumbers[i] = byteCharacters.charCodeAt(i) } + + const byteArray = new Uint8Array(byteNumbers) + return new Blob([byteArray], { type: contentType }) + } catch (error) { + throw new Error(`Failed to convert base64 to blob: ${error.message}`) + } } /** @@ -428,38 +436,34 @@ export function base64ToBlob(base64Data, contentType = 'image/png') { * @param {Object} options [配置选项] */ export function downloadFile(url, params, options = {}) { - return new Promise((resolve, reject) => { - url = base.baseUrl + url; - - const { - timeout = 30000, - responseType = 'blob', - method = 'POST' - } = options; - - const config = { - url, - method, - headers: { - 'Content-Type': 'application/json', - token: appStore().token - }, - responseType, - timeout - }; - - if (method.toUpperCase() === 'POST') { - config.data = params; - } else { - config.params = params; - } + return new Promise((resolve, reject) => { + url = base.baseUrl + url + + const { timeout = 30000, responseType = 'blob', method = 'POST' } = options + + const config = { + url, + method, + headers: { + 'Content-Type': 'application/json', + token: appStore().token, + }, + responseType, + timeout, + } + + if (method.toUpperCase() === 'POST') { + config.data = params + } else { + config.params = params + } - axios(config) - .then(res => { - resolve(res.data); - }) - .catch(err => { - reject(err); - }); - }); + axios(config) + .then((res) => { + resolve(res.data) + }) + .catch((err) => { + reject(err) + }) + }) } diff --git a/src/views/chat.vue b/src/views/chat.vue index 82fb322..b47ba4d 100644 --- a/src/views/chat.vue +++ b/src/views/chat.vue @@ -1,11 +1,11 @@