feat(shopOrder): 实现电脑版后台全局新订单红点提示

- 新建模块级全局单例 useOrderNotify,统一轮询检测新订单,避免多组件重复请求
- 轮询检测到新订单时触发叮声、语音播报,通知所有已注册回调刷新数据
- 订单管理菜单及 dashboard 快速入口显示红点提示新订单未查看
- 进入订单列表页时调用 clearBadge 同步最新订单ID并清除红点,标记订单已查看
- layout 全局启动新订单提醒,任意页面均生效
- menu-title 组件新增纯红点(dot)徽章显示支持
- dashboard 快速入口图标加红点显示及脉冲动画,提升视觉提示效果
- 维护新订单提醒回调集合,组件卸载自动移除回调避免内存泄漏
- 新订单提醒配置项和状态均为模块共享响应式,支持多组件无冲突使用
This commit is contained in:
2026-07-15 15:26:48 +08:00
parent 3a951c70f5
commit d5e040b1e6
6 changed files with 266 additions and 106 deletions

View File

@@ -101,7 +101,10 @@
class="quick-card"
@click="navigateTo(item.to)"
>
<div class="quick-icon" :style="{ background: item.bg }">{{ item.icon }}</div>
<div class="quick-icon" :style="{ background: item.bg }">
{{ item.icon }}
<span v-if="item.badge" class="quick-icon-dot"></span>
</div>
<div class="quick-label">{{ item.label }}</div>
</div>
</div>
@@ -290,7 +293,6 @@
<!-- 支付 Modal微信扫码支付 -->
<a-modal
v-model:open="payModalVisible"
title="微信扫码支付"
:footer="null"
:mask-closable="false"
@cancel="onPayModalClose"
@@ -644,15 +646,15 @@ const todayStats = computed(() => ({
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; }