浏览代码

拼团活动

jun 5 月之前
父节点
当前提交
fb91f59ea3

+ 5 - 1
pages/index/index.vue

@@ -17,7 +17,7 @@
 				<!-- Vue3 项目部分小程序端事件延迟或调用失败 在执行事件的元素上添加 data-eventsync="true" 属性以解决此问题 -->
 				<view @click="toDetail(item)" data-eventsync="true" class="product_item" v-for="(item,index) in productList" :key="index" >
 					<image class="product_image" :src="item.thumb" mode=""></image>
-					<view class="product_name"><text>{{item.name}}</text></view>
+					<view class="product_name"><text class="regiment_title">{{item.regiment_title}}</text><text>{{item.name}}</text></view>
 					<view class="product_spec"><text>{{item.spec}}</text></view>
 					<view class="stock_price">
 						<view class="product_price" v-if="isShowPrice">
@@ -332,6 +332,10 @@
 					margin: 10rpx 0rpx;
 					padding: 0rpx 10rpx;
 					text-overflow: ellipsis;
+          .regiment_title{
+            background-color: red;
+            color: #F9F9F9;
+          }
 				}
 				.product_spec{
 					height: 30rpx;

+ 18 - 0
pages/orders/index.vue

@@ -39,6 +39,9 @@
 					<button class="order_cancel" @click="cancelOrder(index)">取消订单</button>
 					<button class="order_share" @click="toReceipt(item)">我已收货</button>
 				</view>
+        <view class="order_btn"v-if="item.status == 10">
+          <button class="order_cancel" @click="cancelOrderRegiment(index)">取消拼团</button>
+        </view>
 			</view>
 		</view>
 		<view class="to_bottom" v-if="isLast"> -----到底啦-----</view>
@@ -178,6 +181,21 @@
 					}
 				})
 			},
