From b94f34c67dc77d1eefe174b48915b92f6f090aed Mon Sep 17 00:00:00 2001
From: jawe <532159638@qq.com>
Date: Mon, 3 Nov 2025 16:42:13 +0800
Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=E4=BD=BF=E7=94=A8tailwindcss?=
=?UTF-8?q?=E7=AE=80=E5=8C=96style,=E7=BB=9F=E4=B8=80=E6=96=87=E4=BB=B6?=
=?UTF-8?q?=E5=91=BD=E5=90=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
apps/web-antd/.env.development | 2 +-
.../src/components/card-title/CardTitle.vue | 36 --
.../src/components/card-title/index.ts | 1 -
.../promotion/components/diy-editor/index.vue | 2 +-
.../promotion/components/draggable/index.vue | 2 +-
.../kefu/components/KeFuMessageList.vue | 574 ------------------
.../mall/promotion/kefu/components/index.ts | 6 -
.../kefu/components/message/OrderItem.vue | 206 -------
.../src/views/mall/promotion/kefu/index.vue | 54 +-
.../conversation-list.vue} | 103 +---
.../mall/promotion/kefu/modules/index.ts | 3 +
.../member/member-info.vue} | 186 ++----
.../member/order-browsing-history.vue} | 11 +-
.../member/product-browsing-history.vue} | 4 +-
.../promotion/kefu/modules/message-list.vue | 425 +++++++++++++
.../message/message-item.vue} | 1 -
.../kefu/modules/message/order-item.vue | 130 ++++
.../message/product-item.vue} | 68 +--
.../tools/constants.ts | 0
.../tools/emoji-select-popover.vue} | 6 +-
.../{components => modules}/tools/emoji.ts | 0
.../tools/picture-select-upload.vue} | 25 +-
apps/web-antd/vite.config.mts | 2 +-
.../promotion/components/diy-editor/index.vue | 2 +-
packages/locales/src/langs/zh-CN/ui.json | 3 +-
25 files changed, 694 insertions(+), 1158 deletions(-)
delete mode 100644 apps/web-antd/src/components/card-title/CardTitle.vue
delete mode 100644 apps/web-antd/src/components/card-title/index.ts
delete mode 100644 apps/web-antd/src/views/mall/promotion/kefu/components/KeFuMessageList.vue
delete mode 100644 apps/web-antd/src/views/mall/promotion/kefu/components/index.ts
delete mode 100644 apps/web-antd/src/views/mall/promotion/kefu/components/message/OrderItem.vue
rename apps/web-antd/src/views/mall/promotion/kefu/{components/KeFuConversationList.vue => modules/conversation-list.vue} (73%)
create mode 100644 apps/web-antd/src/views/mall/promotion/kefu/modules/index.ts
rename apps/web-antd/src/views/mall/promotion/kefu/{components/member/MemberInfo.vue => modules/member/member-info.vue} (51%)
rename apps/web-antd/src/views/mall/promotion/kefu/{components/member/OrderBrowsingHistory.vue => modules/member/order-browsing-history.vue} (83%)
rename apps/web-antd/src/views/mall/promotion/kefu/{components/member/ProductBrowsingHistory.vue => modules/member/product-browsing-history.vue} (91%)
create mode 100644 apps/web-antd/src/views/mall/promotion/kefu/modules/message-list.vue
rename apps/web-antd/src/views/mall/promotion/kefu/{components/message/MessageItem.vue => modules/message/message-item.vue} (92%)
create mode 100644 apps/web-antd/src/views/mall/promotion/kefu/modules/message/order-item.vue
rename apps/web-antd/src/views/mall/promotion/kefu/{components/message/ProductItem.vue => modules/message/product-item.vue} (51%)
rename apps/web-antd/src/views/mall/promotion/kefu/{components => modules}/tools/constants.ts (100%)
rename apps/web-antd/src/views/mall/promotion/kefu/{components/tools/EmojiSelectPopover.vue => modules/tools/emoji-select-popover.vue} (91%)
rename apps/web-antd/src/views/mall/promotion/kefu/{components => modules}/tools/emoji.ts (100%)
rename apps/web-antd/src/views/mall/promotion/kefu/{components/tools/PictureSelectUpload.vue => modules/tools/picture-select-upload.vue} (76%)
diff --git a/apps/web-antd/.env.development b/apps/web-antd/.env.development
index cff255764..18433b6b5 100644
--- a/apps/web-antd/.env.development
+++ b/apps/web-antd/.env.development
@@ -4,7 +4,7 @@ VITE_PORT=5666
VITE_BASE=/
# 请求路径
-VITE_BASE_URL=http://127.0.0.1:48080
+VITE_BASE_URL=http://47.103.66.220:48080
# 接口地址
VITE_GLOB_API_URL=/admin-api
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
diff --git a/apps/web-antd/src/components/card-title/CardTitle.vue b/apps/web-antd/src/components/card-title/CardTitle.vue
deleted file mode 100644
index b59ed966c..000000000
--- a/apps/web-antd/src/components/card-title/CardTitle.vue
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
- {{ title }}
-
-
-
diff --git a/apps/web-antd/src/components/card-title/index.ts b/apps/web-antd/src/components/card-title/index.ts
deleted file mode 100644
index 4ebdb4494..000000000
--- a/apps/web-antd/src/components/card-title/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { default as CardTitle } from './CardTitle.vue';
diff --git a/apps/web-antd/src/views/mall/promotion/components/diy-editor/index.vue b/apps/web-antd/src/views/mall/promotion/components/diy-editor/index.vue
index 1ae7934ea..6180b67ba 100644
--- a/apps/web-antd/src/views/mall/promotion/components/diy-editor/index.vue
+++ b/apps/web-antd/src/views/mall/promotion/components/diy-editor/index.vue
@@ -556,7 +556,7 @@ $phone-width: 375px;
width: 100%;
margin: 16px 0 0;
overflow: hidden;
- background-color: var(--app-content-bg-color);
+ background-color: var(--background);
/* 手机顶部 */
.editor-design-top {
diff --git a/apps/web-antd/src/views/mall/promotion/components/draggable/index.vue b/apps/web-antd/src/views/mall/promotion/components/draggable/index.vue
index 8bb352329..75295662a 100644
--- a/apps/web-antd/src/views/mall/promotion/components/draggable/index.vue
+++ b/apps/web-antd/src/views/mall/promotion/components/draggable/index.vue
@@ -59,7 +59,7 @@ const handleDelete = function (index: number) {
-import type { UseScrollReturn } from '@vueuse/core';
-
-import type { Emoji } from './tools/emoji';
-
-import type { MallKefuConversationApi } from '#/api/mall/promotion/kefu/conversation';
-import type { MallKefuMessageApi } from '#/api/mall/promotion/kefu/message';
-
-import { computed, nextTick, reactive, ref, unref } from 'vue';
-
-import { UserTypeEnum } from '@vben/constants';
-import { IconifyIcon } from '@vben/icons';
-import { formatDate, isEmpty, jsonParse } from '@vben/utils';
-
-import { vScroll } from '@vueuse/components';
-import { useDebounceFn, useScroll } from '@vueuse/core';
-import {
- Avatar,
- Empty,
- Image,
- Layout,
- notification,
- Textarea,
-} from 'ant-design-vue';
-import dayjs from 'dayjs';
-import relativeTime from 'dayjs/plugin/relativeTime';
-
-import {
- getKeFuMessageList,
- sendKeFuMessage,
- updateKeFuMessageReadStatus,
-} from '#/api/mall/promotion/kefu/message';
-import { useMallKefuStore } from '#/store/mall/kefu';
-
-import MessageItem from './message/MessageItem.vue';
-import OrderItem from './message/OrderItem.vue';
-import ProductItem from './message/ProductItem.vue';
-import { KeFuMessageContentTypeEnum } from './tools/constants';
-import { useEmoji } from './tools/emoji';
-import EmojiSelectPopover from './tools/EmojiSelectPopover.vue';
-import PictureSelectUpload from './tools/PictureSelectUpload.vue';
-
-defineOptions({ name: 'KeFuMessageList' });
-
-dayjs.extend(relativeTime);
-
-const message = ref(''); // 消息弹窗
-const { replaceEmoji } = useEmoji();
-const messageList = ref([]); // 消息列表
-const conversation = ref(
- {} as MallKefuConversationApi.Conversation,
-); // 用户会话
-const showNewMessageTip = ref(false); // 显示有新消息提示
-const queryParams = reactive({
- conversationId: 0,
- createTime: undefined,
-});
-const total = ref(0); // 消息总条数
-const refreshContent = ref(false); // 内容刷新,主要解决会话消息页面高度不一致导致的滚动功能精度失效
-const kefuStore = useMallKefuStore(); // 客服缓存
-
-/** 获悉消息内容 */
-const getMessageContent = computed(
- () => (item: any) => jsonParse(item.content),
-);
-
-/** 获得消息列表 */
-// TODO @jave:idea 的 linter 报错,处理下;
-async function getMessageList() {
- const res: any = await getKeFuMessageList(queryParams as any);
- if (isEmpty(res)) {
- // 当返回的是空列表说明没有消息或者已经查询完了历史消息
- skipGetMessageList.value = true;
- return;
- }
- queryParams.createTime = formatDate((res as any).at(-1).createTime) as any;
-
- // 情况一:加载最新消息
- if (queryParams.createTime) {
- // 情况二:加载历史消息
- for (const item of res as any) {
- pushMessage(item);
- }
- } else {
- messageList.value = res;
- }
- refreshContent.value = true;
-}
-
-/** 添加消息 */
-function pushMessage(message: any) {
- if (messageList.value.some((val) => val.id === message.id)) {
- return;
- }
- messageList.value.push(message);
-}
-
-/** 按照时间倒序,获取消息列表 */
-const getMessageList0 = computed(() => {
- // 使用展开运算符创建新数组,避免直接修改原数组
- return [...messageList.value].sort(
- (a: any, b: any) => a.createTime - b.createTime,
- );
-});
-
-/** 刷新消息列表 */
-async function refreshMessageList(message?: any) {
- if (!conversation.value) {
- return;
- }
-
- if (message === undefined) {
- queryParams.createTime = undefined;
- await getMessageList();
- } else {
- // 当前查询会话与消息所属会话不一致则不做处理
- if (message.conversationId !== conversation.value.id) {
- return;
- }
- pushMessage(message);
- }
-
- if (loadHistory.value) {
- // 右下角显示有新消息提示
- showNewMessageTip.value = true;
- } else {
- // 滚动到最新消息处
- await handleToNewMessage();
- }
-}
-
-/** 获得新会话的消息列表, 点击切换时,读取缓存;然后异步获取新消息,merge 下; */
-async function getNewMessageList(val: MallKefuMessageApi.Message) {
- // 1. 缓存当前会话消息列表
- kefuStore.saveMessageList(conversation.value.id, messageList.value);
- // 2.1 会话切换,重置相关参数
- messageList.value = kefuStore.getConversationMessageList(val.id) || [];
- total.value = messageList.value.length || 0;
- loadHistory.value = false;
- refreshContent.value = false;
- skipGetMessageList.value = false;
- // 2.2 设置会话相关属性
- conversation.value = val;
- queryParams.conversationId = val.id;
- queryParams.createTime = undefined;
- // 3. 获取消息
- await refreshMessageList();
-}
-defineExpose({ getNewMessageList, refreshMessageList });
-
-/** 是否显示聊天区域 */
-function showKeFuMessageList() {
- return !isEmpty(conversation.value);
-}
-
-const skipGetMessageList = ref(false); // 跳过消息获取
-
-/** 处理表情选择 */
-function handleEmojiSelect(item: Emoji) {
- message.value += item.name;
-}
-
-/** 处理图片发送 */
-async function handleSendPicture(picUrl: string) {
- // 组织发送消息
- const msg = {
- conversationId: conversation.value.id,
- contentType: KeFuMessageContentTypeEnum.IMAGE,
- content: JSON.stringify({ picUrl }),
- };
- await sendMessage(msg);
-}
-
-/** 发送文本消息 */
-async function handleSendMessage(event: any) {
- // shift 不发送
- if (event.shiftKey) {
- return;
- }
- // 1. 校验消息是否为空
- if (isEmpty(unref(message.value)?.trim())) {
- notification.warning({ message: '请输入消息后再发送哦!' });
- message.value = '';
- return;
- }
- // 2. 组织发送消息
- const msg = {
- conversationId: conversation.value.id,
- contentType: KeFuMessageContentTypeEnum.TEXT,
- content: JSON.stringify({ text: message.value }),
- };
- await sendMessage(msg);
-}
-
-/** 真正发送消息 【共用】*/
-async function sendMessage(msg: MallKefuMessageApi.MessageSend) {
- // 发送消息
- await sendKeFuMessage(msg);
- message.value = '';
- // 加载消息列表
- await refreshMessageList();
- // 更新会话缓存
- await kefuStore.updateConversation(conversation.value.id);
-}
-
-/** 滚动到底部 */
-const innerRef = ref();
-const scrollbarRef = ref(null);
-const { y } = useScroll(scrollbarRef);
-
-async function scrollToBottom() {
- if (!scrollbarRef.value) return;
- // 1. 首次加载时滚动到最新消息,如果加载的是历史消息则不滚动
- if (loadHistory.value) {
- return;
- }
- // 2.1 滚动到最新消息,关闭新消息提示
- await nextTick();
- // 使用 useScroll 监听滚动容器
-
- y.value = scrollbarRef.value.scrollHeight - innerRef.value!.clientHeight;
- // scrollbarRef.value!.setScrollTop(innerRef.value!.clientHeight)
- showNewMessageTip.value = false;
- // 2.2 消息已读
- await updateKeFuMessageReadStatus(conversation.value.id);
-}
-
-/** 查看新消息 */
-async function handleToNewMessage() {
- loadHistory.value = false;
- await scrollToBottom();
-}
-
-const loadHistory = ref(false); // 加载历史消息
-/** 处理消息列表滚动事件(debounce 限流) */
-const handleScroll = useDebounceFn((state: UseScrollReturn) => {
- const { arrivedState } = state;
- if (skipGetMessageList.value) {
- return;
- }
- // 滚动到底部了
- if (arrivedState.bottom) {
- loadHistory.value = false;
- refreshMessageList();
- }
-
- // 触顶自动加载下一页数据
- if (arrivedState.top) {
- handleOldMessage();
- }
-}, 200);
-/** 加载历史消息 */
-async function handleOldMessage() {
- // 记录已有页面高度
- const oldPageHeight = innerRef.value?.clientHeight;
- if (!oldPageHeight) {
- return;
- }
- loadHistory.value = true;
- await getMessageList();
- // 等页面加载完后,获得上一页最后一条消息的位置,控制滚动到它所在位置
- // scrollbarRef.value!.setScrollTop(innerRef.value!.clientHeight - oldPageHeight)
- y.value =
- scrollbarRef.value.scrollHeight -
- innerRef.value!.clientHeight -
- oldPageHeight;
-}
-
-/** 是否显示时间 */
-function showTime(item: MallKefuMessageApi.Message, index: number) {
- if (unref(messageList.value)[index + 1]) {
- const dateString = dayjs(
- unref(messageList.value)[index + 1].createTime,
- ).fromNow();
- return dateString !== dayjs(unref(item).createTime).fromNow();
- }
- return false;
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ formatDate(item.createTime) }}
-
-
-
- {{ item.content }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 有新消息
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apps/web-antd/src/views/mall/promotion/kefu/components/index.ts b/apps/web-antd/src/views/mall/promotion/kefu/components/index.ts
deleted file mode 100644
index 90ddd0df4..000000000
--- a/apps/web-antd/src/views/mall/promotion/kefu/components/index.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-export { default as KeFuConversationList } from './KeFuConversationList.vue';
-export { default as KeFuMessageList } from './KeFuMessageList.vue';
-export { default as MemberInfo } from './member/MemberInfo.vue';
-
-// TODO @jawe:components =》modules;在 vben 里,modules 是给自己用的,把一个大 vue 拆成 n 个小 vue;components 是给别的模块使用的;
-// TODO @jawe:1)组件名小写,类似 conversation-list.vue;2)KeFu 开头可以去掉,因为已经是当前模块下,不用重复拼写;
diff --git a/apps/web-antd/src/views/mall/promotion/kefu/components/message/OrderItem.vue b/apps/web-antd/src/views/mall/promotion/kefu/components/message/OrderItem.vue
deleted file mode 100644
index c41885b83..000000000
--- a/apps/web-antd/src/views/mall/promotion/kefu/components/message/OrderItem.vue
+++ /dev/null
@@ -1,206 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- 共 {{ getMessageContent?.productCount }} 件商品,总金额:
-
-
- ¥{{ fenToYuan(getMessageContent?.payPrice) }}
-
-
-
-
-
-
-
-
diff --git a/apps/web-antd/src/views/mall/promotion/kefu/index.vue b/apps/web-antd/src/views/mall/promotion/kefu/index.vue
index c45defe83..fb385a150 100644
--- a/apps/web-antd/src/views/mall/promotion/kefu/index.vue
+++ b/apps/web-antd/src/views/mall/promotion/kefu/index.vue
@@ -1,7 +1,7 @@
-
-
-
+
+
-
+
-
+
-
-
diff --git a/apps/web-antd/src/views/mall/promotion/kefu/components/KeFuConversationList.vue b/apps/web-antd/src/views/mall/promotion/kefu/modules/conversation-list.vue
similarity index 73%
rename from apps/web-antd/src/views/mall/promotion/kefu/components/KeFuConversationList.vue
rename to apps/web-antd/src/views/mall/promotion/kefu/modules/conversation-list.vue
index 16d8f349a..c176567d6 100644
--- a/apps/web-antd/src/views/mall/promotion/kefu/components/KeFuConversationList.vue
+++ b/apps/web-antd/src/views/mall/promotion/kefu/modules/conversation-list.vue
@@ -18,8 +18,6 @@ import { useMallKefuStore } from '#/store/mall/kefu';
import { KeFuMessageContentTypeEnum } from './tools/constants';
import { useEmoji } from './tools/emoji';
-defineOptions({ name: 'KeFuConversationList' });
-
/** 打开右侧的消息列表 */
const emits = defineEmits<{
(e: 'change', v: MallKefuConversationApi.Conversation): void;
@@ -160,23 +158,22 @@ onBeforeUnmount(() => {
-
-
+
+
会话记录({{ kefuStore.getConversationList.length }})
-
-
+
+
{
-
-
{{ item.userNickname || 'null' }}
-
+
+ {{
+ item.userNickname || 'null'
+ }}
+
{{ lastMessageTimeMap.get(item.id) ?? '计算中' }}
@@ -201,17 +200,21 @@ onBeforeUnmount(() => {
item.lastMessageContent,
)
"
- class="last-message color-[#999] !flex items-center"
+ class="line-clamp-1 flex items-center text-[13px] text-[#999]"
>
-