Kaynağa Gözat

💄 style(order): 优化订单页面UI设计

- 引入Card、Navbar等组件重构订单详情和订单列表页面布局
- 统一使用主题色和中性色替换硬编码颜色值
- 优化状态标签样式,增加背景色提升视觉层次
- 调整间距和内边距,优化整体排版和视觉呼吸感
- 改进空状态和错误状态的图标大小和文本样式

✨ feat(order): 添加导航栏组件和底部占位

- 在订单页面添加Navbar组件,支持返回功能
- 为滚动区域添加底部占位,避免内容被操作按钮遮挡
- 统一按钮样式,使用variant属性替代自定义类名

♻️ refactor(order): 重构订单卡片组件

- 将订单卡片重构为使用Card组件的结构
- 拆分CardHeader、CardContent和CardFooter,提升代码可维护性
- 优化getStatusColor函数,增加背景色支持
- 移除冗余的className定义,使用组件props控制样式
yourname 3 ay önce
ebeveyn
işleme
3bc634083c
2 değiştirilmiş dosya ile 209 ekleme ve 179 silme
  1. 123 99
      mini/src/pages/order-detail/index.tsx
  2. 86 80
      mini/src/pages/orders/index.tsx

+ 123 - 99
mini/src/pages/order-detail/index.tsx

@@ -1,9 +1,12 @@
 import { useState, useEffect } from 'react';
-import { View, Text, ScrollView, Button } from '@tarojs/components';
+import { View, Text, ScrollView } from '@tarojs/components';
+import { Button } from '@/components/ui/button';
+import { Card, CardContent, CardHeader } from '@/components/ui/card';
 import Taro from '@tarojs/taro';
 import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
 import { orderClient } from '@/api';
 import { OrderStatus } from '@/types/order.types';
+import { Navbar } from '@/components/ui/navbar';
 
 const OrderDetailPage = () => {
   const [orderId, setOrderId] = useState<number | null>(null);
@@ -111,7 +114,7 @@ const OrderDetailPage = () => {
       case OrderStatus.CANCELLED:
         return 'text-error';
       default:
-        return 'text-muted-foreground';
+        return 'text-gray-500';
     }
   };
 
