Skip to content

Commit e45cade

Browse files
author
puhui999
committed
【新增】:mall 客服选择并发送图片信息
1 parent b0b62eb commit e45cade

File tree

8 files changed

+132
-11
lines changed

8 files changed

+132
-11
lines changed

src/views/mall/promotion/kefu/components/KeFuChatBox.vue

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,14 @@
7171
</el-main>
7272
<el-footer height="230px">
7373
<div class="h-[100%]">
74-
<div class="chat-tools">
74+
<div class="chat-tools flex items-center">
7575
<EmojiSelectPopover @select-emoji="handleEmojiSelect" />
76+
<PictureSelectUpload
77+
class="ml-15px mt-3px cursor-pointer"
78+
@send-picture="handleSendPicture"
79+
/>
7680
</div>
77-
<el-input v-model="message" :rows="6" type="textarea" />
81+
<el-input v-model="message" :rows="6" style="border-style: none" type="textarea" />
7882
<div class="h-45px flex justify-end">
7983
<el-button class="mt-10px" type="primary" @click="handleSendMessage">发送</el-button>
8084
</div>
@@ -88,9 +92,10 @@
8892
import { ElScrollbar as ElScrollbarType } from 'element-plus'
8993
import { KeFuMessageApi, KeFuMessageRespVO } from '@/api/mall/promotion/kefu/message'
9094
import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
91-
import EmojiSelectPopover from './EmojiSelectPopover.vue'
92-
import { Emoji, useEmoji } from './emoji'
93-
import { KeFuMessageContentTypeEnum } from './constants'
95+
import EmojiSelectPopover from './tools/EmojiSelectPopover.vue'
96+
import PictureSelectUpload from './tools/PictureSelectUpload.vue'
97+
import { Emoji, useEmoji } from './tools/emoji'
98+
import { KeFuMessageContentTypeEnum } from './tools/constants'
9499
import { isEmpty } from '@/utils/is'
95100
import { UserTypeEnum } from '@/utils/constants'
96101
import { createImageViewer } from '@/components/ImageViewer'
@@ -126,6 +131,16 @@ const showChatBox = computed(() => !isEmpty(keFuConversation.value))
126131
const handleEmojiSelect = (item: Emoji) => {
127132
message.value += item.name
128133
}
134+
// 处理图片发送
135+
const handleSendPicture = async (picUrl: string) => {
136+
// 组织发送消息
137+
const msg = {
138+
conversationId: keFuConversation.value.id,
139+
contentType: KeFuMessageContentTypeEnum.IMAGE,
140+
content: picUrl
141+
}
142+
await sendMessage(msg)
143+
}
129144
// 发送消息
130145
const handleSendMessage = async () => {
131146
// 1. 校验消息是否为空
@@ -139,6 +154,11 @@ const handleSendMessage = async () => {
139154
contentType: KeFuMessageContentTypeEnum.TEXT,
140155
content: message.value
141156
}
157+
await sendMessage(msg)
158+
}
159+
160+
// 发送消息 【共用】
161+
const sendMessage = async (msg: any) => {
142162
await KeFuMessageApi.sendKeFuMessage(msg)
143163
message.value = ''
144164
// 3. 加载消息列表
@@ -248,9 +268,8 @@ onBeforeUnmount(() => {
248268
.chat-tools {
249269
width: 100%;
250270
border: #e4e0e0 solid 1px;
271+
border-radius: 10px;
251272
height: 44px;
252-
display: flex;
253-
align-items: center;
254273
}
255274
256275
::v-deep(textarea) {

src/views/mall/promotion/kefu/components/KeFuConversationBox.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@
3535

3636
<script lang="ts" setup>
3737
import { KeFuConversationApi, KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
38-
import { useEmoji } from './emoji'
38+
import { useEmoji } from './tools/emoji'
3939
import { formatDate, getNowDateTime } from '@/utils/formatTime'
40-
import { KeFuMessageContentTypeEnum } from '@/views/mall/promotion/kefu/components/constants'
40+
import { KeFuMessageContentTypeEnum } from './tools/constants'
4141
4242
defineOptions({ name: 'KeFuConversationBox' })
4343
const { replaceEmoji } = useEmoji()
Lines changed: 10 additions & 0 deletions
Loading
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import KeFuConversationBox from './KeFuConversationBox.vue'
22
import KeFuChatBox from './KeFuChatBox.vue'
3-
import * as Constants from './constants'
3+
import * as Constants from './tools/constants'
44

55
export { KeFuConversationBox, KeFuChatBox, Constants }

src/views/mall/promotion/kefu/components/EmojiSelectPopover.vue renamed to src/views/mall/promotion/kefu/components/tools/EmojiSelectPopover.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<template>
33
<el-popover :width="500" placement="top" trigger="click">
44
<template #reference>
5-
<Icon :size="30" class="ml-10px" icon="twemoji:grinning-face" />
5+
<Icon :size="30" class="ml-10px cursor-pointer" icon="twemoji:grinning-face" />
66
</template>
77
<ElScrollbar height="300px">
88
<ul class="ml-2 flex flex-wrap px-2">
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<template>
2+
<div>
3+
<img :src="Picture" style="width: 35px; height: 35px" @click="selectAndUpload" />
4+
</div>
5+
</template>
6+
7+
<script lang="ts" setup>
8+
import Picture from '@/views/mall/promotion/kefu/components/images/picture.svg'
9+
import * as FileApi from '@/api/infra/file'
10+
11+
defineOptions({ name: 'PictureSelectUpload' })
12+
const message = useMessage()
13+
const emits = defineEmits<{
14+
(e: 'send-picture', v: string): void
15+
}>()
16+
// 选择并上传文件
17+
const selectAndUpload = async () => {
18+
const files: any = await getFiles()
19+
message.success('图片发送请稍等。。。')
20+
const res = await FileApi.updateFile({ file: files[0].file })
21+
message.success('图片发送成功!')
22+
emits('send-picture', res.data)
23+
}
24+
25+
/**
26+
* 唤起文件选择窗口,并获取选择的文件
27+
* @param {Object} options - 配置选项
28+
* @param {boolean} [options.multiple=true] - 是否支持多选
29+
* @param {string} [options.accept=''] - 文件上传格式限制
30+
* @param {number} [options.limit=1] - 单次上传最大文件数
31+
* @param {number} [options.fileSize=500] - 单个文件大小限制(单位:MB)
32+
* @returns {Promise<Array>} 选择的文件列表,每个文件带有一个uid
33+
*/
34+
async function getFiles(options = {}) {
35+
const { multiple, accept, limit, fileSize } = {
36+
multiple: true,
37+
accept: 'image/jpeg, image/png, image/gif',
38+
limit: 1,
39+
fileSize: 500,
40+
...options
41+
}
42+
43+
// 创建文件选择元素
44+
const input = document.createElement('input')
45+
input.type = 'file'
46+
input.style.display = 'none'
47+
if (multiple) input.multiple = true
48+
if (accept) input.accept = accept
49+
50+
// 将文件选择元素添加到文档中
51+
document.body.appendChild(input)
52+
53+
// 触发文件选择元素的点击事件
54+
input.click()
55+
56+
// 等待文件选择元素的 change 事件
57+
try {
58+
const files = await new Promise((resolve, reject) => {
59+
input.addEventListener('change', (event: any) => {
60+
const filesArray = Array.from(event?.target?.files || [])
61+
62+
// 从文档中移除文件选择元素
63+
document.body.removeChild(input)
64+
65+
// 判断是否超出上传数量限制
66+
if (filesArray.length > limit) {
67+
reject({ errorType: 'limit', files: filesArray })
68+
return
69+
}
70+
71+
// 判断是否超出上传文件大小限制
72+
const oversizedFiles = filesArray.filter((file: File) => file.size / 1024 ** 2 > fileSize)
73+
if (oversizedFiles.length > 0) {
74+
reject({ errorType: 'fileSize', files: oversizedFiles })
75+
return
76+
}
77+
78+
// 生成文件列表,并添加 uid
79+
const fileList = filesArray.map((file, index) => ({ file, uid: Date.now() + index }))
80+
resolve(fileList)
81+
})
82+
})
83+
84+
return files
85+
} catch (error) {
86+
console.error('选择文件出错:', error)
87+
throw error
88+
}
89+
}
90+
</script>
91+
92+
<style lang="scss" scoped></style>

0 commit comments

Comments
 (0)