Browse Source

✨ feat(page): 为购物车和商品列表页面添加底部导航栏布局
- 引入TabBarLayout组件并设置activeKey
- 购物车页面使用TabBarLayout包裹内容,activeKey设为"cart"
- 商品列表页面使用TabBarLayout包裹内容,activeKey设为"goods-list"

♻️ refactor(app): 优化应用路由配置
- 移除pages配置中的payment页面路由

yourname 3 months ago
parent
commit
d4633c6d84
3 changed files with 252 additions and 247 deletions
  1. 1 2
      mini/src/app.config.ts
  2. 134 131
      mini/src/pages/cart/index.tsx
  3. 117 114
      mini/src/pages/goods-list/index.tsx

+ 1 - 2
mini/src/app.config.ts

@@ -14,8 +14,7 @@ export default defineAppConfig({
     'pages/order-detail/index',
     'pages/order-submit/index',
     'pages/address-manage/index',
-    'pages/address-edit/index',
-    'pages/payment/index'
+    'pages/address-edit/index'
   ],
   window: {
     backgroundTextStyle: 'light',

+ 134 - 131
mini/src/pages/cart/index.tsx

@@ -6,6 +6,7 @@ import { Card } from '@/components/ui/card'
 import { Button } from '@/components/ui/button'
 import { Image } from '@/components/ui/image'
 import { useCart } from '@/utils/cart'
+import { TabBarLayout } from '@/layouts/tab-bar-layout'
 
 export default function CartPage() {
   const { cart, updateQuantity, removeFromCart, clearCart } = useCart()
@@ -58,154 +59,156 @@ export default function CartPage() {
   }
 
   return (
-    <View className="min-h-screen bg-gray-50">
-      <Navbar
-        title="购物车"
-        leftIcon="i-heroicons-chevron-left-20-solid"
-        onClickLeft={() => Taro.navigateBack()}
-        rightIcon="i-heroicons-trash-20-solid"
-        onClickRight={() => {
-          Taro.showModal({
-            title: '清空购物车',
-            content: '确定要清空购物车吗?',
-            success: (res) => {
-              if (res.confirm) {
-                clearCart()
-                setSelectedItems([])
+    <TabBarLayout activeKey="cart">
+      <View className="min-h-screen bg-gray-50">
+        <Navbar
+          title="购物车"
+          leftIcon="i-heroicons-chevron-left-20-solid"
+          onClickLeft={() => Taro.navigateBack()}
+          rightIcon="i-heroicons-trash-20-solid"
+          onClickRight={() => {
+            Taro.showModal({
+              title: '清空购物车',
+              content: '确定要清空购物车吗?',
+              success: (res) => {
+                if (res.confirm) {
+                  clearCart()
+                  setSelectedItems([])
+                }
               }
-            }
-          })
-        }}
-      />
-      
-      <ScrollView className="h-screen pt-12 pb-20">
-        <View className="px-4 py-4">
-          {cart.items.length === 0 ? (
-            <View className="flex flex-col items-center justify-center py-20">
-              <View className="i-heroicons-shopping-cart-20-solid w-16 h-16 text-gray-300 mb-4" />
-              <Text className="text-gray-500 mb-4">购物车是空的</Text>
-              <Button
-                onClick={() => Taro.navigateTo({ url: '/pages/goods-list/index' })}
-              >
-                去逛逛
-              </Button>
-            </View>
-          ) : (
-            <>
-              {/* 全选 */}
-              <View className="bg-white rounded-lg p-4 mb-4 flex items-center">
-                <View 
-                  className={`w-5 h-5 border-2 rounded-full flex items-center justify-center mr-3 ${
-                    selectedItems.length === cart.items.length 
-                      ? 'bg-blue-500 border-blue-500' 
-                      : 'border-gray-300'
-                  }`}
-                  onClick={toggleSelectAll}
+            })
+          }}
+        />
+        
+        <ScrollView className="h-screen pt-12">
+          <View className="px-4 py-4">
+            {cart.items.length === 0 ? (
+              <View className="flex flex-col items-center justify-center py-20">
+                <View className="i-heroicons-shopping-cart-20-solid w-16 h-16 text-gray-300 mb-4" />
+                <Text className="text-gray-500 mb-4">购物车是空的</Text>
+                <Button
+                  onClick={() => Taro.navigateTo({ url: '/pages/goods-list/index' })}
                 >
-                  {selectedItems.length === cart.items.length && (
-                    <View className="i-heroicons-check-20-solid w-3 h-3 text-white" />
-                  )}
-                </View>
-                <Text className="text-gray-700">全选 ({cart.items.length}件商品)</Text>
+                  去逛逛
+                </Button>
               </View>
+            ) : (
+              <>
+                {/* 全选 */}
+                <View className="bg-white rounded-lg p-4 mb-4 flex items-center">
+                  <View 
+                    className={`w-5 h-5 border-2 rounded-full flex items-center justify-center mr-3 ${
+                      selectedItems.length === cart.items.length 
+                        ? 'bg-blue-500 border-blue-500' 
+                        : 'border-gray-300'
+                    }`}
+                    onClick={toggleSelectAll}
+                  >
+                    {selectedItems.length === cart.items.length && (
+                      <View className="i-heroicons-check-20-solid w-3 h-3 text-white" />
+                    )}
+                  </View>
+                  <Text className="text-gray-700">全选 ({cart.items.length}件商品)</Text>
+                </View>
 
-              {/* 商品列表 */}
-              {cart.items.map((item) => (
-                <Card key={item.id} className="mb-4">
-                  <View className="p-4">
-                    <View className="flex items-start">
-                      <View 
-                        className={`w-5 h-5 border-2 rounded-full flex items-center justify-center mr-3 mt-8 ${
-                          selectedItems.includes(item.id)
-                            ? 'bg-blue-500 border-blue-500'
-                            : 'border-gray-300'
-                        }`}
-                        onClick={() => toggleSelectItem(item.id)}
-                      >
-                        {selectedItems.includes(item.id) && (
-                          <View className="i-heroicons-check-20-solid w-3 h-3 text-white" />
-                        )}
-                      </View>
-                      
-                      <Image 
-                        src={item.image}
-                        className="w-20 h-20 rounded-lg mr-3"
-                        mode="aspectFill"
-                      />
-                      
-                      <View className="flex-1">
-                        <Text className="text-sm font-medium text-gray-900 mb-2 line-clamp-2">
-                          {item.name}
-                        </Text>
+                {/* 商品列表 */}
+                {cart.items.map((item) => (
+                  <Card key={item.id} className="mb-4">
+                    <View className="p-4">
+                      <View className="flex items-start">
+                        <View 
+                          className={`w-5 h-5 border-2 rounded-full flex items-center justify-center mr-3 mt-8 ${
+                            selectedItems.includes(item.id)
+                              ? 'bg-blue-500 border-blue-500'
+                              : 'border-gray-300'
+                          }`}
+                          onClick={() => toggleSelectItem(item.id)}
+                        >
+                          {selectedItems.includes(item.id) && (
+                            <View className="i-heroicons-check-20-solid w-3 h-3 text-white" />
+                          )}
+                        </View>
                         
-                        <Text className="text-red-500 font-bold mb-2">
-                          ¥{item.price.toFixed(2)}
-                        </Text>
+                        <Image 
+                          src={item.image}
+                          className="w-20 h-20 rounded-lg mr-3"
+                          mode="aspectFill"
+                        />
                         
-                        <View className="flex items-center justify-between">
-                          <View className="flex items-center border border-gray-300 rounded">
-                            <Button
-                              size="sm"
-                              variant="ghost"
-                              className="px-2"
-                              onClick={() => updateQuantity(item.id, item.quantity - 1)}
-                            >
-                              -
-                            </Button>
-                            <Text className="px-3 py-1 border-x border-gray-300 text-sm">
-                              {item.quantity}
-                            </Text>
+                        <View className="flex-1">
+                          <Text className="text-sm font-medium text-gray-900 mb-2 line-clamp-2">
+                            {item.name}
+                          </Text>
+                          
+                          <Text className="text-red-500 font-bold mb-2">
+                            ¥{item.price.toFixed(2)}
+                          </Text>
+                          
+                          <View className="flex items-center justify-between">
+                            <View className="flex items-center border border-gray-300 rounded">
+                              <Button
+                                size="sm"
+                                variant="ghost"
+                                className="px-2"
+                                onClick={() => updateQuantity(item.id, item.quantity - 1)}
+                              >
+                                -
+                              </Button>
+                              <Text className="px-3 py-1 border-x border-gray-300 text-sm">
+                                {item.quantity}
+                              </Text>
+                              <Button
+                                size="sm"
+                                variant="ghost"
+                                className="px-2"
+                                onClick={() => updateQuantity(item.id, item.quantity + 1)}
+                              >
+                                +
+                              </Button>
+                            </View>
+                            
                             <Button
                               size="sm"
                               variant="ghost"
-                              className="px-2"
-                              onClick={() => updateQuantity(item.id, item.quantity + 1)}
+                              className="text-red-500"
+                              onClick={() => removeFromCart(item.id)}
                             >
-                              +
+                              删除
                             </Button>
                           </View>
-                          
-                          <Button
-                            size="sm"
-                            variant="ghost"
-                            className="text-red-500"
-                            onClick={() => removeFromCart(item.id)}
-                          >
-                            删除
-                          </Button>
                         </View>
                       </View>
                     </View>
-                  </View>
-                </Card>
-              ))}
-            </>
-          )}
-        </View>
-      </ScrollView>
+                  </Card>
+                ))}
+              </>
+            )}
+          </View>
+        </ScrollView>
 
-      {/* 底部结算栏 */}
-      {cart.items.length > 0 && (
-        <View className="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 px-4 py-3">
-          <View className="flex items-center justify-between">
-            <View>
-              <Text className="text-sm text-gray-600">
-                已选 {selectedItems.length} 件
-              </Text>
-              <Text className="text-lg font-bold text-red-500">
-                ¥{selectedItemsTotal.toFixed(2)}
-              </Text>
+        {/* 底部结算栏 */}
+        {cart.items.length > 0 && (
+          <View className="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 px-4 py-3">
+            <View className="flex items-center justify-between">
+              <View>
+                <Text className="text-sm text-gray-600">
+                  已选 {selectedItems.length} 件
+                </Text>
+                <Text className="text-lg font-bold text-red-500">
+                  ¥{selectedItemsTotal.toFixed(2)}
+                </Text>
+              </View>
+              
+              <Button
+                onClick={handleCheckout}
+                disabled={selectedItems.length === 0}
+              >
+                去结算 ({selectedItems.length})
+              </Button>
             </View>
-            
-            <Button
-              onClick={handleCheckout}
-              disabled={selectedItems.length === 0}
-            >
-              去结算 ({selectedItems.length})
-            </Button>
           </View>
-        </View>
-      )}
-    </View>
+        )}
+      </View>
+    </TabBarLayout>
   )
 }

+ 117 - 114
mini/src/pages/goods-list/index.tsx

@@ -10,6 +10,7 @@ import { Button } from '@/components/ui/button'
 import { useCart } from '@/utils/cart'
 import { Image } from '@/components/ui/image'
 import { Input } from '@/components/ui/input'
+import { TabBarLayout } from '@/layouts/tab-bar-layout'
 
 type GoodsResponse = InferResponseType<typeof goodsClient.$get, 200>
 type Goods = GoodsResponse['data'][0]
@@ -91,128 +92,130 @@ export default function GoodsListPage() {
   }
 
   return (
-    <View className="min-h-screen bg-gray-50">
-      <Navbar
-        title="商品列表"
-        leftIcon="i-heroicons-chevron-left-20-solid"
-        onClickLeft={() => Taro.navigateBack()}
-      />
-      
-      <ScrollView
-        className="h-screen pt-12"
-        scrollY
-        onScrollToLower={handleScrollToLower}
-        refresherEnabled
-        refresherTriggered={false}
-        onRefresherRefresh={onPullDownRefresh}
-      >
-        <View className="px-4 py-4">
-          {/* 搜索栏 */}
-          <View className="bg-white rounded-lg p-3 mb-4 shadow-sm">
-            <Input
-              type="text"
-              placeholder="搜索商品"
-              className="w-full outline-none"
-              value={searchKeyword}
-              onChange={(value) => setSearchKeyword(value)}
-              onConfirm={() => refetch()}
-            />
-          </View>
-
-          {/* 商品列表 */}
-          {isLoading ? (
-            <View className="flex justify-center py-10">
-              <View className="i-heroicons-arrow-path-20-solid animate-spin w-8 h-8 text-blue-500" />
+    <TabBarLayout activeKey="goods-list">
+      <View className="min-h-screen bg-gray-50">
+        <Navbar
+          title="商品列表"
+          leftIcon="i-heroicons-chevron-left-20-solid"
+          onClickLeft={() => Taro.navigateBack()}
+        />
+        
+        <ScrollView
+          className="h-screen pt-12"
+          scrollY
+          onScrollToLower={handleScrollToLower}
+          refresherEnabled
+          refresherTriggered={false}
+          onRefresherRefresh={onPullDownRefresh}
+        >
+          <View className="px-4 py-4">
+            {/* 搜索栏 */}
+            <View className="bg-white rounded-lg p-3 mb-4 shadow-sm">
+              <Input
+                type="text"
+                placeholder="搜索商品"
+                className="w-full outline-none"
+                value={searchKeyword}
+                onChange={(value) => setSearchKeyword(value)}
+                onConfirm={() => refetch()}
+              />
             </View>
-          ) : (
-            <>
-              {allGoods.map((goods) => (
-                <Card key={goods.id} className="mb-4 overflow-hidden">
-                  <View className="flex p-4">
-                    <View 
-                      className="w-24 h-24 bg-gray-100 rounded-lg mr-4"
-                      onClick={() => handleGoodsClick(goods)}
-                    >
-                      {goods.imageFile?.fullUrl ? (
-                        <Image 
-                          src={goods.imageFile.fullUrl} 
-                          className="w-full h-full object-cover rounded-lg"
-                          mode="aspectFill"
-                        />
-                      ) : (
-                        <View className="w-full h-full flex items-center justify-center text-gray-400">
-                          <View className="i-heroicons-photo-20-solid w-8 h-8" />
-                        </View>
-                      )}
-                    </View>
-                    
-                    <View className="flex-1">
-                      <Text 
-                        className="text-lg font-medium text-gray-900 mb-2 line-clamp-2"
+
+            {/* 商品列表 */}
+            {isLoading ? (
+              <View className="flex justify-center py-10">
+                <View className="i-heroicons-arrow-path-20-solid animate-spin w-8 h-8 text-blue-500" />
+              </View>
+            ) : (
+              <>
+                {allGoods.map((goods) => (
+                  <Card key={goods.id} className="mb-4 overflow-hidden">
+                    <View className="flex p-4">
+                      <View
+                        className="w-24 h-24 bg-gray-100 rounded-lg mr-4"
                         onClick={() => handleGoodsClick(goods)}
                       >
-                        {goods.name}
-                      </Text>
-                      
-                      <Text className="text-sm text-gray-500 mb-2">
-                        {goods.instructions || '暂无描述'}
-                      </Text>
+                        {goods.imageFile?.fullUrl ? (
+                          <Image
+                            src={goods.imageFile.fullUrl}
+                            className="w-full h-full object-cover rounded-lg"
+                            mode="aspectFill"
+                          />
+                        ) : (
+                          <View className="w-full h-full flex items-center justify-center text-gray-400">
+                            <View className="i-heroicons-photo-20-solid w-8 h-8" />
+                          </View>
+                        )}
+                      </View>
                       
-                      <View className="flex items-center justify-between">
-                        <View>
-                          <Text className="text-red-500 text-lg font-bold">
-                            ¥{goods.price.toFixed(2)}
-                          </Text>
-                          <Text className="text-xs text-gray-400 ml-2">
-                            已售{goods.salesNum}
-                          </Text>
-                        </View>
+                      <View className="flex-1">
+                        <Text
+                          className="text-lg font-medium text-gray-900 mb-2 line-clamp-2"
+                          onClick={() => handleGoodsClick(goods)}
+                        >
+                          {goods.name}
+                        </Text>
+                        
+                        <Text className="text-sm text-gray-500 mb-2">
+                          {goods.instructions || '暂无描述'}
+                        </Text>
                         
-                        <View className="flex items-center space-x-2">
-                          <Button 
-                            size="sm" 
-                            variant="outline"
-                            onClick={() => handleGoodsClick(goods)}
-                          >
-                            详情
-                          </Button>
-                          <Button 
-                            size="sm" 
-                            onClick={() => handleAddToCart(goods)}
-                            disabled={goods.stock <= 0}
-                          >
-                            {goods.stock > 0 ? '加入购物车' : '已售罄'}
-                          </Button>
+                        <View className="flex items-center justify-between">
+                          <View>
+                            <Text className="text-red-500 text-lg font-bold">
+                              ¥{goods.price.toFixed(2)}
+                            </Text>
+                            <Text className="text-xs text-gray-400 ml-2">
+                              已售{goods.salesNum}
+                            </Text>
+                          </View>
+                          
+                          <View className="flex items-center space-x-2">
+                            <Button
+                              size="sm"
+                              variant="outline"
+                              onClick={() => handleGoodsClick(goods)}
+                            >
+                              详情
+                            </Button>
+                            <Button
+                              size="sm"
+                              onClick={() => handleAddToCart(goods)}
+                              disabled={goods.stock <= 0}
+                            >
+                              {goods.stock > 0 ? '加入购物车' : '已售罄'}
+                            </Button>
+                          </View>
                         </View>
                       </View>
                     </View>
+                  </Card>
+                ))}
+                
+                {isFetchingNextPage && (
+                  <View className="flex justify-center py-4">
+                    <View className="i-heroicons-arrow-path-20-solid animate-spin w-6 h-6 text-blue-500" />
+                    <Text className="ml-2 text-sm text-gray-500">加载更多...</Text>
                   </View>
-                </Card>
-              ))}
-              
-              {isFetchingNextPage && (
-                <View className="flex justify-center py-4">
-                  <View className="i-heroicons-arrow-path-20-solid animate-spin w-6 h-6 text-blue-500" />
-                  <Text className="ml-2 text-sm text-gray-500">加载更多...</Text>
-                </View>
-              )}
-              
-              {!hasNextPage && allGoods.length > 0 && (
-                <View className="text-center py-4 text-sm text-gray-400">
-                  没有更多了
-                </View>
-              )}
-              
-              {!isLoading && allGoods.length === 0 && (
-                <View className="flex flex-col items-center py-10">
-                  <View className="i-heroicons-inbox-20-solid w-12 h-12 text-gray-300 mb-4" />
-                  <Text className="text-gray-500">暂无商品</Text>
-                </View>
-              )}
-            </>
-          )}
-        </View>
-      </ScrollView>
-    </View>
+                )}
+                
+                {!hasNextPage && allGoods.length > 0 && (
+                  <View className="text-center py-4 text-sm text-gray-400">
+                    没有更多了
+                  </View>
+                )}
+                
+                {!isLoading && allGoods.length === 0 && (
+                  <View className="flex flex-col items-center py-10">
+                    <View className="i-heroicons-inbox-20-solid w-12 h-12 text-gray-300 mb-4" />
+                    <Text className="text-gray-500">暂无商品</Text>
+                  </View>
+                )}
+              </>
+            )}
+          </View>
+        </ScrollView>
+      </View>
+    </TabBarLayout>
   )
 }