01401132 пре 4 година
комит
67348a2b3a
100 измењених фајлова са 8581 додато и 0 уклоњено
  1. 24 0
      .gitignore
  2. 275 0
      App.vue
  3. 149 0
      component/share.vue
  4. 632 0
      components/activities/combinedSales.vue
  5. 84 0
      components/basics/categoryList.vue
  6. 222 0
      components/basics/categoryShow.vue
  7. 24 0
      components/canvasShow/basics/assistDiv.vue
  8. 129 0
      components/canvasShow/basics/banner.vue
  9. 129 0
      components/canvasShow/basics/brandList.vue
  10. 110 0
      components/canvasShow/basics/categoryList.vue
  11. 107 0
      components/canvasShow/basics/coupon/app/index.vue
  12. 109 0
      components/canvasShow/basics/coupon/mixin.js
  13. 375 0
      components/canvasShow/basics/coupon/pc/index.vue
  14. 243 0
      components/canvasShow/basics/custom.vue
  15. 265 0
      components/canvasShow/basics/discount/app/index.vue
  16. 110 0
      components/canvasShow/basics/discount/mixin.js
  17. 247 0
      components/canvasShow/basics/discount/pc/index.vue
  18. 186 0
      components/canvasShow/basics/group/app/index.vue
  19. 81 0
      components/canvasShow/basics/group/mixin.js
  20. 250 0
      components/canvasShow/basics/group/pc/index.vue
  21. 119 0
      components/canvasShow/basics/imageText.vue
  22. 135 0
      components/canvasShow/basics/imageTextList.vue
  23. 101 0
      components/canvasShow/basics/imageTextNav.vue
  24. 183 0
      components/canvasShow/basics/newProduct/app/index.vue
  25. 89 0
      components/canvasShow/basics/newProduct/mixin.js
  26. 109 0
      components/canvasShow/basics/notice.vue
  27. 195 0
      components/canvasShow/basics/price/app/index.vue
  28. 73 0
      components/canvasShow/basics/price/mixin.js
  29. 253 0
      components/canvasShow/basics/price/pc/index.vue
  30. 279 0
      components/canvasShow/basics/product/app/index.vue
  31. 93 0
      components/canvasShow/basics/product/mixin.js
  32. 226 0
      components/canvasShow/basics/product/pc/index.vue
  33. 133 0
      components/canvasShow/basics/shop.vue
  34. 163 0
      components/canvasShow/basics/spike/app/index.vue
  35. 137 0
      components/canvasShow/basics/spike/mixin.js
  36. 211 0
      components/canvasShow/basics/spike/pc/index.vue
  37. 79 0
      components/canvasShow/basics/text.vue
  38. 76 0
      components/canvasShow/basics/video.vue
  39. 287 0
      components/canvasShow/basics/vip/app/index.vue
  40. 60 0
      components/canvasShow/basics/vip/mixin.js
  41. 241 0
      components/canvasShow/basics/vip/pc/index.vue
  42. 154 0
      components/canvasShow/canvasShowPage.vue
  43. 103 0
      components/canvasShow/componentMap.js
  44. 31 0
      components/canvasShow/config/api.js
  45. 12 0
      components/canvasShow/config/config.js
  46. 192 0
      components/canvasShow/config/mixin/funMixin.js
  47. 9 0
      components/canvasShow/config/mixin/index.js
  48. 37 0
      components/canvasShow/config/mixin/sendReqMixin.js
  49. 135 0
      components/canvasShow/config/mixin/server.js
  50. BIN
      components/canvasShow/static/images/btn-next.png
  51. BIN
      components/canvasShow/static/images/btn-next2.png
  52. BIN
      components/canvasShow/static/images/btn-prev.png
  53. BIN
      components/canvasShow/static/images/btn-prev2.png
  54. BIN
      components/canvasShow/static/images/coupon/bg-coupon.png
  55. BIN
      components/canvasShow/static/images/coupon/bg-coupon2.png
  56. BIN
      components/canvasShow/static/images/coupon/border_L1.png
  57. 0 0
      components/canvasShow/static/images/coupon/border_L2.png
  58. 0 0
      components/canvasShow/static/images/coupon/border_L3.png
  59. 0 0
      components/canvasShow/static/images/coupon/border_L4.png
  60. 0 0
      components/canvasShow/static/images/coupon/border_R1.png
  61. 0 0
      components/canvasShow/static/images/coupon/border_R2.png
  62. 0 0
      components/canvasShow/static/images/coupon/border_R3.png
  63. 0 0
      components/canvasShow/static/images/coupon/border_R4.png
  64. BIN
      components/canvasShow/static/images/coupon/flag-coupon-r.png
  65. BIN
      components/canvasShow/static/images/coupon/flag-coupon.png
  66. BIN
      components/canvasShow/static/images/coupon/flag-coupon2-r.png
  67. BIN
      components/canvasShow/static/images/coupon/flag-coupon2.png
  68. BIN
      components/canvasShow/static/images/discount/bg-discount-more.png
  69. BIN
      components/canvasShow/static/images/discount/bg-discount-top-text.png
  70. BIN
      components/canvasShow/static/images/discount/bg-discount-top.png
  71. BIN
      components/canvasShow/static/images/discount/flag-discount.png
  72. BIN
      components/canvasShow/static/images/discount/flag-discount2.png
  73. BIN
      components/canvasShow/static/images/discount/img-title.png
  74. BIN
      components/canvasShow/static/images/discountListIcon.png
  75. BIN
      components/canvasShow/static/images/group/flag-group.png
  76. BIN
      components/canvasShow/static/images/group/img-title.png
  77. BIN
      components/canvasShow/static/images/groupBuyIcon.png
  78. BIN
      components/canvasShow/static/images/icon-title.png
  79. BIN
      components/canvasShow/static/images/memberCenterIcon.png
  80. BIN
      components/canvasShow/static/images/newProduct/bg-product-card.png
  81. BIN
      components/canvasShow/static/images/newProduct/flag-new.png
  82. BIN
      components/canvasShow/static/images/notice/ico_notice.png
  83. BIN
      components/canvasShow/static/images/notice/ico_notice2.png
  84. BIN
      components/canvasShow/static/images/price/bg-discount.png
  85. BIN
      components/canvasShow/static/images/price/img-title.png
  86. BIN
      components/canvasShow/static/images/product/img-title.png
  87. BIN
      components/canvasShow/static/images/shop/img-title.png
  88. BIN
      components/canvasShow/static/images/spike/bg-spike.png
  89. BIN
      components/canvasShow/static/images/spike/flag-spike.png
  90. BIN
      components/canvasShow/static/images/spike/img-title.png
  91. BIN
      components/canvasShow/static/images/spike/tit-spike.png
  92. BIN
      components/canvasShow/static/images/spikeIcon.png
  93. BIN
      components/canvasShow/static/images/vip/flag-vip.png
  94. BIN
      components/canvasShow/static/images/vip/flag-vip2.png
  95. BIN
      components/canvasShow/static/images/vip/img-title.png
  96. 488 0
      components/lb-picker/README.md
  97. 86 0
      components/lb-picker/index.vue
  98. 93 0
      components/lb-picker/mixins/index.js
  99. 135 0
      components/lb-picker/pickers/multi-selector-picker.vue
  100. 113 0
      components/lb-picker/pickers/selector-picker.vue

+ 24 - 0
.gitignore

@@ -0,0 +1,24 @@
+.DS_Storep
+.DS_Store
+# Editor directories and files
+.idea
+.vscode
+.hbuilderx
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.hbuilderx
+/unpackage/dist/dev
+/unpackage/dist/static
+/unpackage/dist/build/mp-alipay/
+/unpackage/dist/build/mp-weixin/
+/unpackage/dist/build/app-plus/
+/unpackage/cache/
+/unpackage/cache/apk
+/unpackage/release
+/unpackage/res
+/unpackage/resources
+/node_modules
+package-lock.json
+

+ 275 - 0
App.vue

