From d5e040b1e64487f6a11a0dfbd0cb0c00298f804a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com>
Date: Wed, 15 Jul 2026 15:26:48 +0800
Subject: [PATCH] =?UTF-8?q?feat(shopOrder):=20=E5=AE=9E=E7=8E=B0=E7=94=B5?=
=?UTF-8?q?=E8=84=91=E7=89=88=E5=90=8E=E5=8F=B0=E5=85=A8=E5=B1=80=E6=96=B0?=
=?UTF-8?q?=E8=AE=A2=E5=8D=95=E7=BA=A2=E7=82=B9=E6=8F=90=E7=A4=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新建模块级全局单例 useOrderNotify,统一轮询检测新订单,避免多组件重复请求
- 轮询检测到新订单时触发叮声、语音播报,通知所有已注册回调刷新数据
- 订单管理菜单及 dashboard 快速入口显示红点提示新订单未查看
- 进入订单列表页时调用 clearBadge 同步最新订单ID并清除红点,标记订单已查看
- layout 全局启动新订单提醒,任意页面均生效
- menu-title 组件新增纯红点(dot)徽章显示支持
- dashboard 快速入口图标加红点显示及脉冲动画,提升视觉提示效果
- 维护新订单提醒回调集合,组件卸载自动移除回调避免内存泄漏
- 新订单提醒配置项和状态均为模块共享响应式,支持多组件无冲突使用
---
.workbuddy/memory/2026-07-15.md | 16 ++
src/layout/components/menu-title.vue | 4 +
src/layout/index.vue | 6 +
src/views/shop/dashboard/index.vue | 36 ++-
src/views/shop/shopOrder/index.vue | 14 +-
src/views/shop/shopOrder/useOrderNotify.ts | 296 ++++++++++++++-------
6 files changed, 266 insertions(+), 106 deletions(-)
diff --git a/.workbuddy/memory/2026-07-15.md b/.workbuddy/memory/2026-07-15.md
index d20368d..567b9aa 100644
--- a/.workbuddy/memory/2026-07-15.md
+++ b/.workbuddy/memory/2026-07-15.md
@@ -113,3 +113,19 @@
- 引入 `getCompressedImageUrl`(来自 `src/utils/image.ts`),将商品图片 `:src="record.goodsImage"` 改为 `:src="getCompressedImageUrl(record.goodsImage)"`。
- 与 `shopGoods`、`shopOrder`、`dashboard` 等页面保持一致:默认宽度 240px、质量 90、自动补全 OSS 域名、跳过已含 `x-oss-process` 的 URL。
- 预览行为未改(仍走 a-image 默认 src 预览)。
+
+## 电脑版后台新订单红点提示(全局)
+
+- 需求:电脑版后台有新订单时显示红点,让管理员知道有新订单。
+- 关键发现:系统已有菜单红点基础设施(`menu-title.vue` 读 `item.meta.badge` 渲染徽章;`user.ts` `setMenuBadge(path,value,color)`;`layout/index.vue` 有 `.ele-menu-badge` 样式),但 `useOrderNotify` 原为组件级(dashboard/cms dashboard/shopOrder 三处各自独立轮询),未接通红点。
+- 改造 `src/views/shop/shopOrder/useOrderNotify.ts` 为**模块级全局单例**:
+ - 轮询/状态/红点全部模块级变量(enabled、hasNewOrder、lastOrderId、timer),多组件调用只一个轮询。
+ - 检测到新订单 → triggerNotify(叮声+语音+遍历回调Set)+ `setOrderBadge(true)` 调 `userStore.setMenuBadge('/shop/shopOrder','dot','#ff4d4f')`。
+ - 新增 `clearBadge()`:立即清红点 + 重查最新订单同步 lastOrderId(标记"已查看")。
+ - `onNewOrder` 回调用 Set 管理,组件 onBeforeUnmount 自动移除;`start()` 幂等,由 layout 调一次。
+- `src/layout/components/menu-title.vue`:`badge==='dot'` 时用 `` 纯红点,否则原数字徽章逻辑。
+- `src/layout/index.vue`:setup 调 `useOrderNotify().start()` 全局启动,后台任意页面生效。
+- `src/views/shop/shopOrder/index.vue`:`onMounted` + `onActivated` 调 `clearBadge()`(兼容 keep-alive `cache-key`,用 `orderBadgeMounted` 标志防首次双触发)。
+- `src/views/shop/dashboard/index.vue`:`quickLinks` 改 computed,订单管理项 `badge: hasNewOrder.value`;`quick-icon` 加红点 span + pulse 动画样式。
+- 决策(用户确认):红点=新订单未查看(进订单页清除);位置=订单管理菜单+dashboard快速入口订单管理图标;声音保留并全局化。
+- 构建验证:`vite build --outDir dist_verify` 通过(EXIT:0)。原 `pnpm build` 失败仅因 vite 清空 `dist/assets` 被安全删除保护拦截(83文件>50阈值),与代码无关。
diff --git a/src/layout/components/menu-title.vue b/src/layout/components/menu-title.vue
index 168a697..bd9e900 100644
--- a/src/layout/components/menu-title.vue
+++ b/src/layout/components/menu-title.vue
@@ -1,7 +1,11 @@
{{ item.meta.title }}
@@ -290,7 +293,6 @@
({
couponUsed: couponUsedCount.value || 0,
}));
-// 快速入口
-const quickLinks = [
- { to: '/shop/shopOrder?tab=all', icon: '📦', label: '订单管理', bg: '#f0fdf4' },
+// 快速入口(订单管理项随新订单状态显示红点)
+const quickLinks = computed(() => [
+ { to: '/shop/shopOrder?tab=all', icon: '📦', label: '订单管理', bg: '#f0fdf4', badge: hasNewOrder.value },
{ to: '/shop/shopGoods', icon: '🏸', label: '商品管理', bg: '#ecfdf5' },
{ to: '/shop/shopGoodsCategory', icon: '🗂️', label: '商品分类', bg: '#eff6ff' },
{ to: '/market/coupon', icon: '🎁', label: '优惠券', bg: '#fff7ed' },
{ to: '/system/user', icon: '👥', label: '会员管理', bg: '#faf5ff' },
{ to: '/shop/shopSetting', icon: '⚙️', label: '商城设置', bg: '#fefce8' },
-];
+]);
// 导航跳转
const navigateTo = (path: string) => {
@@ -678,8 +680,8 @@ const handleClearCache = () => {
);
};
-// 新订单声音提醒
-const { enabled: notifyEnabled, toggle: onNotifyToggle, testNotify: onTestNotify } = useOrderNotify({
+// 新订单声音提醒(全局单例:轮询由 layout 启动,此处仅消费状态 + UI)
+const { enabled: notifyEnabled, hasNewOrder, toggle: onNotifyToggle, testNotify: onTestNotify } = useOrderNotify({
onNewOrder: () => loadData()
});
@@ -903,6 +905,24 @@ onUnmounted(() => {
.quick-icon {
width: 44px; height: 44px; border-radius: 10px;
display: flex; align-items: center; justify-content: center; font-size: 22px;
+ position: relative;
+}
+/* 快速入口新订单红点 */
+.quick-icon-dot {
+ position: absolute;
+ top: -3px;
+ right: -3px;
+ width: 10px;
+ height: 10px;
+ background: #ff4d4f;
+ border-radius: 50%;
+ border: 2px solid #fff;
+ box-shadow: 0 0 0 1px rgba(255, 77, 79, 0.3);
+ animation: quick-dot-pulse 1.5s ease-in-out infinite;
+}
+@keyframes quick-dot-pulse {
+ 0%, 100% { transform: scale(1); opacity: 1; }
+ 50% { transform: scale(1.25); opacity: 0.85; }
}
.quick-label { font-size: 13px; color: rgba(0,0,0,0.75); font-weight: 500; }
diff --git a/src/views/shop/shopOrder/index.vue b/src/views/shop/shopOrder/index.vue
index 4b87ac7..284523f 100644
--- a/src/views/shop/shopOrder/index.vue
+++ b/src/views/shop/shopOrder/index.vue
@@ -380,7 +380,7 @@