baijunde 1 年之前
父节点
当前提交
7b7b14209e
共有 70 个文件被更改,包括 5875 次插入 和 2123 次删除
  1. 8 0
      common/router/modules/routes.js
  2. 3 2
      common/service/config.service.js
  3. 1 0
      components/dengrq-datetime-picker/dateSelector/index.css
  4. 106 1
      components/dengrq-datetime-picker/dateSelector/index.js
  5. 32 0
      components/dengrq-datetime-picker/dateSelector/index.vue
  6. 0 4
      node_modules/@amap/amap-jsapi-loader/.vscode/settings.json
  7. 0 111
      node_modules/@amap/amap-jsapi-loader/README.md
  8. 0 11
      node_modules/@amap/amap-jsapi-loader/dist/index.js
  9. 0 69
      node_modules/@amap/amap-jsapi-loader/example/AMapUI.html
  10. 0 76
      node_modules/@amap/amap-jsapi-loader/example/Loca.html
  11. 0 94
      node_modules/@amap/amap-jsapi-loader/example/Multi.html
  12. 0 36
      node_modules/@amap/amap-jsapi-loader/example/example-es6.html
  13. 0 46
      node_modules/@amap/amap-jsapi-loader/example/example.html
  14. 0 0
      node_modules/@amap/amap-jsapi-loader/example/libs/chai.min.js
  15. 0 0
      node_modules/@amap/amap-jsapi-loader/example/libs/mocha.min.css
  16. 0 0
      node_modules/@amap/amap-jsapi-loader/example/libs/mocha.min.js
  17. 0 287
      node_modules/@amap/amap-jsapi-loader/example/test.html
  18. 0 15
      node_modules/@amap/amap-jsapi-loader/index.d.ts
  19. 0 37
      node_modules/@amap/amap-jsapi-loader/package.json
  20. 0 25
      node_modules/@amap/amap-jsapi-loader/rollup.config.js
  21. 0 5
      node_modules/@amap/amap-jsapi-loader/src/global.d.ts
  22. 0 367
      node_modules/@amap/amap-jsapi-loader/src/index.ts
  23. 0 17
      node_modules/@amap/amap-jsapi-loader/test/demo.ts
  24. 0 7
      node_modules/@amap/amap-jsapi-loader/test/test.ts
  25. 275 0
      node_modules/mescroll-uni/README.md
  26. 55 0
      node_modules/mescroll-uni/components/mescroll-down.css
  27. 47 0
      node_modules/mescroll-uni/components/mescroll-down.vue
  28. 118 0
      node_modules/mescroll-uni/components/mescroll-empty.vue
  29. 83 0
      node_modules/mescroll-uni/components/mescroll-top.vue
  30. 47 0
      node_modules/mescroll-uni/components/mescroll-up.css
  31. 39 0
      node_modules/mescroll-uni/components/mescroll-up.vue
  32. 19 0
      node_modules/mescroll-uni/mescroll-body.css
  33. 403 0
      node_modules/mescroll-uni/mescroll-body.vue
  34. 15 0
      node_modules/mescroll-uni/mescroll-i18n.js
  35. 57 0
      node_modules/mescroll-uni/mescroll-mixins.js
  36. 64 0
      node_modules/mescroll-uni/mescroll-uni-option.js
  37. 36 0
      node_modules/mescroll-uni/mescroll-uni.css
  38. 799 0
      node_modules/mescroll-uni/mescroll-uni.js
  39. 480 0
      node_modules/mescroll-uni/mescroll-uni.vue
  40. 47 0
      node_modules/mescroll-uni/mixins/mescroll-comp.js
  41. 66 0
      node_modules/mescroll-uni/mixins/mescroll-more-item.js
  42. 74 0
      node_modules/mescroll-uni/mixins/mescroll-more.js
  43. 39 0
      node_modules/mescroll-uni/package.json
  44. 109 0
      node_modules/mescroll-uni/wxs/mixins.js
  45. 92 0
      node_modules/mescroll-uni/wxs/renderjs.js
  46. 268 0
      node_modules/mescroll-uni/wxs/wxs.wxs
  47. 10 9
      package-lock.json
  48. 4 1
      package.json
  49. 21 13
      pages.json
  50. 446 135
      pages/evaluate/evaluate.vue
  51. 15 3
      pages/index/full.vue
  52. 12 3
      pages/index/index.vue
  53. 93 28
      pages/index/shop.vue
  54. 179 119
      pages/order/details.vue
  55. 304 123
      pages/order/index.vue
  56. 132 60
      pages/product/creatProduct.vue
  57. 40 9
      pages/product/details.vue
  58. 9 0
      pages/product/group.vue
  59. 127 30
      pages/product/index.vue
  60. 9 0
      pages/product/rule.vue
  61. 9 0
      pages/product/usable.vue
  62. 214 0
      pages/scan/check.vue
  63. 223 17
      pages/scan/details.vue
  64. 161 20
      pages/scan/manual.vue
  65. 229 90
      pages/scan/record.vue
  66. 256 253
      pages/scan/scan.nvue
  67. 二进制
      static/index/remove.png
  68. 二进制
      static/scan/qrcode.png
  69. 二进制
      static/scan/screen.png
  70. 二进制
      static/scan/ticket.png

+ 8 - 0
common/router/modules/routes.js

@@ -183,6 +183,14 @@ const routes = [{
 			title: '核销记录',
 		},
 	},
+	{
+		//注意:path必须跟pages.json中的地址对应,最前面别忘了加'/'哦
+		path: '/pages/scan/check',
+		name: 'record',
+		meta: {
+			title: '核销详情',
+		},
+	},
 	{
 		//注意:path必须跟pages.json中的地址对应,最前面别忘了加'/'哦
 		path: '/pages/home/home',

+ 3 - 2
common/service/config.service.js

@@ -2,8 +2,9 @@ let BASE_URL = ''
 
 
 if (process.env.NODE_ENV == 'development') {
-	// BASE_URL = 'http://192.168.0.36:8080/jeecg-boot' // 开发环境
-    BASE_URL = 'http://192.168.0.52:8080/jeecg-boot' // 开发环境
+	// BASE_URL = 'https://test.baoxianzhanggui.com/locallive-java' // 测试环境
+	BASE_URL = 'http://192.168.0.36:8080/jeecg-boot' // 开发环境
+    // BASE_URL = 'http://192.168.0.52:8080/jeecg-boot' // 开发环境
 
 } else {
 	BASE_URL = 'http://boot.jeecg.org:8080/jeecg-boot' // 生产环境

+ 1 - 0
components/dengrq-datetime-picker/dateSelector/index.css

@@ -70,3 +70,4 @@
   font-size: 14px;
   color: #06072e;
 }
+

+ 106 - 1
components/dengrq-datetime-picker/dateSelector/index.js

@@ -12,7 +12,21 @@ export default {
       showEndDatePicker: false,
       startDate: '',
       endDate: '',
-      activeDate: 'startDate' // 正在处理哪一个日期值,startDate/endDate
+      activeDate: 'startDate' ,// 正在处理哪一个日期值,startDate/endDate
+			dateList:[{
+				name:'当日',
+				type:'day'
+			},
+			{
+				name:'本月',
+				type:'month'
+			},
+			{
+				name:'本年',
+				type:'year'
+			}],
+			currentIndex:null,
+			isYear:false
     };
   },
   props: {
@@ -21,6 +35,10 @@ export default {
       type: Number,
       default: DATE_TYPES.YMD
     },
+		isYear:{
+			type:Boolean,
+			default:false
+		},
     // 默认开始日期
     defaultStartDate: {
       type: String,
@@ -117,6 +135,93 @@ export default {
     }
   },
   methods: {
+		selectYear(item,index){
+			this.currentIndex = index
+			//今日
+			if(item.type == 'day'){
+				// 获取当前时间
+				const now = new Date();
+				
+				// 获取今日的起始时间(00:00:00)
+				const startOfDay = new Date(now);
+				startOfDay.setHours(0, 0, 0, 0); // 设置为 00:00:00.000
+				
+				// 获取今日的结束时间(23:59:59)
+				const endOfDay = new Date(now);
+				endOfDay.setHours(23, 59, 59, 999); // 设置为 23:59:59.999
+				
+				// 格式化输出
+				const formatDate = (date) => {
+				  const year = date.getFullYear();
+				  const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从 0 开始,需要 +1
+				  const day = String(date.getDate()).padStart(2, '0');
+				  const hours = String(date.getHours()).padStart(2, '0');
+				  const minutes = String(date.getMinutes()).padStart(2, '0');
+				  const seconds = String(date.getSeconds()).padStart(2, '0');
+				  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+				};
+				
+				console.log("今日起始时间:", formatDate(startOfDay)); // 例如:2023-10-05 00:00:00
+				console.log("今日结束时间:", formatDate(endOfDay));   // 例如:2023-10-05 23:59:59
+				this.startDate = formatDate(startOfDay)
+				this.endDate = formatDate(endOfDay)
+			}
+			//本月
+			else if(item.type == 'month'){
+				// 获取当前时间
+				const now = new Date();
+				
+				// 获取本月的第一天(00:00:00)
+				const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1, 0, 0, 0, 0);
+				
+				// 获取本月的最后一天(23:59:59)
+				const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, 999);
+				
+				// 格式化日期为 YYYY-MM-DD HH:mm:ss
+				const formatDate = (date) => {
+				  const year = date.getFullYear();
+				  const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从 0 开始,需要 +1
+				  const day = String(date.getDate()).padStart(2, '0');
+				  const hours = String(date.getHours()).padStart(2, '0');
+				  const minutes = String(date.getMinutes()).padStart(2, '0');
+				  const seconds = String(date.getSeconds()).padStart(2, '0');
+				  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+				};
+				
+				console.log("本月起始时间:", formatDate(startOfMonth)); // 例如:2023-10-01 00:00:00
+				console.log("本月结束时间:", formatDate(endOfMonth));   // 例如:2023-10-31 23:59:59
+				this.startDate = formatDate(startOfMonth)
+				this.endDate = formatDate(endOfMonth)
+			}
+			//今年
+			else if(item.type == 'year'){
+				// 获取当前时间
+				const now = new Date();
+				
+				// 获取今年的第一天(00:00:00)
+				const startOfYear = new Date(now.getFullYear(), 0, 1, 0, 0, 0, 0); // 月份从 0 开始,0 表示 1 月
+				
+				// 获取今年的最后一天(23:59:59)
+				const endOfYear = new Date(now.getFullYear(), 11, 31, 23, 59, 59, 999); // 11 表示 12 月
+				
+				// 格式化日期为 YYYY-MM-DD HH:mm:ss
+				const formatDate = (date) => {
+				  const year = date.getFullYear();
+				  const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从 0 开始,需要 +1
+				  const day = String(date.getDate()).padStart(2, '0');
+				  const hours = String(date.getHours()).padStart(2, '0');
+				  const minutes = String(date.getMinutes()).padStart(2, '0');
+				  const seconds = String(date.getSeconds()).padStart(2, '0');
+				  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+				};
+				
+				console.log("今年起始时间:", formatDate(startOfYear)); // 例如:2023-01-01 00:00:00
+				console.log("今年结束时间:", formatDate(endOfYear));   // 例如:2023-12-31 23:59:59
+				this.startDate = formatDate(startOfYear)
+				this.endDate = formatDate(endOfYear)
+			}
+			
+		},
     open(){
       this.$refs.popup.open()
     },

+ 32 - 0
components/dengrq-datetime-picker/dateSelector/index.vue

@@ -7,6 +7,13 @@
             <text style=" font-size: 34upx;">选择营业时段</text>
             <text style=" color: #449AFF ;"  @tap="onConfirm">确定</text>
         </view>
+				<view class="year" v-if="isYear">
+					<view class="day" :class="currentIndex == index?'active':''" v-for="(item,index) in dateList" :key="index" @click="selectYear(item,index)">
+						{{item.name}}
+					</view>
+				</view>
+			
+				
       <view class="select-date-wrapper">
         <view class="select-date" :class="{ active: activeDate == 'startDate' }" @tap="onTapStartDate">
           <view class="select-date-value" v-if="startDate">{{ startDate }}</view>
@@ -48,3 +55,28 @@
 <script src="./index.js"></script>
 
 <style lang="css" scoped src="./index.css"></style>
+<style lang="scss" scoped>
+	.year{
+		width: 100%;
+		display: flex;
+		justify-content: space-between;
+		margin-top: 24rpx;
+		margin-bottom: 24rpx;
+	}
+	.day{
+		width: 205rpx;
+		height: 50rpx;
+		border-radius: 4rpx;
+		border: 1px solid #DDDDDD;
+		font-size: 28rpx;
+		color: #999999;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+	}
+	.active{
+		background: rgba(68,154,255,0.1);
+		border: 1px solid #449AFF;
+		color: #449AFF;
+	}
+</style>

+ 0 - 4
node_modules/@amap/amap-jsapi-loader/.vscode/settings.json

@@ -1,4 +0,0 @@
-{
-  "editor.tabSize": 4,
-  "prettier.tabWidth": 4
-}

+ 0 - 111
node_modules/@amap/amap-jsapi-loader/README.md

@@ -1,111 +0,0 @@
-# amap-jsapi-loader
-amap-jsapi-loader 是高德开放平台官网提供的地图 JSAPI 的加载器,可帮助开发者快速定位、有效避免加载引用地图 JSAPI 各种错误用法。
-
-
-该加载器具有以下特性:
-* 支持以 普通JS 和 npm包 两种方式使用;
-* 有效避免错误异步加载导致的 JSAPI 资源加载不完整问题;
-* 对于加载混用多个版本 JSAPI 的错误用法给予报错处理;
-* 对于不合法加载引用 JSAPI 给予报错处理;
-* 支持指定 JSAPI 版本;
-* 支持插件加载;
-* 允许多次执行加载操作,网络资源不会重复请求,便于大型工程模块管理;
-
-
-# USAGE
-
-## AMapLoader.load方法参数说明
-```js
-AMapLoader.load({
-    "key": "",              // 申请好的Web端开发者Key,首次调用 load 时必填
-    "version": "2.0",   // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
-    "plugins": []           // 需要使用的的插件列表,如比例尺'AMap.Scale'等
-    "AMapUI": {             // 是否加载 AMapUI,缺省不加载
-        "version": '1.1',   // AMapUI 缺省 1.1
-        "plugins":[],       // 需要加载的 AMapUI ui插件
-    },
-    "Loca":{                // 是否加载 Loca, 缺省不加载
-        "version": '1.3.2'  // Loca 版本,缺省 1.3.2
-    },
-}).then(()=>{
-    window.AMap.xx;
-    window.AMapUI.xx;
-    window.Loca.xx
-});
-
-```
-
-## AMapUI
-
-AMapUI 用法和官网有一点点区别。通过 AMapUI.xx 来获取组件
-```js
-AMapLoader.load({
-    key: '',//首次load必填
-    version: '2.0',
-    AMapUI: {
-        version: '1.1',
-        plugins: ['overlay/SimpleMarker'],
-    }
-}).then((AMap) => {
-    map = new AMap.Map('container');
-
-    // !!! 通过 AMap.SimpleMarker 获取组件
-    new AMapUI.SimpleMarker({
-        //前景文字
-        iconLabel: 'A',
-        //图标主题
-        iconTheme: 'default',
-        //背景图标样式
-        iconStyle: 'red',
-        map: map,
-        position: map.getCenter()
-    });
-}).catch((e) => {
-    console.error(e);
-});
-```
-
-
-# 使用
-#### 以普通 JS 方式使用 Loader
-尚未发布在线Loader,可将 dist/index.js 复制到项目下
-```
-<script src="../dist/index.js"></script>
-<script>
-    AMapLoader.load({
-        key:'',//首次load必填
-        version:'2.0',
-        plugins:['AMap.Scale']
-    }).then((AMap)=>{
-        map = new AMap.Map('container');
-        map.addControl(new AMap.Scale())
-    }).catch((e)=>{
-        console.error(e);
-    });   
-</script>
-```
-#### 以 NPM 包方式使用 Loader
-安装
-```
-tnpm i @alife/amap-jsapi-loader --save-dev
-```
-使用
-```
-import AMapLoader from '@alife/amap-jsapi-loader';
-
-AMapLoader.load().then((AMap)=>{
-    map = new AMap.Map('container');
-}).catch(e=>{
-    console.log(e);
-})
-
-```
-
-# 相关链接:
-地&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;图 JSAPI:  &nbsp;&nbsp;[示例中心](https://lbs.amap.com/demo-center/js-api)&nbsp;&nbsp;&nbsp;&nbsp;[教程](https://lbs.amap.com/api/javascript-api/summary)&nbsp;&nbsp;&nbsp;&nbsp;[参考手册](https://lbs.amap.com/api/javascript-api/reference/map)
-
-
-
-数据可视化 JSAPI:  &nbsp;&nbsp;[示例中心](https://lbs.amap.com/demo-center/loca-api)&nbsp;&nbsp;&nbsp;&nbsp;[教程](https://lbs.amap.com/api/loca-api/prod_intro)&nbsp;&nbsp;&nbsp;&nbsp;[参考手册](https://lbs.amap.com/api/loca-api/guide/baselayer)
-
-

+ 0 - 11
node_modules/@amap/amap-jsapi-loader/dist/index.js

@@ -1,11 +0,0 @@
-'use strict';(function(m,p){"object"===typeof exports&&"undefined"!==typeof module?module.exports=p():"function"===typeof define&&define.amd?define(p):(m=m||self,m.AMapLoader=p())})(this,function(){function m(a){var b=[];a.AMapUI&&b.push(p(a.AMapUI));a.Loca&&b.push(r(a.Loca));return Promise.all(b)}function p(a){return new Promise(function(h,c){var f=[];if(a.plugins)for(var e=0;e<a.plugins.length;e+=1)-1==d.AMapUI.plugins.indexOf(a.plugins[e])&&f.push(a.plugins[e]);if(g.AMapUI===b.failed)c("\u524d\u6b21\u8bf7\u6c42 AMapUI \u5931\u8d25");
-else if(g.AMapUI===b.notload){g.AMapUI=b.loading;d.AMapUI.version=a.version||d.AMapUI.version;e=d.AMapUI.version;var l=document.body||document.head,k=document.createElement("script");k.type="text/javascript";k.src="https://webapi.amap.com/ui/"+e+"/main.js";k.onerror=function(a){g.AMapUI=b.failed;c("\u8bf7\u6c42 AMapUI \u5931\u8d25")};k.onload=function(){g.AMapUI=b.loaded;if(f.length)window.AMapUI.loadUI(f,function(){for(var a=0,b=f.length;a<b;a++){var c=f[a].split("/").slice(-1)[0];window.AMapUI[c]=
-arguments[a]}for(h();n.AMapUI.length;)n.AMapUI.splice(0,1)[0]()});else for(h();n.AMapUI.length;)n.AMapUI.splice(0,1)[0]()};l.appendChild(k)}else g.AMapUI===b.loaded?a.version&&a.version!==d.AMapUI.version?c("\u4e0d\u5141\u8bb8\u591a\u4e2a\u7248\u672c AMapUI \u6df7\u7528"):f.length?window.AMapUI.loadUI(f,function(){for(var a=0,b=f.length;a<b;a++){var c=f[a].split("/").slice(-1)[0];window.AMapUI[c]=arguments[a]}h()}):h():a.version&&a.version!==d.AMapUI.version?c("\u4e0d\u5141\u8bb8\u591a\u4e2a\u7248\u672c AMapUI \u6df7\u7528"):
-n.AMapUI.push(function(a){a?c(a):f.length?window.AMapUI.loadUI(f,function(){for(var a=0,b=f.length;a<b;a++){var c=f[a].split("/").slice(-1)[0];window.AMapUI[c]=arguments[a]}h()}):h()})})}function r(a){return new Promise(function(h,c){if(g.Loca===b.failed)c("\u524d\u6b21\u8bf7\u6c42 Loca \u5931\u8d25");else if(g.Loca===b.notload){g.Loca=b.loading;d.Loca.version=a.version||d.Loca.version;var f=d.Loca.version,e=d.AMap.version.startsWith("2"),l=f.startsWith("2");if(e&&!l||!e&&l)c("JSAPI \u4e0e Loca \u7248\u672c\u4e0d\u5bf9\u5e94\uff01\uff01");
-else{e=d.key;l=document.body||document.head;var k=document.createElement("script");k.type="text/javascript";k.src="https://webapi.amap.com/loca?v="+f+"&key="+e;k.onerror=function(a){g.Loca=b.failed;c("\u8bf7\u6c42 AMapUI \u5931\u8d25")};k.onload=function(){g.Loca=b.loaded;for(h();n.Loca.length;)n.Loca.splice(0,1)[0]()};l.appendChild(k)}}else g.Loca===b.loaded?a.version&&a.version!==d.Loca.version?c("\u4e0d\u5141\u8bb8\u591a\u4e2a\u7248\u672c Loca \u6df7\u7528"):h():a.version&&a.version!==d.Loca.version?
-c("\u4e0d\u5141\u8bb8\u591a\u4e2a\u7248\u672c Loca \u6df7\u7528"):n.Loca.push(function(a){a?c(a):c()})})}if(!window)throw Error("AMap JSAPI can only be used in Browser.");var b;(function(a){a.notload="notload";a.loading="loading";a.loaded="loaded";a.failed="failed"})(b||(b={}));var d={key:"",AMap:{version:"1.4.15",plugins:[]},AMapUI:{version:"1.1",plugins:[]},Loca:{version:"1.3.2"}},g={AMap:b.notload,AMapUI:b.notload,Loca:b.notload},n={AMap:[],AMapUI:[],Loca:[]},q=[],t=function(a){"function"==typeof a&&
-(g.AMap===b.loaded?a(window.AMap):q.push(a))};return{load:function(a){return new Promise(function(h,c){if(g.AMap==b.failed)c("");else if(g.AMap==b.notload){var f=a.key,e=a.version,l=a.plugins;f?(window.AMap&&"lbs.amap.com"!==location.host&&c("\u7981\u6b62\u591a\u79cdAPI\u52a0\u8f7d\u65b9\u5f0f\u6df7\u7528"),d.key=f,d.AMap.version=e||d.AMap.version,d.AMap.plugins=l||d.AMap.plugins,g.AMap=b.loading,e=document.body||document.head,window.___onAPILoaded=function(d){delete window.___onAPILoaded;if(d)g.AMap=
-b.failed,c(d);else for(g.AMap=b.loaded,m(a).then(function(){h(window.AMap)})["catch"](c);q.length;)q.splice(0,1)[0]()},l=document.createElement("script"),l.type="text/javascript",l.src="https://webapi.amap.com/maps?callback=___onAPILoaded&v="+d.AMap.version+"&key="+f+"&plugin="+d.AMap.plugins.join(","),l.onerror=function(a){g.AMap=b.failed;c(a)},e.appendChild(l)):c("\u8bf7\u586b\u5199key")}else if(g.AMap==b.loaded)if(a.key&&a.key!==d.key)c("\u591a\u4e2a\u4e0d\u4e00\u81f4\u7684 key");else if(a.version&&
-a.version!==d.AMap.version)c("\u4e0d\u5141\u8bb8\u591a\u4e2a\u7248\u672c JSAPI \u6df7\u7528");else{f=[];if(a.plugins)for(e=0;e<a.plugins.length;e+=1)-1==d.AMap.plugins.indexOf(a.plugins[e])&&f.push(a.plugins[e]);if(f.length)window.AMap.plugin(f,function(){m(a).then(function(){h(window.AMap)})["catch"](c)});else m(a).then(function(){h(window.AMap)})["catch"](c)}else if(a.key&&a.key!==d.key)c("\u591a\u4e2a\u4e0d\u4e00\u81f4\u7684 key");else if(a.version&&a.version!==d.AMap.version)c("\u4e0d\u5141\u8bb8\u591a\u4e2a\u7248\u672c JSAPI \u6df7\u7528");
-else{var k=[];if(a.plugins)for(e=0;e<a.plugins.length;e+=1)-1==d.AMap.plugins.indexOf(a.plugins[e])&&k.push(a.plugins[e]);t(function(){if(k.length)window.AMap.plugin(k,function(){m(a).then(function(){h(window.AMap)})["catch"](c)});else m(a).then(function(){h(window.AMap)})["catch"](c)})}})},reset:function(){delete window.AMap;delete window.AMapUI;delete window.Loca;d={key:"",AMap:{version:"1.4.15",plugins:[]},AMapUI:{version:"1.1",plugins:[]},Loca:{version:"1.3.2"}};g={AMap:b.notload,AMapUI:b.notload,
-Loca:b.notload};n={AMap:[],AMapUI:[],Loca:[]}}}})

+ 0 - 69
node_modules/@amap/amap-jsapi-loader/example/AMapUI.html

@@ -1,69 +0,0 @@
-<!doctype html>
-<html lang="en">
-
-<head>
-    <meta charset="utf-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
-    <title>JSAPI Loader</title>
-    <style>
-        html,
-        body,
-        #container {
-            height: 100%;
-            width: 100%;
-            margin: 0;
-        }
-    </style>
-</head>
-
-<body>
-    <div id="container" tabindex="0"></div>
-
-    <script src="../dist/index.js"></script>
-    <script>
-        AMapLoader.load({
-            key: '',//首次load必填
-            version: '2.0Beta',
-            plugins: ['AMap.Scale'],
-            AMapUI: {
-                plugins: ['overlay/SimpleMarker'],
-            }
-        }).then((AMap) => {
-            map = new AMap.Map('container');
-            map.addControl(new AMap.Scale());
-            new AMapUI.SimpleMarker({
-
-                //前景文字
-                iconLabel: 'A',
-
-                //图标主题
-                iconTheme: 'default',
-
-                //背景图标样式
-                iconStyle: 'red',
-
-                //...其他Marker选项...,不包括content
-                map: map,
-                position: map.getCenter()
-            });
-        }).catch((e) => {
-            console.error(e);
-        });
-        setTimeout(function () {
-            AMapLoader.load({
-                plugins: ['AMap.MapType']
-            }).then((AMap) => {
-                map.addControl(new AMap.MapType())
-            }).catch((e) => {
-                console.error(e);
-            });
-        }, 5000);
-
-    </script>
-
-
-    </script>
-</body>
-
-</html>

+ 0 - 76
node_modules/@amap/amap-jsapi-loader/example/Loca.html

@@ -1,76 +0,0 @@
-<!doctype html>
-<html lang="en">
-
-<head>
-    <meta charset="utf-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
-    <title>JSAPI Loader</title>
-    <style>
-        html,
-        body,
-        #container {
-            height: 100%;
-            width: 100%;
-            margin: 0;
-        }
-    </style>
-</head>
-
-<body>
-    <div id="container" tabindex="0"></div>
-    <script src="https://a.amap.com/Loca/static/mock/districts.js"></script>
-    <script src="../dist/index.js"></script>
-    <script>
-        AMapLoader.load({
-            key: 'd3c5511c8a102496b1fdb985e6ca1cd7',//首次load必填
-            // version: '2.0',
-            Loca: {
-                version: '2.0.0beta'
-            }
-        }).then((AMap) => {
-            var map = new AMap.Map('container', {
-                zoom: 4,
-                center: [107.4976, 32.1697],
-                features: ['bg', 'road'],
-                // Loca 自 1.2.0 起 viewMode 模式默认为 3D,如需 2D 模式,请显示配置。
-                // viewMode: '3D'
-            });
-
-            var layer = new Loca.PointLayer({
-                map: map
-            });
-
-            layer.setData(districts, {
-                // 指定经纬度所在字段
-                lnglat: 'center'
-            });
-
-            layer.setOptions({
-                style: {
-                    // 圆形半径,单位像素
-                    radius: 5,
-                    // 填充颜色
-                    color: '#07E8E4',
-                    // 描边颜色
-                    borderColor: '#5DFBF9',
-                    // 描边宽度,单位像素
-                    borderWidth: 1,
-                    // 透明度 [0-1]
-                    opacity: 0.9,
-                }
-            });
-
-            layer.render();
-
-        }).catch((e) => {
-            console.error(e);
-        });
-
-    </script>
-
-
-    </script>
-</body>
-
-</html>

+ 0 - 94
node_modules/@amap/amap-jsapi-loader/example/Multi.html

@@ -1,94 +0,0 @@
-<!doctype html>
-<html lang="en">
-
-<head>
-    <meta charset="utf-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
-    <title>JSAPI Loader</title>
-    <style>
-        html,
-        body,
-        #container {
-            height: 100%;
-            width: 100%;
-            margin: 0;
-        }
-    </style>
-</head>
-
-<body>
-    <div id="container" tabindex="0"></div>
-    <script src="https://a.amap.com/Loca/static/mock/districts.js"></script>
-    <script src="../dist/index.js"></script>
-    <script>
-        AMapLoader.load({
-            key: '',//首次load必填
-            version: '1.4.15',
-            AMapUI: {
-                plugins: ['overlay/SimpleMarker'],
-            },
-            Loca: {
-                version: '1.3.2'
-            }
-        }).then((AMap) => {
-            var map = new AMap.Map('container', {
-                zoom: 4,
-                center: [107.4976, 32.1697],
-                features: ['bg', 'road'],
-                // Loca 自 1.2.0 起 viewMode 模式默认为 3D,如需 2D 模式,请显示配置。
-                // viewMode: '3D'
-            });
-
-            new AMapUI.SimpleMarker({
-
-                //前景文字
-                iconLabel: 'A',
-
-                //图标主题
-                iconTheme: 'default',
-
-                //背景图标样式
-                iconStyle: 'red',
-
-                //...其他Marker选项...,不包括content
-                map: map,
-                position: map.getCenter()
-            });
-            var layer = new Loca.PointLayer({
-                map: map
-            });
-
-            layer.setData(districts, {
-                // 指定经纬度所在字段
-                lnglat: 'center'
-            });
-
-            layer.setOptions({
-                style: {
-                    // 圆形半径,单位像素
-                    radius: 5,
-                    // 填充颜色
-                    color: '#07E8E4',
-                    // 描边颜色
-                    borderColor: '#5DFBF9',
-                    // 描边宽度,单位像素
-                    borderWidth: 1,
-                    // 透明度 [0-1]
-                    opacity: 0.9,
-                }
-            });
-
-            layer.render();
-
-        }).catch((e) => {
-            console.error(e);
-        });
-
-    </script>
-
-
-    </script>
-</body>
-
-</html>

+ 0 - 36
node_modules/@amap/amap-jsapi-loader/example/example-es6.html

@@ -1,36 +0,0 @@
-<!doctype html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
-    <title>JSAPI Loader - ES6</title>
-    <style>
-        html, body, #container {
-            height: 100%;
-            width: 100%;
-            margin: 0;
-        }
-    </style>
-</head>
-<body>
-<div id="container" tabindex="0"></div>
-<script type="module">
-    import AMapLoader from '../src/index.js';
-    AMapLoader.load({
-        key:'',//必填
-        version:'2.0',
-        plugins:['AMap.Scale']
-    }).then((AMap)=>{
-        debugger
-        var map = new AMap.Map('container');
-        map.addControl(new AMap.Scale())
-    }).catch((e)=>{
-        console.error(e);
-    });
-</script>
-
-
-</script>
-</body>
-</html>

+ 0 - 46
node_modules/@amap/amap-jsapi-loader/example/example.html

@@ -1,46 +0,0 @@
-<!doctype html>
-<html lang="en">
-<head>
-    <meta charset="utf-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
-    <title>AMap JSAPI Loader</title>
-    <style>
-        html, body, #container {
-            height: 100%;
-            width: 100%;
-            margin: 0;
-        }
-    </style>
-</head>
-<body>
-<div id="container" tabindex="0"></div>
-
-<script src="https://webapi.amap.com/loader.js"></script>
-<script>
-    AMapLoader.load({ //首次调用 load
-        key:'你申请的高德开放平台 Web 端 key',//首次load key为必填
-        version:'2.0',
-        plugins:['AMap.Scale','AMap.ToolBar']
-    }).then((AMap)=>{
-        map = new AMap.Map('container');
-        map.addControl(new AMap.Scale())
-        map.addControl(new AMap.ToolBar())
-        map.add(new AMap.Marker({
-            position:map.getCenter()
-        }));
-    }).catch((e)=>{
-        console.error(e);
-    });
-   
-    AMapLoader.load({ //可多次调用load
-        plugins:['AMap.MapType']
-    }).then((AMap)=>{
-        map.addControl(new AMap.MapType())
-    }).catch((e)=>{
-        console.error(e);
-    });
-   
-</script>
-</body>
-</html>

文件差异内容过多而无法显示
+ 0 - 0
node_modules/@amap/amap-jsapi-loader/example/libs/chai.min.js


文件差异内容过多而无法显示
+ 0 - 0
node_modules/@amap/amap-jsapi-loader/example/libs/mocha.min.css


文件差异内容过多而无法显示
+ 0 - 0
node_modules/@amap/amap-jsapi-loader/example/libs/mocha.min.js


+ 0 - 287
node_modules/@amap/amap-jsapi-loader/example/test.html

@@ -1,287 +0,0 @@
-<!doctype html>
-<html lang="en">
-
-<head>
-    <meta charset="utf-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
-    <title>JSAPI Loader</title>
-    <style>
-        html,
-        body,
-        #container {
-            height: 100%;
-            width: 100%;
-            margin: 0;
-        }
-    </style>
-</head>
-
-<body>
-    <div id="mocha"></div>
-
-    <link rel="stylesheet" href="./libs/mocha.min.css" />
-    <script src="./libs/chai.min.js"></script>
-    <script src="./libs/mocha.min.js"></script>
-
-    <script>
-        mocha.setup('bdd');
-        mocha.traceIgnores = ['mocha.min.js', 'chai.min.js'];
-        expect = chai.expect;
-    </script>
-    <div id="container" tabindex="0"></div>
-
-    <script src="../dist/index.js"></script>
-    <script>
-        // 请把 key 写在这里
-        const JSAPIKey = '';
-        const chai = window.chai;
-        const mocha = window.mocha;
-        mocha.setup({
-            timeout: '5000',
-            ui: 'bdd',
-        });
-        const expect = chai.expect;
-        describe('JSAPI', () => {
-            beforeEach(function () {
-                AMapLoader.reset()
-            });
-
-            it('Simple', (done) => {
-                AMapLoader.load({
-                    key: JSAPIKey,//首次load必填
-                    version: '2.0',
-                    plugins: ['AMap.Scale'],
-                    AMapUI: {
-                        plugins: ['overlay/SimpleMarker']
-                    }
-                }).then(() => {
-                    expect(Boolean(window.AMap)).eq(true)
-                    expect(Boolean(window.AMapUI)).eq(true)
-                    expect(Boolean(window.AMapUI.SimpleMarker)).eq(true)
-                    expect(Boolean(window.Loca)).eq(false)
-                    done();
-                });
-
-            });
-            it('Request with AMapUI Loca', (done) => {
-                AMapLoader.load({
-                    key: JSAPIKey,//首次load必填
-                    version: '1.4.15',
-                    plugins: ['AMap.Scale'],
-                    AMapUI: {
-                        plugins: ['overlay/SimpleMarker']
-                    },
-                    Loca: {
-                        version: '1.3.2'
-                    }
-                }).then(() => {
-                    expect(Boolean(window.AMap)).eq(true)
-                    expect(Boolean(window.AMapUI)).eq(true)
-                    expect(Boolean(window.AMapUI.SimpleMarker)).eq(true)
-
-                    expect(Boolean(window.Loca)).eq(true)
-                    done();
-                });
-
-            });
-            it('fail with multi version', (done) => {
-                AMapLoader.load({
-                    key: JSAPIKey,//首次load必填
-                    version: '2.0',
-                }).then(() => {
-                    done()
-                });
-                AMapLoader.load({
-                    key: JSAPIKey,//首次load必填
-                    version: '1.4.15',
-                }).then(() => {
-                    done(Error('should not here'))
-                })
-                    .catch(err => {
-                        try {
-                            expect(err).eq('不允许多个版本 JSAPI 混用')
-
-                        } catch (error) {
-                            done(error)
-                        }
-                    })
-
-            });
-            it('fail with multi key', (done) => {
-                AMapLoader.load({
-                    key: JSAPIKey,//首次load必填
-                    version: '2.0',
-                }).then(() => {
-                    done()
-                });
-                AMapLoader.load({
-                    key: 'asdfas',//首次load必填
-                }).then(() => {
-                    done(Error('should not here'))
-                })
-                    .catch(err => {
-                        try {
-                            expect(err).eq('多个不一致的 key')
-
-                        } catch (error) {
-                            done(error)
-                        }
-                    })
-
-            });
-        })
-
-
-        describe('AMapUI', () => {
-            beforeEach(function () {
-                AMapLoader.reset()
-            });
-
-            it('version', (done) => {
-                AMapLoader.load({
-                    key: JSAPIKey,//首次load必填
-                    version: '1.4.15',
-                    AMapUI: {
-                        version: '1.1',
-                        plugins: ['misc/PathSimplifier']
-                    }
-                }).then(() => {
-                    expect(Boolean(window.AMap)).eq(true)
-                    expect(Boolean(window.AMapUI)).eq(true)
-                    expect(Boolean(window.AMapUI.PathSimplifier)).eq(true)
-                    expect(Boolean(window.AMapUI.version.startsWith('1.1'))).eq(true)
-                    done();
-                }).catch(done);
-            });
-
-            it('async', (done) => {
-                console.log(window.AMap);
-                AMapLoader.load({
-                    key: JSAPIKey,//首次load必填
-                    version: '1.4.15',
-                    AMapUI: {
-                        version: '1.0',
-                        plugins: ['overlay/SimpleMarker']
-                    }
-                }).then(() => {
-                    AMapLoader.load({
-                        AMapUI: {
-                            plugins: ['overlay/SimpleInfoWindow']
-                        }
-                    }).then(() => {
-                        expect(Boolean(window.AMap)).eq(true)
-                        expect(Boolean(window.AMapUI.version.startsWith('1.0'))).eq(true)
-                        expect(Boolean(window.AMapUI.SimpleMarker)).eq(true)
-                        expect(Boolean(window.AMapUI.SimpleInfoWindow)).eq(true)
-                        done()
-                    });
-                }).catch(done);
-            })
-        });
-
-        describe('Loca', () => {
-            beforeEach(function () {
-                AMapLoader.reset()
-            });
-
-            it('version', (done) => {
-                AMapLoader.load({
-                    key: JSAPIKey,//首次load必填
-                    version: '1.4.15',
-                    Loca: {
-                        version: '1.3.1'
-                    }
-                }).then(() => {
-                    expect(Boolean(window.AMap)).eq(true)
-                    expect(Boolean(window.Loca)).eq(true)
-                    expect(window.Loca.version).eq('1.3.1')
-                    done()
-                }).catch(done);
-            });
-            it('fail with jsapi2.0', (done) => {
-                AMapLoader.load({
-                    key: JSAPIKey,//首次load必填
-                    version: '2.0',
-                    Loca: {
-                        version: '1.3.1'
-                    }
-                }).then(() => {
-                    done(Error('should not here'))
-                }).catch(err => {
-                    try {
-                        expect(err).eq("Loca 暂不适配 JSAPI 2.0,请使用 1.4.15")
-                        done()
-                    } catch (error) {
-                        done(error)
-                    }
-                });
-            });
-
-
-            it('async', (done) => {
-                AMapLoader.load({
-                    key: JSAPIKey,//首次load必填
-                    version: '1.4.15',
-                    Loca: {
-                        version: '1.2.1'
-                    }
-                }).then(() => {
-                    expect(Boolean(window.AMap)).eq(true)
-                    expect(Boolean(window.Loca)).eq(true)
-                    expect(window.Loca.version).eq('1.2.1')
-                    AMapLoader.load({
-                        Loca: {
-                            version: '1.2.1'
-                        }
-                    }).then(() => {
-                        expect(Boolean(window.AMap)).eq(true)
-                        expect(Boolean(window.Loca)).eq(true)
-                        expect(window.Loca.version).eq('1.2.1')
-                        done()
-                    }).catch(done);
-
-                });
-            })
-        });
-
-
-        describe('Advance', () => {
-            beforeEach(function () {
-                AMapLoader.reset()
-            });
-            it('Multi', (done) => {
-                AMapLoader.load({ key: JSAPIKey, version: '1.4.15' }).then(() => {
-                    expect(Boolean(window.AMap)).eq(true);
-                    expect(window.AMap.v).eq('1.4.15');
-
-                    expect(Boolean(window.AMapUI)).eq(false);
-                    expect(Boolean(window.Loca)).eq(false);
-                    AMapLoader.load({ AMapUI: {} }).then(() => {
-                        expect(Boolean(window.AMap)).eq(true);
-                        expect(window.AMap.v).eq('1.4.15');
-
-                        expect(Boolean(window.AMapUI)).eq(true);
-                        expect(window.AMapUI.version.startsWith('1.1')).eq(true);
-                        expect(Boolean(window.Loca)).eq(false);
-                        AMapLoader.load({ Loca: {} }).then(() => {
-                            expect(Boolean(window.AMap)).eq(true);
-                            expect(window.AMap.v).eq('1.4.15');
-                            expect(Boolean(window.AMapUI)).eq(true);
-                            expect(window.AMapUI.version.startsWith('1.1')).eq(true);
-                            expect(Boolean(window.Loca)).eq(true);
-                            expect(window.Loca.version).eq('1.3.2');
-                            done()
-                        })
-                    })
-                }).catch(done)
-            })
-        })
-        mocha.run()
-    </script>
-
-
-    </script>
-</body>
-
-</html>

