From 408d93a7fed0403381400ce2fc028819bb203eec Mon Sep 17 00:00:00 2001
From: peng <peng.com>
Date: 星期一, 21 七月 2025 09:51:42 +0800
Subject: [PATCH] 解决主包过大问题

---
 pages/subComponents/m-goods-list/common.vue    |   69 +++
 pages/subComponents/m-goods-list/promotion.vue |  171 ++++++++
 pages/product/goods.vue                        |   13 
 pages/subComponents/m-goods-list/base-list.vue |  277 +++++++++++++
 pages/subComponents/m-goods-list/README.md     |   11 
 pages.json                                     |   16 
 pages/subComponents/popups/popups.vue          |  346 ++++++++++++++++
 pages/commodity-square/commoditySquare.vue     |    4 
 pages/subComponents/m-goods-list/list.vue      |  343 ++++++++++++++++
 9 files changed, 1,240 insertions(+), 10 deletions(-)

diff --git a/pages.json b/pages.json
index 06eec49..93752ba 100644
--- a/pages.json
+++ b/pages.json
@@ -1026,7 +1026,8 @@
             "componentPlaceholder": {
               "u-icon": "view",
               "u-navbar": "view",
-              "u-popup": "view"
+              "u-popup": "view",
+			  "popups":"view"
             }
           }
         },
@@ -1714,6 +1715,19 @@
 	  	{
 	  		"navigationBarTitleText" : "鍒嗗寘鍗犱綅"
 	  	}
+	  },{
+	  	"path" : "popups/popups",
+	  	"style" : 
+	  	{
+	  		"componentPlaceholder": {
+	  		  "u-modal": "view",
+	  		  "u-tabs": "view",
+	  		  "u-image": "view",
+	  		  "u-search": "view",
+	  		  "u-icon": "view",
+	  		  "u-loadmore": "view"
+	  		}
+	  	}
 	  }
 	  ]
 	},
diff --git a/pages/commodity-square/commoditySquare.vue b/pages/commodity-square/commoditySquare.vue
index 7bbfbd0..68da6c9 100644
--- a/pages/commodity-square/commoditySquare.vue
+++ b/pages/commodity-square/commoditySquare.vue
@@ -16,8 +16,8 @@
 				</scroll-view>
 
 			</view>
-			<view class="goodsInfos">
-				<scroll-view :scroll-y="true" :show-scrollbar="false">
+			<view class="goodsInfos" >
+				<scroll-view :scroll-y="true" :show-scrollbar="false" style="height: 100%;">
 
 					<view class="goodsItem" v-for="item in goodsList" :key="item.id"
 						@click.prevent="goToGoodsInfo(item.id)"
diff --git a/pages/product/goods.vue b/pages/product/goods.vue
index b905084..c315288 100644
--- a/pages/product/goods.vue
+++ b/pages/product/goods.vue
@@ -184,24 +184,24 @@
           <Evaluation id="main5" :goodsDetail="goodsDetail" />
 
           <!-- 搴楅摵鎺ㄨ崘 -->
-          <storeLayout id="main7" :storeDetail="storeDetail" :goodsDetail="goodsDetail" :res="recommendList" />
+          <!-- <storeLayout id="main7" :storeDetail="storeDetail" :goodsDetail="goodsDetail" :res="recommendList" /> -->
 
           <!-- 瀹濊礉璇︽儏 -->
           <GoodsIntro id="main9" :res="goodsDetail" :goodsParams="goodsParams" :goodsId="goodsDetail.goodsId"
             v-if="goodsDetail.id" />
 
           <!-- 瀹濊礉鎺ㄨ崘 -->
-          <GoodsRecommend id="main11" :res="likeGoodsList" />
+          <!-- <GoodsRecommend id="main11" :res="likeGoodsList" /> -->
         </view>
       </scroll-view>
 
 
       <view class="page-bottom mp-iphonex-bottom" id="pageBottom">
         <view class="icon-btn">
-          <view class="icon-btn-item" @click="navigateToStore(goodsDetail.storeId)">
+<!--          <view class="icon-btn-item" @click="navigateToStore(goodsDetail.storeId)">
             <u-icon size="34" class="red" name="home-fill"></u-icon>
             <view class="red icon-btn-name">搴楅摵</view>
-          </view>
+          </view> -->
           <view class="icon-btn-item" @click="linkMsgDetail()">
             <u-icon size="34" name="kefu-ermai"></u-icon>
             <view class="icon-btn-name">瀹㈡湇</view>
@@ -295,14 +295,14 @@
 import PromotionAssembleListLayout from "./product/promotion/-promotion-assemble-list"; //鎷煎洟鐢ㄦ埛鍒楄〃
 import PromotionCoupon from "./product/promotion/-promotion-coupon"; //浼樻儬鍒哥粍浠�
 import GoodsIntro from "./product/goods/-goods-intro"; //鍟嗗搧浠嬬粛缁勪欢
-import GoodsRecommend from "./product/goods/-goods-recommend"; //瀹濊礉鎺ㄨ崘
+// import GoodsRecommend from "./product/goods/-goods-recommend"; //瀹濊礉鎺ㄨ崘
 import storeLayout from "./product/shop/-shop"; //搴楅摵缁勪欢
 import Evaluation from "./product/evaluation/-evaluation"; //璇勪环缁勪欢
 import GoodsSwiper from "./product/goods/-goods-swiper"; //杞挱鍥剧粍浠�
 import popupGoods from "@/pages/product/m-buy/goods.vue"; //璐墿杞﹀晢鍝佺殑妯″潡
 import popupAddress from "./product/popup/address"; //鍦板潃閫夋嫨妯″潡
 import shares from "@/pages/product/m-share/index.vue"; //鍒嗕韩
