yankun 1 bulan lalu
induk
melakukan
302aa6ce16

+ 1 - 1
components/swiper.vue

@@ -117,7 +117,7 @@ const itemClick = (e) => {
 };
 
 onMounted(() => {
-  // useSystem.setBanner();
+  useSystem.setBanner();
 });
 </script>
 

+ 150 - 8
components/tabbar.vue

@@ -4,7 +4,7 @@
       <view
         class="action"
         @click="NavChange(item, index)"
-        v-for="(item, index) in navList"
+        v-for="(item, index) in currentNavList"
         :key="index"
         :data-cur="item.name"
       >
@@ -42,7 +42,12 @@
 
 <script setup>
 import { onMounted, computed, watch, ref, nextTick } from "vue";
-import { useTabbarStore, useMessageStore, useShopStore } from "@/store";
+import {
+  useTabbarStore,
+  useMessageStore,
+  useShopStore,
+  useUserStore,
+} from "@/store";
 // 导入图片资源(保持不变)
 import channel from "@/static/tabbar/channel.png";
 import home from "@/static/tabbar/home.png";
@@ -63,12 +68,22 @@ import { onShow } from "@dcloudio/uni-app";
 const useTabbar = useTabbarStore();
 const useShop = useShopStore();
 const useMessage = useMessageStore();
+const useUser = useUserStore();
 const { globalMap } = storeToRefs(useMessage);
 const unreadCount = ref(0);
 
 // 1. 定义购物车数量计算属性(响应式)
 const cartNum = computed(() => useShop.getCartNum);
 