+ 0 - 15
node_modules/@amap/amap-jsapi-loader/index.d.ts

@@ -1,15 +0,0 @@
-declare var load: (options: {
-    key: string; // 申请好的Web端开发者Key,首次调用 load 时必填
-    version: string; // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
-    plugins?: string[]; //插件列表
-    // 是否加载 AMapUI,缺省不加载
-    AMapUI?: {
-        version?: string; // AMapUI 缺省 1.1
-        plugins?: string[]; // 需要加载的 AMapUI ui插件
-    };
-    // 是否加载 Loca, 缺省不加载
-    Loca?: {
-        version?: string; // Loca 版本,缺省 1.3.2
-    };
-}) => Promise<any>;
-export { load };

+ 0 - 37
node_modules/@amap/amap-jsapi-loader/package.json

@@ -1,37 +0,0 @@
-{
-  "name": "@amap/amap-jsapi-loader",
-  "version": "1.0.1",
-  "description": "高德官网提供的地图JSAPI加载器,可以避免多种异步加载API的错误用法",
-  "main": "dist/index.js",
-  "types": "index.d.ts",
-  "directories": {
-    "test": "test"
-  },
-  "scripts": {
-    "build": "rollup -c rollup.config.js"
-  },
-  "repository": {
-    "type": "git",
-    "url": "git@gitlab.alibaba-inc.com:amap-web/amap-jsapi-loader.git"
-  },
-  "keywords": [
-    "amap",
-    "jsapi",
-    "sdk",
-    "loader",
-    "地图",
-    "高德"
-  ],
-  "author": "mengmeng.du@alibaba-inc.com",
-  "license": "MIT",
-  "devDependencies": {
-    "@ampproject/rollup-plugin-closure-compiler": "^0.23.0",
-    "@babel/core": "^7.8.7",
-    "@babel/preset-env": "^7.8.7",
-    "rollup": "^1.32.0",
-    "rollup-plugin-babel": "^4.3.3",
-    "rollup-plugin-server": "^0.7.0",
-    "rollup-plugin-typescript2": "^0.27.1",
-    "typescript": "^3.9.7"
-  }
-}

+ 0 - 25
node_modules/@amap/amap-jsapi-loader/rollup.config.js

@@ -1,25 +0,0 @@
-import server from "rollup-plugin-server";
-import babel from "rollup-plugin-babel";
-import compiler from "@ampproject/rollup-plugin-closure-compiler";
-import rollupTypescript from 'rollup-plugin-typescript2';
-
-export default {
-    input: "src/index.ts",
-    output: {
-        file: "dist/index.js",
-        format: "umd",
-        name: "AMapLoader",
-    },
-    plugins: [
-        rollupTypescript(),
-        babel({
-            presets: [["@babel/env", { targets: { ie: 9 } }]],
-        }),
-        compiler(),
-        server({
-            contentBase: "./",
-            host: "127.0.0.1",
-            port: 3601,
-        }),
-    ],
-};

+ 0 - 5
node_modules/@amap/amap-jsapi-loader/src/global.d.ts

@@ -1,5 +0,0 @@
-declare var AMap: any;
-declare var AMapUI: any;
-declare var Loca: any;
-
-declare var ___onAPILoaded: any;

+ 0 - 367
node_modules/@amap/amap-jsapi-loader/src/index.ts

@@ -1,367 +0,0 @@
-if (!window) {
-    throw Error("AMap JSAPI can only be used in Browser.");
-}
-enum LoadStatus {
-    notload = "notload",
-    loading = "loading",
-    loaded = "loaded",
-    failed = "failed",
-}
-let config = {
-    key: "",
-    AMap: {
-        version: "1.4.15",
-        plugins: [],
-    },
-    AMapUI: {
-        version: "1.1",
-        plugins: [],
-    },
-    Loca: {
-        version: "1.3.2",
-    },
-};
-
-let Status = {
-    AMap: LoadStatus.notload,
-    AMapUI: LoadStatus.notload,
-    Loca: LoadStatus.notload,
-};
-let Callback = {
-    AMap: [],
-    AMapUI: [],
-    Loca: [],
-};
-
-let onloadCBKs = [];
-const onload = function (callback) {
-    if (typeof callback == "function") {
-        if (Status.AMap === LoadStatus.loaded) {
-            callback(window.AMap);
-            return;
-        }
-        onloadCBKs.push(callback);
-    }
-};
-
-interface LoadOption {
-    key: string;
-    version?: string;
-    plugins?: string[];
-    AMapUI?: {
-        version?: string;
-        plugins?: string[];
-    };
-    Loca?: {
-        version?: string;
-    };
-}
-function appendOther(option: LoadOption): Promise<any> {
-    let pros: Promise<void>[] = [];
-    if (option.AMapUI) {
-        pros.push(loadAMapUI(option.AMapUI));
-    }
-    if (option.Loca) {
-        pros.push(loadLoca(option.Loca));
-    }
-    return Promise.all(pros);
-}
-function loadAMapUI(params: { version?: string; plugins?: string[] }): Promise<void> {
-    return new Promise((res, rej) => {
-        const newPlugins: string[] = [];
-        if (params.plugins) {
-            for (var i = 0; i < params.plugins.length; i += 1) {
-                if (config.AMapUI.plugins.indexOf(params.plugins[i]) == -1) {
-                    newPlugins.push(params.plugins[i]);
-                }
-            }
-        }
-        if (Status.AMapUI === LoadStatus.failed) {
-            rej("前次请求 AMapUI 失败");
-        } else if (Status.AMapUI === LoadStatus.notload) {
-            Status.AMapUI = LoadStatus.loading;
-            config.AMapUI.version = params.version || config.AMapUI.version;
-            const version = config.AMapUI.version;
-            const parentNode = document.body || document.head;
-            const script = document.createElement("script");
-            script.type = "text/javascript";
-            script.src = `https://webapi.amap.com/ui/${version}/main.js`;
-
-            script.onerror = (e) => {
-                Status.AMapUI = LoadStatus.failed;
-                rej("请求 AMapUI 失败");
-            };
-            script.onload = () => {
-                Status.AMapUI = LoadStatus.loaded;
-                if (newPlugins.length) {
-                    window.AMapUI.loadUI(newPlugins, function () {
-                        for (let i = 0, len = newPlugins.length; i < len; i++) {
-                            const path = newPlugins[i];
-                            const name = path.split("/").slice(-1)[0];
-                            window.AMapUI[name] = arguments[i];
-                        }
-                        res();
-                        while (Callback.AMapUI.length) {
-                            Callback.AMapUI.splice(0, 1)[0]();
-                        }
-                    });
-                } else {
-                    res();
-                    while (Callback.AMapUI.length) {
-                        Callback.AMapUI.splice(0, 1)[0]();
-                    }
-                }
-            };
-            parentNode.appendChild(script);
-        } else if (Status.AMapUI === LoadStatus.loaded) {
-            if (params.version && params.version !== config.AMapUI.version) {
-                rej("不允许多个版本 AMapUI 混用");
-            } else {
-                if (newPlugins.length) {
-                    window.AMapUI.loadUI(newPlugins, function () {
-                        for (let i = 0, len = newPlugins.length; i < len; i++) {
-                            const path = newPlugins[i];
-                            const name = path.split("/").slice(-1)[0];
-                            window.AMapUI[name] = arguments[i];
-                        }
-                        res();
-                    });
-                } else {
-                    res();
-                }
-            }
-        } else {
-            if (params.version && params.version !== config.AMapUI.version) {
-                rej("不允许多个版本 AMapUI 混用");
-            } else {
-                Callback.AMapUI.push((err) => {
-                    if (err) {
-                        rej(err);
-                    } else {
-                        if (newPlugins.length) {
-                            window.AMapUI.loadUI(newPlugins, function () {
-                                for (let i = 0, len = newPlugins.length; i < len; i++) {
-                                    const path = newPlugins[i];
-                                    const name = path.split("/").slice(-1)[0];
-                                    window.AMapUI[name] = arguments[i];
-                                }
-                                res();
-                            });
-                        } else {
-                            res();
-                        }
-                    }
-                });
-            }
-        }
-    });
-}
-
-function loadLoca(params: { version?: string }): Promise<void> {
-    return new Promise((res, rej) => {
-        if (Status.Loca === LoadStatus.failed) {
-            rej("前次请求 Loca 失败");
-        } else if (Status.Loca === LoadStatus.notload) {
-            Status.Loca = LoadStatus.loading;
-            config.Loca.version = params.version || config.Loca.version;
-            const version = config.Loca.version;
-            const isApiV2 = config.AMap.version.startsWith("2");
-            const isLocaV2 = version.startsWith("2");
-            if ((isApiV2 && !isLocaV2) || (!isApiV2 && isLocaV2)) {
-                rej("JSAPI 与 Loca 版本不对应!!");
-                return;
-            }
-            const key = config.key;
-            const parentNode = document.body || document.head;
-            const script = document.createElement("script");
-            script.type = "text/javascript";
-            script.src = `https://webapi.amap.com/loca?v=${version}&key=${key}`;
-
-            script.onerror = (e) => {
-                Status.Loca = LoadStatus.failed;
-                rej("请求 AMapUI 失败");
-            };
-            script.onload = () => {
-                Status.Loca = LoadStatus.loaded;
-                res();
-                while (Callback.Loca.length) {
-                    Callback.Loca.splice(0, 1)[0]();
-                }
-            };
-            parentNode.appendChild(script);
-        } else if (Status.Loca === LoadStatus.loaded) {
-            if (params.version && params.version !== config.Loca.version) {
-                rej("不允许多个版本 Loca 混用");
-            } else {
-                res();
-            }
-        } else {
-            if (params.version && params.version !== config.Loca.version) {
-                rej("不允许多个版本 Loca 混用");
-            } else {
-                Callback.Loca.push((err) => {
-                    if (err) {
-                        rej(err);
-                    } else {
-                        rej();
-                    }
-                });
-            }
-        }
-    });
-}
-
-const load = function (options: LoadOption) {
-    return new Promise((resolve, reject) => {
-        if (Status.AMap == LoadStatus.failed) {
-            reject("");
-        } else if (Status.AMap == LoadStatus.notload) {
-            //初次加载
-            let { key, version, plugins } = options;
-            if (!key) {
-                reject("请填写key");
-                return;
-            }
-            if (window.AMap && location.host !== "lbs.amap.com") {
-                reject("禁止多种API加载方式混用");
-            }
-            config.key = key;
-            config.AMap.version = version || config.AMap.version;
-            config.AMap.plugins = plugins || config.AMap.plugins;
-            Status.AMap = LoadStatus.loading;
-
-            const parentNode = document.body || document.head;
-
-            window.___onAPILoaded = function (err) {
-                delete window.___onAPILoaded;
-                if (err) {
-                    Status.AMap = LoadStatus.failed;
-                    reject(err);
-                } else {
-                    Status.AMap = LoadStatus.loaded;
-                    appendOther(options)
-                        .then(() => {
-                            resolve(window.AMap);
-                        })
-                        .catch(reject);
-                    while (onloadCBKs.length) {
-                        onloadCBKs.splice(0, 1)[0]();
-                    }
-                }
-            };
-            const script = document.createElement("script");
-            script.type = "text/javascript";
-
-            script.src =
-                "https://webapi.amap.com/maps?callback=___onAPILoaded&v=" +
-                config.AMap.version +
-                "&key=" +
-                key +
-                "&plugin=" +
-                config.AMap.plugins.join(",");
-            script.onerror = (e) => {
-                Status.AMap = LoadStatus.failed;
-                reject(e);
-            };
-            parentNode.appendChild(script);
-        } else if (Status.AMap == LoadStatus.loaded) {
-            //deal multi load
-            if (options.key && options.key !== config.key) {
-                reject("多个不一致的 key");
-                return;
-            }
-            if (options.version && options.version !== config.AMap.version) {
-                reject("不允许多个版本 JSAPI 混用");
-                return;
-            }
-            const newPlugins = [];
-            if (options.plugins) {
-                for (var i = 0; i < options.plugins.length; i += 1) {
-                    if (config.AMap.plugins.indexOf(options.plugins[i]) == -1) {
-                        newPlugins.push(options.plugins[i]);
-                    }
-                }
-            }
-            if (newPlugins.length) {
-                window.AMap.plugin(newPlugins, () => {
-                    appendOther(options)
-                        .then(() => {
-                            resolve(window.AMap);
-                        })
-                        .catch(reject);
-                });
-            } else {
-                appendOther(options)
-                    .then(() => {
-                        resolve(window.AMap);
-                    })
-                    .catch(reject);
-            }
-        } else {
-            // loading
-            if (options.key && options.key !== config.key) {
-                reject("多个不一致的 key");
-                return;
-            }
-            if (options.version && options.version !== config.AMap.version) {
-                reject("不允许多个版本 JSAPI 混用");
-                return;
-            }
-            const newPlugins = [];
-            if (options.plugins) {
-                for (var i = 0; i < options.plugins.length; i += 1) {
-                    if (config.AMap.plugins.indexOf(options.plugins[i]) == -1) {
-                        newPlugins.push(options.plugins[i]);
-                    }
-                }
-            }
-            onload(() => {
-                if (newPlugins.length) {
-                    window.AMap.plugin(newPlugins, () => {
-                        appendOther(options)
-                            .then(() => {
-                                resolve(window.AMap);
-                            })
-                            .catch(reject);
-                    });
-                } else {
-                    appendOther(options)
-                        .then(() => {
-                            resolve(window.AMap);
-                        })
-                        .catch(reject);
-                }
-            });
-        }
-    });
-};
-function reset() {
-    delete window.AMap;
-    delete window.AMapUI;
-    delete window.Loca;
-    config = {
-        key: "",
-        AMap: {
-            version: "1.4.15",
-            plugins: [],
-        },
-        AMapUI: {
-            version: "1.1",
-            plugins: [],
-        },
-        Loca: {
-            version: "1.3.2",
-        },
-    };
-    Status = {
-        AMap: LoadStatus.notload,
-        AMapUI: LoadStatus.notload,
-        Loca: LoadStatus.notload,
-    };
-    Callback = {
-        AMap: [],
-        AMapUI: [],
-        Loca: [],
-    };
-}
-export default { load, reset };

+ 0 - 17
node_modules/@amap/amap-jsapi-loader/test/demo.ts

@@ -1,17 +0,0 @@
-import AMapLoader from "../";
-// import {  } from "@ali/test-amap-jsapi";
-
-AMapLoader.load({
-  key: "test",
-  version: "2.0"
-}).then(() => {
-  new AMap.Circle({})
-  const map = new AMap.Map("div");
-  map.on("complete", () => {
-    const circle = new AMap.Circle({
-      center: [135, 45],
-      radius: 40
-    });
-    map.add(circle);
-  });
-});

+ 0 - 7
node_modules/@amap/amap-jsapi-loader/test/test.ts

@@ -1,7 +0,0 @@
-import AMapLoader from "../";
-import "@amap/amap-jsapi-loader";
-
-AMapLoader.load({
-  key: "test",
-  version: "2.0"
-}).then(() => {});

+ 275 - 0
node_modules/mescroll-uni/README.md

@@ -0,0 +1,275 @@
+## mescroll --【wxs+renderjs实现】高性能的下拉刷新上拉加载组件
+1. mescroll的uni版本 是专门用在uni-app的下拉刷新和上拉加载的组件, 支持一套代码编译到iOS、Android、H5、小程序等多个平台
+
+2. mescroll的uni版本 继承了mescroll.js的实用功能: 自动处理分页, 自动控制无数据, 空布局提示, 回到顶部按钮 ..
+
+3. mescroll的uni版本 丰富的案例, 自由灵活的api, 超详细的注释, 可让您快速自定义真正属于自己的下拉上拉组件
+
+<br/>
+
+
+## 最新文档(1.3.7版本): <a href="http://www.mescroll.com/uni.html">http://www.mescroll.com/uni.html</a>
+2021-04-13 by mescroll (文档可能会有缓存,建议打开时刷新一下)
+
+
+## 近期已更新优化的内容:
+1. 微信小程序, app, h5使用高性能wxs和renderjs, 下拉刷新更流畅丝滑, 尤其能明显解决Android小程序下拉卡顿的问题
+2. 新增国际化`mescroll-i18n.vue`示例, 新增轮播吸顶菜单`mescroll-swiper-sticky.vue`示例,    
+3. 新增 "局部区域滚动" 的案例: mescroll-body-part.vue 和 mescroll-uni-part.vue  
+4. 新增 me-video 视频组件, 解决APP端视频下拉悬浮错位的问题, 参考 mescroll-options.vue 示例  
+5. 新增 me-tabs 组件,tabs支持水平滑动; 优化mescroll-more和mescroll-swiper的案例, 顶部tab支持水平滑动
+6. 吸顶悬浮提供了原生sticky和监听滚动条实现的示例: sticky.vue 和 sticky-scroll.vue (推荐使用sticky样式实现)
+7. mescroll.scrollTo(y)的y支持css选择器, 包括跨自定义组件的后代选择器, 支持滚动到子组件的view (参考 mescroll-options.vue)  
+8. topbar 顶部是否预留状态栏的高度, 默认false; 还可支持设置状态栏背景: 如 '#ffff00', 'url(xxx) 0 0/100% 100%', 'linear-gradient(xx)'
+9. down.bgColor 和 up.bgColor 加载区域的背景,不仅支持色值, 而且还是支持背景图和渐变: 如 'url(xxx) 0 0/100% 100%', 'linear-gradient(xx)'
+10. topbar,bgColor支持一行代码定义background: [https://www.runoob.com/cssref/css3-pr-background.html](https://www.runoob.com/cssref/css3-pr-background.html)
+<br/>
+<br/>
+<a href="https://ext.dcloud.net.cn/plugin?id=343&update_log">查看更多 ... </a>
+
+
+### 更新记录:
+
+---
+#### 1.3.7版本 (2021/04/13)  
+1. 新增`mescroll-swiper-sticky.vue`的示例, 轮播吸顶菜单导航  
+2. 新增`mescroll-empty.vue`的示例, 单独使用空布局组件  
+3. 简化tabs在具体项目中的使用,并简化对应的示例  
+4. mescroll-uni 支持动态禁止滚动的属性 disableScroll (注: mescroll-body不支持)  
+-by 小瑾同学
+
+---
+#### 1.3.5版本 (2021/04/10)  
+1. 新增 `mescroll-i18n.vue` 的示例, 支持国际化的配置  
+2. down的`beforeEndDelay`不再默认配置 // (显示加载成功/失败的时长, android小程序设置此项结束下拉会卡顿, 配置后请注意测试)  
+3. mescroll-body-part.vue 和 mescroll-uni-part.vue 的示例新增参考代码: `异步加载左侧菜单(超简单)`  
+4. mescroll的极简示例新增参考代码: `先请求其他接口,再触发upCallback,无需配置auto为false(超简单)`  
+5. 修复mescroll-swiper的tabs在app或h5错位的问题  
+-by 小瑾同学
+
+---
+#### 1.3.3版本 (2020/09/15)  
+1. 新增下拉刷新成功和失败的文本配置, 可在 mescroll-uni-option.js 配置修改  
+1. 新增 me-video 视频组件, 解决APP端视频下拉悬浮错位的问题, 参考最新的 mescroll-options.vue 示例  
+2. 更新 mescroll-comp.vue 的示例, 支持 mescroll-body 子子..子组件 , 包括 mescroll-more.vue 也支持写在子子..子组件  
+3. mescroll-uni 的 down.offset 由原来的 80 调整为 150 , 避免超快速滑动列表到底部,偶尔出现无法再翻页的问题 (mescroll-body无此问题)  
+4. 修复 mescroll-more.vue 和 mescroll-swiper.vue 的示例在字节跳动小程序2.0.0以上新版编辑器无法正常运行的问题  
+5. 修复淘宝和美团的示例下拉卡顿的问题 ( 案例需重新下载 )  
+-by mescroll  
+
+---
+#### 1.3.2版本 (2020/08/05)
+1. mescroll-body新增sticky属性, 简化吸顶悬浮sticky.vue的示例  
+2. QQ小程序支持wxs,解决QQ小程序卡顿和无法隐藏加载状态的问题  
+3. mescroll.scrollTo(y)的y支持css选择器, 包括跨自定义组件的后代选择器, 支持滚动到子组件的view (参考 mescroll-options.vue)  
+-by 小瑾同学
+
+---
+#### 1.3.1版本 (2020/07/27)
+1. 修复Android小程序下拉刷新时, image 和 swiper 脱离文档流的问题  
+2. 修复H5端, 当配置down.use为false时, 返回其他页面无法滚动的问题  
+3. mescroll-comp.js支持mescroll-body写在子子子...组件中 (以前版本仅支持写在一级子组件) 
+4. 吸顶悬浮提供了sticky样式和监听滚动条实现的示例: sticky.vue 和 sticky-scroll.vue (推荐使用sticky样式实现)  
+-by 小瑾同学
+
+---
+#### 1.3.0版本 (2020/07/10)
+1. 微信小程序, app, h5使用高性能wxs和renderjs, 下拉刷新更流畅丝滑, 尤其能明显解决Android小程序下拉卡顿的问题
+2. 使用wxs和renderjs优化所有案例, 尤其是中高级案例, 建议大家重新下载最新的案例
+3. 废弃down的isBounce配置, 已通过renderjs自动判断, 无需配置mescroll-touch
+4. 废弃down的fps配置, 已通过wxs提高性能, 无需手动节流
+5. 新增 "局部区域滚动" 的案例: mescroll-body-part.vue 和 mescroll-uni-part.vue
+6. 解决swiper切换时,有时会触发下拉刷新的问题, 已避免swiper和下拉刷新相互冲突
+7. 解决钉钉小程序mescroll-uni下拉刷新有时无法触发的问题
+8. 解决上拉加载进度在部分Android手机显示不全的问题
+9. 提高 me-tabs 组件在部分Android手机的兼容性  
+-by 小瑾同学
+
+---
+#### 1.2.8版本 (2020/06/28)
+1. 解决 mescroll-uni 再某些情况下列表数据渲染不完全的问题 ( mescroll-body无此问题 )
+2. 优化 me-tabs 组件, 使用支付宝小程序可隐藏滚动条, 同时修复字节跳动小程序tab切换时渲染延迟的问题
+-by 小瑾同学
+
+---
+#### 1.2.7版本 (2020/06/24)
+1. 上拉加载结束隐藏底部加载区域,避免加载区域占位
+2. h5端的tab页默认偏移TabBar的高度,避免h5端列表被TabBar遮住 (如不想偏移,可通过配置 :bottombar="false" 取消)
+3. 新增 me-tabs 组件,tabs支持水平滑动; 优化mescroll-more和mescroll-swiper的案例, 顶部tab支持水平滑动
+
+---
+#### 1.2.6版本 (2020/06/16)
+1. mescroll-uni 和 mescroll-body 的 scrollTo 正式支持 scroll-into-view (传入的 y 为view的id即可生效)
+2. topbar 顶部是否预留状态栏的高度, 默认false; 这个版本还可支持设置状态栏背景: 如 '#ffff00', 'url(xxx)', 'linear-gradient(xx)'
+3. down.bgColor 和 up.bgColor 加载区域的背景,不仅支持色值, 而且还是支持背景图和渐变: 如 'url(xxx)', 'linear-gradient(xx)'
+4. 通过css方式适配iPhoneX, 比之前通过style方式具有更好的兼容性, 也同时消除了edge浏览器重复设置相同属性的警告
+5. 移除非必须的标签选择器,避免微信小程序提示组件内不可使用标签选择器的警告
+6. 修复当配置up的use为false时,默认的下拉刷新有时候无法自动隐藏的问题
+7. 修复当配置down的native为true时,auto失效的问题
+8. 修复空布局在某些情况下图片和文本错位的问题
+
+---
+#### 1.2.5版本 (2020/03/15)
+1. mescroll-body 的 props 支持 safearea 的配置 (需要适配iPhoneX时,配置为 true 即可, 默认 false)
+2. mescroll-uni 的 scrollTo 支持 scroll-into-view (当传入的 y 为view的id时, 即可生效)
+3. 新增 下拉加载聊天记录的案例 list-msg.vue, 类似微信QQ的聊天记录
+
+---
+#### 1.2.4版本 (2020/03/11)
+1. down和up分别新增 bgColor 的配置: 下拉区域背景颜色,默认"transparent"
+2. down和up分别新增 textColor 的配置: 下拉文本的颜色,默认"gray" (当bgColor配置了颜色,而textColor未配置时,则自动默认为白色)
+3. 调整mescroll-more-item.js, 使mescroll-more的案例支持初始化tabIndex大于0的tab页
+4. mescroll-body支持isBounce的配置, 解决H5下拉刷新失效的问题
+5. 解决mescroll-body在Android真机小程序下拉卡顿的问题 (mescroll-uni无此问题)
+
+---
+#### 1.2.3版本 (2020/02/18)
+新增3个mescroll的mixins, 极大简化了mescroll-comp, mescroll-more, mescroll-swiper的案例
+
+
+#### 1.2.2版本 (2020/02/16)
+1. 调整mescroll-more和mescroll-swiper的案例,确保各小程序平台可正确获取到mescroll对象  
+2. 修复字节跳动小程序初始化时的异常警告: <a href="http://www.mescroll.com/qa.html?v=20200216#q26">详情</a>
+
+
+#### 1.2.1版本 (2020/02/08)
+1. 新增 &lt;mescroll-body&gt; 组件, 用来填补 &lt;mescroll-uni&gt; 的不足.
+2. mescroll-body基于原生页的滚动,支持写入原生组件和fixed元素,不必固定高度,不必配置pages.json,简单性能好.
+3. mescroll-body可配置down的native:true, 可直接代理系统自带的下拉组件, 参考 mescroll-native 示例
+4. 新增mescroll-mixins.js,简化代码,兼容更多小程序平台
+5. 修复字节跳动小程序和支付宝小程序的部分异常警告
+
+#### 1.2.0版本 (2020/01/06)
+1. mescroll-uni.vue的props新增height. // 简单快捷设置mescroll的高度, 此项有值,则不使用fixed. 
+	使用场景: 当在弹窗或浮层中使用fixed固定mescroll高度比较麻烦时, 配置此项就很方便了
+	支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight
+2. mescroll-uni.vue的props新增safearea. // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用,此项值对回到顶部按钮生效)
+3. mescroll-uni.vue的props中,top和bottom不仅仅支持数字, 还支持"20rpx", "20px", "20%"格式的值
+4. 补充锁定上拉加载mescroll.lockUpScroll的方法
+5. down.fps默认值提高至80
+6. 独立出空布局的组件&lt;mescroll-empty&gt;, 以便在不使用mescroll的界面也能统一管理空布局
+```
+<template>
+		<view>
+			// 基本用法
+			<mescroll-empty v-if="list.length==0"></mescroll-empty>
+			// 所有配置项 (option同up.empty的配置一致)
+			<mescroll-empty v-if="isShowEmpty" :option="optEmpty" @emptyclick="emptyClick"></mescroll-empty>
+		</view>
+	</template>
+	
+	<script>
+		import MescrollEmpty from '@/components/mescroll-uni/components/mescroll-empty.vue';
+		export default {
+			components: {
+				MescrollEmpty
+			},
+			...
+		}
+```
+7. 为了更快速自定义回到顶部按钮, up.toTop新增以下配置项:
+```
+	toTop: {
+		zIndex: 9990, // fixed定位z-index值
+		right: 20, // 到右边的距离, 默认20 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+		bottom: 120, // 到底部的距离, 默认120 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+		safearea: false, // bottom的偏移量是否加上底部安全区的距离, 默认false. 需要适配iPhoneX时使用 (具体的界面如果不配置此项,则取mescroll-uni.vue的safearea值)
+		width: 72, // 回到顶部图标的宽度, 默认72 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+		radius: "50%", // 圆角, 默认"50%" (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+		left: null, // 到左边的距离, 默认null. 此项有值时,right不生效. (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+		duration: 300, // 回到顶部的动画时长,默认300ms (当值为0或300则使用系统自带回到顶部,更流畅; 其他值则通过step模拟,部分机型可能不够流畅,所以非特殊情况不建议修改此项)
+		src: null, // (已有配置)
+		offset: 1000 // (已有配置)
+	}
+```
+
+
+---
+#### 1.1.9版本 (2019/12/16)
+1. 解决左右滑动屏幕,某些情况下会触发上拉回调的bug
+2. mescroll-uni.vue的props新增topbar: top的偏移量是否加上状态栏高度 (当fixed为false时不生效; 使用场景:取消原生导航栏时,配置此项可自动加上状态栏高度的偏移量)
+
+---
+#### 1.1.8版本 (2019/11/01)
+1. 解决Android小程序快速上拉可能会导致不断翻页直到没有数据为止的bug
+2. 过滤某些情况下touch的警告输出
+
+---
+#### 1.1.7版本 (2019/10/15)
+1. 重点解决下拉刷新卡顿,特别是Android小程序卡顿的问题
+2. 补充之前版本遗漏的mescroll.lockUpScroll方法
+3. 支持数据不满屏时,仍可触发翻页的回调
+
+---
+#### 1.1.5版本 (2019/07/25)
+1. up新增isBounce的配置, 默认值为false. 目的是解决H5在ios下拉出现灰色背景和android端无法正常下拉的问题. 详见: <a href="http://www.mescroll.com/qa.html?v=190725#q25">http://www.mescroll.com/qa.html?v=190725#q25</a>
+2. 修复setClientHeight某些情况下会提示undefined的问题
+3. 修复在mescroll-uni-option.js配置page会导致分页异常的问题
+
+---
+#### 1.1.2版本 (2019/07/19)
+一. 修复和优化的内容:
+1. 修复列表不满屏时,无法上拉的问题
+2. 修复列表滚动到底部,有时无法上拉的问题
+3. 修复快速滚动到顶部时,滚动条的位置有时不为0的问题
+4. 修复配置page.num不生效的问题
+5. 修复小程序android端下拉卡顿的问题 (可配置down的supply,复杂的列表可适当调大值)
+6. 修复H5多mescroll的情况下回到顶部按钮错位的问题
+7. 优化和简化逻辑,去除冗余代码
+
+二. 完善获取节点信息的方式:
+1. 获取mescroll的唯一元素id: mescroll.viewId (可通过uni.createSelectorQuery进一步获取更多信息)
+2. 获取滚动内容的高度: mescroll.getScrollHeight()
+3. 获取mescroll的高度: mescroll.getClientHeight()
+4. 获取滚动条位置: mescroll.getScrollTop()
+5. 获取到底部的距离: mescroll.getScrollBottom()
+6. 获取滚动的方向: mescroll.isScrollUp // true向上滑; false向下滑
+7. 更多信息详见mescroll-options的示例
+
+---
+#### 1.1.1版本 (2019/07/16)
+1. 修复滚动到顶部,有时无法下拉的问题
+2. 修复禁止上拉时,列表底部有空白区域的问题
+
+---
+#### 1.1.0版本 (重要版本) (2019/07/01)
+一. 简化使用:
+1. **无需**在page.json中配置onReachBottomDistance<br/>
+2. **无需**在页面中注册onReachBottom 和 onPageScroll<br/>
+3. 初始化时@init**不再**是必须配置项<br/>
+4. 极大的**简化** list-mescroll-more.vue 的案例<br/>
+5. 全面**支持swiper**,详见 mescroll-swiper.vue 的案例<br/>
+6. 所有基础案例都有所简化, **建议**重新下载参考. (以前版本向下兼容)<br/> 
+7. 所有中高级案例自定义的部分,改动比较大, **必须**重新下载参考. (不兼容以前版本)
+
+二. 优化性能:
+1. 支持fixed定位,可实现局部**区域滚动**<br/>
+2. 支持swiper,scrollview**嵌套**使用<br/>
+3. 优化下拉逻辑,修复下拉刷新卡顿和抖动的问题<br/>
+4. 优化默认样式,避免某些情况出现双滚动条的问题<br/>
+5. up废弃errDistance配置, 内部已优化处理相关问题
+
+三. 新增配置:
+1. down和up新增fps节流配置.(默认40fps, 值越大每秒刷新频率越高)<br/>
+2. up新增offset配置: 距底部多远时,触发upCallback (默认80, 代替page.json的onReachBottomDistance)<br/>
+3. up新增onScroll配置: 是否监听滚动事件,默认false<br/>
+4. < mescroll-uni 新增 @scroll="scroll" 获取滚动条的位置和滚动方向, 需配置up的onScroll为true, 详见 mescroll-options.vue 的示例<br/>
+5. < mescroll-uni 新增 :fixed="true/false" 是否支持fixed定位, 默认 true. 
+<br/> 当:fixed="true", 此时 :top 和 :bottom 为 fixed 的 top 和 bottom
+<br/> 当:fixed="false", 此时 :top 和 :bottom 为 padding-top 和 padding-bottom
+
+
+---
+#### 1.0.3版本 (2019/06/13)
+1. 默认设置page高度100%,使列表不满屏的时候,仍可下拉刷新<br/>
+2. 加入-webkit-overflow-scrolling: touch, 编译到H5和APP,使iOS列表滚动更流畅<br/>
+3. mescroll-empty加入box-sizing: border-box, 修复图标和文本不居中的问题<br/>
+4. 上拉配置新增errDistance,默认110 // mescroll.endErr()的时候需往上滑动一段距离,使其能再次触发上拉加载 (已在1.1.0版本废弃)<br/>
+5. mescroll.endErr(errDistance) 新增的参数 errDistance; 可单独配置异常往上滑动的距离 (已在1.1.0版本废弃)<br/>
+6. 修改了list-mescroll-more的160行为mescroll.endErr(0);(已在1.1.0版本优化)<br/>
+7. 新增mescroll.scrollTo(y,t) 滚动到指定的位置; 本质调用的是uni.pageScrollTo
+
+---
+#### 1.0.2版本 (2019/05/28)  
+1. 组件根元素加入mescroll-uni的样式,empty新增fixed等配置项; <br/>
+2. 修复list-mescroll-more案例切换tabs,某些情况的page.num会错乱的问题

+ 55 - 0
node_modules/mescroll-uni/components/mescroll-down.css

@@ -0,0 +1,55 @@
+/* 下拉刷新区域 */
+.mescroll-downwarp {
+	position: absolute;
+	top: -100%;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	text-align: center;
+}
+
+/* 下拉刷新--内容区,定位于区域底部 */
+.mescroll-downwarp .downwarp-content {
+	position: absolute;
+	left: 0;
+	bottom: 0;
+	width: 100%;
+	min-height: 60rpx;
+	padding: 20rpx 0;
+	text-align: center;
+}
+
+/* 下拉刷新--提示文本 */
+.mescroll-downwarp .downwarp-tip {
+	display: inline-block;
+	font-size: 28rpx;
+	vertical-align: middle;
+	margin-left: 16rpx;
+	/* color: gray; 已在style设置color,此处删去*/
+}
+
+/* 下拉刷新--旋转进度条 */
+.mescroll-downwarp .downwarp-progress {
+	display: inline-block;
+	width: 32rpx;
+	height: 32rpx;
+	border-radius: 50%;
+	border: 2rpx solid gray;
+	border-bottom-color: transparent !important; /*已在style设置border-color,此处需加 !important*/
+	vertical-align: middle;
+}
+
+/* 旋转动画 */
+.mescroll-downwarp .mescroll-rotate {
+	animation: mescrollDownRotate 0.6s linear infinite;
+}
+
+@keyframes mescrollDownRotate {
+	0% {
+		transform: rotate(0deg);
+	}
+
+	100% {
+		transform: rotate(360deg);
+	}
+}

+ 47 - 0
node_modules/mescroll-uni/components/mescroll-down.vue

@@ -0,0 +1,47 @@
+<!-- 下拉刷新区域 -->
+<template>
+	<view v-if="mOption.use" class="mescroll-downwarp" :style="{'background-color':mOption.bgColor,'color':mOption.textColor}">
+		<view class="downwarp-content">
+			<view class="downwarp-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mOption.textColor, 'transform':downRotate}"></view>
+			<view class="downwarp-tip">{{downText}}</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		option: Object , // down的配置项
+		type: Number, // 下拉状态(inOffset:1, outOffset:2, showLoading:3, endDownScroll:4)
+		rate: Number // 下拉比率 (inOffset: rate<1; outOffset: rate>=1)
+	},
+	computed: {
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
+		mOption(){
+			return this.option || {}
+		},
+		// 是否在加载中
+		isDownLoading(){
+			return this.type === 3
+		},
+		// 旋转的角度
+		downRotate(){
+			return 'rotate(' + 360 * this.rate + 'deg)'
+		},
+		// 文本提示
+		downText(){
+			switch (this.type){
+				case 1: return this.mOption.textInOffset;
+				case 2: return this.mOption.textOutOffset;
+				case 3: return this.mOption.textLoading;
+				case 4: return this.mOption.textLoading;
+				default: return this.mOption.textInOffset;
+			}
+		}
+	}
+};
+</script>
+
+<style>
+@import "./mescroll-down.css";
+</style>