@@ -128,7 +131,7 @@ const OrderDetailPage = () => {
       <View className="flex flex-col items-center justify-center py-16">
         <Text className="text-lg text-error mb-4">加载失败</Text>
         <Button
-          className="bg-primary text-primary-foreground px-4 py-2 rounded-md"
+          variant="default"
           onClick={() => Taro.navigateBack()}
         >
           返回
@@ -138,133 +141,154 @@ const OrderDetailPage = () => {
   }
 
   return (
-    <View className="min-h-screen bg-background">
-      <ScrollView className="px-4 py-4" scrollY>
+    <View className="min-h-screen bg-gray-50">
+      <Navbar
+        title="订单详情"
+        backgroundColor="bg-primary"
+        textColor="text-white"
+        border={false}
+        leftIcon="i-heroicons-arrow-left-20-solid"
+        onClickLeft={() => Taro.navigateBack()}
+        rightIcon=""
+      />
+      <ScrollView className="px-4 pt-4" scrollY>
         {/* 订单基本信息 */}
-        <View className="bg-card rounded-card shadow-medium p-card border border-border mb-4">
-          <Text className="text-lg font-semibold mb-4">订单信息</Text>
-
-          <View className="space-y-3">
-            <View className="flex justify-between">
-              <Text className="text-muted-foreground">订单号:</Text>
-              <Text>{order.id}</Text>
-            </View>
+        <Card className="rounded-card shadow-medium mb-4">
+          <CardHeader className="border-b border-gray-200">
+            <Text className="text-lg font-semibold">订单信息</Text>
+          </CardHeader>
+          <CardContent className="p-card">
+            <View className="space-y-3">
+              <View className="flex justify-between">
+                <Text className="text-gray-500">订单号:</Text>
+                <Text>{order.id}</Text>
+              </View>
 
-            <View className="flex justify-between">
-              <Text className="text-muted-foreground">行程路线:</Text>
-              <Text className="text-right">
-                {order.routeSnapshot?.pickupPoint} → {order.routeSnapshot?.dropoffPoint}
-              </Text>
-            </View>
+              <View className="flex justify-between">
+                <Text className="text-gray-500">行程路线:</Text>
+                <Text className="text-right">
+                  {order.routeSnapshot?.pickupPoint} → {order.routeSnapshot?.dropoffPoint}
+                </Text>
+              </View>
 
-            <View className="flex justify-between">
-              <Text className="text-muted-foreground">出发时间:</Text>
-              <Text>
-                {order.routeSnapshot?.departureTime
-                  ? new Date(order.routeSnapshot.departureTime).toLocaleString('zh-CN')
-                  : '未知时间'
-                }
-              </Text>
-            </View>
+              <View className="flex justify-between">
+                <Text className="text-gray-500">出发时间:</Text>
+                <Text>
+                  {order.routeSnapshot?.departureTime
+                    ? new Date(order.routeSnapshot.departureTime).toLocaleString('zh-CN')
+                    : '未知时间'
+                  }
+                </Text>
+              </View>
 
-            <View className="flex justify-between">
-              <Text className="text-muted-foreground">车辆型号:</Text>
-              <Text>{order.routeSnapshot?.vehicleType || '未知车型'}</Text>
-            </View>
+              <View className="flex justify-between">
+                <Text className="text-gray-500">车辆型号:</Text>
+                <Text>{order.routeSnapshot?.vehicleType || '未知车型'}</Text>
+              </View>
 
-            <View className="flex justify-between">
-              <Text className="text-muted-foreground">乘车人数:</Text>
-              <Text>{order.passengerCount}人</Text>
-            </View>
+              <View className="flex justify-between">
+                <Text className="text-gray-500">乘车人数:</Text>
+                <Text>{order.passengerCount}人</Text>
+              </View>
 
-            <View className="flex justify-between">
-              <Text className="text-muted-foreground">订单状态:</Text>
-              <Text className={`font-medium ${getStatusColor(order.status)}`}>
-                {order.status}
-              </Text>
-            </View>
+              <View className="flex justify-between">
+                <Text className="text-gray-500">订单状态:</Text>
+                <Text className={`font-medium ${getStatusColor(order.status)}`}>
+                  {order.status}
+                </Text>
+              </View>
 
-            <View className="flex justify-between">
-              <Text className="text-muted-foreground">支付状态:</Text>
-              <Text>{order.paymentStatus}</Text>
-            </View>
+              <View className="flex justify-between">
+                <Text className="text-gray-500">支付状态:</Text>
+                <Text>{order.paymentStatus}</Text>
+              </View>
 
-            <View className="flex justify-between">
-              <Text className="text-muted-foreground">订单金额:</Text>
-              <Text className="text-lg font-semibold text-primary">¥{order.totalAmount}</Text>
-            </View>
+              <View className="flex justify-between">
+                <Text className="text-gray-500">订单金额:</Text>
+                <Text className="text-lg font-semibold text-primary">¥{order.totalAmount}</Text>
+              </View>
 
-            <View className="flex justify-between">
-              <Text className="text-muted-foreground">下单时间:</Text>
-              <Text>{new Date(order.createdAt).toLocaleString('zh-CN')}</Text>
+              <View className="flex justify-between">
+                <Text className="text-gray-500">下单时间:</Text>
+                <Text>{new Date(order.createdAt).toLocaleString('zh-CN')}</Text>
+              </View>
             </View>
-          </View>
-        </View>
+          </CardContent>
+        </Card>
 
         {/* 乘客信息 */}
         {order.passengerSnapshots && order.passengerSnapshots.length > 0 && (
-          <View className="bg-card rounded-card shadow-medium p-card border border-border mb-4">
-            <Text className="text-lg font-semibold mb-4">乘客信息</Text>
-
-            <View className="space-y-3">
-              {order.passengerSnapshots.map((passenger, index) => (
-                <View key={index} className="border-b border-border pb-3 last:border-b-0 last:pb-0">
-                  <View className="flex justify-between mb-1">
-                    <Text className="font-medium">{passenger.name}</Text>
-                    <Text className="text-sm text-muted-foreground">
-                      {passenger.idType === 'ID_CARD' ? '身份证' : '护照'}
-                    </Text>
-                  </View>
-
-                  <View className="space-y-1 text-sm">
-                    <View className="flex justify-between">
-                      <Text className="text-muted-foreground">证件号码:</Text>
-                      <Text>{passenger.idNumber}</Text>
+          <Card className="rounded-card shadow-medium mb-4">
+            <CardHeader className="border-b border-gray-200">
+              <Text className="text-lg font-semibold">乘客信息</Text>
+            </CardHeader>
+            <CardContent className="p-card">
+              <View className="space-y-3">
+                {order.passengerSnapshots.map((passenger, index) => (
+                  <View key={index} className="border-b border-gray-200 pb-3 last:border-b-0 last:pb-0">
+                    <View className="flex justify-between mb-1">
+                      <Text className="font-medium">{passenger.name}</Text>
+                      <Text className="text-sm text-gray-500">
+                        {passenger.idType === 'ID_CARD' ? '身份证' : '护照'}
+                      </Text>
                     </View>
 
-                    {passenger.phone && (
+                    <View className="space-y-1 text-sm">
                       <View className="flex justify-between">
-                        <Text className="text-muted-foreground">联系电话:</Text>
-                        <Text>{passenger.phone}</Text>
+                        <Text className="text-gray-500">证件号码:</Text>
+                        <Text>{passenger.idNumber}</Text>
                       </View>
-                    )}
+
+                      {passenger.phone && (
+                        <View className="flex justify-between">
+                          <Text className="text-gray-500">联系电话:</Text>
+                          <Text>{passenger.phone}</Text>
+                        </View>
+                      )}
+                    </View>
                   </View>
-                </View>
-              ))}
-            </View>
-          </View>
+                ))}
+              </View>
+            </CardContent>
+          </Card>
         )}
 
         {/* 路线快照信息 */}
         {order.routeSnapshot && (
-          <View className="bg-card rounded-card shadow-medium p-card border border-border mb-4">
-            <Text className="text-lg font-semibold mb-4">行程详情</Text>
-
-            <View className="space-y-3">
-              <View className="flex justify-between">
-                <Text className="text-muted-foreground">活动名称:</Text>
-                <Text>{order.routeSnapshot.name}</Text>
-              </View>
+          <Card className="rounded-card shadow-medium mb-4">
+            <CardHeader className="border-b border-gray-200">
+              <Text className="text-lg font-semibold">行程详情</Text>
+            </CardHeader>
+            <CardContent className="p-card">
+              <View className="space-y-3">
+                <View className="flex justify-between">
+                  <Text className="text-gray-500">活动名称:</Text>
+                  <Text>{order.routeSnapshot.name}</Text>
+                </View>
 
-              <View className="flex justify-between">
-                <Text className="text-muted-foreground">出行方式:</Text>
-                <Text>{order.routeSnapshot.travelMode === 'DRIVING' ? '驾车' : '其他'}</Text>
-              </View>
+                <View className="flex justify-between">
+                  <Text className="text-gray-500">出行方式:</Text>
+                  <Text>{order.routeSnapshot.travelMode === 'DRIVING' ? '驾车' : '其他'}</Text>
+                </View>
 
-              <View className="flex justify-between">
-                <Text className="text-muted-foreground">单价:</Text>
-                <Text>¥{order.routeSnapshot.price}</Text>
+                <View className="flex justify-between">
+                  <Text className="text-gray-500">单价:</Text>
+                  <Text>¥{order.routeSnapshot.price}</Text>
+                </View>
               </View>
-            </View>
-          </View>
+            </CardContent>
+          </Card>
         )}
+        {/* 底部占位,避免被操作按钮遮挡 */}
+        <View className="h-20"></View>
       </ScrollView>
 
       {/* 底部操作按钮 */}
       {order.status === OrderStatus.WAITING_DEPARTURE && (
-        <View className="fixed bottom-0 left-0 right-0 bg-white border-t border-border p-4">
+        <View className="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 p-4">
           <Button
-            className="bg-error text-error-foreground w-full py-3 rounded-md"
+            variant="destructive"
+            className="w-full"
             onClick={handleCancelOrder}
             loading={cancelOrderMutation.isPending}
           >

+ 86 - 80
mini/src/pages/orders/index.tsx

@@ -1,5 +1,7 @@
-import { useState, useEffect } from 'react';
-import { View, Text, ScrollView, Button } from '@tarojs/components';
+import { useState } from 'react';
+import { View, Text, ScrollView } from '@tarojs/components';
+import { Button } from '@/components/ui/button';
+import { Card, CardContent, CardFooter } from '@/components/ui/card';
 import Taro from '@tarojs/taro';
 import { useQuery } from '@tanstack/react-query';
 import { orderClient } from '@/api';
@@ -21,95 +23,96 @@ const OrderCard = ({ order, onViewDetail }) => {
   const getStatusColor = (status: OrderStatus) => {
     switch (status) {
       case OrderStatus.WAITING_DEPARTURE:
-        return 'text-warning';
+        return 'text-warning bg-warning/10';
       case OrderStatus.IN_PROGRESS:
-        return 'text-info';
+        return 'text-info bg-info/10';
       case OrderStatus.COMPLETED:
-        return 'text-success';
+        return 'text-success bg-success/10';
       case OrderStatus.CANCELLED:
-        return 'text-error';
+        return 'text-error bg-error/10';
       default:
-        return 'text-muted-foreground';
+        return 'text-muted-foreground bg-gray-100';
     }
   };
 
   return (
-    <View
-      className="bg-card rounded-card shadow-medium p-card border border-border mb-4"
-      onClick={() => onViewDetail(order.id)}
-    >
-      {/* 订单头部 */}
-      <View className="flex justify-between items-start mb-3">
-        <View className="flex-1">
-          <Text className="text-sm text-muted-foreground">订单号:{order.id}</Text>
-          <Text className="block text-base font-medium mt-1">
-            {order.routeSnapshot?.name || '未知活动'}
-          </Text>
-        </View>
-        <View className={`px-2 py-1 rounded-full text-xs font-medium ${getStatusColor(order.status)}`}>
-          {order.status}
-        </View>
-      </View>
-
-      {/* 行程信息 */}
-      <View className="mb-3">
-        <View className="flex items-center mb-2">
+    <Card className="mb-4 rounded-card shadow-medium" onClick={() => onViewDetail(order.id)}>
+      <CardContent className="p-card">
+        {/* 订单头部 */}
+        <View className="flex justify-between items-start mb-3">
           <View className="flex-1">
-            <Text className="text-sm text-muted-foreground">出发</Text>
-            <Text className="block text-sm">{order.routeSnapshot?.pickupPoint}</Text>
+            <Text className="text-sm text-gray-500">订单号:{order.id}</Text>
+            <Text className="block text-base font-medium mt-1">
+              {order.routeSnapshot?.name || '未知活动'}
+            </Text>
           </View>
-          <Text className="mx-2 text-muted-foreground">→</Text>
-          <View className="flex-1">
-            <Text className="text-sm text-muted-foreground">到达</Text>
-            <Text className="block text-sm">{order.routeSnapshot?.dropoffPoint}</Text>
+          <View className={`px-3 py-1 rounded-full text-xs font-medium ${getStatusColor(order.status)}`}>
+            {order.status}
           </View>
         </View>
 
-        <View className="space-y-1 text-sm">
-          <View className="flex justify-between">
-            <Text className="text-muted-foreground">出发时间:</Text>
-            <Text>
-              {order.routeSnapshot?.departureTime
-                ? new Date(order.routeSnapshot.departureTime).toLocaleString('zh-CN')
-                : '未知时间'
-              }
-            </Text>
+        {/* 行程信息 */}
+        <View className="mb-3">
+          <View className="flex items-center mb-2">
+            <View className="flex-1">
+              <Text className="text-sm text-gray-500">出发</Text>
+              <Text className="block text-sm">{order.routeSnapshot?.pickupPoint}</Text>
+            </View>
+            <Text className="mx-2 text-gray-500">→</Text>
+            <View className="flex-1">
+              <Text className="text-sm text-gray-500">到达</Text>
+              <Text className="block text-sm">{order.routeSnapshot?.dropoffPoint}</Text>
+            </View>
           </View>
-          <View className="flex justify-between">
-            <Text className="text-muted-foreground">车辆型号:</Text>
-            <Text>{order.routeSnapshot?.vehicleType || '未知车型'}</Text>
-          </View>
-          <View className="flex justify-between">
-            <Text className="text-muted-foreground">乘车人数:</Text>
-            <Text>{order.passengerCount}人</Text>
+
+          <View className="space-y-1 text-sm">
+            <View className="flex justify-between">
+              <Text className="text-gray-500">出发时间:</Text>
+              <Text>
+                {order.routeSnapshot?.departureTime
+                  ? new Date(order.routeSnapshot.departureTime).toLocaleString('zh-CN')
+                  : '未知时间'
+                }
+              </Text>
+            </View>
+            <View className="flex justify-between">
+              <Text className="text-gray-500">车辆型号:</Text>
+              <Text>{order.routeSnapshot?.vehicleType || '未知车型'}</Text>
+            </View>
+            <View className="flex justify-between">
+              <Text className="text-gray-500">乘车人数:</Text>
+              <Text>{order.passengerCount}人</Text>
+            </View>
           </View>
         </View>
-      </View>
+      </CardContent>
 
-      {/* 价格信息 */}
-      <View className="flex justify-between items-center border-t border-border pt-3">
-        <Text className="text-sm text-muted-foreground">
-          下单时间:{new Date(order.createdAt).toLocaleString('zh-CN')}
-        </Text>
-        <View className="flex items-center">
-          <Text className="text-sm text-muted-foreground mr-2">订单金额</Text>
-          <Text className="text-lg font-semibold text-primary">¥{order.totalAmount}</Text>
+      <CardFooter className="border-t border-gray-200 pt-3">
+        <View className="flex justify-between items-center w-full">
+          <Text className="text-sm text-gray-500">
+            下单时间:{new Date(order.createdAt).toLocaleString('zh-CN')}
+          </Text>
+          <View className="flex items-center">
+            <Text className="text-sm text-gray-500 mr-2">订单金额</Text>
+            <Text className="text-lg font-semibold text-primary">¥{order.totalAmount}</Text>
+          </View>
         </View>
-      </View>
 
-      {/* 操作按钮 */}
-      <View className="flex justify-end mt-3">
-        <Button
-          className="bg-primary text-primary-foreground px-4 py-2 rounded-md text-sm"
-          onClick={(e) => {
-            e.stopPropagation();
-            onViewDetail(order.id);
-          }}
-        >
-          查看详情
-        </Button>
-      </View>
-    </View>
+        {/* 操作按钮 */}
+        <View className="flex justify-end w-full mt-3">
+          <Button
+            variant="default"
+            size="sm"
+            onClick={(e) => {
+              e.stopPropagation();
+              onViewDetail(order.id);
+            }}
+          >
+            查看详情
+          </Button>
+        </View>
+      </CardFooter>
+    </Card>
   );
 };
 
@@ -133,10 +136,10 @@ const EmptyOrders = ({ currentTab }) => {
   };
 
   return (
-    <View className="flex flex-col items-center justify-center py-16">
-      <Text className="text-4xl mb-4">📋</Text>
-      <Text className="text-lg text-muted-foreground mb-2">{getEmptyMessage()}</Text>
-      <Text className="text-sm text-muted-foreground">您还没有相关状态的订单</Text>
+    <View className="flex flex-col items-center justify-center py-20">
+      <Text className="text-6xl mb-6 text-gray-300">📋</Text>
+      <Text className="text-lg text-gray-500 mb-3">{getEmptyMessage()}</Text>
+      <Text className="text-sm text-gray-400">您还没有相关状态的订单</Text>
     </View>
   );
 };
@@ -195,7 +198,7 @@ const OrdersPage = () => {
       <View className="flex flex-col items-center justify-center py-16">
         <Text className="text-lg text-error mb-4">加载失败</Text>
         <Button
-          className="bg-primary text-primary-foreground px-4 py-2 rounded-md"
+          variant="default"
           onClick={() => refetch()}
         >
           重新加载
@@ -205,9 +208,12 @@ const OrdersPage = () => {
   }
 
   return (
-    <TabBarLayout activeKey="orders" className='bg-background'>
+    <TabBarLayout activeKey="orders" className='bg-gray-50'>
       <Navbar
         title="我的订单"
+        backgroundColor="bg-primary"
+        textColor="text-white"
+        border={false}
         leftIcon=""
         rightIcon=""
       />
@@ -223,8 +229,8 @@ const OrdersPage = () => {
               <View
                 key={tab.key}
                 className={`px-4 py-2 rounded-full mr-3 cursor-pointer transition-colors whitespace-nowrap ${currentTab === tab.key
-                    ? 'bg-primary text-primary-foreground'
-                    : 'bg-muted text-muted-foreground'
+                    ? 'bg-primary text-white'
+                    : 'bg-gray-100 text-gray-600'
                   }`}
                 onClick={() => handleTabChange(tab.key)}
               >