+// 获取用户信息
+const userInfo = computed(() => useUser.getuserInfo);
+
+// 判断用户类型:1为卖家,2为买家
+const isBuyer = computed(() => {
+  console.log(useUser.getUserType);
+  return useUser.getUserType === 2;
+});
+
 const props = defineProps({
   page: {
     type: String,
@@ -117,15 +132,54 @@ const navList = ref([
   },
 ]);
 
+// 买家端导航列表
+const buyerNavList = ref([
+  {
+    name: "home",
+    title: "tabbar.首页",
+    img: home,
+    img_active: home_active,
+    num: 0,
+  },
+  {
+    name: "shop",
+    title: "tabbar.店铺",
+    img: ship,
+    img_active: ship_active,
+    num: 0,
+  },
+  {
+    name: "cart",
+    title: "tabbar.购物车",
+    img: cart,
+    img_active: cart_active,
+    num: cartNum.value,
+  },
+  {
+    name: "profile",
+    title: "tabbar.我的",
+    img: user,
+    img_active: user_active,
+    num: 0,
+  },
+]);
+
 // 3. 监听cartNum变化,实时更新navList中购物车项的num(关键修改)
 watch(
   cartNum,
   (newNum) => {
-    // 找到购物车对应的项并更新数量
+    // 更新卖家端购物车数量
     const cartItem = navList.value.find((item) => item.name === "shop");
     if (cartItem) {
       cartItem.num = newNum;
     }
+    // 更新买家端购物车数量
+    const buyerCartItem = buyerNavList.value.find(
+      (item) => item.name === "cart"
+    );
+    if (buyerCartItem) {
+      buyerCartItem.num = newNum;
+    }
   },
   { immediate: true } // 初始化时立即执行一次
 );
@@ -147,23 +201,111 @@ watch(
 
 const pageCur = computed(() => useTabbar.setPageCur);
 
+// 根据用户类型返回相应的导航列表
+const currentNavList = computed(() => {
+  // 如果用户已登录,根据用户类型显示导航
+  if (userInfo.value && Object.keys(userInfo.value).length > 0) {
+    return isBuyer.value ? buyerNavList.value : navList.value;
+  }
+
+  // 如果用户未登录,根据当前页面路径判断
+  const currentPath = getCurrentPages()[getCurrentPages().length - 1].route;
+  const isBuyerPage = currentPath.includes("pagesBuyer");
+  return isBuyerPage ? buyerNavList.value : navList.value;
+});
+
 const NavChange = (item, index) => {
   if (item.name == pageCur.value) return;
-  if (item.name == "shop") {
-    uni.switchTab({
-      url: `/pages/shop/cart`,
+
+  let targetUrl = "";
+
+  // 根据用户类型决定跳转路径
+  if (userInfo.value && Object.keys(userInfo.value).length > 0) {
+    // 用户已登录,根据用户类型跳转
+    if (isBuyer.value) {
+      // 买家端页面跳转
+      if (item.name === "cart") {
+        targetUrl = "/pagesBuyer/cart/index";
+      } else if (item.name === "home") {
+        targetUrl = "/pagesBuyer/home/index";
+      } else if (item.name === "shop") {
+        targetUrl = "/pagesBuyer/shop/index";
+      } else if (item.name === "profile") {
+        targetUrl = "/pagesBuyer/profile/index";
+      } else {
+        targetUrl = `/pagesBuyer/${item.name}/index`;
+      }
+    } else {
+      // 卖家端页面跳转
+      if (item.name === "shop") {
+        targetUrl = "/pages/shop/cart";
+      } else {
+        targetUrl = `/pages/${item.name}/index`;
+      }
+    }
+  } else {
+    // 用户未登录,根据当前页面路径判断
+    const currentPath = getCurrentPages()[getCurrentPages().length - 1].route;
+    const isBuyerPage = currentPath.includes("pagesBuyer");
+
+    if (isBuyerPage) {
+      // 买家端页面跳转
+      if (item.name === "cart") {
+        targetUrl = "/pagesBuyer/cart/index";
+      } else if (item.name === "home") {
+        targetUrl = "/pagesBuyer/home/index";
+      } else if (item.name === "shop") {
+        targetUrl = "/pagesBuyer/shop/index";
+      } else if (item.name === "profile") {
+        targetUrl = "/pagesBuyer/profile/index";
+      } else {
+        targetUrl = `/pagesBuyer/${item.name}/index`;
+      }
+    } else {
+      // 卖家端页面跳转
+      if (item.name === "shop") {
+        targetUrl = "/pages/shop/cart";
+      } else {
+        targetUrl = `/pages/${item.name}/index`;
+      }
+    }
+  }
+  console.log("跳转URL:", targetUrl);
+
+  // 判断是否为买家端页面
+  const isBuyerPage = targetUrl.includes("pagesBuyer");
+
+  if (isBuyerPage) {
+    // 买家端页面使用 navigateTo
+    uni.navigateTo({
+      url: targetUrl,
       success: () => {
         useTabbar.getPageCur(item.name);
         useTabbar.getPageIndex(index);
       },
+      fail: (err) => {
+        console.error("买家端页面跳转失败:", err);
+        // 如果 navigateTo 失败,尝试使用 reLaunch
+        uni.reLaunch({
+          url: targetUrl,
+          success: () => {
+            useTabbar.getPageCur(item.name);
+            useTabbar.getPageIndex(index);
+          },
+        });
+      },
     });
   } else {
+    // 卖家端页面使用 switchTab
     uni.switchTab({
-      url: `/pages/${item.name}/index`,
+      url: targetUrl,
       success: () => {
         useTabbar.getPageCur(item.name);
         useTabbar.getPageIndex(index);
       },
+      fail: (err) => {
+        console.error("卖家端页面跳转失败:", err);
+      },
     });
   }
 };
@@ -197,7 +339,7 @@ onShow(() => {
   bottom: 0;
 }
 
-/deep/ .cu-bar .content {
+:deep(.cu-bar .content) {
   cursor: pointer;
   pointer-events: unset;
 }

+ 2 - 1
locale/zh.json

@@ -595,5 +595,6 @@
   "请输入联系人姓名": "请输入联系人姓名",
   "最低价": "最低价",
   "最高价": "最高价",
-  "元": "元"
+  "元": "元",
+  "tabbar.店铺": "店铺"
 }

+ 2 - 1
manifest.json

@@ -21,7 +21,8 @@
             "OAuth" : {},
             "Camera" : {},
             "Push" : {},
-            "VideoPlayer" : {}
+            "VideoPlayer" : {},
+            "Record" : {}
         },
         /* 应用发布信息 */
         "distribute" : {

+ 18 - 0
pages.json

@@ -10,6 +10,9 @@
   },
   "pages": [
     //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+    {
+      "path": "pagesBuyer/home/index"
+    },
     {
       "path": "pages/index/index"
     },
@@ -193,6 +196,21 @@
     },
     {
       "path": "pages/purpose/record"
+    },
+    {
+      "path": "pagesBuyer/cart/index"
+    },
+    {
+      "path": "pagesBuyer/profile/index"
+    },
+    {
+      "path": "pagesBuyer/shop/index"
+    },
+    {
+      "path": "pagesBuyer/shop/detail"
+    },
+    {
+      "path": "pagesBuyer/home/record"
     }
   ],
   "tabBar": {

+ 1 - 1
pages/dashboard/warehouse.vue

@@ -97,7 +97,7 @@
               @click.stop="submit"
               :style="{ opacity: ids.length ? 1 : 0.5 }"
             >
-              <trans _t="打包" />
+              <trans _t="上架" />
             </view>
           </view>
         </view>

+ 31 - 1
pages/user/index.vue

@@ -93,6 +93,9 @@
         </view>
       </view>
       <view class="footer">
+        <view class="switch_user_type" @click="switchUserType">
+          <trans _t="切换到买家模式" />
+        </view>
         <view class="exit_btn" @click="exit">
           <trans _t="退出登录" />
         </view>
@@ -381,6 +384,20 @@ uni.hideTabBar();
 const handleAgreementModalClose = () => {
   agreementType.value = "";
 };
+
+// 切换用户类型
+const switchUserType = () => {
+  Modal({
+    content: t("确定要切换到买家模式吗?切换后底部导航将显示买家功能"),
+  }).then(() => {
+    // 设置为买家类型
+    useUser.setUserType(2);
+    // 跳转到买家首页
+    uni.reLaunch({
+      url: "/pagesBuyer/home/index",
+    });
+  });
+};
 </script>
 
 <style lang="less" scoped>
@@ -502,7 +519,7 @@ const handleAgreementModalClose = () => {
         line-height: 128rpx;
         text-transform: uppercase;
         font-family: "HarmonyOS_Sans";
-        text-wrap: nowrap;
+        white-space: nowrap;
       }
     }
 
@@ -593,6 +610,19 @@ const handleAgreementModalClose = () => {
   }
 }
 
+.switch_user_type {
+  margin: 0 24rpx;
+  margin-top: 24rpx;
+  background-color: var(--primary);
+  color: var(--light);
+  .size(28rpx);
+  height: 84rpx;
+  padding: 16rpx 30rpx;
+  .flex_center();
+  border-radius: 16rpx;
+  margin-bottom: 16rpx;
+}
+
 .exit_btn {
   // width: 80%;
   margin: 0 24rpx;

+ 131 - 0
pagesBuyer/USER_TYPE_STORAGE.md

@@ -0,0 +1,131 @@
+# 用户类型独立存储功能说明
+
+## 功能概述
+将用户类型 (`user_type`) 从 `userInfo` 中分离出来,单独存储到本地存储中,确保用户类型在页面刷新后仍然保持。
+
+## 实现方案
+
+### 1. 独立存储字段
+```javascript
+// store/user.js
+const state = () => ({
+  // ... 其他字段
+  userType: getStorage('userType') || 1 // 单独存储用户类型,默认为卖家
+})
+```
+
+### 2. 存储键名
+- **用户类型存储键**: `userType`
+- **存储位置**: 本地存储 (localStorage)
+- **默认值**: `1` (卖家)
+
+### 3. 用户类型定义
+- `1`: 卖家
+- `2`: 买家
+
+## 核心方法
+
+### 设置用户类型
+```javascript
+// 设置为买家
+useUser.setUserType(2);
+
+// 设置为卖家
+useUser.setUserType(1);
+```
+
+### 获取用户类型
+```javascript
+const userType = useUser.getUserType();
+// 返回: 1 或 2
+```
+
+### 判断用户类型
+```javascript
+const isBuyer = computed(() => {
+  return useUser.getUserType() === 2;
+});
+```
+
+## 存储机制
+
+### 1. 数据持久化
+- 用户类型存储在 `localStorage` 中,键名为 `userType`
+- 页面刷新、应用重启后数据仍然保持
+- 不受 `userInfo` 更新影响
+
+### 2. 数据同步
+- 设置用户类型时自动同步到本地存储
+- 应用启动时从本地存储读取用户类型
+- 退出登录时清除用户类型存储
+
+### 3. 默认行为
+- 首次使用默认为卖家类型 (`1`)
+- 退出登录后重置为卖家类型
+- 未登录状态下根据页面路径判断
+
+## 使用场景
+
+### 1. 用户模式切换
+```javascript
+// 切换到买家模式
+const switchToBuyer = () => {
+  useUser.setUserType(2);
+  uni.reLaunch({ url: '/pagesBuyer/home/index' });
+};
+
+// 切换到卖家模式
+const switchToSeller = () => {
+  useUser.setUserType(1);
+  uni.reLaunch({ url: '/pages/index/index' });
+};
+```
+
+### 2. 导航栏显示
+```javascript
+// Tabbar 组件中根据用户类型显示不同导航
+const currentNavList = computed(() => {
+  if (userInfo.value && Object.keys(userInfo.value).length > 0) {
+    return isBuyer.value ? buyerNavList.value : navList.value;
+  }
+  // 未登录时根据页面路径判断
+  const currentPath = getCurrentPages()[getCurrentPages().length - 1].route;
+  const isBuyerPage = currentPath.includes("pagesBuyer");
+  return isBuyerPage ? buyerNavList.value : navList.value;
+});
+```
+
+## 优势
+
+### 1. 数据独立性
+- 用户类型与用户信息分离
+- 不受用户信息更新影响
+- 避免数据冲突
+
+### 2. 持久化保证
+- 页面刷新后用户类型保持不变
+- 应用重启后状态保持
+- 用户体验更佳
+
+### 3. 灵活控制
+- 可以独立控制用户类型
+- 支持动态切换
+- 便于扩展更多用户类型
+
+## 注意事项
+
+1. **存储键名**: 使用 `userType` 作为存储键名,避免与其他数据冲突
+2. **默认值**: 始终设置默认值为 `1` (卖家),确保应用正常运行
+3. **清理机制**: 退出登录时清除用户类型存储,避免数据残留
+4. **兼容性**: 保持与原有代码的兼容性,不影响现有功能
+
+## 调试信息
+
+在买家端首页添加了调试信息显示当前用户类型:
+```vue
+<view class="debug-info" v-if="token">
+  <text>当前用户类型: {{ userType === 1 ? '卖家' : '买家' }} ({{ userType }})</text>
+</view>
+```
+
+可以通过这个信息验证用户类型是否正确存储和显示。

+ 615 - 0
pagesBuyer/cart/index.vue

@@ -0,0 +1,615 @@
+<template>
+  <Theme>
+    <view class="wrap">
+      <Navbar fixed leftShow leftIconColor="var(--bg)">
+        <template #center>
+          <view class="nav_title">
+            <trans _t="购物车" />
+          </view>
+          <view class="nav_title" v-if="cartNum">({{ cartNum }})</view>
+        </template>
+        <template #right>
+          <view
+            class="nav_right"
+            @click.stop="delCart"
+            v-if="selectedIds.length"
+          >
+            <trans _t="删除" />
+          </view>
+        </template>
+      </Navbar>
+
+      <view class="tab" id="tabs">
+        <Tab :active="activeTab" :tabList="tabList" @confirm="tabClick" />
+      </view>
+
+      <view
+        class="content"
+        :style="{ '--height': footerHeight + tabbarHeight + tabHeight + 'px' }"
+      >
+        <view class="cont_wrap">
+          <!-- 购物车为空 -->
+          <view class="empty-cart" v-if="!cartList.length">
+            <image src="/static/shop/empty_cart.png" class="empty-image" />
+            <text class="empty-text">购物车空空如也</text>
+            <view class="empty-btn" @click="goShopping">
+              <trans _t="去逛逛" />
+            </view>
+          </view>
+
+          <!-- 购物车列表 -->
+          <view class="cart-list" v-else>
+            <view class="cart-item" v-for="item in cartList" :key="item.id">
+              <view class="item-checkbox">
+                <up-checkbox
+                  :checked="item.checked"
+                  @change="toggleItem(item, $event)"
+                  activeColor="var(--black)"
+                  shape="circle"
+                />
+              </view>
+
+              <view class="item-content" @click="toProductDetail(item)">
+                <image :src="item.image" class="item-image" />
+                <view class="item-info">
+                  <text class="item-name">{{ item.name }}</text>
+                  <text class="item-spec">{{ item.spec }}</text>
+                  <view class="item-price-row">
+                    <text class="item-price"
+                      >{{ symbol.symbol }}{{ item.price }}</text
+                    >
+                    <view class="quantity-control">
+                      <view
+                        class="quantity-btn minus"
+                        @click.stop="decreaseQuantity(item)"
+                        :class="{ disabled: item.quantity <= 1 }"
+                      >
+                        -
+                      </view>
+                      <input
+                        class="quantity-input"
+                        type="number"
+                        :value="item.quantity"
+                        @input="updateQuantity(item, $event)"
+                        @blur="validateQuantity(item)"
+                      />
+                      <view
+                        class="quantity-btn plus"
+                        @click.stop="increaseQuantity(item)"
+                      >
+                        +
+                      </view>
+                    </view>
+                  </view>
+                </view>
+              </view>
+            </view>
+          </view>
+        </view>
+
+        <view
+          class="footer"
+          id="footer"
+          :style="{ '--tabbarHeight': tabbarHeight + 'px' }"
+          v-if="cartList.length"
+        >
+          <view class="footer_left">
+            <up-checkbox
+              :label="t('全选')"
+              :disabled="!cartList.length"
+              activeColor="var(--black)"
+              shape="circle"
+              labelSize="14"
+              labelColor="#676969"
+              iconSize="16"
+              name="selectAll"
+              usedAlone
+              v-model:checked="selectAllChecked"
+              @change="selectAllChange"
+            />
+          </view>
+          <view class="footer_right">
+            <view class="total_info">
+              <view class="total_price">
+                <text>合计:{{ symbol.symbol }}{{ totalPrice }}</text>
+              </view>
+              <view class="total_desc">
+                <trans _t="已选{{ selectedCount }}件商品" />
+              </view>
+            </view>
+            <view
+              class="total_btn"
+              @click.stop="checkout"
+              :style="{
+                opacity: canCheckout ? 1 : 0.5,
+                'pointer-events': canCheckout ? 'auto' : 'none',
+              }"
+            >
+              <trans _t="去结算" />
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+    <Tabbar page="cart" @getTabbarHeight="getTabbarHeight" />
+  </Theme>
+</template>
+
+<script setup>
+import { computed, ref, onMounted, nextTick, watch } from "vue";
+import Tabbar from "@/components/tabbar";
+import Navbar from "@/components/navbar";
+import Tab from "@/components/tabs";
+import { useUserStore, useShopStore, useSystemStore } from "@/store";
+import { t } from "@/locale";
+import { onShow } from "@dcloudio/uni-app";
+import { query } from "@/utils";
+
+const useUser = useUserStore();
+const useShop = useShopStore();
+const useSystem = useSystemStore();
+const symbol = computed(() => useSystem.getSymbol);
+
+// 购物车数据
+const cartList = ref([
+  {
+    id: 1,
+    name: "时尚连衣裙",
+    spec: "颜色:黑色 尺码:M",
+    image: "/static/shop/product1.png",
+    price: "299.00",
+    quantity: 1,
+    checked: true,
+    stock: 99,
+  },
+  {
+    id: 2,
+    name: "护肤精华",
+    spec: "容量:30ml",
+    image: "/static/shop/product2.png",
+    price: "199.00",
+    quantity: 2,
+    checked: true,
+    stock: 50,
+  },
+  {
+    id: 3,
+    name: "无线耳机",
+    spec: "颜色:白色",
+    image: "/static/shop/product3.png",
+    price: "599.00",
+    quantity: 1,
+    checked: false,
+    stock: 20,
+  },
+]);
+
+const cartNum = computed(() => cartList.value.length);
+const selectAllChecked = ref(false);
+const footerHeight = ref(0);
+const tabbarHeight = ref(0);
+const activeTab = ref(0);
+const tabHeight = ref(0);
+const tabList = ["全部", "降价商品", "库存紧张"];
+
+const userInfo = computed(() => useUser.getuserInfo);
+const token = computed(() => useUser.getToken);
+
+// 选中的商品ID列表
+const selectedIds = computed(() => {
+  return cartList.value.filter((item) => item.checked).map((item) => item.id);
+});
+
+// 选中商品数量
+const selectedCount = computed(() => {
+  return cartList.value
+    .filter((item) => item.checked)
+    .reduce((sum, item) => sum + item.quantity, 0);
+});
+
+// 总价
+const totalPrice = computed(() => {
+  return cartList.value
+    .filter((item) => item.checked)
+    .reduce((sum, item) => sum + parseFloat(item.price) * item.quantity, 0)
+    .toFixed(2);
+});
+
+// 是否可以结算
+const canCheckout = computed(() => {
+  return selectedIds.value.length > 0;
+});
+
+// 监听购物车变化,更新全选状态
+watch(
+  () => cartList.value,
+  (list) => {
+    if (!list.length) {
+      selectAllChecked.value = false;
+      return;
+    }
+    const allChecked = list.every((item) => item.checked);
+    selectAllChecked.value = allChecked;
+  },
+  { immediate: true, deep: true }
+);
+
+// 全选/取消全选
+const selectAllChange = (checked) => {
+  cartList.value.forEach((item) => {
+    item.checked = checked;
+  });
+};
+
+// 切换单个商品选中状态
+const toggleItem = (item, checked) => {
+  item.checked = checked;
+};
+
+// 增加数量
+const increaseQuantity = (item) => {
+  if (item.quantity < item.stock) {
+    item.quantity++;
+  } else {
+    uni.showToast({
+      title: "库存不足",
+      icon: "none",
+    });
+  }
+};
+
+// 减少数量
+const decreaseQuantity = (item) => {
+  if (item.quantity > 1) {
+    item.quantity--;
+  }
+};
+
+// 更新数量
+const updateQuantity = (item, event) => {
+  const value = parseInt(event.detail.value) || 1;
+  if (value > item.stock) {
+    item.quantity = item.stock;
+    uni.showToast({
+      title: "库存不足",
+      icon: "none",
+    });
+  } else if (value < 1) {
+    item.quantity = 1;
+  } else {
+    item.quantity = value;
+  }
+};
+
+// 验证数量
+const validateQuantity = (item) => {
+  if (item.quantity < 1) {
+    item.quantity = 1;
+  }
+};
+
+// 删除购物车
+const delCart = () => {
+  if (selectedIds.value.length === 0) {
+    uni.showToast({
+      title: "请选择要删除的商品",
+      icon: "none",
+    });
+    return;
+  }
+
+  uni.showModal({
+    title: "确认删除",
+    content: `确定要删除选中的${selectedIds.value.length}件商品吗?`,
+    success: (res) => {
+      if (res.confirm) {
+        cartList.value = cartList.value.filter((item) => !item.checked);
+        selectAllChecked.value = false;
+      }
+    },
+  });
+};
+
+// 去结算
+const checkout = () => {
+  if (!canCheckout.value) {
+    uni.showToast({
+      title: "请选择要结算的商品",
+      icon: "none",
+    });
+    return;
+  }
+
+  const selectedItems = cartList.value.filter((item) => item.checked);
+  uni.navigateTo({
+    url: `/pagesBuyer/cart/checkout?items=${JSON.stringify(selectedItems)}`,
+  });
+};
+
+// 去购物
+const goShopping = () => {
+  uni.switchTab({
+    url: "/pagesBuyer/home/index",
+  });
+};
+
+// 查看商品详情
+const toProductDetail = (item) => {
+  uni.navigateTo({
+    url: `/pagesBuyer/home/product?id=${item.id}`,
+  });
+};
+
+// 切换标签
+const tabClick = (item, index) => {
+  if (activeTab.value === index) return;
+  activeTab.value = index;
+  // 根据标签筛选商品
+  filterCartItems(index);
+};
+
+// 筛选购物车商品
+const filterCartItems = (tabIndex) => {
+  // 这里可以根据不同标签筛选商品
+  // 0: 全部, 1: 降价商品, 2: 库存紧张
+  switch (tabIndex) {
+    case 0:
+      // 显示全部商品
+      break;
+    case 1:
+      // 显示降价商品(这里简化处理)
+      break;
+    case 2:
+      // 显示库存紧张商品(库存小于10)
+      break;
+  }
+};
+
+const getHeight = async () => {
+  try {
+    const res = await query("#footer");
+    const resTab = await query("#tabs");
+    nextTick(() => {
+      tabHeight.value = resTab.height;
+      footerHeight.value = res.height;
+    });
+  } catch (error) {}
+};
+
+const getTabbarHeight = (height) => {
+  tabbarHeight.value = height;
+};
+
+onMounted(() => {
+  getHeight();
+});
+
+onShow(() => {
+  setTimeout(() => {
+    getHeight();
+  }, 0);
+});
+</script>
+
+<style lang="less" scoped>
+@import url("@/style.less");
+
+.wrap {
+  min-height: 100vh;
+  background: var(--bg);
+  overflow: hidden;
+  .flex();
+  flex-direction: column;
+
+  :deep(.u-navbar__content) {
+    background-color: var(--black) !important;
+  }
+
+  .nav_title {
+    color: var(--light);
+  }
+
+  .nav_right {
+    color: var(--primary);
+    .size(24rpx);
+    font-weight: 500;
+  }
+
+  .content {
+    flex-grow: 1;
+    height: calc(100vh - 44px - var(--height));
+    overflow-y: scroll;
+    .flex();
+    flex-direction: column;
+
+    .cont_wrap {
+      flex-grow: 1;
+      overflow: hidden scroll;
+      padding: 0 30rpx;
+    }
+
+    .empty-cart {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+      height: 60vh;
+
+      .empty-image {
+        width: 200rpx;
+        height: 200rpx;
+        margin-bottom: 32rpx;
+      }
+
+      .empty-text {
+        font-size: 32rpx;
+        color: var(--text-01);
+        margin-bottom: 48rpx;
+      }
+
+      .empty-btn {
+        background: var(--black);
+        color: var(--light);
+        padding: 24rpx 48rpx;
+        border-radius: 40rpx;
+        font-size: 28rpx;
+      }
+    }
+
+    .cart-list {
+      .cart-item {
+        display: flex;
+        align-items: flex-start;
+        background: var(--light);
+        border-radius: 20rpx;
+        padding: 24rpx;
+        margin-bottom: 16rpx;
+
+        .item-checkbox {
+          margin-right: 20rpx;
+          margin-top: 8rpx;
+        }
+
+        .item-content {
+          flex: 1;
+          display: flex;
+
+          .item-image {
+            width: 160rpx;
+            height: 160rpx;
+            border-radius: 16rpx;
+            margin-right: 20rpx;
+          }
+
+          .item-info {
+            flex: 1;
+            display: flex;
+            flex-direction: column;
+
+            .item-name {
+              font-size: 28rpx;
+              color: var(--black);
+              margin-bottom: 8rpx;
+              display: -webkit-box;
+              -webkit-line-clamp: 2;
+              line-clamp: 2;
+              -webkit-box-orient: vertical;
+              overflow: hidden;
+            }
+
+            .item-spec {
+              font-size: 24rpx;
+              color: var(--text-01);
+              margin-bottom: 16rpx;
+            }
+
+            .item-price-row {
+              display: flex;
+              justify-content: space-between;
+              align-items: center;
+
+              .item-price {
+                font-size: 32rpx;
+                font-weight: bold;
+                color: var(--red);
+              }
+
+              .quantity-control {
+                display: flex;
+                align-items: center;
+                border: 1rpx solid var(--border-color);
+                border-radius: 8rpx;
+
+                .quantity-btn {
+                  width: 60rpx;
+                  height: 60rpx;
+                  display: flex;
+                  align-items: center;
+                  justify-content: center;
+                  font-size: 32rpx;
+                  color: var(--text);
+                  background: var(--bg);
+
+                  &.disabled {
+                    color: var(--text-01);
+                    background: var(--inputBg);
+                  }
+
+                  &.minus {
+                    border-right: 1rpx solid var(--border-color);
+                  }
+
+                  &.plus {
+                    border-left: 1rpx solid var(--border-color);
+                  }
+                }
+
+                .quantity-input {
+                  width: 80rpx;
+                  height: 60rpx;
+                  text-align: center;
+                  font-size: 28rpx;
+                  border: none;
+                  background: transparent;
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+
+    .footer {
+      .flex_position(space-between);
+      flex-wrap: wrap;
+      padding: 8px 12px;
+      background-color: var(--light);
+      box-shadow: 0 -4px 6px #0000000d;
+      position: fixed;
+      bottom: var(--tabbarHeight);
+      left: 0;
+      right: 0;
+      box-sizing: border-box;
+
+      &_left {
+        display: flex;
+        align-items: center;
+      }
+
+      &_right {
+        .ver();
+
+        .total_info {
+          .total_price {
+            text-align: right;
+            color: var(--red);
+            .size();
+            font-weight: 700;
+            line-height: 48rpx;
+          }
+
+          .total_desc {
+            color: var(--text-01);
+            .size(24rpx);
+            line-height: 40rpx;
+            text-align: right;
+          }
+        }
+
+        .total_btn {
+          .size(28rpx);
+          font-weight: 700;
+          height: 38px;
+          margin-left: 16rpx;
+          min-width: 180rpx;
+          background-color: var(--black);
+          color: var(--light);
+          border-radius: 16rpx;
+          padding: 16rpx 30rpx;
+          text-align: center;
+        }
+      }
+    }
+  }
+}
+</style>

+ 731 - 0
pagesBuyer/home/index.vue

@@ -0,0 +1,731 @@
+<template>
+  <Theme>
+    <view class="wrap" :style="{ '--tabbarHeight': tabbarHeight + 'px' }">
+      <view class="cont">
+        <view class="cont_bg" id="tob">
+          <Navbar bgColor="transparent" fixed>
+            <template #left>
+              <view class="nav_left">
+                <image
+                  src="@/static/logo.png"
+                  class="nav_logo"
+                  mode="widthFix"
+                ></image>
+              </view>
+            </template>
+            <template #right>
+              <view class="nav_right">
+                <view class="icon-badge-wrap">
+                  <image
+                    src="/static/home/kefu.png"
+                    class="mobile_system"
+                    @click="toGo('/pages/setting/system')"
+                  >
+                  </image>
+                  <up-badge
+                    type="error"
+                    max="99"
+                    :value="unreadCount"
+                    class="tips"
+                    v-if="unreadCount > 0"
+                  ></up-badge>
+                </view>
+
+                <image
+                  src="/static/language.png"
+                  class="mobile_language"
+                  @click="toGo('/pages/setting/language_currency')"
+                ></image>
+                <navMenu />
+              </view>
+            </template>
+          </Navbar>
+          <view class="bg_top">
+            <view class="search">
+              <Search
+                v-model="searchValue"
+                border="none"
+                @click="searchClick"
+                :placeholder="t('搜索商品')"
+              >
+                <template #prefix>
+                  <i class="icon-font icon-search"></i>
+                </template>
+                <template #suffix>
+                  <i class="icon-font icon-camera"></i>
+                </template>
+              </Search>
+            </view>
+          </view>
+        </view>
+        <scroll-view
+          class="content-scroll"
+          scroll-y
+          refresher-enabled
+          :refresher-triggered="refreshering"
+          @refresherrefresh="onRefresherRefresh"
+          @refresherrestore="onRefresherRestore"
+          refresher-default-style="none"
+          :style="{ height: contentHeight + 'px' }"
+        >
+          <view class="refresher" v-if="refreshering">
+            <up-loadmore :status="'loading'" :loadingText="''" />
+          </view>
+          <view class="content">
+            <!-- 轮播图区域 -->
+            <view class="banner-section">
+              <Swiper @click="swiperClick" class="swiper-box" />
+            </view>
+
+            <!-- 分类导航 -->
+            <view class="category-section">
+              <view class="category-title">
+                <trans _t="商品分类" />
+              </view>
+              <view class="category-grid">
+                <view
+                  class="category-item"
+                  v-for="(item, index) in categoryList"
+                  :key="index"
+                  @click="toCategory(item)"
+                >
+                  <image :src="item.icon" class="category-icon" />
+                  <text class="category-name">{{ t(item.name) }}</text>
+                </view>
+
+                <!-- 录音功能入口 -->
+                <view class="category-item record-item" @click="toRecord">
+                  <view class="record-icon">
+                    <i class="icon-font icon-microphone"></i>
+                  </view>
+                  <text class="category-name">录音功能</text>
+                </view>
+              </view>
+            </view>
+
+            <!-- 热门商品 -->
+            <view class="hot-products-section">
+              <view class="section-title">
+                <trans _t="热门商品" />
+                <view class="more-btn" @click="toMoreProducts">
+                  <trans _t="更多" />
+                  <i class="icon-font icon-arrow-right"></i>
+                </view>
+              </view>
+              <view class="products-grid">
+                <view
+                  class="product-item"
+                  v-for="(item, index) in hotProducts"
+                  :key="index"
+                  @click="toProductDetail(item)"
+                >
+                  <image :src="item.image" class="product-image" />
+                  <view class="product-info">
+                    <text class="product-name">{{ item.name }}</text>
+                    <view class="product-price">
+                      <text class="current-price"
+                        >{{ symbol.symbol }}{{ item.price }}</text
+                      >
+                      <text class="original-price" v-if="item.originalPrice"
+                        >{{ symbol.symbol }}{{ item.originalPrice }}</text
+                      >
+                    </view>
+                    <view class="product-tags">
+                      <text class="tag" v-for="tag in item.tags" :key="tag">{{
+                        tag
+                      }}</text>
+                    </view>
+                  </view>
+                </view>
+              </view>
+            </view>
+          </view>
+        </scroll-view>
+      </view>
+    </view>
+
+    <IosUpdateModal />
+    <blindModel />
+    <Tabbar page="index" @getTabbarHeight="getTabbarHeight" />
+  </Theme>
+</template>
+
+<script setup>
+import Tabbar from "@/components/tabbar";
+import Navbar from "@/components/navbar";
+import Swiper from "@/components/swiper";
+import blindModel from "@/components/blindModel";
+import Search from "@/components/input";
+import navMenu from "@/components/nav_menu";
+import IosUpdateModal from "@/components/IosUpdateModal";
+import { computed, ref, watch, nextTick, onMounted } from "vue";
+import {
+  onLoad,
+  onShow,
+  onUnload,
+  onHide,
+  onPageScroll,
+} from "@dcloudio/uni-app";
+import { storeToRefs } from "pinia";
+import {
+  useShopStore,
+  useSystemStore,
+  useMessageStore,
+  useTabbarStore,
+  useUserStore,
+} from "@/store";
+import { t } from "@/locale";
+import { query, systemInfo } from "@/utils";
+
+const useShop = useShopStore();
+const useSystem = useSystemStore();
+const useTabbar = useTabbarStore();
+const useUser = useUserStore();
+
+const langList = computed(() => useSystem.getLangeuage);
+const lang = computed(() => useSystem.getLang);
+const token = computed(() => useUser.getToken);
+const symbol = computed(() => useSystem.getSymbol);
+const userType = computed(() => useUser.getUserType);
+
+const useMessage = useMessageStore();
+const { globalMap } = storeToRefs(useMessage);
+const unreadCount = ref(0);
+watch(
+  () => globalMap.value.noticeChannel,
+  (newVal, oldVal) => {
+    if (newVal) {
+      unreadCount.value = newVal.unreadCount;
+    }
+  },
+  {
+    deep: true,
+    immediate: true,
+  }
+);
+
+const searchValue = ref("");
+const refreshering = ref(false);
+const contentHeight = ref(0);
+const tabbarHeight = ref(0);
+
+// 轮播图数据
+const bannerList = ref([
+  {
+    id: 1,
+    image: "/static/home/banner1.png",
+    title: "新品上市",
+    action: "goto_product",
+  },
+  {
+    id: 2,
+    image: "/static/home/banner2.png",
+    title: "限时优惠",
+    action: "goto_activity",
+  },
+]);
+
+// 分类数据
+const categoryList = ref([
+  { id: 1, name: "服装", icon: "/static/shop/category_clothes.png" },
+  { id: 2, name: "美妆", icon: "/static/shop/category_beauty.png" },
+  { id: 3, name: "数码", icon: "/static/shop/category_digital.png" },
+  { id: 4, name: "家居", icon: "/static/shop/category_home.png" },
+  { id: 5, name: "食品", icon: "/static/shop/category_food.png" },
+  { id: 6, name: "母婴", icon: "/static/shop/category_baby.png" },
+  { id: 7, name: "运动", icon: "/static/shop/category_sports.png" },
+  { id: 8, name: "更多", icon: "/static/shop/category_more.png" },
+]);
+
+// 热门商品数据
+const hotProducts = ref([
+  {
+    id: 1,
+    name: "时尚连衣裙",
+    image: "/static/shop/product1.png",
+    price: "299.00",
+    originalPrice: "399.00",
+    tags: ["热销", "包邮"],
+  },
+  {
+    id: 2,
+    name: "护肤精华",
+    image: "/static/shop/product2.png",
+    price: "199.00",
+    originalPrice: "299.00",
+    tags: ["新品", "限时"],
+  },
+  {
+    id: 3,
+    name: "无线耳机",
+    image: "/static/shop/product3.png",
+    price: "599.00",
+    originalPrice: "799.00",
+    tags: ["爆款", "包邮"],
+  },
+  {
+    id: 4,
+    name: "智能手表",
+    image: "/static/shop/product4.png",
+    price: "1299.00",
+    originalPrice: "1599.00",
+    tags: ["科技", "热销"],
+  },
+]);
+
+// 推荐商品数据
+const recommendProducts = ref([
+  {
+    id: 5,
+    name: "经典牛仔裤",
+    image: "/static/shop/product5.png",
+    price: "199.00",
+    sales: 1234,
+  },
+  {
+    id: 6,
+    name: "口红套装",
+    image: "/static/shop/product6.png",
+    price: "299.00",
+    sales: 567,
+  },
+  {
+    id: 7,
+    name: "运动鞋",
+    image: "/static/shop/product7.png",
+    price: "399.00",
+    sales: 890,
+  },
+  {
+    id: 8,
+    name: "保温杯",
+    image: "/static/shop/product8.png",
+    price: "89.00",
+    sales: 2345,
+  },
+]);
+
+const searchClick = () => {
+  toGo("/pagesBuyer/home/search");
+};
+
+const toGo = (url) => {
+  if (!url) return;
+  uni.navigateTo({ url });
+};
+
+const toCategory = (category) => {
+  toGo(`/pagesBuyer/home/category?id=${category.id}&name=${category.name}`);
+};
+
+const toProductDetail = (product) => {
+  toGo(`/pagesBuyer/home/product?id=${product.id}`);
+};
+
+const toMoreProducts = () => {
+  toGo("/pagesBuyer/home/products");
+};
+
+const toRecord = () => {
+  toGo("/pagesBuyer/home/record");
+};
+
+const swiperClick = (item) => {
+  if (item.action == "goto_product") {
+    toGo("/pagesBuyer/home/products");
+  } else if (item.action == "goto_activity") {
+    toGo("/pagesBuyer/home/activity");
+  }
+};
+
+const getTabbarHeight = (height) => {
+  tabbarHeight.value = height;
+};
+
+const loadPageData = async () => {
+  // 加载页面数据
+  await useSystem.setBoxes({}, { isLoading: true });
+};
+
+onShow(() => {
+  loadPageData();
+});
+
+onMounted(() => {
+  nextTick(async () => {
+    const res = await query("#tob");
+    const { windowHeight, statusBarHeight } = systemInfo();
+    contentHeight.value = windowHeight - res.height - statusBarHeight;
+  });
+});
+
+uni.hideTabBar();
+
+const onRefresherRefresh = async () => {
+  try {
+    refreshering.value = true;
+    await loadPageData();
+  } finally {
+    refreshering.value = false;
+  }
+};
+
+const onRefresherRestore = () => {
+  refreshering.value = false;
+};
+</script>
+
+<style lang="less" scoped>
+.wrap {
+  background: var(--bg);
+  min-height: calc(100vh - var(--tabbarHeight));
+  padding-bottom: var(--tabbarHeight);
+
+  .nav_left {
+    display: flex;
+    align-items: center;
+
+    .nav_logo {
+      width: 284rpx;
+    }
+  }
+
+  .nav_right {
+    display: flex;
+    align-items: center;
+
+    .mobile_language {
+      width: 48rpx;
+      height: 48rpx;
+      margin: 0 20rpx 0 24rpx;
+    }
+
+    .icon-badge-wrap {
+      position: relative;
+      display: inline-block;
+      width: 48rpx;
+      height: 48rpx;
+
+      .mobile_system {
+        width: 48rpx;
+        height: 48rpx;
+      }
+
+      .tips {
+        position: absolute;
+        right: -10rpx;
+        top: -8rpx;
+        z-index: 2;
+      }
+    }
+  }
+
+  .cont {
+    width: 100%;
+    background-color: var(--light);
+    &_bg {
+      position: sticky;
+      top: 0;
+      z-index: 9999;
+      background-color: var(--bg);
+
+      .bg_top {
+        padding: 0 30rpx;
+
+        .search {
+          padding: 26rpx 0;
+
+          :deep(.u-input) {
+            padding: 0 20px 0 16px !important;
+
+            .u-input__content__field-wrapper__field {
+              height: 47px;
+            }
+          }
+
+          .icon-search {
+            color: var(--text);
+            font-size: 40rpx;
+          }
+
+          .icon-camera {
+            color: var(--text-01);
+            font-size: 70rpx;
+          }
+        }
+
+        .debug-info {
+          padding: 10rpx 0;
+          text-align: center;
+
+          text {
+            font-size: 24rpx;
+            color: var(--primary);
+            background: rgba(255, 107, 107, 0.1);
+            padding: 8rpx 16rpx;
+            border-radius: 20rpx;
+          }
+        }
+      }
+    }
+
+    .content-scroll {
+      overflow: hidden;
+    }
+
+    .content {
+      width: 100%;
+      padding: 0 30rpx 30rpx;
+      position: relative;
+      box-sizing: border-box;
+
+      .banner-section {
+        margin-bottom: 32rpx;
+
+        .swiper-box {
+          width: 100%;
+          border-radius: 20rpx;
+          overflow: hidden;
+        }
+      }
+
+      .category-section {
+        margin-bottom: 32rpx;
+
+        .category-title {
+          font-size: 36rpx;
+          font-weight: bold;
+          color: var(--black);
+          margin-bottom: 24rpx;
+        }
+
+        .category-grid {
+          display: grid;
+          grid-template-columns: repeat(4, 1fr);
+          gap: 24rpx;
+
+          .category-item {
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+            padding: 24rpx 12rpx;
+            background: var(--inputBg);
+            border-radius: 20rpx;
+
+            .category-icon {
+              width: 60rpx;
+              height: 60rpx;
+              margin-bottom: 12rpx;
+            }
+
+            .category-name {
+              font-size: 24rpx;
+              color: var(--text);
+              text-align: center;
+            }
+
+            &.record-item {
+              background: linear-gradient(135deg, var(--primary), #ff6b6b);
+              position: relative;
+              overflow: hidden;
+
+              &::before {
+                content: "";
+                position: absolute;
+                top: -50%;
+                left: -50%;
+                width: 200%;
+                height: 200%;
+                background: radial-gradient(
+                  circle,
+                  rgba(255, 255, 255, 0.1) 0%,
+                  transparent 70%
+                );
+                animation: shimmer 3s infinite;
+              }
+
+              .record-icon {
+                width: 60rpx;
+                height: 60rpx;
+                border-radius: 50%;
+                background: rgba(255, 255, 255, 0.2);
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                margin-bottom: 12rpx;
+                position: relative;
+                z-index: 1;
+
+                .icon-microphone {
+                  font-size: 32rpx;
+                  color: var(--light);
+                }
+              }
+
+              .category-name {
+                color: var(--light);
+                font-weight: bold;
+                position: relative;
+                z-index: 1;
+              }
+            }
+          }
+        }
+      }
+
+      .hot-products-section,
+      .recommend-section {
+        margin-bottom: 32rpx;
+
+        .section-title {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          font-size: 36rpx;
+          font-weight: bold;
+          color: var(--black);
+          margin-bottom: 24rpx;
+
+          .more-btn {
+            display: flex;
+            align-items: center;
+            font-size: 28rpx;
+            color: var(--text-01);
+
+            .icon-arrow-right {
+              margin-left: 8rpx;
+              font-size: 24rpx;
+            }
+          }
+        }
+
+        .products-grid {
+          display: grid;
+          grid-template-columns: repeat(2, 1fr);
+          gap: 24rpx;
+
+          .product-item {
+            background: var(--inputBg);
+            border-radius: 20rpx;
+            overflow: hidden;
+
+            .product-image {
+              width: 100%;
+              height: 300rpx;
+              object-fit: cover;
+            }
+
+            .product-info {
+              padding: 20rpx;
+
+              .product-name {
+                font-size: 28rpx;
+                color: var(--black);
+                display: -webkit-box;
+                -webkit-line-clamp: 2;
+                line-clamp: 2;
+                -webkit-box-orient: vertical;
+                overflow: hidden;
+                margin-bottom: 12rpx;
+              }
+
+              .product-price {
+                display: flex;
+                align-items: center;
+                margin-bottom: 12rpx;
+
+                .current-price {
+                  font-size: 32rpx;
+                  font-weight: bold;
+                  color: var(--red);
+                  margin-right: 12rpx;
+                }
+
+                .original-price {
+                  font-size: 24rpx;
+                  color: var(--text-01);
+                  text-decoration: line-through;
+                }
+              }
+
+              .product-tags {
+                display: flex;
+                gap: 8rpx;
+
+                .tag {
+                  font-size: 20rpx;
+                  color: var(--primary);
+                  background: rgba(255, 107, 107, 0.1);
+                  padding: 4rpx 12rpx;
+                  border-radius: 12rpx;
+                }
+              }
+            }
+          }
+        }
+
+        .recommend-list {
+          .recommend-item {
+            display: flex;
+            background: var(--inputBg);
+            border-radius: 20rpx;
+            padding: 20rpx;
+            margin-bottom: 16rpx;
+
+            .recommend-image {
+              width: 160rpx;
+              height: 160rpx;
+              border-radius: 16rpx;
+              margin-right: 20rpx;
+            }
+
+            .recommend-info {
+              flex: 1;
+              display: flex;
+              flex-direction: column;
+              justify-content: space-between;
+
+              .recommend-name {
+                font-size: 28rpx;
+                color: var(--black);
+                display: -webkit-box;
+                -webkit-line-clamp: 2;
+                line-clamp: 2;
+                -webkit-box-orient: vertical;
+                overflow: hidden;
+                margin-bottom: 12rpx;
+              }
+
+              .recommend-price {
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+
+                .price {
+                  font-size: 32rpx;
+                  font-weight: bold;
+                  color: var(--red);
+                }
+
+                .sales {
+                  font-size: 24rpx;
+                  color: var(--text-01);
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+@keyframes shimmer {
+  0% {
+    transform: rotate(0deg);
+  }
+  100% {
+    transform: rotate(360deg);
+  }
+}
+</style>

+ 739 - 0
pagesBuyer/home/record.vue

@@ -0,0 +1,739 @@
+<template>
+  <Theme>
+    <view class="record-page">
+      <!-- 页面标题 -->
+      <view class="page-title">录音留言</view>
+
+      <!-- 录音区域 -->
+      <view class="record-section">
+        <view
+          class="record-area"
+          :class="{ recording: isRecording }"
+          @click="toggleRecord"
+        >
+          <view class="record-icon">
+            <i class="icon-font icon-microphone"></i>
+          </view>
+          <view class="record-text">
+            {{ isRecording ? "录音中..." : "点击开始录音" }}
+          </view>
+        </view>
+      </view>
+
+      <!-- 录音文件显示区域 -->
+      <view class="audio-display" v-if="voicePath">
+        <view class="audio-waveform">
+          <view
+            class="wave-bar"
+            v-for="(bar, index) in displayWaveBars"
+            :key="index"
+            :style="{ height: bar + 'px' }"
+          ></view>
+        </view>
+
+        <view class="audio-duration">{{ formatTime(recordTime) }}</view>
+
+        <view class="audio-controls">
+          <view
+            class="control-btn play-btn"
+            :class="{ playing: isPlaying }"
+            @click="togglePlay"
+          >
+            <i
+              class="icon-font"
+              :class="isPlaying ? 'icon-pause' : 'icon-play'"
+            ></i>
+          </view>
+
+          <view class="control-btn delete-btn" @click="deleteRecord">
+            <i class="icon-font icon-delete"></i>
+          </view>
+        </view>
+      </view>
+
+      <!-- 空状态 -->
+      <view class="empty-state" v-else>
+        <view class="empty-text">暂无录音</view>
+      </view>
+    </view>
+    <Tabbar page="index" />
+  </Theme>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import Theme from "@/components/Theme.vue";
+import Tabbar from "@/components/tabbar.vue";
+import { onShow } from "@dcloudio/uni-app";
+// 录音状态
+const isRecording = ref(false);
+const recordTime = ref(0);
+const recordTimer = ref(null);
+const voicePath = ref("");
+
+// 播放状态
+const isPlaying = ref(false);
+let audioContext = null;
+
+// 录音管理器
+let recorderManager = null;
+
+// 平台检测
+const platform = ref(uni.getSystemInfoSync().platform);
+const isH5 = ref(process.env.UNI_PLATFORM === "h5");
+const isApp = ref(process.env.UNI_PLATFORM === "app-plus");
+
+// 录音时的波形动画
+const waveBars = ref([]);
+
+// 显示时的静态波形
+const displayWaveBars = ref([]);
+
+// 开始录音
+const startRecord = () => {
+  // 检查平台支持
+  if (isH5.value) {
+    // H5平台使用Web API
+    startH5Record();
+  } else {
+    startAppRecord();
+  }
+};
+
+// H5录音功能
+const startH5Record = () => {
+  // 检查浏览器支持
+  // if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
+  //   uni.showToast({
+  //     title: "浏览器不支持录音功能",
+  //     icon: "none",
+  //   });
+  //   return;
+  // }
+
+  navigator.mediaDevices
+    .getUserMedia({ audio: true })
+    .then((stream) => {
+      // 创建MediaRecorder
+      const mediaRecorder = new MediaRecorder(stream);
+      const audioChunks = [];
+
+      mediaRecorder.ondataavailable = (event) => {
+        audioChunks.push(event.data);
+      };
+
+      mediaRecorder.onstop = () => {
+        const audioBlob = new Blob(audioChunks, { type: "audio/wav" });
+        const audioUrl = URL.createObjectURL(audioBlob);
+
+        // 停止所有音频轨道
+        stream.getTracks().forEach((track) => track.stop());
+
+        // 保存录音文件
+        saveRecordFile({
+          tempFilePath: audioUrl,
+          fileSize: audioBlob.size,
+        });
+      };
+
+      mediaRecorder.onerror = (err) => {
+        console.error("H5录音错误", err);
+        isRecording.value = false;
+        stopRecordTimer();
+        stopWaveAnimation();
+        uni.showToast({
+          title: "录音失败",
+          icon: "none",
+        });
+      };
+
+      // 开始录音
+      mediaRecorder.start();
+      isRecording.value = true;
+      startRecordTimer();
+      startWaveAnimation();
+
+      // 保存MediaRecorder引用用于停止
+      recorderManager = mediaRecorder;
+    })
+    .catch((error) => {
+      console.error("H5录音权限错误", error);
+      uni.showToast({
+        title: "需要录音权限",
+        icon: "none",
+      });
+    });
+};
+
+// APP录音功能
+const startAppRecord = () => {
+  try {
+    recorderManager = uni.getRecorderManager();
+
+    recorderManager.onStart(() => {
+      console.log("录音开始");
+      isRecording.value = true;
+      startRecordTimer();
+      startWaveAnimation();
+    });
+
+    recorderManager.onStop((res) => {
+      console.log("录音结束", res);
+      isRecording.value = false;
+      stopRecordTimer();
+      stopWaveAnimation();
+      saveRecordFile(res);
+    });
+
+    recorderManager.onError((err) => {
+      console.error("录音错误", err);
+      isRecording.value = false;
+      stopRecordTimer();
+      stopWaveAnimation();
+      uni.showToast({
+        title: "录音失败",
+        icon: "none",
+      });
+    });
+
+    const options = {
+      duration: 60000,
+      sampleRate: 16000,
+      numberOfChannels: 1,
+      encodeBitRate: 96000,
+      format: "mp3",
+      frameSize: 50,
+    };
+
+    recorderManager.start(options);
+  } catch (error) {
+    console.error("录音初始化失败", error);
+    uni.showToast({
+      title: "录音初始化失败",
+      icon: "none",
+    });
+  }
+};
+
+// 停止录音
+const stopRecord = () => {
+  if (recorderManager) {
+    if (isH5.value) {
+      // H5平台停止MediaRecorder
+      recorderManager.stop();
+    } else if (isApp.value) {
+      // APP平台停止uni.getRecorderManager
+      recorderManager.stop();
+    }
+  }
+};
+
+// 切换录音状态
+const toggleRecord = () => {
+  if (isRecording.value) {
+    stopRecord();
+  } else {
+    startRecord();
+  }
+};
+
+// 录音计时器
+const startRecordTimer = () => {
+  recordTime.value = 0;
+  recordTimer.value = setInterval(() => {
+    recordTime.value++;
+  }, 1000);
+};
+
+const stopRecordTimer = () => {
+  if (recordTimer.value) {
+    clearInterval(recordTimer.value);
+    recordTimer.value = null;
+  }
+};
+
+// 波形动画
+const startWaveAnimation = () => {
+  const animateWave = () => {
+    if (isRecording.value) {
+      // 创建更真实的波浪效果
+      waveBars.value = Array.from({ length: 25 }, (_, index) => {
+        // 使用正弦波创建更自然的波浪
+        const time = Date.now() / 100;
+        const frequency = 0.1 + (index % 3) * 0.05; // 不同频率
+        const amplitude = 30 + Math.random() * 20; // 随机振幅
+        const baseHeight = 20;
+
+        return (
+          baseHeight +
+          Math.abs(Math.sin(time * frequency + index * 0.5)) * amplitude
+        );
+      });
+      setTimeout(animateWave, 150);
+    }
+  };
+  animateWave();
+};
+
+const stopWaveAnimation = () => {
+  waveBars.value = [];
+};
+
+// 生成静态波形显示
+const generateDisplayWave = () => {
+  // 根据录音时长生成静态波形
+  const duration = recordTime.value;
+  const waveCount = Math.min(30, Math.max(15, duration * 2)); // 根据时长调整波形数量
+
+  displayWaveBars.value = Array.from({ length: waveCount }, (_, index) => {
+    // 使用录音时长作为种子,生成固定的波形
+    const seed = duration + index;
+    const amplitude = 20 + (seed % 40); // 20-60的振幅
+    const baseHeight = 15;
+
+    return baseHeight + amplitude;
+  });
+};
+
+// 保存录音文件
+const saveRecordFile = (res) => {
+  voicePath.value = res.tempFilePath;
+
+  // 生成静态波形显示
+  generateDisplayWave();
+
+  // 保存到本地存储
+  uni.setStorageSync("voicePath", voicePath.value);
+  uni.setStorageSync("recordTime", recordTime.value);
+
+  uni.showToast({
+    title: "录音保存成功",
+    icon: "success",
+  });
+};
+
+// 播放录音
+const togglePlay = () => {
+  if (isPlaying.value) {
+    // 停止播放
+    stopPlay();
+  } else {
+    // 开始播放
+    startPlay();
+  }
+};
+
+// 开始播放
+const startPlay = () => {
+  if (!voicePath.value) {
+    uni.showToast({
+      title: "没有录音文件",
+      icon: "none",
+    });
+    return;
+  }
+
+  if (isH5.value) {
+    // H5平台使用HTML5 Audio
+    startH5Play();
+  } else if (isApp.value) {
+    // APP平台使用uni.createInnerAudioContext
+    startAppPlay();
+  } else {
+    uni.showToast({
+      title: "当前平台不支持播放功能",
+      icon: "none",
+    });
+  }
+};
+
+// H5播放功能
+const startH5Play = () => {
+  if (audioContext) {
+    audioContext.pause();
+    audioContext = null;
+  }
+
+  try {
+    audioContext = new Audio(voicePath.value);
+
+    audioContext.onplay = () => {
+      isPlaying.value = true;
+    };
+
+    audioContext.onended = () => {
+      isPlaying.value = false;
+    };
+
+    audioContext.onerror = (err) => {
+      console.error("H5播放错误", err);
+      isPlaying.value = false;
+      uni.showToast({
+        title: "播放失败",
+        icon: "none",
+      });
+    };
+
+    audioContext.play();
+  } catch (error) {
+    console.error("H5播放初始化失败", error);
+    uni.showToast({
+      title: "播放功能不支持",
+      icon: "none",
+    });
+  }
+};
+
+// APP播放功能
+const startAppPlay = () => {
+  if (audioContext) {
+    audioContext.destroy();
+  }
+
+  try {
+    audioContext = uni.createInnerAudioContext();
+    audioContext.src = voicePath.value;
+
+    audioContext.onPlay(() => {
+      isPlaying.value = true;
+    });
+
+    audioContext.onEnded(() => {
+      isPlaying.value = false;
+    });
+
+    audioContext.onError((err) => {
+      console.error("APP播放错误", err);
+      isPlaying.value = false;
+      uni.showToast({
+        title: "播放失败",
+        icon: "none",
+      });
+    });
+
+    audioContext.play();
+  } catch (error) {
+    console.error("APP播放初始化失败", error);
+    uni.showToast({
+      title: "播放功能不支持",
+      icon: "none",
+    });
+  }
+};
+
+// 停止播放
+const stopPlay = () => {
+  if (audioContext) {
+    if (isH5.value) {
+      // H5平台停止Audio
+      audioContext.pause();
+      audioContext = null;
+    } else if (isApp.value) {
+      // APP平台停止uni.createInnerAudioContext
+      audioContext.stop();
+      audioContext.destroy();
+      audioContext = null;
+    }
+  }
+  isPlaying.value = false;
+};
+
+// 删除录音
+const deleteRecord = () => {
+  uni.showModal({
+    title: "确认删除",
+    content: "确定要删除这条录音吗?",
+    success: (res) => {
+      if (res.confirm) {
+        // 如果正在播放,先停止
+        if (isPlaying.value) {
+          stopPlay();
+        }
+
+        // 删除录音文件
+        if (voicePath.value) {
+          if (isH5.value) {
+            // H5平台释放Blob URL
+            try {
+              URL.revokeObjectURL(voicePath.value);
+              console.log("H5文件URL释放成功");
+            } catch (error) {
+              console.error("H5文件URL释放失败", error);
+            }
+          } else if (isApp.value) {
+            // APP平台删除文件
+            try {
+              uni.getFileSystemManager().unlink({
+                filePath: voicePath.value,
+                success: () => {
+                  console.log("文件删除成功");
+                },
+                fail: (err) => {
+                  console.error("文件删除失败", err);
+                },
+              });
+            } catch (error) {
+              console.error("文件删除异常", error);
+            }
+          }
+        }
+
+        // 清除录音数据
+        voicePath.value = "";
+        recordTime.value = 0;
+        displayWaveBars.value = [];
+
+        // 更新本地存储
+        uni.removeStorageSync("voicePath");
+        uni.removeStorageSync("recordTime");
+
+        uni.showToast({
+          title: "删除成功",
+          icon: "success",
+        });
+      }
+    },
+  });
+};
+
+// 格式化时间
+const formatTime = (seconds) => {
+  const mins = Math.floor(seconds / 60);
+  const secs = seconds % 60;
+  return `${mins.toString().padStart(2, "0")}:${secs
+    .toString()
+    .padStart(2, "0")}`;
+};
+
+// 提交表单
+const submitForm = () => {
+  if (!voicePath.value) {
+    uni.showToast({
+      title: "请先录制语音",
+      icon: "none",
+    });
+    return;
+  }
+
+  // 这里可以调用后台接口提交表单数据
+  const formData = {
+    voiceFile: voicePath.value, // 录音文件路径
+    voiceDuration: recordTime.value, // 录音时长
+    // 其他表单字段...
+  };
+
+  console.log("提交表单数据:", formData);
+  uni.showToast({
+    title: "表单提交成功",
+    icon: "success",
+  });
+
+  // 实际项目中这里应该调用API
+  // uni.request({
+  //   url: 'your-api-url',
+  //   method: 'POST',
+  //   data: formData,
+  //   success: (res) => {
+  //     Toast("提交成功");
+  //   },
+  //   fail: (err) => {
+  //     Toast("提交失败");
+  //   }
+  // });
+};
+
+// 加载录音
+const loadRecord = () => {
+  try {
+    const savedVoicePath = uni.getStorageSync("voicePath");
+    const savedRecordTime = uni.getStorageSync("recordTime");
+
+    if (savedVoicePath) {
+      voicePath.value = savedVoicePath;
+      recordTime.value = savedRecordTime || 0;
+      generateDisplayWave();
+    }
+  } catch (error) {
+    console.error("加载录音失败", error);
+  }
+};
+
+onMounted(() => {
+  loadRecord();
+});
+
+onUnmounted(() => {
+  // 清理资源
+  stopRecordTimer();
+  stopPlay();
+});
+
+onShow(() => {
+  // 页面显示时重新加载录音
+  loadRecord();
+});
+</script>
+
+<style lang="less" scoped>
+.record-page {
+  min-height: 100vh;
+  background: #f5f5f5;
+  padding: 20px;
+  padding-bottom: 120rpx;
+
+  .page-title {
+    font-size: 18px;
+    font-weight: bold;
+    color: #000;
+    margin-bottom: 20px;
+  }
+
+  .record-section {
+    margin-bottom: 20px;
+
+    .record-area {
+      background: #fff;
+      border: 2px solid #007aff;
+      border-radius: 12px;
+      padding: 20px;
+      display: flex;
+      align-items: center;
+      cursor: pointer;
+      transition: all 0.3s ease;
+
+      &.recording {
+        border-color: #ff3b30;
+        background: #fff5f5;
+      }
+
+      .record-icon {
+        width: 24px;
+        height: 24px;
+        margin-right: 12px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+
+        .icon-font {
+          font-size: 24px;
+          color: #999;
+        }
+      }
+
+      .record-text {
+        font-size: 16px;
+        color: #999;
+        flex: 1;
+      }
+    }
+  }
+
+  .audio-display {
+    background: #fff;
+    border: 1px solid #e0e0e0;
+    border-radius: 12px;
+    padding: 16px;
+    display: flex;
+    align-items: center;
+    gap: 12px;
+
+    .audio-waveform {
+      flex: 1;
+      display: flex;
+      align-items: center;
+      gap: 2px;
+      height: 40px;
+
+      .wave-bar {
+        width: 3px;
+        background: #000;
+        border-radius: 1.5px;
+        min-height: 4px;
+      }
+    }
+
+    .audio-duration {
+      font-size: 16px;
+      color: #000;
+      font-weight: 500;
+      min-width: 50px;
+      text-align: center;
+    }
+
+    .audio-controls {
+      display: flex;
+      gap: 8px;
+
+      .control-btn {
+        width: 32px;
+        height: 32px;
+        border-radius: 50%;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        cursor: pointer;
+        transition: all 0.2s ease;
+
+        &.play-btn {
+          background: #000;
+
+          &.playing {
+            background: #ff3b30;
+          }
+
+          .icon-font {
+            font-size: 14px;
+            color: #fff;
+          }
+        }
+
+        &.delete-btn {
+          background: #ff3b30;
+
+          .icon-font {
+            font-size: 14px;
+            color: #fff;
+          }
+        }
+
+        &:active {
+          transform: scale(0.95);
+        }
+      }
+    }
+  }
+
+  .empty-state {
+    background: #fff;
+    border: 1px solid #e0e0e0;
+    border-radius: 12px;
+    padding: 40px 20px;
+    text-align: center;
+
+    .empty-text {
+      font-size: 16px;
+      color: #999;
+    }
+  }
+}
+
+// 录音时的脉冲动画
+@keyframes pulse {
+  0% {
+    transform: scale(1);
+    opacity: 1;
+  }
+  50% {
+    transform: scale(1.05);
+    opacity: 0.8;
+  }
+  100% {
+    transform: scale(1);
+    opacity: 1;
+  }
+}
+
+.recording {
+  animation: pulse 1.5s infinite;
+}
+</style>

+ 602 - 0
pagesBuyer/profile/index.vue

@@ -0,0 +1,602 @@
+<template>
+  <Theme>
+    <view class="wrap">
+      <view class="cont">
+        <Navbar bgColor="transparent" fixed height="0px" leftShow />
+        <view class="cont_bg">
+          <view class="bg_top">
+            <view class="top_left">
+              <image
+                :src="userInfo.userimg"
+                mode="widthFix"
+                class="userInfo_avatar"
+              >
+              </image>
+              <view class="userInfo_" v-if="token">
+                <view class="user_name"> {{ userInfo.username }}</view>
+                <view class="user_id">
+                  <trans _t="ID" />: {{ userInfo.newid }}
+                </view>
+              </view>
+              <view class="login_btn" v-else @click="toLogin">
+                <trans _t="点击登录" />
+              </view>
+            </view>
+            <view class="bg_right" @click="onEdit" v-if="token">
+              <image src="../../static/user/edit.png" class="img"></image>
+            </view>
+          </view>
+          <view class="bg_logo">买家中心</view>
+        </view>
+
+        <!-- 订单状态 -->
+        <view class="order_status" v-if="token">
+          <view class="order_title">
+            <trans _t="我的订单" />
+            <view class="more_btn" @click="toOrderList">
+              <trans _t="查看全部" />
+              <i class="icon-font icon-arrow-right"></i>
+            </view>
+          </view>
+          <view class="order_items">
+            <view
+              class="order_item"
+              v-for="(item, index) in orderStatusList"
+              :key="index"
+              @click="toOrderList(item.status)"
+            >
+              <view class="order_icon">
+                <image :src="item.icon" class="icon" />
+                <view class="badge" v-if="item.count > 0">{{
+                  item.count
+                }}</view>
+              </view>
+              <text class="order_text">{{ t(item.text) }}</text>
+            </view>
+          </view>
+        </view>
+
+        <!-- 功能菜单 -->
+        <view class="menu_section">
+          <view class="menu_title">
+            <trans _t="我的服务" />
+          </view>
+          <view class="menu_list">
+            <view
+              class="menu_item"
+              v-for="(item, index) in menuList"
+              :key="index"
+              @click="handleMenuClick(item)"
+            >
+              <view class="menu_icon">
+                <image :src="item.icon" class="icon" />
+                <view class="badge" v-if="item.badge">{{ item.badge }}</view>
+              </view>
+              <text class="menu_text">{{ t(item.text) }}</text>
+              <i class="icon-font icon-arrow-right menu_arrow"></i>
+            </view>
+          </view>
+        </view>
+
+        <!-- 其他功能 -->
+        <view class="other_section">
+          <view class="menu_title">
+            <trans _t="其他功能" />
+          </view>
+          <view class="menu_list">
+            <view
+              class="menu_item"
+              v-for="(item, index) in otherMenuList"
+              :key="index"
+              @click="handleMenuClick(item)"
+            >
+              <view class="menu_icon">
+                <image :src="item.icon" class="icon" />
+              </view>
+              <text class="menu_text">{{ t(item.text) }}</text>
+              <i class="icon-font icon-arrow-right menu_arrow"></i>
+            </view>
+          </view>
+        </view>
+      </view>
+
+      <view class="footer" v-if="token">
+        <view class="switch_user_type" @click="switchUserType">
+          <trans _t="切换到卖家模式" />
+        </view>
+        <view class="exit_btn" @click="exit">
+          <trans _t="退出登录" />
+        </view>
+        <view class="version">v {{ manifest.versionName }}</view>
+      </view>
+    </view>
+    <Tabbar page="profile" />
+  </Theme>
+</template>
+
+<script setup>
+import Tabbar from "@/components/tabbar";
+import Navbar from "@/components/navbar";
+import { computed, watch, ref, reactive, onMounted, nextTick } from "vue";
+import { storeToRefs } from "pinia";
+import {
+  useUserStore,
+  useTabbarStore,
+  useShopStore,
+  useSystemStore,
+  useMessageStore,
+} from "@/store";
+import { t } from "@/locale";
+import { onShow, onLoad } from "@dcloudio/uni-app";
+import manifest from "@/manifest.json";
+import { Modal, Toast } from "@/utils";
+
+const useUser = useUserStore();
+const useTabbar = useTabbarStore();
+const useSystem = useSystemStore();
+const useShop = useShopStore();
+
+const useMessage = useMessageStore();
+const { globalMap } = storeToRefs(useMessage);
+const unreadCount = ref(0);
+
+watch(
+  () => globalMap.value.serviceChannel,
+  (newVal, oldVal) => {
+    if (newVal) {
+      unreadCount.value = newVal.unreadCount;
+    }
+  },
+  {
+    deep: true,
+    immediate: true,
+  }
+);
+
+const userInfo = computed(() => useUser.getuserInfo);
+const token = computed(() => useUser.getToken);
+
+// 订单状态列表
+const orderStatusList = ref([
+  {
+    icon: "/static/user/payment.png",
+    text: "待支付",
+    status: "waitPay",
+    count: 0,
+  },
+  {
+    icon: "/static/user/shipping.png",
+    text: "待发货",
+    status: "waitShipping",
+    count: 0,
+  },
+  {
+    icon: "/static/user/receive.png",
+    text: "待收货",
+    status: "waitReceive",
+    count: 0,
+  },
+  {
+    icon: "/static/user/comment.png",
+    text: "待评价",
+    status: "waitComment",
+    count: 0,
+  },
+  {
+    icon: "/static/user/refund.png",
+    text: "退款/售后",
+    status: "refund",
+    count: 0,
+  },
+]);
+
+// 功能菜单列表
+const menuList = ref([
+  {
+    icon: "/static/user/wallet.png",
+    text: "我的钱包",
+    url: "/pagesBuyer/profile/wallet",
+  },
+  {
+    icon: "/static/user/coupon.png",
+    text: "优惠券",
+    url: "/pagesBuyer/profile/coupon",
+    badge: "3",
+  },
+  {
+    icon: "/static/user/favorite.png",
+    text: "我的收藏",
+    url: "/pagesBuyer/profile/favorite",
+  },
+  {
+    icon: "/static/user/footprint.png",
+    text: "浏览历史",
+    url: "/pagesBuyer/profile/footprint",
+  },
+  {
+    icon: "/static/user/address.png",
+    text: "收货地址",
+    url: "/pagesBuyer/profile/address",
+  },
+  {
+    icon: "/static/user/invite.png",
+    text: "邀请好友",
+    url: "/pagesBuyer/profile/invite",
+  },
+]);
+
+// 其他功能菜单
+const otherMenuList = ref([
+  {
+    icon: "/static/user/setting.png",
+    text: "设置",
+    url: "/pagesBuyer/profile/setting",
+  },
+  {
+    icon: "/static/user/help.png",
+    text: "帮助中心",
+    url: "/pagesBuyer/profile/help",
+  },
+  {
+    icon: "/static/user/feedback.png",
+    text: "意见反馈",
+    url: "/pagesBuyer/profile/feedback",
+  },
+  {
+    icon: "/static/user/about.png",
+    text: "关于我们",
+    url: "/pagesBuyer/profile/about",
+  },
+]);
+
+// 退出登录
+const exit = () => {
+  Modal({ content: t("确定要退出登录吗") }).then(async () => {
+    useUser.loginOut();
+  });
+};
+
+// 去登录
+const toLogin = () => {
+  uni.navigateTo({
+    url: "/pages/login/login",
+  });
+};
+
+// 编辑用户信息
+const onEdit = () => {
+  uni.navigateTo({ url: "/pagesBuyer/profile/edit" });
+};
+
+// 查看订单列表
+const toOrderList = (status) => {
+  uni.navigateTo({
+    url: `/pagesBuyer/profile/orders?status=${status || ""}`,
+  });
+};
+
+// 处理菜单点击
+const handleMenuClick = (item) => {
+  if (!item.url) {
+    Toast(t("功能开发中"));
+    return;
+  }
+
+  if (item.url === "service") {
+    useSystem.service();
+    return;
+  }
+
+  uni.navigateTo({ url: item.url });
+};
+
+// 获取订单数量
+const getOrderCount = async () => {
+  if (!token.value) return;
+
+  try {
+    // 这里应该调用实际的API获取订单数量
+    // const res = await USER_ORDER_COUNT();
+    // orderStatusList.value[0].count = res.data.waitPay || 0;
+    // orderStatusList.value[1].count = res.data.waitShipping || 0;
+    // orderStatusList.value[2].count = res.data.waitReceive || 0;
+    // orderStatusList.value[3].count = res.data.waitComment || 0;
+    // orderStatusList.value[4].count = res.data.refund || 0;
+  } catch (error) {
+    Toast(error.msg);
+  }
+};
+
+// 切换用户类型
+const switchUserType = () => {
+  Modal({
+    content: t("确定要切换到卖家模式吗?切换后底部导航将显示卖家功能"),
+  }).then(() => {
+    // 设置为卖家类型
+    useUser.setUserType(1);
+    // 跳转到卖家首页
+    uni.reLaunch({
+      url: "/pages/index/index",
+    });
+  });
+};
+
+onMounted(() => {
+  getOrderCount();
+});
+
+onLoad(() => {
+  // useUser.getUserInfo();
+});
+
+onShow(() => {
+  if (token.value) {
+    useUser.getUserInfo({}, { isLoading: true });
+    getOrderCount();
+  }
+});
+
+uni.hideTabBar();
+</script>
+
+<style lang="less" scoped>
+@import url("@/style.less");
+
+.wrap {
+  background: var(--bg);
+  min-height: 100vh;
+  padding-bottom: calc(90rpx + constant(safe-area-inset-bottom));
+  padding-bottom: calc(90rpx + env(safe-area-inset-bottom));
+  .flex();
+  flex-direction: column;
+  justify-content: space-between;
+
+  .cont {
+    padding-bottom: 24rpx;
+    flex-grow: 1;
+
+    &_bg {
+      background: var(--black);
+      border-radius: 0 0 20rpx 20rpx;
+      position: relative;
+
+      .bg_top {
+        .flex_position(space-between);
+        padding: 40rpx 40rpx 140rpx;
+        position: relative;
+        z-index: 1;
+
+        .top_left {
+          .flex();
+
+          .userInfo_avatar {
+            width: 120rpx;
+            height: 120rpx;
+            border-radius: 50%;
+          }
+
+          .login_btn {
+            .flex_center();
+            background-color: var(--primary);
+            border: 2px solid var(--bg-primary);
+            border-radius: 30rpx;
+            color: var(--light);
+            padding: 8rpx 30rpx;
+            .size(28rpx);
+            font-weight: 700;
+            margin-left: 24rpx;
+          }
+
+          .userInfo_ {
+            .hor();
+            flex-direction: column;
+            margin-left: 24rpx;
+            color: var(--light);
+
+            .user_name {
+              .size();
+              font-weight: 700;
+              line-height: 44rpx;
+            }
+
+            .user_id {
+              .size(24rpx);
+              line-height: 40rpx;
+            }
+          }
+        }
+
+        .bg_right {
+          .img {
+            width: 40rpx;
+            height: 40rpx;
+          }
+        }
+      }
+
+      .bg_logo {
+        position: absolute;
+        .size(128rpx);
+        color: var(--inputBg);
+        opacity: 0.06;
+        top: 0;
+        left: 50%;
+        font-weight: bold;
+        transform: translateX(-50%);
+        line-height: 128rpx;
+        text-transform: uppercase;
+        font-family: "HarmonyOS_Sans";
+        white-space: nowrap;
+      }
+    }
+
+    .order_status {
+      margin: -70rpx 24rpx 32rpx;
+      position: relative;
+      z-index: 2;
+      background: var(--light);
+      border-radius: 20rpx;
+      padding: 32rpx 24rpx;
+
+      .order_title {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        font-size: 32rpx;
+        font-weight: bold;
+        color: var(--black);
+        margin-bottom: 24rpx;
+
+        .more_btn {
+          display: flex;
+          align-items: center;
+          font-size: 24rpx;
+          color: var(--text-01);
+
+          .icon-arrow-right {
+            margin-left: 8rpx;
+            font-size: 20rpx;
+          }
+        }
+      }
+
+      .order_items {
+        display: flex;
+        justify-content: space-around;
+
+        .order_item {
+          display: flex;
+          flex-direction: column;
+          align-items: center;
+
+          .order_icon {
+            position: relative;
+            margin-bottom: 12rpx;
+
+            .icon {
+              width: 48rpx;
+              height: 48rpx;
+            }
+
+            .badge {
+              position: absolute;
+              top: -8rpx;
+              right: -8rpx;
+              background: var(--danger);
+              color: var(--light);
+              font-size: 20rpx;
+              padding: 4rpx 8rpx;
+              border-radius: 20rpx;
+              min-width: 32rpx;
+              text-align: center;
+              line-height: 1;
+            }
+          }
+
+          .order_text {
+            font-size: 24rpx;
+            color: var(--text);
+          }
+        }
+      }
+    }
+
+    .menu_section,
+    .other_section {
+      margin: 0 24rpx 32rpx;
+
+      .menu_title {
+        font-size: 32rpx;
+        font-weight: bold;
+        color: var(--black);
+        margin-bottom: 24rpx;
+      }
+
+      .menu_list {
+        background: var(--light);
+        border-radius: 20rpx;
+        overflow: hidden;
+
+        .menu_item {
+          display: flex;
+          align-items: center;
+          padding: 32rpx 24rpx;
+          border-bottom: 1rpx solid var(--border-color);
+
+          &:last-child {
+            border-bottom: none;
+          }
+
+          .menu_icon {
+            position: relative;
+            margin-right: 24rpx;
+
+            .icon {
+              width: 40rpx;
+              height: 40rpx;
+            }
+
+            .badge {
+              position: absolute;
+              top: -8rpx;
+              right: -8rpx;
+              background: var(--danger);
+              color: var(--light);
+              font-size: 20rpx;
+              padding: 4rpx 8rpx;
+              border-radius: 20rpx;
+              min-width: 32rpx;
+              text-align: center;
+              line-height: 1;
+            }
+          }
+
+          .menu_text {
+            flex: 1;
+            font-size: 28rpx;
+            color: var(--black);
+          }
+
+          .menu_arrow {
+            font-size: 24rpx;
+            color: var(--text-01);
+          }
+        }
+      }
+    }
+  }
+
+  .footer {
+    padding: 0 24rpx;
+
+    .switch_user_type {
+      background-color: var(--primary);
+      color: var(--light);
+      .size(28rpx);
+      height: 84rpx;
+      padding: 16rpx 30rpx;
+      .flex_center();
+      border-radius: 16rpx;
+      margin-bottom: 16rpx;
+    }
+
+    .exit_btn {
+      background-color: var(--black);
+      color: var(--light);
+      .size(28rpx);
+      height: 84rpx;
+      padding: 16rpx 30rpx;
+      .flex_center();
+      border-radius: 16rpx;
+      margin-bottom: 24rpx;
+    }
+
+    .version {
+      text-align: center;
+      color: var(--black);
+      .size(24rpx);
+      padding: 24rpx 0;
+    }
+  }
+}
+</style>

+ 212 - 0
pagesBuyer/shop/README.md

@@ -0,0 +1,212 @@
+# 买家端店铺功能说明
+
+## 功能概述
+为买家端添加了店铺功能,包括店铺列表、搜索、店铺详情等核心功能。
+
+## 功能特性
+
+### 1. 店铺列表页面 (`pagesBuyer/shop/index.vue`)
+- **9个店铺展示**: 展示9个不同类别的店铺
+- **搜索功能**: 支持按店铺名称、描述、标签搜索
+- **店铺信息**: 显示店铺头像、名称、描述、评分、销量、距离
+- **营业状态**: 显示店铺是否营业中
+- **标签系统**: 显示店铺特色标签
+- **点击跳转**: 点击店铺进入店铺详情页
+
+### 2. 店铺详情页面 (`pagesBuyer/shop/detail.vue`)
+- **店铺头部**: 显示店铺基本信息
+- **收藏功能**: 可以收藏/取消收藏店铺
+- **功能按钮**: 联系店铺、查看位置、分享店铺
+- **商品分类**: 显示店铺商品分类
+- **商品列表**: 展示店铺热销商品
+- **商品详情**: 点击商品可查看详情
+
+### 3. 底部导航更新
+- **新增店铺选项**: 在买家端导航中添加店铺选项
+- **图标支持**: 使用专门的店铺图标
+- **智能跳转**: 根据用户类型智能跳转
+
+## 页面结构
+
+```
+pagesBuyer/shop/
+├── index.vue      # 店铺列表页面
+├── detail.vue     # 店铺详情页面
+└── README.md      # 说明文档
+```
+
+## 店铺数据示例
+
+### 店铺列表数据
+```javascript
+const shops = ref([
+  {
+    id: 1,
+    name: "时尚潮流店",
+    description: "专注时尚潮流服饰,品质保证",
+    avatar: "/static/shop/shop1.png",
+    rating: 4.8,
+    sales: 1234,
+    distance: "1.2km",
+    status: "online",
+    tags: ["时尚", "潮流", "品质"]
+  },
+  // ... 更多店铺数据
+]);
+```
+
+### 店铺详情数据
+```javascript
+const shopInfo = ref({
+  id: 1,
+  name: "时尚潮流店",
+  description: "专注时尚潮流服饰,品质保证,为您提供最优质的商品和服务",
+  avatar: "/static/shop/shop1.png",
+  rating: 4.8,
+  sales: 1234,
+  distance: "1.2km",
+  status: "online",
+  tags: ["时尚", "潮流", "品质", "正品"]
+});
+```
+
+## 搜索功能
+
+### 搜索范围
+- 店铺名称
+- 店铺描述
+- 店铺标签
+
+### 搜索实现
+```javascript
+const filteredShops = computed(() => {
+  if (!searchValue.value.trim()) {
+    return shops.value;
+  }
+  
+  const keyword = searchValue.value.toLowerCase();
+  return shops.value.filter(shop => 
+    shop.name.toLowerCase().includes(keyword) ||
+    shop.description.toLowerCase().includes(keyword) ||
+    shop.tags.some(tag => tag.toLowerCase().includes(keyword))
+  );
+});
+```
+
+## 导航配置
+
+### 买家端导航更新
+```javascript
+const buyerNavList = ref([
+  {
+    name: "home",
+    title: "tabbar.首页",
+    img: home,
+    img_active: home_active,
+    num: 0,
+  },
+  {
+    name: "shop",
+    title: "tabbar.店铺",
+    img: shop,
+    img_active: shop_active,
+    num: 0,
+  },
+  {
+    name: "cart",
+    title: "tabbar.购物车",
+    img: cart,
+    img_active: cart_active,
+    num: cartNum.value,
+  },
+  {
+    name: "profile",
+    title: "tabbar.我的",
+    img: user,
+    img_active: user_active,
+    num: 0,
+  },
+]);
+```
+
+## 路由配置
+
+### pages.json 配置
+```json
+{
+  "path": "pagesBuyer/shop/index"
+},
+{
+  "path": "pagesBuyer/shop/detail"
+}
+```
+
+## 页面跳转
+
+### 跳转到店铺详情
+```javascript
+const toShopDetail = (shop) => {
+  uni.navigateTo({
+    url: `/pagesBuyer/shop/detail?id=${shop.id}&name=${shop.name}`
+  });
+};
+```
+
+### 跳转到商品详情
+```javascript
+const toProductDetail = (product) => {
+  uni.navigateTo({
+    url: `/pagesBuyer/shop/product?id=${product.id}`
+  });
+};
+```
+
+## 样式特点
+
+### 1. 响应式设计
+- 使用 Grid 布局展示商品
+- 支持不同屏幕尺寸
+- 移动端优化
+
+### 2. 统一风格
+- 遵循项目设计规范
+- 使用项目颜色变量
+- 保持UI一致性
+
+### 3. 交互反馈
+- 点击效果
+- 状态变化
+- 加载状态
+
+## 扩展功能
+
+### 1. 店铺收藏
+- 支持收藏/取消收藏
+- 收藏状态持久化
+- 收藏列表管理
+
+### 2. 店铺评价
+- 用户评价系统
+- 评分统计
+- 评价展示
+
+### 3. 店铺活动
+- 优惠活动展示
+- 促销信息
+- 活动倒计时
+
+## 注意事项
+
+1. **图片资源**: 需要准备店铺头像、商品图片等资源
+2. **数据接口**: 实际使用时需要对接后端API
+3. **权限控制**: 某些功能可能需要登录权限
+4. **性能优化**: 大量数据时考虑分页加载
+5. **错误处理**: 添加网络错误、数据异常等处理
+
+## 技术栈
+
+- Vue 3 Composition API
+- uni-app 框架
+- Less 样式预处理器
+- Pinia 状态管理
+- 响应式设计

+ 167 - 0
pagesBuyer/shop/components/shop_item.vue

@@ -0,0 +1,167 @@
+<template>
+  <view class="shop-item" @click="toShopDetail">
+    <view class="shop-avatar">
+      <image :src="item.avatar" class="avatar-img" />
+      <view class="shop-status" :class="item.status">
+        {{ item.status === "online" ? "营业中" : "休息中" }}
+      </view>
+    </view>
+
+    <view class="shop-info">
+      <view class="shop-name">{{ item.name }}</view>
+      <view class="shop-desc">{{ item.description }}</view>
+      <view class="shop-stats">
+        <view class="stat-item">
+          <text class="stat-label">评分:</text>
+          <text class="stat-value">{{ item.rating }}</text>
+        </view>
+        <view class="stat-item">
+          <text class="stat-label">销量:</text>
+          <text class="stat-value">{{ item.sales }}</text>
+        </view>
+        <view class="stat-item">
+          <text class="stat-label">距离:</text>
+          <text class="stat-value">{{ item.distance }}</text>
+        </view>
+      </view>
+      <view class="shop-tags">
+        <text class="tag" v-for="tag in item.tags" :key="tag">
+          {{ tag }}
+        </text>
+      </view>
+    </view>
+
+    <view class="shop-arrow">
+      <i class="icon-font icon-arrow-right"></i>
+    </view>
+  </view>
+</template>
+
+<script setup>
+const props = defineProps({
+  item: {
+    type: Object,
+    default: () => ({}),
+  },
+});
+
+const emit = defineEmits(["click"]);
+
+const toShopDetail = () => {
+  emit("click", props.item);
+};
+</script>
+
+<style lang="less" scoped>
+@import url("@/style.less");
+
+.shop-item {
+  display: flex;
+  align-items: center;
+  background: var(--light);
+  border-radius: 20rpx;
+  padding: 24rpx;
+  margin-bottom: 20rpx;
+  box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
+
+  .shop-avatar {
+    position: relative;
+    margin-right: 24rpx;
+
+    .avatar-img {
+      width: 120rpx;
+      height: 120rpx;
+      border-radius: 20rpx;
+    }
+
+    .shop-status {
+      position: absolute;
+      bottom: -8rpx;
+      left: 50%;
+      transform: translateX(-50%);
+      font-size: 20rpx;
+      padding: 4rpx 12rpx;
+      border-radius: 20rpx;
+      color: var(--light);
+
+      &.online {
+        background: var(--success);
+      }
+
+      &.offline {
+        background: var(--text-01);
+      }
+    }
+  }
+
+  .shop-info {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+
+    .shop-name {
+      font-size: 32rpx;
+      font-weight: bold;
+      color: var(--black);
+      margin-bottom: 8rpx;
+    }
+
+    .shop-desc {
+      font-size: 24rpx;
+      color: var(--text-01);
+      margin-bottom: 16rpx;
+      display: -webkit-box;
+      -webkit-line-clamp: 1;
+      line-clamp: 1;
+      -webkit-box-orient: vertical;
+      overflow: hidden;
+    }
+
+    .shop-stats {
+      display: flex;
+      gap: 24rpx;
+      margin-bottom: 12rpx;
+
+      .stat-item {
+        display: flex;
+        align-items: center;
+
+        .stat-label {
+          font-size: 22rpx;
+          color: var(--text-01);
+          margin-right: 4rpx;
+        }
+
+        .stat-value {
+          font-size: 22rpx;
+          color: var(--black);
+          font-weight: bold;
+        }
+      }
+    }
+
+    .shop-tags {
+      display: flex;
+      gap: 8rpx;
+      flex-wrap: wrap;
+
+      .tag {
+        font-size: 20rpx;
+        color: var(--primary);
+        background: rgba(255, 107, 107, 0.1);
+        padding: 4rpx 12rpx;
+        border-radius: 12rpx;
+      }
+    }
+  }
+
+  .shop-arrow {
+    margin-left: 16rpx;
+
+    .icon-arrow-right {
+      font-size: 24rpx;
+      color: var(--text-01);
+    }
+  }
+}
+</style>

+ 506 - 0
pagesBuyer/shop/detail.vue

@@ -0,0 +1,506 @@
+<template>
+  <Theme>
+    <view class="wrap">
+      <Navbar fixed leftIconColor="var(--black)">
+        <template #center>
+          <view class="nav_title">
+            {{ shopInfo.name || "店铺详情" }}
+          </view>
+        </template>
+        <template #right>
+          <view class="nav_right" @click="toggleFavorite">
+            <i
+              class="icon-font"
+              :class="isFavorite ? 'icon-heart-fill' : 'icon-heart'"
+            ></i>
+          </view>
+        </template>
+      </Navbar>
+
+      <!-- 店铺头部信息 -->
+      <view class="shop-header">
+        <view class="shop-avatar">
+          <image :src="shopInfo.avatar" class="avatar-img" />
+          <view class="shop-status" :class="shopInfo.status">
+            {{ shopInfo.status === "online" ? "营业中" : "休息中" }}
+          </view>
+        </view>
+
+        <view class="shop-info">
+          <view class="shop-name">{{ shopInfo.name }}</view>
+          <view class="shop-desc">{{ shopInfo.description }}</view>
+          <view class="shop-stats">
+            <view class="stat-item">
+              <text class="stat-label">评分:</text>
+              <text class="stat-value">{{ shopInfo.rating }}</text>
+              <text class="stat-unit">分</text>
+            </view>
+            <view class="stat-item">
+              <text class="stat-label">销量:</text>
+              <text class="stat-value">{{ shopInfo.sales }}</text>
+              <text class="stat-unit">件</text>
+            </view>
+            <view class="stat-item">
+              <text class="stat-label">距离:</text>
+              <text class="stat-value">{{ shopInfo.distance }}</text>
+            </view>
+          </view>
+        </view>
+      </view>
+
+      <!-- 店铺标签 -->
+      <view class="shop-tags">
+        <text class="tag" v-for="tag in shopInfo.tags" :key="tag">
+          {{ tag }}
+        </text>
+      </view>
+
+      <!-- 店铺功能按钮 -->
+      <view class="shop-actions">
+        <view class="action-btn" @click="contactShop">
+          <i class="icon-font icon-chat"></i>
+          <text>联系店铺</text>
+        </view>
+        <view class="action-btn" @click="viewLocation">
+          <i class="icon-font icon-location"></i>
+          <text>查看位置</text>
+        </view>
+        <view class="action-btn" @click="shareShop">
+          <i class="icon-font icon-share"></i>
+          <text>分享店铺</text>
+        </view>
+      </view>
+
+      <!-- 商品分类 -->
+      <view class="category-section">
+        <view class="section-title">商品分类</view>
+        <view class="category-list">
+          <view
+            class="category-item"
+            v-for="category in categories"
+            :key="category.id"
+            @click="selectCategory(category)"
+            :class="{ active: selectedCategory === category.id }"
+          >
+            <image :src="category.icon" class="category-icon" />
+            <text class="category-name">{{ category.name }}</text>
+          </view>
+        </view>
+      </view>
+
+      <!-- 商品列表 -->
+      <view class="products-section">
+        <view class="section-title">热销商品</view>
+        <view class="products-grid">
+          <view
+            class="product-item"
+            v-for="product in products"
+            :key="product.id"
+            @click="toProductDetail(product)"
+          >
+            <image :src="product.image" class="product-image" />
+            <view class="product-info">
+              <text class="product-name">{{ product.name }}</text>
+              <view class="product-price">
+                <text class="current-price"
+                  >{{ symbol.symbol }}{{ product.price }}</text
+                >
+                <text class="original-price" v-if="product.originalPrice"
+                  >{{ symbol.symbol }}{{ product.originalPrice }}</text
+                >
+              </view>
+              <view class="product-sales">已售{{ product.sales }}件</view>
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+  </Theme>
+</template>
+
+<script setup>
+import { ref, computed, onMounted } from "vue";
+import Navbar from "@/components/navbar";
+import { useSystemStore } from "@/store";
+import { t } from "@/locale";
+import { onLoad } from "@dcloudio/uni-app";
+
+const useSystem = useSystemStore();
+const symbol = computed(() => useSystem.getSymbol);
+
+// 店铺信息
+const shopInfo = ref({
+  id: 1,
+  name: "时尚潮流店",
+  description: "专注时尚潮流服饰,品质保证,为您提供最优质的商品和服务",
+  avatar: "/static/shop/shop1.png",
+  rating: 4.8,
+  sales: 1234,
+  distance: "1.2km",
+  status: "online",
+  tags: ["时尚", "潮流", "品质", "正品"],
+});
+
+// 是否收藏
+const isFavorite = ref(false);
+
+// 选中的分类
+const selectedCategory = ref(1);
+
+// 商品分类
+const categories = ref([
+  { id: 1, name: "全部", icon: "/static/shop/category_all.png" },
+  { id: 2, name: "上衣", icon: "/static/shop/category_top.png" },
+  { id: 3, name: "裤子", icon: "/static/shop/category_pants.png" },
+  { id: 4, name: "鞋子", icon: "/static/shop/category_shoes.png" },
+  { id: 5, name: "配饰", icon: "/static/shop/category_accessories.png" },
+]);
+
+// 商品列表
+const products = ref([
+  {
+    id: 1,
+    name: "时尚T恤",
+    image: "/static/shop/product1.png",
+    price: "99.00",
+    originalPrice: "129.00",
+    sales: 234,
+  },
+  {
+    id: 2,
+    name: "休闲牛仔裤",
+    image: "/static/shop/product2.png",
+    price: "199.00",
+    originalPrice: "259.00",
+    sales: 156,
+  },
+  {
+    id: 3,
+    name: "运动鞋",
+    image: "/static/shop/product3.png",
+    price: "299.00",
+    originalPrice: "399.00",
+    sales: 89,
+  },
+  {
+    id: 4,
+    name: "时尚帽子",
+    image: "/static/shop/product4.png",
+    price: "59.00",
+    originalPrice: "79.00",
+    sales: 67,
+  },
+]);
+
+// 切换收藏状态
+const toggleFavorite = () => {
+  isFavorite.value = !isFavorite.value;
+  uni.showToast({
+    title: isFavorite.value ? "已收藏" : "已取消收藏",
+    icon: "none",
+  });
+};
+
+// 联系店铺
+const contactShop = () => {
+  uni.showToast({
+    title: "联系店铺功能开发中",
+    icon: "none",
+  });
+};
+
+// 查看位置
+const viewLocation = () => {
+  uni.showToast({
+    title: "查看位置功能开发中",
+    icon: "none",
+  });
+};
+
+// 分享店铺
+const shareShop = () => {
+  uni.showToast({
+    title: "分享店铺功能开发中",
+    icon: "none",
+  });
+};
+
+// 选择分类
+const selectCategory = (category) => {
+  selectedCategory.value = category.id;
+  // 这里可以根据分类筛选商品
+  console.log("选择分类:", category.name);
+};
+
+// 跳转到商品详情
+const toProductDetail = (product) => {
+  uni.navigateTo({
+    url: `/pagesBuyer/shop/product?id=${product.id}`,
+  });
+};
+
+onLoad((options) => {
+  // 从页面参数获取店铺信息
+  if (options.id) {
+    shopInfo.value.id = options.id;
+  }
+  if (options.name) {
+    shopInfo.value.name = decodeURIComponent(options.name);
+  }
+});
+
+onMounted(() => {
+  // 页面加载完成后的逻辑
+});
+</script>
+
+<style lang="less" scoped>
+@import url("@/style.less");
+
+.wrap {
+  min-height: 100vh;
+  background: var(--bg);
+  padding-bottom: 30rpx;
+
+  .nav_title {
+    color: var(--black);
+    font-size: 36rpx;
+    font-weight: bold;
+  }
+
+  .nav_right {
+    color: var(--primary);
+    font-size: 40rpx;
+  }
+
+  .shop-header {
+    display: flex;
+    align-items: center;
+    background: var(--light);
+    padding: 30rpx;
+    margin-bottom: 20rpx;
+
+    .shop-avatar {
+      position: relative;
+      margin-right: 24rpx;
+
+      .avatar-img {
+        width: 120rpx;
+        height: 120rpx;
+        border-radius: 20rpx;
+      }
+
+      .shop-status {
+        position: absolute;
+        bottom: -8rpx;
+        left: 50%;
+        transform: translateX(-50%);
+        font-size: 20rpx;
+        padding: 4rpx 12rpx;
+        border-radius: 20rpx;
+        color: var(--light);
+
+        &.online {
+          background: var(--success);
+        }
+
+        &.offline {
+          background: var(--text-01);
+        }
+      }
+    }
+
+    .shop-info {
+      flex: 1;
+
+      .shop-name {
+        font-size: 36rpx;
+        font-weight: bold;
+        color: var(--black);
+        margin-bottom: 8rpx;
+      }
+
+      .shop-desc {
+        font-size: 24rpx;
+        color: var(--text-01);
+        margin-bottom: 16rpx;
+        line-height: 1.4;
+      }
+
+      .shop-stats {
+        display: flex;
+        gap: 24rpx;
+
+        .stat-item {
+          display: flex;
+          align-items: center;
+
+          .stat-label {
+            font-size: 22rpx;
+            color: var(--text-01);
+            margin-right: 4rpx;
+          }
+
+          .stat-value {
+            font-size: 24rpx;
+            color: var(--black);
+            font-weight: bold;
+            margin-right: 4rpx;
+          }
+
+          .stat-unit {
+            font-size: 20rpx;
+            color: var(--text-01);
+          }
+        }
+      }
+    }
+  }
+
+  .shop-tags {
+    padding: 0 30rpx 20rpx;
+    display: flex;
+    gap: 12rpx;
+    flex-wrap: wrap;
+
+    .tag {
+      font-size: 22rpx;
+      color: var(--primary);
+      background: rgba(255, 107, 107, 0.1);
+      padding: 8rpx 16rpx;
+      border-radius: 20rpx;
+    }
+  }
+
+  .shop-actions {
+    display: flex;
+    padding: 0 30rpx 30rpx;
+    gap: 20rpx;
+
+    .action-btn {
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      background: var(--light);
+      padding: 24rpx 16rpx;
+      border-radius: 16rpx;
+
+      .icon-font {
+        font-size: 40rpx;
+        color: var(--primary);
+        margin-bottom: 8rpx;
+      }
+
+      text {
+        font-size: 24rpx;
+        color: var(--black);
+      }
+    }
+  }
+
+  .category-section,
+  .products-section {
+    padding: 0 30rpx 30rpx;
+
+    .section-title {
+      font-size: 32rpx;
+      font-weight: bold;
+      color: var(--black);
+      margin-bottom: 20rpx;
+    }
+  }
+
+  .category-list {
+    display: flex;
+    gap: 20rpx;
+    overflow-x: auto;
+    padding-bottom: 10rpx;
+
+    .category-item {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      min-width: 120rpx;
+      padding: 20rpx 16rpx;
+      background: var(--light);
+      border-radius: 16rpx;
+      border: 2rpx solid transparent;
+
+      &.active {
+        border-color: var(--primary);
+        background: rgba(255, 107, 107, 0.05);
+      }
+
+      .category-icon {
+        width: 48rpx;
+        height: 48rpx;
+        margin-bottom: 8rpx;
+      }
+
+      .category-name {
+        font-size: 22rpx;
+        color: var(--black);
+        text-align: center;
+      }
+    }
+  }
+
+  .products-grid {
+    display: grid;
+    grid-template-columns: repeat(2, 1fr);
+    gap: 20rpx;
+
+    .product-item {
+      background: var(--light);
+      border-radius: 16rpx;
+      overflow: hidden;
+
+      .product-image {
+        width: 100%;
+        height: 200rpx;
+        object-fit: cover;
+      }
+
+      .product-info {
+        padding: 16rpx;
+
+        .product-name {
+          font-size: 24rpx;
+          color: var(--black);
+          margin-bottom: 8rpx;
+          display: -webkit-box;
+          -webkit-line-clamp: 2;
+          line-clamp: 2;
+          -webkit-box-orient: vertical;
+          overflow: hidden;
+        }
+
+        .product-price {
+          display: flex;
+          align-items: center;
+          margin-bottom: 8rpx;
+
+          .current-price {
+            font-size: 28rpx;
+            font-weight: bold;
+            color: var(--red);
+            margin-right: 8rpx;
+          }
+
+          .original-price {
+            font-size: 20rpx;
+            color: var(--text-01);
+            text-decoration: line-through;
+          }
+        }
+
+        .product-sales {
+          font-size: 20rpx;
+          color: var(--text-01);
+        }
+      }
+    }
+  }
+}
+</style>

+ 296 - 0
pagesBuyer/shop/index.vue

@@ -0,0 +1,296 @@
+<template>
+  <Theme>
+    <view class="wrap">
+      <Navbar fixed leftShow leftIconColor="var(--bg)">
+        <template #center>
+          <view class="nav_title">
+            <trans _t="店铺" />
+          </view>
+        </template>
+      </Navbar>
+
+      <!-- 搜索栏 - 固定在顶部 -->
+      <view class="search-section">
+        <view class="search-box">
+          <Search
+            v-model="searchValue"
+            border="none"
+            @input="onSearchInput"
+            @click="onSearchClick"
+            :placeholder="t('搜索店铺')"
+          >
+            <template #prefix>
+              <i class="icon-font icon-search"></i>
+            </template>
+          </Search>
+        </view>
+      </view>
+
+      <!-- 店铺列表 - 使用封装的List组件 -->
+      <view class="content">
+        <List
+          ref="listRef"
+          :defaultParams="defaultParams"
+          @datas="getList"
+          :isLoading="false"
+        >
+          <template #item="{ item }">
+            <shopItem :item="item" @click="toShopDetail" />
+          </template>
+        </List>
+      </view>
+    </view>
+    <Tabbar page="shop" />
+  </Theme>
+</template>
+
+<script setup>
+import { ref, reactive, watch } from "vue";
+import Tabbar from "@/components/tabbar";
+import Navbar from "@/components/navbar";
+import Search from "@/components/input";
+import List from "@/components/list";
+import shopItem from "./components/shop_item";
+import { t } from "@/locale";
+import { onShow } from "@dcloudio/uni-app";
+
+// 搜索值
+const searchValue = ref("");
+
+// List组件引用
+const listRef = ref(null);
+
+// 默认参数
+const defaultParams = reactive({
+  keyWord: "",
+});
+
+// 店铺数据
+const shops = ref([
+  {
+    id: 1,
+    name: "时尚潮流店",
+    description: "专注时尚潮流服饰,品质保证",
+    avatar: "/static/shop/shop1.png",
+    rating: 4.8,
+    sales: 1234,
+    distance: "1.2km",
+    status: "online",
+    tags: ["时尚", "潮流", "品质"],
+  },
+  {
+    id: 2,
+    name: "美妆护肤专营",
+    description: "专业美妆护肤产品,正品保证",
+    avatar: "/static/shop/shop2.png",
+    rating: 4.9,
+    sales: 2567,
+    distance: "0.8km",
+    status: "online",
+    tags: ["美妆", "护肤", "正品"],
+  },
+  {
+    id: 3,
+    name: "数码科技馆",
+    description: "最新数码产品,科技前沿",
+    avatar: "/static/shop/shop3.png",
+    rating: 4.7,
+    sales: 1890,
+    distance: "2.1km",
+    status: "offline",
+    tags: ["数码", "科技", "新品"],
+  },
+  {
+    id: 4,
+    name: "家居生活馆",
+    description: "温馨家居,品质生活",
+    avatar: "/static/shop/shop4.png",
+    rating: 4.6,
+    sales: 987,
+    distance: "1.5km",
+    status: "online",
+    tags: ["家居", "生活", "温馨"],
+  },
+  {
+    id: 5,
+    name: "运动健身中心",
+    description: "专业运动装备,健康生活",
+    avatar: "/static/shop/shop5.png",
+    rating: 4.5,
+    sales: 1456,
+    distance: "3.2km",
+    status: "online",
+    tags: ["运动", "健身", "健康"],
+  },
+  {
+    id: 6,
+    name: "美食天地",
+    description: "特色美食,味蕾享受",
+    avatar: "/static/shop/shop6.png",
+    rating: 4.9,
+    sales: 3456,
+    distance: "0.5km",
+    status: "online",
+    tags: ["美食", "特色", "美味"],
+  },
+  {
+    id: 7,
+    name: "母婴用品店",
+    description: "专业母婴用品,呵护宝宝",
+    avatar: "/static/shop/shop7.png",
+    rating: 4.8,
+    sales: 2134,
+    distance: "1.8km",
+    status: "online",
+    tags: ["母婴", "用品", "专业"],
+  },
+  {
+    id: 8,
+    name: "图书文具店",
+    description: "学习用品,知识宝库",
+    avatar: "/static/shop/shop8.png",
+    rating: 4.4,
+    sales: 678,
+    distance: "2.5km",
+    status: "offline",
+    tags: ["图书", "文具", "学习"],
+  },
+  {
+    id: 9,
+    name: "宠物用品店",
+    description: "宠物用品,关爱毛孩子",
+    avatar: "/static/shop/shop9.png",
+    rating: 4.7,
+    sales: 1234,
+    distance: "1.9km",
+    status: "online",
+    tags: ["宠物", "用品", "关爱"],
+  },
+]);
+
+// 过滤后的店铺列表
+const filteredShops = ref([]);
+
+// 搜索输入
+const onSearchInput = (value) => {
+  searchValue.value = value;
+  defaultParams.keyWord = value;
+  // 触发列表刷新
+  if (listRef.value) {
+    listRef.value.handleRefresh();
+  }
+};
+
+// 搜索点击
+const onSearchClick = () => {
+  console.log("搜索:", searchValue.value);
+};
+
+// 获取列表数据
+const getList = (data) => {
+  // 模拟搜索过滤
+  if (searchValue.value.trim()) {
+    const keyword = searchValue.value.toLowerCase();
+    filteredShops.value = shops.value.filter(
+      (shop) =>
+        shop.name.toLowerCase().includes(keyword) ||
+        shop.description.toLowerCase().includes(keyword) ||
+        shop.tags.some((tag) => tag.toLowerCase().includes(keyword))
+    );
+  } else {
+    filteredShops.value = shops.value;
+  }
+
+  // 设置到List组件
+  if (listRef.value) {
+    listRef.value.setList(filteredShops.value);
+  }
+};
+
+// 跳转到店铺详情
+const toShopDetail = (shop) => {
+  uni.navigateTo({
+    url: `/pagesBuyer/shop/detail?id=${shop.id}&name=${shop.name}`,
+  });
+};
+
+onShow(() => {
+  // 页面显示时初始化数据
+  getList();
+});
+</script>
+
+<style lang="less" scoped>
+@import url("@/style.less");
+
+.wrap {
+  min-height: 100vh;
+  background: var(--bg);
+  display: flex;
+  flex-direction: column;
+
+  .nav_title {
+    color: var(--black);
+    font-size: 36rpx;
+    font-weight: bold;
+  }
+
+  .search-section {
+    position: sticky;
+    top: 0;
+    z-index: 10;
+    padding: 20rpx 30rpx;
+    background: var(--light);
+    border-bottom: 1rpx solid var(--border);
+
+    .search-box {
+      :deep(.u-input) {
+        padding: 0 20px 0 16px !important;
+        background: var(--inputBg);
+        border-radius: 20rpx;
+
+        .u-input__content__field-wrapper__field {
+          height: 47px;
+        }
+      }
+
+      .icon-search {
+        color: var(--text);
+        font-size: 40rpx;
+      }
+    }
+  }
+
+  .content {
+    flex: 1;
+    padding: 20rpx 30rpx;
+    padding-bottom: calc(90rpx + constant(safe-area-inset-bottom));
+    padding-bottom: calc(90rpx + env(safe-area-inset-bottom));
+  }
+
+  .empty-state {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    height: 60vh;
+
+    .empty-image {
+      width: 200rpx;
+      height: 200rpx;
+      margin-bottom: 32rpx;
+    }
+
+    .empty-text {
+      font-size: 32rpx;
+      color: var(--text-01);
+      margin-bottom: 16rpx;
+    }
+
+    .empty-desc {
+      font-size: 24rpx;
+      color: var(--text-01);
+    }
+  }
+}
+</style>

+ 12 - 1
store/user.js

@@ -13,7 +13,8 @@ const state = () => ({
   userInfo: getStorage(verConfig.infoName) || {},
   token: getStorage(verConfig.tokenName) || '',
   statics: {},
-  auth_id: ''
+  auth_id: '',
+  userType: getStorage('userType') || 2 // 单独存储用户类型,默认为卖家
 })
 
 export const useUserStore = defineStore('user', {
@@ -48,6 +49,9 @@ export const useUserStore = defineStore('user', {
     },
     getAuthId() {
       return this.auth_id
+    },
+    getUserType() {
+      return this.userType
     }
   },
   actions: {
@@ -145,6 +149,8 @@ export const useUserStore = defineStore('user', {
       const useShop = useShopStore()
       removeStorage(verConfig.infoName);
       removeStorage(verConfig.tokenName);
+      removeStorage('userType'); // 清除用户类型存储
+      this.userType = 1; // 重置为默认卖家类型
       USER_LOGOUT();
       useShop.setCartNum(0);
       useMessage.resetAllGlobalUnreadCount();
@@ -169,6 +175,11 @@ export const useUserStore = defineStore('user', {
     },
     setInitId(_auth_id) {
       this.auth_id = _auth_id
+    },
+    // 设置用户类型:1为卖家,2为买家
+    setUserType(userType) {
+      this.userType = userType;
+      setStorage('userType', userType);
     }
   }
 })