+      // 取消拼团
+      cancelOrderRegiment(index){
+        uni.showModal({
+          title:"确认取消该订单?",
+          success:res=>{
+            if (res.confirm) {
+              // 请求列表
+              this.$http.request('api/orders/cancel_regiment',{id:this.orderList[index].id}).then(re=>{
+                this.orderList[index].state  = '已取消';
+                this.orderList[index].status = 4;
+              });
+            }
+          }
+        })
+      },
 			navToProduct(id){
 				if( id ){
 					uni.navigateTo({

+ 350 - 35
pages/product/index.vue

@@ -18,12 +18,41 @@
 					<text class="product_market">¥{{productInfo.market_price}} </text>
 				</view>
 			</view>
-			<view class="product_name">{{productInfo.name}}</view>
+			<view class="product_name"><text class="regiment_title">{{productInfo.regiment_title}}</text>{{productInfo.name}}</view>
 			<view class="product_spec">
-				<view class="spec_name">{{productInfo.spec}}</view> 
+				<view class="spec_name">{{productInfo.spec}}</view>
 				<view class="product_stock">剩{{productInfo.stock}}个</view>
 			</view>
 		</view>
+    <view class="regiment_list" v-if="productInfo.regiment_list.length">
+      <view class="regiment_list_title">
+        <text class="title1">{{productInfo.regiment_list.length}}人正在拼单</text>
+        <text class="title2" @click="openRegiment()">查看更多</text>
+      </view>
+      <view class="regiment">
+        <swiper class="swiper" :indicator-dots="false" :autoplay="true" :interval="2000"
+                :duration="2000" :circular="true" :vertical="true" :display-multiple-items="2" easing-function="linear">
+          <swiper-item v-for="item in productInfo.regiment_list" :key="index">
+            <view class="swiper-item">
+              <view class="username">
+                <image class="user_image"  :src="item.userpic" ></image>
+                <text class="username_text">{{item.username}}</text>
+              </view>
+              <view class="regiment_right">
+                <button class="regiment_but" data-eventsync="true"  @click="showSpecPopup(4,item.id)">去拼单</button>
+              </view>
+              <view class="regiment_center">
+                <view class="regiment_center_text">还差{{item.surplus_number}}人成团</view>
+                <view class="regiment_center_time">
+                  <view class="center_title">还剩</view>
+                  <uni-countdown color="#E03519" :show-day="false" :hour="remainder(item.end_time,1)" :minute="remainder(item.end_time,2)" :second="remainder(item.end_time,3)"></uni-countdown>
+                </view>
+              </view>
+            </view>
+          </swiper-item>
+        </swiper>
+      </view>
+    </view>
 		<view class="product_business" v-if="productInfo.business_info.name">
 			<image class="logopic" :src="productInfo.business_info.logopic" ></image>
 			<view class="business_info">
@@ -41,17 +70,35 @@
 		<view class="product_poster" v-if="productInfo.poster" >
 			<image class="product_image" :src="productInfo.poster" mode="widthFix"></image>
 		</view>
-		<view class="order_car">
+		<view class="order_car" v-if="productInfo.regiment_type === 2">
 			<navigator url="/pages/car/index" open-type="switchTab" class="to_car" >
 				<image src="../../static/icon/car.png" class="car_icon" ></image>
 			</navigator>
-			<button class="show_order" data-eventsync="true" @click="showSpecPopup(1)" >立即预约</button>
-			<button class="show_car" data-eventsync="true" @click="showSpecPopup(2)" >加入购物车</button>
+			<button class="show_order" data-eventsync="true" @click="showSpecPopup(3)" >{{this.productInfo.regiment_title}}专享/{{this.productInfo.regiment_price}}</button>
+			<button class="show_car" data-eventsync="true" @click="showSpecPopup(1)" >单独预约/{{this.productInfo.price}}</button>
 		</view>
+    <view class="order_car" v-else-if="productInfo.regiment_type === 1">
+      <navigator url="/pages/car/index" open-type="switchTab" class="to_car" >
+        <image src="../../static/icon/car.png" class="car_icon" ></image>
+      </navigator>
+      <view class="show_order_regiment"  @click="showSpecPopup(4)" >
+        <view>立即参与多人团拼单</view>
+        <view class="regiment_countdown">
+          <uni-countdown color="#FFFFFF" :show-day="false" :hour="remainder(productInfo.automatic_info.end_time,1)" :minute="remainder(productInfo.automatic_info.end_time,2)" :second="remainder(productInfo.automatic_info.end_time,3)"></uni-countdown>
+        </view>
+      </view>
+    </view>
+    <view class="order_car" v-else>
+      <navigator url="/pages/car/index" open-type="switchTab" class="to_car" >
+        <image src="../../static/icon/car.png" class="car_icon" ></image>
+      </navigator>
+      <button class="show_order" data-eventsync="true" @click="showSpecPopup(1)" >立即预约</button>
+      <button class="show_car" data-eventsync="true" @click="showSpecPopup(2)" >加入购物车</button>
+    </view>
 		<view class="to_bottom"> --- 到底了 --- </view>
 		<uni-popup ref="specPopup" :show="true" type="bottom" class="popup" background-color="#FFFFFF" @change="popupChange" >
 			<view class="order_info">
-				<view class="custom_addr" v-if="specBtnType==1" @click="showAddrPopup()">
+				<view class="custom_addr" v-if="specBtnType==1 || specBtnType==3 || specBtnType==4" @click="showAddrPopup()">
 					<view class="contact_user">
 						<text class="contact_none" v-if="!checkedAddr.id">请选择收货地址</text>
 						<text class="contact_name" >{{checkedAddr.contact_name}}</text>
@@ -71,7 +118,8 @@
 						<view class="car_name">{{productInfo.name}}</view>
 						<view class="stock_price">
 							<view class="product_price" v-if="isShowPrice">
-								<text>¥{{productInfo.price}} </text>
+								<text v-if="specBtnType==1">¥{{productInfo.price}} </text>
+								<text v-if="specBtnType==3 || specBtnType==4">¥{{productInfo.regiment_price}} </text>
 								<text class="car_market">¥{{productInfo.market_price}}</text>
 							</view>
 							<view class="product_quantity_box">
@@ -82,7 +130,7 @@
 								<button class="product_quantity_add" @click="changeQuantity(+1)" data-eventsync="true">
 									<image class="add_icon" src="../../static/icon/add_icon.png" mode=""></image>
 								</button>
-							</view>	
+							</view>
 						</view>
 						<view class="product_stock">剩{{productInfo.stock}}个</view>
 					</view>
@@ -106,10 +154,12 @@
 					<text>优惠券</text>
 					<view class="coupon_deduction">{{couponUsed}}</view>
 				</view>
-				<view class="order_price" v-if="specBtnType==1" >合计: ¥{{priceTotal}}</view>
+				<view class="order_price" v-if="specBtnType==1 || specBtnType==3" >合计: ¥{{priceTotal}}</view>
 				<view class="order_btn">
 					<button class="to_order" @click="createOrder()" v-if="specBtnType==1" data-eventsync="true" >预约</button>
 					<button class="to_car" @click="createCart()" v-if="specBtnType==2" data-eventsync="true" >加入购物车</button>
+          <button class="to_order" @click="createOrder()" v-if="specBtnType==3" data-eventsync="true" >拼团预约</button>
+          <button class="to_order" @click="createOrder()" v-if="specBtnType==4" data-eventsync="true" >{{ productInfo.regiment_title }}预约</button>
 				</view>
 			</view>
 		</uni-popup>
@@ -129,13 +179,13 @@
 						<view class="coupon_title">
 							<view class="coupon_name" v-if="item.rebate_type == 1">满减券</view>
 							<view class="coupon_name" v-if="item.rebate_type == 2">折扣券</view>
-							<view class="coupon_name" v-if="item.rebate_type == 3">赠品券</view> 
+							<view class="coupon_name" v-if="item.rebate_type == 3">赠品券</view>
 						</view>
 						<view class="product_scope">
 							<text class="" v-if="item.type_id == 1">{{item.is_scope?"当前商品可用":"当前商品不可用"}}</text>
 							<text class="" v-if="item.type_id == 2">全场可用</text>
 							<view class="check_label" >
-								<view class="isstd" v-if="!item.is_std">未达标</view> 
+								<view class="isstd" v-if="!item.is_std">未达标</view>
 								<image class="checkbox" v-if="item.is_std" :src="item.checked?'../../static/icon/checked.png':'../../static/icon/checkbox.png'"></image>
 							</view>
 						</view>
@@ -151,7 +201,7 @@
 			<view class="addr_list">
 				<view class="addr_item" v-for="(item,index) in addrList" :key="index" @click="checkedAddrItem(item)">
 					<view class="radio_label">
-						<image class="radio_icon" :src="item.id == checkedAddr.id? '../../static/icon/radioed.png' : '../../static/icon/radio.png'" ></image> 
+						<image class="radio_icon" :src="item.id == checkedAddr.id? '../../static/icon/radioed.png' : '../../static/icon/radio.png'" ></image>
 					</view>
 					<view class="contact_user">
 						<text class="contact_name">{{item.contact_name}}</text>
@@ -186,6 +236,28 @@
 				</view>
 			</view>
 		</uni-popup>
+    <uni-popup ref="regiment" type="bottom" class="popup" background-color="#FFFFFF" >
+      <view class="popup_title">可参与的拼单</view>
+      <view class="regiment_popup_list">
+        <view class="regiment_popup_item" v-for="(item,index) in productInfo.regiment_list" @click="checkedCoupon(index,false)" :key="index">
+          <view class="username">
+            <image class="user_image"  :src="item.userpic" ></image>
+            <text class="username_text">{{item.username}}</text>
+          </view>
+          <view class="regiment_right">
+            <button class="regiment_but" data-eventsync="true" @click="showSpecPopup(4,item.id)">去拼单</button>
+          </view>
+          <view class="regiment_center">
+            <view class="regiment_center_text">还差{{item.surplus_number}}人成团</view>
+            <view class="regiment_center_time">
+              <view class="center_title">还剩</view>
+              <uni-countdown color="#E03519" :show-day="false" :hour="remainder(item.end_time,1)" :minute="remainder(item.end_time,2)" :second="remainder(item.end_time,3)"></uni-countdown>
+            </view>
+
+          </view>
+        </view>
+      </view>
+    </uni-popup>
 	</view>
 </template>
 <script>
@@ -203,14 +275,24 @@
 					thumb:"",
 					poster:"",
 					description:"",
+          regiment_title:'',
+          automatic_info: {
+            end_time:'',
+          },
+          regiment_active:[],
 					business_info:{
 						name:"",
 						logopic:"",
 						desc:"",
 					},
+          regiment_price:"0.00",
+          regiment_quota:0,
+          regiment_list:[],
 					product_attr:[],
 					photo_list:[],
 					product_sku:[],
+          regiment_type:0,
+          regiment_number:0,
 				},
 				requestParam:{
 					id:0,
@@ -230,7 +312,7 @@
 				// 已经选择的优惠券ID
 				customCoupon: 0,
 				// 规格选择按钮类型
-				specBtnType: 1, // 1预约。2购物车
+				specBtnType: 1, // 1预约。2购物车。3拼团
 				// 地址列表
 				addrList:[],
 				// 已选地址
@@ -254,6 +336,7 @@
 				// 选择的skuid
 				sku_id:0,
 				sku_thumb:"",
+        regiment_id:0,
 			}
 		},
 		onLoad(param) {
@@ -301,6 +384,9 @@
 					if( re.code == 'success' ) {
 						// 刷新数据
 						this.productInfo = re.data;
+            if (this.productInfo.automatic_info.id){
+              this.regiment_id  = this.productInfo.automatic_info.id
+            }
 						//设置默认sku
 						this.sku_id = 0;
 						// 显示图片
@@ -313,22 +399,46 @@
 							})
 						}
 					}
