diff --git a/.env b/.env index 751991fd..4860a552 100644 --- a/.env +++ b/.env @@ -5,7 +5,7 @@ SHOPRO_VERSION = v1.8.3 SHOPRO_BASE_URL = http://api-dashboard.yudao.iocoder.cn # 后端接口 - 测试环境(通过 process.env.NODE_ENV = development) -SHOPRO_DEV_BASE_URL = http://192.168.1.105:48080 +SHOPRO_DEV_BASE_URL = http://127.0.0.1:48080 ### SHOPRO_DEV_BASE_URL = http://yunai.natapp1.cc # 后端接口前缀(一般不建议调整) diff --git a/manifest.json b/manifest.json index 39a3c45e..66674c1c 100644 --- a/manifest.json +++ b/manifest.json @@ -1,227 +1,239 @@ { - "name" : "芋道商城", - "appid" : "__UNI__460BC4C", - "description" : "基于 uni-app + Vue3 技术驱动的在线商城系统,内含诸多功能与丰富的活动,期待您的使用和反馈。", - "versionName" : "2.1.0", - "versionCode" : 183, - "transformPx" : false, - "app-plus" : { - "usingComponents" : true, - "nvueCompiler" : "uni-app", - "nvueStyleCompiler" : "uni-app", - "compilerVersion" : 3, - "nvueLaunchMode" : "fast", - "splashscreen" : { - "alwaysShowBeforeRender" : true, - "waiting" : true, - "autoclose" : true, - "delay" : 0 + "name": "芋道商城", + "appid": "__UNI__460BC4C", + "description": "基于 uni-app + Vue3 技术驱动的在线商城系统,内含诸多功能与丰富的活动,期待您的使用和反馈。", + "versionName": "2.1.0", + "versionCode": 183, + "transformPx": false, + "app-plus": { + "usingComponents": true, + "nvueCompiler": "uni-app", + "nvueStyleCompiler": "uni-app", + "compilerVersion": 3, + "nvueLaunchMode": "fast", + "splashscreen": { + "alwaysShowBeforeRender": true, + "waiting": true, + "autoclose": true, + "delay": 0 + }, + "safearea": { + "bottom": { + "offset": "none" + } + }, + "modules": { + "Payment": {}, + "Share": {}, + "VideoPlayer": {}, + "OAuth": {} + }, + "distribute": { + "android": { + "permissions": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "minSdkVersion": 21, + "schemes": "shopro" + }, + "ios": { + "urlschemewhitelist": [ + "baidumap", + "iosamap" + ], + "dSYMs": false, + "privacyDescription": { + "NSPhotoLibraryUsageDescription": "需要同意访问您的相册选取图片才能完善该条目", + "NSPhotoLibraryAddUsageDescription": "需要同意访问您的相册才能保存该图片", + "NSCameraUsageDescription": "需要同意访问您的摄像头拍摄照片才能完善该条目", + "NSUserTrackingUsageDescription": "开启追踪并不会获取您在其它站点的隐私信息,该行为仅用于标识设备,保障服务安全和提升浏览体验" }, - "safearea" : { - "bottom" : { - "offset" : "none" - } + "urltypes": "shopro", + "capabilities": { + "entitlements": { + "com.apple.developer.associated-domains": [ + "applinks:shopro.sheepjs.com" + ] + } }, - "modules" : { - "Payment" : {}, - "Share" : {}, - "VideoPlayer" : {}, - "OAuth" : {} + "idfa": true + }, + "sdkConfigs": { + "speech": { + "ifly": {} }, - "distribute" : { - "android" : { - "permissions" : [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "minSdkVersion" : 21, - "schemes" : "shopro" - }, - "ios" : { - "urlschemewhitelist" : [ "baidumap", "iosamap" ], - "dSYMs" : false, - "privacyDescription" : { - "NSPhotoLibraryUsageDescription" : "需要同意访问您的相册选取图片才能完善该条目", - "NSPhotoLibraryAddUsageDescription" : "需要同意访问您的相册才能保存该图片", - "NSCameraUsageDescription" : "需要同意访问您的摄像头拍摄照片才能完善该条目", - "NSUserTrackingUsageDescription" : "开启追踪并不会获取您在其它站点的隐私信息,该行为仅用于标识设备,保障服务安全和提升浏览体验" - }, - "urltypes" : "shopro", - "capabilities" : { - "entitlements" : { - "com.apple.developer.associated-domains" : [ "applinks:shopro.sheepjs.com" ] - } - }, - "idfa" : true - }, - "sdkConfigs" : { - "speech" : { - "ifly" : {} - }, - "ad" : {}, - "oauth" : { - "apple" : {}, - "weixin" : { - "appid" : "wxae7a0c156da9383b", - "UniversalLinks" : "https://shopro.sheepjs.com/uni-universallinks/__UNI__082C0BA/" - } - }, - "payment" : { - "weixin" : { - "__platform__" : [ "ios", "android" ], - "appid" : "wxae7a0c156da9383b", - "UniversalLinks" : "https://shopro.sheepjs.com/uni-universallinks/__UNI__082C0BA/" - }, - "alipay" : { - "__platform__" : [ "ios", "android" ] - } - }, - "share" : { - "weixin" : { - "appid" : "wxae7a0c156da9383b", - "UniversalLinks" : "https://shopro.sheepjs.com/uni-universallinks/__UNI__082C0BA/" - } - } - }, - "orientation" : [ "portrait-primary" ], - "splashscreen" : { - "androidStyle" : "common", - "iosStyle" : "common", - "useOriginalMsgbox" : true - }, - "icons" : { - "android" : { - "hdpi" : "unpackage/res/icons/72x72.png", - "xhdpi" : "unpackage/res/icons/96x96.png", - "xxhdpi" : "unpackage/res/icons/144x144.png", - "xxxhdpi" : "unpackage/res/icons/192x192.png" - }, - "ios" : { - "appstore" : "unpackage/res/icons/1024x1024.png", - "ipad" : { - "app" : "unpackage/res/icons/76x76.png", - "app@2x" : "unpackage/res/icons/152x152.png", - "notification" : "unpackage/res/icons/20x20.png", - "notification@2x" : "unpackage/res/icons/40x40.png", - "proapp@2x" : "unpackage/res/icons/167x167.png", - "settings" : "unpackage/res/icons/29x29.png", - "settings@2x" : "unpackage/res/icons/58x58.png", - "spotlight" : "unpackage/res/icons/40x40.png", - "spotlight@2x" : "unpackage/res/icons/80x80.png" - }, - "iphone" : { - "app@2x" : "unpackage/res/icons/120x120.png", - "app@3x" : "unpackage/res/icons/180x180.png", - "notification@2x" : "unpackage/res/icons/40x40.png", - "notification@3x" : "unpackage/res/icons/60x60.png", - "settings@2x" : "unpackage/res/icons/58x58.png", - "settings@3x" : "unpackage/res/icons/87x87.png", - "spotlight@2x" : "unpackage/res/icons/80x80.png", - "spotlight@3x" : "unpackage/res/icons/120x120.png" - } - } - } + "ad": {}, + "oauth": { + "apple": {}, + "weixin": { + "appid": "wxae7a0c156da9383b", + "UniversalLinks": "https://shopro.sheepjs.com/uni-universallinks/__UNI__082C0BA/" + } + }, + "payment": { + "weixin": { + "__platform__": [ + "ios", + "android" + ], + "appid": "wxae7a0c156da9383b", + "UniversalLinks": "https://shopro.sheepjs.com/uni-universallinks/__UNI__082C0BA/" + }, + "alipay": { + "__platform__": [ + "ios", + "android" + ] + } + }, + "share": { + "weixin": { + "appid": "wxae7a0c156da9383b", + "UniversalLinks": "https://shopro.sheepjs.com/uni-universallinks/__UNI__082C0BA/" + } } - }, - "quickapp" : {}, - "quickapp-native" : { - "icon" : "/static/logo.png", - "package" : "com.example.demo", - "features" : [ - { - "name" : "system.clipboard" - } - ] - }, - "quickapp-webview" : { - "icon" : "/static/logo.png", - "package" : "com.example.demo", - "minPlatformVersion" : 1070, - "versionName" : "1.0.0", - "versionCode" : 100 - }, - "mp-weixin" : { - "appid" : "wx98df718e528399d2", - "setting" : { - "urlCheck" : false, - "minified" : true, - "postcss" : true + }, + "orientation": [ + "portrait-primary" + ], + "splashscreen": { + "androidStyle": "common", + "iosStyle": "common", + "useOriginalMsgbox": true + }, + "icons": { + "android": { + "hdpi": "unpackage/res/icons/72x72.png", + "xhdpi": "unpackage/res/icons/96x96.png", + "xxhdpi": "unpackage/res/icons/144x144.png", + "xxxhdpi": "unpackage/res/icons/192x192.png" }, - "optimization" : { - "subPackages" : true - }, - "plugins" : {}, - "lazyCodeLoading" : "requiredComponents", - "usingComponents" : {}, - "permission" : {}, - "requiredPrivateInfos" : [ "chooseAddress" ] - }, - "mp-alipay" : { - "usingComponents" : true - }, - "mp-baidu" : { - "usingComponents" : true - }, - "mp-toutiao" : { - "usingComponents" : true - }, - "mp-jd" : { - "usingComponents" : true - }, - "h5" : { - "template" : "index.html", - "router" : { - "mode" : "history", - "base" : "./" - }, - "sdkConfigs" : { - "maps" : {} - }, - "async" : { - "timeout" : 20000 - }, - "title" : "芋道商城", - "optimization" : { - "treeShaking" : { - "enable" : true - } - }, - "devServer" : { - "port" : 8383 + "ios": { + "appstore": "unpackage/res/icons/1024x1024.png", + "ipad": { + "app": "unpackage/res/icons/76x76.png", + "app@2x": "unpackage/res/icons/152x152.png", + "notification": "unpackage/res/icons/20x20.png", + "notification@2x": "unpackage/res/icons/40x40.png", + "proapp@2x": "unpackage/res/icons/167x167.png", + "settings": "unpackage/res/icons/29x29.png", + "settings@2x": "unpackage/res/icons/58x58.png", + "spotlight": "unpackage/res/icons/40x40.png", + "spotlight@2x": "unpackage/res/icons/80x80.png" + }, + "iphone": { + "app@2x": "unpackage/res/icons/120x120.png", + "app@3x": "unpackage/res/icons/180x180.png", + "notification@2x": "unpackage/res/icons/40x40.png", + "notification@3x": "unpackage/res/icons/60x60.png", + "settings@2x": "unpackage/res/icons/58x58.png", + "settings@3x": "unpackage/res/icons/87x87.png", + "spotlight@2x": "unpackage/res/icons/80x80.png", + "spotlight@3x": "unpackage/res/icons/120x120.png" + } } + } + } + }, + "quickapp": {}, + "quickapp-native": { + "icon": "/static/logo.png", + "package": "com.example.demo", + "features": [ + { + "name": "system.clipboard" + } + ] + }, + "quickapp-webview": { + "icon": "/static/logo.png", + "package": "com.example.demo", + "minPlatformVersion": 1070, + "versionName": "1.0.0", + "versionCode": 100 + }, + "mp-weixin": { + "appid": "wx63c280fe3248a3e7", + "setting": { + "urlCheck": false, + "minified": true, + "postcss": true }, - "vueVersion" : "3", - "_spaceID" : "192b4892-5452-4e1d-9f09-eee1ece40639", - "locale" : "zh-Hans", - "fallbackLocale" : "zh-Hans" + "optimization": { + "subPackages": true + }, + "plugins": {}, + "lazyCodeLoading": "requiredComponents", + "usingComponents": {}, + "permission": {}, + "requiredPrivateInfos": [ + "chooseAddress" + ] + }, + "mp-alipay": { + "usingComponents": true + }, + "mp-baidu": { + "usingComponents": true + }, + "mp-toutiao": { + "usingComponents": true + }, + "mp-jd": { + "usingComponents": true + }, + "h5": { + "template": "index.html", + "router": { + "mode": "hash", + "base": "./" + }, + "sdkConfigs": { + "maps": {} + }, + "async": { + "timeout": 20000 + }, + "title": "芋道商城", + "optimization": { + "treeShaking": { + "enable": true + } + } + }, + "vueVersion": "3", + "_spaceID": "192b4892-5452-4e1d-9f09-eee1ece40639", + "locale": "zh-Hans", + "fallbackLocale": "zh-Hans" } diff --git a/pages.json b/pages.json index 3bf16ebc..72d50091 100644 --- a/pages.json +++ b/pages.json @@ -86,8 +86,7 @@ ], "subPackages": [{ "root": "pages/goods", - "pages": [ - { + "pages": [{ "path": "index", "style": { "navigationBarTitleText": "商品详情" @@ -151,8 +150,7 @@ }, { "root": "pages/order", - "pages": [ - { + "pages": [{ "path": "detail", "style": { "navigationBarTitleText": "订单详情" @@ -251,8 +249,7 @@ }, { "root": "pages/user", - "pages": [ - { + "pages": [{ "path": "info", "style": { "navigationBarTitleText": "我的信息" @@ -338,8 +335,7 @@ }, { "root": "pages/commission", - "pages": [ - { + "pages": [{ "path": "index", "style": { "navigationBarTitleText": "分销" @@ -436,8 +432,7 @@ }, { "root": "pages/app", - "pages": [ - { + "pages": [{ "path": "sign", "style": { "navigationBarTitleText": "签到中心" @@ -452,8 +447,7 @@ }, { "root": "pages/public", - "pages": [ - { + "pages": [{ "path": "setting", "style": { "navigationBarTitleText": "系统设置" @@ -502,8 +496,7 @@ }, { "root": "pages/coupon", - "pages": [ - { + "pages": [{ "path": "list", "style": { "navigationBarTitleText": "领券中心" @@ -545,8 +538,7 @@ }, { "root": "pages/pay", - "pages": [ - { + "pages": [{ "path": "index", "style": { "navigationBarTitleText": "收银台" @@ -586,8 +578,7 @@ }, { "root": "pages/activity", - "pages": [ - { + "pages": [{ "path": "groupon/detail", "style": { "navigationBarTitleText": "拼团详情" diff --git a/pages/chat/components/chatBox.vue b/pages/chat/components/chatBox.vue index 84124e54..e89ddd98 100644 --- a/pages/chat/components/chatBox.vue +++ b/pages/chat/components/chatBox.vue @@ -133,11 +133,6 @@ - - diff --git a/pages/chat/index.vue b/pages/chat/index.vue index 25b4a54e..4614fa34 100644 --- a/pages/chat/index.vue +++ b/pages/chat/index.vue @@ -61,7 +61,7 @@ contentType: KeFuMessageContentTypeEnum.TEXT, content: chat.msg, }; - await KeFuApi.sendMessage(data); + await KeFuApi.sendKefuMessage(data); await getMessageList() chat.msg = ''; } finally { diff --git a/pages/chat/index1.vue b/pages/chat/index1.vue deleted file mode 100644 index 38258933..00000000 --- a/pages/chat/index1.vue +++ /dev/null @@ -1,678 +0,0 @@ - - - - - - diff --git a/pages/chat/socket.js b/pages/chat/socket.js deleted file mode 100644 index 181b6277..00000000 --- a/pages/chat/socket.js +++ /dev/null @@ -1,818 +0,0 @@ -import { reactive, ref, unref } from 'vue'; -import sheep from '@/sheep'; -// import chat from '@/sheep/api/chat'; -import dayjs from 'dayjs'; -import io from '@hyoga/uni-socket.io'; -import { baseUrl, websocketPath } from '@/sheep/config'; - -export function useChatWebSocket(socketConfig) { - let SocketIo = null; - - // chat状态数据 - const state = reactive({ - chatDotNum: 0, //总状态红点 - chatList: [], //会话信息 - customerUserInfo: {}, //用户信息 - customerServerInfo: { - //客服信息 - title: '客服已接入', - state: 'connecting', - avatar: null, - nickname: '', - }, - socketState: { - isConnect: true, //是否连接成功 - isConnecting: false, //重连中,不允许新的socket开启。 - tip: '', - }, - chatHistoryPagination: { - page: 0, //当前页 - list_rows: 10, //每页条数 - last_id: 0, //最后条ID - lastPage: 0, //总共多少页 - loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态 - }, - templateChatList: [], //猜你想问 - - chatConfig: {}, // 配置信息 - - isSendSuccess: -1, // 是否发送成功 -1=发送中|0=发送成功|1发送失败 - }); - - /** - * 连接初始化 - * @param {Object} config - 配置信息 - * @param {Function} callBack -回调函数,有新消息接入,保持底部 - */ - const socketInit = (config, callBack) => { - state.chatConfig = config; - if (SocketIo && SocketIo.connected) return; // 如果socket已经连接,返回false - if (state.socketState.isConnecting) return; // 重连中,返回false - - // 启动初始化 - SocketIo = io(baseUrl + websocketPath, { - path:websocketPath, - query:{ - token: getAccessToken() - }, - reconnection: true, // 默认 true 是否断线重连 - reconnectionAttempts: 5, // 默认无限次 断线尝试次数 - reconnectionDelay: 1000, // 默认 1000,进行下一次重连的间隔。 - reconnectionDelayMax: 5000, // 默认 5000, 重新连接等待的最长时间 默认 5000 - randomizationFactor: 0.5, // 默认 0.5 [0-1],随机重连延迟时间 - timeout: 20000, // 默认 20s - transports: ['websocket', 'polling'], // websocket | polling, - ...config, - }); - - // 监听连接 - SocketIo.on('connect', async (res) => { - socketReset(callBack); - // socket连接 - // 用户登录 - // 顾客登录 - console.log('socket:connect'); - }); - // 监听消息 - SocketIo.on('message', (res) => { - if (res.error === 0) { - const { message, sender } = res.data; - state.chatList.push(formatMessage(res.data.message)); - - // 告诉父级页面 - // window.parent.postMessage({ - // chatDotNum: ++state.chatDotNum - // }) - callBack && callBack(); - } - }); - // 监听客服接入成功 - SocketIo.on('customer_service_access', (res) => { - if (res.error === 0) { - editCustomerServerInfo({ - title: res.data.customer_service.name, - state: 'online', - avatar: res.data.customer_service.avatar, - }); - state.chatList.push(formatMessage(res.data.message)); - // callBack && callBack() - } - }); - // 监听排队等待 - SocketIo.on('waiting_queue', (res) => { - if (res.error === 0) { - editCustomerServerInfo({ - title: res.data.title, - state: 'waiting', - avatar: '', - }); - // callBack && callBack() - } - }); - // 监听没有客服在线 - SocketIo.on('no_customer_service', (res) => { - if (res.error === 0) { - editCustomerServerInfo({ - title: '暂无客服在线...', - state: 'waiting', - avatar: '', - }); - } - state.chatList.push(formatMessage(res.data.message)); - // callBack && callBack() - }); - // 监听客服上线 - SocketIo.on('customer_service_online', (res) => { - if (res.error === 0) { - editCustomerServerInfo({ - title: res.data.customer_service.name, - state: 'online', - avatar: res.data.customer_service.avatar, - }); - } - }); - // 监听客服下线 - SocketIo.on('customer_service_offline', (res) => { - if (res.error === 0) { - editCustomerServerInfo({ - title: res.data.customer_service.name, - state: 'offline', - avatar: res.data.customer_service.avatar, - }); - } - }); - // 监听客服忙碌 - SocketIo.on('customer_service_busy', (res) => { - if (res.error === 0) { - editCustomerServerInfo({ - title: res.data.customer_service.name, - state: 'busy', - avatar: res.data.customer_service.avatar, - }); - } - }); - // 监听客服断开链接 - SocketIo.on('customer_service_break', (res) => { - if (res.error === 0) { - editCustomerServerInfo({ - title: '客服服务结束', - state: 'offline', - avatar: '', - }); - state.socketState.isConnect = false; - state.socketState.tip = '当前服务已结束'; - } - state.chatList.push(formatMessage(res.data.message)); - // callBack && callBack() - }); - // 监听自定义错误 custom_error - SocketIo.on('custom_error', (error) => { - editCustomerServerInfo({ - title: error.msg, - state: 'offline', - avatar: '', - }); - console.log('custom_error:', error); - }); - // 监听错误 error - SocketIo.on('error', (error) => { - console.log('error:', error); - }); - // 重连失败 connect_error - SocketIo.on('connect_error', (error) => { - console.log('connect_error'); - }); - // 连接上,但无反应 connect_timeout - SocketIo.on('connect_timeout', (error) => { - console.log(error, 'connect_timeout'); - }); - // 服务进程销毁 disconnect - SocketIo.on('disconnect', (error) => { - console.log(error, 'disconnect'); - }); - // 服务重启重连上reconnect - SocketIo.on('reconnect', (error) => { - console.log(error, 'reconnect'); - }); - // 开始重连reconnect_attempt - SocketIo.on('reconnect_attempt', (error) => { - state.socketState.isConnect = false; - state.socketState.isConnecting = true; - editCustomerServerInfo({ - title: `重连中,第${error}次尝试...`, - state: 'waiting', - avatar: '', - }); - console.log(error, 'reconnect_attempt'); - }); - // 重新连接中reconnecting - SocketIo.on('reconnecting', (error) => { - console.log(error, 'reconnecting'); - }); - // 重新连接错误reconnect_error - SocketIo.on('reconnect_error', (error) => { - console.log('reconnect_error'); - }); - // 重新连接失败reconnect_failed - SocketIo.on('reconnect_failed', (error) => { - state.socketState.isConnecting = false; - editCustomerServerInfo({ - title: `重连失败,请刷新重试~`, - state: 'waiting', - avatar: '', - }); - console.log(error, 'reconnect_failed'); - - // setTimeout(() => { - state.isSendSucces = 1; - // }, 500) - }); - }; - - // 重置socket - const socketReset = (callBack) => { - state.chatList = []; - state.chatHistoryList = []; - state.chatHistoryPagination = { - page: 0, - per_page: 10, - last_id: 0, - totalPage: 0, - }; - socketConnection(callBack); // 连接 - }; - - // 退出连接 - const socketClose = () => { - SocketIo.emit('customer_logout', {}, (res) => { - console.log('socket:退出', res); - }); - }; - - // 测试事件 - const socketTest = () => { - SocketIo.emit('test', {}, (res) => { - console.log('test:test', res); - }); - }; - - // 发送消息 - const socketSendMsg = (data, sendMsgCallBack) => { - state.isSendSucces = -1; - state.chatList.push(data); - sendMsgCallBack && sendMsgCallBack(); - SocketIo.emit( - 'message', - { - message: formatInput(data), - ...data.customData, - }, - (res) => { - // setTimeout(() => { - state.isSendSucces = res.error; - // }, 500) - - // console.log(res, 'socket:send'); - // sendMsgCallBack && sendMsgCallBack() - }, - ); - }; - - // 连接socket,存入sessionId - const socketConnection = (callBack) => { - SocketIo.emit( - 'connection', - { - auth: 'user', - token: uni.getStorageSync('socketUserToken') || '', - session_id: uni.getStorageSync('socketSessionId') || '', - }, - (res) => { - if (res.error === 0) { - socketCustomerLogin(callBack); - uni.setStorageSync('socketSessionId', res.data.session_id); - // uni.getStorageSync('socketUserToken') && socketLogin(uni.getStorageSync( - // 'socketUserToken')) // 如果有用户token,绑定 - state.customerUserInfo = res.data.chat_user; - state.socketState.isConnect = true; - } else { - editCustomerServerInfo({ - title: `服务器异常!`, - state: 'waiting', - avatar: '', - }); - state.socketState.isConnect = false; - } - }, - ); - }; - - // 获取token - const getAccessToken = () => { - return uni.getStorageSync('token'); - }; - - // 用户登录 - const socketLogin = (token) => { - SocketIo.emit( - 'login', - { - token: token, - }, - (res) => { - console.log(res, 'socket:login'); - state.customerUserInfo = res.data.chat_user; - }, - ); - }; - - // 顾客登录 - const socketCustomerLogin = (callBack) => { - SocketIo.emit( - 'customer_login', - { - room_id: state.chatConfig.room_id, - }, - (res) => { - state.templateChatList = res.data.questions.length ? res.data.questions : []; - state.chatList.push({ - from: 'customer_service', // 用户customer右 | 顾客customer_service左 | 系统system中间 - mode: 'template', // goods,order,image,text,system - date: new Date().getTime(), //时间 - content: { - //内容 - list: state.templateChatList, - }, - }); - res.error === 0 && socketHistoryList(callBack); - }, - ); - }; - - // 获取历史消息 - const socketHistoryList = (historyCallBack) => { - state.chatHistoryPagination.loadStatus = 'loading'; - state.chatHistoryPagination.page += 1; - SocketIo.emit('messages', state.chatHistoryPagination, (res) => { - if (res.error === 0) { - state.chatHistoryPagination.total = res.data.messages.total; - state.chatHistoryPagination.lastPage = res.data.messages.last_page; - state.chatHistoryPagination.page = res.data.messages.current_page; - res.data.messages.data.forEach((item) => { - item.message_type && state.chatList.unshift(formatMessage(item)); - }); - state.chatHistoryPagination.loadStatus = - state.chatHistoryPagination.page < state.chatHistoryPagination.lastPage - ? 'loadmore' - : 'nomore'; - if (state.chatHistoryPagination.last_id == 0) { - state.chatHistoryPagination.last_id = res.data.messages.data.length - ? res.data.messages.data[0].id - : 0; - } - state.chatHistoryPagination.page === 1 && historyCallBack && historyCallBack(); - } - - // 历史记录之后,猜你想问 - // state.chatList.push({ - // from: 'customer_service', // 用户customer右 | 顾客customer_service左 | 系统system中间 - // mode: 'template', // goods,order,image,text,system - // date: new Date().getTime(), //时间 - // content: { //内容 - // list: state.templateChatList - // } - // }) - }); - }; - - // 修改客服信息 - const editCustomerServerInfo = (data) => { - state.customerServerInfo = { - ...state.customerServerInfo, - ...data, - }; - }; - - /** - * ================ - * 工具函数 ↓ - * =============== - */ - - /** - * 是否显示时间 - * @param {*} item - 数据 - * @param {*} index - 索引 - */ - const showTime = (item, index) => { - if (unref(state.chatList)[index + 1]) { - let dateString = dayjs(unref(state.chatList)[index + 1].date).fromNow(); - if (dateString === dayjs(unref(item).date).fromNow()) { - return false; - } else { - dateString = dayjs(unref(item).date).fromNow(); - return true; - } - } - return false; - }; - - /** - * 格式化时间 - * @param {*} time - 时间戳 - */ - const formatTime = (time) => { - let diffTime = new Date().getTime() - time; - if (diffTime > 28 * 24 * 60 * 1000) { - return dayjs(time).format('MM/DD HH:mm'); - } - if (diffTime > 360 * 28 * 24 * 60 * 1000) { - return dayjs(time).format('YYYY/MM/DD HH:mm'); - } - return dayjs(time).fromNow(); - }; - - /** - * 获取焦点 - * @param {*} virtualNode - 节点信息 ref - */ - const getFocus = (virtualNode) => { - if (window.getSelection) { - let chatInput = unref(virtualNode); - chatInput.focus(); - let range = window.getSelection(); - range.selectAllChildren(chatInput); - range.collapseToEnd(); - } else if (document.selection) { - let range = document.selection.createRange(); - range.moveToElementText(chatInput); - range.collapse(false); - range.select(); - } - }; - - /** - * 文件上传 - * @param {Blob} file -文件数据流 - * @return {path,fullPath} - */ - - const upload = (name, file) => { - return new Promise((resolve, reject) => { - let data = new FormData(); - data.append('file', file, name); - data.append('group', 'chat'); - ajax({ - url: '/upload', - method: 'post', - headers: { - 'Content-Type': 'multipart/form-data', - }, - data, - success: function (res) { - resolve(res); - }, - error: function (err) { - reject(err); - }, - }); - }); - }; - - /** - * 粘贴到输入框 - * @param {*} e - 粘贴内容 - * @param {*} uploadHttp - 上传图片地址 - */ - const onPaste = async (e) => { - let paste = e.clipboardData || window.clipboardData; - let filesArr = Array.from(paste.files); - filesArr.forEach(async (child) => { - if (child && child.type.includes('image')) { - e.preventDefault(); //阻止默认 - let file = child; - const img = await readImg(file); - const blob = await compressImg(img, file.type); - const { data } = await upload(file.name, blob); - let image = ``; - document.execCommand('insertHTML', false, image); - } else { - document.execCommand('insertHTML', false, paste.getData('text')); - } - }); - }; - - /** - * 拖拽到输入框 - * @param {*} e - 粘贴内容 - * @param {*} uploadHttp - 上传图片地址 - */ - const onDrop = async (e) => { - e.preventDefault(); //阻止默认 - let filesArr = Array.from(e.dataTransfer.files); - filesArr.forEach(async (child) => { - if (child && child.type.includes('image')) { - let file = child; - const img = await readImg(file); - const blob = await compressImg(img, file.type); - const { data } = await upload(file.name, blob); - let image = ``; - document.execCommand('insertHTML', false, image); - } else { - ElMessage({ - message: '禁止拖拽非图片资源', - type: 'warning', - }); - } - }); - }; - - /** - * 解析富文本输入框内容 - * @param {*} virtualNode -节点信息 - * @param {Function} formatInputCallBack - cb 回调 - */ - const formatChatInput = (virtualNode, formatInputCallBack) => { - let res = ''; - let elemArr = Array.from(virtualNode.childNodes); - elemArr.forEach((child, index) => { - if (child.nodeName === '#text') { - //如果为文本节点 - res += child.nodeValue; - if ( - //文本节点的后面是图片,并且不是emoji,分开发送。输入框中的图片和文本表情分开。 - elemArr[index + 1] && - elemArr[index + 1].nodeName === 'IMG' && - elemArr[index + 1] && - elemArr[index + 1].name !== 'emoji' - ) { - const data = { - from: 'customer', - mode: 'text', - date: new Date().getTime(), - content: { - text: filterXSS(res), - }, - }; - formatInputCallBack && formatInputCallBack(data); - res = ''; - } - } else if (child.nodeName === 'BR') { - res += '
'; - } else if (child.nodeName === 'IMG') { - // 有emjio 和 一般图片 - // 图片解析后直接发送,不跟文字表情一组 - if (child.name !== 'emoji') { - let srcReg = /src=[\'\']?([^\'\']*)[\'\']?/i; - let src = child.outerHTML.match(srcReg); - const data = { - from: 'customer', - mode: 'image', - date: new Date().getTime(), - content: { - url: src[1], - path: src[1].replace(/http:\/\/[^\/]*/, ''), - }, - }; - formatInputCallBack && formatInputCallBack(data); - } else { - // 非表情图片跟文字一起发送 - res += child.outerHTML; - } - } else if (child.nodeName === 'DIV') { - res += `
${child.outerHTML}
`; - } - }); - if (res) { - const data = { - from: 'customer', - mode: 'text', - date: new Date().getTime(), - content: { - text: filterXSS(res), - }, - }; - formatInputCallBack && formatInputCallBack(data); - } - unref(virtualNode).innerHTML = ''; - }; - - /** - * 状态回调 - * @param {*} res -接口返回数据 - */ - const callBackNotice = (res) => { - ElNotification({ - title: 'socket', - message: res.msg, - showClose: true, - type: res.error === 0 ? 'success' : 'warning', - duration: 1200, - }); - }; - - /** - * 格式化发送信息 - * @param {Object} message - * @returns obj - 消息对象 - */ - const formatInput = (message) => { - let obj = {}; - switch (message.mode) { - case 'text': - obj = { - message_type: 'text', - message: message.content.text, - }; - break; - case 'image': - obj = { - message_type: 'image', - message: message.content.path, - }; - break; - case 'goods': - obj = { - message_type: 'goods', - message: message.content.item, - }; - break; - case 'order': - obj = { - message_type: 'order', - message: message.content.item, - }; - break; - default: - break; - } - return obj; - }; - /** - * 格式化接收信息 - * @param {*} message - * @returns obj - 消息对象 - */ - const formatMessage = (message) => { - let obj = {}; - switch (message.message_type) { - case 'system': - obj = { - from: 'system', // 用户customer左 | 顾客customer_service右 | 系统system中间 - mode: 'system', // goods,order,image,text,system - date: message.create_time * 1000, //时间 - content: { - //内容 - text: message.message, - }, - }; - break; - case 'text': - obj = { - from: message.sender_identify, - mode: message.message_type, - date: message.create_time * 1000, //时间 - sender: message.sender, - content: { - text: message.message, - messageId: message.id, - }, - }; - break; - case 'image': - obj = { - from: message.sender_identify, - mode: message.message_type, - date: message.create_time * 1000, //时间 - sender: message.sender, - content: { - url: sheep.$url.cdn(message.message), - messageId: message.id, - }, - }; - break; - case 'goods': - obj = { - from: message.sender_identify, - mode: message.message_type, - date: message.create_time * 1000, //时间 - sender: message.sender, - content: { - item: message.message, - messageId: message.id, - }, - }; - break; - case 'order': - obj = { - from: message.sender_identify, - mode: message.message_type, - date: message.create_time * 1000, //时间 - sender: message.sender, - content: { - item: message.message, - messageId: message.id, - }, - }; - break; - default: - break; - } - return obj; - }; - - /** - * file 转换为 img - * @param {*} file - file 文件 - * @returns img - img标签 - */ - const readImg = (file) => { - return new Promise((resolve, reject) => { - const img = new Image(); - const reader = new FileReader(); - reader.onload = function (e) { - img.src = e.target.result; - }; - reader.onerror = function (e) { - reject(e); - }; - reader.readAsDataURL(file); - img.onload = function () { - resolve(img); - }; - img.onerror = function (e) { - reject(e); - }; - }); - }; - - /** - * 压缩图片 - *@param img -被压缩的img对象 - * @param type -压缩后转换的文件类型 - * @param mx -触发压缩的图片最大宽度限制 - * @param mh -触发压缩的图片最大高度限制 - * @returns blob - 文件流 - */ - const compressImg = (img, type = 'image/jpeg', mx = 1000, mh = 1000, quality = 1) => { - return new Promise((resolve, reject) => { - const canvas = document.createElement('canvas'); - const context = canvas.getContext('2d'); - const { width: originWidth, height: originHeight } = img; - // 最大尺寸限制 - const maxWidth = mx; - const maxHeight = mh; - // 目标尺寸 - let targetWidth = originWidth; - let targetHeight = originHeight; - if (originWidth > maxWidth || originHeight > maxHeight) { - if (originWidth / originHeight > 1) { - // 宽图片 - targetWidth = maxWidth; - targetHeight = Math.round(maxWidth * (originHeight / originWidth)); - } else { - // 高图片 - targetHeight = maxHeight; - targetWidth = Math.round(maxHeight * (originWidth / originHeight)); - } - } - canvas.width = targetWidth; - canvas.height = targetHeight; - context.clearRect(0, 0, targetWidth, targetHeight); - // 图片绘制 - context.drawImage(img, 0, 0, targetWidth, targetHeight); - canvas.toBlob( - function (blob) { - resolve(blob); - }, - type, - quality, - ); - }); - }; - - return { - compressImg, - readImg, - formatMessage, - formatInput, - callBackNotice, - - socketInit, - socketSendMsg, - socketClose, - socketHistoryList, - - getFocus, - formatChatInput, - onDrop, - onPaste, - upload, - state, - - socketTest, - - showTime, - formatTime, - }; -} diff --git a/sheep/api/promotion/kefu.js b/sheep/api/promotion/kefu.js index ae3610e5..86481ab0 100644 --- a/sheep/api/promotion/kefu.js +++ b/sheep/api/promotion/kefu.js @@ -1,7 +1,7 @@ import request from '@/sheep/request'; const KeFuApi = { - sendMessage: (data) => { + sendKefuMessage: (data) => { return request({ url: '/promotion/kefu-message/send', method: 'POST', @@ -15,7 +15,7 @@ const KeFuApi = { }, }); }, - getMessageListPage: (params) => { + getKefuMessagePage: (params) => { return request({ url: '/promotion/kefu-message/page', method: 'GET',