From 97fc9959bb5f50a1896ca1a4bc87ea01996ff2d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Fri, 28 Nov 2025 23:00:40 +0800 Subject: [PATCH] Refactor cache clearing functionality in settings page to improve user feedback and error handling. Update WeChat API functions to support debounce options for contact and group list retrieval. Enhance message rendering logic to include red packet messages and improve user identification in chat records. --- .../src/pages/mobile/mine/setting/index.tsx | 39 ++++- Cunkebao/src/utils/cacheCleaner.ts | 70 ++++++++ Moncter/src/utils/cacheCleaner.ts | 73 ++++++++ Touchkebao/src/pages/pc/ckbox/weChat/api.ts | 24 ++- .../RedPacketMessage.module.scss | 160 ++++++++++++++++++ .../components/RedPacketMessage/index.tsx | 62 +++++++ .../components/MessageRecord/index.tsx | 31 +++- .../SidebarMenu/WechatFriends/extend.ts | 4 +- Touchkebao/src/utils/dbAction/message.ts | 4 +- 9 files changed, 442 insertions(+), 25 deletions(-) create mode 100644 Cunkebao/src/utils/cacheCleaner.ts create mode 100644 Moncter/src/utils/cacheCleaner.ts create mode 100644 Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/RedPacketMessage/RedPacketMessage.module.scss create mode 100644 Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/RedPacketMessage/index.tsx diff --git a/Cunkebao/src/pages/mobile/mine/setting/index.tsx b/Cunkebao/src/pages/mobile/mine/setting/index.tsx index 1ddcf226..c002543f 100644 --- a/Cunkebao/src/pages/mobile/mine/setting/index.tsx +++ b/Cunkebao/src/pages/mobile/mine/setting/index.tsx @@ -8,7 +8,6 @@ import { LogoutOutlined, SettingOutlined, LockOutlined, - ReloadOutlined, } from "@ant-design/icons"; import Layout from "@/components/Layout/Layout"; import { useUserStore } from "@/store/module/user"; @@ -16,7 +15,7 @@ import { useSettingsStore } from "@/store/module/settings"; import style from "./index.module.scss"; import NavCommon from "@/components/NavCommon"; import { sendMessageToParent, TYPE_EMUE } from "@/utils/postApp"; -import { updateChecker } from "@/utils/updateChecker"; +import { clearApplicationCache } from "@/utils/cacheCleaner"; interface SettingItem { id: string; @@ -58,13 +57,35 @@ const Setting: React.FC = () => { const handleClearCache = () => { Dialog.confirm({ content: "确定要清除缓存吗?这将清除所有本地数据。", - onConfirm: () => { - sendMessageToParent( - { - action: "clearCache", - }, - TYPE_EMUE.FUNCTION, - ); + onConfirm: async () => { + const handler = Toast.show({ + icon: "loading", + content: "正在清理缓存...", + duration: 0, + }); + try { + await clearApplicationCache(); + sendMessageToParent( + { + action: "clearCache", + }, + TYPE_EMUE.FUNCTION, + ); + handler.close(); + Toast.show({ + icon: "success", + content: "缓存清理完成", + position: "top", + }); + } catch (error) { + console.error("clear cache failed", error); + handler.close(); + Toast.show({ + icon: "fail", + content: "缓存清理失败,请稍后再试", + position: "top", + }); + } }, }); }; diff --git a/Cunkebao/src/utils/cacheCleaner.ts b/Cunkebao/src/utils/cacheCleaner.ts new file mode 100644 index 00000000..3be09fbd --- /dev/null +++ b/Cunkebao/src/utils/cacheCleaner.ts @@ -0,0 +1,70 @@ +// 全局缓存清理工具:浏览器存储 + IndexedDB + Zustand store +import { clearAllPersistedData } from "@/store"; +import { useUserStore } from "@/store/module/user"; +import { useAppStore } from "@/store/module/app"; +import { useSettingsStore } from "@/store/module/settings"; + +const isBrowser = typeof window !== "undefined"; + +const safeStorageClear = (storage?: Storage) => { + if (!storage) return; + try { + storage.clear(); + } catch (error) { + console.warn("清理存储失败:", error); + } +}; + +export const clearBrowserStorage = () => { + if (!isBrowser) return; + safeStorageClear(window.localStorage); + safeStorageClear(window.sessionStorage); + try { + clearAllPersistedData(); + } catch (error) { + console.warn("清理持久化 store 失败:", error); + } +}; + +export const clearAllIndexedDB = async (): Promise => { + if (!isBrowser || !window.indexedDB || !indexedDB.databases) return; + + const databases = await indexedDB.databases(); + const deleteJobs = databases + .map(db => db.name) + .filter((name): name is string => Boolean(name)) + .map( + name => + new Promise((resolve, reject) => { + const request = indexedDB.deleteDatabase(name); + request.onsuccess = () => resolve(); + request.onerror = () => reject(new Error(`删除数据库 ${name} 失败`)); + request.onblocked = () => { + setTimeout(() => { + const retry = indexedDB.deleteDatabase(name); + retry.onsuccess = () => resolve(); + retry.onerror = () => + reject(new Error(`删除数据库 ${name} 失败`)); + }, 100); + }; + }), + ); + + await Promise.allSettled(deleteJobs); +}; + +export const resetAllStores = () => { + const userStore = useUserStore.getState(); + const appStore = useAppStore.getState(); + const settingsStore = useSettingsStore.getState(); + + userStore?.clearUser?.(); + appStore?.resetAppState?.(); + settingsStore?.resetSettings?.(); +}; + +export const clearApplicationCache = async () => { + clearBrowserStorage(); + await clearAllIndexedDB(); + resetAllStores(); +}; diff --git a/Moncter/src/utils/cacheCleaner.ts b/Moncter/src/utils/cacheCleaner.ts new file mode 100644 index 00000000..78f30c50 --- /dev/null +++ b/Moncter/src/utils/cacheCleaner.ts @@ -0,0 +1,73 @@ +// 缓存清理工具,统一处理浏览器存储与 Zustand store +import { clearAllPersistedData } from "@/store"; +import { useUserStore } from "@/store/module/user"; +import { useAppStore } from "@/store/module/app"; +import { useSettingsStore } from "@/store/module/settings"; + +const isBrowser = typeof window !== "undefined"; + +const safeStorageClear = (storage?: Storage) => { + if (!storage) return; + try { + storage.clear(); + } catch (error) { + console.warn("清理存储失败:", error); + } +}; + +export const clearBrowserStorage = () => { + if (!isBrowser) return; + safeStorageClear(window.localStorage); + safeStorageClear(window.sessionStorage); + // 清理自定义持久化数据 + try { + clearAllPersistedData(); + } catch (error) { + console.warn("清理持久化 store 失败:", error); + } +}; + +export const clearAllIndexedDB = async (): Promise => { + if (!isBrowser || !window.indexedDB || !indexedDB.databases) return; + + const databases = await indexedDB.databases(); + const deleteJobs = databases + .map(db => db.name) + .filter((name): name is string => Boolean(name)) + .map( + name => + new Promise((resolve, reject) => { + const request = indexedDB.deleteDatabase(name); + request.onsuccess = () => resolve(); + request.onerror = () => + reject(new Error(`删除数据库 ${name} 失败`)); + request.onblocked = () => { + setTimeout(() => { + const retry = indexedDB.deleteDatabase(name); + retry.onsuccess = () => resolve(); + retry.onerror = () => + reject(new Error(`删除数据库 ${name} 失败`)); + }, 100); + }; + }), + ); + + await Promise.allSettled(deleteJobs); +}; + +export const resetAllStores = () => { + const userStore = useUserStore.getState(); + const appStore = useAppStore.getState(); + const settingsStore = useSettingsStore.getState(); + + userStore?.clearUser?.(); + appStore?.resetAppState?.(); + settingsStore?.resetSettings?.(); +}; + +export const clearApplicationCache = async () => { + clearBrowserStorage(); + await clearAllIndexedDB(); + resetAllStores(); +}; + diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/api.ts b/Touchkebao/src/pages/pc/ckbox/weChat/api.ts index 32bb9604..7e77433e 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/api.ts +++ b/Touchkebao/src/pages/pc/ckbox/weChat/api.ts @@ -28,14 +28,30 @@ export function getTrafficPoolList() { "GET", ); } +type ListRequestOptions = { + debounceGap?: number; +}; + // 好友列表 -export function getContactList(params) { - return request("/v1/kefu/wechatFriend/list", params, "GET"); +export function getContactList(params, options?: ListRequestOptions) { + return request( + "/v1/kefu/wechatFriend/list", + params, + "GET", + undefined, + options?.debounceGap, + ); } // 群列表 -export function getGroupList(params) { - return request("/v1/kefu/wechatChatroom/list", params, "GET"); +export function getGroupList(params, options?: ListRequestOptions) { + return request( + "/v1/kefu/wechatChatroom/list", + params, + "GET", + undefined, + options?.debounceGap, + ); } // 分组列表 export function getLabelsListByGroup(params) { diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/RedPacketMessage/RedPacketMessage.module.scss b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/RedPacketMessage/RedPacketMessage.module.scss new file mode 100644 index 00000000..1d8603b0 --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/RedPacketMessage/RedPacketMessage.module.scss @@ -0,0 +1,160 @@ +// 红包消息样式 +.redPacketMessage { + background: transparent; + box-shadow: none; + max-width: 300px; +} + +.redPacketCard { + position: relative; + display: flex; + flex-direction: column; + padding: 16px 20px; + background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%); + border-radius: 8px; + cursor: pointer; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: 0 4px 12px rgba(255, 107, 107, 0.3); + overflow: hidden; + + // 红包装饰背景 + &::before { + content: ""; + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: radial-gradient( + circle, + rgba(255, 215, 0, 0.15) 0%, + transparent 70% + ); + animation: shimmer 3s ease-in-out infinite; + } + + // 金色装饰边框 + &::after { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border: 2px solid rgba(255, 215, 0, 0.4); + border-radius: 8px; + pointer-events: none; + } + + &:hover { + transform: translateY(-2px); + box-shadow: 0 6px 16px rgba(255, 107, 107, 0.4); + background: linear-gradient(135deg, #ff7b7b 0%, #ff6b7f 100%); + } + + &:active { + transform: translateY(0); + } +} + +@keyframes shimmer { + 0%, + 100% { + transform: rotate(0deg); + } + 50% { + transform: rotate(180deg); + } +} + +.redPacketHeader { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 12px; + position: relative; + z-index: 1; +} + +.redPacketIcon { + font-size: 32px; + line-height: 1; + filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2)); + animation: bounce 2s ease-in-out infinite; +} + +@keyframes bounce { + 0%, + 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-4px); + } +} + +.redPacketTitle { + flex: 1; + font-size: 16px; + font-weight: 600; + color: #ffffff; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); + letter-spacing: 0.5px; + line-height: 1.4; + word-break: break-word; +} + +.redPacketFooter { + display: flex; + align-items: center; + justify-content: flex-end; + position: relative; + z-index: 1; + padding-top: 8px; + border-top: 1px solid rgba(255, 255, 255, 0.3); +} + +.redPacketLabel { + font-size: 12px; + color: rgba(255, 255, 255, 0.9); + font-weight: 500; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + + &::before { + content: "💰"; + margin-right: 4px; + font-size: 14px; + } +} + +// 消息文本样式(用于错误提示) +.messageText { + line-height: 1.4; + white-space: pre-wrap; + word-break: break-word; + color: #8c8c8c; + font-size: 13px; +} + +// 响应式设计 +@media (max-width: 768px) { + .redPacketMessage { + max-width: 200px; + } + + .redPacketCard { + padding: 12px 16px; + } + + .redPacketIcon { + font-size: 28px; + } + + .redPacketTitle { + font-size: 14px; + } + + .redPacketLabel { + font-size: 11px; + } +} diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/RedPacketMessage/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/RedPacketMessage/index.tsx new file mode 100644 index 00000000..75bf86ff --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/components/RedPacketMessage/index.tsx @@ -0,0 +1,62 @@ +import React from "react"; +import styles from "./RedPacketMessage.module.scss"; + +interface RedPacketData { + nativeurl?: string; + paymsgid?: string; + sendertitle?: string; + [key: string]: any; +} + +interface RedPacketMessageProps { + content: string; +} + +const RedPacketMessage: React.FC = ({ content }) => { + const renderErrorMessage = (fallbackText: string) => ( +
{fallbackText}
+ ); + + if (typeof content !== "string" || !content.trim()) { + return renderErrorMessage("[红包消息 - 无效内容]"); + } + + try { + const trimmedContent = content.trim(); + const jsonData: RedPacketData = JSON.parse(trimmedContent); + + // 验证是否为红包消息 + const isRedPacket = + jsonData.nativeurl && + typeof jsonData.nativeurl === "string" && + jsonData.nativeurl.includes( + "wxpay://c2cbizmessagehandler/hongbao/receivehongbao", + ); + + if (!isRedPacket) { + return renderErrorMessage("[红包消息 - 格式错误]"); + } + + const title = jsonData.sendertitle || "恭喜发财,大吉大利"; + const paymsgid = jsonData.paymsgid || ""; + + return ( +
+
+
+
🧧
+
{title}
+
+
+ 微信红包 +
+
+
+ ); + } catch (e) { + console.warn("红包消息解析失败:", e); + return renderErrorMessage("[红包消息 - 解析失败]"); + } +}; + +export default RedPacketMessage; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/index.tsx index bd5813d0..ccd4759b 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/index.tsx @@ -7,6 +7,7 @@ import VideoMessage from "./components/VideoMessage"; import ClickMenu from "./components/ClickMeau"; import LocationMessage from "./components/LocationMessage"; import SystemRecommendRemarkMessage from "./components/SystemRecommendRemarkMessage/index"; +import RedPacketMessage from "./components/RedPacketMessage"; import { ChatRecord, ContractData, weChatGroup } from "@/pages/pc/ckbox/data"; import { formatWechatTime } from "@/utils/common"; import { getEmojiPath } from "@/components/EmojiSeclection/wechatEmoji"; @@ -254,6 +255,7 @@ const MessageRecord: React.FC = ({ contract }) => { msg?: ChatRecord, contract?: ContractData | weChatGroup, ) => { + console.log("红包"); if (isLegacyEmojiContent(trimmedContent)) { return renderEmojiContent(rawContent); } @@ -261,6 +263,17 @@ const MessageRecord: React.FC = ({ contract }) => { const jsonData = tryParseContentJson(trimmedContent); if (jsonData && typeof jsonData === "object") { + // 判断是否为红包消息 + if ( + jsonData.nativeurl && + typeof jsonData.nativeurl === "string" && + jsonData.nativeurl.includes( + "wxpay://c2cbizmessagehandler/hongbao/receivehongbao", + ) + ) { + return ; + } + if (jsonData.type === "file" && msg && contract) { return ( = ({ contract }) => { if (!msg) { return { avatar: "", nickname: "" }; } - const member = - groupRender.find(user => user?.identifier === msg?.sender?.wechatId) || - groupRender.find(user => user?.wechatId === msg?.sender?.wechatId); + + const member = groupRender.find( + user => user?.identifier === msg?.senderWechatId, + ); + console.log(member, "member"); return { - avatar: member?.avatar || msg?.sender?.avatar || "", - nickname: member?.nickname || msg?.sender?.nickname || "", + avatar: member?.avatar || msg?.avatar, + nickname: member?.nickname || msg?.senderNickname, }; }; @@ -615,7 +630,7 @@ const MessageRecord: React.FC = ({ contract }) => { const isOwn = msg?.isSend; const isGroup = !!contract.chatroomId; - const groupUser = isGroup ? renderGroupUser(msg) : null; + return (
= ({ contract }) => { )} } className={styles.messageAvatar} />
{!isOwn && (
- {groupUser?.nickname} + {renderGroupUser(msg)?.nickname}
)} <> diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/extend.ts b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/extend.ts index cf2b8117..fff567c7 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/extend.ts +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/extend.ts @@ -18,7 +18,7 @@ export const getAllFriends = async () => { let hasMore = true; while (hasMore) { - const result = await getContactList({ page, limit }); + const result = await getContactList({ page, limit }, { debounceGap: 0 }); const friendList = result?.list || []; if ( @@ -56,7 +56,7 @@ export const getAllGroups = async () => { let hasMore = true; while (hasMore) { - const result = await getGroupList({ page, limit }); + const result = await getGroupList({ page, limit }, { debounceGap: 0 }); const groupList = result?.list || []; if (!groupList || !Array.isArray(groupList) || groupList.length === 0) { diff --git a/Touchkebao/src/utils/dbAction/message.ts b/Touchkebao/src/utils/dbAction/message.ts index 0d8f71d3..53610a63 100644 --- a/Touchkebao/src/utils/dbAction/message.ts +++ b/Touchkebao/src/utils/dbAction/message.ts @@ -659,7 +659,7 @@ export class MessageManager { updatedSession.sortKey = this.generateSortKey(updatedSession); await chatSessionService.update(serverId, updatedSession); - console.log(`会话时间已更新: ${serverId} -> ${newTime}`); + await this.triggerCallbacks(userId); } } catch (error) { console.error("更新会话时间失败:", error); @@ -830,7 +830,7 @@ export class MessageManager { }; await chatSessionService.create(sessionWithSortKey); - console.log(`创建新会话: ${session.nickname || session.wechatId}`); + await this.triggerCallbacks(userId); } catch (error) { console.error("创建会话失败:", error); throw error;