-				});		
-			};
+				});
+			}
 		},
 		methods: {
+      //计算剩余时间
+      remainder(end_time,type){
+        const now = Math.floor(Date.now() / 1000);
+        const distance = end_time - now;
+
+        if (distance < 0) {
+          return 0
+        }
+        const hours = Math.floor(distance / (60 * 60));
+        const minutes = Math.floor((distance % (60 * 60)) / (60));
+        const seconds = Math.floor((distance % (60)));
+        if (type === 1){
+          return hours
+        }
+        if (type === 2){
+          return minutes
+        }
+        if (type === 3){
+          return seconds
+        }
+      },
 			// 地址弹出层
 			showAddrPopup(){
 				// 显示下单弹出层
 				this.$refs.addrPopup.open('bottom');
 			},
 			// 规格弹出层
-			showSpecPopup(type){
+			showSpecPopup(type,regiment_id=0){
 				// 登录提示
 				if( !this.$checkAccess.alterLogin()) return ;
 				// 判断是否弹出
 				if( this.$checkAccess.getFollowQrcode() ) return this.openAddFollow();
-				// 显示类型,1预约,2购物车
+        if (regiment_id){
+          this.regiment_id  = regiment_id
+        }
+				// 显示类型,1预约,2购物车,3拼团
 				this.specBtnType  = type;
 				// 恢复优惠券使用按钮
 				this.couponUsed   = "去使用 >";
@@ -356,10 +466,14 @@
 					this.defaultSkuStatus();
 				}
 			},
-			// 优惠券弹出层
-			openCoupon(){
-				this.$refs.couponPopup.open('bottom')
+			// 拼团列表弹出层
+			openRegiment(){
+				this.$refs.regiment.open('bottom')
 			},
+      // 优惠券弹出层
+      openCoupon(){
+        this.$refs.couponPopup.open('bottom')
+      },
 			// 数量调整
 			changeQuantity(number){
 				// 如果不是0.表示两侧按钮点击,0表示输入的修改
@@ -375,6 +489,13 @@
 						icon:"none"
 					})
 				}
