fix(index): 修复首页铃铛权限漏洞和非门店用户新订单提醒
- 针对铃铛红点和计数,改为仅门店角色可见 - 新订单提醒回调增加角色校验,非门店不弹Toast和震动 - 订单管理页增加访问控制,非门店自动跳回防止越权访问 - 保留消息点击事件中对角色的原有区分逻辑 - 弹窗宽度调整及登录同步逻辑未变动
This commit is contained in:
@@ -9,3 +9,12 @@
|
|||||||
- 弹窗宽度从 300px 加宽到 **340px**
|
- 弹窗宽度从 300px 加宽到 **340px**
|
||||||
- 登录成功后调用 `useUserContext().syncFromStorage()` 同步 React 登录态(`loginBySms` 内部已写入 storage 但不返回数据)
|
- 登录成功后调用 `useUserContext().syncFromStorage()` 同步 React 登录态(`loginBySms` 内部已写入 storage 但不返回数据)
|
||||||
- 弹窗 UI 使用 NutUI `Input` + `Button`,带 60 秒倒计时
|
- 弹窗 UI 使用 NutUI `Input` + `Button`,带 60 秒倒计时
|
||||||
|
|
||||||
|
## 修复首页铃铛权限漏洞和非门店用户新订单提醒
|
||||||
|
- **问题1**:非门店角色用户也能通过铃铛进入订单管理页
|
||||||
|
- **问题2**:新订单提醒(红点badge + Toast)对非门店用户也显示
|
||||||
|
- **修复**:
|
||||||
|
1. `src/pages/index/index.tsx`:铃铛 badge 显示条件从 `isLoggedIn` 改为 `isClerk`(红点和计数仅门店角色可见)
|
||||||
|
2. `src/pages/index/index.tsx`:`onNewOrders` 回调增加 `isClerkRef.current` 检查,非门店角色不弹 Toast/震动
|
||||||
|
3. `src/pages/store/orders/index.tsx`:订单管理页增加访问控制,`getMyClerk()` 校验非门店角色自动跳回(防御性拦截)
|
||||||
|
- `handleMessageClick` 中已有 `isClerk` 判断(店员→订单管理,普通用户→消息通知页),无需修改
|
||||||
|
|||||||
@@ -85,6 +85,8 @@ const IndexPage: React.FC = () => {
|
|||||||
return pageShopOrder({ page: 1, limit: 5 }).then(r => r?.list || [])
|
return pageShopOrder({ page: 1, limit: 5 }).then(r => r?.list || [])
|
||||||
}, []),
|
}, []),
|
||||||
onNewOrders: useCallback((count) => {
|
onNewOrders: useCallback((count) => {
|
||||||
|
// 非门店店员不弹新订单提醒
|
||||||
|
if (!isClerkRef.current) return
|
||||||
Taro.vibrateShort({ type: 'medium' })
|
Taro.vibrateShort({ type: 'medium' })
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: `您有 ${count} 条新订单`,
|
title: `您有 ${count} 条新订单`,
|
||||||
@@ -189,14 +191,14 @@ const IndexPage: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
<View onClick={handleMessageClick} className='relative'>
|
<View onClick={handleMessageClick} className='relative'>
|
||||||
<Text className='text-xl'>🔔</Text>
|
<Text className='text-xl'>🔔</Text>
|
||||||
{isLoggedIn && newOrderCount > 0 && (
|
{isClerk && newOrderCount > 0 && (
|
||||||
<View className='absolute -top-1 -right-1 min-w-[16px] h-4 px-1 bg-red-500 rounded-full flex items-center justify-center'>
|
<View className='absolute -top-1 -right-1 min-w-[16px] h-4 px-1 bg-red-500 rounded-full flex items-center justify-center'>
|
||||||
<Text className='text-white text-[10px] leading-none font-medium'>
|
<Text className='text-white text-[10px] leading-none font-medium'>
|
||||||
{newOrderCount > 99 ? '99+' : newOrderCount}
|
{newOrderCount > 99 ? '99+' : newOrderCount}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
{isLoggedIn && newOrderCount === 0 && (
|
{isClerk && newOrderCount === 0 && (
|
||||||
<View className='absolute top-0 right-0 w-2 h-2 bg-red-500 rounded-full' />
|
<View className='absolute top-0 right-0 w-2 h-2 bg-red-500 rounded-full' />
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type {ShopOrder, ShopOrderParam} from '@/api/shop/shopOrder/model'
|
|||||||
import {TenantId} from '@/config/app'
|
import {TenantId} from '@/config/app'
|
||||||
import {useNewOrderDetector} from '@/hooks/useNewOrderDetector'
|
import {useNewOrderDetector} from '@/hooks/useNewOrderDetector'
|
||||||
import { getCompressedImageUrl } from '@/utils/image'
|
import { getCompressedImageUrl } from '@/utils/image'
|
||||||
|
import { getMyClerk } from '@/api/shop/shopStoreUser'
|
||||||
|
|
||||||
definePageConfig({
|
definePageConfig({
|
||||||
navigationBarTitleText: '订单管理',
|
navigationBarTitleText: '订单管理',
|
||||||
@@ -156,6 +157,25 @@ export default function StoreOrdersPage() {
|
|||||||
const pageSize = 10
|
const pageSize = 10
|
||||||
const loadingRef = useRef(false)
|
const loadingRef = useRef(false)
|
||||||
|
|
||||||
|
// ─── 访问控制:仅门店店员可访问此页面 ──────────────────────
|
||||||
|
const [accessChecking, setAccessChecking] = useState(true)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getMyClerk()
|
||||||
|
.then(data => {
|
||||||
|
if (!data) {
|
||||||
|
Taro.showToast({ title: '无权访问', icon: 'none', duration: 1500 })
|
||||||
|
setTimeout(() => Taro.navigateBack(), 1500)
|
||||||
|
} else {
|
||||||
|
setAccessChecking(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
Taro.showToast({ title: '无权访问', icon: 'none', duration: 1500 })
|
||||||
|
setTimeout(() => Taro.navigateBack(), 1500)
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
// ─── 新订单轮询检测 ──────────────────────────────────────────
|
// ─── 新订单轮询检测 ──────────────────────────────────────────
|
||||||
const { newOrderCount, clearUnread } = useNewOrderDetector({
|
const { newOrderCount, clearUnread } = useNewOrderDetector({
|
||||||
interval: 30000, // 30 秒轮询一次
|
interval: 30000, // 30 秒轮询一次
|
||||||
@@ -530,6 +550,11 @@ export default function StoreOrdersPage() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 访问校验中或已拒绝:不渲染页面内容
|
||||||
|
if (accessChecking) {
|
||||||
|
return <View className='min-h-full bg-gray-50' />
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className='min-h-full bg-gray-50'>
|
<View className='min-h-full bg-gray-50'>
|
||||||
{/* Tab 栏 */}
|
{/* Tab 栏 */}
|
||||||
|
|||||||
Reference in New Issue
Block a user