mirror of
https://gitee.com/hhyykk/ipms-sjy-ui.git
synced 2025-08-07 22:51:53 +08:00
【新增】:mall 客服选择并发送图片信息
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
<!-- emoji 表情选择组件 -->
|
||||
<template>
|
||||
<el-popover :width="500" placement="top" trigger="click">
|
||||
<template #reference>
|
||||
<Icon :size="30" class="ml-10px cursor-pointer" icon="twemoji:grinning-face" />
|
||||
</template>
|
||||
<ElScrollbar height="300px">
|
||||
<ul class="ml-2 flex flex-wrap px-2">
|
||||
<li
|
||||
v-for="(item, index) in emojiList"
|
||||
:key="index"
|
||||
:style="{
|
||||
borderColor: 'var(--el-color-primary)',
|
||||
color: 'var(--el-color-primary)'
|
||||
}"
|
||||
:title="item.name"
|
||||
class="icon-item mr-2 mt-1 w-1/10 flex cursor-pointer items-center justify-center border border-solid p-2"
|
||||
@click="handleSelect(item)"
|
||||
>
|
||||
<img :src="item.url" style="width: 24px; height: 24px" />
|
||||
</li>
|
||||
</ul>
|
||||
</ElScrollbar>
|
||||
</el-popover>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
defineOptions({ name: 'EmojiSelectPopover' })
|
||||
import { Emoji, useEmoji } from './emoji'
|
||||
|
||||
const { getEmojiList } = useEmoji()
|
||||
const emojiList = computed(() => getEmojiList())
|
||||
|
||||
const emits = defineEmits<{
|
||||
(e: 'select-emoji', v: Emoji)
|
||||
}>()
|
||||
const handleSelect = (item: Emoji) => {
|
||||
// 整个 emoji 数据传递出去,方便以后输入框直接显示表情
|
||||
emits('select-emoji', item)
|
||||
}
|
||||
</script>
|
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div>
|
||||
<img :src="Picture" style="width: 35px; height: 35px" @click="selectAndUpload" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Picture from '@/views/mall/promotion/kefu/components/images/picture.svg'
|
||||
import * as FileApi from '@/api/infra/file'
|
||||
|
||||
defineOptions({ name: 'PictureSelectUpload' })
|
||||
const message = useMessage()
|
||||
const emits = defineEmits<{
|
||||
(e: 'send-picture', v: string): void
|
||||
}>()
|
||||
// 选择并上传文件
|
||||
const selectAndUpload = async () => {
|
||||
const files: any = await getFiles()
|
||||
message.success('图片发送请稍等。。。')
|
||||
const res = await FileApi.updateFile({ file: files[0].file })
|
||||
message.success('图片发送成功!')
|
||||
emits('send-picture', res.data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 唤起文件选择窗口,并获取选择的文件
|
||||
* @param {Object} options - 配置选项
|
||||
* @param {boolean} [options.multiple=true] - 是否支持多选
|
||||
* @param {string} [options.accept=''] - 文件上传格式限制
|
||||
* @param {number} [options.limit=1] - 单次上传最大文件数
|
||||
* @param {number} [options.fileSize=500] - 单个文件大小限制(单位:MB)
|
||||
* @returns {Promise<Array>} 选择的文件列表,每个文件带有一个uid
|
||||
*/
|
||||
async function getFiles(options = {}) {
|
||||
const { multiple, accept, limit, fileSize } = {
|
||||
multiple: true,
|
||||
accept: 'image/jpeg, image/png, image/gif',
|
||||
limit: 1,
|
||||
fileSize: 500,
|
||||
...options
|
||||
}
|
||||
|
||||
// 创建文件选择元素
|
||||
const input = document.createElement('input')
|
||||
input.type = 'file'
|
||||
input.style.display = 'none'
|
||||
if (multiple) input.multiple = true
|
||||
if (accept) input.accept = accept
|
||||
|
||||
// 将文件选择元素添加到文档中
|
||||
document.body.appendChild(input)
|
||||
|
||||
// 触发文件选择元素的点击事件
|
||||
input.click()
|
||||
|
||||
// 等待文件选择元素的 change 事件
|
||||
try {
|
||||
const files = await new Promise((resolve, reject) => {
|
||||
input.addEventListener('change', (event: any) => {
|
||||
const filesArray = Array.from(event?.target?.files || [])
|
||||
|
||||
// 从文档中移除文件选择元素
|
||||
document.body.removeChild(input)
|
||||
|
||||
// 判断是否超出上传数量限制
|
||||
if (filesArray.length > limit) {
|
||||
reject({ errorType: 'limit', files: filesArray })
|
||||
return
|
||||
}
|
||||
|
||||
// 判断是否超出上传文件大小限制
|
||||
const oversizedFiles = filesArray.filter((file: File) => file.size / 1024 ** 2 > fileSize)
|
||||
if (oversizedFiles.length > 0) {
|
||||
reject({ errorType: 'fileSize', files: oversizedFiles })
|
||||
return
|
||||
}
|
||||
|
||||
// 生成文件列表,并添加 uid
|
||||
const fileList = filesArray.map((file, index) => ({ file, uid: Date.now() + index }))
|
||||
resolve(fileList)
|
||||
})
|
||||
})
|
||||
|
||||
return files
|
||||
} catch (error) {
|
||||
console.error('选择文件出错:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
10
src/views/mall/promotion/kefu/components/tools/constants.ts
Normal file
10
src/views/mall/promotion/kefu/components/tools/constants.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export const KeFuMessageContentTypeEnum = {
|
||||
TEXT: 1, // 文本消息
|
||||
IMAGE: 2, // 图片消息
|
||||
VOICE: 3, // 语音消息
|
||||
VIDEO: 4, // 视频消息
|
||||
SYSTEM: 5, // 系统消息
|
||||
// ========== 商城特殊消息 ==========
|
||||
PRODUCT: 10, // 商品消息
|
||||
ORDER: 11 // 订单消息"
|
||||
}
|
115
src/views/mall/promotion/kefu/components/tools/emoji.ts
Normal file
115
src/views/mall/promotion/kefu/components/tools/emoji.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { isEmpty } from '@/utils/is'
|
||||
|
||||
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 useEmoji = () => {
|
||||
const emojiPathList = ref<any[]>([])
|
||||
// 加载本地图片
|
||||
const getStaticEmojiPath = async () => {
|
||||
const pathList = import.meta.glob(
|
||||
'@/views/mall/promotion/kefu/components/images/*.{png,jpg,jpeg,svg}'
|
||||
)
|
||||
for (const path in pathList) {
|
||||
const imageModule: any = await pathList[path]()
|
||||
emojiPathList.value.push(imageModule.default)
|
||||
}
|
||||
}
|
||||
// 初始化
|
||||
onMounted(async () => {
|
||||
if (isEmpty(emojiPathList.value)) {
|
||||
await getStaticEmojiPath()
|
||||
}
|
||||
})
|
||||
|
||||
// 处理表情
|
||||
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
|
||||
}
|
||||
|
||||
// 获得所有表情
|
||||
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 }
|
||||
}
|
Reference in New Issue
Block a user