@@ -0,0 +1,275 @@
+<script>
+// // #ifdef MP-WEIXIN
+// const miniShopPlugin = requirePlugin('mini-shop-plugin');
+// // #endif
+export default {
+	onLaunch: function(options) {
+		if (options && options.path === 'pages_category_page1/goodsModule/goodsDetails' && options.query) {
+			this.globalData.productShareItem = options.query
+		}
+		if (options && options.path === 'pages_category_page1/store/index' && options.query) {
+			this.globalData.shopShareItem = options.query
+		}
+        if (options && options.path === 'pages_category_page1/distributionModule/recruit' && options.query) {
+            this.globalData.distributeRecruitItem = options.query
+        }
+		//判断设备是否为 iPhone
+		const self = this
+		uni.getSystemInfo({
+		  success: function(res) {
+			// 根据 model 进行判断
+			if (res.model === 'iPhone X'|| res.model === 'iPhone XR' || res.model === 'iPhone XS Max' || res.model === 'iPhone 11' || res.model === 'iPhone 11 Pro' || res.model === 'iPhone 11 Pro Max' || res.model === 'iPhone 12' || res.model === 'iPhone 12 Pro' || res.model === 'iPhone 12 Pro Max') {
+			  self.globalData.isIphone = true
+			}
+		  }
+		})
+		// 购物车右上角数量
+		let allCartNum = (uni.getStorageSync('allCartNum')).toString()
+		if(allCartNum > 0){
+			uni.setTabBarBadge({
+			   index: 2,
+			   text: allCartNum
+			})
+		}
+	},
+	// onLoad:function(){
+  //   // #ifdef MP-WEIXIN
+  //   miniShopPlugin.initApp(this, wx);
+  //   // #endif
+	// },
+	onShow: function() {
+
+	},
+	onHide: function() {
+	},
+	globalData: {
+		isIphone:false,
+	},
+};
+</script>
+
+<style lang="scss">
+	/*每个页面公共css */
+	@import "uview-ui/index.scss";
+  uni-rich-text img {
+    max-width: 100% !important;
+  }
+
+	.line1{
+		text-overflow: ellipsis;
+		white-space: nowrap;
+		overflow: hidden;
+	}
+
+	.wid{width: 100%;}
+	.fs4{font-size: 4upx;}
+	.fs18{font-size: 18upx;}
+	.fs20{font-size: 20upx;}
+	.fs22{font-size: 22upx;}
+	.fs24{font-size: 24upx;}
+	.fs26{font-size: 26upx;}
+	.fs28{font-size: 28upx;}
+	.fs30{font-size: 30upx;}
+	.fs32{font-size: 32upx;}
+	.fs34{font-size: 34upx;}
+	.fs36{font-size: 36upx;}
+	.fs38{font-size: 38upx;}
+	.fs40{font-size: 40upx;}
+	.fs42{font-size: 42upx;}
+	.fs44{font-size: 44upx;}
+	.fs46{font-size: 46upx;}
+	.fs50{font-size: 50upx;}
+	.fs60{font-size: 60upx;}
+	.fs-bold{font-weight: bold;}
+	.fs-weight-300{font-weight: 300;}
+	.fs-weight-200{font-weight: 200;}
+
+
+	.flex-display{display: flex;}
+	.flex-center{display: flex;justify-content: center;}
+	.flex-items{display: flex;align-items:center;}
+	.flex-items-plus{display: flex;justify-content: center;align-items:center;}
+	.flex-start{display: flex;justify-content: flex-start;}
+	.flex-end{display: flex;justify-content: flex-end;}
+	.flex-end-plus{display: flex;justify-content: flex-end;align-items:center;}
+	.flex-column{flex-direction: column}
+	.flex-column-plus{display: flex;flex-direction: column}
+	.flex-row{flex-direction: row}
+	.flex-row-plus{display: flex;flex-direction: row}
+	.flex-sp-around{justify-content:space-around;}
+	.flex-sp-between{justify-content:space-between;}
+	.text-align{text-align: center;}
+	.flex-wrap-1{display: flex;flex-wrap: wrap}
+	.flex-nowrap-1{display: flex;flex-wrap: nowrap}
+	.align-end{display: flex;align-items:flex-end;}
+	.align-sp-between{align-content: space-between;}
+
+
+	.mar-top-5{margin-top: 5upx;}
+	.mar-top-10{margin-top: 10upx;}
+	.mar-top-20{margin-top: 20upx;}
+	.mar-top-30{margin-top: 30upx;}
+	.mar-top-32{margin-top: 32upx;}
+	.mar-top-36{margin-top: 36upx;}
+	.mar-top-40{margin-top: 40upx;}
+	.mar-top-50{margin-top: 50upx;}
+	.mar-top-60{margin-top: 60upx;}
+	.mar-top-70{margin-top: 70upx;}
+	.mar-top-100{margin-top: 100upx;}
+	.mar-top-percent40{margin-top: 40%;}
+	.mar-top-half{margin-top: 50%;}
+	.mar-left-6{margin-left: 6upx;}
+	.mar-left-5{margin-left: 5upx;}
+	.mar-left-10{margin-left: 10upx;}
+	.mar-left-20{margin-left: 20upx;}
+	.mar-left-30{margin-left: 30upx;}
+  .mar-left-35{margin-left: 35upx;}
+	.mar-left-40{margin-left: 40upx;}
+	.mar-left-50{margin-left: 50upx;}
+	.mar-left-60{margin-left: 60upx;}
+	.mar-left-70{margin-left: 70upx;}
+	.mar-right-10{margin-right: 10upx;}
+	.mar-right-20{margin-right: 20upx;}
+	.mar-right-30{margin-right: 30upx;}
+  .mar-right-35{margin-right: 35upx;}
+	.mar-right-40{margin-right: 40upx;}
+	.mar-right-50{margin-right: 50upx;}
+	.pad-left-10{padding-left: 10upx;}
+	.pad-left-20{padding-left: 20upx;}
+	.pad-left-40{padding-left: 40upx;}
+	.pad-right-20{padding-right: 20upx;}
+	.pad-top-20{padding-top: 20upx;}
+	.pad-top-40{padding-top: 40upx;}
+	.pad-bot-20{padding-bottom: 20upx;}
+	.pad-topbot-20{padding:20upx 0upx;}
+	.pad-topbot-5{padding:0upx 5upx;}
+	.pad-topbot-10{padding:0upx 10upx;}
+	.pad-topbot-50{padding:50upx 0upx;}
+	.pad-bot-20{padding-bottom:20upx;}
+	.pad-bot-30{padding-bottom:30upx;}
+	.pad-bot-40{padding-bottom:40upx;}
+	.pad-bot-100{padding-bottom:100upx;}
+	.pad-bot-140{padding-bottom:140upx;}
+
+	.bor-rad-30{border-radius: 30upx;}
+	.bor-rad-45{border-radius: 45upx;}
+	.bor-rad-half{border-radius: 50%;}
+
+	.backColor{background-color: #009688;}
+	.backColorFFF{background-color: #FFFFFF;}
+
+	.pos-abs{position: absolute;}
+
+	.bor-bot-line{border-bottom: #C8C7CC 1upx solid;}
+	.bor-line-F7F7F7{border-bottom: #F7F7F7 1upx solid;}
+	.bor-line-E5E5E5{border-bottom: #E5E5E5 1upx solid;}
+	.borRig-line-E5E5E5{border-right: #DDDDDD 2upx solid;}
+	.borRig-line-20{border-bottom: #F7F7F7 20upx solid;}
+
+	.font-color-red{color: red;}
+	.font-color-FFF{color: #FFFFFF;}
+	.font-color-8A734A{color: #8A734A;}
+	.font-color-71521B{color: #71521B;}
+	.font-color-222{color: #222222;}
+	.font-color-333{color: #333333;}
+	.font-color-666{color: #666666;}
+	.font-color-999{color: #999999;}
+	.font-color-656{color: #656565;}
+	.font-color-DDD{color: #DDDDDD;}
+	.font-color-CCC{color: #CCCCCC;}
+	.font-color-FFEBC4{color: #FFEBC4;}
+	.font-color-1CC363{color: #1CC363;}
+	.font-color-47A7EE{color: #47A7EE;}
+	.font-color-C5AA7B{color: #C5AA7B;}
+	.font-color-FF7700{color: #FF7700;}
+	.font-color-FF7911{color: #FF7911;}
+	.font-color-80{color: #808080;}
+	.font-color-DD{color: #DD524D;}
+	.font-color-C83732{color: #C83732;}
+	.font-color-3F{color: #3F536E;}
+	.font-color-009{color: #009688;}
+	.font-weight-500{font-weight: 500;}
+	.font-weight-bold{font-weight: bold;}
+	.overflow{overflow: hidden;text-overflow:ellipsis;white-space: nowrap;}
+	.overflowNoDot{display:block;overflow:hidden;}
+	.discountsPriceLine{text-decoration:line-through;}
+	.border-bottom-Line{border-bottom: 1upx solid #EDEDED;}
+  .anonymous {
+    margin-top: 25upx;
+    .uni-checkbox-input {
+      border-color: #C5AA7B !important;
+      width: 30upx;
+      height: 30upx;
+    }
+    .uni-checkbox-input-checked:before {
+      font-size: 30upx !important;
+    }
+    .uni-checkbox-input-checked {
+      background: #C5AA7B;
+    }
+  }
+  .footprint {
+    .itemList {
+      .uni-checkbox-input {
+        border-color: #C5AA7B !important;
+        width: 36upx;
+        height: 36upx;
+        border-radius: 50%;
+        margin-right: 20upx;
+      }
+      .uni-checkbox-input-checked:before {
+        font-size: 36upx !important;
+      }
+      .uni-checkbox-input-checked {
+        background: #C5AA7B;
+      }
+    }
+  }
+  .itemInfo {
+    uni-slider {
+      margin: 0;
+      .uni-slider-thumb {
+        display: none;
+      }
+      .uni-slider-handle-wrapper {
+        height: 18upx;
+        border-radius: 0;
+        border: 1upx solid #FF736C;
+      }
+      .uni-slider-track {
+        border-radius: 0;
+      }
+      .uni-slider-tap-area {
+        flex: 0 0 70%;
+        padding: 0;
+      }
+    }
+  }
+  .uni-modal {
+    padding: 20rpx;
+    box-sizing: border-box;
+  }
+  uni-modal .uni-modal__ft:after {
+    border-top: none;
+  }
+  uni-modal .uni-modal__btn {
+    color: #333333;
+    border: 2rpx solid #333333;
+    font-weight: 400;
+    margin: 0 10rpx;
+    font-size: 28rpx;
+  }
+  .uni-tabbar .uni-tabbar__reddot{
+	  background: #C5AA7B;
+	  color: #FFFFFF;
+  }
+  uni-checkbox:not([disabled]) .uni-checkbox-input:hover {
+    border-color: #C5AA7B;
+  }
+</style>
+<style>
+.uni-modal__btn_primary {
+  background: #333333;
+  color: #FFEBC4 !important;
+}
+</style>

+ 149 - 0
component/share.vue

@@ -0,0 +1,149 @@
+<template>
+	<view class="isshareShow" v-show="isshareShow">
+		<!-- <u-mask :show="true" class="flex-items-plus flex-row"> -->
+			<view class="share-box">
+				<view class="inactive-box flex-items-plus flex-column-plus">
+					<view class="flex-row-plus">
+						<view class="fs36 mar-left-60">邀请好友</view>
+						<view @click="cancel" class="close-btn">
+							<image class="close-icon" src="../static/images/close.png"></image>
+						</view>
+					</view>
+					<view class="mar-top-50 flex-row-plus">
+						<!-- <view class="flex-column-plus flex-items-plus">
+							<view class="weixin-box flex-items-plus">
+								<image src="../static/img/weixin2x.png"></image>
+							</view>
+							<label class="fs24 mar-top-20">微信</label>
+						</view>
+						<view class="flex-column-plus flex-items-plus mar-left-50">
+							<view class="weixin-box flex-items-plus">
+								<image src="../static/images/qq2x.png"></image>
+							</view>
+							<label class="fs24 mar-top-20">QQ</label>
+						</view>
+						<view class="flex-column-plus flex-items-plus mar-left-50">
+							<view class="weixin-box flex-items-plus">
+								<image src="../static/images/weibo2x.png"></image>
+							</view>
+							<label class="fs24 mar-top-20">微博</label>
+						</view> -->
+						<view class="copy-color " :data-clipboard-text="url" @click="copy">
+							<view class="weixin-box flex-items-plus">
+							</view>
+							<label class="fs24 mar-top-20">链接</label>
+						</view>
+					</view>
+				</view>
+			</view>
+		<!-- </u-mask> -->
+	</view>
+</template>
+
+<script>
+	import ClipboardJS from "clipboard"
+  import UImage from "../uview-ui/components/u-image/u-image";
+	export default {
+    components: {UImage},
+    props:{
+			isshareShow:{
+				type:Boolean,
+				dafault:false
+			},
+			url:{
+				type:String,
+				dafault:''
+			}
+		},
+		name:"share",
+		data() {
+			return {
+
+			};
+		},
+		methods:{
+			cancel(){
+				this.$emit('shareCancel')
+			},
+			copy() {
+				// #ifndef H5
+				uni.setClipboardData({
+				    data: this.url,
+				    success: function () {
+				        uni.showToast({
+							title:'复制成功',
+							icon:'success'
+						})
+				    }
+				});
+				// #endif
+				// #ifdef H5
+				let clipboard = new ClipboardJS('.copy-color');
+				clipboard.on('success', function (e) {
+				   uni.showToast({
+									title:'复制成功',
+									icon:'success'
+								})
+				   clipboard.destroy();
+				})
+				clipboard.on('error', function (e) {
+				   uni.showToast({
+				   	title:'复制失败',
+				   	icon:'success'
+				   })
+				   clipboard.destroy();
+				})
+				// #endif
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.isshareShow{
+		width: 100%;
+		height: 100%;
+		position: absolute;
+		top: 0;
+		left: 0;
+		background-color: #636363;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		z-index: 999;
+	}
+.close-btn{
+	position: relative;
+	left: 120rpx;
+}
+.close-icon{
+	width: 46rpx;
+	height: 46rpx;
+}
+.copy-color{
+	display: flex;
+	justify-content: center;
+	align-items:center;
+	flex-direction: column;
+}
+.share-box{
+	background-color: #FFFFFF;
+	width: 550rpx;
+	height: 380rpx;
+	border-radius: 20rpx;
+}
+.inactive-box{
+	padding: 40rpx 40rpx;
+	.weixin-box{
+		width: 100rpx;
+		height: 100rpx;
+		border-radius: 50%;
+    background: url("../static/img/lianjie.png") no-repeat center center;
+    background-size: contain;
+		image{
+			width: 55rpx;
+			height: 55rpx;
+		}
+	}
+}
+</style>

+ 632 - 0
components/activities/combinedSales.vue

@@ -0,0 +1,632 @@
+<template>
+  <view class="group-list" v-if="selectComposeData && selectComposeData.length > 0">
+    <view class="group-warp">
+      <view class="title">
+        <label>
+          <image class="title-img" src="../../static/images/combinationIcon.png" alt="组合销售" mode="widthFix"></image>
+        </label>
+        <view class="price-text">
+          组合价:¥{{composePrice}}
+        </view>
+      </view>
+      <view>
+        <scroll-view class="tabs-nav" scroll-x="true">
+          <view class="ul">
+            <view v-for="(item,index) in selectComposeData" :key="index" class="li" :class="tabIndex===index && 'on'" @click="tabChange(index)">
+              {{ item.composeName }}</view>
+          </view>
+        </scroll-view>
+        <view class="tabs-item" v-for="(item,index) in selectComposeData" :key="index" :class="tabIndex===index && 'on'">
+          <swiper class="swiper pro-box" :indicator-dots="false" :autoplay="true" :display-multiple-items="item.composeProductInfoList.length < 3?item.composeProductInfoList.length:3" @change="swiperChange" :disable-touch="item.composeProductInfoList.length <= 3">
+            <swiper-item class="pro-item-warp" v-for="(itemJ,indexJ) in item.composeProductInfoList" :key="indexJ" >
+              <view class="pro-item-inner">
+                <view class="pro-item">
+                  <view class="pro-item-img">
+                    <image class="img" :src="itemJ.productImage"></image>
+                  </view>
+                  <view class="pro-item-info">
+                    <h3 class="name">
+                      {{itemJ.productName}}
+                    </h3>
+                    <view class="sku" @click.stop="changeSkuItemValue(itemJ, indexJ)">
+                      <view class="text">{{itemJ.skuItem.skuName}}</view>
+                    </view>
+                  </view>
+                </view>
+              </view>
+            </swiper-item>
+          </swiper>
+          <view class="swiper-dots" v-if='item.composeProductInfoList.length > 3'>
+            <text :class="{'dot-active':swiperCurrent === index,'dot': true}"
+                  v-for="(dot, index) in item.composeProductInfoList.length - 2"
+                  :key="index"
+            ></text>
+          </view>
+        </view>
+        <view class="btn-buy" @click="doBuy">立即购买</view>
+      </view>
+    </view>
+    <!-- 商品详情 -->
+    <u-popup v-model="goodsDetailShowFlag" mode="bottom" border-radius="14">
+      <view class="goosDetailshow-box">
+        <view class="detailImg-box flex-row-plus">
+          <image class="detailImg" :src="selectedSku.image"></image>
+          <view class="flex-column-plus mar-left-40">
+            <view class="font-color-C5AA7B">
+              <label class="fs24">¥</label>
+              <label class="fs36 mar-left-10"
+                     v-text="getPrice(selectedSku)"></label>
+            </view>
+            <label class="fs24 font-color-999 mar-top-20">库存 {{selectedSku.stockNumber}} 件</label>
+            <label class="fs24 mar-top-20">已选</label>
+          </view>
+        </view>
+        <view class="color-box flex-column-plus">
+          <view v-for="(attritem,index) in skuProData.names" :key="index" class="skuStyle">
+            <label class="fs24 font-color-999">{{attritem.skuName}}</label>
+            <view class="colorName-box">
+              <view class="pad-bot-30" v-for="(attrRes, resIndex) in attritem.values" :key="resIndex">
+                <view class="colorName"
+                      :class="{'colorName-on' : getselectedAttrVal(attritem.nameCode) == attrRes.valueCode}"
+                      @click="nameCodeValueCodeClick(attritem.nameCode, attrRes.valueCode, true)">
+                  {{attrRes.skuValue}}
+                </view>
+              </view>
+            </view>
+          </view>
+        </view>
+        <!--        <view class="goodsNum-box flex-row-plus flex-sp-between" :class="{'bottom-line' :supportHuabei}">-->
+        <!--          <label class="font-color-999 fs24">数量</label>-->
+        <!--          <view class="goodsNum">-->
+        <!--            <text class="subtract" @click="updateNumSub()">-</text>-->
+        <!--            <text class="goodsNumber" v-model="buyNum">{{buyNum}}</text>-->
+        <!--            <text class="add" @click.stop="-->
+        <!--            ()">+</text>-->
+        <!--          </view>-->
+        <!--        </view>-->
+        <view class="goosDetailbut-box flex-items-plus">
+          <!--          <button type="default" @click="goodsDateils(shopId,productId,skuId)" >查看详情</button>-->
+          <button type="default" class="submitBtn" @click="submitBtn()">确认</button>
+        </view>
+      </view>
+    </u-popup>
+  </view>
+</template>
+
+<script>
+import NET from "../../utils/request";
+import API from "../../config/api";
+
+export default {
+  name: "combinedSales",
+  props: {
+    pid: {
+      type: String,
+      default: ''
+    },
+    productData: {
+      type: Object,
+      default: ()=>{}
+    }
+  },
+  data () {
+    return {
+      skuShowFalg: false,
+      tabIndex: 0,
+      swiperCurrent: 0,
+      selectComposeData: [],
+      curProIndex: 0,
+      selectedSku: [],
+      selectedAttr: [],
+      skuProData: {},
+      goodsDetailShowFlag: false,
+      composePrice: 0
+    }
+  },
+  mounted() {
+    this.getSelectCompose()
+  },
+  methods:{
+    // 切换套餐
+    tabChange(index){
+      this.tabIndex = index
+      this.calculatePrice()
+    },
+    // 滑动回调方法
+    swiperChange(e) {
+      this.swiperCurrent = e.detail.current;
+    },
+    // 获取组合销售数据
+    getSelectCompose() {
+      NET.request(API.selectCompose, {
+            productId: this.pid
+          },
+          "GET").then(res => {
+        this.selectComposeData = res.data
+        for(let i=0;i< this.selectComposeData.length;i++) {
+          let proList = this.selectComposeData[i].composeProductInfoList
+          for (let j = 0; j < proList.length; j++) {
+            proList[j].skuItem = proList[j].composeSkuInfoList[0]
+          }
+        }
+        this.calculatePrice()
+      }).catch(res => {
+
+      })
+    },
+    // 更换商品样式
+    changeSkuItemValue(item,index) {
+      this.curProIndex = index
+      uni.showLoading({
+        title: '加载中...'
+      })
+      NET.request(API.QueryProductDetail, {
+            shopId: this.productData.shopId,
+            productId: item.productId,
+            skuId: item.skuItem.skuId,
+            terminal: 1
+          },
+          "GET").then(res => {
+        uni.hideLoading()
+        this.skuProData = res.data
+        // console.log(this.skuProData,'this.skuProData', res.data)
+        //如果是单款式商品,需要特殊处理productData.names
+        const mapKeys = Object.keys(this.skuProData.map)
+        if (mapKeys.length === 1 && mapKeys[0] === '单款项') {
+          this.skuProData.names[0].values.push({
+            skuValue: '单款项',
+            valueCode: '单款项'
+          })
+        }
+
+        //如果sku的图像为空,设置为商品的图像
+        for (var key in this.skuProData.map) {
+          let skuImage = this.skuProData.map[key].image
+          if (!skuImage) {
+            this.skuProData.map[key].image = this.skuProData.images[0]
+          }
+        }
+        this.goodsDetailShowFlag = true
+        this.selectBySkuId(item.skuItem.skuId)
+      }).catch(res => {
+        uni.hideLoading()
+      })
+    },
+    selectBySkuId(skuId) {
+      if (skuId) {
+        let mapinfo = this.skuProData.map
+        let flag = true
+        for (var key in mapinfo) {
+          if (parseInt(mapinfo[key].skuId) === parseInt(skuId)) {
+            flag = false
+            this.selectedSku = mapinfo[key]
+            // 选中sku对应的规格
+            const valueCodeList = key.split(',')
+            this.selectedAttr = []
+            this.skuProData.names.forEach(attr => {
+              for (var index in attr.values) {
+                let valueCode = attr.values[index].valueCode
+                if (valueCodeList.includes(valueCode)) {
+                  this.nameCodeValueCodeClick(attr.nameCode, valueCode, false)
+                  break
+                }
+              }
+            })
+            break
+          }
+        }
+        // 匹配不上就赋值第一个
+        if(flag){
+          for (var key in mapinfo) {
+            console.log(key,'key')
+            this.selectedSku = mapinfo[key]
+            break
+          }
+        }
+      }
+    },
+    nameCodeValueCodeClick(nameCode, valueCode, reSelectSku) {
+      this.selectedAttr[nameCode] = valueCode
+      console.log(this.selectedAttr, 'this.selectedAttr')
+      if (reSelectSku) {
+        let attrList = []
+        for (var key in this.selectedAttr) {
+          attrList.push(this.selectedAttr[key])
+        }
+        const attrkey = attrList.join(',')
+        let mapinfo = this.skuProData.map
+        for (var key in mapinfo) {
+          if (attrkey === key) {
+            this.selectedSku = mapinfo[key]
+          }
+        }
+      }
+      this.$forceUpdate(); // 重绘
+    },
+    // 提交更换商品规格
+    submitBtn() {
+      console.log(this.selectedSku,'this.selectedSku')
+      const curPro = this.selectComposeData[this.tabIndex].composeProductInfoList[this.curProIndex]
+      for(let i=0;i<curPro.composeSkuInfoList.length;i++){
+        if(curPro.composeSkuInfoList[i].skuId === this.selectedSku.skuId){
+          this.selectedSku.skuName = curPro.composeSkuInfoList[i].skuName
+        }
+      }
+      curPro.skuItem = this.selectedSku
+      this.calculatePrice()
+      this.goodsDetailShowFlag = false
+    },
+    // 计算组合价
+    calculatePrice(){
+      const proList = this.selectComposeData[this.tabIndex].composeProductInfoList,composeType= this.selectComposeData[this.tabIndex].composeType,promote= this.selectComposeData[this.tabIndex].promote
+      let total = 0
+      for(let i=0;i<proList.length;i++){
+        total += this.getPrice(proList[i].skuItem)
+      }
+      switch (composeType){
+        case 1:
+          this.composePrice = promote.toFixed(2)
+          break
+        case 2:
+          this.composePrice = (total - promote).toFixed(2)
+          break
+        case 3:
+          this.composePrice = parseFloat(total * promote / 10).toFixed(2)
+          break
+      }
+      console.log(total, this.composePrice, 'total')
+    },
+    // 根据活动显示不同价格
+    getPrice(item){
+      // 所属活动 0-常规商品 1-拼团活动 2-秒杀活动 3-限时折扣活动 4-平台秒杀 5-平台折扣 6-定价捆绑 7-组合捆绑 8-场景营销 9-会员价
+      if(item.activityType){
+        if(item.activityType === 0 || item.activityType === 6 || item.activityType === 7){
+          return item.price
+        } else {
+          return item.originalPrice
+        }
+      } else {
+        return item.price
+      }
+    },
+    // 立即购买
+    doBuy(){
+      let addCart = []
+      let shopObj = {}
+      shopObj["shopId"] = this.productData.shopId
+      shopObj["composeId"] = this.selectComposeData[this.tabIndex].composeId
+      shopObj["skus"] = []
+      let proList = this.selectComposeData[this.tabIndex].composeProductInfoList
+      let len2 = proList.length
+      for (let j = 0; j < len2; j++) {
+        let skusObj = {}
+        skusObj["number"] = 1
+        skusObj["skuId"] = proList[j].skuItem.skuId
+        shopObj.skus.push(skusObj)
+      }
+      addCart.push(shopObj)
+      uni.setStorageSync('skuItemDTOList', addCart)
+      uni.navigateTo({
+        url: '../../pages_category_page1/orderModule/orderConfirm?type=2'
+      })
+    },
+    getselectedAttrVal(item){
+      return this.selectedAttr[item]
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.group-list{
+  padding: 10upx 20upx 60upx;
+  border-top: 12upx solid #F8F8F8;
+  .group-warp{
+    height: 680upx;
+
+    background: #333333;
+    box-shadow: 0 20upx 30upx rgba(0, 0, 0, 0.3);
+    opacity: 1;
+    border-radius: 20upx;
+  }
+  .title{
+    display: flex;
+    align-items:center;
+    position: relative;
+    justify-content: space-between;
+    padding: 32upx 0 20upx 30upx;
+    .title-img{
+      width: 203upx;
+    }
+    .price-text{
+      padding: 0 34upx;
+      margin-right: 10upx;
+      height: 50upx;
+      background: linear-gradient(90deg, #C83732 0%, #E25C44 100%);
+      box-shadow: 0 6upx 12upx rgba(233, 0, 0, 0.3);
+      border-radius: 26upx;
+      font-size: 24upx;
+      color: #fff;
+      text-align: center;
+      line-height: 50upx;
+      margin-left: 20upx;
+      .swiper{
+        height: 50upx;
+      }
+    }
+  }
+  .tabs-nav{
+    padding: 0 10upx;
+    margin-bottom: 20upx;
+    .ul{
+      display: flex;
+      .li{
+        //flex: 1 0 auto;
+        text-align: center;
+        font-size: 26rpx;
+        color: #999;
+        position: relative;
+        padding: 12px 40px;
+        word-break: keep-all;
+        &:first-child{
+          margin-left: 0;
+        }
+        &.on{
+          color: #FFEBC4;
+          &:after{
+            content: '';
+            width: 100%;
+            height: 2rpx;
+            background: #FFEBC4;
+            position: absolute;
+            left: 0;
+            bottom: 0;
+          }
+        }
+      }
+    }
+  }
+  .tabs-item{
+    display: none;
+    &.on{
+      display: block;
+    }
+  }
+  .pro-box{
+    height: 318upx;
+    padding: 0 2upx 20upx;
+    .pro-item-inner{
+      padding: 0 8upx;
+      display: flex;
+      justify-content: center;
+    }
+    .pro-item{
+      width: 219upx;
+      background: #FFFFFF;
+      padding: 10upx;
+      height: 318upx;
+      .pro-item-img{
+        width: 100%;
+        height: 160upx;
+        .img{
+          width: 100%;
+          height: 100%;
+          object-fit: contain;
+
+        }
+      }
+      .pro-item-info{
+        .name{
+          font-size: 24upx;
+          line-height: 34upx;
+          color: #333333;
+          overflow: hidden;
+          text-overflow:ellipsis;
+          white-space: nowrap;
+          text-align: center;
+          font-weight: normal;
+          margin: 8upx 0 26upx;
+        }
+        .sku{
+          width: 180upx;
+          height: 50upx;
+          margin: 0 auto;
+          line-height: 50upx;
+          border: 2upx solid #E4E5E6;
+          background: url("../../static/images/arrow-suk-select.png") no-repeat right center / 60upx 60upx;
+          .text{
+            font-size: 24upx;
+            color: #999;
+            padding-left: 20upx;
+            width: 126px;
+            text-overflow:ellipsis;
+            white-space: nowrap;
+          }
+        }
+      }
+    }
+  }
+  .swiper-dots{
+    display: flex;
+    justify-content: center;
+    .dot{
+      display: block;
+      width: 24upx;
+      height: 4upx;
+      background: #FFFFFF;
+      opacity: 0.5;
+      border-radius: 2upx;
+      margin: 0 20upx;
+      &.dot-active{
+        opacity: 1;
+      }
+    }
+  }
+}
+.btn-buy{
+  width: 688upx;
+  height: 84upx;
+  //border: 2upx solid rgba(0, 0, 0, 0);
+  background: linear-gradient(88deg, #C5AA7B 0%, #FFEBC4 100%);
+  font-size: 28upx;
+  color: #333;
+  line-height: 84upx;
+  margin: 30upx auto 0;
+  text-align: center;
+}
+
+.goosDetailshow-box {
+  .detailImg-box {
+    margin-top: 30rpx;
+    margin-left: 30rpx;
+    border-radius: 10rpx;
+    border-bottom: 1rpx solid #EDEDED;
+    padding-bottom: 20rpx;
+    width: 690rpx;
+
+    .detailImg {
+      width: 180rpx;
+      height: 180rpx;
+    }
+  }
+
+  .color-box {
+    padding: 30rpx 30rpx;
+    border-bottom: 1rpx solid #EDEDED;
+    width: 690rpx;
+    .skuStyle{
+      padding: 20rpx 0;
+    }
+
+    .skuStyle:nth-child(2) {
+      border-top: 1px solid #F3F4F5;
+    }
+
+    .colorName-box {
+      display: flex;
+      flex-wrap: wrap;
+      flex-direction: row;
+      justify-content: flex-start;
+      align-items: center;
+      margin-top: 30rpx;
+      margin-left: -30rpx;
+
+      .colorName-on {
+        background-color: #FFE5D0;
+        color: #C5AA7B;
+        margin-left: 30rpx;
+        padding: 10rpx 32rpx;
+        border-radius: 28rpx;
+        border: 1rpx solid #C5AA7B;
+        font-size: 26rpx;
+        text-align: center;
+        z-index: 1;
+      }
+
+      .colorName {
+        background-color: #F5F5F5;
+        margin-left: 30rpx;
+        padding: 10rpx 32rpx;
+        border-radius: 28rpx;
+        font-size: 26rpx;
+        z-index: 2;
+      }
+    }
+
+  }
+
+  .modelNum-box {
+    padding: 30rpx 30rpx;
+    border-bottom: 1rpx solid #EDEDED;
+    width: 690rpx;
+
+    .modelNumName-box {
+      display: flex;
+      flex-wrap: wrap;
+      flex-direction: row;
+      justify-content: flex-start;
+      align-items: center;
+      margin-top: 30rpx;
+      margin-left: -30rpx;
+
+      .modelNumName-on {
+        background-color: #FFE4D0;
+        color: #C5AA7B;
+        margin-left: 30rpx;
+        padding: 10rpx 32rpx;
+        border-radius: 28rpx;
+        border: 1rpx solid #C5AA7B;
+        font-size: 26rpx;
+        text-align: center;
+      }
+
+      .modelNumName {
+        background-color: #F5F5F5;
+        margin-left: 30rpx;
+        padding: 10rpx 32rpx;
+        border-radius: 28rpx;
+        font-size: 26rpx;
+      }
+    }
+  }
+
+  .goodsNum-box {
+    padding: 30rpx 30rpx;
+    width: 690rpx;
+    padding-bottom: 140rpx;
+
+    .goodsNumber {
+      text-align: center;
+      border: 1rpx solid #999999;
+      padding: 3rpx 20rpx;
+    }
+
+    .subtract {
+      border: 1rpx solid #999999;
+      padding: 3rpx 20rpx;
+      margin-right: -1rpx;
+    }
+
+    .add {
+      border: 1rpx solid #999999;
+      padding: 3rpx 20rpx;
+      margin-left: -1rpx;
+    }
+  }
+
+  .goosDetailbut-box {
+    .joinShopCartBut {
+      width: 343rpx;
+      height: 80rpx;
+      background-color: #FFC300;
+      color: #FFFEFE;
+      font-size: 28rpx;
+      line-height: 80rpx;
+      text-align: center;
+      margin-left: 30rpx;
+    }
+
+    .buyNowBut {
+      width: 343rpx;
+      height: 80rpx;
+      border-radius: 0 40rpx 40rpx 0;
+      background-color: #FF6F00;
+      color: #FFFEFE;
+      font-size: 28rpx;
+      line-height: 80rpx;
+      text-align: center;
+    }
+  }
+  .submitBtn {
+    width: 342rpx;
+    height: 100rpx;
+    line-height: 100rpx;
+    font-size: 28rpx;
+    border: 1px solid;
+    border-radius: 0;
+    color: #FFEBC4;
+    background: #333333;
+    margin: 20rpx 0;
+  }
+}
+</style>

+ 84 - 0
components/basics/categoryList.vue

@@ -0,0 +1,84 @@
+<template>
+  <view class="tabs-nav-warp">
+    <scroll-view class="tabs-nav" scroll-x="true">
+      <view class="ul">
+        <view class="li" :class="{'on':activeTab===0}" @click="tabChange(0)">首页</view>
+        <view class="li" :class="{'on':activeTab===index+1}" v-for="(item,index) in categoryList" :key="index" @click="tabChange(index+1,item.classifyId)">
+          {{item.classifyName}}
+        </view>
+      </view>
+    </scroll-view>
+  </view>
+</template>
+
+<script>
+const NET = require('@/utils/request')
+const API = require('@/config/api')
+export default {
+  name: "categoryList",
+  data () {
+    return {
+      activeTab: 0,
+      categoryList: []
+    }
+  },
+  mounted () {
+    this.getCategoryData();
+  },
+  methods:{
+    tabChange (index, id) {
+      this.activeTab = index
+      this.$emit('tabChange', index, id)
+    },
+    getCategoryData(){
+      uni.showLoading({
+        title:'加载中...'
+      })
+      NET.request(API.FindCategoryListByDepth, {
+      }, 'GET').then(res => {
+        this.categoryList = res.data
+        uni.hideLoading()
+      }).catch(res => {
+        uni.hideLoading()
+      })
+    }
+  }
+
+}
+</script>
+
+<style lang="scss" scoped>
+.tabs-nav-warp{
+  margin-top: 20rpx;
+  padding:0 30rpx;
+  .tabs-nav{
+    .ul{
+      display: flex;
+      .li{
+        flex: 1 0 auto;
+        margin-left: 36rpx;
+        font-size: 30rpx;
+        color: #999999;
+        position: relative;
+        padding-bottom: 18rpx;
+        &:first-child{
+          margin-left: 0;
+        }
+        &.on{
+          &:after{
+            content: '';
+            width: 100%;
+            height: 4rpx;
+            background: #C5AA7B;
+            position: absolute;
+            left: 0;
+            bottom: 0;
+          }
+          font-weight:bold;
+        }
+      }
+    }
+  }
+}
+
+</style>

+ 222 - 0
components/basics/categoryShow.vue

@@ -0,0 +1,222 @@
+<template>
+  <view class="goodRecommend">
+    <view class="product-list" v-if="productList.length>0">
+      <view class="product-list-box" >
+        <view class="product-list-item-warp" v-for="(item,index) in productList" :key="index">
+          <view class="product-list-item">
+            <view class="product-list-img" @click="jumpProductDetail(item)">
+              <img v-show="item.image" class="img" :src="item.image">
+            </view>
+            <view class="product-list-info">
+              <view class="product-name">{{item.productName}}</view>
+              <view>
+                <view class="shop-box" @click.stop="jumpStore(item)">
+                  <view class="shop-name" @click="jumpProductDetail(item)">{{item.shopName}}</view>
+                  <view class="shop-logo">
+                    <img :src="item.shopLogo">
+                  </view>
+                </view>
+                <view class="buy-count">{{item.users?item.users: 0}}人付款</view>
+              </view>
+              <view class="price-warp">
+                <view class="price">
+                  ¥ {{item.price}}
+                </view>
+                <view class="original-price">
+                  ¥ {{item.originalPrice}}
+                </view>
+              </view>
+            </view>
+          </view>
+        </view>
+      </view>
+    </view>
+    <view v-else class="emptyCart-box flex-items-plus flex-column">
+        <image class="emptyCart-img" src="@/static/img/bgnull.png" mode="widthFix" />
+        <label class="font-color-999 fs26 mar-top-30">这里空空如也~</label>
+    </view>
+  </view>
+</template>
+
+<script>
+const NET = require('@/utils/request')
+const API = require('@/config/api')
+export default {
+name: "categoryShow",
+  props: {
+    categoryid: {
+      type: Number,
+      default: 0
+    }
+  },
+  data () {
+    return {
+      activeTab: 0,
+      page:1,
+      pageSize:10,
+      total: 0,
+      productList: []
+    }
+  },
+  mounted () {
+    this.getData();
+  },
+  methods:{
+    getData(){
+      uni.showLoading({
+        title:'加载中...'
+      })
+      NET.request(API.getProducts, {
+        classifyId: this.categoryid,
+        page:this.page,
+        pageSize:this.pageSize
+      }, 'GET').then(res => {
+        this.productList = res.data.list
+        uni.hideLoading()
+      }).catch(res => {
+        uni.hideLoading()
+      })
+    },
+    // 跳转到店铺主页
+    jumpStore(item){
+      uni.navigateTo({
+        url: `/pages_category_page1/store/index?storeId=${item.shopId}`
+      })
+    },
+    // 跳转到商品详情
+    jumpProductDetail(item){
+      uni.navigateTo({
+        url: '/pages_category_page1/goodsModule/goodsDetails?shopId=' + item.shopId + '&productId=' + item.productId + '&skuId=' + item
+            .skuId
+      })
+    }
+  },
+  watch: {
+    'categoryid': {
+      handler(newVal, oldVal) {
+        this.getData()
+      },
+      deep: true
+    }
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+.goodRecommend {
+  padding-top: 20rpx;
+  .product-list {
+    position: relative;
+    padding: 0 13upx;
+    &-box {
+      display: flex;
+      flex-wrap: wrap;
+      flex-direction: row;
+      &.swiper{
+        height: 620upx;
+      }
+    }
+    &.product-swiper .product-list-box{
+      padding-left: 0;
+    }
+    &-item-warp{
+      margin: 0 0 20upx 0;
+    }
+    &-item {
+      width: 348upx;
+      padding: 0 7upx;
+      box-sizing: content-box;
+    }
+    &-img {
+      width: 348upx;
+      height: 348upx;
+      background-color: #d0d0d0;
+      border-radius: 10upx 10upx 0 0;
+      .img {
+        width: 100%;
+        height: 100%;
+        object-fit: contain;
+      }
+    }
+    &-info {
+      background-color: #FFFFFF;
+      //box-shadow: 0px 0px 15px 0px rgba(52, 52, 52, 0.15);
+      border-radius: 0 0 10upx 10upx;
+      padding: 20upx;
+      label{
+        font-weight: normal;
+      }
+      .product-name{
+        font-size: 28upx;
+        color: #333;
+        display: block;
+        overflow: hidden;
+        text-overflow:ellipsis;
+        white-space: nowrap;
+        margin-bottom: 18upx;
+        line-height: 40upx;
+      }
+      .shop-box{
+        background-color: #333333;
+        border-radius: 0upx 20upx 20upx 0upx;
+        line-height: 40upx;
+        display: inline-block;
+        height: 40upx;
+        margin-right: 10upx;
+        float: left;
+        .shop-name{
+          font-size: 20upx;
+          color: #FFEBC4;
+          padding: 0 8upx 0 12upx;
+          line-height: 40upx;
+          display: inline-block;
+          float: left;
+        }
+        .shop-logo{
+          border: 2upx solid #707070;
+          border-radius: 50%;
+          overflow: hidden;
+          float: right;
+          img{
+            width: 34upx;
+            height: 34upx;
+            display: block;
+          }
+        }
+      }
+      .buy-count{
+        color: #C5AA7B;
+        font-size: 20upx;
+        margin-bottom: 16upx;
+        border: 2upx solid #E4E5E6;
+        line-height: 36upx;
+        padding: 0 5upx;
+        display: inline-block;
+
+      }
+      .price-warp{
+        display: flex;
+        align-items: baseline;
+        line-height: 56upx;
+        .price{
+          color: #C83732;
+          font-size: 40upx;
+          margin-right: 20upx;
+        }
+        .original-price{
+          font-size: 24upx;
+          color: #ccc;
+          text-decoration: line-through;
+        }
+      }
+    }
+  }
+  .emptyCart-box {
+    margin-top: 70rpx;
+    .emptyCart-img {
+      width: 216rpx;
+      height: 156rpx;
+    }
+  }
+}
+</style>

+ 24 - 0
components/canvasShow/basics/assistDiv.vue

@@ -0,0 +1,24 @@
+<template>
+  <div class="div" :style="{backgroundColor:componentContent.bgColor,height:componentContent.height + 'rpx'}"></div>
+</template>
+
+<script>
+  export default {
+    name: 'assistDiv',
+    props: {
+      terminal: {
+        type: Number,
+        default: 4
+      },
+      componentContent: {
+        type: Object
+      }
+    }
+  }
+</script>
+
+<style scoped>
+  .div{
+    width: 100%;
+  }
+</style>

+ 129 - 0
components/canvasShow/basics/banner.vue

@@ -0,0 +1,129 @@
+<template>
+  <div class="banner" :class="'terminal' + terminal">
+    <swiper class="swiper" :indicator-dots="true" :autoplay="true" :style="{'height':componentContent.height + 'rpx'}">
+      <swiper-item class="banner-item" v-for="(item,index) in bannerList" :key="index" :style="{backgroundImage: 'url('+ item.bannerUrl +')','height':componentContent.height + 'rpx'}">
+        <div class="a-link" @click="jumpLink(item.linkObj)"><img class="img" :src="item.bannerUrl" v-show="item.bannerUrl" mode="widthFix"></div>
+      </swiper-item>
+    </swiper>
+    <div class="swiper-pagination" slot="pagination"></div>
+  </div>
+</template>
+
+<script>
+// import { directive, Swiper, SwiperSlide } from 'vue-awesome-swiper'
+// import 'swiper/css/swiper.css'
+  import {funMixin} from '../config/mixin'
+  export default {
+    name: 'cereBanner',
+    mixins: [funMixin],
+    data () {
+      return {
+        // swiperOption: {
+        //   autoplay: false, // 可选选项,自动滑动
+        //   loop: true,
+        //   pagination: {
+        //     el: '.swiper-pagination'
+        //   }
+        // }
+      }
+    },
+    props: {
+      terminal: {
+        type: Number,
+        default: 4
+      },
+      componentContent: {
+        type: Object
+      }
+    },
+    // components: {
+    //   Swiper,
+    //   SwiperSlide
+    // },
+    // directives: {
+    //   swiper: directive
+    // },
+    mounted() {
+      this.$forceUpdate() // 刷新轮播图
+    },
+    computed: {
+      bannerList: function () {
+        console.log(this.componentContent)
+        return this.componentContent.bannerData.filter(function (item) {
+          return item.bannerUrl
+        })
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+.banner{
+  .banner-item {
+    width: 100%;
+    img {
+      width: 100%;
+    }
+  }
+  &.terminal4{
+    ::v-deep .el-carousel{
+      height: 100%;
+      .el-carousel__container{
+        height: 100%;
+      }
+    }
+    .banner-item{
+      background-repeat: no-repeat;
+      background-position: center;
+      background-size: auto 100%;
+      img{
+        display: none;
+      }
+    }
+  }
+  ::v-deep .uni-swiper-dots{
+      display: flex;
+      justify-content: center;
+      bottom: 20upx;
+      width: 100%;
+     .uni-swiper-dot{
+        width: 30upx;
+        height: 4upx;
+        background: #fff;
+        opacity: 0.5;
+        border-radius: 2upx;
+        margin: 0 7.5upx;
+       .uni-swiper-dot-active{
+          opacity: 1;
+          width: 58upx;
+        }
+      }
+  }
+
+  /* wx-swiper-dots wx-swiper-dots-horizontal */
+  wx-swiper .wx-swiper-dot-active{
+    width: 40rpx;
+    border-radius: 10rpx;
+    background: #eb544d;
+  }
+
+  //.swiper-pagination{
+  //  display: flex;
+  //  justify-content: center;
+  //  bottom: 20upx;
+  //  width: 100%;
+  //  ::v-deep .swiper-pagination-bullet{
+  //    width: 30upx;
+  //    height: 4upx;
+  //    background: #fff;
+  //    opacity: 0.5;
+  //    border-radius: 2upx;
+  //    margin: 0 7.5upx;
+  //  }
+  //  ::v-deep .swiper-pagination-bullet-active{
+  //    opacity: 1;
+  //    width: 58upx;
+  //  }
+  //}
+}
+</style>

+ 129 - 0
components/canvasShow/basics/brandList.vue

@@ -0,0 +1,129 @@
+<template>
+  <div class="brand-list warp" :class="'terminal' + terminal">
+    <h2 class="hom-title" :style="{textAlign:componentContent.textAlign}">{{componentContent.title}}</h2>
+    <div class="content-warp">
+      <div class="ul clearfix">
+        <div class="li" v-for="(item, index) in componentContent.imgList" :key="index">
+          <a class="item a-link" @click="jumpLink(item.linkObj)">
+            <div class="imgBox">
+              <img :src="item.imgData" v-show="item.imgData" :alt="item.title">
+            </div>
+            <h4 class="h4">{{item.title}}</h4>
+          </a>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+  import {funMixin} from '../config/mixin'
+  export default {
+    name: 'brandList',
+    mixins: [funMixin],
+    props: {
+      terminal: {
+        type: Number,
+        default: 4
+      },
+      componentContent: {
+        type: Object
+      }
+    }
+  }
+</script>
+<style lang="scss" scoped>
+  .brand-list{
+    color: #fff;
+    padding: 20upx 0;
+    .hom-title{
+      font-size: 22upx;
+      color: #333;
+      line-height: 1em;
+      margin-bottom: 23upx;
+      font-weight: bold;
+    }
+    .content-warp{
+      display: flex;
+    }
+    .first{
+      width: 24%;
+      padding-top: 10upx;
+      .item{
+        background-color: #7A8594;
+        height: 336upx;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        .h3{
+          font-size: 30upx;
+          line-height: 46upx;
+          margin-bottom: 10upx;
+        }
+      }
+    }
+    .ul{
+      width: 100%;
+      .li{
+        width: 25%;
+        float: left;
+        padding: 10upx 0 0 10upx;
+        .item{
+          height: auto;
+          display: flex;
+          flex-direction: column;
+          justify-content: center;
+          .imgBox {
+            padding-bottom: 80%;
+            background-color: #cacaca;
+            position: relative;
+          }
+          .h4{
+            font-size: 18upx;
+            color: #333333;
+            text-align: center;
+            height: 40upx;
+            line-height: 40upx;
+          }
+          .p{
+            font-size: 12upx;
+            margin: 7upx 0 12upx;
+          }
+          img {
+            max-width: 100%;
+            height: 100%;
+            max-height: 100%;
+            position: absolute;
+            margin: auto;
+            top: 0;
+            right: 0;
+            bottom: 0;
+            left: 0;
+          }
+        }
+      }
+    }
+  }
+  .terminal1,.terminal2,.terminal3{
+    &.brand-list{
+      width: 710upx;
+      margin: 0 auto;
+      .content-warp{
+        display: block;
+      }
+      .first{
+        width: 100%;
+      }
+      .ul{
+        width: auto;
+        margin-left: -10upx;
+        .li{
+          width: 50%;
+          .item {
+            padding-left: 0;
+          }
+        }
+      }
+    }
+  }
+</style>

+ 110 - 0
components/canvasShow/basics/categoryList.vue

@@ -0,0 +1,110 @@
+<template>
+  <div class="category-list warp" :class="'terminal' + terminal">
+    <h2 class="hom-title" :style="{textAlign:componentContent.textAlign}">{{componentContent.title}}</h2>
+    <div class="content-warp">
+      <div class="ul clearfix" :class="{number5: componentContent.categoryData.length === 5}">
+        <div class="li" v-for="(item) of componentContent.categoryData" :key="item.id">
+            <a class="item a-link" @click="jumpCategory(item)">
+            <div class="imgBox">
+               <img ref="getHeight" :src="item.img" v-show="item.img" :alt="item.name">
+            </div>
+            </a>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import {funMixin} from '../config/mixin'
+  export default {
+    name: 'categoryList',
+    mixins: [funMixin],
+    props: {
+      terminal: {
+        type: Number,
+        default: 4
+      },
+      componentContent: {
+        type: Object
+      }
+    }
+  }
+</script>
+<style lang="scss" scoped>
+  .category-list{
+    padding: 20upx 0;
+    .hom-title{
+      font-size: 22upx;
+      color: #333;
+      line-height: 1em;
+      margin-bottom: 23upx;
+      font-weight: bold;
+      text-align: center;
+    }
+    .content-warp{
+      display: flex;
+      .ul{
+        width: 100%;
+        display: flex;
+        flex-wrap: wrap;
+        .li{
+          flex: 1;
+          padding: 10upx 0 0 10upx;
+          .item{
+            height: auto;
+            display: flex;
+            flex-direction: column;
+            justify-content: center;
+            .imgBox {
+              padding-bottom: 80%;
+              background-color: #cacaca;
+              position: relative;
+            }
+            img {
+              max-width: 100%;
+              height: 100%;
+              max-height: 100%;
+              position: absolute;
+              margin: auto;
+              top: 0;
+              right: 0;
+              bottom: 0;
+              left: 0;
+            }
+          }
+        }
+      }
+      .number5 {
+        display: block;
+        .li {
+          width: 25%;
+          float: left;
+        }
+        .li:nth-child(1) {
+          width: 50%;
+        }
+      }
+    }
+  }
+  @media screen and (max-width: 768px) {
+    .category-list .content-warp .ul .li{
+      flex: 0 0 50%;
+      width: 0;
+    }
+  }
+  .terminal1,.terminal2,.terminal3{
+    width: 710upx;
+    margin: 0 auto;
+    &.category-list .content-warp .ul{
+      margin: -15upx 0 0 -15upx;
+      .li{
+        flex: 0 0 50%;
+        width: 0;
+        &:nth-child(1){
+          width: 0;
+        }
+      }
+    }
+  }
+</style>

+ 107 - 0
components/canvasShow/basics/coupon/app/index.vue

@@ -0,0 +1,107 @@
+<template>
+  <div class="coupon">
+    <div class="coupon-list">
+      <div class="coupon-item" v-for="(item,index) in couponsData" :key="index" :class="item.state && item.state !== 3 && 'isReceive'">
+        <img class="coupon-item-bg" src="../../../static/images/coupon/bg-coupon.png" v-if="item.state && item.state === 3" mode="widthFix">
+        <img class="coupon-item-bg" src="../../../static/images/coupon/bg-coupon2.png" v-else mode="widthFix">
+        <div class="coupon-item-cont">
+        <div class="coupon-item-content">
+          <div class="coupon-item-price">
+            <div class="span" v-if="item.couponType !== 2">¥</div>
+            <div class="b" v-if="typeId !== 1">{{item.couponContent}}</div>
+            <div class="b" v-else>{{item.reduceMoney}}</div>
+            <div class="b" v-if="item.couponType == 2">折券</div>
+          </div>
+          <div class="coupon-item-date">{{item.startTime.split(' ')[0]}} - {{item.endTime.split(' ')[0]}}</div>
+          <div class="coupon-item-text">{{item.content}}</div>
+        </div>
+        <button v-if="item.state === 0" class="coupon-item-btn">已领取</button>
+        <button v-else-if="item.state === 1" class="coupon-item-btn">已使用</button>
+        <button v-else-if="item.state === 2" class="coupon-item-btn">已过期</button>
+        <button v-else @click="receiveCoupon(item)" class="coupon-item-btn">立即领取</button>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import {commonMixin} from '../mixin'
+export default {
+  mixins: [commonMixin]
+}
+</script>
+
+<style lang="scss" scoped>
+.coupon {
+  padding: 25upx;
+  &-list{
+    display: flex;
+    flex-wrap: wrap;
+  }
+  &-item{
+    width: 335upx;
+    height: 292upx;
+    margin-left: 28upx;
+    text-align: center;
+    position: relative;
+    margin-bottom: 10upx;
+    &-bg{
+      width: 100%;
+      height: 100%;
+    }
+    &-cont{
+      position: absolute;
+      width: 100%;
+      height: 100%;
+      left: 0;
+      top: 0;
+    }
+    &:nth-child(2n+1){
+      margin-left: 0;
+    }
+    &-content{
+      text-align: center;
+    }
+    &-price{
+      color: #C5AA7B;
+      margin: 26upx 0 10upx;
+      .span{
+        display: inline;
+        font-size: 36upx;
+        line-height: 68upx;
+      }
+      .b{
+        display: inline;
+        font-size: 50upx;
+        line-height: 68upx;
+      }
+    }
+    &-date,&-text{
+      font-size: 20upx;
+      line-height: 28upx;
+      color: #999999;
+    }
+    &-date{
+      margin-bottom: 8upx;
+    }
+    &-btn{
+      display: inline-block;
+      margin: 60upx auto 0;
+      height: 48upx;
+      line-height: 48upx;
+      background-color: #C5AA7B;
+      color: #fff;
+      font-size: 14upx
+    }
+    &.isReceive{
+      &-price{
+        color: #999;
+      }
+      &-btn{
+        background-color: #ccc;
+      }
+    }
+  }
+}
+</style>

+ 109 - 0
components/canvasShow/basics/coupon/mixin.js

@@ -0,0 +1,109 @@
+import api from '../../config/api'
+import NET from '../../../../utils/request'
+import { funMixin } from '../../config/mixin'
+
+export const commonMixin = {
+  name: 'textComponent',
+  mixins: [funMixin],
+  data () {
+    return {
+      couponsData: []
+    }
+  },
+  props: {
+    terminal: {
+      type: Number,
+      default: 4
+    },
+    typeId: {
+      type: Number,
+      default: 1
+    },
+    shopId: {
+      type: Number,
+      default: 0
+    },
+    componentContent: {
+      type: Object
+    }
+  },
+  watch: {
+    'componentContent': {
+      handler(newVal, oldVal) {
+        this.getData()
+      },
+      deep: true
+    }
+  },
+  created() {
+    this.getData()
+  },
+  methods: {
+    getData() {
+      const _ = this
+      if(_.componentContent.selectedCoupon && _.componentContent.selectedCoupon.length > 0){
+        let _url = ''
+        if(_.typeId === 1){
+          _url =`${api.getCoupons}?page=1&pageSize=99&ids=${_.componentContent.selectedCoupon}`
+        } else if(_.typeId === 3) {
+          _url =`${api.getShopCoupons}?page=1&pageSize=99&shopId=${_.shopId}&ids=${_.componentContent.selectedCoupon}`
+        }
+        const params = {
+          method: 'GET',
+          url: _url,
+        }
+        this.sendReq(params, (res) => {
+          _.couponsData = res.data.list
+          if(_.typeId === 1){
+            _.couponsData.forEach(item=>{
+              item.couponName = item.activityName
+              item.effectiveStart = item.activityStartTime
+              item.effectiveEnd = item.activityEndTime
+            })
+          }
+          if(JSON.stringify(_.componentContent.couponList) !== JSON.stringify(_.couponsData)){
+            _.componentContent.couponList = _.couponsData
+          }
+        })
+      } else {
+        _.couponsData = []
+      }
+    },
+    // 领取优惠券
+    receiveCoupon(item) {
+      const res = uni.getStorageSync('storage_key');
+      const token = res.token
+      if (token) {
+        var paramsData = {}
+        if(this.typeId === 1){
+          paramsData.couponId = item.couponId
+        } else if(this.typeId === 3) {
+          paramsData.shopCouponId = item.shopCouponId
+          paramsData.shopId = this.shopId
+        }
+        NET.request(api.takeCoupon, paramsData, 'POST').then(res => {
+          this.getData()
+          uni.showToast({
+            title:'领取成功',
+            icon:"success"
+          })
+        }).catch(res => {
+          if(res.data.code !== '200'){
+            uni.showToast({
+              title:res.data.message,
+              icon:"none"
+            })
+          }
+        })
+      } else {
+        uni.showToast({
+          title:'请先登录',
+          icon:"none"
+        })
+        uni.navigateTo({
+          url:'/pages_category_page2/userModule/login'
+        })
+      }
+    }
+  }
+}

+ 375 - 0
components/canvasShow/basics/coupon/pc/index.vue

@@ -0,0 +1,375 @@
+<template>
+  <div class="couponBox warp" :class="['terminal' + terminal,'arrange'+(componentContent.arrangeActiveIndex+1),'color'+(componentContent.colorActiveIndex+1)]">
+    <div class="couponListBox" v-if="componentContent.selectedCoupon">
+      <div class="listItemBox" v-for="(item,index) in couponsData" :key="index" :class="item.state && item.state !== 3 && 'isReceive'">
+        <div class="listItemBoxInner">
+          <div class="itemInfo">
+            <i class="flag" :class="'flag'+item.couponType"></i>
+            <div class="amount">
+              <b v-if="item.couponType !== 2">¥</b>
+              <span v-if="typeId !== 1">
+                {{item.couponContent}}
+                </span>
+              <span v-else>
+                {{item.reduceMoney}}
+                </span>
+              <b v-if="item.couponType == 2">折</b>
+            </div>
+            <div class="couponInfo">
+              <p>{{item.content}}</p>
+            </div>
+          </div>
+          <!--            <div class="itemInfo" v-else>-->
+          <!--              <div class="amount">-->
+          <!--                <b>¥</b><span>{{item.reduceMoney}}<i>满减卷</i></span>-->
+          <!--              </div>-->
+          <!--              <div class="couponInfo">-->
+          <!--                <p>{{item.content}}</p>-->
+          <!--              </div>-->
+          <!--            </div>-->
+          <div v-if="item.state === 0" class="receiveBtn">
+            <span>己领取</span>
+          </div>
+          <div v-else-if="item.state === 1" class="receiveBtn">
+            <span>已使用</span>
+          </div>
+          <div v-else-if="item.state === 2" class="receiveBtn">
+            <span>已过期</span>
+          </div>
+          <div v-else class="receiveBtn" @click="receiveCoupon(item)">
+            <span>立即领取</span>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import {commonMixin} from '../mixin'
+export default {
+  mixins: [commonMixin]
+}
+</script>
+
+<style lang="scss" scoped>
+.couponBox {
+  min-height: 177px;
+  margin: 0 auto;
+  padding: 20px 0;
+  .couponListBox {
+    flex-wrap: wrap;
+    /**默认**/
+    .listItemBox {
+      //background-image:url('../../../static/images/coupon/border_L1.png'), url('../../../static/images/coupon/border_R1.png');
+      //background-repeat: no-repeat, no-repeat;
+      //background-position: left top, right top;
+      box-sizing: border-box;
+      margin-bottom: 20px;
+      .listItemBoxInner {
+        width: 100%;
+        height: 150px;
+        display: flex;
+        background-color: #FAFAFA;
+        align-items: center;
+        justify-content: space-between;
+        position: relative;
+        .flag{
+          display: block;
+          width: 71px;
+          height: 71px;
+          background-repeat: no-repeat;
+          position: absolute;
+          top: 0;
+          left: 0;
+          &.flag1{
+            background-image: url("../../../static/images/coupon/flag-coupon.png");
+          }
+          &.flag2{
+            background-image: url("../../../static/images/coupon/flag-coupon2.png");
+          }
+        }
+        .itemInfo {
+          flex: 1;
+        }
+        .amount {
+          max-width: 90%;
+          margin: 0 auto;
+          display: flex;
+          align-items: baseline;
+          justify-content: center;
+          line-height: 60px;
+          b {
+            font-size: 30px;
+          }
+          span {
+            font-size: 60px;
+            font-weight: bold;
+          }
+          i {
+            font-style: normal;
+            font-size: 18px;
+            margin-left: 5px;
+          }
+        }
+        .couponInfo {
+          text-align: center;
+          p {
+            display: inline-block;
+            padding: 0 42px;
+            text-align: center;
+            font-size: 18px;
+            line-height: 40px;
+            color: #C83732;
+            background: #F5E5E5;
+          }
+        }
+      }
+      .receiveBtn {
+        width: 72px;
+        margin-right: 5px;
+        background: #C5AA7B;
+        height: 100%;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        cursor: pointer;
+        position: relative;
+        &:before,&:after{
+          content: '';
+          display: block;
+          width: 25px;
+          height: 25px;
+          background-color: #fff;
+          position: absolute;
+          left: -12.5px;
+          border-radius: 50%;
+        }
+        &:before{
+          top: -12.5px;
+        }
+        &:after{
+          bottom: -12.5px;
+        }
+        span {
+          color: #FFFFFF !important;
+          writing-mode: vertical-lr;
+          font-size: 19px;
+        }
+      }
+      &.isReceive {
+        //background-image:url('../../../static/images/coupon/border_L4.png'), url('../../../static/images/coupon/border_R4.png');
+        .listItemBoxInner {
+          .flag{
+            &.flag1{
+              background-image: url("../../../static/images/coupon/flag-coupon-r.png");
+            }
+            &.flag2{
+              background-image: url("../../../static/images/coupon/flag-coupon2-r.png");
+            }
+          }
+          .itemInfo {
+            color: #999;
+            .couponInfo {
+              p {
+                color: #999;
+                background: #F1F1F1;
+              }
+            }
+          }
+          .receiveBtn {
+            cursor: auto;
+            background: #999;
+          }
+        }
+      }
+    }
+  }
+
+  @mixin cardColor($bgColor: #FF3737,$fontColor: #fff) {
+    .couponListBox {
+      .listItemBox {
+        .listItemBoxInner{
+          background: $bgColor;
+        }
+        .itemInfo {
+          .amount {
+            b {
+              color: #EC4B42;
+            }
+            span {
+              color: #EC4B42;
+              i {
+                color: #EC4B42;
+              }
+            }
+          }
+          .couponInfo {
+            color:#EC4B42;
+          }
+        }
+        .receiveBtn {
+          span {
+            color: #EC4B42;
+          }
+        }
+        &.cardStyle3{
+          .itemInfo {
+            .amount {
+              span {
+                color: $bgColor;
+                i {
+                  color: $bgColor;
+                }
+              }
+            }
+            .couponInfo {
+              color:$bgColor;
+            }
+          }
+        }
+        &.cardStyle4{
+          border: 2px solid $bgColor;
+          padding: 5px;
+          .listItemBoxInner{
+            padding: 20px 15px;
+            height: 85px;
+            border: 1px solid $bgColor;
+          }
+          .itemInfo {
+            .amount {
+              span {
+                color: $bgColor;
+                i {
+                  color: $bgColor;
+                }
+              }
+            }
+            .couponInfo {
+              color:$bgColor;
+            }
+          }
+          .receiveBtn {
+            border-left: 1px $bgColor dashed;
+            span {
+              color: $bgColor;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  &.arrange1{
+
+  }
+  &.arrange2{
+    max-width: 100%;
+    .couponListBox{
+      display: flex;
+      flex-wrap: wrap;
+      justify-content: space-between;
+      .listItemBox{
+        width: 48%;
+      }
+    }
+  }
+  &.arrange3{
+    max-width: 100%;
+    .couponListBox{
+      display: flex;
+      flex-wrap: wrap;
+      justify-content: space-between;
+      .listItemBox{
+        width: 32%;
+      }
+    }
+  }
+  &.arrange4{
+    max-width: 100%;
+    .couponListBox{
+      display: flex;
+      overflow: hidden;
+      .listItemBox{
+        width: 268px;
+        flex: 0 0 268px;
+        margin:0 25px 25px 0;
+        &:nth-child(4n){
+          margin-right: 0;
+        }
+      }
+    }
+  }
+  //&.color1{
+  //  .listItemBox {
+  //    background-image:url('../../../static/images/coupon/border_L1.png'), url('../../../static/images/coupon/border_R1.png');
+  //    .listItemBoxInner {
+  //      border-top: 1px solid #EC4B42;
+  //      border-bottom: 1px solid #EC4B42;
+  //      .itemInfo {
+  //        color: #EC4B42;
+  //        .amount {
+  //          border-bottom: 1px solid #EC4B42;
+  //        }
+  //        .couponInfo {
+  //          p {
+  //            color: #EC4B42;
+  //          }
+  //        }
+  //      }
+  //      .receiveBtn {
+  //        background: #EC4B42;
+  //      }
+  //    }
+  //  }
+  //}
+
+  &.color2{
+    .listItemBox {
+      background-image:url('../../../static/images/coupon/border_L2.png'), url('../../../static/images/coupon/border_R2.png');
+      .listItemBoxInner {
+        border-top: 1px solid #FF7800;
+        border-bottom: 1px solid #FF7800;
+        .itemInfo {
+          color: #FF7800;
+          .amount {
+            border-bottom: 1px solid #FF7800;
+          }
+          .couponInfo {
+            p {
+              color: #FF7800;
+            }
+          }
+        }
+        .receiveBtn {
+          background: #FF7800;
+        }
+      }
+    }
+  }
+
+  &.color3{
+    .listItemBox {
+      background-image:url('../../../static/images/coupon/border_L3.png'), url('../../../static/images/coupon/border_R3.png');
+      .listItemBoxInner {
+        border-top: 1px solid #86A7FF;
+        border-bottom: 1px solid #86A7FF;
+        .itemInfo {
+          color: #86A7FF;
+          .amount {
+            border-bottom: 1px solid #86A7FF;
+          }
+          .couponInfo {
+            p {
+              color: #86A7FF;
+            }
+          }
+        }
+        .receiveBtn {
+          background: #86A7FF;
+        }
+      }
+    }
+  }
+}
+</style>

+ 243 - 0
components/canvasShow/basics/custom.vue

@@ -0,0 +1,243 @@
+<template>
+  <div class="custom warp" :class="'terminal' + terminal">
+    <div class="rowLayout" v-if="componentContent.layoutType ==='L1' || componentContent.layoutType ==='L2' || componentContent.layoutType ==='L3' || componentContent.layoutType ==='L4'">
+      <div class="customLayout" :style="{'padding':'0 ' + componentContent.pageSpacing + 'px','marginLeft':(-componentContent.imgClearance) +'px'}">
+        <div class="ul clearfix" :class="'layout'+componentContent.layoutType">
+          <a class="a-link" @click="jumpLink(item.linkObj)">
+          <div class='li' v-if="item.src" v-for="(item,index) of componentContent.imgData" :key="index" :style="{'backgroundImage':'url(' + item.src + ')','width':getItemValue(item.width) + '%','height':getItemValue(item.height) + '%','left':getItemValue(item.left) + '%','top':getItemValue(item.top) + '%','paddingLeft':componentContent.imgClearance +'px'}">
+<!--           <img class="img" :src="item.src" v-if="item.src" mode="widthFix">-->
+          </div>
+          </a>
+        </div>
+      </div>
+    </div>
+    <div class="boxLayout" v-else :style="{'padding':'0 ' + componentContent.pageSpacing + 'px','paddingBottom':componentContent.maxH !== 0?getItemValue(componentContent.maxH) + '%': '100%'}">
+      <div class="boxLayoutInner">
+        <div class="boxWarp">
+          <div class="customLayout" :style="{'marginLeft':(-componentContent.imgClearance) +'px','top':(-componentContent.imgClearance) +'px'}">
+            <div class="ul clearfix" :class="'layout'+componentContent.layoutType">
+              <a class="a-link" @click="jumpLink(item.linkObj)">
+                <div class='li' v-for="(item,index) of componentContent.imgData" v-if="item.src" :key="index" :style="{'backgroundImage':'url(' + item.src + ')','width':getItemValue(item.width) + '%','height':getItemValue(item.height) + '%','left':getItemValue(item.left) + '%','top':getItemValue(item.top) + '%','padding':componentContent.imgClearance +'px 0 0 ' + componentContent.imgClearance +'px'}">
+<!--                  <img class="img" :src="item.src" v-if="item.src" mode="widthFix">-->
+                </div>
+              </a>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+  import {funMixin} from '../config/mixin'
+  export default {
+    name: 'customComponent',
+    mixins: [funMixin],
+    data () {
+      return {
+      }
+    },
+    props: {
+      terminal: {
+        type: Number,
+        default: 4
+      },
+      componentContent: {
+        type: Object
+      }
+    },
+    methods: {
+      // 计算生成格子百分比
+      getItemValue (val) {
+        const density = parseInt(this.componentContent.density)
+        if (val === 0 || density === 0) {
+          return 0
+        }
+        return (val / density * 10000 / 100.00)// 小数点后两位百分比
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .custom{
+	  width: 710upx;
+	  margin: 0 auto;
+    .boxLayout{
+      position: relative;
+      .boxLayoutInner{
+        padding-bottom: 100%;
+        position: absolute;
+        width: 100%;
+        left: 0;
+        top: 0;
+      }
+      .boxWarp{
+        position: absolute;
+        width: 100%;
+        height: 100%;
+        left: 0;
+        top: 0;
+        overflow: hidden;
+      }
+    }
+    .customLayout{
+      position: relative;
+      .ul{
+        display: flex;
+        flex-wrap: wrap;
+        position: relative;
+      }
+      .li{
+		 box-sizing: border-box;
+        background-position: center center;
+        background-size: contain;
+        background-repeat: no-repeat;
+        .img{
+          width: 100%;
+          display: block;
+        }
+      }
+      .layoutL1 .li{
+        flex: 0 0 100%;
+      }
+      .layoutL2 .li{
+        flex: 0 0 50%;
+      }
+      .layoutL3 .li{
+        flex: 0 0 33.3%;
+      }
+      .layoutL4 .li{
+        flex: 0 0 25%;
+      }
+      .layoutT2B2{
+        padding-bottom: 100%;
+        .li{
+          width: 50%;
+          height: 50%;
+          position: absolute;
+          .img{
+            width: 100%;
+            height: 100%;
+          }
+          &:nth-child(1){
+            left: 0;
+            top: 0;
+          }
+          &:nth-child(2){
+            right: 0;
+            top: 0;
+          }
+          &:nth-child(3){
+            left: 0;
+            bottom: 0;
+          }
+          &:nth-child(4){
+            right: 0;
+            bottom: 0;
+          }
+        }
+      }
+      .layoutL1R2{
+        padding-bottom: 100%;
+        .li{
+          width: 50%;
+          height: 50%;
+          position: absolute;
+          .img{
+            width: 100%;
+            height: 100%;
+          }
+          &:nth-child(1){
+            height: 100%;
+            left: 0;
+            top: 0;
+          }
+          &:nth-child(2){
+            right: 0;
+            top: 0;
+          }
+          &:nth-child(3){
+            right: 0;
+            bottom: 0;
+          }
+        }
+      }
+      .layoutT1B2{
+        padding-bottom: 100%;
+        .li{
+          width: 50%;
+          height: 50%;
+          position: absolute;
+          .img{
+            width: 100%;
+            height: 100%;
+          }
+          &:nth-child(1){
+            width: 100%;
+            left: 0;
+            top: 0;
+          }
+          &:nth-child(2){
+            left: 0;
+            bottom: 0;
+          }
+          &:nth-child(3){
+            right: 0;
+            bottom: 0;
+          }
+        }
+      }
+      .layoutL1T1B2{
+        padding-bottom: 50%;
+        .li{
+          position: absolute;
+          .img{
+            width: 100%;
+            height: 100%;
+          }
+          &:nth-child(1){
+            width: 50%;
+            height: 100%;
+            left: 0;
+            top: 0;
+          }
+          &:nth-child(2){
+            right: 0;
+            top: 0;
+            width: 50%;
+            height: 50%;
+          }
+          &:nth-child(3){
+            left: 50%;
+            bottom: 0;
+            width: 25%;
+            height: 50%;
+          }
+          &:nth-child(4){
+            right: 0;
+            bottom: 0;
+            width: 25%;
+            height: 50%;
+          }
+        }
+      }
+      .layoutaverage{
+        padding-bottom: 100%;
+        .li{
+          position: absolute;
+          .img{
+            width: 100%;
+            height: 100%;
+          }
+        }
+      }
+      // #ifdef MP
+      .layoutaverage{
+        padding-bottom: 91%;
+      }
+      // #endif
+    }
+  }
+</style>

+ 265 - 0
components/canvasShow/basics/discount/app/index.vue

@@ -0,0 +1,265 @@
+<template>
+  <div class="hom-pro-list">
+    <div class="title">
+      <img class="title-img" src="../../../static/images/discount/img-title.png" alt="限时折扣" mode="widthFix"/>
+    </div>
+    <div v-if="componentContent.arrangeType == '横向滑动' && productData.products.length > 2" class="product-list">
+      <swiper ref="mySwiper" class="swiper product-list-box" :indicator-dots="true" :autoplay="true" :display-multiple-items="2">
+        <swiper-item class="swiper-slide product-list-item-warp" v-for="(item,index) in productData.products.slice(0, 10)" :key="index" @click="jumpProductDetail(item)">
+          <div class="product-list-item">
+          <div class="product-list-img">
+            <img v-show="item.image" class="img" :src="item.image">
+          </div>
+          <div class="product-list-info">
+            <label class="product-name">{{item.productName}}</label>
+            <div>
+              <div class="flag">
+                <img src="../../../static/images/discount/flag-discount2.png" alt="限时折扣" mode="widthFix"/>
+              </div>
+              <label class="buy-count">剩余{{item.stockNumber}}件</label>
+            </div>
+            <div class="price-warp">
+              <div class="price">
+                ¥ {{item.price}}
+              </div>
+              <div class="original-price">
+                ¥ {{item.originalPrice}}
+              </div>
+            </div>
+          </div>
+          </div>
+        </swiper-item>
+      </swiper>
+<!--      <div class="pagination discount-pagination" slot="pagination"></div>-->
+    </div>
+    <div v-else class="product-list">
+      <div class="product-list-box" >
+        <div class="product-list-item-warp" v-for="(item,index) in productData.products" :key="index" @click="jumpProductDetail(item)">
+          <div class="product-list-item">
+          <div class="product-list-img">
+            <img v-show="item.image" class="img" :src="item.image">
+          </div>
+          <div class="product-list-info">
+            <label class="product-name">{{item.productName}}</label>
+            <div>
+              <div class="flag">
+                <img src="../../../static/images/discount/flag-discount2.png" alt="限时折扣" mode="widthFix"/>
+              </div>
+              <label class="buy-count">剩余{{item.stockNumber}}件</label>
+            </div>
+            <div class="price-warp">
+              <div class="price">
+                ¥ {{item.price}}
+              </div>
+              <div class="original-price">
+                ¥ {{item.originalPrice}}
+              </div>
+            </div>
+          </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <button v-show="componentContent.showMore" class="btn-more" @click="jumpDiscount(productData)">查看全部 <span class="icon iconfont icon-arrow-right"></span></button>
+  </div>
+</template>
+
+<script>
+import {commonMixin} from '../mixin'
+export default {
+  mixins: [commonMixin],
+  data () {
+    return {
+      // swiperOption: {
+      //   slidesPerView: 2,
+      //   spaceBetween: 14,
+      //   autoplay: false, // 可选选项,自动滑动
+      //   loop: true,
+      //   pagination: {
+      //     el: '.discount-pagination',
+      //     clickable: true
+      //   },
+      //   navigation: {
+      //     nextEl: '.swiper-button-next',
+      //     prevEl: '.swiper-button-prev'
+      //   }
+      // }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.hom-pro-list{
+  padding: 20px 0;
+  .title{
+    text-align: center;
+    margin-bottom: 20upx;
+    .title-img{
+      width: 203upx;
+    }
+  }
+  /**多行多列**/
+  .product-list {
+    position: relative;
+    &-box {
+      display: flex;
+      flex-wrap: wrap;
+      flex-direction: row;
+      padding-left: 20upx;
+      &.swiper{
+        height: 620upx;
+      }
+    }
+    &.product-swiper .product-list-box{
+      margin: 0 20upx;
+      padding-left: 0;
+    }
+    &-item-warp{
+      margin: 0 0 20upx 0;
+    }
+    &-item {
+      width: 348upx;
+      padding: 0 7upx;
+      box-sizing: content-box;
+    }
+    &-img {
+      width: 348upx;
+      height: 348upx;
+      background-color: #f5f5f5;
+      border-radius: 10upx 10upx 0 0;
+      .img {
+        width: 100%;
+        height: 100%;
+        object-fit: contain;
+      }
+    }
+    &-info {
+      background-color: #FFFFFF;
+      //box-shadow: 0px 0px 15px 0px rgba(52, 52, 52, 0.15);
+      border-radius: 0 0 10upx 10upx;
+      padding: 20upx;
+      label{
+        font-weight: normal;
+      }
+      .product-name{
+        font-size: 28upx;
+        color: #333;
+        display: block;
+        overflow: hidden;
+        text-overflow:ellipsis;
+        white-space: nowrap;
+        margin-bottom: 18upx;
+        line-height: 40upx;
+      }
+      .shop-box{
+        background-color: #333333;
+        border-radius: 0px 20upx 20upx 0upx;
+        line-height: 40upx;
+        display: inline-block;
+        height: 40upx;
+        margin-right: 10upx;
+        .shop-name{
+          font-size: 20upx;
+          color: #FFEBC4;
+          padding: 0 8px 0 12upx;
+        }
+        .shop-logo{
+          border: 2px solid #707070;
+          border-radius: 50%;
+          overflow: hidden;
+          float: right;
+          img{
+            width: 34upx;
+            height: 34upx;
+            display: block;
+          }
+        }
+      }
+      .flag{
+        float: left;
+        margin-right: 20upx;
+        img{
+          width: 100upx;
+        }
+      }
+      .buy-count{
+        color: #C5AA7B;
+        font-size: 20upx;
+        border: 2upx solid #E4E5E6;
+        line-height: 40upx;
+        padding: 0 5upx;
+        display: inline-block;
+      }
+      .price-warp{
+        display: flex;
+        align-items: baseline;
+        line-height: 56upx;
+        margin-top: 16upx;
+        .price{
+          color: #C83732;
+          font-size: 40upx;
+          margin-right: 20upx;
+        }
+        .original-price{
+          font-size: 24upx;
+          color: #ccc;
+          text-decoration: line-through;
+        }
+      }
+    }
+    //::v-deep .swiper-pagination-bullet{
+    //  display: none;
+    //}
+  }
+}
+
+::v-deep .uni-swiper-dots{
+  display: flex;
+  justify-content: center;
+  padding: 10upx 0;
+  .uni-swiper-dot{
+    width: 10upx;
+    height: 10upx;
+    background: #333333;
+    opacity: 0.3;
+    border-radius: 5upx;
+    margin: 0 5upx;
+    &-active{
+      width: 20upx;
+      height: 10upx;
+      opacity: 1;
+    }
+  }
+}
+//.pagination{
+//  display: flex;
+//  justify-content: center;
+//  padding: 20upx 0;
+//  ::v-deep .swiper-pagination-bullet{
+//    width: 10upx;
+//    height: 10upx;
+//    background: #333333;
+//    opacity: 0.3;
+//    border-radius: 5upx;
+//    margin: 0 5upx;
+//  }
+//  ::v-deep .swiper-pagination-bullet-active{
+//    width: 20upx;
+//    height: 10upx;
+//    opacity: 1;
+//  }
+//}
+.btn-more {
+  width: 170upx;
+  height: 54upx;
+  line-height: 54upx;
+  border: 2upx solid #C5AA7B;
+  color: #C5AA7B;
+  font-size: 24upx;
+  background-color: transparent;
+  margin: 20upx auto 0;
+  display: block;
+}
+
+</style>

+ 110 - 0
components/canvasShow/basics/discount/mixin.js

@@ -0,0 +1,110 @@
+// import { directive, Swiper, SwiperSlide } from 'vue-awesome-swiper'
+// import 'swiper/css/swiper.css'
+import api from '../../config/api'
+import {funMixin} from '../../config/mixin'
+
+export const commonMixin = {
+  name: 'discountList',
+  mixins: [funMixin],
+  data () {
+    return {
+      value: 100,
+      productData: {
+        products: []
+      },
+      count: [],
+      timer: null
+    }
+  },
+  props: {
+    terminal: {
+      type: Number,
+      default: 4
+    },
+    typeId: {
+      type: Number,
+      default: 1
+    },
+    shopId: {
+      type: Number,
+      default: 0
+    },
+    componentContent: {
+      type: Object
+    }
+  },
+  // components: {
+  //   Swiper,
+  //   SwiperSlide
+  // },
+  // directives: {
+  //   swiper: directive
+  // },
+  watch: {
+    'componentContent': {
+      handler(newVal, oldVal) {
+        this.getData()
+      },
+      deep: true
+    }
+  },
+  created() {
+    this.getData()
+  },
+  methods: {
+    getData() {
+      const _ = this
+      if(_.componentContent.discountId){
+        var _url= ''
+        if(this.typeId === 1){
+          _url= `${api.getMinDiscount}?ids=${_.componentContent.discountId}`
+        }
+        if(this.typeId === 3){
+          _url= `${api.getDiscounts}?shopId=${_.shopId}&ids=${_.componentContent.discountId}`
+        }
+        const params = {
+          method: 'GET',
+          url: _url,
+        }
+        this.sendReq(params, (res) => {
+          if(res.data.length> 0){
+            _.productData = res.data[0]
+            // 只有进行中和未开始活动, 用倒计时
+            if(_.productData.state !==2) {
+              this.timer = setInterval(()=>{
+                _.getTime(_.productData)
+              }, 1000)
+            }
+          }
+        })
+      } else {
+        _.productData = {
+          products:[]
+        }
+      }
+    },
+    getTime(info) {
+      const date = new Date().getTime()
+      const startTime = new Date(info.startTime.replace(/-/g,'/')).getTime()
+      const endTime = new Date(info.endTime.replace(/-/g,'/')).getTime()
+      if(startTime > date) {
+        this.countDown(startTime-date,true) // 未开始
+      } else {
+        this.countDown(endTime-date) // 进行中
+      }
+
+    },
+    countDown(time, isStart) {
+      const fn = (v) =>  v < 10 ? `0${v}` : v
+      const t = parseInt(time / 1000)
+      const text = isStart ? '开始' : '结束'
+      const hour = parseInt(t / 3600)
+      const min = parseInt((t % 3600) / 60)
+      const s = t % 60
+      this.count = [text, fn(hour), fn(min), fn(s)]
+    }
+  },
+  beforeDestroy() {
+    clearInterval(this.timer)
+  }
+}

+ 247 - 0
components/canvasShow/basics/discount/pc/index.vue

@@ -0,0 +1,247 @@
+<template>
+  <div class="discount">
+    <div class="discount-top">
+      <div class="discount-top-text">全场5折起</div>
+      <div class="discount-top-time">
+<!--        距{{count[0]}}-->
+        距离本场结束还有
+        <div class="time"><span>{{count[1]}}</span>:<span>{{count[2]}}</span>:<span>{{count[3]}}</span></div></div>
+    </div>
+    <div class="discount-more" :style="{backgroundImage: 'url('+ componentContent.moreBg +')'}">
+      <div class="discount-more-overlay">
+        <button class="btn-more" @click="jumpDiscount(productData)">查看全部</button>
+      </div>
+    </div>
+    <div class="discount-list">
+      <div class="swiper-button-prev"></div>
+      <div class="swiper-button-next"></div>
+      <swiper class="products-swiper" :options="swiperOption">
+        <swiper-slide class="products-swiper-slide item" v-for="(item,index) in productData.products" :key="index">
+          <div class="a-link" @click="jumpProductDetail(item)">
+            <div class="itemImgBox">
+              <div class="imgBox">
+                <el-image
+                  :src="item.image"
+                  fit="contain"></el-image>
+              </div>
+            </div>
+            <div class="text">
+              <h4 class="h4">{{item.productName}}</h4>
+              <div class="priceBox">
+                <span class="discount" v-if="item.originalPrice">¥{{item.originalPrice}}</span>
+                <dl>
+                  <dt><img src="../../../static/images/discount/flag-discount.png" alt="折扣价"></dt>
+                  <dd>
+                    ¥{{item.price}}
+                  </dd>
+                </dl>
+              </div>
+            </div>
+          </div>
+        </swiper-slide>
+      </swiper>
+    </div>
+  </div>
+</template>
+
+<script>
+import {commonMixin} from '../mixin'
+export default {
+  mixins: [commonMixin],
+  data () {
+    return {
+      swiperOption: {
+        slidesPerView: 3, // 显示数量
+        spaceBetween: 13, // 间隔
+        autoplay: false, // 可选选项,自动滑动
+        loop: true,
+        pagination: {
+          el: '.discount-pagination'
+        },
+        navigation: {
+          nextEl: '.swiper-button-next',
+          prevEl: '.swiper-button-prev'
+        }
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.discount{
+  width: 1200px;
+  max-width: 100%;
+  margin: 0 auto;
+  overflow: hidden;
+  &-top{
+    height: 250px;
+    padding-top: 96px;
+    margin-bottom: 15px;
+    background: url("../../../static/images/discount/bg-discount-top.png") no-repeat;
+    &-text{
+      background: url("../../../static/images/discount/bg-discount-top-text.png") no-repeat;
+      width: 176px;
+      height: 83px;
+      padding-top: 13px;
+      line-height: 50px;
+      font-size: 25px;
+      color: #fff;
+      margin: 0px auto 18px;
+      text-align: center;
+    }
+    &-time{
+      margin: 0 auto;
+      text-align: center;
+      font-size: 16px;
+      color: #FFEBC4;
+      .time{
+        font-size: 20px;
+        color: #999;
+        display: inline-block;
+        span{
+          display: inline-block;
+          line-height: 40px;
+          padding: 0 9px;
+          margin: 0 5px;
+          background-color: #343434;
+          color: #fff;
+        }
+      }
+    }
+  }
+  &-list{
+    margin-right: 303px;
+    position: relative;
+    .swiper-button-prev,.swiper-button-next{
+      width: 95px;
+      height: 95px;
+      position: absolute;
+      cursor:pointer;
+      top: 115px;
+      background-repeat: no-repeat;
+      &:after{
+        content: '';
+      }
+    }
+    .swiper-button-prev{
+      left: -22px;
+      background: url('../../../static/images/btn-prev2.png');
+    }
+    .swiper-button-next{
+      right: -22px;
+      background: url('../../../static/images/btn-next2.png');
+    }
+    .a-link{
+      cursor: pointer;
+      &:hover{
+        box-shadow: 3px 4px 20px 0px rgba(186, 186, 186, 0.5);
+      }
+      .itemImgBox {
+        height: auto;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        .imgBox {
+          padding-bottom: 100%;
+          background-color: #cacaca;
+          position: relative;
+          .el-image {
+            width: 100%;
+            height: 100%;
+            position: absolute;
+            top: 0;
+            left: 0;
+          }
+        }
+      }
+      .text{
+        padding:25px 20px 17px;
+        text-align: center;
+        //height: 180px;
+        .h4{
+          font-size: 18px;
+          line-height: 24px;
+          overflow: hidden;
+          text-overflow:ellipsis;
+          white-space: nowrap;
+          color: #333333;
+          //max-height: 48px;
+        }
+        .p{
+          color: #999;
+          font-size: 16px;
+          overflow: hidden;
+          text-overflow:ellipsis;
+          white-space: nowrap;
+          padding-top: 18px;
+          position: relative;
+          margin-top: 8px;
+          &:after{
+            position: absolute;
+            top: 0;
+            left: 50%;
+            margin-left: -80px;
+            width: 160px;
+            height: 2px;
+            background: #F0F0F0;
+            content: '';
+          }
+        }
+        .priceBox {
+          dl {
+            display: inline-block;
+            min-width: 130px;
+            dt{
+              float: left;
+              img{
+                display: block;
+              }
+            }
+            dd{
+              border: 1px solid #F3F4F5;
+              font-size: 25px;
+              line-height: 34px;
+              color: #C83732;
+              margin-left: 57px;
+              padding: 0 10px;
+            }
+          }
+          span.discount {
+            display: block;
+            font-size: 18px;
+            line-height: 24px;
+            padding: 15px 0 11px;
+            color: #ccc;
+            text-decoration: line-through;
+          }
+        }
+      }
+    }
+  }
+  &-more{
+    width: 290px;
+    height: 466px;
+    float: right;
+    position: relative;
+    &-overlay{
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      background-color: rgba(0,0,0,0.6);
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      .btn-more{
+        width: 130px;
+        height: 41px;
+        background-color: #fff;
+        font-size: 18px;
+        color: #C5AA7B;
+      }
+    }
+  }
+}
+</style>

+ 186 - 0
components/canvasShow/basics/group/app/index.vue

@@ -0,0 +1,186 @@
+<template>
+  <div class="group-list">
+    <div class="group-warp">
+      <div class="title">
+        <label>
+          <img class="title-img" src="../../../static/images/group/img-title.png" alt="拼团专区" mode="widthFix"/>
+        </label>
+        <a v-show="componentContent.showMore" class="btn-all a-link" @click="jumpGroupWorks(productData)">更多<i class="iconfont icon-arrow-right"></i></a>
+      </div>
+      <div>
+      <swiper class="swiper pro-box" :indicator-dots="true" :autoplay="true" :display-multiple-items="3">
+        <swiper-item class="swiper-slide pro-item-warp" v-for="(item,index) in productData.products" :key="index" @click="jumpProductDetail(item)">
+         <div class="pro-item-inner">
+           <div class="pro-item">
+             <div class="pro-item-img">
+               <img v-show="item.image" class="img" :src="item.image">
+             </div>
+             <div class="pro-item-info">
+               <label class="name">{{item.productName}}</label>
+               <div class="price">
+                 <label class="unit">¥ </label>
+                 <label class="val"> {{item.price}}</label>
+               </div>
+               <label class="buyCount">{{item.workUsers?item.workUsers:0}}人已拼</label>
+             </div>
+           </div>
+         </div>
+        </swiper-item>
+      </swiper>
+<!--      <div class="pagination group-pagination"></div>-->
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import {commonMixin} from '../mixin'
+export default {
+  mixins: [commonMixin],
+  data () {
+    return {
+      // swiperOption: {
+      //   slidesPerView: 3, // 显示数量
+      //   spaceBetween: 0, // 间隔
+      //   autoplay: false, // 可选选项,自动滑动
+      //   loop: true,
+      //   pagination: {
+      //     el: '.group-pagination'
+      //   },
+      //   navigation: {
+      //     nextEl: '.swiper-button-next',
+      //     prevEl: '.swiper-button-prev'
+      //   }
+      // }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+  .group-list{
+    padding: 30upx 20upx 60upx;
+    .group-warp{
+      width: 710upx;
+      height: 528upx;
+      padding: 0 2upx;
+      background: #333333;
+      box-shadow: 0px 20upx 30upx rgba(0, 0, 0, 0.3);
+      opacity: 1;
+      border-radius: 20upx;
+    }
+    .title{
+      display: flex;
+      align-items:center;
+      position: relative;
+      padding: 40upx 0 25upx 20upx;
+      .title-img{
+        width: 189upx;
+      }
+      .btn-all{
+        position: absolute;
+        right: 8upx;
+        top: 40upx;
+        line-height: 33upx;
+        padding-right: 25upx;
+        font-size: 24upx;
+        color: #FFEBC4;
+        .iconfont{
+          content: '';
+          font-size: 26upx;
+          position: absolute;
+          right: 0;
+          top: 0;
+        }
+      }
+    }
+    .pro-box{
+      height: 432upx;
+      display: flex;
+      &.swiper-disabled{
+        .uni-swiper-wrapper{
+          position: static;
+        }
+      }
+      .pro-item-warp{
+        width: 236px;
+      }
+      .pro-item{
+        width: 220upx;
+        height: 382upx;
+        background: #FFFFFF;
+        .pro-item-img{
+          .img{
+            width: 100%;
+            height: 220upx;
+          }
+        }
+        &-inner{
+          padding: 0 8upx;
+        }
+        .pro-item-info{
+          text-align: center;
+          padding: 0px 10upx 20upx;
+          .name{
+            font-size: 24upx;
+            font-weight: normal;
+            color: #FFEBC4;
+            line-height: 50upx;
+            background-color: #333333;
+            text-align: center;
+            margin-bottom: 18upx;
+            padding: 0 5px;
+            display: block;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+          }
+          .price{
+            color: #C83732;
+            font-size: 28upx;
+            font-weight: bold;
+            line-height: 40upx;
+          }
+          .buyCount{
+            font-size: 24upx;
+            color: #ccc;
+            line-height: 34upx;
+            font-weight: normal;
+          }
+        }
+      }
+    }
+    ::v-deep .uni-swiper-dots{
+      display: flex;
+      justify-content: center;
+      bottom: 27px;
+      .uni-swiper-dot{
+        width: 24upx;
+        height: 4upx;
+        background: #fff;
+        opacity: 0.5;
+        border-radius: 2upx;
+        margin: 0 5upx;
+        &-active{
+          opacity: 1;
+        }
+      }
+    }
+
+    //.pagination{
+    //  display: flex;
+    //  justify-content: center;
+    //  ::v-deep .swiper-pagination-bullet{
+    //    width: 24upx;
+    //    height: 4upx;
+    //    background: #fff;
+    //    opacity: 0.5;
+    //    border-radius: 2upx;
+    //    margin: 0 5upx;
+    //  }
+    //  ::v-deep .swiper-pagination-bullet-active{
+    //    opacity: 1;
+    //  }
+    //}
+  }
+</style>

+ 81 - 0
components/canvasShow/basics/group/mixin.js

@@ -0,0 +1,81 @@
+// import { directive, Swiper, SwiperSlide } from 'vue-awesome-swiper'
+// import 'swiper/css/swiper.css'
+import api from '../../config/api'
+import {funMixin} from '../../config/mixin'
+
+export const commonMixin = {
+  name: 'productList',
+  mixins: [funMixin],
+  props: {
+    terminal: {
+      type: Number,
+      default: 4
+    },
+    typeId: {
+      type: Number,
+      default: 1
+    },
+    shopId: {
+      type: Number,
+      default: 0
+    },
+    componentContent: {
+      type: Object
+    }
+  },
+  // components: {
+  //   Swiper,
+  //   SwiperSlide
+  // },
+  // directives: {
+  //   swiper: directive
+  // },
+  data () {
+    return {
+      productData: {
+        products: []
+      }
+    }
+  },
+  watch: {
+    'componentContent': {
+      handler(newVal, oldVal) {
+        this.getData()
+      },
+      deep: true
+    }
+  },
+  created() {
+    this.getData()
+  },
+  methods: {
+      getData() {
+        const _ = this
+        let _url = ''
+        if(_.typeId === 1){
+          const params = {
+            method: 'GET',
+            url: `${api.getAdminGroupWorks}`,
+          }
+          this.sendReq(params, (res) => {
+            _.productData.products = res.data
+          })
+        } else if(_.typeId === 3) {
+          if(_.componentContent.shopGroupWorkId){
+            const params = {
+              method: 'GET',
+              url: `${api.getGroupWorks}?shopId=${_.shopId}&ids=${_.componentContent.shopGroupWorkId}`,
+            }
+            this.sendReq(params, (res) => {
+              _.productData = res.data[0]
+            })
+          } else {
+            _.productData = {
+              products:[]
+            }
+          }
+        }
+
+      },
+  }
+}

+ 250 - 0
components/canvasShow/basics/group/pc/index.vue

@@ -0,0 +1,250 @@
+<template>
+  <div class="product-list" :class="'terminal'+terminal">
+    <div class="picListWarp" v-if="componentContent.arrangeType == '横向滑动'">
+      <div class="picList" v-if="productData.products && productData.products.length>0">
+        <div class="swiper-button-prev"></div>
+        <div class="swiper-button-next"></div>
+        <swiper class="products-swiper" :options="swiperOption">
+          <swiper-slide class="products-swiper-slide item" v-for="(item,index) in productData.products" :key="index">
+            <div class="a-link" @click="jumpProductDetail(item)">
+              <div class="itemImgBox">
+                <div class="imgBox">
+                  <el-image
+                    :src="item.image"
+                    fit="contain"></el-image>
+                </div>
+              </div>
+              <div class="text">
+                <h4 class="h4">{{item.productName}}</h4>
+                <div class="priceBox">
+                  <span class="discount" v-if="item.originalPrice">¥{{item.originalPrice}}</span>
+                  <dl>
+                    <dt><img src="../../../static/images/group/flag-group.png" alt="拼团价"></dt>
+                    <dd>
+                      ¥{{item.price}}
+                    </dd>
+                  </dl>
+                </div>
+              </div>
+            </div>
+          </swiper-slide>
+        </swiper>
+      </div>
+    </div>
+    <div v-else class="picList" >
+      <ul class="clearfix" :class="'imgTextNum' +  componentContent.productNum"  v-if="productData.products && productData.products.length>0">
+        <li class="item" v-for="(item,index) in productData.products.slice(0, componentContent.productRowNum * componentContent.productNum)" :key="index">
+          <div class="a-link" @click="jumpProductDetail(item)">
+            <div class="itemImgBox">
+              <div class="imgBox">
+                <el-image
+                  :src="item.image"
+                  fit="contain"></el-image>
+              </div>
+            </div>
+            <div class="text">
+              <h4 class="h4">{{item.productName}}</h4>
+              <div class="priceBox">
+                <span class="discount" v-if="item.originalPrice">¥{{item.originalPrice}}</span>
+                <dl>
+                  <dt><img src="../../../static/images/group/flag-group.png" alt="拼团价"></dt>
+                  <dd>
+                    ¥{{item.price}}
+                  </dd>
+                </dl>
+              </div>
+            </div>
+          </div>
+        </li>
+      </ul>
+    </div>
+    <button v-show="componentContent.showMore" class="btn-more" @click="jumpGroupWorks(productData)">查看全部 <span class="icon iconfont icon-arrow-right"></span></button>
+  </div>
+</template>
+
+<script>
+  import {commonMixin} from '../mixin'
+  export default {
+    mixins: [commonMixin],
+    data () {
+      return {
+        swiperOption: {
+          slidesPerView: 4, // 显示数量
+          spaceBetween: 13, // 间隔
+          autoplay: false, // 可选选项,自动滑动
+          loop: true,
+          pagination: {
+            el: '.group-pagination'
+          },
+          navigation: {
+            nextEl: '.swiper-button-next',
+            prevEl: '.swiper-button-prev'
+          }
+        }
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+.product-list{
+  padding: 20px 0;
+  background-color: #fff;
+  .picListWarp{
+    width: 1380px;
+    max-width: 100%;
+    margin: 0 auto;
+    position: relative;
+  }
+  .picList{
+    width: 1200px;
+    max-width: 100%;
+    margin: 0 auto;
+    .swiper-button-prev,.swiper-button-next{
+      width: 50px;
+      height: 50px;
+      position: absolute;
+      cursor:pointer;
+      top: 140px;
+      background-repeat: no-repeat;
+      &:after{
+        content: '';
+      }
+    }
+    .swiper-button-prev{
+      left: 0;
+      background: url('../../../static/images/btn-prev.png');
+    }
+    .swiper-button-next{
+      right: 0;
+      background: url('../../../static/images/btn-next.png');
+    }
+    .a-link{
+      cursor: pointer;
+      &:hover{
+        box-shadow: 3px 4px 20px 0px rgba(186, 186, 186, 0.5);
+      }
+      .itemImgBox {
+        height: auto;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        .imgBox {
+          padding-bottom: 100%;
+          background-color: #cacaca;
+          position: relative;
+          .el-image {
+            width: 100%;
+            height: 100%;
+            position: absolute;
+            top: 0;
+            left: 0;
+          }
+        }
+      }
+      .text{
+        padding:25px 20px 17px;
+        text-align: center;
+        //height: 180px;
+        .h4{
+          font-size: 18px;
+          line-height: 24px;
+          overflow: hidden;
+          text-overflow:ellipsis;
+          white-space: nowrap;
+          color: #333333;
+          //max-height: 48px;
+        }
+        .p{
+          color: #999;
+          font-size: 16px;
+          overflow: hidden;
+          text-overflow:ellipsis;
+          white-space: nowrap;
+          padding-top: 18px;
+          position: relative;
+          margin-top: 8px;
+          &:after{
+            position: absolute;
+            top: 0;
+            left: 50%;
+            margin-left: -80px;
+            width: 160px;
+            height: 2px;
+            background: #F0F0F0;
+            content: '';
+          }
+        }
+        .priceBox {
+          dl {
+            display: inline-block;
+            min-width: 130px;
+            dt{
+              float: left;
+              img{
+                display: block;
+              }
+            }
+            dd{
+              border: 1px solid #F3F4F5;
+              font-size: 25px;
+              line-height: 34px;
+              color: #C83732;
+              margin-left: 57px;
+              padding: 0 10px;
+            }
+          }
+          span.discount {
+            display: block;
+            font-size: 18px;
+            line-height: 24px;
+            padding: 15px 0 11px;
+            color: #ccc;
+            text-decoration: line-through;
+          }
+        }
+      }
+    }
+    ul{
+      margin: -15px 0 0 -15px;
+      display: flex;
+      flex-wrap: wrap;
+      li{
+        flex: 0 0 50%;
+        padding: 15px 0 0 15px;
+        width: 0;
+      }
+    }
+    .imgTextNum2 {
+      li {
+        flex: 0 0 50%;
+      }
+    }
+    .imgTextNum3 {
+      li {
+        flex: 0 0 33.33%;
+      }
+    }
+    .imgTextNum4 {
+      li {
+        flex: 0 0 25%;
+      }
+    }
+    .imgTextNum5 {
+      li {
+        flex: 0 0 20%;
+      }
+    }
+  }
+}
+.btn-more {
+  width: 130px;
+  height: 41px;
+  border: 2px solid #C5AA7B;
+  color: #C5AA7B;
+  font-size: 18px;
+  background-color: transparent;
+  margin: 20px auto 0;
+  display: block;
+}
+</style>

+ 119 - 0
components/canvasShow/basics/imageText.vue

@@ -0,0 +1,119 @@
+<template>
+  <div class="imageText warp" :class="['terminal'+terminal,'pos-' + componentContent.positionValue]">
+    <div class="img img-left">
+      <a class="item a-link" @click="jumpLink(componentContent.linkObj)"><img :src="componentContent.imageUrl" alt=""></a>
+    </div>
+    <div class="text">
+      <h3 class="h3">{{componentContent.title}}</h3>
+      <div v-html="componentContent.content"></div>
+    </div>
+    <div class="img img-right">
+      <a class="item a-link" @click="jumpLink(componentContent.linkObj)"><img :src="componentContent.imageUrl" alt=""></a>
+    </div>
+  </div>
+</template>
+
+<script>
+  import {funMixin} from '../config/mixin'
+  export default {
+    name: 'imageTextComponent',
+    mixins: [funMixin],
+    data () {
+      return {
+      }
+    },
+    props: {
+      terminal: {
+        type: Number,
+        default: 4
+      },
+      componentContent: {
+        type: Object
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .imageText{
+	  width: 710upx;
+	  margin: 0 auto;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 20upx 0;
+    .img{
+      width: 50%;
+      padding-bottom: 30%;
+      background-color: #cacaca;
+      position: relative;
+      img{
+        max-width: 100%;
+        height: 100%;
+        max-height: 100%;
+        position: absolute;
+        margin: auto;
+        top: 0;
+        right: 0;
+        bottom: 0;
+        left: 0;
+      }
+    }
+    .text{
+      width: 40%;
+      .h3{
+        font-size: 30upx;
+        margin-bottom: 24upx;
+      }
+      .p{
+        font-size: 16upx;
+      }
+    }
+    &.pos-top{
+      display: block;
+      text-align: center;
+      .img{
+        width: 100%;
+      }
+      .text{
+        width: 100%;
+        margin-top: 30upx;
+      }
+      .img-right{
+        display: none;
+      }
+    }
+    &.pos-bottom{
+      display: block;
+      text-align: center;
+      .img{
+        width: 100%;
+      }
+      .text{
+        width: 100%;
+        margin-bottom: 30upx;
+      }
+      .img-left{
+        display: none;
+      }
+    }
+    &.pos-left{
+      .img-right{
+        display: none;
+      }
+    }
+    &.pos-right{
+      .text{
+        padding-left: 20upx;
+      }
+      .img-left{
+        display: none;
+      }
+    }
+
+  }
+  .terminal1,.terminal2,.terminal3{
+    width: 710upx;
+    margin: 0 auto;
+  }
+</style>

+ 135 - 0
components/canvasShow/basics/imageTextList.vue

@@ -0,0 +1,135 @@
+<template>
+  <div class="hom-pro-list warp" :class="'terminal'+terminal">
+    <div class="title">
+      <h2 class="h2" :style="{textAlign:componentContent.textAlign}">{{componentContent.title}}</h2>
+    </div>
+    <div class="ul clearfix" :class="{imgTextNum4: componentContent.imgTextData.length === 4, imgTextNum5: componentContent.imgTextData.length === 5, imgTextStyle: componentContent.imgTextData.length >= 6 || componentContent.imgTextData.length === 3}">
+      <div class="li" v-for="(item,index) in componentContent.imgTextData" :key="index">
+        <a class="item a-link" @click="jumpLink(item.linkObj)">
+          <div class="itemImgBox" v-show="item.isShow">
+            <div class="imgBox">
+              <img ref="getHeight" :src="item.imgData" v-show="item.imgData" :alt="item.title">
+            </div>
+          </div>
+          <div class="text">
+            <h4 class="h4">{{item.title}}</h4>
+            <p class="p">{{item.describe}}</p>
+          </div>
+        </a>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+  import {funMixin} from '../config/mixin'
+  export default {
+    name: 'imageTextList',
+    mixins: [funMixin],
+    props: {
+      terminal: {
+        type: Number,
+        default: 4
+      },
+      componentContent: {
+        type: Object
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .hom-pro-list{
+    min-height: 450upx;
+    padding: 20upx 0;
+    .title{
+      margin-bottom: 23upx;
+      position: relative;
+      .h2{
+        font-size: 22upx;
+        color: #333;
+        line-height: 1em;
+        font-weight: bold;
+      }
+    }
+    .ul{
+      margin: -15upx 0 0 -15upx;
+      display: flex;
+      flex-wrap: wrap;
+      .li{
+        flex: 0 0 50%;
+        padding: 15upx 0 0 15upx;
+        .item{
+          .itemImgBox {
+            height: auto;
+            display: flex;
+            flex-direction: column;
+            justify-content: center;
+            .imgBox {
+              padding-bottom: 80%;
+              background-color: #cacaca;
+              position: relative;
+              img {
+                max-width: 100%;
+                height: 100%;
+                max-height: 100%;
+                position: absolute;
+                margin: auto;
+                top: 0;
+                right: 0;
+                bottom: 0;
+                left: 0;
+              }
+            }
+          }
+          .text{
+            padding:16upx 20upx;
+            text-align: center;
+            .h4{
+              line-height: 25upx;
+              overflow: hidden;
+              color: #333333;
+            }
+            .p{
+              color: #666666;
+              padding: 5upx 0 10upx;
+            }
+          }
+        }
+      }
+    }
+    .imgTextNum4 {
+      .li {
+        flex: 0 0 50%;
+      }
+    }
+    .imgTextNum5 {
+      .li {
+        flex: 0 0 33.33%;
+      }
+      .li:nth-child(1) {
+        flex: 0 0 50%;
+      }
+      .li:nth-child(2) {
+        flex: 0 0 50%;
+      }
+    }
+    .imgTextStyle {
+      .li {
+        flex: 0 0 33.33%;
+      }
+    }
+  }
+  @media screen and (max-width: 768px) {
+    .hom-pro-list .ul .li{
+      flex: 0 0 50%;
+    }
+  }
+  .terminal1,.terminal2,.terminal3{
+    width: 710upx;
+    margin: 0 auto;
+    &.hom-pro-list .ul .li{
+      flex: 0 0 50%;
+    }
+  }
+</style>

+ 101 - 0
components/canvasShow/basics/imageTextNav.vue

@@ -0,0 +1,101 @@
+<template>
+  <div class="ul image-text-nav" :class="'terminal' + terminal">
+    <div class="li" v-for="(item,index) in componentContent.imgTextData" :key="index" :style="{'flex':'0 0 '+ getItemValue() + '%'}" @click="jumpLink(item.linkObj)">
+      <!--<router-link class="item" :to="jumpLink(item.linkObj)">-->
+        <div class="img-box">
+          <div class="img-box-inner">
+			  <div class="imgBox">
+			    <img class="img" :src="item.img"/>
+			  </div>
+          </div>
+        </div>
+        <h4 class="h4">{{item.title}}</h4>
+      <!--</router-link>-->
+    </div>
+  </div>
+</template>
+
+<script>
+  import {funMixin} from '../config/mixin'
+  export default {
+    name: 'imageTextNav',
+    mixins: [funMixin],
+    props: {
+      terminal: {
+        type: Number,
+        default: 4
+      },
+      componentContent: {
+        type: Object
+      }
+    },
+    methods: {
+      // 计算生成格子百分比
+      getItemValue (val) {
+        const len = parseInt(this.componentContent.imgTextData.length)
+        if (len === 0) {
+          return 0
+        } else {
+          return (1 / len * 10000 / 100.00)
+        }
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .image-text-nav{
+    min-height: 100upx;
+    width: 710upx;
+    margin: 0 auto;
+    display: flex;
+    padding: 20upx 0;
+    .li{
+      text-align: center;
+      .img-box{
+        .imgBox{
+          width: 100upx;
+          height: 100upx;
+		  display: inline-block;
+		  .img{
+			  width: 100%;
+			  height: 100%;
+			  object-fit: cover;
+		  }
+        }
+      }
+      .h4{
+        font-size: 26upx;
+        color: #333;
+        line-height: 33upx;
+      }
+    }
+    &.terminal4{
+      width: 1000upx;
+      .li{
+        .img-box{
+          display: inline-block;
+          width: 100upx;
+          height: 100upx;
+          box-shadow: 0 10upx 30upx rgba(51, 51, 51, 0.15);
+          &-inner{
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            height: 100%;
+          }
+          .imgBox{
+            width: 60upx;
+            height: 60upx;
+          }
+        }
+        .h4{
+          font-size: 18upx;
+          color: #ccc;
+          line-height: 1em;
+          padding-top: 20upx;
+        }
+      }
+    }
+  }
+</style>

+ 183 - 0
components/canvasShow/basics/newProduct/app/index.vue

@@ -0,0 +1,183 @@
+<template>
+  <div class="hom-pro-list">
+    <div class="product-swiper">
+      <div class="product-swiper-box">
+        <div class="product-swiper-warp" v-for="(item,index) in productData.slice(0, 3)" :key="index">
+          <div class=" product-swiper-item" @click="jumpProductDetail(item)">
+            <div class="product-swiper-img">
+              <img class="img" :src="item.image">
+            </div>
+            <div class="product-swiper-info">
+              <label class="product-name">{{item.productName}}</label>
+              <div class="price-warp">
+                <image class="iconImg" v-if="item.activityType == 1" src="../../../static/images/groupBuyIcon.png"></image>
+                <image class="iconImg" v-if="item.activityType == 2" src="../../../static/images/spikeIcon.png"></image>
+                <image class="iconImg" v-if="item.activityType == 4" src="../../../static/images/spikeIcon.png"></image>
+                <image class="iconImg" v-if="item.activityType == 3" src="../../../static/images/discountListIcon.png"></image>
+                <image class="iconImg" v-if="item.activityType == 5" src="../../../static/images/discountListIcon.png"></image>
+                <image class="iconImg" v-if="item.activityType == 8" src="../../../static/images/memberCenterIcon.png"></image>
+                <div class="price">
+                  ¥ {{item.price}}
+                </div>
+<!--                <div class="original-price">-->
+<!--                  ¥ {{item.originalPrice}}-->
+<!--                </div>-->
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="pagination new-pagination"></div>
+    </div>
+    <button v-show="componentContent.showMore" class="btn-more" @click="jumpProList(componentContent.productData)">查看全部 <span class="icon iconfont icon-arrow-right"></span></button>
+  </div>
+</template>
+
+<script>
+import {commonMixin} from '../mixin'
+export default {
+  mixins: [commonMixin],
+  // data () {
+    // return {
+    //   index: 1,
+    //   swiperOption: {
+    //     slidesPerView: 3,
+    //     spaceBetween: 12,
+    //     autoplay: false, // 可选选项,自动滑动
+    //     loop: true,
+    //     pagination: {
+    //       el: '.new-pagination'
+    //     }
+    //   }
+    // }
+  // }
+}
+</script>
+
+<style lang="scss" scoped>
+.hom-pro-list{
+  ::v-deep .swiper-wrapper{
+    position: static;
+  }
+  /**横向滑动**/
+  .product-swiper{
+    width: 100%;
+    height: 454upx;
+    padding: 90upx 34upx 0;
+    background: url("../../../static/images/newProduct/bg-product-card.png") no-repeat center;
+    background-size: 710upx 454upx;
+    box-sizing: border-box;
+    position: relative;
+    &+.btn-more{
+      margin-top: 20upx;
+    }
+    .title{
+      padding: 22upx 0upx 0 0;
+      label{
+        background-image: none;
+        color: #A56C4C;
+        font-style: italic;
+        padding: 0;
+      }
+    }
+    &-box {
+      padding-bottom: 20upx;
+      display: flex;
+    }
+    &-warp{
+      padding: 0 5upx;
+    }
+    &-item {
+      width: 220upx;
+      position: relative;
+      background-color: #FFFFFF;
+    }
+    &-img {
+      width: 220upx;
+      height: 220upx;
+      position: relative;
+      &:after{
+        content: '';
+        display: block;
+        width: 54upx;
+        height: 54upx;
+        background: url("../../../static/images/newProduct/flag-new.png") no-repeat;
+        background-size: 100% 100%;
+        position: absolute;
+        top: 0;
+        left: 0;
+      }
+      .img {
+        width: 100%;
+        height: 100%;
+        object-fit: contain;
+      }
+    }
+    &-info {
+      background-color: #FFFFFF;
+      padding: 10upx;
+      text-align: center;
+      .product-name{
+        font-size: 20upx;
+        color: #333;
+        display: block;
+        overflow: hidden;
+        text-overflow:ellipsis;
+        white-space: nowrap;
+        margin-bottom: 6upx;
+        line-height: 28upx;
+      }
+      .price-warp{
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        line-height: 28upx;
+        .iconImg {
+          width: 58rpx;
+          height: 36rpx;
+          margin-right: 10rpx;
+        }
+        .price{
+          color: #C83732;
+          font-size: 20upx;
+          margin-right: 10upx;
+        }
+        .original-price{
+          font-size: 16upx;
+          color: #ccc;
+          text-decoration: line-through;
+        }
+      }
+    }
+  }
+}
+
+.pagination{
+  display: flex;
+  justify-content: center;
+  width: 100%;
+  bottom: 0;
+  ::v-deep .swiper-pagination-bullet{
+    width: 24upx;
+    height: 4upx;
+    background: #FFFFFF;
+    opacity: 0.5;
+    border-radius: 2upx;
+    margin: 0 10upx;
+  }
+  ::v-deep .swiper-pagination-bullet-active{
+    opacity: 1;
+  }
+}
+.btn-more {
+  width: 170upx;
+  height: 54upx;
+  border: 2upx solid #C5AA7B;
+  color: #C5AA7B;
+  font-size: 24upx;
+  background-color: transparent;
+  margin: 20upx auto 0;
+  display: block;
+}
+
+</style>

+ 89 - 0
components/canvasShow/basics/newProduct/mixin.js

@@ -0,0 +1,89 @@
+
+import {funMixin} from '../../config/mixin'
+// import { directive, Swiper, SwiperSlide } from 'vue-awesome-swiper'
+// import 'swiper/css/swiper.css'
+import api from '../../config/api'
+
+export const commonMixin = {
+  name: 'productList',
+   mixins: [funMixin],
+  props: {
+    terminal: {
+      type: Number,
+      default: 4
+    },
+    typeId: {
+      type: Number,
+      default: 1
+    },
+    shopId: {
+      type: Number,
+      default: 0
+    },
+    componentContent: {
+      type: Object
+    }
+  },
+  // components: {
+  //   Swiper,
+  //   SwiperSlide
+  // },
+  // directives: {
+  //   swiper: directive
+  // },
+  data () {
+    return {
+      productData: []
+    }
+  },
+  mounted() {
+    this.getData(true)
+  },
+  watch: {
+    'componentContent': {
+      handler(newVal, oldVal) {
+        this.getData()
+      },
+      deep: true
+    }
+  },
+  methods: {
+      getData(isFirst) {
+        const _ = this
+        if (_.componentContent.productData.sourceType === '1') {
+          if(_.componentContent.productData.productIdList && _.componentContent.productData.productIdList.length>0){
+            _.sendReq({
+              url: `${api.getProducts}?page=1&pageSize=99&ids=${_.componentContent.productData.productIdList}`,
+              method: 'GET'
+            }, (proRes) => {
+              _.productData = proRes.data.list
+              if(isFirst){
+                _.componentContent.productData.imgTextData = _.productData
+              }
+              // _.$forceUpdate() // 刷新轮播图
+  
+            })
+          } else {
+            _.productData = []
+          }
+        } else if(_.componentContent.productData.sourceType === '2'){
+          if(_.componentContent.productData.categoryId) {
+            _.sendReq({
+              url: `${api.getProducts}?page=1&pageSize=99&classifyId=${_.componentContent.productData.categoryId}`,
+              method: 'GET'
+            }, (proRes) => {
+              _.productData = proRes.data.list
+              if(isFirst){
+                _.componentContent.productData.imgTextData = _.productData
+              }
+              _.$forceUpdate() // 刷新轮播图
+              // _.swiper.update()
+            })
+          } else {
+            _.productData = []
+  
+        }
+      }
+    }
+  },
+}

+ 109 - 0
components/canvasShow/basics/notice.vue

@@ -0,0 +1,109 @@
+<template>
+  <div class="notice-list" :class="'terminal'+terminal" :style="{backgroundColor:componentContent.bgColor}">
+    <div class="swiper-wrapper">
+      <div class="swiper-slide" v-for="(item,index) in noticesList" :key="index">
+        <div class="a-link" @click="jumpNoticeDetail(item)" :style="{color:componentContent.titColor}"><span>{{item.noticeTitle}}</span></div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import api from '../config/api'
+import { funMixin } from '../config/mixin'
+import { directive, Swiper, SwiperSlide } from 'vue-awesome-swiper'
+import 'swiper/css/swiper.css'
+export default {
+  name: "noticeComponent",
+  mixins: [funMixin],
+  data () {
+    return {
+      noticesList: [],
+      // swiperOption: {
+      //   autoplay: false, // 可选选项,自动滑动
+      //   loop: true,
+      //   pagination: {
+      //     el: '.swiper-pagination'
+      //   }
+      // }
+    }
+  },
+  props: {
+    terminal: {
+      type: Number,
+      default: 4
+    },
+    componentContent: {
+      type: Object
+    }
+  },
+  // components: {
+  //   Swiper,
+  //   SwiperSlide
+  // },
+  // directives: {
+  //   swiper: directive
+  // },
+  mounted() {
+    this.getData()
+  },
+  methods: {
+    getData() {
+      const _ = this
+      let _url = `${api.getNotices}`
+      const params = {
+        method: 'GET',
+        url: _url,
+      }
+      this.sendReq(params, (res) => {
+        _.noticesList = res.data
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.notice-list{
+  height: 60upx;
+  line-height: 60upx;
+  padding: 0 20upx;
+  .a-link{
+    display: block;
+    cursor: pointer;
+    overflow: hidden;
+    text-overflow:ellipsis;
+    white-space: nowrap;
+    text-align: center;
+    span{
+      display: inline-block;
+      padding-left: 50upx;
+      font-size: 24upx;
+      background: url("../static/images/notice/ico_notice2.png") no-repeat left center;
+      background-size: 30upx 30upx;
+    }
+  }
+  &.terminal4{
+    height: 50upx;
+    line-height: 50upx;
+    padding: 0;
+    .swiper-container{
+      height: 100%;
+      width: 1200upx;
+      max-width: 100%;
+      margin: 0 auto;
+    }
+    .a-link{
+      display: block;
+      cursor: pointer;
+      text-align: left;
+      span{
+        display: block;
+        padding-left: 25upx;
+        font-size: 14upx;
+        background: url("../static/images/notice/ico_notice.png") no-repeat left center;
+      }
+    }
+  }
+}
+</style>

+ 195 - 0
components/canvasShow/basics/price/app/index.vue

@@ -0,0 +1,195 @@
+<template>
+  <div class="group-list">
+    <div class="group-warp">
+      <div class="title">
+        <label>
+          <img class="title-img" src="../../../static/images/price/img-title.png" alt="组合优惠" mode="widthFix"/>
+        </label>
+        <div class="price-text">
+          <swiper class="swiper" :autoplay="true" :vertical="true">
+            <swiper-item v-for="(item,index) in productData.rules" :key="index">
+              {{item.price}}元任选{{item.number}}件
+            </swiper-item>
+          </swiper>
+        </div>
+        <a v-show="componentContent.showMore" class="btn-all a-link" @click="jumpCombination(productData)">更多<i class="iconfont icon-arrow-right"></i></a>
+      </div>
+      <div>
+      <swiper class="swiper pro-box" :indicator-dots="true" :autoplay="true" :display-multiple-items="2">
+        <swiper-item class="pro-item-warp" v-for="(item,index) in productData.composeProducts" :key="index" @click="jumpProductDetail(item)">
+          <div class="pro-item-inner">
+          <div class="pro-item">
+          <div class="pro-item-img">
+            <img v-show="item.image" class="img" :src="item.image">
+          </div>
+          <div class="pro-item-info">
+            <h3 class="name">
+              {{item.productName}}
+            </h3>
+            <div class="stock">
+              还剩{{item.stockNumber}}件
+            </div>
+            <div class="price-warp">
+              <div class="price">
+                ¥ {{item.price}}
+              </div>
+              <div class="original-price">
+                ¥ {{item.originalPrice}}
+              </div>
+            </div>
+          </div>
+          </div>
+          </div>
+        </swiper-item>
+      </swiper>
+      </div>
+    </div>
+  </div>
+
+</template>
+
+<script>
+import {commonMixin} from '../mixin'
+export default {
+  mixins: [commonMixin],
+  // data () {
+    // return {
+    //   swiperOption: {
+    //     slidesPerView: 2, // 显示数量
+    //     spaceBetween: 20, // 间隔
+    //     autoplay: false, // 可选选项,自动滑动
+    //     loop: true,
+    //     pagination: {
+    //       el: '.price-pagination'
+    //     },
+    //     navigation: {
+    //       nextEl: '.swiper-button-next',
+    //       prevEl: '.swiper-button-prev'
+    //     }
+    //   }
+    // }
+  // }
+}
+</script>
+
+<style lang="scss" scoped>
+.group-list{
+  padding: 30upx 20upx 60upx;
+  .group-warp{
+    height: 544upx;
+    padding: 0 10upx;
+    background: #333333;
+    box-shadow: 0 20upx 30upx rgba(0, 0, 0, 0.3);
+    opacity: 1;
+    border-radius: 20upx;
+  }
+  .title{
+    display: flex;
+    align-items:center;
+    position: relative;
+    padding: 32upx 0 20upx 20upx;
+    .title-img{
+      width: 203upx;
+    }
+    .price-text{
+      width: 300upx;
+      height: 50upx;
+      background: linear-gradient(90deg, #C83732 0%, #E25C44 100%);
+      box-shadow: 0 6upx 12upx rgba(233, 0, 0, 0.3);
+      border-radius: 26upx;
+      font-size: 24upx;
+      color: #fff;
+      text-align: center;
+      line-height: 50upx;
+      margin-left: 20upx;
+      .swiper{
+        height: 50upx;
+      }
+    }
+    .btn-all{
+      position: absolute;
+      right: 8upx;
+      top: 40upx;
+      line-height: 33upx;
+      padding-right: 25upx;
+      font-size: 24upx;
+      color: #FFEBC4;
+      .iconfont{
+        content: '';
+        font-size: 26upx;
+        position: absolute;
+        right: 0;
+        top: 0;
+      }
+    }
+  }
+  .pro-box{
+    padding-bottom: 20upx;
+    height: 450upx;
+    .pro-item-inner{
+      padding: 0 10upx;
+    }
+    .pro-item{
+      width: 100%;
+      height: 412upx;
+      background: #FFFFFF;
+      .pro-item-img{
+        .img{
+          width: 100%;
+          height: 236upx;
+        }
+      }
+      .pro-item-info{
+        padding: 0 20upx;
+        .name{
+          font-size: 24upx;
+          line-height: 40upx;
+          color: #333333;
+          overflow: hidden;
+          text-overflow:ellipsis;
+          white-space: nowrap;
+        }
+        .stock{
+          padding: 0 8upx;
+          height: 40upx;
+          border: 2upx solid #E4E5E6;
+          line-height: 40upx;
+          margin: 10upx 0;
+          display: inline-block;
+          font-size: 20upx;
+          color: #C5AA7B;
+        }
+        .price{
+          font-size: 32upx;
+          font-weight: bold;
+          line-height: 44upx;
+          color: #C83732;
+          padding-right: 10upx;
+          display: inline-block;
+        }
+        .original-price{
+          font-size: 20upx;
+          line-height: 28upx;
+          color: #CCCCCC;
+          display: inline-block;
+        }
+      }
+    }
+  }
+  .pagination{
+    display: flex;
+    justify-content: center;
+    ::v-deep .swiper-pagination-bullet{
+      width: 24upx;
+      height: 4upx;
+      background: #fff;
+      opacity: 0.5;
+      border-radius: 2upx;
+      margin: 0 5upx;
+    }
+    ::v-deep .swiper-pagination-bullet-active{
+      opacity: 1;
+    }
+  }
+}
+</style>

+ 73 - 0
components/canvasShow/basics/price/mixin.js

@@ -0,0 +1,73 @@
+import { directive, Swiper, SwiperSlide } from 'vue-awesome-swiper'
+import 'swiper/css/swiper.css'
+import api from '../../config/api'
+import {funMixin} from '../../config/mixin'
+
+export const commonMixin = {
+  name: 'price',
+  mixins: [funMixin],
+  props: {
+    terminal: {
+      type: Number,
+      default: 4
+    },
+    typeId: {
+      type: Number,
+      default: 1
+    },
+    shopId: {
+      type: Number,
+      default: 0
+    },
+    componentContent: {
+      type: Object
+    }
+  },
+  components: {
+    Swiper,
+    SwiperSlide
+  },
+  directives: {
+    swiper: directive
+  },
+  data () {
+    return {
+      productData: {
+        composeProducts: [],
+        rules: []
+      }
+    }
+  },
+  watch: {
+    'componentContent': {
+      handler(newVal, oldVal) {
+        this.getData()
+      },
+      deep: true
+    }
+  },
+  created() {
+    this.getData()
+  },
+  methods: {
+      getData() {
+        const _ = this
+        if(_.componentContent.priceId){
+          const params = {
+            method: 'GET',
+            url: `${api.getPrices}?shopId=${_.shopId}&ids=${_.componentContent.priceId}&page=1&pageSize=10`,
+          }
+          this.sendReq(params, (res) => {
+            if( res.data.length > 0){
+              _.productData = res.data[0]
+            }
+          })
+        } else {
+          _.productData = {
+            composeProducts: [],
+            rules: []
+          }
+        }
+      },
+  }
+}

+ 253 - 0
components/canvasShow/basics/price/pc/index.vue

@@ -0,0 +1,253 @@
+<template>
+  <div class="product-list" :class="'terminal'+terminal">
+    <div class="picListWarp" v-if="componentContent.arrangeType == '横向滑动'">
+      <div class="picList" v-if="productData.products && productData.products.length>0">
+        <div class="swiper-button-prev"></div>
+        <div class="swiper-button-next"></div>
+        <swiper class="products-swiper" :options="swiperOption">
+          <swiper-slide class="products-swiper-slide item" v-for="(item,index) in productData.composeProducts" :key="index">
+            <div class="a-link" @click="jumpProductDetail(item)">
+              <div class="itemImgBox">
+                <div class="imgBox">
+                  <el-image
+                    :src="item.image"
+                    fit="contain"></el-image>
+                </div>
+              </div>
+              <div class="text">
+                <div class="discount-text">
+                  <span>任选{{productData.rules[0].number}}件{{productData.rules[0].price}}元</span>
+                </div>
+                <h4 class="h4">{{item.productName}}</h4>
+                <div class="priceBox">
+                  <span>¥{{item.price}}</span>
+                </div>
+                <button class="btn-cart" @click="addCart(item.id)">加入购物车</button>
+              </div>
+            </div>
+          </swiper-slide>
+        </swiper>
+      </div>
+    </div>
+    <div v-else class="picList" >
+      <ul class="clearfix" :class="'imgTextNum' +  componentContent.productNum">
+        <li class="item" v-for="(item,index) in productData.composeProducts.slice(0, componentContent.productRowNum * componentContent.productNum)" :key="index">
+          <div class="a-link" @click="jumpProductDetail(item)">
+            <div class="itemImgBox">
+              <div class="imgBox">
+                <el-image
+                  :src="item.image"
+                  fit="contain"></el-image>
+              </div>
+            </div>
+            <div class="text">
+              <div class="discount-text">
+                <span>任选{{productData.rules[0].number}}件{{productData.rules[0].price}}元</span>
+              </div>
+              <h4 class="h4">{{item.productName}}</h4>
+              <div class="priceBox">
+                <span>¥{{item.price}}</span>
+              </div>
+              <button class="btn-cart" @click="addCart(item.id)">加入购物车</button>
+            </div>
+          </div>
+        </li>
+      </ul>
+    </div>
+    <button v-show="componentContent.showMore" class="btn-more" @click="jumpPice(productData)">查看全部 <span class="icon iconfont icon-arrow-right"></span></button>
+  </div>
+</template>
+
+<script>
+  import {commonMixin} from '../mixin'
+  export default {
+    mixins: [commonMixin],
+    data () {
+      return {
+        swiperOption: {
+          slidesPerView: 4, // 显示数量
+          spaceBetween: 13, // 间隔
+          autoplay: false, // 可选选项,自动滑动
+          loop: true,
+          pagination: {
+            el: '.price-pagination'
+          },
+          navigation: {
+            nextEl: '.swiper-button-next',
+            prevEl: '.swiper-button-prev'
+          }
+        }
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+.product-list{
+  padding: 20px 0;
+  background-color: #fff;
+  .picListWarp{
+    width: 1380px;
+    max-width: 100%;
+    margin: 0 auto;
+    position: relative;
+  }
+  .picList{
+    width: 1200px;
+    max-width: 100%;
+    margin: 0 auto;
+    .swiper-button-prev,.swiper-button-next{
+      width: 50px;
+      height: 50px;
+      position: absolute;
+      cursor:pointer;
+      top: 140px;
+      background-repeat: no-repeat;
+      &:after{
+        content: '';
+      }
+    }
+    .swiper-button-prev{
+      left: 0;
+      background: url('../../../static/images/btn-prev.png');
+    }
+    .swiper-button-next{
+      right: 0;
+      background: url('../../../static/images/btn-next.png');
+    }
+    .a-link{
+      cursor: pointer;
+      &:hover{
+        box-shadow: 3px 4px 20px 0px rgba(186, 186, 186, 0.5);
+      }
+      .itemImgBox {
+        height: auto;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        .imgBox {
+          padding-bottom: 100%;
+          background-color: #cacaca;
+          position: relative;
+          .el-image {
+            width: 100%;
+            height: 100%;
+            position: absolute;
+            top: 0;
+            left: 0;
+          }
+        }
+      }
+      .text{
+        padding:25px 10px 17px;
+        text-align: center;
+        position: relative;
+        //height: 180px;
+        .discount-text{
+          background: url("../../../static/images/price/bg-discount.png") no-repeat;
+          width: 100%;
+          height: 47px;
+          font-size: 14px;
+          color: #fff;
+          line-height: 60px;
+          text-align: center;
+          position: absolute;
+          top: -47px;
+          left: 0;
+        }
+        .h4{
+          font-size: 18px;
+          line-height: 24px;
+          overflow: hidden;
+          text-overflow:ellipsis;
+          white-space: nowrap;
+          color: #333333;
+          //max-height: 48px;
+        }
+        .p{
+          color: #999;
+          font-size: 16px;
+          overflow: hidden;
+          text-overflow:ellipsis;
+          white-space: nowrap;
+          padding-top: 18px;
+          position: relative;
+          margin-top: 8px;
+          &:after{
+            position: absolute;
+            top: 0;
+            left: 50%;
+            margin-left: -80px;
+            width: 160px;
+            height: 2px;
+            background: #F0F0F0;
+            content: '';
+          }
+        }
+        .priceBox {
+          padding-top: 11px;
+          line-height: 33px;
+          span {
+            font-size: 25px;
+            color: #C83732;
+            padding-right: 12px;
+          }
+          span.discount {
+            font-size: 18px;
+            color: #ccc;
+            text-decoration: line-through;
+          }
+        }
+        .btn-cart{
+          margin-top: 5px;
+          width: 270px;
+          height: 40px;
+          background-color: #333;
+          font-size: 18px;
+          color: #FFEBC4;
+        }
+      }
+    }
+    ul{
+      margin: -15px 0 0 -15px;
+      display: flex;
+      flex-wrap: wrap;
+      li{
+        flex: 0 0 50%;
+        padding: 15px 0 0 15px;
+        width: 0;
+      }
+    }
+    .imgTextNum2 {
+      li {
+        flex: 0 0 50%;
+      }
+    }
+    .imgTextNum3 {
+      li {
+        flex: 0 0 33.33%;
+      }
+    }
+    .imgTextNum4 {
+      li {
+        flex: 0 0 25%;
+      }
+    }
+    .imgTextNum5 {
+      li {
+        flex: 0 0 20%;
+      }
+    }
+  }
+}
+.btn-more {
+  width: 130px;
+  height: 41px;
+  border: 2px solid #C5AA7B;
+  color: #C5AA7B;
+  font-size: 18px;
+  background-color: transparent;
+  margin: 20px auto 0;
+  display: block;
+}
+</style>

+ 279 - 0
components/canvasShow/basics/product/app/index.vue

@@ -0,0 +1,279 @@
+<template>
+  <div class="hom-pro-list">
+    <div class="title">
+      <img class="title-img" src="../../../static/images/product/img-title.png" alt="商品推荐" mode="widthFix"/>
+    </div>
+    <div v-if="componentContent.arrangeType == '横向滑动' && productData.length > 2" class="product-list">
+      <swiper ref="mySwiper" class="swiper product-list-box" :indicator-dots="true" :autoplay="true" :display-multiple-items="2">
+        <swiper-item class="product-list-item-warp" v-for="(item,index) in productData.slice(0, 10)" :key="index" @click="jumpProductDetail(item)">
+          <div class="product-list-item">
+            <div class="product-list-img">
+              <img v-show="item.image" class="img" :src="item.image">
+            </div>
+            <div class="product-list-info">
+              <label class="product-name">{{item.productName}}</label>
+              <div>
+                <div class="shop-box" v-if="typeId == 1" @click.stop="jumpStore(item)">
+                  <label class="shop-name">{{item.shopName}}</label>
+                  <div class="shop-logo">
+                    <img :src="item.shopLogo">
+                  </div>
+                </div>
+                <label class="buy-count">{{item.users?item.users: 0}}人付款</label>
+              </div>
+              <div class="price-warp">
+                <image class="iconImg" v-if="item.activityType == 1" src="../../../static/images/groupBuyIcon.png"></image>
+                <image class="iconImg" v-if="item.activityType == 2" src="../../../static/images/spikeIcon.png"></image>
+                <image class="iconImg" v-if="item.activityType == 4" src="../../../static/images/spikeIcon.png"></image>
+                <image class="iconImg" v-if="item.activityType == 3" src="../../../static/images/discountListIcon.png"></image>
+                <image class="iconImg" v-if="item.activityType == 5" src="../../../static/images/discountListIcon.png"></image>
+                <image class="iconImg" v-if="item.activityType == 8" src="../../../static/images/memberCenterIcon.png"></image>
+                <div class="price">
+                  ¥ {{item.price}}
+                </div>
+                <div class="original-price">
+                  ¥ {{item.originalPrice}}
+                </div>
+              </div>
+            </div>
+          </div>
+        </swiper-item>
+      </swiper>
+<!--      <div class="pagination product-pagination" slot="pagination"></div>-->
+    </div>
+    <div v-else class="product-list">
+      <div class="product-list-box" >
+        <div class="product-list-item-warp" v-for="(item,index) in productData" :key="index" @click="jumpProductDetail(item)">
+          <div class="product-list-item">
+            <div class="product-list-img">
+              <img v-show="item.image" class="img" :src="item.image">
+            </div>
+            <div class="product-list-info">
+              <label class="product-name">{{item.productName}}</label>
+              <div>
+                <div class="shop-box" v-if="typeId == 1" @click.stop="jumpStore(item)">
+                  <label class="shop-name">{{item.shopName}}</label>
+                  <div class="shop-logo">
+                    <img :src="item.shopLogo">
+                  </div>
+                </div>
+                <label class="buy-count">{{item.users?item.users: 0}}人付款</label>
+              </div>
+              <div class="price-warp">
+                <image class="iconImg" v-if="item.activityType == 1" src="../../../static/images/groupBuyIcon.png"></image>
+                <image class="iconImg" v-if="item.activityType == 2" src="../../../static/images/spikeIcon.png"></image>
+                <image class="iconImg" v-if="item.activityType == 4" src="../../../static/images/spikeIcon.png"></image>
+                <image class="iconImg" v-if="item.activityType == 3" src="../../../static/images/discountListIcon.png"></image>
+                <image class="iconImg" v-if="item.activityType == 5" src="../../../static/images/discountListIcon.png"></image>
+                <image class="iconImg" v-if="item.activityType == 8" src="../../../static/images/memberCenterIcon.png"></image>
+                <div class="price">
+                  ¥ {{item.price}}
+                </div>
+                <div class="original-price">
+                  ¥ {{item.originalPrice}}
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <button v-show="componentContent.showMore" class="btn-more" @click="jumpProList(componentContent.productData)">查看全部 <span class="icon iconfont icon-arrow-right"></span></button>
+  </div>
+</template>
+
+<script>
+import {commonMixin} from '../mixin'
+export default {
+  mixins: [commonMixin],
+  data () {
+    return {
+      // swiperOption: {
+      //   slidesPerView: 2,
+      //   spaceBetween: 0,
+      //   autoplay: false, // 可选选项,自动滑动
+      //   loop: true,
+      //   pagination: {
+      //     el: '.product-pagination',
+      //     clickable: true
+      //   },
+      //   navigation: {
+      //     nextEl: '.swiper-button-next',
+      //     prevEl: '.swiper-button-prev'
+      //   }
+      // }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.hom-pro-list{
+  padding: 20upx 13upx;
+  .title{
+    text-align: center;
+    margin-bottom: 20upx;
+    .title-img{
+      width: 211upx;
+    }
+  }
+  /**多行多列**/
+  .product-list {
+    position: relative;
+    &-box {
+      display: flex;
+      flex-wrap: wrap;
+      flex-direction: row;
+      &.swiper{
+        height: 620upx;
+      }
+    }
+    &.product-swiper .product-list-box{
+      padding-left: 0;
+    }
+    &-item-warp{
+      margin: 0 0 20upx 0;
+    }
+    &-item {
+      width: 348upx;
+      padding: 0 7upx;
+      box-sizing: content-box;
+    }
+    &-img {
+      width: 348upx;
+      height: 348upx;
+      background-color: #f5f5f5;
+      border-radius: 10upx 10upx 0 0;
+      .img {
+        width: 100%;
+        height: 100%;
+        object-fit: contain;
+      }
+    }
+    &-info {
+      background-color: #FFFFFF;
+      //box-shadow: 0px 0px 15px 0px rgba(52, 52, 52, 0.15);
+      border-radius: 0 0 10upx 10upx;
+      padding: 20upx;
+      label{
+        font-weight: normal;
+      }
+      .product-name{
+        font-size: 28upx;
+        color: #333;
+        display: block;
+        overflow: hidden;
+        text-overflow:ellipsis;
+        white-space: nowrap;
+        margin-bottom: 18upx;
+        line-height: 40upx;
+      }
+      .shop-box{
+        background-color: #333333;
+        border-radius: 0upx 20upx 20upx 0upx;
+        line-height: 40upx;
+        display: inline-block;
+        height: 40upx;
+        margin-right: 10upx;
+        .shop-name{
+          font-size: 20upx;
+          color: #FFEBC4;
+          padding: 0 8upx 0 12upx;
+        }
+        .shop-logo{
+          border: 2upx solid #707070;
+          border-radius: 50%;
+          overflow: hidden;
+          float: right;
+          img{
+            width: 34upx;
+            height: 34upx;
+            display: block;
+          }
+        }
+      }
+      .buy-count{
+        color: #C5AA7B;
+        font-size: 20upx;
+        margin-bottom: 16upx;
+        border: 2upx solid #E4E5E6;
+        line-height: 40upx;
+        padding: 0 5upx;
+        display: inline-block;
+      }
+      .price-warp{
+        display: flex;
+        align-items: baseline;
+        line-height: 56upx;
+        .iconImg {
+          width: 58rpx;
+          height: 36rpx;
+          margin-right: 10rpx;
+        }
+        .price{
+          color: #C83732;
+          font-size: 40upx;
+          margin-right: 20upx;
+        }
+        .original-price{
+          font-size: 24upx;
+          color: #ccc;
+          text-decoration: line-through;
+        }
+      }
+    }
+    //::v-deep .swiper-pagination-bullet{
+    //  display: none;
+    //}
+  }
+}
+::v-deep .uni-swiper-dots{
+  display: flex;
+  justify-content: center;
+  padding: 10upx 0;
+  .uni-swiper-dot{
+    width: 10upx;
+    height: 10upx;
+    background: #333333;
+    opacity: 0.3;
+    border-radius: 5upx;
+    margin: 0 5upx;
+    &-active{
+      width: 20upx;
+      height: 10upx;
+      opacity: 1;
+    }
+  }
+}
+//.pagination{
+//  display: flex;
+//  justify-content: center;
+//  padding: 20upx 0;
+//  ::v-deep .swiper-pagination-bullet{
+//    width: 10upx;
+//    height: 10upx;
+//    background: #333333;
+//    opacity: 0.3;
+//    border-radius: 5upx;
+//    margin: 0 5upx;
+//  }
+//  ::v-deep .swiper-pagination-bullet-active{
+//    width: 20upx;
+//    height: 10upx;
+//    opacity: 1;
+//  }
+//}
+
+.btn-more {
+  width: 170upx;
+  height: 54upx;
+  line-height: 54upx;
+  border: 2upx solid #C5AA7B;
+  color: #C5AA7B;
+  font-size: 24upx;
+  background-color: transparent;
+  margin: 20upx auto 0;
+  display: block;
+}
+
+</style>

+ 93 - 0
components/canvasShow/basics/product/mixin.js

@@ -0,0 +1,93 @@
+// import { directive, Swiper, SwiperSlide } from 'vue-awesome-swiper'
+// import 'swiper/css/swiper.css'
+import {funMixin} from '../../config/mixin'
+import api from '../../config/api'
+
+export const commonMixin = {
+  name: 'productList',
+  mixins: [funMixin],
+  props: {
+    terminal: {
+      type: Number,
+      default: 4
+    },
+    typeId: {
+      type: Number,
+      default: 1
+    },
+    shopId: {
+      type: Number,
+      default: 0
+    },
+    componentContent: {
+      type: Object
+    }
+  },
+  // components: {
+  //   Swiper,
+  //   SwiperSlide
+  // },
+  // directives: {
+  //   swiper: directive
+  // },
+  data () {
+    return {
+      productData: []
+    }
+  },
+  watch: {
+    'componentContent': {
+      handler(newVal, oldVal) {
+        this.getData()
+      },
+      deep: true
+    }
+  },
+  created() {
+    this.getData(true)
+  },
+  computed: {
+    swiper() {
+      if(this.$refs.mySwiper){
+        return this.$refs.mySwiper.$swiper
+      }
+    }
+  },
+  methods: {
+    getData(isFirst) {
+      const _ = this
+      if (_.componentContent.productData.sourceType === '1') {
+        if(_.componentContent.productData.productIdList && _.componentContent.productData.productIdList.length>0){
+          _.sendReq({
+            url: `${api.getProducts}?page=1&pageSize=99&ids=${_.componentContent.productData.productIdList}`,
+            method: 'GET'
+          }, (proRes) => {
+            _.productData = proRes.data.list
+            if(isFirst){
+              _.componentContent.productData.imgTextData = _.productData
+            }
+          })
+        } else {
+          _.productData = []
+        }
+      } else if(_.componentContent.productData.sourceType === '2'){
+        if(_.componentContent.productData.categoryId) {
+          _.sendReq({
+            url: `${api.getProducts}?page=1&pageSize=99&classifyId=${_.componentContent.productData.categoryId}`,
+            method: 'GET'
+          }, (proRes) => {
+            _.productData = proRes.data.list
+            if(isFirst){
+              _.componentContent.productData.imgTextData = _.productData
+            }
+            // _.swiper.update()
+          })
+        } else {
+          _.productData = {
+            products:[]
+          }
+        }
+      }
+    },
+  }
+}

+ 226 - 0
components/canvasShow/basics/product/pc/index.vue

@@ -0,0 +1,226 @@
+<template>
+  <div class="product-list" :class="'terminal'+terminal">
+    <div class="picListWarp" v-if="componentContent.arrangeType == '横向滑动'">
+      <div class="picList" v-if="productData && productData.length>0">
+        <div class="swiper-button-prev"></div>
+        <div class="swiper-button-next"></div>
+        <swiper ref="swiper" class="products-swiper" :options="swiperOption">
+          <swiper-slide class="products-swiper-slide item" v-for="(item,index) in productData" :key="index">
+            <div class="a-link" @click="jumpProductDetail(item)">
+              <div class="itemImgBox">
+                <div class="imgBox">
+                  <el-image
+                      :src="item.image"
+                      fit="contain"></el-image>
+                </div>
+              </div>
+              <div class="text">
+                <h4 class="h4">{{item.productName}}</h4>
+                <div class="priceBox">
+                  <span>¥{{item.price}}</span>
+                  <span class="discount" v-if="item.originalPrice">¥{{item.originalPrice}}</span>
+                </div>
+              </div>
+            </div>
+          </swiper-slide>
+        </swiper>
+      </div>
+    </div>
+    <div v-else class="picList" >
+      <ul class="clearfix" :class="'imgTextNum' +  componentContent.productNum">
+        <li class="item" v-for="(item,index) in productData.slice(0, componentContent.productRowNum * componentContent.productNum)" :key="index">
+          <div class="a-link" @click="jumpProductDetail(item)">
+            <div class="itemImgBox">
+              <div class="imgBox">
+                <el-image
+                    :src="item.image"
+                    fit="contain"></el-image>
+              </div>
+            </div>
+            <div class="text">
+              <h4 class="h4">{{item.productName}}</h4>
+              <div class="priceBox">
+                <span>¥{{item.price}}</span>
+                <span class="discount" v-if="item.originalPrice">¥{{item.originalPrice}}</span>
+              </div>
+            </div>
+          </div>
+        </li>
+      </ul>
+    </div>
+    <button v-show="componentContent.showMore" class="btn-more" @click="jumpProList(componentContent.productData)">查看全部 <span class="icon iconfont icon-arrow-right"></span></button>
+  </div>
+</template>
+
+<script>
+import {commonMixin} from '../mixin'
+export default {
+  mixins: [commonMixin],
+  data () {
+    return {
+      swiperOption: {
+        slidesPerView: 4, // 显示数量
+        spaceBetween: 13, // 间隔
+        autoplay: false, // 可选选项,自动滑动
+        loop: true,
+        pagination: {
+          el: '.product-pagination'
+        },
+        navigation: {
+          nextEl: '.swiper-button-next',
+          prevEl: '.swiper-button-prev'
+        }
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.product-list{
+  padding: 20px 0;
+  background-color: #fff;
+  .picListWarp{
+    width: 1380px;
+    max-width: 100%;
+    margin: 0 auto;
+    position: relative;
+  }
+  .picList{
+    width: 1200px;
+    max-width: 100%;
+    margin: 0 auto;
+    .swiper-button-prev,.swiper-button-next{
+      width: 50px;
+      height: 50px;
+      position: absolute;
+      cursor:pointer;
+      top: 140px;
+      background-repeat: no-repeat;
+      &:after{
+        content: '';
+      }
+    }
+    .swiper-button-prev{
+      left: 0;
+      background: url('../../../static/images/btn-prev.png');
+    }
+    .swiper-button-next{
+      right: 0;
+      background: url('../../../static/images/btn-next.png');
+    }
+    .a-link{
+      cursor: pointer;
+      &:hover{
+        box-shadow: 3px 4px 20px 0px rgba(186, 186, 186, 0.5);
+      }
+      .itemImgBox {
+        height: auto;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        .imgBox {
+          padding-bottom: 100%;
+          background-color: #cacaca;
+          position: relative;
+          .el-image {
+            width: 100%;
+            height: 100%;
+            position: absolute;
+            top: 0;
+            left: 0;
+          }
+        }
+      }
+      .text{
+        padding:25px 20px 17px;
+        text-align: center;
+        //height: 180px;
+        .h4{
+          font-size: 18px;
+          line-height: 24px;
+          overflow: hidden;
+          text-overflow:ellipsis;
+          white-space: nowrap;
+          color: #333333;
+          //max-height: 48px;
+        }
+        .p{
+          color: #999;
+          font-size: 16px;
+          overflow: hidden;
+          text-overflow:ellipsis;
+          white-space: nowrap;
+          padding-top: 18px;
+          position: relative;
+          margin-top: 8px;
+          &:after{
+            position: absolute;
+            top: 0;
+            left: 50%;
+            margin-left: -80px;
+            width: 160px;
+            height: 2px;
+            background: #F0F0F0;
+            content: '';
+          }
+        }
+        .priceBox {
+          padding-top: 11px;
+          line-height: 33px;
+          span {
+            font-size: 25px;
+            color: #C83732;
+            padding-right: 12px;
+          }
+          span.discount {
+            font-size: 18px;
+            color: #ccc;
+            text-decoration: line-through;
+          }
+        }
+      }
+    }
+    ul{
+      margin: -15px 0 0 -15px;
+      display: flex;
+      flex-wrap: wrap;
+      li{
+        flex: 0 0 50%;
+        padding: 15px 0 0 15px;
+        width: 0;
+      }
+    }
+    .imgTextNum2 {
+      li {
+        flex: 0 0 50%;
+      }
+    }
+    .imgTextNum3 {
+      li {
+        flex: 0 0 33.33%;
+      }
+    }
+    .imgTextNum4 {
+      li {
+        flex: 0 0 25%;
+      }
+    }
+    .imgTextNum5 {
+      li {
+        flex: 0 0 20%;
+      }
+    }
+  }
+}
+.btn-more {
+  width: 130px;
+  height: 41px;
+  border: 2px solid #C5AA7B;
+  color: #C5AA7B;
+  font-size: 18px;
+  background-color: transparent;
+  margin: 20px auto 0;
+  display: block;
+}
+</style>

+ 133 - 0
components/canvasShow/basics/shop.vue

@@ -0,0 +1,133 @@
+<template>
+  <div class="shop"  :class="'terminal' + terminal">
+    <swiper class="swiper" :indicator-dots="true" :autoplay="true">
+      <swiper-item class="shop-item" v-for="(item,index) in imgList" :key="index">
+        <div class="shop-item-warp">
+          <img class="img" :src="item.img" mode="widthFix">
+          <div class="a-link" @click="jumpLink(item.linkObj)">
+            进店逛逛<i class="iconfont icon-arrow-right"></i>
+          </div>
+        </div>
+      </swiper-item>
+    </swiper>
+<!--    <div class="pagination shop-pagination"></div>-->
+  </div>
+</template>
+
+<script>
+// import { directive, Swiper, SwiperSlide } from 'vue-awesome-swiper'
+// import 'swiper/css/swiper.css'
+import {funMixin} from '../config/mixin'
+export default {
+  name: 'shop',
+  mixins: [funMixin],
+  data () {
+    return {
+      // swiperOption: {
+      //   autoplay: false, // 可选选项,自动滑动
+      //   loop: true,
+      //   pagination: {
+      //     el: '.shop-pagination'
+      //   }
+      // }
+    }
+  },
+  props: {
+    terminal: {
+      type: Number,
+      default: 4
+    },
+    componentContent: {
+      type: Object
+    }
+  },
+  // components: {
+  //   Swiper,
+  //   SwiperSlide
+  // },
+  // directives: {
+  //   swiper: directive
+  // },
+  computed: {
+    imgList: function () {
+      console.log(this.componentContent)
+      return this.componentContent.imgTextData.filter(function (item) {
+        return item.img
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.shop{
+  position: relative;
+  .swiper{
+    height: 480upx;
+  }
+  &-item{
+    &-warp{
+      position: relative;
+      padding: 0 20upx;
+      .img{
+        width: 100%;
+      }
+      .a-link{
+        width: 220upx;
+        height: 80upx;
+        line-height: 80upx;
+        background: linear-gradient(225deg, #585858 0%, #333333 100%);
+        box-shadow: 0px 20upx 40upx rgba(0, 0, 0, 0.3);
+        display: block;
+        color: #fff;
+        font-size: 28upx;
+        text-align: center;
+        position: absolute;
+        right: 0;
+        bottom: 30upx;
+        .icon{
+          margin-left: 20upx;
+        }
+      }
+    }
+  }
+  //.pagination{
+  //  display: flex;
+  //  justify-content: center;
+  //  padding:20upx 0;
+  //  ::v-deep .swiper-pagination-bullet{
+  //    width: 12upx;
+  //    height: 12upx;
+  //    background: #333333;
+  //    border-radius: 50%;
+  //    opacity: 0.2;
+  //    margin: 0 10upx;
+  //  }
+  //  ::v-deep .swiper-pagination-bullet-active{
+  //    width: 24upx;
+  //    height: 12upx;
+  //    background: #333333;
+  //    opacity: 1;
+  //    border-radius: 8upx;
+  //  }
+  //}
+  ::v-deep .uni-swiper-dots{
+    display: flex;
+    justify-content: center;
+    padding: 0upx 0;
+    .uni-swiper-dot{
+      width: 10upx;
+      height: 10upx;
+      background: #333333;
+      opacity: 0.3;
+      border-radius: 5upx;
+      margin: 0 5upx;
+      &-active{
+        width: 20upx;
+        height: 10upx;
+        opacity: 1;
+      }
+    }
+  }
+}
+</style>

+ 163 - 0
components/canvasShow/basics/spike/app/index.vue

@@ -0,0 +1,163 @@
+<template>
+  <div class="spike">
+    <div class="spike-card" v-if="productData.products && productData.products.length>0">
+      <div class="spike-card-top">
+        <h2 class="spike-card-top-title">
+          <img class="title-img" src="../../../static/images/spike/img-title.png" alt="秒杀专区" mode="widthFix"/>
+        </h2>
+        <div class="spike-card-top-time" v-if="state===2">
+          活动已结束
+        </div>
+        <div class="spike-card-top-time" v-else>
+          距离{{count[0]}}还有 <div class="span">{{count[1]}}:{{count[2]}}:{{count[3]}}</div>
+        </div>
+        <a class="btn-more" @click="jumpSeckills(productData)">更多<i class="iconfont icon-arrow-right"></i></a>
+      </div>
+      <div class="spike-card-list">
+        <div class="spike-card-item"  v-for='item in productData.products.slice(0,4)' :key='item.productId' @click="jumpProductDetail(item)">
+          <div class="spike-card-item-img">
+            <img :src="item.image" alt="">
+          </div>
+          <div class="spike-card-item-info">
+            <h3 class="name">
+              {{item.productName}}
+            </h3>
+            <div class="stock">
+              还剩{{item.stockNumber}}件
+            </div>
+            <div class="price-warp">
+              <div class="price">
+                ¥ {{item.price}}
+              </div>
+              <div class="original-price">
+                ¥ {{item.originalPrice}}
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import {commonMixin} from '../mixin'
+export default {
+  mixins: [commonMixin]
+}
+</script>
+
+<style lang="scss" scoped>
+.spike{
+  background: #F8F8F8;
+  padding: 20upx;
+  &-card{
+    height: 498upx;
+    background: #FFFFFF;
+    border-radius: 20upx;
+    &-top{
+      position: relative;
+      padding: 32upx 0 22upx;
+      display: flex;
+      &-title{
+        padding: 9upx 20upx 9upx 30upx;
+        .title-img{
+          width: 204upx;
+          display: block;
+        }
+      }
+      &-time{
+        padding: 0 18upx;
+        height: 50upx;
+        background: linear-gradient(90deg, #C83732 0%, #E25C44 100%);
+        box-shadow: 0px 6upx 12upx rgba(233, 0, 0, 0.3);
+        opacity: 1;
+        border-radius: 26upx;
+        font-size: 24upx;
+        line-height: 50upx;
+        color: #fff;
+        text-align: center;
+        .span{
+          display: inline;
+        }
+      }
+      .btn-more{
+        position: absolute;
+        right: 8upx;
+        top: 40upx;
+        line-height: 33upx;
+        padding-right: 25upx;
+        font-size: 24upx;
+        color: #333;
+        .iconfont{
+          content: '';
+          font-size: 26upx;
+          position: absolute;
+          right: 0;
+          top: 0;
+        }
+      }
+    }
+    &-list{}
+    &-item{
+      width: 50%;
+      border-top: 1px solid  #F3F4F5;
+      border-left: 1px solid  #F3F4F5;
+      float: left;
+      align-items: center;
+      box-sizing: border-box;
+      &:nth-child(2n+1){
+        border-left: 0px;
+      }
+      &-img{
+        width: 162upx;
+        height: 196upx;
+        margin-right: 10upx;
+        float: left;
+        img {
+          width: 100%;
+          height: 100%;
+          object-fit: contain;
+        }
+      }
+      &-info{
+        height: 100%;
+        margin-left: 172upx;
+        width: 168upx;
+        .name{
+          font-size: 24upx;
+          line-height: 40upx;
+          color: #333333;
+          overflow: hidden;
+          text-overflow:ellipsis;
+          white-space: nowrap;
+        }
+        .stock{
+          padding: 0 8upx;
+          height: 40upx;
+          border: 2upx solid #E4E5E6;
+          line-height: 40upx;
+          margin: 10upx 0;
+          display: inline-block;
+          font-size: 20upx;
+          color: #C5AA7B;
+        }
+        .price{
+          font-size: 32upx;
+          font-weight: bold;
+          line-height: 44upx;
+          color: #C83732;
+          padding-right: 10upx;
+          display: inline-block;
+        }
+        .original-price{
+          font-size: 20upx;
+          line-height: 28upx;
+          color: #CCCCCC;
+          display: inline-block;
+        }
+      }
+    }
+  }
+}
+</style>

+ 137 - 0
components/canvasShow/basics/spike/mixin.js

@@ -0,0 +1,137 @@
+import api from '../../config/api'
+import {funMixin} from '../../config/mixin'
+
+export const commonMixin = {
+  name: 'spikeList',
+  mixins: [funMixin],
+  data () {
+    return {
+      productData: {
+        products: []
+      },
+      count: [],
+      state: 0,
+      timer: null
+    }
+  },
+  props: {
+    terminal: {
+      type: Number,
+      default: 4
+    },
+    typeId: {
+      type: Number,
+      default: 1
+    },
+    shopId: {
+      type: Number,
+      default: 0
+    },
+    componentContent: {
+      type: Object
+    }
+  },
+  watch: {
+    'componentContent': {
+      handler(newVal, oldVal) {
+        this.getData()
+      },
+      deep: true
+    }
+  },
+  created() {
+    this.getData()
+  },
+  methods: {
+    getData() {
+      const _ = this
+      if(_.componentContent.shopSeckillId){
+        if(this.typeId === 1){
+          const params = {
+            method: 'GET',
+            url: `${api.getPlatformSeckills}?ids=${_.componentContent.shopSeckillId}`,
+          }
+          this.sendReq(params, (res) => {
+            if(res.data.length> 0){
+              _.productData = res.data[0]
+              _.productData.products.map(function(value){
+                value.sliderVal = (value.stockNumber/value.total*100).toFixed(2)
+                return value;
+              });
+              // 只有进行中和未开始活动, 用倒计时
+              this.timer = setInterval(()=>{
+                _.getTime(_.productData)
+              }, 1000)
+            }
+          })
+        }
+        if(this.typeId === 3){
+          const params = {
+            method: 'GET',
+            url: `${api.getSeckills}?shopId=${_.shopId}&ids=${_.componentContent.shopSeckillId}`,
+          }
+          this.sendReq(params, (res) => {
+            if(res.data.length> 0){
+              _.productData = res.data[0]
+              _.productData.products.map(function(value){
+                value.sliderVal = (value.stockNumber/value.total*100).toFixed(2)
+                return value;
+              });
+              // 只有进行中和未开始活动, 用倒计时
+              if(_.productData.state !==2) {
+                this.timer = setInterval(()=>{
+                  _.getTime(_.productData)
+                }, 1000)
+              }
+            } else {
+              _.productData = {
+                products:[]
+              }
+            }
+          })
+        }
+      } else {
+        _.productData = {
+          products:[]
+        }
+      }
+    },
+    getTime(info) {
+      const date = new Date().getTime()
+      let startTime = ''
+      let endTime = ''
+      if(this.typeId === 1){
+        startTime = new Date(info.startTime.replace(/-/g,'/')).getTime()
+        endTime = new Date(info.endTime.replace(/-/g,'/')).getTime()
+      } else {
+        startTime = new Date(info.effectiveStart.replace(/-/g,'/')).getTime()
+        endTime = new Date(info.effectiveEnd.replace(/-/g,'/')).getTime()
+      }
+      if(date > endTime){
+        this.state = 2
+      } else if(startTime > date) {
+        this.state = 0
+        this.countDown(startTime-date) // 未开始
+      } else {
+        this.state = 1
+        this.countDown(endTime-date) // 进行中
+      }
+
+    },
+
+    countDown(time) {
+      const fn = (v) =>  v < 10 ? `0${v}` : v
+      const t = parseInt(time / 1000)
+      const text = this.state == 0 ? '开始' : '结束'
+      const hour = parseInt(t / 3600)
+      const min = parseInt((t % 3600) / 60)
+      const s = t % 60
+      // console.log(min, '分',t)
+      this.count = [text, fn(hour), fn(min), fn(s)]
+      // console.log(text, fn(hour), fn(min), fn(s))
+    }
+  },
+  beforeDestroy() {
+    clearInterval(this.timer)
+  }
+}

+ 211 - 0
components/canvasShow/basics/spike/pc/index.vue

@@ -0,0 +1,211 @@
+<template>
+  <div class="spikeList" :class="'terminal'+terminal">
+    <div class="warp" v-if="componentContent.shopSeckillId && productData">
+      <div>
+        <div class="spikeCard">
+          <div class="listLeft">
+            <img src="../../../static/images/spike/bg-spike.png">
+            <div class="bgBox">
+              <div class="title"><img src="../../../static/images/spike/tit-spike.png" alt="秒杀专区"></div>
+              <div class="text">整点秒杀,数量有限</div>
+              <div class="sessions">{{productData.seckillName}} {{isStart && '倒计时'}}</div>
+              <div class="time-text">距离{{count[0]}}还有</div>
+              <div class="timeBox">
+                <span>{{count[1]}}</span><i>:</i><span>{{count[2]}}</span><i>:</i><span>{{count[3]}}</span>
+              </div>
+              <button class="btn-more" @click="jumpSeckills(productData)">查看全部 <span class="icon iconfont icon-arrow-right"></span></button>
+            </div>
+          </div>
+          <div class="listRight">
+            <div class="listItem" v-for='it in productData.products' :key='it.productId' @click="jumpProductDetail(it)">
+              <div class="imgBox">
+                <img :src="it.image" alt="">
+              </div>
+              <div class="itemInfo">
+                <h3>{{it.productName}}</h3>
+                <div class="begrenzt">
+                  <span class="people">还剩{{it.stockNumber}}件</span>
+                  <div class="progress" :style="{'width':it.sliderVal + '%'}"></div>
+                </div>
+                <div class="originalPrice">¥{{it.originalPrice}}</div>
+                <dl class="discountPrice">
+                  <dt>
+                    <img src="../../../static/images/spike/flag-spike.png" alt="秒杀价">
+                  </dt>
+                  <dd>
+                    <span>¥</span><b>{{it.price}}</b>
+                  </dd>
+                </dl>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import {commonMixin} from '../mixin'
+export default {
+  mixins: [commonMixin]
+}
+</script>
+
+<style lang="scss" scoped>
+.spikeList{
+  min-height: 100px;
+  &.terminal4{
+    background-color: #F3F4F5;
+    .warp{
+      width: 1200px;
+    }
+    .spikeCard {
+      display: flex;
+      width: 100%;
+      .listLeft {
+        width: 487px;
+        height: 758px;
+        text-align: center;
+        position: relative;
+        .bgBox {
+          position: absolute;
+          top: 0;
+          left: 0;
+          width: 100%;
+          .title{
+            padding-top: 140px;
+          }
+          .text{
+            color: #8E8D8C;
+            font-size: 30px;
+            line-height: 40px;
+            margin: 15px 0 100px 0;
+          }
+          .sessions{
+            color: #FFEBC4;
+            font-size: 30px;
+            line-height: 40px;
+          }
+          .time-text{
+            padding-top: 60px;
+            color: #FFEBC4;
+            font-size: 18px;
+          }
+          .timeBox {
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            margin:10px 0 50px;
+            span {
+              display: block;
+              width: 60px;
+              height: 60px;
+              background: rgba(51,51,51,0.8);
+              color: #FFFFFF;
+              font-size: 30px;
+              line-height: 60px;
+            }
+            i {
+              font-size: 30px;
+              color: #fff;
+              font-style: normal;
+              font-weight: bold;
+              padding: 0 13px;
+            }
+          }
+          .btn-more {
+            width: 130px;
+            height: 41px;
+            border: 2px solid #FFEBC4;
+            color: #FFEBC4;
+            font-size: 18px;
+            background-color: transparent;
+          }
+        }
+      }
+      .listRight {
+        padding: 40px 0 0 8px;
+        .listItem {
+          float:left;
+          width: 220px;
+          height: 335px;
+          margin:0 0 15px 15px;
+          background-color: #fff;
+          .imgBox {
+            width: 220px;
+            height: 220px;
+            img {
+              width: 100%;
+              height: 100%;
+            }
+          }
+          .itemInfo {
+            position: relative;
+            padding: 10px;
+            text-align: center;
+            h3{
+              font-size: 14px;
+              color: #333;
+              line-height: 18px;
+              overflow: hidden;
+              text-overflow:ellipsis;
+              white-space: nowrap;
+            }
+            .begrenzt{
+              position: absolute;
+              left: 5px;
+              top: -35px;
+              width: 210px;
+              height: 30px;
+              border: 1px solid #C83732;
+              background-color: #C83732;
+              text-align: center;
+              .people{
+                position: relative;
+                z-index: 2;
+                font-size: 12px;
+                color: #FFBAB8;
+                line-height: 30px;
+              }
+              .progress{
+                position: absolute;
+                right: 0;
+                top: 0;
+                background-color: #fff;
+                height: 28px;
+              }
+            }
+            .discountPrice {
+              display: inline-block;
+              width: 130px;
+              dt{
+                float: left;
+              }
+              dd{
+                border: 1px solid #F3F4F5;
+                color: #C83732;
+                line-height: 30px;
+                font-weight: bold;
+                span {
+                  font-size: 18px;
+                }
+                b {
+                  font-size: 18px;
+                }
+              }
+            }
+            .originalPrice {
+              font-size: 14px;
+              line-height: 19px;
+              padding: 10px 0 6px;
+              color: #ccc;
+              text-decoration: line-through;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 79 - 0
components/canvasShow/basics/text.vue

@@ -0,0 +1,79 @@
+<template>
+  <div class="text warp" :class="['text-'+componentContent.textPos,{'show-more':componentContent.showMore},'terminal' + terminal]" :style="{backgroundColor:componentContent.bgColor}">
+    <div class="line-warp" :class="{'borderBot':componentContent.showLine}">
+      <h3 class="h3" :style="{fontSize:componentContent.fontSizeNum+'px',fontWeight:componentContent.textFontW,color:componentContent.titColor}">{{componentContent.title}}</h3>
+      <p class="p" :style="{fontSize:componentContent.describeSizeNum+'px',fontWeight:componentContent.describeFontW,color:componentContent.describeColor}">{{componentContent.describe}}</p>
+      <div class="btn-more" v-show="componentContent.showMore" :class="'style'+componentContent.styleValue" @click="jumpLink(item.linkObj)"><span>查看更多</span><i class="iconfont icon-arrow-right"></i></div>
+    </div>
+  </div>
+</template>
+
+<script>
+  export default {
+    name: 'textComponent',
+    data () {
+      return {
+      }
+    },
+    props: {
+      terminal: {
+        type: Number,
+        default: 4
+      },
+      componentContent: {
+        type: Object
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .text{
+    padding: 0 20upx;
+    position: relative;
+    .line-warp{
+      padding: 10upx 0;
+    }
+    .borderBot{
+      border-bottom: 1upx solid #cccc;
+    }
+    .h3{
+      line-height: 1.5;
+    }
+    .p{
+      line-height: 1.5;
+      margin-top: 5upx;
+    }
+    .style1{
+
+    }
+    .style2{
+      .iconfont{
+        display: none;
+      }
+    }
+    .style3{
+      span{
+        display: none;
+      }
+    }
+    &.text-left{
+      text-align: left;
+      &.show-more{
+        position: relative;
+        padding-right: 20upx;
+        .btn-more{
+          position: absolute;
+          right: 0;
+          top: 10upx;
+        }
+      }
+    }
+    &.text-center{
+      text-align: center;
+    }
+    &.text-right{
+      text-align: right;
+    }
+  }
+</style>

+ 76 - 0
components/canvasShow/basics/video.vue

@@ -0,0 +1,76 @@
+<template>
+  <div class="videoBox warp" :class="'terminal' + terminal">
+    <div class="videoLeftBox">
+      <h3>{{componentContent.title}}</h3>
+      <div v-html="componentContent.mainBody"></div>
+    </div>
+    <div class="videoRightBox">
+      <video class="myVideo" id="myVideo" :src="componentContent.videoUrl" enable-danmu danmu-btn controls></video>
+    </div>
+    <div class="clear"></div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'videoBox',
+  props: {
+    terminal: {
+      type: Number,
+      default: 4
+    },
+    componentContent: {
+      type: Object
+    }
+  },
+  data () {
+    return {
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+ .videoBox {
+   padding: 20upx 0;
+   display: flex;
+   justify-content: flex-start;
+   align-items: center;
+   .videoLeftBox {
+     width: 50%;
+     padding-right: 10%;
+     h3 {
+       font-size: 28upx;
+       color: #333333;
+       margin-bottom: 30upx;
+     }
+     p {
+       color: #333333;
+       font-size: 14upx;
+       line-height: 30upx;
+     }
+   }
+   .videoRightBox {
+     width: 50%;
+     video {
+       width: 100%;
+     }
+   }
+   .clear {
+     clear: both;
+   }
+ }
+ .terminal1,.terminal2,.terminal3{
+   &.videoBox{
+     display: block;
+     .videoLeftBox{
+       width: 100%;
+       text-align: center;
+       margin-bottom: 20upx;
+     }
+     .videoRightBox {
+       width: 100%;
+     }
+   }
+ }
+</style>

+ 287 - 0
components/canvasShow/basics/vip/app/index.vue

@@ -0,0 +1,287 @@
+<template>
+  <div class="vip">
+    <div class="vip-card">
+      <div class="vip-title">
+        <image class="title-img" src="../../../static/images/vip/img-title.png" alt="会员专区" mode="widthFix"/>
+        <a v-show="componentContent.showMore" class="btn-more a-link" @click="jumpVip">更多<i class="iconfont icon-arrow-right"></i></a>
+      </div>
+      <div>
+      <swiper class="swiper vip-list" :indicator-dots="true" :autoplay="true" v-if="productData.length > 2">
+        <swiper-item class="vip-item-warp" v-for="(itemJ,indexJ) in listTemp" :key="indexJ">
+          <div class="vip-item"  v-for="(item,index) in itemJ" :key="index"  @click="jumpProductDetail(item)">
+            <div class="vip-item-img">
+              <image v-show="item.image" class="img" :src="item.image">
+            </div>
+            <div class="vip-item-info">
+              <h3 class="name">
+                {{item.productName}}
+              </h3>
+              <div class="stock">
+                还剩{{item.stockNumber}}件
+              </div>
+              <div class="original-price">
+                ¥ {{item.originalPrice}}
+              </div>
+              <div class="price-warp">
+                <div class="flag">
+                  <image src="../../../static/images/vip/flag-vip.png" alt="会员价" class="flagImg"/>
+                </div>
+                <div class="price">
+                  ¥ {{item.price}}
+                </div>
+              </div>
+              <div class="btn-buy">
+                <span>去购买</span>
+                <div class="progress">
+                  <i></i>
+                </div>
+              </div>
+            </div>
+          </div>
+        </swiper-item>
+      </swiper>
+        <div class="swiper vip-list" v-else>
+          <div class="vip-item-warp" v-for="(itemJ,indexJ) in listTemp" :key="indexJ">
+            <div class="vip-item"  v-for="(item,index) in itemJ" :key="index" @click="jumpProductDetail(item)">
+              <div class="vip-item-img">
+                <image v-show="item.image" class="img" :src="item.image">
+              </div>
+              <div class="vip-item-info">
+                <h3 class="name">
+                  {{item.productName}}
+                </h3>
+                <div class="stock">
+                  还剩{{item.stockNumber}}件
+                </div>
+                <div class="original-price">
+                  ¥ {{item.originalPrice}}
+                </div>
+                <div class="price-warp">
+                  <div class="flag">
+                    <image src="../../../static/images/vip/flag-vip.png" alt="会员价" class="flagImg"/>
+                  </div>
+                  <div class="price">
+                    ¥ {{item.price}}
+                  </div>
+                </div>
+                <div class="btn-buy">
+                  <span>去购买</span>
+                  <div class="progress">
+                    <i></i>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+<!--      <div class="pagination vip-pagination"></div>-->
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import {commonMixin} from '../mixin'
+export default {
+  mixins: [commonMixin],
+  data () {
+    return {
+      // swiperOption: {
+      //   autoplay: false, // 可选选项,自动滑动
+      //   loop: true,
+      //   pagination: {
+      //     el: '.vip-pagination'
+      //   }
+      // }
+    }
+  },
+  computed: {
+    listTemp: function () {
+      var list = this.productData;
+      var arrTemp = [];
+      var index = -1;
+      var count = 2; // 每组多少条
+      for (var i = 0; i < list.length; i++) {
+        if (i % count==0) {
+          arrTemp.push([]);
+          index ++
+        }
+        arrTemp[index].push(list[i]);
+      }
+      return arrTemp;
+    }
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+.vip{
+  &-card{
+    background: #333333;
+    padding: 0 20upx 20upx;
+  }
+  &-title{
+    padding: 42upx 0 28upx 30upx;
+    position: relative;
+    .title-img{
+      display: block;
+      width: 197upx;
+    }
+    .btn-more{
+      position: absolute;
+      right: 8upx;
+      top: 40upx;
+      line-height: 33upx;
+      padding-right: 25upx;
+      font-size: 24upx;
+      color: #FFEBC4;
+      .iconfont{
+        content: '';
+        font-size: 26upx;
+        position: absolute;
+        right: 0;
+        top: 0;
+      }
+    }
+  }
+  &-list{
+    height: 620upx;
+  }
+  &-item{
+    display: flex;
+    background-color: #fff;
+    margin-top: 20upx;
+    &:first-child{
+      margin-top: 0upx;
+    }
+    &-img{
+      width: 260upx;
+      height: 260upx;
+      margin-right: 20upx;
+      background-color: #ececec;
+      .img {
+        width: 100%;
+        height: 100%;
+        object-fit: contain;
+        display: block;
+      }
+    }
+    &-info{
+      flex: 1;
+      position: relative;
+      .name{
+        font-size: 28upx;
+        height: 80rpx;
+        line-height: 40upx;
+        color: #333333;
+        padding: 30upx 0 10upx;
+        overflow:hidden;
+        text-overflow:ellipsis;
+        display:-webkit-box;
+        -webkit-box-orient:vertical;
+        -webkit-line-clamp:2;
+      }
+      .stock{
+        color: #C5AA7B;
+        font-size: 20upx;
+        border: 2upx solid #E4E5E6;
+        line-height: 40upx;
+        padding: 0 5upx;
+        display: inline-block;
+        margin-bottom: 30upx;
+      }
+      .original-price{
+        font-size: 24upx;
+        line-height: 34upx;
+        color: #CCCCCC;
+      }
+      .flag{
+        float: left;
+        padding: 12upx 10upx 0 0;
+        .flagImg {
+          width: 58upx;
+          height: 36upx;
+          display: block;
+        }
+      }
+      .price{
+        font-size: 40upx;
+        font-weight: bold;
+        line-height: 56upx;
+        color: #C83732;
+      }
+      .btn-buy{
+        position: absolute;
+        right: 20upx;
+        bottom: 20upx;
+        width: 160upx;
+        height: 84upx;
+        background: linear-gradient(90deg, #C83732 0%, #E25C44 100%);
+        box-shadow: 0px 6upx 12upx rgba(233, 0, 0, 0.3);
+        border-radius: 10upx;
+        text-align: center;
+        padding: 16upx 20upx 0;
+        span{
+          font-size: 24upx;
+          line-height: 34upx;
+          color: #FFFFFF;
+          margin-bottom: 10upx;
+        }
+        .progress{
+          height: 8upx;
+          background: rgba(255,255,255,0.5);
+          border-radius: 4upx;
+          position: relative;
+          i{
+            position: absolute;
+            left: 0;
+            top: 0;
+            width: 50%;
+            height: 8upx;
+            background-color: #fff;
+            border-radius: 4upx;
+          }
+        }
+      }
+    }
+  }
+  ::v-deep .uni-swiper-dots{
+    display: flex;
+    justify-content: center;
+    padding-top:20upx;
+    .uni-swiper-dot{
+      width: 24upx;
+      height: 4upx;
+      background: #fff;
+      opacity: 0.5;
+      border-radius: 2upx;
+      margin: 0 5upx;
+      &-active{
+        opacity: 1;
+      }
+    }
+  }
+.vip-item-info {
+  .price-warp {
+    display: flex;
+    align-items: center;
+  }
+}
+  //.pagination{
+  //  display: flex;
+  //  justify-content: center;
+  //  padding-top:20upx;
+  //  ::v-deep .swiper-pagination-bullet{
+  //    width: 24upx;
+  //    height: 4upx;
+  //    background: #fff;
+  //    opacity: 0.5;
+  //    border-radius: 2upx;
+  //    margin: 0 5upx;
+  //  }
+  //  ::v-deep .swiper-pagination-bullet-active{
+  //    opacity: 1;
+  //  }
+  //}
+}
+</style>

+ 60 - 0
components/canvasShow/basics/vip/mixin.js

@@ -0,0 +1,60 @@
+// import { directive, Swiper, SwiperSlide } from 'vue-awesome-swiper'
+// import 'swiper/css/swiper.css'
+import api from '../../config/api'
+import {funMixin} from '../../config/mixin'
+
+export const commonMixin = {
+  name: 'productList',
+  mixins: [funMixin],
+  props: {
+    terminal: {
+      type: Number,
+      default: 4
+    },
+    typeId: {
+      type: Number,
+      default: 1
+    },
+    shopId: {
+      type: Number,
+      default: 0
+    },
+    componentContent: {
+      type: Object
+    }
+  },
+  // components: {
+  //   Swiper,
+  //   SwiperSlide
+  // },
+  // directives: {
+  //   swiper: directive
+  // },
+  data () {
+    return {
+      productData: []
+    }
+  },
+  watch: {
+    'componentContent': {
+      handler(newVal, oldVal) {
+        this.getData()
+      },
+      deep: true
+    }
+  },
+  created() {
+    this.getData()
+  },
+  methods: {
+    getData() {
+      const _ = this
+      _.sendReq({
+        url: `${api.getMemberProducts}?page=1&pageSize=20`,
+        method: 'GET'
+      }, (proRes) => {
+        _.productData = proRes.data.list
+      })
+    }
+  }
+}

+ 241 - 0
components/canvasShow/basics/vip/pc/index.vue

@@ -0,0 +1,241 @@
+<template>
+  <div class="product-list" :class="'terminal'+terminal">
+    <div class="picListWarp" v-if="componentContent.arrangeType == '横向滑动'">
+      <div class="picList" v-if="productData && productData.length>0">
+        <div class="vip-button-prev"></div>
+        <div class="vip-button-next"></div>
+        <swiper class="products-swiper" :options="swiperOption">
+          <swiper-slide class="products-swiper-slide item" v-for="(item,index) in productData" :key="index">
+            <div class="a-link" @click="jumpProductDetail(item)">
+              <div class="itemImgBox">
+                <div class="imgBox">
+                  <el-image
+                    :src="item.image"
+                    fit="contain"></el-image>
+                </div>
+              </div>
+              <div class="text">
+                <h4 class="h4">{{item.productName}}</h4>
+                <div class="priceBox">
+                  <dl>
+                    <dt><img src="../../../static/images/vip/flag-vip.png" alt="会员价"></dt>
+                    <dd>
+                      ¥{{item.price}}
+                    </dd>
+                  </dl>
+                </div>
+              </div>
+            </div>
+          </swiper-slide>
+        </swiper>
+      </div>
+    </div>
+    <div v-else class="picList" >
+      <ul class="clearfix" :class="'imgTextNum' +  componentContent.productNum">
+        <li class="item" v-for="(item,index) in productData.slice(0, componentContent.productRowNum * componentContent.productNum)" :key="index">
+          <div class="a-link" @click="jumpProductDetail(item)">
+            <div class="itemImgBox">
+              <div class="imgBox">
+                <el-image
+                  :src="item.image"
+                  fit="contain"></el-image>
+              </div>
+            </div>
+            <div class="text">
+              <h4 class="h4">{{item.productName}}</h4>
+              <div class="priceBox">
+                <dl>
+                  <dt><img src="../../../static/images/vip/flag-vip.png" alt="会员价"></dt>
+                  <dd>
+                    ¥{{item.price}}
+                  </dd>
+                </dl>
+              </div>
+            </div>
+          </div>
+        </li>
+      </ul>
+    </div>
+    <button v-show="componentContent.showMore" class="btn-more" @click="jumpVip">查看全部 <span class="icon iconfont icon-arrow-right"></span></button>
+  </div>
+</template>
+
+<script>
+import {commonMixin} from '../mixin'
+  export default {
+    mixins: [commonMixin],
+    data () {
+      return {
+        swiperOption: {
+          slidesPerView: 4, // 显示数量
+          spaceBetween: 13, // 间隔
+          autoplay: false, // 可选选项,自动滑动
+          loop: true,
+          pagination: {
+            el: '.vip-pagination'
+          },
+          navigation: {
+            nextEl: '.vip-button-next',
+            prevEl: '.vip-button-prev'
+          }
+        }
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .product-list{
+    padding: 20px 0;
+    background-color: #fff;
+    .picListWarp{
+      width: 1380px;
+      max-width: 100%;
+      margin: 0 auto;
+      position: relative;
+    }
+    .picList{
+      width: 1200px;
+      max-width: 100%;
+      margin: 0 auto;
+      .vip-button-prev,.vip-button-next{
+        width: 50px;
+        height: 50px;
+        position: absolute;
+        cursor:pointer;
+        top: 140px;
+        background-repeat: no-repeat;
+        &:after{
+          content: '';
+        }
+      }
+      .vip-button-prev{
+        left: 0;
+        background: url('../../../static/images/btn-prev.png');
+      }
+      .vip-button-next{
+        right: 0;
+        background: url('../../../static/images/btn-next.png');
+      }
+      .a-link{
+        cursor: pointer;
+        &:hover{
+          box-shadow: 3px 4px 20px 0px rgba(186, 186, 186, 0.5);
+        }
+        .itemImgBox {
+          height: auto;
+          display: flex;
+          flex-direction: column;
+          justify-content: center;
+          .imgBox {
+            padding-bottom: 100%;
+            background-color: #cacaca;
+            position: relative;
+            .el-image {
+              width: 100%;
+              height: 100%;
+              position: absolute;
+              top: 0;
+              left: 0;
+            }
+          }
+        }
+        .text{
+          padding:25px 20px 17px;
+          text-align: center;
+          //height: 180px;
+          .h4{
+            font-size: 18px;
+            line-height: 24px;
+            overflow: hidden;
+            text-overflow:ellipsis;
+            white-space: nowrap;
+            color: #333333;
+            margin-bottom: 15px;
+            //max-height: 48px;
+          }
+          .p{
+            color: #999;
+            font-size: 16px;
+            overflow: hidden;
+            text-overflow:ellipsis;
+            white-space: nowrap;
+            padding-top: 18px;
+            position: relative;
+            margin-top: 8px;
+            &:after{
+              position: absolute;
+              top: 0;
+              left: 50%;
+              margin-left: -80px;
+              width: 160px;
+              height: 2px;
+              background: #F0F0F0;
+              content: '';
+            }
+          }
+          .priceBox {
+            dl {
+              display: inline-block;
+              min-width: 130px;
+              dt{
+                float: left;
+                img{
+                  display: block;
+                }
+              }
+              dd{
+                border: 1px solid #F3F4F5;
+                font-size: 25px;
+                line-height: 34px;
+                color: #C83732;
+                margin-left: 57px;
+                padding: 0 10px;
+              }
+            }
+          }
+        }
+      }
+      ul{
+        margin: -15px 0 0 -15px;
+        display: flex;
+        flex-wrap: wrap;
+        li{
+          flex: 0 0 50%;
+          padding: 15px 0 0 15px;
+          width: 0;
+        }
+      }
+      .imgTextNum2 {
+        li {
+          flex: 0 0 50%;
+        }
+      }
+      .imgTextNum3 {
+        li {
+          flex: 0 0 33.33%;
+        }
+      }
+      .imgTextNum4 {
+        li {
+          flex: 0 0 25%;
+        }
+      }
+      .imgTextNum5 {
+        li {
+          flex: 0 0 20%;
+        }
+      }
+    }
+  }
+  .btn-more {
+    width: 130px;
+    height: 41px;
+    border: 2px solid #C5AA7B;
+    color: #C5AA7B;
+    font-size: 18px;
+    background-color: transparent;
+    margin: 20px auto 0;
+    display: block;
+  }
+</style>

+ 154 - 0
components/canvasShow/canvasShowPage.vue

@@ -0,0 +1,154 @@
+<template>
+  <div class="layout hom-layout">
+      <div class="list-group-item" v-for="(item,index) in componentsData" :key="index">
+<!--        <component :is="componentMap[terminal-1].get(item.type)" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></component>-->
+        <com-banner v-if="item.type==='banner'" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></com-banner>
+        <com-text v-if="item.type==='text'" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></com-text>
+        <com-image-text v-if="item.type==='imageText'" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></com-image-text>
+        <com-brand-list v-if="item.type==='brandList'" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></com-brand-list>
+        <com-category-list v-if="item.type==='categoryList'" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></com-category-list>
+        <com-image-text-list v-if="item.type==='imageTextList'" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></com-image-text-list>
+        <com-assist-div v-if="item.type==='assistDiv'" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></com-assist-div>
+        <com-image-text-nav v-if="item.type==='imageTextNav'" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></com-image-text-nav>
+        <com-product v-if="item.type==='productList'" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></com-product>
+        <com-video-box v-if="item.type==='videoBox'" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></com-video-box>
+        <com-coupon v-if="item.type==='coupon'" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></com-coupon>
+        <com-custom v-if="item.type==='custom'" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></com-custom>
+        <com-notice v-if="item.type==='notice'" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></com-notice>
+        <com-vip v-if="item.type==='vip'" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></com-vip>
+        <com-group v-if="item.type==='groupList'" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></com-group>
+        <com-discount v-if="item.type==='discountList'" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></com-discount>
+        <com-spike v-if="item.type==='spikeList'" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></com-spike>
+        <com-price v-if="item.type==='priceList'" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></com-price>
+        <com-new-product v-if="item.type==='newProduct'" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></com-new-product>
+        <com-shop v-if="item.type==='shop'" :componentContent="item.componentContent" :terminal="terminal" :typeId="typeId" :shopId="shopId"></com-shop>
+      </div>
+  </div>
+</template>
+
+<script>
+  // import comComponentMap from './componentMap'
+  import comBanner from '@/components/canvasShow/basics/banner'
+  import comText from '@/components/canvasShow/basics/text'
+  import comImageText from '@/components/canvasShow/basics/imageText'
+  import comBrandList from '@/components/canvasShow/basics/brandList'
+  import comCategoryList from '@/components/canvasShow/basics/categoryList'
+  import comImageTextList from '@/components/canvasShow/basics/imageTextList'
+  import comAssistDiv from '@/components/canvasShow/basics/assistDiv'
+  import comImageTextNav from '@/components/canvasShow/basics/imageTextNav'
+  import comProduct from '@/components/canvasShow/basics/product/app/index'
+  import comVideoBox from '@/components/canvasShow/basics/video'
+  import comCoupon from '@/components/canvasShow/basics/coupon/app'
+  import comCustom from '@/components/canvasShow/basics/custom'
+  import comNotice from '@/components/canvasShow/basics/notice'
+  import comVip from '@/components/canvasShow/basics/vip/app'
+  import comGroup from '@/components/canvasShow/basics/group/app/index'
+  import comDiscount from '@/components/canvasShow/basics/discount/app'
+  import comSpike from '@/components/canvasShow/basics/spike/app'
+  import comPrice from '@/components/canvasShow/basics/price/app'
+  import comNewProduct from '@/components/canvasShow/basics/newProduct/app'
+  import comShop from '@/components/canvasShow/basics/shop'
+  import api from './config/api'
+  import {sendReqMixin} from './config/mixin'
+  export default {
+    name: 'canvasPage',
+    mixins: [sendReqMixin],
+    data () {
+      return {
+        // terminal: 4, // 画布设备 1 小程序,2 H5,3 App 4 电脑
+        // typeId: 3, // 画布类型 1 平台画布,2 自定义页面,3 商家店铺装修
+        // shopId: 0, // 店铺id
+        componentsData: [],
+        activeComponent: -1,
+        // componentMap: componentMap
+      }
+    },
+    components: {
+      comBanner,
+      comText,
+      comImageText,
+      comBrandList,
+      comCategoryList,
+      comImageTextList,
+      comAssistDiv,
+      comImageTextNav,
+      comProduct,
+      comVideoBox,
+      comCoupon,
+      comCustom,
+      comGroup,
+      comDiscount,
+      comSpike,
+      comPrice,
+      comNotice,
+      comVip,
+      comNewProduct,
+      comShop
+    },
+    props: {
+      terminal: {
+        type: Number,
+        default: 4
+      },
+      typeId: {
+        type: Number,
+        default: 1
+      },
+      shopId: {
+        type: Number,
+        default: 0
+      }
+    },
+    mounted () {
+      // this.shopId = Cookies.get('cereShopId')
+      this.canvasGet()
+    },
+    methods: {
+      // 读取画布
+      canvasGet () {
+        var _this = this
+        var apiUrl = api.getCanvas + '?terminal=' + this.terminal + '&type=' + this.typeId
+        if (this.shopId) {
+          apiUrl += '&shopId=' + this.shopId
+        }
+        let params = {
+          url: apiUrl,
+          method: 'GET'
+        }
+        this.sendReq(params, (res) => {
+          if (JSON.stringify(res.data) !== '{}') {
+            _this.canvasId = res.data.canvasId
+            var componentsData = JSON.parse(res.data.json)
+            _this.componentsData = componentsData
+          } else {
+            _this.canvasId = ''
+          }
+          // console.log(JSON.parse(res.data.json))
+        })
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  .hom-layout{
+    background-color: #ffffff;
+    width: 100%;
+    overflow: hidden;
+  }
+</style>
+
+<style lang="scss">
+  .warp{
+    width: 710px;
+    margin: 0 auto;
+    max-width: 100%;
+    &.terminal4{
+      width: 1200px;
+      max-width: 100%;
+    }
+  }
+  .flex-box{
+    display: flex;
+  }
+</style>

+ 103 - 0
components/canvasShow/componentMap.js

@@ -0,0 +1,103 @@
+export const componentMap = [
+  // 小程序
+  new Map([
+    // ['shopHeader', () => import('./cereshop/app/shopHeader')], // 首页头部
+    ['banner', () => import('./basics/banner')], // 轮播图
+    ['text', () => import('./basics/text')], // 文本
+    ['imageText', () => import('./basics/imageText')], // 图文
+    ['brandList', () => import('./basics/brandList')], // 品牌列表
+    ['categoryList', () => import('./basics/categoryList')], // 品牌列表
+    ['imageTextList', () => import('./basics/imageTextList')], // 图文列表
+    ['assistDiv', () => import('./basics/assistDiv')], // 铺助分割
+    ['imageTextNav', () => import('./basics/imageTextNav')], // 图文导航
+    ['productList', () => import('./basics/product/app')], // 商品列表
+    ['videoBox', () => import('./basics/video')], // 视频
+    ['coupon', () => import('./basics/coupon/app')], // 优惠券
+    ['custom', () => import('./basics/custom')], // 自定义
+    ['notice', () => import('./basics/notice')], // 公告
+    ['vip', () => import('./basics/vip/app')], // 会员专区
+    // ['shopHead', () => import('./basics/shopHead')], // 店铺头部
+    ['groupList', () => import('./basics/group/app')], // 拼团专区
+    ['spikeList', () => import('./basics/spike/app')], // 秒杀专区
+    ['priceList', () => import('./basics/price/app')], // 定价捆绑
+    ['discountList', () => import('./basics/discount/app')], // 限时折扣
+    ['newProduct', () => import('./basics/newProduct/app')], // 每日上新
+    ['shop', () => import('./basics/shop')] // 每日好店
+  ]),
+  // H5
+  new Map([
+    // ['shopHeader', () => import('./cereshop/app/shopHeader')], // 首页头部
+    ['banner', () => import('./basics/banner')], // 轮播图
+    ['text', () => import('./basics/text')], // 文本
+    ['imageText', () => import('./basics/imageText')], // 图文
+    ['brandList', () => import('./basics/brandList')], // 品牌列表
+    ['categoryList', () => import('./basics/categoryList')], // 品牌列表
+    ['imageTextList', () => import('./basics/imageTextList')], // 图文列表
+    ['assistDiv', () => import('./basics/assistDiv')], // 铺助分割
+    ['imageTextNav', () => import('./basics/imageTextNav')], // 图文导航
+    ['productList', () => import('./basics/product/app')], // 商品列表
+    ['videoBox', () => import('./basics/video')], // 视频
+    ['coupon', () => import('./basics/coupon/app')], // 优惠券
+    ['custom', () => import('./basics/custom')], // 自定义
+    ['notice', () => import('./basics/notice')], // 公告
+    ['vip', () => import('./basics/vip/app')], // 会员专区
+    // ['shopHead', () => import('./basics/shopHead')], // 店铺头部
+    ['groupList', () => import('./basics/group/app')], // 拼团专区
+    ['spikeList', () => import('./basics/spike/app')], // 秒杀专区
+    ['priceList', () => import('./basics/price/app')], // 定价捆绑
+    ['discountList', () => import('./basics/discount/app')], // 限时折扣
+    ['newProduct', () => import('./basics/newProduct/app')], // 每日上新
+    ['shop', () => import('./basics/shop')] // 每日好店
+  ]),
+  // APP
+  new Map([
+    // ['shopHeader', () => import('./cereshop/app/shopHeader')], // 首页头部
+    ['banner', () => import('./basics/banner')], // 轮播图
+    ['text', () => import('./basics/text')], // 文本
+    ['imageText', () => import('./basics/imageText')], // 图文
+    ['brandList', () => import('./basics/brandList')], // 品牌列表
+    ['categoryList', () => import('./basics/categoryList')], // 品牌列表
+    ['imageTextList', () => import('./basics/imageTextList')], // 图文列表
+    ['assistDiv', () => import('./basics/assistDiv')], // 铺助分割
+    ['imageTextNav', () => import('./basics/imageTextNav')], // 图文导航
+    ['productList', () => import('./basics/product/app')], // 商品列表
+    ['videoBox', () => import('./basics/video')], // 视频
+    ['coupon', () => import('./basics/coupon/app')], // 优惠券
+    ['custom', () => import('./basics/custom')], // 自定义
+    ['notice', () => import('./basics/notice')], // 公告
+    ['vip', () => import('./basics/vip/app')], // 会员专区
+    // ['shopHead', () => import('./basics/shopHead')], // 店铺头部
+    ['groupList', () => import('./basics/group/app')], // 拼团专区
+    ['spikeList', () => import('./basics/spike/app')], // 秒杀专区
+    ['priceList', () => import('./basics/price/app')], // 定价捆绑
+    ['discountList', () => import('./basics/discount/app')], // 限时折扣
+    ['newProduct', () => import('./basics/newProduct/app')], // 每日上新
+    ['shop', () => import('./basics/shop')] // 每日好店
+  ]),
+  // PC
+  new Map([
+    // ['shopHeader', () => import('./cereshop/pc/shopHeader')], // 首页头部
+    ['banner', () => import('./basics/banner')], // 轮播图
+    ['text', () => import('./basics/text')], // 文本
+    ['imageText', () => import('./basics/imageText')], // 图文
+    ['brandList', () => import('./basics/brandList')], // 品牌列表
+    ['categoryList', () => import('./basics/categoryList')], // 品牌列表
+    ['imageTextList', () => import('./basics/imageTextList')], // 图文列表
+    ['assistDiv', () => import('./basics/assistDiv')], // 铺助分割
+    ['imageTextNav', () => import('./basics/imageTextNav')], // 图文导航
+    ['productList', () => import('./basics/product/pc')], // 商品列表
+    ['videoBox', () => import('./basics/video')], // 视频
+    ['coupon', () => import('./basics/coupon/pc')], // 优惠券
+    ['custom', () => import('./basics/custom')], // 自定义
+    ['notice', () => import('./basics/notice')], // 公告
+    ['vip', () => import('./basics/vip/pc')], // 会员专区
+    // ['shopHead', () => import('./basics/shopHead')], // 店铺头部
+    ['groupList', () => import('./basics/group/pc')], // 拼团专区
+    ['spikeList', () => import('./basics/spike/pc')], // 秒杀专区
+    ['priceList', () => import('./basics/price/pc')], // 定价捆绑
+    ['discountList', () => import('./basics/discount/pc')], // 限时折扣
+    ['newProduct', () => import('./basics/newProduct/app')], // 每日上新
+    ['shop', () => import('./basics/shop')] // 每日好店
+  ])
+]
+export default componentMap

+ 31 - 0
components/canvasShow/config/api.js

@@ -0,0 +1,31 @@
+// 导入api接口模块
+
+// 获取当前环境变量 true => 生产环境 false => 开发环境
+// const BASEURL = process.env.VUE_APP_DOMAIN_PREFIX
+const BASEURL = (process.env.NODE_ENV === 'production') ? 'https://ceres.zkthink.com/api' : 'https://ceres.zkthink.com/api'
+
+
+export const api = {
+  // 画布模块
+  fileUpload: BASEURL + '/file/upload', // 文件上传
+  getClassify: BASEURL + '/canvas/getClassify', // 查询分类层级
+  getProducts: BASEURL + '/canvas/getProducts', // 选择商品查询
+  saveCanvas: BASEURL + '/canvas/saveCanvas', // 保存画布
+  getCanvas: BASEURL + '/canvas/getCanvas', // 读取画布
+  getShops: BASEURL + '/canvas/getShops', // 选择店铺查询
+  getCoupons: BASEURL + '/canvas/getCoupons', // 优惠券查询
+  getShopCoupons: BASEURL + '/canvas/getShopCoupons', // 优惠券查询
+  takeCoupon: BASEURL + '/coupon/takeCoupon', // 领取优惠券
+  selectCanvasCustomList: BASEURL + '/canvas/selectCanvasCustomList', // 自定义页面查询
+  getPlatformSeckills: `${BASEURL}/canvas/getPlatformSeckills`, // 平台秒杀活动
+  getSeckills: `${BASEURL}/renovation/getSeckills`, // 商家秒杀活动
+  getMinDiscount: `${BASEURL}/canvas/getMinDiscount`, // 平台限时折扣
+  getDiscounts: `${BASEURL}/renovation/getDiscounts`, // 商家限时折扣
+  getAdminGroupWorks: `${BASEURL}/canvas/getGroupWorks`, // 平台拼团专区
+  getGroupWorks: `${BASEURL}/renovation/getGroupWorks`, // 商家拼团专区
+  getPriceProducts: `${BASEURL}/canvas/getPriceProducts`, // 商家定价捆绑
+  getPrices: `${BASEURL}/canvas/getPrices`, // 商家定价捆绑
+  getMemberProducts: `${BASEURL}/canvas/getMemberProducts`, // 查询会员商品数据
+  getNotices: `${BASEURL}/canvas/getNotices`, // 平台获取公告数据
+}
+export default api

+ 12 - 0
components/canvasShow/config/config.js

@@ -0,0 +1,12 @@
+// 画布配置
+// import Cookies from 'js-cookie'
+
+const config = {
+  // terminal: 4, // 画布设备 1 小程序,2 H5,3 App 4 电脑
+  typeId: 0, // 页面类型 0 C端 1 平台画布,2 自定义页面,3 商家店铺装修
+  getToken: function(){
+    return uni.getStorageSync('storage_key').token
+  }
+}
+
+export default config

+ 192 - 0
components/canvasShow/config/mixin/funMixin.js

@@ -0,0 +1,192 @@
+// import router from '@/router'
+import api from '../api'
+import {sendReq} from './sendReqMixin'
+import { mapMutations } from 'vuex'
+import canvasConfig from '../config'
+/*
+ * 公共方法的 mixin
+ */
+export const tool = {
+  mixins: [sendReq],
+  props: {
+    isNoData: {
+      type: Boolean,
+      default: false
+    },
+    comType: {
+      type: String,
+      default: ''
+    }
+  },
+  mounted() {
+    console.log('加载了组件')
+  },
+  methods: {
+    ...mapMutations({
+      setCurrentPro: 'SET_CURRENTPRO'
+    }),
+    // 判断url
+    jumpLink (linkObj) {
+      var link = ''
+      if(linkObj && linkObj.typeText && linkObj.data){
+        switch (linkObj.typeText) {
+          case '类别':
+            this.jumpCategory(linkObj.data)
+            break
+          case '店辅':
+            this.jumpStore(linkObj.data)
+            break
+          case '商品':
+            this.jumpProductDetail(linkObj.data)
+            break
+          case '自定义':
+            // router.push("/category");
+            break
+        }
+      } else if(linkObj.selsectValue==='/index'){
+        uni.navigateTo({
+          url: `/pages/index/index`
+        })
+      }
+      return link
+    },
+    // 跳转到类别主页
+    jumpCategory(item){
+      uni.navigateTo({
+        url: `/pages_category_page1/goodsModule/goodsList?category3Id=${item.id}`
+      })
+    },
+    // 跳转到产品列表
+    jumpProList(item){
+      if(item.sourceType === '1'){
+        uni.navigateTo({
+          url: `/pages_category_page1/goodsModule/canvasGoods?sourceType=${item.sourceType}&ids=${item.productIdList}`
+        })
+      } else if(item.sourceType === '2'){
+        uni.navigateTo({
+          url: `/pages_category_page1/goodsModule/canvasGoods?sourceType=${item.sourceType}&classifyId=${item.categoryId}`
+        })
+      }
+    },
+    // 跳转到店铺主页
+    jumpStore(item){
+      uni.navigateTo({
+        url: `/pages_category_page1/store/index?storeId=${item.shopId}`
+      })
+    },
+    // 跳转到商品详情
+    jumpProductDetail(item){
+      uni.navigateTo({
+        url: '/pages_category_page1/goodsModule/goodsDetails?shopId=' + item.shopId + '&productId=' + item.productId + '&skuId=' + item
+            .skuId
+      })
+    },
+    // 跳转到秒杀专区
+    jumpSeckills(item){
+      if(item.shopId){
+        uni.navigateTo({
+          url: '/pages_category_page1/discount/spikeList?shopId=' + item.shopId + '&shopSeckillId=' + item.shopSeckillId
+        })
+      } else {
+        uni.navigateTo({
+          url: '/pages_category_page1/discount/spikeList'
+        })
+      }
+    },
+    // 跳转到拼团专区
+    jumpGroupWorks(item){
+      if(item.shopId){
+        uni.navigateTo({
+          url: '/pages_category_page1/discount/groupBuy?shopId=' + item.shopId + '&shopGroupWorkId=' + item.shopGroupWorkId
+        })
+      } else {
+        uni.navigateTo({
+          url: '/pages_category_page1/discount/groupBuy?'
+        })
+      }
+    },
+    // 跳转到折扣专区
+    jumpDiscount(item){
+      if(item.shopId){
+        if (item.shopDiscountId) {
+          uni.navigateTo({
+            url: '/pages_category_page1/discount/discount?shopId=' + item.shopId + '&shopDiscountId=' + item.shopDiscountId
+          })
+        } else {
+          uni.showToast({
+            title: '暂无活动',
+            icon: "none"
+          });
+        }
+      } else {
+        if (item.discountId) {
+          uni.navigateTo({
+            url: '/pages_category_page1/discount/platformDiscount?discountId=' + item.discountId
+          })
+        } else {
+          uni.showToast({
+            title: '暂无活动',
+            icon: "none"
+          });
+        }
+      }
+    },
+    // 跳转到会员专区
+    jumpVip(){
+      uni.navigateTo({
+        url: '/pages_category_page1/memberCenter/activityList',
+        success: res => {},fail: () => {},complete: () => {}
+      })
+    },
+    // 跳转组合支付
+    jumpCombination(item){
+      if (item.priceId) {
+        uni.navigateTo({
+          url: '/pages_category_page1/goodsModule/combination?priceId=' + item.priceId
+        })
+      } else {
+        uni.showToast({
+          title: '暂无活动',
+          icon: "none"
+        });
+      }
+    },
+    // 跳转到公告详情
+    jumpNoticeDetail(item){
+    },
+    // 领取优惠券
+    // receiveCoupon(item) {
+    //   var key = canvasConfig.getToken()
+    //   if (key) {
+    //     var paramsData = {}
+    //     if(this.typeId === 1){
+    //       paramsData.couponId = item.couponId
+    //     } else if(this.typeId === 3) {
+    //       paramsData.shopCouponId = item.shopCouponId
+    //       paramsData.shopId = this.shopId
+    //     }
+    //     let params = {
+    //       url: api.takeCoupon,
+    //       method: 'POST',
+    //       data: paramsData
+    //     }
+    //     this.sendReq(params, (res) => {
+    //       this.$message({
+    //         message: '领取成功!',
+    //         type: 'success'
+    //       })
+    //       this.getData()
+    //     })
+    //   } else {
+    //     this.$message({
+    //       message: '请先登录'
+    //     })
+    //     this.$router.push({path: '/login'})
+    //   }
+    // },
+    // 加入购物车
+    addCart(id){
+      console.log(id)
+    }
+  }
+}

+ 9 - 0
components/canvasShow/config/mixin/index.js

@@ -0,0 +1,9 @@
+/*
+ * 用于组件复用
+ * 参考链接 https://cn.vuejs.org/v2/guide/mixins.html#全局混合
+ * 混合 (mixins) 是一种分发 Vue 组件中可复用功能的非常灵活的方式。混合对象可以包含任意组件选项。以组件使用混合对象时,所有混合对象的选项将被混入该组件本身的选项。
+ */
+import { tool } from './funMixin.js'
+import { sendReq } from './sendReqMixin.js'
+export const funMixin = { ...tool }
+export const sendReqMixin = { ...sendReq }

+ 37 - 0
components/canvasShow/config/mixin/sendReqMixin.js

@@ -0,0 +1,37 @@
+/*
+ * 发送请求 mixin
+ */
+import request from './server'
+
+/* eslint-disable */
+export const sendReq = {
+  data () {
+    return {
+      // 加载中
+      loading: false,
+    }
+  },
+  methods: {
+    /*
+     * 发送请求
+     */
+    sendReq (params, callback) {
+      let self = this
+      request({
+        method: params.method || 'POST',
+        url: params.url,
+        data: params.data || {},
+        withCredentials : true,
+        headers: {
+          'Content-type': params.contentType || 'application/json;charset=utf-8'
+        }
+      }).then((res) => {
+        if (res && res.data) {
+          callback && callback(res.data)
+        }
+      }, (error) => {
+        console.log(error)
+      })
+    }
+  }
+}

+ 135 - 0
components/canvasShow/config/mixin/server.js

@@ -0,0 +1,135 @@
+// 引入axios
+// import router from './../../router'
+import Vue from 'vue'
+// import promise from 'es6-promise'
+import axios from 'axios'
+import canvasConfig from '../config'
+// import Cookies from 'js-cookie'
+// import localStorage from '../storage/localStorage'
+// promise.polyfill()
+
+const service = axios.create({
+  headers: {
+    'X-Requested-With': 'XMLHttpRequest'
+  },
+  withCredentials: true,
+  timeout: 20000 // 请求超时 20s
+})
+
+// 请求拦截器
+service.interceptors.request.use(config => {
+  // 是否为当前的请求加上请求头 token
+  const token = canvasConfig.getToken()
+  if (token) {
+    if(canvasConfig.typeId === 1){
+      config.headers['Authorization-admin'] = token
+    } else if(canvasConfig.typeId === 3){
+      config.headers['Authorization-business'] = token
+    } else {
+      config.headers['Authorization'] = token
+    }
+  }
+  return config
+}, error => {
+  return Promise.reject(error)
+})
+
+// 响应拦截器
+service.interceptors.response.use(
+  (response) => {
+    console.log(response)
+    if (response.data.code && response.data.code !=='200' && response.data.message) {
+      Vue.prototype.$message.error(response.data.message)
+    }
+    return response
+  },
+  err => {
+    console.log(err)
+    // 失败响应
+    if (err && err.response) {
+      switch (err.response.status) {
+        case 400:
+          err.message = '请求无效,请检查参数是否正确!'
+          break
+
+        case 401:
+          err.message = '未经授权,访问被拒!'
+          break
+
+        case 403:
+          err.message = '拒绝访问!'
+          break
+
+        case 404:
+          err.message = `地址不存在!`
+          break
+
+        case 408:
+          err.message = '请求超时!'
+          break
+
+        case 500:
+          err.message = '系统错误!'
+          break
+
+        case 501:
+          err.message = '该方法未实现!'
+          break
+
+        case 502:
+          err.message = '网关出错!'
+          break
+
+        case 503:
+          err.message = '服务不可用!'
+          break
+
+        case 504:
+          err.message = '网关请求超时'
+          break
+
+        case 505:
+          err.message = 'HTTP版本不受支持'
+          break
+
+        default:
+      }
+      if (err.response.data.error) {
+        err.message = err.response.data.error
+      }
+      Vue.prototype.$message.closeAll()
+      Vue.prototype.$message.error(err.message)
+      // router.push({name: 'error', params: {message: err.message, status: err.response.status}})
+    }
+  }
+)
+
+//真机获取
+service.defaults.adapter = function (config) {
+  return new Promise((resolve, reject) => {
+    console.log(config)
+    var settle = require('axios/lib/core/settle');
+    var buildURL = require('axios/lib/helpers/buildURL');
+    uni.request({
+      method: config.method.toUpperCase(),
+      url: buildURL(config.url, config.params, config.paramsSerializer),
+      header: config.headers,
+      data: config.data,
+      dataType: config.dataType,
+      responseType: config.responseType,
+      sslVerify: config.sslVerify,
+      complete:function complete(response){
+        response = {
+          data: response.data,
+          status: response.statusCode,
+          errMsg: response.errMsg,
+          header: response.header,
+          config: config
+        };
+
+        settle(resolve, reject, response);
+      }
+    })
+  })
+}
+export default service

BIN
components/canvasShow/static/images/btn-next.png


BIN
components/canvasShow/static/images/btn-next2.png


BIN
components/canvasShow/static/images/btn-prev.png


BIN
components/canvasShow/static/images/btn-prev2.png


BIN
components/canvasShow/static/images/coupon/bg-coupon.png


BIN
components/canvasShow/static/images/coupon/bg-coupon2.png


BIN
components/canvasShow/static/images/coupon/border_L1.png


+ 0 - 0
components/canvasShow/static/images/coupon/border_L2.png


+ 0 - 0
components/canvasShow/static/images/coupon/border_L3.png


+ 0 - 0
components/canvasShow/static/images/coupon/border_L4.png


+ 0 - 0
components/canvasShow/static/images/coupon/border_R1.png


+ 0 - 0
components/canvasShow/static/images/coupon/border_R2.png


+ 0 - 0
components/canvasShow/static/images/coupon/border_R3.png


+ 0 - 0
components/canvasShow/static/images/coupon/border_R4.png


BIN
components/canvasShow/static/images/coupon/flag-coupon-r.png


BIN
components/canvasShow/static/images/coupon/flag-coupon.png


BIN
components/canvasShow/static/images/coupon/flag-coupon2-r.png


BIN
components/canvasShow/static/images/coupon/flag-coupon2.png


BIN
components/canvasShow/static/images/discount/bg-discount-more.png


BIN
components/canvasShow/static/images/discount/bg-discount-top-text.png


BIN
components/canvasShow/static/images/discount/bg-discount-top.png


BIN
components/canvasShow/static/images/discount/flag-discount.png


BIN
components/canvasShow/static/images/discount/flag-discount2.png


BIN
components/canvasShow/static/images/discount/img-title.png


BIN
components/canvasShow/static/images/discountListIcon.png


BIN
components/canvasShow/static/images/group/flag-group.png


BIN
components/canvasShow/static/images/group/img-title.png


BIN
components/canvasShow/static/images/groupBuyIcon.png


BIN
components/canvasShow/static/images/icon-title.png


BIN
components/canvasShow/static/images/memberCenterIcon.png


BIN
components/canvasShow/static/images/newProduct/bg-product-card.png


BIN
components/canvasShow/static/images/newProduct/flag-new.png


BIN
components/canvasShow/static/images/notice/ico_notice.png


BIN
components/canvasShow/static/images/notice/ico_notice2.png


BIN
components/canvasShow/static/images/price/bg-discount.png


BIN
components/canvasShow/static/images/price/img-title.png


BIN
components/canvasShow/static/images/product/img-title.png


BIN
components/canvasShow/static/images/shop/img-title.png


BIN
components/canvasShow/static/images/spike/bg-spike.png


BIN
components/canvasShow/static/images/spike/flag-spike.png


BIN
components/canvasShow/static/images/spike/img-title.png


BIN
components/canvasShow/static/images/spike/tit-spike.png


BIN
components/canvasShow/static/images/spikeIcon.png


BIN
components/canvasShow/static/images/vip/flag-vip.png


BIN
components/canvasShow/static/images/vip/flag-vip2.png


BIN
components/canvasShow/static/images/vip/img-title.png


+ 488 - 0
components/lb-picker/README.md

@@ -0,0 +1,488 @@
+<p align="center">
+  <a href="https://github.com/liub1934/uni-lb-picker">
+    <img src="https://img.shields.io/github/stars/liub1934/uni-lb-picker">
+  </a>
+  <a href="https://github.com/liub1934/uni-lb-picker/fork">
+    <img src="https://img.shields.io/github/forks/liub1934/uni-lb-picker">
+  </a>
+  <a href="https://github.com/liub1934/uni-lb-picker/issues">
+    <img src="https://img.shields.io/github/issues/liub1934/uni-lb-picker">
+  </a>
+  <a href="https://www.npmjs.com/package/uni-lb-picker">
+    <img src="https://img.shields.io/npm/v/uni-lb-picker">
+  </a>
+  <a href="https://npmcharts.com/compare/uni-lb-picker?minimal=true">
+    <img src="https://img.shields.io/npm/dm/uni-lb-picker">
+  </a>
+  <a href="https://standardjs.com">
+    <img src="https://img.shields.io/badge/code%20style-standard-brightgreen">
+  </a>
+  <a href="https://github.com/liub1934/uni-lb-picker/blob/master/LICENSE">
+    <img src="https://img.shields.io/github/license/liub1934/uni-lb-picker">
+  </a>
+</p>
+
+插件市场里面的 picker 选择器不满足自己的需求,所以自己写了一个简单的 picker 选择器,可扩展、可自定义,一般满足日常需要。  
+Github:[uni-lb-picker](https://github.com/liub1934/uni-lb-picker)  
+插件市场:[uni-lb-picker](https://ext.dcloud.net.cn/plugin?id=1111)  
+H5 Demo:[点击预览](https://github.liubing.me/uni-lb-picker)
+
+> 如果问题最好去 github 反馈,插件市场评论区留下五星好评即可,[点我去反馈](https://github.com/liub1934/uni-lb-picker/issues/new)
+
+> **由于之前`cancel`拼写失误,写成了`cancle`,`v1.08`现已修正,如果之前版本有使用`cancel`事件的,更新后请及时修正。**
+
+## 兼容性
+
+App + Nvue + H5 + 各平台小程序(快应用及 360 未测试)
+
+## 功能
+
+1、单选  
+2、多级联动,非多级联动,理论支持任意级数  
+3、省市区选择,基于多级联动  
+4、自定义选择器头部确定取消按钮颜色及插槽支持  
+5、选择器可视区自定义滚动个数  
+6、自定义数据字段,满足不同人的需求  
+7、自定义选择器样式  
+8、formatter 格式化自定义显示  
+9、单选及非联动选择支持扁平化的简单数据,如下形式:
+
+```javascript
+// 单选列表
+list1: ['选项1', '选项2', '选项2'],
+// 非联动选择列表
+list2: [
+  ['选项1', '选项2', '选项3'],
+  ['选项11', '选项22', '选项33'],
+  ['选项111', '选项222', '选项333']
+]
+```
+
+## 引入插件
+
+单独引入,在需要使用的页面上 import 引入即可
+
+```html
+<template>
+  <view>
+    <lb-picker></lb-picker>
+  </view>
+</template>
+
+<script>
+  import LbPicker from '@/components/lb-picker'
+  export default {
+    components: {
+      LbPicker
+    }
+  }
+</script>
+```
+
+全局引入,`main.js`中 import 引入并注册即可全局使用
+
+```jsvascript
+import LbPicker from '@/components/lb-picker'
+Vue.component("lb-picker", LbPicker)
+```
+
+easycom 引入
+
+`pages.json`加上如下配置:
+
+```json
+"easycom": {
+  "autoscan": true,
+  "custom": {
+    "lb-picker": "@/components/lb-picker/index.vue"
+  }
+}
+```
+
+npm 安装引入:
+
+```shell
+npm install uni-lb-picker
+```
+
+```jsvascript
+import LbPicker from 'uni-lb-picker'
+```
+
+## 选择器数据格式
+
+### 单选
+
+常规数据
+
+```javascript
+list: [
+  {
+    label: '选项1',
+    value: '1'
+  },
+  {
+    label: '选项2',
+    value: '2'
+  }
+]
+```
+
+扁平化简单数据
+
+```javascript
+list: ['选项1', '选项2']
+```
+
+### 多级联动
+
+```javascript
+list: [
+  {
+    label: '选项1',
+    value: '1',
+    children: [
+      {
+        label: '选项1-1',
+        value: '1-1',
+        children: [
+          {
+            label: '选项1-1-1',
+            value: '1-1-1'
+          }
+        ]
+      }
+    ]
+  }
+]
+```
+
+### 非联动选择
+
+常规数据
+
+```javascript
+list: [
+  [
+    { label: '选项1', value: '1' },
+    { label: '选项2', value: '2' },
+    { label: '选项3', value: '3' }
+  ],
+  [
+    { label: '选项11', value: '11' },
+    { label: '选项22', value: '22' },
+    { label: '选项33', value: '33' }
+  ],
+  [
+    { label: '选项111', value: '111' },
+    { label: '选项222', value: '222' },
+    { label: '选项333', value: '333' }
+  ]
+]
+```
+
+扁平化简单数据
+
+```javascript
+list: [
+  ['选项1', '选项2', '选项3'],
+  ['选项11', '选项22', '选项33'],
+  ['选项111', '选项222', '选项333']
+]
+```
+
+## 调用显示选择器
+
+通过`ref`形式手动调用`show`方法显示,隐藏同理调用`hide`
+
+```html
+<lb-picker ref="picker"></lb-picker>
+```
+
+```javascript
+this.$refs.picker.show() // 显示
+this.$refs.picker.hide() // 隐藏
+```
+
+`v1.1.3`新增,将需要点击的元素包裹在`lb-picker`中即可。
+
+```html
+<lb-picker>
+  <button>点我直接打开选择器</button>
+</lb-picker>
+```
+
+## 绑定值及设置默认值
+
+支持 vue 中`v-model`写法绑定值,无需自己维护选中值的索引。
+
+```javascript
+<lb-picker v-model="value1"></lb-picker>
+<lb-picker v-model="value2"></lb-picker>
+
+data () {
+  return {
+    value1: '' // 单选
+    value2: [] // 多列联动选择
+  }
+}
+```
+
+## 多个选择器
+
+通过设置不同的`ref`,然后调用即可
+
+```javascript
+<lb-picker ref="picker1"></lb-picker>
+<lb-picker ref="picker2"></lb-picker>
+
+this.$refs.picker1.show() // picker1显示
+this.$refs.picker2.show() // picker2显示
+```
+
+## 省市区选择
+
+省市区选择是基于多列联动选择,数据来源:[https://github.com/modood/Administrative-divisions-of-China](https://github.com/modood/Administrative-divisions-of-China),  
+省市区文件位于`/pages/demos/area-data-min.js`,自行引入即可,可参考`demo3省市区选择`,  
+也可使用自己已有的省市区数据,如果数据字段不一样,也可以自定义,参考下方自定义数据字段。
+
+## 自定义数据字段
+
+为了满足不同人的需求,插件支持自定义数据字段名称, 插件默认的数据字段如下形式:
+
+```javascript
+list: [
+  {
+    label: '选择1',
+    value: 1,
+    children: []
+  },
+  {
+    label: '选择1',
+    value: 1,
+    children: []
+  }
+]
+```
+
+如果你的数据字段和上面不一样,如下形式:
+
+```javascript
+list: [
+  {
+    text: '选择1',
+    id: 1,
+    child: []
+  },
+  {
+    text: '选择1',
+    id: 1,
+    child: []
+  }
+]
+```
+
+通过设置参数中的`props`即可,如下所示:
+
+```javascript
+<lb-picker :props="myProps"></lb-picker>
+
+data () {
+  return {
+    myProps: {
+      label: 'text',
+      value: 'id',
+      children: 'child'
+    }
+  }
+}
+```
+
+## 插槽使用
+
+选择器支持一些可自定义化的插槽,如选择器的取消和确定文字按钮,如果需要对其自定义处理的话,比如加个 icon 图标之类的,可使用插槽,使用方法如下:
+
+```html
+<lb-picker>
+  <view slot="cancel-text">我是自定义取消</view>
+  <view slot="confirm-text">我是自定义确定</view>
+</lb-picker>
+```
+
+也可参考示例中的`demo5`,自定义插槽元素样式交给开发者自由调整,插槽仅提供预留位置。
+
+其他插槽见下。
+
+## 参数及事件
+
+### Props
+
+| 参数                   | 说明                                                                                                                                                                                                                                                                                            | 类型                                      | 可选值                                                           | 默认值                                            |
+| :--------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------- | :--------------------------------------------------------------- | :------------------------------------------------ |
+| value/v-model          | 绑定值,联动选择为 Array 类型                                                                                                                                                                                                                                                                   | String/Number/Array                       | -                                                                | -                                                 |
+| mode                   | 选择器类型,支持单列,多列联动                                                                                                                                                                                                                                                                  | String                                    | selector 单选/multiSelector 多级联动/unlinkedSelector 多级非联动 | selector                                          |
+| list                   | 选择器数据(v1.0.7 单选及非联动多选支持扁平数据:['选项 1', '选项 2'])                                                                                                                                                                                                                           | Array                                     | -                                                                | -                                                 |
+| level                  | 多列联动层级,仅 mode 为 multiSelector 有效                                                                                                                                                                                                                                                     | Number                                    | -                                                                | 1                                                 |
+| props                  | 自定义数据字段                                                                                                                                                                                                                                                                                  | Object                                    | -                                                                | {label:'label',value:'value',children:'children'} |
+| cancel-text            | 取消文字                                                                                                                                                                                                                                                                                        | String                                    | -                                                                | 取消                                              |
+| cancel-color           | 取消文字颜色                                                                                                                                                                                                                                                                                    | String                                    | -                                                                | #999                                              |
+| confirm-text           | 确定文字                                                                                                                                                                                                                                                                                        | String                                    | -                                                                | 确定                                              |
+| confirm-color          | 确定文字颜色                                                                                                                                                                                                                                                                                    | String                                    | -                                                                | #007aff                                           |
+| empty-text             | `(v1.0.7 新增)`选择器列表为空的时候显示的文字                                                                                                                                                                                                                                                   | String                                    | -                                                                | 暂无数据                                          |
+| empty-color            | `(v1.0.7 新增)`暂无数据文字颜色                                                                                                                                                                                                                                                                 | String                                    | -                                                                | #999                                              |
+| column-num             | 可视滚动区域内滚动个数,最好设置奇数值                                                                                                                                                                                                                                                          | Number                                    | -                                                                | 5                                                 |
+| radius                 | 选择器顶部圆角,支持 rpx,如 radius="10rpx"                                                                                                                                                                                                                                                     | String                                    | -                                                                | -                                                 |
+| column-style           | `(v1.1.6 重新新增)`选择器默认样式,仅`nvue`支持,其他端见下`选择器自定义样式`说明                                                                                                                                                                                                               | Object                                    | -                                                                | -                                                 |
+| active-column-style    | `(v1.1.6 重新新增)`选择器选中样式,仅`nvue`支持,其他端见下`选择器自定义样式`说明                                                                                                                                                                                                               | Object                                    | -                                                                | -                                                 |
+| loading                | 选择器是否显示加载中,可使用 loading 插槽自定义加载效果                                                                                                                                                                                                                                         | Boolean                                   | -                                                                | -                                                 |
+| mask-color             | 遮罩层颜色                                                                                                                                                                                                                                                                                      | String                                    | -                                                                | rgba(0, 0, 0, 0.4)                                |
+| show-mask              | `(v1.1.0 新增)`是否显示遮罩层                                                                                                                                                                                                                                                                   | Boolean                                   | true/false                                                       | true                                              |
+| close-on-click-mask    | 点击遮罩层是否关闭选择器                                                                                                                                                                                                                                                                        | Boolean                                   | true/false                                                       | true                                              |
+| ~~change-on-init~~     | ~~(v1.0.7 已弃用)初始化时是否触发 change 事件~~                                                                                                                                                                                                                                                 | Boolean                                   | true/false                                                       | -                                                 |
+| dataset                | `(v1.0.7 新增)`可以向组件中传递任意的自定义的数据(对象形式数据),如`:dataset="{name:'test'}"`,在`confirm`或`change`事件中可以取到                                                                                                                                                            | Object                                    | -                                                                | -                                                 |
+| show-header            | `(v1.0.8 新增)`是否显示选择器头部                                                                                                                                                                                                                                                               | Boolean                                   | -                                                                | true                                              |
+| inline                 | `(v1.0.8 新增)`inline 模式,开启后默认显示选择器,无需点击弹出,可以配合`show-header`一起使用                                                                                                                                                                                                   | Boolean                                   | -                                                                | -                                                 |
+| z-index                | `(v1.0.9 新增)`选择器层级,遮罩层默认-1                                                                                                                                                                                                                                                         | Number                                    | -                                                                | 999                                               |
+| safe-area-inset-bottom | `(v1.1.4 新增)`是否留出底部安全距离(nvue 无效)                                                                                                                                                                                                                                                | Boolean                                   | true/false                                                       | true                                              |
+| disabled               | `(v1.1.4 新增)`是否禁用选择器,禁用后无法弹出选择器                                                                                                                                                                                                                                             | Boolean                                   | -                                                                | -                                                 |
+| align                  | `(v1.1.6 新增)`选择器中文字对齐方式,默认居中                                                                                                                                                                                                                                                   | String                                    | left/center/right                                                | center                                            |
+| press-enable           | `(v1.1.6 新增)`是否开启长按选择器数据`showtoast`弹出`label`提示,部分情况下选择器数据文字过长会显示省略号,如需查看完整的文字内容,可开启此选项,长按后会`showtoast`弹出完整的文字内容,默认不开启(支付宝小程序暂不支持[详情](https://ask.dcloud.net.cn/question/106237))                     | Boolean                                   | -                                                                | -                                                 |
+| press-time             | `(v1.1.6 新增)`长按触发时间,单位毫秒 ms                                                                                                                                                                                                                                                        | Number                                    | -                                                                | 500                                               |
+| formatter              | `(v1.1.7 新增)`格式化自定义选择器文字内容,`Function`类型,`return`一个字符串(仅`app` `nvue` `h5`支持),`item`当前项信息,`rowIndex`当前数据所在行数,`columnIndex`当前数据所在列数,写法参考[demo14](https://github.com/liub1934/uni-lb-picker/blob/master/pages/demos/demo14/demo14.vue#L206) | Function({ item, rowIndex, columnIndex }) | -                                                                | -                                                 |
+
+### 方法
+
+| 方法名         | 说明                                   | 参数            | 返回值                                                                                                       |
+| :------------- | :------------------------------------- | :-------------- | :----------------------------------------------------------------------------------------------------------- |
+| show           | 打开选择器                             | -               |                                                                                                              |
+| hide           | 关闭选择器                             | -               |                                                                                                              |
+| getColumnsInfo | (v1.1.0 新增)根据 value 获取选择器信息 | 绑定值的`value` | 同`change` `confirm`回调参数,如果传入的`value`获取不到信息则只返回一个含有`dataset`的对象,具体自行打印查看 |
+
+### Events
+
+| 事件名称 | 说明                                     | 回调参数                                                                                                                                                                                                                                                                                                                             |
+| :------- | :--------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| show     | 选择器打开时触发                         | -                                                                                                                                                                                                                                                                                                                                    |
+| hide     | 选择器隐藏时触发                         | -                                                                                                                                                                                                                                                                                                                                    |
+| change   | 选择器滚动时触发,此时不会改变绑定的值   | `{ index, item, value, change }` `index`触发滚动后新的索引,单选时是具体的索引值,多列联动选择时为数组。`item`触发滚动后新的的完整内容,包括`label`、`value`等,单选时为对象,多列选择时为数组对象。`value`触发滚动后新的 value 值,单列选择时为具体值,多列联动选择时为数组。`change`触发事件的类型,详情参考下面的 change 事件备注 |
+| confirm  | 点击选择器确定时触发,此时会改变绑定的值 | 同上`change`事件说明                                                                                                                                                                                                                                                                                                                 |
+| cancel   | 点击选择器取消时触发                     | 同上`change`事件说明                                                                                                                                                                                                                                                                                                                 |
+
+### `change` 事件备注
+
+如果绑定的值是空的,`change`触发后里面的内容都是列表的第一项。  
+`change`事件会在以下情况触发:
+
+- 初始化
+- 绑定值 value 变化
+- 选择器 list 列表变化
+- 滚动选择器
+
+以上情况会在回调函数中都可以取到`change`变化的类型,对应上面的情况包括以下:
+
+- `init`
+- `value`
+- `list`
+- `scroll`
+
+根据这些类型大家可以在`change`的时候按需处理自己的业务逻辑,`init`现在指挥在调用选择器弹出的时候触发。  
+下面的说明情况已失效,如需要在页面显示的时候根据`value`的值显示相应的中文,调用`v1.10`新增的方法`getColumnsInfo`,传入绑定的值即可获取到你想要的所有信息。  
+~~比如一种常见的情况,有默认值的时候需要显示默认值的文字,此时可以`change`事件中判断`change`的类型是否是`init`,如果是的话可以取事件回调中的`item`进行显示绑定值对应的文字信息。~~
+
+```javascript
+handleChange (e) {
+  if (e.change === 'init') {
+    console.log(e.item.label) // 单选 选项1
+    console.log(e.item.map(item => item.label).join('-')) // 多选 选项1-选项11
+  }
+}
+```
+
+### 插槽
+
+| 插槽名        | 说明                   |
+| :------------ | :--------------------- |
+| cancel-text   | 选择器取消文字插槽     |
+| action-center | 选择器顶部中间插槽     |
+| confirm-text  | 选择器确定文字插槽     |
+| loading       | 选择器 loading 插槽    |
+| empty         | 选择器 空数据 插槽     |
+| header-top    | 选择器头部顶部插槽     |
+| header-bottom | 选择器头部底部插槽     |
+| picker-top    | 选择器滚动部分顶部插槽 |
+| picker-bottom | 选择器滚动部分底部插槽 |
+
+### 选择器自定义样式
+
+> nvue 专属写法,需要定义`column-style`和`active-column-style`,写法如下:
+
+```html
+<lb-picker
+  :column-style="columnStyle"
+  :active-column-style="activeColumnStyle"
+></lb-picker>
+```
+
+```javascript
+data () {
+  return {
+    // 默认样式
+    columnStyle: {
+      color: '#f0ad4e'
+    },
+    // 选择样式
+    activeColumnStyle: {
+      color: '#007aff',
+      fontWeight: 700
+    }
+  }
+}
+```
+
+> 其他端写法,覆盖默认样式即可。
+
+```css
+<style lang="scss" scoped>
+/deep/ .lb-picker {
+  .lb-picker-column-label {
+    color: #f0ad4e;
+  }
+  .lb-picker-column-active {
+    .lb-picker-column-label {
+      color: #007aff;
+      font-weight: 700;
+    }
+  }
+}
+</style>
+```
+
+完整代码可以参考`demo9`。
+
+### 获取选中值的文字
+
+`@confirm`事件中可以拿到:
+
+单选:
+
+```javascript
+handleConfirm (e) {
+  console.log(e.item.label) // 选项1
+}
+```
+
+联动选择:
+
+```javascript
+handleConfirm (e) {
+  console.log(e.item.map(item => item.label).join('-')) // 选项1-选项11
+}
+```
+
+## Tips
+
+微信小程序端,滚动时在 iOS 自带振动反馈,可在系统设置 -> 声音与触感 -> 系统触感反馈中关闭
+
+## 其他
+
+其他功能参考示例 Demo 代码。

Разлика између датотеке није приказан због своје велике величине
+ 86 - 0
components/lb-picker/index.vue


+ 93 - 0
components/lb-picker/mixins/index.js

@@ -0,0 +1,93 @@
+import { getColumns, isObject, isFunction } from '../utils'
+export const commonMixin = {
+  data () {
+    return {
+      isConfirmChange: false,
+      indicatorStyle: `height: 34px`,
+      pressTimeout: null
+    }
+  },
+  created () {
+    this.init('init')
+  },
+  methods: {
+    init (changeType) {
+      if (this.list && this.list.length) {
+        const column = getColumns({
+          value: this.value,
+          list: this.list,
+          mode: this.mode,
+          props: this.props,
+          level: this.level
+        })
+        const { columns, value, item, index } = column
+        this.selectValue = value
+        this.selectItem = item
+        this.pickerColumns = columns
+        this.pickerValue = index
+        this.$emit('change', {
+          value: this.selectValue,
+          item: this.selectItem,
+          index: this.pickerValue,
+          change: changeType
+        })
+      }
+    },
+    touchstart (e) {
+      if (!this.pressEnable) return
+      clearTimeout(this.pressTimeout)
+      this.pressTimeout = setTimeout(() => {
+        let item = {}
+        let toastTitle = ''
+        // #ifdef APP-NVUE
+        item = e.target.dataset.item
+        // #endif
+
+        // #ifdef H5
+        item = JSON.parse(e.currentTarget.dataset.item)
+        // #endif
+
+        // #ifndef APP-NVUE || H5
+        item = e.currentTarget.dataset.item
+        // #endif
+
+        // #ifdef APP-PLUS || H5
+        toastTitle = this.getLabel(item)
+        // #endif
+
+        // #ifndef APP-PLUS || H5
+        toastTitle = item[this.props.label] || item
+        // #endif
+        uni.showToast({
+          title: toastTitle,
+          icon: 'none'
+        })
+      }, this.pressTime)
+    },
+    touchmove () {
+      if (!this.pressEnable) return
+      clearTimeout(this.pressTimeout)
+    },
+    touchend () {
+      if (!this.pressEnable) return
+      clearTimeout(this.pressTimeout)
+    },
+    getLabel (item, rowIndex, columnIndex) {
+      if (this.formatter && isFunction(this.formatter)) {
+        return this.formatter({ item, rowIndex, columnIndex })
+      } else {
+        return item[this.props.label] || item
+      }
+    }
+  },
+  watch: {
+    value () {
+      if (!this.isConfirmChange) {
+        this.init('value')
+      }
+    },
+    list () {
+      this.init('list')
+    }
+  }
+}

+ 135 - 0
components/lb-picker/pickers/multi-selector-picker.vue

@@ -0,0 +1,135 @@
+<template>
+  <view class="lb-multi-selector lb-picker-item"
+    :style="{ height: height }">
+    <picker-view :value="pickerValue"
+      :indicator-style="indicatorStyle"
+      :style="{ height: height }"
+      @change="handleChange">
+      <picker-view-column v-for="(column, index) in pickerColumns"
+        :key="index">
+        <!-- #ifdef H5 -->
+        <view v-for="(item, i) in column || []"
+          :class="[
+				    'lb-picker-column',
+				    item[props.value] === selectValue[index]
+				      ? 'lb-picker-column-active'
+				      : ''
+				  ]"
+          :key="i"
+          :data-item="pressEnable ? JSON.stringify(item) : ''"
+          @touchstart="touchstart"
+          @touchmove="touchmove"
+          @touchend="touchend">
+          <!-- #endif -->
+          <!-- #ifndef H5 -->
+          <view v-for="(item, i) in column || []"
+            :class="[
+            'lb-picker-column',
+            item[props.value] === selectValue[index]
+              ? 'lb-picker-column-active'
+              : ''
+          ]"
+            :key="i"
+            :data-item="item"
+            @touchstart="touchstart"
+            @touchmove="touchmove"
+            @touchend="touchend">
+            <!-- #endif -->
+            <!-- #ifdef APP-PLUS || H5 -->
+            <text :class="[
+              'lb-picker-column-label',
+              `lb-picker-column-label-${align}`
+            ]"
+              :style="[
+              item[props.value] === selectValue[index]
+              ? activeColumnStyle
+              : columnStyle
+            ]">{{ getLabel(item, i, index) }}</text>
+            <!-- #endif -->
+
+            <!-- #ifndef APP-PLUS || H5 -->
+            <text :class="[
+              'lb-picker-column-label',
+              `lb-picker-column-label-${align}`
+            ]">{{ item[props.label] || item }}</text>
+            <!-- #endif -->
+          </view>
+      </picker-view-column>
+    </picker-view>
+  </view>
+</template>
+
+<script>
+import { commonMixin } from '../mixins'
+export default {
+  props: {
+    value: Array,
+    list: Array,
+    mode: String,
+    props: Object,
+    level: Number,
+    visible: Boolean,
+    height: String,
+    columnStyle: Object,
+    activeColumnStyle: Object,
+    align: String,
+    pressEnable: Boolean,
+    pressTime: Number,
+    formatter: Function
+  },
+  mixins: [commonMixin],
+  data () {
+    return {
+      pickerValue: [],
+      pickerColumns: [],
+      selectValue: [],
+      selectItem: []
+    }
+  },
+  methods: {
+    handleChange (item) {
+      const pickerValue = item.detail.value
+      const columnIndex = pickerValue.findIndex(
+        (item, i) => item !== this.pickerValue[i]
+      )
+      const valueIndex = pickerValue[columnIndex]
+      this.setPickerChange(pickerValue, valueIndex, columnIndex)
+    },
+    setPickerChange (pickerValue, valueIndex, columnIndex) {
+      for (let i = 0; i < this.level; i++) {
+        if (i > columnIndex) {
+          pickerValue[i] = 0
+          const column =
+            this.pickerColumns[i - 1][valueIndex] ||
+            this.pickerColumns[i - 1][0]
+          this.$set(this.pickerColumns, i, column[this.props.children] || [])
+          valueIndex = 0
+        }
+        this.$set(this.pickerValue, i, pickerValue[i])
+        const selectItem = this.pickerColumns[i][pickerValue[i]]
+        if (selectItem) {
+          this.selectItem[i] = selectItem
+          this.selectValue[i] = selectItem[this.props.value]
+        } else {
+          const spliceNum = this.level - i
+          this.pickerValue.splice(i, spliceNum)
+          this.selectValue.splice(i, spliceNum)
+          this.selectItem.splice(i, spliceNum)
+          this.pickerColumns.splice(i, spliceNum)
+          break
+        }
+      }
+      this.$emit('change', {
+        value: this.selectValue,
+        item: this.selectItem,
+        index: this.pickerValue,
+        change: 'scroll'
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import "../style/picker-item.scss";
+</style>

+ 113 - 0
components/lb-picker/pickers/selector-picker.vue

@@ -0,0 +1,113 @@
+<template>
+  <view class="lb-selector-picker lb-picker-item"
+    :style="{ height: height }">
+    <picker-view :value="pickerValue"
+      :style="{ height: height }"
+      :indicator-style="indicatorStyle"
+      @change="handleChange">
+      <picker-view-column>
+        <!-- #ifdef H5 -->
+        <view v-for="(item, i) in list"
+          :class="[
+				    'lb-picker-column',
+				    (item[props.value] || item) === selectValue
+				      ? 'lb-picker-column-active'
+				      : ''
+				  ]"
+          :key="i"
+          :data-item="pressEnable ? JSON.stringify(item) : ''"
+          @touchstart="touchstart"
+          @touchmove="touchmove"
+          @touchend="touchend">
+          <!-- #endif -->
+          <!-- #ifndef H5 -->
+          <view v-for="(item, i) in list"
+            :class="[
+            'lb-picker-column',
+            (item[props.value] || item) === selectValue
+              ? 'lb-picker-column-active'
+              : ''
+          ]"
+            :key="i"
+            :data-item="item"
+            @touchstart="touchstart"
+            @touchmove="touchmove"
+            @touchend="touchend">
+            <!-- #endif -->
+            <!-- #ifdef APP-PLUS || H5 -->
+            <text :class="[
+              'lb-picker-column-label',
+              `lb-picker-column-label-${align}`
+            ]"
+              :style="[
+              (item[props.value] || item) === selectValue
+                ? activeColumnStyle
+                : columnStyle
+            ]">{{ getLabel(item, i, 0) }}</text>
+            <!-- #endif -->
+
+            <!-- #ifndef APP-PLUS || H5 -->
+            <text :class="[
+              'lb-picker-column-label',
+              `lb-picker-column-label-${align}`
+            ]">{{ item[props.label] || item }}</text>
+            <!-- #endif -->
+          </view>
+      </picker-view-column>
+    </picker-view>
+  </view>
+</template>
+
+<script>
+import { isObject } from '../utils'
+import { commonMixin } from '../mixins'
+export default {
+  props: {
+    value: [String, Number],
+    list: Array,
+    mode: String,
+    props: Object,
+    visible: Boolean,
+    height: String,
+    columnStyle: Object,
+    activeColumnStyle: Object,
+    align: String,
+    pressEnable: Boolean,
+    pressTime: Number,
+    formatter: Function
+  },
+  mixins: [commonMixin],
+  data () {
+    return {
+      pickerValue: [],
+      selectValue: '',
+      selectItem: null
+    }
+  },
+  computed: {
+    isH5 () {
+      return false
+    }
+  },
+  methods: {
+    handleChange (item) {
+      const index = item.detail.value[0] || 0
+      this.selectItem = this.list[index]
+      this.selectValue = isObject(this.selectItem)
+        ? this.selectItem[this.props.value]
+        : this.selectItem
+      this.pickerValue = item.detail.value
+      this.$emit('change', {
+        value: this.selectValue,
+        item: this.selectItem,
+        index: index,
+        change: 'scroll'
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import "../style/picker-item.scss";
+</style>

Неке датотеке нису приказане због велике количине промена