+        if ((this.specBtnType === 3 || specBtnType==4) && this.quantity >this.productInfo.regiment_quota){
+          this.quantity = this.productInfo.regiment_quota;
+          uni.showToast({
+            title:"拼团限购"+this.productInfo.regiment_quota+"份",
+            icon:"none"
+          })
+        }
 				// 如果小于1.设置为1
 				if( this.quantity < 1 )		{
 					this.quantity = 1;
@@ -422,6 +543,31 @@
 					})
 					return;
 				}
+        if (this.specBtnType === 3 || this.specBtnType === 4){
+          if (this.quantity >this.productInfo.regiment_quota){
+            this.quantity = this.productInfo.regiment_quota;
+            uni.showToast({
+              title:"拼团限购"+this.productInfo.regiment_quota+"份",
+              icon:"none"
+            })
+            return;
+          }
+          const timestamp = Math.round(new Date().getTime()/1000).toString();
+          if (timestamp > this.productInfo.regiment_active.end_time){
+            uni.showToast({
+              title:"拼团活动已结束",
+              icon:"none"
+            })
+            return;
+          }
+          if (this.specBtnType === 4 && timestamp > this.productInfo.automatic_info.end_time){
+            uni.showToast({
+              title:"该团已过期",
+              icon:"none"
+            })
+            return;
+          }
+        }
 				// 地址未填
 				if( !this.checkedAddr.id ) {
 					this.showAddrPopup();
@@ -437,20 +583,37 @@
 				let productList = [{product_id:this.productInfo.id,buy_num:this.quantity,product_skuid:this.sku_id}];
 				// 转成json字符串传输
 				productList		= JSON.stringify(productList);
-				// 请求接口
-				this.$http.request('api/orders/create',{"product_list":productList,"custom_coupon_id":this.customCoupon,'addr_id':this.checkedAddr.id},'post').then((re)=>{
-					// 判断结果
-					if( re.code == 'success' ) {
-						// 处理结果
-						this.productInfo.stock = this.productInfo.stock - this.quantity;
-						// 关闭弹窗
-						this.$refs.specPopup.close();
-						// 跳转到报单完成页面
-						uni.navigateTo({url:`/pages/orders/completion?params=${encodedArray}`});
-					}else{
-						uni.showModal({title:re.msg,showCancel:false});
-					}
-				});
+        if (this.specBtnType === 3 || this.specBtnType===4){//拼团
+          // 请求接口
+          this.$http.request('api/orders/create_regiment',{"product_list":productList,"custom_coupon_id":this.customCoupon,'addr_id':this.checkedAddr.id,'btn_type':this.specBtnType,'regiment_id':this.regiment_id,'regiment_active_id':this.productInfo.regiment_active_id},'post').then((re)=>{
+            // 判断结果
+            if( re.code == 'success' ) {
+              // 处理结果
+              this.productInfo.stock = this.productInfo.stock - this.quantity;
+              // 关闭弹窗
+              this.$refs.specPopup.close();
+              // 跳转到报单完成页面
+              uni.navigateTo({url:`/pages/orders/completion?params=${encodedArray}`});
+            }else{
+              uni.showModal({title:re.msg,showCancel:false});
+            }
+          });
+        }else {
+          // 请求接口
+          this.$http.request('api/orders/create',{"product_list":productList,"custom_coupon_id":this.customCoupon,'addr_id':this.checkedAddr.id},'post').then((re)=>{
+            // 判断结果
+            if( re.code == 'success' ) {
+              // 处理结果
+              this.productInfo.stock = this.productInfo.stock - this.quantity;
+              // 关闭弹窗
+              this.$refs.specPopup.close();
+              // 跳转到报单完成页面
+              uni.navigateTo({url:`/pages/orders/completion?params=${encodedArray}`});
+            }else{
+              uni.showModal({title:re.msg,showCancel:false});
+            }
+          });
+        }
 			},
 			// 加入购物车
 			createCart(){
@@ -601,7 +764,12 @@
 			// 计算价格
 			priceHandler(){
 				// 总价格
-				let priceTotal 	= this.$decimal.mul(this.productInfo.price,this.quantity);
+        let priceTotal = 0
+        if (this.specBtnType == 3){
+          priceTotal 	= this.$decimal.mul(this.productInfo.regiment_price,this.quantity);
+        }else {
+          priceTotal 	= this.$decimal.mul(this.productInfo.price,this.quantity);
+        }
 				// 扣减数据
 				priceTotal		= priceTotal.sub(this.rebatePrice);
 				// 小数点保留
@@ -886,6 +1054,10 @@
 			line-height: 40rpx;
 			overflow: hidden;
 			padding: 10rpx 0rpx;
+      .regiment_title{
+        background-color: red;
+        color: #F9F9F9;
+      }
 		}
 		.product_spec{
 			color: #999999;
@@ -1001,6 +1173,23 @@
 			border-top-right-radius: 20rpx;
 			border-bottom-right-radius: 20rpx;
 		}
+    .show_order_regiment{
+      float: right;
+      width: 280rpx;
+      height: 80rpx;
+      display: block;
+      color: #FFFFFF;
+      font-size: 25rpx;
+      overflow: hidden;
+      padding: 0rpx 0rpx;
+      text-align: center;
+      margin-right: 35rpx;
+      background-color: #E03519;
+      border-radius: 20rpx;
+      .regiment_countdown{
+        padding-left: 50rpx;
+      }
+    }
 		.show_car{
 			float: right;
 			width: 280rpx;
@@ -1496,6 +1685,62 @@
 				background-color: #E03519;
 			}
 		}