+ 118 - 0
node_modules/mescroll-uni/components/mescroll-empty.vue

@@ -0,0 +1,118 @@
+<!--空布局:
+
+可作为独立的组件, 不使用mescroll的页面也能单独引入, 以便APP全局统一管理:
+import MescrollEmpty from '@/components/mescroll-uni/components/mescroll-empty.vue';
+<mescroll-empty v-if="isShowEmpty" :option="optEmpty" @emptyclick="emptyClick"></mescroll-empty>
+-->
+<template>
+	<view class="mescroll-empty" :class="{ 'empty-fixed': option.fixed }" :style="{ 'z-index': option.zIndex, top: option.top }">
+		<view> <image v-if="icon" class="empty-icon" :src="icon" mode="widthFix" /> </view>
+		<view v-if="tip" class="empty-tip">{{ tip }}</view>
+		<view v-if="btnText" class="empty-btn" @click="emptyClick">{{ btnText }}</view>
+	</view>
+</template>
+
+<script>
+// 引入全局配置
+import GlobalOption from './../mescroll-uni-option.js';
+// 引入国际化工具类
+import mescrollI18n from './../mescroll-i18n.js';
+export default {
+	props: {
+		// empty的配置项: 默认为GlobalOption.up.empty
+		option: {
+			type: Object,
+			default() {
+				return {};
+			}
+		}
+	},
+	// 使用computed获取配置,用于支持option的动态配置
+	computed: {
+		// 图标
+		icon() {
+			if (this.option.icon != null) { // 此处不使用短路求值, 用于支持传空串不显示图标
+				return this.option.icon
+			} else{
+				let i18nType = mescrollI18n.getType() // 国际化配置
+				if (this.option.i18n) {
+					return this.option.i18n[i18nType].icon
+				} else{
+					return GlobalOption.i18n[i18nType].up.empty.icon || GlobalOption.up.empty.icon
+				}
+			}
+		},
+		// 文本提示
+		tip() {
+			if (this.option.tip != null) { // 支持传空串不显示文本提示
+				return this.option.tip
+			} else{
+				let i18nType = mescrollI18n.getType() // 国际化配置
+				if (this.option.i18n) {
+					return this.option.i18n[i18nType].tip
+				} else{
+					return GlobalOption.i18n[i18nType].up.empty.tip || GlobalOption.up.empty.tip
+				}
+			}
+		},
+		// 按钮文本
+		btnText() {
+			if (this.option.i18n) {
+				let i18nType = mescrollI18n.getType() // 国际化配置
+				return this.option.i18n[i18nType].btnText
+			} else{
+				return this.option.btnText
+			}
+		}
+	},
+	methods: {
+		// 点击按钮
+		emptyClick() {
+			this.$emit('emptyclick');
+		}
+	}
+};
+</script>
+
+<style>
+/* 无任何数据的空布局 */
+.mescroll-empty {
+	box-sizing: border-box;
+	width: 100%;
+	padding: 100rpx 50rpx;
+	text-align: center;
+}
+
+.mescroll-empty.empty-fixed {
+	z-index: 99;
+	position: absolute; /*transform会使fixed失效,最终会降级为absolute */
+	top: 100rpx;
+	left: 0;
+}
+
+.mescroll-empty .empty-icon {
+	width: 280rpx;
+	height: 280rpx;
+}
+
+.mescroll-empty .empty-tip {
+	margin-top: 20rpx;
+	font-size: 24rpx;
+	color: gray;
+}
+
+.mescroll-empty .empty-btn {
+	display: inline-block;
+	margin-top: 40rpx;
+	min-width: 200rpx;
+	padding: 18rpx;
+	font-size: 28rpx;
+	border: 1rpx solid #e04b28;
+	border-radius: 60rpx;
+	color: #e04b28;
+}
+
+.mescroll-empty .empty-btn:active {
+	opacity: 0.75;
+}
+</style>

+ 83 - 0
node_modules/mescroll-uni/components/mescroll-top.vue

@@ -0,0 +1,83 @@
+<!-- 回到顶部的按钮 -->
+<template>
+	<image
+		v-if="mOption.src"
+		class="mescroll-totop"
+		:class="[value ? 'mescroll-totop-in' : 'mescroll-totop-out', {'mescroll-totop-safearea': mOption.safearea}]"
+		:style="{'z-index':mOption.zIndex, 'left': left, 'right': right, 'bottom':addUnit(mOption.bottom), 'width':addUnit(mOption.width), 'border-radius':addUnit(mOption.radius)}"
+		:src="mOption.src"
+		mode="widthFix"
+		@click="toTopClick"
+	/>
+</template>
+
+<script>
+export default {
+	props: {
+		// up.toTop的配置项
+		option: Object,
+		// 是否显示
+		value: false
+	},
+	computed: {
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
+		mOption(){
+			return this.option || {}
+		},
+		// 优先显示左边
+		left(){
+			return this.mOption.left ? this.addUnit(this.mOption.left) : 'auto';
+		},
+		// 右边距离 (优先显示左边)
+		right() {
+			return this.mOption.left ? 'auto' : this.addUnit(this.mOption.right);
+		}
+	},
+	methods: {
+		addUnit(num){
+			if(!num) return 0;
+			if(typeof num === 'number') return num + 'rpx';
+			return num
+		},
+		toTopClick() {
+			this.$emit('input', false); // 使v-model生效
+			this.$emit('click'); // 派发点击事件
+		}
+	}
+};
+</script>
+
+<style>
+/* 回到顶部的按钮 */
+.mescroll-totop {
+	z-index: 9990;
+	position: fixed !important; /* 加上important避免编译到H5,在多mescroll中定位失效 */
+	right: 20rpx;
+	bottom: 120rpx;
+	width: 72rpx;
+	height: auto;
+	border-radius: 50%;
+	opacity: 0;
+	transition: opacity 0.5s; /* 过渡 */
+	margin-bottom: var(--window-bottom); /* css变量 */
+}
+
+/* 适配 iPhoneX */
+@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
+	.mescroll-totop-safearea {
+		margin-bottom: calc(var(--window-bottom) + constant(safe-area-inset-bottom)); /* window-bottom + 适配 iPhoneX */
+		margin-bottom: calc(var(--window-bottom) + env(safe-area-inset-bottom));
+	}
+}
+
+/* 显示 -- 淡入 */
+.mescroll-totop-in {
+	opacity: 1;
+}
+
+/* 隐藏 -- 淡出且不接收事件*/
+.mescroll-totop-out {
+	opacity: 0;
+	pointer-events: none;
+}
+</style>

+ 47 - 0
node_modules/mescroll-uni/components/mescroll-up.css

@@ -0,0 +1,47 @@
+/* 上拉加载区域 */
+.mescroll-upwarp {
+	box-sizing: border-box;
+	min-height: 110rpx;
+	padding: 30rpx 0;
+	text-align: center;
+	clear: both;
+}
+
+/*提示文本 */
+.mescroll-upwarp .upwarp-tip,
+.mescroll-upwarp .upwarp-nodata {
+	display: inline-block;
+	font-size: 28rpx;
+	vertical-align: middle;
+	/* color: gray; 已在style设置color,此处删去*/
+}
+
+.mescroll-upwarp .upwarp-tip {
+	margin-left: 16rpx;
+}
+
+/*旋转进度条 */
+.mescroll-upwarp .upwarp-progress {
+	display: inline-block;
+	width: 32rpx;
+	height: 32rpx;
+	border-radius: 50%;
+	border: 2rpx solid gray;
+	border-bottom-color: transparent !important; /*已在style设置border-color,此处需加 !important*/
+	vertical-align: middle;
+}
+
+/* 旋转动画 */
+.mescroll-upwarp .mescroll-rotate {
+	animation: mescrollUpRotate 0.6s linear infinite;
+}
+
+@keyframes mescrollUpRotate {
+	0% {
+		transform: rotate(0deg);
+	}
+
+	100% {
+		transform: rotate(360deg);
+	}
+}

+ 39 - 0
node_modules/mescroll-uni/components/mescroll-up.vue

@@ -0,0 +1,39 @@
+<!-- 上拉加载区域 -->
+<template>
+	<view class="mescroll-upwarp" :style="{'background-color':mOption.bgColor,'color':mOption.textColor}">
+		<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
+		<view v-show="isUpLoading">
+			<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mOption.textColor}"></view>
+			<view class="upwarp-tip">{{ mOption.textLoading }}</view>
+		</view>
+		<!-- 无数据 -->
+		<view v-if="isUpNoMore" class="upwarp-nodata">{{ mOption.textNoMore }}</view>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		option: Object, // up的配置项
+		type: Number // 上拉加载的状态:0(loading前),1(loading中),2(没有更多了)
+	},
+	computed: {
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
+		mOption() {
+			return this.option || {};
+		},
+		// 加载中
+		isUpLoading() {
+			return this.type === 1;
+		},
+		// 没有更多了
+		isUpNoMore() {
+			return this.type === 2;
+		}
+	}
+};
+</script>
+
+<style>
+@import './mescroll-up.css';
+</style>

+ 19 - 0
node_modules/mescroll-uni/mescroll-body.css

@@ -0,0 +1,19 @@
+.mescroll-body {
+	position: relative; /* 下拉刷新区域相对自身定位 */
+	height: auto; /* 不可固定高度,否则overflow:hidden导致无法滑动; 同时使设置的最小高生效,实现列表不满屏仍可下拉*/
+	overflow: hidden; /* 当有元素写在mescroll-body标签前面时,可遮住下拉刷新区域 */
+	box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
+}
+
+/* 使sticky生效: 父元素不能overflow:hidden或者overflow:auto属性 */
+.mescroll-body.mescorll-sticky{
+	overflow: unset !important
+}
+
+/* 适配 iPhoneX */
+@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
+	.mescroll-safearea {
+		padding-bottom: constant(safe-area-inset-bottom);
+		padding-bottom: env(safe-area-inset-bottom);
+	}
+}

+ 403 - 0
node_modules/mescroll-uni/mescroll-body.vue

@@ -0,0 +1,403 @@
+<template>
+	<view 
+	class="mescroll-body mescroll-render-touch" 
+	:class="{'mescorll-sticky': sticky}"
+	:style="{'minHeight':minHeight, 'padding-top': padTop, 'padding-bottom': padBottom}" 
+	@touchstart="wxsBiz.touchstartEvent" 
+	@touchmove="wxsBiz.touchmoveEvent" 
+	@touchend="wxsBiz.touchendEvent" 
+	@touchcancel="wxsBiz.touchendEvent"
+	:change:prop="wxsBiz.propObserver"
+	:prop="wxsProp"
+	>
+		<!-- 状态栏 -->
+		<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
+		
+		<view class="mescroll-body-content mescroll-wxs-content" :style="{ transform: translateY, transition: transition }" :change:prop="wxsBiz.callObserver" :prop="callProp">
+			<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
+			<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
+			<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
+				<view class="downwarp-content">
+					<view class="downwarp-progress mescroll-wxs-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mescroll.optDown.textColor, 'transform': downRotate}"></view>
+					<view class="downwarp-tip">{{downText}}</view>
+				</view>
+			</view>
+	
+			<!-- 列表内容 -->
+			<slot></slot>
+
+			<!-- 空布局 -->
+			<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
+
+			<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
+			<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
+			<view v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
+				<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
+				<view v-show="upLoadType===1">
+					<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
+					<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
+				</view>
+				<!-- 无数据 -->
+				<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
+			</view>
+		</view>
+		
+		<!-- 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效) -->
+		<!-- #ifdef H5 -->
+		<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
+		<!-- #endif -->
+		
+		<!-- 适配iPhoneX -->
+		<view v-if="safearea" class="mescroll-safearea"></view>
+		
+		<!-- 回到顶部按钮 (fixed元素需写在transform外面,防止降级为absolute)-->
+		<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
+		
+		<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
+		<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
+		<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
+		<!-- #endif -->
+	</view>
+</template>
+
+<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
+<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
+<script src="./wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
+<!-- #endif -->
+
+<!-- app, h5使用renderjs -->
+<!-- #ifdef APP-PLUS || H5 -->
+<script module="renderBiz" lang="renderjs">
+	import renderBiz from './wxs/renderjs.js';
+	export default {
+		mixins: [renderBiz]
+	}
+</script>
+<!-- #endif -->
+
+<script>
+	// 引入mescroll-uni.js,处理核心逻辑
+	import MeScroll from './mescroll-uni.js';
+	// 引入全局配置
+	import GlobalOption from './mescroll-uni-option.js';
+	// 引入空布局组件
+	import MescrollEmpty from './components/mescroll-empty.vue';
+	// 引入国际化工具类
+	import mescrollI18n from './mescroll-i18n.js';
+	// 引入回到顶部组件
+	import MescrollTop from './components/mescroll-top.vue';
+	// 引入兼容wxs(含renderjs)写法的mixins
+	import WxsMixin from './wxs/mixins.js';
+	
+	/**
+	 * mescroll-body 基于page滚动的下拉刷新和上拉加载组件, 支持嵌套原生组件, 性能好
+	 * @property {Object} down 下拉刷新的参数配置
+	 * @property {Object} up 上拉加载的参数配置
+	 * @property {Object} i18n 国际化的参数配置
+	 * @property {String, Number} top 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+	 * @property {Boolean, String} topbar 偏移量top是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
+	 * @property {String, Number} bottom 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+	 * @property {Boolean} safearea 偏移量bottom是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
+	 * @property {Boolean} fixed 是否通过fixed固定mescroll的高度, 默认true
+	 * @property {String, Number} height 指定mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
+	 * @property {Boolean} bottombar 底部是否偏移TabBar的高度 (仅在H5端的tab页生效)
+	 * @property {Boolean} sticky 是否支持sticky,默认false; 当值配置true时,需避免在mescroll-body标签前面加非定位的元素,否则下拉区域无法隐藏
+	 * @event {Function} init 初始化完成的回调 
+	 * @event {Function} down 下拉刷新的回调
+	 * @event {Function} up 上拉加载的回调 
+	 * @event {Function} emptyclick 点击empty配置的btnText按钮回调
+	 * @event {Function} topclick 点击回到顶部的按钮回调
+	 * @event {Function} scroll 滚动监听 (需在 up 配置 onScroll:true 才生效)
+	 * @example <mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback"> ... </mescroll-body>
+	 */
+	export default {
+		name: 'mescroll-body',
+		mixins: [WxsMixin],
+		components: {
+			MescrollEmpty,
+			MescrollTop
+		},
+		props: {
+			down: Object,
+			up: Object,
+			i18n: Object,
+			top: [String, Number],
+			topbar: [Boolean, String],
+			bottom: [String, Number],
+			safearea: Boolean,
+			height: [String, Number],
+			bottombar:{
+				type: Boolean,
+				default: true
+			},
+			sticky: Boolean
+		},
+		data() {
+			return {
+				mescroll: {optDown:{},optUp:{}}, // mescroll实例
+				downHight: 0, //下拉刷新: 容器高度
+				downRate: 0, // 下拉比率(inOffset: rate<1; outOffset: rate>=1)
+				downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
+				upLoadType: 0, // 上拉加载状态:0(loading前),1(loading中),2(没有更多了,显示END文本提示),3(没有更多了,不显示END文本提示)
+				isShowEmpty: false, // 是否显示空布局
+				isShowToTop: false, // 是否显示回到顶部按钮
+				windowHeight: 0, // 可使用窗口的高度
+				windowBottom: 0, // 可使用窗口的底部位置
+				statusBarHeight: 0 // 状态栏高度
+			};
+		},
+		computed: {
+			// mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
+			minHeight(){
+				return this.toPx(this.height || '100%') + 'px'
+			},
+			// 下拉布局往下偏移的距离 (px)
+			numTop() {
+				return this.toPx(this.top)
+			},
+			padTop() {
+				return this.numTop + 'px';
+			},
+			// 上拉布局往上偏移 (px)
+			numBottom() {
+				return this.toPx(this.bottom);
+			},
+			padBottom() {
+				return this.numBottom + 'px';
+			},
+			// 是否为重置下拉的状态
+			isDownReset() {
+				return this.downLoadType === 3 || this.downLoadType === 4;
+			},
+			// 过渡
+			transition() {
+				return this.isDownReset ? 'transform 300ms' : '';
+			},
+			translateY() {
+				return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : ''; // transform会使fixed失效,需注意把fixed元素写在mescroll之外
+			},
+			// 是否在加载中
+			isDownLoading(){
+				return this.downLoadType === 3
+			},
+			// 旋转的角度
+			downRotate(){
+				return 'rotate(' + 360 * this.downRate + 'deg)'
+			},
+			// 文本提示
+			downText(){
+				if(!this.mescroll) return ""; // 避免头条小程序初始化时报错
+				switch (this.downLoadType){
+					case 1: return this.mescroll.optDown.textInOffset;
+					case 2: return this.mescroll.optDown.textOutOffset;
+					case 3: return this.mescroll.optDown.textLoading;
+					case 4: return this.mescroll.isDownEndSuccess ? this.mescroll.optDown.textSuccess : this.mescroll.isDownEndSuccess==false ? this.mescroll.optDown.textErr : this.mescroll.optDown.textInOffset;
+					default: return this.mescroll.optDown.textInOffset;
+				}
+			}
+		},
+		methods: {
+			//number,rpx,upx,px,% --> px的数值
+			toPx(num) {
+				if (typeof num === 'string') {
+					if (num.indexOf('px') !== -1) {
+						if (num.indexOf('rpx') !== -1) {
+							// "10rpx"
+							num = num.replace('rpx', '');
+						} else if (num.indexOf('upx') !== -1) {
+							// "10upx"
+							num = num.replace('upx', '');
+						} else {
+							// "10px"
+							return Number(num.replace('px', ''));
+						}
+					} else if (num.indexOf('%') !== -1) {
+						// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
+						let rate = Number(num.replace('%', '')) / 100;
+						return this.windowHeight * rate;
+					}
+				}
+				return num ? uni.upx2px(Number(num)) : 0;
+			},
+			// 点击空布局的按钮回调
+			emptyClick() {
+				this.$emit('emptyclick', this.mescroll);
+			},
+			// 点击回到顶部的按钮回调
+			toTopClick() {
+				this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
+				this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
+			}
+		},
+		// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
+		created() {
+			let vm = this;
+
+			let diyOption = {
+				// 下拉刷新的配置
+				down: {
+					inOffset() {
+						vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
+					},
+					outOffset() {
+						vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
+					},
+					onMoving(mescroll, rate, downHight) {
+						// 下拉过程中的回调,滑动过程一直在执行;
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						vm.downRate = rate; //下拉比率 (inOffset: rate<1; outOffset: rate>=1)
+					},
+					showLoading(mescroll, downHight) {
+						vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+					},
+					beforeEndDownScroll(mescroll){
+						vm.downLoadType = 4; 
+						return mescroll.optDown.beforeEndDelay // 延时结束的时长
+					},
+					endDownScroll() {
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						if(vm.downResetTimer) {clearTimeout(vm.downResetTimer); vm.downResetTimer = null} // 移除重置倒计时
+						vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,避免下次inOffset不及时显示textInOffset
+							if(vm.downLoadType === 4) vm.downLoadType = 0
+						},300)
+					},
+					// 派发下拉刷新的回调
+					callback: function(mescroll) {
+						vm.$emit('down', mescroll);
+					}
+				},
+				// 上拉加载的配置
+				up: {
+					// 显示加载中的回调
+					showLoading() {
+						vm.upLoadType = 1;
+					},
+					// 显示无更多数据的回调
+					showNoMore() {
+						vm.upLoadType = 2;
+					},
+					// 隐藏上拉加载的回调
+					hideUpScroll(mescroll) {
+						vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
+					},
+					// 空布局
+					empty: {
+						onShow(isShow) {
+							// 显示隐藏的回调
+							vm.isShowEmpty = isShow;
+						}
+					},
+					// 回到顶部
+					toTop: {
+						onShow(isShow) {
+							// 显示隐藏的回调
+							vm.isShowToTop = isShow;
+						}
+					},
+					// 派发上拉加载的回调
+					callback: function(mescroll) {
+						vm.$emit('up', mescroll);
+					}
+				}
+			};
+			
+			let i18nType = mescrollI18n.getType() // 当前语言类型
+			let i18nOption = {type: i18nType} // 国际化配置
+			MeScroll.extend(i18nOption, vm.i18n) // 具体页面的国际化配置
+			MeScroll.extend(i18nOption, GlobalOption.i18n) // 全局的国际化配置
+			MeScroll.extend(diyOption, i18nOption[i18nType]); // 混入国际化配置
+			MeScroll.extend(diyOption, {down:GlobalOption.down, up:GlobalOption.up}); // 混入全局的配置
+			let myOption = JSON.parse(JSON.stringify({down: vm.down,up: vm.up})); // 深拷贝,避免对props的影响
+			MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
+
+			// 初始化MeScroll对象
+			vm.mescroll = new MeScroll(myOption, true); // 传入true,标记body为滚动区域
+			// 挂载语言包
+			vm.mescroll.i18n = i18nOption;
+			// init回调mescroll对象
+			vm.$emit('init', vm.mescroll);
+
+			// 设置高度
+			const sys = uni.getSystemInfoSync();
+			if (sys.windowHeight) vm.windowHeight = sys.windowHeight;
+			if (sys.windowBottom) vm.windowBottom = sys.windowBottom;
+			if (sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
+			// 使down的bottomOffset生效
+			vm.mescroll.setBodyHeight(sys.windowHeight);
+
+			// 因为使用的是page的scroll,这里需自定义scrollTo
+			vm.mescroll.resetScrollTo((y, t) => {
+				if(typeof y === 'string'){
+					// 滚动到指定view (y为css选择器)
+					setTimeout(()=>{ // 延时确保view已渲染; 不使用$nextTick
+						let selector;
+						if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
+							selector = '#'+y // 不带#和. 则默认为id选择器
+						}else{
+							selector = y
+							// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
+							if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
+								selector = y.split('>>>')[1].trim()
+							}
+							// #endif
+						}
+						uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
+							if (rect) {
+								let top = rect.top
+								top += vm.mescroll.getScrollTop()
+								uni.pageScrollTo({
+									scrollTop: top,
+									duration: t
+								})
+							} else{
+								console.error(selector + ' does not exist');
+							}
+						}).exec()
+					},30)
+				} else{
+					// 滚动到指定位置 (y必须为数字)
+					uni.pageScrollTo({
+						scrollTop: y,
+						duration: t
+					})
+				}
+			});
+
+			// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
+			if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
+				vm.mescroll.optUp.toTop.safearea = vm.safearea;
+			}
+			
+			// 全局配置监听
+			uni.$on("setMescrollGlobalOption", options=>{
+				if(!options) return;
+				let i18nType = options.i18n ? options.i18n.type : null
+				if(i18nType && vm.mescroll.i18n.type != i18nType){
+					vm.mescroll.i18n.type = i18nType
+					mescrollI18n.setType(i18nType)
+					MeScroll.extend(options, vm.mescroll.i18n[i18nType])
+				}
+				if(options.down){
+					let down = MeScroll.extend({}, options.down)
+					vm.mescroll.optDown = MeScroll.extend(down, vm.mescroll.optDown)
+				}
+				if(options.up){
+					let up = MeScroll.extend({}, options.up)
+					vm.mescroll.optUp = MeScroll.extend(up, vm.mescroll.optUp)
+				}
+			})
+		},
+		destroyed() {
+			// 注销全局配置监听
+			uni.$off("setMescrollGlobalOption")
+		}
+	};
+</script>
+
+<style>
+	@import "./mescroll-body.css";
+	@import "./components/mescroll-down.css";
+	@import './components/mescroll-up.css';
+</style>

+ 15 - 0
node_modules/mescroll-uni/mescroll-i18n.js

@@ -0,0 +1,15 @@
+// 国际化工具类
+const mescrollI18n = {
+	// 默认语言
+	def: "zh",
+	// 获取当前语言类型
+	getType(){
+		return uni.getStorageSync("mescroll-i18n") || this.def
+	},
+	// 设置当前语言类型
+	setType(type){
+		uni.setStorageSync("mescroll-i18n", type)
+	}
+}
+
+export default mescrollI18n

+ 57 - 0
node_modules/mescroll-uni/mescroll-mixins.js

@@ -0,0 +1,57 @@
+// mescroll-body 和 mescroll-uni 通用
+const MescrollMixin = {
+	data() {
+		return {
+			mescroll: null //mescroll实例对象
+		}
+	},
+	// 注册系统自带的下拉刷新 (配置down.native为true时生效, 还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
+	onPullDownRefresh(){
+		this.mescroll && this.mescroll.onPullDownRefresh();
+	},
+	// 注册列表滚动事件,用于判定在顶部可下拉刷新,在指定位置可显示隐藏回到顶部按钮 (此方法为页面生命周期,无法在子组件中触发, 仅在mescroll-body生效)
+	onPageScroll(e) {
+		this.mescroll && this.mescroll.onPageScroll(e);
+	},
+	// 注册滚动到底部的事件,用于上拉加载 (此方法为页面生命周期,无法在子组件中触发, 仅在mescroll-body生效)
+	onReachBottom() {
+		this.mescroll && this.mescroll.onReachBottom();
+	},
+	methods: {
+		// mescroll组件初始化的回调,可获取到mescroll对象
+		mescrollInit(mescroll) {
+			this.mescroll = mescroll;
+			this.mescrollInitByRef(); // 兼容字节跳动小程序
+		},
+		// 以ref的方式初始化mescroll对象 (兼容字节跳动小程序)
+		mescrollInitByRef() {
+			if(!this.mescroll || !this.mescroll.resetUpScroll){
+				let mescrollRef = this.$refs.mescrollRef;
+				if(mescrollRef) this.mescroll = mescrollRef.mescroll
+			}
+		},
+		// 下拉刷新的回调 (mixin默认resetUpScroll)
+		downCallback() {
+			if(this.mescroll.optUp.use){
+				this.mescroll.resetUpScroll()
+			}else{
+				setTimeout(()=>{
+					this.mescroll.endSuccess();
+				}, 500)
+			}
+		},
+		// 上拉加载的回调
+		upCallback() {
+			// mixin默认延时500自动结束加载
+			setTimeout(()=>{
+				this.mescroll.endErr();
+			}, 500)
+		}
+	},
+	mounted() {
+		this.mescrollInitByRef(); // 兼容字节跳动小程序, 避免未设置@init或@init此时未能取到ref的情况
+	}
+	
+}
+
+export default MescrollMixin;

+ 64 - 0
node_modules/mescroll-uni/mescroll-uni-option.js

@@ -0,0 +1,64 @@
+// 全局配置
+// mescroll-body 和 mescroll-uni 通用
+const GlobalOption = {
+	down: {
+		// 其他down的配置参数也可以写,这里只展示了常用的配置:
+		offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
+		native: false // 是否使用系统自带的下拉刷新; 默认false; 仅在mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
+	},
+	up: {
+		// 其他up的配置参数也可以写,这里只展示了常用的配置:
+		offset: 150, // 距底部多远时,触发upCallback,仅mescroll-uni生效 ( mescroll-body配置的是pages.json的 onReachBottomDistance )
+		toTop: {
+			// 回到顶部按钮,需配置src才显示
+			src: "https://www.mescroll.com/img/mescroll-totop.png", // 图片路径 (建议放入static目录, 如 /static/img/mescroll-totop.png )
+			offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000px
+			right: 20, // 到右边的距离, 默认20 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+			bottom: 120, // 到底部的距离, 默认120 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+			width: 72 // 回到顶部图标的宽度, 默认72 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+		},
+		empty: {
+			use: true, // 是否显示空布局
+			icon: "https://www.mescroll.com/img/mescroll-empty.png" // 图标路径 (建议放入static目录, 如 /static/img/mescroll-empty.png )
+		}
+	},
+	// 国际化配置
+	i18n: {
+		// 中文
+		zh: {
+			down: {
+				textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
+				textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
+				textLoading: '加载中 ...', // 加载中的提示文本
+				textSuccess: '加载成功', // 加载成功的文本
+				textErr: '加载失败', // 加载失败的文本
+			},
+			up: {
+				textLoading: '加载中 ...', // 加载中的提示文本
+				textNoMore: '-- END --', // 没有更多数据的提示文本
+				empty: {
+					tip: '~ 空空如也 ~' // 空提示
+				}
+			}
+		},
+		// 英文
+		en: {
+			down: {
+				textInOffset: 'drop down refresh',
+				textOutOffset: 'release updates',
+				textLoading: 'loading ...',
+				textSuccess: 'loaded successfully',
+				textErr: 'loading failed'
+			},
+			up: {
+				textLoading: 'loading ...',
+				textNoMore: '-- END --',
+				empty: {
+					tip: '~ absolutely empty ~'
+				}
+			}
+		}
+	}
+}
+
+export default GlobalOption

+ 36 - 0
node_modules/mescroll-uni/mescroll-uni.css

@@ -0,0 +1,36 @@
+.mescroll-uni-warp{
+	height: 100%;
+}
+
+.mescroll-uni-content{
+	height: 100%;
+}
+
+.mescroll-uni {
+	position: relative;
+	width: 100%;
+	height: 100%;
+	min-height: 200rpx;
+	overflow-y: auto;
+	box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
+}
+
+/* 定位的方式固定高度 */
+.mescroll-uni-fixed{
+	z-index: 1;
+	position: fixed;
+	top: 0;
+	left: 0;
+	right: 0;
+	bottom: 0;
+	width: auto; /* 使right生效 */
+	height: auto; /* 使bottom生效 */
+}
+
+/* 适配 iPhoneX */
+@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
+	.mescroll-safearea {
+		padding-bottom: constant(safe-area-inset-bottom);
+		padding-bottom: env(safe-area-inset-bottom);
+	}
+}

+ 799 - 0
node_modules/mescroll-uni/mescroll-uni.js

