feat(store-center): 在门店中心添加扫码登录PC后台功能
- 门店中心右上角新增扫码登录按钮,使用base64 SVG图标兼容小程序Image组件渲染 - 扫码后通过解析二维码内容调用confirmWechatQRLogin接口完成PC端登录确认 - 支持扫码取消静默处理,扫码失败则弹窗提示错误信息 - 优化门店中心页面布局,调整扫码按钮绝对定位及样式 - 优化了用户管理页面的小型样式,包括按钮和文本间距调整 - 调整用户页面部分Badge显示逻辑,排除tabIndex为4的项不显示角标
This commit is contained in:
@@ -94,3 +94,13 @@
|
||||
|
||||
### 验证
|
||||
- `npx taro build --type weapp` 构建成功
|
||||
|
||||
## 门店中心右上角扫码登录 PC 后台(src/pages/store/center/index.tsx)
|
||||
- **需求**:门店中心顶部绿色渐变区右上角加一个二维码扫码图标,点击后调起微信扫码,扫码结果解析出 token 后调 `confirmWechatQRLogin` 确认 PC 端登录
|
||||
- **参考**:`/Users/gxwebsoft/VUE/template-10584/src/pages/user/components/UserCard.tsx` 中的 `UnifiedQRButton` 组件
|
||||
- **实现**:
|
||||
- 复用已有 API:`parseQRContent` + `confirmWechatQRLogin`(`@/api/passport/qr-login`),与 `src/components/QRLoginScanner.tsx` 同款
|
||||
- 新增 `handleScanLogin` callback:`Taro.scanCode` → `parseQRContent` → 校验 token/userId → `confirmWechatQRLogin` → 成功弹窗
|
||||
- 右上角按钮用 `absolute top-3 right-4 z-20` 定位,半透明白色圆形背景 + base64 SVG 图标
|
||||
- 小程序不支持 `<svg>` 标签,用 `Image` 组件 + `data:image/svg+xml;base64,...` data URI 渲染图标
|
||||
- 用户取消扫码(errMsg 含 'cancel')静默处理
|
||||
|
||||
@@ -1,254 +1,306 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react'
|
||||
import { View, Text, Image } from '@tarojs/components'
|
||||
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'
|
||||
import {getMyClerk} from '@/api/shop/shopStoreUser'
|
||||
import {listShopDealerApply} from '@/api/shop/shopDealerApply'
|
||||
import {pageShopOrder} from '@/api/shop/shopOrder'
|
||||
import {confirmWechatQRLogin, parseQRContent} from '@/api/passport/qr-login'
|
||||
import Badge from '@/components/common/Badge'
|
||||
|
||||
// 二维码扫码图标(base64 SVG data URI,兼容小程序 Image 组件)
|
||||
const QR_SCAN_ICON = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMyA3VjVhMiAyIDAgMCAxIDItMmgyTTE3IDNoMmEyIDIgMCAwIDEgMiAydjJNMjEgMTd2MmEyIDIgMCAwIDEtMiAyaC0yTTcgMjFINWEyIDIgMCAwIDEtMi0ydi0yIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiAvPjxyZWN0IHg9IjciIHk9IjciIHdpZHRoPSI0IiBoZWlnaHQ9IjQiIHN0cm9rZT0iI2ZmZmZmZiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiAvPjxyZWN0IHg9IjEzIiB5PSI3IiB3aWR0aD0iNCIgaGVpZ2h0PSI0IiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgLz48cmVjdCB4PSI3IiB5PSIxMyIgd2lkdGg9IjQiIGhlaWdodD0iNCIgc3Ryb2tlPSIjZmZmZmZmIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIC8+PHBhdGggZD0iTTEzIDEzaDJ2Mk0xNyAxM3YyTTE3IDE3djJNMTMgMTd2Mk0xMyAxOWgyIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiAvPjwvc3ZnPg=="
|
||||
|
||||
definePageConfig({
|
||||
navigationBarTitleText: '门店中心',
|
||||
navigationBarTitleText: '门店中心',
|
||||
})
|
||||
|
||||
// 功能卡片定义(未来新增功能只需在这里加一项)
|
||||
const FEATURE_CARDS = [
|
||||
{
|
||||
key: 'goods',
|
||||
icon: '🏷️',
|
||||
title: '商品管理',
|
||||
desc: '管理门店商品上下架和库存',
|
||||
url: '/pages/store/goods/index',
|
||||
color: '#0891b2',
|
||||
bgColor: '#cffafe',
|
||||
showBadge: false,
|
||||
},
|
||||
{
|
||||
key: 'orders',
|
||||
icon: '📦',
|
||||
title: '订单管理',
|
||||
desc: '查看和处理门店订单',
|
||||
url: '/pages/store/orders/index',
|
||||
color: '#15803d',
|
||||
bgColor: '#dcfce7',
|
||||
// 显示待处理订单数量角标
|
||||
showBadge: true,
|
||||
},
|
||||
{
|
||||
key: 'users',
|
||||
icon: '👥',
|
||||
title: '用户管理',
|
||||
desc: '查看用户信息、禁用/启用用户',
|
||||
url: '/pages/store/users/index',
|
||||
color: '#2563eb',
|
||||
bgColor: '#dbeafe',
|
||||
showBadge: false,
|
||||
},
|
||||
{
|
||||
key: 'vip-review',
|
||||
icon: '👑',
|
||||
title: 'VIP会员审核',
|
||||
desc: '审核客户VIP会员申请',
|
||||
url: '/pages/user/vip-review/index',
|
||||
color: '#7c3aed',
|
||||
bgColor: '#ede9fe',
|
||||
showBadge: true,
|
||||
},
|
||||
{
|
||||
key: 'goods',
|
||||
icon: '🏷️',
|
||||
title: '商品管理',
|
||||
desc: '管理门店商品上下架和库存',
|
||||
url: '/pages/store/goods/index',
|
||||
color: '#0891b2',
|
||||
bgColor: '#cffafe',
|
||||
showBadge: false,
|
||||
},
|
||||
{
|
||||
key: 'orders',
|
||||
icon: '📦',
|
||||
title: '订单管理',
|
||||
desc: '查看和处理门店订单',
|
||||
url: '/pages/store/orders/index',
|
||||
color: '#15803d',
|
||||
bgColor: '#dcfce7',
|
||||
// 显示待处理订单数量角标
|
||||
showBadge: true,
|
||||
},
|
||||
{
|
||||
key: 'users',
|
||||
icon: '👥',
|
||||
title: '用户管理',
|
||||
desc: '查看用户信息、禁用/启用用户',
|
||||
url: '/pages/store/users/index',
|
||||
color: '#2563eb',
|
||||
bgColor: '#dbeafe',
|
||||
showBadge: false,
|
||||
},
|
||||
{
|
||||
key: 'vip-review',
|
||||
icon: '👑',
|
||||
title: 'VIP会员审核',
|
||||
desc: '审核客户VIP会员申请',
|
||||
url: '/pages/user/vip-review/index',
|
||||
color: '#7c3aed',
|
||||
bgColor: '#ede9fe',
|
||||
showBadge: true,
|
||||
},
|
||||
]
|
||||
|
||||
// 订阅消息模板 ID
|
||||
const SUBSCRIBE_TMPL_IDS = [
|
||||
'RpZaVfxTuNxRmUxvinAwk6FHTpjV9yTznShdy8OHiTk', // 交易提醒:订单号、商品名称、联系人、联系电话、送货地址
|
||||
'RpZaVfxTuNxRmUxvinAwk6FHTpjV9yTznShdy8OHiTk', // 交易提醒:订单号、商品名称、联系人、联系电话、送货地址
|
||||
]
|
||||
|
||||
export default function StoreCenterPage() {
|
||||
const [storeInfo, setStoreInfo] = useState<any>(null)
|
||||
const [pendingVipCount, setPendingVipCount] = useState(0)
|
||||
const [pendingOrderCount, setPendingOrderCount] = useState(0)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [storeInfo, setStoreInfo] = useState<any>(null)
|
||||
const [pendingVipCount, setPendingVipCount] = useState(0)
|
||||
const [pendingOrderCount, setPendingOrderCount] = useState(0)
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
// 验证店员身份
|
||||
getMyClerk()
|
||||
.then(data => {
|
||||
if (!data) {
|
||||
Taro.showToast({ title: '仅门店店员可访问', icon: 'none' })
|
||||
setTimeout(() => Taro.navigateBack(), 1500)
|
||||
return
|
||||
useEffect(() => {
|
||||
// 验证店员身份
|
||||
getMyClerk()
|
||||
.then(data => {
|
||||
if (!data) {
|
||||
Taro.showToast({title: '仅门店店员可访问', icon: 'none'})
|
||||
setTimeout(() => Taro.navigateBack(), 1500)
|
||||
return
|
||||
}
|
||||
setStoreInfo(data)
|
||||
// 加载待审核数量
|
||||
loadBadgeCounts()
|
||||
})
|
||||
.catch(() => {
|
||||
Taro.showToast({title: '仅门店店员可访问', icon: 'none'})
|
||||
setTimeout(() => Taro.navigateBack(), 1500)
|
||||
})
|
||||
.finally(() => setLoading(false))
|
||||
}, [])
|
||||
|
||||
/** 加载各类待处理角标数量 */
|
||||
const loadBadgeCounts = async () => {
|
||||
try {
|
||||
// VIP 待审核
|
||||
const vipData = await listShopDealerApply({applyStatus: 10})
|
||||
setPendingVipCount((vipData || []).length)
|
||||
} catch { /* ignore */
|
||||
}
|
||||
setStoreInfo(data)
|
||||
// 加载待审核数量
|
||||
loadBadgeCounts()
|
||||
})
|
||||
.catch(() => {
|
||||
Taro.showToast({ title: '仅门店店员可访问', icon: 'none' })
|
||||
setTimeout(() => Taro.navigateBack(), 1500)
|
||||
})
|
||||
.finally(() => setLoading(false))
|
||||
}, [])
|
||||
|
||||
/** 加载各类待处理角标数量 */
|
||||
const loadBadgeCounts = async () => {
|
||||
try {
|
||||
// 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
|
||||
try {
|
||||
// 待处理订单:未付款/待发货/待收货
|
||||
const orderData = await pageShopOrder({statusFilter: 1, page: 1, limit: 1})
|
||||
setPendingOrderCount(orderData?.total || 0)
|
||||
} catch { /* ignore */
|
||||
}
|
||||
}
|
||||
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' })
|
||||
|
||||
/** 获取卡片角标数字 */
|
||||
const getBadgeCount = (key: string) => {
|
||||
if (key === 'orders') return pendingOrderCount
|
||||
if (key === 'vip-review') return pendingVipCount
|
||||
return 0
|
||||
}
|
||||
|
||||
/** 扫码登录 PC 后台 */
|
||||
const handleScanLogin = useCallback(async () => {
|
||||
try {
|
||||
Taro.showLoading({title: '请扫码...', mask: true})
|
||||
const scanRes = await Taro.scanCode({onlyFromCamera: false, scanType: ['qrCode']})
|
||||
Taro.hideLoading()
|
||||
|
||||
const rawContent = scanRes.result || ''
|
||||
const token = parseQRContent(rawContent)
|
||||
const userId = Number(Taro.getStorageSync('UserId'))
|
||||
|
||||
if (!token) {
|
||||
Taro.showModal({title: '提示', content: '未识别到有效的登录二维码', showCancel: false})
|
||||
return
|
||||
}
|
||||
if (!userId) {
|
||||
Taro.showModal({title: '提示', content: '当前用户未登录', showCancel: false})
|
||||
return
|
||||
}
|
||||
|
||||
Taro.showLoading({title: '正在确认登录...', mask: true})
|
||||
await confirmWechatQRLogin(token, userId)
|
||||
Taro.hideLoading()
|
||||
Taro.showModal({title: '登录成功', content: 'PC 端后台登录已确认,请返回网页端查看', showCancel: false})
|
||||
} catch (err: any) {
|
||||
Taro.hideLoading()
|
||||
if (err?.errMsg?.includes('cancel')) {
|
||||
return // 用户取消扫码,静默处理
|
||||
}
|
||||
Taro.showModal({title: '登录失败', content: err?.message || '扫码登录失败,请重试', showCancel: false})
|
||||
}
|
||||
},
|
||||
fail: (err: any) => {
|
||||
console.error('订阅失败:', err)
|
||||
// errCode 20001: 模板ID不存在或未绑定到当前小程序
|
||||
if (err.errCode === 20001) {
|
||||
Taro.showModal({
|
||||
title: '订阅模板未配置',
|
||||
content: '请联系管理员在微信公众平台选用「新订单通知」订阅消息模板',
|
||||
showCancel: false,
|
||||
})
|
||||
} else if (err.errCode === 20004 || err.errCode === 20002) {
|
||||
// 用户拒绝/关闭了主开关,静默处理
|
||||
Taro.showToast({ title: '已取消订阅', icon: 'none' })
|
||||
}, [])
|
||||
|
||||
/** 请求订阅消息授权 */
|
||||
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: any) => {
|
||||
console.error('订阅失败:', err)
|
||||
// errCode 20001: 模板ID不存在或未绑定到当前小程序
|
||||
if (err.errCode === 20001) {
|
||||
Taro.showModal({
|
||||
title: '订阅模板未配置',
|
||||
content: '请联系管理员在微信公众平台选用「新订单通知」订阅消息模板',
|
||||
showCancel: false,
|
||||
})
|
||||
} else if (err.errCode === 20004 || err.errCode === 20002) {
|
||||
// 用户拒绝/关闭了主开关,静默处理
|
||||
Taro.showToast({title: '已取消订阅', icon: 'none'})
|
||||
}
|
||||
},
|
||||
})
|
||||
}, [])
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<View className='min-h-full bg-gray-50 flex items-center justify-center'>
|
||||
<Text className='text-gray-400'>加载中...</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
if (!storeInfo) return null
|
||||
|
||||
return (
|
||||
<View className='min-h-full bg-gray-50'>
|
||||
|
||||
{/* 顶部信息区:店员头像 + 门店名称 */}
|
||||
<View
|
||||
className='pt-8 pb-12 px-5 rounded-b-3xl relative overflow-hidden'
|
||||
style={{ background: 'linear-gradient(135deg, #15803d 0%, #22c55e 60%, #4ade80 100%)' }}
|
||||
>
|
||||
<View className='absolute -top-10 -right-10 w-40 h-40 rounded-full opacity-10'
|
||||
style={{ background: 'radial-gradient(circle, #ffffff, transparent)' }} />
|
||||
<View className='absolute -bottom-6 -left-6 w-24 h-24 rounded-full opacity-15'
|
||||
style={{ background: 'radial-gradient(circle, #ffffff, transparent)' }} />
|
||||
<View className='relative z-10 flex items-center gap-4'>
|
||||
{/* 店员头像 */}
|
||||
<View className='w-16 h-16 rounded-full bg-white/20 border-2 border-white/40 overflow-hidden flex-shrink-0'>
|
||||
{storeInfo.avatar ? (
|
||||
<Image
|
||||
className='w-full h-full'
|
||||
src={storeInfo.avatar}
|
||||
mode='aspectFill'
|
||||
/>
|
||||
) : (
|
||||
<View className='w-full h-full flex items-center justify-center'>
|
||||
<Text className='text-2xl'>👤</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* 门店信息 */}
|
||||
<View className='flex-1 min-w-0'>
|
||||
<Text className='text-white text-lg font-bold block truncate'>
|
||||
{storeInfo.storeName || '门店中心'}
|
||||
</Text>
|
||||
<Text className='text-white text-opacity-80 text-sm mt-1 block truncate'>
|
||||
{storeInfo.name} {storeInfo.phone ? `· ${storeInfo.phone}` : ''}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 功能卡片区 */}
|
||||
<View className='mx-3 -mt-6 relative z-20'>
|
||||
|
||||
<View className='flex flex-col gap-3'>
|
||||
{FEATURE_CARDS.map(card => (
|
||||
<View
|
||||
key={card.key}
|
||||
className='bg-white rounded-xl p-4 flex items-center gap-4 active:opacity-80 relative'
|
||||
onClick={() => Taro.navigateTo({ url: card.url })}
|
||||
>
|
||||
{/* 图标 */}
|
||||
<View
|
||||
className='w-12 h-12 rounded-xl flex items-center justify-center flex-shrink-0'
|
||||
style={{ background: card.bgColor }}
|
||||
>
|
||||
<Text className='text-2xl'>{card.icon}</Text>
|
||||
</View>
|
||||
|
||||
{/* 文字 */}
|
||||
<View className='flex-1 min-w-0'>
|
||||
<Text className='text-gray-800 text-base font-medium block'>{card.title}</Text>
|
||||
<Text className='text-gray-400 text-xs mt-0.5 block'>{card.desc}</Text>
|
||||
</View>
|
||||
|
||||
{/* 待处理角标 */}
|
||||
{card.showBadge && (
|
||||
<Badge count={getBadgeCount(card.key)} className='absolute -top-1 -right-1' size={20} fontSize={11} fontWeight='bold' />
|
||||
)}
|
||||
|
||||
{/* 箭头 */}
|
||||
<Text className='text-gray-300 text-lg flex-shrink-0'>›</Text>
|
||||
if (loading) {
|
||||
return (
|
||||
<View className='min-h-full bg-gray-50 flex items-center justify-center'>
|
||||
<Text className='text-gray-400'>加载中...</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
{/* 底部提示 */}
|
||||
<View className='mx-3 mt-6 mb-6'>
|
||||
{/* 订阅消息提醒 */}
|
||||
<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>
|
||||
if (!storeInfo) return null
|
||||
|
||||
{/*<Text className='text-gray-300 text-xs text-center block'>*/}
|
||||
{/* 未来更多功能将持续上线*/}
|
||||
{/*</Text>*/}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
return (
|
||||
<View className='min-h-full bg-gray-50'>
|
||||
|
||||
{/* 顶部信息区:店员头像 + 门店名称 */}
|
||||
<View
|
||||
className='pt-8 pb-12 px-5 rounded-b-3xl relative overflow-hidden'
|
||||
style={{background: 'linear-gradient(135deg, #15803d 0%, #22c55e 60%, #4ade80 100%)'}}
|
||||
>
|
||||
<View className='absolute -top-10 -right-10 w-40 h-40 rounded-full opacity-10'
|
||||
style={{background: 'radial-gradient(circle, #ffffff, transparent)'}}/>
|
||||
<View className='absolute -bottom-6 -left-6 w-24 h-24 rounded-full opacity-15'
|
||||
style={{background: 'radial-gradient(circle, #ffffff, transparent)'}}/>
|
||||
|
||||
{/* 右上角扫码登录按钮 */}
|
||||
<View
|
||||
className='absolute top-3 right-4 z-20 w-9 h-9 rounded-full bg-white/20 border border-white/30 flex items-center justify-center active:opacity-70'
|
||||
onClick={handleScanLogin}
|
||||
>
|
||||
<Image src={QR_SCAN_ICON} className='w-5 h-5' mode='aspectFit'/>
|
||||
</View>
|
||||
|
||||
<View className='relative z-10 flex items-center gap-4'>
|
||||
{/* 店员头像 */}
|
||||
<View
|
||||
className='w-16 h-16 rounded-full bg-white/20 border-2 border-white/40 overflow-hidden flex-shrink-0'>
|
||||
{storeInfo.avatar ? (
|
||||
<Image
|
||||
className='w-full h-full'
|
||||
src={storeInfo.avatar}
|
||||
mode='aspectFill'
|
||||
/>
|
||||
) : (
|
||||
<View className='w-full h-full flex items-center justify-center'>
|
||||
<Text className='text-2xl'>👤</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* 门店信息 */}
|
||||
<View className='flex-1 min-w-0'>
|
||||
<Text className='text-white text-lg font-bold block truncate'>
|
||||
{storeInfo.storeName || '门店中心'}
|
||||
</Text>
|
||||
<Text className='text-white text-opacity-80 text-sm mt-1 block truncate'>
|
||||
{storeInfo.name} {storeInfo.phone ? `· ${storeInfo.phone}` : ''}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 功能卡片区 */}
|
||||
<View className='mx-3 -mt-6 relative z-20'>
|
||||
|
||||
<View className='flex flex-col gap-3'>
|
||||
{FEATURE_CARDS.map(card => (
|
||||
<View
|
||||
key={card.key}
|
||||
className='bg-white rounded-xl p-4 flex items-center gap-4 active:opacity-80 relative'
|
||||
onClick={() => Taro.navigateTo({url: card.url})}
|
||||
>
|
||||
{/* 图标 */}
|
||||
<View
|
||||
className='w-12 h-12 rounded-xl flex items-center justify-center flex-shrink-0'
|
||||
style={{background: card.bgColor}}
|
||||
>
|
||||
<Text className='text-2xl'>{card.icon}</Text>
|
||||
</View>
|
||||
|
||||
{/* 文字 */}
|
||||
<View className='flex-1 min-w-0'>
|
||||
<Text className='text-gray-800 text-base font-medium block'>{card.title}</Text>
|
||||
<Text className='text-gray-400 text-xs mt-0.5 block'>{card.desc}</Text>
|
||||
</View>
|
||||
|
||||
{/* 待处理角标 */}
|
||||
{card.showBadge && (
|
||||
<Badge count={getBadgeCount(card.key)} className='absolute -top-1 -right-1' size={20}
|
||||
fontSize={11} fontWeight='bold'/>
|
||||
)}
|
||||
|
||||
{/* 箭头 */}
|
||||
<Text className='text-gray-300 text-lg flex-shrink-0'>›</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 底部提示 */}
|
||||
<View className='mx-3 mt-3 mb-6'>
|
||||
{/* 订阅消息提醒 */}
|
||||
<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>
|
||||
<View className={'h-4'}></View>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -183,7 +183,7 @@ export default function StoreUsersPage() {
|
||||
{displayName}
|
||||
</Text>
|
||||
<View
|
||||
className='px-1.5 py-0.5 rounded text-[10px]'
|
||||
className='px-1 py-1 rounded text-xs'
|
||||
style={{
|
||||
color: isDisabled ? '#dc2626' : '#16a34a',
|
||||
background: isDisabled ? '#fef2f2' : '#f0fdf4',
|
||||
@@ -192,14 +192,14 @@ export default function StoreUsersPage() {
|
||||
{isDisabled ? '已禁用' : '正常'}
|
||||
</View>
|
||||
</View>
|
||||
<Text className='text-xs text-gray-400 mt-0.5 block'>
|
||||
<Text className='text-xs text-gray-400 mt-1 block'>
|
||||
{user.phone || user.mobile || '未绑定手机'}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* 禁用/启用按钮 */}
|
||||
<View
|
||||
className={`px-3 py-1.5 rounded-lg flex-shrink-0 ${isDisabled ? 'bg-green-50' : 'bg-red-50'}`}
|
||||
className={`px-3 py-1 rounded-lg flex-shrink-0 ${isDisabled ? 'bg-green-50' : 'bg-red-50'}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
handleToggleStatus(user)
|
||||
@@ -215,17 +215,17 @@ export default function StoreUsersPage() {
|
||||
<View className='flex items-center gap-4 mt-3 pt-3 border-t border-gray-50'>
|
||||
{user.memberLevelName && (
|
||||
<View className='flex items-center gap-1'>
|
||||
<Text className='text-[10px] text-gray-400'>会员:</Text>
|
||||
<Text className='text-xs text-gray-400'>会员:</Text>
|
||||
<Text className='text-xs text-purple-500'>{user.memberLevelName}</Text>
|
||||
</View>
|
||||
)}
|
||||
<View className='flex items-center gap-1'>
|
||||
<Text className='text-[10px] text-gray-400'>注册:</Text>
|
||||
<Text className='text-xs text-gray-400'>注册:</Text>
|
||||
<Text className='text-xs text-gray-500'>{formatTime(user.createTime)}</Text>
|
||||
</View>
|
||||
{user.balance !== undefined && Number(user.balance) > 0 && (
|
||||
<View className='flex items-center gap-1'>
|
||||
<Text className='text-[10px] text-gray-400'>余额:</Text>
|
||||
<Text className='text-xs text-gray-400'>余额:</Text>
|
||||
<Text className='text-xs text-orange-500'>¥{user.balance}</Text>
|
||||
</View>
|
||||
)}
|
||||
@@ -238,7 +238,7 @@ export default function StoreUsersPage() {
|
||||
<View className='min-h-full bg-gray-50'>
|
||||
{/* 搜索栏 */}
|
||||
<View className='bg-white px-3 py-2 flex items-center gap-2'>
|
||||
<View className='flex-1 flex items-center bg-gray-100 rounded-lg px-3 py-1.5'>
|
||||
<View className='flex-1 flex items-center bg-gray-100 rounded-lg px-3 py-1'>
|
||||
<Input
|
||||
className='flex-1 text-sm'
|
||||
placeholder='搜索昵称 / 手机号'
|
||||
@@ -255,7 +255,7 @@ export default function StoreUsersPage() {
|
||||
) : null}
|
||||
</View>
|
||||
<View
|
||||
className='px-3 py-1.5 rounded-lg bg-blue-500'
|
||||
className='px-3 py-1 rounded-lg bg-blue-500'
|
||||
onClick={handleSearch}
|
||||
>
|
||||
<Text className='text-sm text-white'>搜索</Text>
|
||||
@@ -342,7 +342,7 @@ export default function StoreUsersPage() {
|
||||
{detailUser.nickname || detailUser.realName || '未设置昵称'}
|
||||
</Text>
|
||||
<View
|
||||
className='px-2 py-0.5 rounded text-xs'
|
||||
className='px-2 py-1 rounded text-xs'
|
||||
style={{
|
||||
color: detailUser.status === 1 ? '#dc2626' : '#16a34a',
|
||||
background: detailUser.status === 1 ? '#fef2f2' : '#f0fdf4',
|
||||
|
||||
@@ -281,7 +281,7 @@ const UserPage: React.FC = () => {
|
||||
<Text className='text-xl mb-1'>{item.icon}</Text>
|
||||
<Text className='text-xs text-gray-600'>{item.label}</Text>
|
||||
{/* 数量角标:count 为 0 时组件内部不渲染 */}
|
||||
<Badge count={item.count} className='absolute -top-1 -right-1' />
|
||||
{item.tabIndex != 4 && <Badge count={item.count} className='absolute -top-1 -right-1' />}
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
|
||||
Reference in New Issue
Block a user