Figo il y a 1 mois
Parent
commit
2657088119

+ 7 - 0
pages.json

@@ -60,6 +60,13 @@
 					}
 				},
 				{
+					"path": "orderDetail",
+					"style": {
+						"navigationBarTitleText": "订单详情",
+						"navigationStyle": "custom"
+					}
+				},
+				{
 					"path": "success",
 					"style": {
 						"navigationBarTitleText": "支付成功",

Fichier diff supprimé car celui-ci est trop grand
+ 609 - 555
pages/index/index.vue


+ 48 - 58
pages/order/index.vue

@@ -15,10 +15,10 @@
       </u-tabs>
     </view>
     <view class="content">
-      <mescroll-body height="100%" class="custom-height" ref="mescrollRef" @init="mescrollInit" @down="downCallback"
+      <mescroll-body height="100%" class="custom-height" ref="mescrollRef" @down="downCallback"
                      :up="upOption" @up="upCallback">
         <view class="order-list" v-if="arrList && arrList.length">
-          <view class="tab-content" v-for="(item, index) in arrList" :key="index">
+          <view class="tab-content" v-for="(item, index) in arrList" :key="index" @click="goOrderDetail(item)">
             <view class="order-top flex-start">
               <text class="name">{{ item.packageName }}({{ item.quantity }})</text>
               <text v-if="item.paymentStatus==='CANCELLED'" class="status cancel">{{
@@ -42,9 +42,11 @@
               </view>
               <view class="order-middle-right" v-if="item.paymentStatus==='SHOOTING'">
                 <view class="progress-bar">
-                  <view class="progress" :style="{ width: (item.completedQuantity/item.quantity)*100+ '%' }">{{item.completedQuantity/item.quantity*100}}%</view>
+                  <view class="progress" :style="{ width: (item.completedQuantity/item.quantity)*100+ '%' }">
+                    {{ item.completedQuantity }}/{{item.quantity}}
+                  </view>
                 </view>
-<!--                <view class="progress-container">{{item.completedQuantity}}/{{item.quantity}}</view>-->
+                <!--                <view class="progress-container">{{item.completedQuantity}}/{{item.quantity}}</view>-->
               </view>
               <view class="order-middle-right cancel flex-start" v-if="item.paymentStatus==='REFUNDED'">
                 <view class="right-label fs24 ">已退款</view>
@@ -55,8 +57,8 @@
               <view class="order-bottom-left">
               </view>
               <view class="order-bottom-right flex">
-                <view class="btn btn1 p2" @click="cancelOrder(item.orderId)">取消</view>
-                <view class="btn btn2 p1" @click="submitOrder(item)">支付</view>
+                <view class="btn btn1 p2" @click.stop="cancelOrder(item.orderId)">取消</view>
+                <view class="btn btn2 p1" @click.stop="submitOrder(item)">支付</view>
                 <!-- <view class="btn3">寄样</view> -->
               </view>
             </view>
@@ -66,29 +68,29 @@
                 <view class="fs28 fw ml-6"> ¥{{ item.totalAmount }}</view>
               </view>
               <view class="order-bottom-right flex">
-                <view class="btn btn1 p2" v-if="item.paymentStatus==='WAIT_PAYMENT'" @click="cancelOrder(item.orderId)">
+                <view class="btn btn1 p2" v-if="item.paymentStatus==='WAIT_PAYMENT'" @click.stop="cancelOrder(item.orderId)">
                   取消
                 </view>
-                <view class="btn btn1 p2" v-if="item.paymentStatus==='WAIT_SEND'" @click="cancelOrder(item.orderId)">
+                <view class="btn btn1 p2" v-if="item.paymentStatus==='WAIT_SEND'" @click.stop="cancelOrder(item.orderId)">
                   取消
                 </view>
-                <view class="btn btn1 p2" v-if="item.paymentStatus==='WAIT_ORDER'" @click="cancelOrder(item.orderId)">
+                <view class="btn btn1 p2" v-if="item.paymentStatus==='WAIT_ORDER'" @click.stop="cancelOrder(item.orderId)">
                   取消
                 </view>
-                <view v-if="item.paymentStatus==='WAIT_SEND'" class="btn btn1 p2 ml-6">寄样</view>
-                <view class="btn btn1 p2" v-if="item.paymentStatus==='SHOOTING'" @click="cancelOrder(item.orderId)">申请售后
+               <!-- <view v-if="item.paymentStatus==='WAIT_SEND'" class="btn btn1 p2 ml-6 btn-theme">寄样</view> -->
+                <view class="btn btn1 p2" v-if="item.paymentStatus==='SHOOTING'" @click.stop="aftermarketOrder(item.orderId)">申请售后
                 </view>
                 <view class="btn btn1 p2" v-if="item.paymentStatus==='DELIVERED'"
-                      @click="aftermarketOrder(item.orderId)">申请售后
+                      @click.stop="aftermarketOrder(item.orderId)">申请售后
                 </view>
                 <view class="btn btn1 p2" v-if="item.paymentStatus==='WAIT_CHECK'"
-                      @click="aftermarketOrder(item.orderId)">申请售后
+                      @click.stop="aftermarketOrder(item.orderId)">申请售后
                 </view>
                 <view class="btn btn1 p2" v-if="item.paymentStatus==='AFTER_SALE'"
-                      @click="cancelAftermarketOrder(item.orderId)">撤回申请
+                      @click.stopk="cancelAftermarketOrder(item.orderId)">撤回申请
                 </view>
-                <view class="btn btn1 p2 ml-6" v-if="item.paymentStatus==='WAIT_CHECK'"
-                      @click="checkOrder(item.orderId)">确认验收
+                <view class="btn btn1 p2 ml-6 btn-theme" v-if="item.paymentStatus==='WAIT_CHECK'"
+                      @click.stop="checkOrder(item.orderId)">确认验收
                 </view>
               </view>
             </view>
@@ -128,10 +130,23 @@ export default {
       timeImg: 'https://ovp-shop.oss-cn-hangzhou.aliyuncs.com/static/9dshop/ic_time.png',
       arrList: [],
       isInit: false,
+      downOption: {
+        auto: false //是否在初始化完毕之后自动执行下拉回调callback; 默认true
+      },
+      upOption: {
+        page: {
+          num: 0,
+          size: 10 // 每页数据的数量,默认10
+        },
+        noMoreSize: 5, // 配置列表的总数量要大于等于5条才显示'-- END --'的提示
+        empty: {
+          tip: '暂无相关数据'
+        }
+      },
       paymentStatus: 'WAIT_PAYMENT',
       tabLists: [{name: '待付款', value: 'WAIT_PAYMENT'},
         {name: '待接单', value: 'WAIT_ORDER'},
-        {name: '待寄样', value: 'WAIT_SEND'},
+        // {name: '待寄样', value: 'WAIT_SEND'},
         {name: '拍摄中', value: 'SHOOTING'},
         {name: '待验收', value: 'WAIT_CHECK'},
         {name: '已交付', value: 'DELIVERED'},
@@ -146,25 +161,12 @@ export default {
   onLoad() {
     this.getOrderListPage()
   },
-  filters: {
-    orderStatus(value) {
-      const orderStatusList =
-          [{name: '待付款', value: 'WAIT_PAYMENT'},
-            {name: '待接单', value: 'WAIT_ORDER'},
-            {name: '待寄样', value: 'WAIT_SEND'},
-            {name: '拍摄中', value: 'SHOOTING'},
-            {name: '待验收', value: 'WAIT_CHECK'},
-            {name: '已交付', value: 'DELIVERED'},
-            {name: '售后中', value: 'AFTER_SALE'},
-            {name: '已取消', value: 'CANCELLED'},
-            {name: '已退款', value: 'REFUNDED'}
-          ]
-
-      const orderItem = orderStatusList.find(item => item.value === value)
-      return orderItem ? orderItem.name : ''
-    }
-  },
   methods: {
+    goOrderDetail(item){
+      uni.navigateTo({
+        url: '/pages/order/orderDetail?orderId='+item.orderId
+      })
+    },
     submitOrder(params) {
       submitOrder({
         ...params
@@ -226,11 +228,17 @@ export default {
       getOrderListPage({
         paymentStatus: this.paymentStatus,
         page: 1,
-        limit: 10,
+        limit: 50,
       }).then(res => {
         this.arrList = res.list
       })
     },
+    downCallback() {
+      this.page=1
+      this.getOrderListPage()
+      this.mescroll.endSuccess(); // 结束下拉刷新状态
+    },
+
     cancelOrder(orderId) {
       const that = this
       uni.showModal({
@@ -426,25 +434,7 @@ export default {
             padding: 16rpx 42rpx;
           }
 
-          .btn {
-            font-weight: 500;
-            font-size: 28rpx;
-            display: flex;
-            justify-content: center;
-            align-items: center;
-            border-radius: 36rpx;
-          }
 
-          .btn1 {
-            color: #0D121A;
-            border: 3rpx solid #E2E4EB;
-          }
-
-          .btn2 {
-            margin-left: 16rpx;
-            background: #FF7441;
-            color: #FFF;
-          }
         }
       }
     }
@@ -463,12 +453,13 @@ export default {
   background-color: #FF7441;
   width: 0%;
   text-align: center;
-  line-height: 30rrpx;
+  line-height: 30 rrpx;
   color: white;
   font-size: 20rpx;
   border-radius: 9999px;
   transition: width 0.5s ease;
 }
+
 .progress-container {
   width: 40rpx; /* 可按需调整宽度 */
   height: 20rpx; /* 可按需调整高度 */
@@ -481,7 +472,6 @@ export default {
   color: white; /* 文字颜色 */
   font-weight: bold; /* 文字加粗 */
 }
-.cancel {
-  color: #9EA3B1 !important;
-}
+
+
 </style>

+ 506 - 0
pages/order/orderDetail.vue

@@ -0,0 +1,506 @@
+<template>
+	<view class="success" :style="{ background: `url(${bgImg}) #F4F5F9` }">
+		<view class="success_status">
+			<view class="status flex aic">
+				<view class="status_text">{{ orderInfo.paymentStatus | orderStatus }}</view>
+			</view>
+		</view>
+		<view class="success_content">
+			<view class="success_info">
+				<view class="">
+					<view class="success_item flex jcsb aic">
+						<view class="flex aic">
+							<view class="label">收件地址</view>
+							<text class="bold">{{ orderInfo.email }}</text>
+						</view>
+						<!--            <view class="success_btn" @click="copyText()">复制</view>-->
+					</view>
+					<view class="success_item flex aic">
+						<view class="label">收件人</view>
+						<text>{{ orderInfo.recipientName }}({{ orderInfo.contactPhone }})</text>
+					</view>
+					<view v-if="orderInfo.completedLink" class="success_item flex aic">
+						<view class="label">拍摄视频地址</view>
+						<u-link :href="baiduLink" text="百度云盘链接" @click="click"></u-link>
+						<view class="copy" @click="copyText(orderInfo.completedLink)">复制</view>
+					</view>
+				</view>
+			</view>
+			<view class="line" :style="{ background: `url(${successImg}) no-repeat` }">
+				<view class="line-circle"></view>
+			</view>
+			<view class="detail">
+				<view class="detail_item flex aic jcsb">
+					<text>订单金额</text>
+					<view class="datas flex">
+						<text>{{ orderInfo.totalAmount }}元</text>
+					</view>
+				</view>
+				<view class="detail_item flex aic jcsb" @click="goCase">
+					<text>套餐名称</text>
+					<view class="datas flex">
+						<text>{{ orderInfo.packageName }}</text>
+						<view class="copy">详情</view>
+					</view>
+				</view>
+				<view class="detail_item flex aic jcsb">
+					<text>套餐内容</text>
+					<view class="datas flex">
+						<text>{{ orderInfo.quantity }}条 (每条15-20s)</text>
+					</view>
+				</view>
+				<view class="detail_item flex aic jcsb" v-if="orderInfo.paymentStatus!=='WAIT_PAYMENT'">
+					<text>支付方式</text>
+					<view class="datas flex">
+						<text>微信支付</text>
+					</view>
+				</view>
+				<view class="detail_item flex aic jcsb">
+					<text>订单编号</text>
+					<view class="datas flex">
+						<text>{{ orderInfo.orderNumber }}</text>
+						<view class="copy" @click="copyText(orderInfo.orderNumber)">复制</view>
+					</view>
+				</view>
+				<view class="detail_item flex aic jcsb">
+					<text>订单时间</text>
+					<view class="datas flex">
+						<text>{{ orderInfo.createDate }}</text>
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="btns">
+			<view v-if="orderInfo.paymentStatus==='WAIT_PAYMENT'" class="order-bottom-right flex">
+				<view class="btn btn1 p2" @click.stop="handleCancelOrder(orderInfo.orderId)">取消</view>
+				<view class="btn btn2 p1" @click.stop="submitOrder(orderInfo)">支付</view>
+			</view>
+			<view v-else class="order-bottom-right flex">
+				<view class="btn btn1 p2" v-if="orderInfo.paymentStatus==='WAIT_SEND'"
+					@click.stop="handleCancelOrder(orderInfo.orderId)">
+					取消
+				</view>
+				<view class="btn btn1 p2" v-if="orderInfo.paymentStatus==='WAIT_ORDER'"
+					@click.stop="handleCancelOrder(orderInfo.orderId)">
+					取消
+				</view>
+				<view v-if="orderInfo.paymentStatus==='WAIT_SEND'" class="btn btn1 p2 ml-6 btn-theme">寄样</view>
+				<view class="btn btn1 p2" v-if="orderInfo.paymentStatus==='SHOOTING'"
+					@click.stop="handleAftermarketOrder(orderInfo.orderId)">申请售后
+				</view>
+				<view class="btn btn1 p2" v-if="orderInfo.paymentStatus==='DELIVERED'"
+					@click.stop="handleAftermarketOrder(orderInfo.orderId)">申请售后
+				</view>
+				<view class="btn btn1 p2" v-if="orderInfo.paymentStatus==='WAIT_CHECK'"
+					@click.stop="handleAftermarketOrder(orderInfo.orderId)">申请售后
+				</view>
+				<view class="btn btn1 p2" v-if="orderInfo.paymentStatus==='AFTER_SALE'"
+					@click.stop="handleCancelAftermarketOrder(orderInfo.orderId)">撤回申请
+				</view>
+				<view class="btn btn1 p2 ml-6 btn-theme" v-if="orderInfo.paymentStatus==='WAIT_CHECK'"
+					@click.stop="handleCheckOrder(orderInfo.orderId)">确认验收
+				</view>
+			</view>
+
+
+			<view class="back" @click="goHome">返回首页</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		copy
+	} from "@/utils/utils";
+	import {
+		aftermarketOrder,
+		cancelAftermarketOrder,
+		cancelOrder,
+		checkOrder,
+		getOrderDetail,
+		updateOrder
+	} from "../../apis/order";
+	import {
+		goHome
+	} from "../../utils/utils";
+	import {
+		submitOrder
+	} from "../../apis";
+
+	export default {
+		components: {},
+		data() {
+			return {
+				orderId: '',
+				orderInfo: {
+					completedLink: '',
+					paymentStatus: '',
+					packageName: "",
+					createDate: "",
+					orderNumber: '',
+					shootingRequirements: '',
+					email: '',
+				},
+				bgImg: "https://ovp-shop.oss-cn-hangzhou.aliyuncs.com/static/9dshop/bg_top%403x.png",
+				payImg: "https://ovp-shop.oss-cn-hangzhou.aliyuncs.com/static/9dshop/ic_paysuccess.png",
+				successImg: 'https://ovp-shop.oss-cn-hangzhou.aliyuncs.com/static/9dshop/img_quekou.png'
+			};
+		},
+		onLoad(options) {
+			if (options.orderId) {
+				this.orderId = options.orderId;
+			}
+			this.getData();
+		},
+		computed: {
+			baiduLink() {
+
+				const linkRegex = /链接:\s*(https:\/\/pan.baidu.com\/s\/\S+)/;
+				const codeRegex = /提取码:\s*(\w+)/;
+
+				const linkMatch = this.orderInfo.completedLink.match(linkRegex);
+				const codeMatch = this.orderInfo.completedLink.match(codeRegex);
+
+				let link = "";
+				let accessCode = "";
+
+				if (linkMatch && linkMatch.length > 1) {
+					link = linkMatch[1];
+				}
+
+				if (codeMatch && codeMatch.length > 1) {
+					accessCode = codeMatch[1];
+				}
+				
+				return link +'?pwd='+ accessCode
+			}
+
+		},
+		watch: {
+			// 可以在这里添加监听器,例如监听订单状态变化后更新页面显示
+		},
+		methods: {
+
+			getData() {
+				getOrderDetail({
+					orderId: this.orderId
+				}).then(res => {
+					this.orderInfo = res;
+				});
+			},
+			goCase() {
+				uni.navigateTo({
+					url: '/pages/index/detail?type=1'
+				});
+			},
+			goHome() {
+				goHome();
+			},
+			// 复制
+			copyText(text) {
+				copy(text);
+			},
+			// 处理取消订单逻辑
+			handleCancelOrder(orderId) {
+				this.showConfirmModal('你确定要取消订单吗?', () => {
+					cancelOrder({
+						orderId: Number(orderId)
+					}).then(() => {
+						uni.navigateBack();
+					});
+				});
+			},
+			// 处理更新订单逻辑
+			handleUpdateOrder(orderId) {
+				this.showConfirmModal('你确定要取消订单吗?', () => {
+					updateOrder({
+						orderId: Number(orderId),
+					}).then(() => {
+						uni.navigateBack();
+					});
+				});
+			},
+			// 处理确认验收逻辑
+			handleCheckOrder(orderId) {
+				this.showConfirmModal('你确定要验收该订单吗?', () => {
+					checkOrder({
+						orderId: Number(orderId),
+					}).then(() => {
+						uni.navigateBack();
+					});
+				});
+			},
+			// 处理申请售后逻辑
+			handleAftermarketOrder(orderId) {
+				this.showConfirmModal('你确定要申请售后吗?', () => {
+					aftermarketOrder({
+						orderId: Number(orderId)
+					}).then(() => {
+						uni.navigateBack();
+					});
+				});
+			},
+			// 处理撤销售后逻辑
+			handleCancelAftermarketOrder(orderId) {
+				this.showConfirmModal('你确定要撤销售后吗?', () => {
+					cancelAftermarketOrder({
+						orderId: Number(orderId)
+					}).then(() => {
+						uni.navigateBack();
+					});
+				});
+			},
+			// 显示确认模态框的通用函数
+			showConfirmModal(content, confirmCallback) {
+				uni.showModal({
+					title: '提示',
+					content: content,
+					success: function(res) {
+						if (res.confirm) {
+							confirmCallback();
+						} else if (res.cancel) {
+							console.log('用户点击取消');
+						}
+					}
+				});
+			},
+			submitOrder(params) {
+				submitOrder({
+					...params
+				}).then((res) => {
+					// 检查是否在微信浏览器中
+					if (typeof WeixinJSBridge === 'undefined') {
+						if (document.addEventListener) {
+							document.addEventListener('WeixinJSBridgeReady', () => {
+								this.invokePayment(res);
+							}, false);
+						} else if (document.attachEvent) {
+							document.attachEvent('WeixinJSBridgeReady', () => {
+								this.invokePayment(res);
+							});
+							document.attachEvent('onWeixinJSBridgeReady', () => {
+								this.invokePayment(res);
+							});
+						}
+					} else {
+						this.invokePayment(res);
+					}
+				});
+			},
+			invokePayment(rePayInfo) {
+				WeixinJSBridge.invoke(
+					'getBrandWCPayRequest', {
+						appId: rePayInfo.appid,
+						timeStamp: rePayInfo.timeStamp,
+						nonceStr: rePayInfo.nonceStr,
+						package: rePayInfo.packages,
+						signType: rePayInfo.signType,
+						paySign: rePayInfo.sign
+					},
+					(res) => {
+						if (res.err_msg === 'get_brand_wcpay_request:ok') {
+							this.getData();
+						} else if (res.err_msg === 'get_brand_wcpay_request:cancel') {
+							uni.showToast({
+								title: '用户取消了支付'
+							});
+						} else {
+							console.error('支付失败', res.err_msg);
+							// 处理支付失败的逻辑
+						}
+					}
+				);
+			},
+		},
+		created() {
+			// 可以在这里添加组件创建时的初始化逻辑
+		},
+		mounted() {
+			// 可以在这里添加组件挂载后的逻辑,例如绑定一些事件监听器等
+		},
+	};
+</script>
+
+<style lang='scss' scoped>
+	.success {
+		width: 100%;
+		position: absolute;
+		top: 0;
+		bottom: 0;
+		display: flex;
+		flex-direction: column;
+		background-size: contain !important;
+		background-repeat: no-repeat !important;
+		background-position: top;
+		padding: 0 24rpx;
+
+		.success_status {
+			margin-top: 64rpx;
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+
+			.pay_img {
+				width: 48rpx;
+				height: 48rpx;
+				margin-left: 16rpx;
+			}
+
+			.status_text {
+				font-weight: 500;
+				font-size: 40rpx;
+				color: #0D121A;
+			}
+
+			.status_tip {
+				font-weight: 400;
+				font-size: 28rpx;
+				color: #9EA3B1;
+				margin-top: 20rpx;
+			}
+		}
+
+		.success_content {
+			border-radius: 32rpx;
+			overflow: hidden;
+			margin-top: 64rpx;
+
+			.success_info {
+				background-color: #fff;
+				padding: 36rpx 32rpx 8rpx;
+
+				.success_item {
+					margin-bottom: 20rpx;
+
+					.label {
+						min-width: 120rpx;
+						font-weight: 400;
+						font-size: 28rpx;
+						color: #9EA3B1;
+						margin-right: 16rpx;
+					}
+
+					text {
+						font-size: 32rpx;
+						color: #0D121A;
+					}
+				}
+			}
+
+			.success_btn {
+				width: 80rpx;
+				height: 40rpx;
+				font-weight: 500;
+				background: #F4F5F9;
+				border-radius: 48rpx;
+				font-size: 24rpx;
+				color: #0D121A;
+				text-align: center;
+				line-height: 40rpx;
+			}
+
+			.line {
+				width: 100%;
+				height: 40rpx;
+				background-size: cover !important;
+				padding: 0 32rpx;
+				display: flex;
+				justify-content: center;
+				align-items: center;
+
+				.line-circle {
+					width: 100%;
+					border: 2rpx dashed #EDEDED;
+					height: 2rpx;
+				}
+			}
+
+			.detail {
+				background-color: #fff;
+				padding: 32rpx 32rpx 40rpx;
+
+				.detail_item {
+					margin-bottom: 36rpx;
+
+					text {
+						white-space: nowrap;
+						font-weight: 400;
+						font-size: 28rpx;
+						color: #0D121A;
+					}
+
+					.datas {
+						.copy {
+							width: 80rpx;
+							height: 40rpx;
+							background: #F4F5F9;
+							border-radius: 48rpx;
+							font-weight: 500;
+							font-size: 24rpx;
+							color: #061714;
+							text-align: center;
+							line-height: 40rpx;
+							margin-left: 16rpx;
+						}
+					}
+				}
+
+				.detail_item:nth-last-child(1) {
+					margin-bottom: 0;
+				}
+			}
+
+			.success_title {
+				color: #9DA3A2;
+			}
+		}
+
+		.btns {
+			padding: 96rpx 32rpx;
+
+			view {
+				width: 100%;
+				height: 84rpx;
+				// border-radius: 48rpx;
+				display: flex;
+				justify-content: center;
+				align-items: center;
+				font-size: 32rpx;
+			}
+
+			.bind {
+				color: #061714;
+				margin-bottom: 32rpx;
+				font-weight: 500rpx;
+				background: linear-gradient(140deg, #246AF1 0%, #2F98EF 100%);
+			}
+
+			.back {
+				font-weight: bold;
+				background: #FFFFFF;
+				// border: 4rpx solid #E7EAEA;
+				color: #061714;
+			}
+		}
+
+		.ml16 {
+			margin-left: 16rpx;
+		}
+	}
+
+	.order-bottom-right {
+		margin-bottom: 36rpx;
+	}
+	.copy {
+		width: 80rpx;
+		height: 40rpx;
+		background: #F4F5F9;
+		border-radius: 48rpx;
+		font-weight: 500;
+		font-size: 24rpx;
+		color: #061714;
+		text-align: center;
+		line-height: 40rpx;
+		margin-left: 16rpx;
+	}
+</style>

Fichier diff supprimé car celui-ci est trop grand
+ 789 - 761
pages/order/order_confirm.vue


+ 7 - 2
pages/order/success.vue

@@ -14,7 +14,7 @@
           <view class="success_item flex jcsb aic">
             <view class="flex aic">
               <view class="label">收件地址</view>
-              <text class="bold">{{ orderInfo.email }}</text>
+              <text class="bold">{{ orderInfo.contactPhone }}</text>
             </view>
 <!--            <view class="success_btn" @click="copyText()">复制</view>-->
           </view>
@@ -70,7 +70,7 @@
       </view>
     </view>
     <view class="btns">
-      <view class="bind" @click="bindCard">查看详情</view>
+      <view class="bind" @click="goOrderDetail">查看详情</view>
       <view class="back" @click="goHome">返回首页</view>
     </view>
   </view>
@@ -113,6 +113,11 @@ export default {
         this.orderInfo = res
       })
     },
+    goOrderDetail(item){
+      uni.navigateTo({
+        url: '/pages/order/orderDetail?orderId='+this.orderId
+      })
+    },
     goHome(){
       goHome()
     },

+ 20 - 1
pages/user/index.vue

@@ -6,6 +6,7 @@
     </view>
     <view class="news">
       <image :src="avatar"></image>
+      `
       <view v-if="realName" class="phone">{{ realName }}</view>
       <view v-else class="phone" @click="toLogin()">点击登录</view>
     </view>
@@ -34,7 +35,7 @@
           </view>
         </view>
       </view>
-      <view class="cantact">
+      <view class="cantact" @click="serviceVisible=true">
         <view class="canl">
           <image :src="cantact"></image>
           <text>联系客服</text>
@@ -43,6 +44,13 @@
       </view>
       <view class="outline" @click="outLogin()">退出登录</view>
     </view>
+    <u-modal :show="serviceVisible" title="联系客服" :closeOnClickOverlay="true" @confirm="serviceVisible=false">
+      <view class="slot-content">
+        <view class="flex-center service-box">
+          <img src="" alt="">
+        </view>
+      </view>
+    </u-modal>
   </view>
 </template>
 
@@ -53,6 +61,7 @@ export default {
   components: {},
   data() {
     return {
+      serviceVisible: false,
       realName: uni.getStorageSync('realName'),
       token: uni.getStorageSync('token'),
       bgImg: "https://ovp-shop.oss-cn-hangzhou.aliyuncs.com/static/9dshop/bg_top%403x.png",
@@ -109,6 +118,7 @@ export default {
     outLogin() {
       logout().then(res => {
         uni.removeStorageSync('token')
+        uni.removeStorageSync('realName')
         this.$router.push({
           path: '/pages/index/index'
         })
@@ -321,4 +331,13 @@ export default {
     margin: 48rpx auto;
   }
 }
+
+.service-box {
+  padding: 32rpx;
+}
+
+.service-box img {
+  width: 300rpx;
+  height: 300rpx;
+}
 </style>

+ 256 - 0
plugins/uview-ui/components/u-swipe-action-item/index - backup.wxs

@@ -0,0 +1,256 @@
+/**
+ * 此为wxs模块,只支持APP-VUE,微信和QQ小程序以及H5平台
+ * wxs内部不支持es6语法,变量只能使用var定义,无法使用解构,箭头函数等特性
+ */
+
+// 开始触摸
+function touchstart(event, ownerInstance) {
+	// 触发事件的组件的ComponentDescriptor实例
+	var instance = event.instance
+	// wxs内的局部变量快照,此快照是属于整个组件的,在touchstart和touchmove事件中都能获取到相同的结果
+	var state = instance.getState()
+	if (state.disable) return
+	var touches = event.touches
+	// 如果进行的是多指触控,不允许进行操作
+	if (touches && touches.length > 1) return
+	// 标识当前为滑动中状态
+	state.moving = true
+	// 记录触摸开始点的坐标值
+	state.startX = touches[0].pageX
+	state.startY = touches[0].pageY
+}
+
+// 触摸滑动
+function touchmove(event, ownerInstance) {
+	// 触发事件的组件的ComponentDescriptor实例
+	var instance = event.instance
+	// wxs内的局部变量快照
+	var state = instance.getState()
+	if (state.disabled || !state.moving) return
+
+	var touches = event.touches
+	var pageX = touches[0].pageX
+	var pageY = touches[0].pageY
+	var moveX = pageX - state.startX
+	var moveY = pageY - state.startY
+	var buttonsWidth = state.buttonsWidth
+
+	// 移动的X轴距离大于Y轴距离,也即终点与起点位置连线,与X轴夹角小于45度时,禁止页面滚动
+	if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > state.threshold) {
+		event.preventDefault()
+		event.stopPropagation()
+	}
+	// 如果移动的X轴距离小于Y轴距离,也即终点位置与起点位置连线,与Y轴夹角小于45度时,认为是页面上下滑动,而不是左右滑动单元格
+	if (Math.abs(moveX) < Math.abs(moveY)) return
+
+	// 限制右滑的距离,不允许内容部分往右偏移,右滑会导致X轴偏移值大于0,以此做判断
+	// 此处不能直接return,因为滑动过程中会缺失某些关键点坐标,会导致错乱,最好的办法就是
+	// 在超出后,设置为0
+	if (state.status === 'open') {
+		// 在开启状态下,向左滑动,需忽略
+		if (moveX < 0) moveX = 0
+		// 想要收起菜单,最大能移动的距离为按钮的总宽度
+		if (moveX > buttonsWidth) moveX = buttonsWidth
+		// 如果是已经打开了的状态,向左滑动时,移动收起菜单
+		moveSwipeAction(-buttonsWidth + moveX, instance, ownerInstance)
+	} else {
+		// 关闭状态下,右滑动需忽略
+		if (moveX > 0) moveX = 0
+		// 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数
+		if (Math.abs(moveX) > buttonsWidth) moveX = -buttonsWidth
+		// 只要是在滑过程中,就不断移动菜单的内容部分,从而使隐藏的菜单显示出来
+		moveSwipeAction(moveX, instance, ownerInstance)
+	}
+}
+
+// 触摸结束
+function touchend(event, ownerInstance) {
+	// 触发事件的组件的ComponentDescriptor实例
+	var instance = event.instance
+	// wxs内的局部变量快照
+	var state = instance.getState()
+	if (!state.moving || state.disabled) return
+	var touches = event.changedTouches ? event.changedTouches[0] : {}
+	var pageX = touches.pageX
+	var pageY = touches.pageY
+	var moveX = pageX - state.startX
+	if (state.status === 'open') {
+		// 在展开的状态下,继续左滑,无需操作
+		if (moveX < 0) return
+		// 在开启状态下,点击一下内容区域,moveX为0,也即没有进行移动,这时执行收起菜单逻辑
+		if (moveX === 0) {
+			return closeSwipeAction(instance, ownerInstance)
+		}
+		// 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态
+		if (Math.abs(moveX) < state.threshold) {
+			openSwipeAction(instance, ownerInstance)
+		} else {
+			// 如果滑动距离大于阈值,则执行收起逻辑
+			closeSwipeAction(instance, ownerInstance)
+		}
+	} else {
+		// 在关闭的状态下,右滑,无需操作
+		if (moveX > 0) return
+		// 理由同上
+		if (Math.abs(moveX) < state.threshold) {
+			closeSwipeAction(instance, ownerInstance)
+		} else {
+			openSwipeAction(instance, ownerInstance)
+		}
+	}
+}
+
+// 获取过渡时间
+function getDuration(value) {
+	if (value.toString().indexOf('s') >= 0) return value
+	return value > 30 ? value + 'ms' : value + 's'
+}
+
+// 滑动结束时判断滑动的方向
+function getMoveDirection(instance, ownerInstance) {
+	var state = instance.getState()
+}
+
+// 移动滑动选择器内容区域,同时显示出其隐藏的菜单
+function moveSwipeAction(moveX, instance, ownerInstance) {
+	var state = instance.getState()
+	// 获取所有按钮的实例,需要通过它去设置按钮的位移
+	var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button')
+	var len = buttons.length
+	var previewButtonsMoveX = 0
+
+	// 设置菜单内容部分的偏移
+	instance.requestAnimationFrame(function() {
+		instance.setStyle({
+			// 设置translateX的值
+			'transition': 'none',
+			transform: 'translateX(' + moveX + 'px)',
+			'-webkit-transform': 'translateX(' + moveX + 'px)'
+		})
+		// 折叠按钮动画
+		for (var i = len - 1; i >= 0; i--) {
+			// 通过比例,得出元素自身该移动的距离
+			var translateX = state.buttons[i].width / state.buttonsWidth * moveX
+			// 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和
+			var realTranslateX = translateX + previewButtonsMoveX
+			buttons[i].setStyle({
+				// 在移动期间,不能使用过渡效果,否则会造成卡顿,本质原因是每次移动一点,就要花一定时间去过渡这个过程
+				'transition': 'none',
+				'transform': 'translateX(' + realTranslateX + 'px)',
+				'-webkit-transform': 'translateX(' + realTranslateX + 'px)'
+			})
+			// 记录本按钮之前的所有按钮的移动距离之和
+			previewButtonsMoveX += translateX
+		}
+	})
+}
+
+// 一次性展开滑动菜单
+function openSwipeAction(instance, ownerInstance) {
+	var state = instance.getState()
+	// 获取所有按钮的实例,需要通过它去设置按钮的位移
+	var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button')
+	var len = buttons.length
+	// 处理duration单位问题
+	const duration = getDuration(state.duration)
+	// 展开过程中,是向左移动,所以X的偏移应该为负值
+	var buttonsWidth = -state.buttonsWidth
+	var previewButtonsMoveX = 0
+	instance.requestAnimationFrame(function() {
+		// 设置菜单主体内容
+		instance.setStyle({
+			'transition': 'transform ' + duration,
+			'transform': 'translateX(' + buttonsWidth + 'px)',
+			'-webkit-transform': 'translateX(' + buttonsWidth + 'px)',
+		})
+		// 设置各个隐藏的按钮为展开的状态
+		for (var i = len - 1; i >= 0; i--) {
+			// 通过比例,得出元素自身该移动的距离
+			var translateX = state.buttons[i].width / state.buttonsWidth * buttonsWidth
+			// 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和
+			var realTranslateX = translateX + previewButtonsMoveX
+			buttons[i].setStyle({
+				// 在移动期间,需要加上动画效果
+				'transition': 'transform ' + duration,
+				'transform': 'translateX(' + realTranslateX + 'px)',
+				'-webkit-transform': 'translateX(' + realTranslateX + 'px)'
+			})
+			// 记录本按钮之前的所有按钮的移动距离之和
+			previewButtonsMoveX += translateX
+		}
+	})
+	setStatus('open', instance)
+}
+
+// 标记菜单的当前状态,open-已经打开,close-已经关闭
+function setStatus(status, instance) {
+	var state = instance.getState()
+	state.status = status
+}
+
+// 一次性收起滑动菜单
+function closeSwipeAction(instance, ownerInstance) {
+	var state = instance.getState()
+	// 获取所有按钮的实例,需要通过它去设置按钮的位移
+	var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button')
+	var len = buttons.length
+	// 处理duration单位问题
+	const duration = getDuration(state.duration)
+	instance.requestAnimationFrame(function() {
+		// 设置菜单主体内容
+		instance.setStyle({
+			'transition': 'transform ' + duration,
+			'transform': 'translateX(0px)',
+			'-webkit-transform': 'translateX(0px)'
+		})
+		// 设置各个隐藏的按钮为收起的状态
+		for (var i = len - 1; i >= 0; i--) {
+			buttons[i].setStyle({
+				'transition': 'transform ' + duration,
+				'transform': 'translateX(0px)',
+				'-webkit-transform': 'translateX(0px)'
+			})
+		}
+	})
+	setStatus('close', instance)
+}
+
+// show的状态发生变化
+function showChange(newValue, oldValue, ownerInstance, instance) {
+	var state = instance.getState()
+	if (state.disabled) return
+	// 打开或关闭单元格
+	if (newValue) {
+		openSwipeAction(instance, ownerInstance)
+	} else {
+		closeSwipeAction(instance, ownerInstance)
+	}
+}
+
+// 菜单尺寸发生变化
+function sizeChange(newValue, oldValue, ownerInstance, instance) {
+	// wxs内的局部变量快照
+	var state = instance.getState()
+	state.disabled = newValue.disabled
+	state.duration = newValue.duration
+	state.show = newValue.show
+	state.threshold = newValue.threshold
+	state.buttons = newValue.buttons
+
+	var len = state.buttons.length
+	if (len) {
+		var buttonsWidth = 0
+		var buttons = newValue.buttons
+		for (var i = 0; i < len; i++) {
+			buttonsWidth += buttons[i].width
+		}
+	}
+	state.buttonsWidth = buttonsWidth
+}
+
+module.exports = {
+	touchstart: touchstart,
+	touchmove: touchmove,
+	touchend: touchend,
+	sizeChange: sizeChange
+}

+ 225 - 0
plugins/uview-ui/components/u-swipe-action-item/index.wxs

@@ -0,0 +1,225 @@
+/**
+ * 此为wxs模块,只支持APP-VUE,微信和QQ小程序以及H5平台
+ * wxs内部不支持es6语法,变量只能使用var定义,无法使用解构,箭头函数等特性
+ */
+
+// 开始触摸
+function touchstart(event, ownerInstance) {
+	// 触发事件的组件的ComponentDescriptor实例
+	var instance = event.instance
+	// wxs内的局部变量快照,此快照是属于整个组件的,在touchstart和touchmove事件中都能获取到相同的结果
+	var state = instance.getState()
+	if (state.disabled) return
+	var touches = event.touches
+	// 如果进行的是多指触控,不允许进行操作
+	if (touches && touches.length > 1) return
+	// 标识当前为滑动中状态
+	state.moving = true
+	// 记录触摸开始点的坐标值
+	state.startX = touches[0].pageX
+	state.startY = touches[0].pageY
+	
+	ownerInstance.callMethod('closeOther')
+}
+
+// 触摸滑动
+function touchmove(event, ownerInstance) {
+	// 触发事件的组件的ComponentDescriptor实例
+	var instance = event.instance
+	// wxs内的局部变量快照
+	var state = instance.getState()
+	if (state.disabled || !state.moving) return
+	var touches = event.touches
+	var pageX = touches[0].pageX
+	var pageY = touches[0].pageY
+	var moveX = pageX - state.startX
+	var moveY = pageY - state.startY
+	var buttonsWidth = state.buttonsWidth
+
+	// 移动的X轴距离大于Y轴距离,也即终点与起点位置连线,与X轴夹角小于45度时,禁止页面滚动
+	if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > state.threshold) {
+		event.preventDefault && event.preventDefault()
+		event.stopPropagation && event.stopPropagation()
+	}
+	// 如果移动的X轴距离小于Y轴距离,也即终点位置与起点位置连线,与Y轴夹角小于45度时,认为是页面上下滑动,而不是左右滑动单元格
+	if (Math.abs(moveX) < Math.abs(moveY)) return
+
+	// 限制右滑的距离,不允许内容部分往右偏移,右滑会导致X轴偏移值大于0,以此做判断
+	// 此处不能直接return,因为滑动过程中会缺失某些关键点坐标,会导致错乱,最好的办法就是
+	// 在超出后,设置为0
+	if (state.status === 'open') {
+		// 在开启状态下,向左滑动,需忽略
+		if (moveX < 0) moveX = 0
+		// 想要收起菜单,最大能移动的距离为按钮的总宽度
+		if (moveX > buttonsWidth) moveX = buttonsWidth
+		// 如果是已经打开了的状态,向左滑动时,移动收起菜单
+		moveSwipeAction(-buttonsWidth + moveX, instance, ownerInstance)
+	} else {
+		// 关闭状态下,右滑动需忽略
+		if (moveX > 0) moveX = 0
+		// 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数
+		if (Math.abs(moveX) > buttonsWidth) moveX = -buttonsWidth
+		// 只要是在滑过程中,就不断移动单元格内容部分,从而使隐藏的菜单显示出来
+		moveSwipeAction(moveX, instance, ownerInstance)
+	}
+}
+
+// 触摸结束
+function touchend(event, ownerInstance) {
+	// 触发事件的组件的ComponentDescriptor实例
+	var instance = event.instance
+	// wxs内的局部变量快照
+	var state = instance.getState()
+	if (!state.moving || state.disabled) return
+	var touches = event.changedTouches ? event.changedTouches[0] : {}
+	var pageX = touches.pageX
+	var pageY = touches.pageY
+	var moveX = pageX - state.startX
+	if (state.status === 'open') {
+		// 在展开的状态下,继续左滑,无需操作
+		if (moveX < 0) return
+		// 在开启状态下,点击一下内容区域,moveX为0,也即没有进行移动,这时执行收起菜单逻辑
+		if (moveX === 0) {
+			return closeSwipeAction(instance, ownerInstance)
+		}
+		// 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态
+		if (Math.abs(moveX) < state.threshold) {
+			openSwipeAction(instance, ownerInstance)
+		} else {
+			// 如果滑动距离大于阈值,则执行收起逻辑
+			closeSwipeAction(instance, ownerInstance)
+		}
+	} else {
+		// 在关闭的状态下,右滑,无需操作
+		if (moveX > 0) return
+		// 理由同上
+		if (Math.abs(moveX) < state.threshold) {
+			closeSwipeAction(instance, ownerInstance)
+		} else {
+			openSwipeAction(instance, ownerInstance)
+		}
+	}
+}
+
+// 获取过渡时间
+function getDuration(value) {
+	if (value.toString().indexOf('s') >= 0) return value
+	return value > 30 ? value + 'ms' : value + 's'
+}
+
+// 滑动结束时判断滑动的方向
+function getMoveDirection(instance, ownerInstance) {
+	var state = instance.getState()
+}
+
+// 移动滑动选择器内容区域,同时显示出其隐藏的菜单
+function moveSwipeAction(moveX, instance, ownerInstance) {
+	var state = instance.getState()
+	// 获取所有按钮的实例,需要通过它去设置按钮的位移
+	var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button')
+
+	// 设置菜单内容部分的偏移
+	instance.requestAnimationFrame(function() {
+		instance.setStyle({
+			// 设置translateX的值
+			'transition': 'none',
+			transform: 'translateX(' + moveX + 'px)',
+			'-webkit-transform': 'translateX(' + moveX + 'px)'
+		})
+	})
+}
+
+// 一次性展开滑动菜单
+function openSwipeAction(instance, ownerInstance) {
+	var state = instance.getState()
+	// 获取所有按钮的实例,需要通过它去设置按钮的位移
+	var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button')
+	// 处理duration单位问题
+	var duration = getDuration(state.duration)
+	// 展开过程中,是向左移动,所以X的偏移应该为负值
+	var buttonsWidth = -state.buttonsWidth
+	instance.requestAnimationFrame(function() {
+		// 设置菜单主体内容
+		instance.setStyle({
+			'transition': 'transform ' + duration,
+			'transform': 'translateX(' + buttonsWidth + 'px)',
+			'-webkit-transform': 'translateX(' + buttonsWidth + 'px)',
+		})
+	})
+	setStatus('open', instance, ownerInstance)
+}
+
+// 标记菜单的当前状态,open-已经打开,close-已经关闭
+function setStatus(status, instance, ownerInstance) {
+	var state = instance.getState()
+	state.status = status
+	ownerInstance.callMethod('setState', status)
+}
+
+// 一次性收起滑动菜单
+function closeSwipeAction(instance, ownerInstance) {
+	var state = instance.getState()
+	// 获取所有按钮的实例,需要通过它去设置按钮的位移
+	var buttons = ownerInstance.selectAllComponents('.u-swipe-action-item__right__button')
+	var len = buttons.length
+	// 处理duration单位问题
+	var duration = getDuration(state.duration)
+	instance.requestAnimationFrame(function() {
+		// 设置菜单主体内容
+		instance.setStyle({
+			'transition': 'transform ' + duration,
+			'transform': 'translateX(0px)',
+			'-webkit-transform': 'translateX(0px)'
+		})
+		// 设置各个隐藏的按钮为收起的状态
+		for (var i = len - 1; i >= 0; i--) {
+			buttons[i].setStyle({
+				'transition': 'transform ' + duration,
+				'transform': 'translateX(0px)',
+				'-webkit-transform': 'translateX(0px)'
+			})
+		}
+	})
+	setStatus('close', instance, ownerInstance)
+}
+
+// status的状态发生变化
+function statusChange(newValue, oldValue, ownerInstance, instance) {
+	var state = instance.getState()
+	if (state.disabled) return
+	// 打开或关闭单元格
+	if (newValue === 'close' && state.status === 'open') {
+		closeSwipeAction(instance, ownerInstance)
+	} else if(newValue === 'open' && state.status === 'close') {
+		openSwipeAction(instance, ownerInstance)
+	}
+}
+
+// 菜单尺寸发生变化
+function sizeChange(newValue, oldValue, ownerInstance, instance) {
+	// wxs内的局部变量快照
+	var state = instance.getState()
+	state.disabled = newValue.disabled
+	state.duration = newValue.duration
+	state.show = newValue.show
+	state.threshold = newValue.threshold
+	state.buttons = newValue.buttons
+
+	if (state.buttons) {
+		var len = state.buttons.length
+		var buttonsWidth = 0
+		var buttons = newValue.buttons
+		for (var i = 0; i < len; i++) {
+			buttonsWidth += buttons[i].width
+		}
+	}
+	state.buttonsWidth = buttonsWidth
+}
+
+module.exports = {
+	touchstart: touchstart,
+	touchmove: touchmove,
+	touchend: touchend,
+	sizeChange: sizeChange,
+	statusChange: statusChange
+}

+ 270 - 0
plugins/uview-ui/components/u-swipe-action-item/nvue - backup.js

@@ -0,0 +1,270 @@
+// nvue操作dom的库,用于获取dom的尺寸信息
+const dom = uni.requireNativePlugin('dom')
+// nvue中用于操作元素动画的库,类似于uni.animation,只不过uni.animation不能用于nvue
+const animation = uni.requireNativePlugin('animation')
+
+export default {
+    data() {
+        return {
+            // 是否滑动中
+            moving: false,
+            // 状态,open-打开状态,close-关闭状态
+            status: 'close',
+            // 开始触摸点的X和Y轴坐标
+            startX: 0,
+            startY: 0,
+            // 所有隐藏按钮的尺寸信息数组
+            buttons: [],
+            // 所有按钮的总宽度
+            buttonsWidth: 0,
+            // 记录上一次移动的位置值
+            moveX: 0,
+            // 记录上一次滑动的位置,用于前后两次做对比,如果移动的距离小于某一阈值,则认为前后之间没有移动,为了解决可能存在的通信阻塞问题
+            lastX: 0
+        }
+    },
+    computed: {
+        // 获取过渡时间
+        getDuratin() {
+            let duration = String(this.duration)
+            // 如果ms为单位,返回ms的数值部分
+            if (duration.indexOf('ms') >= 0) return parseInt(duration)
+            // 如果s为单位,为了得到ms的数值,需要乘以1000
+            if (duration.indexOf('s') >= 0) return parseInt(duration) * 1000
+            // 如果值传了数值,且小于30,认为是s单位
+            duration = Number(duration)
+            return duration < 30 ? duration * 1000 : duration
+        }
+    },
+    watch: {
+        show: {
+            immediate: true,
+            handler(n) {
+                // if(n === true) {
+                // 	uni.$u.sleep(50).then(() => {
+                // 		this.openSwipeAction()
+                // 	})
+                // } else {
+                // 	this.closeSwipeAction()
+                // }
+            }
+        }
+    },
+    mounted() {
+        uni.$u.sleep(20).then(() => {
+            this.queryRect()
+        })
+    },
+    methods: {
+        close() {
+            this.closeSwipeAction()
+        },
+        // 触摸单元格
+        touchstart(event) {
+            if (this.disabled) return
+            this.closeOther()
+            const { touches } = event
+            // 记录触摸开始点的坐标值
+            this.startX = touches[0].pageX
+            this.startY = touches[0].pageY
+        },
+        // // 触摸滑动
+        touchmove(event) {
+            if (this.disabled) return
+            const { touches } = event
+            const { pageX } = touches[0]
+            const { pageY } = touches[0]
+            let moveX = pageX - this.startX
+            const moveY = pageY - this.startY
+            const { buttonsWidth } = this
+            const len = this.buttons.length
+
+            // 判断前后两次的移动距离,如果小于一定值,则不进行移动处理
+            if (Math.abs(pageX - this.lastX) < 0.3) return
+            this.lastX = pageX
+
+            // 移动的X轴距离大于Y轴距离,也即终点与起点位置连线,与X轴夹角小于45度时,禁止页面滚动
+            if (Math.abs(moveX) > Math.abs(moveY) || Math.abs(moveX) > this.threshold) {
+                event.stopPropagation()
+            }
+            // 如果移动的X轴距离小于Y轴距离,也即终点位置与起点位置连线,与Y轴夹角小于45度时,认为是页面上下滑动,而不是左右滑动单元格
+            if (Math.abs(moveX) < Math.abs(moveY)) return
+
+            // 限制右滑的距离,不允许内容部分往右偏移,右滑会导致X轴偏移值大于0,以此做判断
+            // 此处不能直接return,因为滑动过程中会缺失某些关键点坐标,会导致错乱,最好的办法就是
+            // 在超出后,设置为0
+            if (this.status === 'open') {
+                // 在开启状态下,向左滑动,需忽略
+                if (moveX < 0) moveX = 0
+                // 想要收起菜单,最大能移动的距离为按钮的总宽度
+                if (moveX > buttonsWidth) moveX = buttonsWidth
+                // 如果是已经打开了的状态,向左滑动时,移动收起菜单
+                this.moveSwipeAction(-buttonsWidth + moveX)
+            } else {
+                // 关闭状态下,右滑动需忽略
+                if (moveX > 0) moveX = 0
+                // 滑动的距离不允许超过所有按钮的总宽度,此时只能是左滑,最终设置按钮的总宽度,同时为负数
+                if (Math.abs(moveX) > buttonsWidth) moveX = -buttonsWidth
+                // 只要是在滑过程中,就不断移动菜单的内容部分,从而使隐藏的菜单显示出来
+                this.moveSwipeAction(moveX)
+            }
+        },
+        // 单元格结束触摸
+        touchend(event) {
+            if (this.disabled) return
+            const touches = event.changedTouches ? event.changedTouches[0] : {}
+            const { pageX } = touches
+            const { pageY } = touches
+            const { buttonsWidth } = this
+            this.moveX = pageX - this.startX
+            if (this.status === 'open') {
+                // 在展开的状态下,继续左滑,无需操作
+                if (this.moveX < 0) this.moveX = 0
+                if (this.moveX > buttonsWidth) this.moveX = buttonsWidth
+                // 在开启状态下,点击一下内容区域,moveX为0,也即没有进行移动,这时执行收起菜单逻辑
+                if (this.moveX === 0) {
+                    return this.closeSwipeAction()
+                }
+                // 在开启状态下,滑动距离小于阈值,则默认为不关闭,同时恢复原来的打开状态
+                if (Math.abs(this.moveX) < this.threshold) {
+                    this.openSwipeAction()
+                } else {
+                    // 如果滑动距离大于阈值,则执行收起逻辑
+                    this.closeSwipeAction()
+                }
+            } else {
+                // 在关闭的状态下,右滑,无需操作
+                if (this.moveX >= 0) this.moveX = 0
+                if (this.moveX <= -buttonsWidth) this.moveX = -buttonsWidth
+                // 理由同上
+                if (Math.abs(this.moveX) < this.threshold) {
+                    this.closeSwipeAction()
+                } else {
+                    this.openSwipeAction()
+                }
+            }
+        },
+        // 移动滑动选择器内容区域,同时显示出其隐藏的菜单
+        moveSwipeAction(moveX) {
+            if (this.moving) return
+            this.moving = true
+
+            let previewButtonsMoveX = 0
+            const len = this.buttons.length
+            animation.transition(this.$refs['u-swipe-action-item__content'].ref, {
+                styles: {
+                    transform: `translateX(${moveX}px)`
+                },
+                timingFunction: 'linear'
+            }, () => {
+                this.moving = false
+            })
+            // 按钮的组的长度
+            for (let i = len - 1; i >= 0; i--) {
+                const buttonRef = this.$refs[`u-swipe-action-item__right__button-${i}`][0].ref
+                // 通过比例,得出元素自身该移动的距离
+                const translateX = this.buttons[i].width / this.buttonsWidth * moveX
+                // 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和
+                const realTranslateX = translateX + previewButtonsMoveX
+                animation.transition(buttonRef, {
+                    styles: {
+                        transform: `translateX(${realTranslateX}px)`
+                    },
+                    duration: 0,
+                    delay: 0,
+                    timingFunction: 'linear'
+                }, () => {})
+                // 记录本按钮之前的所有按钮的移动距离之和
+                previewButtonsMoveX += translateX
+            }
+        },
+        // 关闭菜单
+        closeSwipeAction() {
+            if (this.status === 'close') return
+            this.moving = true
+            const { buttonsWidth } = this
+            animation.transition(this.$refs['u-swipe-action-item__content'].ref, {
+                styles: {
+                    transform: 'translateX(0px)'
+                },
+                duration: this.getDuratin,
+                timingFunction: 'ease-in-out'
+            }, () => {
+                this.status = 'close'
+                this.moving = false
+                this.closeHandler()
+            })
+            // 按钮的组的长度
+            const len = this.buttons.length
+            for (let i = len - 1; i >= 0; i--) {
+                const buttonRef = this.$refs[`u-swipe-action-item__right__button-${i}`][0].ref
+                // 如果不满足边界条件,返回
+                if (this.buttons.length === 0 || !this.buttons[i] || !this.buttons[i].width) return
+
+                animation.transition(buttonRef, {
+                    styles: {
+                        transform: 'translateX(0px)'
+                    },
+                    duration: this.getDuratin,
+                    timingFunction: 'ease-in-out'
+                }, () => {})
+            }
+        },
+        // 打开菜单
+        openSwipeAction() {
+            if (this.status === 'open') return
+            this.moving = true
+            const buttonsWidth = -this.buttonsWidth
+            let previewButtonsMoveX = 0
+            animation.transition(this.$refs['u-swipe-action-item__content'].ref, {
+                styles: {
+                    transform: `translateX(${buttonsWidth}px)`
+                },
+                duration: this.getDuratin,
+                timingFunction: 'ease-in-out'
+            }, () => {
+                this.status = 'open'
+                this.moving = false
+                this.openHandler()
+            })
+            // 按钮的组的长度
+            const len = this.buttons.length
+            for (let i = len - 1; i >= 0; i--) {
+                const buttonRef = this.$refs[`u-swipe-action-item__right__button-${i}`][0].ref
+                // 如果不满足边界条件,返回
+                if (this.buttons.length === 0 || !this.buttons[i] || !this.buttons[i].width) return
+                // 通过比例,得出元素自身该移动的距离
+                const translateX = this.buttons[i].width / this.buttonsWidth * buttonsWidth
+                // 最终移动的距离,是通过自身比例算出的距离,再加上在它之前所有按钮移动的距离之和
+                const realTranslateX = translateX + previewButtonsMoveX
+                animation.transition(buttonRef, {
+                    styles: {
+                        transform: `translateX(${realTranslateX}px)`
+                    },
+                    duration: this.getDuratin,
+                    timingFunction: 'ease-in-out'
+                }, () => {})
+                previewButtonsMoveX += translateX
+            }
+        },
+        // 查询按钮节点信息
+        queryRect() {
+            // 历遍所有按钮数组,通过getRectByDom返回一个promise
+            const promiseAll = this.rightOptions.map((item, index) => this.getRectByDom(this.$refs[`u-swipe-action-item__right__button-${index}`][0]))
+            // 通过promise.all方法,让所有按钮的查询结果返回一个数组的形式
+            Promise.all(promiseAll).then((sizes) => {
+                this.buttons = sizes
+                // 计算所有按钮总宽度
+                this.buttonsWidth = sizes.reduce((sum, cur) => sum + cur.width, 0)
+            })
+        },
+        // 通过nvue的dom模块,查询节点信息
+        getRectByDom(ref) {
+            return new Promise((resolve) => {
+                dom.getComponentRect(ref, (res) => {
+                    resolve(res.size)
+                })
+            })
+        }
+    }
+}

+ 174 - 0
plugins/uview-ui/components/u-swipe-action-item/nvue.js

@@ -0,0 +1,174 @@
+// nvue操作dom的库,用于获取dom的尺寸信息
+const dom = uni.requireNativePlugin('dom');
+const bindingX = uni.requireNativePlugin('bindingx');
+const animation = uni.requireNativePlugin('animation');
+
+export default {
+	data() {
+		return {
+			// 所有按钮的总宽度
+			buttonsWidth: 0,
+			// 是否正在移动中
+			moving: false
+		}
+	},
+	computed: {
+		// 获取过渡时间
+		getDuratin() {
+			let duration = String(this.duration)
+			// 如果ms为单位,返回ms的数值部分
+			if (duration.indexOf('ms') >= 0) return parseInt(duration)
+			// 如果s为单位,为了得到ms的数值,需要乘以1000
+			if (duration.indexOf('s') >= 0) return parseInt(duration) * 1000
+			// 如果值传了数值,且小于30,认为是s单位
+			duration = Number(duration)
+			return duration < 30 ? duration * 1000 : duration
+		}
+	},
+	watch: {
+		show(n) {
+			if(n) {
+				this.moveCellByAnimation('open') 
+			} else {
+				this.moveCellByAnimation('close') 
+			}
+		}
+	},
+	mounted() {
+		this.initialize()
+	},
+	methods: {
+		initialize() {
+			this.queryRect() 
+		},
+		// 关闭单元格,用于打开一个,自动关闭其他单元格的场景
+		closeHandler() {
+			if(this.status === 'open') {
+				// 如果在打开状态下,进行点击的话,直接关闭单元格
+				return this.moveCellByAnimation('close') && this.unbindBindingX()
+			}
+		},
+		// 点击单元格
+		clickHandler() {
+			// 如果在移动中被点击,进行忽略
+			if(this.moving) return
+			// 尝试关闭其他打开的单元格
+			this.parent && this.parent.closeOther(this)
+			if(this.status === 'open') {
+				// 如果在打开状态下,进行点击的话,直接关闭单元格
+				return this.moveCellByAnimation('close') && this.unbindBindingX()
+			}
+		},
+		// 滑动单元格
+		onTouchstart(e) {
+			// 如果当前正在移动中,或者disabled状态,则返回
+			if(this.moving || this.disabled) { 
+				return this.unbindBindingX()   
+			}
+			if(this.status === 'open') {
+				// 如果在打开状态下,进行点击的话,直接关闭单元格
+				return this.moveCellByAnimation('close') && this.unbindBindingX()
+			}
+			// 特殊情况下,e可能不为一个对象
+			e?.stopPropagation && e.stopPropagation() 
+			e?.preventDefault && e.preventDefault()
+			this.moving = true
+			// 获取元素ref
+			const content = this.getContentRef()
+			let expression = `min(max(${-this.buttonsWidth}, x), 0)`
+			// 尝试关闭其他打开的单元格
+			this.parent && this.parent.closeOther(this)
+			
+			// 阿里为了KPI而开源的BindingX
+			this.panEvent = bindingX.bind({
+				anchor: content,
+				eventType: 'pan',
+				props: [{
+					element: content,
+					// 绑定width属性,设置其宽度值
+					property: 'transform.translateX',
+					expression
+				}]
+			}, (res) => {
+				this.moving = false
+				if (res.state === 'end' || res.state === 'exit') {
+					const deltaX = res.deltaX
+					if(deltaX <= -this.buttonsWidth || deltaX >= 0) {
+						// 如果触摸滑动的过程中,大于单元格的总宽度,或者大于0,意味着已经动过滑动达到了打开或者关闭的状态
+						// 这里直接进行状态的标记
+						this.$nextTick(() => {
+							this.status = deltaX <= -this.buttonsWidth ? 'open' : 'close'
+						})
+					} else if(Math.abs(deltaX) > uni.$u.getPx(this.threshold)) {
+						// 在移动大于阈值、并且小于总按钮宽度时,进行自动打开或者关闭
+						// 移动距离大于0时,意味着需要关闭状态
+						if(Math.abs(deltaX) < this.buttonsWidth) {
+							this.moveCellByAnimation(deltaX > 0 ? 'close' : 'open')
+						}
+					} else {
+						// 在小于阈值时,进行关闭操作(如果在打开状态下,将不会执行bindingX)
+						this.moveCellByAnimation('close')
+					}
+				}
+			})
+		},
+		// 释放bindingX
+		unbindBindingX() {
+			// 释放上一次的资源
+			if (this?.panEvent?.token != 0) {
+				bindingX.unbind({
+					token: this.panEvent?.token,
+					// pan为手势事件
+					eventType: 'pan'
+				})
+			}
+		},
+		// 查询按钮节点信息
+		queryRect() {
+			// 历遍所有按钮数组,通过getRectByDom返回一个promise
+			const promiseAll = this.options.map((item, index) => {
+				return this.getRectByDom(this.$refs[`u-swipe-action-item__right__button-${index}`][0])
+			})
+			// 通过promise.all方法,让所有按钮的查询结果返回一个数组的形式
+			Promise.all(promiseAll).then(sizes => {
+				this.buttons = sizes
+				// 计算所有按钮总宽度
+				this.buttonsWidth = sizes.reduce((sum, cur) => sum + cur.width, 0)
+			})
+		},
+		// 通过nvue的dom模块,查询节点信息
+		getRectByDom(ref) {
+			return new Promise(resolve => {
+				dom.getComponentRect(ref, res => {
+					resolve(res.size)
+				})
+			}) 
+		},
+		// 移动单元格到左边或者右边尽头
+		moveCellByAnimation(status = 'open') {
+			if(this.moving) return
+			// 标识当前状态
+			this.moveing = true
+			const content = this.getContentRef()
+			const x = status === 'open' ? -this.buttonsWidth : 0 
+			animation.transition(content, {
+				styles: {
+					transform: `translateX(${x}px)`,
+				},
+				duration: uni.$u.getDuration(this.duration, false),
+				timingFunction: 'ease-in-out'
+			}, () => {
+				this.moving = false
+				this.status = status
+				this.unbindBindingX()
+			})
+		},
+		// 获取元素ref
+		getContentRef() {
+			return this.$refs['u-swipe-action-item__content'].ref
+		},
+		beforeDestroy() {
+			this.unbindBindingX()
+		}
+	}
+}

+ 41 - 0
plugins/uview-ui/components/u-swipe-action-item/props.js

@@ -0,0 +1,41 @@
+export default {
+    props: {
+        // 控制打开或者关闭
+        show: {
+            type: Boolean,
+            default: uni.$u.props.swipeActionItem.show
+        },
+        // 标识符,如果是v-for,可用index索引值
+        name: {
+            type: [String, Number],
+            default: uni.$u.props.swipeActionItem.name
+        },
+        // 是否禁用
+        disabled: {
+            type: Boolean,
+            default: uni.$u.props.swipeActionItem.disabled
+        },
+        // 是否自动关闭其他swipe按钮组
+        autoClose: {
+            type: Boolean,
+            default: uni.$u.props.swipeActionItem.autoClose
+        },
+        // 滑动距离阈值,只有大于此值,才被认为是要打开菜单
+        threshold: {
+            type: Number,
+            default: uni.$u.props.swipeActionItem.threshold
+        },
+        // 右侧按钮内容
+        options: {
+            type: Array,
+            default() {
+                return uni.$u.props.swipeActionItem.rightOptions
+            }
+        },
+        // 动画过渡时间,单位ms
+        duration: {
+            type: [String, Number],
+            default: uni.$u.props.swipeActionItem.duration
+        }
+    }
+}

+ 190 - 0
plugins/uview-ui/components/u-swipe-action-item/u-swipe-action-item.vue

@@ -0,0 +1,190 @@
+<template>
+	<view class="u-swipe-action-item" ref="u-swipe-action-item">
+		<view class="u-swipe-action-item__right">
+			<slot name="button">
+				<view v-for="(item,index) in options" :key="index" class="u-swipe-action-item__right__button"
+					:ref="`u-swipe-action-item__right__button-${index}`" :style="[{
+						alignItems: item.style && item.style.borderRadius ? 'center' : 'stretch'
+					}]" @tap="buttonClickHandler(item, index)">
+					<view class="u-swipe-action-item__right__button__wrapper" :style="[{
+							backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD',
+							borderRadius: item.style && item.style.borderRadius ? item.style.borderRadius : '0',
+							padding: item.style && item.style.borderRadius ? '0' : '0 15px',
+						}, item.style]">
+						<u-icon v-if="item.icon" :name="item.icon"
+							:color="item.style && item.style.color ? item.style.color : '#ffffff'"
+							:size="item.iconSize ? $u.addUnit(item.iconSize) : item.style && item.style.fontSize ? $u.getPx(item.style.fontSize) * 1.2 : 17"
+							:customStyle="{
+								marginRight: item.text ? '2px' : 0
+							}"></u-icon>
+						<text v-if="item.text" class="u-swipe-action-item__right__button__wrapper__text u-line-1"
+							:style="[{
+								color: item.style && item.style.color ? item.style.color : '#ffffff',
+								fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px',
+								lineHeight: item.style && item.style.fontSize ? item.style.fontSize : '16px',
+							}]">{{ item.text }}</text>
+					</view>
+				</view>
+			</slot>
+		</view>
+		<!-- #ifdef APP-VUE || MP-WEIXIN || H5 || MP-QQ -->
+		<view class="u-swipe-action-item__content" @touchstart="wxs.touchstart" @touchmove="wxs.touchmove"
+			@touchend="wxs.touchend" :status="status" :change:status="wxs.statusChange" :size="size"
+			:change:size="wxs.sizeChange">
+			<!-- #endif -->
+			<!-- #ifdef APP-NVUE -->
+			<view class="u-swipe-action-item__content" ref="u-swipe-action-item__content" @panstart="onTouchstart"
+				@tap="clickHandler">
+				<!-- #endif -->
+				<slot />
+			</view>
+		</view>
+</template>
+<!-- #ifdef APP-VUE || MP-WEIXIN || H5 || MP-QQ -->
+<script src="./index.wxs" module="wxs" lang="wxs"></script>
+<!-- #endif -->
+<script>
+	import touch from '../../libs/mixin/touch.js'
+	import props from './props.js';
+	// #ifdef APP-NVUE
+	import nvue from './nvue.js';
+	// #endif
+	// #ifdef APP-VUE || MP-WEIXIN || H5 || MP-QQ
+	import wxs from './wxs.js';
+	// #endif
+	/**
+	 * SwipeActionItem 滑动单元格子组件
+	 * @description 该组件一般用于左滑唤出操作菜单的场景,用的最多的是左滑删除操作
+	 * @tutorial https://www.uviewui.com/components/swipeAction.html
+	 * @property {Boolean}			show			控制打开或者关闭(默认 false )
+	 * @property {String | Number}	index			标识符,如果是v-for,可用index索引
+	 * @property {Boolean}			disabled		是否禁用(默认 false )
+	 * @property {Boolean}			autoClose		是否自动关闭其他swipe按钮组(默认 true )
+	 * @property {Number}			threshold		滑动距离阈值,只有大于此值,才被认为是要打开菜单(默认 30 )
+	 * @property {Array}			options			右侧按钮内容
+	 * @property {String | Number}	duration		动画过渡时间,单位ms(默认 350 )
+	 * @event {Function(index)}	open	组件打开时触发
+	 * @event {Function(index)}	close	组件关闭时触发
+	 * @example	<u-swipe-action><u-swipe-action-item :options="options1" ></u-swipe-action-item></u-swipe-action>
+	 */
+	export default {
+		name: 'u-swipe-action-item',
+		mixins: [uni.$u.mpMixin, uni.$u.mixin, props, touch],
+		// #ifdef APP-NVUE
+		mixins: [uni.$u.mpMixin, uni.$u.mixin, props, nvue, touch],
+		// #endif
+		// #ifdef APP-VUE || MP-WEIXIN || H5 || MP-QQ
+		mixins: [uni.$u.mpMixin, uni.$u.mixin, props, touch, wxs],
+		// #endif
+		data() {
+			return {
+				// 按钮的尺寸信息
+				size: {},
+				// 父组件u-swipe-action的参数
+				parentData: {
+					autoClose: true,
+				},
+				// 当前状态,open-打开,close-关闭
+				status: 'close',
+			}
+		},
+		watch: {
+			// 由于wxs无法直接读取外部的值,需要在外部值变化时,重新执行赋值逻辑
+			wxsInit(newValue, oldValue) {
+				this.queryRect()
+			}
+		},
+		computed: {
+			wxsInit() {
+				return [this.disabled, this.autoClose, this.threshold, this.options, this.duration]
+			}
+		},
+		mounted() {
+			this.init()
+		},
+		methods: {
+			init() {
+				// 初始化父组件数据
+				this.updateParentData()
+				// #ifndef APP-NVUE
+				uni.$u.sleep().then(() => {
+					this.queryRect()
+				})
+				// #endif
+			},
+			updateParentData() {
+				// 此方法在mixin中
+				this.getParentData('u-swipe-action')
+			},
+			// #ifndef APP-NVUE
+			// 查询节点
+			queryRect() {
+				this.$uGetRect('.u-swipe-action-item__right__button', true).then(buttons => {
+					this.size = {
+						buttons,
+						show: this.show,
+						disabled: this.disabled,
+						threshold: this.threshold,
+						duration: this.duration
+					}
+				})
+			},
+			// #endif
+			// 按钮被点击
+			buttonClickHandler(item, index) {
+				this.$emit('click', {
+					index,
+					name: this.name
+				})
+			}
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	.u-swipe-action-item {
+		position: relative;
+		overflow: hidden;
+		/* #ifndef APP-NVUE || MP-WEIXIN */
+		touch-action: pan-y;
+		/* #endif */
+
+		&__content {
+			background-color: #FFFFFF;
+			z-index: 10;
+		}
+
+		&__right {
+			position: absolute;
+			top: 0;
+			bottom: 0;
+			right: 0;
+			@include flex;
+
+			&__button {
+				@include flex;
+				justify-content: center;
+				overflow: hidden;
+				align-items: center;
+
+				&__wrapper {
+					@include flex;
+					align-items: center;
+					justify-content: center;
+					padding: 0 15px;
+
+					&__text {
+						@include flex;
+						align-items: center;
+						color: #FFFFFF;
+						font-size: 15px;
+						text-align: center;
+						justify-content: center;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 15 - 0
plugins/uview-ui/components/u-swipe-action-item/wxs.js

@@ -0,0 +1,15 @@
+export default {
+    methods: {
+        // 关闭时执行
+        closeHandler() {
+            this.status = 'close'
+        },
+        setState(status) {
+            this.status = status
+        },
+        closeOther() {
+            // 尝试关闭其他打开的单元格
+            this.parent && this.parent.closeOther(this)
+        }
+    }
+}

+ 9 - 0
plugins/uview-ui/components/u-swipe-action/props.js

@@ -0,0 +1,9 @@
+export default {
+    props: {
+        // 是否自动关闭其他swipe按钮组
+        autoClose: {
+            type: Boolean,
+            default: uni.$u.props.swipeAction.autoClose
+        }
+    }
+}

+ 67 - 0
plugins/uview-ui/components/u-swipe-action/u-swipe-action.vue

@@ -0,0 +1,67 @@
+<template>
+	<view class="u-swipe-action">
+		<slot></slot>
+	</view>
+</template>
+
+<script>
+	import props from './props.js';
+	/**
+	 * SwipeAction 滑动单元格 
+	 * @description 该组件一般用于左滑唤出操作菜单的场景,用的最多的是左滑删除操作
+	 * @tutorial https://www.uviewui.com/components/swipeAction.html
+	 * @property {Boolean}	autoClose	是否自动关闭其他swipe按钮组
+	 * @event {Function(index)}	click	点击组件时触发
+	 * @example	<u-swipe-action><u-swipe-action-item :rightOptions="options1" ></u-swipe-action-item></u-swipe-action>
+	 */
+	export default {
+		name: 'u-swipe-action',
+		mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
+		data() {
+			return {}
+		},
+		provide() {
+			return {
+				swipeAction: this
+			}
+		},
+		computed: {
+			// 这里computed的变量,都是子组件u-swipe-action-item需要用到的,由于头条小程序的兼容性差异,子组件无法实时监听父组件参数的变化
+			// 所以需要手动通知子组件,这里返回一个parentData变量,供watch监听,在其中去通知每一个子组件重新从父组件(u-swipe-action-item)
+			// 拉取父组件新的变化后的参数
+			parentData() {
+				return [this.autoClose]
+			}
+		},
+		watch: {
+			// 当父组件需要子组件需要共享的参数发生了变化,手动通知子组件
+			parentData() {
+				if (this.children.length) {
+					this.children.map(child => {
+						// 判断子组件(u-swipe-action-item)如果有updateParentData方法的话,就就执行(执行的结果是子组件重新从父组件拉取了最新的值)
+						typeof(child.updateParentData) === 'function' && child.updateParentData()
+					})
+				}
+			},
+		},
+		created() {
+			this.children = []
+		},
+		methods: {
+			closeOther(child) {
+				if (this.autoClose) {
+					// 历遍所有的单元格,找出非当前操作中的单元格,进行关闭
+					this.children.map((item, index) => {
+						if (child !== item) {
+							item.closeHandler()
+						}
+					})
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 29 - 0
plugins/uview-ui/components/u-swiper-indicator/props.js

@@ -0,0 +1,29 @@
+export default {
+    props: {
+        // 轮播的长度
+        length: {
+            type: [String, Number],
+            default: uni.$u.props.swiperIndicator.length
+        },
+        // 当前处于活动状态的轮播的索引
+        current: {
+            type: [String, Number],
+            default: uni.$u.props.swiperIndicator.current
+        },
+        // 指示器非激活颜色
+        indicatorActiveColor: {
+            type: String,
+            default: uni.$u.props.swiperIndicator.indicatorActiveColor
+        },
+        // 指示器的激活颜色
+        indicatorInactiveColor: {
+            type: String,
+            default: uni.$u.props.swiperIndicator.indicatorInactiveColor
+        },
+		// 指示器模式,line-线型,dot-点型
+		indicatorMode: {
+		    type: String,
+		    default: uni.$u.props.swiperIndicator.indicatorMode
+		}
+    }
+}

+ 110 - 0
plugins/uview-ui/components/u-swiper-indicator/u-swiper-indicator.vue

@@ -0,0 +1,110 @@
+<template>
+	<view class="u-swiper-indicator">
+		<view
+			class="u-swiper-indicator__wrapper"
+			v-if="indicatorMode === 'line'"
+			:class="[`u-swiper-indicator__wrapper--${indicatorMode}`]"
+			:style="{
+				width: $u.addUnit(lineWidth * length),
+				backgroundColor: indicatorInactiveColor
+			}"
+		>
+			<view
+				class="u-swiper-indicator__wrapper--line__bar"
+				:style="[lineStyle]"
+			></view>
+		</view>
+		<view
+			class="u-swiper-indicator__wrapper"
+			v-if="indicatorMode === 'dot'"
+		>
+			<view
+				class="u-swiper-indicator__wrapper__dot"
+				v-for="(item, index) in length"
+				:key="index"
+				:class="[index === current && 'u-swiper-indicator__wrapper__dot--active']"
+				:style="[dotStyle(index)]"
+			>
+
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import props from './props.js';
+	/**
+	 * SwiperIndicator 轮播图指示器
+	 * @description 该组件一般用于导航轮播,广告展示等场景,可开箱即用,
+	 * @tutorial https://www.uviewui.com/components/swiper.html
+	 * @property {String | Number}	length					轮播的长度(默认 0 )
+	 * @property {String | Number}	current					当前处于活动状态的轮播的索引(默认 0 )
+	 * @property {String}			indicatorActiveColor	指示器非激活颜色
+	 * @property {String}			indicatorInactiveColor	指示器的激活颜色
+	 * @property {String}			indicatorMode			指示器模式(默认 'line' )
+	 * @example	<u-swiper :list="list4" indicator keyName="url" :autoplay="false"></u-swiper>
+	 */
+	export default {
+		name: 'u-swiper-indicator',
+		mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
+		data() {
+			return {
+				lineWidth: 22
+			}
+		},
+		computed: {
+			// 指示器为线型的样式
+			lineStyle() {
+				let style = {}
+				style.width = uni.$u.addUnit(this.lineWidth)
+				style.transform = `translateX(${ uni.$u.addUnit(this.current * this.lineWidth) })`
+				style.backgroundColor = this.indicatorActiveColor
+				return style
+			},
+			// 指示器为点型的样式
+			dotStyle() {
+				return index => {
+					let style = {}
+					style.backgroundColor = index === this.current ? this.indicatorActiveColor : this.indicatorInactiveColor
+					return style
+				}
+			}
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	.u-swiper-indicator {
+
+		&__wrapper {
+			@include flex;
+
+			&--line {
+				border-radius: 100px;
+				height: 4px;
+
+				&__bar {
+					width: 22px;
+					height: 4px;
+					border-radius: 100px;
+					background-color: #FFFFFF;
+					transition: transform 0.3s;
+				}
+			}
+
+			&__dot {
+				width: 5px;
+				height: 5px;
+				border-radius: 100px;
+				margin: 0 4px;
+
+				&--active {
+					width: 12px;
+				}
+			}
+
+		}
+	}
+</style>

+ 125 - 0
plugins/uview-ui/components/u-swiper/props.js

@@ -0,0 +1,125 @@
+export default {
+    props: {
+        // 列表数组,元素可为字符串,如为对象可通过keyName指定目标属性名
+        list: {
+            type: Array,
+            default: uni.$u.props.swiper.list
+        },
+        // 是否显示面板指示器
+        indicator: {
+            type: Boolean,
+            default: uni.$u.props.swiper.indicator
+        },
+        // 指示器非激活颜色
+        indicatorActiveColor: {
+            type: String,
+            default: uni.$u.props.swiper.indicatorActiveColor
+        },
+        // 指示器的激活颜色
+        indicatorInactiveColor: {
+            type: String,
+            default: uni.$u.props.swiper.indicatorInactiveColor
+        },
+        // 指示器样式,可通过bottom,left,right进行定位
+        indicatorStyle: {
+            type: [String, Object],
+            default: uni.$u.props.swiper.indicatorStyle
+        },
+        // 指示器模式,line-线型,dot-点型
+        indicatorMode: {
+            type: String,
+            default: uni.$u.props.swiper.indicatorMode
+        },
+        // 是否自动切换
+        autoplay: {
+            type: Boolean,
+            default: uni.$u.props.swiper.autoplay
+        },
+        // 当前所在滑块的 index
+        current: {
+            type: [String, Number],
+            default: uni.$u.props.swiper.current
+        },
+        // 当前所在滑块的 item-id ,不能与 current 被同时指定
+        currentItemId: {
+            type: String,
+            default: uni.$u.props.swiper.currentItemId
+        },
+        // 滑块自动切换时间间隔
+        interval: {
+            type: [String, Number],
+            default: uni.$u.props.swiper.interval
+        },
+        // 滑块切换过程所需时间
+        duration: {
+            type: [String, Number],
+            default: uni.$u.props.swiper.duration
+        },
+        // 播放到末尾后是否重新回到开头
+        circular: {
+            type: Boolean,
+            default: uni.$u.props.swiper.circular
+        },
+        // 前边距,可用于露出前一项的一小部分,nvue和支付宝不支持
+        previousMargin: {
+            type: [String, Number],
+            default: uni.$u.props.swiper.previousMargin
+        },
+        // 后边距,可用于露出后一项的一小部分,nvue和支付宝不支持
+        nextMargin: {
+            type: [String, Number],
+            default: uni.$u.props.swiper.nextMargin
+        },
+        // 当开启时,会根据滑动速度,连续滑动多屏,支付宝不支持
+        acceleration: {
+            type: Boolean,
+            default: uni.$u.props.swiper.acceleration
+        },
+        // 同时显示的滑块数量,nvue、支付宝小程序不支持
+        displayMultipleItems: {
+            type: Number,
+            default: uni.$u.props.swiper.displayMultipleItems
+        },
+        // 指定swiper切换缓动动画类型,有效值:default、linear、easeInCubic、easeOutCubic、easeInOutCubic
+        // 只对微信小程序有效
+        easingFunction: {
+            type: String,
+            default: uni.$u.props.swiper.easingFunction
+        },
+        // list数组中指定对象的目标属性名
+        keyName: {
+            type: String,
+            default: uni.$u.props.swiper.keyName
+        },
+        // 图片的裁剪模式
+        imgMode: {
+            type: String,
+            default: uni.$u.props.swiper.imgMode
+        },
+        // 组件高度
+        height: {
+            type: [String, Number],
+            default: uni.$u.props.swiper.height
+        },
+        // 背景颜色
+        bgColor: {
+            type: String,
+            default: uni.$u.props.swiper.bgColor
+        },
+        // 组件圆角,数值或带单位的字符串
+        radius: {
+            type: [String, Number],
+            default: uni.$u.props.swiper.radius
+        },
+        // 是否加载中
+        loading: {
+            type: Boolean,
+            default: uni.$u.props.swiper.loading
+        },
+        // 是否显示标题,要求数组对象中有title属性
+        showTitle: {
+            type: Boolean,
+            default: uni.$u.props.swiper.showTitle
+        }
+    }
+}

+ 255 - 0
plugins/uview-ui/components/u-swiper/u-swiper.vue

@@ -0,0 +1,255 @@
+<template>
+	<view
+		class="u-swiper"
+		:style="{
+			backgroundColor: bgColor,
+			height: $u.addUnit(height),
+			borderRadius: $u.addUnit(radius)
+		}"
+	>
+		<view
+			class="u-swiper__loading"
+			v-if="loading"
+		>
+			<u-loading-icon mode="circle"></u-loading-icon>
+		</view>
+		<swiper
+			v-else
+			class="u-swiper__wrapper"
+			:style="{
+				height: $u.addUnit(height),
+			}"
+			@change="change"
+			:circular="circular"
+			:interval="interval"
+			:duration="duration"
+			:autoplay="autoplay"
+			:current="current"
+			:currentItemId="currentItemId"
+			:previousMargin="$u.addUnit(previousMargin)"
+			:nextMargin="$u.addUnit(nextMargin)"
+			:acceleration="acceleration"
+			:displayMultipleItems="displayMultipleItems"
+			:easingFunction="easingFunction"
+		>
+			<swiper-item
+				class="u-swiper__wrapper__item"
+				v-for="(item, index) in list"
+				:key="index"
+			>
+				<view
+					class="u-swiper__wrapper__item__wrapper"
+					:style="[itemStyle(index)]"
+				>
+					<!-- 在nvue中,image图片的宽度默认为屏幕宽度,需要通过flex:1撑开,另外必须设置高度才能显示图片 -->
+					<image
+						class="u-swiper__wrapper__item__wrapper__image"
+						v-if="getItemType(item) === 'image'"
+						:src="getSource(item)"
+						:mode="imgMode"
+						@tap="clickHandler(index)"
+						:style="{
+							height: $u.addUnit(height),
+							borderRadius: $u.addUnit(radius)
+						}"
+					></image>
+					<video
+						class="u-swiper__wrapper__item__wrapper__video"
+						v-if="getItemType(item) === 'video'"
+						:id="`video-${index}`"
+						:enable-progress-gesture="false"
+						:src="getSource(item)"
+						:poster="getPoster(item)"
+						:title="showTitle && $u.test.object(item) && item.title ? item.title : ''"
+						:style="{
+							height: $u.addUnit(height)
+						}"
+						controls
+						@tap="clickHandler(index)"
+					></video>
+					<text
+						v-if="showTitle && $u.test.object(item) && item.title && $u.test.image(getSource(item))"
+						class="u-swiper__wrapper__item__wrapper__title u-line-1"
+					>{{ item.title }}</text>
+				</view>
+			</swiper-item>
+		</swiper>
+		<view class="u-swiper__indicator" :style="[$u.addStyle(indicatorStyle)]">
+			<slot name="indicator">
+				<u-swiper-indicator
+					v-if="!loading && indicator && !showTitle"
+					:indicatorActiveColor="indicatorActiveColor"
+					:indicatorInactiveColor="indicatorInactiveColor"
+					:length="list.length"
+					:current="currentIndex"
+					:indicatorMode="indicatorMode"
+				></u-swiper-indicator>
+			</slot>
+		</view>
+	</view>
+</template>
+
+<script>
+	import props from './props.js';
+	/**
+	 * Swiper 轮播图
+	 * @description 该组件一般用于导航轮播,广告展示等场景,可开箱即用,
+	 * @tutorial https://www.uviewui.com/components/swiper.html
+	 * @property {Array}			list					轮播图数据
+	 * @property {Boolean}			indicator				是否显示面板指示器(默认 false )
+	 * @property {String}			indicatorActiveColor	指示器非激活颜色(默认 '#FFFFFF' )
+	 * @property {String}			indicatorInactiveColor	指示器的激活颜色(默认 'rgba(255, 255, 255, 0.35)' )
+	 * @property {String | Object}	indicatorStyle			指示器样式,可通过bottom,left,right进行定位
+	 * @property {String}			indicatorMode			指示器模式(默认 'line' )
+	 * @property {Boolean}			autoplay				是否自动切换(默认 true )
+	 * @property {String | Number}	current					当前所在滑块的 index(默认 0 )
+	 * @property {String}			currentItemId			当前所在滑块的 item-id ,不能与 current 被同时指定
+	 * @property {String | Number}	interval				滑块自动切换时间间隔(ms)(默认 3000 )
+	 * @property {String | Number}	duration				滑块切换过程所需时间(ms)(默认 300 )
+	 * @property {Boolean}			circular				播放到末尾后是否重新回到开头(默认 false )
+	 * @property {String | Number}	previousMargin			前边距,可用于露出前一项的一小部分,nvue和支付宝不支持(默认 0 )
+	 * @property {String | Number}	nextMargin				后边距,可用于露出后一项的一小部分,nvue和支付宝不支持(默认 0 )
+	 * @property {Boolean}			acceleration			当开启时,会根据滑动速度,连续滑动多屏,支付宝不支持(默认 false )
+	 * @property {Number}			displayMultipleItems	同时显示的滑块数量,nvue、支付宝小程序不支持(默认 1 )
+	 * @property {String}			easingFunction			指定swiper切换缓动动画类型, 只对微信小程序有效(默认 'default' )
+	 * @property {String}			keyName					list数组中指定对象的目标属性名(默认 'url' )
+	 * @property {String}			imgMode					图片的裁剪模式(默认 'aspectFill' )
+	 * @property {String | Number}	height					组件高度(默认 130 )
+	 * @property {String}			bgColor					背景颜色(默认 	'#f3f4f6' )
+	 * @property {String | Number}	radius					组件圆角,数值或带单位的字符串(默认 4 )
+	 * @property {Boolean}			loading					是否加载中(默认 false )
+	 * @property {Boolean}			showTitle				是否显示标题,要求数组对象中有title属性(默认 false )
+	 * @event {Function(index)}	click	点击轮播图时触发	index:点击了第几张图片,从0开始
+	 * @event {Function(index)}	change	轮播图切换时触发(自动或者手动切换)	index:切换到了第几张图片,从0开始
+	 * @example	<u-swiper :list="list4" keyName="url" :autoplay="false"></u-swiper>
+	 */
+	export default {
+		name: 'u-swiper',
+		mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
+		data() {
+			return {
+				currentIndex: 0
+			}
+		},
+		watch: {
+			current(val, preVal) {
+				if(val === preVal) return;
+				this.currentIndex = val; // 和上游数据关联上
+			}
+		},
+		computed: {
+			itemStyle() {
+				return index => {
+					const style = {}
+					// #ifndef APP-NVUE || MP-TOUTIAO
+					// 左右流出空间的写法不支持nvue和头条
+					// 只有配置了此二值,才加上对应的圆角,以及缩放
+					if (this.nextMargin && this.previousMargin) {
+						style.borderRadius = uni.$u.addUnit(this.radius)
+						if (index !== this.currentIndex) style.transform = 'scale(0.92)'
+					}
+					// #endif
+					return style
+				}
+			}
+		},
+		methods: {
+      getItemType(item) {
+        if (typeof item === 'string') return uni.$u.test.video(this.getSource(item)) ? 'video' : 'image'
+        if (typeof item === 'object' && this.keyName) {
+          if (!item.type) return uni.$u.test.video(this.getSource(item)) ? 'video' : 'image'
+          if (item.type === 'image') return 'image'
+          if (item.type === 'video') return 'video'
+          return 'image'
+        }
+      },
+			// 获取目标路径,可能数组中为字符串,对象的形式,额外可指定对象的目标属性名keyName
+			getSource(item) {
+				if (typeof item === 'string') return item
+				if (typeof item === 'object' && this.keyName) return item[this.keyName]
+				else uni.$u.error('请按格式传递列表参数')
+				return ''
+			},
+			// 轮播切换事件
+			change(e) {
+				// 当前的激活索引
+				const {
+					current
+				} = e.detail
+				this.pauseVideo(this.currentIndex)
+				this.currentIndex = current
+				this.$emit('change', e.detail)
+			},
+			// 切换轮播时,暂停视频播放
+			pauseVideo(index) {
+				const lastItem = this.getSource(this.list[index])
+				if (uni.$u.test.video(lastItem)) {
+					// 当视频隐藏时,暂停播放
+					const video = uni.createVideoContext(`video-${index}`, this)
+					video.pause()
+				}
+			},
+			// 当一个轮播item为视频时,获取它的视频海报
+			getPoster(item) {
+				return typeof item === 'object' && item.poster ? item.poster : ''
+			},
+			// 点击某个item
+			clickHandler(index) {
+				this.$emit('click', index)
+			}
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import "../../libs/css/components.scss";
+
+	.u-swiper {
+		@include flex;
+		justify-content: center;
+		align-items: center;
+		position: relative;
+		overflow: hidden;
+
+		&__wrapper {
+			flex: 1;
+
+			&__item {
+				flex: 1;
+
+				&__wrapper {
+					@include flex;
+					position: relative;
+					overflow: hidden;
+					transition: transform 0.3s;
+					flex: 1;
+
+					&__image {
+						flex: 1;
+					}
+
+					&__video {
+						flex: 1;
+					}
+
+					&__title {
+						position: absolute;
+						background-color: rgba(0, 0, 0, 0.3);
+						bottom: 0;
+						left: 0;
+						right: 0;
+						font-size: 28rpx;
+						padding: 12rpx 24rpx;
+						color: #FFFFFF;
+						flex: 1;
+					}
+				}
+			}
+		}
+
+		&__indicator {
+			position: absolute;
+			bottom: 10px;
+		}
+	}
+</style>

+ 115 - 139
request/index.js

@@ -1,154 +1,130 @@
 import {
-    baseUrl
+	baseUrl
 } from "@/config"
 import {
-    wipeNulish
+	wipeNulish
 } from '@/utils/utils.js'
 
-import {getWxUserInfo} from "../utils/wxAuth";
+import {
+	getWxUserInfo
+} from "../utils/wxAuth";
 
 function requestHandle(obj) {
-    const reqData = wipeNulish(obj.data) || {}
-    const token = uni.getStorageSync('token') || 'no-login'
-
-    let base = baseUrl
-    const BASE_URL = obj.url.indexOf("http") > -1 || obj.url.indexOf("https://") > -1 ? obj.url : base + obj.url;
-    return new Promise((resolve, reject) => {
-        uni.request({
-            method: obj.method,
-            url: BASE_URL,
-            data: reqData,
-            header: {
-                token,
-            },
-            dataType: "json"
-        }).then(response => {
-            const {
-                data: {
-                    data,
-                    code,
-                    msg
-                }
-            } = response || {}
-            if (code === 10021) {
-                getWxUserInfo()
-                return
-            }
-            if (code === 0) {
-                if (msg) {
-                    uni.showToast({
-                        title: msg,
-                        icon: 'success'
-                    })
-                }
-
-            } else {
-
-                resolve(data)
-            }
+	const reqData = wipeNulish(obj.data) || {}
+	const token = uni.getStorageSync('token') || 'no-login'
 
-            if (code !== 0) {
-                uni.showToast({
-                    title: msg || '服务器开小差',
-                    icon: 'none'
-                })
-            } else {
+	let base = baseUrl
+	const BASE_URL = obj.url.indexOf("http") > -1 || obj.url.indexOf("https://") > -1 ? obj.url : base + obj.url;
+	return new Promise((resolve, reject) => {
+		uni.request({
+			method: obj.method,
+			url: BASE_URL,
+			data: reqData,
+			header: {
+				token,
+			},
+			success: (resReq) => {
+				// 判断请求状态码是否为 200
+				if (resReq.statusCode === 200) {
+					// 状态码为 200 时,将响应数据传递给 resolve 函数
+					if (resReq.data.code === 0 && resReq.data.msg) {
+						uni.showToast({
+							title: resReq.data.msg,
+							icon: 'success'
+						})
 
-                resolve(data)
-            }
-        }).catch(error => {
-            /**获取网络状态 */
-            uni.getNetworkType({
-                success: function (res) {
-                    if (res.networkType === 'none') {
-                        uni.showToast({
-                            title: '网络未连接,请检查网络状态',
-                            mask: false,
-                            icon: 'none'
-                        })
-                    } else {
-                        uni.showToast({
-                            title: '网络连接超时',
-                            icon: 'none'
-                        })
-                    }
-                }
-            })
-            reject(error)
-        })
-    })
+					}
+					if (resReq.data.code === 500) {
+						uni.showToast({
+							title: msg || '服务器开小差',
+							icon: 'none'
+						})
+					}
+					if (resReq.data.code === 10021) {
+						uni.removeStorageSync('token')
+						getWxUserInfo()
+						return
+					}
+					resolve(resReq.data.data);
+				} else {
+					// 状态码不为 200 时,将错误信息传递给 reject 函数
+					reject(new Error(`Request failed with status code ${resReq.statusCode}`));
+				}
+			},
+		})
+	})
 }
 
 // customRes 用于自定义反参状态处理,凡是未加此参数 都当作反参code是0处理
 const request = {
-    get: (obj) => {
-        return new Promise((resolve, reject) => {
-            requestHandle({
-                method: 'GET',
-                url: obj.url,
-                data: obj.data,
-                header: obj.header,
-                customRes: obj.customRes
-            })
-                .then((res) => {
-                    resolve(res)
-                })
-                .catch((err) => {
-                    reject(err)
-                })
-        });
-    },
-    post: (obj) => {
-        return new Promise((resolve, reject) => {
-            requestHandle({
-                method: 'POST',
-                url: obj.url,
-                data: obj.data,
-                header: obj.header,
-                customRes: obj.customRes
-            })
-                .then((res) => {
-                    resolve(res)
-                })
-                .catch((err) => {
-                    reject(err)
-                })
-        })
-    },
-    put: (obj) => {
-        return new Promise((resolve, reject) => {
-            requestHandle({
-                method: 'PUT',
-                url: obj.url,
-                data: obj.data,
-                header: obj.header,
-                customRes: obj.customRes
-            })
-                .then((res) => {
-                    resolve(res)
-                })
-                .catch((err) => {
-                    reject(err)
-                })
-        })
-    },
-    del: (obj) => {
-        return new Promise((resolve, reject) => {
-            requestHandle({
-                method: 'DELETE',
-                url: obj.url,
-                data: obj.data,
-                header: obj.header,
-                customRes: obj.customRes
-            })
-                .then((res) => {
-                    resolve(res)
-                })
-                .catch((err) => {
-                    reject(err)
-                })
-        })
-    }
+	get: (obj) => {
+		return new Promise((resolve, reject) => {
+			requestHandle({
+					method: 'GET',
+					url: obj.url,
+					data: obj.data,
+					header: obj.header,
+					customRes: obj.customRes
+				})
+				.then((res) => {
+					resolve(res)
+				})
+				.catch((err) => {
+					reject(err)
+				})
+		});
+	},
+	post: (obj) => {
+		return new Promise((resolve, reject) => {
+			requestHandle({
+					method: 'POST',
+					url: obj.url,
+					data: obj.data,
+					header: obj.header,
+					customRes: obj.customRes
+				})
+				.then((res) => {
+					resolve(res)
+				})
+				.catch((err) => {
+					reject(err)
+				})
+		})
+	},
+	put: (obj) => {
+		return new Promise((resolve, reject) => {
+			requestHandle({
+					method: 'PUT',
+					url: obj.url,
+					data: obj.data,
+					header: obj.header,
+					customRes: obj.customRes
+				})
+				.then((res) => {
+					resolve(res)
+				})
+				.catch((err) => {
+					reject(err)
+				})
+		})
+	},
+	del: (obj) => {
+		return new Promise((resolve, reject) => {
+			requestHandle({
+					method: 'DELETE',
+					url: obj.url,
+					data: obj.data,
+					header: obj.header,
+					customRes: obj.customRes
+				})
+				.then((res) => {
+					resolve(res)
+				})
+				.catch((err) => {
+					reject(err)
+				})
+		})
+	}
 }
 
-export default request
+export default request

+ 26 - 1
uni.scss

@@ -377,4 +377,29 @@ $color-orange: #FF6234;
 }
  .mt-4{
    margin-top: 8rpx;
- }
+ }
+.btn {
+  font-weight: 500;
+  font-size: 28rpx;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-radius: 36rpx;
+}
+
+.btn1 {
+  color: #0D121A;
+  border: 3rpx solid #E2E4EB;
+}
+
+.btn2 {
+  margin-left: 16rpx;
+  background: #FF7441;
+  color: #FFF;
+}
+.cancel {
+  color: #9EA3B1 !important;
+}
+.btn-theme{
+  background: linear-gradient(144deg, #00C1FF 0%, #2FFFFC 100%);
+}

+ 17 - 40
utils/filter.js

@@ -1,45 +1,22 @@
 // 定义全局过滤器
 
-const filters={
-    // orderStatus:function (value){
-    //     const orderStatusList = [
-    //         {
-    //             name: '待付款',
-    //             value: 0
-    //         }, {
-    //             name: '待接单',
-    //             value: 1
-    //         },
-    //         {
-    //             name: '拍摄中',
-    //             value: 2
-    //         },
-    //         {
-    //             name: '待验收',
-    //             value: 3
-    //         },
-    //         {
-    //             name: '已交付',
-    //             value: 4
-    //         },
-    //         {
-    //             name: '售后中',
-    //             value: 5
-    //         },
-    //         {
-    //             name: '已取消',
-    //             value: 6
-    //         },
-    //         {
-    //             name: '已退款',
-    //             value: 7
-    //         }
-    //     ]
-    //
-    //     const orderItem = orderStatusList.find(item => item.value === value)
-    //
-    //     return orderItem? orderItem.name: ''
-    // }
+const filters = {
+    orderStatus: function (value) {
+        const orderStatusList = [{name: '待付款', value: 'WAIT_PAYMENT'},
+            {name: '待接单', value: 'WAIT_ORDER'},
+            {name: '待寄样', value: 'WAIT_SEND'},
+            {name: '拍摄中', value: 'SHOOTING'},
+            {name: '待验收', value: 'WAIT_CHECK'},
+            {name: '已交付', value: 'DELIVERED'},
+            {name: '售后中', value: 'AFTER_SALE'},
+            {name: '已取消', value: 'CANCELLED'},
+            {name: '已退款', value: 'REFUNDED'}
+        ]
+
+        const orderItem = orderStatusList.find(item => item.value === value)
+
+        return orderItem ? orderItem.name : ''
+    }
 }
 export default filters
 

+ 10 - 18
utils/preCheck.js

@@ -1,18 +1,10 @@
-// import platform from './platform.js'
-// import {getWxUserInfo} from "./wxAuth";
-// if (platform() === 'H5') {
-//     const getWxQuery = (name) => {
-//         const url = window.location.href;
-//         const reg = /code=([^&]*)/;
-//         const arr = url.match(reg);
-//         return arr ? arr[1] : null;
-//     }
-//     const code = getWxQuery('code')
-//     console.log('code',code)
-//     if(!code){
-//         if (!uni.getStorageSync('token')) {
-//             console.log('preCheck-login')
-//             getWxUserInfo()
-//         }
-//     }
-// }
+import platform from './platform.js'
+import {
+	getWxUserInfo
+} from "./wxAuth";
+if (platform() === 'H5') {
+	if (!uni.getStorageSync('token')) {
+		console.log('preCheck-login')
+		getWxUserInfo()
+	}
+}

+ 2 - 2
utils/utils.js

@@ -27,8 +27,8 @@ export let debounce = (func, wait, immediate) => {
     };
 };
 export const goHome = () => {
-    uni.redirectTo({
-        url: '/page/index/index'
+    uni.switchTab({
+        url: '/pages/index/index'
     })
     return
 }

+ 2 - 1
utils/wxAuth.js

@@ -35,8 +35,9 @@ export async function getWxUserInfo() {
         return null;
     }
     loginWx({
-        code: code
+        code
     }).then(res => {
+		console.log('code',res);
         uni.setStorageSync('token', res.token)
         uni.setStorageSync('realName',res.realName)
         return res