@@ -0,0 +1,799 @@
+/* mescroll
+ * version 1.3.7
+ * 2021-04-13 wenju
+ * https://www.mescroll.com
+ */
+
+export default function MeScroll(options, isScrollBody) {
+	let me = this;
+	me.version = '1.3.7'; // mescroll版本号
+	me.options = options || {}; // 配置
+	me.isScrollBody = isScrollBody || false; // 滚动区域是否为原生页面滚动; 默认为scroll-view
+
+	me.isDownScrolling = false; // 是否在执行下拉刷新的回调
+	me.isUpScrolling = false; // 是否在执行上拉加载的回调
+	let hasDownCallback = me.options.down && me.options.down.callback; // 是否配置了down的callback
+
+	// 初始化下拉刷新
+	me.initDownScroll();
+	// 初始化上拉加载,则初始化
+	me.initUpScroll();
+
+	// 自动加载
+	setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
+		// 自动触发下拉刷新 (只有配置了down的callback才自动触发下拉刷新)
+		if ((me.optDown.use || me.optDown.native) && me.optDown.auto && hasDownCallback) {
+			if (me.optDown.autoShowLoading) {
+				me.triggerDownScroll(); // 显示下拉进度,执行下拉回调
+			} else {
+				me.optDown.callback && me.optDown.callback(me); // 不显示下拉进度,直接执行下拉回调
+			}
+		}
+		// 自动触发上拉加载
+		if(!me.isUpAutoLoad){ // 部分小程序(头条小程序)emit是异步, 会导致isUpAutoLoad判断有误, 先延时确保先执行down的callback,再执行up的callback
+			setTimeout(function(){
+				me.optUp.use && me.optUp.auto && !me.isUpAutoLoad && me.triggerUpScroll();
+			},100)
+		}
+	}, 30); // 需让me.optDown.inited和me.optUp.inited先执行
+}
+
+/* 配置参数:下拉刷新 */
+MeScroll.prototype.extendDownScroll = function(optDown) {
+	// 下拉刷新的配置
+	MeScroll.extend(optDown, {
+		use: true, // 是否启用下拉刷新; 默认true
+		auto: true, // 是否在初始化完毕之后自动执行下拉刷新的回调; 默认true
+		native: false, // 是否使用系统自带的下拉刷新; 默认false; 仅mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
+		autoShowLoading: false, // 如果设置auto=true(在初始化完毕之后自动执行下拉刷新的回调),那么是否显示下拉刷新的进度; 默认false
+		isLock: false, // 是否锁定下拉刷新,默认false;
+		offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
+		startTop: 100, // scroll-view快速滚动到顶部时,此时的scroll-top可能大于0, 此值用于控制最大的误差
+		inOffsetRate: 1, // 在列表顶部,下拉的距离小于offset时,改变下拉区域高度比例;值小于1且越接近0,高度变化越小,表现为越往下越难拉
+		outOffsetRate: 0.2, // 在列表顶部,下拉的距离大于offset时,改变下拉区域高度比例;值小于1且越接近0,高度变化越小,表现为越往下越难拉
+		bottomOffset: 20, // 当手指touchmove位置在距离body底部20px范围内的时候结束上拉刷新,避免Webview嵌套导致touchend事件不执行
+		minAngle: 45, // 向下滑动最少偏移的角度,取值区间  [0,90];默认45度,即向下滑动的角度大于45度则触发下拉;而小于45度,将不触发下拉,避免与左右滑动的轮播等组件冲突;
+		textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
+		textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
+		textLoading: '加载中 ...', // 加载中的提示文本
+		textSuccess: '加载成功', // 加载成功的文本
+		textErr: '加载失败', // 加载失败的文本
+		beforeEndDelay: 0, // 延时结束的时长 (显示加载成功/失败的时长, android小程序设置此项结束下拉会卡顿, 配置后请注意测试)
+		bgColor: "transparent", // 背景颜色 (建议在pages.json中再设置一下backgroundColorTop)
+		textColor: "gray", // 文本颜色 (当bgColor配置了颜色,而textColor未配置时,则textColor会默认为白色)
+		inited: null, // 下拉刷新初始化完毕的回调
+		inOffset: null, // 下拉的距离进入offset范围内那一刻的回调
+		outOffset: null, // 下拉的距离大于offset那一刻的回调
+		onMoving: null, // 下拉过程中的回调,滑动过程一直在执行; rate下拉区域当前高度与指定距离的比值(inOffset: rate<1; outOffset: rate>=1); downHight当前下拉区域的高度
+		beforeLoading: null, // 准备触发下拉刷新的回调: 如果return true,将不触发showLoading和callback回调; 常用来完全自定义下拉刷新, 参考案例【淘宝 v6.8.0】
+		showLoading: null, // 显示下拉刷新进度的回调
+		afterLoading: null, // 显示下拉刷新进度的回调之后,马上要执行的代码 (如: 在wxs中使用)
+		beforeEndDownScroll: null, // 准备结束下拉的回调. 返回结束下拉的延时执行时间,默认0ms; 常用于结束下拉之前再显示另外一小段动画,才去隐藏下拉刷新的场景, 参考案例【dotJump】
+		endDownScroll: null, // 结束下拉刷新的回调
+		afterEndDownScroll: null, // 结束下拉刷新的回调,马上要执行的代码 (如: 在wxs中使用)
+		callback: function(mescroll) {
+			// 下拉刷新的回调;默认重置上拉加载列表为第一页
+			mescroll.resetUpScroll();
+		}
+	})
+}
+
+/* 配置参数:上拉加载 */
+MeScroll.prototype.extendUpScroll = function(optUp) {
+	// 上拉加载的配置
+	MeScroll.extend(optUp, {
+		use: true, // 是否启用上拉加载; 默认true
+		auto: true, // 是否在初始化完毕之后自动执行上拉加载的回调; 默认true
+		isLock: false, // 是否锁定上拉加载,默认false;
+		isBoth: true, // 上拉加载时,如果滑动到列表顶部是否可以同时触发下拉刷新;默认true,两者可同时触发;
+		callback: null, // 上拉加载的回调;function(page,mescroll){ }
+		page: {
+			num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
+			size: 10, // 每页数据的数量
+			time: null // 加载第一页数据服务器返回的时间; 防止用户翻页时,后台新增了数据从而导致下一页数据重复;
+		},
+		noMoreSize: 5, // 如果列表已无数据,可设置列表的总数量要大于等于5条才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看
+		offset: 150, // 距底部多远时,触发upCallback,仅mescroll-uni生效 ( mescroll-body配置的是pages.json的 onReachBottomDistance )
+		textLoading: '加载中 ...', // 加载中的提示文本
+		textNoMore: '-- END --', // 没有更多数据的提示文本
+		bgColor: "transparent", // 背景颜色 (建议在pages.json中再设置一下backgroundColorBottom)
+		textColor: "gray", // 文本颜色 (当bgColor配置了颜色,而textColor未配置时,则textColor会默认为白色)
+		inited: null, // 初始化完毕的回调
+		showLoading: null, // 显示加载中的回调
+		showNoMore: null, // 显示无更多数据的回调
+		hideUpScroll: null, // 隐藏上拉加载的回调
+		errDistance: 60, // endErr的时候需往上滑动一段距离,使其往下滑动时再次触发onReachBottom,仅mescroll-body生效
+		toTop: {
+			// 回到顶部按钮,需配置src才显示
+			src: null, // 图片路径,默认null (绝对路径或网络图)
+			offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000
+			duration: 300, // 回到顶部的动画时长,默认300ms (当值为0或300则使用系统自带回到顶部,更流畅; 其他值则通过step模拟,部分机型可能不够流畅,所以非特殊情况不建议修改此项)
+			btnClick: null, // 点击按钮的回调
+			onShow: null, // 是否显示的回调
+			zIndex: 9990, // fixed定位z-index值
+			left: null, // 到左边的距离, 默认null. 此项有值时,right不生效. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+			right: 20, // 到右边的距离, 默认20 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+			bottom: 120, // 到底部的距离, 默认120 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+			safearea: false, // bottom的偏移量是否加上底部安全区的距离, 默认false, 需要适配iPhoneX时使用 (具体的界面如果不配置此项,则取本vue的safearea值)
+			width: 72, // 回到顶部图标的宽度, 默认72 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+			radius: "50%" // 圆角, 默认"50%" (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+		},
+		empty: {
+			use: true, // 是否显示空布局
+			icon: null, // 图标路径
+			tip: '~ 暂无相关数据 ~', // 提示
+			btnText: '', // 按钮
+			btnClick: null, // 点击按钮的回调
+			onShow: null, // 是否显示的回调
+			fixed: false, // 是否使用fixed定位,默认false; 配置fixed为true,以下的top和zIndex才生效 (transform会使fixed失效,最终会降级为absolute)
+			top: "100rpx", // fixed定位的top值 (完整的单位值,如 "10%"; "100rpx")
+			zIndex: 99 // fixed定位z-index值
+		},
+		onScroll: false // 是否监听滚动事件
+	})
+}
+
+/* 配置参数 */
+MeScroll.extend = function(userOption, defaultOption) {
+	if (!userOption) return defaultOption;
+	for (let key in defaultOption) {
+		if (userOption[key] == null) {
+			let def = defaultOption[key];
+			if (def != null && typeof def === 'object') {
+				userOption[key] = MeScroll.extend({}, def); // 深度匹配
+			} else {
+				userOption[key] = def;
+			}
+		} else if (typeof userOption[key] === 'object') {
+			MeScroll.extend(userOption[key], defaultOption[key]); // 深度匹配
+		}
+	}
+	return userOption;
+}
+
+/* 简单判断是否配置了颜色 (非透明,非白色) */
+MeScroll.prototype.hasColor = function(color) {
+	if(!color) return false;
+	let c = color.toLowerCase();
+	return c != "#fff" && c != "#ffffff" && c != "transparent" && c != "white"
+}
+
+/* -------初始化下拉刷新------- */
+MeScroll.prototype.initDownScroll = function() {
+	let me = this;
+	// 配置参数
+	me.optDown = me.options.down || {};
+	if(!me.optDown.textColor && me.hasColor(me.optDown.bgColor)) me.optDown.textColor = "#fff"; // 当bgColor有值且textColor未设置,则textColor默认白色
+	me.extendDownScroll(me.optDown);
+	
+	// 如果是mescroll-body且配置了native,则禁止自定义的下拉刷新
+	if(me.isScrollBody && me.optDown.native){
+		me.optDown.use = false
+	}else{
+		me.optDown.native = false // 仅mescroll-body支持,mescroll-uni不支持
+	}
+	
+	me.downHight = 0; // 下拉区域的高度
+
+	// 在页面中加入下拉布局
+	if (me.optDown.use && me.optDown.inited) {
+		// 初始化完毕的回调
+		setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
+			me.optDown.inited(me);
+		}, 0)
+	}
+}
+
+/* 列表touchstart事件 */
+MeScroll.prototype.touchstartEvent = function(e) {
+	if (!this.optDown.use) return;
+
+	this.startPoint = this.getPoint(e); // 记录起点
+	this.startTop = this.getScrollTop(); // 记录此时的滚动条位置
+	this.startAngle = 0; // 初始角度
+	this.lastPoint = this.startPoint; // 重置上次move的点
+	this.maxTouchmoveY = this.getBodyHeight() - this.optDown.bottomOffset; // 手指触摸的最大范围(写在touchstart避免body获取高度为0的情况)
+	this.inTouchend = false; // 标记不是touchend
+}
+
+/* 列表touchmove事件 */
+MeScroll.prototype.touchmoveEvent = function(e) {
+	if (!this.optDown.use) return;
+	let me = this;
+
+	let scrollTop = me.getScrollTop(); // 当前滚动条的距离
+	let curPoint = me.getPoint(e); // 当前点
+
+	let moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+
+	// 向下拉 && 在顶部
+	// mescroll-body,直接判定在顶部即可
+	// scroll-view在滚动时不会触发touchmove,当触顶/底/左/右时,才会触发touchmove
+	// scroll-view滚动到顶部时,scrollTop不一定为0,也有可能大于0; 在iOS的APP中scrollTop可能为负数,不一定和startTop相等
+	if (moveY > 0 && (
+			(me.isScrollBody && scrollTop <= 0)
+			||
+			(!me.isScrollBody && (scrollTop <= 0 || (scrollTop <= me.optDown.startTop && scrollTop === me.startTop)) )
+		)) {
+		// 可下拉的条件
+		if (!me.inTouchend && !me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling &&
+				me.optUp.isBoth))) {
+
+			// 下拉的初始角度是否在配置的范围内
+			if(!me.startAngle) me.startAngle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90]
+			if (me.startAngle < me.optDown.minAngle) return; // 如果小于配置的角度,则不往下执行下拉刷新
+
+			// 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发
+			if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) {
+				me.inTouchend = true; // 标记执行touchend
+				me.touchendEvent(); // 提前触发touchend
+				return;
+			}
+			
+			me.preventDefault(e); // 阻止默认事件
+
+			let diff = curPoint.y - me.lastPoint.y; // 和上次比,移动的距离 (大于0向下,小于0向上)
+
+			// 下拉距离  < 指定距离
+			if (me.downHight < me.optDown.offset) {
+				if (me.movetype !== 1) {
+					me.movetype = 1; // 加入标记,保证只执行一次
+					me.isDownEndSuccess = null; // 重置是否加载成功的状态 (wxs执行的是wxs.wxs)
+					me.optDown.inOffset && me.optDown.inOffset(me); // 进入指定距离范围内那一刻的回调,只执行一次
+					me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
+				}
+				me.downHight += diff * me.optDown.inOffsetRate; // 越往下,高度变化越小
+
+				// 指定距离  <= 下拉距离
+			} else {
+				if (me.movetype !== 2) {
+					me.movetype = 2; // 加入标记,保证只执行一次
+					me.optDown.outOffset && me.optDown.outOffset(me); // 下拉超过指定距离那一刻的回调,只执行一次
+					me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
+				}
+				if (diff > 0) { // 向下拉
+					me.downHight += diff * me.optDown.outOffsetRate; // 越往下,高度变化越小
+				} else { // 向上收
+					me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
+				}
+			}
+			
+			me.downHight = Math.round(me.downHight) // 取整
+			let rate = me.downHight / me.optDown.offset; // 下拉区域当前高度与指定距离的比值
+			me.optDown.onMoving && me.optDown.onMoving(me, rate, me.downHight); // 下拉过程中的回调,一直在执行
+		}
+	}
+
+	me.lastPoint = curPoint; // 记录本次移动的点
+}
+
+/* 列表touchend事件 */
+MeScroll.prototype.touchendEvent = function(e) {
+	if (!this.optDown.use) return;
+	// 如果下拉区域高度已改变,则需重置回来
+	if (this.isMoveDown) {
+		if (this.downHight >= this.optDown.offset) {
+			// 符合触发刷新的条件
+			this.triggerDownScroll();
+		} else {
+			// 不符合的话 则重置
+			this.downHight = 0;
+			this.endDownScrollCall(this);
+		}
+		this.movetype = 0;
+		this.isMoveDown = false;
+	} else if (!this.isScrollBody && this.getScrollTop() === this.startTop) { // scroll-view到顶/左/右/底的滑动事件
+		let isScrollUp = this.getPoint(e).y - this.startPoint.y < 0; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+		// 上滑
+		if (isScrollUp) {
+			// 需检查滑动的角度
+			let angle = this.getAngle(this.getPoint(e), this.startPoint); // 两点之间的角度,区间 [0,90]
+			if (angle > 80) {
+				// 检查并触发上拉
+				this.triggerUpScroll(true);
+			}
+		}
+	}
+}
+
+/* 根据点击滑动事件获取第一个手指的坐标 */
+MeScroll.prototype.getPoint = function(e) {
+	if (!e) {
+		return {
+			x: 0,
+			y: 0
+		}
+	}
+	if (e.touches && e.touches[0]) {
+		return {
+			x: e.touches[0].pageX,
+			y: e.touches[0].pageY
+		}
+	} else if (e.changedTouches && e.changedTouches[0]) {
+		return {
+			x: e.changedTouches[0].pageX,
+			y: e.changedTouches[0].pageY
+		}
+	} else {
+		return {
+			x: e.clientX,
+			y: e.clientY
+		}
+	}
+}
+
+/* 计算两点之间的角度: 区间 [0,90]*/
+MeScroll.prototype.getAngle = function(p1, p2) {
+	let x = Math.abs(p1.x - p2.x);
+	let y = Math.abs(p1.y - p2.y);
+	let z = Math.sqrt(x * x + y * y);
+	let angle = 0;
+	if (z !== 0) {
+		angle = Math.asin(y / z) / Math.PI * 180;
+	}
+	return angle
+}
+
+/* 触发下拉刷新 */
+MeScroll.prototype.triggerDownScroll = function() {
+	if (this.optDown.beforeLoading && this.optDown.beforeLoading(this)) {
+		//return true则处于完全自定义状态
+	} else {
+		this.showDownScroll(); // 下拉刷新中...
+		!this.optDown.native && this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据
+	}
+}
+
+/* 显示下拉进度布局 */
+MeScroll.prototype.showDownScroll = function() {
+	this.isDownScrolling = true; // 标记下拉中
+	if (this.optDown.native) {
+		uni.startPullDownRefresh(); // 系统自带的下拉刷新
+		this.showDownLoadingCall(0); // 仍触发showLoading,因为上拉加载用到
+	} else{
+		this.downHight = this.optDown.offset; // 更新下拉区域高度
+		this.showDownLoadingCall(this.downHight); // 下拉刷新中...
+	}
+}
+
+MeScroll.prototype.showDownLoadingCall = function(downHight) {
+	this.optDown.showLoading && this.optDown.showLoading(this, downHight); // 下拉刷新中...
+	this.optDown.afterLoading && this.optDown.afterLoading(this, downHight); // 下拉刷新中...触发之后马上要执行的代码
+}
+
+/* 显示系统自带的下拉刷新时需要处理的业务 */
+MeScroll.prototype.onPullDownRefresh = function() {
+	this.isDownScrolling = true; // 标记下拉中
+	this.showDownLoadingCall(0); // 仍触发showLoading,因为上拉加载用到
+	this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据
+}
+
+/* 结束下拉刷新 */
+MeScroll.prototype.endDownScroll = function() {
+	if (this.optDown.native) { // 结束原生下拉刷新
+		this.isDownScrolling = false;
+		this.endDownScrollCall(this);
+		uni.stopPullDownRefresh();
+		return
+	}
+	let me = this;
+	// 结束下拉刷新的方法
+	let endScroll = function() {
+		me.downHight = 0;
+		me.isDownScrolling = false;
+		me.endDownScrollCall(me);
+		if(!me.isScrollBody){
+			me.setScrollHeight(0) // scroll-view重置滚动区域,使数据不满屏时仍可检查触发翻页
+			me.scrollTo(0,0) // scroll-view需重置滚动条到顶部,避免startTop大于0时,对下拉刷新的影响
+		}
+	}
+	// 结束下拉刷新时的回调
+	let delay = 0;
+	if (me.optDown.beforeEndDownScroll) {
+		delay = me.optDown.beforeEndDownScroll(me); // 结束下拉刷新的延时,单位ms
+		if(me.isDownEndSuccess == null) delay = 0; // 没有执行加载中,则不延时
+	}
+	if (typeof delay === 'number' && delay > 0) {
+		setTimeout(endScroll, delay);
+	} else {
+		endScroll();
+	}
+}
+
+MeScroll.prototype.endDownScrollCall = function() {
+	this.optDown.endDownScroll && this.optDown.endDownScroll(this);
+	this.optDown.afterEndDownScroll && this.optDown.afterEndDownScroll(this);
+}
+
+/* 锁定下拉刷新:isLock=ture,null锁定;isLock=false解锁 */
+MeScroll.prototype.lockDownScroll = function(isLock) {
+	if (isLock == null) isLock = true;
+	this.optDown.isLock = isLock;
+}
+
+/* 锁定上拉加载:isLock=ture,null锁定;isLock=false解锁 */
+MeScroll.prototype.lockUpScroll = function(isLock) {
+	if (isLock == null) isLock = true;
+	this.optUp.isLock = isLock;
+}
+
+/* -------初始化上拉加载------- */
+MeScroll.prototype.initUpScroll = function() {
+	let me = this;
+	// 配置参数
+	me.optUp = me.options.up || {use: false}
+	if(!me.optUp.textColor && me.hasColor(me.optUp.bgColor)) me.optUp.textColor = "#fff"; // 当bgColor有值且textColor未设置,则textColor默认白色
+	me.extendUpScroll(me.optUp);
+
+	if (me.optUp.use === false) return; // 配置不使用上拉加载时,则不初始化上拉布局
+	me.optUp.hasNext = true; // 如果使用上拉,则默认有下一页
+	me.startNum = me.optUp.page.num + 1; // 记录page开始的页码
+
+	// 初始化完毕的回调
+	if (me.optUp.inited) {
+		setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
+			me.optUp.inited(me);
+		}, 0)
+	}
+}
+
+/*滚动到底部的事件 (仅mescroll-body生效)*/
+MeScroll.prototype.onReachBottom = function() {
+	if (this.isScrollBody && !this.isUpScrolling) { // 只能支持下拉刷新的时候同时可以触发上拉加载,否则滚动到底部就需要上滑一点才能触发onReachBottom
+		if (!this.optUp.isLock && this.optUp.hasNext) {
+			this.triggerUpScroll();
+		}
+	}
+}
+
+/*列表滚动事件 (仅mescroll-body生效)*/
+MeScroll.prototype.onPageScroll = function(e) {
+	if (!this.isScrollBody) return;
+	
+	// 更新滚动条的位置 (主要用于判断下拉刷新时,滚动条是否在顶部)
+	this.setScrollTop(e.scrollTop);
+
+	// 顶部按钮的显示隐藏
+	if (e.scrollTop >= this.optUp.toTop.offset) {
+		this.showTopBtn();
+	} else {
+		this.hideTopBtn();
+	}
+}
+
+/*列表滚动事件*/
+MeScroll.prototype.scroll = function(e, onScroll) {
+	// 更新滚动条的位置
+	this.setScrollTop(e.scrollTop);
+	// 更新滚动内容高度
+	this.setScrollHeight(e.scrollHeight);
+
+	// 向上滑还是向下滑动
+	if (this.preScrollY == null) this.preScrollY = 0;
+	this.isScrollUp = e.scrollTop - this.preScrollY > 0;
+	this.preScrollY = e.scrollTop;
+
+	// 上滑 && 检查并触发上拉
+	this.isScrollUp && this.triggerUpScroll(true);
+
+	// 顶部按钮的显示隐藏
+	if (e.scrollTop >= this.optUp.toTop.offset) {
+		this.showTopBtn();
+	} else {
+		this.hideTopBtn();
+	}
+
+	// 滑动监听
+	this.optUp.onScroll && onScroll && onScroll()
+}
+
+/* 触发上拉加载 */
+MeScroll.prototype.triggerUpScroll = function(isCheck) {
+	if (!this.isUpScrolling && this.optUp.use && this.optUp.callback) {
+		// 是否校验在底部; 默认不校验
+		if (isCheck === true) {
+			let canUp = false;
+			// 还有下一页 && 没有锁定 && 不在下拉中
+			if (this.optUp.hasNext && !this.optUp.isLock && !this.isDownScrolling) {
+				if (this.getScrollBottom() <= this.optUp.offset) { // 到底部
+					canUp = true; // 标记可上拉
+				}
+			}
+			if (canUp === false) return;
+		}
+		this.showUpScroll(); // 上拉加载中...
+		this.optUp.page.num++; // 预先加一页,如果失败则减回
+		this.isUpAutoLoad = true; // 标记上拉已经自动执行过,避免初始化时多次触发上拉回调
+		this.num = this.optUp.page.num; // 把最新的页数赋值在mescroll上,避免对page的影响
+		this.size = this.optUp.page.size; // 把最新的页码赋值在mescroll上,避免对page的影响
+		this.time = this.optUp.page.time; // 把最新的页码赋值在mescroll上,避免对page的影响
+		this.optUp.callback(this); // 执行回调,联网加载数据
+	}
+}
+
+/* 显示上拉加载中 */
+MeScroll.prototype.showUpScroll = function() {
+	this.isUpScrolling = true; // 标记上拉加载中
+	this.optUp.showLoading && this.optUp.showLoading(this); // 回调
+}
+
+/* 显示上拉无更多数据 */
+MeScroll.prototype.showNoMore = function() {
+	this.optUp.hasNext = false; // 标记无更多数据
+	this.optUp.showNoMore && this.optUp.showNoMore(this); // 回调
+}
+
+/* 隐藏上拉区域**/
+MeScroll.prototype.hideUpScroll = function() {
+	this.optUp.hideUpScroll && this.optUp.hideUpScroll(this); // 回调
+}
+
+/* 结束上拉加载 */
+MeScroll.prototype.endUpScroll = function(isShowNoMore) {
+	if (isShowNoMore != null) { // isShowNoMore=null,不处理下拉状态,下拉刷新的时候调用
+		if (isShowNoMore) {
+			this.showNoMore(); // isShowNoMore=true,显示无更多数据
+		} else {
+			this.hideUpScroll(); // isShowNoMore=false,隐藏上拉加载
+		}
+	}
+	this.isUpScrolling = false; // 标记结束上拉加载
+}
+
+/* 重置上拉加载列表为第一页
+ *isShowLoading 是否显示进度布局;
+ * 1.默认null,不传参,则显示上拉加载的进度布局
+ * 2.传参true, 则显示下拉刷新的进度布局
+ * 3.传参false,则不显示上拉和下拉的进度 (常用于静默更新列表数据)
+ */
+MeScroll.prototype.resetUpScroll = function(isShowLoading) {
+	if (this.optUp && this.optUp.use) {
+		let page = this.optUp.page;
+		this.prePageNum = page.num; // 缓存重置前的页码,加载失败可退回
+		this.prePageTime = page.time; // 缓存重置前的时间,加载失败可退回
+		page.num = this.startNum; // 重置为第一页
+		page.time = null; // 重置时间为空
+		if (!this.isDownScrolling && isShowLoading !== false) { // 如果不是下拉刷新触发的resetUpScroll并且不配置列表静默更新,则显示进度;
+			if (isShowLoading == null) {
+				this.removeEmpty(); // 移除空布局
+				this.showUpScroll(); // 不传参,默认显示上拉加载的进度布局
+			} else {
+				this.showDownScroll(); // 传true,显示下拉刷新的进度布局,不清空列表
+			}
+		}
+		this.isUpAutoLoad = true; // 标记上拉已经自动执行过,避免初始化时多次触发上拉回调
+		this.num = page.num; // 把最新的页数赋值在mescroll上,避免对page的影响
+		this.size = page.size; // 把最新的页码赋值在mescroll上,避免对page的影响
+		this.time = page.time; // 把最新的页码赋值在mescroll上,避免对page的影响
+		this.optUp.callback && this.optUp.callback(this); // 执行上拉回调
+	}
+}
+
+/* 设置page.num的值 */
+MeScroll.prototype.setPageNum = function(num) {
+	this.optUp.page.num = num - 1;
+}
+
+/* 设置page.size的值 */
+MeScroll.prototype.setPageSize = function(size) {
+	this.optUp.page.size = size;
+}
+
+/* 联网回调成功,结束下拉刷新和上拉加载
+ * dataSize: 当前页的数据量(必传)
+ * totalPage: 总页数(必传)
+ * systime: 服务器时间 (可空)
+ */
+MeScroll.prototype.endByPage = function(dataSize, totalPage, systime) {
+	let hasNext;
+	if (this.optUp.use && totalPage != null) hasNext = this.optUp.page.num < totalPage; // 是否还有下一页
+	this.endSuccess(dataSize, hasNext, systime);
+}
+
+/* 联网回调成功,结束下拉刷新和上拉加载
+ * dataSize: 当前页的数据量(必传)
+ * totalSize: 列表所有数据总数量(必传)
+ * systime: 服务器时间 (可空)
+ */
+MeScroll.prototype.endBySize = function(dataSize, totalSize, systime) {
+	let hasNext;
+	if (this.optUp.use && totalSize != null) {
+		let loadSize = (this.optUp.page.num - 1) * this.optUp.page.size + dataSize; // 已加载的数据总数
+		hasNext = loadSize < totalSize; // 是否还有下一页
+	}
+	this.endSuccess(dataSize, hasNext, systime);
+}
+
+/* 联网回调成功,结束下拉刷新和上拉加载
+ * dataSize: 当前页的数据个数(不是所有页的数据总和),用于上拉加载判断是否还有下一页.如果不传,则会判断还有下一页
+ * hasNext: 是否还有下一页,布尔类型;用来解决这个小问题:比如列表共有20条数据,每页加载10条,共2页.如果只根据dataSize判断,则需翻到第三页才会知道无更多数据,如果传了hasNext,则翻到第二页即可显示无更多数据.
+ * systime: 服务器时间(可空);用来解决这个小问题:当准备翻下一页时,数据库新增了几条记录,此时翻下一页,前面的几条数据会和上一页的重复;这里传入了systime,那么upCallback的page.time就会有值,把page.time传给服务器,让后台过滤新加入的那几条记录
+ */
+MeScroll.prototype.endSuccess = function(dataSize, hasNext, systime) {
+	let me = this;
+	// 结束下拉刷新
+	if (me.isDownScrolling) {
+		me.isDownEndSuccess = true
+		me.endDownScroll();
+	}
+
+	// 结束上拉加载
+	if (me.optUp.use) {
+		let isShowNoMore; // 是否已无更多数据
+		if (dataSize != null) {
+			let pageNum = me.optUp.page.num; // 当前页码
+			let pageSize = me.optUp.page.size; // 每页长度
+			// 如果是第一页
+			if (pageNum === 1) {
+				if (systime) me.optUp.page.time = systime; // 设置加载列表数据第一页的时间
+			}
+			if (dataSize < pageSize || hasNext === false) {
+				// 返回的数据不满一页时,则说明已无更多数据
+				me.optUp.hasNext = false;
+				if (dataSize === 0 && pageNum === 1) {
+					// 如果第一页无任何数据且配置了空布局
+					isShowNoMore = false;
+					me.showEmpty();
+				} else {
+					// 总列表数少于配置的数量,则不显示无更多数据
+					let allDataSize = (pageNum - 1) * pageSize + dataSize;
+					if (allDataSize < me.optUp.noMoreSize) {
+						isShowNoMore = false;
+					} else {
+						isShowNoMore = true;
+					}
+					me.removeEmpty(); // 移除空布局
+				}
+			} else {
+				// 还有下一页
+				isShowNoMore = false;
+				me.optUp.hasNext = true;
+				me.removeEmpty(); // 移除空布局
+			}
+		}
+
+		// 隐藏上拉
+		me.endUpScroll(isShowNoMore);
+	}
+}
+
+/* 回调失败,结束下拉刷新和上拉加载 */
+MeScroll.prototype.endErr = function(errDistance) {
+	// 结束下拉,回调失败重置回原来的页码和时间
+	if (this.isDownScrolling) {
+		this.isDownEndSuccess = false
+		let page = this.optUp.page;
+		if (page && this.prePageNum) {
+			page.num = this.prePageNum;
+			page.time = this.prePageTime;
+		}
+		this.endDownScroll();
+	}
+	// 结束上拉,回调失败重置回原来的页码
+	if (this.isUpScrolling) {
+		this.optUp.page.num--;
+		this.endUpScroll(false);
+		// 如果是mescroll-body,则需往回滚一定距离
+		if(this.isScrollBody && errDistance !== 0){ // 不处理0
+			if(!errDistance) errDistance = this.optUp.errDistance; // 不传,则取默认
+			this.scrollTo(this.getScrollTop() - errDistance, 0) // 往上回滚的距离
+		}
+	}
+}
+
+/* 显示空布局 */
+MeScroll.prototype.showEmpty = function() {
+	this.optUp.empty.use && this.optUp.empty.onShow && this.optUp.empty.onShow(true)
+}
+
+/* 移除空布局 */
+MeScroll.prototype.removeEmpty = function() {
+	this.optUp.empty.use && this.optUp.empty.onShow && this.optUp.empty.onShow(false)
+}
+
+/* 显示回到顶部的按钮 */
+MeScroll.prototype.showTopBtn = function() {
+	if (!this.topBtnShow) {
+		this.topBtnShow = true;
+		this.optUp.toTop.onShow && this.optUp.toTop.onShow(true);
+	}
+}
+
+/* 隐藏回到顶部的按钮 */
+MeScroll.prototype.hideTopBtn = function() {
+	if (this.topBtnShow) {
+		this.topBtnShow = false;
+		this.optUp.toTop.onShow && this.optUp.toTop.onShow(false);
+	}
+}
+
+/* 获取滚动条的位置 */
+MeScroll.prototype.getScrollTop = function() {
+	return this.scrollTop || 0
+}
+
+/* 记录滚动条的位置 */
+MeScroll.prototype.setScrollTop = function(y) {
+	this.scrollTop = y;
+}
+
+/* 滚动到指定位置 */
+MeScroll.prototype.scrollTo = function(y, t) {
+	this.myScrollTo && this.myScrollTo(y, t) // scrollview需自定义回到顶部方法
+}
+
+/* 自定义scrollTo */
+MeScroll.prototype.resetScrollTo = function(myScrollTo) {
+	this.myScrollTo = myScrollTo
+}
+
+/* 滚动条到底部的距离 */
+MeScroll.prototype.getScrollBottom = function() {
+	return this.getScrollHeight() - this.getClientHeight() - this.getScrollTop()
+}
+
+/* 计步器
+ star: 开始值
+ end: 结束值
+ callback(step,timer): 回调step值,计步器timer,可自行通过window.clearInterval(timer)结束计步器;
+ t: 计步时长,传0则直接回调end值;不传则默认300ms
+ rate: 周期;不传则默认30ms计步一次
+ * */
+MeScroll.prototype.getStep = function(star, end, callback, t, rate) {
+	let diff = end - star; // 差值
+	if (t === 0 || diff === 0) {
+		callback && callback(end);
+		return;
+	}
+	t = t || 300; // 时长 300ms
+	rate = rate || 30; // 周期 30ms
+	let count = t / rate; // 次数
+	let step = diff / count; // 步长
+	let i = 0; // 计数
+	let timer = setInterval(function() {
+		if (i < count - 1) {
+			star += step;
+			callback && callback(star, timer);
+			i++;
+		} else {
+			callback && callback(end, timer); // 最后一次直接设置end,避免计算误差
+			clearInterval(timer);
+		}
+	}, rate);
+}
+
+/* 滚动容器的高度 */
+MeScroll.prototype.getClientHeight = function(isReal) {
+	let h = this.clientHeight || 0
+	if (h === 0 && isReal !== true) { // 未获取到容器的高度,可临时取body的高度 (可能会有误差)
+		h = this.getBodyHeight()
+	}
+	return h
+}
+MeScroll.prototype.setClientHeight = function(h) {
+	this.clientHeight = h;
+}
+
+/* 滚动内容的高度 */
+MeScroll.prototype.getScrollHeight = function() {
+	return this.scrollHeight || 0;
+}
+MeScroll.prototype.setScrollHeight = function(h) {
+	this.scrollHeight = h;
+}
+
+/* body的高度 */
+MeScroll.prototype.getBodyHeight = function() {
+	return this.bodyHeight || 0;
+}
+MeScroll.prototype.setBodyHeight = function(h) {
+	this.bodyHeight = h;
+}
+
+/* 阻止浏览器默认滚动事件 */
+MeScroll.prototype.preventDefault = function(e) {
+	// 小程序不支持e.preventDefault, 已在wxs中禁止
+	// app的bounce只能通过配置pages.json的style.app-plus.bounce为"none"来禁止, 或使用renderjs禁止
+	// cancelable:是否可以被禁用; defaultPrevented:是否已经被禁用
+	if (e && e.cancelable && !e.defaultPrevented) e.preventDefault()
+}

+ 480 - 0
node_modules/mescroll-uni/mescroll-uni.vue