-import popups from "@/pages/product/popups/popups.vue"; //姘旀场妗�
+import popups from "@/pages/subComponents/popups/popups.vue"; //姘旀场妗�
 import takeDownFormSaleGoods from "@/pages/product/m-take-down-sale-goods/index"; //涓嬫灦妗�
 import setup from "./product/popup/popup";
 	import {
@@ -318,7 +318,6 @@
     PromotionAssembleListLayout,
     PromotionCoupon,
     GoodsIntro,
-    GoodsRecommend,
     storeLayout,
     Evaluation,
     GoodsSwiper,
diff --git a/pages/subComponents/m-goods-list/README.md b/pages/subComponents/m-goods-list/README.md
new file mode 100644
index 0000000..bade838
--- /dev/null
+++ b/pages/subComponents/m-goods-list/README.md
@@ -0,0 +1,11 @@
+## 鍟嗗搧鍒楄〃灞曠ず
+
+### OBJECT 鍙傛暟璇存槑
+
+| 灞炴��        | 璇存槑                                                       | 绫诲瀷    | 蹇呭~ |
+| ----------- | ---------------------------------------------------------- | ------- | ---- |
+| `res`       | 鏄剧ず鏁版嵁                                                   | Array   | 鏄�   |
+| `type`      | 鍟嗗搧灞曠ず绫诲瀷 oneColumns twoColumns  锛岄粯璁ゅ睍绀轰竴琛屼袱鍒楀晢鍝� | String  | 鍚�   |
+| `storeName` | 鏄惁灞曠ず搴楅摵鍚嶇О锛岄粯璁ゅ睍绀�                                 | Boolean | 鍚�   |
+| `keywords` | 楂樹寒灞曠ず鎼滅储鍐呭                                 | String | 鍚�   |
+
diff --git a/pages/subComponents/m-goods-list/base-list.vue b/pages/subComponents/m-goods-list/base-list.vue
new file mode 100644
index 0000000..62d57a4
--- /dev/null
+++ b/pages/subComponents/m-goods-list/base-list.vue
@@ -0,0 +1,277 @@
+<template>
+  <div>
+    <!-- 涓�琛屼袱鍒楀晢鍝佸睍绀� -->
+    <view class="goods-list" v-if="type == 'twoColumns'">
+      <view v-for="(item, index) in res" :key="index" class="goods-item">
+        <view class="image-wrapper" @click="navigateToDetailPage(item)">
+          <u-image
+            :src="item.thumbnail"
+            width="100%"
+            height="330rpx"
+           mode="aspectFit"
+          >
+            <u-loading slot="loading"></u-loading>
+          </u-image>
+        </view>
+        <view class="goods-detail">
+          <div
+            class="title clamp"
+            v-html="lightSearchStr(keyword, item.goodsName)"
+            @click="navigateToDetailPage(item)"
+          ></div>
+          <view class="price-box" @click="navigateToDetailPage(item)">
+            <div class="price" v-if="item.price != undefined">
+              楼<span
+                >{{
+                  $options.filters.goodsFormatPrice(item.price)[0]
+                }} </span
+              >.{{ $options.filters.goodsFormatPrice(item.price)[1] }}
+            </div>
+          </view>
+          <div class="count-config" @click="navigateToDetailPage(item)">
+            <span>宸插敭 {{ item.buyCount || "0" }}</span>
+            <span>{{ item.commentNum || "0" }}鏉¤瘎璁�</span>
+          </div>
+          <div
+            class="store-seller-name"
+            v-if="storeName"
+            @click="navigateToStoreDetailPage(item)"
+          >
+            <div class="text-hidden">
+              <u-tag
+                style="margin-right: 10rpx"
+                size="mini"
+                mode="dark"
+                v-if="item.selfOperated"
+                text="鑷惀"
+                type="error"
+              />
+              <span>{{ item.storeName || "鏆傛棤" }}</span>
+            </div>
+            <span>
+              <u-icon name="arrow-right"></u-icon>
+            </span>
+          </div>
+        </view>
+      </view>
+    </view>
+	 <!-- 涓�琛屼竴鍒楀晢鍝佸睍绀� -->
+    <div v-if="type == 'oneColumns'">
+      <div  v-for="(item, index) in res" :key="index" class="goods-row">
+        <div class="flex goods-col">
+          <div class="goods-img" @click="navigateToDetailPage(item)">
+            <u-image
+              width="230rpx"
+              border-radius="16"
+              height="230rpx"
+							mode="aspectFit"
+              :src="item.goodsImage || item.thumbnail"
+            >
+              <u-loading slot="loading"></u-loading>
+            </u-image>
+          </div>
+          <div class="goods-detail">
+            <div class="title clamp3" @click="navigateToDetailPage(item)">
+              {{ item.goodsName }}
+            </div>
+            <view class="price-box" @click="navigateToDetailPage(item)">
+              <div class="price" v-if="item.price != undefined">
+                楼<span
+                  >{{ $options.filters.goodsFormatPrice(item.price)[0] }} </span
+                >.{{ $options.filters.goodsFormatPrice(item.price)[1] }}
+              </div>
+            </view>
+            <div class="promotion" @click="navigateToDetailPage(item)">
+              <div v-if="item.salesModel == 'WHOLESALE'">
+                <span>鎵�</span>
+              </div>
+              <div
+                v-for="(promotionItem, promotionIndex) in getPromotion(item)"
+                :key="promotionIndex"
+              >
+                <span v-if="promotionItem.indexOf('COUPON') != -1">鍔�</span>
+                <span v-if="promotionItem.indexOf('FULL_DISCOUNT') != -1"
+                  >婊″噺</span
+                >
+                <span v-if="promotionItem.indexOf('SECKILL') != -1">绉掓潃</span>
+              </div>
+            </div>
+            <div
+              style="overflow: hidden"
+              @click="navigateToDetailPage(item)"
+              class="count-config"
+            >
+              <span style="float: left; font-size: 22rpx"
+                >宸插敭 {{ item.buyCount || "0" }}</span
+              >
+              <span style="float: right; font-size: 22rpx"
+                >{{ item.commentNum || "0" }}鏉¤瘎璁�</span
+              >
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import commonTpl from "@/pages/product/m-goods-list/common.vue";
+export default {
+  data() {
+    return {
+      lightColor: this.$mainColor,
+    };
+  },
+  mixins: [commonTpl],
+  props: {
+	// 灞曠ず鐨勭被鍨�
+	type:{
+		type:String,
+		default:"oneColumns"
+	},
+    // 閬嶅巻鐨勬暟鎹�
+    res: {
+      type: Array,
+      default: () => {
+        return [];
+      },
+    },
+  },
+  methods: {
+    // 璺宠浆鍒板晢鍝佽鎯�
+    navigateToDetailPage(item) {
+      uni.navigateTo({
+        url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
+      });
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.goods-list {
+  display: flex;
+  flex-wrap: wrap;
+  margin: 10rpx 20rpx 284rpx;
+  width: 100%;
+
+  > .goods-item {
+    background-color: #ffffff;
+    display: flex;
+    border-radius: 16rpx;
+    flex-direction: column;
+    width: calc(50% - 30rpx);
+    margin-bottom: 20rpx;
+    padding-bottom: 20rpx;
+
+    &:nth-child(2n + 1) {
+      margin-right: 20rpx;
+    }
+
+    .image-wrapper {
+      width: 100%;
+      height: 330rpx;
+      border-radius: 16rpx 16rpx 0 0;
+      overflow: hidden;
+      padding: 0;
+    }
+  }
+
+  .count-config,
+  .store-seller-name {
+    font-size: $font-sm;
+  }
+
+  .text-hidden {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+}
+
+.goods-row {
+  background: #fff;
+  padding: 16rpx;
+
+  > .goods-col {
+    display: flex;
+
+    > .goods-img {
+      overflow: hidden;
+      flex: 4;
+    }
+
+    > .goods-detail {
+      flex: 7;
+    }
+  }
+}
+
+.goods-detail {
+  margin: 0 20rpx;
+
+  > .title {
+    font-size: $font-base;
+    color: $font-color-dark;
+    line-height: 1.5;
+    height: 86rpx;
+    padding: 10rpx 0 0;
+    display: -webkit-box;
+    -webkit-box-orient: vertical;
+    -webkit-line-clamp: 2;
+    overflow: hidden;
+  }
+
+  .promotion {
+    margin-top: 4rpx;
+    display: flex;
+
+    div {
+      span {
+        font-size: 24rpx;
+        color: $light-color;
+        margin-right: 10rpx;
+        padding: 0 4rpx;
+        border-radius: 2rpx;
+      }
+    }
+  }
+
+  .store-seller-name {
+    color: #666;
+    overflow: hidden;
+    display: flex;
+    justify-content: space-between;
+  }
+
+  .count-config {
+    padding: 5rpx 0;
+    color: #666;
+    display: flex;
+    font-size: 24rpx;
+    justify-content: space-between;
+  }
+
+  > .price-box {
+    margin-top: 10rpx;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding-right: 10rpx;
+    font-size: 24rpx;
+    color: $font-color-light;
+
+    > .price {
+      font-size: 26rpx;
+      line-height: 1;
+      color: $main-color;
+      font-weight: bold;
+
+      /deep/ span:nth-of-type(1) {
+        font-size: 38rpx;
+      }
+    }
+  }
+}
+</style>
diff --git a/pages/subComponents/m-goods-list/common.vue b/pages/subComponents/m-goods-list/common.vue
new file mode 100644
index 0000000..c4cdc56
--- /dev/null
+++ b/pages/subComponents/m-goods-list/common.vue
@@ -0,0 +1,69 @@
+<template>
+</template>
+
+<script>
+	export default {
+		methods: {
+			// 楂樹寒鏄剧ず鎼滅储鍐呭	
+			lightSearchStr(keyword, str) {
+				if (!keyword) {
+					return str
+				} else {
+					let unicodes = '';
+					for (let i of Array.from(keyword)) {
+						unicodes += this.unicode(i) + "|"
+					}
+					const rule = '(' + unicodes + ')'
+					const reg = new RegExp(rule, 'gi');
+					return str ? str.replace(reg, matchValue =>
+						`<span style="color:${this.lightColor}">${matchValue}</span>`
+					) : ''
+				}
+			},
+			//  杞崲涓簎nicode
+			unicode(str) {
+				var value = '';
+				for (var i = 0; i < str.length; i++) {
+					value += '\\u' + this.left_zero_4(parseInt(str.charCodeAt(i)).toString(16));
+				}
+				return value;
+			},
+			left_zero_4(str) {
+				if (str != null && str != '' && str != 'undefined') {
+					if (str.length == 2) {
+						return '00' + str;
+					}
+				}
+				return str;
+			},
+			// 鏁版嵁鍘婚噸涓�涓� 鍙樉绀轰竴娆� 鍑忓厤 鍔� 浠�涔堢殑
+			getPromotion(item) {
+				if (item.promotionMap) {
+					let array = [];
+					Object.keys(item.promotionMap).forEach((child) => {
+						if (!array.includes(child.split("-")[0])) {
+							array.push(child.split("-")[0]);
+						}
+					});
+					return array;
+				}
+			},
+			// 璺宠浆鍒板晢鍝佽鎯�
+			navigateToDetailPage(item) {
+				uni.navigateTo({
+					url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
+				});
+			},
+			// 璺宠浆鍦板潃
+			navigateToStoreDetailPage(item) {
+				uni.navigateTo({
+					url: `/pages/product/shopPage?id=${item.storeId}`,
+				});
+			},
+		},
+	}
+</script>
+
+<style lang='scss' scoped>
+	
+</style>
diff --git a/pages/subComponents/m-goods-list/list.vue b/pages/subComponents/m-goods-list/list.vue
new file mode 100644
index 0000000..fa54ff5
--- /dev/null
+++ b/pages/subComponents/m-goods-list/list.vue
@@ -0,0 +1,343 @@
+<template>
+	<view>
+		<!-- 涓�琛屼袱鍒楀晢鍝佸睍绀� -->
+		<view class="goods-list" v-if="type == 'twoColumns'">
+			<view v-for="(item, index) in res" :key="index" class="goods-item">
+				<view class="image-wrapper" @click="navigateToDetailPage(item)">
+					<u-image :src="item.thumbnail" width="100%" height='330rpx' mode="aspectFit">
+						<u-loading slot="loading"></u-loading>
+					</u-image>
+				</view>
+				<view class="goods-detail">
+					<div class="title clamp" v-html="lightSearchStr(keyword,item.goodsName)"
+						@click="navigateToDetailPage(item)">
+
+					</div>
+					<view class="price-box" @click="navigateToDetailPage(item)">
+						<div class="price" v-if="item.price!=undefined">
+							楼<span>{{ $options.filters.goodsFormatPrice(item.price )[0] }} </span>.{{
+	            $options.filters.goodsFormatPrice(item.price )[1]
+	          }}
+						</div>
+					</view>
+					<div class="promotion" @click="navigateToDetailPage(item)">
+						<div v-if="item.salesModel == 'WHOLESALE'">
+							<span>鎵�</span>
+						</div>
+						<div v-for="(promotionItem,promotionIndex) in  getPromotion(item)" :key="promotionIndex">
+							<span v-if="promotionItem.indexOf('COUPON') != -1">鍔�</span>
+							<span v-if="promotionItem.indexOf('FULL_DISCOUNT') != -1">婊″噺</span>
+							<span v-if="promotionItem.indexOf('SECKILL') != -1">绉掓潃</span>
+						</div>
+					</div>
+					<div class="count-config" @click="navigateToDetailPage(item)">
+						<span>宸插敭 {{ item.buyCount || "0" }}</span>
+						<span>{{ item.commentNum || "0" }}鏉¤瘎璁�</span>
+					</div>
+					<div class="store-seller-name" v-if="storeName" @click="navigateToStoreDetailPage(item)">
+						<div class="text-hidden">
+							<u-tag style="margin-right: 10rpx" size="mini" mode="dark" v-if="item.selfOperated"
+								text="鑷惀" type="error" />
+							<span>{{ item.storeName || "鏆傛棤" }}</span>
+						</div>
+						<span>
+							<u-icon name="arrow-right"></u-icon>
+						</span>
+					</div>
+				</view>
+			</view>
+		</view>
+		<!-- 涓�琛屼竴鍒楀晢鍝佸睍绀� -->
+		<div v-if="type == 'oneColumns'"  class="goods-one-row">
+			<div v-for="(item, index) in res" :key="index" class="goods-row">
+				<div class="flex goods-col">
+					<div class="goods-img" @click="navigateToDetailPage(item)">
+						<u-image width="230rpx" mode="aspectFit" border-radius='16' height="230rpx" :src="item.thumbnail">
+							<u-loading slot="loading"></u-loading>
+						</u-image>
+					</div>
+					<div class="goods-detail">
+						<div class="title clamp3" @click="navigateToDetailPage(item)">{{ item.goodsName }}</div>
+						<view class="price-box" @click="navigateToDetailPage(item)">
+							<div class="price" v-if="item.price!=undefined">
+								楼<span>{{ $options.filters.goodsFormatPrice(item.price )[0] }} </span>.{{
+			            $options.filters.goodsFormatPrice(item.price )[1]
+			          }}
+							</div>
+						</view>
+						<div class="promotion" @click="navigateToDetailPage(item)">
+							<div v-if="item.salesModel == 'WHOLESALE'">
+								<span>鎵�</span>
+							</div>
+							<div v-for="(promotionItem,promotionIndex) in  getPromotion(item)" :key="promotionIndex">
+								<span v-if="promotionItem.indexOf('COUPON') != -1">鍔�</span>
+								<span v-if="promotionItem.indexOf('FULL_DISCOUNT') != -1">婊″噺</span>
+								<span v-if="promotionItem.indexOf('SECKILL') != -1">绉掓潃</span>
+							</div>
+						</div>
+						<div style="overflow: hidden" @click="navigateToDetailPage(item)" class="count-config">
+							<span style="float: left; font-size: 22rpx">宸插敭 {{ item.buyCount || '0' }}</span>
+							<span style="float: right; font-size: 22rpx">{{ item.commentNum || '0' }}鏉¤瘎璁�</span>
+						</div>
+						<div style="overflow: hidden" @click="navigateToStoreDetailPage(item)" class="count-config">
+							<div class="text-hidden" v-if="storeName">
+								<u-tag style="margin-right: 10rpx" size="mini" mode="dark" v-if="item.selfOperated"
+									text="鑷惀" type="error" />
+								<span class="line1-store-name">{{ item.storeName }}</span>
+								<span class="to-store">杩涘簵<u-icon size="24" name="arrow-right" color="#666"></u-icon>
+								</span>
+							</div>
+							<span>
+								<u-icon name="arrow-right" color="#c5c5c5"></u-icon>
+							</span>
+						</div>
+					</div>
+				</div>
+
+			</div>
+		</div>
+
+	</view>
+</template>
+
+<script>
+	import '@/components/uview-components/uview-ui';
+	
+	import commonTpl from '@/pages/product/m-goods-list/common.vue'
+	export default {
+		data() {
+			return {
+				lightColor: this.$mainColor
+			}
+		},
+		mixins: [commonTpl],
+		
+		props: {
+			// 閬嶅巻鐨勬暟鎹�
+			res: {
+				type: Array,
+				default: () => {
+					return []
+				}
+			},
+			// 涓�琛屼袱鍒楄繕鏄竴琛屼竴鍒楁樉绀�
+			type: {
+				type: String,
+				default: 'twoColumns',
+				validator() {
+					return ['twoColumns', 'oneColumns']
+				}
+			},
+			storeName: {
+				type: Boolean,
+				default: true
+			},
+			keyword: {
+				type: null,
+				default: ''
+			}
+
+		},
+		watch: {
+			keyword(val) {
+				if (val) {
+					this.lightSearchStr(val)
+				}
+			}
+		},
+		methods: {
+
+			// 楂樹寒鏄剧ず鎼滅储鍐呭	
+			lightSearchStr(keyword, str) {
+				if (!keyword) {
+					return str
+				} else {
+					let unicodes = '';
+					for (let i of Array.from(keyword)) {
+						unicodes += this.unicode(i) + "|"
+					}
+					const rule = '(' + unicodes + ')'
+					const reg = new RegExp(rule, 'gi');
+					return str ? str.replace(reg, matchValue =>
+						`<span style="color:${this.lightColor}">${matchValue}</span>`
+					) : ''
+				}
+			},
+			//  杞崲涓簎nicode
+			unicode(str) {
+				var value = '';
+				for (var i = 0; i < str.length; i++) {
+					value += '\\u' + this.left_zero_4(parseInt(str.charCodeAt(i)).toString(16));
+				}
+				return value;
+			},
+			left_zero_4(str) {
+				if (str != null && str != '' && str != 'undefined') {
+					if (str.length == 2) {
+						return '00' + str;
+					}
+				}
+				return str;
+			},
+			// 鏁版嵁鍘婚噸涓�涓� 鍙樉绀轰竴娆� 鍑忓厤 鍔� 浠�涔堢殑
+			getPromotion(item) {
+				if (item ? item.promotionMap : item.promotionMap) {
+					const fieldList = item ? item.promotionMap : item.promotionMap
+					let array = [];
+					Object.keys(fieldList).forEach((child) => {
+						if (!array.includes(child.split("-")[0])) {
+							array.push(child.split("-")[0]);
+						}
+					});
+					return array;
+				}
+			},
+			// 璺宠浆鍒板晢鍝佽鎯�
+			navigateToDetailPage(item) {
+				uni.navigateTo({
+					url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
+				});
+			},
+			// 璺宠浆鍦板潃
+			navigateToStoreDetailPage(item) {
+				uni.navigateTo({
+					url: `/pages/product/shopPage?id=${item.storeId}`,
+				});
+			},
+		}
+	}
+</script>
+
+<style lang='scss' scoped>
+		.goods-one-row{
+			padding-bottom: 250rpx;
+		}
+		/* 鍟嗗搧鍒楄〃 */
+		.goods-list {
+			display: flex;
+			flex-wrap: wrap;
+			margin: 10rpx 20rpx 284rpx;
+			width: 100%;
+	
+			>.goods-item {
+				background-color: #ffffff;
+				display: flex;
+				border-radius: 16rpx;
+				flex-direction: column;
+				width: calc(50% - 30rpx);
+				margin-bottom: 20rpx;
+				padding-bottom: 20rpx;
+	
+				&:nth-child(2n + 1) {
+					margin-right: 20rpx;
+				}
+	
+	
+				.image-wrapper {
+					width: 100%;
+					height: 330rpx;
+					border-radius: 16rpx 16rpx 0 0;
+					overflow: hidden;
+					padding: 0;
+				}
+			}
+	
+			.count-config,
+			.store-seller-name {
+				font-size: $font-sm;
+			}
+	
+			.text-hidden {
+				overflow: hidden;
+				text-overflow: ellipsis;
+				white-space: nowrap;
+			}
+		}
+	
+	
+		.goods-row {
+			background: #fff;
+			padding: 16rpx;
+	
+			>.goods-col {
+				display: flex;
+	
+				>.goods-img {
+					overflow: hidden;
+					flex: 4;
+				}
+	
+				>.goods-detail {
+					flex: 7;
+				}
+			}
+		}
+	
+		.goods-detail {
+			margin: 0 20rpx;
+	
+			>.title {
+				font-size: $font-base;
+				color: $font-color-dark;
+				line-height: 1.5;
+				height: 86rpx;
+				padding: 10rpx 0 0;
+				display: -webkit-box;
+				-webkit-box-orient: vertical;
+				-webkit-line-clamp: 2;
+				overflow: hidden;
+			}
+	
+			.promotion {
+				margin-top: 4rpx;
+				display: flex;
+	
+				div {
+					span {
+						font-size: 24rpx;
+						color: $light-color;
+						margin-right: 10rpx;
+						padding: 0 4rpx;
+						border-radius: 2rpx;
+					}
+				}
+			}
+	
+			.store-seller-name {
+				color: #666;
+				overflow: hidden;
+				display: flex;
+				justify-content: space-between;
+			}
+	
+			.count-config {
+				padding: 5rpx 0;
+				color: #666;
+				display: flex;
+				font-size: 24rpx;
+				justify-content: space-between;
+			}
+	
+			>.price-box {
+				margin-top: 10rpx;
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				padding-right: 10rpx;
+				font-size: 24rpx;
+				color: $font-color-light;
+	
+				>.price {
+					font-size: 26rpx;
+					line-height: 1;
+					color: $main-color;
+					font-weight: bold;
+	
+					/deep/ span:nth-of-type(1) {
+						font-size: 38rpx;
+					}
+				}
+			}
+	
+		}
+</style>
diff --git a/pages/subComponents/m-goods-list/promotion.vue b/pages/subComponents/m-goods-list/promotion.vue
new file mode 100644
index 0000000..90bc250
--- /dev/null
+++ b/pages/subComponents/m-goods-list/promotion.vue
@@ -0,0 +1,171 @@
+<template>
+	<div>
+		<div v-for="(item, index) in res" :key="index" class="goods-row" @click="navigateToDetailPage(item)">
+			<div class="flex goods-col">
+				<div class="goods-img">
+					<u-image width="230rpx" mode="aspectFit" border-radius='16' height="230rpx" :src="item.goodsImage || item.thumbnail">
+						<u-loading slot="loading"></u-loading>
+					</u-image>
+				</div>
+				<div class="goods-detail">
+					<div class="title clamp3">{{ item.goodsName }}</div>
+					<div class='flex flex-a-c flex-j-sb'>
+						<view class="price-box">
+							<!-- 绉掓潃 / 鎷煎洟 -->
+							<div class="price" v-if="!type && item.price!=undefined">
+								楼<span>{{ $options.filters.goodsFormatPrice(item.price )[0] }} </span>.{{
+										$options.filters.goodsFormatPrice(item.price )[1]
+									}}
+							</div>
+							<!-- 鐮嶄环 -->
+							<div class="price" v-if="type && item.purchasePrice!=undefined">
+								鏈�浣庯細
+								楼<span>{{ $options.filters.goodsFormatPrice(item.purchasePrice )[0] }} </span>.{{
+										$options.filters.goodsFormatPrice(item.purchasePrice )[1]
+									}}
+							</div>
+							<!-- 鍏滃簳绛栫暐濡傛灉閲戦鏄�0 -->
+							<div class="price" v-if="!item.price && !type">
+								楼<span>0 </span>.00
+							</div>
+						</view>
+						<div>
+							<image class='buy' :src="buy"></image>
+						</div>
+					</div>
+					<div class='count-config' v-if="!type">
+						<span>鍗冲皢鎭㈠{{ item.originalPrice}}鍏�</span>
+					</div>
+				</div>
+			</div>
+
+		</div>
+	</div>
+</template>
+
+<script>
+	import '@/components/uview-components/uview-ui'
+	import commonTpl from '@/pages/product/m-goods-list/common.vue'
+	export default {
+		data() {
+			return {
+				lightColor: this.$mainColor,
+				buy: require('@/pages/subComponents/static/buy.png')
+			}
+		},
+		mixins: [commonTpl],
+		props: {
+			// 閬嶅巻鐨勬暟鎹�
+			res: {
+				type: Array,
+				default: () => {
+					return []
+				}
+			},
+			type:{
+				type:null,
+				default:""
+			}
+		},
+		methods: {
+			// 璺宠浆鍒板晢鍝佽鎯�
+			navigateToDetailPage(item) {
+				if(this.type == 'kanJia'){
+					 uni.navigateTo({
+						url: `/pages/promotion/bargain/detail?id=${item.id}`,
+					});
+					return
+				}
+				uni.navigateTo({
+					url: `/pages/product/goods?id=${item.skuId}&goodsId=${item.goodsId}`,
+				});
+			},
+		}
+	}
+</script>
+
+<style lang='scss' scoped>
+	.buy {
+		width: 152rpx;
+		height: 108rpx;
+	}
+	.flex-j-sb {
+		width: 100%;
+	}
+	.goods-row {
+		background: #fff;
+		padding: 16rpx;
+		>.goods-col {
+			display: flex;
+			>.goods-img {
+				overflow: hidden;
+				flex: 4;
+			}
+			>.goods-detail {
+				flex: 7;
+			}
+		}
+	}
+	.goods-detail {
+		margin: 0 20rpx;
+		>.title {
+			font-size: $font-base;
+			color: $font-color-dark;
+			line-height: 1.5;
+			height: 86rpx;
+			padding: 10rpx 0 0;
+			display: -webkit-box;
+			-webkit-box-orient: vertical;
+			-webkit-line-clamp: 2;
+			overflow: hidden;
+		}
+
+		.promotion {
+			margin-top: 4rpx;
+			display: flex;
+
+			div {
+				span {
+					font-size: 24rpx;
+					color: $light-color;
+					margin-right: 10rpx;
+					padding: 0 4rpx;
+					border-radius: 2rpx;
+				}
+			}
+		}
+
+		.count-config {
+			padding: 5rpx 0;
+			color: #666;
+			display: flex;
+			font-size: 24rpx;
+			letter-spacing:2rpx;
+			padding-left: 10rpx;
+		}
+
+
+
+	}
+
+	.price-box {
+		margin-top: 10rpx;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		padding-right: 10rpx;
+		font-size: 24rpx;
+		color: $font-color-light;
+
+		>.price {
+			font-size: 26rpx;
+			line-height: 1;
+			color: $main-color;
+			font-weight: bold;
+
+			/deep/ span:nth-of-type(1) {
+				font-size: 48rpx;
+			}
+		}
+	}
+</style>
diff --git a/pages/subComponents/popups/popups.vue b/pages/subComponents/popups/popups.vue
new file mode 100644
index 0000000..ce373f9
--- /dev/null
+++ b/pages/subComponents/popups/popups.vue
@@ -0,0 +1,346 @@
+<template>
+  <view class="shadow" :class="!show?'':'shadow-show'" :style="{backgroundColor:show?maskBg:'rgba(0,0,0,0)'}" @tap="tapMask">
+    <view class="popups" :class="[theme]" :style="{top: popupsTop ,left: popupsLeft,flexDirection:direction}">
+      <text :class="dynPlace" :style="{width:'0px',height:'0px'}" v-if="triangle"></text>
+      <view v-for="(item,index) in popData" :key="index" @tap.stop="tapItem(item)" class="itemChild view" :class="[direction=='row'?'solid-right':'solid-bottom',item.disabled?'disabledColor':'']">
+        <u-icon size="35" :name="item.icon" v-if="item.icon"></u-icon><span class="title">{{item.title}}</span>
+      </view>
+      <slot></slot>
+    </view>
+  </view>
+</template>
+
+<script>
+import '@/components/uview-components/uview-ui';
+
+export default {
+
+  props: {
+    maskBg: {
+      type: String,
+      default: "rgba(0,0,0,0)",
+    },
+    placement: {
+      type: String,
+      default: "default", //default top-start top-end bottom-start bottom-end
+    },
+    direction: {
+      type: String,
+      default: "column", //column row
+    },
+    x: {
+      type: Number,
+      default: 0,
+    },
+    y: {
+      type: Number,
+      default: 0,
+    },
+    value: {
+      type: Boolean,
+      default: false,
+    },
+    popData: {
+      type: Array,
+      default: () => [],
+    },
+    theme: {
+      type: String,
+      default: "light", //light dark
+    },
+    dynamic: {
+      type: Boolean,
+      default: false,
+    },
+    gap: {
+      type: Number,
+      default: 20,
+    },
+    triangle: {
+      type: Boolean,
+      default: true,
+    },
+  },
+  data() {
+    return {
+      popupsTop: "0rpx",
+      popupsLeft: "0rpx",
+      show: false,
+      dynPlace: "",
+    };
+  },
+  mounted() {
+    this.popupsPosition();
+  },
+  methods: {
+    tapMask() {
+      this.$emit("input", !this.value);
+    },
+    tapItem(item) {
+      if (item.disabled) return;
+      this.$emit("tapPopup", item);
+      this.$emit("input", !this.value);
+    },
+    getStatusBar() {
+      let promise = new Promise((resolve, reject) => {
+        uni.getSystemInfo({
+          success: function (e) {
+            let customBar;
+            // #ifdef H5
+
+            customBar = e.statusBarHeight + e.windowTop;
+
+            // #endif
+            resolve(customBar);
+          },
+        });
+      });
+      return promise;
+    },
+    async popupsPosition() {
+      let statusBar = await this.getStatusBar();
+      let promise = new Promise((resolve, reject) => {
+        let popupsDom = uni.createSelectorQuery().in(this).select(".popups");
+        popupsDom
+          .fields(
+            {
+              size: true,
+            },
+            (data) => {
+              let width = data.width;
+              let height = data.height;
+
+             
+
+              let y = this.dynamic
+                ? this.dynamicGetY(this.y, this.gap)
+                : this.transformRpx(this.y);
+
+              let x = this.dynamic
+                ? this.dynamicGetX(this.x, this.gap)
+                : this.transformRpx(this.x);
+
+              // #ifdef H5
+              y = this.dynamic
+                ? this.y + statusBar
+                : this.transformRpx(this.y + statusBar);
+              // #endif
+
+              this.dynPlace =
+                this.placement == "default"
+                  ? this.getPlacement(x, y)
+                  : this.placement;
+
+              switch (this.dynPlace) {
+                case "top-start":
+                  this.popupsTop = `${y + 9}rpx`;
+                  this.popupsLeft = `${x - 15}rpx`;
+                  break;
+                case "top-end":
+                  this.popupsTop = `${y + 9}rpx`;
+                  this.popupsLeft = `${x + 15 - width}rpx`;
+                  break;
+                case "bottom-start":
+                  this.popupsTop = `${y - 18 - height}rpx`;
+                  this.popupsLeft = `${x - 15}rpx`;
+                  break;
+                case "bottom-end":
+                  this.popupsTop = `${y - 9 - height}rpx`;
+                  this.popupsLeft = `${x + 15 - width}rpx`;
+                  break;
+              }
+              resolve();
+            }
+          )
+          .exec();
+      });
+      return promise;
+    },
+    getPlacement(x, y) {
+      let width = uni.getSystemInfoSync().windowWidth;
+      let height = uni.getSystemInfoSync().windowHeight;
+      if (x > width / 2 && y > height / 2) {
+        return "bottom-end";
+      } else if (x < width / 2 && y < height / 2) {
+        return "top-start";
+      } else if (x > width / 2 && y < height / 2) {
+        return "top-end";
+      } else if (x < width / 2 && y > height / 2) {
+        return "bottom-start";
+      } else if (x > width / 2) {
+        return "top-end";
+      } else {
+        return "top-start";
+      }
+    },
+    dynamicGetY(y, gap) {
+      let height = uni.getSystemInfoSync().windowHeight;
+      y = y < gap ? gap : y;
+      y = height - y < gap ? height - gap : y;
+
+      return y;
+    },
+    dynamicGetX(x, gap) {
+      let width = uni.getSystemInfoSync().windowWidth;
+      x = x < gap ? gap : x;
+      x = width - x < gap ? width - gap : x;
+      return x;
+    },
+    transformRpx(params) {
+      return (params * uni.getSystemInfoSync().screenWidth) / 375;
+    },
+  },
+  watch: {
+    value: {
+      immediate: true,
+      handler: async function (newVal, oldVal) {
+        if (newVal) await this.popupsPosition();
+        this.show = newVal;
+      },
+    },
+    placement: {
+      immediate: true,
+      handler(newVal, oldVal) {
+        this.dynPlace = newVal;
+      },
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.title {
+  margin-left: 20rpx;
+}
+.shadow {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  height: 100%;
+  z-index: 9999;
+  transition: background 0.3s ease-in-out;
+  visibility: hidden;
+
+  &.shadow-show {
+    visibility: visible;
+  }
+}
+
+.popups {
+  position: absolute;
+  padding: 20rpx;
+  border-radius: 5px;
+  display: flex;
+  .view {
+    display: flex;
+    align-items: center;
+    padding: 15rpx 10rpx;
+    font-size: 25rpx;
+  }
+  .image {
+    display: inline-block;
+    vertical-align: middle;
+    width: 40rpx;
+    height: 40rpx;
+    margin-right: 20rpx;
+  }
+}
+.dark {
+  background-color: #4c4c4c;
+  color: #fff;
+  .top-start:after {
+    content: "";
+    position: absolute;
+    top: -18rpx;
+    left: 10rpx;
+    border-width: 0 20rpx 20rpx;
+    border-style: solid;
+    border-color: transparent transparent #4c4c4c;
+  }
+  .top-end:after {
+    content: "";
+    position: absolute;
+    top: -18rpx;
+    right: 10rpx;
+    border-width: 0 20rpx 20rpx;
+    border-style: solid;
+    border-color: transparent transparent #4c4c4c;
+  }
+  .bottom-start:after {
+    content: "";
+    position: absolute;
+    bottom: -18rpx;
+    left: 10rpx;
+    border-width: 20rpx 20rpx 0;
+    border-style: solid;
+    border-color: #4c4c4c transparent transparent;
+  }
+  .bottom-end:after {
+    content: "";
+    position: absolute;
+    bottom: -18rpx;
+    right: 10rpx;
+    border-width: 20rpx 20rpx 0;
+    border-style: solid;
+    border-color: #4c4c4c transparent transparent;
+  }
+  .disabledColor {
+    color: #c5c8ce;
+  }
+}
+.light {
+  color: #515a6e;
+  box-shadow: 0upx 0upx 30upx rgba(0, 0, 0, 0.2);
+  background: #fff;
+  .top-start:after {
+    content: "";
+    position: absolute;
+    top: -18rpx;
+    left: 10rpx;
+    border-width: 0 20rpx 20rpx;
+    border-style: solid;
+    border-color: transparent transparent #fff;
+  }
+  .top-end:after {
+    content: "";
+    position: absolute;
+    top: -18rpx;
+    right: 10rpx;
+    border-width: 0 20rpx 20rpx;
+    border-style: solid;
+    border-color: transparent transparent #fff;
+  }
+  .bottom-start:after {
+    content: "";
+    position: absolute;
+    bottom: -18rpx;
+    left: 10rpx;
+    border-width: 20rpx 20rpx 0;
+    border-style: solid;
+    border-color: #fff transparent transparent;
+  }
+  .bottom-end:after {
+    content: "";
+    position: absolute;
+    bottom: -18rpx;
+    right: 10rpx;
+    border-width: 20rpx 20rpx 0;
+    border-style: solid;
+    border-color: #fff transparent transparent;
+  }
+  .disabledColor {
+    color: #c5c8ce;
+  }
+}
+.solid-bottom {
+  border-bottom: 1px solid #f3f5f7;
+}
+.solid-right {
+  border-right: 1px solid #ccc;
+}
+.popups .itemChild:last-child {
+  border: none;
+}
+</style>

--
Gitblit v1.8.0