Skip to content

Commit 6eb013b

Browse files
YunaiVgitee-org
authored andcommitted
!601 同步商城的客服优化、门店自提优化
Merge pull request !601 from 芋道源码/dev
2 parents 22199c6 + 6dee741 commit 6eb013b

File tree

17 files changed

+790
-463
lines changed

17 files changed

+790
-463
lines changed

src/api/mall/promotion/kefu/conversation/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ export const KeFuConversationApi = {
2121
getConversationList: async () => {
2222
return await request.get({ url: '/promotion/kefu-conversation/list' })
2323
},
24+
// 获得客服会话
25+
getConversation: async (id: number) => {
26+
return await request.get({ url: `/promotion/kefu-conversation/get?id=` + id })
27+
},
2428
// 客服会话置顶
2529
updateConversationPinned: async (data: any) => {
2630
return await request.put({
@@ -30,6 +34,6 @@ export const KeFuConversationApi = {
3034
},
3135
// 删除客服会话
3236
deleteConversation: async (id: number) => {
33-
return await request.delete({ url: `/promotion/kefu-conversation/delete?id=${id}`})
37+
return await request.delete({ url: `/promotion/kefu-conversation/delete?id=${id}` })
3438
}
3539
}

src/api/mall/promotion/kefu/message/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ export const KeFuMessageApi = {
2929
url: '/promotion/kefu-message/update-read-status?conversationId=' + conversationId
3030
})
3131
},
32-
// 获得消息分页数据
33-
getKeFuMessagePage: async (params: any) => {
34-
return await request.get({ url: '/promotion/kefu-message/page', params })
32+
// 获得消息列表(流式加载)
33+
getKeFuMessageList: async (params: any) => {
34+
return await request.get({ url: '/promotion/kefu-message/list', params })
3535
}
3636
}

src/store/modules/mall/kefu.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { store } from '@/store'
2+
import { defineStore } from 'pinia'
3+
import { KeFuConversationApi, KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
4+
import { KeFuMessageRespVO } from '@/api/mall/promotion/kefu/message'
5+
import { isEmpty } from '@/utils/is'
6+
7+
interface MallKefuInfoVO {
8+
conversationList: KeFuConversationRespVO[] // 会话列表
9+
conversationMessageList: Map<number, KeFuMessageRespVO[]> // 会话消息
10+
}
11+
12+
export const useMallKefuStore = defineStore('mall-kefu', {
13+
state: (): MallKefuInfoVO => ({
14+
conversationList: [],
15+
conversationMessageList: new Map<number, KeFuMessageRespVO[]>() // key 会话,value 会话消息列表
16+
}),
17+
getters: {
18+
getConversationList(): KeFuConversationRespVO[] {
19+
return this.conversationList
20+
},
21+
getConversationMessageList(): (conversationId: number) => KeFuMessageRespVO[] | undefined {
22+
return (conversationId: number) => this.conversationMessageList.get(conversationId)
23+
}
24+
},
25+
actions: {
26+
// ======================= 会话消息相关 =======================
27+
/** 缓存历史消息 */
28+
saveMessageList(conversationId: number, messageList: KeFuMessageRespVO[]) {
29+
this.conversationMessageList.set(conversationId, messageList)
30+
},
31+
32+
// ======================= 会话相关 =======================
33+
/** 加载会话缓存列表 */
34+
async setConversationList() {
35+
this.conversationList = await KeFuConversationApi.getConversationList()
36+
this.conversationSort()
37+
},
38+
/** 更新会话缓存已读 */
39+
async updateConversationStatus(conversationId: number) {
40+
if (isEmpty(this.conversationList)) {
41+
return
42+
}
43+
const conversation = this.conversationList.find((item) => item.id === conversationId)
44+
conversation && (conversation.adminUnreadMessageCount = 0)
45+
},
46+
/** 更新会话缓存 */
47+
async updateConversation(conversationId: number) {
48+
if (isEmpty(this.conversationList)) {
49+
return
50+
}
51+
52+
const conversation = await KeFuConversationApi.getConversation(conversationId)
53+
this.deleteConversation(conversationId)
54+
conversation && this.conversationList.push(conversation)
55+
this.conversationSort()
56+
},
57+
/** 删除会话缓存 */
58+
deleteConversation(conversationId: number) {
59+
const index = this.conversationList.findIndex((item) => item.id === conversationId)
60+
// 存在则删除
61+
if (index > -1) {
62+
this.conversationList.splice(index, 1)
63+
}
64+
},
65+
conversationSort() {
66+
// 按置顶属性和最后消息时间排序
67+
this.conversationList.sort((a, b) => {
68+
// 按照置顶排序,置顶的会在前面
69+
if (a.adminPinned !== b.adminPinned) {
70+
return a.adminPinned ? -1 : 1
71+
}
72+
// 按照最后消息时间排序,最近的会在前面
73+
return (b.lastMessageTime as unknown as number) - (a.lastMessageTime as unknown as number)
74+
})
75+
}
76+
}
77+
})
78+
79+
export const useMallKefuStoreWithOut = () => {
80+
return useMallKefuStore(store)
81+
}

src/views/mall/promotion/components/SpuSelect.vue

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ import { getPropertyList, PropertyAndValues, SkuList } from '@/views/mall/produc
115115
import { ElTable } from 'element-plus'
116116
import { dateFormatter } from '@/utils/formatTime'
117117
import { createImageViewer } from '@/components/ImageViewer'
118-
import { formatToFraction } from '@/utils'
118+
import { floatToFixed2, formatToFraction } from '@/utils'
119119
import { defaultProps, handleTree } from '@/utils/tree'
120120
121121
import * as ProductCategoryApi from '@/api/mall/product/category'
@@ -228,6 +228,13 @@ const expandChange = async (row: ProductSpuApi.Spu, expandedRows?: ProductSpuApi
228228
}
229229
// 获取 SPU 详情
230230
const res = (await ProductSpuApi.getSpu(row.id as number)) as ProductSpuApi.Spu
231+
res.skus?.forEach((item) => {
232+
item.price = floatToFixed2(item.price)
233+
item.marketPrice = floatToFixed2(item.marketPrice)
234+
item.costPrice = floatToFixed2(item.costPrice)
235+
item.firstBrokeragePrice = floatToFixed2(item.firstBrokeragePrice)
236+
item.secondBrokeragePrice = floatToFixed2(item.secondBrokeragePrice)
237+
})
231238
propertyList.value = getPropertyList(res)
232239
spuData.value = res
233240
isExpand.value = true

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

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
<template>
2-
<div class="kefu">
2+
<el-aside class="kefu pt-5px h-100%" width="260px">
3+
<div class="color-[#999] font-bold my-10px">
4+
会话记录({{ kefuStore.getConversationList.length }})
5+
</div>
36
<div
4-
v-for="item in conversationList"
7+
v-for="item in kefuStore.getConversationList"
58
:key="item.id"
69
:class="{ active: item.id === activeConversationId, pinned: item.adminPinned }"
7-
class="kefu-conversation flex items-center"
10+
class="kefu-conversation px-10px flex items-center"
811
@click="openRightMessage(item)"
912
@contextmenu.prevent="rightClick($event as PointerEvent, item)"
1013
>
@@ -22,16 +25,16 @@
2225
<div class="ml-10px w-100%">
2326
<div class="flex justify-between items-center w-100%">
2427
<span class="username">{{ item.userNickname }}</span>
25-
<span class="color-[var(--left-menu-text-color)]" style="font-size: 13px">
26-
{{ formatPast(item.lastMessageTime, 'YYYY-MM-DD') }}
28+
<span class="color-[#999]" style="font-size: 13px">
29+
{{ lastMessageTimeMap.get(item.id) ?? '计算中' }}
2730
</span>
2831
</div>
2932
<!-- 最后聊天内容 -->
3033
<div
3134
v-dompurify-html="
3235
getConversationDisplayText(item.lastMessageContentType, item.lastMessageContent)
3336
"
34-
class="last-message flex items-center color-[var(--left-menu-text-color)]"
37+
class="last-message flex items-center color-[#999]"
3538
>
3639
</div>
3740
</div>
@@ -65,7 +68,7 @@
6568
取消
6669
</li>
6770
</ul>
68-
</div>
71+
</el-aside>
6972
</template>
7073

7174
<script lang="ts" setup>
@@ -74,29 +77,36 @@ import { useEmoji } from './tools/emoji'
7477
import { formatPast } from '@/utils/formatTime'
7578
import { KeFuMessageContentTypeEnum } from './tools/constants'
7679
import { useAppStore } from '@/store/modules/app'
80+
import { useMallKefuStore } from '@/store/modules/mall/kefu'
81+
import { jsonParse } from '@/utils'
7782
7883
defineOptions({ name: 'KeFuConversationList' })
7984
8085
const message = useMessage() // 消息弹窗
8186
const appStore = useAppStore()
87+
const kefuStore = useMallKefuStore() // 客服缓存
8288
const { replaceEmoji } = useEmoji()
83-
const conversationList = ref<KeFuConversationRespVO[]>([]) // 会话列表
8489
const activeConversationId = ref(-1) // 选中的会话
8590
const collapse = computed(() => appStore.getCollapse) // 折叠菜单
8691
87-
/** 加载会话列表 */
88-
const getConversationList = async () => {
89-
const list = await KeFuConversationApi.getConversationList()
90-
list.sort((a: KeFuConversationRespVO, _) => (a.adminPinned ? -1 : 1))
91-
conversationList.value = list
92+
/** 计算消息最后发送时间距离现在过去了多久 */
93+
const lastMessageTimeMap = ref<Map<number, string>>(new Map<number, string>())
94+
const calculationLastMessageTime = () => {
95+
kefuStore.getConversationList?.forEach((item) => {
96+
lastMessageTimeMap.value.set(item.id, formatPast(item.lastMessageTime, 'YYYY-MM-DD'))
97+
})
9298
}
93-
defineExpose({ getConversationList })
99+
defineExpose({ calculationLastMessageTime })
94100
95101
/** 打开右侧的消息列表 */
96102
const emits = defineEmits<{
97103
(e: 'change', v: KeFuConversationRespVO): void
98104
}>()
99105
const openRightMessage = (item: KeFuConversationRespVO) => {
106+
// 同一个会话则不处理
107+
if (activeConversationId.value === item.id) {
108+
return
109+
}
100110
activeConversationId.value = item.id
101111
emits('change', item)
102112
}
@@ -118,7 +128,7 @@ const getConversationDisplayText = computed(
118128
case KeFuMessageContentTypeEnum.VOICE:
119129
return '[语音消息]'
120130
case KeFuMessageContentTypeEnum.TEXT:
121-
return replaceEmoji(lastMessageContent)
131+
return replaceEmoji(jsonParse(lastMessageContent).text || lastMessageContent)
122132
default:
123133
return ''
124134
}
@@ -155,7 +165,7 @@ const updateConversationPinned = async (adminPinned: boolean) => {
155165
message.notifySuccess(adminPinned ? '置顶成功' : '取消置顶成功')
156166
// 2. 关闭右键菜单,更新会话列表
157167
closeRightMenu()
158-
await getConversationList()
168+
await kefuStore.updateConversation(rightClickConversation.value.id)
159169
}
160170
161171
/** 删除会话 */
@@ -165,7 +175,7 @@ const deleteConversation = async () => {
165175
await KeFuConversationApi.deleteConversation(rightClickConversation.value.id)
166176
// 2. 关闭右键菜单,更新会话列表
167177
closeRightMenu()
168-
await getConversationList()
178+
kefuStore.deleteConversation(rightClickConversation.value.id)
169179
}
170180
171181
/** 监听右键菜单的显示状态,添加点击事件监听器 */
@@ -176,42 +186,48 @@ watch(showRightMenu, (val) => {
176186
document.body.removeEventListener('click', closeRightMenu)
177187
}
178188
})
189+
190+
const timer = ref<any>()
191+
/** 初始化 */
192+
onMounted(() => {
193+
timer.value = setInterval(calculationLastMessageTime, 1000 * 10) // 十秒计算一次
194+
})
195+
/** 组件卸载前 */
196+
onBeforeUnmount(() => {
197+
clearInterval(timer.value)
198+
})
179199
</script>
180200

181201
<style lang="scss" scoped>
182202
.kefu {
203+
background-color: #e5e4e4;
204+
183205
&-conversation {
184206
height: 60px;
185-
padding: 10px;
186207
//background-color: #fff;
187-
transition: border-left 0.05s ease-in-out; /* 设置过渡效果 */
208+
//transition: border-left 0.05s ease-in-out; /* 设置过渡效果 */
188209
189210
.username {
190211
min-width: 0;
191212
max-width: 60%;
213+
}
214+
215+
.last-message {
216+
font-size: 13px;
217+
}
218+
219+
.last-message,
220+
.username {
192221
overflow: hidden;
193222
text-overflow: ellipsis;
194223
display: -webkit-box;
195224
-webkit-box-orient: vertical;
196225
-webkit-line-clamp: 1;
197226
}
198-
199-
.last-message {
200-
font-size: 13px;
201-
width: 200px;
202-
overflow: hidden; // 隐藏超出的文本
203-
white-space: nowrap; // 禁止换行
204-
text-overflow: ellipsis; // 添加省略号
205-
}
206227
}
207228
208229
.active {
209-
border-left: 5px #3271ff solid;
210-
background-color: var(--login-bg-color);
211-
}
212-
213-
.pinned {
214-
background-color: var(--left-menu-bg-active-color);
230+
background-color: rgba(128, 128, 128, 0.5); // 透明色,暗黑模式下也能体现
215231
}
216232
217233
.right-menu-ul {

0 commit comments

Comments
 (0)