@@ -0,0 +1,480 @@
+<template>
+	<view class="mescroll-uni-warp">
+		<scroll-view :id="viewId" class="mescroll-uni" :class="{'mescroll-uni-fixed':isFixed}" :style="{'height':scrollHeight,'padding-top':padTop,'padding-bottom':padBottom,'top':fixedTop,'bottom':fixedBottom}" :scroll-top="scrollTop" :scroll-with-animation="scrollAnim" @scroll="scroll" :scroll-y='scrollable' :enable-back-to-top="true" :throttle="false">
+			<view class="mescroll-uni-content mescroll-render-touch"
+			@touchstart="wxsBiz.touchstartEvent" 
+			@touchmove="wxsBiz.touchmoveEvent" 
+			@touchend="wxsBiz.touchendEvent" 
+			@touchcancel="wxsBiz.touchendEvent"
+			:change:prop="wxsBiz.propObserver"
+			:prop="wxsProp">
+				<!-- 状态栏 -->
+				<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
+		
+				<view class="mescroll-wxs-content" :style="{'transform': translateY, 'transition': transition}" :change:prop="wxsBiz.callObserver" :prop="callProp">
+					<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
+					<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
+					<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
+						<view class="downwarp-content">
+							<view class="downwarp-progress mescroll-wxs-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mescroll.optDown.textColor, 'transform': downRotate}"></view>
+							<view class="downwarp-tip">{{downText}}</view>
+						</view>
+					</view>
+
+					<!-- 列表内容 -->
+					<slot></slot>
+
+					<!-- 空布局 -->
+					<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
+
+					<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
+					<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
+					<view v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
+						<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
+						<view v-show="upLoadType===1">
+							<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
+							<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
+						</view>
+						<!-- 无数据 -->
+						<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
+					</view>
+				</view>
+			
+				<!-- 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效) -->
+				<!-- #ifdef H5 -->
+				<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
+				<!-- #endif -->
+				
+				<!-- 适配iPhoneX -->
+				<view v-if="safearea" class="mescroll-safearea"></view>
+			</view>
+		</scroll-view>
+
+		<!-- 回到顶部按钮 (fixed元素,需写在scroll-view外面,防止滚动的时候抖动)-->
+		<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
+		
+		<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
+		<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
+		<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
+		<!-- #endif -->
+	</view>
+</template>
+
+<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
+<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
+<script src="./wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
+<!-- #endif -->
+
+<!-- app, h5使用renderjs -->
+<!-- #ifdef APP-PLUS || H5 -->
+<script module="renderBiz" lang="renderjs">
+	import renderBiz from './wxs/renderjs.js';
+	export default {
+		mixins:[renderBiz]
+	}
+</script>
+<!-- #endif -->
+
+<script>
+	// 引入mescroll-uni.js,处理核心逻辑
+	import MeScroll from './mescroll-uni.js';
+	// 引入全局配置
+	import GlobalOption from './mescroll-uni-option.js';
+	// 引入空布局组件
+	import MescrollEmpty from './components/mescroll-empty.vue';
+	// 引入国际化工具类
+	import mescrollI18n from './mescroll-i18n.js';
+	// 引入回到顶部组件
+	import MescrollTop from './components/mescroll-top.vue';
+	// 引入兼容wxs(含renderjs)写法的mixins
+	import WxsMixin from './wxs/mixins.js';
+	
+	/**
+	 * mescroll-uni 嵌在页面某个区域的下拉刷新和上拉加载组件, 如嵌在弹窗,浮层,swiper中...
+	 * @property {Object} down 下拉刷新的参数配置
+	 * @property {Object} up 上拉加载的参数配置
+	 * @property {Object} i18n 国际化的参数配置
+	 * @property {String, Number} top 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+	 * @property {Boolean, String} topbar 偏移量top是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
+	 * @property {String, Number} bottom 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+	 * @property {Boolean} safearea 偏移量bottom是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
+	 * @property {Boolean} fixed 是否通过fixed固定mescroll的高度, 默认true
+	 * @property {String, Number} height 指定mescroll的高度, 此项有值,则不使用fixed. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+	 * @property {Boolean} bottombar 底部是否偏移TabBar的高度 (仅在H5端的tab页生效)
+	 * @property {Boolean} disableScroll 是否禁止滚动, 默认false
+	 * @event {Function} init 初始化完成的回调 
+	 * @event {Function} down 下拉刷新的回调
+	 * @event {Function} up 上拉加载的回调 
+	 * @event {Function} emptyclick 点击empty配置的btnText按钮回调
+	 * @event {Function} topclick 点击回到顶部的按钮回调
+	 * @event {Function} scroll 滚动监听 (需在 up 配置 onScroll:true 才生效)
+	 * @example <mescroll-uni ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback"> ... </mescroll-uni>
+	 */
+	export default {
+		name: 'mescroll-uni',
+		mixins: [WxsMixin],
+		components: {
+			MescrollEmpty,
+			MescrollTop
+		},
+		props: {
+			down: Object,
+			up: Object,
+			i18n: Object,
+			top: [String, Number],
+			topbar: [Boolean, String],
+			bottom: [String, Number],
+			safearea: Boolean,
+			fixed: {
+				type: Boolean,
+				default: true
+			},
+			height: [String, Number],
+			bottombar:{
+				type: Boolean,
+				default: true
+			},
+			disableScroll: Boolean
+		},
+		data() {
+			return {
+				mescroll: {optDown:{},optUp:{}}, // mescroll实例
+				viewId: 'id_' + Math.random().toString(36).substr(2,16), // 随机生成mescroll的id(不能数字开头,否则找不到元素)
+				downHight: 0, //下拉刷新: 容器高度
+				downRate: 0, // 下拉比率(inOffset: rate<1; outOffset: rate>=1)
+				downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
+				upLoadType: 0, // 上拉加载状态: 0(loading前), 1loading中, 2没有更多了,显示END文本提示, 3(没有更多了,不显示END文本提示)
+				isShowEmpty: false, // 是否显示空布局
+				isShowToTop: false, // 是否显示回到顶部按钮
+				scrollTop: 0, // 滚动条的位置
+				scrollAnim: false, // 是否开启滚动动画
+				windowTop: 0, // 可使用窗口的顶部位置
+				windowBottom: 0, // 可使用窗口的底部位置
+				windowHeight: 0, // 可使用窗口的高度
+				statusBarHeight: 0 // 状态栏高度
+			}
+		},
+		computed: {
+			// 是否使用fixed定位 (当height有值,则不使用)
+			isFixed(){
+				return !this.height && this.fixed
+			},
+			// mescroll的高度
+			scrollHeight(){
+				if (this.isFixed) {
+					return "auto"
+				} else if(this.height){
+					return this.toPx(this.height) + 'px'
+				}else{
+					return "100%"
+				}
+			},
+			// 下拉布局往下偏移的距离 (px)
+			numTop() {
+				return this.toPx(this.top)
+			},
+			fixedTop() {
+				return this.isFixed ? (this.numTop + this.windowTop) + 'px' : 0
+			},
+			padTop() {
+				return !this.isFixed ? this.numTop + 'px' : 0
+			},
+			// 上拉布局往上偏移 (px)
+			numBottom() {
+				return this.toPx(this.bottom)
+			},
+			fixedBottom() {
+				return this.isFixed ? (this.numBottom + this.windowBottom) + 'px' : 0
+			},
+			padBottom() {
+				return !this.isFixed ? this.numBottom + 'px' : 0
+			},
+			// 是否为重置下拉的状态
+			isDownReset(){
+				return this.downLoadType===3 || this.downLoadType===4
+			},
+			// 过渡
+			transition() {
+				return this.isDownReset ? 'transform 300ms' : '';
+			},
+			translateY() {
+				return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : ''; // transform会使fixed失效,需注意把fixed元素写在mescroll之外
+			},
+			// 列表是否可滑动
+			scrollable(){
+				if(this.disableScroll) return false
+				return this.downLoadType===0 || this.isDownReset
+			},
+			// 是否在加载中
+			isDownLoading(){
+				return this.downLoadType === 3
+			},
+			// 旋转的角度
+			downRotate(){
+				return 'rotate(' + 360 * this.downRate + 'deg)'
+			},
+			// 文本提示
+			downText(){
+				if(!this.mescroll) return ""; // 避免头条小程序初始化时报错
+				switch (this.downLoadType){
+					case 1: return this.mescroll.optDown.textInOffset;
+					case 2: return this.mescroll.optDown.textOutOffset;
+					case 3: return this.mescroll.optDown.textLoading;
+					case 4: return this.mescroll.isDownEndSuccess ? this.mescroll.optDown.textSuccess : this.mescroll.isDownEndSuccess==false ? this.mescroll.optDown.textErr : this.mescroll.optDown.textInOffset;
+					default: return this.mescroll.optDown.textInOffset;
+				}
+			}
+		},
+		methods: {
+			//number,rpx,upx,px,% --> px的数值
+			toPx(num){
+				if(typeof num === "string"){
+					if (num.indexOf('px') !== -1) {
+						if(num.indexOf('rpx') !== -1) { // "10rpx"
+							num = num.replace('rpx', '');
+						} else if(num.indexOf('upx') !== -1) { // "10upx"
+							num = num.replace('upx', '');
+						} else { // "10px"
+							return Number(num.replace('px', ''))
+						}
+					}else if (num.indexOf('%') !== -1){
+						// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
+						let rate = Number(num.replace("%","")) / 100
+						return this.windowHeight * rate
+					}
+				}
+				return num ? uni.upx2px(Number(num)) : 0
+			},
+			//注册列表滚动事件,用于下拉刷新和上拉加载
+			scroll(e) {
+				this.mescroll.scroll(e.detail, () => {
+					this.$emit('scroll', this.mescroll) // 此时可直接通过 this.mescroll.scrollTop获取滚动条位置; this.mescroll.isScrollUp获取是否向上滑动
+				})
+			},
+			// 点击空布局的按钮回调
+			emptyClick() {
+				this.$emit('emptyclick', this.mescroll)
+			},
+			// 点击回到顶部的按钮回调
+			toTopClick() {
+				this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
+				this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
+			},
+			// 更新滚动区域的高度 (使内容不满屏和到底,都可继续翻页)
+			setClientHeight() {
+				if (this.mescroll.getClientHeight(true) === 0 && !this.isExec) {
+					this.isExec = true; // 避免多次获取
+					this.$nextTick(() => { // 确保dom已渲染
+						this.getClientInfo(data=>{
+							this.isExec = false;
+							if (data) {
+								this.mescroll.setClientHeight(data.height);
+							} else if (this.clientNum != 3) { // 极少部分情况,可能dom还未渲染完毕,递归获取,最多重试3次
+								this.clientNum = this.clientNum == null ? 1 : this.clientNum + 1;
+								setTimeout(() => {
+									this.setClientHeight()
+								}, this.clientNum * 100)
+							}
+						})
+					})
+				}
+			},
+			// 获取滚动区域的信息
+			getClientInfo(success){
+				let query = uni.createSelectorQuery();
+				// #ifndef MP-ALIPAY || MP-DINGTALK
+				query = query.in(this) // 支付宝小程序不支持in(this),而字节跳动小程序必须写in(this), 否则都取不到值
+				// #endif
+				let view = query.select('#' + this.viewId);
+				view.boundingClientRect(data => {
+					success(data)
+				}).exec();
+			}
+		},
+		// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
+		created() {
+			let vm = this;
+
+			let diyOption = {
+				// 下拉刷新的配置
+				down: {
+					inOffset() {
+						vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
+					},
+					outOffset() {
+						vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
+					},
+					onMoving(mescroll, rate, downHight) {
+						// 下拉过程中的回调,滑动过程一直在执行;
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						vm.downRate = rate; //下拉比率 (inOffset: rate<1; outOffset: rate>=1)
+					},
+					showLoading(mescroll, downHight) {
+						vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+					},
+					beforeEndDownScroll(mescroll){
+						vm.downLoadType = 4; 
+						return mescroll.optDown.beforeEndDelay // 延时结束的时长
+					},
+					endDownScroll() {
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						vm.downResetTimer && clearTimeout(vm.downResetTimer)
+						vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,以便置空this.transition,避免iOS小程序列表渲染不完整
+							if(vm.downLoadType===4) vm.downLoadType = 0
+						},300)
+					},
+					// 派发下拉刷新的回调
+					callback: function(mescroll) {
+						vm.$emit('down', mescroll)
+					}
+				},
+				// 上拉加载的配置
+				up: {
+					// 显示加载中的回调
+					showLoading() {
+						vm.upLoadType = 1;
+					},
+					// 显示无更多数据的回调
+					showNoMore() {
+						vm.upLoadType = 2;
+					},
+					// 隐藏上拉加载的回调
+					hideUpScroll(mescroll) {
+						vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
+					},
+					// 空布局
+					empty: {
+						onShow(isShow) { // 显示隐藏的回调
+							vm.isShowEmpty = isShow;
+						}
+					},
+					// 回到顶部
+					toTop: {
+						onShow(isShow) { // 显示隐藏的回调
+							vm.isShowToTop = isShow;
+						}
+					},
+					// 派发上拉加载的回调
+					callback: function(mescroll) {
+						vm.$emit('up', mescroll);
+						// 更新容器的高度 (多mescroll的情况)
+						vm.setClientHeight()
+					}
+				}
+			}
+
+			let i18nType = mescrollI18n.getType() // 当前语言类型
+			let i18nOption = {type: i18nType} // 国际化配置
+			MeScroll.extend(i18nOption, vm.i18n) // 具体页面的国际化配置
+			MeScroll.extend(i18nOption, GlobalOption.i18n) // 全局的国际化配置
+			MeScroll.extend(diyOption, i18nOption[i18nType]); // 混入国际化配置
+			MeScroll.extend(diyOption, {down:GlobalOption.down, up:GlobalOption.up}); // 混入全局的配置
+			let myOption = JSON.parse(JSON.stringify({'down': vm.down,'up': vm.up})) // 深拷贝,避免对props的影响
+			MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
+
+			// 初始化MeScroll对象
+			vm.mescroll = new MeScroll(myOption);
+			vm.mescroll.viewId = vm.viewId; // 附带id
+			vm.mescroll.i18n = i18nOption; // 挂载语言包
+			// init回调mescroll对象
+			vm.$emit('init', vm.mescroll);
+			
+			// 设置高度
+			const sys = uni.getSystemInfoSync();
+			if(sys.windowTop) vm.windowTop = sys.windowTop;
+			if(sys.windowBottom) vm.windowBottom = sys.windowBottom;
+			if(sys.windowHeight) vm.windowHeight = sys.windowHeight;
+			if(sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
+			// 使down的bottomOffset生效
+			vm.mescroll.setBodyHeight(sys.windowHeight);
+
+			// 因为使用的是scrollview,这里需自定义scrollTo
+			vm.mescroll.resetScrollTo((y, t) => {
+				vm.scrollAnim = (t !== 0); // t为0,则不使用动画过渡
+				if(typeof y === 'string'){
+					// 小程序不支持slot里面的scroll-into-view, 统一使用计算的方式实现
+					vm.getClientInfo(function(rect){
+						let mescrollTop = rect.top // mescroll到顶部的距离
+						let selector;
+						if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
+							selector = '#'+y // 不带#和. 则默认为id选择器
+						}else{
+							selector = y
+							// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
+							if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
+								selector = y.split('>>>')[1].trim()
+							}
+							// #endif
+						}
+						uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
+							if (rect) {
+								let curY = vm.mescroll.getScrollTop()
+								let top = rect.top - mescrollTop
+								top += curY
+								if(!vm.isFixed) top -= vm.numTop
+								vm.scrollTop = curY;
+								vm.$nextTick(function() {
+									vm.scrollTop = top
+								})
+							} else{
+								console.error(selector + ' does not exist');
+							}
+						}).exec()
+					})
+					return;
+				}
+				let curY = vm.mescroll.getScrollTop()
+				if (t === 0 || t === 300) { // 当t使用默认配置的300时,则使用系统自带的动画过渡
+					vm.scrollTop = curY;
+					vm.$nextTick(function() {
+						vm.scrollTop = y
+					})
+				} else {
+					vm.mescroll.getStep(curY, y, step => { // 此写法可支持配置t
+						vm.scrollTop = step
+					}, t)
+				}
+			})
+			
+			// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
+			if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
+				vm.mescroll.optUp.toTop.safearea = vm.safearea;
+			}
+			
+			// 全局配置监听
+			uni.$on("setMescrollGlobalOption", options=>{
+				if(!options) return;
+				let i18nType = options.i18n ? options.i18n.type : null
+				if(i18nType && vm.mescroll.i18n.type != i18nType){
+					vm.mescroll.i18n.type = i18nType
+					mescrollI18n.setType(i18nType)
+					MeScroll.extend(options, vm.mescroll.i18n[i18nType])
+				}
+				if(options.down){
+					let down = MeScroll.extend({}, options.down)
+					vm.mescroll.optDown = MeScroll.extend(down, vm.mescroll.optDown)
+				}
+				if(options.up){
+					let up = MeScroll.extend({}, options.up)
+					vm.mescroll.optUp = MeScroll.extend(up, vm.mescroll.optUp)
+				}
+			})
+		},
+		mounted() {
+			// 设置容器的高度
+			this.setClientHeight()
+		},
+		destroyed() {
+			// 注销全局配置监听
+			uni.$off("setMescrollGlobalOption")
+		}
+	}
+</script>
+
+<style>
+	@import "./mescroll-uni.css";
+	@import "./components/mescroll-down.css";
+	@import './components/mescroll-up.css';
+</style>

+ 47 - 0
node_modules/mescroll-uni/mixins/mescroll-comp.js

@@ -0,0 +1,47 @@
+/**
+ * mescroll-body写在子组件时,需通过mescroll的mixins补充子组件缺少的生命周期
+ */
+const MescrollCompMixin = {
+	// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件 (一级)
+	onPageScroll(e) {
+		this.handlePageScroll(e)
+	},
+	onReachBottom() {
+		this.handleReachBottom()
+	},
+	// 当down的native: true时, 还需传递此方法进到子组件
+	onPullDownRefresh(){
+		this.handlePullDownRefresh()
+	},
+	data() {
+		return {
+			mescroll: { // mescroll-body写在子子子...组件的情况 (多级)
+				onPageScroll: e=>{
+					this.handlePageScroll(e)
+				},
+				onReachBottom: ()=>{
+					this.handleReachBottom()
+				},
+				onPullDownRefresh: ()=>{
+					this.handlePullDownRefresh()
+				}
+			}
+		}
+	},
+	methods:{
+		handlePageScroll(e){
+			let item = this.$refs["mescrollItem"];
+			if(item && item.mescroll) item.mescroll.onPageScroll(e);
+		},
+		handleReachBottom(){
+			let item = this.$refs["mescrollItem"];
+			if(item && item.mescroll) item.mescroll.onReachBottom();
+		},
+		handlePullDownRefresh(){
+			let item = this.$refs["mescrollItem"];
+			if(item && item.mescroll) item.mescroll.onPullDownRefresh();
+		}
+	}
+}
+
+export default MescrollCompMixin;

+ 66 - 0
node_modules/mescroll-uni/mixins/mescroll-more-item.js

@@ -0,0 +1,66 @@
+/**
+ * mescroll-more-item的mixins, 仅在多个 mescroll-body 写在子组件时使用 (参考 mescroll-more 案例)
+ */
+const MescrollMoreItemMixin = {
+	// 支付宝小程序不支持props的mixin,需写在具体的页面中
+	// #ifndef MP-ALIPAY || MP-DINGTALK
+	props:{
+		i: Number, // 每个tab页的专属下标
+		index: { // 当前tab的下标
+			type: Number,
+			default(){
+				return 0
+			}
+		}
+	},
+	// #endif
+	data() {
+		return {
+			downOption:{
+				auto:false // 不自动加载
+			},
+			upOption:{
+				auto:false // 不自动加载
+			},
+			isInit: false // 当前tab是否已初始化
+		}
+	},
+	watch:{
+		// 监听下标的变化
+		index(val){
+			if (this.i === val && !this.isInit) this.mescrollTrigger()
+		}
+	},
+	methods: {
+		// 以ref的方式初始化mescroll对象 (兼容字节跳动小程序)
+		mescrollInitByRef() {
+			if(!this.mescroll || !this.mescroll.resetUpScroll){
+				// 字节跳动小程序编辑器不支持一个页面存在相同的ref, 多mescroll的ref需动态生成, 格式为'mescrollRef下标'
+				let mescrollRef = this.$refs.mescrollRef || this.$refs['mescrollRef'+this.i];
+				if(mescrollRef) this.mescroll = mescrollRef.mescroll
+			}
+		},
+		// mescroll组件初始化的回调,可获取到mescroll对象 (覆盖mescroll-mixins.js的mescrollInit, 为了标记isInit)
+		mescrollInit(mescroll) {
+			this.mescroll = mescroll;
+			this.mescrollInitByRef && this.mescrollInitByRef(); // 兼容字节跳动小程序
+			// 自动加载当前tab的数据
+			if(this.i === this.index){
+				this.mescrollTrigger()
+			}
+		},
+		// 主动触发加载
+		mescrollTrigger(){
+			this.isInit = true; // 标记为true
+			if (this.mescroll) {
+				if (this.mescroll.optDown.use) {
+					this.mescroll.triggerDownScroll();
+				} else{
+					this.mescroll.triggerUpScroll();
+				}
+			}
+		}
+	}
+}
+
+export default MescrollMoreItemMixin;

+ 74 - 0
node_modules/mescroll-uni/mixins/mescroll-more.js

@@ -0,0 +1,74 @@
+/**
+ * mescroll-body写在子组件时, 需通过mescroll的mixins补充子组件缺少的生命周期
+ */
+const MescrollMoreMixin = {
+	data() {
+		return {
+			tabIndex: 0, // 当前tab下标
+			mescroll: { // mescroll-body写在子子子...组件的情况 (多级)
+				onPageScroll: e=>{
+					this.handlePageScroll(e)
+				},
+				onReachBottom: ()=>{
+					this.handleReachBottom()
+				},
+				onPullDownRefresh: ()=>{
+					this.handlePullDownRefresh()
+				}
+			}
+		}
+	},
+	// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件
+	onPageScroll(e) {
+		this.handlePageScroll(e)
+	},
+	onReachBottom() {
+		this.handleReachBottom()
+	},
+	// 当down的native: true时, 还需传递此方法进到子组件
+	onPullDownRefresh(){
+		this.handlePullDownRefresh()
+	},
+	methods:{
+		handlePageScroll(e){
+			let mescroll = this.getMescroll(this.tabIndex);
+			mescroll && mescroll.onPageScroll(e);
+		},
+		handleReachBottom(){
+			let mescroll = this.getMescroll(this.tabIndex);
+			mescroll && mescroll.onReachBottom();
+		},
+		handlePullDownRefresh(){
+			let mescroll = this.getMescroll(this.tabIndex);
+			mescroll && mescroll.onPullDownRefresh();
+		},
+		// 根据下标获取对应子组件的mescroll
+		getMescroll(i){
+			if(!this.mescrollItems) this.mescrollItems = [];
+			if(!this.mescrollItems[i]) {
+				// v-for中的refs
+				let vForItem = this.$refs["mescrollItem"];
+				if(vForItem){
+					this.mescrollItems[i] = vForItem[i]
+				}else{
+					// 普通的refs,不可重复
+					this.mescrollItems[i] = this.$refs["mescrollItem"+i];
+				}
+			}
+			let item = this.mescrollItems[i]
+			return item ? item.mescroll : null
+		},
+		// 切换tab,恢复滚动条位置
+		tabChange(i){
+			let mescroll = this.getMescroll(i);
+			if(mescroll){
+				// 延时(比$nextTick靠谱一些),确保元素已渲染
+				setTimeout(()=>{
+					mescroll.scrollTo(mescroll.getScrollTop(),0)
+				},30)
+			}
+		}
+	}
+}
+
+export default MescrollMoreMixin;

+ 39 - 0
node_modules/mescroll-uni/package.json

@@ -0,0 +1,39 @@
+{
+  "_from": "mescroll-uni",
+  "_id": "mescroll-uni@1.3.7",
+  "_inBundle": false,
+  "_integrity": "sha512-1pQMtGA+iVRKhfJZZNXdBx05NnthIk6zm3hRbumswSA54eaKOMgpUDb9AQ2+rRdXmS6kLkEYSbW/fkb7/IyoAg==",
+  "_location": "/mescroll-uni",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "tag",
+    "registry": true,
+    "raw": "mescroll-uni",
+    "name": "mescroll-uni",
+    "escapedName": "mescroll-uni",
+    "rawSpec": "",
+    "saveSpec": null,
+    "fetchSpec": "latest"
+  },
+  "_requiredBy": [
+    "#USER",
+    "/"
+  ],
+  "_resolved": "https://registry.npmmirror.com/mescroll-uni/-/mescroll-uni-1.3.7.tgz",
+  "_shasum": "6b31114c92162315dfd2ac2e7809c2eacd15dc91",
+  "_spec": "mescroll-uni",
+  "_where": "D:\\Desktop\\本地生活服务客户端\\LocalLivingServicesUniapp",
+  "author": {
+    "name": "wenju"
+  },
+  "bundleDependencies": false,
+  "deprecated": false,
+  "description": "mescroll的uni-app版本:【wxs+renderjs实现】高性能的下拉刷新上拉加载组件",
+  "license": "MIT",
+  "main": "mescroll-uni.vue",
+  "name": "mescroll-uni",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "version": "1.3.7"
+}

+ 109 - 0
node_modules/mescroll-uni/wxs/mixins.js

@@ -0,0 +1,109 @@
+// 定义在wxs (含renderjs) 逻辑层的数据和方法, 与视图层相互通信
+const WxsMixin = {
+	data() {
+		return {
+			// 传入wxs视图层的数据 (响应式)
+			wxsProp: {
+				optDown:{}, // 下拉刷新的配置
+				scrollTop:0, // 滚动条的距离
+				bodyHeight:0, // body的高度
+				isDownScrolling:false, // 是否正在下拉刷新中
+				isUpScrolling:false, // 是否正在上拉加载中
+				isScrollBody:true, // 是否为mescroll-body滚动
+				isUpBoth:true, // 上拉加载时,是否同时可以下拉刷新
+				t: 0 // 数据更新的标记 (只有数据更新了,才会触发wxs的Observer)
+			},
+			
+			// 标记调用wxs视图层的方法
+			callProp: {
+				callType: '', // 方法名
+				t: 0 // 数据更新的标记 (只有数据更新了,才会触发wxs的Observer)
+			},
+			
+			// 不用wxs的平台使用此处的wxsBiz对象,抹平wxs的写法 (微信小程序和APP使用的wxsBiz对象是./wxs/wxs.wxs)
+			// #ifndef MP-WEIXIN || MP-QQ || APP-PLUS || H5
+			wxsBiz: {
+				//注册列表touchstart事件,用于下拉刷新
+				touchstartEvent: e=> {
+					this.mescroll.touchstartEvent(e);
+				},
+				//注册列表touchmove事件,用于下拉刷新
+				touchmoveEvent: e=> {
+					this.mescroll.touchmoveEvent(e);
+				},
+				//注册列表touchend事件,用于下拉刷新
+				touchendEvent: e=> {
+					this.mescroll.touchendEvent(e);
+				},
+				propObserver(){}, // 抹平wxs的写法
+				callObserver(){} // 抹平wxs的写法
+			},
+			// #endif
+			
+			// 不用renderjs的平台使用此处的renderBiz对象,抹平renderjs的写法 (app 和 h5 使用的renderBiz对象是./wxs/renderjs.js)
+			// #ifndef APP-PLUS || H5
+			renderBiz: {
+				propObserver(){} // 抹平renderjs的写法
+			}
+			// #endif
+		}
+	},
+	methods: {
+		// wxs视图层调用逻辑层的回调
+		wxsCall(msg){
+			if(msg.type === 'setWxsProp'){
+				// 更新wxsProp数据 (值改变才触发更新)
+				this.wxsProp = {
+					optDown: this.mescroll.optDown,
+					scrollTop: this.mescroll.getScrollTop(),
+					bodyHeight: this.mescroll.getBodyHeight(),
+					isDownScrolling: this.mescroll.isDownScrolling,
+					isUpScrolling: this.mescroll.isUpScrolling,
+					isUpBoth: this.mescroll.optUp.isBoth,
+					isScrollBody:this.mescroll.isScrollBody,
+					t: Date.now()
+				}
+			}else if(msg.type === 'setLoadType'){
+				// 设置inOffset,outOffset的状态
+				this.downLoadType = msg.downLoadType
+				// 状态挂载到mescroll对象, 以便在其他组件中使用, 比如<me-video>中
+				this.$set(this.mescroll, 'downLoadType', this.downLoadType)
+				// 重置是否加载成功的状态
+				this.$set(this.mescroll, 'isDownEndSuccess', null)
+			}else if(msg.type === 'triggerDownScroll'){
+				// 主动触发下拉刷新
+				this.mescroll.triggerDownScroll();
+			}else if(msg.type === 'endDownScroll'){
+				// 结束下拉刷新
+				this.mescroll.endDownScroll();
+			}else if(msg.type === 'triggerUpScroll'){
+				// 主动触发上拉加载
+				this.mescroll.triggerUpScroll(true);
+			}
+		}
+	},
+	mounted() {
+		// #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5
+		// 配置主动触发wxs显示加载进度的回调
+		this.mescroll.optDown.afterLoading = ()=>{
+			this.callProp = {callType: "showLoading", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
+		}
+		// 配置主动触发wxs隐藏加载进度的回调
+		this.mescroll.optDown.afterEndDownScroll = ()=>{
+			this.callProp = {callType: "endDownScroll", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
+			let delay = 300 + (this.mescroll.optDown.beforeEndDelay || 0)
+			setTimeout(()=>{
+				if(this.downLoadType === 4 || this.downLoadType === 0){
+					this.callProp = {callType: "clearTransform", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
+				}
+				// 状态挂载到mescroll对象, 以便在其他组件中使用, 比如<me-video>中
+				this.$set(this.mescroll, 'downLoadType', this.downLoadType)
+			}, delay)
+		}
+		// 初始化wxs的数据
+		this.wxsCall({type: 'setWxsProp'})
+		// #endif
+	}
+}
+
+export default WxsMixin;

+ 92 - 0
node_modules/mescroll-uni/wxs/renderjs.js

@@ -0,0 +1,92 @@
+// 使用renderjs直接操作window对象,实现动态控制app和h5的bounce
+// bounce: iOS橡皮筋,Android半月弧,h5浏览器下拉背景等效果 (下拉刷新时禁止)
+// https://uniapp.dcloud.io/frame?id=renderjs
+
+// 与wxs的me实例一致
+var me = {}
+
+// 初始化window对象的touch事件 (仅初始化一次)
+if(window && !window.$mescrollRenderInit){
+	window.$mescrollRenderInit = true
+	
+	
+	window.addEventListener('touchstart', function(e){
+		if (me.disabled()) return;
+		me.startPoint = me.getPoint(e); // 记录起点
+	}, {passive: true})
+	
+	
+	window.addEventListener('touchmove', function(e){
+		if (me.disabled()) return;
+		if (me.getScrollTop() > 0) return; // 需在顶部下拉,才禁止bounce
+		
+		var curPoint = me.getPoint(e); // 当前点
+		var moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+		// 向下拉
+		if (moveY > 0) {
+			// 可下拉的条件
+			if (!me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling && me.isUpBoth))) {
+				
+				// 只有touch在mescroll的view上面,才禁止bounce
+				var el = e.target;
+				var isMescrollTouch = false;
+				while (el && el.tagName && el.tagName !== 'UNI-PAGE-BODY' && el.tagName != "BODY") {
+					var cls = el.classList;
+					if (cls && cls.contains('mescroll-render-touch')) {
+						isMescrollTouch = true
+						break;
+					}
+					el = el.parentNode; // 继续检查其父元素
+				}
+				// 禁止bounce (不会对swiper和iOS侧滑返回造成影响)
+				if (isMescrollTouch && e.cancelable && !e.defaultPrevented) e.preventDefault();
+			}
+		}
+	}, {passive: false})
+}
+
+/* 获取滚动条的位置 */
+me.getScrollTop = function() {
+	return me.scrollTop || 0
+}
+
+/* 是否禁用下拉刷新 */
+me.disabled = function(){
+	return !me.optDown || !me.optDown.use || me.optDown.native
+}
+
+/* 根据点击滑动事件获取第一个手指的坐标 */
+me.getPoint = function(e) {
+	if (!e) {
+		return {x: 0,y: 0}
+	}
+	if (e.touches && e.touches[0]) {
+		return {x: e.touches[0].pageX,y: e.touches[0].pageY}
+	} else if (e.changedTouches && e.changedTouches[0]) {
+		return {x: e.changedTouches[0].pageX,y: e.changedTouches[0].pageY}
+	} else {
+		return {x: e.clientX,y: e.clientY}
+	}
+}
+
+/**
+ * 监听逻辑层数据的变化 (实时更新数据)
+ */
+function propObserver(wxsProp) {
+	me.optDown = wxsProp.optDown
+	me.scrollTop = wxsProp.scrollTop
+	me.isDownScrolling = wxsProp.isDownScrolling
+	me.isUpScrolling = wxsProp.isUpScrolling
+	me.isUpBoth = wxsProp.isUpBoth
+}
+
+/* 导出模块 */
+const renderBiz = {
+	data() {
+		return {
+			propObserver: propObserver,
+		}
+	}
+}
+
+export default renderBiz;

+ 268 - 0
node_modules/mescroll-uni/wxs/wxs.wxs

@@ -0,0 +1,268 @@
+// 使用wxs处理交互动画, 提高性能, 同时避免小程序bounce对下拉刷新的影响
+// https://uniapp.dcloud.io/frame?id=wxs
+// https://developers.weixin.qq.com/miniprogram/dev/framework/view/interactive-animation.html 
+
+// 模拟mescroll实例, 与mescroll.js的写法尽量保持一致
+var me = {}
+
+// ------ 自定义下拉刷新动画 start ------
+
+/* 下拉过程中的回调,滑动过程一直在执行 (rate<1为inOffset; rate>1为outOffset) */
+me.onMoving = function (ins, rate, downHight){
+	ins.requestAnimationFrame(function () {
+		ins.selectComponent('.mescroll-wxs-content').setStyle({
+			'will-change': 'transform', // 可解决下拉过程中, image和swiper脱离文档流的问题
+			'transform': 'translateY(' + downHight + 'px)',
+			'transition': ''
+		})
+		// 环形进度条
+		var progress = ins.selectComponent('.mescroll-wxs-progress')
+		progress && progress.setStyle({transform: 'rotate(' + 360 * rate + 'deg)'})
+	})
+}
+
+/* 显示下拉刷新进度 */
+me.showLoading = function (ins){
+	me.downHight = me.optDown.offset
+	ins.requestAnimationFrame(function () {
+		ins.selectComponent('.mescroll-wxs-content').setStyle({
+			'will-change': 'auto',
+			'transform': 'translateY(' + me.downHight + 'px)',
+			'transition': 'transform 300ms'
+		})
+	})
+}
+
+/* 结束下拉 */
+me.endDownScroll = function (ins){
+	me.downHight = 0;
+	me.isDownScrolling = false;
+	ins.requestAnimationFrame(function () {
+		ins.selectComponent('.mescroll-wxs-content').setStyle({
+			'will-change': 'auto',
+			'transform': 'translateY(0)', // 不可以写空串,否则scroll-view渲染不完整 (延时350ms会调clearTransform置空)
+			'transition': 'transform 300ms'
+		})
+	})
+}
+
+/* 结束下拉动画执行完毕后, 清除transform和transition, 避免对列表内容样式造成影响, 如: h5的list-msg示例下拉进度条漏出来等 */
+me.clearTransform = function (ins){
+	ins.requestAnimationFrame(function () {
+		ins.selectComponent('.mescroll-wxs-content').setStyle({
+			'will-change': '',
+			'transform': '',
+			'transition': ''
+		})
+	})
+}
+
+// ------ 自定义下拉刷新动画 end ------
+
+/**
+ * 监听逻辑层数据的变化 (实时更新数据)
+ */
+function propObserver(wxsProp) {
+	me.optDown = wxsProp.optDown
+	me.scrollTop = wxsProp.scrollTop
+	me.bodyHeight = wxsProp.bodyHeight
+	me.isDownScrolling = wxsProp.isDownScrolling
+	me.isUpScrolling = wxsProp.isUpScrolling
+	me.isUpBoth = wxsProp.isUpBoth
+	me.isScrollBody = wxsProp.isScrollBody
+	me.startTop = wxsProp.scrollTop // 及时更新touchstart触发的startTop, 避免scroll-view快速惯性滚动到顶部取值不准确
+}
+
+/**
+ * 监听逻辑层数据的变化 (调用wxs的方法)
+ */
+function callObserver(callProp, oldValue, ins) {
+	if (me.disabled()) return;
+	if(callProp.callType){
+		// 逻辑层(App Service)的style已失效,需在视图层(Webview)设置style
+		if(callProp.callType === 'showLoading'){
+			me.showLoading(ins)
+		}else if(callProp.callType === 'endDownScroll'){
+			me.endDownScroll(ins)
+		}else if(callProp.callType === 'clearTransform'){
+			me.clearTransform(ins)
+		}
+	}
+}
+
+/**
+ * touch事件
+ */
+function touchstartEvent(e, ins) {
+	me.downHight = 0; // 下拉的距离
+	me.startPoint = me.getPoint(e); // 记录起点
+	me.startTop = me.getScrollTop(); // 记录此时的滚动条位置
+	me.startAngle = 0; // 初始角度
+	me.lastPoint = me.startPoint; // 重置上次move的点
+	me.maxTouchmoveY = me.getBodyHeight() - me.optDown.bottomOffset; // 手指触摸的最大范围(写在touchstart避免body获取高度为0的情况)
+	me.inTouchend = false; // 标记不是touchend
+	
+	me.callMethod(ins, {type: 'setWxsProp'}) // 同步更新wxsProp的数据 (小程序是异步的,可能touchmove先执行,才到propObserver; h5和app是同步)
+}
+
+function touchmoveEvent(e, ins) {
+	var isPrevent = true // false表示不往上冒泡,相当于调用了同时调用了stopPropagation和preventDefault (对小程序生效, h5和app无效)
+	
+	if (me.disabled()) return isPrevent;
+	
+	var scrollTop = me.getScrollTop(); // 当前滚动条的距离
+	var curPoint = me.getPoint(e); // 当前点
+	
+	var moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+	
+	// 向下拉 && 在顶部
+	// mescroll-body,直接判定在顶部即可
+	// scroll-view在滚动时不会触发touchmove,当触顶/底/左/右时,才会触发touchmove
+	// scroll-view滚动到顶部时,scrollTop不一定为0,也有可能大于0; 在iOS的APP中scrollTop可能为负数,不一定和startTop相等
+	if (moveY > 0 && (
+			(me.isScrollBody && scrollTop <= 0)
+			||
+			(!me.isScrollBody && (scrollTop <= 0 || (scrollTop <= me.optDown.startTop && scrollTop === me.startTop)) )
+		)) {
+		// 可下拉的条件
+		if (!me.inTouchend && !me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling &&
+				me.isUpBoth))) {
+	
+			// 下拉的角度是否在配置的范围内
+			if(!me.startAngle) me.startAngle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90]
+			if (me.startAngle < me.optDown.minAngle) return isPrevent; // 如果小于配置的角度,则不往下执行下拉刷新
+	
+			// 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发
+			if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) {
+				me.inTouchend = true; // 标记执行touchend
+				touchendEvent(e, ins); // 提前触发touchend
+				return isPrevent;
+			}
+			
+			isPrevent = false // 小程序是return false
+	
+			var diff = curPoint.y - me.lastPoint.y; // 和上次比,移动的距离 (大于0向下,小于0向上)
+	
+			// 下拉距离  < 指定距离
+			if (me.downHight < me.optDown.offset) {
+				if (me.movetype !== 1) {
+					me.movetype = 1; // 加入标记,保证只执行一次
+					// me.optDown.inOffset && me.optDown.inOffset(me); // 进入指定距离范围内那一刻的回调,只执行一次
+					me.callMethod(ins, {type: 'setLoadType', downLoadType: 1})
+					me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
+				}
+				me.downHight += diff * me.optDown.inOffsetRate; // 越往下,高度变化越小
+	
+				// 指定距离  <= 下拉距离
+			} else {
+				if (me.movetype !== 2) {
+					me.movetype = 2; // 加入标记,保证只执行一次
+					// me.optDown.outOffset && me.optDown.outOffset(me); // 下拉超过指定距离那一刻的回调,只执行一次
+					me.callMethod(ins, {type: 'setLoadType', downLoadType: 2})
+					me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
+				}
+				if (diff > 0) { // 向下拉
+					me.downHight += diff * me.optDown.outOffsetRate; // 越往下,高度变化越小
+				} else { // 向上收
+					me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
+				}
+			}
+			
+			me.downHight = Math.round(me.downHight) // 取整
+			var rate = me.downHight / me.optDown.offset; // 下拉区域当前高度与指定距离的比值
+			// me.optDown.onMoving && me.optDown.onMoving(me, rate, me.downHight); // 下拉过程中的回调,一直在执行
+			me.onMoving(ins, rate, me.downHight)
+		}
+	}
+	
+	me.lastPoint = curPoint; // 记录本次移动的点
+	
+	return isPrevent // false表示不往上冒泡,相当于调用了同时调用了stopPropagation和preventDefault (对小程序生效, h5和app无效)
+}
+
+function touchendEvent(e, ins) {
+	// 如果下拉区域高度已改变,则需重置回来
+	if (me.isMoveDown) {
+		if (me.downHight >= me.optDown.offset) {
+			// 符合触发刷新的条件
+			me.downHight = me.optDown.offset; // 更新下拉区域高度
+			// me.triggerDownScroll();
+			me.callMethod(ins, {type: 'triggerDownScroll'})
+		} else {
+			// 不符合的话 则重置
+			me.downHight = 0;
+			// me.optDown.endDownScroll && me.optDown.endDownScroll(me);
+			me.callMethod(ins, {type: 'endDownScroll'})
+		}
+		me.movetype = 0;
+		me.isMoveDown = false;
+	} else if (!me.isScrollBody && me.getScrollTop() === me.startTop) { // scroll-view到顶/左/右/底的滑动事件
+		var isScrollUp = me.getPoint(e).y - me.startPoint.y < 0; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+		// 上滑
+		if (isScrollUp) {
+			// 需检查滑动的角度
+			var angle = me.getAngle(me.getPoint(e), me.startPoint); // 两点之间的角度,区间 [0,90]
+			if (angle > 80) {
+				// 检查并触发上拉
+				// me.triggerUpScroll(true);
+				me.callMethod(ins, {type: 'triggerUpScroll'})
+			}
+		}
+	}
+	me.callMethod(ins, {type: 'setWxsProp'}) // 同步更新wxsProp的数据 (小程序是异步的,可能touchmove先执行,才到propObserver; h5和app是同步)
+}
+
+/* 是否禁用下拉刷新 */
+me.disabled = function(){
+	return !me.optDown || !me.optDown.use || me.optDown.native
+}
+
+/* 根据点击滑动事件获取第一个手指的坐标 */
+me.getPoint = function(e) {
+	if (!e) {
+		return {x: 0,y: 0}
+	}
+	if (e.touches && e.touches[0]) {
+		return {x: e.touches[0].pageX,y: e.touches[0].pageY}
+	} else if (e.changedTouches && e.changedTouches[0]) {
+		return {x: e.changedTouches[0].pageX,y: e.changedTouches[0].pageY}
+	} else {
+		return {x: e.clientX,y: e.clientY}
+	}
+}
+
+/* 计算两点之间的角度: 区间 [0,90]*/
+me.getAngle = function (p1, p2) {
+	var x = Math.abs(p1.x - p2.x);
+	var y = Math.abs(p1.y - p2.y);
+	var z = Math.sqrt(x * x + y * y);
+	var angle = 0;
+	if (z !== 0) {
+		angle = Math.asin(y / z) / Math.PI * 180;
+	}
+	return angle
+}
+
+/* 获取滚动条的位置 */
+me.getScrollTop = function() {
+	return me.scrollTop || 0
+}
+
+/* 获取body的高度 */
+me.getBodyHeight = function() {
+	return me.bodyHeight || 0;
+}
+
+/* 调用逻辑层的方法 */
+me.callMethod = function(ins, param) {
+	if(ins) ins.callMethod('wxsCall', param)
+}
+
+/* 导出模块 */
+module.exports = {
+	propObserver: propObserver,
+	callObserver: callObserver,
+	touchstartEvent: touchstartEvent,
+	touchmoveEvent: touchmoveEvent,
+	touchendEvent: touchendEvent
+}

+ 10 - 9
package-lock.json

@@ -1,14 +1,13 @@
 {
-  "name": "locallivingservicesuniapp",
-  "version": "1.0.0",
+  "name": "日期时间选择器组件",
+  "version": "1.0.6",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
     "decode-uri-component": {
       "version": "0.4.1",
       "resolved": "https://registry.npmmirror.com/decode-uri-component/-/decode-uri-component-0.4.1.tgz",
-      "integrity": "sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==",
-      "dev": true
+      "integrity": "sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ=="
     },
     "echarts": {
       "version": "5.6.0",
@@ -22,14 +21,17 @@
     "filter-obj": {
       "version": "5.1.0",
       "resolved": "https://registry.npmmirror.com/filter-obj/-/filter-obj-5.1.0.tgz",
-      "integrity": "sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==",
-      "dev": true
+      "integrity": "sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng=="
+    },
+    "mescroll-uni": {
+      "version": "1.3.7",
+      "resolved": "https://registry.npmmirror.com/mescroll-uni/-/mescroll-uni-1.3.7.tgz",
+      "integrity": "sha512-1pQMtGA+iVRKhfJZZNXdBx05NnthIk6zm3hRbumswSA54eaKOMgpUDb9AQ2+rRdXmS6kLkEYSbW/fkb7/IyoAg=="
     },
     "query-string": {
       "version": "9.1.1",
       "resolved": "https://registry.npmmirror.com/query-string/-/query-string-9.1.1.tgz",
       "integrity": "sha512-MWkCOVIcJP9QSKU52Ngow6bsAWAPlPK2MludXvcrS2bGZSl+T1qX9MZvRIkqUIkGLJquMJHWfsT6eRqUpp4aWg==",
-      "dev": true,
       "requires": {
         "decode-uri-component": "^0.4.1",
         "filter-obj": "^5.1.0",
@@ -39,8 +41,7 @@
     "split-on-first": {
       "version": "3.0.0",
       "resolved": "https://registry.npmmirror.com/split-on-first/-/split-on-first-3.0.0.tgz",
-      "integrity": "sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==",
-      "dev": true
+      "integrity": "sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA=="
     },
     "tslib": {
       "version": "2.3.0",

+ 4 - 1
package.json

@@ -15,5 +15,8 @@
             "前端组件",
             "通用组件"
         ]
+    },
+    "dependencies": {
+        "mescroll-uni": "^1.3.7"
     }
-}
+}

+ 21 - 13
pages.json

@@ -80,7 +80,11 @@
 		},
 		{
 			"path": "pages/evaluate/evaluate",
-			"style": {}
+			"style": {
+				"app-plus" : {
+								"bounce" : "none" // 取消APP端iOS回弹,避免与下拉刷新冲突 (可统一配在 'globalStyle')
+							}
+			}
 		},
 		{
 			"path": "pages/scan/scan",
@@ -102,6 +106,10 @@
 			"path": "pages/scan/record",
 			"style": {}
 		},
