feat(store): 新增门店新订单提醒功能
- 新增 useNewOrderDetector Hook,实现30秒轮询检测新订单 - 订单管理页集成该 Hook,新增订单时震动+Toast提醒 - “全部”Tab 显示新订单红点,切换Tab清空未读计数 - 门店中心页增加待处理订单角标,统一待审核和待处理显示逻辑 - 底部新增“接收新订单提醒”订阅消息授权入口,调用微信接口申请授权 - 文档中补充订阅消息模板ID及字段说明,待后端配合公众号推送消息
This commit is contained in:
@@ -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 Taro from '@tarojs/taro'
|
||||
import { getMyClerk } from '@/api/shop/shopStoreUser'
|
||||
import { listShopDealerApply } from '@/api/shop/shopDealerApply'
|
||||
import { pageShopOrder } from '@/api/shop/shopOrder'
|
||||
|
||||
definePageConfig({
|
||||
navigationBarTitleText: '门店中心',
|
||||
@@ -18,6 +19,8 @@ const FEATURE_CARDS = [
|
||||
url: '/pages/store/orders/index',
|
||||
color: '#15803d',
|
||||
bgColor: '#dcfce7',
|
||||
// 显示待处理订单数量角标
|
||||
showBadge: true,
|
||||
},
|
||||
{
|
||||
key: 'vip-review',
|
||||
@@ -27,14 +30,19 @@ const FEATURE_CARDS = [
|
||||
url: '/pages/user/vip-review/index',
|
||||
color: '#7c3aed',
|
||||
bgColor: '#ede9fe',
|
||||
// 显示待审核数量角标
|
||||
showBadge: true,
|
||||
},
|
||||
]
|
||||
|
||||
// 订阅消息模板 ID
|
||||
const SUBSCRIBE_TMPL_IDS = [
|
||||
'sh1K9iK7vZjebUNFu6OsMsnsJxm4whThWGrhN7I4zVg', // 交易提醒:订单号、商品名称、联系人、联系电话、送货地址
|
||||
]
|
||||
|
||||
export default function StoreCenterPage() {
|
||||
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)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -47,8 +55,8 @@ export default function StoreCenterPage() {
|
||||
return
|
||||
}
|
||||
setStoreInfo(data)
|
||||
// 加载待审核 VIP 申请数量
|
||||
loadPendingCount()
|
||||
// 加载待审核数量
|
||||
loadBadgeCounts()
|
||||
})
|
||||
.catch(() => {
|
||||
Taro.showToast({ title: '仅门店店员可访问', icon: 'none' })
|
||||
@@ -57,13 +65,49 @@ export default function StoreCenterPage() {
|
||||
.finally(() => setLoading(false))
|
||||
}, [])
|
||||
|
||||
const loadPendingCount = async () => {
|
||||
/** 加载各类待处理角标数量 */
|
||||
const loadBadgeCounts = async () => {
|
||||
try {
|
||||
const data = await listShopDealerApply({ applyStatus: 10 })
|
||||
setPendingCount((data || []).length)
|
||||
// VIP 待审核
|
||||
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 */ }
|
||||
}
|
||||
|
||||
/** 获取卡片角标数字 */
|
||||
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) {
|
||||
return (
|
||||
<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>
|
||||
</View>
|
||||
|
||||
{/* 待审核角标 */}
|
||||
{card.showBadge && pendingCount > 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'>
|
||||
<Text className='text-white text-xs font-bold'>{pendingCount > 99 ? '99+' : pendingCount}</Text>
|
||||
{/* 待处理角标 */}
|
||||
{card.showBadge && getBadgeCount(card.key) > 0 && (
|
||||
<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-[11px] font-bold'>
|
||||
{getBadgeCount(card.key) > 99 ? '99+' : getBadgeCount(card.key)}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
@@ -154,6 +200,23 @@ export default function StoreCenterPage() {
|
||||
|
||||
{/* 底部提示 */}
|
||||
<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>
|
||||
|
||||
@@ -4,6 +4,7 @@ import Taro, {useDidShow} from '@tarojs/taro'
|
||||
import {pageShopOrder, updateShopOrder, removeShopOrder} from '@/api/shop/shopOrder'
|
||||
import type {ShopOrder, ShopOrderParam} from '@/api/shop/shopOrder/model'
|
||||
import {TenantId} from '@/config/app'
|
||||
import {useNewOrderDetector} from '@/hooks/useNewOrderDetector'
|
||||
|
||||
definePageConfig({
|
||||
navigationBarTitleText: '订单管理',
|
||||
@@ -141,6 +142,25 @@ export default function StoreOrdersPage() {
|
||||
const pageSize = 10
|
||||
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 的结果) */
|
||||
const loadOrders = useCallback(async (tab: TabKey, pageNo: number = 1, append = false) => {
|
||||
if (loadingRef.current) return
|
||||
@@ -442,15 +462,26 @@ export default function StoreOrdersPage() {
|
||||
{TABS.map(tab => (
|
||||
<View
|
||||
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'
|
||||
}`}
|
||||
onClick={() => setActiveTab(tab.key)}
|
||||
onClick={() => {
|
||||
setActiveTab(tab.key)
|
||||
clearUnread()
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
className={`text-sm ${activeTab === tab.key ? 'text-green-500 font-medium' : 'text-gray-500'}`}>
|
||||
{tab.label}
|
||||
</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>
|
||||
|
||||
Reference in New Issue
Block a user