+    .regiment_popup_list{
+      display: block;
+      overflow: hidden;
+      min-height: 600rpx;
+      margin: 10rpx auto;
+      background: #FFFFFF;
+      padding-bottom: 50rpx;
+      .regiment_popup_item{
+        height: 100rpx;
+        display: block;
+        margin: 10rpx auto;
+        border-bottom: 2rpx solid #DDDDDD;
+        .username{
+          float: left;
+          padding-left: 20rpx;
+          padding-top: 20rpx;
+          .user_image{
+            float: left;
+            display: block;
+            width: 80rpx;
+            height: 80rpx;
+            border-radius: 40%;
+          }
+          .username_text{
+            height: 80rpx;
+            line-height: 80rpx;
+          }
+        }
+        .regiment_center{
+          float: right;
+          padding-right: 18rpx;
+          padding-top: 25rpx;
+          .regiment_center_text{
+            font-size: 25rpx;
+          }
+          .regiment_center_time{
+            font-size: 25rpx;
+            .center_title{
+              float: left;
+            }
+          }
+        }
+        .regiment_right{
+          float: right;
+          padding-top: 25rpx;
+          .regiment_but{
+            font-size: 25rpx;
+            background-color: #E03519;
+            color: #F9F9F9;
+          }
+        }
+      }
+      .coupon_item:last-child{
+        border-bottom: none;
+      }
+    }
 	}
 	// 添加客服
 	.center_popup{
@@ -1571,4 +1816,74 @@
 			}
 		}
 	}
