diff --git a/yudao-admin-vue3/src/api/mall/promotion/kefu/conversation/index.ts b/yudao-admin-vue3/src/api/mall/promotion/kefu/conversation/index.ts
index 2dbf331..6e9589c 100644
--- a/yudao-admin-vue3/src/api/mall/promotion/kefu/conversation/index.ts
+++ b/yudao-admin-vue3/src/api/mall/promotion/kefu/conversation/index.ts
@@ -1,4 +1,5 @@
import request from '@/config/axios'
+import { number } from 'vue-types'
export interface KeFuConversationRespVO {
id: number // 编号
@@ -21,6 +22,10 @@ export const KeFuConversationApi = {
getConversationList: async () => {
return await request.get({ url: '/promotion/kefu-conversation/list' })
},
+ // 获得客服会话列表
+ getConversationListByKefuId: async (kefuId: number) => {
+ return await request.get({ url: '/promotion/kefu-conversation/list?kefuId=' + kefuId })
+ },
// 客服会话置顶
updateConversationPinned: async (data: any) => {
return await request.put({
@@ -30,6 +35,10 @@ export const KeFuConversationApi = {
},
// 删除客服会话
deleteConversation: async (id: number) => {
- return await request.get({ url: '/promotion/kefu-conversation/delete?id' + id })
+ return await request.delete({ url: '/promotion/kefu-conversation/delete?id=' + id })
+ },
+ // 转接会话给其它客服
+ transferConversion: async (id: number, kefuId: number) => {
+ return await request.get({ url: `/promotion/kefu-conversation/transfer/${id}/${kefuId}`})
}
}
diff --git a/yudao-admin-vue3/src/api/mall/promotion/supportstaff/index.ts b/yudao-admin-vue3/src/api/mall/promotion/supportstaff/index.ts
index b8e7424..99dce9c 100644
--- a/yudao-admin-vue3/src/api/mall/promotion/supportstaff/index.ts
+++ b/yudao-admin-vue3/src/api/mall/promotion/supportstaff/index.ts
@@ -25,6 +25,7 @@ export const SupportStaffApi = {
return await request.get({ url: `/promotion/support-staff/get?id=` + id })
},
+
// 新增客服人员
createSupportStaff: async (data: SupportStaffVO) => {
return await request.post({ url: `/promotion/support-staff/create`, data })
diff --git a/yudao-admin-vue3/src/utils/auth.ts b/yudao-admin-vue3/src/utils/auth.ts
index c68a67a..18f48ff 100644
--- a/yudao-admin-vue3/src/utils/auth.ts
+++ b/yudao-admin-vue3/src/utils/auth.ts
@@ -6,6 +6,7 @@ const { wsCache } = useCache()
const AccessTokenKey = 'ACCESS_TOKEN'
const RefreshTokenKey = 'REFRESH_TOKEN'
+const StaffTokenKey = 'STAFF_TOKEN'
// 获取token
export const getAccessToken = () => {
@@ -30,6 +31,16 @@ export const removeToken = () => {
wsCache.delete(RefreshTokenKey)
}
+// 获取客服id TODO:后面可以完善为token
+export const getStaffToken = () => {
+ // 此处与TokenKey相同,此写法解决初始化时Cookies中不存在TokenKey报错
+ return wsCache.get(StaffTokenKey) ? wsCache.get(StaffTokenKey) : wsCache.get('STAFF_TOKEN')
+}
+// 设置客服id TODO:后面可以完善为token
+export const setStaffToken = (id: number) => {
+ wsCache.set(StaffTokenKey, id)
+}
+
/** 格式化token(jwt格式) */
export const formatToken = (token: string): string => {
return 'Bearer ' + token
diff --git a/yudao-admin-vue3/src/views/mall/promotion/kefu/components/KeFuConversationList.vue b/yudao-admin-vue3/src/views/mall/promotion/kefu/components/KeFuConversationList.vue
index 6e01123..079c415 100644
--- a/yudao-admin-vue3/src/views/mall/promotion/kefu/components/KeFuConversationList.vue
+++ b/yudao-admin-vue3/src/views/mall/promotion/kefu/components/KeFuConversationList.vue
@@ -74,6 +74,7 @@ import { useEmoji } from './tools/emoji'
import { formatPast } from '@/utils/formatTime'
import { KeFuMessageContentTypeEnum } from './tools/constants'
import { useAppStore } from '@/store/modules/app'
+import { getStaffToken} from '@/utils/auth'
defineOptions({ name: 'KeFuConversationList' })
@@ -86,7 +87,7 @@ const collapse = computed(() => appStore.getCollapse) // 折叠菜单
/** 加载会话列表 */
const getConversationList = async () => {
- const list = await KeFuConversationApi.getConversationList()
+ const list = await KeFuConversationApi.getConversationListByKefuId(getStaffToken())
list.sort((a: KeFuConversationRespVO, _) => (a.adminPinned ? -1 : 1))
conversationList.value = list
}
@@ -206,8 +207,9 @@ watch(showRightMenu, (val) => {
}
.active {
- border-left: 5px #3271ff solid;
- background-color: var(--login-bg-color);
+ border-left: 5px #0256ff solid;
+ // background-color: var(--login-bg-color);
+ background-color: #409eff;
}
.pinned {
diff --git a/yudao-admin-vue3/src/views/mall/promotion/kefu/components/KeFuMessageList.vue b/yudao-admin-vue3/src/views/mall/promotion/kefu/components/KeFuMessageList.vue
index 0a59b0f..7d22683 100644
--- a/yudao-admin-vue3/src/views/mall/promotion/kefu/components/KeFuMessageList.vue
+++ b/yudao-admin-vue3/src/views/mall/promotion/kefu/components/KeFuMessageList.vue
@@ -10,7 +10,8 @@
-
{{ formatDate(item.createTime) }}
@@ -20,16 +21,19 @@
{{ item.content }}
-
-
-
@@ -40,13 +44,15 @@
-
-
-
-
-
有新消息
@@ -81,6 +90,19 @@
+
+
+
+
+
+
+
+ {{ staff.name }}
+
+
+
+
+
@@ -119,6 +141,7 @@
import PictureSelectUpload from './tools/PictureSelectUpload.vue'
// import VerbalTrick from './tools/VerbalTrick.vue'
import Picture from '@/views/mall/promotion/kefu/components/asserts/huashu.png'
+ import Picture2 from '@/views/mall/promotion/kefu/components/asserts/zhuanjie.png'
import ProductItem from './message/ProductItem.vue'
import OrderItem from './message/OrderItem.vue'
import { Emoji, useEmoji } from './tools/emoji'
@@ -130,6 +153,9 @@
import relativeTime from 'dayjs/plugin/relativeTime'
import { debounce } from 'lodash-es'
import { jsonParse } from '@/utils'
+ import { getStaffToken, setStaffToken} from '@/utils/auth'
+ import type { DropdownInstance } from 'element-plus'
+ import { ref } from 'vue'
dayjs.extend(relativeTime)
@@ -145,6 +171,10 @@
const messageList = ref([]) // 消息列表
const conversation = ref({} as KeFuConversationRespVO) // 用户会话
const showNewMessageTip = ref(false) // 显示有新消息提示
+ import { SupportStaffApi, SupportStaffVO } from '@/api/mall/promotion/supportstaff' // 客服列表接口
+ import { KeFuConversationApi} from '@/api/mall/promotion/kefu/conversation'
+ import { number } from 'vue-types'
+ const onlineStaffList = ref([]) // 在线客服列表的数据
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
@@ -152,6 +182,7 @@
})
const total = ref(0) // 消息总条数
const refreshContent = ref(false) // 内容刷新,主要解决会话消息页面高度不一致导致的滚动功能精度失效
+ const dropdown1 = ref()
/** 获悉消息内容 */
const getMessageContent = computed(() => (item : any) => jsonParse(item.content))
@@ -360,8 +391,29 @@
}
return false
})
+ /** 查询在线客服人员列表 */
+ const getOnlineStaffList = async () => {
+ if (!dropdown1.value) return
+ dropdown1.value.handleOpen()
+ try {
+ const data = await SupportStaffApi.getSupportStaffPage({
+ pageNo: 1,
+ pageSize: 10,
+ status: 1,
+ })
+ onlineStaffList.value = data.list
+ } finally {
+ }
+ }
-
+ /** 转接客服人员列表 id:会话id kefuId:客服人员id */
+ const transferConversion = async (kefuId: number) => {
+ try {
+ await KeFuConversationApi.transferConversion(queryParams.conversationId, kefuId)
+ } finally {
+ // todo 刷新会话列表
+ }
+ }
/** 初始化 **/
onMounted(() => {
getVerbalTrickList()
diff --git a/yudao-admin-vue3/src/views/mall/promotion/kefu/components/asserts/zhuanjie.png b/yudao-admin-vue3/src/views/mall/promotion/kefu/components/asserts/zhuanjie.png
new file mode 100644
index 0000000..97e866a
Binary files /dev/null and b/yudao-admin-vue3/src/views/mall/promotion/kefu/components/asserts/zhuanjie.png differ
diff --git a/yudao-admin-vue3/src/views/mall/promotion/kefu/components/message/ProductItem.vue b/yudao-admin-vue3/src/views/mall/promotion/kefu/components/message/ProductItem.vue
index 4e59365..1e30ca3 100644
--- a/yudao-admin-vue3/src/views/mall/promotion/kefu/components/message/ProductItem.vue
+++ b/yudao-admin-vue3/src/views/mall/promotion/kefu/components/message/ProductItem.vue
@@ -153,9 +153,16 @@ const openDetail = (spuId: number) => {
}
.title-text {
- font-size: 13px;
+ font-size: 12px;
font-weight: 500;
line-height: 20px;
+ display: -webkit-box; /* 对于支持的浏览器 */
+ -webkit-box-orient: vertical; /* 垂直排列 */
+ overflow: hidden; /* 隐藏超出部分 */
+ -webkit-line-clamp: 2; /* 限制为2行 */
+ text-overflow: ellipsis; /* 超出部分显示省略号 */
+ line-height: 1.5; /* 行高,可以根据需要调整 */
+ max-height: 3rem; /* 根据行高和行数设置最大高度,2行 * 行高 */
}
.spec-text {
diff --git a/yudao-admin-vue3/src/views/mall/promotion/kefu/index.vue b/yudao-admin-vue3/src/views/mall/promotion/kefu/index.vue
index 325bd32..bf94e40 100644
--- a/yudao-admin-vue3/src/views/mall/promotion/kefu/index.vue
+++ b/yudao-admin-vue3/src/views/mall/promotion/kefu/index.vue
@@ -1,19 +1,19 @@
-
+
-
+
-
+
-
+
@@ -26,7 +26,7 @@
import { KeFuConversationList, KeFuMessageList, MemberBrowsingHistory } from './components'
import { WebSocketMessageTypeConstants } from './components/tools/constants'
import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
- import { getRefreshToken } from '@/utils/auth'
+ import { getRefreshToken, getAccessToken } from '@/utils/auth'
import { useWebSocket } from '@vueuse/core'
defineOptions({ name: 'KeFu' })
@@ -42,7 +42,7 @@
/** 发起 WebSocket 连接 */
const { data, close, open } = useWebSocket(server.value, {
- autoReconnect: true,
+ autoReconnect: false,
heartbeat: true
})
diff --git a/yudao-admin-vue3/src/views/mall/promotion/supportstaff/index.vue b/yudao-admin-vue3/src/views/mall/promotion/supportstaff/index.vue
index ebb7a82..ca1bf85 100644
--- a/yudao-admin-vue3/src/views/mall/promotion/supportstaff/index.vue
+++ b/yudao-admin-vue3/src/views/mall/promotion/supportstaff/index.vue
@@ -166,7 +166,7 @@
:formatter="dateFormatter"
width="180px"
/>
-
+
删除
+
+ 进入工作台
+
@@ -206,7 +214,7 @@ import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { SupportStaffApi, SupportStaffVO } from '@/api/mall/promotion/supportstaff'
import SupportStaffForm from './SupportStaffForm.vue'
-
+import { setStaffToken} from '@/utils/auth'
/** 客服人员 列表 */
defineOptions({ name: 'SupportStaff' })
@@ -275,7 +283,11 @@ const handleDelete = async (id: number) => {
await getList()
} catch {}
}
-
+/** 客服进入工作台 */
+const handleEnterConsole = async (id: number) => {
+ setStaffToken(id);
+ window.open(`${window.location.origin}/kefu/kefu`, '_blank');
+}
/** 导出按钮操作 */
const handleExport = async () => {
try {
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/KeFuConversationController.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/KeFuConversationController.java
index 2190a66..74ca43f 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/KeFuConversationController.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/KeFuConversationController.java
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationRespVO;
import cn.iocoder.yudao.module.promotion.controller.admin.kefu.vo.conversation.KeFuConversationUpdatePinnedReqVO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.kefu.KeFuConversationDO;
import cn.iocoder.yudao.module.promotion.service.kefu.KeFuConversationService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -16,6 +17,7 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -54,16 +56,37 @@ public class KeFuConversationController {
@GetMapping("/list")
@Operation(summary = "获得客服会话列表")
@PreAuthorize("@ss.hasPermission('promotion:kefu-conversation:query')")
- public CommonResult> getConversationList() {
- // 查询会话列表
- List respList = BeanUtils.toBean(conversationService.getKefuConversationList(),
- KeFuConversationRespVO.class);
+ public CommonResult> getConversationList(@RequestParam(required = false, value = "kefuId") Long kefuId) {
+ // 如果会话列表中的kefuId为null,则转接给当前kefuId(处理小程序新会话)
+ if (kefuId != null) {
+ List updateList = new ArrayList<>();
+ List allConversationList = BeanUtils.toBean(conversationService.getKefuConversationList(),
+ KeFuConversationRespVO.class);
+ for (KeFuConversationRespVO keFuConversationRespVO : allConversationList) {
+ if (keFuConversationRespVO.getKefuId() == null) {
+ keFuConversationRespVO.setKefuId(kefuId);
+ updateList.add(BeanUtils.toBean(keFuConversationRespVO, KeFuConversationDO.class));
+ }
+ }
+ if (!updateList.isEmpty()) {
+ conversationService.updateConversation(updateList);
+ }
+ }
+ // 查询会话列表
+ List respList = BeanUtils.toBean(conversationService.getKefuConversationList(kefuId),
+ KeFuConversationRespVO.class);
// 拼接数据
Map userMap = memberUserApi.getUserMap(convertSet(respList, KeFuConversationRespVO::getUserId));
- respList.forEach(item-> findAndThen(userMap, item.getUserId(),
- memberUser-> item.setUserAvatar(memberUser.getAvatar()).setUserNickname(memberUser.getNickname())));
+ respList.forEach(item -> findAndThen(userMap, item.getUserId(),
+ memberUser -> item.setUserAvatar(memberUser.getAvatar()).setUserNickname(memberUser.getNickname())));
return success(respList);
}
+ @GetMapping("/transfer/{id}/{kefuId}")
+ public CommonResult getSupportStaff(@PathVariable("id") Long id, @PathVariable("kefuId") Long kefuId) {
+ conversationService.transferConversation(id, kefuId);
+ // 处理逻辑
+ return success(true);
+ }
}
\ No newline at end of file
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/vo/conversation/KeFuConversationRespVO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/vo/conversation/KeFuConversationRespVO.java
index 98cd5ac..0a9a624 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/vo/conversation/KeFuConversationRespVO.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/kefu/vo/conversation/KeFuConversationRespVO.java
@@ -43,4 +43,7 @@ public class KeFuConversationRespVO {
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
+ @Schema(description = "所属客服编号")
+ private Long kefuId;
+
}
\ No newline at end of file
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/kefu/KeFuConversationDO.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/kefu/KeFuConversationDO.java
index 482b6cd..ad4fbc5 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/kefu/KeFuConversationDO.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/kefu/KeFuConversationDO.java
@@ -80,4 +80,9 @@ public class KeFuConversationDO extends BaseDO {
*/
private Integer adminUnreadMessageCount;
+ /**
+ * 所属客服编号
+ */
+ private Long kefuId;
+
}
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/kefu/KeFuConversationMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/kefu/KeFuConversationMapper.java
index 40efa44..6178cb2 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/kefu/KeFuConversationMapper.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/kefu/KeFuConversationMapper.java
@@ -21,6 +21,12 @@ public interface KeFuConversationMapper extends BaseMapperX
.eq(KeFuConversationDO::getAdminDeleted, Boolean.FALSE)
.orderByDesc(KeFuConversationDO::getCreateTime));
}
+ default List selectConversationList(Long kefuId) {
+ return selectList(new LambdaQueryWrapperX()
+ .eq(KeFuConversationDO::getAdminDeleted, Boolean.FALSE)
+ .eqIfPresent(KeFuConversationDO::getKefuId, kefuId)
+ .orderByDesc(KeFuConversationDO::getCreateTime));
+ }
default void updateAdminUnreadMessageCountIncrement(Long id) {
update(new LambdaUpdateWrapper()
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuConversationService.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuConversationService.java
index 2da8d0b..9fd313d 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuConversationService.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuConversationService.java
@@ -50,11 +50,18 @@ public interface KeFuConversationService {
void updateConversationAdminDeleted(Long id, Boolean adminDeleted);
/**
- * 【管理员】获得客服会话列表
+ * 【管理员】获得全部客服会话列表
*
* @return 会话列表
*/
List getKefuConversationList();
+ /**
+ * 【管理员】获得客服会话列表
+ *
+ * @param kefuId 客服编号
+ * @return 会话列表
+ */
+ List getKefuConversationList(Long kefuId);
/**
* 【会员】获得或创建会话
@@ -82,4 +89,18 @@ public interface KeFuConversationService {
*/
KeFuConversationDO getConversationByUserId(Long userId);
+ /**
+ * 更新客服列表
+ * @param updateList
+ * @return void
+ */
+ void updateConversation(List updateList);
+
+ /**
+ * 转接会话
+ * @param id 会话id
+ * @param kefuId 客服id
+ * @return void
+ */
+ void transferConversation(Long id, Long kefuId);
}
\ No newline at end of file
diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuConversationServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuConversationServiceImpl.java
index ee67a57..b53e08d 100644
--- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuConversationServiceImpl.java
+++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/kefu/KeFuConversationServiceImpl.java
@@ -87,6 +87,11 @@ public class KeFuConversationServiceImpl implements KeFuConversationService {
return conversationMapper.selectConversationList();
}
+ @Override
+ public List getKefuConversationList(Long kefuId) {
+ return conversationMapper.selectConversationList(kefuId);
+ }
+
@Override
public KeFuConversationDO getOrCreateConversation(Long userId) {
KeFuConversationDO conversation = conversationMapper.selectOne(KeFuConversationDO::getUserId, userId);
@@ -115,4 +120,17 @@ public class KeFuConversationServiceImpl implements KeFuConversationService {
return conversationMapper.selectByUserId(userId);
}
+ @Override
+ public void updateConversation(List updateList) {
+ conversationMapper.updateBatch(updateList);
+ }
+
+ @Override
+ public void transferConversation(Long id, Long kefuId) {
+ KeFuConversationDO keFuConversationDO = new KeFuConversationDO();
+ keFuConversationDO.setId(id);
+ keFuConversationDO.setKefuId(kefuId);
+ conversationMapper.updateById(keFuConversationDO);
+ }
+
}
\ No newline at end of file