+		{
+			"path": "pages/scan/check",
+			"style": {}
+		},
 		{
 			"path": "pages/basics/layout",
 			"style": {}
@@ -308,19 +316,19 @@
 				"pagePath": "pages/order/index",
 				"iconPath": "static/tabbar/order_tab.png",
 				"selectedIconPath": "static/tabbar/order_select_tab.png"
-			},
-			{ 
-				"text": "消息",
-				"pagePath": "pages/identify/identify",
-				"iconPath": "static/tabbar/msg_tab.png",
-				"selectedIconPath": "static/tabbar/msg_select_tab.png"
-			},
-			{
-				"text": "设置",
-				"pagePath": "pages/my/my",
-				"iconPath": "static/tabbar/set_tab.png",
-				"selectedIconPath": "static/tabbar/set_select_tab.png"
 			}
+			// { 
+			// 	"text": "消息",
+			// 	"pagePath": "pages/message/message",
+			// 	"iconPath": "static/tabbar/msg_tab.png",
+			// 	"selectedIconPath": "static/tabbar/msg_select_tab.png"
+			// },
+			// {
+			// 	"text": "设置",
+			// 	"pagePath": "pages/my/my",
+			// 	"iconPath": "static/tabbar/set_tab.png",
+			// 	"selectedIconPath": "static/tabbar/set_select_tab.png"
+			// }
 		]
 	},
 	"usingComponts": true,

+ 446 - 135
pages/evaluate/evaluate.vue

@@ -1,137 +1,209 @@
 <template>
 	<view class="evaluate">
+		<view class="top"></view>
 		<view class="head">
 			<span class="cuIcon-back" @click="back"></span>
 			<view class="case">
 				客户评价
 			</view>
 		</view>
-		<view class="score">
-			<view class="picture">
-				<view class="left">
-					<view>
-						<echartsRound />
+		<view class="center">
+			<view class="score">
+				<view class="picture">
+					<view class="left">
+						<view class="mark">
+							{{statistics.grade}}
+						</view>
+						<view class="num">
+							<u-rate :count="count" v-model="statistics.grade" active-icon="heart-fill" inactive-icon="heart"
+								size="20"></u-rate>
+							<span>{{statistics.sum}}条评价</span>
+						</view>
 					</view>
-				</view>
-				<view class="right">
-					<view class="line">
-						推荐<u-line-progress :percent="70" style="width: 60%;"></u-line-progress>62.23%
-					</view>
-					<view class="line">
-						一般<u-line-progress :percent="70" style="width: 60%;"></u-line-progress>62.23%
-					</view>
-					<view class="line">
-						差评<u-line-progress :percent="70" style="width: 60%;"></u-line-progress>62.23%
+					<view class="separate"></view>
+					<view class="right">
+						<view class="line" v-for="(item,index) in statistics.commentAppSumVos">
+							<span>{{item.name}}</span><u-line-progress :percent="item.percent" active-color="#FF5D39"
+								style="width: 230rpx;margin-right:5rpx"></u-line-progress>{{item.percent}}%
+						</view>
 					</view>
 				</view>
-			</view>
-			<view class="people_num">
-				163人推荐,32人觉得一般,16人觉得差
-			</view>
-		</view>
-		<view class="appraise">
-			<view class="title">
-				全部评价
-			</view>
-			<view class="select">
-				<view class="type" v-for="(item,index) in typeList" :key="index">
-					{{item.name}}
+				<view class="people_num">
+					<view class="statis" v-for="(item,index) in statistics.commentAppSumVos" :key="index">
+						{{item.value}}人{{item.name}}<span></span>
+					</view>
 				</view>
 			</view>
-			<view class="list" v-for="(item,index) in appraiseList" :key="index">
-				<view class="head_url">
-					<image :src="item.url" mode=""></image>
-					{{item.name}}
-				</view>
-				<view class="type">
-					评价为{{item.type}}
-				</view>
-				<view class="content">
-					{{item.content}}
-				</view>
-				<view class="time">
-					{{item.time}}发布
-				</view>
-				<view class="reply"  v-if="item.reply==''">
-					<u-input v-model="reply" :border="border"/>
-				</view>
-				<view class="" v-else>
-					{{item.reply}}
-					<u-button>删除回复</u-button>
+			<view class="appraise">
+				<view class="select">
+					<view class="type" :class="curremtIndex == index?'active':''" v-for="(item,index) in typeList" :key="index"
+						@click="change(item,index)">
+						{{item.name}}
+					</view>
+					<view style="width: 144rpx;"></view>
 				</view>
+				<mescroll-uni class="list" :fixed="false" ref="mescrollRef" @up="getcommentList" @init="mescrollInit"
+					style="height: 64vh;" :use="false">
+					<view class="item" v-for=" (item,index) in appraiseList" :key="index">
+						<view class=" headr">
+							<view class="left">
+								<view class="img">
+									<image :src="item.userAvatar" mode=""></image>
+								</view>
+								<view class="name">
+									{{item.userName}}<br />
+									<span>{{item.commentTime}}发布</span>
+								</view>
+							</view>
+
+							<view class="right">
+								<u-rate :count="count" v-model="item.commentGrade" active-icon="heart-fill" size="20" :disabled="true"
+									inactive-icon="heart"></u-rate><span>{{item.grade}}</span>
+							</view>
+						</view>
+						<view class="product">
+							{{item.productName}}
+						</view>
+						<view class="content">
+							{{item.commentContent}}
+						</view>
+						<view class="image" v-if="item.imagesList">
+							<view class="img" v-for="(row,index) in item.imagesList" :key="index">
+								<image :src="row" mode=""></image>
+							</view>
+						</view>
+						<view class="recover" v-if="item.commentsReply">
+							商家回复:{{item.commentContent}}
+						</view>
+
+						<view class="reply">
+							<input v-model="reply" placeholder="回复..." style="height: 36rpx;" />
+						</view>
+						<view class="remove" v-if="item.commentsReply">
+							{{item.time}}{{item.commentContent}}
+							<view class="btn">删除回复</view>
+						</view>
+					</view>
+				</mescroll-uni>
 			</view>
 		</view>
 	</view>
 </template>
 
 <script>