+  .regiment_list{
+    width: 700rpx;
+    margin-left: 20rpx;
+    background-color: red;
+    border-radius: 10px;
+    .regiment_list_title{
+      padding-top: 20rpx;
+      height: 60rpx;
+      .title1{
+        margin-top: 20rpx;
+        padding-left: 20rpx;
+        color: #F9F9F9;
+      }
+      .title2{
+        padding-left: 350rpx;
+        color: #F9F9F9;
+      }
+    }
+    .regiment{
+      width: 685rpx;
+      height: 250rpx;
+      background-color: #F9F9F9;
+      margin-left: 10rpx;
+      border-radius: 10px;
+      .swiper{
+        height: 250rpx;
+        .swiper-item{
+          .username{
+            float: left;
+            padding-left: 20rpx;
+            padding-top: 20rpx;
+            .user_image{
+              float: left;
+              display: block;
+              width: 80rpx;
+              height: 80rpx;
+              border-radius: 40%;
+            }
+            .username_text{
+              height: 80rpx;
+              line-height: 80rpx;
+            }
+          }
+          .regiment_center{
+            float: right;
+            padding-right: 18rpx;
+            padding-top: 25rpx;
+            .regiment_center_text{
+              font-size: 25rpx;
+            }
+            .regiment_center_time{
+              font-size: 25rpx;
+              .center_title{
+                float: left;
+              }
+            }
+          }
+          .regiment_right{
+            float: right;
+            padding-top: 25rpx;
+            .regiment_but{
+              font-size: 25rpx;
+              background-color: #E03519;
+              color: #F9F9F9;
+            }
+          }
+        }
+      }
+    }
+  }
 </style>

+ 28 - 0
uni_modules/uni-countdown/changelog.md

@@ -0,0 +1,28 @@
+## 1.2.4(2024-09-21)
+- 新增 支持控制显示位数 默认显示2位
+## 1.2.3(2024-02-20)
+- 新增 支持控制小时,分钟的显隐:showHour showMinute
+## 1.2.2(2022-01-19)
+- 修复 在微信小程序中样式不生效的bug
+## 1.2.1(2022-01-18)
+- 新增 update 方法 ,在动态更新时间后,刷新组件
+## 1.2.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-countdown](https://uniapp.dcloud.io/component/uniui/uni-countdown)
+## 1.1.3(2021-10-18)
+- 重构
+- 新增 font-size 支持自定义字体大小
+## 1.1.2(2021-08-24)
+- 新增 支持国际化
+## 1.1.1(2021-07-30)
+- 优化 vue3下小程序事件警告的问题
+## 1.1.0(2021-07-30)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.0.5(2021-06-18)
+- 修复 uni-countdown 重复赋值跳两秒的 bug
+## 1.0.4(2021-05-12)
+- 新增 组件示例地址
+## 1.0.3(2021-05-08)
+- 修复 uni-countdown 不能控制倒计时的 bug
+## 1.0.2(2021-02-04)
+- 调整为uni_modules目录规范

+ 6 - 0
uni_modules/uni-countdown/components/uni-countdown/i18n/en.json

@@ -0,0 +1,6 @@
+{
+	"uni-countdown.day": "day",
+	"uni-countdown.h": "h",
+	"uni-countdown.m": "m",
+	"uni-countdown.s": "s"
+}

+ 8 - 0
uni_modules/uni-countdown/components/uni-countdown/i18n/index.js

@@ -0,0 +1,8 @@
+import en from './en.json'
+import zhHans from './zh-Hans.json'
+import zhHant from './zh-Hant.json'
+export default {
+	en,
+	'zh-Hans': zhHans,
+	'zh-Hant': zhHant
+}

+ 6 - 0
uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hans.json

@@ -0,0 +1,6 @@
+{
+	"uni-countdown.day": "天",
+	"uni-countdown.h": "时",
+	"uni-countdown.m": "分",
+	"uni-countdown.s": "秒"
+}

+ 6 - 0
uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hant.json

@@ -0,0 +1,6 @@
+{
+	"uni-countdown.day": "天",
+	"uni-countdown.h": "時",
+	"uni-countdown.m": "分",
+	"uni-countdown.s": "秒"
+}

+ 276 - 0
uni_modules/uni-countdown/components/uni-countdown/uni-countdown.vue

