feat(store): 新增门店新订单提醒功能
- 新增 useNewOrderDetector Hook,实现30秒轮询检测新订单 - 订单管理页集成该 Hook,新增订单时震动+Toast提醒 - “全部”Tab 显示新订单红点,切换Tab清空未读计数 - 门店中心页增加待处理订单角标,统一待审核和待处理显示逻辑 - 底部新增“接收新订单提醒”订阅消息授权入口,调用微信接口申请授权 - 文档中补充订阅消息模板ID及字段说明,待后端配合公众号推送消息
This commit is contained in:
@@ -74,3 +74,44 @@
|
|||||||
|
|
||||||
### 涉及文件(补充)
|
### 涉及文件(补充)
|
||||||
- 修改:`src/pages/shop/product-detail.tsx`
|
- 修改:`src/pages/shop/product-detail.tsx`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 10. 新订单提醒功能(前端轮询 + 订阅消息)
|
||||||
|
|
||||||
|
#### 10.1 创建 useNewOrderDetector Hook
|
||||||
|
- 新建 `src/hooks/useNewOrderDetector.ts`
|
||||||
|
- 封装新订单轮询检测逻辑:
|
||||||
|
- 首次加载自动建立订单 ID 基准线,不触发提醒
|
||||||
|
- 后续轮询对比发现新订单后触发 `onNewOrders` 回调
|
||||||
|
- 页面隐藏时自动暂停轮询,显示时恢复
|
||||||
|
- 提供 `clearUnread()` 方法清除未读计数
|
||||||
|
- 导出类型:`NewOrderItem`、`UseNewOrderDetectorOptions`、`UseNewOrderDetectorReturn`
|
||||||
|
- 注册到 `src/hooks/index.ts`
|
||||||
|
|
||||||
|
#### 10.2 改造订单管理页(前端轮询)
|
||||||
|
- 修改 `src/pages/store/orders/index.tsx`
|
||||||
|
- 集成 `useNewOrderDetector`:30 秒轮询,静默检测新订单
|
||||||
|
- 发现新订单时:震动提醒(`Taro.vibrateShort`)+ Toast 提示("您有 N 条新订单")
|
||||||
|
- "全部" Tab 增加红色角标显示未读新订单数(>99 显示 99+)
|
||||||
|
- 切换 Tab 时自动清除未读计数
|
||||||
|
|
||||||
|
#### 10.3 改造门店中心页
|
||||||
|
- 修改 `src/pages/store/center/index.tsx`
|
||||||
|
- 订单管理卡片增加待处理订单角标(查询 statusFilter=1 的 total)
|
||||||
|
- VIP 审核角标同步增强,统一使用 `getBadgeCount()` 方法
|
||||||
|
- 底部新增"接收新订单提醒"卡片:
|
||||||
|
- 调用 `Taro.requestSubscribeMessage` 请求微信订阅消息授权
|
||||||
|
- 模板 ID 通过 `SUBSCRIBE_TMPL_IDS` 常量配置(待微信公众平台申请后填入)
|
||||||
|
- 授权成功后 Toast 提示
|
||||||
|
|
||||||
|
#### 待后端配合
|
||||||
|
- 微信公众平台申请订阅消息模板("新订单通知"类目)
|
||||||
|
- 将模板 ID 填入 `store/center/index.tsx` 的 `SUBSCRIBE_TMPL_IDS` 数组
|
||||||
|
- 后端在订单创建成功后调用 `subscribeMessage.send` API 向店员推送
|
||||||
|
|
||||||
|
#### 涉及文件
|
||||||
|
- 新建:`src/hooks/useNewOrderDetector.ts`
|
||||||
|
- 修改:`src/hooks/index.ts`
|
||||||
|
- 修改:`src/pages/store/orders/index.tsx`
|
||||||
|
- 修改:`src/pages/store/center/index.tsx`
|
||||||
|
|||||||
@@ -25,3 +25,9 @@
|
|||||||
- VIP 审核页:`src/pages/user/vip-review/index.tsx`
|
- VIP 审核页:`src/pages/user/vip-review/index.tsx`
|
||||||
- VIP 升级页:`src/pages/user/vip-upgrade/index.tsx`
|
- VIP 升级页:`src/pages/user/vip-upgrade/index.tsx`
|
||||||
- 门店中心:`src/pages/store/center/index.tsx`
|
- 门店中心:`src/pages/store/center/index.tsx`
|
||||||
|
|
||||||
|
## 订阅消息
|
||||||
|
- 模板 ID:`sh1K9iK7vZjebUNFu6OsMsnsJxm4whThWGrhN7I4zVg`(交易提醒)
|
||||||
|
- 字段:订单号(`character_string1`)、商品名称(`thing4`)、联系人(`thing10`)、联系电话(`phone_number7`)、送货地址(`thing8`)
|
||||||
|
- 场景说明:下单提醒
|
||||||
|
- 新订单检测 Hook:`src/hooks/useNewOrderDetector.ts`(30秒轮询,页面隐藏暂停)
|
||||||
|
|||||||
@@ -26,3 +26,5 @@ export { useCoupon } from './useCoupon'
|
|||||||
export { usePagination } from './usePagination'
|
export { usePagination } from './usePagination'
|
||||||
export { usePayment } from './usePayment'
|
export { usePayment } from './usePayment'
|
||||||
export { useScrollHeight } from './useScrollHeight'
|
export { useScrollHeight } from './useScrollHeight'
|
||||||
|
export { useNewOrderDetector } from './useNewOrderDetector'
|
||||||
|
export type { NewOrderItem, UseNewOrderDetectorOptions, UseNewOrderDetectorReturn } from './useNewOrderDetector'
|
||||||
|
|||||||
129
src/hooks/useNewOrderDetector.ts
Normal file
129
src/hooks/useNewOrderDetector.ts
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
import { useRef, useState, useCallback } from 'react'
|
||||||
|
import { useDidShow, useDidHide } from '@tarojs/taro'
|
||||||
|
|
||||||
|
export interface NewOrderItem {
|
||||||
|
orderId?: number
|
||||||
|
orderNo?: string
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UseNewOrderDetectorOptions {
|
||||||
|
/** 轮询间隔(毫秒),默认 30000(30秒) */
|
||||||
|
interval?: number
|
||||||
|
/** 获取最新订单列表(按时间降序),返回前几条用于对比 */
|
||||||
|
fetchLatestOrders: () => Promise<NewOrderItem[]>
|
||||||
|
/** 发现新订单时的回调 */
|
||||||
|
onNewOrders?: (count: number, newOrders: NewOrderItem[]) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UseNewOrderDetectorReturn {
|
||||||
|
/** 未读新订单数 */
|
||||||
|
newOrderCount: number
|
||||||
|
/** 新订单列表 */
|
||||||
|
newOrders: NewOrderItem[]
|
||||||
|
/** 清除未读计数(用户查看后调用) */
|
||||||
|
clearUnread: () => void
|
||||||
|
/** 手动触发一次检测 */
|
||||||
|
checkNow: () => Promise<void>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新订单轮询检测 Hook
|
||||||
|
*
|
||||||
|
* 用于门店订单管理页面,轮询检测是否有新订单产生。
|
||||||
|
* 页面显示时自动启动轮询,隐藏时暂停,避免无效请求。
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { newOrderCount, newOrders, clearUnread } = useNewOrderDetector({
|
||||||
|
* fetchLatestOrders: () => pageShopOrder({ page: 1, limit: 5 }),
|
||||||
|
* onNewOrders: (count) => Taro.vibrateShort({ type: 'medium' }),
|
||||||
|
* })
|
||||||
|
*/
|
||||||
|
export function useNewOrderDetector(
|
||||||
|
options: UseNewOrderDetectorOptions,
|
||||||
|
): UseNewOrderDetectorReturn {
|
||||||
|
const { interval = 30000, fetchLatestOrders, onNewOrders } = options
|
||||||
|
|
||||||
|
// 已见过的所有 orderId 集合(初始化时需要先加载一次建立基准)
|
||||||
|
const knownOrderIdsRef = useRef<Set<number> | null>(null)
|
||||||
|
// 轮询定时器
|
||||||
|
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||||
|
// 是否正在检测中(防止并发)
|
||||||
|
const detectingRef = useRef(false)
|
||||||
|
|
||||||
|
const [newOrderCount, setNewOrderCount] = useState(0)
|
||||||
|
const [newOrders, setNewOrders] = useState<NewOrderItem[]>([])
|
||||||
|
|
||||||
|
/** 执行一次检测 */
|
||||||
|
const doCheck = useCallback(async () => {
|
||||||
|
if (detectingRef.current) return
|
||||||
|
detectingRef.current = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const list = await fetchLatestOrders()
|
||||||
|
if (!list || list.length === 0) return
|
||||||
|
|
||||||
|
// 首次加载:建立基准线,不触发提醒
|
||||||
|
if (knownOrderIdsRef.current === null) {
|
||||||
|
knownOrderIdsRef.current = new Set(
|
||||||
|
list.map(o => o.orderId).filter(Boolean) as number[],
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对比检测新订单
|
||||||
|
const freshOrders: NewOrderItem[] = []
|
||||||
|
const newIds: number[] = []
|
||||||
|
|
||||||
|
for (const order of list) {
|
||||||
|
const id = order.orderId
|
||||||
|
if (id != null && !knownOrderIdsRef.current.has(id)) {
|
||||||
|
freshOrders.push(order)
|
||||||
|
newIds.push(id)
|
||||||
|
knownOrderIdsRef.current.add(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (freshOrders.length > 0) {
|
||||||
|
setNewOrderCount(prev => prev + freshOrders.length)
|
||||||
|
setNewOrders(prev => [...freshOrders, ...prev])
|
||||||
|
onNewOrders?.(freshOrders.length, freshOrders)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// 静默失败,不影响页面正常使用
|
||||||
|
} finally {
|
||||||
|
detectingRef.current = false
|
||||||
|
}
|
||||||
|
}, [fetchLatestOrders, onNewOrders])
|
||||||
|
|
||||||
|
/** 清除未读 */
|
||||||
|
const clearUnread = useCallback(() => {
|
||||||
|
setNewOrderCount(0)
|
||||||
|
setNewOrders([])
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
/** 手动触发检测 */
|
||||||
|
const checkNow = useCallback(async () => {
|
||||||
|
await doCheck()
|
||||||
|
}, [doCheck])
|
||||||
|
|
||||||
|
// 页面显示时:初始化基准线 + 启动轮询
|
||||||
|
useDidShow(() => {
|
||||||
|
// 首次加载建立基准(不触发提醒)
|
||||||
|
doCheck()
|
||||||
|
|
||||||
|
// 启动定时轮询
|
||||||
|
if (timerRef.current) clearInterval(timerRef.current)
|
||||||
|
timerRef.current = setInterval(doCheck, interval)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 页面隐藏时:停止轮询
|
||||||
|
useDidHide(() => {
|
||||||
|
if (timerRef.current) {
|
||||||
|
clearInterval(timerRef.current)
|
||||||
|
timerRef.current = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return { newOrderCount, newOrders, clearUnread, checkNow }
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect, useCallback } from 'react'
|
||||||
import { View, Text, Image } from '@tarojs/components'
|
import { View, Text, Image } from '@tarojs/components'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import { getMyClerk } from '@/api/shop/shopStoreUser'
|
import { getMyClerk } from '@/api/shop/shopStoreUser'
|
||||||
import { listShopDealerApply } from '@/api/shop/shopDealerApply'
|
import { listShopDealerApply } from '@/api/shop/shopDealerApply'
|
||||||
|
import { pageShopOrder } from '@/api/shop/shopOrder'
|
||||||
|
|
||||||
definePageConfig({
|
definePageConfig({
|
||||||
navigationBarTitleText: '门店中心',
|
navigationBarTitleText: '门店中心',
|
||||||
@@ -18,6 +19,8 @@ const FEATURE_CARDS = [
|
|||||||
url: '/pages/store/orders/index',
|
url: '/pages/store/orders/index',
|
||||||
color: '#15803d',
|
color: '#15803d',
|
||||||
bgColor: '#dcfce7',
|
bgColor: '#dcfce7',
|
||||||
|
// 显示待处理订单数量角标
|
||||||
|
showBadge: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'vip-review',
|
key: 'vip-review',
|
||||||
@@ -27,14 +30,19 @@ const FEATURE_CARDS = [
|
|||||||
url: '/pages/user/vip-review/index',
|
url: '/pages/user/vip-review/index',
|
||||||
color: '#7c3aed',
|
color: '#7c3aed',
|
||||||
bgColor: '#ede9fe',
|
bgColor: '#ede9fe',
|
||||||
// 显示待审核数量角标
|
|
||||||
showBadge: true,
|
showBadge: true,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// 订阅消息模板 ID
|
||||||
|
const SUBSCRIBE_TMPL_IDS = [
|
||||||
|
'sh1K9iK7vZjebUNFu6OsMsnsJxm4whThWGrhN7I4zVg', // 交易提醒:订单号、商品名称、联系人、联系电话、送货地址
|
||||||
|
]
|
||||||
|
|
||||||
export default function StoreCenterPage() {
|
export default function StoreCenterPage() {
|
||||||
const [storeInfo, setStoreInfo] = useState<any>(null)
|
const [storeInfo, setStoreInfo] = useState<any>(null)
|
||||||
const [pendingCount, setPendingCount] = useState(0)
|
const [pendingVipCount, setPendingVipCount] = useState(0)
|
||||||
|
const [pendingOrderCount, setPendingOrderCount] = useState(0)
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -47,8 +55,8 @@ export default function StoreCenterPage() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
setStoreInfo(data)
|
setStoreInfo(data)
|
||||||
// 加载待审核 VIP 申请数量
|
// 加载待审核数量
|
||||||
loadPendingCount()
|
loadBadgeCounts()
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
Taro.showToast({ title: '仅门店店员可访问', icon: 'none' })
|
Taro.showToast({ title: '仅门店店员可访问', icon: 'none' })
|
||||||
@@ -57,13 +65,49 @@ export default function StoreCenterPage() {
|
|||||||
.finally(() => setLoading(false))
|
.finally(() => setLoading(false))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const loadPendingCount = async () => {
|
/** 加载各类待处理角标数量 */
|
||||||
|
const loadBadgeCounts = async () => {
|
||||||
try {
|
try {
|
||||||
const data = await listShopDealerApply({ applyStatus: 10 })
|
// VIP 待审核
|
||||||
setPendingCount((data || []).length)
|
const vipData = await listShopDealerApply({ applyStatus: 10 })
|
||||||
|
setPendingVipCount((vipData || []).length)
|
||||||
|
} catch { /* ignore */ }
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 待处理订单:未付款/待发货/待收货
|
||||||
|
const orderData = await pageShopOrder({ statusFilter: 1, page: 1, limit: 1 })
|
||||||
|
setPendingOrderCount(orderData?.total || 0)
|
||||||
} catch { /* ignore */ }
|
} catch { /* ignore */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 获取卡片角标数字 */
|
||||||
|
const getBadgeCount = (key: string) => {
|
||||||
|
if (key === 'orders') return pendingOrderCount
|
||||||
|
if (key === 'vip-review') return pendingVipCount
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 请求订阅消息授权 */
|
||||||
|
const handleSubscribe = useCallback(() => {
|
||||||
|
if (SUBSCRIBE_TMPL_IDS.length === 0) {
|
||||||
|
Taro.showToast({ title: '暂无可订阅的消息模板', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Taro.requestSubscribeMessage({
|
||||||
|
tmplIds: SUBSCRIBE_TMPL_IDS,
|
||||||
|
success: (res) => {
|
||||||
|
// res[templateId] === 'accept' 表示用户同意订阅
|
||||||
|
const accepted = SUBSCRIBE_TMPL_IDS.filter(id => res[id] === 'accept')
|
||||||
|
if (accepted.length > 0) {
|
||||||
|
Taro.showToast({ title: '订阅成功', icon: 'success' })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('订阅失败:', err)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<View className='min-h-full bg-gray-50 flex items-center justify-center'>
|
<View className='min-h-full bg-gray-50 flex items-center justify-center'>
|
||||||
@@ -138,10 +182,12 @@ export default function StoreCenterPage() {
|
|||||||
<Text className='text-gray-400 text-xs mt-0.5 block'>{card.desc}</Text>
|
<Text className='text-gray-400 text-xs mt-0.5 block'>{card.desc}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 待审核角标 */}
|
{/* 待处理角标 */}
|
||||||
{card.showBadge && pendingCount > 0 && (
|
{card.showBadge && getBadgeCount(card.key) > 0 && (
|
||||||
<View className='absolute -top-1 -right-1 min-w-5 h-5 rounded-full bg-red-500 flex items-center justify-center px-1'>
|
<View className='absolute -top-1 -right-1 min-w-[20px] h-[20px] rounded-full bg-red-500 flex items-center justify-center px-1.5'>
|
||||||
<Text className='text-white text-xs font-bold'>{pendingCount > 99 ? '99+' : pendingCount}</Text>
|
<Text className='text-white text-[11px] font-bold'>
|
||||||
|
{getBadgeCount(card.key) > 99 ? '99+' : getBadgeCount(card.key)}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -154,6 +200,23 @@ export default function StoreCenterPage() {
|
|||||||
|
|
||||||
{/* 底部提示 */}
|
{/* 底部提示 */}
|
||||||
<View className='mx-3 mt-6 mb-4'>
|
<View className='mx-3 mt-6 mb-4'>
|
||||||
|
{/* 订阅消息提醒 */}
|
||||||
|
<View
|
||||||
|
className='bg-white rounded-xl p-4 flex items-center gap-3 active:opacity-80 mb-3'
|
||||||
|
onClick={handleSubscribe}
|
||||||
|
>
|
||||||
|
<View className='w-10 h-10 rounded-full bg-orange-50 flex items-center justify-center flex-shrink-0'>
|
||||||
|
<Text className='text-xl'>🔔</Text>
|
||||||
|
</View>
|
||||||
|
<View className='flex-1 min-w-0'>
|
||||||
|
<Text className='text-gray-800 text-sm font-medium block'>接收新订单提醒</Text>
|
||||||
|
<Text className='text-gray-400 text-xs mt-0.5 block'>
|
||||||
|
开启后新订单将通过微信服务通知提醒您
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<Text className='text-orange-500 text-sm flex-shrink-0'>去开启</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
<Text className='text-gray-300 text-xs text-center block'>
|
<Text className='text-gray-300 text-xs text-center block'>
|
||||||
未来更多功能将持续上线
|
未来更多功能将持续上线
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import Taro, {useDidShow} from '@tarojs/taro'
|
|||||||
import {pageShopOrder, updateShopOrder, removeShopOrder} from '@/api/shop/shopOrder'
|
import {pageShopOrder, updateShopOrder, removeShopOrder} from '@/api/shop/shopOrder'
|
||||||
import type {ShopOrder, ShopOrderParam} from '@/api/shop/shopOrder/model'
|
import type {ShopOrder, ShopOrderParam} from '@/api/shop/shopOrder/model'
|
||||||
import {TenantId} from '@/config/app'
|
import {TenantId} from '@/config/app'
|
||||||
|
import {useNewOrderDetector} from '@/hooks/useNewOrderDetector'
|
||||||
|
|
||||||
definePageConfig({
|
definePageConfig({
|
||||||
navigationBarTitleText: '订单管理',
|
navigationBarTitleText: '订单管理',
|
||||||
@@ -141,6 +142,25 @@ export default function StoreOrdersPage() {
|
|||||||
const pageSize = 10
|
const pageSize = 10
|
||||||
const loadingRef = useRef(false)
|
const loadingRef = useRef(false)
|
||||||
|
|
||||||
|
// ─── 新订单轮询检测 ──────────────────────────────────────────
|
||||||
|
const { newOrderCount, clearUnread } = useNewOrderDetector({
|
||||||
|
interval: 30000, // 30 秒轮询一次
|
||||||
|
fetchLatestOrders: useCallback(
|
||||||
|
() => pageShopOrder({ page: 1, limit: 5 }).then(r => r?.list || []),
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
onNewOrders: useCallback((count) => {
|
||||||
|
// 震动提醒
|
||||||
|
Taro.vibrateShort({ type: 'medium' })
|
||||||
|
// Toast 提醒
|
||||||
|
Taro.showToast({
|
||||||
|
title: `您有 ${count} 条新订单`,
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2500,
|
||||||
|
})
|
||||||
|
}, []),
|
||||||
|
})
|
||||||
|
|
||||||
/** 加载订单列表(待处理 Tab 会合并多个 statusFilter 的结果) */
|
/** 加载订单列表(待处理 Tab 会合并多个 statusFilter 的结果) */
|
||||||
const loadOrders = useCallback(async (tab: TabKey, pageNo: number = 1, append = false) => {
|
const loadOrders = useCallback(async (tab: TabKey, pageNo: number = 1, append = false) => {
|
||||||
if (loadingRef.current) return
|
if (loadingRef.current) return
|
||||||
@@ -442,15 +462,26 @@ export default function StoreOrdersPage() {
|
|||||||
{TABS.map(tab => (
|
{TABS.map(tab => (
|
||||||
<View
|
<View
|
||||||
key={tab.key}
|
key={tab.key}
|
||||||
className={`flex-1 text-center py-3 border-b-2 ${
|
className={`flex-1 text-center py-3 border-b-2 relative ${
|
||||||
activeTab === tab.key ? 'border-green-500' : 'border-transparent'
|
activeTab === tab.key ? 'border-green-500' : 'border-transparent'
|
||||||
}`}
|
}`}
|
||||||
onClick={() => setActiveTab(tab.key)}
|
onClick={() => {
|
||||||
|
setActiveTab(tab.key)
|
||||||
|
clearUnread()
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
className={`text-sm ${activeTab === tab.key ? 'text-green-500 font-medium' : 'text-gray-500'}`}>
|
className={`text-sm ${activeTab === tab.key ? 'text-green-500 font-medium' : 'text-gray-500'}`}>
|
||||||
{tab.label}
|
{tab.label}
|
||||||
</Text>
|
</Text>
|
||||||
|
{/* 新订单角标:在"全部"Tab 上显示 */}
|
||||||
|
{tab.key === 'all' && newOrderCount > 0 && (
|
||||||
|
<View className='absolute -top-0.5 right-2 min-w-[18px] h-[18px] rounded-full bg-red-500 flex items-center justify-center px-1'>
|
||||||
|
<Text className='text-white text-[10px] font-bold'>
|
||||||
|
{newOrderCount > 99 ? '99+' : newOrderCount}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
Reference in New Issue
Block a user