yankun hai 1 mes
pai
achega
1af21f046f
Modificáronse 48 ficheiros con 7218 adicións e 2931 borrados
  1. 1 0
      App.vue
  2. 2 1
      api/index.js
  3. 204 0
      api/seller.js
  4. 26 22
      components/tabbar.vue
  5. 22 1
      locale/zh.json
  6. 20 2
      pages.json
  7. 244 0
      pages/dashboard/components/PricePopup.vue
  8. 7 2
      pages/dashboard/components/cancelModel.vue
  9. 107 7
      pages/dashboard/warehouse.vue
  10. 368 0
      pages/shop/components/order_item.vue
  11. 4 4
      pages/shop/components/shopModel.vue
  12. 15 2
      pages/shop/components/successModel.vue
  13. 1 0
      pages/shop/payment.vue
  14. 255 0
      pages/store/components/order_item.vue
  15. 571 0
      pages/store/detail.vue
  16. 148 0
      pages/store/index.vue
  17. 6 1
      pages/user/index.vue
  18. 1 0
      pagesBuyer/USER_TYPE_STORAGE.md
  19. 84 404
      pagesBuyer/cart/index.vue
  20. 155 0
      pagesBuyer/home/components/product_item.vue
  21. 113 457
      pagesBuyer/home/index.vue
  22. 0 739
      pagesBuyer/home/record.vue
  23. 698 0
      pagesBuyer/order/details.vue
  24. 585 0
      pagesBuyer/order/index.vue
  25. 283 0
      pagesBuyer/profile/enter.vue
  26. 247 350
      pagesBuyer/profile/index.vue
  27. 0 212
      pagesBuyer/shop/README.md
  28. 321 0
      pagesBuyer/shop/cart.vue
  29. 147 0
      pagesBuyer/shop/components/product_item.vue
  30. 249 0
      pagesBuyer/shop/components/quantity_popup.vue
  31. 134 114
      pagesBuyer/shop/components/shop_item.vue
  32. 453 403
      pagesBuyer/shop/detail.vue
  33. 117 203
      pagesBuyer/shop/index.vue
  34. 681 0
      pagesBuyer/shop/shopConfirm.vue
  35. 147 0
      pagesBuyer/store/components/product_item.vue
  36. 183 0
      pagesBuyer/store/components/shop_item.vue
  37. 365 0
      pagesBuyer/store/detail.vue
  38. 207 0
      pagesBuyer/store/index.vue
  39. 1 1
      static/css/theme.less
  40. BIN=BIN
      static/seller/address.png
  41. BIN=BIN
      static/tabbar/index.png
  42. BIN=BIN
      static/tabbar/index_active.png
  43. BIN=BIN
      static/tabbar/shop.png
  44. BIN=BIN
      static/tabbar/shop_active.png
  45. BIN=BIN
      static/user/enter.png
  46. 20 1
      store/shop.js
  47. 7 5
      store/user.js
  48. 19 0
      utils/tool.js

+ 1 - 0
App.vue

