feat(add): 新增多页面新增和编辑表单功能
- 添加编辑和新增收货地址页面,支持表单数据加载和提交 - 新增应用密钥凭证、新增应用操作动态、新增应用成员、新增应用版本页面配置 - 实现文章新增及编辑页面,包含图片上传及多种文章属性配置 - 增加注册会员页面,支持头像上传、手机号获取和邀请人关系处理 - 引入统一表单提交成功和失败处理,支持编辑模式数据回显 - 配置统一eslint和editorconfig规则,增强代码规范和编辑体验 - 新增.gitignore规则,屏蔽无关文件和目录,优化版本管理
This commit is contained in:
4
src/rider/index.config.ts
Normal file
4
src/rider/index.config.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '骑手中心'
|
||||
})
|
||||
|
||||
197
src/rider/index.tsx
Normal file
197
src/rider/index.tsx
Normal file
@@ -0,0 +1,197 @@
|
||||
import React, { useCallback, useState, useEffect } from 'react'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { View, Text } from '@tarojs/components'
|
||||
import { Avatar, Button, ConfigProvider, Grid, Empty } from '@nutui/nutui-react-taro'
|
||||
import { User, Location, Order, Refresh } from '@nutui/icons-react-taro'
|
||||
import { useThemeStyles } from '@/hooks/useTheme'
|
||||
import { useUser } from '@/hooks/useUser'
|
||||
import { listShopOrder } from '@/api/shop/shopOrder'
|
||||
import type { ShopOrder } from '@/api/shop/shopOrder/model'
|
||||
|
||||
const RiderIndex: React.FC = () => {
|
||||
const themeStyles = useThemeStyles()
|
||||
const { isLoggedIn, loading: userLoading, getAvatarUrl, getDisplayName } = useUser()
|
||||
|
||||
const [stats, setStats] = useState({ pending: 0, delivering: 0, completed: 0, today: 0 })
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const loadStats = useCallback(async () => {
|
||||
const userId = Taro.getStorageSync('UserId')
|
||||
if (!userId) return
|
||||
|
||||
setLoading(true)
|
||||
try {
|
||||
// 查询待配送和配送中的订单
|
||||
const [pendingRes, deliveringRes, completedRes] = await Promise.allSettled([
|
||||
listShopOrder({ riderId: Number(userId), orderStatus: 30, page: 1, limit: 1 }),
|
||||
listShopOrder({ riderId: Number(userId), orderStatus: 40, page: 1, limit: 1 }),
|
||||
listShopOrder({ riderId: Number(userId), orderStatus: 50, page: 1, limit: 1 }),
|
||||
])
|
||||
|
||||
const pending = pendingRes.status === 'fulfilled' ? (pendingRes.value?.count || 0) : 0
|
||||
const delivering = deliveringRes.status === 'fulfilled' ? (deliveringRes.value?.count || 0) : 0
|
||||
const completed = completedRes.status === 'fulfilled' ? (completedRes.value?.count || 0) : 0
|
||||
|
||||
setStats({ pending, delivering, completed, today: pending + delivering })
|
||||
} catch (e) {
|
||||
console.error('加载骑手统计失败', e)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoggedIn) {
|
||||
loadStats()
|
||||
}
|
||||
}, [isLoggedIn, loadStats])
|
||||
|
||||
const navigateToPage = (url: string) => {
|
||||
if (!isLoggedIn) {
|
||||
Taro.showToast({ title: '请先登录', icon: 'none', duration: 1500 })
|
||||
return
|
||||
}
|
||||
Taro.navigateTo({ url })
|
||||
}
|
||||
|
||||
if (!isLoggedIn && !userLoading) {
|
||||
return (
|
||||
<View className="bg-gray-100 min-h-screen p-4">
|
||||
<View className="bg-white rounded-xl p-4">
|
||||
<Text className="text-gray-700">请先登录后再进入骑手中心</Text>
|
||||
<View className="mt-3">
|
||||
<Button type="primary" onClick={() => Taro.navigateTo({ url: '/passport/login' })}>
|
||||
去登录
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const statCards = [
|
||||
{ label: '待取货', value: stats.pending, color: 'bg-orange-50', textColor: 'text-orange-600' },
|
||||
{ label: '配送中', value: stats.delivering, color: 'bg-blue-50', textColor: 'text-blue-600' },
|
||||
{ label: '已完成', value: stats.completed, color: 'bg-green-50', textColor: 'text-green-600' },
|
||||
{ label: '今日任务', value: stats.today, color: 'bg-purple-50', textColor: 'text-purple-600' },
|
||||
]
|
||||
|
||||
return (
|
||||
<View className="bg-gray-100 min-h-screen">
|
||||
{/* 头部信息 */}
|
||||
<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="flex items-center justify-between relative z-10">
|
||||
<View className="flex items-center">
|
||||
<Avatar
|
||||
size="50"
|
||||
src={getAvatarUrl()}
|
||||
icon={<User />}
|
||||
className="mr-4"
|
||||
style={{ border: '2px solid rgba(255, 255, 255, 0.3)' }}
|
||||
/>
|
||||
<View>
|
||||
<View className="text-white text-lg font-bold mb-1">{getDisplayName()}</View>
|
||||
<View className="text-sm" style={{ color: 'rgba(255, 255, 255, 0.8)' }}>
|
||||
骑手中心
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<Button
|
||||
size="small"
|
||||
style={{
|
||||
background: 'rgba(255, 255, 255, 0.18)',
|
||||
color: '#fff',
|
||||
border: '1px solid rgba(255, 255, 255, 0.25)'
|
||||
}}
|
||||
loading={loading}
|
||||
onClick={loadStats}
|
||||
>
|
||||
<Refresh size="14" />
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 数据统计卡片 */}
|
||||
<View className="mx-4 -mt-6 rounded-xl bg-white p-4 relative z-10">
|
||||
<View className="font-semibold text-gray-400 text-sm mb-3">今日概览</View>
|
||||
<View className="grid grid-cols-4 gap-2">
|
||||
{statCards.map((card) => (
|
||||
<View key={card.label} className={`${card.color} rounded-lg p-3 text-center`}>
|
||||
<View className={`text-2xl font-bold ${card.textColor}`}>{card.value}</View>
|
||||
<View className="text-xs text-gray-500 mt-1">{card.label}</View>
|
||||
</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={3}
|
||||
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-orange-50 rounded-xl flex items-center justify-center mx-auto mb-2">
|
||||
<Order color="#f97316" size="20" />
|
||||
</View>
|
||||
{stats.pending > 0 && (
|
||||
<View className="absolute top-0 right-2 bg-red-500 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center">
|
||||
{stats.pending > 9 ? '9+' : stats.pending}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</Grid.Item>
|
||||
|
||||
<Grid.Item text="配送中" onClick={() => navigateToPage('/rider/orders/index')}>
|
||||
<View className="text-center">
|
||||
<View className="w-12 h-12 bg-blue-500 rounded-xl flex items-center justify-center mx-auto mb-2">
|
||||
<Location color="#3b82f6" size="20" />
|
||||
</View>
|
||||
{stats.delivering > 0 && (
|
||||
<View className="absolute top-0 right-2 bg-red-500 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center">
|
||||
{stats.delivering > 9 ? '9+' : stats.delivering}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</Grid.Item>
|
||||
|
||||
<Grid.Item text="全部订单" onClick={() => navigateToPage('/rider/orders/index')}>
|
||||
<View className="text-center">
|
||||
<View className="w-12 h-12 bg-gray-50 rounded-xl flex items-center justify-center mx-auto mb-2">
|
||||
<Order color="#6b7280" size="20" />
|
||||
</View>
|
||||
</View>
|
||||
</Grid.Item>
|
||||
</Grid>
|
||||
</ConfigProvider>
|
||||
</View>
|
||||
|
||||
{/* 提示信息 */}
|
||||
{stats.today === 0 && !loading && (
|
||||
<View className="mx-4 mt-4">
|
||||
<Empty description="暂无配送任务" imageSize={60} />
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View className="h-20"></View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default RiderIndex
|
||||
5
src/rider/orders/index.config.ts
Normal file
5
src/rider/orders/index.config.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '骑手订单',
|
||||
navigationBarTextStyle: 'black'
|
||||
})
|
||||
|
||||
50
src/rider/orders/index.tsx
Normal file
50
src/rider/orders/index.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import {useMemo} from 'react'
|
||||
import Taro from '@tarojs/taro'
|
||||
import {Button} from '@nutui/nutui-react-taro'
|
||||
import {View, Text} from '@tarojs/components'
|
||||
import OrderList from '@/user/order/components/OrderList'
|
||||
|
||||
export default function RiderOrders() {
|
||||
const isLoggedIn = useMemo(() => {
|
||||
return !!Taro.getStorageSync('access_token') && !!Taro.getStorageSync('UserId')
|
||||
}, [])
|
||||
|
||||
const riderId = useMemo(() => {
|
||||
const raw = Number(Taro.getStorageSync('UserId'))
|
||||
return Number.isFinite(raw) && raw > 0 ? raw : undefined
|
||||
}, [])
|
||||
|
||||
if (!isLoggedIn) {
|
||||
return (
|
||||
<View className="bg-gray-50 min-h-screen p-4">
|
||||
<View className="bg-white rounded-lg p-4">
|
||||
<Text className="text-sm text-gray-700">请先登录</Text>
|
||||
<View className="mt-3">
|
||||
<Button type="primary" size="small" onClick={() => Taro.navigateTo({url: '/passport/login'})}>
|
||||
去登录
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
if (!riderId) {
|
||||
return (
|
||||
<View className="bg-gray-50 min-h-screen p-4">
|
||||
<View className="bg-white rounded-lg p-4">
|
||||
<Text className="text-sm text-gray-700">未获取到骑手信息(UserId)</Text>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<View className="bg-gray-50 min-h-screen">
|
||||
<View className="px-3">
|
||||
<OrderList mode="rider" baseParams={{riderId}} />
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user