forked from gxwebsoft/mp-10550
feat(rider): 添加配送员模块和订单图片保存功能
- 新增配送员首页界面,包含订单管理、工资明细、配送小区、仓库地址等功能入口 - 实现小程序码保存到相册功能,支持权限检查和错误处理 - 添加相册写入权限配置和图片下载临时路径处理 - 修复订单列表商品信息显示问题,优化支付流程 - 更新首页轮播图广告代码,调整用户中心网格布局 - 增加订单页面返回时的数据刷新机制,提升用户体验
This commit is contained in:
@@ -90,6 +90,12 @@ export default {
|
|||||||
'comments/index',
|
'comments/index',
|
||||||
'search/index']
|
'search/index']
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"root": "rider",
|
||||||
|
"pages": [
|
||||||
|
"index"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"root": "admin",
|
"root": "admin",
|
||||||
"pages": [
|
"pages": [
|
||||||
@@ -138,6 +144,9 @@ export default {
|
|||||||
permission: {
|
permission: {
|
||||||
"scope.userLocation": {
|
"scope.userLocation": {
|
||||||
"desc": "你的位置信息将用于小程序位置接口的效果展示"
|
"desc": "你的位置信息将用于小程序位置接口的效果展示"
|
||||||
|
},
|
||||||
|
"scope.writePhotosAlbum": {
|
||||||
|
"desc": "用于保存小程序码到相册,方便分享给好友"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {businessGradients} from '@/styles/gradients'
|
|||||||
const DealerQrcode: React.FC = () => {
|
const DealerQrcode: React.FC = () => {
|
||||||
const [miniProgramCodeUrl, setMiniProgramCodeUrl] = useState<string>('')
|
const [miniProgramCodeUrl, setMiniProgramCodeUrl] = useState<string>('')
|
||||||
const [loading, setLoading] = useState<boolean>(false)
|
const [loading, setLoading] = useState<boolean>(false)
|
||||||
|
const [saving, setSaving] = useState<boolean>(false)
|
||||||
// const [inviteStats, setInviteStats] = useState<InviteStats | null>(null)
|
// const [inviteStats, setInviteStats] = useState<InviteStats | null>(null)
|
||||||
// const [statsLoading, setStatsLoading] = useState<boolean>(false)
|
// const [statsLoading, setStatsLoading] = useState<boolean>(false)
|
||||||
const {dealerUser} = useDealerUser()
|
const {dealerUser} = useDealerUser()
|
||||||
@@ -67,6 +68,66 @@ const DealerQrcode: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}, [dealerUser?.userId])
|
}, [dealerUser?.userId])
|
||||||
|
|
||||||
|
const isAlbumAuthError = (errMsg?: string) => {
|
||||||
|
if (!errMsg) return false
|
||||||
|
// WeChat uses variants like: "saveImageToPhotosAlbum:fail auth deny",
|
||||||
|
// "saveImageToPhotosAlbum:fail auth denied", "authorize:fail auth deny"
|
||||||
|
return (
|
||||||
|
errMsg.includes('auth deny') ||
|
||||||
|
errMsg.includes('auth denied') ||
|
||||||
|
errMsg.includes('authorize') ||
|
||||||
|
errMsg.includes('scope.writePhotosAlbum')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ensureWriteAlbumPermission = async (): Promise<boolean> => {
|
||||||
|
try {
|
||||||
|
const setting = await Taro.getSetting()
|
||||||
|
if (setting?.authSetting?.['scope.writePhotosAlbum']) return true
|
||||||
|
|
||||||
|
await Taro.authorize({scope: 'scope.writePhotosAlbum'})
|
||||||
|
return true
|
||||||
|
} catch (error: any) {
|
||||||
|
const modal = await Taro.showModal({
|
||||||
|
title: '提示',
|
||||||
|
content: '需要您授权保存图片到相册,请在设置中开启相册权限',
|
||||||
|
confirmText: '去设置'
|
||||||
|
})
|
||||||
|
if (modal.confirm) {
|
||||||
|
await Taro.openSetting()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadImageToLocalPath = async (url: string): Promise<string> => {
|
||||||
|
// saveImageToPhotosAlbum must receive a local temp path (e.g. `http://tmp/...` or `wxfile://...`).
|
||||||
|
// Some environments may return a non-existing temp path from getImageInfo, so we verify.
|
||||||
|
if (url.startsWith('http://tmp/') || url.startsWith('wxfile://')) {
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = Taro.getStorageSync('access_token')
|
||||||
|
const tenantId = Taro.getStorageSync('TenantId')
|
||||||
|
const header: Record<string, string> = {}
|
||||||
|
if (token) header.Authorization = token
|
||||||
|
if (tenantId) header.TenantId = tenantId
|
||||||
|
|
||||||
|
// 先下载到本地临时文件再保存到相册
|
||||||
|
const res = await Taro.downloadFile({url, header})
|
||||||
|
if (res.statusCode !== 200 || !res.tempFilePath) {
|
||||||
|
throw new Error(`图片下载失败(${res.statusCode || 'unknown'})`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double-check file exists to avoid: saveImageToPhotosAlbum:fail no such file or directory
|
||||||
|
try {
|
||||||
|
await Taro.getFileInfo({filePath: res.tempFilePath})
|
||||||
|
} catch (_) {
|
||||||
|
throw new Error('图片临时文件不存在,请重试')
|
||||||
|
}
|
||||||
|
return res.tempFilePath
|
||||||
|
}
|
||||||
|
|
||||||
// 保存小程序码到相册
|
// 保存小程序码到相册
|
||||||
const saveMiniProgramCode = async () => {
|
const saveMiniProgramCode = async () => {
|
||||||
if (!miniProgramCodeUrl) {
|
if (!miniProgramCodeUrl) {
|
||||||
@@ -78,39 +139,64 @@ const DealerQrcode: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 先下载图片到本地
|
if (saving) return
|
||||||
const res = await Taro.downloadFile({
|
setSaving(true)
|
||||||
url: miniProgramCodeUrl
|
Taro.showLoading({title: '保存中...'})
|
||||||
})
|
|
||||||
|
|
||||||
if (res.statusCode === 200) {
|
const hasPermission = await ensureWriteAlbumPermission()
|
||||||
// 保存到相册
|
if (!hasPermission) return
|
||||||
await Taro.saveImageToPhotosAlbum({
|
|
||||||
filePath: res.tempFilePath
|
let filePath = await downloadImageToLocalPath(miniProgramCodeUrl)
|
||||||
})
|
try {
|
||||||
|
await Taro.saveImageToPhotosAlbum({filePath})
|
||||||
|
} catch (e: any) {
|
||||||
|
const msg = e?.errMsg || e?.message || ''
|
||||||
|
// Fallback: some devices/clients may fail to save directly from a temp path.
|
||||||
|
if (
|
||||||
|
msg.includes('no such file or directory') &&
|
||||||
|
(filePath.startsWith('http://tmp/') || filePath.startsWith('wxfile://'))
|
||||||
|
) {
|
||||||
|
const saved = (await Taro.saveFile({tempFilePath: filePath})) as unknown as { savedFilePath?: string }
|
||||||
|
if (saved?.savedFilePath) {
|
||||||
|
filePath = saved.savedFilePath
|
||||||
|
}
|
||||||
|
await Taro.saveImageToPhotosAlbum({filePath})
|
||||||
|
} else {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '保存成功',
|
title: '保存成功',
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
})
|
})
|
||||||
}
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error.errMsg?.includes('auth deny')) {
|
const errMsg = error?.errMsg || error?.message
|
||||||
Taro.showModal({
|
if (errMsg?.includes('cancel')) {
|
||||||
|
Taro.showToast({title: '已取消', icon: 'none'})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAlbumAuthError(errMsg)) {
|
||||||
|
const modal = await Taro.showModal({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
content: '需要您授权保存图片到相册',
|
content: '需要您授权保存图片到相册',
|
||||||
success: (res) => {
|
confirmText: '去设置'
|
||||||
if (res.confirm) {
|
|
||||||
Taro.openSetting()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
if (modal.confirm) {
|
||||||
|
await Taro.openSetting()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Taro.showToast({
|
// Prefer a modal so we can show the real reason (e.g. domain whitelist / network error).
|
||||||
|
await Taro.showModal({
|
||||||
title: '保存失败',
|
title: '保存失败',
|
||||||
icon: 'error'
|
content: errMsg || '保存失败,请稍后重试',
|
||||||
|
showCancel: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
Taro.hideLoading()
|
||||||
|
setSaving(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,7 +344,7 @@ const DealerQrcode: React.FC = () => {
|
|||||||
block
|
block
|
||||||
icon={<Download/>}
|
icon={<Download/>}
|
||||||
onClick={saveMiniProgramCode}
|
onClick={saveMiniProgramCode}
|
||||||
disabled={!miniProgramCodeUrl || loading}
|
disabled={!miniProgramCodeUrl || loading || saving}
|
||||||
>
|
>
|
||||||
保存小程序码到相册
|
保存小程序码到相册
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -181,11 +181,11 @@ const DealerWithdraw: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (amount < 100) {
|
if (amount < 100) {
|
||||||
Taro.showToast({
|
// Taro.showToast({
|
||||||
title: '最低提现金额为100元',
|
// title: '最低提现金额为100元',
|
||||||
icon: 'error'
|
// icon: 'error'
|
||||||
})
|
// })
|
||||||
return
|
// return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (amount > available) {
|
if (amount > available) {
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const MyPage = () => {
|
|||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const [flashRes] = await Promise.allSettled([
|
const [flashRes] = await Promise.allSettled([
|
||||||
getCmsAdByCode('flash'),
|
getCmsAdByCode('mp-ad'),
|
||||||
getCmsAdByCode('hot_today'),
|
getCmsAdByCode('hot_today'),
|
||||||
pageCmsArticle({ limit: 1, recommend: 1 }),
|
pageCmsArticle({ limit: 1, recommend: 1 }),
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const UserCell = () => {
|
|||||||
border: 'none'
|
border: 'none'
|
||||||
} as React.CSSProperties}
|
} as React.CSSProperties}
|
||||||
>
|
>
|
||||||
<Grid.Item text="企业采购" onClick={() => navTo('/user/poster/poster', true)}>
|
<Grid.Item text="配送订单" onClick={() => navTo('/rider/index', true)}>
|
||||||
<View className="text-center">
|
<View className="text-center">
|
||||||
<View className="w-12 h-12 bg-blue-50 rounded-xl flex items-center justify-center mx-auto mb-2">
|
<View className="w-12 h-12 bg-blue-50 rounded-xl flex items-center justify-center mx-auto mb-2">
|
||||||
<ShoppingAdd color="#3b82f6" size="20"/>
|
<ShoppingAdd color="#3b82f6" size="20"/>
|
||||||
|
|||||||
3
src/rider/index.config.ts
Normal file
3
src/rider/index.config.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '配送中心'
|
||||||
|
})
|
||||||
0
src/rider/index.scss
Normal file
0
src/rider/index.scss
Normal file
295
src/rider/index.tsx
Normal file
295
src/rider/index.tsx
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import {View, Text} from '@tarojs/components'
|
||||||
|
import {ConfigProvider, Button, Grid, Avatar} from '@nutui/nutui-react-taro'
|
||||||
|
import {
|
||||||
|
User,
|
||||||
|
Shopping,
|
||||||
|
Dongdong,
|
||||||
|
ArrowRight,
|
||||||
|
Purse,
|
||||||
|
People
|
||||||
|
} from '@nutui/icons-react-taro'
|
||||||
|
import {useDealerUser} from '@/hooks/useDealerUser'
|
||||||
|
import { useThemeStyles } from '@/hooks/useTheme'
|
||||||
|
import {businessGradients, cardGradients, gradientUtils} from '@/styles/gradients'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
|
||||||
|
const DealerIndex: React.FC = () => {
|
||||||
|
const {
|
||||||
|
dealerUser,
|
||||||
|
error,
|
||||||
|
refresh,
|
||||||
|
} = useDealerUser()
|
||||||
|
|
||||||
|
// 使用主题样式
|
||||||
|
const themeStyles = useThemeStyles()
|
||||||
|
|
||||||
|
// 导航到各个功能页面
|
||||||
|
const navigateToPage = (url: string) => {
|
||||||
|
Taro.navigateTo({url})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化金额
|
||||||
|
const formatMoney = (money?: string) => {
|
||||||
|
if (!money) return '0.00'
|
||||||
|
return parseFloat(money).toFixed(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化时间
|
||||||
|
const formatTime = (time?: string) => {
|
||||||
|
if (!time) return '-'
|
||||||
|
return new Date(time).toLocaleDateString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户主题
|
||||||
|
const userTheme = gradientUtils.getThemeByUserId(dealerUser?.userId)
|
||||||
|
|
||||||
|
// 获取渐变背景
|
||||||
|
const getGradientBackground = (themeColor?: string) => {
|
||||||
|
if (themeColor) {
|
||||||
|
const darkerColor = gradientUtils.adjustColorBrightness(themeColor, -30)
|
||||||
|
return gradientUtils.createGradient(themeColor, darkerColor)
|
||||||
|
}
|
||||||
|
return userTheme.background
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(getGradientBackground(),'getGradientBackground()')
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<View className="p-4">
|
||||||
|
<View className="bg-red-50 border border-red-200 rounded-lg p-4 mb-4">
|
||||||
|
<Text className="text-red-600">{error}</Text>
|
||||||
|
</View>
|
||||||
|
<Button type="primary" onClick={refresh}>
|
||||||
|
重试
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="bg-gray-100 min-h-screen">
|
||||||
|
<View>
|
||||||
|
{/*头部信息*/}
|
||||||
|
{dealerUser && (
|
||||||
|
<View className="px-4 py-6 relative overflow-hidden" style={themeStyles.primaryBackground}>
|
||||||
|
{/* 装饰性背景元素 - 小程序兼容版本 */}
|
||||||
|
<View className="absolute w-32 h-32 rounded-full" style={{
|
||||||
|
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
||||||
|
top: '-16px',
|
||||||
|
right: '-16px'
|
||||||
|
}}></View>
|
||||||
|
<View className="absolute w-24 h-24 rounded-full" style={{
|
||||||
|
backgroundColor: 'rgba(255, 255, 255, 0.08)',
|
||||||
|
bottom: '-12px',
|
||||||
|
left: '-12px'
|
||||||
|
}}></View>
|
||||||
|
<View className="absolute w-16 h-16 rounded-full" style={{
|
||||||
|
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
||||||
|
top: '60px',
|
||||||
|
left: '120px'
|
||||||
|
}}></View>
|
||||||
|
<View className="flex items-center justify-between relative z-10 mb-4">
|
||||||
|
<Avatar
|
||||||
|
size="50"
|
||||||
|
src={dealerUser?.qrcode}
|
||||||
|
icon={<User/>}
|
||||||
|
className="mr-4"
|
||||||
|
style={{
|
||||||
|
border: '2px solid rgba(255, 255, 255, 0.3)'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<View className="flex-1 flex-col">
|
||||||
|
<View className="text-white text-lg font-bold mb-1" style={{
|
||||||
|
}}>
|
||||||
|
{dealerUser?.realName || '分销商'}
|
||||||
|
</View>
|
||||||
|
<View className="text-sm" style={{
|
||||||
|
color: 'rgba(255, 255, 255, 0.8)'
|
||||||
|
}}>
|
||||||
|
ID: {dealerUser.userId}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View className="text-right hidden">
|
||||||
|
<Text className="text-xs" style={{
|
||||||
|
color: 'rgba(255, 255, 255, 0.9)'
|
||||||
|
}}>加入时间</Text>
|
||||||
|
<Text className="text-xs" style={{
|
||||||
|
color: 'rgba(255, 255, 255, 0.7)'
|
||||||
|
}}>
|
||||||
|
{formatTime(dealerUser.createTime)}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 佣金统计卡片 */}
|
||||||
|
{dealerUser && (
|
||||||
|
<View className="mx-4 -mt-6 rounded-xl p-4 relative z-10" style={cardGradients.elevated}>
|
||||||
|
<View className="mb-4">
|
||||||
|
<Text className="font-semibold text-gray-800">工资统计</Text>
|
||||||
|
</View>
|
||||||
|
<View className="grid grid-cols-3 gap-3">
|
||||||
|
<View className="text-center p-3 rounded-lg flex flex-col" style={{
|
||||||
|
background: businessGradients.money.available
|
||||||
|
}}>
|
||||||
|
<Text className="text-lg font-bold mb-1 text-white">
|
||||||
|
{formatMoney(dealerUser.money)}
|
||||||
|
</Text>
|
||||||
|
<Text className="text-xs" style={{ color: 'rgba(255, 255, 255, 0.9)' }}>工资收入</Text>
|
||||||
|
</View>
|
||||||
|
<View className="text-center p-3 rounded-lg flex flex-col" style={{
|
||||||
|
background: businessGradients.money.frozen
|
||||||
|
}}>
|
||||||
|
<Text className="text-lg font-bold mb-1 text-white">
|
||||||
|
{formatMoney(dealerUser.freezeMoney)}
|
||||||
|
</Text>
|
||||||
|
<Text className="text-xs" style={{ color: 'rgba(255, 255, 255, 0.9)' }}>桶数</Text>
|
||||||
|
</View>
|
||||||
|
<View className="text-center p-3 rounded-lg flex flex-col" style={{
|
||||||
|
background: businessGradients.money.total
|
||||||
|
}}>
|
||||||
|
<Text className="text-lg font-bold mb-1 text-white">
|
||||||
|
{formatMoney(dealerUser.totalMoney)}
|
||||||
|
</Text>
|
||||||
|
<Text className="text-xs" style={{ color: 'rgba(255, 255, 255, 0.9)' }}>累计收入</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 团队统计 */}
|
||||||
|
{dealerUser && (
|
||||||
|
<View className="bg-white mx-4 mt-4 rounded-xl p-4 hidden">
|
||||||
|
<View className="flex items-center justify-between mb-4">
|
||||||
|
<Text className="font-semibold text-gray-800">我的邀请</Text>
|
||||||
|
<View
|
||||||
|
className="text-gray-400 text-sm flex items-center"
|
||||||
|
onClick={() => navigateToPage('/dealer/team/index')}
|
||||||
|
>
|
||||||
|
<Text>查看详情</Text>
|
||||||
|
<ArrowRight size="12"/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View className="grid grid-cols-3 gap-4">
|
||||||
|
<View className="text-center grid">
|
||||||
|
<Text className="text-xl font-bold text-purple-500 mb-1">
|
||||||
|
{dealerUser.firstNum || 0}
|
||||||
|
</Text>
|
||||||
|
<Text className="text-xs text-gray-500">一级成员</Text>
|
||||||
|
</View>
|
||||||
|
<View className="text-center grid">
|
||||||
|
<Text className="text-xl font-bold text-indigo-500 mb-1">
|
||||||
|
{dealerUser.secondNum || 0}
|
||||||
|
</Text>
|
||||||
|
<Text className="text-xs text-gray-500">二级成员</Text>
|
||||||
|
</View>
|
||||||
|
<View className="text-center grid">
|
||||||
|
<Text className="text-xl font-bold text-pink-500 mb-1">
|
||||||
|
{dealerUser.thirdNum || 0}
|
||||||
|
</Text>
|
||||||
|
<Text className="text-xs text-gray-500">三级成员</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 功能导航 */}
|
||||||
|
<View className="bg-white mx-4 mt-4 rounded-xl p-4">
|
||||||
|
<View className="font-semibold mb-4 text-gray-800">配送工具</View>
|
||||||
|
<ConfigProvider>
|
||||||
|
<Grid
|
||||||
|
columns={4}
|
||||||
|
className="no-border-grid"
|
||||||
|
style={{
|
||||||
|
'--nutui-grid-border-color': 'transparent',
|
||||||
|
'--nutui-grid-item-border-width': '0px',
|
||||||
|
border: 'none'
|
||||||
|
} as React.CSSProperties}
|
||||||
|
>
|
||||||
|
<Grid.Item text="配送订单" onClick={() => navigateToPage('/rider/orders/index')}>
|
||||||
|
<View className="text-center">
|
||||||
|
<View className="w-12 h-12 bg-blue-50 rounded-xl flex items-center justify-center mx-auto mb-2">
|
||||||
|
<Shopping color="#3b82f6" size="20"/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Grid.Item>
|
||||||
|
|
||||||
|
<Grid.Item text={'工资明细'} onClick={() => navigateToPage('/rider/withdraw/index')}>
|
||||||
|
<View className="text-center">
|
||||||
|
<View className="w-12 h-12 bg-green-50 rounded-xl flex items-center justify-center mx-auto mb-2">
|
||||||
|
<Purse color="#10b981" size="20"/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Grid.Item>
|
||||||
|
|
||||||
|
<Grid.Item text={'配送小区'} onClick={() => navigateToPage('/rider/team/index')}>
|
||||||
|
<View className="text-center">
|
||||||
|
<View className="w-12 h-12 bg-purple-50 rounded-xl flex items-center justify-center mx-auto mb-2">
|
||||||
|
<People color="#8b5cf6" size="20"/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Grid.Item>
|
||||||
|
|
||||||
|
<Grid.Item text={'仓库地址'} onClick={() => navigateToPage('/rider/qrcode/index')}>
|
||||||
|
<View className="text-center">
|
||||||
|
<View className="w-12 h-12 bg-orange-50 rounded-xl flex items-center justify-center mx-auto mb-2">
|
||||||
|
<Dongdong color="#f59e0b" size="20"/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Grid.Item>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* 第二行功能 */}
|
||||||
|
{/*<Grid*/}
|
||||||
|
{/* columns={4}*/}
|
||||||
|
{/* className="no-border-grid mt-4"*/}
|
||||||
|
{/* style={{*/}
|
||||||
|
{/* '--nutui-grid-border-color': 'transparent',*/}
|
||||||
|
{/* '--nutui-grid-item-border-width': '0px',*/}
|
||||||
|
{/* border: 'none'*/}
|
||||||
|
{/* } as React.CSSProperties}*/}
|
||||||
|
{/*>*/}
|
||||||
|
{/* <Grid.Item text={'邀请统计'} onClick={() => navigateToPage('/dealer/invite-stats/index')}>*/}
|
||||||
|
{/* <View className="text-center">*/}
|
||||||
|
{/* <View className="w-12 h-12 bg-indigo-50 rounded-xl flex items-center justify-center mx-auto mb-2">*/}
|
||||||
|
{/* <Presentation color="#6366f1" size="20"/>*/}
|
||||||
|
{/* </View>*/}
|
||||||
|
{/* </View>*/}
|
||||||
|
{/* </Grid.Item>*/}
|
||||||
|
|
||||||
|
{/* /!* 预留其他功能位置 *!/*/}
|
||||||
|
{/* <Grid.Item text={''}>*/}
|
||||||
|
{/* <View className="text-center">*/}
|
||||||
|
{/* <View className="w-12 h-12 bg-gray-50 rounded-xl flex items-center justify-center mx-auto mb-2">*/}
|
||||||
|
{/* </View>*/}
|
||||||
|
{/* </View>*/}
|
||||||
|
{/* </Grid.Item>*/}
|
||||||
|
|
||||||
|
{/* <Grid.Item text={''}>*/}
|
||||||
|
{/* <View className="text-center">*/}
|
||||||
|
{/* <View className="w-12 h-12 bg-gray-50 rounded-xl flex items-center justify-center mx-auto mb-2">*/}
|
||||||
|
{/* </View>*/}
|
||||||
|
{/* </View>*/}
|
||||||
|
{/* </Grid.Item>*/}
|
||||||
|
|
||||||
|
{/* <Grid.Item text={''}>*/}
|
||||||
|
{/* <View className="text-center">*/}
|
||||||
|
{/* <View className="w-12 h-12 bg-gray-50 rounded-xl flex items-center justify-center mx-auto mb-2">*/}
|
||||||
|
{/* </View>*/}
|
||||||
|
{/* </View>*/}
|
||||||
|
{/* </Grid.Item>*/}
|
||||||
|
{/*</Grid>*/}
|
||||||
|
</ConfigProvider>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 底部安全区域 */}
|
||||||
|
<View className="h-20"></View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DealerIndex
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import {Avatar, Cell, Space, Empty, Tabs, Button, TabPane, Image, Dialog} from '@nutui/nutui-react-taro'
|
import {Avatar, Cell, Space, Empty, Tabs, Button, TabPane, Image, Dialog} from '@nutui/nutui-react-taro'
|
||||||
import {useEffect, useState, useCallback, CSSProperties} from "react";
|
import {useEffect, useState, useCallback, useRef, CSSProperties} from "react";
|
||||||
import {View, Text} from '@tarojs/components'
|
import {View, Text} from '@tarojs/components'
|
||||||
import Taro from '@tarojs/taro';
|
import Taro from '@tarojs/taro';
|
||||||
import {InfiniteLoading} from '@nutui/nutui-react-taro'
|
import {InfiniteLoading} from '@nutui/nutui-react-taro'
|
||||||
@@ -80,7 +80,8 @@ const tabs = [
|
|||||||
|
|
||||||
// 扩展订单接口,包含商品信息
|
// 扩展订单接口,包含商品信息
|
||||||
interface OrderWithGoods extends ShopOrder {
|
interface OrderWithGoods extends ShopOrder {
|
||||||
orderGoods?: ShopOrderGoods[];
|
// 避免与 ShopOrder.orderGoods (OrderGoods[]) 冲突:这里使用单独字段保存接口返回的商品明细
|
||||||
|
orderGoodsList?: ShopOrderGoods[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OrderListProps {
|
interface OrderListProps {
|
||||||
@@ -92,8 +93,9 @@ interface OrderListProps {
|
|||||||
|
|
||||||
function OrderList(props: OrderListProps) {
|
function OrderList(props: OrderListProps) {
|
||||||
const [list, setList] = useState<OrderWithGoods[]>([])
|
const [list, setList] = useState<OrderWithGoods[]>([])
|
||||||
const [page, setPage] = useState(1)
|
const pageRef = useRef(1)
|
||||||
const [hasMore, setHasMore] = useState(true)
|
const [hasMore, setHasMore] = useState(true)
|
||||||
|
const [payingOrderId, setPayingOrderId] = useState<number | null>(null)
|
||||||
// 根据传入的statusFilter设置初始tab索引
|
// 根据传入的statusFilter设置初始tab索引
|
||||||
const getInitialTabIndex = () => {
|
const getInitialTabIndex = () => {
|
||||||
if (props.searchParams?.statusFilter !== undefined) {
|
if (props.searchParams?.statusFilter !== undefined) {
|
||||||
@@ -183,7 +185,7 @@ function OrderList(props: OrderListProps) {
|
|||||||
const reload = useCallback(async (resetPage = false, targetPage?: number) => {
|
const reload = useCallback(async (resetPage = false, targetPage?: number) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null); // 清除之前的错误
|
setError(null); // 清除之前的错误
|
||||||
const currentPage = resetPage ? 1 : (targetPage || page);
|
const currentPage = resetPage ? 1 : (targetPage || pageRef.current);
|
||||||
const statusParams = getOrderStatusParams(tapIndex);
|
const statusParams = getOrderStatusParams(tapIndex);
|
||||||
// 合并搜索条件,tab的statusFilter优先级更高
|
// 合并搜索条件,tab的statusFilter优先级更高
|
||||||
const searchConditions: any = {
|
const searchConditions: any = {
|
||||||
@@ -205,7 +207,6 @@ function OrderList(props: OrderListProps) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await pageShopOrder(searchConditions);
|
const res = await pageShopOrder(searchConditions);
|
||||||
let newList: OrderWithGoods[];
|
|
||||||
|
|
||||||
if (res?.list && res?.list.length > 0) {
|
if (res?.list && res?.list.length > 0) {
|
||||||
// 批量获取订单商品信息,限制并发数量
|
// 批量获取订单商品信息,限制并发数量
|
||||||
@@ -220,13 +221,13 @@ function OrderList(props: OrderListProps) {
|
|||||||
const orderGoods = await listShopOrderGoods({orderId: order.orderId});
|
const orderGoods = await listShopOrderGoods({orderId: order.orderId});
|
||||||
return {
|
return {
|
||||||
...order,
|
...order,
|
||||||
orderGoods: orderGoods || []
|
orderGoodsList: orderGoods || []
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取订单商品失败:', error);
|
console.error('获取订单商品失败:', error);
|
||||||
return {
|
return {
|
||||||
...order,
|
...order,
|
||||||
orderGoods: []
|
orderGoodsList: []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -248,7 +249,7 @@ function OrderList(props: OrderListProps) {
|
|||||||
setHasMore(false);
|
setHasMore(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
setPage(currentPage);
|
pageRef.current = currentPage;
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载订单失败:', error);
|
console.error('加载订单失败:', error);
|
||||||
@@ -260,14 +261,14 @@ function OrderList(props: OrderListProps) {
|
|||||||
icon: 'none'
|
icon: 'none'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [tapIndex, page, props.searchParams]); // 移除 list 依赖
|
}, [tapIndex, props.searchParams]); // 移除 list/page 依赖,避免useEffect触发循环
|
||||||
|
|
||||||
const reloadMore = useCallback(async () => {
|
const reloadMore = useCallback(async () => {
|
||||||
if (loading || !hasMore) return; // 防止重复加载
|
if (loading || !hasMore) return; // 防止重复加载
|
||||||
const nextPage = page + 1;
|
const nextPage = pageRef.current + 1;
|
||||||
setPage(nextPage);
|
pageRef.current = nextPage;
|
||||||
await reload(false, nextPage);
|
await reload(false, nextPage);
|
||||||
}, [loading, hasMore, page, reload]);
|
}, [loading, hasMore, reload]);
|
||||||
|
|
||||||
// 确认收货 - 显示确认对话框
|
// 确认收货 - 显示确认对话框
|
||||||
const confirmReceive = (order: ShopOrder) => {
|
const confirmReceive = (order: ShopOrder) => {
|
||||||
@@ -325,7 +326,7 @@ function OrderList(props: OrderListProps) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 更新本地状态
|
// 更新本地状态
|
||||||
setDataSource(prev => prev.map(item =>
|
setList(prev => prev.map(item =>
|
||||||
item.orderId === order.orderId ? {...item, orderStatus: 4} : item
|
item.orderId === order.orderId ? {...item, orderStatus: 4} : item
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -351,49 +352,23 @@ function OrderList(props: OrderListProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 再次购买 (已完成状态)
|
// 再次购买 (已完成状态)
|
||||||
const buyAgain = (order: ShopOrder) => {
|
const buyAgain = (order: OrderWithGoods) => {
|
||||||
console.log('再次购买:', order);
|
console.log('再次购买:', order);
|
||||||
goTo(`/shop/orderConfirm/index?goodsId=${order.orderGoods[0].goodsId}`)
|
const goodsId = order.orderGoodsList?.[0]?.goodsId
|
||||||
|
if (!goodsId) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '订单商品信息缺失',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
goTo(`/shop/orderConfirm/index?goodsId=${goodsId}`)
|
||||||
// Taro.showToast({
|
// Taro.showToast({
|
||||||
// title: '再次购买功能开发中',
|
// title: '再次购买功能开发中',
|
||||||
// icon: 'none'
|
// icon: 'none'
|
||||||
// });
|
// });
|
||||||
};
|
};
|
||||||
|
|
||||||
// 评价商品 (已完成状态)
|
|
||||||
const evaluateGoods = (order: ShopOrder) => {
|
|
||||||
// 跳转到评价页面
|
|
||||||
Taro.navigateTo({
|
|
||||||
url: `/user/order/evaluate/index?orderId=${order.orderId}&orderNo=${order.orderNo}`
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 查看进度 (退款/售后状态)
|
|
||||||
const viewProgress = (order: ShopOrder) => {
|
|
||||||
// 根据订单状态确定售后类型
|
|
||||||
let afterSaleType = 'refund' // 默认退款
|
|
||||||
|
|
||||||
if (order.orderStatus === 4) {
|
|
||||||
afterSaleType = 'refund' // 退款申请中
|
|
||||||
} else if (order.orderStatus === 7) {
|
|
||||||
afterSaleType = 'return' // 退货申请中
|
|
||||||
}
|
|
||||||
|
|
||||||
// 跳转到售后进度页面
|
|
||||||
Taro.navigateTo({
|
|
||||||
url: `/user/order/progress/index?orderId=${order.orderId}&orderNo=${order.orderNo}&type=${afterSaleType}`
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 撤销申请 (退款/售后状态)
|
|
||||||
const cancelApplication = (order: ShopOrder) => {
|
|
||||||
console.log('撤销申请:', order);
|
|
||||||
Taro.showToast({
|
|
||||||
title: '撤销申请功能开发中',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 取消订单
|
// 取消订单
|
||||||
const cancelOrder = (order: ShopOrder) => {
|
const cancelOrder = (order: ShopOrder) => {
|
||||||
setOrderToCancel(order);
|
setOrderToCancel(order);
|
||||||
@@ -437,7 +412,7 @@ function OrderList(props: OrderListProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 立即支付
|
// 立即支付
|
||||||
const payOrder = async (order: ShopOrder) => {
|
const payOrder = async (order: OrderWithGoods) => {
|
||||||
try {
|
try {
|
||||||
if (!order.orderId || !order.orderNo) {
|
if (!order.orderId || !order.orderNo) {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
@@ -447,6 +422,11 @@ function OrderList(props: OrderListProps) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (payingOrderId === order.orderId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setPayingOrderId(order.orderId);
|
||||||
|
|
||||||
// 检查订单是否已过期
|
// 检查订单是否已过期
|
||||||
if (order.createTime && isPaymentExpired(order.createTime)) {
|
if (order.createTime && isPaymentExpired(order.createTime)) {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
@@ -475,11 +455,34 @@ function OrderList(props: OrderListProps) {
|
|||||||
|
|
||||||
Taro.showLoading({title: '发起支付...'});
|
Taro.showLoading({title: '发起支付...'});
|
||||||
|
|
||||||
// 构建商品数据
|
// 构建商品数据:优先使用列表已加载的商品信息;缺失时再补拉一次,避免goodsItems为空导致后端拒绝/再次支付失败
|
||||||
const goodsItems = order.orderGoods?.map(goods => ({
|
let orderGoods = order.orderGoodsList || [];
|
||||||
goodsId: goods.goodsId,
|
if (!orderGoods.length) {
|
||||||
quantity: goods.totalNum || 1
|
try {
|
||||||
})) || [];
|
orderGoods = (await listShopOrderGoods({orderId: order.orderId})) || [];
|
||||||
|
} catch (e) {
|
||||||
|
// 继续走下面的校验提示
|
||||||
|
console.error('补拉订单商品失败:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const goodsItems = orderGoods
|
||||||
|
.filter(g => !!g.goodsId)
|
||||||
|
.map(goods => ({
|
||||||
|
goodsId: goods.goodsId as number,
|
||||||
|
quantity: goods.totalNum || 1,
|
||||||
|
// 若后端按SKU计算价格/库存,补齐SKU/规格信息更安全
|
||||||
|
skuId: (goods as any).skuId,
|
||||||
|
specInfo: (goods as any).spec || (goods as any).specInfo
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!goodsItems.length) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '订单商品信息缺失,请稍后重试',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 对于已存在的订单,我们需要重新发起支付
|
// 对于已存在的订单,我们需要重新发起支付
|
||||||
// 构建支付请求数据,包含完整的商品信息
|
// 构建支付请求数据,包含完整的商品信息
|
||||||
@@ -488,7 +491,13 @@ function OrderList(props: OrderListProps) {
|
|||||||
orderNo: order.orderNo,
|
orderNo: order.orderNo,
|
||||||
goodsItems: goodsItems,
|
goodsItems: goodsItems,
|
||||||
addressId: order.addressId,
|
addressId: order.addressId,
|
||||||
payType: PaymentType.WECHAT
|
payType: PaymentType.WECHAT,
|
||||||
|
// 尽量携带原订单信息,避免后端重新计算/校验不一致(如使用了优惠券/自提等)
|
||||||
|
couponId: order.couponId,
|
||||||
|
deliveryType: order.deliveryType,
|
||||||
|
selfTakeMerchantId: order.selfTakeMerchantId,
|
||||||
|
comments: order.comments,
|
||||||
|
title: order.title
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('重新支付数据:', paymentData);
|
console.log('重新支付数据:', paymentData);
|
||||||
@@ -506,6 +515,7 @@ function OrderList(props: OrderListProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 调用微信支付
|
// 调用微信支付
|
||||||
|
try {
|
||||||
await Taro.requestPayment({
|
await Taro.requestPayment({
|
||||||
timeStamp: result.timeStamp,
|
timeStamp: result.timeStamp,
|
||||||
nonceStr: result.nonceStr,
|
nonceStr: result.nonceStr,
|
||||||
@@ -513,6 +523,18 @@ function OrderList(props: OrderListProps) {
|
|||||||
signType: (result.signType || 'MD5') as 'MD5' | 'HMAC-SHA256',
|
signType: (result.signType || 'MD5') as 'MD5' | 'HMAC-SHA256',
|
||||||
paySign: result.paySign,
|
paySign: result.paySign,
|
||||||
});
|
});
|
||||||
|
} catch (payError: any) {
|
||||||
|
const msg: string = payError?.errMsg || payError?.message || '';
|
||||||
|
if (msg.includes('cancel')) {
|
||||||
|
// 用户主动取消,不当作“失败”强提示
|
||||||
|
Taro.showToast({
|
||||||
|
title: '已取消支付',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw payError;
|
||||||
|
}
|
||||||
|
|
||||||
// 支付成功
|
// 支付成功
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
@@ -533,13 +555,14 @@ function OrderList(props: OrderListProps) {
|
|||||||
console.error('支付失败:', error);
|
console.error('支付失败:', error);
|
||||||
|
|
||||||
let errorMessage = '支付失败,请重试';
|
let errorMessage = '支付失败,请重试';
|
||||||
if (error.message) {
|
const rawMsg: string = error?.errMsg || error?.message || '';
|
||||||
if (error.message.includes('cancel')) {
|
if (rawMsg) {
|
||||||
|
if (rawMsg.includes('cancel')) {
|
||||||
errorMessage = '用户取消支付';
|
errorMessage = '用户取消支付';
|
||||||
} else if (error.message.includes('余额不足')) {
|
} else if (rawMsg.includes('余额不足')) {
|
||||||
errorMessage = '账户余额不足';
|
errorMessage = '账户余额不足';
|
||||||
} else {
|
} else {
|
||||||
errorMessage = error.message;
|
errorMessage = rawMsg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -549,13 +572,13 @@ function OrderList(props: OrderListProps) {
|
|||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
Taro.hideLoading();
|
Taro.hideLoading();
|
||||||
|
setPayingOrderId(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
void reload(true); // 首次加载或tab切换时重置页码
|
void reload(true); // 首次加载、tab切换或搜索条件变化时重置页码
|
||||||
}, [tapIndex]); // 只监听tapIndex变化,避免reload依赖循环
|
}, [reload]);
|
||||||
|
|
||||||
// 监听外部statusFilter变化,同步更新tab索引
|
// 监听外部statusFilter变化,同步更新tab索引
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -705,8 +728,8 @@ function OrderList(props: OrderListProps) {
|
|||||||
|
|
||||||
{/* 商品信息 */}
|
{/* 商品信息 */}
|
||||||
<View className={'goods-info'}>
|
<View className={'goods-info'}>
|
||||||
{item.orderGoods && item.orderGoods.length > 0 ? (
|
{item.orderGoodsList && item.orderGoodsList.length > 0 ? (
|
||||||
item.orderGoods.map((goods, goodsIndex) => (
|
item.orderGoodsList.map((goods, goodsIndex) => (
|
||||||
<View key={goodsIndex} className={'flex items-center mb-2'}>
|
<View key={goodsIndex} className={'flex items-center mb-2'}>
|
||||||
<Image
|
<Image
|
||||||
src={goods.image || '/default-goods.png'}
|
src={goods.image || '/default-goods.png'}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {Space, NavBar, Button, Input} from '@nutui/nutui-react-taro'
|
|||||||
import {Search, Filter, ArrowLeft} from '@nutui/icons-react-taro'
|
import {Search, Filter, ArrowLeft} from '@nutui/icons-react-taro'
|
||||||
import {View} from '@tarojs/components';
|
import {View} from '@tarojs/components';
|
||||||
import OrderList from "./components/OrderList";
|
import OrderList from "./components/OrderList";
|
||||||
import {useRouter} from '@tarojs/taro'
|
import {useDidShow, useRouter} from '@tarojs/taro'
|
||||||
import {ShopOrderParam} from "@/api/shop/shopOrder/model";
|
import {ShopOrderParam} from "@/api/shop/shopOrder/model";
|
||||||
import './order.scss'
|
import './order.scss'
|
||||||
|
|
||||||
@@ -72,6 +72,17 @@ function Order() {
|
|||||||
reload().then()
|
reload().then()
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// 页面从其它页面返回/重新展示时,刷新一次列表数据
|
||||||
|
// 典型场景:微信支付取消后返回到待支付列表,需要重新拉取订单/商品信息,避免使用旧数据再次支付失败
|
||||||
|
useDidShow(() => {
|
||||||
|
const statusFilter =
|
||||||
|
params.statusFilter != undefined && params.statusFilter !== ''
|
||||||
|
? parseInt(params.statusFilter)
|
||||||
|
: -1;
|
||||||
|
// 同步路由上的statusFilter,并触发子组件重新拉取列表
|
||||||
|
setSearchParams(prev => ({ ...prev, statusFilter }));
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className="bg-gray-50 min-h-screen">
|
<View className="bg-gray-50 min-h-screen">
|
||||||
<View style={{height: `${statusBarHeight || 0}px`, backgroundColor: '#ffffff'}}></View>
|
<View style={{height: `${statusBarHeight || 0}px`, backgroundColor: '#ffffff'}}></View>
|
||||||
|
|||||||
Reference in New Issue
Block a user