@@ -31,6 +31,7 @@ function initFunc() {
   useSystem.setWinnotice();
   useSystem.setRateList();
   useShop.setCartList();
+  useShop.setSellerList();
   useShop.setHotLink();
   useScoket.connect();
   // useVideo.setVideoList();

+ 2 - 1
api/index.js

@@ -2,4 +2,5 @@ export * from './user'
 export * from './system'
 export * from './shop'
 export * from './bank'
-export * from './video'
+export * from './video'
+export * from './seller'

+ 204 - 0
api/seller.js

@@ -0,0 +1,204 @@
+import { $post } from '@/utils'
+
+export const SELLER_GOODS_UP = (params) => new Promise(async (resolve, reject) => {
+  try {
+    const res = await $post('/seller/goods/up', params);
+    const { ret } = res.data
+    if (ret == 1) {
+      resolve(res.data)
+    }
+    reject(res.data)
+  } catch (error) { }
+})
+
+export const SELLER_GOODS_DOWN = (id = null) => new Promise(async (resolve, reject) => {
+  try {
+    const res = await $post('/seller/goods/down', { id });
+    const { ret } = res.data
+    if (ret == 1) {
+      resolve(res.data)
+    }
+    reject(res.data)
+  } catch (error) { }
+})
+
+export const SELLER_GOODS_INFO = (params) => new Promise(async (resolve, reject) => {
+  try {
+    const res = await $post('/seller/goods/info', params);
+    const { ret } = res.data
+    if (ret == 1) {
+      resolve(res.data)
+    }
+    reject(res.data)
+  } catch (error) { }
+})
+
+export const SELLER_ADD_CART = (params) => new Promise(async (resolve, reject) => {
+  try {
+    const res = await $post('/seller/add/cart', params);
+    const { ret } = res.data
+    if (ret == 1) {
+      resolve(res.data)
+    }
+    reject(res.data)
+  } catch (error) { }
+})
+
+export const SELLER_DEL_DOWN = (id = null) => new Promise(async (resolve, reject) => {
+  try {
+    const res = await $post('/seller/del/cart', { id });
+    const { ret } = res.data
+    if (ret == 1) {
+      resolve(res.data)
+    }
+    reject(res.data)
+  } catch (error) { }
+})
+
+export const SELLER_CART_COUNT = () => new Promise(async (resolve, reject) => {
+  try {
+    const res = await $post('/seller/cart/count');
+    const { ret } = res.data
+    if (ret == 1) {
+      resolve(res.data)
+    }
+    reject(res.data)
+  } catch (error) { }
+})
+
+export const SELLER_CART_LIsts = () => new Promise(async (resolve, reject) => {
+  try {
+    const res = await $post('/seller/cart/lists');
+    const { ret } = res.data
+    if (ret == 1) {
+      resolve(res.data)
+    }
+    reject(res.data)
+  } catch (error) { }
+})
+
+export const SELLER_CART_CONFIRM = (params) => new Promise(async (resolve, reject) => {
+  try {
+    const res = await $post('/seller/cart/confirm', params);
+    const { ret } = res.data
+    if (ret == 1) {
+      resolve(res.data)
+    }
+    reject(res.data)
+  } catch (error) { }
+})
+
+export const SELLER_ORDER_SUBMIT = (params) => new Promise(async (resolve, reject) => {
+  try {
+    const res = await $post('/seller/order/submit', params);
+    const { ret } = res.data
+    if (ret == 1) {
+      resolve(res.data)
+    }
+    reject(res.data)
+  } catch (error) { }
+})
+
+
+export const SELLER_DEL_CART = (id = null) => new Promise(async (resolve, reject) => {
+  try {
+    const res = await $post('/seller/del/cart', { id });
+    const { ret } = res.data
+    if (ret == 1) {
+      resolve(res.data)
+    }
+    reject(res.data)
+  } catch (error) { }
+})
+
+
+export const SELLER_ORDER_DETAIL = (orderid = null) => new Promise(async (resolve, reject) => {
+  try {
+    const res = await $post('/seller/order/detail', { orderid });
+    const { ret } = res.data
+    if (ret == 1) {
+      resolve(res.data)
+    }
+    reject(res.data)
+  } catch (error) { }
+})
+
+export const SELLER_ORDER_FINISH = (orderid = null) => new Promise(async (resolve, reject) => {
+  try {
+    const res = await $post('/seller/order/finish', { orderid });
+    const { ret } = res.data
+    if (ret == 1) {
+      resolve(res.data)
+    }
+    reject(res.data)
+  } catch (error) { }
+})
+
+export const SELLER_ORDER_CANCEL = (orderid = null) => new Promise(async (resolve, reject) => {
+  try {
+    const res = await $post('/seller/order/cancel', { orderid });
+    const { ret } = res.data
+    if (ret == 1) {
+      resolve(res.data)
+    }
+    reject(res.data)
+  } catch (error) { }
+})
+
+export const SELLER_ORDER_GETPAY = (orderids = null) => new Promise(async (resolve, reject) => {
+  try {
+    const res = await $post('/seller/order/getpay', { orderids });
+    const { ret } = res.data
+    if (ret == 1) {
+      resolve(res.data)
+    }
+    reject(res.data)
+  } catch (error) { }
+})
+
+export const SELLER_SELLER_INFO = (params) => new Promise(async (resolve, reject) => {
+  try {
+    const res = await $post('/seller/seller/info', params);
+    const { ret } = res.data
+    if (ret == 1) {
+      resolve(res.data)
+    }
+    reject(res.data)
+  } catch (error) { }
+})
+
+export const SELLER_APPLY_SELLER = (params) => new Promise(async (resolve, reject) => {
+  try {
+    const res = await $post('/seller/apply/seller', params);
+    const { ret } = res.data
+    if (ret == 1) {
+      resolve(res.data)
+    }
+    reject(res.data)
+  } catch (error) { }
+})
+
+
+export const SELLER_SELLER_DETAIL = (orderid = null) => new Promise(async (resolve, reject) => {
+  try {
+    const res = await $post('/seller/seller/detail', { orderid });
+    const { ret } = res.data
+    if (ret == 1) {
+      resolve(res.data)
+    }
+    reject(res.data)
+  } catch (error) { }
+})
+
+export const SELLER_SELLER_GETPAY = (orderids = null) => new Promise(async (resolve, reject) => {
+  try {
+    const res = await $post('/seller/seller/getpay', { orderids });
+    const { ret } = res.data
+    if (ret == 1) {
+      resolve(res.data)
+    }
+    reject(res.data)
+  } catch (error) { }
+})
+
+

+ 26 - 22
components/tabbar.vue

@@ -60,6 +60,11 @@ import chat from "@/static/tabbar/chat.png";
 import chat_active from "@/static/tabbar/chat_active.png";
 import user from "@/static/tabbar/user.png";
 import user_active from "@/static/tabbar/user_active.png";
+import shop from "@/static/tabbar/shop.png";
+import shop_active from "@/static/tabbar/shop_active.png";
+import index from "@/static/tabbar/index.png";
+import index_active from "@/static/tabbar/index_active.png";
+
 import { t } from "@/locale";
 import { storeToRefs } from "pinia";
 import { query } from "@/utils";
@@ -74,14 +79,14 @@ const unreadCount = ref(0);
 
 // 1. 定义购物车数量计算属性(响应式)
 const cartNum = computed(() => useShop.getCartNum);
+const sellerNum = computed(() => useShop.getSellerNum);
 
 // 获取用户信息
 const userInfo = computed(() => useUser.getuserInfo);
 
-// 判断用户类型:1为卖家,2为买家
 const isBuyer = computed(() => {
-  console.log(useUser.getUserType);
-  return useUser.getUserType === 2;
+  console.log(useUser.getUserType, "123");
+  return useUser.getUserType == 0;
 });
 
 const props = defineProps({
@@ -135,17 +140,17 @@ const navList = ref([
 // 买家端导航列表
 const buyerNavList = ref([
   {
-    name: "home",
+    name: "index",
     title: "tabbar.首页",
-    img: home,
-    img_active: home_active,
+    img: index,
+    img_active: index_active,
     num: 0,
   },
   {
-    name: "shop",
-    title: "tabbar.店铺",
-    img: ship,
-    img_active: ship_active,
+    name: "store",
+    title: "tabbar.店铺",
+    img: shop,
+    img_active: shop_active,
     num: 0,
   },
   {
@@ -153,10 +158,10 @@ const buyerNavList = ref([
     title: "tabbar.购物车",
     img: cart,
     img_active: cart_active,
-    num: cartNum.value,
+    num: sellerNum.value,
   },
   {
-    name: "profile",
+    name: "user",
     title: "tabbar.我的",
     img: user,
     img_active: user_active,
@@ -226,11 +231,11 @@ const NavChange = (item, index) => {
       // 买家端页面跳转
       if (item.name === "cart") {
         targetUrl = "/pagesBuyer/cart/index";
-      } else if (item.name === "home") {
+      } else if (item.name === "index") {
         targetUrl = "/pagesBuyer/home/index";
-      } else if (item.name === "shop") {
-        targetUrl = "/pagesBuyer/shop/index";
-      } else if (item.name === "profile") {
+      } else if (item.name === "store") {
+        targetUrl = "/pagesBuyer/store/index";
+      } else if (item.name === "user") {
         targetUrl = "/pagesBuyer/profile/index";
       } else {
         targetUrl = `/pagesBuyer/${item.name}/index`;
@@ -252,11 +257,11 @@ const NavChange = (item, index) => {
       // 买家端页面跳转
       if (item.name === "cart") {
         targetUrl = "/pagesBuyer/cart/index";
-      } else if (item.name === "home") {
+      } else if (item.name === "index") {
         targetUrl = "/pagesBuyer/home/index";
-      } else if (item.name === "shop") {
-        targetUrl = "/pagesBuyer/shop/index";
-      } else if (item.name === "profile") {
+      } else if (item.name === "store") {
+        targetUrl = "/pagesBuyer/store/index";
+      } else if (item.name === "user") {
         targetUrl = "/pagesBuyer/profile/index";
       } else {
         targetUrl = `/pagesBuyer/${item.name}/index`;
@@ -270,8 +275,7 @@ const NavChange = (item, index) => {
       }
     }
   }
-  console.log("跳转URL:", targetUrl);
-
+  useTabbar.getPageIndex(index);
   // 判断是否为买家端页面
   const isBuyerPage = targetUrl.includes("pagesBuyer");
 

+ 22 - 1
locale/zh.json

@@ -596,5 +596,26 @@
   "最低价": "最低价",
   "最高价": "最高价",
   "元": "元",
-  "tabbar.店铺": "店铺"
+  "tabbar.店铺街": "店铺街",
+  "确定要下架该商品吗?": "确定要下架该商品吗?",
+  "销售价格": "销售价格",
+  "请输入销售价格": "请输入销售价格",
+  "上架": "上架",
+  "确认上架": "确认上架",
+  "原价": "原价",
+  "请输入原价": "请输入原价",
+  "建议原价": "建议原价",
+  "建议销售价格": "建议销售价格",
+  "新品": "新品",
+  "请粘贴商品链接或关键字": "请粘贴商品链接或关键字",
+  "店铺街": "店铺街",
+  "请搜索关键字": "请搜索关键字",
+  "商家入驻": "商家入驻",
+  "店铺名称": "店铺名称",
+  "请输入店铺名称": "请输入店铺名称",
+  "店铺logo": "店铺logo",
+  "店铺地址": "店铺地址",
+  "请输入店铺地址": "请输入店铺地址",
+  "联系方式": "联系方式",
+  "请输入联系方式": "请输入联系方式"
 }

+ 20 - 2
pages.json

@@ -204,13 +204,31 @@
       "path": "pagesBuyer/profile/index"
     },
     {
-      "path": "pagesBuyer/shop/index"
+      "path": "pagesBuyer/profile/enter"
+    },
+    {
+      "path": "pagesBuyer/store/index"
+    },
+    {
+      "path": "pagesBuyer/store/detail"
     },
     {
       "path": "pagesBuyer/shop/detail"
     },
     {
-      "path": "pagesBuyer/home/record"
+      "path": "pagesBuyer/shop/shopConfirm"
+    },
+    {
+      "path": "pagesBuyer/order/index"
+    },
+    {
+      "path": "pagesBuyer/order/details"
+    },
+    {
+      "path": "pages/store/index"
+    },
+    {
+      "path": "pages/store/detail"
     }
   ],
   "tabBar": {

+ 244 - 0
pages/dashboard/components/PricePopup.vue

@@ -0,0 +1,244 @@
+<template>
+  <Popup
+    ref="popupRef"
+    title="上架"
+    :isClose="true"
+    mode="bottom"
+    :round="20"
+    @close="onClose"
+  >
+    <template #content>
+      <view class="price-content">
+        <view class="price-section">
+          <view class="section-title">
+            <trans _t="原价" />
+          </view>
+          <view class="price-input-wrapper">
+            <view class="currency-symbol">{{ symbol }}</view>
+            <Input
+              v-model="originalPrice"
+              type="number"
+              :placeholder="t('请输入原价')"
+              :clearable="true"
+              border="none"
+              class="price-input"
+            />
+          </view>
+          <view class="price-tip">
+            <text
+              ><trans _t="建议原价" />:{{ symbol
+              }}{{ suggestedOriginalPrice }}</text
+            >
+          </view>
+        </view>
+        <view class="price-section">
+          <view class="section-title">
+            <trans _t="销售价格" />
+          </view>
+          <view class="price-input-wrapper">
+            <view class="currency-symbol">{{ symbol }}</view>
+            <Input
+              v-model="salePrice"
+              type="number"
+              :placeholder="t('请输入销售价格')"
+              :focus="true"
+              :clearable="true"
+              border="none"
+              class="price-input"
+            />
+          </view>
+          <view class="price-tip">
+            <text
+              ><trans _t="建议销售价格" />:{{ symbol
+              }}{{ suggestedPrice }}</text
+            >
+          </view>
+        </view>
+      </view>
+    </template>
+
+    <template #footer>
+      <view class="btn-group">
+        <view class="btn cancel-btn" @click="onClose">
+          <trans _t="取消" />
+        </view>
+        <view class="btn submit-btn" @click="submitPrice">
+          <trans _t="确认上架" />
+        </view>
+      </view>
+    </template>
+  </Popup>
+</template>
+
+<script setup>
+import { ref, computed, watch } from "vue";
+import Popup from "@/components/popup";
+import Input from "@/components/input";
+import { t } from "@/locale";
+import { useSystemStore } from "@/store";
+import { SELLER_GOODS_UP } from "@/api";
+import { Toast } from "@/utils";
+
+const props = defineProps({
+  show: {
+    type: Boolean,
+    default: false,
+  },
+  item: {
+    type: Object,
+    default: () => ({}),
+  },
+});
+
+const emit = defineEmits(["close", "confirm"]);
+
+const useSystem = useSystemStore();
+const symbol = computed(() => useSystem.getSymbol.symbol);
+
+const popupRef = ref(null);
+const salePrice = ref("");
+const originalPrice = ref("");
+
+// 建议价格(原价的1.3倍)
+const suggestedPrice = computed(() => {
+  if (props.item?.price) {
+    return (parseFloat(props.item.price) * 1.3).toFixed(2);
+  }
+  return "0.00";
+});
+
+// 建议原价(原价的40%)
+const suggestedOriginalPrice = computed(() => {
+  if (props.item?.price) {
+    return (parseFloat(props.item.price) * 1.4).toFixed(2);
+  }
+  return "0.00";
+});
+
+watch(
+  () => props.show,
+  (newVal) => {
+    if (newVal) {
+      salePrice.value = suggestedPrice.value;
+      originalPrice.value = suggestedOriginalPrice.value;
+      popupRef.value?.open();
+    } else {
+      popupRef.value?.close();
+    }
+  }
+);
+
+const onClose = () => {
+  emit("close");
+};
+
+const submitPrice = async () => {
+  if (!salePrice.value || parseFloat(salePrice.value) <= 0) {
+    return Toast(t("请输入有效的销售价格"));
+  }
+  if (!originalPrice.value || parseFloat(originalPrice.value) <= 0) {
+    return Toast(t("请输入有效的原价"));
+  }
+  try {
+    const para = {
+      id: props.item.id,
+      price: parseFloat(salePrice.value),
+      originalprice: parseFloat(originalPrice.value),
+    };
+    const res = await SELLER_GOODS_UP(para);
+    emit("confirm");
+    popupRef.value?.close();
+  } catch (error) {
+    Toast(error.msg);
+  }
+};
+</script>
+
+<style lang="less" scoped>
+@import url("@/style.less");
+
+.price-content {
+  width: 100%;
+  .price-section {
+    margin-bottom: 32rpx;
+
+    &:last-child {
+      margin-bottom: 0;
+    }
+
+    .section-title {
+      font-size: 32rpx;
+      font-weight: 600;
+      color: var(--black);
+      margin-bottom: 24rpx;
+    }
+
+    .price-input-wrapper {
+      display: flex;
+      align-items: center;
+      background: var(--bg);
+      border-radius: 16rpx;
+      padding: 0 24rpx;
+      margin-bottom: 16rpx;
+      border: 2rpx solid var(--border);
+
+      .currency-symbol {
+        font-size: 32rpx;
+        font-weight: bold;
+        color: var(--primary);
+        margin-right: 16rpx;
+      }
+
+      .price-input {
+        flex: 1;
+
+        :deep(.u-input) {
+          // padding: 0 !important;
+          // background: transparent !important;
+          // border: none !important;
+
+          .u-input__content__field-wrapper__field {
+            font-size: 32rpx;
+            font-weight: bold;
+            color: var(--black);
+          }
+        }
+      }
+    }
+
+    .price-tip {
+      font-size: 24rpx;
+      color: var(--text-01);
+      text-align: center;
+    }
+  }
+}
+
+.btn-group {
+  display: flex;
+  gap: 20rpx;
+
+  .btn {
+    flex: 1;
+    height: 80rpx;
+    border-radius: 16rpx;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 32rpx;
+    font-weight: 600;
+    transition: all 0.3s ease;
+
+    &.cancel-btn {
+      background: var(--bg);
+      color: var(--text-01);
+      border: 2rpx solid var(--border);
+    }
+
+    &.submit-btn {
+      background: var(--primary);
+      color: var(--light);
+    }
+  }
+}
+</style>

+ 7 - 2
pages/dashboard/components/cancelModel.vue

@@ -44,13 +44,14 @@
 import { reactive, ref, nextTick } from "vue";
 import Popup from "@/components/popup.vue";
 import { t } from "@/locale";
-import { SHOP_ORDER_CANCEL } from "@/api";
+import { SHOP_ORDER_CANCEL, SELLER_ORDER_CANCEL } from "@/api";
 import { Toast } from "@/utils";
 import { useTabbarStore } from "@/store";
 
 const useTabbar = useTabbarStore();
 const props = defineProps({
   oid: { type: String, default: "" },
+  isBuyer: Boolean,
 });
 const form = reactive({
   orderid: 0,
@@ -91,10 +92,14 @@ const submit = async () => {
     return;
   }
   try {
-    let res = await SHOP_ORDER_CANCEL(form);
+    let URL = props.isBuyer ? SELLER_ORDER_CANCEL : SHOP_ORDER_CANCEL;
+    let res = await URL(form);
     close();
     Toast(res.msg, 1000).then(() => {
       setTimeout(() => {
+        if (props.isBuyer) {
+          return uni.navigateTo({ url: "/pagesBuyer/order/index" });
+        }
         uni.switchTab({ url: "/pages/order/index" });
         useTabbar.getPageCur("order");
       }, 1000);

+ 107 - 7
pages/dashboard/warehouse.vue

@@ -35,7 +35,7 @@
               <view class="list_wrapper">
                 <view class="cart_cont">
                   <view class="cont_list">
-                    <view class="checkout">
+                    <!-- <view class="checkout">
                       <up-checkbox-group
                         activeColor="var(--black)"
                         shape="circle"
@@ -47,7 +47,7 @@
                       >
                         <up-checkbox :name="item.id" />
                       </up-checkbox-group>
-                    </view>
+                    </view> -->
                     <view class="list_right" @click="listClick(item)">
                       <view class="img">
                         <up-lazy-load
@@ -67,6 +67,27 @@
                           </view>
                           <view class="unit_num">x{{ item.total }}</view>
                         </view>
+                        <view class="info_action">
+                          <view class="sale_price">
+                            <template v-if="item.seller_goods_status === 1">
+                              <text class="label">销售价:</text>
+                              <view class="unit_price">
+                                <text>{{ symbol.symbol }}</text>
+                                <rich-text
+                                  :nodes="Moneyhtml(item?.seller_goods_price)"
+                                ></rich-text>
+                              </view>
+                            </template>
+                          </view>
+                          <view
+                            class="action_btn"
+                            @click.stop="toggleStatus(item)"
+                          >
+                            <text>{{
+                              item.seller_goods_status === 1 ? "下架" : "上架"
+                            }}</text>
+                          </view>
+                        </view>
                       </view>
                     </view>
                   </view>
@@ -75,7 +96,7 @@
             </template>
           </List>
         </view>
-        <view class="footer" :class="!navShow ? 'footer_bottom' : ''">
+        <!-- <view class="footer" :class="!navShow ? 'footer_bottom' : ''">
           <view class="footer_left">
             <up-checkbox
               :label="t('全部')"
@@ -100,9 +121,17 @@
               <trans _t="上架" />
             </view>
           </view>
-        </view>
+        </view> -->
       </view>
     </view>
+
+    <!-- 价格设置弹框 -->
+    <PricePopup
+      :show="pricePopupShow"
+      :item="currentItem"
+      @close="onPriceClose"
+      @confirm="onPriceConfirm"
+    />
   </Theme>
 </template>
 <script setup>
@@ -111,12 +140,14 @@ import navMenu from "@/components/nav_menu";
 import navFilter from "@/components/nav_filter";
 import Search from "@/components/input";
 import List from "@/components/list";
+import PricePopup from "./components/PricePopup.vue";
 import { ref, onMounted, nextTick, computed } from "vue";
 import { t } from "@/locale";
-import { useGlobal, Toast, Moneyhtml } from "@/utils";
+import { useGlobal, Toast, Moneyhtml, Modal } from "@/utils";
 import { useSystemStore } from "@/store";
 import { onReachBottom } from "@dcloudio/uni-app";
 import { onShow } from "@dcloudio/uni-app";
+import { SELLER_GOODS_DOWN } from "@/api";
 
 const props = defineProps({
   navShow: Boolean,
@@ -134,6 +165,10 @@ const allId = ref([]);
 const symbol = computed(() => useSystem.getSymbol);
 const selectAllChecked = ref(false);
 
+// 价格弹框相关状态
+const pricePopupShow = ref(false);
+const currentItem = ref(null);
+
 const searchConfirm = () => {
   nextTick(() => {
     listRef.value && listRef.value.handleRefresh();
@@ -183,6 +218,35 @@ const submit = () => {
   if (!ids.value.length) return;
   uni.navigateTo({ url: `/pages/dashboard/pack?ids=${ids.value.join(",")}` });
 };
+
+const toggleStatus = (item) => {
+  if (item.seller_goods_status === 1) {
+    Modal({ content: t("确定要下架该商品吗?") }).then(async () => {
+      try {
+        const res = await SELLER_GOODS_DOWN(item.id);
+        Toast(res.msg);
+        nextTick(() => {
+          listRef.value && listRef.value.handleRefresh();
+        });
+      } catch (error) {
+        Toast(error.msg);
+      }
+    });
+  } else {
+    currentItem.value = item;
+    pricePopupShow.value = true;
+  }
+};
+
+const onPriceClose = () => {
+  pricePopupShow.value = false;
+  currentItem.value = null;
+};
+const onPriceConfirm = () => {
+  onPriceClose();
+  listRef.value && listRef.value.getData();
+};
+
 onMounted(() => {
   listRef.value && listRef.value.getData();
 });
@@ -286,6 +350,7 @@ onReachBottom(() => {
               flex: 1;
 
               .info_name {
+                .ellipsis(2);
                 font-weight: 700;
                 color: var(--text);
                 line-height: 48rpx;
@@ -320,6 +385,43 @@ onReachBottom(() => {
                   padding: 4rpx 8rpx;
                 }
               }
+
+              .info_action {
+                .flex_position(space-between);
+                margin-top: 12rpx;
+                align-items: center;
+
+                .sale_price {
+                  .flex();
+                  align-items: center;
+                  column-gap: 8rpx;
+                  line-height: 60rpx;
+                  .label {
+                    .size(24rpx);
+                    color: var(--text-01);
+                  }
+
+                  .unit_price {
+                    .size(24rpx);
+                    color: var(--red);
+                    font-weight: 700;
+                    .ver();
+                  }
+                }
+
+                .action_btn {
+                  .size(24rpx);
+                  font-weight: 600;
+                  height: 60rpx;
+                  min-width: 80rpx;
+                  background-color: var(--black);
+                  color: var(--light);
+                  border-radius: 12rpx;
+                  padding: 0 16rpx;
+                  text-align: center;
+                  line-height: 60rpx;
+                }
+              }
             }
           }
         }
@@ -341,8 +443,6 @@ onReachBottom(() => {
       &.footer_bottom {
         bottom: 0;
       }
-      &_left {
-      }
 
       &_right {
         .ver();

+ 368 - 0
pages/shop/components/order_item.vue

@@ -0,0 +1,368 @@
+<template>
+  <view class="order-item" @click="handleClick">
+    <!-- 订单头部 -->
+    <view class="order-header">
+      <view class="order-info">
+        <text class="order-number">{{ t('订单号') }}:{{ item.orderNo }}</text>
+        <text class="order-time">{{ formatTime(item.createTime) }}</text>
+      </view>
+      <view class="order-status" :class="getStatusClass(item.status)">
+        {{ getStatusText(item.status) }}
+      </view>
+    </view>
+
+    <!-- 商品信息 -->
+    <view class="product-section">
+      <view class="product-list">
+        <view 
+          v-for="(product, index) in item.goods" 
+          :key="index"
+          class="product-item"
+        >
+          <view class="product-image">
+            <image :src="product.image" mode="aspectFill" />
+          </view>
+          <view class="product-info">
+            <view class="product-name">{{ product.name }}</view>
+            <view class="product-spec" v-if="product.spec">{{ product.spec }}</view>
+            <view class="product-price">
+              <text class="price">{{ formatCurrency(product.price) }}</text>
+              <text class="quantity">x{{ product.quantity }}</text>
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <!-- 订单金额 -->
+    <view class="order-amount">
+      <view class="amount-info">
+        <text class="total-label">{{ t('商品数量') }}:{{ item.totalQuantity }}</text>
+        <text class="total-amount">{{ t('实付') }}:{{ formatCurrency(item.totalAmount) }}</text>
+      </view>
+      <!-- 待付款运费 -->
+      <view class="shipping-fee" v-if="item.status == 300 && item.money">
+        <text class="fee-label">{{ t('运费') }}:</text>
+        <text class="fee-amount">{{ formatCurrency(item.money) }}</text>
+      </view>
+    </view>
+
+    <!-- 操作按钮 -->
+    <view class="order-actions" v-if="getActionButtons(item.status).length > 0">
+      <view 
+        v-for="action in getActionButtons(item.status)"
+        :key="action.type"
+        class="action-btn"
+        :class="action.class"
+        @click.stop="handleAction(action.type)"
+      >
+        {{ action.text }}
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+import { defineProps, defineEmits } from "vue";
+import { formatCurrency } from "@/utils";
+import { t } from "@/locale";
+
+const props = defineProps({
+  item: {
+    type: Object,
+    required: true,
+  },
+});
+
+const emit = defineEmits(["click", "action"]);
+
+const handleClick = () => {
+  emit("click", props.item);
+};
+
+const handleAction = (actionType) => {
+  emit("action", props.item, actionType);
+};
+
+// 格式化时间
+const formatTime = (timestamp) => {
+  if (!timestamp) return '';
+  const date = new Date(timestamp);
+  const year = date.getFullYear();
+  const month = String(date.getMonth() + 1).padStart(2, '0');
+  const day = String(date.getDate()).padStart(2, '0');
+  const hours = String(date.getHours()).padStart(2, '0');
+  const minutes = String(date.getMinutes()).padStart(2, '0');
+  return `${year}-${month}-${day} ${hours}:${minutes}`;
+};
+
+// 获取状态文本
+const getStatusText = (status) => {
+  const statusMap = {
+    0: '待付款',
+    100: '待发货',
+    200: '已发货',
+    300: '待发货',
+    400: '已完成',
+    500: '已取消',
+    600: '已退款'
+  };
+  return statusMap[status] || '未知状态';
+};
+
+// 获取状态样式类
+const getStatusClass = (status) => {
+  const classMap = {
+    0: 'status-pending',
+    100: 'status-shipping',
+    200: 'status-shipped',
+    300: 'status-shipping',
+    400: 'status-completed',
+    500: 'status-cancelled',
+    600: 'status-refunded'
+  };
+  return classMap[status] || 'status-default';
+};
+
+// 获取操作按钮
+const getActionButtons = (status) => {
+  const buttonMap = {
+    0: [], // 待付款 - 买家操作
+    100: [
+      { type: 'ship', text: '发货', class: 'btn-primary' }
+    ],
+    200: [], // 已发货 - 买家操作
+    300: [
+      { type: 'ship', text: '发货', class: 'btn-primary' }
+    ],
+    400: [], // 已完成
+    500: [], // 已取消
+    600: [] // 已退款
+  };
+  return buttonMap[status] || [];
+};
+</script>
+
+<style lang="less" scoped>
+@import url("@/style.less");
+
+.order-item {
+  background: var(--light);
+  border-radius: 16rpx;
+  padding: 30rpx;
+  margin-bottom: 20rpx;
+  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+
+  .order-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 24rpx;
+    padding-bottom: 20rpx;
+    border-bottom: 1rpx solid var(--border);
+
+    .order-info {
+      display: flex;
+      flex-direction: column;
+      gap: 8rpx;
+
+      .order-number {
+        font-size: 28rpx;
+        font-weight: bold;
+        color: var(--black);
+      }
+
+      .order-time {
+        font-size: 24rpx;
+        color: var(--text-01);
+      }
+    }
+
+    .order-status {
+      font-size: 24rpx;
+      font-weight: bold;
+      padding: 8rpx 16rpx;
+      border-radius: 20rpx;
+
+      &.status-pending {
+        color: var(--primary);
+        background: rgba(255, 107, 53, 0.1);
+      }
+
+      &.status-shipping {
+        color: var(--primary);
+        background: rgba(255, 107, 53, 0.1);
+      }
+
+      &.status-shipped {
+        color: var(--success);
+        background: rgba(76, 175, 80, 0.1);
+      }
+
+      &.status-completed {
+        color: var(--success);
+        background: rgba(76, 175, 80, 0.1);
+      }
+
+      &.status-cancelled {
+        color: var(--text-01);
+        background: rgba(158, 158, 158, 0.1);
+      }
+
+      &.status-refunded {
+        color: var(--warning);
+        background: rgba(255, 193, 7, 0.1);
+      }
+    }
+  }
+
+  .product-section {
+    margin-bottom: 24rpx;
+
+    .product-list {
+      display: flex;
+      flex-direction: column;
+      gap: 20rpx;
+
+      .product-item {
+        display: flex;
+        gap: 20rpx;
+
+        .product-image {
+          width: 120rpx;
+          height: 120rpx;
+          border-radius: 12rpx;
+          overflow: hidden;
+          background: var(--bg);
+
+          image {
+            width: 100%;
+            height: 100%;
+          }
+        }
+
+        .product-info {
+          flex: 1;
+          display: flex;
+          flex-direction: column;
+          justify-content: space-between;
+
+          .product-name {
+            font-size: 28rpx;
+            color: var(--black);
+            line-height: 1.4;
+            margin-bottom: 8rpx;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            display: -webkit-box;
+            -webkit-line-clamp: 2;
+            line-clamp: 2;
+            -webkit-box-orient: vertical;
+          }
+
+          .product-spec {
+            font-size: 24rpx;
+            color: var(--text-01);
+            margin-bottom: 8rpx;
+          }
+
+          .product-price {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+
+            .price {
+              font-size: 28rpx;
+              font-weight: bold;
+              color: var(--primary);
+            }
+
+            .quantity {
+              font-size: 24rpx;
+              color: var(--text-01);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  .order-amount {
+    margin-bottom: 24rpx;
+    padding-top: 20rpx;
+    border-top: 1rpx solid var(--border);
+
+    .amount-info {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 12rpx;
+
+      .total-label {
+        font-size: 24rpx;
+        color: var(--text-01);
+      }
+
+      .total-amount {
+        font-size: 32rpx;
+        font-weight: bold;
+        color: var(--black);
+      }
+    }
+
+    .shipping-fee {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 8rpx 0;
+      background: rgba(255, 193, 7, 0.1);
+      border-radius: 8rpx;
+      padding: 12rpx 16rpx;
+
+      .fee-label {
+        font-size: 24rpx;
+        color: var(--warning);
+      }
+
+      .fee-amount {
+        font-size: 26rpx;
+        font-weight: bold;
+        color: var(--warning);
+      }
+    }
+  }
+
+  .order-actions {
+    display: flex;
+    justify-content: flex-end;
+    gap: 20rpx;
+    padding-top: 20rpx;
+    border-top: 1rpx solid var(--border);
+
+    .action-btn {
+      padding: 16rpx 32rpx;
+      border-radius: 40rpx;
+      font-size: 24rpx;
+      font-weight: bold;
+      text-align: center;
+      min-width: 120rpx;
+
+      &.btn-primary {
+        background: var(--primary);
+        color: var(--light);
+      }
+
+      &.btn-secondary {
+        background: var(--light);
+        color: var(--black);
+        border: 1rpx solid var(--border);
+      }
+
+      &.btn-danger {
+        background: var(--danger);
+        color: var(--light);
+      }
+    }
+  }
+}
+</style>

+ 4 - 4
pages/shop/components/shopModel.vue

@@ -18,7 +18,7 @@
               <up-checkbox :name="item.seller_id" />
             </up-checkbox-group>
           </view>
-          <view class="img">
+          <view class="img" v-if="item.channel">
             <image
               :src="`../../static/shop/icon_${item.channel}.png`"
               class="cart_icon"
@@ -42,11 +42,11 @@
               </up-checkbox-group>
             </view>
             <view class="img">
-              <image :src="val.pic_url" class="list_icon"></image>
+              <image :src="val.pic_url || val.picurl" class="list_icon"></image>
             </view>
             <view class="goods_info">
-              <view class="info_name">{{ val.title }}</view>
-              <view class="info_desc">{{ val.sku_desc }}</view>
+              <view class="info_name">{{ val.title || val.goodsName }}</view>
+              <view class="info_desc">{{ val.sku_desc || val.skudesc }}</view>
               <view class="info_price">
                 <view class="unit_price">
                   <text>{{ symbol.symbol }}</text>

+ 15 - 2
pages/shop/components/successModel.vue

@@ -28,10 +28,12 @@ import { ref } from "vue";
 import Popup from "@/components/popup.vue";
 import { t } from "@/locale";
 import { useTabbarStore } from "@/store";
+import { onLoad } from "@dcloudio/uni-app";
 
 const useTabbar = useTabbarStore();
 
 const popRef = ref(null);
+const optionObj = ref({});
 
 const emit = defineEmits(["complete", "close", "open"]);
 
@@ -46,14 +48,25 @@ const close = () => {
 };
 
 const toShop = () => {
-  uni.navigateTo({ url: "/pages/index/products?channel=1" });
+  if (optionObj.value.type == "sellerorder") {
+    uni.navigateTo({ url: "/pagesBuyer/home/index" });
+    useTabbar.getPageCur("home");
+  } else {
+    uni.navigateTo({ url: "/pages/index/products?channel=1" });
+  }
 };
 
 const seeOrder = () => {
+  if (optionObj.value.type == "sellerorder") {
+    return uni.navigateTo({ url: "/pagesBuyer/home/index" });
+  }
   uni.switchTab({ url: "/pages/order/index" });
   useTabbar.getPageCur("order");
 };
-
+onLoad((options) => {
+  optionObj.value = options;
+  console.log(options);
+});
 defineExpose({ open, close });
 </script>
 

+ 1 - 0
pages/shop/payment.vue

@@ -508,6 +508,7 @@ const getPayInfo = async () => {
   try {
     const res = await SHOP_PAY_INFO({
       oid: oid.value,
+      sid: oid.value,
       type: type.value,
       couponId: payInfo.value.couponId,
     });

+ 255 - 0
pages/store/components/order_item.vue

@@ -0,0 +1,255 @@
+<template>
+  <view class="order-item" @click="handleClick">
+    <!-- 订单头部 -->
+    <view class="order-header">
+      <view class="order-info">
+        <text class="order-number">{{ t("订单号") }}:{{ item.orderNo }}</text>
+        <text class="order-time">{{ useGlobal().$format(item.indate) }}</text>
+      </view>
+      <view class="order-status">
+        {{ item.status_txt }}
+      </view>
+    </view>
+
+    <!-- 商品信息 -->
+    <view class="product-section">
+      <view class="product-list">
+        <view
+          v-for="(product, index) in item.goods"
+          :key="index"
+          class="product-item"
+        >
+          <view class="product-image">
+            <image :src="product.pic_url" mode="aspectFill" />
+          </view>
+          <view class="product-info">
+            <view class="product-name">{{ product.shopName }}</view>
+            <view class="product-spec" v-if="product.sku_desc">{{
+              product.sku_desc
+            }}</view>
+            <view class="product-price">
+              <text class="price">{{ formatCurrency(product.price) }}</text>
+              <text class="quantity">x{{ product.total }}</text>
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <!-- 订单金额 -->
+    <view class="order-amount">
+      <view class="shipping-fee" v-if="item.status == 300 && item.money">
+        <text class="fee-label">{{ t("运费") }}:</text>
+        <text class="fee-amount">{{ formatCurrency(item.money) }}</text>
+      </view>
+    </view>
+
+    <!-- 操作按钮 -->
+    <view class="order-actions" v-if="item.status == 300">
+      <view class="action-btn" @click.stop="handleAction()">
+        <trans _t="付款" />
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+import { useGlobal, formatCurrency } from "@/utils";
+import { t } from "@/locale";
+
+const props = defineProps({
+  item: {
+    type: Object,
+    required: true,
+  },
+});
+
+const emit = defineEmits(["click", "action"]);
+
+const handleClick = () => {
+  emit("click", props.item);
+};
+
+const handleAction = () => {
+  emit("action", props.item);
+};
+</script>
+
+<style lang="less" scoped>
+@import url("@/style.less");
+
+.order-item {
+  background: var(--light);
+  border-radius: 16rpx;
+  padding: 30rpx;
+  margin-bottom: 20rpx;
+  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+
+  .order-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 24rpx;
+    padding-bottom: 20rpx;
+    border-bottom: 1rpx solid var(--border);
+
+    .order-info {
+      display: flex;
+      flex-direction: column;
+      gap: 8rpx;
+
+      .order-number {
+        font-size: 28rpx;
+        font-weight: bold;
+        color: var(--black);
+      }
+
+      .order-time {
+        font-size: 24rpx;
+        color: var(--text-01);
+      }
+    }
+
+    .order-status {
+      font-size: 24rpx;
+      font-weight: bold;
+      padding: 8rpx 16rpx;
+      border-radius: 20rpx;
+      color: var(--success);
+      background: rgba(76, 175, 80, 0.1);
+    }
+  }
+
+  .product-section {
+    margin-bottom: 24rpx;
+
+    .product-list {
+      display: flex;
+      flex-direction: column;
+      gap: 20rpx;
+
+      .product-item {
+        display: flex;
+        gap: 20rpx;
+
+        .product-image {
+          width: 120rpx;
+          height: 120rpx;
+          border-radius: 12rpx;
+          overflow: hidden;
+          background: var(--bg);
+
+          image {
+            width: 100%;
+            height: 100%;
+          }
+        }
+
+        .product-info {
+          flex: 1;
+          display: flex;
+          flex-direction: column;
+          justify-content: space-between;
+
+          .product-name {
+            font-size: 28rpx;
+            color: var(--black);
+            line-height: 1.4;
+            margin-bottom: 8rpx;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            display: -webkit-box;
+            -webkit-line-clamp: 2;
+            -webkit-box-orient: vertical;
+          }
+
+          .product-spec {
+            font-size: 24rpx;
+            color: var(--text-01);
+            margin-bottom: 8rpx;
+          }
+
+          .product-price {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+
+            .price {
+              font-size: 28rpx;
+              font-weight: bold;
+              color: var(--primary);
+            }
+
+            .quantity {
+              font-size: 24rpx;
+              color: var(--text-01);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  .order-amount {
+    margin-bottom: 24rpx;
+    padding-top: 20rpx;
+    border-top: 1rpx solid var(--border);
+
+    .amount-info {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+
+      .total-label {
+        font-size: 24rpx;
+        color: var(--text-01);
+      }
+
+      .total-amount {
+        font-size: 32rpx;
+        font-weight: bold;
+        color: var(--black);
+      }
+    }
+    .shipping-fee {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 8rpx 0;
+      background: rgba(255, 193, 7, 0.1);
+      border-radius: 8rpx;
+      padding: 12rpx 16rpx;
+
+      .fee-label {
+        font-size: 24rpx;
+        color: var(--warning);
+      }
+
+      .fee-amount {
+        font-size: 26rpx;
+        font-weight: bold;
+        color: var(--warning);
+      }
+    }
+  }
+
+  .order-actions {
+    display: flex;
+    justify-content: flex-end;
+    gap: 20rpx;
+    padding-top: 20rpx;
+    border-top: 1rpx solid var(--border);
+
+    .action-btn {
+      padding: 12rpx 24rpx;
+      border-radius: 40rpx;
+      font-size: 24rpx;
+      font-weight: bold;
+      text-align: center;
+      min-width: 120rpx;
+      background: var(--primary);
+      color: var(--light);
+    }
+  }
+}
+</style>

+ 571 - 0
pages/store/detail.vue

@@ -0,0 +1,571 @@
+<template>
+  <Theme>
+    <view class="wrap">
+      <!-- 导航栏 -->
+      <Navbar fixed leftIconColor="var(--black)" title="店铺订单详情"> </Navbar>
+
+      <!-- 订单状态 -->
+      <view class="status-section">
+        <view class="status-icon">
+          <i class="icon-font" :class="getStatusIcon(orderDetail.status)"></i>
+        </view>
+        <view class="status-info">
+          <view class="status-text">{{
+            getStatusText(orderDetail.status)
+          }}</view>
+          <view class="status-desc">{{
+            getStatusDesc(orderDetail.status)
+          }}</view>
+        </view>
+      </view>
+
+      <!-- 收货信息 -->
+      <view class="address-section" v-if="orderDetail.address">
+        <view class="section-header">
+          <i class="icon-font icon-location"></i>
+          <text>收货信息</text>
+        </view>
+        <view class="address-info">
+          <view class="receiver">
+            <text class="name">{{ orderDetail.address.name }}</text>
+            <text class="phone">{{ orderDetail.address.phone }}</text>
+          </view>
+          <view class="address">{{ orderDetail.address.address }}</view>
+        </view>
+      </view>
+
+      <!-- Tab导航 -->
+      <view class="tab-section">
+        <Tab :active="activeTab" :tabList="tabList" @confirm="onTabChange" />
+      </view>
+
+      <!-- 商品信息 -->
+      <view class="products-section" v-if="activeTab === 0">
+        <view class="section-header">
+          <i class="icon-font icon-goods"></i>
+          <text>商品信息</text>
+        </view>
+        <view class="product-list">
+          <view
+            v-for="(product, index) in orderDetail.goods"
+            :key="index"
+            class="product-item"
+          >
+            <view class="product-image">
+              <image :src="product.image" mode="aspectFill" />
+            </view>
+            <view class="product-info">
+              <view class="product-name">{{ product.name }}</view>
+              <view class="product-spec" v-if="product.spec">{{
+                product.spec
+              }}</view>
+              <view class="product-price">
+                <text class="price">{{ formatCurrency(product.price) }}</text>
+                <text class="quantity">x{{ product.quantity }}</text>
+              </view>
+            </view>
+          </view>
+        </view>
+      </view>
+
+      <!-- 订单信息 -->
+      <view class="order-info-section">
+        <view class="section-header">
+          <i class="icon-font icon-order"></i>
+          <text>订单信息</text>
+        </view>
+        <view class="info-list">
+          <view class="info-item">
+            <text class="label">订单号</text>
+            <text class="value">{{ orderDetail.orderNo }}</text>
+          </view>
+          <view class="info-item">
+            <text class="label">下单时间</text>
+            <text class="value">{{ formatTime(orderDetail.createTime) }}</text>
+          </view>
+          <view class="info-item" v-if="orderDetail.payTime">
+            <text class="label">支付时间</text>
+            <text class="value">{{ formatTime(orderDetail.payTime) }}</text>
+          </view>
+          <view class="info-item" v-if="orderDetail.shipTime">
+            <text class="label">发货时间</text>
+            <text class="value">{{ formatTime(orderDetail.shipTime) }}</text>
+          </view>
+          <view class="info-item" v-if="orderDetail.completeTime">
+            <text class="label">完成时间</text>
+            <text class="value">{{
+              formatTime(orderDetail.completeTime)
+            }}</text>
+          </view>
+          <view class="info-item" v-if="orderDetail.remark">
+            <text class="label">备注</text>
+            <text class="value">{{ orderDetail.remark }}</text>
+          </view>
+        </view>
+      </view>
+
+      <!-- 金额明细 -->
+      <view class="amount-section">
+        <view class="section-header">
+          <i class="icon-font icon-money"></i>
+          <text>金额明细</text>
+        </view>
+        <view class="amount-list">
+          <view class="amount-item">
+            <text class="label">商品金额</text>
+            <text class="value">{{ formatCurrency(orderDetail.goodsAmount) }}</text>
+          </view>
+          <view class="amount-item" v-if="orderDetail.shippingFee > 0">
+            <text class="label">运费</text>
+            <text class="value">{{ formatCurrency(orderDetail.shippingFee) }}</text>
+          </view>
+          <view class="amount-item" v-if="orderDetail.discountAmount > 0">
+            <text class="label">优惠金额</text>
+            <text class="value discount"
+              >-{{ formatCurrency(orderDetail.discountAmount) }}</text
+            >
+          </view>
+          <view class="amount-item total">
+            <text class="label">实付金额</text>
+            <text class="value">{{ formatCurrency(orderDetail.totalAmount) }}</text>
+          </view>
+        </view>
+      </view>
+
+      <!-- 物流信息 -->
+      <view class="logistics-section" v-if="orderDetail.logistics">
+        <view class="section-header">
+          <i class="icon-font icon-truck"></i>
+          <text>物流信息</text>
+        </view>
+        <view class="logistics-info">
+          <view class="logistics-item">
+            <text class="label">快递公司</text>
+            <text class="value">{{ orderDetail.logistics.company }}</text>
+          </view>
+          <view class="logistics-item">
+            <text class="label">快递单号</text>
+            <text class="value">{{
+              orderDetail.logistics.trackingNumber
+            }}</text>
+          </view>
+          <view class="logistics-item" v-if="orderDetail.logistics.status">
+            <text class="label">物流状态</text>
+            <text class="value">{{ orderDetail.logistics.status }}</text>
+          </view>
+        </view>
+      </view>
+
+      <!-- 操作按钮 -->
+      <!-- <view class="action-section" v-if="getActionButtons(orderDetail.status).length > 0">
+        <view 
+          v-for="action in getActionButtons(orderDetail.status)"
+          :key="action.type"
+          class="action-btn"
+          :class="action.class"
+          @click="handleAction(action.type)"
+        >
+          {{ action.text }}
+        </view>
+      </view> -->
+    </view>
+  </Theme>
+</template>
+
+<script setup>
+import { ref, onMounted } from "vue";
+import Navbar from "@/components/navbar";
+import Tab from "@/components/tabs";
+import { t } from "@/locale";
+import { onLoad } from "@dcloudio/uni-app";
+import { Toast, formatCurrency } from "@/utils";
+import { SELLER_SELLER_DETAIL } from "@/api";
+
+// 订单详情数据
+const orderDetail = ref({});
+
+// Tab相关
+const tabList = ref(["商品信息", "订单详情"]);
+const activeTab = ref(0);
+
+// Tab切换
+const onTabChange = (index) => {
+  activeTab.value = index;
+};
+
+// 获取订单详情
+const getOrderDetail = async (orderId) => {
+  try {
+    const res = await SELLER_SELLER_DETAIL(orderId);
+    orderDetail.value = res.data;
+  } catch (error) {
+    Toast(error.msg);
+  }
+};
+
+// 格式化时间
+const formatTime = (timestamp) => {
+  if (!timestamp) return "";
+  const date = new Date(timestamp);
+  const year = date.getFullYear();
+  const month = String(date.getMonth() + 1).padStart(2, "0");
+  const day = String(date.getDate()).padStart(2, "0");
+  const hours = String(date.getHours()).padStart(2, "0");
+  const minutes = String(date.getMinutes()).padStart(2, "0");
+  return `${year}-${month}-${day} ${hours}:${minutes}`;
+};
+
+// 获取状态文本
+const getStatusText = (status) => {
+  const statusMap = {
+    0: "待付款",
+    100: "待发货",
+    200: "已发货",
+    300: "待发货",
+    400: "已完成",
+    500: "已取消",
+    600: "已退款",
+  };
+  return statusMap[status] || "未知状态";
+};
+
+// 获取状态描述
+const getStatusDesc = (status) => {
+  const descMap = {
+    0: "等待买家付款",
+    100: "等待您发货",
+    200: "商品已发出",
+    300: "等待您发货",
+    400: "订单已完成",
+    500: "订单已取消",
+    600: "订单已退款",
+  };
+  return descMap[status] || "";
+};
+
+// 获取状态图标
+const getStatusIcon = (status) => {
+  const iconMap = {
+    0: "icon-time",
+    100: "icon-package",
+    200: "icon-truck",
+    300: "icon-package",
+    400: "icon-check",
+    500: "icon-close",
+    600: "icon-refund",
+  };
+  return iconMap[status] || "icon-order";
+};
+
+// 获取操作按钮
+const getActionButtons = (status) => {
+  const buttonMap = {
+    0: [], // 待付款 - 买家操作
+    100: [{ type: "ship", text: "发货", class: "btn-primary" }],
+    200: [], // 已发货 - 买家操作
+    300: [{ type: "ship", text: "发货", class: "btn-primary" }],
+    400: [], // 已完成
+    500: [], // 已取消
+    600: [], // 已退款
+  };
+  return buttonMap[status] || [];
+};
+
+// 处理操作
+const handleAction = (actionType) => {
+  switch (actionType) {
+    case "ship":
+      // 发货
+      Toast("发货功能待实现");
+      break;
+    case "cancel":
+      // 取消订单
+      Toast("取消订单功能待实现");
+      break;
+    case "refund":
+      // 退款
+      Toast("退款功能待实现");
+      break;
+    default:
+      break;
+  }
+};
+
+onLoad((options) => {
+  if (options.id) {
+    getOrderDetail(options.id);
+  }
+});
+</script>
+
+<style lang="less" scoped>
+@import url("@/style.less");
+
+.wrap {
+  min-height: 100vh;
+  background: var(--bg);
+  padding-bottom: 40rpx;
+
+  .nav_title {
+    color: var(--black);
+    font-size: 36rpx;
+    font-weight: bold;
+  }
+
+  .status-section {
+    background: var(--light);
+    padding: 40rpx 30rpx;
+    margin-bottom: 20rpx;
+    display: flex;
+    align-items: center;
+    gap: 30rpx;
+
+    .status-icon {
+      width: 100rpx;
+      height: 100rpx;
+      border-radius: 50%;
+      background: var(--primary);
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      .icon-font {
+        font-size: 48rpx;
+        color: var(--light);
+      }
+    }
+
+    .status-info {
+      flex: 1;
+
+      .status-text {
+        font-size: 32rpx;
+        font-weight: bold;
+        color: var(--black);
+        margin-bottom: 8rpx;
+      }
+
+      .status-desc {
+        font-size: 24rpx;
+        color: var(--text-01);
+      }
+    }
+  }
+
+  .tab-section {
+    background: var(--light);
+    margin-bottom: 20rpx;
+    padding: 0 30rpx;
+  }
+
+  .address-section,
+  .products-section,
+  .order-info-section,
+  .amount-section,
+  .logistics-section {
+    background: var(--light);
+    margin-bottom: 20rpx;
+    padding: 30rpx;
+
+    .section-header {
+      display: flex;
+      align-items: center;
+      gap: 12rpx;
+      margin-bottom: 24rpx;
+      font-size: 28rpx;
+      font-weight: bold;
+      color: var(--black);
+
+      .icon-font {
+        font-size: 32rpx;
+        color: var(--primary);
+      }
+    }
+  }
+
+  .address-info {
+    .receiver {
+      display: flex;
+      align-items: center;
+      gap: 20rpx;
+      margin-bottom: 12rpx;
+
+      .name {
+        font-size: 28rpx;
+        font-weight: bold;
+        color: var(--black);
+      }
+
+      .phone {
+        font-size: 24rpx;
+        color: var(--text-01);
+      }
+    }
+
+    .address {
+      font-size: 26rpx;
+      color: var(--text);
+      line-height: 1.5;
+    }
+  }
+
+  .product-list {
+    .product-item {
+      display: flex;
+      gap: 20rpx;
+      padding: 20rpx 0;
+      border-bottom: 1rpx solid var(--border);
+
+      &:last-child {
+        border-bottom: none;
+      }
+
+      .product-image {
+        width: 120rpx;
+        height: 120rpx;
+        border-radius: 12rpx;
+        overflow: hidden;
+        background: var(--bg);
+
+        image {
+          width: 100%;
+          height: 100%;
+        }
+      }
+
+      .product-info {
+        flex: 1;
+        display: flex;
+        flex-direction: column;
+        justify-content: space-between;
+
+        .product-name {
+          font-size: 28rpx;
+          color: var(--black);
+          line-height: 1.4;
+          margin-bottom: 8rpx;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          display: -webkit-box;
+          -webkit-line-clamp: 2;
+          line-clamp: 2;
+          -webkit-box-orient: vertical;
+        }
+
+        .product-spec {
+          font-size: 24rpx;
+          color: var(--text-01);
+          margin-bottom: 8rpx;
+        }
+
+        .product-price {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+
+          .price {
+            font-size: 28rpx;
+            font-weight: bold;
+            color: var(--primary);
+          }
+
+          .quantity {
+            font-size: 24rpx;
+            color: var(--text-01);
+          }
+        }
+      }
+    }
+  }
+
+  .info-list,
+  .amount-list,
+  .logistics-info {
+    .info-item,
+    .amount-item,
+    .logistics-item {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 16rpx 0;
+      border-bottom: 1rpx solid var(--border);
+
+      &:last-child {
+        border-bottom: none;
+      }
+
+      .label {
+        font-size: 26rpx;
+        color: var(--text-01);
+      }
+
+      .value {
+        font-size: 26rpx;
+        color: var(--text);
+        text-align: right;
+
+        &.discount {
+          color: var(--success);
+        }
+      }
+
+      &.total {
+        border-top: 2rpx solid var(--border);
+        margin-top: 16rpx;
+        padding-top: 20rpx;
+
+        .label {
+          font-size: 28rpx;
+          font-weight: bold;
+          color: var(--black);
+        }
+
+        .value {
+          font-size: 32rpx;
+          font-weight: bold;
+          color: var(--primary);
+        }
+      }
+    }
+  }
+
+  .action-section {
+    position: fixed;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    background: var(--light);
+    padding: 20rpx 30rpx;
+    padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
+    border-top: 1rpx solid var(--border);
+    display: flex;
+    gap: 20rpx;
+    justify-content: flex-end;
+
+    .action-btn {
+      padding: 20rpx 40rpx;
+      border-radius: 40rpx;
+      font-size: 28rpx;
+      font-weight: bold;
+      text-align: center;
+      min-width: 120rpx;
+
+      &.btn-primary {
+        background: var(--primary);
+        color: var(--light);
+      }
+
+      &.btn-secondary {
+        background: var(--light);
+        color: var(--black);
+        border: 1rpx solid var(--border);
+      }
+
+      &.btn-danger {
+        background: var(--danger);
+        color: var(--light);
+      }
+    }
+  }
+}
+</style>

+ 148 - 0
pages/store/index.vue

@@ -0,0 +1,148 @@
+<template>
+  <Theme>
+    <view class="wrap">
+      <!-- 导航栏 -->
+      <Navbar fixed leftIconColor="var(--black)" title="店铺订单列表"> </Navbar>
+
+      <view class="tab-section">
+        <Tab :active="currentTab" :tabList="tabList" @confirm="onTabChange" />
+      </view>
+
+      <!-- 订单列表 -->
+      <view class="content">
+        <List
+          ref="listRef"
+          :defaultParams="defaultParams"
+          @datas="getList"
+          :isLoading="false"
+          url="/seller/seller/order"
+        >
+          <template #item="{ item }">
+            <orderItem
+              :item="item"
+              @click="toOrderDetail"
+              @action="handleOrderAction"
+            />
+          </template>
+        </List>
+      </view>
+    </view>
+  </Theme>
+</template>
+  
+  <script setup>
+import { ref, reactive, onMounted } from "vue";
+import Navbar from "@/components/navbar";
+import Tab from "@/components/tabs";
+import List from "@/components/list";
+import orderItem from "./components/order_item";
+import { t } from "@/locale";
+import { Toast } from "@/utils";
+import { SELLER_SELLER_GETPAY } from "@/api";
+
+const listRef = ref(null);
+
+// Tab列表
+const tabList = ref(["全部", "待发货"]);
+
+// 当前Tab
+const currentTab = ref(0);
+
+// 默认参数
+const defaultParams = reactive({
+  status: 0, // 0=>全部 300=>待发货
+});
+
+// Tab切换
+const onTabChange = (item, index) => {
+  currentTab.value = index;
+  const statusMap = [0, 300];
+  defaultParams.status = statusMap[index];
+  listRef.value && listRef.value.handleRefresh();
+};
+
+// 获取列表数据
+const getList = (data) => {
+  console.log("订单列表数据:", data);
+};
+
+// 跳转到订单详情
+const toOrderDetail = (order) => {
+  uni.navigateTo({
+    url: `/pages/store/detail?id=${order.id}`,
+  });
+};
+
+// 处理订单操作
+const handleOrderAction = async (order) => {
+  console.log("订单操作:", order);
+  try {
+    const res = await SELLER_SELLER_GETPAY(order.id);
+    uni.navigateTo({
+      url: `/pages/shop/payment?oid=${res.data.sid}&type=sellerpay`,
+    });
+  } catch (error) {
+    Toast(error.msg);
+  }
+};
+
+onMounted(() => {
+  listRef.value && listRef.value.handleRefresh();
+});
+</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);
+  }
+
+  .tab-section {
+    background: var(--light);
+    border-bottom: 1rpx solid var(--border);
+    padding: 0 30rpx;
+
+    :deep(.u-tabs) {
+      .u-tabs__wrapper__nav {
+        padding: 0;
+      }
+
+      .u-tabs__wrapper__nav__item {
+        padding: 24rpx 16rpx;
+        font-size: 28rpx;
+        font-weight: 500;
+      }
+
+      .u-tabs__wrapper__nav__item--active {
+        font-weight: bold;
+      }
+
+      .u-tabs__wrapper__scroll-view {
+        height: 80rpx;
+      }
+    }
+  }
+
+  .content {
+    flex: 1;
+    padding: 20rpx 30rpx;
+    padding-bottom: calc(90rpx + constant(safe-area-inset-bottom));
+    padding-bottom: calc(90rpx + env(safe-area-inset-bottom));
+
+    // 列表样式
+    :deep(.list-container) {
+      display: flex;
+      flex-direction: column;
+      gap: 20rpx;
+    }
+  }
+}
+</style>
+  

+ 6 - 1
pages/user/index.vue

@@ -339,6 +339,11 @@ const card = [
     text: "意向表单",
     url: "/pages/purpose/index",
   },
+  {
+    icon: "store",
+    text: "店铺订单",
+    url: "/pages/store/index",
+  },
 ];
 
 const exit = () => {
@@ -391,7 +396,7 @@ const switchUserType = () => {
     content: t("确定要切换到买家模式吗?切换后底部导航将显示买家功能"),
   }).then(() => {
     // 设置为买家类型
-    useUser.setUserType(2);
+    useUser.setUserType(0);
     // 跳转到买家首页
     uni.reLaunch({
       url: "/pagesBuyer/home/index",

+ 1 - 0
pagesBuyer/USER_TYPE_STORAGE.md

@@ -129,3 +129,4 @@ const currentNavList = computed(() => {
 ```
 
 可以通过这个信息验证用户类型是否正确存储和显示。
+

+ 84 - 404
pagesBuyer/cart/index.vue

@@ -9,117 +9,54 @@
           <view class="nav_title" v-if="cartNum">({{ cartNum }})</view>
         </template>
         <template #right>
-          <view
-            class="nav_right"
-            @click.stop="delCart"
-            v-if="selectedIds.length"
-          >
+          <view class="nav_right" @click.stop="delCart" v-if="ids.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' }"
+        :style="{ '--height': footerHeight + tabbarHeight + '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>
+          <shop-model :list="cartList" ref="shopRef" @getIds="getIds" />
         </view>
-
         <view
           class="footer"
           id="footer"
           :style="{ '--tabbarHeight': tabbarHeight + 'px' }"
-          v-if="cartList.length"
         >
           <view class="footer_left">
             <up-checkbox
-              :label="t('全')"
+              :label="t('全部')"
               :disabled="!cartList.length"
               activeColor="var(--black)"
               shape="circle"
               labelSize="14"
               labelColor="#676969"
               iconSize="16"
-              name="selectAll"
+              name="agree"
               usedAlone
               v-model:checked="selectAllChecked"
               @change="selectAllChange"
-            />
+            >
+            </up-checkbox>
           </view>
           <view class="footer_right">
             <view class="total_info">
               <view class="total_price">
-                <text>合计:{{ symbol.symbol }}{{ totalPrice }}</text>
+                <!-- {{ t(`已选${money.total_num}件,合计:`) }} -->
+                <text
+                  >{{ symbol.symbol }} {{ Moneyhtml(money.total_price) }}</text
+                >
               </view>
               <view class="total_desc">
-                <trans _t="已选{{ selectedCount }}件商品" />
+                <trans _t="不包括运费" />
               </view>
             </view>
             <view
               class="total_btn"
-              @click.stop="checkout"
+              @click.stop="submit"
               :style="{
                 opacity: canCheckout ? 1 : 0.5,
                 'pointer-events': canCheckout ? 'auto' : 'none',
@@ -131,7 +68,7 @@
         </view>
       </view>
     </view>
-    <Tabbar page="cart" @getTabbarHeight="getTabbarHeight" />
+    <Tabbar page="shop" @getTabbarHeight="getTabbarHeight" />
   </Theme>
 </template>
 
@@ -141,241 +78,111 @@ import Tabbar from "@/components/tabbar";
 import Navbar from "@/components/navbar";
 import Tab from "@/components/tabs";
 import { useUserStore, useShopStore, useSystemStore } from "@/store";
+import { SELLER_DEL_CART } from "@/api";
 import { t } from "@/locale";
 import { onShow } from "@dcloudio/uni-app";
-import { query } from "@/utils";
+import shopModel from "@/pages/shop/components/shopModel";
+import { query, Moneyhtml } 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 cartList = computed(() => useShop.getSellerList);
+const cartNum = computed(() => useShop.getSellerNum);
+const selectAllChecked = ref(true);
+const shopRef = ref(null);
 const footerHeight = ref(0);
 const tabbarHeight = ref(0);
-const activeTab = ref(0);
-const tabHeight = ref(0);
-const tabList = ["全部", "降价商品", "库存紧张"];
+const ids = ref([]);
+const idsObj = ref({});
 
 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 money = computed(() => {
+  if (!cartList.value.length) {
+    selectAllChecked.value = false;
+  }
+  let result = cartList.value.reduce(
+    (acc, seller) => {
+      const sellerId = seller.seller_id;
+      if (idsObj.value[sellerId]) {
+        const totalPrice = seller.goods
+          .filter((good) => idsObj.value[sellerId].includes(good.id))
+          .reduce((sum, good) => sum + parseFloat(good.price) * good.total, 0);
+        acc.total_price = (acc.total_price - 0 + (totalPrice - 0)).toFixed(2);
+        const totalNum = seller.goods
+          .filter((good) => idsObj.value[sellerId].includes(good.id))
+          .reduce((sum, good) => sum + good.total, 0);
+        acc.total_num = acc.total_num - 0 + (totalNum - 0);
+      }
+      return acc;
+    },
+    {
+      total_price: 0,
+      total_num: 0,
+      orginal_price: 0,
+    }
+  );
+  return result;
 });
-
-// 是否可以结算
 const canCheckout = computed(() => {
-  return selectedIds.value.length > 0;
+  if (!ids.value.length) return false;
+  return true;
 });
-
-// 监听购物车变化,更新全选状态
 watch(
   () => cartList.value,
   (list) => {
-    if (!list.length) {
+    const hasData = Array.isArray(list) && list.length > 0;
+    if (!hasData) {
       selectAllChecked.value = false;
       return;
     }
-    const allChecked = list.every((item) => item.checked);
-    selectAllChecked.value = allChecked;
+    nextTick(() => {
+      selectAllChecked.value = true;
+      selectAllChange(true);
+    });
   },
   { 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 selectAllChange = (e) => {
+  shopRef.value && shopRef.value.allStatus(e);
 };
-
-// 更新数量
-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 getIds = (arr, flag) => {
+  ids.value = Object.values(arr).flat(1);
+  idsObj.value = arr;
+  selectAllChecked.value = flag;
 };
 
-// 验证数量
-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) => {
+const submit = () => {
+  if (!canCheckout.value) return;
   uni.navigateTo({
-    url: `/pagesBuyer/home/product?id=${item.id}`,
+    url: `/pagesBuyer/shop/shopConfirm?comfirmId=${ids.value.join(",")}`,
   });
 };
 
-// 切换标签
-const tabClick = (item, index) => {
-  if (activeTab.value === index) return;
-  activeTab.value = index;
-  // 根据标签筛选商品
-  filterCartItems(index);
+const delCart = () => {
+  removeCart();
 };
 
-// 筛选购物车商品
-const filterCartItems = (tabIndex) => {
-  // 这里可以根据不同标签筛选商品
-  // 0: 全部, 1: 降价商品, 2: 库存紧张
-  switch (tabIndex) {
-    case 0:
-      // 显示全部商品
-      break;
-    case 1:
-      // 显示降价商品(这里简化处理)
-      break;
-    case 2:
-      // 显示库存紧张商品(库存小于10)
-      break;
+const removeCart = async () => {
+  try {
+    let id = (ids.value.length && ids.value.join(",")) || "";
+    await SELLER_DEL_CART(id);
+    useShop.setSellerList();
+    shopRef.value && shopRef.value.allStatus(false);
+  } catch (error) {
+    toast(error.msg);
   }
 };
-
 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;
 };
@@ -385,6 +192,7 @@ onMounted(() => {
 });
 
 onShow(() => {
+  token.value && useShop.setSellerList();
   setTimeout(() => {
     getHeight();
   }, 0);
@@ -395,7 +203,7 @@ onShow(() => {
 @import url("@/style.less");
 
 .wrap {
-  min-height: 100vh;
+  min-height: 100bh;
   background: var(--bg);
   overflow: hidden;
   .flex();
@@ -425,138 +233,6 @@ onShow(() => {
     .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 {
@@ -567,13 +243,13 @@ onShow(() => {
       box-shadow: 0 -4px 6px #0000000d;
       position: fixed;
       bottom: var(--tabbarHeight);
+      // bottom: calc(var(--tabbarHeight) + constant(safe-area-inset-bottom));
+      // bottom: calc(var(--tabbarHeight) + env(safe-area-inset-bottom));
       left: 0;
       right: 0;
       box-sizing: border-box;
 
       &_left {
-        display: flex;
-        align-items: center;
       }
 
       &_right {
@@ -593,6 +269,10 @@ onShow(() => {
             .size(24rpx);
             line-height: 40rpx;
             text-align: right;
+
+            .icon-font {
+              .size();
+            }
           }
         }
 

+ 155 - 0
pagesBuyer/home/components/product_item.vue

@@ -0,0 +1,155 @@
+<template>
+  <view class="product-item" @click="handleClick">
+    <view class="product-image">
+      <image :src="item.picurl" mode="aspectFill" />
+      <!-- 视频播放图标 -->
+      <view v-if="item.isVideo" class="play-icon">
+        <i class="icon-font icon-play"></i>
+      </view>
+      <!-- 收藏图标 -->
+      <view class="favorite-icon" @click.stop="toggleFavorite">
+        <i class="icon-font icon-star" :class="{ active: item.iscollect }"></i>
+      </view>
+    </view>
+    <view class="product-info">
+      <view class="product-title">{{ item.goodsName }}</view>
+      <view class="product-tag" v-if="item.tag">{{ item.tag }}</view>
+      <view class="product-price">
+        <text class="current-price">{{ symbol }}{{ item.price }}</text>
+        <text class="original-price" v-if="item.originalprice"
+          >{{ symbol }}{{ item.originalprice }}</text
+        >
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+import { defineProps, defineEmits, computed } from "vue";
+import { useSystemStore } from "@/store";
+
+const props = defineProps({
+  item: {
+    type: Object,
+    required: true,
+  },
+});
+
+const emit = defineEmits(["click", "favorite"]);
+
+const useSystem = useSystemStore();
+const symbol = computed(() => useSystem.getSymbol.symbol);
+
+const handleClick = () => {
+  emit("click", props.item);
+  // 跳转到商品详情页
+  uni.navigateTo({
+    url: `/pagesBuyer/shop/detail?id=${props.item.id}`,
+  });
+};
+
+const toggleFavorite = () => {
+  emit("favorite", props.item);
+};
+</script>
+
+<style lang="less" scoped>
+@import url("@/style.less");
+
+.product-item {
+  background: var(--light);
+  border-radius: 16rpx;
+  overflow: hidden;
+
+  .product-image {
+    width: 100%;
+    height: 300rpx;
+    position: relative;
+
+    image {
+      width: 100%;
+      height: 100%;
+    }
+
+    .play-icon {
+      position: absolute;
+      top: 20rpx;
+      left: 20rpx;
+      width: 60rpx;
+      height: 60rpx;
+      background: rgba(0, 0, 0, 0.6);
+      border-radius: 50%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      .icon-play {
+        color: white;
+        font-size: 32rpx;
+      }
+    }
+
+    .favorite-icon {
+      position: absolute;
+      bottom: 20rpx;
+      right: 20rpx;
+      width: 60rpx;
+      height: 60rpx;
+      background: rgba(255, 255, 255, 0.9);
+      border-radius: 50%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      .icon-star {
+        color: var(--text-01);
+        font-size: 32rpx;
+
+        &.active {
+          color: var(--primary);
+        }
+      }
+    }
+  }
+
+  .product-info {
+    padding: 24rpx;
+
+    .product-title {
+      font-size: 28rpx;
+      color: var(--black);
+      line-height: 1.4;
+      margin-bottom: 12rpx;
+      .ellipsis(2);
+    }
+
+    .product-tag {
+      font-size: 24rpx;
+      color: var(--primary);
+      margin-bottom: 12rpx;
+      background: rgba(255, 107, 53, 0.1);
+      padding: 4rpx 12rpx;
+      border-radius: 8rpx;
+      display: inline-block;
+    }
+
+    .product-price {
+      display: flex;
+      align-items: center;
+      gap: 12rpx;
+      line-height: 1;
+      .current-price {
+        font-size: 32rpx;
+        font-weight: bold;
+        color: var(--red);
+      }
+
+      .original-price {
+        font-size: 24rpx;
+        color: var(--text-01);
+        text-decoration: line-through;
+      }
+    }
+  }
+}
+</style>

+ 113 - 457
pagesBuyer/home/index.vue

@@ -43,10 +43,11 @@
           <view class="bg_top">
             <view class="search">
               <Search
-                v-model="searchValue"
+                v-model="defaultParams.keywords"
                 border="none"
+                @input="onSearchInput"
                 @click="searchClick"
-                :placeholder="t('搜索商品')"
+                :placeholder="t('请粘贴商品链接或关键字')"
               >
                 <template #prefix>
                   <i class="icon-font icon-search"></i>
@@ -57,90 +58,49 @@
               </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" />
+            <Swiper @click="swiperClick" height="108px" 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>
+          <!-- Tab导航 -->
+          <view class="tab-section">
+            <up-tabs
+              :list="tabList"
+              :current="currentTab"
+              key="currentTab"
+              keyName="name"
+              @change="onTabChange"
+              :lineHeight="2"
+              lineColor="var(--black)"
+              :activeStyle="{
+                color: 'var(--black)',
+                fontWeight: 'bold',
+                transform: 'scale(1.05)',
+              }"
+              :inactiveStyle="{
+                color: '#767676',
+                transform: 'scale(1)',
+              }"
+            />
                 </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 class="products_section">
+            <List
+              ref="listRef"
+              url="/seller/goods/lists"
+              :defaultParams="defaultParams"
+              @datas="getList"
+            >
+              <template #item="{ item }">
+                <productItem :item="item" />
+              </template>
+            </List>
           </view>
-        </scroll-view>
+        </view>
       </view>
     </view>
 
@@ -158,14 +118,18 @@ 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 List from "@/components/list";
+import productItem from "./components/product_item";
 import {
-  onLoad,
-  onShow,
-  onUnload,
-  onHide,
-  onPageScroll,
-} from "@dcloudio/uni-app";
+  computed,
+  ref,
+  watch,
+  nextTick,
+  onMounted,
+  reactive,
+  watchEffect,
+} from "vue";
+import { onLoad, onShow, onUnload, onHide } from "@dcloudio/uni-app";
 import { storeToRefs } from "pinia";
 import {
   useShopStore,
@@ -205,129 +169,55 @@ watch(
 );
 
 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: ["科技", "热销"],
-  },
-]);
+// Tab数据
+const tabList = ref([]);
 
-// 推荐商品数据
-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,
-  },
-]);
+watchEffect(() => {
+  tabList.value = [
+    { name: t("推荐"), sort: "views" },
+    { name: t("新品"), sort: "id" },
+  ];
+});
+const currentTab = ref(0);
 
-const searchClick = () => {
-  toGo("/pagesBuyer/home/search");
-};
+// List组件引用
+const listRef = ref(null);
 
-const toGo = (url) => {
-  if (!url) return;
-  uni.navigateTo({ url });
+// 默认参数
+const defaultParams = reactive({
+  keywords: "",
+  sort: "views",
+});
+
+// Tab切换
+const onTabChange = (item, index) => {
+  if (!item) return;
+  currentTab.value = index;
+  defaultParams.sort = item.sort;
+  listRef.value && listRef.value.handleRefresh();
 };
 
-const toCategory = (category) => {
-  toGo(`/pagesBuyer/home/category?id=${category.id}&name=${category.name}`);
+// 搜索输入
+const onSearchInput = (value) => {
+  searchValue.value = value;
+  defaultParams.keywords = value;
+  listRef.value && listRef.value.handleRefresh();
 };
 
-const toProductDetail = (product) => {
-  toGo(`/pagesBuyer/home/product?id=${product.id}`);
+// 获取列表数据
+const getList = (data) => {
+  // console.log("商品列表数据:", data);
 };
 
-const toMoreProducts = () => {
-  toGo("/pagesBuyer/home/products");
+const searchClick = () => {
+  // toGo("/pagesBuyer/home/search");
 };
 
-const toRecord = () => {
-  toGo("/pagesBuyer/home/record");
+const toGo = (url) => {
+  if (!url) return;
+  uni.navigateTo({ url });
 };
 
 const swiperClick = (item) => {
@@ -343,7 +233,7 @@ const getTabbarHeight = (height) => {
 };
 
 const loadPageData = async () => {
-  // 加载页面数据
+  listRef.value && listRef.value.handleRefresh();
   await useSystem.setBoxes({}, { isLoading: true });
 };
 
@@ -352,32 +242,15 @@ onShow(() => {
 });
 
 onMounted(() => {
-  nextTick(async () => {
-    const res = await query("#tob");
-    const { windowHeight, statusBarHeight } = systemInfo();
-    contentHeight.value = windowHeight - res.height - statusBarHeight;
-  });
+  loadPageData();
 });
-
-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>
+@import url("@/style.less");
+
 .wrap {
-  background: var(--bg);
+  background: var(--bor-color);
   min-height: calc(100vh - var(--tabbarHeight));
   padding-bottom: var(--tabbarHeight);
 
@@ -424,16 +297,16 @@ const onRefresherRestore = () => {
     width: 100%;
     background-color: var(--light);
     &_bg {
-      position: sticky;
-      top: 0;
-      z-index: 9999;
-      background-color: var(--bg);
+      // position: sticky;
+      // top: 0;
+      z-index: 88;
+      background-color: var(--black);
 
       .bg_top {
         padding: 0 30rpx;
 
         .search {
-          padding: 26rpx 0;
+          padding: 26rpx 0 60rpx 0;
 
           :deep(.u-input) {
             padding: 0 20px 0 16px !important;
@@ -469,249 +342,41 @@ const onRefresherRestore = () => {
       }
     }
 
-    .content-scroll {
-      overflow: hidden;
-    }
-
     .content {
       width: 100%;
-      padding: 0 30rpx 30rpx;
+      padding: 30rpx 30rpx;
       position: relative;
       box-sizing: border-box;
+      margin-top: -30rpx;
+      border-radius: 20rpx 20rpx 0 0;
+      z-index: 99;
+      background-color: var(--bor-color);
 
       .banner-section {
+        margin-bottom: 38rpx;
+      }
+
+      .tab-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;
+      .products_section {
+        /deep/ .uni-scroll-view-content {
+          margin-top: 16rpx;
 
-            .category-icon {
-              width: 60rpx;
-              height: 60rpx;
-              margin-bottom: 12rpx;
-            }
-
-            .category-name {
-              font-size: 24rpx;
-              color: var(--text);
-              text-align: center;
-            }
+          > uni-view {
+            .flex();
+            flex-wrap: wrap;
+            gap: 8rpx 16rpx;
 
-            &.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);
-                }
-              }
+            .u-list-item {
+              width: calc((100% - 16rpx) / 2);
             }
           }
         }
@@ -719,13 +384,4 @@ const onRefresherRestore = () => {
     }
   }
 }
-
-@keyframes shimmer {
-  0% {
-    transform: rotate(0deg);
-  }
-  100% {
-    transform: rotate(360deg);
-  }
-}
 </style>

+ 0 - 739
pagesBuyer/home/record.vue

@@ -1,739 +0,0 @@
-<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>

+ 698 - 0
pagesBuyer/order/details.vue

@@ -0,0 +1,698 @@
+<template>
+  <Theme>
+    <Navbar fixed>
+      <template #center>
+        <view class="nav_title">
+          <trans _t="tabbar.订单详情" />
+        </view>
+      </template>
+    </Navbar>
+    <view class="content">
+      <view class="overview">
+        <view class="img_box">
+          <image
+            class="img"
+            mode="aspectFit"
+            :src="queryData?.goods[0]['pic_url']"
+          ></image>
+        </view>
+        <view class="information">
+          <text class="name">{{ queryData?.goods[0]["goodTitle"] }}</text>
+          <view
+            v-for="(title, index) in queryData?.goods[0]['sku_desc'].split(';')"
+            :key="index"
+          >
+            <text>{{ title }}</text>
+          </view>
+          <view class="num">x{{ queryData?.goods[0]["total"] }}</view>
+        </view>
+      </view>
+      <view class="price_box">
+        <text class="currency">{{ symbol.symbol }}:</text>
+        <rich-text
+          class="texts"
+          :nodes="Moneyhtml(queryData?.goods[0]['price'])"
+        ></rich-text>
+      </view>
+
+      <view class="dashed"></view>
+
+      <view class="text_box">
+        <trans class="title" _t="实付"></trans>
+        <view class="price_box">
+          <text class="currency">{{ symbol.symbol }}</text>
+          <rich-text
+            class="texts"
+            :nodes="Moneyhtml(queryData?.amount)"
+          ></rich-text>
+        </view>
+      </view>
+      <view class="text_box" v-if="queryData?.payType">
+        <trans class="title" _t="支付方式"></trans>
+        <text>{{ queryData?.payType }}</text>
+      </view>
+      <view class="text_box">
+        <trans class="title" _t="订单状态"></trans>
+        <text>{{ queryData?.status_txt }}</text>
+      </view>
+      <view class="text_box">
+        <trans class="title" _t="平台订单号"></trans>
+        <up-copy
+          :content="queryData?.orderNo"
+          :notice="t('复制成功')"
+          class="right_value"
+        >
+          <up-icon name="file-text" size="16px"></up-icon>
+          <text>{{ queryData?.orderNo }}</text>
+        </up-copy>
+      </view>
+      <view class="text_box" v-if="queryData?.payDate">
+        <trans class="title" _t="订单时间"></trans>
+        <text>{{ useGlobal().$format(queryData?.payDate) }}</text>
+      </view>
+
+      <view class="dashed"></view>
+
+      <view class="quality_inspection" v-if="queryData?.checkStatus > 0">
+        <view class="head">
+          <text class="state">{{ t("当前状态") }}:</text>
+          <view class="progress">
+            <text class="text_red">{{ currentStatus + 1 }}</text>
+            <text class="icon">/</text>
+            <text class="sum">{{
+              !queryData?.bzList.length ? 0 : queryData?.bzList.length
+            }}</text>
+          </view>
+          <text class="hint">{{ t("专业质检") }}</text>
+          <!-- <up-icon class="icon" color="#FF0000" name="info-circle"></up-icon> -->
+        </view>
+
+        <view class="body">
+          <text>{{ queryData?.checkRemark }}</text>
+        </view>
+
+        <view class="foot">
+          <view class="button" @click="noLook">
+            <trans _t="查看" />
+          </view>
+        </view>
+      </view>
+      <view v-if="checkstatus" class="popup_close" @click="toPay">
+        <trans class="menu_text" _t="去支付" />
+      </view>
+      <view
+        v-if="queryData?.status > 300"
+        class="popup_cancle"
+        @click="logisticsPop"
+      >
+        <trans class="menu_text" _t="查看物流" />
+      </view>
+      <view
+        class="popup_cancle"
+        @click="orderCancel"
+        v-if="queryData?.status == 100"
+      >
+        <trans class="menu_text" _t="取消" />
+      </view>
+      <view
+        class="popup_cancle"
+        @click="orderFinish"
+        v-if="queryData?.status == 500"
+      >
+        <trans class="menu_text" _t="完成" />
+      </view>
+      <view class="popup_cancle" @click="onClose">
+        <trans class="menu_text" _t="返回" />
+      </view>
+      <Popup ref="popRef" mode="center" isClose>
+        <template #content>
+          <view class="popup_content">
+            <view class="img_box">
+              <Swiper
+                :listArr="queryData?.checkImg"
+                urlName=""
+                autoplay
+                indicatorDots
+                circular
+              />
+            </view>
+            <view class="hint" @click="openKf">
+              <text class="text"
+                >{{ t("kf_hint")
+                }}<up-icon
+                  class="icon"
+                  color="#EB3A68"
+                  name="kefu-ermai"
+                ></up-icon
+              ></text>
+            </view>
+            <view class="checkbox" v-if="queryData?.checkStatus != 10">
+              <Tip title="同意并已阅读免责说明" :show="tipShow">
+                <view class="confirm_box" style="">
+                  <up-checkbox
+                    activeColor="var(--black)"
+                    labelSize="14"
+                    labelColor="#000000"
+                    iconSize="16"
+                    size="16"
+                    name="agree"
+                    usedAlone
+                    v-model:checked="aloneChecked"
+                    shape="circle"
+                  >
+                    <template #label>
+                      <trans class="text" _t="同意并已阅读免责说明" />
+                    </template>
+                  </up-checkbox>
+                </view>
+              </Tip>
+            </view>
+          </view>
+        </template>
+        <template #footer>
+          <view class="footer_button">
+            <template v-if="queryData?.checkStatus != 10">
+              <view class="accept" @click="accept">{{ t("接受瑕疵") }}</view>
+              <view class="refund" @click="refund">{{ t("退款") }}</view>
+            </template>
+            <view class="refund" @click="close" v-else>{{ t("关闭") }}</view>
+          </view>
+        </template>
+      </Popup>
+
+      <popup ref="popRefExpress" :title="t('物流详细')" isClose>
+        <template #content>
+          <up-steps direction="column" current="0">
+            <up-steps-item v-for="(item, index) in detail" :key="index">
+              <template #content>
+                <view class="default" :class="index == 0 ? 'express' : ''">
+                  <view class="slot-title">{{ item.status }}</view>
+                  <view class="slot-time">{{ item.time }}</view>
+                  <view class="slot-desc">{{ item.context }}</view>
+                </view>
+              </template>
+            </up-steps-item>
+          </up-steps>
+        </template>
+      </popup>
+    </view>
+
+    <cancelModel ref="cancelModelRef" />
+  </Theme>
+</template>
+
+<script setup>
+import { ref, watchEffect, computed, watch, onMounted, nextTick } from "vue";
+import { $post, useGlobal } from "@/utils";
+import { useSystemStore } from "@/store";
+import { Moneyhtml, Modal, Toast } from "@/utils";
+import { t } from "@/locale";
+import Navbar from "@/components/navbar";
+import Popup from "@/components/popup.vue";
+import { onLoad } from "@dcloudio/uni-app";
+import {
+  SHOP_ORDER_GETPAY,
+  SHOP_ACTION,
+  SELLER_ORDER_DETAIL,
+  SELLER_ORDER_FINISH,
+} from "@/api";
+import Swiper from "@/components/swiper";
+import Tip from "@/components/tooltip";
+import cancelModel from "@/pages/dashboard/components/cancelModel.vue";
+import { SHOP_ORDER_EXPRESS } from "@/api";
+
+const popRef = ref(null);
+const parmes = ref();
+const useSystem = useSystemStore();
+const queryData = ref();
+const currentStatus = ref(-1);
+const flexdw = ref(0);
+const symbol = computed(() => useSystem.getSymbol);
+const tipShow = ref(false);
+const aloneChecked = ref(false);
+
+const popRefExpress = ref(null);
+const detail = ref({});
+watch(aloneChecked, (newVal) => {
+  if (newVal) tipShow.value = false;
+});
+
+const checkstatus = ref(false);
+watchEffect(async () => {
+  try {
+    const res = await SELLER_ORDER_DETAIL(parmes.value.orderid);
+    queryData.value = res.data;
+    res.data.payStatus == 100
+      ? (checkstatus.value = true)
+      : (checkstatus.value = false);
+    res.data?.bzList?.forEach((item) => {
+      if (item.checked == 1) {
+        currentStatus.value += item.checked;
+        return;
+      }
+    });
+    nextTick(() => {
+      if (currentStatus.value >= 3) {
+        flexdw.value = currentStatus.value * 90;
+      }
+    });
+  } catch (err) {}
+});
+
+const noLook = () => {
+  popRef.value && popRef.value.open();
+};
+
+const close = () => {
+  popRef.value && popRef.value.close();
+};
+const openKf = () => {
+  uni.navigateTo({ url: "/pages/setting/system" });
+};
+const onClose = () => {
+  uni.navigateBack();
+};
+const previewImage = (index) => {
+  uni.previewImage({
+    current: index,
+    urls: queryData.value.checkImg,
+    indicator: "number",
+    loop: true,
+  });
+};
+
+const oids = ref("");
+const buyCart = async (oid) => {
+  const res = await SHOP_ORDER_GETPAY(oid);
+  oids.value = res.data.oid;
+};
+const toPay = () => {
+  uni.navigateTo({ url: `/pages/shop/payment?oid=${oids.value}&type=order` });
+};
+const accept = () => {
+  if (aloneChecked.value == false) {
+    tipShow.value = true;
+  } else {
+    acfun(30);
+  }
+};
+
+const acfun = async (val) => {
+  try {
+    const res = await SHOP_ACTION({
+      orderid: parmes.value.orderid,
+      type: val,
+    });
+    Toast(res.msg, 1000).then(() => {
+      let timer = setTimeout(() => {
+        popRef.value && popRef.value.close();
+        clearTimeout(timer);
+      }, 1000);
+    });
+  } catch (error) {
+    Toast(error.msg);
+  }
+};
+
+const logisticsPop = async () => {
+  try {
+    const res = await SHOP_ORDER_EXPRESS(queryData.value.id);
+    detail.value = res.data;
+    nextTick(() => {
+      popRefExpress.value && popRefExpress.value.open();
+    });
+  } catch (error) {
+    Toast(error.msg);
+  }
+};
+
+const orderFinish = async () => {
+  try {
+    const res = await SELLER_ORDER_FINISH(parmes.value.orderid);
+    Toast(res.msg);
+  } catch (error) {
+    Toast(error.msg);
+  }
+};
+
+const cancelModelRef = ref(null);
+
+const orderCancel = () => {
+  cancelModelRef.value && cancelModelRef.value.open(queryData.value.id);
+};
+
+const refund = () => {
+  acfun(40);
+};
+onLoad((options) => {
+  parmes.value = options;
+  // buyCart(options.orderid);
+});
+</script>
+
+<style lang="less" scoped>
+@import url("@/style.less");
+
+.scroll-Y {
+  height: 300rpx;
+}
+
+.scroll-view_H {
+  width: 280%;
+}
+
+.scroll-view-item_H {
+  width: 35%;
+}
+
+.content {
+  padding: 15rpx 5%;
+  border-top: 1rpx solid #ccc;
+
+  .popup_close {
+    background-color: var(--black);
+    color: var(--light);
+    .flex_center();
+    padding: 28rpx 0;
+    border-radius: 20rpx;
+  }
+
+  .popup_cancle {
+    background-color: var(--white);
+    color: var(--black);
+    .flex_center();
+    border-radius: 20rpx;
+    margin-top: 15rpx;
+    padding: 28rpx 0;
+    border: 1px solid var(--black);
+  }
+
+  .steps_box {
+    width: 100%;
+    overflow-x: scroll;
+    margin: 20rpx 0;
+    border: 1px solid #ccc;
+    padding: 10rpx 0;
+  }
+
+  .overview {
+    color: var(--text);
+    .flex();
+    gap: 24rpx;
+
+    .img_box {
+      flex: 4;
+      border-radius: 20rpx;
+
+      .img {
+        width: 100%;
+        height: 100%;
+      }
+    }
+
+    .information {
+      flex: 6;
+      .size(24rpx);
+
+      .name {
+        // .ellipsis(2);
+        margin-bottom: 26rpx;
+        .size(28rpx);
+      }
+
+      .num {
+        text-align: right;
+      }
+    }
+  }
+
+  .orginal_price {
+    .hor(end);
+  }
+
+  .text_box {
+    .flex_position(space-between, flex-end);
+    margin: 8rpx 0;
+    .size(24rpx);
+
+    .title {
+      color: var(--text-01);
+    }
+
+    .currency,
+    .texts {
+      color: var(--red);
+      .size(28rpx);
+      font-weight: 500;
+    }
+
+    .right_value {
+      .flex();
+    }
+  }
+
+  .price_box {
+    .flex_position(end, flex-end);
+    color: var(--black);
+    .size(28rpx);
+
+    .currency {
+      margin-right: 10rpx;
+    }
+  }
+
+  .texts /deep/ {
+    .price {
+      .size();
+    }
+  }
+  .check_box {
+    .image-scroll {
+      width: 100%;
+      margin: 20rpx 0;
+      white-space: nowrap;
+      .image-row {
+        display: inline-flex;
+        gap: 12rpx;
+        .image-item {
+          width: 140rpx;
+          height: 140rpx;
+          border-radius: 12rpx;
+          overflow: hidden;
+          box-shadow: 0 2rpx 5rpx rgba(0, 0, 0, 0.1);
+          .img {
+            width: 100%;
+            height: 100%;
+            object-fit: cover;
+          }
+        }
+      }
+    }
+
+    // 图片行容器
+
+    // 图片项
+  }
+  .title_box {
+    .size(28rpx);
+    font-weight: 500;
+    color: var(--text);
+  }
+
+  .hint {
+    .size(24rpx);
+    color: var(--text-01);
+    margin-top: 10rpx;
+
+    .steps-scroll-view {
+      flex: 1;
+      width: 100%;
+      white-space: nowrap;
+      padding: 24rpx 0;
+      scroll-behavior: smooth;
+
+      .custom-steps {
+        display: inline-flex;
+        min-width: 100%;
+
+        .step-item {
+          min-width: 120px;
+          max-width: 180px;
+          padding: 0 12rpx;
+          box-sizing: border-box;
+          white-space: normal;
+          word-break: break-word;
+          cursor: pointer;
+          transition: all 0.3s;
+
+          &:deep(.up-steps-item-title) {
+            .size(28rpx);
+            color: #333;
+            font-weight: 500;
+            // text-align: center;
+          }
+        }
+      }
+    }
+  }
+
+  .quality_inspection {
+    margin-top: 10rpx;
+    background-color: var(--bor-color);
+    padding: 0 20rpx;
+    border-radius: 20rpx;
+
+    .head {
+      .ver(flex-end);
+
+      .icon {
+        margin-left: 5rpx;
+      }
+
+      .state {
+        .size(20rpx);
+      }
+
+      .progress {
+        .ver(baseline);
+
+        .text_red {
+          color: var(--red);
+          .size(28rpx);
+          margin-left: 10rpx;
+        }
+
+        .icon,
+        .sum {
+          .size(24rpx);
+          color: var(--text);
+        }
+      }
+
+      .hint {
+        .size(24rpx);
+        font-weight: 500;
+        margin-left: 10rpx;
+      }
+    }
+
+    .body {
+      .size(24rpx);
+      color: var(--text);
+      margin-top: 16rpx;
+    }
+
+    .foot {
+      margin-top: 26rpx;
+      .hor(end);
+
+      .button {
+        .size(24rpx);
+        color: var(--light);
+        background-color: var(--black);
+        padding: 14rpx 28rpx;
+        border-radius: 20rpx;
+        margin-bottom: 16rpx;
+      }
+    }
+  }
+
+  /deep/ .u-popup__content {
+    width: 80vw !important;
+    position: relative;
+  }
+
+  .popup_content {
+    .img_box {
+      height: 400rpx;
+      border-radius: 20rpx;
+
+      .img {
+        max-width: 100%;
+        max-height: 100%;
+      }
+    }
+
+    .hint {
+      .ver(flex-start);
+
+      .text {
+        flex: 1;
+        word-break: break-word;
+        padding-right: 40rpx;
+        position: relative;
+      }
+
+      .icon {
+        position: absolute;
+        right: 40rpx;
+        bottom: 0;
+        margin-left: 8rpx;
+      }
+    }
+
+    .checkbox {
+      margin-top: 44rpx;
+
+      /deep/ .u-checkbox {
+        .ver(baseline);
+      }
+    }
+  }
+
+  .footer_button {
+    .hor(space-between);
+    .size(28rpx);
+    gap: 50rpx;
+    text-align: center;
+
+    .accept {
+      flex: 1;
+      color: var(--text);
+      padding: 24rpx 0;
+      border: 2rpx solid var(--black);
+      border-radius: 20rpx;
+    }
+
+    .refund {
+      flex: 1;
+      color: var(--light);
+      padding: 24rpx 0rpx;
+      background-color: var(--black);
+      border-radius: 20rpx;
+    }
+  }
+
+  // .popup_close {
+  //   position: absolute;
+  //   top: -50rpx;
+  //   right: 4rpx;
+  //   padding: 6rpx;
+  //   .flex_position(center, center);
+  //   background-color: var(--light);
+  //   border-radius: 50%;
+
+  // }
+}
+
+.default {
+  color: var(--text-02);
+
+  &.express {
+    color: var(--black);
+  }
+
+  .slot-title {
+    .size(28rpx);
+    font-weight: 600;
+  }
+
+  .slot-desc {
+    .size(20rpx);
+  }
+
+  .slot-time {
+    .size(22rpx);
+  }
+}
+</style>

+ 585 - 0
pagesBuyer/order/index.vue

@@ -0,0 +1,585 @@
+<template>
+  <Theme>
+    <view class="wrap">
+      <Navbar fixed border :navShow="navShow" title="我的订单"> </Navbar>
+      <view class="content1">
+        <view class="cont_tab">
+          <up-tabs
+            :key="tabActive"
+            :current="tabActive"
+            :list="tabList"
+            keyName="text"
+            @click="tabConfirm"
+            lineColor="var(--black)"
+            :activeStyle="{
+              color: 'var(--black)',
+              fontWeight: 'bold',
+              transform: 'scale(1.05)',
+            }"
+            :inactiveStyle="{
+              color: '#606266',
+              transform: 'scale(1)',
+            }"
+          >
+          </up-tabs>
+        </view>
+        <view
+          class="cont"
+          :style="{ '--height': footerHeight + tabbarHeight + 'px' }"
+        >
+          <List
+            url="/seller/order/lists"
+            :topHeight="tabHeight"
+            :defaultParams="{ keywords: searchValue, ...params }"
+            ref="listRef"
+            @datas="getAllData"
+          >
+            <template #item="{ item }">
+              <view class="order_list">
+                <view class="order_header">
+                  <template v-if="tabActive == 1 && navShow">
+                    <up-checkbox-group
+                      :modelValue="ids"
+                      activeColor="var(--black)"
+                      labelSize="14"
+                      labelColor="#676969"
+                      iconSize="16"
+                      @change="checkedChanges(item.id)"
+                    >
+                      <up-checkbox :name="item.id" />
+                    </up-checkbox-group>
+                  </template>
+                  <view class="create_time">
+                    <trans _t="创建时间" />:
+                    {{ useGlobal().$format(item.indate) }}
+                  </view>
+                </view>
+                <view class="order_item">
+                  <view
+                    class="order_item_wrapper"
+                    v-for="(val, num) in item.goods"
+                    :key="num"
+                    @click="listClick(item)"
+                  >
+                    <view class="_item_wrapper">
+                      <view class="thumb_img">
+                        <image :src="val.pic_url" class="_img"></image>
+                      </view>
+                      <view class="goods_info">
+                        <view class="info_name">
+                          <view class="_name">{{ val.goodTitle }}</view>
+                          <view class="_price"
+                            >{{ symbol.symbol }}&nbsp;{{
+                              Moneyhtml(val.price)
+                            }}</view
+                          >
+                        </view>
+                        <view class="spec_info">
+                          <view class="spec_desc">{{ val.sku_desc }}</view>
+                          <view class="spec_num">x{{ val.total }}</view>
+                        </view>
+                      </view>
+                    </view>
+                    <view class="order_status">
+                      <text>{{ item.status_txt }}&nbsp;</text>
+                      <!-- <i class="icon-font icon-question2"></i> -->
+                    </view>
+                  </view>
+                  <view class="order_footer">
+                    <view class="order_price">
+                      <view class="price_text"> <trans _t="总价" />: </view>
+                      <text
+                        >{{ symbol.symbol }}{{ Moneyhtml(item.amount) }}</text
+                      >
+                      <!-- <i class="icon-font icon-question2"></i> -->
+                    </view>
+                    <!-- <view class="end_time" v-if="item.payStatus == 100">
+                      <trans _t="结束时间" />:
+                      {{ useGlobal().$format(item.enddate) }}
+                    </view> -->
+                    <view class="order_btns" v-if="item.status == 900">
+                      <view class="btn">
+                        <trans _t="删除订单" />
+                      </view>
+                    </view>
+                    <view class="order_btns" v-if="item.status == 100">
+                      <view class="btn" @click="orderCancel(item)">
+                        <trans _t="取消" />
+                      </view>
+                      <view class="btn pay_btn" @click="paySubmit(item)">
+                        <trans _t="支付" />
+                      </view>
+                    </view>
+                  </view>
+                </view>
+              </view>
+            </template>
+          </List>
+        </view>
+        <view
+          class="cont_footer"
+          id="footer"
+          v-if="tabActive == 1 && navShow"
+          :style="{ '--tabbarHeight': tabbarHeight + 'px' }"
+        >
+          <up-checkbox
+            :label="t('全选')"
+            :disabled="!allId.length"
+            activeColor="var(--black)"
+            labelSize="14"
+            labelColor="#676969"
+            iconSize="16"
+            name="agree"
+            usedAlone
+            v-model:checked="selectAllChecked"
+            @change="selectAllChange"
+          >
+          </up-checkbox>
+          <view
+            class="total_btn"
+            @click.stop="submit"
+            :style="{ opacity: ids.length ? 1 : 0.5 }"
+          >
+            <trans _t="合并付款" />
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <cancelModel ref="cancelModelRef" isBuyer />
+  </Theme>
+</template>
+<script setup>
+import Navbar from "@/components/navbar";
+import navMenu from "@/components/nav_menu";
+import navFilter from "@/components/nav_filter";
+import Search from "@/components/input";
+import Tab from "@/components/tabs";
+import List from "@/components/list";
+import cancelModel from "@/pages/dashboard/components/cancelModel.vue";
+import { ref, onMounted, nextTick, computed, watch, watchEffect } from "vue";
+import { t } from "@/locale";
+import { useGlobal, Toast, Moneyhtml, query } from "@/utils";
+import { useSystemStore, useShopStore } from "@/store";
+import { SELLER_ORDER_GETPAY } from "@/api";
+import { onShow, onReachBottom, onLoad } from "@dcloudio/uni-app";
+
+const props = defineProps({
+  tabHeight: {
+    type: Number,
+    default: 0,
+  },
+  tabbarHeight: {
+    type: Number,
+    default: 0,
+  },
+  tabParams: {
+    type: Object,
+  },
+  navShow: Boolean,
+});
+
+const useSystem = useSystemStore();
+const useShop = useShopStore();
+const searchValue = ref("");
+const tabActive = ref(0);
+const footerHeight = ref(0);
+const listRef = ref(null);
+
+const tabList = ref([]);
+
+watchEffect(() => {
+  tabList.value = [
+    { text: t("全部"), status: 0 },
+    { text: t("待付款"), status: 100 },
+  ];
+});
+const params = ref({
+  status: 0,
+});
+const symbol = computed(() => useSystem.getSymbol);
+const selectAllChecked = ref(false);
+const allId = ref([]);
+const ids = ref([]);
+
+watch(
+  () => props.tabParams,
+  (newVal) => {
+    if (newVal && newVal.tab == 0) {
+      tabActive.value = newVal.type;
+      params.value.status = newVal.status;
+      useShop.setTabParams(null);
+
+      nextTick(() => {
+        listRef.value?.handleRefresh();
+        getHeight();
+      });
+    }
+  },
+  { immediate: true }
+);
+
+const searchConfirm = () => {
+  nextTick(() => {
+    listRef.value && listRef.value.handleRefresh();
+  });
+};
+const getAllData = (list) => {
+  if (tabActive.value == 0) return;
+  if (!list.length) return (selectAllChecked.value = false);
+  allId.value = list.reduce((acc, item) => {
+    acc.push(item.id);
+    return acc;
+  }, []);
+};
+
+const checkedChanges = (id) => {
+  let arr = JSON.parse(JSON.stringify(ids.value));
+  let findIndex = ids.value.findIndex((item) => item == id);
+  if (findIndex != -1) {
+    arr = arr.filter((item) => item != id);
+  } else {
+    arr.push(id);
+  }
+  ids.value = arr;
+  let flag = allId.value.every((value) => arr.includes(value));
+  selectAllChecked.value = flag;
+};
+const selectAllChange = (e) => {
+  selectAllChecked.value = e;
+  ids.value = e ? allId.value : [];
+};
+
+const paySubmit = (item) => {
+  ids.value = [item.id];
+  nextTick(() => {
+    shopConfirm();
+  });
+};
+
+const cancelModelRef = ref(null);
+const orderCancel = (item) => {
+  cancelModelRef.value && cancelModelRef.value.open(item.id);
+};
+
+const listClick = (item) => {
+  uni.navigateTo({ url: `/pagesBuyer/order/details?orderid=${item.id}` });
+};
+
+const submit = () => {
+  if (!ids.value.length) return;
+  shopConfirm();
+};
+
+const tabConfirm = (item, index) => {
+  if (!item) return;
+  tabActive.value = index;
+  params.value.status = item.status;
+  nextTick(() => {
+    listRef.value && listRef.value.handleRefresh();
+  });
+};
+
+const filterSubmit = (obj) => {
+  params.value = obj;
+  nextTick(() => {
+    listRef.value && listRef.value.handleRefresh();
+  });
+};
+
+const shopConfirm = async () => {
+  try {
+    const res = await SELLER_ORDER_GETPAY(ids.value.join(","));
+    uni.navigateTo({
+      url: `/pages/shop/payment?oid=${res.data.sid}&page=order&type=sellerorder`,
+    });
+    ids.value = [];
+    selectAllChecked.value = false;
+  } catch (error) {
+    Toast(error.msg);
+  }
+};
+
+const getHeight = async () => {
+  try {
+    const res = await query("#footer");
+    nextTick(() => {
+      footerHeight.value = res.height;
+    });
+  } catch (error) {}
+};
+
+onLoad((options) => {
+  params.value.status = options.status || 0;
+});
+onShow(() => {
+  nextTick(() => {
+    listRef.value && listRef.value.getData();
+  });
+});
+onReachBottom(() => {
+  nextTick(() => {
+    listRef.value && listRef.value.scrolltolower();
+  });
+});
+</script>
+<style lang="less" scoped>
+@import url("@/style.less");
+
+.wrap {
+  max-height: calc(100vh - 44rpx);
+  background-color: var(--bg);
+
+  .nav_search {
+    .icon-search {
+      color: #a8abb2;
+      .size(28px);
+    }
+  }
+
+  .content1 {
+    flex-grow: 1;
+    flex-direction: column;
+    .flex();
+    // height: calc(100vh - 44px - var(--height));
+    .cont_tab {
+      background-color: var(--light);
+      padding: 0 48rpx;
+
+      :deep(.tab) {
+        .active {
+          color: var(--text) !important;
+
+          &::before {
+            background-color: var(--text) !important;
+          }
+        }
+      }
+    }
+
+    .cont {
+      flex-grow: 1;
+      overflow: hidden scroll;
+      padding: 0 24rpx var(--height);
+
+      .order_list {
+        margin-top: 24rpx;
+        padding: 16rpx 24rpx;
+        background-color: var(--light);
+        border-radius: 16rpx;
+
+        .order_header {
+          .ver();
+          margin-bottom: 16rpx;
+
+          .header_img {
+            width: 48rpx;
+            height: 48rpx;
+
+            .img {
+              width: inherit;
+              height: inherit;
+              border-radius: 8rpx;
+            }
+          }
+
+          .create_time {
+            .size(24rpx);
+            color: var(--text);
+            font-weight: 700;
+
+            text {
+              font-weight: 500;
+            }
+          }
+        }
+
+        .order_item {
+          &_wrapper {
+            margin-top: 16rpx;
+
+            ._item_wrapper {
+              .flex();
+
+              .thumb_img {
+                width: 140rpx;
+                height: 140rpx;
+
+                ._img {
+                  width: inherit;
+                  height: inherit;
+                  border-radius: 16rpx;
+                }
+              }
+
+              .goods_info {
+                margin-left: 16rpx;
+                flex: 1;
+
+                .info_name {
+                  color: var(--text);
+                  .size(24rpx);
+                  font-weight: 700;
+                  line-height: 44rpx;
+                  .ver();
+
+                  ._name {
+                    flex: 1;
+                    .ellipsis();
+                    margin-right: 8rpx;
+                  }
+                }
+
+                .spec_info {
+                  .flex();
+                  flex: 1;
+                  margin-top: 8rpx;
+                  color: var(--text-01);
+                  .size(24rpx);
+                  line-height: 40rpx;
+
+                  .spec_desc {
+                    flex: 1;
+                    margin-right: 8rpx;
+                  }
+                }
+
+                .no_return {
+                  border: var(--danger-bor);
+                  border-radius: 40rpx;
+                  color: var(--danger);
+                  cursor: pointer;
+                  .size(24rpx);
+                  line-height: 40rpx;
+                  margin-top: 16rpx;
+                  padding: 0 24rpx;
+                  width: max-content;
+                }
+              }
+            }
+
+            .order_status {
+              color: var(--primary);
+              .size(24rpx);
+              height: 40rpx;
+              .flex_position(flex-end);
+              margin-top: 16rpx;
+
+              .icon-question2 {
+                .size(36rpx);
+              }
+            }
+
+            .order-cancel {
+              margin-top: 16rpx;
+              color: var(--text-01);
+              .size(24rpx);
+              line-height: 40rpx;
+
+              .time {
+                color: var(--danger);
+                font-weight: 700;
+              }
+
+              .content {
+                margin-top: 8rpx;
+                color: var(--text);
+                font-weight: 500;
+              }
+            }
+          }
+
+          .order_footer {
+            margin-top: 16rpx;
+            border-top: 1px solid #f5f6f7;
+
+            .order_price {
+              padding-top: 16rpx;
+              .flex_position(flex-end);
+              color: var(--primary);
+              color: var(--red);
+              .size();
+              font-weight: 700;
+              height: 48rpx;
+              line-height: 1;
+
+              .price_text {
+                color: var(--text);
+                .size(24rpx);
+                font-weight: 400;
+              }
+
+              .icon-question2 {
+                font-weight: 400;
+                color: var(--text-01);
+                margin-left: 8rpx;
+              }
+            }
+
+            .end_time {
+              .flex_position(flex-end);
+              margin-top: 16rpx;
+              color: #e62e2e;
+              font-weight: 700;
+              .size(24rpx);
+
+              text {
+                font-weight: 400;
+              }
+            }
+
+            .order_btns {
+              .flex_position(flex-end);
+              margin-top: 16rpx;
+              gap: 16rpx;
+
+              .btn {
+                .flex_center();
+                border-radius: 16rpx;
+                padding: 0 30rpx;
+                .size(24rpx);
+                height: 48rpx;
+                border: 1px solid var(--black);
+                background-color: var(--light);
+                color: var(--black);
+              }
+
+              .pay_btn {
+                color: var(--light);
+                background-color: var(--black);
+              }
+            }
+          }
+        }
+      }
+    }
+
+    .cont_footer {
+      position: fixed;
+      bottom: var(--tabbarHeight);
+      left: 0;
+      right: 0;
+      line-height: 100rpx;
+      background-color: var(--light);
+      box-shadow: 0 -4px 6px #0000000d;
+      padding: 0 24rpx;
+      .flex_position(space-between);
+
+      .total_btn {
+        .size(24rpx);
+        height: 38px;
+        margin-left: 16rpx;
+        min-width: 180rpx;
+        background-color: var(--primary);
+        background-color: var(--black);
+        color: var(--light);
+        border-radius: 16rpx;
+        padding: 16rpx 30rpx;
+        text-align: center;
+        .flex_center();
+        margin-left: 24rpx;
+      }
+    }
+  }
+}
+</style>

+ 283 - 0
pagesBuyer/profile/enter.vue

@@ -0,0 +1,283 @@
+<template>
+  <Theme>
+    <view class="wrap">
+      <Navbar title="商家入驻" fixed border> </Navbar>
+      <view class="form">
+        <view class="form_item">
+          <view class="item_label _required">
+            <trans _t="店铺名称" />
+          </view>
+          <view class="item_value">
+            <Input
+              :placeholder="t('请输入店铺名称')"
+              border="surround"
+              v-model="form.name"
+              :maxlength="50"
+            />
+          </view>
+        </view>
+
+        <view class="form_item">
+          <view class="item_label _required">
+            <trans _t="店铺logo" />
+          </view>
+          <view class="item_value">
+            <imageUpload v-model="imageList" :maxCount="1" multiple />
+          </view>
+        </view>
+        <view class="form_item">
+          <view class="item_label">
+            <trans _t="店铺地址" />
+          </view>
+          <view class="item_value">
+            <up-textarea
+              v-model="form.address"
+              :placeholder="t('请输入店铺地址')"
+              count
+              autoHeight
+              border="surround"
+              maxlength="500"
+            ></up-textarea>
+          </view>
+        </view>
+
+        <view class="form_item">
+          <view class="item_label">
+            <trans _t="联系方式" />
+          </view>
+          <view class="item_value">
+            <Input
+              :placeholder="t('请输入联系方式')"
+              border="surround"
+              type="Number"
+              v-model="form.tel"
+            />
+          </view>
+        </view>
+
+        <!-- <view class="form_item">
+          <view class="item_label">
+            <trans _t="经度" />
+          </view>
+          <view class="item_value">
+            <Input
+              :placeholder="t('请输入经度')"
+              border="surround"
+              v-model="form.longitude"
+            />
+          </view>
+        </view>
+        <view class="form_item">
+          <view class="item_label">
+            <trans _t="纬度" />
+          </view>
+          <view class="item_value">
+            <Input
+              :placeholder="t('请输入纬度')"
+              border="surround"
+              type="Number"
+              v-model="form.latitude"
+            />
+          </view>
+        </view> -->
+      </view>
+      <view class="footer">
+        <view class="submit_btn" @click="submit">
+          <trans _t="提交" />
+        </view>
+      </view>
+    </view>
+  </Theme>
+</template>
+<script setup>
+import Navbar from "@/components/navbar";
+import { reactive, ref, watch, nextTick } from "vue";
+import { t } from "@/locale";
+import Input from "@/components/input";
+import imageUpload from "@/components/imageUpload.vue";
+import { SELLER_APPLY_SELLER } from "@/api";
+import { Toast, Modal } from "@/utils";
+import { onLoad } from "@dcloudio/uni-app";
+
+const imageList = ref([]);
+
+const form = reactive({
+  name: "",
+  logo: "",
+  address: "",
+  tel: "",
+  longitude: "",
+  latitude: "",
+});
+
+const submit = () => {
+  if (!form.name) return Toast(t("店铺名称"));
+  // if (!imageList.value.length) return Toast(t("请上传商品图片"));
+  if (imageList.value.length) {
+    form.logo = imageList.value.map((item) => item.url).join(",");
+  }
+  verificationAdd();
+};
+
+const verificationAdd = async () => {
+  try {
+    const res = await SELLER_APPLY_SELLER({ ...form });
+    Toast(res.msg, 1000).then(() => {
+      setTimeout(() => {
+        // uni.navigateTo({
+        //   url: `/pages/assistant/system?id=${res.data.id}`,
+        // });
+      }, 1000);
+    });
+  } catch (error) {
+    Toast(error.msg);
+    close();
+  }
+};
+
+onLoad((options) => {
+  form.cateid = options.cateid;
+});
+</script>
+<style lang="less" scoped>
+@import url("@/style.less");
+
+.wrap {
+  background: var(--bg);
+  min-height: 100vh;
+  padding: 24rpx;
+  padding-bottom: calc(24rpx + constant(safe-area-inset-bottom));
+  padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
+  .nav_right {
+    color: var(--text);
+    .size(28rpx);
+  }
+  .form {
+    &_item {
+      margin-top: 24rpx;
+
+      &:first-child {
+        margin-top: 0;
+      }
+
+      .item_label {
+        font-weight: 700;
+        color: var(--text);
+        .size(28rpx);
+        line-height: 60rpx;
+        white-space: nowrap;
+        padding-right: 24rpx;
+        height: 64rpx;
+        width: fit-content;
+        position: relative;
+
+        // &::before {
+        //   content: "*";
+        //   color: #f56c6c;
+        //   margin-right: 8rpx;
+        // }
+      }
+
+      ._required {
+        &::before {
+          content: "*";
+          color: #f56c6c;
+          margin-right: 8rpx;
+        }
+      }
+
+      .item_value {
+        column-gap: 24rpx;
+        flex: 1;
+        display: flex;
+        align-items: center;
+        gap: 24rpx;
+
+        .value_box {
+          flex: 1;
+          display: flex;
+          flex-direction: column;
+          gap: 12rpx;
+
+          .title {
+            color: var(--text);
+            .size(24rpx);
+            font-weight: 500;
+          }
+          .unit {
+            .size(22rpx);
+            color: var(--text-01);
+          }
+        }
+
+        .price_separator {
+          width: 60rpx;
+          height: 2rpx;
+          margin: 0 12rpx;
+          align-self: flex-end;
+          margin-bottom: 28rpx;
+          background-color: var(--text-01);
+        }
+
+        /deep/ .u-input {
+          height: 80rpx;
+          border-radius: 20rpx;
+          padding: 0 20rpx !important;
+          background-color: var(--inputBg);
+
+          .u-input__content {
+            display: flex;
+          }
+        }
+        /deep/ .u-textarea {
+          border-radius: 20rpx;
+          padding: 0 20rpx !important;
+          background-color: var(--inputBg);
+        }
+        /deep/ .u-number-box {
+          border: 1px solid #dcdfe6;
+          border-radius: 16rpx;
+
+          .u-number-box__minus,
+          .u-number-box__plus {
+            width: 56rpx !important;
+            height: 56rpx !important;
+            background-color: transparent !important;
+
+            .u-icon__icon {
+              .size(26rpx) !important;
+              color: var(--text) !important;
+            }
+
+            &--hover {
+              .u-icon__icon {
+                color: var(--primary) !important;
+              }
+            }
+          }
+
+          .u-number-box__input {
+            margin: 0;
+            border-left: 1px solid #dcdfe6;
+            border-right: 1px solid #dcdfe6;
+            height: 56rpx !important;
+            width: 100rpx !important;
+            background-color: transparent !important;
+          }
+        }
+      }
+    }
+  }
+
+  .submit_btn {
+    height: 100rpx;
+    padding: 16rpx 30rpx;
+    background-color: var(--black);
+    color: var(--light);
+    .flex_center();
+    border-radius: 20rpx;
+    .size(24rpx);
+    margin-top: 40rpx;
+  }
+}
+</style>

+ 247 - 350
pagesBuyer/profile/index.vue

@@ -12,111 +12,105 @@
                 class="userInfo_avatar"
               >
               </image>
+              <!-- <view class="btns" v-if="!token">
+                <view class="btn">
+                  <trans _t="登录" />
+                </view>
+                <view class="btn is_plain">
+                  <trans _t="注册" />
+                </view>
+              </view> -->
               <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">
+            <view class="bg_right" @click="onEdit">
               <image src="../../static/user/edit.png" class="img"></image>
             </view>
           </view>
-          <view class="bg_logo">买家中心</view>
+          <view class="bg_logo">{{ verConfig.appNames }}</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="entry_container">
+          <view class="entry_wrapper">
             <view
-              class="order_item"
-              v-for="(item, index) in orderStatusList"
+              class="entry_item"
+              v-for="(item, index) in entryList"
               :key="index"
-              @click="toOrderList(item.status)"
+              @click="item.callback"
             >
-              <view class="order_icon">
-                <image :src="item.icon" class="icon" />
-                <view class="badge" v-if="item.count > 0">{{
-                  item.count
+              <view class="icons">
+                <view class="icons_img">
+                  <image
+                    class="img"
+                    :src="`../../static/user/${item.icon}.png`"
+                  ></image>
+                </view>
+                <view class="entry_num" v-if="item.num > 0">{{
+                  item.num
                 }}</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>
+              <trans class="entry_text" :_t="item.text" />
             </view>
           </view>
         </view>
-
-        <!-- 其他功能 -->
-        <view class="other_section">
+        <view class="content">
           <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" />
+          <!-- <plaCard mode="horizontal" /> -->
+          <view class="entry_container user_menu">
+            <view class="entry_wrapper">
+              <view
+                class="entry_item"
+                v-for="(item, index) in card"
+                :key="index"
+                @click="handleTo(item.url)"
+              >
+                <view class="icons">
+                  <view class="icons_img">
+                    <image
+                      class="img"
+                      :src="`../../static/user/${item.icon}.png`"
+                    ></image>
+                  </view>
+                  <view class="entry_num" v-if="item.num > 0">{{
+                    item.num
+                  }}</view>
+                </view>
+                <trans class="entry_text" :_t="item.text" />
               </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="footer">
         <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" />
+    <Tabbar page="user" />
+    <SimpleAgreementModal
+      ref="agreementModal"
+      :type="agreementType"
+      @close="handleAgreementModalClose"
+    />
   </Theme>
 </template>
 
 <script setup>
 import Tabbar from "@/components/tabbar";
 import Navbar from "@/components/navbar";
+import SimpleAgreementModal from "@/components/simpleAgreementModal";
+import plaCard from "@/components/placard";
 import { computed, watch, ref, reactive, onMounted, nextTick } from "vue";
 import { storeToRefs } from "pinia";
 import {
@@ -128,18 +122,19 @@ import {
 } from "@/store";
 import { t } from "@/locale";
 import { onShow, onLoad } from "@dcloudio/uni-app";
+import verConfig from "@/ver.config";
 import manifest from "@/manifest.json";
 import { Modal, Toast } from "@/utils";
+import { USER_ORDER_COUNT } from "@/api";
 
 const useUser = useUserStore();
 const useTabbar = useTabbarStore();
 const useSystem = useSystemStore();
-const useShop = useShopStore();
 
 const useMessage = useMessageStore();
 const { globalMap } = storeToRefs(useMessage);
 const unreadCount = ref(0);
-
+const customerUnreadCount = ref(0);
 watch(
   () => globalMap.value.serviceChannel,
   (newVal, oldVal) => {
@@ -152,191 +147,118 @@ watch(
     immediate: true,
   }
 );
+watch(
+  () => globalMap.value.noticeChannel,
+  (newVal, oldVal) => {
+    if (newVal) {
+      customerUnreadCount.value = newVal.unreadCount;
+    }
+  },
+  {
+    deep: true,
+    immediate: true,
+  }
+);
 
+const useShop = useShopStore();
 const userInfo = computed(() => useUser.getuserInfo);
 const token = computed(() => useUser.getToken);
+// const cartNum = computed(() => useShop.getCartNum)
+const statics = computed(() => useUser.getStatics);
 
-// 订单状态列表
-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 agreementType = ref("");
 
-// 功能菜单列表
-const menuList = ref([
-  {
-    icon: "/static/user/wallet.png",
-    text: "我的钱包",
-    url: "/pagesBuyer/profile/wallet",
-  },
+const entryList = ref([
   {
-    icon: "/static/user/coupon.png",
-    text: "优惠券",
-    url: "/pagesBuyer/profile/coupon",
-    badge: "3",
+    icon: "allwet",
+    text: "钱包",
+    num: 0,
+    callback: () => {
+      uni.navigateTo({ url: "/pages/bank/wallet" });
+    },
   },
   {
-    icon: "/static/user/favorite.png",
-    text: "我的收藏",
-    url: "/pagesBuyer/profile/favorite",
+    icon: "storage",
+    text: "我的订单",
+    num: 0,
+    callback: () => {
+      uni.navigateTo({ url: "/pagesBuyer/order/index" });
+    },
   },
   {
-    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",
+    icon: "payment",
+    text: "待支付",
+    num: 0,
+    callback: () => {
+      uni.navigateTo({ url: `/pagesBuyer/order/index?status=100` });
+    },
   },
 ]);
 
-// 其他功能菜单
-const otherMenuList = ref([
+const card = [
   {
-    icon: "/static/user/setting.png",
-    text: "设置",
-    url: "/pagesBuyer/profile/setting",
+    icon: "address",
+    text: "我的地址",
+    url: "/pages/address/index",
   },
   {
-    icon: "/static/user/help.png",
-    text: "帮助中心",
-    url: "/pagesBuyer/profile/help",
+    icon: "invite",
+    text: "邀请有礼",
+    url: "/pages/user/invite",
   },
   {
-    icon: "/static/user/feedback.png",
-    text: "意见反馈",
-    url: "/pagesBuyer/profile/feedback",
+    icon: "setting",
+    text: "系统设置",
+    url: "/pages/setting/index",
   },
   {
-    icon: "/static/user/about.png",
-    text: "关于我们",
-    url: "/pagesBuyer/profile/about",
+    icon: "enter",
+    text: "商家入驻",
+    url: "/pagesBuyer/profile/enter",
   },
-]);
+];
 
-// 退出登录
 const exit = () => {
   Modal({ content: t("确定要退出登录吗") }).then(async () => {
     useUser.loginOut();
   });
 };
 
-// 去登录
-const toLogin = () => {
-  uni.navigateTo({
-    url: "/pages/login/login",
-  });
+const handleTo = (url) => {
+  if (!url) return Toast(t("敬请期待"));
+  if (url === "service") return useSystem.service();
+  uni.navigateTo({ url });
 };
 
-// 编辑用户信息
 const onEdit = () => {
-  uni.navigateTo({ url: "/pagesBuyer/profile/edit" });
-};
-
-// 查看订单列表
-const toOrderList = (status) => {
-  uni.navigateTo({
-    url: `/pagesBuyer/profile/orders?status=${status || ""}`,
-  });
+  uni.navigateTo({ url: "/pages/user/edit_user" });
 };
 
-// 处理菜单点击
-const handleMenuClick = (item) => {
-  if (!item.url) {
-    Toast(t("功能开发中"));
-    return;
-  }
-
-  if (item.url === "service") {
-    useSystem.service();
-    return;
-  }
-
-  uni.navigateTo({ url: item.url });
-};
+onMounted(() => {});
+onLoad(() => {
+  // useUser.getUserInfo();
+});
+onShow(() => {
+  useUser.getUserInfo({}, { isLoading: true });
+});
 
-// 获取订单数量
-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 handleAgreementModalClose = () => {
+  agreementType.value = "";
 };
 
 // 切换用户类型
 const switchUserType = () => {
   Modal({
-    content: t("确定要切换到卖家模式吗?切换后底部导航将显示卖家功能"),
+    content: t("确定要切换到买家模式吗?切换后底部导航将显示买家功能"),
   }).then(() => {
-    // 设置为家类型
-    useUser.setUserType(1);
-    // 跳转到家首页
+    // 设置为买家类型
+    useUser.setUserType(2);
+    // 跳转到买家首页
     uni.reLaunch({
-      url: "/pages/index/index",
+      url: "/pagesBuyer/home/index",
     });
   });
 };
-
-onMounted(() => {
-  getOrderCount();
-});
-
-onLoad(() => {
-  // useUser.getUserInfo();
-});
-
-onShow(() => {
-  if (token.value) {
-    useUser.getUserInfo({}, { isLoading: true });
-    getOrderCount();
-  }
-});
-
-uni.hideTabBar();
 </script>
 
 <style lang="less" scoped>
@@ -350,7 +272,6 @@ uni.hideTabBar();
   .flex();
   flex-direction: column;
   justify-content: space-between;
-
   .cont {
     padding-bottom: 24rpx;
     flex-grow: 1;
@@ -375,16 +296,26 @@ uni.hideTabBar();
             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;
+          .btns {
+            .ver();
             margin-left: 24rpx;
+            column-gap: 20rpx;
+
+            .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;
+            }
+
+            .is_plain {
+              background-color: var(--bg-primary);
+              color: var(--primary);
+            }
           }
 
           .userInfo_ {
@@ -414,6 +345,29 @@ uni.hideTabBar();
         }
       }
 
+      .info_wrapper {
+        .flex();
+
+        .info_item {
+          flex: 1;
+          .flex_center();
+          flex-direction: column;
+          color: var(--light);
+
+          .item_num {
+            .size();
+            font-weight: 700;
+            line-height: 60rpx;
+          }
+
+          .item_text {
+            .size(24rpx);
+            font-weight: 500;
+            line-height: 40rpx;
+          }
+        }
+      }
+
       .bg_logo {
         position: absolute;
         .size(128rpx);
@@ -430,173 +384,116 @@ uni.hideTabBar();
       }
     }
 
-    .order_status {
-      margin: -70rpx 24rpx 32rpx;
+    .entry_container {
+      padding: 40rpx 24rpx;
+      margin-top: -70px;
       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 {
+      .entry_wrapper {
         display: flex;
-        justify-content: space-around;
-
-        .order_item {
-          display: flex;
+        flex-wrap: wrap;
+        gap: 30rpx 10rpx;
+        box-sizing: border-box;
+        background-color: #fafafa;
+        border-radius: 20rpx;
+        // box-shadow: 0 2px 30px #0000000d;
+        padding: 32rpx 0 24rpx;
+        color: var(--text);
+
+        .entry_item {
+          width: calc(33.333% - 10rpx);
+          // flex: 1;
+          .flex_center();
           flex-direction: column;
-          align-items: center;
 
-          .order_icon {
+          .icons {
             position: relative;
-            margin-bottom: 12rpx;
 
-            .icon {
+            .icons_img {
               width: 48rpx;
               height: 48rpx;
+
+              .img {
+                width: inherit;
+                height: inherit;
+              }
             }
 
-            .badge {
+            .entry_num {
               position: absolute;
               top: -8rpx;
-              right: -8rpx;
-              background: var(--danger);
+              right: -10rpx;
+              background-color: var(--danger);
+              border: 1px solid var(--light);
               color: var(--light);
-              font-size: 20rpx;
-              padding: 4rpx 8rpx;
-              border-radius: 20rpx;
-              min-width: 32rpx;
-              text-align: center;
-              line-height: 1;
+              width: 36rpx;
+              height: 36rpx;
+              border-radius: 50%;
+              .size(18rpx);
+              .flex_center();
             }
           }
 
-          .order_text {
-            font-size: 24rpx;
-            color: var(--text);
+          .entry_text {
+            width: max-content;
+            .size(28rpx);
+            line-height: 44rpx;
+            margin-top: 12rpx;
           }
         }
       }
     }
 
-    .menu_section,
-    .other_section {
-      margin: 0 24rpx 32rpx;
-
+    .content {
       .menu_title {
-        font-size: 32rpx;
+        margin: 0 24rpx;
         font-weight: bold;
-        color: var(--black);
-        margin-bottom: 24rpx;
+        color: var(--text);
+        .size(28rpx);
       }
 
-      .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);
-          }
+      .user_menu {
+        margin-top: 0;
+        .icons_img {
+          width: 40rpx !important;
+          height: 40rpx !important;
         }
       }
     }
   }
 
-  .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;
-    }
+  .version {
+    text-align: center;
+    color: var(--black);
+    .size(24rpx);
+    padding: 24rpx 0;
+  }
+}
 
-    .exit_btn {
-      background-color: var(--black);
-      color: var(--light);
-      .size(28rpx);
-      height: 84rpx;
-      padding: 16rpx 30rpx;
-      .flex_center();
-      border-radius: 16rpx;
-      margin-bottom: 24rpx;
-    }
+.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;
+}
 
-    .version {
-      text-align: center;
-      color: var(--black);
-      .size(24rpx);
-      padding: 24rpx 0;
-    }
-  }
+.exit_btn {
+  // width: 80%;
+  margin: 0 24rpx;
+  margin-top: 24rpx;
+  background-color: var(--black);
+  color: var(--light);
+  .size(28rpx);
+  height: 84rpx;
+  padding: 16rpx 30rpx;
+  .flex_center();
+  border-radius: 16rpx;
 }
 </style>

+ 0 - 212
pagesBuyer/shop/README.md

@@ -1,212 +0,0 @@
-# 买家端店铺功能说明
-
-## 功能概述
-为买家端添加了店铺功能,包括店铺列表、搜索、店铺详情等核心功能。
-
-## 功能特性
-
-### 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 状态管理
-- 响应式设计

+ 321 - 0
pagesBuyer/shop/cart.vue

@@ -0,0 +1,321 @@
+<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="ids.length">
+            <trans _t="删除" />
+          </view>
+        </template>
+      </Navbar>
+      <view class="tab" id="tabs">
+        <Tab :active="actvieNum" :tabList="tabList" @confirm="tabClick" />
+      </view>
+      <view
+        class="content"
+        :style="{ '--height': footerHeight + tabbarHeight + tabHeight + 'px' }"
+      >
+        <view class="cont_wrap">
+          <shop-model
+            :list="cartList"
+            ref="shopRef"
+            @getIds="getIds"
+            :isFree="actvieNum"
+            :key="actvieNum"
+          />
+        </view>
+        <view
+          class="footer"
+          id="footer"
+          :style="{ '--tabbarHeight': tabbarHeight + 'px' }"
+        >
+          <view class="footer_left">
+            <up-checkbox
+              :label="t('全部')"
+              :disabled="!cartList.length"
+              activeColor="var(--black)"
+              shape="circle"
+              labelSize="14"
+              labelColor="#676969"
+              iconSize="16"
+              name="agree"
+              usedAlone
+              v-model:checked="selectAllChecked"
+              @change="selectAllChange"
+            >
+            </up-checkbox>
+          </view>
+          <view class="footer_right">
+            <view class="total_info">
+              <view class="total_price">
+                <!-- {{ t(`已选${money.total_num}件,合计:`) }} -->
+                <text
+                  >{{ symbol.symbol }} {{ Moneyhtml(money.total_price) }}</text
+                >
+              </view>
+              <view class="total_desc">
+                <trans _t="199包邮" v-if="actvieNum == 1" />
+                <trans _t="不包括运费" v-else />
+              </view>
+            </view>
+            <view
+              class="total_btn"
+              @click.stop="submit"
+              :style="{
+                opacity: canCheckout ? 1 : 0.5,
+                'pointer-events': canCheckout ? 'auto' : 'none',
+              }"
+            >
+              <trans _t="去结算" />
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+    <Tabbar page="shop" @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 { SHOP_CART_DEL } from "@/api";
+import { t } from "@/locale";
+import { onShow } from "@dcloudio/uni-app";
+import shopModel from "./components/shopModel";
+import { query, Moneyhtml } from "@/utils";
+
+const useUser = useUserStore();
+const useShop = useShopStore();
+const useSystem = useSystemStore();
+const symbol = computed(() => useSystem.getSymbol);
+const cartList = computed(() => useShop.getCartList);
+const cartNum = computed(() => useShop.getCartNum);
+const selectAllChecked = ref(true);
+const shopRef = ref(null);
+const footerHeight = ref(0);
+const tabbarHeight = ref(0);
+const ids = ref([]);
+const idsObj = ref({});
+const actvieNum = ref(0);
+const tabHeight = ref(0);
+const tabList = ["代购", "包邮"];
+
+const userInfo = computed(() => useUser.getuserInfo);
+const token = computed(() => useUser.getToken);
+const money = computed(() => {
+  if (!cartList.value.length) {
+    selectAllChecked.value = false;
+  }
+  let result = cartList.value.reduce(
+    (acc, seller) => {
+      const sellerId = seller.seller_id;
+      if (idsObj.value[sellerId]) {
+        const totalPrice = seller.goods
+          .filter((good) => idsObj.value[sellerId].includes(good.id))
+          .reduce((sum, good) => sum + parseFloat(good.price) * good.total, 0);
+        acc.total_price = (acc.total_price - 0 + (totalPrice - 0)).toFixed(2);
+        const totalNum = seller.goods
+          .filter((good) => idsObj.value[sellerId].includes(good.id))
+          .reduce((sum, good) => sum + good.total, 0);
+        acc.total_num = acc.total_num - 0 + (totalNum - 0);
+      }
+      return acc;
+    },
+    {
+      total_price: 0,
+      total_num: 0,
+      orginal_price: 0,
+    }
+  );
+  return result;
+});
+const canCheckout = computed(() => {
+  if (!ids.value.length) return false;
+  const total = parseFloat(money.value.total_price || 0);
+  if (actvieNum.value === 1) {
+    return total >= 199;
+  }
+  return true;
+});
+watch(
+  () => cartList.value,
+  (list) => {
+    const hasData = Array.isArray(list) && list.length > 0;
+    if (!hasData) {
+      selectAllChecked.value = false;
+      return;
+    }
+    nextTick(() => {
+      selectAllChecked.value = true;
+      selectAllChange(true);
+    });
+  },
+  { immediate: true, deep: true }
+);
+const selectAllChange = (e) => {
+  shopRef.value && shopRef.value.allStatus(e);
+};
+const getIds = (arr, flag) => {
+  ids.value = Object.values(arr).flat(1);
+  idsObj.value = arr;
+  selectAllChecked.value = flag;
+};
+
+const submit = () => {
+  if (!canCheckout.value) return;
+  uni.navigateTo({
+    url: `/pages/shop/shopConfirm?comfirmId=${ids.value.join(",")}&isFree=${
+      actvieNum.value
+    }`,
+  });
+};
+
+const delCart = () => {
+  removeCart();
+};
+
+const removeCart = async () => {
+  try {
+    let id = (ids.value.length && ids.value.join(",")) || "";
+    await SHOP_CART_DEL(id);
+    useShop.setCartList(actvieNum.value);
+    shopRef.value && shopRef.value.allStatus(false);
+  } catch (error) {
+    toast(error.msg);
+  }
+};
+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;
+};
+const tabClick = (item, index) => {
+  if (actvieNum.value == index) return;
+  actvieNum.value = index;
+  useShop.setCartList(index);
+};
+
+onMounted(() => {
+  getHeight();
+});
+
+onShow(() => {
+  token.value && useShop.setCartList(actvieNum.value);
+  setTimeout(() => {
+    getHeight();
+  }, 0);
+});
+</script>
+
+<style lang="less" scoped>
+@import url("@/style.less");
+
+.wrap {
+  min-height: 100bh;
+  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;
+    }
+
+    .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);
+      // bottom: calc(var(--tabbarHeight) + constant(safe-area-inset-bottom));
+      // bottom: calc(var(--tabbarHeight) + env(safe-area-inset-bottom));
+      left: 0;
+      right: 0;
+      box-sizing: border-box;
+
+      &_left {
+      }
+
+      &_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;
+
+            .icon-font {
+              .size();
+            }
+          }
+        }
+
+        .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>

+ 147 - 0
pagesBuyer/shop/components/product_item.vue

@@ -0,0 +1,147 @@
+<template>
+  <view class="product-item" @click="handleClick">
+    <view class="product-image">
+      <image :src="item.image" mode="aspectFill" />
+      <!-- 视频播放图标 -->
+      <view v-if="item.isVideo" class="play-icon">
+        <i class="icon-font icon-play"></i>
+      </view>
+      <!-- 收藏图标 -->
+      <view class="favorite-icon" @click.stop="toggleFavorite">
+        <i class="icon-font icon-star" :class="{ active: item.isFavorite }"></i>
+      </view>
+    </view>
+    <view class="product-info">
+      <view class="product-title">{{ item.title }}</view>
+      <view class="product-sales">{{ item.sales }}</view>
+      <view class="product-price">
+        <text class="current-price">¥{{ item.currentPrice }}</text>
+        <text class="original-price">¥{{ item.originalPrice }}</text>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+import { defineProps, defineEmits } from "vue";
+
+const props = defineProps({
+  item: {
+    type: Object,
+    required: true,
+  },
+});
+
+const emit = defineEmits(["click", "favorite"]);
+
+const handleClick = () => {
+  emit("click", props.item);
+};
+
+const toggleFavorite = () => {
+  emit("favorite", props.item);
+};
+</script>
+
+<style lang="less" scoped>
+@import url("@/style.less");
+
+.product-item {
+  background: var(--light);
+  border-radius: 16rpx;
+  overflow: hidden;
+  margin-bottom: 20rpx;
+  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+
+  .product-image {
+    width: 100%;
+    height: 300rpx;
+    position: relative;
+
+    image {
+      width: 100%;
+      height: 100%;
+    }
+
+    .play-icon {
+      position: absolute;
+      top: 20rpx;
+      left: 20rpx;
+      width: 60rpx;
+      height: 60rpx;
+      background: rgba(0, 0, 0, 0.6);
+      border-radius: 50%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      .icon-play {
+        color: white;
+        font-size: 32rpx;
+      }
+    }
+
+    .favorite-icon {
+      position: absolute;
+      bottom: 20rpx;
+      right: 20rpx;
+      width: 60rpx;
+      height: 60rpx;
+      background: rgba(255, 255, 255, 0.9);
+      border-radius: 50%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      .icon-star {
+        color: var(--text-01);
+        font-size: 32rpx;
+
+        &.active {
+          color: var(--primary);
+        }
+      }
+    }
+  }
+
+  .product-info {
+    padding: 24rpx;
+
+    .product-title {
+      font-size: 28rpx;
+      color: var(--black);
+      line-height: 1.4;
+      margin-bottom: 12rpx;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      display: -webkit-box;
+      -webkit-line-clamp: 2;
+      -webkit-box-orient: vertical;
+    }
+
+    .product-sales {
+      font-size: 24rpx;
+      color: var(--text-01);
+      margin-bottom: 12rpx;
+    }
+
+    .product-price {
+      display: flex;
+      align-items: center;
+      gap: 12rpx;
+
+      .current-price {
+        font-size: 32rpx;
+        font-weight: bold;
+        color: var(--primary);
+      }
+
+      .original-price {
+        font-size: 24rpx;
+        color: var(--text-01);
+        text-decoration: line-through;
+      }
+    }
+  }
+}
+</style>

+ 249 - 0
pagesBuyer/shop/components/quantity_popup.vue

@@ -0,0 +1,249 @@
+<template>
+  <popup
+    @close="close"
+    ref="popupRef"
+    isClose
+    iconSize="16px"
+    zIndex="998"
+    iconColor="var(--text)"
+  >
+    <template #title>
+      <view class="product_info">
+        <image
+          :src="detail.picurl"
+          class="cur_img"
+          @click="previewImage(1, detail.picurl)"
+        ></image>
+        <view class="product_price">
+          <rich-text
+            class="texts"
+            :nodes="MoneyAbouthtml(detail.price, true)"
+          ></rich-text>
+        </view>
+      </view>
+    </template>
+    <template #content>
+      <scroll-view class="sku_info" scroll-y>
+        <view class="prop_wrapper">
+          <view class="spec_title"><trans _t="数量" /></view>
+          <view class="spec_items decrease">
+            <up-number-box
+              v-model="total"
+              :min="1"
+              @change="totalChange"
+            ></up-number-box>
+            <view class="inventory">
+              <trans _t="库存" />:{{ detail.total }}
+            </view>
+          </view>
+        </view>
+      </scroll-view>
+    </template>
+    <template #footer>
+      <view class="footer_btn" @click="btnClick">
+        <trans :_t="btnText == 'add' ? '加入购物车' : '立即购买'" />
+      </view>
+    </template>
+  </popup>
+</template>
+
+<script setup>
+import popup from "@/components/popup";
+import { ref, computed } from "vue";
+import { useSystemStore } from "@/store";
+import { MoneyAbouthtml, Toast } from "@/utils";
+import { SELLER_ADD_CART, SHOP_CART_CONFIRM } from "@/api";
+import { useShopStore } from "@/store";
+
+const useShop = useShopStore();
+const props = defineProps({
+  detail: {
+    type: Object,
+    default: () => ({}),
+  },
+});
+
+const emit = defineEmits(["open", "close"]);
+
+const total = ref(1);
+const btnText = ref("add");
+const useSystem = useSystemStore();
+const popupRef = ref(null);
+const symbol = computed(() => useSystem.getSymbol);
+
+const totalChange = (e) => {};
+
+const btnClick = () => {
+  addCart();
+};
+
+const close = () => {
+  popupRef.value && popupRef.value.close();
+  emit("close");
+};
+
+const open = (value = "add") => {
+  btnText.value = value;
+  total.value = 1; // 重置数量
+  popupRef.value && popupRef.value.open();
+};
+
+const addCart = async () => {
+  try {
+    const res = await SELLER_ADD_CART({
+      id: props.detail.id,
+      total: total.value,
+    });
+    useShop.setSellerNum(res.data.cartCount);
+    Toast(res.msg);
+    if (btnText.value == "buy") {
+      uni.navigateTo({
+        url: `/pagesBuyer/shop/shopConfirm?comfirmId=${res.data.cartids}`,
+      });
+    } else {
+      close();
+    }
+  } catch (error) {
+    Toast(error.msg);
+  }
+};
+
+function previewImage(index, urls) {
+  uni.previewImage({
+    current: index,
+    urls: [urls],
+    success() {},
+  });
+}
+
+defineExpose({
+  open,
+  close,
+});
+</script>
+
+<style lang="less" scoped>
+@import url("@/style.less");
+
+/deep/ .pop {
+  .top {
+    align-items: unset;
+    padding-bottom: 24rpx;
+  }
+
+  .conts {
+    padding-top: 0;
+  }
+}
+
+.uni-preview-container {
+  z-index: 99999 !important;
+  /* 确保高于其他元素 */
+}
+
+.product_info {
+  .flex();
+
+  .cur_img {
+    width: 144rpx;
+    height: 144rpx;
+    border-radius: 8rpx;
+    margin-right: 24rpx;
+  }
+
+  .product_price {
+    color: var(--red);
+    .size(40rpx);
+    font-weight: 700;
+    .flex();
+
+    .texts {
+      margin-left: 8rpx;
+
+      /deep/ div {
+        .ver(baseline);
+
+        .decimal {
+          .size(24rpx);
+        }
+      }
+    }
+  }
+}
+
+.sku_info {
+  .prop_wrapper {
+    margin-top: 8rpx;
+
+    .spec_title {
+      .ver();
+      color: var(--text);
+      .size(28rpx);
+      font-weight: 700;
+      line-height: 40rpx;
+      min-height: 60rpx;
+    }
+
+    .spec_items {
+      .flex();
+      flex-wrap: wrap;
+      gap: 16rpx;
+      margin-top: 24rpx;
+
+      .decrease {
+        .ver();
+
+        /deep/ .u-number-box {
+          border: 1px solid #dcdfe6;
+          border-radius: 16rpx;
+
+          .u-number-box__minus,
+          .u-number-box__plus {
+            width: 76rpx !important;
+            height: 76rpx !important;
+            background-color: transparent !important;
+
+            .u-icon__icon {
+              .size(26rpx) !important;
+              color: var(--text) !important;
+            }
+
+            &--hover {
+              .u-icon__icon {
+                color: var(--primary) !important;
+              }
+            }
+          }
+
+          .u-number-box__input {
+            margin: 0;
+            border-left: 1px solid #dcdfe6;
+            border-right: 1px solid #dcdfe6;
+            height: 76rpx !important;
+            width: 160rpx !important;
+            background-color: transparent !important;
+          }
+        }
+
+        .inventory {
+          color: var(--text);
+          margin-left: 8rpx;
+          .size(28rpx);
+          line-height: 60rpx;
+        }
+      }
+    }
+  }
+}
+
+.footer_btn {
+  background-color: var(--black);
+  color: var(--light);
+  border-radius: 20rpx;
+  padding: 16rpx 30rpx;
+  .flex_center();
+  .size();
+  font-weight: bold;
+  height: 100rpx;
+}
+</style>

+ 134 - 114
pagesBuyer/shop/components/shop_item.vue

@@ -1,167 +1,187 @@
 <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 class="shop-item" @click="handleClick">
+    <!-- 店铺头部信息 -->
+    <view class="shop-header">
+      <view class="shop-avatar">
+        <image :src="item.logo" mode="aspectFill" />
       </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 class="shop-info">
+        <view class="shop-name">{{ item.name }}</view>
+        <view class="shop-visits">
+          <image
+            class="img"
+            src="/static/seller/address.png"
+            model="fixwidth"
+          ></image>
+          <text>{{ item.distance }}km</text>
         </view>
       </view>
-      <view class="shop-tags">
-        <text class="tag" v-for="tag in item.tags" :key="tag">
-          {{ tag }}
-        </text>
+      <view class="shop-arrow">
+        <up-rate
+          readonly
+          :count="5"
+          size="16"
+          active-color="var(--red)"
+          v-model="item.score"
+        ></up-rate>
       </view>
     </view>
 
-    <view class="shop-arrow">
-      <i class="icon-font icon-arrow-right"></i>
+    <!-- 商品横向滚动列表 -->
+    <view class="products-scroll">
+      <up-scroll-list class="scroll-container" :indicator="false">
+        <view class="products-list">
+          <view
+            v-for="product in item.goods"
+            :key="product.id"
+            class="product-item"
+            @click.stop="handleProductClick(product)"
+          >
+            <view class="product-image">
+              <image :src="product.picurl" mode="aspectFill" />
+            </view>
+            <view class="product-info">
+              <view class="product-desc">{{ product.goodsName }}</view>
+              <view class="product-price">¥{{ product.price }}</view>
+            </view>
+          </view>
+        </view>
+      </up-scroll-list>
     </view>
   </view>
 </template>
 
 <script setup>
+import { defineProps, defineEmits } from "vue";
+
 const props = defineProps({
   item: {
     type: Object,
-    default: () => ({}),
+    required: true,
   },
 });
 
-const emit = defineEmits(["click"]);
+const emit = defineEmits(["click", "productClick"]);
 
-const toShopDetail = () => {
+const handleClick = () => {
   emit("click", props.item);
 };
+
+const handleProductClick = (product) => {
+  emit("productClick", product);
+  // 跳转到商品详情页
+  uni.navigateTo({
+    url: `/pagesBuyer/shop/detail?id=${product.id}`,
+  });
+};
 </script>
 
 <style lang="less" scoped>
 @import url("@/style.less");
 
 .shop-item {
-  display: flex;
-  align-items: center;
+  width: 100%;
   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;
+  margin-bottom: 24rpx;
+  padding: 14rpx 30rpx;
+  box-sizing: border-box;
+
+  .shop-header {
+    display: flex;
+    align-items: center;
+    margin-bottom: 30rpx;
+
+    .shop-avatar {
+      width: 66rpx;
+      height: 66rpx;
+      border-radius: 50%;
+      overflow: hidden;
+      margin-right: 10rpx;
+
+      image {
+        width: 100%;
+        height: 100%;
+      }
     }
 
-    .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);
+    .shop-info {
+      flex: 1;
+
+      .shop-name {
+        font-size: 28rpx;
+        font-weight: bold;
+        color: var(--black);
+        margin-bottom: 8rpx;
+      }
+
+      .shop-visits {
+        display: flex;
+        font-size: 24rpx;
+        color: var(--text-01);
+        line-height: 24rpx;
+        .img {
+          width: 18rpx;
+          height: 24rpx;
+          margin-right: 8rpx;
+        }
       }
+    }
 
-      &.offline {
-        background: var(--text-01);
+    .shop-arrow {
+      .icon-right {
+        color: var(--text-01);
+        font-size: 24rpx;
       }
     }
   }
 
-  .shop-info {
-    flex: 1;
-    display: flex;
-    flex-direction: column;
+  .products-scroll {
+    width: 100%;
+    overflow-x: auto;
 
-    .shop-name {
-      font-size: 32rpx;
-      font-weight: bold;
-      color: var(--black);
-      margin-bottom: 8rpx;
+    .scroll-container {
+      display: flex;
+      flex-direction: row;
     }
 
-    .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;
+    .products-list {
+      display: flex;
+      gap: 20rpx;
     }
 
-    .shop-stats {
-      display: flex;
-      gap: 24rpx;
-      margin-bottom: 12rpx;
+    .product-item {
+      width: 200rpx;
 
-      .stat-item {
-        display: flex;
-        align-items: center;
+      .product-image {
+        width: 100%;
+        height: 180rpx;
+        position: relative;
 
-        .stat-label {
-          font-size: 22rpx;
-          color: var(--text-01);
-          margin-right: 4rpx;
+        image {
+          width: 100%;
+          height: 100%;
+          border-radius: 16rpx;
         }
+      }
+
+      .product-info {
+        padding: 14rpx 0;
 
-        .stat-value {
-          font-size: 22rpx;
-          color: var(--black);
+        .product-price {
+          font-size: 24rpx;
           font-weight: bold;
+          color: var(--red);
         }
-      }
-    }
 
-    .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;
+        .product-desc {
+          font-size: 20rpx;
+          color: var(--text-01);
+          line-height: 1.4;
+          .ellipsis(1);
+          margin-bottom: 8rpx;
+        }
       }
     }
   }
-
-  .shop-arrow {
-    margin-left: 16rpx;
-
-    .icon-arrow-right {
-      font-size: 24rpx;
-      color: var(--text-01);
-    }
-  }
 }
 </style>

+ 453 - 403
pagesBuyer/shop/detail.vue

@@ -1,256 +1,279 @@
 <template>
   <Theme>
     <view class="wrap">
-      <Navbar fixed leftIconColor="var(--black)">
-        <template #center>
-          <view class="nav_title">
-            {{ shopInfo.name || "店铺详情" }}
+      <Navbar
+        :bgColor="
+          scrollTop >= tabInitTop || activeNum == 1
+            ? 'var(--light)'
+            : 'transparent'
+        "
+        fixed
+      >
+        <template #left>
+          <view class="nav_left">
+            <up-icon name="arrow-left" size="16" color="var(--light)"></up-icon>
           </view>
         </template>
         <template #right>
-          <view class="nav_right" @click="toggleFavorite">
-            <i
-              class="icon-font"
-              :class="isFavorite ? 'icon-heart-fill' : 'icon-heart'"
-            ></i>
+          <view class="nav_right">
+            <view class="nav_item" @click.stop="collectClick">
+              <up-icon
+                name="star-fill"
+                size="16"
+                :color="isCollect ? 'var(--primary)' : 'var(--light)'"
+              ></up-icon>
+            </view>
+            <view class="nav_item" @click.stop="shareClick">
+              <up-icon name="share" size="16" color="var(--light)"></up-icon>
+            </view>
           </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 class="content" id="conts" ref="contRef">
+        <scroll-view
+          scroll-with-animation
+          :scroll-into-view="scrollId"
+          :scroll-y="true"
+          class="scroll-view"
+        >
+          <view id="scroll_item_0" class="scroll_item0">
+            <view id="tabInit" v-if="activeNum == 0">
+              <view class="swiper">
+                <Swiper
+                  :listArr="swiperImgs"
+                  autoplay
+                  height="100%"
+                  circular
+                  indicator-dots
+                  indicator-active-color="var(--black)"
+                  indicator-color="var(--light)"
+                  urlName="url"
+                  borderRadius="0"
+                />
+              </view>
             </view>
-            <view class="stat-item">
-              <text class="stat-label">距离:</text>
-              <text class="stat-value">{{ shopInfo.distance }}</text>
+            <view class="info_wrapper">
+              <view class="price_wrapper">
+                <view class="texts">
+                  <rich-text
+                    :nodes="MoneyAbouthtml(detail.price, true)"
+                  ></rich-text>
+                  <text class="original-price" v-if="detail.originalprice"
+                    >{{ symbol.symbol }}{{ detail.originalprice }}</text
+                  >
+                </view>
+
+                <view class="sale_right" v-if="detail.sale">
+                  <trans _t="总销量" /> {{ detail.sale }}
+                </view>
+              </view>
+              <view class="info_name">{{
+                detail.title || detail.goodsName
+              }}</view>
             </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 id="scroll_item_1" class="scroll_item1" v-if="activeNum == 0">
+            <view
+              class="detail_wrapper"
+              v-if="detail.propArr && detail.propArr.length"
+            >
+              <view class="module_title">
+                <trans _t="规格参数" />
+              </view>
+              <Norm :item="detail.propArr" />
+            </view>
+            <view class="detail_wrapper" v-if="detail.desc">
+              <view class="module_title">
+                <trans _t="产品详情" />
               </view>
-              <view class="product-sales">已售{{ product.sales }}件</view>
+              <view class="desc_imgs">
+                <rich-text class="desc_cont" :nodes="detail.desc"></rich-text>
+              </view>
+            </view>
+          </view>
+        </scroll-view>
+        <view class="footer_btns">
+          <view class="btn_wrapper">
+            <view class="btn btn_add" @click="add('add')">
+              <trans _t="加入购物车" />
+            </view>
+            <view class="btn btn_buy" @click="add('buy')">
+              <trans _t="立即购买" />
             </view>
           </view>
         </view>
       </view>
     </view>
+    <quantityPopup :detail="detail" ref="quantityRef" />
   </Theme>
 </template>
 
 <script setup>
-import { ref, computed, onMounted } from "vue";
+import { ref, onMounted, nextTick, computed } from "vue";
 import Navbar from "@/components/navbar";
-import { useSystemStore } from "@/store";
+import { SELLER_GOODS_INFO } from "@/api";
+import { onLoad, onPageScroll } from "@dcloudio/uni-app";
 import { t } from "@/locale";
-import { onLoad } from "@dcloudio/uni-app";
-
+import {
+  useShopStore,
+  useSystemStore,
+  useTabbarStore,
+  useCacheStore,
+} from "@/store";
+import Swiper from "@/components/swiper";
+import {
+  systemInfo,
+  openUrl,
+  refresh,
+  query,
+  MoneyAbouthtml,
+  Toast,
+  TimeOut,
+  urlStrToArr,
+} from "@/utils";
+import Tab from "@/components/tabs";
+import ImageGrid from "@/components/ImageGrid.vue";
+import selectSpec from "@/pages/shop/components/select_spec";
+import Norm from "@/pages/shop/components/norm";
+import quantityPopup from "./components/quantity_popup";
+
+const useShop = useShopStore();
 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 useTabbar = useTabbarStore();
+const useCache = useCacheStore();
+
+const goodsId = ref(0);
+const detail = ref({});
+const activeNum = ref(0);
+const isCollect = ref(false);
+const selectRef = ref(null);
+const quantityRef = ref(null);
+
+const tabInitTop = ref(0);
+const initTab = ref(0);
+const scrollTop = ref(0);
+const tabList = ["详情", "评论"];
+const contRef = ref(null);
+const scrollId = ref("scroll_item_0");
+
+const swiperImgs = computed(() => {
+  if (detail.value.item_imgs && detail.value.item_imgs.length) {
+    return detail.value.item_imgs.slice(0, 6);
+  }
+  if (detail.value.picurl) {
+    return [{ url: detail.value.picurl }];
+  }
+  return [];
 });
 
-// 是否收藏
-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 currency = computed(() => useSystem.getCurrency);
+const symbol = computed(() => useSystem.getSymbol);
+
+const top = systemInfo().statusBarHeight || 0;
+
+const tabClick = (item, index) => {
+  activeNum.value = index;
+  getHeight("scroll_item_" + index);
+  let newTop =
+    index == 0
+      ? 0
+      : index == 1
+      ? height0.value - 44 - top - initTab.value
+      : height0.value + height1.value - 44 - top - initTab.value;
+  uni.pageScrollTo({
+    scrollTop: newTop,
+    duration: 300,
   });
 };
 
-// 联系店铺
-const contactShop = () => {
-  uni.showToast({
-    title: "联系店铺功能开发中",
-    icon: "none",
-  });
+const height0 = ref(0);
+const height1 = ref(0);
+
+const getHeight = async (id) => {
+  let ids = "#" + id;
+  try {
+    const res = await query(ids);
+    if (id == "scroll_item_0" && !height0.value) {
+      height0.value = res.height;
+    }
+    if (id == "scroll_item_1" && !height1.value) {
+      height1.value = res.height;
+    }
+  } catch (error) {}
 };
 
-// 查看位置
-const viewLocation = () => {
-  uni.showToast({
-    title: "查看位置功能开发中",
-    icon: "none",
-  });
+const collectClick = () => {
+  // TODO: 实现收藏功能
+  Toast("收藏功能待实现");
 };
 
-// 分享店铺
-const shareShop = () => {
-  uni.showToast({
-    title: "分享店铺功能开发中",
-    icon: "none",
-  });
+const shareClick = () => {
+  // TODO: 实现分享功能
+  Toast("分享功能待实现");
 };
 
-// 选择分类
-const selectCategory = (category) => {
-  selectedCategory.value = category.id;
-  // 这里可以根据分类筛选商品
-  console.log("选择分类:", category.name);
+const add = (type) => {
+  quantityRef.value && quantityRef.value.open(type);
 };
 
-// 跳转到商品详情
-const toProductDetail = (product) => {
-  uni.navigateTo({
-    url: `/pagesBuyer/shop/product?id=${product.id}`,
+const getDetail = async () => {
+  const params = {
+    id: goodsId.value,
+  };
+
+  try {
+    const res = await SELLER_GOODS_INFO(params);
+    detail.value = res.data;
+    isCollect.value = res.data.isCollect ? true : false;
+
+    if (res.data.skudesc) {
+      detail.value.propArr = parseSkuDesc(res.data.skudesc);
+    }
+  } catch (error) {
+    Toast(error.msg);
+  }
+};
+
+// 解析skudesc字段,转换为规格参数数组
+const parseSkuDesc = (skudesc) => {
+  if (!skudesc) return [];
+
+  const specs = skudesc.split(";");
+  const propArr = [];
+
+  specs.forEach((spec) => {
+    if (spec.trim()) {
+      const [name, value] = spec.split(":");
+      if (name && value) {
+        propArr.push({
+          name: name.trim(),
+          value: value.trim(),
+        });
+      }
+    }
   });
+
+  return propArr;
 };
 
+onPageScroll((e) => {
+  scrollTop.value = e.scrollTop;
+});
+
 onLoad((options) => {
-  // 从页面参数获取店铺信息
-  if (options.id) {
-    shopInfo.value.id = options.id;
-  }
-  if (options.name) {
-    shopInfo.value.name = decodeURIComponent(options.name);
-  }
+  const { id = 0 } = options;
+  goodsId.value = id;
 });
 
 onMounted(() => {
-  // 页面加载完成后的逻辑
+  nextTick(async () => {
+    getDetail();
+    const scrollItem0 = await query("#tabInit");
+    tabInitTop.value = scrollItem0.height;
+    const scrollTab = await query("#tab");
+    initTab.value = scrollTab.height;
+  });
 });
 </script>
 
@@ -258,249 +281,276 @@ onMounted(() => {
 @import url("@/style.less");
 
 .wrap {
+  background-color: var(--bg);
   min-height: 100vh;
-  background: var(--bg);
-  padding-bottom: 30rpx;
 
-  .nav_title {
-    color: var(--black);
-    font-size: 36rpx;
-    font-weight: bold;
+  .nav_left {
+    background: rgba(0, 0, 0, 0.6);
+    border-radius: 10rpx;
+    width: 50rpx;
+    height: 50rpx;
+    .flex_center();
   }
 
   .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;
-      }
+    .flex_center();
+    gap: 12rpx;
 
-      .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);
-        }
+    .nav_item {
+      width: 50rpx;
+      height: 50rpx;
+      .flex_center();
+      background: rgba(0, 0, 0, 0.6);
+      color: var(--light);
+      border-radius: 10rpx;
+    }
+  }
 
-        &.offline {
-          background: var(--text-01);
-        }
-      }
+  .tab {
+    z-index: 11;
+    background-color: var(--light);
+    &.active_tab {
+      position: fixed;
+      top: 44px;
+      left: 0;
+      right: 0;
     }
+  }
 
-    .shop-info {
-      flex: 1;
+  .content {
+    padding-bottom: 148rpx;
+    margin-top: -44px;
 
-      .shop-name {
-        font-size: 36rpx;
-        font-weight: bold;
-        color: var(--black);
-        margin-bottom: 8rpx;
+    .scroll_item0 {
+      /deep/ .swiper_list {
+        object-fit: cover;
+        aspect-ratio: 1 / 1;
       }
 
-      .shop-desc {
-        font-size: 24rpx;
-        color: var(--text-01);
-        margin-bottom: 16rpx;
-        line-height: 1.4;
+      .swiper {
+        position: relative;
+
+        /deep/ .uni-swiper-dots-horizontal {
+          bottom: 40px;
+        }
       }
 
-      .shop-stats {
-        display: flex;
-        gap: 24rpx;
+      .info_wrapper {
+        background-color: var(--light);
+        padding: 24rpx;
+        border-radius: 60rpx 60rpx 0 0;
+        margin-top: -30px;
+        position: relative;
 
-        .stat-item {
-          display: flex;
+        .price_wrapper {
+          .hor(space-between);
           align-items: center;
-
-          .stat-label {
-            font-size: 22rpx;
-            color: var(--text-01);
-            margin-right: 4rpx;
+          height: 60rpx;
+          font-weight: bold;
+          color: var(--red);
+          .size(40rpx);
+          margin-bottom: 24rpx;
+
+            .texts {
+              display: flex;
+              align-items: baseline;
+              line-height: 1;
+              margin-left: 8rpx;
+
+            /deep/ div {
+              .ver(baseline);
+
+              .decimal {
+                .size(24rpx);
+              }
+            }
+            .original-price {
+              margin-left: 6rpx;
+              font-size: 24rpx;
+              color: var(--bor-color1);
+              text-decoration: line-through;
+            }
           }
 
-          .stat-value {
-            font-size: 24rpx;
-            color: var(--black);
-            font-weight: bold;
-            margin-right: 4rpx;
+          .sale_right {
+            .size(22rpx);
+            color: var(--text);
           }
+        }
 
-          .stat-unit {
-            font-size: 20rpx;
-            color: var(--text-01);
-          }
+        .info_name {
+          .size(28rpx);
+          line-height: 48rpx;
+          font-weight: 700;
+          color: var(--text);
         }
       }
-    }
-  }
 
-  .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;
+      .service_wrapper {
+        padding: 24rpx;
+        background-color: var(--inputBg);
 
-      .icon-font {
-        font-size: 40rpx;
-        color: var(--primary);
-        margin-bottom: 8rpx;
-      }
+        .spec_title {
+          .size(28rpx);
+          font-weight: 700;
+          color: var(--text);
+          line-height: 60rpx;
+        }
 
-      text {
-        font-size: 24rpx;
-        color: var(--black);
+        .spec_info {
+          border-radius: 16rpx;
+          display: grid;
+          gap: 10rpx;
+          grid-template-columns: repeat(2, 1fr);
+          margin-top: 16rpx;
+          padding: 16rpx 0;
+          color: var(--text-02);
+          .size(24rpx);
+
+          .spec_item {
+            .ver();
+
+            .spec_value {
+              align-items: center;
+              color: var(--text);
+              display: inline-flex;
+              font-weight: 500;
+              margin-left: 12px;
+            }
+          }
+        }
       }
     }
-  }
 
-  .category-section,
-  .products-section {
-    padding: 0 30rpx 30rpx;
-
-    .section-title {
-      font-size: 32rpx;
-      font-weight: bold;
-      color: var(--black);
-      margin-bottom: 20rpx;
-    }
-  }
+    .scroll_item1 {
+      .detail_wrapper {
+        background-color: var(--light);
+        padding: 24rpx;
+        margin-top: 24rpx;
+
+        .module_title {
+          .size(28rpx);
+          font-weight: 700;
+          line-height: 60rpx;
+          color: var(--text);
+        }
 
-  .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);
-      }
+        .desc_imgs {
+          .desc_cont {
+            /deep/ div {
+              div {
+                width: 100% !important;
+
+                div {
+                  width: 100% !important;
+                }
+              }
+
+              img {
+                max-width: 100% !important;
+                width: 100%;
+                height: auto;
+                display: block;
+              }
+            }
+          }
 
-      .category-icon {
-        width: 48rpx;
-        height: 48rpx;
-        margin-bottom: 8rpx;
-      }
+          /deep/ img {
+            max-width: 100%;
+          }
+        }
 
-      .category-name {
-        font-size: 22rpx;
-        color: var(--black);
-        text-align: center;
+        .spec_list {
+          .spec_item {
+            display: flex;
+            justify-content: space-between;
+            padding: 16rpx 0;
+            border-bottom: 1px solid var(--border);
+
+            .spec_label {
+              color: var(--text-02);
+              .size(24rpx);
+            }
+
+            .spec_value {
+              color: var(--text);
+              .size(24rpx);
+              font-weight: 500;
+            }
+          }
+        }
       }
     }
-  }
 
-  .products-grid {
-    display: grid;
-    grid-template-columns: repeat(2, 1fr);
-    gap: 20rpx;
+    .scroll_item2 {
+      padding: 24rpx 16rpx;
 
-    .product-item {
-      background: var(--light);
-      border-radius: 16rpx;
-      overflow: hidden;
+      .title {
+        font-weight: 700;
+        line-height: 60rpx;
+        .size(28rpx);
+      }
 
-      .product-image {
+      .comment_item {
         width: 100%;
-        height: 200rpx;
-        object-fit: cover;
+        margin-top: 16rpx;
+        padding: 12rpx;
+        border: 1px solid var(--border);
+        border-radius: 10rpx;
+        .comment_text {
+          margin-top: 6rpx;
+          color: var(--text);
+        }
       }
 
-      .product-info {
-        padding: 16rpx;
+      .tips {
+        text-align: center;
+        font-size: 28rpx;
+        color: #333;
+      }
+    }
 
-        .product-name {
-          font-size: 24rpx;
+    .footer_btns {
+      position: fixed;
+      bottom: 0;
+      left: 0;
+      right: 0;
+      z-index: 10;
+      padding: 12rpx 24rpx;
+      background-color: var(--light);
+      .flex_center();
+      padding-bottom: calc(12rpx + env(safe-area-inset-bottom));
+      padding-bottom: calc(12rpx + constant(safe-area-inset-bottom));
+      box-sizing: border-box;
+
+      .btn_wrapper {
+        flex: 1;
+        .ver();
+        column-gap: 64rpx;
+
+        .btn {
+          flex: 1;
+          .size(28rpx);
+          height: 96rpx;
+          line-height: 28rpx;
+          padding: 8rpx 16rpx;
+          .flex_center();
+          border: 2px solid var(--black);
           color: var(--black);
-          margin-bottom: 8rpx;
-          display: -webkit-box;
-          -webkit-line-clamp: 2;
-          line-clamp: 2;
-          -webkit-box-orient: vertical;
-          overflow: hidden;
+          border-radius: 20rpx;
+          font-weight: bold;
         }
 
-        .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;
-          }
+        .btn_buy {
+          background-color: var(--light);
         }
 
-        .product-sales {
-          font-size: 20rpx;
-          color: var(--text-01);
+        .btn_add {
+          background-color: var(--black);
+          color: var(--light);
         }
       }
     }
   }
 }
-</style>
+</style>

+ 117 - 203
pagesBuyer/shop/index.vue

@@ -1,28 +1,31 @@
 <template>
   <Theme>
     <view class="wrap">
-      <Navbar fixed leftShow leftIconColor="var(--bg)">
-        <template #center>
-          <view class="nav_title">
-            <trans _t="店铺" />
+      <view class="cont_bg" id="tob">
+        <Navbar fixed leftShow bgColor="transparent">
+          <template #center>
+            <view class="nav_title">
+              <trans _t="店铺街" />
+            </view>
+          </template>
+        </Navbar>
+        <view class="bg_top">
+          <view class="search">
+            <Search
+              v-model="defaultParams.keywords"
+              border="none"
+              @input="onSearchInput"
+              @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>
-        </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>
 
@@ -30,12 +33,16 @@
       <view class="content">
         <List
           ref="listRef"
+          url="/seller/seller/lists"
           :defaultParams="defaultParams"
           @datas="getList"
-          :isLoading="false"
         >
           <template #item="{ item }">
-            <shopItem :item="item" @click="toShopDetail" />
+            <shopItem
+              :item="item"
+              @click="toShopDetail"
+              @productClick="toProductDetail"
+            />
           </template>
         </List>
       </view>
@@ -45,7 +52,7 @@
 </template>
 
 <script setup>
-import { ref, reactive, watch } from "vue";
+import { ref, reactive, watch, onMounted } from "vue";
 import Tabbar from "@/components/tabbar";
 import Navbar from "@/components/navbar";
 import Search from "@/components/input";
@@ -62,149 +69,53 @@ const listRef = ref(null);
 
 // 默认参数
 const defaultParams = reactive({
-  keyWord: "",
+  keywords: "",
+  longitude: "",
+  latitude: "",
 });
 
-// 店铺数据
-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 getLocation = () => {
+  uni.getLocation({
+    // type: "gcj02",
+    success: (res) => {
+      defaultParams.longitude = res.longitude.toString();
+      defaultParams.latitude = res.latitude.toString();
+      console.log("获取位置成功:", res);
+    },
+    fail: (err) => {
+      console.log("获取位置失败:", err);
+      // 设置默认位置(北京)
+      defaultParams.longitude = "116.397128";
+      defaultParams.latitude = "39.916527";
+    },
+  });
+};
+
 // 搜索输入
 const onSearchInput = (value) => {
   searchValue.value = value;
-  defaultParams.keyWord = value;
-  // 触发列表刷新
-  if (listRef.value) {
-    listRef.value.handleRefresh();
-  }
+  defaultParams.keywords = value;
+  listRef.value && listRef.value.handleRefresh();
 };
 
 // 搜索点击
-const onSearchClick = () => {
+const searchClick = () => {
   console.log("搜索:", searchValue.value);
 };
 
+// 相机点击
+const onCameraClick = () => {
+  console.log("相机搜索");
+  // 这里可以添加相机搜索功能
+};
+
 // 获取列表数据
 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);
-  }
+  console.log("商品列表数据:", data);
 };
 
 // 跳转到店铺详情
@@ -214,10 +125,24 @@ const toShopDetail = (shop) => {
   });
 };
 
+// 跳转到商品详情
+const toProductDetail = (product) => {
+  uni.navigateTo({
+    url: `/pagesBuyer/product/detail?id=${product.id}`,
+  });
+};
+
 onShow(() => {
-  // 页面显示时初始化数据
-  getList();
+  getLocation();
+  listRef.value && listRef.value.handleRefresh();
+});
+
+onMounted(() => {
+  getLocation();
+  listRef.value && listRef.value.handleRefresh();
 });
+
+uni.hideTabBar();
 </script>
 
 <style lang="less" scoped>
@@ -225,72 +150,61 @@ onShow(() => {
 
 .wrap {
   min-height: 100vh;
-  background: var(--bg);
+  background: var(--inputBg);
   display: flex;
   flex-direction: column;
 
-  .nav_title {
-    color: var(--black);
-    font-size: 36rpx;
-    font-weight: bold;
-  }
+  .cont_bg {
+    z-index: 88;
+    background-color: var(--black);
+    .nav_title {
+      color: var(--bg);
+    }
+    .bg_top {
+      padding: 0 30rpx;
 
-  .search-section {
-    position: sticky;
-    top: 0;
-    z-index: 10;
-    padding: 20rpx 30rpx;
-    background: var(--light);
-    border-bottom: 1rpx solid var(--border);
+      .search {
+        padding: 26rpx 0 60rpx 0;
 
-    .search-box {
-      :deep(.u-input) {
-        padding: 0 20px 0 16px !important;
-        background: var(--inputBg);
-        border-radius: 20rpx;
+        :deep(.u-input) {
+          padding: 0 20px 0 16px !important;
+
+          .u-input__content__field-wrapper__field {
+            height: 47px;
+          }
+        }
 
-        .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;
         }
       }
 
-      .icon-search {
-        color: var(--text);
-        font-size: 40rpx;
+      .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 {
+    width: 100%;
     flex: 1;
-    padding: 20rpx 30rpx;
+    padding: 20rpx 0;
     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>

+ 681 - 0
pagesBuyer/shop/shopConfirm.vue

@@ -0,0 +1,681 @@
+<template>
+  <Theme>
+    <view class="wrap">
+      <Navbar title="确认订单" fixed border autoBack @leftClick="leftClick">
+        <template #right>
+          <!-- <navMenu :options="{ icon: 'icon-home', text: '主页' }" /> -->
+        </template>
+      </Navbar>
+      <view class="content">
+        <view class="cont">
+          <addressModel @confirm="addressConfirm" />
+          <view
+            class="card_wrapper"
+            v-for="(item, index) in orderList"
+            :key="index"
+          >
+            <view class="card_header">
+              <!-- <view class="header_img">
+                <image
+                  class="img"
+                  :src="`../../static/shop/icon_${item.channel}.png`"
+                ></image>
+              </view> -->
+              <view class="header_name">{{ item.seller_name }}</view>
+            </view>
+            <view class="card_item">
+              <view
+                class="card_item_wrapper"
+                v-for="(val, num) in item.goods"
+                :key="num"
+              >
+                <view class="item_top">
+                  <view class="item_left">
+                    <image :src="val.picurl" class="item_img"></image>
+                  </view>
+                  <view class="item_middle">
+                    <view class="title">{{ val.goodsName }}</view>
+                    <view class="desc">{{ val.skudesc }}</view>
+                  </view>
+                  <view class="item_right">
+                    <view class="price">
+                      <text>{{ symbol.symbol }}</text>
+                      <text>{{ Moneyhtml(val.price) }}</text>
+                    </view>
+                    <view class="total_num">X {{ val.total }}</view>
+                  </view>
+                </view>
+              </view>
+              <view class="card_footer">
+                <up-collapse :border="false" :value="collapseValue">
+                  <up-collapse-item :name="item.seller_id" :border="false">
+                    <template #title>
+                      <view class="collapse_title">
+                        <trans _t="订单总价" />:
+                      </view>
+                    </template>
+                    <template #value>
+                      <view class="collapse_value">
+                        <text>{{ symbol.symbol }} </text>
+                        <text class="item_num">{{
+                          Moneyhtml(totalAmount)
+                        }}</text>
+                      </view>
+                    </template>
+                    <view class="card_footer_item" style="padding-top: 24rpx">
+                      <view class="item_label"> <trans _t="总价" />: </view>
+                      <view class="item_value item_red">
+                        <text>{{ symbol.symbol }} </text>
+                        <text class="item_num">{{
+                          Moneyhtml(item.allAmt)
+                        }}</text>
+                      </view>
+                    </view>
+                    <!-- <view class="card_footer_item">
+                      <view class="item_label">
+                        <trans _t="运费到仓库" />:
+                      </view>
+                      <view class="item_value">
+                        <text>{{ symbol.symbol }}</text>
+                        <text class="item_num">{{
+                          Moneyhtml(item.postAmt)
+                        }}</text>
+                      </view>
+                    </view>
+                    <view class="card_footer_item">
+                      <view class="item_label"> <trans _t="折扣" />: </view>
+                      <view class="item_value item_red">
+                        <text>-{{ symbol.symbol }}</text>
+                        <text class="item_num">{{
+                          Moneyhtml(item.disAMt)
+                        }}</text>
+                      </view>
+                    </view> -->
+                  </up-collapse-item>
+                </up-collapse>
+              </view>
+            </view>
+          </view>
+          <!-- <view class="friendly_reminder">
+            <trans class="reminder_title" _t="温馨提示" />
+            <view class="reminder_desc">
+              <trans _t="提示内容" />
+            </view>
+          </view> -->
+
+          <!-- <couponModel
+            :item="couponList"
+            @change="changeId"
+            isWidth
+            style="margin-top: 24rpx"
+            v-if="isFree == 0"
+          /> -->
+        </view>
+        <view class="footer">
+          <view class="footer_protocol">
+            <view class="protocol">
+              <view
+                class="protocol_item"
+                v-for="(item, index) in protocolList"
+                :key="index"
+                @click="openAgent(item)"
+              >
+                <trans :_t="item.text" />
+              </view>
+            </view>
+            <view class="agreed_protocol">
+              <view
+                class="confirm_box"
+                :class="lang == 'zh' ? 'zh_protocol' : ''"
+              >
+                <Tip title="请阅读并同意" :show="tipShow">
+                  <up-checkbox
+                    activeColor="var(--black)"
+                    labelSize="14"
+                    labelColor="#676969"
+                    iconSize="16"
+                    name="agree"
+                    usedAlone
+                    v-model:checked="selectAllChecked"
+                    class="right-check"
+                  >
+                    <template #label>
+                      <trans class="text" _t="我已阅读并同意" />
+                    </template>
+                  </up-checkbox>
+                </Tip>
+              </view>
+            </view>
+          </view>
+          <view class="footer_confirm">
+            <view class="total_info">
+              <view class="price_num">
+                <text class="price_text" v-if="postAmt > 0">
+                  (
+                  <trans _t="增值费用" />:
+                  <text class="amont_num"
+                    >{{ symbol.symbol }}{{ Moneyhtml(postAmt) }}</text
+                  >)
+                </text>
+                <text>{{ symbol.symbol }}</text>
+                <text class="num">{{ Moneyhtml(totalAmount) }}</text>
+              </view>
+              <view class="price_text">
+                <trans _t="总计不含国际运费" />
+              </view>
+            </view>
+            <view class="total_btn" @click="submit">
+              <trans _t="提交" />
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+    <SimpleAgreementModal ref="agreementModal" />
+  </Theme>
+</template>
+
+<script setup>
+import Navbar from "@/components/navbar";
+import navMenu from "@/components/nav_menu";
+import { onLoad } from "@dcloudio/uni-app";
+import { ref, nextTick, computed, watch } from "vue";
+import { SELLER_CART_CONFIRM, SELLER_ORDER_SUBMIT } from "@/api";
+import { Toast, Moneyhtml } from "@/utils";
+import { useSystemStore, useTabbarStore, useShopStore } from "@/store";
+import Tip from "@/components/tooltip";
+import SimpleAgreementModal from "@/components/simpleAgreementModal";
+import addressModel from "@/pages/address/components/addressModel";
+import { t } from "@/locale";
+
+const useSystem = useSystemStore();
+const useTabbar = useTabbarStore();
+const useShop = useShopStore();
+const symbol = computed(() => useSystem.getSymbol);
+const lang = computed(() => useSystem.getLang);
+const logistics = computed(() => useShop.getLogistics);
+const confirmId = ref("");
+const totalAmount = ref(0);
+const selectAllChecked = ref(false);
+const selectIsChecked = ref(false);
+const orderList = ref([]);
+const couponId = ref("");
+const isPage = ref("");
+const tipShow = ref(false);
+const qualityAmount = ref(0);
+const agreementModal = ref(null);
+const couponList = ref([]);
+const checkDate = ref(null);
+const postAmt = ref(0);
+
+const selectItem = ref(null);
+
+const valueAddedModel = ref(null);
+watch(selectAllChecked, (newVal) => {
+  if (newVal) tipShow.value = false;
+});
+watch(selectIsChecked, (newVal) => {
+  buyCart();
+});
+
+const protocolList = [
+  {
+    text: "违禁物品说明",
+    type: "violation",
+  },
+  {
+    text: "《服务条款》",
+    type: "agree",
+  },
+  {
+    text: "退货说明",
+    type: "retreat",
+  },
+  {
+    text: "免责声明",
+    type: "disclaimer",
+  },
+];
+
+const addressConfirm = (item) => {
+  selectItem.value = item;
+};
+const openAgent = (item) => {
+  agreementModal.value && agreementModal.value.open(item.type);
+};
+
+const collapseValue = computed(() => {
+  let arr = orderList.value.reduce((acc, item) => {
+    acc.push(item.seller_id);
+    return acc;
+  }, []);
+  return arr;
+});
+
+const calculateAll = (arr) => {
+  let totalQuantity = arr.reduce((sum, item) => sum + item.total, 0);
+  return totalQuantity;
+};
+
+const leftClick = () => {
+  uni.navigateBack();
+  // if (isPage.value == "order") {
+  //   uni.navigateBack();
+  // } else {
+  //   uni.navigateTo({ url: "/pages/index/products?channel=1" });
+  // }
+};
+
+const issubnum = ref(0);
+const submit = () => {
+  if (issubnum.value == 0) {
+    if (!selectItem.value.id) return Toast(t("请先添加收件地址"));
+    if (!selectAllChecked.value) return (tipShow.value = true);
+    shopConfirm();
+  }
+};
+const getCoupon = async (money) => {
+  try {
+    const res = await SHOP_GET_SHOPCOUPON(money);
+    couponList.value = res.data || [];
+  } catch (error) {}
+};
+const changeId = (id, data) => {
+  if (id) {
+    couponId.value = id;
+    checkDate.value = data;
+    buyCart();
+  } else {
+    couponId.value = "";
+    checkDate.value = null;
+  }
+};
+
+onLoad((options) => {
+  confirmId.value = options.comfirmId;
+  isPage.value = options.page || "";
+  nextTick(() => {
+    buyCart();
+  });
+});
+
+const shopConfirm = async () => {
+  try {
+    const res = await SELLER_ORDER_SUBMIT({
+      cartids: confirmId.value,
+      address_id: selectItem.value?.id,
+    });
+    uni.navigateTo({
+      url: `/pages/shop/payment?oid=${res.data.sid}&type=sellerorder`,
+    });
+  } catch (error) {
+    Toast(error.msg);
+  }
+};
+
+const buyCart = async () => {
+  try {
+    const res = await SELLER_CART_CONFIRM({
+      cartids: confirmId.value,
+    });
+    orderList.value = res.data.cartList || [];
+    totalAmount.value = res.data.totalAmount || 0;
+    postAmt.value = res.data.postAmt || 0;
+  } catch (error) {
+    Toast(error.msg);
+  }
+};
+</script>
+
+<style lang="less" scoped>
+@import url("@/style.less");
+
+.uni-stat-tooltip {
+  min-width: 400rpx;
+  max-height: 400rpx;
+  overflow: scroll;
+  word-break: break-word;
+}
+
+.wrap {
+  max-height: 100vh;
+
+  .confirm_box {
+    width: 100%;
+    .hor(end);
+
+    .confirm_item {
+      .ver();
+    }
+  }
+
+  .zh_protocol {
+    // .flex_position(flex-end);
+
+    // .right-check {
+    //   margin-left: 30rpx;
+    // }
+  }
+
+  background-color: var(--bg);
+  .flex();
+  flex-direction: column;
+  overflow: hidden;
+
+  .content {
+    flex-grow: 1;
+    height: calc(100vh - 44px);
+    overflow: hidden scroll;
+    .flex();
+    flex-direction: column;
+
+    .cont {
+      padding: 0 24rpx 48rpx;
+      flex-grow: 1;
+      overflow: hidden scroll;
+
+      .card_wrapper {
+        padding: 16rpx 0;
+        background-color: var(--light);
+        margin-top: 24rpx;
+        border-radius: 16rpx;
+
+        .card_header {
+          .ver();
+          padding: 0 32rpx;
+
+          .header_img {
+            width: 48rpx;
+            height: 48rpx;
+
+            .img {
+              width: inherit;
+              height: inherit;
+            }
+          }
+
+          .header_name {
+            margin-left: 16rpx;
+            font-weight: 700;
+            .size(28rpx);
+            color: var(--text);
+          }
+        }
+
+        .card_item {
+          &_wrapper {
+            padding: 0 32rpx;
+            padding-top: 16rpx;
+
+            .item_top {
+              .flex();
+              column-gap: 16rpx;
+              padding-bottom: 16rpx;
+
+              .item_left {
+                width: 128rpx;
+                height: 128rpx;
+
+                .item_img {
+                  width: inherit;
+                  height: inherit;
+                  border-radius: 16rpx;
+                }
+              }
+
+              .item_middle {
+                flex: 1;
+
+                .title {
+                  color: var(--text);
+                  .size(24rpx);
+                  line-height: 48rpx;
+                  font-weight: 700;
+                  .ellipsis(2);
+                }
+
+                .desc {
+                  color: var(--text-01);
+                  .size(24rpx);
+                  font-weight: 400;
+                  line-height: 40rpx;
+                  margin-top: 8rpx;
+                }
+              }
+
+              .item_right {
+                flex: 0 0 180rpx;
+                text-align: right;
+
+                .price {
+                  .size(24rpx);
+                  color: var(--red);
+                  font-weight: 700;
+                  line-height: 60rpx;
+                }
+
+                .total_num {
+                  color: #7d8fb3;
+                  .size(24rpx);
+                  font-weight: 400;
+                  line-height: 40rpx;
+                }
+              }
+            }
+          }
+
+          .card_footer {
+            border-top: 1px solid var(--bg);
+
+            /deep/ .u-collapse {
+              .u-cell--clickable {
+                background-color: transparent;
+              }
+
+              .u-cell__body {
+                padding: 0 40rpx;
+                height: 48rpx;
+                margin-top: 4rpx;
+
+                .u-cell__left-icon-wrap {
+                  margin-right: 0;
+                }
+              }
+
+              // .u-collapse-item__content{
+              //   padding-top: 24rpx;
+              // }
+              .u-collapse-item__content__text {
+                padding: 0 40rpx;
+              }
+
+              .uicon-arrow-right {
+                color: var(--black) !important;
+                .size() !important;
+                font-weight: 700 !important;
+              }
+            }
+
+            .collapse_title {
+              .size(24rpx);
+              color: var(--text);
+              font-weight: 500;
+            }
+
+            .collapse_value {
+              color: var(--red);
+              font-weight: 700;
+              .size();
+
+              .item_num {
+                display: inline-block;
+                margin-left: 8rpx;
+              }
+            }
+
+            .card_footer_item {
+              color: var(--text-01);
+              .size(24rpx);
+              height: 40rpx;
+              .flex_position(space-between);
+              box-sizing: content-box;
+
+              .item_value {
+                &.item_red {
+                  color: var(--red);
+                }
+
+                .item_num {
+                  // .blocked();
+                  display: inline-block;
+                  margin-left: 8rpx;
+                }
+              }
+            }
+          }
+        }
+      }
+
+      .friendly_reminder {
+        padding: 16rpx 24rpx 24rpx;
+        background-color: var(--light);
+        margin-top: 20rpx;
+        border-radius: 16rpx;
+        color: #346;
+        .size(24rpx);
+        min-height: 260rpx;
+
+        .reminder_title {
+          font-weight: 700;
+          color: var(--text);
+          .size(28rpx);
+          line-height: 60rpx;
+        }
+      }
+
+      .add_on_box {
+        width: 100%;
+        padding: 16rpx 24rpx;
+        background-color: var(--bg-primary);
+        border-radius: 16rpx;
+
+        .title {
+          color: var(--text);
+          .size(28rpx);
+          font-weight: 700;
+          line-height: 60rpx;
+          .title-tips {
+            margin: 0 12rpx;
+            padding: 2rpx 6rpx;
+            color: #fff;
+            .size(24rpx);
+            background-color: red;
+            border-radius: 8rpx;
+          }
+        }
+
+        .add_on_content {
+          .hor(space-between);
+          flex-wrap: wrap;
+
+          .checkbox-row {
+            .ver();
+          }
+
+          .question-icon {
+            margin-left: 8rpx;
+            font-size: 32rpx;
+          }
+        }
+      }
+    }
+
+    .footer {
+      padding-bottom: env(safe-area-inset-bottom);
+      padding-bottom: constant(safe-area-inset-bottom);
+      box-sizing: border-box;
+      &_protocol {
+        background-color: #fff3ea;
+        padding: 16rpx 24rpx;
+
+        .protocol {
+          .flex_position(flex-end);
+          flex-wrap: wrap;
+
+          &_item {
+            text-wrap: nowrap;
+            color: var(--black);
+            .size(24rpx);
+            margin-left: 8rpx;
+          }
+        }
+
+        .agreed_protocol {
+          // .flex_position(flex-end);
+          // height: 60rpx;
+
+          .text {
+            .size(28rpx);
+            color: var(--black);
+          }
+        }
+      }
+
+      &_confirm {
+        background-color: var(--light);
+        border-top: var(--bor);
+        height: 120rpx;
+        .flex_position(flex-end);
+        padding: 0 24rpx;
+
+        .total_info {
+          .ver(flex-end);
+          flex-direction: column;
+
+          .price_num {
+            color: var(--red);
+            .size();
+            font-weight: 700;
+            line-height: 48rpx;
+
+            .num {
+              display: inline-block;
+              margin-left: 8rpx;
+            }
+
+            .icon-question2 {
+              color: var(--text-01);
+              font-weight: 400;
+              .size(40rpx);
+              margin-left: 8rpx;
+            }
+          }
+
+          .price_text {
+            color: var(--text-01);
+            .size(24rpx);
+            line-height: 40rpx;
+
+            .amont_num {
+              color: var(--red);
+            }
+          }
+        }
+
+        .total_btn {
+          background-color: var(--black);
+          color: var(--light);
+          height: 76rpx;
+          .size(28rpx);
+          font-weight: 700;
+          min-width: 180rpx;
+          margin-left: 24rpx;
+          border-radius: 16rpx;
+          .flex_center();
+          padding: 16rpx 30rpx;
+        }
+      }
+    }
+  }
+}
+</style>

+ 147 - 0
pagesBuyer/store/components/product_item.vue

@@ -0,0 +1,147 @@
+<template>
+  <view class="product-item" @click="handleClick">
+    <view class="product-image">
+      <image :src="item.image" mode="aspectFill" />
+      <!-- 视频播放图标 -->
+      <view v-if="item.isVideo" class="play-icon">
+        <i class="icon-font icon-play"></i>
+      </view>
+      <!-- 收藏图标 -->
+      <view class="favorite-icon" @click.stop="toggleFavorite">
+        <i class="icon-font icon-star" :class="{ active: item.isFavorite }"></i>
+      </view>
+    </view>
+    <view class="product-info">
+      <view class="product-title">{{ item.title }}</view>
+      <view class="product-sales">{{ item.sales }}</view>
+      <view class="product-price">
+        <text class="current-price">¥{{ item.currentPrice }}</text>
+        <text class="original-price">¥{{ item.originalPrice }}</text>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script setup>
+import { defineProps, defineEmits } from "vue";
+
+const props = defineProps({
+  item: {
+    type: Object,
+    required: true,
+  },
+});
+
+const emit = defineEmits(["click", "favorite"]);
+
+const handleClick = () => {
+  emit("click", props.item);
+};
+
+const toggleFavorite = () => {
+  emit("favorite", props.item);
+};
+</script>
+
+<style lang="less" scoped>
+@import url("@/style.less");
+
+.product-item {
+  background: var(--light);
+  border-radius: 16rpx;
+  overflow: hidden;
+  margin-bottom: 20rpx;
+  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+
+  .product-image {
+    width: 100%;
+    height: 300rpx;
+    position: relative;
+
+    image {
+      width: 100%;
+      height: 100%;
+    }
+
+    .play-icon {
+      position: absolute;
+      top: 20rpx;
+      left: 20rpx;
+      width: 60rpx;
+      height: 60rpx;
+      background: rgba(0, 0, 0, 0.6);
+      border-radius: 50%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      .icon-play {
+        color: white;
+        font-size: 32rpx;
+      }
+    }
+
+    .favorite-icon {
+      position: absolute;
+      bottom: 20rpx;
+      right: 20rpx;
+      width: 60rpx;
+      height: 60rpx;
+      background: rgba(255, 255, 255, 0.9);
+      border-radius: 50%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      .icon-star {
+        color: var(--text-01);
+        font-size: 32rpx;
+
+        &.active {
+          color: var(--primary);
+        }
+      }
+    }
+  }
+
+  .product-info {
+    padding: 24rpx;
+
+    .product-title {
+      font-size: 28rpx;
+      color: var(--black);
+      line-height: 1.4;
+      margin-bottom: 12rpx;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      display: -webkit-box;
+      -webkit-line-clamp: 2;
+      -webkit-box-orient: vertical;
+    }
+
+    .product-sales {
+      font-size: 24rpx;
+      color: var(--text-01);
+      margin-bottom: 12rpx;
+    }
+
+    .product-price {
+      display: flex;
+      align-items: center;
+      gap: 12rpx;
+
+      .current-price {
+        font-size: 32rpx;
+        font-weight: bold;
+        color: var(--primary);
+      }
+
+      .original-price {
+        font-size: 24rpx;
+        color: var(--text-01);
+        text-decoration: line-through;
+      }
+    }
+  }
+}
+</style>

+ 183 - 0
pagesBuyer/store/components/shop_item.vue

@@ -0,0 +1,183 @@
+<template>
+  <view class="shop-item" @click="handleClick">
+    <!-- 店铺头部信息 -->
+    <view class="shop-header">
+      <view class="shop-avatar">
+        <image :src="item.logo" mode="aspectFill" />
+      </view>
+      <view class="shop-info">
+        <view class="shop-name">{{ item.name }}</view>
+        <view class="shop-visits">
+          <image
+            class="img"
+            src="/static/seller/address.png"
+            model="fixwidth"
+          ></image>
+          <text>{{ item.distance }}km</text>
+        </view>
+      </view>
+      <view class="shop-arrow">
+        <up-rate
+          readonly
+          :count="5"
+          size="16"
+          active-color="var(--red)"
+          v-model="item.score"
+        ></up-rate>
+      </view>
+    </view>
+
+    <!-- 商品横向滚动列表 -->
+    <view class="products-scroll">
+      <up-scroll-list class="scroll-container" :indicator="false">
+        <view class="products-list">
+          <view
+            v-for="product in item.goods"
+            :key="product.id"
+            class="product-item"
+            @click.stop="handleProductClick(product)"
+          >
+            <view class="product-image">
+              <image :src="product.picurl" mode="aspectFill" />
+            </view>
+            <view class="product-info">
+              <view class="product-desc">{{ product.goodsName }}</view>
+              <view class="product-price">¥{{ product.price }}</view>
+            </view>
+          </view>
+        </view>
+      </up-scroll-list>
+    </view>
+  </view>
+</template>
+
+<script setup>
+import { defineProps, defineEmits } from "vue";
+
+const props = defineProps({
+  item: {
+    type: Object,
+    required: true,
+  },
+});
+
+const emit = defineEmits(["click", "productClick"]);
+
+const handleClick = () => {
+  emit("click", props.item);
+};
+
+const handleProductClick = (product) => {
+  emit("productClick", product);
+};
+</script>
+
+<style lang="less" scoped>
+@import url("@/style.less");
+
+.shop-item {
+  width: 100%;
+  background: var(--light);
+  margin-bottom: 24rpx;
+  padding: 14rpx 30rpx;
+  box-sizing: border-box;
+
+  .shop-header {
+    display: flex;
+    align-items: center;
+    margin-bottom: 30rpx;
+
+    .shop-avatar {
+      width: 66rpx;
+      height: 66rpx;
+      border-radius: 50%;
+      overflow: hidden;
+      margin-right: 10rpx;
+
+      image {
+        width: 100%;
+        height: 100%;
+      }
+    }
+
+    .shop-info {
+      flex: 1;
+
+      .shop-name {
+        font-size: 28rpx;
+        font-weight: bold;
+        color: var(--black);
+        margin-bottom: 8rpx;
+      }
+
+      .shop-visits {
+        display: flex;
+        font-size: 24rpx;
+        color: var(--text-01);
+        line-height: 24rpx;
+        .img {
+          width: 18rpx;
+          height: 24rpx;
+          margin-right: 8rpx;
+        }
+      }
+    }
+
+    .shop-arrow {
+      .icon-right {
+        color: var(--text-01);
+        font-size: 24rpx;
+      }
+    }
+  }
+
+  .products-scroll {
+    width: 100%;
+    overflow-x: auto;
+
+    .scroll-container {
+      display: flex;
+      flex-direction: row;
+    }
+
+    .products-list {
+      display: flex;
+      gap: 20rpx;
+    }
+
+    .product-item {
+      width: 200rpx;
+
+      .product-image {
+        width: 100%;
+        height: 180rpx;
+        position: relative;
+
+        image {
+          width: 100%;
+          height: 100%;
+          border-radius: 16rpx;
+        }
+      }
+
+      .product-info {
+        padding: 14rpx 0;
+
+        .product-price {
+          font-size: 24rpx;
+          font-weight: bold;
+          color: var(--red);
+        }
+
+        .product-desc {
+          font-size: 20rpx;
+          color: var(--text-01);
+          line-height: 1.4;
+          .ellipsis(1);
+          margin-bottom: 8rpx;
+        }
+      }
+    }
+  }
+}
+</style>

+ 365 - 0
pagesBuyer/store/detail.vue

@@ -0,0 +1,365 @@
+<template>
+  <Theme>
+    <view class="wrap">
+      <!-- 黑色导航栏 -->
+      <Navbar fixed leftIconColor="var(--light)" :bgColor="'var(--black)'">
+        <template #center>
+          <view class="nav_search">
+            <Search
+              v-model="searchValue"
+              border="none"
+              @input="onSearchInput"
+              @click="onSearchClick"
+              :placeholder="t('请输入商品链接或关键字')"
+            >
+              <template #prefix>
+                <i class="icon-font icon-search"></i>
+              </template>
+            </Search>
+          </view>
+        </template>
+      </Navbar>
+
+      <!-- 店铺信息区域 -->
+      <view class="shop-info-section">
+        <view class="shop-header">
+          <view class="shop-avatar">
+            <image :src="shopInfo.avatar" mode="aspectFill" />
+          </view>
+          <view class="shop-details">
+            <view class="shop-name">{{ shopInfo.name }}</view>
+            <view class="shop-stats">
+              <text class="followers">{{ shopInfo.followers }}粉丝</text>
+              <text class="divider">|</text>
+              <text class="rating">{{ shopInfo.rating }}%好评率</text>
+            </view>
+          </view>
+          <view class="follow-btn" @click="toggleFollow">
+            <i class="icon-font icon-plus"></i>
+            <text>{{ isFollowed ? "已关注" : "关注" }}</text>
+          </view>
+        </view>
+      </view>
+
+      <!-- 商品列表 -->
+      <view class="content">
+        <List
+          ref="listRef"
+          :defaultParams="defaultParams"
+          @datas="getList"
+          :isLoading="false"
+        >
+          <template #item="{ item }">
+            <productItem
+              :item="item"
+              @click="toProductDetail"
+              @favorite="toggleProductFavorite"
+            />
+          </template>
+        </List>
+      </view>
+    </view>
+    <Tabbar page="shop" />
+  </Theme>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from "vue";
+import Tabbar from "@/components/tabbar";
+import Navbar from "@/components/navbar";
+import Search from "@/components/input";
+import List from "@/components/list";
+import productItem from "./components/product_item";
+import { t } from "@/locale";
+import { onLoad } from "@dcloudio/uni-app";
+import { SELLER_SELLER_INFO } from "@/api";
+
+// 搜索值
+const searchValue = ref("");
+
+// List组件引用
+const listRef = ref(null);
+
+// 默认参数
+const defaultParams = reactive({
+  keyWord: "",
+  id: "",
+});
+
+// 店铺信息
+const shopInfo = ref({
+  id: 1,
+  name: "香皂旗舰店",
+  avatar: "/static/shop/shop1.png",
+  followers: 100,
+  rating: 90,
+});
+
+// 是否已关注
+const isFollowed = ref(false);
+
+// 商品数据
+const products = ref([
+  {
+    id: 1,
+    image: "/static/products/product1.jpg",
+    title: "种草这24cm不粘锅....",
+    sales: "已售3000+",
+    currentPrice: "80.20",
+    originalPrice: "1380.00",
+    isVideo: true,
+    isFavorite: false,
+  },
+  {
+    id: 2,
+    image: "/static/products/product2.jpg",
+    title: "#时尚锋芒计划",
+    sales: "已售30万+",
+    currentPrice: "210.99",
+    originalPrice: "1380.00",
+    isVideo: true,
+    isFavorite: false,
+  },
+  {
+    id: 3,
+    image: "/static/products/product3.jpg",
+    title: "种草这24cm不粘锅...",
+    sales: "已售200+",
+    currentPrice: "2100.00",
+    originalPrice: "2380.00",
+    isVideo: false,
+    isFavorite: false,
+  },
+  {
+    id: 4,
+    image: "/static/products/product4.jpg",
+    title: "种草这24cm不粘锅...",
+    sales: "已售300+",
+    currentPrice: "10.60",
+    originalPrice: "13.00",
+    isVideo: false,
+    isFavorite: false,
+  },
+  {
+    id: 5,
+    image: "/static/products/product5.jpg",
+    title: "种草这24cm不粘锅...",
+    sales: "已售30",
+    currentPrice: "2100.00",
+    originalPrice: "2380.00",
+    isVideo: false,
+    isFavorite: false,
+  },
+  {
+    id: 6,
+    image: "/static/products/product6.jpg",
+    title: "种草这24cm不粘锅...",
+    sales: "已售100+",
+    currentPrice: "10.60",
+    originalPrice: "13.00",
+    isVideo: false,
+    isFavorite: false,
+  },
+]);
+
+// 过滤后的商品列表
+const filteredProducts = ref([]);
+
+// 搜索输入
+const onSearchInput = (value) => {
+  searchValue.value = value;
+  defaultParams.keyWord = value;
+  // 触发列表刷新
+  if (listRef.value) {
+    listRef.value.handleRefresh();
+  }
+};
+
+// 搜索点击
+const onSearchClick = () => {
+  console.log("搜索:", searchValue.value);
+};
+
+// Tab切换
+const onTabChange = (index) => {
+  currentTab.value = index;
+  // 根据Tab切换商品分类
+  const category = tabList.value[index].name;
+  defaultParams.category = category;
+  // 刷新列表
+  if (listRef.value) {
+    listRef.value.handleRefresh();
+  }
+};
+
+// 切换关注状态
+const toggleFollow = () => {
+  isFollowed.value = !isFollowed.value;
+  console.log("关注状态:", isFollowed.value);
+};
+
+// 获取列表数据
+const getList = (data) => {
+  // 模拟搜索过滤
+  if (searchValue.value.trim()) {
+    const keyword = searchValue.value.toLowerCase();
+    filteredProducts.value = products.value.filter((product) =>
+      product.title.toLowerCase().includes(keyword)
+    );
+  } else {
+    filteredProducts.value = products.value;
+  }
+
+  // 设置到List组件
+  if (listRef.value) {
+    listRef.value.setList(filteredProducts.value);
+  }
+};
+
+// 跳转到商品详情
+const toProductDetail = (product) => {
+  uni.navigateTo({
+    url: `/pagesBuyer/product/detail?id=${product.id}`,
+  });
+};
+
+// 切换商品收藏状态
+const toggleProductFavorite = (product) => {
+  product.isFavorite = !product.isFavorite;
+  console.log("商品收藏状态:", product.isFavorite);
+};
+
+const getDetail = async () => {
+  try {
+    const res = await SELLER_SELLER_INFO(defaultParams);
+    shopInfo.value = res.data;
+    isCollect.value = res.data.isCollect ? true : false;
+  } catch (error) {
+    Toast(error.msg);
+  }
+};
+onLoad((options) => {
+  if (options.id) {
+    defaultParams.id = options.id;
+    getDetail();
+  }
+
+  // 初始化数据
+  getList();
+});
+</script>
+
+<style lang="less" scoped>
+@import url("@/style.less");
+
+.wrap {
+  min-height: 100vh;
+  background: var(--bg);
+  display: flex;
+  flex-direction: column;
+
+  .nav_search {
+    flex: 1;
+    padding: 0 40rpx 0 80rpx;
+
+    :deep(.u-input) {
+      padding: 0 26rpx !important;
+      background: var(--light);
+      backdrop-filter: blur(20rpx);
+      color: var(--text-01);
+
+      .u-input__content__field-wrapper__field {
+        color: var(--text-01);
+      }
+
+      .u-input__content__field-wrapper__field::placeholder {
+        color: rgba(255, 255, 255, 0.7);
+      }
+    }
+
+    .icon-search {
+      color: var(--text-01);
+      font-size: 38rpx;
+    }
+  }
+
+  .shop-info-section {
+    padding: 30rpx;
+    background: var(--black);
+    border-bottom: 1rpx solid var(--border);
+
+    .shop-header {
+      display: flex;
+      align-items: center;
+
+      .shop-avatar {
+        width: 100rpx;
+        height: 100rpx;
+        border-radius: 50%;
+        overflow: hidden;
+        margin-right: 20rpx;
+        background: var(--primary);
+
+        image {
+          width: 100%;
+          height: 100%;
+        }
+      }
+
+      .shop-details {
+        flex: 1;
+
+        .shop-name {
+          font-size: 32rpx;
+          font-weight: bold;
+          color: var(--black);
+          margin-bottom: 8rpx;
+        }
+
+        .shop-stats {
+          display: flex;
+          align-items: center;
+          font-size: 24rpx;
+          color: var(--text-01);
+
+          .divider {
+            margin: 0 8rpx;
+            color: var(--text-02);
+          }
+        }
+      }
+
+      .follow-btn {
+        display: flex;
+        align-items: center;
+        gap: 8rpx;
+        padding: 12rpx 24rpx;
+        background: var(--light);
+        border: 1rpx solid var(--border);
+        border-radius: 40rpx;
+        color: var(--black);
+        font-size: 24rpx;
+        font-weight: bold;
+
+        .icon-plus {
+          font-size: 20rpx;
+        }
+      }
+    }
+  }
+
+  .content {
+    flex: 1;
+    padding: 20rpx 30rpx;
+    padding-bottom: calc(90rpx + constant(safe-area-inset-bottom));
+    padding-bottom: calc(90rpx + env(safe-area-inset-bottom));
+
+    // 网格布局样式
+    :deep(.list-container) {
+      display: grid;
+      grid-template-columns: 1fr 1fr;
+      gap: 20rpx;
+    }
+  }
+}
+</style>

+ 207 - 0
pagesBuyer/store/index.vue

@@ -0,0 +1,207 @@
+<template>
+  <Theme>
+    <view class="wrap">
+      <view class="cont_bg" id="tob">
+        <Navbar fixed leftShow bgColor="transparent">
+          <template #center>
+            <view class="nav_title">
+              <trans _t="店铺街" />
+            </view>
+          </template>
+        </Navbar>
+        <view class="bg_top">
+          <view class="search">
+            <Search
+              v-model="defaultParams.keywords"
+              border="none"
+              @input="onSearchInput"
+              @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>
+
+      <!-- 店铺列表 - 使用封装的List组件 -->
+      <view class="content">
+        <List
+          ref="listRef"
+          url="/seller/seller/lists"
+          :defaultParams="defaultParams"
+          @datas="getList"
+        >
+          <template #item="{ item }">
+            <shopItem
+              :item="item"
+              @click="toShopDetail"
+              @productClick="toProductDetail"
+            />
+          </template>
+        </List>
+      </view>
+    </view>
+    <Tabbar page="shop" />
+  </Theme>
+</template>
+
+<script setup>
+import { ref, reactive, watch, onMounted } 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({
+  keywords: "",
+  longitude: "",
+  latitude: "",
+});
+
+// 过滤后的店铺列表
+const filteredShops = ref([]);
+
+// 获取地理位置
+const getLocation = () => {
+  uni.getLocation({
+    type: "gcj02",
+    success: (res) => {
+      defaultParams.longitude = res.longitude.toString();
+      defaultParams.latitude = res.latitude.toString();
+      console.log("获取位置成功:", res);
+    },
+    fail: (err) => {
+      console.log("获取位置失败:", err);
+      // 设置默认位置(北京)
+      defaultParams.longitude = "116.397128";
+      defaultParams.latitude = "39.916527";
+    },
+  });
+  listRef.value && listRef.value.handleRefresh();
+};
+
+// 搜索输入
+const onSearchInput = (value) => {
+  searchValue.value = value;
+  defaultParams.keywords = value;
+  listRef.value && listRef.value.handleRefresh();
+};
+
+// 搜索点击
+const searchClick = () => {
+  console.log("搜索:", searchValue.value);
+};
+
+// 相机点击
+const onCameraClick = () => {
+  console.log("相机搜索");
+  // 这里可以添加相机搜索功能
+};
+
+// 获取列表数据
+const getList = (data) => {
+  console.log("商品列表数据:", data);
+};
+
+// 跳转到店铺详情
+const toShopDetail = (shop) => {
+  uni.navigateTo({
+    url: `/pagesBuyer/store/detail?id=${shop.id}`,
+  });
+};
+
+// 跳转到商品详情
+const toProductDetail = (product) => {
+  uni.navigateTo({
+    url: `/pagesBuyer/product/detail?id=${product.id}`,
+  });
+};
+
+onShow(() => {
+  getLocation();
+});
+
+onMounted(() => {
+  getLocation();
+});
+</script>
+
+<style lang="less" scoped>
+@import url("@/style.less");
+
+.wrap {
+  min-height: 100vh;
+  background: var(--inputBg);
+  display: flex;
+  flex-direction: column;
+
+  .cont_bg {
+    z-index: 88;
+    background-color: var(--black);
+    .nav_title {
+      color: var(--bg);
+    }
+    .bg_top {
+      padding: 0 30rpx;
+
+      .search {
+        padding: 26rpx 0 60rpx 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 {
+    width: 100%;
+    flex: 1;
+    padding: 20rpx 0;
+    padding-bottom: calc(90rpx + constant(safe-area-inset-bottom));
+    padding-bottom: calc(90rpx + env(safe-area-inset-bottom));
+  }
+}
+</style>

+ 1 - 1
static/css/theme.less

@@ -4,7 +4,7 @@
   --avatar-size: 44px;
   --avatar-font-size: 12px;
   --decimal-font-size: 14px;
-  --primary: #f0700c;
+  --primary: #000000;
   --text: #3d3d3d;
   --text-01: #979797;
   --text-02: #767676;

BIN=BIN
static/seller/address.png


BIN=BIN
static/tabbar/index.png


BIN=BIN
static/tabbar/index_active.png


BIN=BIN
static/tabbar/shop.png


BIN=BIN
static/tabbar/shop_active.png


BIN=BIN
static/user/enter.png


+ 20 - 1
store/shop.js

@@ -1,10 +1,13 @@
 import { defineStore } from "pinia";
-import { SHOP_CART_LIST, SHOP_ORDER_STATUS, CONTENT_FLOAT, SHOP_ADDRESS_LIST } from "@/api";
+import { SHOP_CART_LIST, SHOP_ORDER_STATUS, CONTENT_FLOAT, SHOP_ADDRESS_LIST, SELLER_CART_LIsts } from "@/api";
 import { setStorage, getStorage } from '@/utils'
 
 const state = () => ({
   cartList: [],
   cartNum: 0,
+  sellerList: [],
+  sellerNum: 0,
+
   orderStatus: [],
   hotLink: [],
   addressList: [],
@@ -20,6 +23,12 @@ export const useShopStore = defineStore('shop', {
     getCartNum() {
       return this.cartNum
     },
+    getSellerList() {
+      return this.sellerList
+    },
+    getSellerNum() {
+      return this.sellerNum
+    },
     getOrderStatus() {
       return this.orderStatus
     },
@@ -50,6 +59,16 @@ export const useShopStore = defineStore('shop', {
     setCartNum(_num_) {
       this.cartNum = _num_
     },
+    async setSellerList() {
+      try {
+        const { data: { list = [], cartCount = 0 } } = await SELLER_CART_LIsts();
+        this.sellerList = list || [];
+        this.sellerNum = cartCount;
+      } catch (error) { }
+    },
+    setSellerNum(_num_) {
+      this.sellerNum = _num_
+    },
     async setOrderStatus() {
       try {
         const res = await SHOP_ORDER_STATUS();

+ 7 - 5
store/user.js

@@ -14,7 +14,7 @@ const state = () => ({
   token: getStorage(verConfig.tokenName) || '',
   statics: {},
   auth_id: '',
-  userType: getStorage('userType') || 2 // 单独存储用户类型,默认为卖家
+  is_seller: getStorage('is_seller') || 0
 })
 
 export const useUserStore = defineStore('user', {
@@ -51,7 +51,7 @@ export const useUserStore = defineStore('user', {
       return this.auth_id
     },
     getUserType() {
-      return this.userType
+      return this.is_seller
     }
   },
   actions: {
@@ -64,6 +64,7 @@ export const useUserStore = defineStore('user', {
             this.token = res.data.token;
             setStorage(verConfig.tokenName, res.data.token);
             setStorage(verConfig.infoName, res.data);
+            setStorage('is_seller', res.data.is_seller);
             this.setAuth({ id: res.data.id, socket_id: useSocket.socketId });
             const useTabbar = useTabbarStore();
             Toast(res.msg, 1000).then(() => {
@@ -107,6 +108,7 @@ export const useUserStore = defineStore('user', {
           this.token = res.data.token;
           setStorage(verConfig.tokenName, res.data.token);
           setStorage(verConfig.infoName, res.data);
+          setStorage('is_seller', res.data.is_seller);
         })
       } catch (error) { }
     },
@@ -176,10 +178,10 @@ export const useUserStore = defineStore('user', {
     setInitId(_auth_id) {
       this.auth_id = _auth_id
     },
-    // 设置用户类型:1为卖家,2为买家
+    // 设置用户类型:1为卖家,0为买家
     setUserType(userType) {
-      this.userType = userType;
-      setStorage('userType', userType);
+      this.is_seller = userType;
+      setStorage('is_seller', userType);
     }
   }
 })

+ 19 - 0
utils/tool.js

@@ -414,3 +414,22 @@ export const openUrlBlank = (url) => {
   plus.runtime.openURL(url)
   // #endif
 };
+
+/**
+ * 格式化货币显示 - 根据当前语言返回对应的货币符号
+ * @param {number|string} amount 金额
+ * @param {boolean} showAmount 是否显示金额,默认true
+ * @returns {string} 格式化后的货币字符串
+ */
+export const formatCurrency = (amount, showAmount = true) => {
+  useSystem = useSystemStore();
+  const symbol = useSystem.getSymbol;
+  if (!showAmount) {
+    return symbol;
+  }
+  if (!amount && amount !== 0) {
+    return `${symbol.symbol}0.00`;
+  }
+  const formattedAmount = Number(amount).toFixed(2);
+  return `${symbol.symbol}${formattedAmount}`;
+};