@@ -0,0 +1,276 @@
+<template>
+	<view class="uni-countdown">
+		<text v-if="showDay" :style="[timeStyle]" class="uni-countdown__number">{{ d }}</text>
+		<text v-if="showDay" :style="[splitorStyle]" class="uni-countdown__splitor">{{dayText}}</text>
+		<text v-if="showHour" :style="[timeStyle]" class="uni-countdown__number">{{ h }}</text>
+		<text v-if="showHour" :style="[splitorStyle]" class="uni-countdown__splitor">{{ showColon ? ':' : hourText }}</text>
+		<text v-if="showMinute" :style="[timeStyle]" class="uni-countdown__number">{{ i }}</text>
+		<text v-if="showMinute" :style="[splitorStyle]" class="uni-countdown__splitor">{{ showColon ? ':' : minuteText }}</text>
+		<text :style="[timeStyle]" class="uni-countdown__number">{{ s }}</text>
+		<text v-if="!showColon" :style="[splitorStyle]" class="uni-countdown__splitor">{{secondText}}</text>
+	</view>
+</template>
+<script>
+	import {
+		initVueI18n
+	} from '@dcloudio/uni-i18n'
+	import messages from './i18n/index.js'
+	const {
+		t
+	} = initVueI18n(messages)
+	/**
+	 * Countdown 倒计时
+	 * @description 倒计时组件
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=25
+	 * @property {String} backgroundColor 背景色
+	 * @property {String} color 文字颜色
+	 * @property {Number} day 天数
+	 * @property {Number} hour 小时
+	 * @property {Number} minute 分钟
+	 * @property {Number} second 秒
+	 * @property {Number} timestamp 时间戳
+	 * @property {Boolean} showDay = [true|false] 是否显示天数
+	 * @property {Boolean} showHour = [true|false] 是否显示小时
+	 * @property {Boolean} showMinute = [true|false] 是否显示分钟
+	 * @property {Boolean} show-colon = [true|false] 是否以冒号为分隔符
+	 * @property {String} splitorColor 分割符号颜色
+	 * @event {Function} timeup 倒计时时间到触发事件
+	 * @example <uni-countdown :day="1" :hour="1" :minute="12" :second="40"></uni-countdown>
+	 */
+	export default {
+		name: 'UniCountdown',
+		emits: ['timeup'],
+		props: {
+			showDay: {
+				type: Boolean,
+				default: true
+			},
+			showHour: {
+				type: Boolean,
+				default: true
+			},
+			showMinute: {
+				type: Boolean,
+				default: true
+			},
+			showColon: {
+				type: Boolean,
+				default: true
+			},
+			start: {
+				type: Boolean,
+				default: true
+			},
+			backgroundColor: {
+				type: String,
+				default: ''
+			},
+			color: {
+				type: String,
+				default: '#333'
+			},
+			fontSize: {
+				type: Number,
+				default: 14
+			},
+			splitorColor: {
+				type: String,
+				default: '#333'
+			},
+			day: {
+				type: Number,
+				default: 0
+			},
+			hour: {
+				type: Number,
+				default: 0
+			},
+			minute: {
+				type: Number,
+				default: 0
+			},
+			second: {
+				type: Number,
+				default: 0
+			},
+			timestamp: {
+				type: Number,
+				default: 0
+			},
+			filterShow : {
+				type:Object,
+				default:{}
+			}
+		},
+		data() {
+			return {
+				timer: null,
+				syncFlag: false,
+				d: '00',
+				h: '00',
+				i: '00',
+				s: '00',
+				leftTime: 0,
+				seconds: 0
+			}
+		},
+		computed: {
+			dayText() {
+				return t("uni-countdown.day")
+			},
+			hourText(val) {
+				return t("uni-countdown.h")
+			},
+			minuteText(val) {
+				return t("uni-countdown.m")
+			},
+			secondText(val) {
+				return t("uni-countdown.s")
+			},
+			timeStyle() {
+				const {
+					color,
+					backgroundColor,
+					fontSize
+				} = this
+				return {
+					color,
+					backgroundColor,
+					fontSize: `${fontSize}px`,
+					width: `${fontSize * 22 / 14}px`, // 按字体大小为 14px 时的比例缩放
+ 					lineHeight: `${fontSize * 20 / 14}px`,
+					borderRadius: `${fontSize * 3 / 14}px`,
+				}
+			},
+			splitorStyle() {
+				const { splitorColor, fontSize, backgroundColor } = this
+				return {
+					color: splitorColor,
+					fontSize: `${fontSize * 12 / 14}px`,
+					margin: backgroundColor ? `${fontSize * 4 / 14}px` : ''
+				}
+			}
+		},
+		watch: {
+			day(val) {
+				this.changeFlag()
+			},
+			hour(val) {
+				this.changeFlag()
+			},
+			minute(val) {
+				this.changeFlag()
+			},
+			second(val) {
+				this.changeFlag()
+			},
+			start: {
+				immediate: true,
+				handler(newVal, oldVal) {
+					if (newVal) {
+						this.startData();
+					} else {
+						if (!oldVal) return
+						clearInterval(this.timer)
+					}
+				}
+
+			}
+		},
+		created: function(e) {
+			this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second)
+			this.countDown()
+		},
+		// #ifndef VUE3
+		destroyed() {
+			clearInterval(this.timer)
+		},
+		// #endif
+		// #ifdef VUE3
+		unmounted() {
+			clearInterval(this.timer)
+		},
+		// #endif
+		methods: {
+			toSeconds(timestamp, day, hours, minutes, seconds) {
+				if (timestamp) {
+					return timestamp - parseInt(new Date().getTime() / 1000, 10)
+				}
+				return day * 60 * 60 * 24 + hours * 60 * 60 + minutes * 60 + seconds
+			},
+			timeUp() {
+				clearInterval(this.timer)
+				this.$emit('timeup')
+			},
+			countDown() {
+				let seconds = this.seconds
+				let [day, hour, minute, second] = [0, 0, 0, 0]
+				if (seconds > 0) {
+					day = Math.floor(seconds / (60 * 60 * 24))
+					hour = Math.floor(seconds / (60 * 60)) - (day * 24)
+					minute = Math.floor(seconds / 60) - (day * 24 * 60) - (hour * 60)
+					second = Math.floor(seconds) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60)
+				} else {
+					this.timeUp()
+				}
+				this.d  = String(day).padStart(this.validFilterShow(this.filterShow.d), '0')
+				this.h = String(hour).padStart(this.validFilterShow(this.filterShow.h), '0')
+				this.i = String(minute).padStart(this.validFilterShow(this.filterShow.m), '0')
+				this.s = String(second).padStart(this.validFilterShow(this.filterShow.s), '0')
+			},
+			validFilterShow(filter){
+				return (filter && filter > 0) ? filter : 2;
+			},
+			startData() {
+				this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second)
+				if (this.seconds <= 0) {
+					this.seconds = this.toSeconds(0, 0, 0, 0, 0)
+					this.countDown()
+					return
+				}
+				clearInterval(this.timer)
+				this.countDown()
+				this.timer = setInterval(() => {
+					this.seconds--
+					if (this.seconds < 0) {
+						this.timeUp()
+						return
+					}
+					this.countDown()
+				}, 1000)
+			},
+			update(){
+				this.startData();
+			},
+			changeFlag() {
+				if (!this.syncFlag) {
+					this.seconds = this.toSeconds(this.timestamp, this.day, this.hour, this.minute, this.second)
+					this.startData();
+					this.syncFlag = true;
+				}
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	$font-size: 14px;
+
+	.uni-countdown {
+		display: flex;
+		flex-direction: row;
+		justify-content: flex-start;
+		align-items: center;
+
+		&__splitor {
+			margin: 0 2px;
+			font-size: $font-size;
+			color: #333;
+		}
+
+		&__number {
+			border-radius: 3px;
+			text-align: center;
+			font-size: $font-size;
+		}
+	}
+</style>

+ 84 - 0
uni_modules/uni-countdown/package.json

@@ -0,0 +1,84 @@
+{
+  "id": "uni-countdown",
+  "displayName": "uni-countdown 倒计时",
+  "version": "1.2.4",
+  "description": "CountDown 倒计时组件",
+  "keywords": [
+    "uni-ui",
+    "uniui",
+    "countdown",
+    "倒计时"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": ""
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+"dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
+    "type": "component-vue"
+  },
+  "uni_modules": {
+    "dependencies": ["uni-scss"],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y",
+        "alipay": "n"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "y"
+        }
+      }
+    }
+  }
+}

+ 10 - 0
uni_modules/uni-countdown/readme.md

@@ -0,0 +1,10 @@
+
+
+## CountDown 倒计时
+> **组件名:uni-countdown**
+> 代码块: `uCountDown`
+
+倒计时组件。
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-countdown)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 1 - 0
utils/request.js

@@ -1,5 +1,6 @@
 // 请求域名
 var domain = 'https://kailin.dfwy.tech/'
+//var domain = 'http://kailin.test/'
 // #ifdef MP-WEIXIN
 // 'http://127.0.0.1:8000/';
 domain = uni.getAccountInfoSync().miniProgram.envVersion == 'release' ? 'https://mall.findit.ltd/' : 'https://kailin.dfwy.tech/';