-	import echartsRound from '@/components/echarts_round.vue'
+	// import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
 	export default {
-		components: {
-			echartsRound
-		},
+		// mixins: [MescrollMixin], // 使用mixin
 		data() {
 			return {
+				merchantId: '',
+				statistics: {},
+				curremtIndex: 0,
 				timestamp: 86400,
 				checked: true,
+				count: 5,
+				value: 3,
+				icons: ['thumb-down-fill', 'thumb-down-fill', 'thumb-up-fill', 'thumb-up-fill'],
 				typeList: [{
 						name: '全部',
-						type: 1
+						commentGrade: 0
 					},
 					{
 						name: '推荐',
-						type: 1
+						commentGrade: 1
 					},
 					{
 						name: '一般',
-						type: 1
+						commentGrade: 2
 					},
 					{
 						name: '差评',
-						type: 1
+						commentGrade: 3
 					},
 					{
 						name: '未回复',
-						type: 1
+						commentGrade: 4
 					},
 					{
 						name: '有文字',
-						type: 1
+						commentGrade: 5
 					},
 					{
-						name: '有图片/视频',
-						type: 1
+						name: '图/视频',
+						commentGrade: 6
 					},
 				],
-				appraiseList: [{
-					name: '名称',
-					url:'/static/avatar_girl.png',
-					type:'推荐',
-					content:'这是谁?朕刚活过来,你让朕自杀?不怕朕诛你十族吗?还有,长公子是哪位?',
-					time:'17.05',
-					reply:''
+				appraiseList: [],
+				reply: '',
+				commentGrade: 0,
+				upOption: {
+					page: {
+						num: 0,
+						size: 10 // 每页数据的数量,默认10
+					},
+					noMoreSize: 5, // 配置列表的总数量要大于等于5条才显示'-- END --'的提示
+					empty: {
+						tip: '暂无相关数据'
+					}
 				},
-				{
-					name: '名称',
-					url:'/static/avatar_girl.png',
-					type:'推荐',
-					content:'这是谁?朕刚活过来,你让朕自杀?不怕朕诛你十族吗?还有,长公子是哪位?',
-					time:'17.05',
-					reply:'众人你看看我,我看看你,却没人动。'
-				}],
-				reply:''
 			};
 		},
 		mounted() {
-
+			this.getcomment()
+			this.mescroll.optDown.use = false
+			// this.getcommentList()
 		},
 		methods: {
+			mescrollInit(mescroll) {
+				this.mescroll = mescroll;
+			},
 			//返回
 			back() {
 				uni.navigateBack({
 					delta: 1
 				});
 			},
+			//切换条件
+			change(item, index) {
+				this.curremtIndex = index
+				this.commentGrade = item.commentGrade
+				let page = {
+					num: 1,
+					size: 10
+				}
+				this.appraiseList = []
+				this.getcommentList(page)
+			},
+			//获取用户评论数据
+			getcomment() {
+				this.$http.get("/app/comments/statistics?merchantId=" + this.merchantId).then(res => {
+					if (res.data.code == 200) {
+						this.statistics = res.data.result
+					}
+				})
+			},
+			//获取用户评论列表
+			getcommentList(page) {
+				console.log(page)
+				this.$http.get("/app/comments/list?merchantId=" + this.merchantId + '&commentGrade=' + this.commentGrade +
+					'&pageNo=' + page.num + '&pageSize=' + page.size).then(
+					res => {
+						if (res.data.code == 200) {
+							console.log(res)
+							this.appraiseList = this.appraiseList.concat(res.data.result.records)
+							this.mescroll.endSuccess(res.data.result.records.length, res.data.result.total);; // 结束下拉刷新状态
+						}
+					})
+			},
+		},
+		onShow() {
+			let that = this
+			uni.getStorage({
+				key: 'login_user_info',
+				success: function(res) {
+					that.merchantId = res.data.merchantId
+				}
+			})
 		}
 	};
 </script>
@@ -140,99 +212,338 @@
 	.evaluate {
 		width: 100vw;
 		height: 100vh;
+		background: #F7F8FC;
+		display: flex;
+		flex-direction: column;
+		overflow: hidden;
+
+		/*#ifdef APP-PLUS*/
+		.top {
+			width: 100%;
+			height: var(--status-bar-height);
+				background: linear-gradient(90deg, #DCF0FF 4%, #FFE3CE 67%, #FFFCE6 100%), linear-gradient(0, #F7F8FC 0%, rgba(247, 248, 252, 0) 100%);
+		}
 
+		/*#endif*/
 		.head {
 			width: 100%;
+			height: 112rpx;
 			padding: 20rpx;
 			display: flex;
 			align-items: center;
-			background-color: #fff;
+			background: linear-gradient(90deg, #DCF0FF 4%, #FFE3CE 67%, #FFFCE6 100%), linear-gradient(0, #F7F8FC 0%, rgba(247, 248, 252, 0) 100%);
+			position: relative;
 
 			.cuIcon-back {
 				font-size: 40rpx;
 				margin-right: 40rpx;
 			}
+
+			.case {
+				position: absolute;
+				left: 50%;
+				transform: translateX(-50%);
+				font-size: 36rpx;
+				font-weight: 600;
+			}
 		}
 
-		.score {
-			width: 90%;
-			height: 300rpx;
-			margin: 20rpx auto;
-			background-color: #fff;
+		/*#ifdef APP-PLUS*/
+		.head {
+			width: 100%;
+			height: var(--status-bar-height);
+			background: linear-gradient(90deg, #DCF0FF 4%, #FFE3CE 67%, #FFFCE6 100%), linear-gradient(0, #F7F8FC 0%, rgba(247, 248, 252, 0) 100%);
+		}
 
-			.picture {
-				display: flex;
+		/*#endif*/
+		.center {
+			flex: 1;
+			padding: 0px 22rpx 10rpx;
+			overflow-y: auto;
 
-				.left {
-					width: 30%;
-				}
+			.score {
+				width: 100%;
+				margin: 20rpx auto 0px;
+				background: #FFFFFF;
+				border-radius: 16rpx;
+				padding: 16rpx;
 
-				.right {
-					width: 70%;
-					padding: 20rpx;
+				.picture {
 					display: flex;
-					flex-direction: column;
-					// align-items: center;
 					justify-content: space-between;
+					border-bottom: 1px solid #eeeeee;
+					padding-bottom: 10rpx;
+
+					.left {
+						width: 225rpx;
+						height: 93rpx;
+						display: flex;
+						align-items: center;
+
+						.mark {
+							flex: 1;
+							font-size: 64rpx;
+							font-weight: 600;
+							margin-right: 16rpx;
+						}
+
+						.num {
+							font-size: 22rpx;
+							color: #999999;
 
-					.line {
-						width: 100%;
+							span {
+								display: block;
+								margin-top: 8rpx;
+							}
+						}
+					}
+
+					.separate {
+						width: 1px;
+						height: 92rpx;
+						background-color: #eeeeee;
+					}
+
+					.right {
+						width: 354rpx;
+						// padding: 20rpx;
+						display: flex;
+						flex-direction: column;
+						// align-items: center;
+						justify-content: space-between;
+						font-size: 20rpx;
+						color: #999999;
+
+						.line {
+							width: 100%;
+							display: flex;
+							align-items: center;
+
+							span {
+								margin-right: 10rpx;
+							}
+						}
 					}
 				}
-			}
 
-			.people_num {
-				height: 100rpx;
-			}
-		}
+				.people_num {
+					width: 100%;
+					display: flex;
+					align-items: center;
+					font-size: 22rpx;
+					color: #999999;
+					margin-top: 10rpx;
 
-		.appraise {
-			width: 100%;
-			margin-top: 20rpx;
-			background-color: #fff;
-			padding: 20rpx;
+					.statis {
+						display: flex;
+						align-items: center;
+
+						span {
+							display: block;
+							width: 1rpx;
+							height: 22rpx;
+							background-color: #999999;
+							margin: 0px 16rpx;
+						}
+					}
 
-			.title {}
 
-			.select {
-				width: 100%;
-				display: flex;
-				flex-wrap: wrap;
-
-				.type {
-					padding: 0px 10rpx;
-					background-color: #ccc;
-					margin-right: 20rpx;
-					margin-top: 20rpx;
 				}
 			}
-			.list{
+
+			.appraise {
+				width: 100%;
 				margin-top: 20rpx;
-				.head_url{
+
+				.select {
 					width: 100%;
 					display: flex;
-					align-items: center;
-					image{
-						width: 80rpx;
-						height: 80rpx;
-						margin-right: 20rpx;
+					justify-content: space-between;
+					flex-wrap: wrap;
+					gap: 33rpx;
+					margin-bottom: 10rpx;
+
+					.type {
+						width: 144rpx;
+						height: 52rpx;
+						display: flex;
+						align-items: center;
+						justify-content: center;
+						background-color: #ccc;
+						// margin-right: 20rpx;
+						// margin-bottom: 20rpx;
+						background: #FFFFFF;
+						border-radius: 4rpx;
+					}
+
+					.active {
+						background: rgba(68, 154, 255, 0.1);
+						border: 1px solid #449AFF;
+						color: #2983EC;
 					}
 				}
-				.type{
-					margin-top: 20rpx;
-				}
-				.content{
-					margin-top: 20rpx;
-				}
-				.time{
-					margin-top: 20rpx;
-				}
-				.reply{
-					background-color: #ebebeb;
-					margin-top: 20rpx;
+
+				.list {
+
+					.item {
+						margin-top: 20rpx;
+						background: #ffffff;
+						padding: 16rpx;
+						border-radius: 16rpx;
+						box-shadow: 0px 2px 8px 0px rgba(214, 225, 237, 0.1);
+					}
+
+					.headr {
+						display: flex;
+						justify-content: space-between;
+
+						.left {
+							display: flex;
+
+							.img {
+								width: 70rpx;
+								height: 70rpx;
+								border-radius: 8rpx;
+								overflow: hidden;
+								margin-right: 12rpx;
+
+								image {
+									width: 100%;
+									height: 100%;
+								}
+							}
+						}
+
+						.name {
+							font-size: 24rpx;
+							color: #000000;
+
+							span {
+								font-size: 22rpx;
+								color: #999999;
+							}
+						}
+
+						.right {
+							width: 200rpx;
+							display: flex;
+							align-items: center;
+							justify-content: space-between;
+							font-size: 24rpx;
+							color: #FF2848;
+
+							span {
+								display: block;
+							}
+						}
+					}
+
+					.product {
+						width: 50%;
+						height: 36rpx;
+						background: #F4F4F4;
+						border-radius: 4rpx;
+						font-size: 22rpx;
+						color: #666666;
+						display: flex;
+						align-items: center;
+						padding: 0 12rpx;
+						margin: 10rpx 0px;
+					}
+
+					.type {
+						margin-top: 20rpx;
+					}
+
+					.content {
+						margin-top: 20rpx;
+					}
+
+					.recover {
+						background: #F7F8FC;
+						border-radius: 4rpx;
+						padding: 12rpx;
+						margin: 16rpx 0px 10rpx;
+					}
+
+					.image {
+						display: flex;
+						align-items: center;
+						margin: 10rpx 0px 16rpx;
+
+						.img {
+							width: 156rpx;
+							height: 156rpx;
+							border-radius: 4rpx;
+							overflow: hidden;
+							margin-right: 7rpx;
+						}
+
+						.img:nth-child(4n) {
+							margin: 0;
+						}
+
+						image {
+							width: 156rpx;
+							height: 156rpx;
+						}
+					}
+
+					.time {
+						margin-top: 20rpx;
+					}
+
+					.reply {
+						height: 60rpx;
+						background: #F7F8FC;
+						margin-top: 16rpx;
+						padding: 12rpx;
+						font-size: 24rpx;
+						display: flex;
+						align-items: center;
+
+						input {
+							font-size: 22rpx;
+						}
+					}
+
+					.remove {
+						display: flex;
+						align-items: center;
+						justify-content: space-between;
+						font-size: 22rpx;
+						color: #999999;
+
+						.btn {
+							margin-top: 10rpx;
+							width: 144rpx;
+							height: 47rpx;
+							border-radius: 47rpx;
+							border: 1px solid #449AFF;
+							font-size: 24rpx;
+							color: #449AFF;
+							display: flex;
+							align-items: center;
+							justify-content: center;
+						}
+					}
 				}
+
 			}
-			
 		}
+
+
+
+	}
+
+	/deep/.u-iconfont {
+		font-size: 20rpx;
+		width: 20rpx;
+		height: 20rpx;
+	}
+
+	/deep/.u-progress {
+		height: 12rpx !important;
+		border-radius: 53rpx;
 	}
 </style>

+ 15 - 3
pages/index/full.vue

@@ -1,5 +1,6 @@
 <template>
 	<view class="service">
+		<view class="top"></view>
 		<view class="head">
 			<span class="cuIcon-back" @click="back"></span>
 			<view class="case">
@@ -60,7 +61,7 @@
 					},
 					{
 						name: '客户评价',
-					url: '/static/index/evaluate.png'
+						url: '/static/index/evaluate.png'
 					},
 					{
 						name: '秒杀活动',
@@ -153,6 +154,14 @@
 		height: 100%;
 		background: #F7F8FC;
 
+		/*#ifdef APP-PLUS*/
+		.top {
+			width: 100%;
+			height: var(--status-bar-height);
+			background: linear-gradient(180deg, #D6E9FF 0%, #FFFFFF 100%);
+		}
+
+		/*#endif*/
 		.head {
 			width: 100%;
 			height: 112rpx;
@@ -205,16 +214,19 @@
 					// margin-bottom: 20rpx;
 					// margin-right: 40rpx;
 					font-size: 24rpx;
+
 					image {
 						width: 56rpx;
 						height: 56rpx;
 						margin-bottom: 10rpx;
 					}
 				}
-				.nomargin{
+
+				.nomargin {
 					margin-bottom: 0px;
 				}
-				.item:nth-child(-n+5){
+
+				.item:nth-child(-n+5) {
 					margin-bottom: 36rpx;
 				}
 			}

+ 12 - 3
pages/index/index.vue

@@ -1,5 +1,6 @@
 <template>
 	<view class="index">
+		<view class="top"></view>
 		<view class="head">
 			<view class="header" @click="shopDetails">
 				<image src="/static/index/address.png" mode=""></image>
@@ -28,7 +29,7 @@
 		<view class="service">
 			<view class="main">
 				<view class="scan" @click="scan">
-					<image src="/static/index/scan.png" mode=""></image>扫码核销
+					<image src="/static/index/scan.png" mode=""></image>核销
 				</view>
 				<view class="scan" @click="scanRecord">
 					<image src="/static/index/record.png" mode=""></image>核销记录
@@ -99,7 +100,7 @@
 			//扫码核销
 			scan() {
 				uni.navigateTo({
-					url: '/pages/scan/scan'
+					url: '/pages/scan/manual'
 				})
 			},
 			//跳转至门店详情页
@@ -181,6 +182,14 @@
 				}
 			}
 		}
+		/*#ifdef APP-PLUS*/
+		.top {
+			width: 100%;
+			height: var(--status-bar-height);
+			background: linear-gradient(90deg, #E6FFFF 0%, #CEF7FF 40%, #A3CEFE 73%, #BCD6FF 100%), linear-gradient(to bottom , #F7F8FC 0%, rgba(247,248,252,0) 100%);
+		}
+		
+		/*#endif*/
 
 		.data_box {
 			width: 676rpx;
@@ -252,7 +261,7 @@
 					flex-direction: column;
 					font-size: 26rpx;
 					font-weight: 600;
-
+					align-items: center;
 					image {
 						width: 106rpx;
 						height: 106rpx;

+ 93 - 28
pages/index/shop.vue

@@ -1,5 +1,6 @@
 <template>
 	<view class="shop">
+			<view class="top"></view>
 		<view class="head">
 			<span class="cuIcon-back" @click="back"></span>
 			<view class="case">
@@ -24,8 +25,8 @@
 							<span>{{shopData.commentGradeAvg}}分</span>
 						</view>
 						<span v-if="shopData.merchantStatus == 1">营业中</span>
-						<span v-if="shopData.merchantStatus == 2" style="color: #FF8024;">歇业中</span>
-						<span v-if="shopData.merchantStatus == 3" style="color: #999999;">门店关闭</span>
+						<span v-if="shopData.merchantStatus == 3" style="color: #FF8024;">歇业中</span>
+						<span v-if="shopData.merchantStatus == 2" style="color: #999999;">门店关闭</span>
 					</view>
 				</view>
 				<view class="address">
@@ -74,27 +75,27 @@
 					<view class="cancel" @click="closePopup">
 						取消
 					</view>
-					<view class="submit">
+					<view class="submit" @click="changeStatus">
 						确定
 					</view>
 				</view>
 			</view>
 		</view>
 		<view class="popup res" v-if="isSuccess">
-			<view class="content">
+			<view class="c">
 				<view class="title">
-					<span v-if="status == 1">正常营业</span>
-					<span v-if="status == 2">门店歇业</span>
-					<span v-if="status == 3|| status == 4">关闭门店</span>
+					<span v-if="shopStatus == 1">正常营业</span>
+					<span v-if="shopStatus == 3">门店歇业</span>
+					<span v-if="shopStatus == 2">关闭门店</span>
 				</view>
 				<view class="center">
-					<image src="/static/index/start.png" mode="" v-if="status == 1"></image>
-					<image src="/static/index/rest.png" mode="" v-if="status == 2"></image>
-					<image src="/static/index/close.png" mode="" v-if="status == 3"></image>
-					<span v-if="shopData.merchantStatus == 1">恭喜发财,开业大吉!</span>
-					<span v-if="shopData.merchantStatus == 2">当前门店关闭成功</span>
-					<span v-if="shopData.merchantStatus == 3">当前门店已歇业</span>
-					<view class="error" v-if="shopData.merchantStatus == 3|| shopData.merchantStatus == 4">
+					<image src="/static/index/start.png" mode="" v-if="shopStatus == 1"></image>
+					<image src="/static/index/close.png" mode="" v-if="shopStatus == 2"></image>
+					<image src="/static/index/rest.png" mode="" v-if="shopStatus == 3"></image>
+					<span v-if="shopStatus == 1">恭喜发财,开业大吉!</span>
+					<span v-if="shopStatus == 2">当前门店关闭成功</span>
+					<span v-if="shopStatus == 3">当前门店已歇业</span>
+					<view class="error" v-if="shopStatus == 4">
 						当前门店还存在有效服务券<br />
 						暂时无法关闭门店
 					</view>
@@ -112,13 +113,13 @@
 		data() {
 			return {
 				btnList: [{
-						name: '正常营业', 
+						name: '正常营业',
 					},
 					{
-						name: '门店歇业', 
-					}, 
+						name: '门店歇业',
+					},
 					{
-						name: '关闭门店', 
+						name: '关闭门店',
 					}
 				],
 				currentIndex: 0,
@@ -128,11 +129,12 @@
 				isSuccess: false,
 				count: 5,
 				shopData: {},
-				shopStatus:null
+				shopStatus: null,
+				id: ''
 			}
 		},
 		methods: {
-			edit(){
+			edit() {
 				uni.navigateTo({
 					url: '/pages/store/editStore'
 				})
@@ -147,8 +149,47 @@
 			toggle(item, index) {
 				this.currentIndex = index
 			},
+			changeStatus() {
+				if (this.currentIndex == 0) {
+					let data = {
+						id: this.id,
+						merchantStatus: 1
+					}
+					this.$http.post("/merchant/localMerchantInfo/editStatus", data).then(res => {
+						if (res.data.code == 200) {
+							this.isPopup = false
+							this.getShopData()
+							this.isSuccess = true
+						}
+					})
+				} else if (this.currentIndex == 1) {
+					let data = {
+						id: this.id,
+						merchantStatus: 3
+					}
+					this.$http.post("/merchant/localMerchantInfo/editStatus", data).then(res => {
+						if (res.data.code == 200) {
+							this.isPopup = false
+							this.getShopData()
+							this.isSuccess = true
+						}
+					})
+				} else if (this.currentIndex == 2) {
+					let data = {
+						id: this.id,
+						merchantStatus: 2
+					}
+					this.$http.post("/merchant/localMerchantInfo/editStatus", data).then(res => {
+						if (res.data.code == 200) {
+							this.isPopup = false
+							this.getShopData()
+							this.isSuccess = true
+						}
+					})
+				}
+			},
 			getShopData() {
-				this.$http.get("/merchant/localMerchantInfo/queryById?id=" + '1887039799020273666').then(res => {
+				this.$http.get("/merchant/localMerchantInfo/queryById?id=" + this.id).then(res => {
 					if (res.data.code == 200) {
 						this.shopData = res.data.result
 						this.shopStatus = res.data.result.merchantStatus
@@ -156,22 +197,34 @@
 					}
 				})
 			},
-			currentStatus(){
-				if(this.shopStatus ==1){
+			currentStatus() {
+				if (this.shopStatus == 1) {
 					this.currentIndex = 0
-				}else if(this.shopStatus ==2){
-					this.currentIndex = 1
-				} else if(this.shopStatus ==3){
+					this.dynamicClass = 'start'
+				} else if (this.shopStatus == 2) {
 					this.currentIndex = 2
+					this.dynamicClass = 'close'
+				} else if (this.shopStatus == 3) {
+					this.currentIndex = 1
+					this.dynamicClass = 'rest'
 				}
 			},
-			closePopup(){
+			closePopup() {
 				this.isPopup = false
 				this.currentStatus()
 			}
 		},
 		mounted() {
 			this.getShopData()
+		},
+		onShow() {
+			let that = this
+			uni.getStorage({
+				key: 'login_user_info',
+				success: function(res) {
+					that.id = res.data.merchantId
+				}
+			})
 		}
 	}
 </script>
@@ -189,7 +242,14 @@
 		display: flex;
 		flex-direction: column;
 		justify-content: space-between;
+	/*#ifdef APP-PLUS*/
+			.top {
+				width: 100%;
+				height: var(--status-bar-height);
+				background: linear-gradient(180deg, #FFF9F1 0%, #FFFFFF 100%);
+			}
 
+			/*#endif*/
 		.head {
 			width: 100%;
 			height: 112rpx;
@@ -199,6 +259,7 @@
 			background: linear-gradient(180deg, #FFF9F1 0%, #FFFFFF 100%);
 			position: relative;
 
+		
 			.cuIcon-back {
 				font-size: 40rpx;
 				margin-right: 40rpx;
@@ -209,7 +270,7 @@
 				left: 50%;
 				transform: translateX(-50%);
 				font-size: 36rpx;
-				font-weight: 500;
+				font-weight: 600;
 			}
 		}
 
@@ -546,6 +607,10 @@
 					background: #53CC48;
 				}
 
+				.close {
+					background: #449AFF;
+				}
+
 				.rest {
 					background: #FF8024;
 				}

+ 179 - 119
pages/order/details.vue

@@ -1,169 +1,229 @@
 <template>
-	<view class="details">
+	<view class="check">
+		<view class="top">
+
+		</view>
 		<view class="head">
 			<span class="cuIcon-back" @click="back"></span>
 			<view class="case">
-				订单详情
-			</view>
-		</view>
-		<view class="status">
-			<view class="title">
-				订单状态
-			</view>
-			提交3张现场服务凭证,平台审核完成后,赚取1元现金奖励
-		</view>
-		<view class="client">
-			客户信息:{{name}}{{plate}}
-		</view>
-		<view class="product">
-			<view class="img">
-				<image src="/static/avatar_girl.png" mode=""></image>
-			</view>
-			<view class="price">
-				服务项目名称 
-				<span>¥120.00</span>
+				售后详情
 			</view>
 		</view>
-		<view class="proof">
-			<view class="title">
-				服务现场凭证
+		<view class="content">
+			<view class="status">
+				{{product.state}}<br />
+				<span>顾客于2025/12/30 12:20:47下单</span>
 			</view>
-			<view class="describe">
-				订单奖励:¥1.00(需上传5张服务凭证的现场照片)
-			</view>
-			<view class="image">
-				<view class="describe_image" v-for="(item,index) in imgList" :key="index">
-					<image :src="item.url" mode=""></image>
+			<view class="product">
+				<view class="title">
+					商品信息
+				</view>
+				<view class="center">
+					<view class="img">
+						<image :src="product.productImage" mode=""></image>
+					</view>
+					<view class="name">
+						{{product.productName}}
+						<view class="price">
+							¥{{product.costPrice}}/份
+							<span>实付款:¥ {{product.orderPrice}}</span>
+						</view>
+					</view>
 				</view>
 			</view>
-		</view>
-		<view class="proof">
-			<view class="title">
-				订单信息
-			</view>
-			<view class="data">
-				<br/>
-				订单号:{{num}}<br/>
-				<br/>
-				提交人:<br/>
-				<br/>
-				提交时间<br/>
+			<view class="product">
+				<view class="title">
+					订单信息
+				</view>
+				<view class="data">
+					<span>订单号</span>
+					{{product.orderId}}
+				</view>
+				<view class="data">
+					<span>支付流水号</span>
+					{{product.tradeId}}
+				</view>
+				<view class="data">
+					<span>提交人</span>
+					{{product.linkName}}
+				</view>
+				<view class="data">
+					<span>联系电话</span>
+					{{product.userPhone}}
+				</view>
+				<view class="data">
+					<span>下单时间</span>
+					{{product.buyTime}}
+				</view>
+				<view class="data">
+					<span>付款时间</span>
+					{{product.payTime}}
+				</view>
+				<view class="data">
+					<span>使用时间</span>
+					{{product.useTime}}
+				</view>
 			</view>
 		</view>
 	</view>
 </template>
 
 <script>
-	export default{
+	export default {
 		data() {
-			return{
-				name:'**是',
-				plate:'晋B11111',
-				imgList:[
-					{
-						url:'/static/avatar_girl.png'
-					},
-					{
-						url:'/static/avatar_girl.png'
-					},
-					{
-						url:'/static/avatar_girl.png'
-					},
-					{
-						url:'/static/avatar_girl.png'
-					},
-					{
-						url:'/static/avatar_girl.png'
-					},
-				],
-				num:10212001221002
+			return {
+			orderId:'',
+			product:{
 				
 			}
+			}
 		},
-		methods:{
+		methods: {
 			//返回
 			back() {
 				uni.navigateBack({
 					delta: 1
 				});
 			},
+			getDetail(){
+			this.$http.get("/app/order/locaOrder/queryById?orderId="+this.orderId,).then(res => {
+				if (res.data.code == 200) {
+					console.log(res)
+					this.product = res.data.result
+				}
+			})
+			}
+		},
+		onLoad(option) {
+			this.orderId = option.orderId
+		},
+		mounted() {
+			this.getDetail()
 		}
 	}
 </script>
 
 <style lang="scss" scoped>
-	.details{
-		width: 100%;
-		height: 100%;
+	.check {
+		width: 100vw;
+		height: 100vh;
+		background: #F7F8FC;
+
+		/*#ifdef APP-PLUS*/
+		.top {
+			width: 100%;
+			height: var(--status-bar-height);
+			background: linear-gradient(87deg, #E2F4F8 0%, #A3CDFF 100%), linear-gradient(360deg, #FFFFFF 0%, rgba(255, 255, 255, 0) 100%);
+		}
+
+		/*#endif*/
 		.head {
 			width: 100%;
+			height: 112rpx;
 			padding: 20rpx;
 			display: flex;
 			align-items: center;
-			background-color: #fff;
+			background: linear-gradient(87deg, #E2F4F8 0%, #A3CDFF 100%), linear-gradient(360deg, #FFFFFF 0%, rgba(255, 255, 255, 0) 100%);
 			position: relative;
-		
+
 			.cuIcon-back {
 				font-size: 40rpx;
 				margin-right: 40rpx;
 			}
-		
-			.step {
+
+			.case {
 				position: absolute;
-				right: 20rpx;
-			}
-		}
-		.status{
-			background-color: #fff;
-			border-top: 1px solid #ccc;
-			padding: 20rpx;
-			.title{
-				
+				left: 50%;
+				transform: translateX(-50%);
+				font-size: 36rpx;
+				font-weight: 600;
 			}
 		}
-		.client{
-			background-color: #fff;
-			margin-top: 20rpx;
-			padding: 20rpx;
-		}
-		.product{
-			display: flex;
-			align-items: center;
-			background-color: #fff;
-			margin-top: 20rpx;
-			padding: 20rpx;
-			.img{
-				image{
-					width: 100rpx;
-					height: 100rpx;
+
+		.content {
+			padding: 24rpx;
+
+			.status {
+				font-size: 44rpx;
+				padding: 0px 20rpx;
+				color: #333333;
+
+				span {
+					font-size: 28rpx;
+					color: #666666;
+					line-height: 33rpx;
 				}
 			}
-			.price{
+
+			.product {
 				width: 100%;
-				display: flex;
-				flex-direction: column;
-				justify-content: space-between;
-				margin-left: 50rpx;
-				
-			}
-		}
-		.proof{
-			background-color: #fff;
-			margin-top: 20rpx;
-			padding: 20rpx;
-			.title{
-				font-weight: 600;
-			}
-			.describe{
+				margin-top: 24rpx;
+				background: #FFFFFF;
+				box-shadow: 0px 2px 8px 0px rgba(214, 225, 237, 0.1);
+				border-radius: 16px;
+				padding: 20rpx;
+
+				.title {
+					font-size: 32rpx;
+					color: #333333;
+				}
+
+				.center {
+					display: flex;
+					border-radius: 16rpx;
+					margin-top: 16rpx;
+
+					.img {
+						width: 132rpx;
+						height: 132rpx;
+						border-radius: 12rpx;
+						overflow: hidden;
+						margin-right: 16rpx;
+
+						image {
+							width: 132rpx;
+							height: 132rpx;
+						}
+					}
+
+					.name {
+						flex: 1;
+						display: flex;
+						flex-direction: column;
+						justify-content: space-between;
+						font-size: 30rpx;
+						color: #000000;
+						line-height: 35rpx;
+						font-weight: 600;
+
+						.price {
+							font-size: 24rpx;
+							display: flex;
+							align-items: center;
+							justify-content: space-between;
+							color: #FF6A42;
+
+							span {
+								color: #666666;
+							}
+						}
+					}
+				}
+
+				.data {
+					width: 100%;
+					height: 38rpx;
+					display: flex;
+					align-items: center;
+					justify-content: space-between;
 					margin-top: 20rpx;
-			}
-			.image{
-				display: flex;
-				justify-content: space-between;
-				margin-top: 20rpx;
-				image{
-					width:100rpx ;
-					height: 100rpx;
+					font-size: 28rpx;
+					color: #333333;
+
+					span {
+						font-size: 26rpx;
+						color: #8A91A8;
+					}
 				}
 			}
 		}

+ 304 - 123
pages/order/index.vue

@@ -1,128 +1,210 @@
 <template>
 	<view class="order">
-		<view class="head">
-			<view class="cu-bar search bg-white">
-				<view class="search-form round">
-					<input @blur="InputBlur" :adjust-position="false" type="text" placeholder="订单号,客户姓名,手机号搜索"
-						confirm-type="search"></input>
-					<text class="cuIcon-search"></text>
+		<view class="fix">
+
+			<view class="top"></view>
+			<view class="head">
+				<span class="cuIcon-back" @click="back"></span>
+				<view class="case">
+					订单
 				</view>
-				<view class="action ">
+				<view class="action " @click="selectTime">
 					<view class="cuIcon-filter"></view>
 				</view>
 			</view>
-			<view class="type">
-				<view class="type_item" :class="currentIndex == index?'actice':''" v-for="(item,index) in typeList" :key="index"
-					@click="select(item,index)">
+			<view class="search">
+				<image src="/static/index/search.png" mode=""></image>
+				<u-input type="text" v-model="merchantUserInfo" placeholder="请输入关键词" />
+				<span @click="searchList">搜索</span>
+			</view>
+			<view class="select">
+				<view class="type" :class="currentIndex == index?'active':''" v-for="(item,index) in typeList" :key="index"
+					@click="selectType(item,index)">
 					{{item.name}}
 				</view>
 			</view>
-
 		</view>
-			<view class="order_list">
-				<view class="order_item" v-for="(item,index) in orderList" :key="index">
-					<view class="order_id">
-						<view class="id_left">
-							订单号:{{item.orderId}}
-						</view>
-						<view class="id_right">
-							{{item.status}}
-						</view>
+		<mescroll-uni class="list" :fixed="false" ref="mescrollRef" @up="getList" @init="mescrollInit" :use="false">
+			<!-- <view class="order_list"> -->
+			<view class="order_item" v-for="(item,index) in orderList" :key="item.id" @click="geDetail(item)">
+				<view class="order_id">
+					<view class="id_left">
+						订单编号:{{item.orderId}}
+					</view>
+					<view class="id_right" v-if="item.stateCode ==5">
+						<!-- {{item.state}} -->
+						待付款
+					</view>
+					<view class="id_right" v-if="item.stateCode ==0" style="color: #ff9900;">
+						待使用
+					</view>
+					<view class="id_right" v-if="item.stateCode == 4" style="color:#449AFF ;">
+						退款/售后
 					</view>
-					<view class="order_product">
-						<view class="img">
-							<image :src="item.url" mode=""></image>
-						</view>.
-						<view class="name">
-							{{item.name}}
-							<span>¥{{item.price}}</span>
-						</view>
-						<view class="schedule">
-							{{item.schedule}}
-						</view>
+					<view class="id_right" v-if="item.stateCode ==1">
+						已使用
 					</view>
-					<view class="time">
-						<view class="time_left">
-							{{item.time}}
-						</view>
-						<view class="time_right" @click="goDetails">
-							查看详情
-						</view>
+				</view>
+				<view class="order_product">
+					<view class="img">
+						<image :src="item.productImage" mode=""></image>
+					</view>
+					<view class="name">
+						{{item.productName}}
+						<span>¥{{item.orderPrice}}</span>
+					</view>
+				</view>
+				<view class="time">
+					<view class="time_left">
+						顾客于{{item.buyTime}}
+					</view>
+					<view class="time_right" @click="goDetails">
+						去处理
 					</view>
 				</view>
 			</view>
+			<!-- </view> -->
+		</mescroll-uni>
+		<rangTime ref="rangTime" :mode="mode" :isYear="true" @onSubmit="onEndTimeChange" />
 	</view>
 
 </template>
 
 <script>
+	import rangTime from '../../components/dengrq-datetime-picker/dateSelector/index.vue'
 	export default {
+		components: {
+			rangTime,
+		},
 		data() {
 			return {
 				search: '',
+				mode: 1,
 				currentIndex: 0,
+				merchantId: '',
+				buyStartTime:'',
+				buyEndTime:'',
 				typeList: [{
-						type: 1,
+						type: '',
 						name: '全部'
 					},
 					{
-						type: 2,
-						name: '待使用'
+						type: 5,
+						name: '待付款'
 					},
 					{
-						type: 3,
-						name: '已完成'
+						type: 0,
+						name: '待使用'
 					},
 					{
-						type: 4,
-						name: '已失效'
+						type: 1,
+						name: '已使用'
 					},
 					{
-						type: 5,
-						name: '售后'
+						type: 4,
+						name: '退款/售后'
 					}
 				],
-				orderList: [{
-						orderId: 1111111111,
-						status: '待使用',
-						url: '/static/avatar_girl.png',
-						name: '普通洗车',
-						price: 58.88,
-						time: '2020.12.31.12.20',
-						schedule: '待处理'
+				orderList: [],
+				state: '',
+				merchantUserInfo: '',
+				upOption: {
+					page: {
+						num: 0,
+						size: 10 // 每页数据的数量,默认10
 					},
-					{
-						orderId: 1111111111,
-						status: '待使用',
-						url: '/static/avatar_girl.png',
-						name: '普通洗车',
-						price: 58.88,
-						time: '2020.12.31.12.20'
-					},
-					{
-						orderId: 1111111111,
-						status: '待使用',
-						url: '/static/avatar_girl.png',
-						name: '普通洗车',
-						price: 58.88,
-						time: '2020.12.31.12.20'
-					},
-				]
+					noMoreSize: 5, // 配置列表的总数量要大于等于5条才显示'-- END --'的提示
+					empty: {
+						tip: '暂无相关数据'
+					}
+				},
 			}
 		},
 		methods: {
+			selectTime() {
+				this.$refs.rangTime.open()
+				uni.hideTabBar()
+			},
+			onEndTimeChange(val) {
+				this.buyStartTime = val.startDate
+				this.buyEndTime = val.endDate
+				let page = {
+					num: 1,
+					size: 10
+				}
+				this.getList(page)
+			},
+			mescrollInit(mescroll) {
+				this.mescroll = mescroll;
+			},
 			InputBlur(e) {
 				this.search = e.detail.value
 			},
-			select(item, index) {
+			selectType(item, index) {
 				this.currentIndex = index
+				this.state = item.type
+				this.orderList = []
+				this.mescroll.resetUpScroll(); 
+				// let page = {
+				// 	num: 1,
+				// 	size: 10
+				// }
+				// this.getList(page)
+			},
+			searchList() {
+				let page = {
+					num: 1,
+					size: 10
+				}
+				this.orderList = []
+				this.getList(page)
 			},
 			//跳转至订单详情页
-			goDetails(){
+			goDetails() {
+				uni.navigateTo({
+					url: '/pages/order/details'
+				})
+			},
+			getList(page) {
+				let data = {
+					merchantId: this.merchantId,
+					pageSize: page.size,
+					pageNo: page.num,
+					state: this.state,
+					merchantUserInfo: this.merchantUserInfo,
+					buyStartTime:this.buyStartTime,
+					buyEndTime:this.buyEndTime
+				}
+				this.$http.post("/app/order/locaOrder/list", data).then(res => {
+					if (res.data.code == 200) {
+						console.log(res)
+						this.orderList = this.orderList.concat(res.data.result.records)
+						this.mescroll.endSuccess(res.data.result.records.length, res.data.result.total);
+						uni.showTabBar()
+					}
+					else{
+						this.orderList = []
+					}
+				})
+			},
+			geDetail(item) {
 				uni.navigateTo({
-					url:'/pages/order/details'
+					url: '/pages/order/details?orderId=' + item.orderId
 				})
 			}
+		},
+		mounted() {
+			this.mescroll.optDown.use = false
+			// this.getList()
+		},
+		onShow() {
+			let that = this
+			uni.getStorage({
+				key: 'login_user_info',
+				success: function(res) {
+					that.merchantId = res.data.merchantId
+				}
+			})
 		}
 	}
 </script>
@@ -130,96 +212,195 @@
 <style lang="scss" scoped>
 	.order {
 		width: 100%;
-		height: 100%;
+		height: 100vh;
+		background: #F7F8FC;
+		display: flex;
+		flex-direction: column;
+		position: fixed;
 
-		.search-form {
-			padding: 0px 20rpx;
-		}
+		.fix {
+			width: 100%;
+			height: auto;
+			background: #F7F8FC;
+			position: sticky;
+			top: 0px;
 
-		.head {
-			background-color: #fff;
-			.type {
+			/*#ifdef APP-PLUS*/
+			.top {
+				width: 100%;
+				height: var(--status-bar-height);
+				background: linear-gradient(87deg, #E2F4F8 0%, #A3CDFF 100%), linear-gradient(to bottom, #FFFFFF, #FFFFFF);
+			}
+
+			/*#endif*/
+			.head {
+				width: 100%;
+				height: 112rpx;
+				padding: 20rpx;
+				display: flex;
+				align-items: center;
+				background: #fff;
+				position: relative;
+				background: linear-gradient(87deg, #E2F4F8 0%, #A3CDFF 100%), linear-gradient(to bottom, #FFFFFF, #FFFFFF);
+
+				.cuIcon-back {
+					font-size: 40rpx;
+					margin-right: 40rpx;
+				}
+
+				.case {
+					position: absolute;
+					left: 50%;
+					transform: translateX(-50%);
+					font-size: 36rpx;
+					font-weight: 600;
+				}
+
+				.action {
+					position: absolute;
+					right: 24rpx;
+				}
+			}
+
+			.search {
+				width: 93%;
+				height: 55rpx;
+				background: #F7F8FC;
+				border-radius: 12rpx;
+				padding: 0rpx 24rpx;
 				display: flex;
+				align-items: center;
 				justify-content: space-between;
-				border-top: 1px solid #999;
-				margin-top: 20rpx;
+				margin: 16rpx auto 0px;
 
-				.type_item {
-					width: 100rpx;
-					height: 80rpx;
-					display: flex;
-					justify-content: center;
-					align-items: center;
+				image {
+					width: 36rpx;
+					height: 36rpx;
+				}
+
+				input {
+					// height: 36rpx;
+					font-size: 26rpx;
+				}
+
+				span {
+					font-size: 26rpx;
+					color: #333333;
+				}
+			}
+
+			.select {
+				width: 100%;
+				height: 78rpx;
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				padding: 0px 24rpx;
+
+				.type {
+					height: 54rpx;
+					padding: 8rpx 12rpx;
+					background: #F2F3F5;
+					border-radius: 8rpx;
 				}
 
-				.actice {
-					background-color: antiquewhite;
+				.active {
+					background: #ECF5FF;
+					color: #2D88F4;
 				}
 			}
 		}
 
-		.order_list {
+		.list {
+			flex: 1;
 			width: 100%;
 			margin-top: 20rpx;
-		
-			padding: 20rpx;
-			.order_item{
+			overflow-y: auto;
+			padding-bottom: 100rpx;
+			.order_item {
 				width: 100%;
 				background-color: #fff;
-				padding: 20rpx;
+				padding: 20rpx 40rpx;
 				margin-bottom: 20rpx;
-				.order_id{
+
+				.order_id {
 					width: 100%;
+					height: 41rpx;
+					font-size: 26rpx;
+					color: #8B91A4;
 					display: flex;
 					justify-content: space-between;
 					align-items: center;
-					.id_left{
-						
-					}
-					.id_right{
-						border: 1px solid #999;
-						padding: 5rpx 10rpx;
-					}
+
+					.id_right {}
 				}
-				.order_product{
+
+				.order_product {
 					width: 100%;
-					height: 100rpx;
+					height: 132rpx;
 					display: flex;
-					// justify-content: space-between;
-					align-items: end;
 					margin: 10rpx 0px;
-					.img{
-						image{
-							width: 100rpx;
-							height: 100rpx;
+
+					.img {
+						width: 132rpx;
+						height: 132rpx;
+						border-radius: 12rpx;
+						overflow: hidden;
+
+						image {
+							width: 132rpx;
+							height: 132rpx;
 						}
 					}
-					.name{
-						// width: 60%;
+
+					.name {
+						height: 100%;
 						flex: 1;
-						height: 100rpx;
 						display: flex;
 						flex-direction: column;
 						justify-content: space-between;
 						margin-left: 20rpx;
+						font-size: 28rpx;
+						color: #000000;
+
+						span {
+							font-size: 22rpx;
+							color: #F0761D;
+						}
 					}
-					.schedule{
+
+					.schedule {
 						width: 15%;
 					}
 				}
-				.time{
+
+				.time {
 					width: 100%;
 					display: flex;
+					align-items: center;
 					justify-content: space-between;
-					.time_left{
-						
+					margin-top: 20rpx;
+
+					.time_left {
+						font-size: 24rpx;
+						color: #8A91A8;
 					}
-					.time_right{
-						border: 1px solid #999;
-						padding: 5rpx 10rpx;
+
+					.time_right {
+						width: 126rpx;
+						height: 50rpx;
+						background: rgba(240, 118, 29, 0.05);
+						border-radius: 8rpx;
+						border: 1px solid #F0761D;
+						font-size: 26rpx;
+						color: #F0761D;
+						display: flex;
+						align-items: center;
+						justify-content: center;
 					}
 				}
 			}
+
 		}
 	}
 </style>

+ 132 - 60
pages/product/creatProduct.vue

@@ -1,5 +1,6 @@
 <template>
 	<view class="creat">
+		<view class="top"></view>
 		<view class="head">
 			<span class="cuIcon-back" @click="back"></span>
 			<view class="case">
@@ -36,8 +37,8 @@
 					<view class="tips">
 						支持jpg、png格式,尺寸为00px*00px
 					</view>
-					<u-upload ref="uUpload" :action="action" :file-list="fmImageList" max-count="1" width="140rpx"
-						height="140rpx"></u-upload>
+					<u-upload ref="uUpload" :action="action" :file-list="fmImageList" @on-success="handleSuccess"
+						@on-remove="handleRemove" max-count="1" width="140rpx" height="140rpx"></u-upload>
 				</view>
 				<view class="item upload">
 					<view class="label">
@@ -46,8 +47,8 @@
 					<view class="tips">
 						支持jpg、png格式,尺寸为00px*00px,最多20张
 					</view>
-					<u-upload ref="dUpload" :action="action" :file-list="xqImage" max-count="20" width="140rpx"
-						height="140rpx"></u-upload>
+					<u-upload ref="dUpload" :action="action" :file-list="xqImage" @on-success="detailsSuccess"
+						@on-remove="detailsRemove" max-count="20" width="140rpx" height="140rpx"></u-upload>
 				</view>
 				<view class="item">
 					<view class="label">
@@ -112,7 +113,7 @@
 							<u-icon name="arrow-right" style="margin-left: 17rpx;"></u-icon>
 						</view>
 						<view class="picker set inp" @click="setUsable" v-else>
-							{{showSellDate}}
+							<span style="color: #333333;">{{showSellDate}}</span>
 							<u-icon name="arrow-right" style="margin-left: 17rpx;"></u-icon>
 						</view>
 					</view>
@@ -147,7 +148,7 @@
 						<u-input v-model="form.instructions" type="textarea" placeholder="输入库存数量" maxlength="500" />
 					</view>
 					<view class="num">
-						{{form.instructions.length}}/500
+						<span v-if="form.instructions">{{form.instructions.length>0?form.instructions.length:'0'}}</span>/500
 					</view>
 				</view>
 			</view>
@@ -226,11 +227,11 @@
 					// refundLabel:null,
 					// refundConfirm:null
 				},
-				fmImageList: [], //封面图
-				xqImage: [], //详情图
+				fmImageList: [], //封面图回显
+				xqImage: [], //详情图回显
 				showRule: '', //显示退款规则文字
 				action: 'http://192.168.0.36:8080/jeecg-boot/sys/common/upload', //图片上传地址
-				schedule: 1, //进度
+				schedule: 2, //进度
 				fileList: [],
 				selectedTime: null,
 				isSelectDate: false,
@@ -248,36 +249,46 @@
 				showDate: '',
 				showSellDate: '',
 				makeChecked: false,
-				imageHttp:'http://192.168.0.36:8080/jeecg-boot/'//上传图片前缀
+				imageHttp: 'http://192.168.0.36:8080/jeecg-boot/', //上传图片前缀+
+				productId: '',
+				postImage: [], //上传图片详情
 			}
 		},
 		methods: {
+			// 上传封面图成功回调
+			handleSuccess(res, index, lists) {
+
+				this.form.productImage = this.imageHttp + res.message
+			},
+			// 移除封面图文件回调
+			handleRemove(index, lists) {
+				this.fmImageList = [];
+			},
+			//上传详情图
+			detailsSuccess(res, index, lists) {
+				// this.postImage.push({url:this.imageHttp + res.message})
+				this.postImage = lists
+
+			},
+			detailsRemove(index, lists) {
+				this.postImage = lists
+			},
 			//下一步
 			next() {
-				//封面图处理
-				if (this.$refs.uUpload.lists.length > 0) {
-					let files = [];
-					// 通过filter,筛选出上传进度为100的文件(因为某些上传失败的文件,进度值不为100,这个是可选的操作)
-					files = this.$refs.uUpload.lists.filter(val => {
-						return val.progress == 100;
+				let temp = []
+				if (this.postImage.length > 0) {
+					this.postImage.forEach(item => {
+						temp.push(this.imageHttp + item.response.message)
 					})
-					this.form.productImage =this.imageHttp + files[0].response.message
 				}
-				// 详情图处理
-				if (this.$refs.dUpload.lists.length > 0) {
-					let files = [];
-					// 通过filter,筛选出上传进度为100的文件(因为某些上传失败的文件,进度值不为100,这个是可选的操作)
-					files = this.$refs.dUpload.lists.filter(val => {
-						return val.progress == 100;
-					})
-					let temp = []
-					files.map(item => {
-						temp.push(this.imageHttp+item.response.message)
-					})
-					this.form.detailImages = temp.join(',')
+				if (this.xqImage.length > 0) {
+					this.xqImage.forEach((item => {
+						temp.push(item.url)
+					}))
+
 				}
+				this.form.detailImages = temp.join(',')
 				//基础信息必填提示
-				console.log(this.form)
 				if (!this.form.categoryId) {
 					uni.showToast({
 						title: '请选择商品分类',
@@ -339,7 +350,6 @@
 			// 处理 confirm 事件,接收子组件传递的时间段数据
 			handleConfirm(time) {
 				this.selectedTime = time; // 保存时间段数据
-				console.log(this.selectedTime)
 				this.isSelectDate = false
 			},
 			//隐藏弹窗
@@ -354,13 +364,11 @@
 			},
 			// 保存退款规则
 			saveFound(rules) {
-				console.log(rules)
 				this.form.refundConfirm = rules.refundConfirm.refundConfirm
 				this.form.refundLabel = rules.refundLabel.refundLabel
 				let value1 = []
 				value1.push(rules.refundConfirm.name, rules.refundLabel.name)
 				this.showRule = value1.join("·")
-				// console.log(this.showRule)
 				// this.selectedRules = rules; 
 				this.isRefund = false
 			},
@@ -459,7 +467,6 @@
 			onEndTimeChange(val) {
 				this.form.saleStartDate = val.startDate
 				this.form.saleEndDate = val.endDate
-				console.log(this.form.saleStartDate, this.form.saleEndDate)
 				this.showDate = val.startDate + '-' + val.endDate
 				this.$forceUpdate(); // 强制刷新视图
 			},
@@ -485,33 +492,85 @@
 					});
 					return
 				} else {
-					this.$http.post("/app/product/add", this.form).then(res => {
-						if (res.data.code == 200) {
-							uni.showToast({
-								title: '添加成功',
-								icon: 'none'
+					if (this.productId == '') {
+						this.$http.post("/app/product/add", this.form).then(res => {
+							if (res.data.code == 200) {
+								uni.showToast({
+									title: '添加成功',
+									icon: 'none'
+								});
+							}
+							uni.removeStorage({
+								key: 'smdate',
+							});
+							uni.removeStorage({
+								key: 'group',
+							});
+							this.back()
+						})
+					} else {
+						this.form.productId = this.productId
+						this.$http.post("/app/product/edit", this.form).then(res => {
+							if (res.data.code == 200) {
+								uni.showToast({
+									title: '编辑成功',
+									icon: 'none'
+								});
+							}
+							uni.removeStorage({
+								key: 'smdate',
+							});
+							uni.removeStorage({
+								key: 'group',
 							});
+							this.back()
+						})
+					}
+
+				}
+			},
+			//回显数据
+			getForm() {
+				this.$http.get("/app/product/queryById?id=" + this.productId, {}).then(res => {
+					console.log(res)
+					if (res.data.code == 200) {
+						this.form = res.data.result
+						this.showText = res.data.result.productCategory
+						this.fmImageList.push({
+							url: res.data.result.productImage
+						})
+						let xqtemp = []
+						xqtemp = res.data.result.detailImages.split(',')
+						xqtemp.map(item => {
+							this.xqImage.push({
+								url: item
+							})
+						})
+						this.showDate = res.data.result.saleStartDate.slice(0, 10) + '-' + res.data.result.saleEndDate.slice(0,
+							10)
+						if (res.data.result.availableEndDate) {
+							this.showSellDate = res.data.result.availableStartDate.slice(0, 10) + '-' + res.data.result
+								.availableEndDate.slice(0, 10)
+						} else if (res.data.result.expirationDays) {
+							this.showSellDate = res.data.result.expirationDays + '天后失效'
 						}
-						uni.removeStorage({
-							key: 'smdate',
-						});
-						uni.removeStorage({
+						res.data.result.preBook == 0 ? this.makeChecked = false : this.makeChecked = true
+						this.showRule = res.data.result.refundLabelName + '·' + res.data.result.refundConfirmName
+						uni.setStorage({
 							key: 'group',
-						});
-						// this.back()
-					})
-				}
+							data: res.data.result.productGroups
+						})
+					}
+				})
 			}
 		},
 		mounted() {
 			this.getLeftTree()
+			this.getForm()
 		},
 		onLoad(options) {
-			if (options.data) {
-				let objStr = decodeURIComponent(options.data); // 获取并解码参数
-				let obj = JSON.parse(objStr); // 将字符串转换回对象
-				this.form.productGroups = obj
-				console.log(this.form.productGroups);
+			if (options.productId) {
+				this.productId = options.productId
 			}
 		},
 		onShow() {
@@ -519,21 +578,26 @@
 			uni.getStorage({
 				key: 'login_user_info',
 				success: function(res) {
-					that.form.merchantId = res.data.id
+					that.form.merchantId = res.data.merchantId
 				}
 			})
 			uni.getStorage({
 				key: 'smdate',
-				success: function(res) {
+				success: (res) => {
 					if (res.data.availableEndDate) {
-						that.showSellDate = res.data.availableStartDate + '-' + res.data.availableEndDate
-						that.form.availableStartDate = res.data.availableStartDate
-						that.form.availableEndDate = res.data.availableEndDate
-
+						this.showSellDate = res.data.availableStartDate + '-' + res.data.availableEndDate
+						this.form.availableStartDate = res.data.availableStartDate
+						this.form.availableEndDate = res.data.availableEndDate
+						this.form.expirationDays = null
 					} else if (res.data.expirationDays) {
-						that.form.expirationDays = res.data.expirationDays
-						that.showSellDate = res.data.expirationDays
+						this.form.expirationDays = res.data.expirationDays
+						this.form.availableEndDate = null
+						this.form.availableStartDate = null
+						this.$nextTick(() => {
+							this.showSellDate = res.data.expirationDays + '天后失效'
+						});
 					}
+
 				}
 			})
 			uni.getStorage({
@@ -555,6 +619,14 @@
 		flex-direction: column;
 		justify-content: space-between;
 
+		/*#ifdef APP-PLUS*/
+		.top {
+			width: 100%;
+			height: var(--status-bar-height);
+				background-color: #fff;
+		}
+
+		/*#endif*/
 		.head {
 			width: 100%;
 			height: 112rpx;

+ 40 - 9
pages/product/details.vue

@@ -1,5 +1,6 @@
 <template>
 	<view class="details">
+		<view class="top"></view>
 		<view class="head">
 			<view class="head_left">
 				<span class="cuIcon-back" @click="back"></span>
@@ -76,7 +77,7 @@
 					<view class="" v-if="form.availableStartDate">
 						{{form.availableStartDate.slice(0,10)}}-{{form.availableEndDate.slice(0,10)}}
 					</view>
-					<view class="">
+					<view class="" v-else>
 						购买后{{form.expirationDays}}天失效
 					</view>
 				</view>
@@ -84,10 +85,13 @@
 					<span class="span">需要预约</span>{{form.preBook==0?'不需要预约':'需要预约'}}
 				</view>
 				<view class="data">
-						退款规则
-					<view class="rule_item">
-						<view class="item" v-for="(item,index) in ruleList" :key="index">
-							{{item.name}}
+					<span class="span">退款规则 </span>
+					<view class="ruler">
+						<view class="item">
+							{{form.refundLabelName}}
+						</view>
+						<view class="item">
+							{{form.refundConfirmName}}
 						</view>
 					</view>
 				</view>
@@ -112,8 +116,8 @@
 				oldPrice: 300,
 				price: 280,
 				ruleList: [],
-				productId:'',
-				imageHttp:'http://192.168.0.36:8080/jeecg-boot/'
+				productId: '',
+				imageHttp: 'http://192.168.0.36:8080/jeecg-boot/'
 			}
 		},
 		methods: {
@@ -146,6 +150,14 @@
 		flex-direction: column;
 		overflow: hidden;
 		background: #F7F8FC;
+		/*#ifdef APP-PLUS*/
+		.top {
+			width: 100%;
+			height: var(--status-bar-height);
+				background-color: #fff;
+		}
+		
+		/*#endif*/
 
 		.head {
 			width: 100%;
@@ -197,6 +209,7 @@
 			padding-bottom: 16rpx;
 			margin-top: 16rpx;
 			font-size: 28rpx;
+
 			.span {
 				display: inline-block;
 				margin-right: 20rpx;
@@ -204,7 +217,24 @@
 				font-size: 28rpx;
 				color: #666666;
 			}
-
+			.ruler{
+				display: flex;
+				align-items: center;
+				.item{
+					width: auto;
+					height: 44rpx;
+					padding: 0rpx 16rpx;
+					display: flex;
+					align-items: center;
+					justify-content: center;
+					line-height: 28rpx;
+					color: #2983EC;
+					background: rgba(68,154,255,0.1);
+					border-radius: 4rpx;
+					border: 1px solid #449AFF;
+					margin-right: 20rpx;
+				}
+			}
 		}
 
 		.img_box {
@@ -223,8 +253,9 @@
 				flex-wrap: wrap;
 				// justify-content: space-between;
 				gap: 27rpx;
+
 				.img {
-					 flex: 0 0 calc(25% - 27rpx);
+					flex: 0 0 calc(25% - 27rpx);
 					height: 140rpx;
 					border-radius: 12rpx;
 					overflow: hidden;

+ 9 - 0
pages/product/group.vue

@@ -1,5 +1,6 @@
 <template>
 	<view class="group">
+		<view class="top"></view>
 		<view class="head">
 			<view class="head_left">
 				<span class="cuIcon-back" @click="back"></span>
@@ -208,6 +209,14 @@
 		flex-direction: column;
 		justify-content: space-between;
 		background: #F7F8FC;
+		/*#ifdef APP-PLUS*/
+		.top {
+			width: 100%;
+			height: var(--status-bar-height);
+				background-color: #fff;
+		}
+		
+		/*#endif*/
 
 		.head {
 			width: 100%;

+ 127 - 30
pages/product/index.vue

@@ -1,5 +1,6 @@
 <template>
 	<view class="product">
+		<view class="top"></view>
 		<view class="head">
 			<view class="handle">
 				<view class="head_left">
@@ -25,7 +26,7 @@
 			<view class="type">
 				<view class="type_item" :class="currentIndex == index?'active':''" v-for="(item,index) in typeList" :key="index"
 					@click="selectType(item,index)">
-					{{item.type}}
+					{{item.statusName}}({{item.number}})
 					<span class="line" v-if="currentIndex == index"></span>
 				</view>
 			</view>
@@ -45,7 +46,7 @@
 				</view>
 				<view class="details">
 					<view class="img">
-						<image :src="item.url" mode=""></image>
+						<image :src="item.productImage" mode=""></image>
 					</view>
 					<view class="data">
 						<span>{{item.productName}}</span>
@@ -64,10 +65,13 @@
 					</view>
 				</view>
 				<view class="btn_box">
-					<view class="delist" @click.stop="delist(item)">
+					<view class="delist" @click.stop="sell(item)" v-if="item.productStatus==0">
+						上架
+					</view>
+					<view class="delist" @click.stop="delist(item)" v-if="item.productStatus==1">
 						下架
 					</view>
-					<view class="edit delist">
+					<view class="edit delist" @click.stop="edit(item)">
 						编辑
 					</view>
 				</view>
@@ -80,7 +84,7 @@
 					<image src="/static/product/round.png" mode="" v-else></image>
 					全选
 				</view>
-				<view class="delete">删除</view>
+				<view class="delete" @click="remove">删除</view>
 				<view class="delete delist" @click="deletes">下架</view>
 			</view>
 			<view class="btn" @click="creatProduct" v-else>
@@ -125,15 +129,14 @@
 				isDelete: false,
 				canBeDelete: true,
 				productName: '',
-				typeList: [
-					{
+				typeList: [{
 						type: '待上架',
 						productStatus: 0
 					},
 					{
-							type: '售卖中',
-							productStatus: 1
-						},
+						type: '售卖中',
+						productStatus: 1
+					},
 					{
 						type: '审核中',
 						productStatus: 2
@@ -151,9 +154,9 @@
 		methods: {
 			//返回
 			back() {
-				uni.navigateBack({
-					delta: 1
-				});
+				uni.switchTab({
+					url: '/pages/index/index'
+				})
 			},
 			handleClear() {
 				this.getProductList()
@@ -163,7 +166,7 @@
 			selectType(item, index) {
 				if (!this.isManage) {
 					this.currentIndex = index
-					this.productStatus = item.productStatus
+					this.productStatus = item.code
 					this.getProductList()
 				}
 
@@ -171,6 +174,8 @@
 			//是否管理商品
 			manage() {
 				this.isManage = !this.isManage
+				this.isAll = true
+				this.allSelect()
 			},
 			// 跳转至搜索页面
 			goSearch() {
@@ -190,42 +195,87 @@
 					url: '/pages/product/creatProduct'
 				})
 			},
+			//编辑
+			edit(item) {
+				uni.navigateTo({
+					url: '/pages/product/creatProduct?productId=' + item.productId
+				})
+			},
 			select(item) {
 				item.select = !item.select
 				this.productHandleList.push(item.productId)
 			},
 			//批量下架
-			deletes(){
-					let shelves = []
-				this.productHandleList.forEach(item=> {
+			deletes() {
+				let shelves = []
+				this.productHandleList.forEach(item => {
 					shelves.push({
-						productId:item.productId,
-						productStatus:0
+						productId: item.productId,
+						productStatus: 0
 					})
 				})
 				let that = this
 				uni.showModal({
-					title: '确定下架商品',
-					success: function (res) {
+					title: '确定下架商品?',
+					success: function(res) {
 						if (res.confirm) {
-							that.$http.post("/app/product/shelves",shelves )
+							that.$http.post("/app/product/shelves", shelves)
 								.then(res => {
 									if (res.data.code == 200) {
 										console.log(res)
+
 										uni.showToast({
 											title: '下架成功',
 											duration: 2000,
-											icon:'none'
+											icon: 'none'
 										});
+										that.getProductList()
 										that.isManage = false
+										that.isAll = false
 									}
 								})
 						} else if (res.cancel) {
 							console.log('用户点击取消');
+							that.isAll = false
+							that.allSelect()
 						}
 					}
 				});
 			},
+			//批量删除
+			remove() {
+				this.productList.forEach((item => {
+					this.productHandleList.push(item.productId)
+				}))
+				let ids = this.productHandleList.join(',')
+				console.log(ids)
+				let that = this
+				uni.showModal({
+					title: '确定删除商品?',
+					success: function(res) {
+						if (res.confirm) {
+							that.$http.get("/app/product/deleteBatch?ids=" + ids, )
+								.then(res => {
+									if (res.data.code == 200) {
+										console.log(res)
+
+										uni.showToast({
+											title: '删除成功',
+											duration: 2000,
+											icon: 'none'
+										});
+										that.getProductList()
+										that.isManage = false
+										that.isAll = false
+									}
+								})
+						} else if (res.cancel) {
+							console.log('用户点击取消');
+						}
+					}
+				});
+			},
+			//全选
 			allSelect() {
 				if (this.isAll) {
 					this.isAll = false
@@ -256,23 +306,23 @@
 				console.log(item)
 				let shelves = []
 				shelves.push({
-					productId:item.productId,
-					productStatus:0
+					productId: item.productId,
+					productStatus: 0
 				})
 				let that = this
 				uni.showModal({
 					title: '确定下架商品',
-					success: function (res) {
+					success: function(res) {
 						if (res.confirm) {
-							that.$http.post("/app/product/shelves",shelves )
+							that.$http.post("/app/product/shelves", shelves)
 								.then(res => {
 									if (res.data.code == 200) {
-										console.log(res)
 										uni.showToast({
 											title: '下架成功',
 											duration: 2000,
-											icon:'none'
+											icon: 'none'
 										});
+										that.getProductList()
 									}
 								})
 						} else if (res.cancel) {
@@ -280,11 +330,49 @@
 						}
 					}
 				});
-				
+
+			},
+			//上架
+			sell(item) {
+				console.log(item)
+				let shelves = []
+				shelves.push({
+					productId: item.productId,
+					productStatus: 1
+				})
+				let that = this
+				uni.showModal({
+					title: '确定上架商品?',
+					success: function(res) {
+						if (res.confirm) {
+							that.$http.post("/app/product/shelves", shelves)
+								.then(res => {
+									if (res.data.code == 200) {
+										uni.showToast({
+											title: '上架成功',
+											duration: 2000,
+											icon: 'none'
+										});
+										that.getProductList()
+									}
+								})
+						} else if (res.cancel) {
+							console.log('用户点击取消');
+						}
+					}
+				});
+			},
+			getStatus() {
+				this.$http.get("/app/product/statusCount?merchantId=" + this.merchantId, ).then(res => {
+					if (res.data.code == 200) {
+						this.typeList = res.data.result
+					}
+				})
 			}
 		},
 		mounted() {
 			this.getProductList()
+			this.getStatus()
 		},
 		onShow() {
 			let that = this
@@ -307,6 +395,13 @@
 		justify-content: space-between;
 		background: #F7F8FC;
 
+		/*#ifdef APP-PLUS*/
+		.top {
+			width: 100%;
+			height: var(--status-bar-height);
+			background: linear-gradient(87deg, #FFFFFF 0%, #A3CDFF 100%), linear-gradient(360deg, #FFFFFF 0%, rgba(255, 255, 255, 0) 100%);
+		}
+
 		.head {
 			width: 100%;
 			background: #ffffff;
@@ -516,6 +611,8 @@
 
 					.img {
 						margin-right: 16rpx;
+						border-radius: 12rpx;
+						overflow: hidden;
 
 						image {
 							width: 132rpx;

+ 9 - 0
pages/product/rule.vue

@@ -1,5 +1,6 @@
 <template>
 	<view class="rule">
+		<view class="top"></view>
 		<view class="head">
 			<span class="cuIcon-back" @click="back"></span>
 			<view class="case">
@@ -131,6 +132,14 @@
 		flex-direction: column;
 		justify-content: space-between;
 		padding-bottom: 20rpx;
+		/*#ifdef APP-PLUS*/
+		.top {
+			width: 100%;
+			height: var(--status-bar-height);
+				background-color: #fff;
+		}
+		
+		/*#endif*/
 		.head {
 			width: 100%;
 			padding: 20rpx;

+ 9 - 0
pages/product/usable.vue

@@ -1,5 +1,6 @@
 <template>
 	<view class="usable">
+		<view class="top"></view>
 		<view class="head">
 			<view class="head_left">
 				<span class="cuIcon-back" @click="back"></span>
@@ -126,6 +127,14 @@
 		flex-direction: column;
 		justify-content: space-between;
 		background: #F7F8FC;
+		/*#ifdef APP-PLUS*/
+		.top {
+			width: 100%;
+			height: var(--status-bar-height);
+				background-color: #fff;
+		}
+		
+		/*#endif*/
 
 		.head {
 			width: 100%;

+ 214 - 0
pages/scan/check.vue

@@ -0,0 +1,214 @@
+<template>
+	<view class="check">
+		<view class="top"></view>
+		<view class="head">
+			<span class="cuIcon-back" @click="back"></span>
+			<view class="case">
+				核销详情
+			</view>
+		</view>
+		<view class="content">
+			<view class="status">
+				已核销<br />
+				<span>已完成</span>
+			</view>
+			<view class="product">
+				<view class="title">
+					商品信息
+				</view>
+				<view class="center">
+					<view class="img">
+						<image src="/static/avatar_girl.png" mode=""></image>
+					</view>
+					<view class="name">
+						一次普通洗车兑换服务代金券(限7座以下的轿车使用)
+						<view class="price">
+							¥228.3/份
+							<span>实付款:¥ 228.3</span>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view class="product">
+				<view class="title">
+					订单信息
+				</view>
+				<view class="data">
+					<span>订单号</span>
+					1544566553265864656515
+				</view>
+				<view class="data">
+					<span>券码</span>
+					1544566553265864656515
+				</view>
+				<view class="data">
+					<span>支付流水号</span>
+					1544566553265864656515
+				</view>
+				<view class="data">
+					<span>提交人</span>
+					1544566553265864656515
+				</view>
+				<view class="data">
+					<span>联系电话</span>
+					1544566553265864656515
+				</view>
+				<view class="data">
+					<span>下单时间</span>
+					1544566553265864656515
+				</view>
+				<view class="data">
+					<span>付款时间</span>
+					1544566553265864656515
+				</view>
+				<view class="data">
+					<span>使用时间</span>
+					1544566553265864656515
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default{
+		data(){
+			return{
+				
+			}
+		},
+		methods:{
+			//返回
+			back() {
+				uni.navigateBack({
+					delta: 1
+				});
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.check {
+		width: 100vw;
+		height: 100vh;
+		background: #F7F8FC;
+		/*#ifdef APP-PLUS*/
+		.top {
+			width: 100%;
+			background: linear-gradient( 87deg, #E2F4F8 0%, #A3CDFF 100%), linear-gradient( 360deg, #FFFFFF 0%, rgba(255,255,255,0) 100%);
+			background: #fff;
+		}
+		
+		/*#endif*/
+
+		.head {
+			width: 100%;
+			height: 112rpx;
+			padding: 20rpx;
+			display: flex;
+			align-items: center;
+			background: linear-gradient( 87deg, #E2F4F8 0%, #A3CDFF 100%), linear-gradient( 360deg, #FFFFFF 0%, rgba(255,255,255,0) 100%);
+			position: relative;
+
+			.cuIcon-back {
+				font-size: 40rpx;
+				margin-right: 40rpx;
+			}
+
+			.case {
+				position: absolute;
+				left: 50%;
+				transform: translateX(-50%);
+				font-size: 36rpx;
+				font-weight: 600;
+			}
+		}
+
+		.content {
+			padding: 24rpx;
+
+			.status {
+				font-size: 44rpx;
+				padding: 0px 20rpx;
+				color: #333333;
+
+				span {
+					font-size: 28rpx;
+					color: #666666;
+					line-height: 33rpx;
+				}
+			}
+
+			.product {
+				width: 100%;
+				margin-top: 24rpx;
+				background: #FFFFFF;
+				box-shadow: 0px 2px 8px 0px rgba(214, 225, 237, 0.1);
+				border-radius: 16px;
+				padding: 20rpx;
+
+				.title {
+					font-size: 32rpx;
+					color: #333333;
+				}
+
+				.center {
+					display: flex;
+					border-radius: 16rpx;
+					margin-top: 16rpx;
+
+					.img {
+						width: 132rpx;
+						height: 132rpx;
+						border-radius: 12rpx;
+						overflow: hidden;
+						margin-right: 16rpx;
+
+						image {
+							width: 132rpx;
+							height: 132rpx;
+						}
+					}
+
+					.name {
+						flex: 1;
+						display: flex;
+						flex-direction: column;
+						justify-content: space-between;
+						font-size: 30rpx;
+						color: #000000;
+						line-height: 35rpx;
+						font-weight: 600;
+
+						.price {
+							font-size: 24rpx;
+							display: flex;
+							align-items: center;
+							justify-content: space-between;
+							color: #FF6A42;
+
+							span {
+								color: #666666;
+							}
+						}
+					}
+				}
+				.data{
+					width: 100%;
+					height: 38rpx;
+					display: flex;
+					align-items: center;
+					justify-content: space-between;
+					margin-top:20rpx ;
+					font-size: 28rpx;
+					color: #333333;
+					span{
+						font-size: 26rpx;
+						color: #8A91A8;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 223 - 17
pages/scan/details.vue

@@ -1,5 +1,6 @@
 <template>
 	<view class="details">
+		<view class="top"></view>
 		<view class="head">
 			<span class="cuIcon-back" @click="back"></span>
 			<view class="case">
@@ -9,58 +10,263 @@
 		<view class="content">
 			<view class="product">
 				<view class="img">
-					<image src="/static/avatar_girl.png" mode=""></image>
+					<image :src="product.productImage" mode=""></image>
 				</view>
 				<view class="name">
-					商品名称商品名称
+					{{product.productName}}
+					<view class="price">
+						<span>¥</span>{{product.orderPrice}} <span>/份</span>
+					</view>
+				</view>
+			</view>
+
+		</view>
+		<view class="foot">
+			<view class="btn" @click="verify">
+				确认消费
+			</view>
+		</view>
+		<view class="popup " v-if="isSuccess">
+			<view class="content">
+				<view class="title">
+					<span>消费成功</span>
+				</view>
+				<view class="center">
+					<image src="/static/index/close.png" mode=""></image>
+					<span>消费成功!</span>
+				</view>
+				<view class="btn"  @click="goIndex">
+					确定
 				</view>
 			</view>
-			<u-button>确认消费</u-button>
 		</view>
 	</view>
 </template>
 
 <script>
-	export default{
+	export default {
 		data() {
-			return{
-				
+			return {
+				isSuccess: false,
+				product:null,
+				merchantId:''
+			}
+		},
+		methods:{
+			//返回
+			back() {
+				uni.navigateBack({
+					delta: 1
+				});
+			},
+			verify(){
+				// this.isSuccess = true
+				let data = {
+					merchantId:this.merchantId,
+					voucherCode:this.product.voucherCode
+				}
+				this.$http.post("app/writeOff/add",data).then(res => {
+					if (res.data.code == 200) {
+						console.log(res)
+						this.isSuccess = true
+					}else{
+						uni.showToast({
+							title: res.data.message,
+							icon:'none'
+						});
+					}
+				})
+			},
+			goIndex(){
+				this.isSuccess = false
+				uni.switchTab({
+					url:'/pages/index/index'
+				})
 			}
+		},
+		onLoad(options) {
+			this.product =JSON.parse(decodeURIComponent(options.params)) 
+			console.log(this.product )
+		},
+		onShow() {
+			let that = this
+			uni.getStorage({
+				key: 'login_user_info',
+				success: function(res) {
+					that.merchantId = res.data.merchantId
+				}
+			})
 		}
 	}
 </script>
 
 <style lang="scss" scoped>
-	.details{
+	.details {
 		width: 100vw;
 		height: 100vh;
+		background: #F7F8FC;
+		/*#ifdef APP-PLUS*/
+		.top {
+			width: 100%;
+			background: linear-gradient(90deg, #E6FFFF 0%, #CEF7FF 40%, #A3CEFE 73%, #BCD6FF 100%), linear-gradient(0, #F7F8FC 0%, rgba(255, 255, 255, 0) 100%);
+			background: #fff;
+		}
+		
+		/*#endif*/
+
 		.head {
 			width: 100%;
+			height: 112rpx;
 			padding: 20rpx;
 			display: flex;
 			align-items: center;
-			background-color: #fff;
+			background: #fff;
+			position: relative;
+
 			.cuIcon-back {
 				font-size: 40rpx;
 				margin-right: 40rpx;
 			}
+
+			.case {
+				position: absolute;
+				left: 50%;
+				transform: translateX(-50%);
+				font-size: 36rpx;
+				font-weight: 600;
+			}
 		}
-		.content{
+
+		.content {
 			width: 100%;
-			margin-top: 20rpx;
-			padding: 20rpx;
-			background-color: #fff;
-			.product{
+			margin-top: 24rpx;
+			padding: 24rpx;
+
+
+			.product {
+				background-color: #fff;
 				display: flex;
 				margin-bottom: 20rpx;
-				.img{
-					margin-right: 40rpx;
-					image{
+				padding: 20rpx 16rpx;
+				border-radius: 16rpx;
+
+				.img {
+					width: 132rpx;
+					height: 132rpx;
+					border-radius: 12rpx;
+					overflow: hidden;
+					margin-right: 16rpx;
+
+					image {
+						width: 132rpx;
+						height: 132rpx;
+					}
+				}
+
+				.name {
+					flex: 1;
+					display: flex;
+					flex-direction: column;
+					justify-content: space-between;
+					font-size: 30rpx;
+					color: #000000;
+					line-height: 35rpx;
+					font-weight: 600;
+
+					.price {
+						font-size: 28rpx;
+						color: #FF6A42;
+						span{
+							font-size: 24rpx
+						}
+					}
+				}
+			}
+		}
+
+		.foot {
+			width: 100%;
+			height: 112rpx;
+			position: fixed;
+			bottom: 0;
+			border-top: 1px solid #eeeeee;
+			padding: 16rpx 24rpx 20rpx;
+
+			.btn {
+				width: 100%;
+				height: 76rpx;
+				background: linear-gradient(90deg, #77B6FF 0%, #449AFF 100%);
+				border-radius: 53rpx;
+				font-size: 28rpx;
+				color: #FFFFFF;
+				display: flex;
+				align-items: center;
+				justify-content: center;
+			}
+		}
+		.popup {
+			width: 100vw;
+			height: 100vh;
+			position: fixed;
+			top: 0;
+			left: 0;
+			background: rgba(0, 0, 0, 0.4);
+			display: flex;
+			align-items: center;
+			justify-content: center;
+		
+			.content {
+				width: 570rpx;
+				background: #FFFFFF;
+				border-radius: 16rpx;
+				padding: 0px 24rpx;
+				padding-bottom: 16rpx;
+
+				.title {
+					width: 100%;
+					height: 86rpx;
+					font-size: 32rpx;
+					color: #333333;
+					font-weight: 600;
+					border-bottom: 1px solid #EEEEEE;
+					display: flex;
+					align-items: center;
+					justify-content: center;
+				}
+
+				.center {
+					font-size: 30rpx;
+					color: #333333;
+					line-height: 35rpx;
+					display: flex;
+					flex-direction: column;
+					align-items: center;
+					justify-content: center;
+					padding-top: 28rpx;
+					border-bottom: 1px solid #EEEEEE;
+					padding-bottom: 40rpx;
+					margin-bottom: 16rpx;
+
+					image {
 						width: 100rpx;
 						height: 100rpx;
+						margin-bottom: 10rpx;
 					}
 				}
-				.name{
+
+				.btn {
+					width: 320rpx;
+					height: 68rpx;
+					border-radius: 66rpx;
+					font-size: 28rpx;
+					color: #FFFFFF;
+					display: flex;
+					align-items: center;
+					justify-content: center;
+					margin: 0rpx auto;
+					background: #449AFF;
+				}
+				.close{
 					
 				}
 			}

+ 161 - 20
pages/scan/manual.vue

@@ -1,5 +1,6 @@
 <template>
 	<view class="manual">
+		<view class="top"></view>
 		<view class="head">
 			<span class="cuIcon-back" @click="back"></span>
 			<view class="case">
@@ -7,61 +8,201 @@
 			</view>
 		</view>
 		<view class="content">
-			<view class="title">
-				请输入用户出示的**位纯数字的核销码
+			<view class="bgc">
+
+			</view>
+			<view class="import">
+				请输入核销码
+				<view class="inp">
+					<!-- {{value}} -->
+					<u-input v-model="voucherCode" placeholder="请输入核销码" />
+				</view>
 			</view>
-			<view class="inp">
-				<u-input v-model="value" placeholder="请输入"/>
+			<view class="keyboard" style="margin-top: 30px;">
+				<view class="qrcode" @click="scan">
+					<image src="/static/scan/qrcode.png" mode=""></image>去扫码
+				</view>
+				<view class="qrcode ticket" @click="verify">
+					<image src="/static/scan/ticket.png" mode=""></image>验券核销
+				</view>
 			</view>
-			<u-button>确认</u-button>
 		</view>
 	</view>
 </template>
 
 <script>
-	export default{
+	export default {
 		data() {
-			return{
-				value:''
+			return {
+				voucherCode: '',
+				merchantId:''
 			}
 		},
-		methods:{
+		methods: {
 			//返回
-			back(){
+			back() {
 				uni.navigateBack({
-					 delta: 1
+					delta: 1
 				});
 			},
+			verify(){
+				if(this.voucherCode == ''){
+					uni.showToast({
+						title: '请输入核销码',
+						icon:'none'
+					});
+				}
+				let data = {
+					merchantId:this.merchantId,
+					voucherCode:this.voucherCode
+				}
+				this.$http.post("/app/writeOff/details",data).then(res => {
+					if (res.data.code == 200) {
+						console.log()
+						const params = encodeURIComponent(JSON.stringify(res.data.result));
+						uni.navigateTo({
+							  url:`/pages/scan/details?params=${params}`
+						})
+					}else{
+						uni.showToast({
+							title: res.data.message,
+							icon:'none'
+						});
+					}
+				})
+			},
+			scan(){
+				uni.scanCode({
+					success: function (res) {
+						console.log('条码内容:' + res.result);
+					}
+				});
+			}
+		},
+		onShow() {
+			let that = this
+			uni.getStorage({
+				key: 'login_user_info',
+				success: function(res) {
+					that.merchantId = res.data.merchantId
+				}
+			})
 		}
 	}
 </script>
 
 <style lang="scss" scoped>
-	.manual{
+	.manual {
 		width: 100vw;
 		height: 100vh;
+		background: #F7F8FC;
+
+		/*#ifdef APP-PLUS*/
+		.top {
+			width: 100%;
+			background: linear-gradient(90deg, #E6FFFF 0%, #CEF7FF 40%, #A3CEFE 73%, #BCD6FF 100%), linear-gradient(0, #F7F8FC 0%, rgba(255, 255, 255, 0) 100%);
+			background: #fff;
+		}
+
+		/*#endif*/
+
 		.head {
 			width: 100%;
+			height: 112rpx;
 			padding: 20rpx;
 			display: flex;
 			align-items: center;
-			background-color: #fff;
+			background: linear-gradient(90deg, #E6FFFF 0%, #CEF7FF 40%, #A3CEFE 73%, #BCD6FF 100%), linear-gradient(0, #F7F8FC 0%, rgba(255, 255, 255, 0) 100%);
+			position: relative;
+
 			.cuIcon-back {
 				font-size: 40rpx;
 				margin-right: 40rpx;
 			}
+
+			.case {
+				position: absolute;
+				left: 50%;
+				transform: translateX(-50%);
+				font-size: 36rpx;
+				font-weight: 600;
+			}
 		}
-		.content{
+
+
+		.content {
 			width: 100%;
-			padding: 20rpx;
-			background-color: #fff;
-			border-top: 1px solid #ccc;
-			.inp{
+			position: relative;
+
+			.bgc {
+				width: 100%;
+				height: 160rpx;
+				background: linear-gradient(90deg, #E6FFFF 0%, #CEF7FF 40%, #A3CEFE 73%, #BCD6FF 100%);
+			}
+
+			.import {
 				width: 100%;
-				background-color: #efefef;
 				margin: 20rpx 0px;
-				padding:0px 20rpx;
+				padding: 0px 20rpx;
 				box-sizing: border-box;
+				position: absolute;
+				top: 0rpx;
+				display: flex;
+				flex-direction: column;
+				justify-content: center;
+				align-items: center;
+				font-size: 40rpx;
+				color: #333333;
+
+				.inp {
+					width: 656rpx;
+					height: 132rpx;
+					background: #FFFFFF;
+					border-radius: 12rpx;
+					border: 2rpx solid #449AFF;
+					margin-top: 28rpx;
+					padding: 0px 20rpx;
+					display: flex;
+					align-items: center;
+
+					u-input {
+						width: 100%;
+						height: 100%;
+					}
+				}
+			}
+
+			.keyboard {
+				width: 100%;
+				height: 112rpx;
+				position: fixed;
+				bottom: 0;
+				display: flex;
+				align-items: center;
+				justify-content: space-around;
+				background-color: #fff;
+
+				.qrcode {
+					width: 324rpx;
+					height: 76rpx;
+					background: linear-gradient(90deg, #77B6FF 0%, #449AFF 100%);
+					border-radius: 12rpx;
+					display: flex;
+					align-items: center;
+					justify-content: center;
+					font-size: 28rpx;
+					color: #FFFFFF;
+
+					image {
+						width: 40rpx;
+						height: 40rpx;
+						margin-right: 10rpx;
+					}
+				}
+
+				.ticket {
+					background: linear-gradient(90deg, #FFA05B 0%, #FF7918 100%);
+				}
 			}
 		}
 	}

+ 229 - 90
pages/scan/record.vue

@@ -1,43 +1,58 @@
 <template>
 	<view class="record">
+		<view class="top"></view>
 		<view class="head">
 			<span class="cuIcon-back" @click="back"></span>
 			<view class="case">
-				<u-search placeholder="日照香炉生紫烟" :show-action="false" v-model="keyword"></u-search>
+				核销记录
 			</view>
 		</view>
 		<view class="select">
-			<u-dropdown>
+			<view class="moun">
+				<image src="/static/scan/screen.png" mode=""></image>
+				一月
+			</view>
+			<view class="search">
+				<image src="/static/index/search.png" mode=""></image>
+				<u-input type="text" v-model="productName" placeholder="请输入关键词" />
+				<span @click="searchList">搜索</span>
+			</view>
+			<!-- <u-dropdown>
 				<u-dropdown-item v-model="month" title="一月" :options="options1"></u-dropdown-item>
-			</u-dropdown>
+			</u-dropdown> -->
 		</view>
-		<view class="list">
-				<view class="item" v-for="(item,index) in list" :key="index">
-					<view class="time">
-						<view class="left">
-							核销时间:{{item.hxsj}}
-						</view>
-						<view class="right">
-							购买时间:{{item.gmsj}}
-						</view>
+		<mescroll-uni class="list" :fixed="false" ref="mescrollRef" @up="getList" @init="mescrollInit" :use="false">
+			<view class="item" v-for="(item,index) in list" :key="index">
+				<view class="time">
+					{{item.writeOffTime}}核销
+				</view>
+				<view class="product">
+					<view class="img">
+						<image :src="item.productImage" mode=""></image>
 					</view>
-					<view class="product">
-						<view class="left">
-							<view class="img">
-								<image :src="item.img" mode=""></image>
-							</view>
-							<view class="name">
-								{{item.name}}<br/>
-								{{item.price}}
-							</view>
-						</view>
-						<view class="right">
-							<image :src="item.url" mode=""></image>
-							{{item.yhmc}}
+					<view class="name">
+						{{item.productName}}<br />
+						<span>¥{{item.price}}</span>
+					</view>
+					<!-- <view class="right">
+						<image :src="item.url" mode=""></image>
+						{{item.yhmc}}
+					</view> -->
+				</view>
+				<view class="user">
+					<view class="left">
+						{{item.buyTime}}购买
+					</view>
+					<view class="right">
+						<view class="img">
+
+							<image :src="item.userAvatar" mode=""></image>
 						</view>
+						{{item.userNick}}
 					</view>
 				</view>
-		</view>
+			</view>
+		</mescroll-uni>
 	</view>
 </template>
 
@@ -47,48 +62,62 @@
 			return {
 				keyword: '',
 				month: '',
-				options1: [{
-						label: '一月',
-						value: 1,
+				merchantId: '',
+				productName:'',
+				options1: [],
+				list: [],
+				upOption: {
+					page: {
+						num: 0,
+						size: 10 // 每页数据的数量,默认10
 					},
-					{
-						label: '二月',
-						value: 2,
-					},
-					{
-						label: '三月',
-						value: 3,
+					noMoreSize: 5, // 配置列表的总数量要大于等于5条才显示'-- END --'的提示
+					empty: {
+						tip: '暂无相关数据'
 					}
-				],
-				list:[
-					{
-						hxsj:'1-03 16:25',
-						gmsj:'2024.12.03 16:25',
-						img:'/static/avatar_girl.png',
-						name:'服务名称',
-						price:'230',
-						url:'/static/avatar_girl.png',
-						yhmc:'用户昵称'
-					},
-					{
-						hxsj:'1-03 16:25',
-						gmsj:'2024.12.03 16:25',
-						img:'/static/avatar_girl.png',
-						name:'服务名称',
-						price:'230',
-						url:'/static/avatar_girl.png',
-						yhmc:'用户昵称'
-					}
-				]
+				},
 			}
 		},
-		methods:{
+		methods: {
+			mescrollInit(mescroll) {
+				this.mescroll = mescroll;
+			},
 			//返回
-			back(){
+			back() {
 				uni.navigateBack({
-					 delta: 1
+					delta: 1
 				});
 			},
+			getList(page) {
+				this.$http.get("/app/writeOff/list?merchantId=" + this.merchantId+'&productName='+this.productName+'&pageNo='+page.num+'&pageSize='+page.size, ).then(res => {
+					if (res.data.code == 200) {
+						console.log(res)
+						this.list =this.list.concat(res.data.result.records)
+						this.mescroll.endSuccess(res.data.result.records.length, res.data.result.total);
+					}
+				})
+			},
+			searchList(){
+				this.list = []
+				let page = {
+					num: 1,
+					size: 10
+				}
+				this.getList(page)
+			}
+		},
+		mounted() {
+			this.mescroll.optDown.use = false
+			// this.getList()
+		},
+		onShow() {
+			let that = this
+			uni.getStorage({
+				key: 'login_user_info',
+				success: function(res) {
+					that.merchantId = res.data.merchantId
+				}
+			})
 		}
 	}
 </script>
@@ -96,62 +125,172 @@
 <style lang="scss" scoped>
 	.record {
 		width: 100%;
-		height: 100%;
+		height: 100vh;
+		display: flex;
+		flex-direction:column;
+		background: #F7F8FC;
+
+		/*#ifdef APP-PLUS*/
+		.top {
+			width: 100%;
+			height: var(--status-bar-height);
+			background: #fff;
+		}
+
+		/*#endif*/
 
 		.head {
 			width: 100%;
+			height: 112rpx;
 			padding: 20rpx;
 			display: flex;
 			align-items: center;
-			background-color: #fff;
+			background: #fff;
+			position: relative;
 
 			.cuIcon-back {
 				font-size: 40rpx;
 				margin-right: 40rpx;
 			}
+
+			.case {
+				position: absolute;
+				left: 50%;
+				transform: translateX(-50%);
+				font-size: 36rpx;
+				font-weight: 600;
+			}
 		}
-		.list{
-			padding: 20rpx;
-			.item{
+
+		.select {
+			width: 100%;
+			height: 88rpx;
+			padding: 0px 32rpx;
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			background: #ffffff;
+			border-top: 1px solid #eeeeee;
+			border-bottom: 1px solid #eeeeee;
+
+			.moun {
+				width: 149rpx;
+				height: 56rpx;
+				background: #F7F8FC;
+				border-radius: 12rpx;
+				display: flex;
+				align-items: center;
+				padding: 0px 12rpx;
+
+				image {
+					width: 36rpx;
+					height: 36rpx;
+					margin-right: 12rpx;
+				}
+			}
+
+			.search {
+				width: 483rpx;
+				height: 55rpx;
+				background: #F7F8FC;
+				border-radius: 12rpx;
+				padding: 0rpx 20rpx;
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+
+				image {
+					width: 36rpx;
+					height: 36rpx;
+				}
+
+				input {
+					// height: 36rpx;
+					font-size: 26rpx;
+				}
+
+				span {
+					font-size: 26rpx;
+					color: #333333;
+				}
+			}
+		}
+
+		.list {
+			flex: 1;
+			overflow-y: auto;
+			margin-top: 3rpx;
+
+			.item {
 				width: 100%;
 				background-color: #fff;
 				margin-bottom: 20rpx;
-				padding: 20rpx;
+				padding: 20rpx 32rpx;
 				border-radius: 20rpx;
-				.time{
+
+				.time {
+					font-size: 26rpx;
+					color: #6B6E79;
+				}
+
+				.product {
 					display: flex;
-					justify-content: space-between;
-					border-bottom: 1px solid #ccc;
-					.left{
-						
+					// justify-content: space-between;
+					margin-top: 20rpx;
+
+					.img {
+						width: 128rpx;
+						height: 128rpx;
+						border-radius: 12rpx;
+						overflow: hidden;
+						margin-right: 16rpx;
+
+						image {
+							width: 128rpx;
+							height: 128rpx;
+						}
 					}
-					.right{
-						
+
+					.name {
+						display: flex;
+						flex-direction: column;
+						justify-content: space-between;
+
+						span {
+							font-size: 22rpx;
+							color: #FD4912;
+						}
 					}
 				}
-				.product{
+
+				.user {
 					display: flex;
+					align-items: center;
 					justify-content: space-between;
 					margin-top: 20rpx;
-					.left{
-						display: flex;
-						image{
-							width: 100rpx;
-							height: 100rpx;
-						}
-						.name{
-							display: flex;
-							flex-direction: column;
-						}
+
+					.left {
+						font-size: 24rpx;
+						color: #8A91A8;
 					}
-					.right{
+
+					.right {
+						font-size: 24rpx;
+						color: #8A91A8;
 						display: flex;
-						flex-direction: column;
-						justify-content: center;
 						align-items: center;
-						image{
-							width: 60rpx;
-							height: 60rpx;
+
+						.img {
+							width: 40rpx;
+							height: 40rpx;
+							border-radius: 50%;
+							overflow: hidden;
+							margin-right: 8rpx;
+
+							image {
+								width: 40rpx;
+								height: 40rpx;
+							}
 						}
 					}
 				}

+ 256 - 253
pages/scan/scan.nvue

@@ -1,279 +1,282 @@
-<!-- 扫一扫 -->
 <template>
-	<view class="scan-view">
-		<view class="status_bar">
-			<!-- 这里是状态栏 -->
-		</view>
-		<!-- <u-navbar  :title="userInfo.channelname " :background="'#000'" :title-color="'#fff'" :border-bottom="false"></u-navbar> -->
-		<!-- 自定义导航栏 -->
-		<view class="nav-bar">
-			<view class="left" @click="onClickBack">
-				<text class="fwhfont icon-icon-chevron-left" style="font-size: 50rpx;"></text>
-			</view>
-			<view class="title">
-				<!-- <text class="title-text">{{userInfo.channelname}}</text> -->
-			</view>
-		</view>
-		<u-toast ref="uToast" />
-		<view class="scan-border">
-			<view
-				style="width: 500rpx;height: 600rpx;border: 1rpx solid #ccc;margin-bottom: 120rpx;position: relative;">
-				<view class="Bordertop">
-					<view style="width: 44rpx;height: 10rpx;background-color: #FBCC04;"></view>
-					<view style="width: 44rpx;height: 10rpx;background-color:  #FBCC04;"></view>
-				</view>
-				<view class="Borderright">
-					<view style="width: 10rpx;height: 44rpx;background-color:  #FBCC04;"></view>
-					<view style="width:10rpx;height: 44rpx;background-color:  #FBCC04;position: absolute;bottom: 0rpx;">
-					</view>
-				</view>
-				<view class="Borderbottom">
-					<view style="width: 44rpx;height: 10rpx;background-color:  #FBCC04;"></view>
-					<view style="width: 44rpx;height: 10rpx;background-color:  #FBCC04;"></view>
-				</view>
-				<view class="Borderleft">
-					<view style="width: 10rpx;height: 44rpx;background-color:  #FBCC04;"></view>
-					<view
-						style="width: 10rpx;height: 44rpx;background-color:  #FBCC04;position: absolute;bottom: 0rpx;">
-					</view>
-				</view>
-				<!-- bindscancode="onScancode" @initdone="onCameraInitDone" -->
-				<camera class="scan-camera" @scancode="onScancode" @error="onError" mode="scanCode"
-					device-position="back" flash="auto">
-					<cover-view class="scan-animation" :animation="animation"></cover-view>
-				</camera>
-			</view>
-			<view class="scanText">将二维码/条码放到取景框内,即可自动扫描</view>
-			<view class="footerButton">
-				<view class="btns"  @click="goManual">
-					<text class="fwhfont icon-jianpan" style="padding-right: 20rpx;"></text>
-					<text>输码</text>
-				</view>
-				<view class="btns">
-					<text class="fwhfont icon-icon-list" style="padding-right: 20rpx;"></text>
-					<text>记录</text>
-				</view>
-			</view> 
+	<view class="scancode">
+		<view class="scanKuang">
+			<view class="kuang" id='barcodekuang'></view>
 		</view>
 	</view>
 </template>
+
 <script>
-	var animation = uni.createAnimation({
-		timingFunction: "linear",
-		delay: 0
-	});
+	// import {
+	// 	equipmentScanApi,
+	// 	subtenancyApplicationApi
+	// } from '@/common/api/homeApi.js'
 	export default {
 		data() {
 			return {
-				animation: {},
-				
-			};
+				barcode: null,
+				flash: false,
+				isUser: true,
+
+			}
+		},
+		onLoad(opt) {
+			// #ifdef APP-PLUS
+			plus.navigator.setFullscreen(true); //全屏
+			let currentWebview = this.$scope.$getAppWebview();
+			this.createBarcode(currentWebview)
+			this.createFlashBarView(currentWebview)
+			this.createTipInfoView(currentWebview)
+			// #endif
 		},
-	
-		onShow() {
-			this.lineAnimation()
+		onBackPress() {
+			uni.navigateBack({
+				delta: 1
+			})
+
 		},
 		methods: {
- 
-			/**扫码线条动画*/
-			lineAnimation() {
-				this.animation = animation;
-				let that = this;
-				let scode = true;
-				this.timer = setInterval(
-					function() {
-						if (scode) {
-							animation.translateY(260).step({
-								duration: 1500,
-							});
-							scode = !scode;
-						} else {
-							animation.translateY(-10).step({
-								duration: 1500,
-							});
-							scode = !scode;
-						}
-						that.animation = animation.export();
-					}.bind(this),
-					1500
-				);
-			},
-			// 相机初始化完成回调函数
-			onCameraInitDone() {
-				// setInterval(() => {
-				// 	this.$refs.camera.scanCode();
-				// }, 1000);
+			toStart() {
+				this.barcode.start()
 			},
-			onError() {
-				this.$toast('扫码错误')
+			goback() {
+				uni.switchTab({
+					url: '/pages/home/index'
+				})
 			},
-			
-			onClickBack() {
-				uni.navigateBack();
+			/**
+			 * 创建二维码
+			 * @param {Object} currentWebview
+			 */
+			createBarcode(currentWebview) {
+				if (!this.barcode) {
+					this.barcode = plus.barcode.create('barcodekuang', [plus.barcode.QR], {
+						top: `auto`,
+						left: 'auto',
+						height: `100%`,
+						width: '100%',
+						position:'absolute',
+						background: '#1A86F4',
+						frameColor: '#1A86F4',
+						scanbarColor: '#1A86F4',
+					});
+					this.barcode.onmarked = this.onmarked;
+					this.barcode.setFlash(this.flash);
+					//此处未演示扫码成功回调的地址设置,实际请参考HTML5Plus API自行处理  
+					//注意扫码区域需为正方形,否则影响扫码识别率 
+					currentWebview.append(this.barcode);
+					this.barcode.start()
+				}
+
 			},
-			/**扫码成功*/
-			onScancode(e) {
+			/**
+			 * 创建提示信息
+			 * @param {Object} currentWebview
+			 */
+			createTipInfoView(currentWebview) {
+				const content = new plus.nativeObj.View('content', {
+						bottom: '170px',
+						height: '50px',
+						width: '100%',
+						textAlign:'center',
+					},
+					[{
+						tag: 'font',
+						id: 'scanTips',
+						text: '请将QR Code放入框内,即可自动扫描',
+						textStyles: {
+							size: '14px',
+							color: '#ffffff',
+							whiteSpace: 'normal'
+						},
+						position: {
+							top: '15px',
+							left: '10%',
+							width: '80%',
+							height: 'wrap_content'
+						}
+					}]);
+				currentWebview.append(content);
+				const contentBtn = new plus.nativeObj.View('contentBtn', {
+						bottom: '80px',
+						left: '15%',
+						height: '40px',
+						width: '70%',
+						textAlign:'center',
+						backgroundColor: '#1A86F4',
+						borderRadius: '20px',
+						lineHeight:'30px'
+					},
+					[{
+						tag: 'font',
+						id: 'scanTips',
+						text: '手动输入',
+						textStyles: {
+							size: '16px',
+							color: '#ffffff',
+							whiteSpace: 'normal'
+						},
+						position: {
+							top: '15px',
+							left: '10%',
+							width: '80%',
+							height: 'wrap_content'
+						}
+					}]);
+				currentWebview.append(contentBtn);
 				
- 
+				contentBtn.addEventListener('click', () => {
+					this.inputCode();
+				})
+			},
+
+
+			// 创建 开关灯按钮
+			createFlashBarView(currentWebview) {
+
+				const openImg = this.crtFlashImg()
+				const closeImg = this.crtFlashImg()
+				const scanBarVew = new plus.nativeObj.View('scanBarVew', {
+						top: '425px',
+						left: '40%',
+						height: '10%',
+						width: '20%',
+					},
+					closeImg);
+				scanBarVew.interceptTouchEvent(true);
+
+				currentWebview.append(scanBarVew);
+
+				scanBarVew.addEventListener("click", (e) => { //点亮手电筒
+					this.flash = !this.flash;
+					if (this.flash) {
+						scanBarVew.draw(openImg);
+					} else {
+						scanBarVew.draw(closeImg)
+					}
+					if (this.barcode) {
+						this.barcode.setFlash(this.flash);
+					}
+				}, false)
+			},
+			crtFlashImg() {
+				return [{
+					tag: 'font',
+					id: 'font',
+					text: '轻触照亮',
+					textStyles: {
+						size: '14px',
+						color: '#ffffff'
+					},
+					position: {
+						width: '100%',
+						left: '10%',
+						// top: `500px`,
+					}
+				}]
 			},
-			goManual(){
-				uni.navigateTo({
-					url:'/pages/scan/manual'
+
+
+			// 扫码成功回调
+			onmarked(type, result) {
+				let obj = this.barcode
+				console.log('条码类型:' + type);
+				console.log('条码内容:' + result);
+				// 业务代码
+				// 核对扫描结果
+				// 判断是否是正确的格式
+				// 不正确则跳转到 错误页面
+				if (type == 0) {
+					// equipmentScanApi({
+					// 	userid: getApp().globalData.userid,
+					// 	qrcodecontent: result,
+					// }).then((res) => {
+					// 	if (res.resultCode === 0) {
+					// 		if (this.isUser) {
+					// 			uni.redirectTo({
+					// 				url: '/pages/home/linkEpt?code=' + result
+					// 			})
+					// 		} else {
+					// 			uni.redirectTo({
+					// 				url: '/pages/equipment/devicedetails?equipmentId=' + res.data.equipmentid + '&outboundId=' +
+					// 					res.data.outboundid
+					// 			})
+					// 		}
+
+					// 	} else {
+					// 		// console.log('错误')
+					// 		uni.showToast({
+					// 			icon: 'none',
+					// 			title: res.resultMsg
+					// 		})
+					// 	}
+					// })
+				} else {
+					uni.showToast({
+						icon: 'none',
+						title: '请扫描正确的二维码'
+					})
+				}
+
+			},
+			inputCode() {
+				uni.redirectTo({
+					url: '/pages/home/inputCode'
 				})
 			}
- 
-		},
-		
-		onUnload() {
-			
-		},
-	};
-</script>
-<style lang="scss" scoped>
-	// page {
-	// 	background-color: black;
-	// }
- 
-	.status_bar {
-		height: var(--status-bar-height);
-		width: 100%;
-	}
- 
-	.scan-view {
-		width: 100%;
-		height: 100%;
-		display: flex;
-		flex-direction: column;
-		position: fixed;
-		align-items: center;
-		justify-content: space-around;
-		background-color: #000;
-	}
- 
-	.Bordertop {
-		display: flex;
-		justify-content: space-between;
-		position: absolute;
-		z-index: 99;
-		width: 100%;
-		top: -2rpx;
-	}
- 
-	.Borderleft {
-		left: -2rpx;
-		z-index: 99;
-		position: absolute;
-		height: 100%;
-	}
- 
-	.Borderright {
-		z-index: 99;
-		position: absolute;
-		right: -2rpx;
-		height: 100%;
-	}
- 
-	.Borderbottom {
-		display: flex;
-		z-index: 99;
-		justify-content: space-between;
-		position: absolute;
-		width: 100%;
-		bottom: -6rpx;
+
+		}
 	}
- 
-	.scan-border {
-		width: 100%;
-		height: 75%;
-		/* border: 6rpx solid #08FDFE; */
-		display: flex;
-		flex-direction: column;
-		align-items: center;
- 
-		.scan-camera {
+</script>
+
+<style lang="scss">
+	.scancode {
+		height: 100vh;
+		background: #1B1B1F;
+		// padding-top: 20px;
+		position: relative;
+
+		.scanKuang {
+			// margin-top: 240rpx;
+			// text-align: center;
 			width: 100%;
 			height: 100%;
-			border-radius: 6rpx;
+			.kuang {
+				width: 100%;
+				height: 100%;
+				// display: inline-block;
+				// background: url('../../static/img/saomiaokuang.png') no-repeat;
+				// background-size: 100% 100%;
+			}
 		}
-	}
- 
-	.scan-animation {
-		position: absolute;
-		top: 4%;
-		left: 2%;
-		width: 480rpx;
-		height: 2rpx;
-		background-color: #FBCC04;
-		border-radius: 50%;
-	}
- 
-	.footerButton {
-		width: 100%;
-		margin-top: 100rpx;
-		display: flex;
-		justify-content: space-around;
-		align-items: center;
- 
-		.btns {
-			color: #ebebeb;
-			display: flex;
-			justify-content: space-around;
-			align-items: center;
-			margin: 10rpx;
-			padding: 20rpx;
-			background-color: #333333;
-			border-radius: 20rpx;
+
+		.desc {
+			margin-top: 150rpx;
+			font-size: 30rpx;
+			font-family: PingFang SC;
+			font-weight: 400;
+			color: #FFFFFF;
+			text-align: center;
+		}
+
+		.btnsty {
+			text-align: center;
+			margin-top: 122rpx;
+
+			.btnin {
+				width: 520rpx;
+				height: 80rpx;
+				background: linear-gradient(90deg, #0173F4 0%, #65BFF5 100%);
+				border-radius: 20rpx;
+				line-height: 80rpx;
+				color: #FFFFFF;
+				display: inline-block;
+			}
 		}
 	}
- 
-	.scanText {
-		position: absolute;
-		bottom: 32%;
-		// left: 15%;
-		color: #ebebeb;
-	}
- 
-	/* 导航栏样式 */
-	.nav-bar {
-		display: flex;
-		justify-content: space-between;
-		align-items: center;
-		position: fixed;
-		top: 3%;
-		left: 0;
-		width: 100%;
-		height: 110rpx;
-		padding: 0 10px;
-		box-sizing: border-box;
-		z-index: 999;
-		margin-top: 22rpx;
-	}
- 
-	/* 左侧按钮样式 */
-	.left {
-		display: flex;
-		justify-content: center;
-		align-items: center;
-		color: #000;
-		background: #fff;
-		border-radius: 60rpx;
-	}
- 
-	/* 标题样式 */
-	.title {
-		flex: 1;
-		display: flex;
-		align-items: center;
-		margin-left: 26rpx;
-	}
- 
-	/* 标题文字样式 */
-	.title-text {
-		max-width: 200px;
-		overflow: hidden;
-		text-overflow: ellipsis;
-		white-space: nowrap;
-		color: #FFFFFF;
-		font-size: 36rpx;
+
+	.barcode {
+		width: 534rpx;
+		height: 534rpx;
 	}
 </style>

二进制
static/index/remove.png


二进制
static/scan/qrcode.png


二进制
static/scan/screen.png


二进制
static/scan/ticket.png


部分文件因为文件数量过多而无法显示