diff --git a/.env.local b/.env.local index 005d2f0d..71530cc6 100644 --- a/.env.local +++ b/.env.local @@ -29,5 +29,8 @@ VITE_BASE_PATH=/ # 商城H5会员端域名 VITE_MALL_H5_DOMAIN='http://localhost:3000' +# 客户端静态资源地址 空=默认使用服务端指定的CDN资源地址前缀 | local=本地 | http(s)://xxx.xxx=自定义静态资源地址前缀 +VITE_STATIC_URL = https://file.sheepjs.com + # 验证码的开关 VITE_APP_CAPTCHA_ENABLE=false diff --git a/src/api/mall/promotion/kefu/conversation/index.ts b/src/api/mall/promotion/kefu/conversation/index.ts new file mode 100644 index 00000000..96a23706 --- /dev/null +++ b/src/api/mall/promotion/kefu/conversation/index.ts @@ -0,0 +1,71 @@ +import request from '@/config/axios' + +export interface KeFuConversationRespVO { + /** + * 编号 + */ + id: number + /** + * 会话所属用户 + */ + userId: number + /** + * 会话所属用户头像 + */ + userAvatar: string + /** + * 会话所属用户昵称 + */ + userNickname: string + /** + * 最后聊天时间 + */ + lastMessageTime: Date + /** + * 最后聊天内容 + */ + lastMessageContent: string + /** + * 最后发送的消息类型 + */ + lastMessageContentType: number + /** + * 管理端置顶 + */ + adminPinned: boolean + /** + * 用户是否可见 + */ + userDeleted: boolean + /** + * 管理员是否可见 + */ + adminDeleted: boolean + /** + * 管理员未读消息数 + */ + adminUnreadMessageCount: number + /** + * 创建时间 + */ + createTime?: string +} + +// 客服会话 API +export const KeFuConversationApi = { + // 获得客服会话列表 + getConversationList: async () => { + return await request.get({ url: '/promotion/kefu-conversation/list' }) + }, + // 客服会话置顶 + updateConversationPinned: async (data: any) => { + return await request.put({ + url: '/promotion/kefu-conversation/update-conversation-pinned', + data + }) + }, + // 删除客服会话 + deleteConversation: async (id: number) => { + return await request.get({ url: '/promotion/kefu-conversation/delete?id' + id }) + } +} diff --git a/src/api/mall/promotion/kefu/message/index.ts b/src/api/mall/promotion/kefu/message/index.ts new file mode 100644 index 00000000..a598b541 --- /dev/null +++ b/src/api/mall/promotion/kefu/message/index.ts @@ -0,0 +1,70 @@ +import request from '@/config/axios' + +export interface KeFuMessageRespVO { + /** + * 编号 + */ + id: number + /** + * 会话编号 + */ + conversationId: number + /** + * 发送人编号 + */ + senderId: number + /** + * 发送人头像 + */ + senderAvatar: string + /** + * 发送人类型 + */ + senderType: number + /** + * 接收人编号 + */ + receiverId: number + /** + * 接收人类型 + */ + receiverType: number + /** + * 消息类型 + */ + contentType: number + /** + * 消息 + */ + content: string + /** + * 是否已读 + */ + readStatus: boolean + /** + * 创建时间 + */ + createTime: Date +} + +// 客服会话 API +export const KeFuMessageApi = { + // 发送客服消息 + sendKeFuMessage: async (data: any) => { + return await request.post({ + url: '/promotion/kefu-message/send', + data + }) + }, + // 更新客服消息已读状态 + updateKeFuMessageReadStatus: async (data: any) => { + return await request.put({ + url: '/promotion/kefu-message/update-read-status', + data + }) + }, + // 获得消息分页数据 + getKeFuMessagePage: async (params: any) => { + return await request.get({ url: '/promotion/kefu-message/page', params }) + } +} diff --git a/src/views/infra/webSocket/index.vue b/src/views/infra/webSocket/index.vue index 0f0e351f..300f6d92 100644 --- a/src/views/infra/webSocket/index.vue +++ b/src/views/infra/webSocket/index.vue @@ -29,8 +29,8 @@ :autosize="{ minRows: 2, maxRows: 4 }" :disabled="!getIsOpen" clearable - type="textarea" placeholder="请输入你要发送的消息" + type="textarea" /> @@ -71,7 +71,7 @@ diff --git a/src/views/mall/promotion/kefu/components/KeFuChatBox.vue b/src/views/mall/promotion/kefu/components/KeFuChatBox.vue new file mode 100644 index 00000000..bda7c483 --- /dev/null +++ b/src/views/mall/promotion/kefu/components/KeFuChatBox.vue @@ -0,0 +1,258 @@ + + + + + diff --git a/src/views/mall/promotion/kefu/components/KeFuConversationBox.vue b/src/views/mall/promotion/kefu/components/KeFuConversationBox.vue new file mode 100644 index 00000000..19984db2 --- /dev/null +++ b/src/views/mall/promotion/kefu/components/KeFuConversationBox.vue @@ -0,0 +1,114 @@ + + + + + diff --git a/src/views/mall/promotion/kefu/components/constants.ts b/src/views/mall/promotion/kefu/components/constants.ts new file mode 100644 index 00000000..f8599160 --- /dev/null +++ b/src/views/mall/promotion/kefu/components/constants.ts @@ -0,0 +1,10 @@ +export const KeFuMessageContentTypeEnum = { + TEXT: 1, // 文本消息 + IMAGE: 2, // 图片消息 + VOICE: 3, // 语音消息 + VIDEO: 4, // 视频消息 + SYSTEM: 5, // 系统消息 + // ========== 商城特殊消息 ========== + PRODUCT: 10, // 商品消息 + ORDER: 11 // 订单消息" +} diff --git a/src/views/mall/promotion/kefu/components/emoji.ts b/src/views/mall/promotion/kefu/components/emoji.ts new file mode 100644 index 00000000..765d2a6e --- /dev/null +++ b/src/views/mall/promotion/kefu/components/emoji.ts @@ -0,0 +1,106 @@ +export const emojiList = [ + { name: '[笑掉牙]', file: 'xiaodiaoya.png' }, + { name: '[可爱]', file: 'keai.png' }, + { name: '[冷酷]', file: 'lengku.png' }, + { name: '[闭嘴]', file: 'bizui.png' }, + { name: '[生气]', file: 'shengqi.png' }, + { name: '[惊恐]', file: 'jingkong.png' }, + { name: '[瞌睡]', file: 'keshui.png' }, + { name: '[大笑]', file: 'daxiao.png' }, + { name: '[爱心]', file: 'aixin.png' }, + { name: '[坏笑]', file: 'huaixiao.png' }, + { name: '[飞吻]', file: 'feiwen.png' }, + { name: '[疑问]', file: 'yiwen.png' }, + { name: '[开心]', file: 'kaixin.png' }, + { name: '[发呆]', file: 'fadai.png' }, + { name: '[流泪]', file: 'liulei.png' }, + { name: '[汗颜]', file: 'hanyan.png' }, + { name: '[惊悚]', file: 'jingshu.png' }, + { name: '[困~]', file: 'kun.png' }, + { name: '[心碎]', file: 'xinsui.png' }, + { name: '[天使]', file: 'tianshi.png' }, + { name: '[晕]', file: 'yun.png' }, + { name: '[啊]', file: 'a.png' }, + { name: '[愤怒]', file: 'fennu.png' }, + { name: '[睡着]', file: 'shuizhuo.png' }, + { name: '[面无表情]', file: 'mianwubiaoqing.png' }, + { name: '[难过]', file: 'nanguo.png' }, + { name: '[犯困]', file: 'fankun.png' }, + { name: '[好吃]', file: 'haochi.png' }, + { name: '[呕吐]', file: 'outu.png' }, + { name: '[龇牙]', file: 'ziya.png' }, + { name: '[懵比]', file: 'mengbi.png' }, + { name: '[白眼]', file: 'baiyan.png' }, + { name: '[饿死]', file: 'esi.png' }, + { name: '[凶]', file: 'xiong.png' }, + { name: '[感冒]', file: 'ganmao.png' }, + { name: '[流汗]', file: 'liuhan.png' }, + { name: '[笑哭]', file: 'xiaoku.png' }, + { name: '[流口水]', file: 'liukoushui.png' }, + { name: '[尴尬]', file: 'ganga.png' }, + { name: '[惊讶]', file: 'jingya.png' }, + { name: '[大惊]', file: 'dajing.png' }, + { name: '[不好意思]', file: 'buhaoyisi.png' }, + { name: '[大闹]', file: 'danao.png' }, + { name: '[不可思议]', file: 'bukesiyi.png' }, + { name: '[爱你]', file: 'aini.png' }, + { name: '[红心]', file: 'hongxin.png' }, + { name: '[点赞]', file: 'dianzan.png' }, + { name: '[恶魔]', file: 'emo.png' } +] + +export interface Emoji { + name: string + url: string +} + +export const emojiPage = {} +emojiList.forEach((item, index) => { + if (!emojiPage[Math.floor(index / 30) + 1]) { + emojiPage[Math.floor(index / 30) + 1] = [] + } + emojiPage[Math.floor(index / 30) + 1].push(item) +}) + +// 后端上传地址 +const staticUrl = import.meta.env.VITE_STATIC_URL +// 后缀 +const suffix = '/static/img/chat/emoji/' + +// 处理表情 +export function replaceEmoji(data: string) { + let newData = data + if (typeof newData !== 'object') { + const reg = /\[(.+?)\]/g // [] 中括号 + const zhEmojiName = newData.match(reg) + if (zhEmojiName) { + zhEmojiName.forEach((item) => { + const emojiFile = selEmojiFile(item) + newData = newData.replace( + item, + `` + ) + }) + } + } + return newData +} + +// 获得所有表情 +export function getEmojiList(): Emoji[] { + return emojiList.map((item) => ({ + url: staticUrl + suffix + item.file, + name: item.name + })) as Emoji[] +} + +function selEmojiFile(name: string) { + for (const index in emojiList) { + if (emojiList[index].name === name) { + return emojiList[index].file + } + } + return false +} diff --git a/src/views/mall/promotion/kefu/components/index.ts b/src/views/mall/promotion/kefu/components/index.ts new file mode 100644 index 00000000..fcf6bd5b --- /dev/null +++ b/src/views/mall/promotion/kefu/components/index.ts @@ -0,0 +1,5 @@ +import KeFuConversationBox from './KeFuConversationBox.vue' +import KeFuChatBox from './KeFuChatBox.vue' +import * as Constants from './constants' + +export { KeFuConversationBox, KeFuChatBox, Constants } diff --git a/src/views/mall/promotion/kefu/index.vue b/src/views/mall/promotion/kefu/index.vue new file mode 100644 index 00000000..1204b8dc --- /dev/null +++ b/src/views/mall/promotion/kefu/index.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/types/env.d.ts b/types/env.d.ts index 057b5268..63d9c3ee 100644 --- a/types/env.d.ts +++ b/types/env.d.ts @@ -19,6 +19,7 @@ interface ImportMetaEnv { readonly VITE_UPLOAD_URL: string readonly VITE_API_URL: string readonly VITE_BASE_PATH: string + readonly VITE_STATIC_URL: string readonly VITE_DROP_DEBUGGER: string readonly VITE_DROP_CONSOLE: string readonly VITE_SOURCEMAP: string