【优化】:mall 客服表情包存放到本地使用
|
@ -29,9 +29,5 @@ VITE_BASE_PATH=/
|
||||||
# 商城H5会员端域名
|
# 商城H5会员端域名
|
||||||
VITE_MALL_H5_DOMAIN='http://localhost:3000'
|
VITE_MALL_H5_DOMAIN='http://localhost:3000'
|
||||||
|
|
||||||
# TODO puhui999:这个可以不走 cdn 地址么?
|
|
||||||
# 客户端静态资源地址 空=默认使用服务端指定的CDN资源地址前缀 | local=本地 | http(s)://xxx.xxx=自定义静态资源地址前缀
|
|
||||||
VITE_STATIC_URL = https://file.sheepjs.com
|
|
||||||
|
|
||||||
# 验证码的开关
|
# 验证码的开关
|
||||||
VITE_APP_CAPTCHA_ENABLE=false
|
VITE_APP_CAPTCHA_ENABLE=false
|
||||||
|
|
|
@ -26,8 +26,9 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
defineOptions({ name: 'EmojiSelectPopover' })
|
defineOptions({ name: 'EmojiSelectPopover' })
|
||||||
import { Emoji, getEmojiList } from './emoji'
|
import { Emoji, useEmoji } from './emoji'
|
||||||
|
|
||||||
|
const { getEmojiList } = useEmoji()
|
||||||
const emojiList = computed(() => getEmojiList())
|
const emojiList = computed(() => getEmojiList())
|
||||||
|
|
||||||
const emits = defineEmits<{
|
const emits = defineEmits<{
|
||||||
|
|
|
@ -89,13 +89,14 @@ import { ElScrollbar as ElScrollbarType } from 'element-plus'
|
||||||
import { KeFuMessageApi, KeFuMessageRespVO } from '@/api/mall/promotion/kefu/message'
|
import { KeFuMessageApi, KeFuMessageRespVO } from '@/api/mall/promotion/kefu/message'
|
||||||
import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
|
import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
|
||||||
import EmojiSelectPopover from './EmojiSelectPopover.vue'
|
import EmojiSelectPopover from './EmojiSelectPopover.vue'
|
||||||
import { Emoji, replaceEmoji } from './emoji'
|
import { Emoji, useEmoji } from './emoji'
|
||||||
import { KeFuMessageContentTypeEnum } from './constants'
|
import { KeFuMessageContentTypeEnum } from './constants'
|
||||||
import { isEmpty } from '@/utils/is'
|
import { isEmpty } from '@/utils/is'
|
||||||
import { UserTypeEnum } from '@/utils/constants'
|
import { UserTypeEnum } from '@/utils/constants'
|
||||||
import { createImageViewer } from '@/components/ImageViewer'
|
import { createImageViewer } from '@/components/ImageViewer'
|
||||||
|
|
||||||
defineOptions({ name: 'KeFuMessageBox' })
|
defineOptions({ name: 'KeFuMessageBox' })
|
||||||
|
const { replaceEmoji } = useEmoji()
|
||||||
const messageTool = useMessage()
|
const messageTool = useMessage()
|
||||||
const message = ref('') // 消息
|
const message = ref('') // 消息
|
||||||
const messageList = ref<KeFuMessageRespVO[]>([]) // 消息列表
|
const messageList = ref<KeFuMessageRespVO[]>([]) // 消息列表
|
||||||
|
@ -130,6 +131,7 @@ const handleSendMessage = async () => {
|
||||||
// 1. 校验消息是否为空
|
// 1. 校验消息是否为空
|
||||||
if (isEmpty(unref(message.value))) {
|
if (isEmpty(unref(message.value))) {
|
||||||
messageTool.warning('请输入消息后再发送哦!')
|
messageTool.warning('请输入消息后再发送哦!')
|
||||||
|
return
|
||||||
}
|
}
|
||||||
// 2. 组织发送消息
|
// 2. 组织发送消息
|
||||||
const msg = {
|
const msg = {
|
||||||
|
|
|
@ -35,11 +35,12 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { KeFuConversationApi, KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
|
import { KeFuConversationApi, KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
|
||||||
import { replaceEmoji } from '@/views/mall/promotion/kefu/components/emoji'
|
import { useEmoji } from './emoji'
|
||||||
import { formatDate, getNowDateTime } from '@/utils/formatTime'
|
import { formatDate, getNowDateTime } from '@/utils/formatTime'
|
||||||
import { KeFuMessageContentTypeEnum } from '@/views/mall/promotion/kefu/components/constants'
|
import { KeFuMessageContentTypeEnum } from '@/views/mall/promotion/kefu/components/constants'
|
||||||
|
|
||||||
defineOptions({ name: 'KeFuConversationBox' })
|
defineOptions({ name: 'KeFuConversationBox' })
|
||||||
|
const { replaceEmoji } = useEmoji()
|
||||||
const activeConversationIndex = ref(-1) // 选中的会话
|
const activeConversationIndex = ref(-1) // 选中的会话
|
||||||
const conversationList = ref<KeFuConversationRespVO[]>([]) // 会话列表
|
const conversationList = ref<KeFuConversationRespVO[]>([]) // 会话列表
|
||||||
const getConversationList = async () => {
|
const getConversationList = async () => {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
export const emojiList = [
|
import { isEmpty } from '@/utils/is'
|
||||||
|
|
||||||
|
const emojiList = [
|
||||||
{ name: '[笑掉牙]', file: 'xiaodiaoya.png' },
|
{ name: '[笑掉牙]', file: 'xiaodiaoya.png' },
|
||||||
{ name: '[可爱]', file: 'keai.png' },
|
{ name: '[可爱]', file: 'keai.png' },
|
||||||
{ name: '[冷酷]', file: 'lengku.png' },
|
{ name: '[冷酷]', file: 'lengku.png' },
|
||||||
|
@ -54,53 +56,60 @@ export interface Emoji {
|
||||||
url: string
|
url: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const emojiPage = {}
|
export const useEmoji = () => {
|
||||||
emojiList.forEach((item, index) => {
|
const emojiPathList = ref<any[]>([])
|
||||||
if (!emojiPage[Math.floor(index / 30) + 1]) {
|
// 加载本地图片
|
||||||
emojiPage[Math.floor(index / 30) + 1] = []
|
const getStaticEmojiPath = async () => {
|
||||||
}
|
const pathList = import.meta.glob(
|
||||||
emojiPage[Math.floor(index / 30) + 1].push(item)
|
'@/views/mall/promotion/kefu/components/images/*.{png,jpg,jpeg,svg}'
|
||||||
})
|
)
|
||||||
|
for (const path in pathList) {
|
||||||
// 后端上传地址
|
const imageModule: any = await pathList[path]()
|
||||||
const staticUrl = import.meta.env.VITE_STATIC_URL
|
emojiPathList.value.push(imageModule.default)
|
||||||
// 后缀
|
|
||||||
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,
|
|
||||||
`<img class="chat-img" style="width: 24px;height: 24px;margin: 0 3px;" src="${
|
|
||||||
staticUrl + suffix + emojiFile
|
|
||||||
}"/>`
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newData
|
// 初始化
|
||||||
}
|
onMounted(async () => {
|
||||||
|
if (isEmpty(emojiPathList.value)) {
|
||||||
// 获得所有表情
|
await getStaticEmojiPath()
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 处理表情
|
||||||
|
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,
|
||||||
|
`<img class="chat-img" style="width: 24px;height: 24px;margin: 0 3px;" src="${emojiFile}"/>`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newData
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
|
// 获得所有表情
|
||||||
|
function getEmojiList(): Emoji[] {
|
||||||
|
return emojiList.map((item) => ({
|
||||||
|
url: selEmojiFile(item.name),
|
||||||
|
name: item.name
|
||||||
|
})) as Emoji[]
|
||||||
|
}
|
||||||
|
|
||||||
|
function selEmojiFile(name: string) {
|
||||||
|
for (const emoji of emojiList) {
|
||||||
|
if (emoji.name === name) {
|
||||||
|
return emojiPathList.value.find((item: string) => item.indexOf(emoji.file) > -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return { replaceEmoji, getEmojiList }
|
||||||
}
|
}
|
||||||
|
|
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 4.0 KiB |