hmp 4 лет назад
Родитель
Сommit
25a7eb19b1
100 измененных файлов с 6100 добавлено и 3177 удалено
  1. 1 0
      .gitignore
  2. 23 5
      assets/http/api.js
  3. 8 1
      assets/http/global.js
  4. 0 411
      components/ay-qrcode/ay-qrcode.vue
  5. 0 872
      components/ay-qrcode/qrcode_wx.js
  6. 0 263
      components/ay-qrcode/weapp-qrcode.js
  7. 0 123
      components/custom_bar.vue
  8. 0 162
      components/echarts/qiun-loading/loading1.vue
  9. 0 170
      components/echarts/qiun-loading/loading2.vue
  10. 0 173
      components/echarts/qiun-loading/loading3.vue
  11. 0 222
      components/echarts/qiun-loading/loading4.vue
  12. 0 229
      components/echarts/qiun-loading/loading5.vue
  13. 0 36
      components/echarts/qiun-loading/qiun-loading.vue
  14. 8 45
      package-lock.json
  15. 0 3
      package.json
  16. 239 163
      pages.json
  17. 0 22
      pages/agent/myIncome/myIncome.vue
  18. 0 72
      pages/agent/myPromote.vue
  19. 0 21
      pages/agent/promote.vue
  20. 3 7
      pages/consume/comps/card1.vue
  21. 1 1
      pages/consume/comps/tabsSwiper.vue
  22. 132 0
      pages/consume/search/comps/search-list.vue
  23. 135 0
      pages/consume/search/list.vue
  24. 172 0
      pages/consume/search/search.vue
  25. 1 1
      pages/gain/comps/card1.vue
  26. 53 13
      pages/gain/comps/card2.vue
  27. 7 3
      pages/gain/gain.vue
  28. 31 26
      pages/mine/mine.vue
  29. 0 117
      pages/test/test.vue
  30. 1 2
      pagesA/comps/echarts/qiun-data-charts/qiun-data-charts.vue
  31. 0 0
      pagesA/comps/echarts/qiun-error/qiun-error.vue
  32. 0 0
      pagesA/comps/echarts/u-charts/config-echarts.js
  33. 0 0
      pagesA/comps/echarts/u-charts/config-ucharts.js
  34. 0 0
      pagesA/comps/echarts/u-charts/readme.md
  35. 0 0
      pagesA/comps/echarts/u-charts/u-charts.js
  36. 10 2
      pagesA/pages/bill/bill-detail.vue
  37. 6 5
      pagesA/pages/my-balance/my-balance.vue
  38. 3 3
      pagesA/pages/my-balance/statistics.vue
  39. 0 0
      pagesA/pages/my-points/accredit.vue
  40. 0 0
      pagesA/pages/my-points/exchange.vue
  41. 2 2
      pagesA/pages/my-points/my-points.vue
  42. 0 0
      pagesA/pages/myInfo.vue
  43. 0 0
      pagesA/pages/setting/pay-setting.vue
  44. 0 0
      pagesA/pages/setting/question.vue
  45. 2 2
      pagesA/pages/setting/setting.vue
  46. 447 0
      pagesB/comps/amountInput/amountInput.vue
  47. 0 0
      pagesB/comps/back.vue
  48. 55 0
      pagesB/comps/mescroll-body/components/mescroll-down.css
  49. 47 0
      pagesB/comps/mescroll-body/components/mescroll-down.vue
  50. 90 0
      pagesB/comps/mescroll-body/components/mescroll-empty.vue
  51. 83 0
      pagesB/comps/mescroll-body/components/mescroll-top.vue
  52. 47 0
      pagesB/comps/mescroll-body/components/mescroll-up.css
  53. 39 0
      pagesB/comps/mescroll-body/components/mescroll-up.vue
  54. 19 0
      pagesB/comps/mescroll-body/mescroll-body.css
  55. 348 0
      pagesB/comps/mescroll-body/mescroll-body.vue
  56. 65 0
      pagesB/comps/mescroll-body/mescroll-mixins.js
  57. 36 0
      pagesB/comps/mescroll-body/mescroll-uni-option.js
  58. 36 0
      pagesB/comps/mescroll-body/mescroll-uni.css
  59. 799 0
      pagesB/comps/mescroll-body/mescroll-uni.js
  60. 424 0
      pagesB/comps/mescroll-body/mescroll-uni.vue
  61. 48 0
      pagesB/comps/mescroll-body/mixins/mescroll-comp.js
  62. 59 0
      pagesB/comps/mescroll-body/mixins/mescroll-more-item.js
  63. 74 0
      pagesB/comps/mescroll-body/mixins/mescroll-more.js
  64. 109 0
      pagesB/comps/mescroll-body/wxs/mixins.js
  65. 92 0
      pagesB/comps/mescroll-body/wxs/renderjs.js
  66. 268 0
      pagesB/comps/mescroll-body/wxs/wxs.wxs
  67. 0 0
      pagesB/comps/u-city-select.vue
  68. 21 0
      pagesB/comps/u-draw-poster/LICENSE
  69. 230 0
      pagesB/comps/u-draw-poster/README.md
  70. 9 0
      pagesB/comps/u-draw-poster/core/debugginglog.d.ts
  71. 21 0
      pagesB/comps/u-draw-poster/core/debugginglog.js
  72. 1 0
      pagesB/comps/u-draw-poster/core/debugginglog.js.map
  73. 97 0
      pagesB/comps/u-draw-poster/core/index.d.ts
  74. 165 0
      pagesB/comps/u-draw-poster/core/index.js
  75. 0 0
      pagesB/comps/u-draw-poster/core/index.js.map
  76. 37 0
      pagesB/comps/u-draw-poster/core/plugin.d.ts
  77. 52 0
      pagesB/comps/u-draw-poster/core/plugin.js
  78. 0 0
      pagesB/comps/u-draw-poster/core/plugin.js.map
  79. 1 0
      pagesB/comps/u-draw-poster/index.d.ts
  80. 11 0
      pagesB/comps/u-draw-poster/index.js
  81. 1 0
      pagesB/comps/u-draw-poster/index.js.map
  82. 44 0
      pagesB/comps/u-draw-poster/package.json
  83. 17 0
      pagesB/comps/u-draw-poster/plugins/drawImage/index.d.ts
  84. 43 0
      pagesB/comps/u-draw-poster/plugins/drawImage/index.js
  85. 1 0
      pagesB/comps/u-draw-poster/plugins/drawImage/index.js.map
  86. 7 0
      pagesB/comps/u-draw-poster/plugins/drawImage/utils.d.ts
  87. 27 0
      pagesB/comps/u-draw-poster/plugins/drawImage/utils.js
  88. 1 0
      pagesB/comps/u-draw-poster/plugins/drawImage/utils.js.map
  89. 21 0
      pagesB/comps/u-draw-poster/plugins/drawImageFit/index.d.ts
  90. 35 0
      pagesB/comps/u-draw-poster/plugins/drawImageFit/index.js
  91. 1 0
      pagesB/comps/u-draw-poster/plugins/drawImageFit/index.js.map
  92. 38 0
      pagesB/comps/u-draw-poster/plugins/drawImageFit/object-sizing.d.ts
  93. 78 0
      pagesB/comps/u-draw-poster/plugins/drawImageFit/object-sizing.js
  94. 0 0
      pagesB/comps/u-draw-poster/plugins/drawImageFit/object-sizing.js.map
  95. 28 0
      pagesB/comps/u-draw-poster/plugins/drawQrCode/index.d.ts
  96. 12 0
      pagesB/comps/u-draw-poster/plugins/drawQrCode/index.js
  97. 1 0
      pagesB/comps/u-draw-poster/plugins/drawQrCode/index.js.map
  98. 2 0
      pagesB/comps/u-draw-poster/plugins/drawQrCode/uQRCode.d.ts
  99. 1075 0
      pagesB/comps/u-draw-poster/plugins/drawQrCode/uQRCode.js
  100. 0 0
      pagesB/comps/u-draw-poster/plugins/drawQrCode/uQRCode.js.map

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+node_modules

+ 23 - 5
assets/http/api.js

@@ -5,10 +5,14 @@ const wxApp={
 	getOpenId:p => http.get('/wx-app/getOpenId', {params:p}),
 	getOpenData:p => http.post('/wx-app/getOpenData',p),
 	getGenerateScheme:p => http.get('/wx-app/getGenerateScheme', {params:p}),
+	createWxaQrCode:p => http.get('/wx-app/createWxaQrCode', {params:p}),
 }
 
 //字典
-const dict=p => http.get('/blade-system/dict-biz/dictionary', {params:p})
+const dict={
+	list:p => http.get('/blade-system/dict-biz/dictionary', {params:p}),
+}
+
 
 //文件上传
 const uploadFile =(p)=> http.upload('blade-resource/oss/endpoint/put-file', {name:'file',filePath:p})
@@ -40,8 +44,9 @@ const loginUser={
 
 //商户管理
 const shop={
-	list:p => http.get('ldt_shop/shop/list', {params:p}),
-		detail:p => http.get('ldt_shop/shop/detail', {params:p}),
+	list:p => http.get('shop/list', {params:p}),
+	detail:p => http.get('ldt_shop/shop/detail', {params:p}),
+	indexInfo:p => http.get("shop/indexInfo", {params:p}),
 }
 
 //审核管理
@@ -61,6 +66,8 @@ const mall={
 const agenter={
 	list:p => http.get('ldt_agent/agentrecord/list',{params:p}),
 	submit:p => http.post('ldt_agent/agentrecord/submit', p),
+	statistics:p => http.get('app/agent/agenterInfo',{params:p}),
+	earningsDetail:p => http.get('app/agent/earningsDetail',{params:p}),
 }
 
 //代理类型
@@ -73,7 +80,7 @@ const agenterType={
 const userBank={
 	detail:p => http.get('ldt_user/userbank/detail',{params:p}),
 	list:p => http.get('ldt_user/userbank/list',{params:p}),
-	appList:p => http.get('ldt_user/userbank/appList',{params:p}),
+	appList:p => http.get('common/userbank/list',{params:p}),
 	submit:p => http.post('ldt_user/userbank/submit', p),
 	remove:p => http.post('ldt_user/userbank/remove?ids='+p),
 }
@@ -126,6 +133,15 @@ const joinRecord={
 	list:p => http.get("ldt_activity/joinrecord/list", {params:p}),
 }
 
+const statistics={
+	getBillStatistics:p => http.get('/common/bills/getBillStatistics',{params:p})
+}
+
+const withdraw={
+	submit:p => http.post('common/withdrawrec/withdraw',p),
+	payOrder:p => http.get('payment/payOrder/YEE_PAY/USER_WITHDRAW',{params:p}),
+	list:p => http.get("ldt_bills/withdrawrec/list", {params:p}),
+}
 
 
 
@@ -148,5 +164,7 @@ export const api={
 	setting,
 	account,
 	webSocket,
-	joinRecord
+	joinRecord,
+	statistics,
+	withdraw
 }

+ 8 - 1
assets/http/global.js

@@ -15,10 +15,17 @@ global.socket_prefix = 'USER:'
 
 global.socket_prefix_SHOP = 'SHOP:'
 
+global.platform={
+	SHOP:"SHOP",
+	MALL:"MALL"
+}
+
 global.userType = {
 	USER: "普通用户",
 	CHANNEL: "渠道代理",
-	CITY: "城市代理"
+	CITY: "城市代理",
+	SHOP:"商户",
+	MALL:"商场",
 }
 
 // global.agenterTypeId='1423464045809364994'

+ 0 - 411
components/ay-qrcode/ay-qrcode.vue

@@ -1,411 +0,0 @@
-<template>
-	<view :class="modal?'show-qrcode':'hide-qrcode'">
-		<view class="box-qrcode" :style="{'margin-left':  marginLeft + 'px'}" @longtap="longtapCode">
-			<!-- style="width: 550rpx;height: 550rpx;" -->
-			
-			<canvas class="canvas-qrcode" :style="style_w_h" :canvas-id="qrcode_id">
-				
-				<!-- #ifndef MP -->
-				<view v-if="modal&&is_themeImg" :style="style_w_h" class="box-img-qrcode">
-					<image :style="style_w_h_img" mode="scaleToFill" :src="themeImg"></image>
-				</view>
-				<!-- #endif -->
-				
-			</canvas>
-			
-			<!-- <image mode="scaleToFill" :src="imagePath"></image> -->
-			
-		</view>
-	</view>
-</template>
-
-<script>
-	var qr_we = require("./qrcode_wx.js");
-	const qrCode = require('./weapp-qrcode.js')
-	export default {
-		data() {
-			return {
-				isAndroid : false ,
-				show: true,
-				imagePath: '',
-				// qrcode_id: 'qrcode_id',
-				marginLeft: 0,
-				//一般的安卓app只需加30就能显示全
-				//苹果app的不加就能显示全,加了就要弄margin-left
-				//有些安卓app显示不全
-				add_num : 30 ,
-				add_num_key : 'rectify_code_key',
-			}
-		},
-		props: {
-			modal: {
-				type: Boolean,
-				default: false
-			},
-			url: {
-				type: String,
-				default: ''
-			},
-			height: {
-				type: Number,
-				default: 260
-			},
-			width: {
-				type: Number,
-				default: 260
-			},
-			themeColor: {
-				type: String,
-				default: '#333333',
-			},
-			qrcode_id: {
-				type: String,
-				default: 'qrcode_id',
-			},
-			is_themeImg: {
-				type: Boolean,
-				default: false,
-			},
-			themeImg: {
-				type: String,
-				default: 'https://cdn.pixabay.com/photo/2016/11/29/13/24/balloons-1869816__340.jpg',
-			},
-			h_w_img: {
-				type: Number,
-				default: 30
-			},
-			
-			
-		},
-		watch:{
-			
-		},
-		computed: {
-			style_w_h() {
-				return this.set_style_w_h();
-			},
-			style_w_h_img() {
-				let that = this;
-				var height = parseInt(that.h_w_img);
-				var width = parseInt(that.h_w_img);
-				var style = '';
-				if (height > 0) {
-					style = `height:${height*2}rpx;`;
-				}
-				if (width > 0) {
-					style += `width:${width*2}rpx;z-index: 2;`;
-				}
-			
-				return style;
-			},
-		},
-		created: function() {
-			let that = this;
-			try {
-				//app苹果二维码不居中
-				//#ifndef MP
-				let isAndroid = false ;
-			    const res = uni.getSystemInfoSync();
-			    if(res.platform == 'android'){
-					isAndroid = true ;
-				}else{
-					isAndroid = false ;
-				}
-				
-				
-				if (!isAndroid) {
-					that.marginLeft = 46;
-				}
-				
-				that.isAndroid = isAndroid ;
-				try {
-					const add_num = uni.getStorageSync(that.add_num_key);
-					if (add_num) {
-						that.add_num = add_num;
-					}
-					
-				} catch (e) {
-					// error
-				
-				}
-				// #endif
-
-			} catch (e) {
-			    // error
-			}
-
-			//#ifdef MP
-			//that.marginLeft = 40;
-			// #endif
-
-		},
-		methods: {
-			set_style_w_h(){
-				
-				let that = this;
-				var height = parseInt(that.height);
-				var width = parseInt(that.width);
-				var style = '';
-				var height = height*2 ;
-				var width = width*2 ;
-				
-				//#ifndef MP
-				var add = that.add_num ;
-				
-				height +=  add;
-				width +=  add;
-				// #endif
-				
-				if (height > 0) {
-					style = `height:${height}rpx;`;
-				}
-				if (width > 0) {
-					style += `width:${width}rpx;`;
-				}
-				
-				return style;
-			},
-			hideQrcode() {
-				this.$emit("hideQrcode")
-			},
-			// 二维码生成工具
-			crtQrCode() {
-				let that = this;
-				//#ifndef MP
-				new qrCode(that.qrcode_id, {
-					text: this.url,
-					width: that.width,
-					height: that.height,
-					colorDark: that.themeColor,//#333333
-					colorLight: "#FFFFFF",
-					correctLevel: qrCode.CorrectLevel.H,
-				})
-				// #endif
-				//#ifdef MP
-				that.createQrCode(this.url, that.qrcode_id, that.width, that.height,that.themeColor,that.is_themeImg,that.themeImg,that.h_w_img);
-				// #endif
-
-				//that.createQrCode(this.url, that.qrcode_id, that.width, that.height);
-			},
-			//#ifdef MP
-
-			createQrCode: function(url, canvasId, cavW, cavH,cavColor,haveImg,imgurl,imgsize) {
-				//调用插件中的draw方法,绘制二维码图片
-				qr_we.api.draw(url, canvasId, cavW, cavH,cavColor,haveImg,imgurl,imgsize, this, this.canvasToTempImage);
-				// setTimeout(() => { this.canvasToTempImage();},100);
-
-			},
-			
-			// #endif
-			//获取临时缓存照片路径,存入data中
-			canvasToTempImage: function() {
-				var that = this;
-			},
-			saveImage: function() {
-				var that = this;
-				uni.canvasToTempFilePath({
-					canvasId: that.qrcode_id,
-					success: function(res) {
-						var tempFilePath = res.tempFilePath;
-						console.log(tempFilePath);
-						that.imagePath = tempFilePath;
-						
-						//保存到相册
-						// uni.saveFile({
-						//       tempFilePath: tempFilePath,
-						//       success: function (res2) {
-						//         var savedFilePath = res2.savedFilePath;
-								
-								
-						//       }
-						// });
-						uni.saveImageToPhotosAlbum({
-							filePath : tempFilePath ,
-							success: function (res3) {
-								uni.showModal({
-									title: '提示',
-									content: '保存成功',
-									confirmText: '确定',
-									showCancel: false,
-									confirmColor: '#33CCCC',
-									success(res4) {
-										
-									}
-								}) 
-							},
-						});
-					},
-					fail: function(res) {
-						console.log(res);
-					}
-				}, that);
-			},
-			//微信小程序支持:长按二维码,提示是否保存相册
-			//安卓APP长按校正二维码
-			longtapCode(){
-				var that = this;
-				
-				//#ifndef MP
-				uni.showModal({
-					title: '校正二维码',
-					content: '二维码是否异常',
-					confirmText: '确定',
-					confirmColor: '#33CCCC',
-					success(res) {
-						if (res.confirm) {
-							that.rectify_code();
-						}
-					}
-				})
-				// #endif
-				
-				//#ifdef MP-WEIXIN
-				uni.showModal({
-					title: '提示',
-					content: '是否保存到相册',
-					confirmText: '确定',
-					confirmColor: '#33CCCC',
-					success(res) {
-						if (res.confirm) {
-							that.saveImage();
-						}
-					}
-				})
-				// #endif
-			},
-			//安卓有些手机不正常,长按可选择矫正
-			rectify_code(){
-				var that = this;
-				let add_num = that.add_num ;
-				add_num += 30 ;
-				that.add_num = add_num;
-				that.crtQrCode();//重新生成才会立即覆盖
-				try {
-					//第一次长按校正设置了就不用在设置
-					uni.setStorage({
-						key: that.add_num_key,
-						data: add_num,
-						success: function() {
-							
-						}
-					});
-				} catch (e) {
-					// error
-				
-				}
-			},
-		},
-		mounted() {}
-	}
-</script>
-
-<style scoped lang="scss">
-	// .qrcode-box {
-	// 	position: fixed;
-	// 	left: 0;
-	// 	top: 0;
-	// 	right: 0;
-	// 	bottom: 0;
-	// 	height: 100vh;
-	// 	width: 100vw;
-	// 	background-color: rgba(59, 59, 59, 0.6);
-	// 	// opacity: 0.8;
-	// 	text-align: center;
-	// 	display: flex;
-	// 	align-items: center;
-	// 	display: none;
-
-	// 	.qrcode-item {
-	// 		flex: 1;
-	// 		position: relative;
-	// 		text-align: center;
-
-	// 		.item-box {
-	// 			width: 90%;
-	// 			margin: auto;
-	// 			display: inline-block;
-	// 			margin-top: 30%;
-	// 			padding-bottom: 30rpx;
-
-	// 			// animation: show 0.7s;
-	// 			.title {
-	// 				font-size: 46rpx;
-	// 				text-align: center;
-	// 				margin-bottom: 24rpx;
-	// 			}
-
-	// 			.canvas {
-	// 				margin: auto;
-	// 				display: inline-block;
-	// 				margin: auto;
-	// 			}
-
-	// 			background-color: #FFFFFF;
-	// 		}
-
-	// 	}
-	// }
-	.box-qrcode{
-		text-align: center;
-		position: relative;
-		.box-img-qrcode{
-			position: absolute;
-			display: flex;
-			flex-direction: column;
-			justify-content: center;
-			align-items: center;
-			z-index: 2;
-		}
-	}
-	image{
-		width: 60upx;
-		height: 60upx;
-		border-radius: 50%;
-		
-	}
-	.canvas-qrcode {
-		
-		margin: auto;
-		display: inline-block;
-		float: left;
-	}
-	
-	
-	.opacity-qrcode {
-		opacity: 0;
-		display: block;
-	}
-
-	.show-qrcode {
-		display: block;
-		animation: fade 0.7s;
-
-		// -moz-animation: fade 0.5s; /* Firefox */
-		// -webkit-animation: fade 0.5s; /* Safari 和 Chrome */
-		// -o-animation: fade 0.5s;
-	}
-
-	.hide-qrcode {
-		animation: hide 0.7s;
-	}
-
-	@keyframes fade {
-		from {
-			opacity: 0.8;
-		}
-
-		to {
-			opacity: 1;
-		}
-	}
-
-	@keyframes hide {
-		from {
-			opacity: 1;
-		}
-
-		to {
-			opacity: 0;
-		}
-	}
-</style>

+ 0 - 872
components/ay-qrcode/qrcode_wx.js

@@ -1,872 +0,0 @@
-!(function() {
-
-	// alignment pattern
-	var adelta = [
-		0, 11, 15, 19, 23, 27, 31,
-		16, 18, 20, 22, 24, 26, 28, 20, 22, 24, 24, 26, 28, 28, 22, 24, 24,
-		26, 26, 28, 28, 24, 24, 26, 26, 26, 28, 28, 24, 26, 26, 26, 28, 28
-	];
-
-	// version block
-	var vpat = [
-		0xc94, 0x5bc, 0xa99, 0x4d3, 0xbf6, 0x762, 0x847, 0x60d,
-		0x928, 0xb78, 0x45d, 0xa17, 0x532, 0x9a6, 0x683, 0x8c9,
-		0x7ec, 0xec4, 0x1e1, 0xfab, 0x08e, 0xc1a, 0x33f, 0xd75,
-		0x250, 0x9d5, 0x6f0, 0x8ba, 0x79f, 0xb0b, 0x42e, 0xa64,
-		0x541, 0xc69
-	];
-
-	// final format bits with mask: level << 3 | mask
-	var fmtword = [
-		0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976, //L
-		0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0, //M
-		0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed, //Q
-		0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b //H
-	];
-
-	// 4 per version: number of blocks 1,2; data width; ecc width
-	var eccblocks = [
-		1, 0, 19, 7, 1, 0, 16, 10, 1, 0, 13, 13, 1, 0, 9, 17,
-		1, 0, 34, 10, 1, 0, 28, 16, 1, 0, 22, 22, 1, 0, 16, 28,
-		1, 0, 55, 15, 1, 0, 44, 26, 2, 0, 17, 18, 2, 0, 13, 22,
-		1, 0, 80, 20, 2, 0, 32, 18, 2, 0, 24, 26, 4, 0, 9, 16,
-		1, 0, 108, 26, 2, 0, 43, 24, 2, 2, 15, 18, 2, 2, 11, 22,
-		2, 0, 68, 18, 4, 0, 27, 16, 4, 0, 19, 24, 4, 0, 15, 28,
-		2, 0, 78, 20, 4, 0, 31, 18, 2, 4, 14, 18, 4, 1, 13, 26,
-		2, 0, 97, 24, 2, 2, 38, 22, 4, 2, 18, 22, 4, 2, 14, 26,
-		2, 0, 116, 30, 3, 2, 36, 22, 4, 4, 16, 20, 4, 4, 12, 24,
-		2, 2, 68, 18, 4, 1, 43, 26, 6, 2, 19, 24, 6, 2, 15, 28,
-		4, 0, 81, 20, 1, 4, 50, 30, 4, 4, 22, 28, 3, 8, 12, 24,
-		2, 2, 92, 24, 6, 2, 36, 22, 4, 6, 20, 26, 7, 4, 14, 28,
-		4, 0, 107, 26, 8, 1, 37, 22, 8, 4, 20, 24, 12, 4, 11, 22,
-		3, 1, 115, 30, 4, 5, 40, 24, 11, 5, 16, 20, 11, 5, 12, 24,
-		5, 1, 87, 22, 5, 5, 41, 24, 5, 7, 24, 30, 11, 7, 12, 24,
-		5, 1, 98, 24, 7, 3, 45, 28, 15, 2, 19, 24, 3, 13, 15, 30,
-		1, 5, 107, 28, 10, 1, 46, 28, 1, 15, 22, 28, 2, 17, 14, 28,
-		5, 1, 120, 30, 9, 4, 43, 26, 17, 1, 22, 28, 2, 19, 14, 28,
-		3, 4, 113, 28, 3, 11, 44, 26, 17, 4, 21, 26, 9, 16, 13, 26,
-		3, 5, 107, 28, 3, 13, 41, 26, 15, 5, 24, 30, 15, 10, 15, 28,
-		4, 4, 116, 28, 17, 0, 42, 26, 17, 6, 22, 28, 19, 6, 16, 30,
-		2, 7, 111, 28, 17, 0, 46, 28, 7, 16, 24, 30, 34, 0, 13, 24,
-		4, 5, 121, 30, 4, 14, 47, 28, 11, 14, 24, 30, 16, 14, 15, 30,
-		6, 4, 117, 30, 6, 14, 45, 28, 11, 16, 24, 30, 30, 2, 16, 30,
-		8, 4, 106, 26, 8, 13, 47, 28, 7, 22, 24, 30, 22, 13, 15, 30,
-		10, 2, 114, 28, 19, 4, 46, 28, 28, 6, 22, 28, 33, 4, 16, 30,
-		8, 4, 122, 30, 22, 3, 45, 28, 8, 26, 23, 30, 12, 28, 15, 30,
-		3, 10, 117, 30, 3, 23, 45, 28, 4, 31, 24, 30, 11, 31, 15, 30,
-		7, 7, 116, 30, 21, 7, 45, 28, 1, 37, 23, 30, 19, 26, 15, 30,
-		5, 10, 115, 30, 19, 10, 47, 28, 15, 25, 24, 30, 23, 25, 15, 30,
-		13, 3, 115, 30, 2, 29, 46, 28, 42, 1, 24, 30, 23, 28, 15, 30,
-		17, 0, 115, 30, 10, 23, 46, 28, 10, 35, 24, 30, 19, 35, 15, 30,
-		17, 1, 115, 30, 14, 21, 46, 28, 29, 19, 24, 30, 11, 46, 15, 30,
-		13, 6, 115, 30, 14, 23, 46, 28, 44, 7, 24, 30, 59, 1, 16, 30,
-		12, 7, 121, 30, 12, 26, 47, 28, 39, 14, 24, 30, 22, 41, 15, 30,
-		6, 14, 121, 30, 6, 34, 47, 28, 46, 10, 24, 30, 2, 64, 15, 30,
-		17, 4, 122, 30, 29, 14, 46, 28, 49, 10, 24, 30, 24, 46, 15, 30,
-		4, 18, 122, 30, 13, 32, 46, 28, 48, 14, 24, 30, 42, 32, 15, 30,
-		20, 4, 117, 30, 40, 7, 47, 28, 43, 22, 24, 30, 10, 67, 15, 30,
-		19, 6, 118, 30, 18, 31, 47, 28, 34, 34, 24, 30, 20, 61, 15, 30
-	];
-
-	// Galois field log table
-	var glog = [
-		0xff, 0x00, 0x01, 0x19, 0x02, 0x32, 0x1a, 0xc6, 0x03, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b,
-		0x04, 0x64, 0xe0, 0x0e, 0x34, 0x8d, 0xef, 0x81, 0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x08, 0x4c, 0x71,
-		0x05, 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0x0f, 0x21, 0x35, 0x93, 0x8e, 0xda, 0xf0, 0x12, 0x82, 0x45,
-		0x1d, 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9, 0xc9, 0x9a, 0x09, 0x78, 0x4d, 0xe4, 0x72, 0xa6,
-		0x06, 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd, 0xe2, 0x98, 0x25, 0xb3, 0x10, 0x91, 0x22, 0x88,
-		0x36, 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd, 0xf1, 0xd2, 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40,
-		0x1e, 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e, 0x6b, 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d,
-		0xca, 0x5e, 0x9b, 0x9f, 0x0a, 0x15, 0x79, 0x2b, 0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57,
-		0x07, 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, 0x0d, 0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18,
-		0xe3, 0xa5, 0x99, 0x77, 0x26, 0xb8, 0xb4, 0x7c, 0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e,
-		0x37, 0x3f, 0xd1, 0x5b, 0x95, 0xbc, 0xcf, 0xcd, 0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61,
-		0xf2, 0x56, 0xd3, 0xab, 0x14, 0x2a, 0x5d, 0x9e, 0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2,
-		0x1f, 0x2d, 0x43, 0xd8, 0xb7, 0x7b, 0xa4, 0x76, 0xc4, 0x17, 0x49, 0xec, 0x7f, 0x0c, 0x6f, 0xf6,
-		0x6c, 0xa1, 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa, 0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a,
-		0xcb, 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51, 0x0b, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7,
-		0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8, 0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf
-	];
-
-	// Galios field exponent table
-	var gexp = [
-		0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26,
-		0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, 0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0,
-		0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35, 0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23,
-		0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0, 0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1,
-		0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc, 0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0,
-		0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f, 0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2,
-		0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce,
-		0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93, 0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc,
-		0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9, 0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54,
-		0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa, 0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73,
-		0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e, 0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff,
-		0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4, 0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41,
-		0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6,
-		0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef, 0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09,
-		0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5, 0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16,
-		0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83, 0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x00
-	];
-
-	// Working buffers:
-	// data input and ecc append, image working buffer, fixed part of image, run lengths for badness
-	var strinbuf = [],
-		eccbuf = [],
-		qrframe = [],
-		framask = [],
-		rlens = [];
-	// Control values - width is based on version, last 4 are from table.
-	var version, width, neccblk1, neccblk2, datablkw, eccblkwid;
-	var ecclevel = 2;
-	// set bit to indicate cell in qrframe is immutable.  symmetric around diagonal
-	function setmask(x, y) {
-		var bt;
-		if (x > y) {
-			bt = x;
-			x = y;
-			y = bt;
-		}
-		// y*y = 1+3+5...
-		bt = y;
-		bt *= y;
-		bt += y;
-		bt >>= 1;
-		bt += x;
-		framask[bt] = 1;
-	}
-
-	// enter alignment pattern - black to qrframe, white to mask (later black frame merged to mask)
-	function putalign(x, y) {
-		var j;
-
-		qrframe[x + width * y] = 1;
-		for (j = -2; j < 2; j++) {
-			qrframe[(x + j) + width * (y - 2)] = 1;
-			qrframe[(x - 2) + width * (y + j + 1)] = 1;
-			qrframe[(x + 2) + width * (y + j)] = 1;
-			qrframe[(x + j + 1) + width * (y + 2)] = 1;
-		}
-		for (j = 0; j < 2; j++) {
-			setmask(x - 1, y + j);
-			setmask(x + 1, y - j);
-			setmask(x - j, y - 1);
-			setmask(x + j, y + 1);
-		}
-	}
-
-	//========================================================================
-	// Reed Solomon error correction
-	// exponentiation mod N
-	function modnn(x) {
-		while (x >= 255) {
-			x -= 255;
-			x = (x >> 8) + (x & 255);
-		}
-		return x;
-	}
-
-	var genpoly = [];
-
-	// Calculate and append ECC data to data block.  Block is in strinbuf, indexes to buffers given.
-	function appendrs(data, dlen, ecbuf, eclen) {
-		var i, j, fb;
-
-		for (i = 0; i < eclen; i++)
-			strinbuf[ecbuf + i] = 0;
-		for (i = 0; i < dlen; i++) {
-			fb = glog[strinbuf[data + i] ^ strinbuf[ecbuf]];
-			if (fb != 255) /* fb term is non-zero */
-				for (j = 1; j < eclen; j++)
-					strinbuf[ecbuf + j - 1] = strinbuf[ecbuf + j] ^ gexp[modnn(fb + genpoly[eclen - j])];
-			else
-				for (j = ecbuf; j < ecbuf + eclen; j++)
-					strinbuf[j] = strinbuf[j + 1];
-			strinbuf[ecbuf + eclen - 1] = fb == 255 ? 0 : gexp[modnn(fb + genpoly[0])];
-		}
-	}
-
-	//========================================================================
-	// Frame data insert following the path rules
-
-	// check mask - since symmetrical use half.
-	function ismasked(x, y) {
-		var bt;
-		if (x > y) {
-			bt = x;
-			x = y;
-			y = bt;
-		}
-		bt = y;
-		bt += y * y;
-		bt >>= 1;
-		bt += x;
-		return framask[bt];
-	}
-
-	//========================================================================
-	//  Apply the selected mask out of the 8.
-	function applymask(m) {
-		var x, y, r3x, r3y;
-
-		switch (m) {
-			case 0:
-				for (y = 0; y < width; y++)
-					for (x = 0; x < width; x++)
-						if (!((x + y) & 1) && !ismasked(x, y))
-							qrframe[x + y * width] ^= 1;
-				break;
-			case 1:
-				for (y = 0; y < width; y++)
-					for (x = 0; x < width; x++)
-						if (!(y & 1) && !ismasked(x, y))
-							qrframe[x + y * width] ^= 1;
-				break;
-			case 2:
-				for (y = 0; y < width; y++)
-					for (r3x = 0, x = 0; x < width; x++, r3x++) {
-						if (r3x == 3)
-							r3x = 0;
-						if (!r3x && !ismasked(x, y))
-							qrframe[x + y * width] ^= 1;
-					}
-				break;
-			case 3:
-				for (r3y = 0, y = 0; y < width; y++, r3y++) {
-					if (r3y == 3)
-						r3y = 0;
-					for (r3x = r3y, x = 0; x < width; x++, r3x++) {
-						if (r3x == 3)
-							r3x = 0;
-						if (!r3x && !ismasked(x, y))
-							qrframe[x + y * width] ^= 1;
-					}
-				}
-				break;
-			case 4:
-				for (y = 0; y < width; y++)
-					for (r3x = 0, r3y = ((y >> 1) & 1), x = 0; x < width; x++, r3x++) {
-						if (r3x == 3) {
-							r3x = 0;
-							r3y = !r3y;
-						}
-						if (!r3y && !ismasked(x, y))
-							qrframe[x + y * width] ^= 1;
-					}
-				break;
-			case 5:
-				for (r3y = 0, y = 0; y < width; y++, r3y++) {
-					if (r3y == 3)
-						r3y = 0;
-					for (r3x = 0, x = 0; x < width; x++, r3x++) {
-						if (r3x == 3)
-							r3x = 0;
-						if (!((x & y & 1) + !(!r3x | !r3y)) && !ismasked(x, y))
-							qrframe[x + y * width] ^= 1;
-					}
-				}
-				break;
-			case 6:
-				for (r3y = 0, y = 0; y < width; y++, r3y++) {
-					if (r3y == 3)
-						r3y = 0;
-					for (r3x = 0, x = 0; x < width; x++, r3x++) {
-						if (r3x == 3)
-							r3x = 0;
-						if (!(((x & y & 1) + (r3x && (r3x == r3y))) & 1) && !ismasked(x, y))
-							qrframe[x + y * width] ^= 1;
-					}
-				}
-				break;
-			case 7:
-				for (r3y = 0, y = 0; y < width; y++, r3y++) {
-					if (r3y == 3)
-						r3y = 0;
-					for (r3x = 0, x = 0; x < width; x++, r3x++) {
-						if (r3x == 3)
-							r3x = 0;
-						if (!(((r3x && (r3x == r3y)) + ((x + y) & 1)) & 1) && !ismasked(x, y))
-							qrframe[x + y * width] ^= 1;
-					}
-				}
-				break;
-		}
-		return;
-	}
-
-	// Badness coefficients.
-	var N1 = 3,
-		N2 = 3,
-		N3 = 40,
-		N4 = 10;
-
-	// Using the table of the length of each run, calculate the amount of bad image 
-	// - long runs or those that look like finders; called twice, once each for X and Y
-	function badruns(length) {
-		var i;
-		var runsbad = 0;
-		for (i = 0; i <= length; i++)
-			if (rlens[i] >= 5)
-				runsbad += N1 + rlens[i] - 5;
-		// BwBBBwB as in finder
-		for (i = 3; i < length - 1; i += 2)
-			if (rlens[i - 2] == rlens[i + 2] &&
-				rlens[i + 2] == rlens[i - 1] &&
-				rlens[i - 1] == rlens[i + 1] &&
-				rlens[i - 1] * 3 == rlens[i]
-				// white around the black pattern? Not part of spec
-				&&
-				(rlens[i - 3] == 0 // beginning
-					||
-					i + 3 > length // end
-					||
-					rlens[i - 3] * 3 >= rlens[i] * 4 || rlens[i + 3] * 3 >= rlens[i] * 4)
-			)
-				runsbad += N3;
-		return runsbad;
-	}
-
-	// Calculate how bad the masked image is - blocks, imbalance, runs, or finders.
-	function badcheck() {
-		var x, y, h, b, b1;
-		var thisbad = 0;
-		var bw = 0;
-
-		// blocks of same color.
-		for (y = 0; y < width - 1; y++)
-			for (x = 0; x < width - 1; x++)
-				if ((qrframe[x + width * y] && qrframe[(x + 1) + width * y] &&
-						qrframe[x + width * (y + 1)] && qrframe[(x + 1) + width * (y + 1)]) // all black
-					||
-					!(qrframe[x + width * y] || qrframe[(x + 1) + width * y] ||
-						qrframe[x + width * (y + 1)] || qrframe[(x + 1) + width * (y + 1)])) // all white
-					thisbad += N2;
-
-		// X runs
-		for (y = 0; y < width; y++) {
-			rlens[0] = 0;
-			for (h = b = x = 0; x < width; x++) {
-				if ((b1 = qrframe[x + width * y]) == b)
-					rlens[h]++;
-				else
-					rlens[++h] = 1;
-				b = b1;
-				bw += b ? 1 : -1;
-			}
-			thisbad += badruns(h);
-		}
-
-		// black/white imbalance
-		if (bw < 0)
-			bw = -bw;
-
-		var big = bw;
-		var count = 0;
-		big += big << 2;
-		big <<= 1;
-		while (big > width * width)
-			big -= width * width, count++;
-		thisbad += count * N4;
-
-		// Y runs
-		for (x = 0; x < width; x++) {
-			rlens[0] = 0;
-			for (h = b = y = 0; y < width; y++) {
-				if ((b1 = qrframe[x + width * y]) == b)
-					rlens[h]++;
-				else
-					rlens[++h] = 1;
-				b = b1;
-			}
-			thisbad += badruns(h);
-		}
-		return thisbad;
-	}
-
-	function genframe(instring) {
-		var x, y, k, t, v, i, j, m;
-
-		// find the smallest version that fits the string
-		t = instring.length;
-		version = 0;
-		do {
-			version++;
-			k = (ecclevel - 1) * 4 + (version - 1) * 16;
-			neccblk1 = eccblocks[k++];
-			neccblk2 = eccblocks[k++];
-			datablkw = eccblocks[k++];
-			eccblkwid = eccblocks[k];
-			k = datablkw * (neccblk1 + neccblk2) + neccblk2 - 3 + (version <= 9);
-			if (t <= k)
-				break;
-		} while (version < 40);
-
-		// FIXME - insure that it fits insted of being truncated
-		width = 17 + 4 * version;
-
-		// allocate, clear and setup data structures
-		v = datablkw + (datablkw + eccblkwid) * (neccblk1 + neccblk2) + neccblk2;
-		for (t = 0; t < v; t++)
-			eccbuf[t] = 0;
-		strinbuf = instring.slice(0);
-
-		for (t = 0; t < width * width; t++)
-			qrframe[t] = 0;
-
-		for (t = 0; t < (width * (width + 1) + 1) / 2; t++)
-			framask[t] = 0;
-
-		// insert finders - black to frame, white to mask
-		for (t = 0; t < 3; t++) {
-			k = 0;
-			y = 0;
-			if (t == 1)
-				k = (width - 7);
-			if (t == 2)
-				y = (width - 7);
-			qrframe[(y + 3) + width * (k + 3)] = 1;
-			for (x = 0; x < 6; x++) {
-				qrframe[(y + x) + width * k] = 1;
-				qrframe[y + width * (k + x + 1)] = 1;
-				qrframe[(y + 6) + width * (k + x)] = 1;
-				qrframe[(y + x + 1) + width * (k + 6)] = 1;
-			}
-			for (x = 1; x < 5; x++) {
-				setmask(y + x, k + 1);
-				setmask(y + 1, k + x + 1);
-				setmask(y + 5, k + x);
-				setmask(y + x + 1, k + 5);
-			}
-			for (x = 2; x < 4; x++) {
-				qrframe[(y + x) + width * (k + 2)] = 1;
-				qrframe[(y + 2) + width * (k + x + 1)] = 1;
-				qrframe[(y + 4) + width * (k + x)] = 1;
-				qrframe[(y + x + 1) + width * (k + 4)] = 1;
-			}
-		}
-
-		// alignment blocks
-		if (version > 1) {
-			t = adelta[version];
-			y = width - 7;
-			for (;;) {
-				x = width - 7;
-				while (x > t - 3) {
-					putalign(x, y);
-					if (x < t)
-						break;
-					x -= t;
-				}
-				if (y <= t + 9)
-					break;
-				y -= t;
-				putalign(6, y);
-				putalign(y, 6);
-			}
-		}
-
-		// single black
-		qrframe[8 + width * (width - 8)] = 1;
-
-		// timing gap - mask only
-		for (y = 0; y < 7; y++) {
-			setmask(7, y);
-			setmask(width - 8, y);
-			setmask(7, y + width - 7);
-		}
-		for (x = 0; x < 8; x++) {
-			setmask(x, 7);
-			setmask(x + width - 8, 7);
-			setmask(x, width - 8);
-		}
-
-		// reserve mask-format area
-		for (x = 0; x < 9; x++)
-			setmask(x, 8);
-		for (x = 0; x < 8; x++) {
-			setmask(x + width - 8, 8);
-			setmask(8, x);
-		}
-		for (y = 0; y < 7; y++)
-			setmask(8, y + width - 7);
-
-		// timing row/col
-		for (x = 0; x < width - 14; x++)
-			if (x & 1) {
-				setmask(8 + x, 6);
-				setmask(6, 8 + x);
-			}
-		else {
-			qrframe[(8 + x) + width * 6] = 1;
-			qrframe[6 + width * (8 + x)] = 1;
-		}
-
-		// version block
-		if (version > 6) {
-			t = vpat[version - 7];
-			k = 17;
-			for (x = 0; x < 6; x++)
-				for (y = 0; y < 3; y++, k--)
-					if (1 & (k > 11 ? version >> (k - 12) : t >> k)) {
-						qrframe[(5 - x) + width * (2 - y + width - 11)] = 1;
-						qrframe[(2 - y + width - 11) + width * (5 - x)] = 1;
-					}
-			else {
-				setmask(5 - x, 2 - y + width - 11);
-				setmask(2 - y + width - 11, 5 - x);
-			}
-		}
-
-		// sync mask bits - only set above for white spaces, so add in black bits
-		for (y = 0; y < width; y++)
-			for (x = 0; x <= y; x++)
-				if (qrframe[x + width * y])
-					setmask(x, y);
-
-		// convert string to bitstream
-		// 8 bit data to QR-coded 8 bit data (numeric or alphanum, or kanji not supported)
-		v = strinbuf.length;
-
-		// string to array
-		for (i = 0; i < v; i++)
-			eccbuf[i] = strinbuf.charCodeAt(i);
-		strinbuf = eccbuf.slice(0);
-
-		// calculate max string length
-		x = datablkw * (neccblk1 + neccblk2) + neccblk2;
-		if (v >= x - 2) {
-			v = x - 2;
-			if (version > 9)
-				v--;
-		}
-
-		// shift and repack to insert length prefix
-		i = v;
-		if (version > 9) {
-			strinbuf[i + 2] = 0;
-			strinbuf[i + 3] = 0;
-			while (i--) {
-				t = strinbuf[i];
-				strinbuf[i + 3] |= 255 & (t << 4);
-				strinbuf[i + 2] = t >> 4;
-			}
-			strinbuf[2] |= 255 & (v << 4);
-			strinbuf[1] = v >> 4;
-			strinbuf[0] = 0x40 | (v >> 12);
-		} else {
-			strinbuf[i + 1] = 0;
-			strinbuf[i + 2] = 0;
-			while (i--) {
-				t = strinbuf[i];
-				strinbuf[i + 2] |= 255 & (t << 4);
-				strinbuf[i + 1] = t >> 4;
-			}
-			strinbuf[1] |= 255 & (v << 4);
-			strinbuf[0] = 0x40 | (v >> 4);
-		}
-		// fill to end with pad pattern
-		i = v + 3 - (version < 10);
-		while (i < x) {
-			strinbuf[i++] = 0xec;
-			// buffer has room    if (i == x)      break;
-			strinbuf[i++] = 0x11;
-		}
-
-		// calculate and append ECC
-
-		// calculate generator polynomial
-		genpoly[0] = 1;
-		for (i = 0; i < eccblkwid; i++) {
-			genpoly[i + 1] = 1;
-			for (j = i; j > 0; j--)
-				genpoly[j] = genpoly[j] ?
-				genpoly[j - 1] ^ gexp[modnn(glog[genpoly[j]] + i)] : genpoly[j - 1];
-			genpoly[0] = gexp[modnn(glog[genpoly[0]] + i)];
-		}
-		for (i = 0; i <= eccblkwid; i++)
-			genpoly[i] = glog[genpoly[i]]; // use logs for genpoly[] to save calc step
-
-		// append ecc to data buffer
-		k = x;
-		y = 0;
-		for (i = 0; i < neccblk1; i++) {
-			appendrs(y, datablkw, k, eccblkwid);
-			y += datablkw;
-			k += eccblkwid;
-		}
-		for (i = 0; i < neccblk2; i++) {
-			appendrs(y, datablkw + 1, k, eccblkwid);
-			y += datablkw + 1;
-			k += eccblkwid;
-		}
-		// interleave blocks
-		y = 0;
-		for (i = 0; i < datablkw; i++) {
-			for (j = 0; j < neccblk1; j++)
-				eccbuf[y++] = strinbuf[i + j * datablkw];
-			for (j = 0; j < neccblk2; j++)
-				eccbuf[y++] = strinbuf[(neccblk1 * datablkw) + i + (j * (datablkw + 1))];
-		}
-		for (j = 0; j < neccblk2; j++)
-			eccbuf[y++] = strinbuf[(neccblk1 * datablkw) + i + (j * (datablkw + 1))];
-		for (i = 0; i < eccblkwid; i++)
-			for (j = 0; j < neccblk1 + neccblk2; j++)
-				eccbuf[y++] = strinbuf[x + i + j * eccblkwid];
-		strinbuf = eccbuf;
-
-		// pack bits into frame avoiding masked area.
-		x = y = width - 1;
-		k = v = 1; // up, minus
-		/* inteleaved data and ecc codes */
-		m = (datablkw + eccblkwid) * (neccblk1 + neccblk2) + neccblk2;
-		for (i = 0; i < m; i++) {
-			t = strinbuf[i];
-			for (j = 0; j < 8; j++, t <<= 1) {
-				if (0x80 & t)
-					qrframe[x + width * y] = 1;
-				do { // find next fill position
-					if (v)
-						x--;
-					else {
-						x++;
-						if (k) {
-							if (y != 0)
-								y--;
-							else {
-								x -= 2;
-								k = !k;
-								if (x == 6) {
-									x--;
-									y = 9;
-								}
-							}
-						} else {
-							if (y != width - 1)
-								y++;
-							else {
-								x -= 2;
-								k = !k;
-								if (x == 6) {
-									x--;
-									y -= 8;
-								}
-							}
-						}
-					}
-					v = !v;
-				} while (ismasked(x, y));
-			}
-		}
-
-		// save pre-mask copy of frame
-		strinbuf = qrframe.slice(0);
-		t = 0; // best
-		y = 30000; // demerit
-		// for instead of while since in original arduino code
-		// if an early mask was "good enough" it wouldn't try for a better one
-		// since they get more complex and take longer.
-		for (k = 0; k < 8; k++) {
-			applymask(k); // returns black-white imbalance
-			x = badcheck();
-			if (x < y) { // current mask better than previous best?
-				y = x;
-				t = k;
-			}
-			if (t == 7)
-				break; // don't increment i to a void redoing mask
-			qrframe = strinbuf.slice(0); // reset for next pass
-		}
-		if (t != k) // redo best mask - none good enough, last wasn't t
-			applymask(t);
-
-		// add in final mask/ecclevel bytes
-		y = fmtword[t + ((ecclevel - 1) << 3)];
-		// low byte
-		for (k = 0; k < 8; k++, y >>= 1)
-			if (y & 1) {
-				qrframe[(width - 1 - k) + width * 8] = 1;
-				if (k < 6)
-					qrframe[8 + width * k] = 1;
-				else
-					qrframe[8 + width * (k + 1)] = 1;
-			}
-		// high byte
-		for (k = 0; k < 7; k++, y >>= 1)
-			if (y & 1) {
-				qrframe[8 + width * (width - 7 + k)] = 1;
-				if (k)
-					qrframe[(6 - k) + width * 8] = 1;
-				else
-					qrframe[7 + width * 8] = 1;
-			}
-		return qrframe;
-	}
-
-
-
-
-	var _canvas = null;
-
-	var api = {
-
-		get ecclevel() {
-			return ecclevel;
-		},
-
-		set ecclevel(val) {
-			ecclevel = val;
-		},
-
-		get size() {
-			return _size;
-		},
-
-		set size(val) {
-			_size = val
-		},
-
-		get canvas() {
-			return _canvas;
-		},
-
-		set canvas(el) {
-			_canvas = el;
-		},
-
-		getFrame: function(string) {
-			return genframe(string);
-		},
-		//这里的utf16to8(str)是对Text中的字符串进行转码,让其支持中文
-		utf16to8: function(str) {
-			var out, i, len, c;
-
-			out = "";
-			len = str.length;
-			for (i = 0; i < len; i++) {
-				c = str.charCodeAt(i);
-				if ((c >= 0x0001) && (c <= 0x007F)) {
-					out += str.charAt(i);
-				} else if (c > 0x07FF) {
-					out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
-					out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
-					out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
-				} else {
-					out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
-					out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
-				}
-			}
-			return out;
-		},
-		/**
-		 * 新增$this参数,传入组件的this,兼容在组件中生成
-		 */
-		draw: function(str, canvas, cavW, cavH, cavColor, haveImg, imageUrl, imageSize, $this, cb = function() {}, ecc) {
-			var that = this;
-			ecclevel = ecc || ecclevel;
-			canvas = canvas || _canvas;
-			if (!canvas) {
-				console.warn('No canvas provided to draw QR code in!')
-				return;
-			}
-			
-			
-			let pre_background = "#ffffff";
-			var size = Math.min(cavW, cavH);
-			str = that.utf16to8(str); //增加中文显示
-
-			var frame = that.getFrame(str);
-				// 组件中生成qrcode需要绑定this 
-			var ctx = uni.createCanvasContext(canvas, $this);
-			var px = Math.round(size / (width ));
-			
-			var roundedSize = px * (width);
-			// var px = 1 ;
-			// var roundedSize = px * (width + 8) ;
-			
-			//var roundedSize = 0 ;
-			//var offset = Math.floor((size - roundedSize) / 2);
-			var offset = 0 ;
-			size = roundedSize;
-			//ctx.clearRect(0, 0, cavW, cavW);
-			ctx.setFillStyle(pre_background)
-			ctx.fillRect(0, 0, cavW, cavW);
-			ctx.setFillStyle(cavColor);
-			for (var i = 0; i < width; i++) {
-				for (var j = 0; j < width; j++) {
-					if (frame[j * width + i]) {
-						ctx.fillRect(px * ( i) + offset, px * ( j) + offset, px, px);
-					}
-				}
-			}
-
-			//画图片
-			if (haveImg) {
-				try {
-					var x = Number(((cavW - imageSize - 14) / 2).toFixed(2));
-					var y = Number(((cavH - imageSize -14) / 2).toFixed(2));
-					drawRoundedRect(ctx, x, y, imageSize, imageSize, imageSize / 2, 6, true, true)
-
-					let isNetImg = false;
-
-					isNetImg = imageUrl.substr(0, 4) == 'http' ? true : false;
-
-					if (isNetImg) {
-						//网络图片下载到本地
-						uni.getImageInfo({
-							src: imageUrl,
-							success: function(res) {
-								ctx.drawImage(res.path, x, y, imageSize, imageSize);
-								//--增加绘制完成回调
-								ctx.draw(false, function() {
-									cb();
-								})
-							}
-						})
-					} else {
-						ctx.drawImage(imageUrl, x, y, imageSize, imageSize);
-						//--增加绘制完成回调
-						ctx.draw(false, function() {
-							cb();
-						})
-					}
-
-
-
-
-					// 画圆角矩形
-					function drawRoundedRect(ctxi, x, y, width, height, r, lineWidth, fill, stroke) {
-						ctxi.setLineWidth(lineWidth);
-						ctxi.setFillStyle(pre_background);
-						ctxi.setStrokeStyle(pre_background);
-						ctxi.beginPath(); // draw top and top right corner 
-						ctxi.moveTo(x + r, y);
-						ctxi.arcTo(x + width, y, x + width, y + r, r); // draw right side and bottom right corner 
-						ctxi.arcTo(x + width, y + height, x + width - r, y + height, r); // draw bottom and bottom left corner 
-						ctxi.arcTo(x, y + height, x, y + height - r, r); // draw left and top left corner 
-						ctxi.arcTo(x, y, x + r, y, r);
-						ctxi.closePath();
-						if (fill) {
-							ctxi.fill();
-						}
-						if (stroke) {
-							ctxi.stroke();
-						}
-					}
-				} catch (e) {
-					//TODO handle the exception
-				}
-
-			} else {
-				//--增加绘制完成回调
-				ctx.draw(false, function() {
-					cb();
-				})
-			}
-
-
-
-		}
-	}
-	module.exports = {
-		api
-	}
-})();

Разница между файлами не показана из-за своего большого размера
+ 0 - 263
components/ay-qrcode/weapp-qrcode.js


+ 0 - 123
components/custom_bar.vue

@@ -1,123 +0,0 @@
-<template>
-  <view>
-    <view :style="{ background: background }" class="custom-header-container">
-      <view :style="{height:getStausBarHeight + 'px'}" class="custom-header-status-bar"></view>
-      <view :class="{'ios-center': isIos }" class="custom-header-top-container">
-        <view :style="{color: color}" :class="{isIos: isIos}" class="custom-back-btn iconfont" v-if="showBack" @tap="backTap">
-			<image v-if="color!='#ffffff'" src="http://139.9.103.171:1888/img/image/ic_back_black.png" mode="widthFix" style="width: 20px;"></image>
-			<image v-if="color=='#ffffff'" src="http://139.9.103.171:1888/img/image/ic_back_white.png" mode="widthFix" style="width: 20px;"></image>
-		</view>
-        <view :style="{color: color}" :class="[{'ios-center': isIos },{'android-left-30':!isIos&&!showBack},{'android-left-80':!isIos&&showBack}]" class="custom-header-title" >{{ title }}</view></view>
-    </view>
-    <view :style="{ height: getStausBarHeight + (isIos ? 45 : 48) + 'px' }" class="custom-header-height"></view>
-  </view>
-</template>
-<script>
-export default {
-    props: {
-      title:{
-        type:String,
-		default:''
-	  },
-      background: {
-        type: String,
-		default:'transparent'
-      },
-      color: {
-        type: String,
-		default:'#ffffff'
-      },
-      showBack: {
-        type: Boolean,
-		default:false
-      },
-    },
-    computed: {
-      getStausBarHeight() {
-        try {
-          const res = uni.getSystemInfoSync();
-          return res.statusBarHeight;
-        } catch(e) {}
-      },
-      isIos() {
-        return uni.getSystemInfoSync().system.indexOf('iOS') > -1
-      }
-    },
-    methods: {
-      backTap() {
-		uni.navigateBack({
-			delta:1
-		})
-      }
-    }
-  }
-</script>
-<style scoped lang="scss">
-	.custom-header-top-container {
-		display:flex;
-		flex-flow:row nowrap;
-		justify-content:flex-start;
-		width:100%;
-		align-items:center;
-		&.ios-center {
-		 justify-content:center;
-	  }
-	}
-	.custom-header-container {
-		z-index:9;
-		width:750upx;
-		display:flex;
-		flex-direction:column;
-		align-items:center;
-		position:fixed;
-		top:0;
-	}
-	.custom-back-btn {
-		height:48px;
-		line-height:48px;
-		width:40px;
-		margin:0;
-		padding:0;
-		border-radius:0 !important;
-		display:flex;
-		align-items:center;
-		justify-content:center;
-		position:absolute;
-		left:0upx;
-		font-size:18px;
-		border-radius:5px;
-		font-weight:500;
-		color:#FFFFFF;
-		&:active {
-		 background:#0D72DF;
-	   }
-	  &.isIos {
-		line-height:45px;
-		height:45px;
-	 }
-	}
-	.custom-header-status-bar {
-		width:100%;
-		top:0;
-		position:sticky;
-		z-index:100;
-	}
-	.custom-header-title {
-		height:48px;
-		line-height:48px;
-		font-size:16px;
-		color:#FFFFFF;
-		&.ios-center {
-			margin-left: 0;
-			line-height:45px;
-			height:45px;
-		}
-		&.android-left-30{
-			margin-left: 30upx;
-		}
-		&.android-left-80{
-			margin-left: 80upx;
-		}
-			
-	}
-</style>

+ 0 - 162
components/echarts/qiun-loading/loading1.vue

@@ -1,162 +0,0 @@
-<template>
-	 <view class="container loading1">
-		<view class="shape shape1"></view>
-		<view class="shape shape2"></view>
-		<view class="shape shape3"></view>
-		<view class="shape shape4"></view>
-	</view>
-</template>
-
-<script>
-	export default {
-		name: 'loading1',
-		data() {
-			return {
-				
-			};
-		}
-	}
-</script>
-
-<style scoped="true">
-.container {
-  width: 30px;
-  height: 30px;
-  position: relative;
-}
-.container.loading1 {
-  -webkit-transform: rotate(45deg);
-          transform: rotate(45deg);
-}
-
-.container .shape {
-  position: absolute;
-  width: 10px;
-  height: 10px;
-  border-radius: 1px;
-}
-.container .shape.shape1 {
-  left: 0;
-  background-color: #1890FF;
-}
-.container .shape.shape2 {
-  right: 0;
-  background-color: #91CB74;
-}
-.container .shape.shape3 {
-  bottom: 0;
-  background-color: #FAC858;
-}
-.container .shape.shape4 {
-  bottom: 0;
-  right: 0;
-  background-color: #EE6666;
-}
-
-.loading1 .shape1 {
-  -webkit-animation: animation1shape1 0.5s ease 0s infinite alternate;
-          animation: animation1shape1 0.5s ease 0s infinite alternate;
-}
-
-@-webkit-keyframes animation1shape1 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(16px, 16px);
-            transform: translate(16px, 16px);
-  }
-}
-
-@keyframes animation1shape1 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(16px, 16px);
-            transform: translate(16px, 16px);
-  }
-}
-.loading1 .shape2 {
-  -webkit-animation: animation1shape2 0.5s ease 0s infinite alternate;
-          animation: animation1shape2 0.5s ease 0s infinite alternate;
-}
-
-@-webkit-keyframes animation1shape2 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(-16px, 16px);
-            transform: translate(-16px, 16px);
-  }
-}
-
-@keyframes animation1shape2 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(-16px, 16px);
-            transform: translate(-16px, 16px);
-  }
-}
-.loading1 .shape3 {
-  -webkit-animation: animation1shape3 0.5s ease 0s infinite alternate;
-          animation: animation1shape3 0.5s ease 0s infinite alternate;
-}
-
-@-webkit-keyframes animation1shape3 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(16px, -16px);
-            transform: translate(16px, -16px);
-  }
-}
-
-@keyframes animation1shape3 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(16px, -16px);
-            transform: translate(16px, -16px);
-  }
-}
-.loading1 .shape4 {
-  -webkit-animation: animation1shape4 0.5s ease 0s infinite alternate;
-          animation: animation1shape4 0.5s ease 0s infinite alternate;
-}
-
-@-webkit-keyframes animation1shape4 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(-16px, -16px);
-            transform: translate(-16px, -16px);
-  }
-}
-
-@keyframes animation1shape4 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(-16px, -16px);
-            transform: translate(-16px, -16px);
-  }
-}
-
-
-</style>

+ 0 - 170
components/echarts/qiun-loading/loading2.vue

@@ -1,170 +0,0 @@
-<template>
-	 <view class="container loading2">
-		<view class="shape shape1"></view>
-		<view class="shape shape2"></view>
-		<view class="shape shape3"></view>
-		<view class="shape shape4"></view>
-	</view>
-</template>
-
-<script>
-	export default {
-		name: 'loading2',
-		data() {
-			return {
-				
-			};
-		}
-	}
-</script>
-
-<style scoped="true">
-.container {
-  width: 30px;
-  height: 30px;
-  position: relative;
-}
-
-.container.loading2 {
-  -webkit-transform: rotate(10deg);
-          transform: rotate(10deg);
-}
-.container.loading2 .shape {
-  border-radius: 5px;
-}
-.container.loading2{
-  -webkit-animation: rotation 1s infinite;
-          animation: rotation 1s infinite;
-}
-
-.container .shape {
-  position: absolute;
-  width: 10px;
-  height: 10px;
-  border-radius: 1px;
-}
-.container .shape.shape1 {
-  left: 0;
-  background-color: #1890FF;
-}
-.container .shape.shape2 {
-  right: 0;
-  background-color: #91CB74;
-}
-.container .shape.shape3 {
-  bottom: 0;
-  background-color: #FAC858;
-}
-.container .shape.shape4 {
-  bottom: 0;
-  right: 0;
-  background-color: #EE6666;
-}
-
-
-.loading2 .shape1 {
-  -webkit-animation: animation2shape1 0.5s ease 0s infinite alternate;
-          animation: animation2shape1 0.5s ease 0s infinite alternate;
-}
-
-@-webkit-keyframes animation2shape1 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(20px, 20px);
-            transform: translate(20px, 20px);
-  }
-}
-
-@keyframes animation2shape1 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(20px, 20px);
-            transform: translate(20px, 20px);
-  }
-}
-.loading2 .shape2 {
-  -webkit-animation: animation2shape2 0.5s ease 0s infinite alternate;
-          animation: animation2shape2 0.5s ease 0s infinite alternate;
-}
-
-@-webkit-keyframes animation2shape2 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(-20px, 20px);
-            transform: translate(-20px, 20px);
-  }
-}
-
-@keyframes animation2shape2 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(-20px, 20px);
-            transform: translate(-20px, 20px);
-  }
-}
-.loading2 .shape3 {
-  -webkit-animation: animation2shape3 0.5s ease 0s infinite alternate;
-          animation: animation2shape3 0.5s ease 0s infinite alternate;
-}
-
-@-webkit-keyframes animation2shape3 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(20px, -20px);
-            transform: translate(20px, -20px);
-  }
-}
-
-@keyframes animation2shape3 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(20px, -20px);
-            transform: translate(20px, -20px);
-  }
-}
-.loading2 .shape4 {
-  -webkit-animation: animation2shape4 0.5s ease 0s infinite alternate;
-          animation: animation2shape4 0.5s ease 0s infinite alternate;
-}
-
-@-webkit-keyframes animation2shape4 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(-20px, -20px);
-            transform: translate(-20px, -20px);
-  }
-}
-
-@keyframes animation2shape4 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(-20px, -20px);
-            transform: translate(-20px, -20px);
-  }
-}
-
-</style>

+ 0 - 173
components/echarts/qiun-loading/loading3.vue

@@ -1,173 +0,0 @@
-<template>
-	 <view class="container loading3">
-		<view class="shape shape1"></view>
-		<view class="shape shape2"></view>
-		<view class="shape shape3"></view>
-		<view class="shape shape4"></view>
-	</view>
-</template>
-
-<script>
-	export default {
-		name: 'loading3',
-		data() {
-			return {
-				
-			};
-		}
-	}
-</script>
-
-<style scoped="true">
-.container {
-  width: 30px;
-  height: 30px;
-  position: relative;
-}
-
- .container.loading3 {
-  -webkit-animation: rotation 1s infinite;
-          animation: rotation 1s infinite;
-}
-.container.loading3 .shape1 {
-  border-top-left-radius: 10px;
-}
-.container.loading3 .shape2 {
-  border-top-right-radius: 10px;
-}
-.container.loading3 .shape3 {
-  border-bottom-left-radius: 10px;
-}
-.container.loading3 .shape4 {
-  border-bottom-right-radius: 10px;
-}
-
-.container .shape {
-  position: absolute;
-  width: 10px;
-  height: 10px;
-  border-radius: 1px;
-}
-.container .shape.shape1 {
-  left: 0;
-  background-color: #1890FF;
-}
-.container .shape.shape2 {
-  right: 0;
-  background-color: #91CB74;
-}
-.container .shape.shape3 {
-  bottom: 0;
-  background-color: #FAC858;
-}
-.container .shape.shape4 {
-  bottom: 0;
-  right: 0;
-  background-color: #EE6666;
-}
-
-.loading3 .shape1 {
-  -webkit-animation: animation3shape1 0.5s ease 0s infinite alternate;
-          animation: animation3shape1 0.5s ease 0s infinite alternate;
-}
-
-@-webkit-keyframes animation3shape1 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(5px, 5px);
-            transform: translate(5px, 5px);
-  }
-}
-
-@keyframes animation3shape1 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(5px, 5px);
-            transform: translate(5px, 5px);
-  }
-}
-.loading3 .shape2 {
-  -webkit-animation: animation3shape2 0.5s ease 0s infinite alternate;
-          animation: animation3shape2 0.5s ease 0s infinite alternate;
-}
-
-@-webkit-keyframes animation3shape2 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(-5px, 5px);
-            transform: translate(-5px, 5px);
-  }
-}
-
-@keyframes animation3shape2 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(-5px, 5px);
-            transform: translate(-5px, 5px);
-  }
-}
-.loading3 .shape3 {
-  -webkit-animation: animation3shape3 0.5s ease 0s infinite alternate;
-          animation: animation3shape3 0.5s ease 0s infinite alternate;
-}
-
-@-webkit-keyframes animation3shape3 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(5px, -5px);
-            transform: translate(5px, -5px);
-  }
-}
-
-@keyframes animation3shape3 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(5px, -5px);
-            transform: translate(5px, -5px);
-  }
-}
-.loading3 .shape4 {
-  -webkit-animation: animation3shape4 0.5s ease 0s infinite alternate;
-          animation: animation3shape4 0.5s ease 0s infinite alternate;
-}
-
-@-webkit-keyframes animation3shape4 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(-5px, -5px);
-            transform: translate(-5px, -5px);
-  }
-}
-
-@keyframes animation3shape4 {
-  from {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  to {
-    -webkit-transform: translate(-5px, -5px);
-            transform: translate(-5px, -5px);
-  }
-}
-</style>

+ 0 - 222
components/echarts/qiun-loading/loading4.vue

@@ -1,222 +0,0 @@
-<template>
-	 <view class="container loading5">
-		<view class="shape shape1"></view>
-		<view class="shape shape2"></view>
-		<view class="shape shape3"></view>
-		<view class="shape shape4"></view>
-	</view>
-</template>
-
-<script>
-	export default {
-		name: 'loading5',
-		data() {
-			return {
-				
-			};
-		}
-	}
-</script>
-
-<style scoped="true">
-.container {
-  width: 30px;
-  height: 30px;
-  position: relative;
-}
-
-.container.loading5 .shape {
-  width: 15px;
-  height: 15px;
-}
-
-.container .shape {
-  position: absolute;
-  width: 10px;
-  height: 10px;
-  border-radius: 1px;
-}
-.container .shape.shape1 {
-  left: 0;
-  background-color: #1890FF;
-}
-.container .shape.shape2 {
-  right: 0;
-  background-color: #91CB74;
-}
-.container .shape.shape3 {
-  bottom: 0;
-  background-color: #FAC858;
-}
-.container .shape.shape4 {
-  bottom: 0;
-  right: 0;
-  background-color: #EE6666;
-}
-
-.loading5 .shape1 {
-  animation: animation5shape1 2s ease 0s infinite reverse;
-}
-
-@-webkit-keyframes animation5shape1 {
-  0% {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  25% {
-    -webkit-transform: translate(0, 15px);
-            transform: translate(0, 15px);
-  }
-  50% {
-    -webkit-transform: translate(15px, 15px);
-            transform: translate(15px, 15px);
-  }
-  75% {
-    -webkit-transform: translate(15px, 0);
-            transform: translate(15px, 0);
-  }
-}
-
-@keyframes animation5shape1 {
-  0% {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  25% {
-    -webkit-transform: translate(0, 15px);
-            transform: translate(0, 15px);
-  }
-  50% {
-    -webkit-transform: translate(15px, 15px);
-            transform: translate(15px, 15px);
-  }
-  75% {
-    -webkit-transform: translate(15px, 0);
-            transform: translate(15px, 0);
-  }
-}
-.loading5 .shape2 {
-  animation: animation5shape2 2s ease 0s infinite reverse;
-}
-
-@-webkit-keyframes animation5shape2 {
-  0% {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  25% {
-    -webkit-transform: translate(-15px, 0);
-            transform: translate(-15px, 0);
-  }
-  50% {
-    -webkit-transform: translate(-15px, 15px);
-            transform: translate(-15px, 15px);
-  }
-  75% {
-    -webkit-transform: translate(0, 15px);
-            transform: translate(0, 15px);
-  }
-}
-
-@keyframes animation5shape2 {
-  0% {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  25% {
-    -webkit-transform: translate(-15px, 0);
-            transform: translate(-15px, 0);
-  }
-  50% {
-    -webkit-transform: translate(-15px, 15px);
-            transform: translate(-15px, 15px);
-  }
-  75% {
-    -webkit-transform: translate(0, 15px);
-            transform: translate(0, 15px);
-  }
-}
-.loading5 .shape3 {
-  animation: animation5shape3 2s ease 0s infinite reverse;
-}
-
-@-webkit-keyframes animation5shape3 {
-  0% {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  25% {
-    -webkit-transform: translate(15px, 0);
-            transform: translate(15px, 0);
-  }
-  50% {
-    -webkit-transform: translate(15px, -15px);
-            transform: translate(15px, -15px);
-  }
-  75% {
-    -webkit-transform: translate(0, -15px);
-            transform: translate(0, -15px);
-  }
-}
-
-@keyframes animation5shape3 {
-  0% {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  25% {
-    -webkit-transform: translate(15px, 0);
-            transform: translate(15px, 0);
-  }
-  50% {
-    -webkit-transform: translate(15px, -15px);
-            transform: translate(15px, -15px);
-  }
-  75% {
-    -webkit-transform: translate(0, -15px);
-            transform: translate(0, -15px);
-  }
-}
-.loading5 .shape4 {
-  animation: animation5shape4 2s ease 0s infinite reverse;
-}
-
-@-webkit-keyframes animation5shape4 {
-  0% {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  25% {
-    -webkit-transform: translate(0, -15px);
-            transform: translate(0, -15px);
-  }
-  50% {
-    -webkit-transform: translate(-15px, -15px);
-            transform: translate(-15px, -15px);
-  }
-  75% {
-    -webkit-transform: translate(-15px, 0);
-            transform: translate(-15px, 0);
-  }
-}
-
-@keyframes animation5shape4 {
-  0% {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  25% {
-    -webkit-transform: translate(0, -15px);
-            transform: translate(0, -15px);
-  }
-  50% {
-    -webkit-transform: translate(-15px, -15px);
-            transform: translate(-15px, -15px);
-  }
-  75% {
-    -webkit-transform: translate(-15px, 0);
-            transform: translate(-15px, 0);
-  }
-}
-
-</style>

+ 0 - 229
components/echarts/qiun-loading/loading5.vue

@@ -1,229 +0,0 @@
-<template>
-	 <view class="container loading6">
-		<view class="shape shape1"></view>
-		<view class="shape shape2"></view>
-		<view class="shape shape3"></view>
-		<view class="shape shape4"></view>
-	</view>
-</template>
-
-<script>
-	export default {
-		name: 'loading6',
-		data() {
-			return {
-				
-			};
-		}
-	}
-</script>
-<style scoped="true">
-.container {
-  width: 30px;
-  height: 30px;
-  position: relative;
-}
-
-.container.loading6 {
-  -webkit-animation: rotation 1s infinite;
-          animation: rotation 1s infinite;
-}
-.container.loading6 .shape {
-  width: 12px;
-  height: 12px;
-  border-radius: 2px;
-}
-.container .shape {
-  position: absolute;
-  width: 10px;
-  height: 10px;
-  border-radius: 1px;
-}
-.container .shape.shape1 {
-  left: 0;
-  background-color: #1890FF;
-}
-.container .shape.shape2 {
-  right: 0;
-  background-color: #91CB74;
-}
-.container .shape.shape3 {
-  bottom: 0;
-  background-color: #FAC858;
-}
-.container .shape.shape4 {
-  bottom: 0;
-  right: 0;
-  background-color: #EE6666;
-}
-
-
-.loading6 .shape1 {
-  -webkit-animation: animation6shape1 2s linear 0s infinite normal;
-          animation: animation6shape1 2s linear 0s infinite normal;
-}
-
-@-webkit-keyframes animation6shape1 {
-  0% {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  25% {
-    -webkit-transform: translate(0, 18px);
-            transform: translate(0, 18px);
-  }
-  50% {
-    -webkit-transform: translate(18px, 18px);
-            transform: translate(18px, 18px);
-  }
-  75% {
-    -webkit-transform: translate(18px, 0);
-            transform: translate(18px, 0);
-  }
-}
-
-@keyframes animation6shape1 {
-  0% {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  25% {
-    -webkit-transform: translate(0, 18px);
-            transform: translate(0, 18px);
-  }
-  50% {
-    -webkit-transform: translate(18px, 18px);
-            transform: translate(18px, 18px);
-  }
-  75% {
-    -webkit-transform: translate(18px, 0);
-            transform: translate(18px, 0);
-  }
-}
-.loading6 .shape2 {
-  -webkit-animation: animation6shape2 2s linear 0s infinite normal;
-          animation: animation6shape2 2s linear 0s infinite normal;
-}
-
-@-webkit-keyframes animation6shape2 {
-  0% {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  25% {
-    -webkit-transform: translate(-18px, 0);
-            transform: translate(-18px, 0);
-  }
-  50% {
-    -webkit-transform: translate(-18px, 18px);
-            transform: translate(-18px, 18px);
-  }
-  75% {
-    -webkit-transform: translate(0, 18px);
-            transform: translate(0, 18px);
-  }
-}
-
-@keyframes animation6shape2 {
-  0% {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  25% {
-    -webkit-transform: translate(-18px, 0);
-            transform: translate(-18px, 0);
-  }
-  50% {
-    -webkit-transform: translate(-18px, 18px);
-            transform: translate(-18px, 18px);
-  }
-  75% {
-    -webkit-transform: translate(0, 18px);
-            transform: translate(0, 18px);
-  }
-}
-.loading6 .shape3 {
-  -webkit-animation: animation6shape3 2s linear 0s infinite normal;
-          animation: animation6shape3 2s linear 0s infinite normal;
-}
-
-@-webkit-keyframes animation6shape3 {
-  0% {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  25% {
-    -webkit-transform: translate(18px, 0);
-            transform: translate(18px, 0);
-  }
-  50% {
-    -webkit-transform: translate(18px, -18px);
-            transform: translate(18px, -18px);
-  }
-  75% {
-    -webkit-transform: translate(0, -18px);
-            transform: translate(0, -18px);
-  }
-}
-
-@keyframes animation6shape3 {
-  0% {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  25% {
-    -webkit-transform: translate(18px, 0);
-            transform: translate(18px, 0);
-  }
-  50% {
-    -webkit-transform: translate(18px, -18px);
-            transform: translate(18px, -18px);
-  }
-  75% {
-    -webkit-transform: translate(0, -18px);
-            transform: translate(0, -18px);
-  }
-}
-.loading6 .shape4 {
-  -webkit-animation: animation6shape4 2s linear 0s infinite normal;
-          animation: animation6shape4 2s linear 0s infinite normal;
-}
-
-@-webkit-keyframes animation6shape4 {
-  0% {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  25% {
-    -webkit-transform: translate(0, -18px);
-            transform: translate(0, -18px);
-  }
-  50% {
-    -webkit-transform: translate(-18px, -18px);
-            transform: translate(-18px, -18px);
-  }
-  75% {
-    -webkit-transform: translate(-18px, 0);
-            transform: translate(-18px, 0);
-  }
-}
-
-@keyframes animation6shape4 {
-  0% {
-    -webkit-transform: translate(0, 0);
-            transform: translate(0, 0);
-  }
-  25% {
-    -webkit-transform: translate(0, -18px);
-            transform: translate(0, -18px);
-  }
-  50% {
-    -webkit-transform: translate(-18px, -18px);
-            transform: translate(-18px, -18px);
-  }
-  75% {
-    -webkit-transform: translate(-18px, 0);
-            transform: translate(-18px, 0);
-  }
-}
-</style>

+ 0 - 36
components/echarts/qiun-loading/qiun-loading.vue

@@ -1,36 +0,0 @@
-<template>
-	<view>
-	 <Loading1 v-if="loadingType==1"/>
-	 <Loading2 v-if="loadingType==2"/>
-	 <Loading3 v-if="loadingType==3"/>
-	 <Loading4 v-if="loadingType==4"/>
-	 <Loading5 v-if="loadingType==5"/>
-	</view>
-</template>
-
-<script>
-	import Loading1 from "./loading1.vue";
-	import Loading2 from "./loading2.vue";
-	import Loading3 from "./loading3.vue";
-	import Loading4 from "./loading4.vue";
-	import Loading5 from "./loading5.vue";
-	export default {
-		components:{Loading1,Loading2,Loading3,Loading4,Loading5},
-		name: 'qiun-loading',
-		props: {
-			loadingType: {
-				type: Number,
-				default: 2
-			},
-		},
-		data() {
-			return {
-				
-			};
-		},
-	}
-</script>
-
-<style>
-
-</style>

+ 8 - 45
package-lock.json

@@ -5,59 +5,22 @@
   "requires": true,
   "dependencies": {
     "@dcloudio/types": {
-      "version": "2.4.4",
-      "resolved": "https://registry.npmjs.org/@dcloudio/types/-/types-2.4.4.tgz",
-      "integrity": "sha512-EzUqM5uDg3ZauhnDj9bJp8OVrO/J1JHRktrO3kkoEtK6sECU/F87GtVVpVActyKAMLOSw65yIfeF17Yl+ctaNA=="
-    },
-    "hotp-totp-generator": {
-      "version": "1.1.3",
-      "resolved": "https://registry.nlark.com/hotp-totp-generator/download/hotp-totp-generator-1.1.3.tgz",
-      "integrity": "sha1-OrB48CnCqFhYjp9K8RS+ujwxOIM=",
-      "requires": {
-        "moment": "^2.19.3",
-        "pad-component": "0.0.1"
-      }
-    },
-    "jssha": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/jssha/-/jssha-3.2.0.tgz",
-      "integrity": "sha512-QuruyBENDWdN4tZwJbQq7/eAK85FqrI4oDbXjy5IBhYD+2pTJyBUWZe8ctWaCkrV0gy6AaelgOZZBMeswEa/6Q=="
+      "version": "2.5.6",
+      "resolved": "https://registry.nlark.com/@dcloudio/types/download/@dcloudio/types-2.5.6.tgz",
+      "integrity": "sha1-+6kMwEIOEj6rCeiV1brOsLc+uyE="
     },
     "luch-request": {
-      "version": "3.0.6",
-      "resolved": "https://registry.npmjs.org/luch-request/-/luch-request-3.0.6.tgz",
-      "integrity": "sha512-Usv3fcQzrPL1RsVVLTJjPrzyv7NGxPTXoz+hnmq/bDPpcQeKApD2iCSHjxey0zbA6LPB6MbPZL9zBtLKl+GznA==",
+      "version": "3.0.7",
+      "resolved": "https://registry.nlark.com/luch-request/download/luch-request-3.0.7.tgz",
+      "integrity": "sha1-WJT8PT/2JiKwQshJU8EZLM3q+qU=",
       "requires": {
         "@dcloudio/types": "^2.0.16"
       }
     },
-    "moment": {
-      "version": "2.29.1",
-      "resolved": "https://registry.nlark.com/moment/download/moment-2.29.1.tgz",
-      "integrity": "sha1-sr52n6MZQL6e7qZGnAdeNQBvo9M="
-    },
-    "pad-component": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npm.taobao.org/pad-component/download/pad-component-0.0.1.tgz",
-      "integrity": "sha1-rR8izhvw/cDW3dkIrxfzUaQEuKw="
-    },
-    "tki-qrcode": {
-      "version": "0.1.6",
-      "resolved": "https://registry.npmjs.org/tki-qrcode/-/tki-qrcode-0.1.6.tgz",
-      "integrity": "sha512-EnnlS8psowC7PsW3MDYcxvJYkuklX3WAZ/BYanR4TdBHTu74GfjTBX8Y16REP+AeDENiVtBPh4jtTRL2P736hQ=="
-    },
-    "totp-generator": {
-      "version": "0.0.9",
-      "resolved": "https://registry.npmjs.org/totp-generator/-/totp-generator-0.0.9.tgz",
-      "integrity": "sha512-ZrUn747mg6+FabOeUKP4neX2oUm69HjV1484kDYvtLfcGlO4XAq5xFwet/KJW7+zc2b09puTKCKMeTxzxfaKjw==",
-      "requires": {
-        "jssha": "^3.1.2"
-      }
-    },
     "uview-ui": {
       "version": "1.8.4",
-      "resolved": "https://registry.npmjs.org/uview-ui/-/uview-ui-1.8.4.tgz",
-      "integrity": "sha512-Zr+D5dFdNy6CdHQLBEXeGq/w0LkjxzBtsgaaDwLl0P58g67H7fBBDvy6AzWK/k0c7dwPYMBiK7I4Tr9p92+0DA=="
+      "resolved": "https://registry.npm.taobao.org/uview-ui/download/uview-ui-1.8.4.tgz",
+      "integrity": "sha1-4yu/I3lCHTGQIuMk4ct7U4fTvUQ="
     }
   }
 }

+ 0 - 3
package.json

@@ -10,10 +10,7 @@
   "author": "",
   "license": "ISC",
   "dependencies": {
-    "hotp-totp-generator": "^1.1.3",
     "luch-request": "^3.0.6",
-    "tki-qrcode": "^0.1.6",
-    "totp-generator": "0.0.9",
     "uview-ui": "^1.8.4"
   }
 }

+ 239 - 163
pages.json

@@ -1,8 +1,15 @@
 {
+	/**
+	 * pagesA ==> tarbar mine下的页面分包
+	 * 
+	 * pagesB ==> agente 代理分包
+	 * 
+	 * pagesC ==> pay 支付交易的分包
+	 */
 	"easycom": {
 		"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
 	},
-	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+	"pages": [
 
 		{
 			"path": "pages/mine/mine",
@@ -27,222 +34,291 @@
 				"navigationBarTitleText": "花积分"
 			}
 		}, {
-			"path": "pages/mine/setting/setting",
+			"path": "pages/search/search",
 			"style": {
-				"navigationBarTitleText": "设置",
+				"navigationBarTitleText": "搜索",
 				"enablePullDownRefresh": false
 			}
 
 		}, {
-			"path": "pages/mine/setting/pay-setting",
+			"path": "pages/webview/webview",
 			"style": {
-				"navigationBarTitleText": "支付设置",
+				"navigationBarTitleText": "",
 				"enablePullDownRefresh": false
 			}
 
 		}, {
-			"path": "pages/mine/my-points/my-points",
+			"path": "pages/consume/detail/shop-detail",
 			"style": {
-				"navigationBarTitleText": "积分资产",
+				"navigationBarTitleText": "商户详情",
 				"enablePullDownRefresh": false
 			}
 
-		}, {
-			"path": "pages/mine/my-points/accredit",
+		},
+		{
+			"path": "pages/activity/activity",
 			"style": {
-				"navigationBarTextStyle": "white",
-				"navigationBarBackgroundColor": "#FF9447",
-				"navigationBarTitleText": "积分授权",
+				"navigationBarTitleText": "活动列表",
 				"enablePullDownRefresh": false
 			}
 
 		}, {
-			"path": "pages/mine/my-balance/my-balance",
+			"path": "pages/activity/detail",
 			"style": {
-				"navigationBarTitleText": "余额",
-				"enablePullDownRefresh": false
+				"navigationBarTitleText": "活动详情",
+				"enablePullDownRefresh": false,
+				"navigationStyle": "custom"
 			}
 
 		}, {
-			"path": "pages/mine/bill-detail",
+			"path": "pages/consume/search/search",
 			"style": {
-				"navigationBarTitleText": "账单详情",
+				"navigationBarTitleText": "搜索",
 				"enablePullDownRefresh": false
 			}
 
 		}, {
-			"path": "pages/mine/my-points/exchange",
+			"path": "pages/consume/search/list",
 			"style": {
-				"navigationBarTitleText": "积分通兑",
+				"navigationBarTitleText": "搜索结果",
 				"enablePullDownRefresh": false
 			}
 
-		}, {
-			"path": "pages/checkstand/index",
-			"style": {
-				"navigationStyle": "custom",
-				"navigationBarTitleText": "收银台",
-				"enablePullDownRefresh": false
-			}
+		}
+	],
+	"subPackages": [{
+			//tarbar下mine的分包
+			"root": "pagesA/",
+			"pages": [{
+				"path": "pages/my-balance/statistics",
+				"style": {
+					"navigationBarTitleText": "账单统计"
+				}
+			}, {
+				"path": "pages/my-balance/my-balance",
+				"style": {
+					"navigationBarTitleText": "积分余额",
+					"enablePullDownRefresh": false
+				}
 
-		}, {
-			"path": "pages/checkstand/order-res",
-			"style": {
-				"navigationBarTitleText": "支付结果",
-				"enablePullDownRefresh": false
-			}
+			}, {
+				"path": "pages/setting/pay-setting",
+				"style": {
+					"navigationBarTitleText": "支付设置",
+					"enablePullDownRefresh": false
+				}
 
-		}, {
-			"path": "pages/checkstand/pay-qrcode",
-			"style": {
-				"navigationBarTitleText": "付款码",
-				"navigationBarTextStyle": "white",
-				"navigationBarBackgroundColor": "#18b566",
-				"enablePullDownRefresh": false
-			}
-		}, {
-			"path": "pages/search/search",
-			"style": {
-				"navigationBarTitleText": "搜索",
-				"enablePullDownRefresh": false
-			}
+			}, {
+				"path": "pages/setting/question",
+				"style": {
+					"navigationBarTitleText": "问题反馈",
+					"enablePullDownRefresh": false
+				}
+			}, {
+				"path": "pages/setting/setting",
+				"style": {
+					"navigationBarTitleText": "设置",
+					"enablePullDownRefresh": false
+				}
 
-		}, {
-			"path": "pages/agent/agent",
-			"style": {
-				"navigationBarTitleText": "填写资料"
-			}
+			}, {
+				"path": "pages/myInfo",
+				"style": {
+					"navigationBarTitleText": "我的资料",
+					"enablePullDownRefresh": false
+				}
 
-		}, {
-			"path": "pages/agent/pay",
-			"style": {
-				"navigationBarTitleText": "支付页面"
-			}
+			}, {
+				"path": "pages/my-points/accredit",
+				"style": {
+					"navigationBarTextStyle": "white",
+					"navigationBarBackgroundColor": "#FF9447",
+					"navigationBarTitleText": "积分授权",
+					"enablePullDownRefresh": false
+				}
 
-		}, {
-			"path": "pages/agent/promote",
-			"style": {
-				"navigationBarTitleText": "去推广"
-			}
+			}, {
+				"path": "pages/my-points/my-points",
+				"style": {
+					"navigationBarTitleText": "积分资产",
+					"enablePullDownRefresh": false
+				}
 
-		}, {
-			"path": "pages/agent/myPromote",
-			"style": {
-				"navigationBarTitleText": "我的推广"
-			}
+			}, {
+				"path": "pages/my-points/exchange",
+				"style": {
+					"navigationBarTitleText": "积分通兑",
+					"enablePullDownRefresh": false
+				}
+			}, {
+				"path": "pages/bill/bill-detail",
+				"style": {
+					"navigationBarTitleText": "账单详情",
+					"enablePullDownRefresh": false
+				}
 
+			}]
 		}, {
-			"path": "pages/agent/myIncome/myIncome",
-			"style": {
-				"navigationBarTitleText": "我的收益"
-			}
+			//tarbar下gain 代理的分包
+			"root": "pagesB/",
+			"pages": [{
+					"path": "pages/agent/agent",
+					"style": {
+						"navigationBarTitleText": "填写资料"
+					}
 
-		}, {
-			"path": "pages/agent/myIncome/todayIncome",
-			"style": {
-				"navigationBarTitleText": "今日收益"
-			}
+				}, {
+					"path": "pages/agent/pay",
+					"style": {
+						"navigationBarTitleText": "支付页面"
+					}
 
-		}, {
-			"path": "pages/agent/myIncome/detail",
-			"style": {
-				"navigationBarTitleText": "收益详情"
-			}
+				}, {
+					"path": "pages/agent/promote",
+					"style": {
+						"navigationBarTitleText": "去推广"
+					}
 
-		}, {
-			"path": "pages/agent/agentDetail",
-			"style": {
-				"navigationStyle": "custom",
-				"navigationBarTitleText": ""
-			}
+				}, {
+					"path": "pages/agent/myPromote",
+					"style": {
+						"navigationBarTitleText": "我的推广"
+					}
 
-		}, {
-			"path": "pages/webview/webview",
-			"style": {
-				"navigationBarTitleText": "",
-				"enablePullDownRefresh": false
-			}
+				}, {
+					"path": "pages/agent/myIncome/myIncome",
+					"style": {
+						"navigationBarTitleText": "我的收益"
+					}
 
-		}, {
-			"path": "pages/userBank/userBank",
-			"style": {
-				"navigationBarTitleText": "银行",
-				"enablePullDownRefresh": false
-			}
+				}, {
+					"path": "pages/agent/myIncome/todayIncome",
+					"style": {
+						"navigationBarTitleText": "今日收益"
+					}
 
-		}, {
-			"path": "pages/userBank/add",
-			"style": {
-				"navigationBarTitleText": "添加银行",
-				"enablePullDownRefresh": false
-			}
+				}, {
+					"path": "pages/agent/myIncome/detail",
+					"style": {
+						"navigationBarTitleText": "收益详情"
+					}
 
-		}, {
-			"path": "pages/mine/myInfo",
-			"style": {
-				"navigationBarTitleText": "我的资料",
-				"enablePullDownRefresh": false
-			}
+				}, {
+					"path": "pages/agent/agentDetail",
+					"style": {
+						"navigationStyle": "custom",
+						"navigationBarTitleText": ""
+					}
 
-		}, {
-			"path": "pages/mine/setting/question",
-			"style": {
-				"navigationBarTitleText": "问题反馈",
-				"enablePullDownRefresh": false
-			}
-		}, {
-			"path": "pages/consume/detail/shop-detail",
-			"style": {
-				"navigationBarTitleText": "商户详情",
-				"enablePullDownRefresh": false
-			}
+				},
+				{
+					"path": "pages/agent/createPoster",
+					"style": {
+						"navigationBarTitleText": "生成海报",
+						"enablePullDownRefresh": false
+					}
 
-		},
-		{
-			"path": "pages/activity/activity",
-			"style": {
-				"navigationBarTitleText": "活动列表",
-				"enablePullDownRefresh": false
-			}
+				}, {
+					"path": "pages/shop-detail",
+					"style": {
+						"navigationBarTitleText": "商户详情",
+						"enablePullDownRefresh": false
+					}
 
-		}, {
-			"path": "pages/activity/detail",
-			"style": {
-				"navigationBarTitleText": "活动详情",
-				"enablePullDownRefresh": false,
-				"navigationStyle": "custom"
-			}
+				},
+				{
+					"path": "pages/withdraw/select-bank",
+					"style": {
+						"navigationBarTitleText": "选择银行卡",
+						"enablePullDownRefresh": false
+					}
+				},
+				{
+					"path": "pages/withdraw/withdraw",
+					"style": {
+						"navigationBarTitleText": "提现",
+						"enablePullDownRefresh": false
+					}
+				},
+				{
+					"path": "pages/userBank/userBank",
+					"style": {
+						"navigationBarTitleText": "我的银行卡",
+						"enablePullDownRefresh": false
+					}
+				},
+				{
+					"path": "pages/userBank/add",
+					"style": {
+						"navigationBarTitleText": "我的银行卡",
+						"enablePullDownRefresh": false
+					}
+				}, {
+					"path": "pages/withdraw/records",
+					"style": {
+						"navigationBarTitleText": "提现记录",
+						"enablePullDownRefresh": false
+					}
 
+				}
+			]
 		}, {
-			"path": "pages/checkstand/pay-qrcode1",
-			"style": {
-				"navigationBarTitleText": "付款码",
-				"navigationBarTextStyle": "white",
-				"navigationBarBackgroundColor": "#18b566",
-				"enablePullDownRefresh": false
-			}
+			//支付特定包,扫码支付,付款码支付
+			"root": "pagesC/",
+			"pages": [{
+				"path": "pages/checkstand/index",
+				"style": {
+					"navigationStyle": "custom",
+					"navigationBarTitleText": "收银台",
+					"enablePullDownRefresh": false
+				}
 
+			}, {
+				"path": "pages/checkstand/order-res",
+				"style": {
+					"navigationBarTitleText": "支付结果",
+					"enablePullDownRefresh": false
+				}
+
+			}, {
+				"path": "pages/checkstand/pay-qrcode",
+				"style": {
+					"navigationBarTitleText": "付款码",
+					"navigationBarTextStyle": "white",
+					"navigationBarBackgroundColor": "#18b566",
+					"enablePullDownRefresh": false
+				}
+			}, {
+				"path": "pages/checkstand/pay-result",
+				"style": {
+					"navigationStyle": "custom",
+					"navigationBarTitleText": "支付结果",
+					"enablePullDownRefresh": false
+				}
+
+			}, {
+				"path": "pages/checkstand/pay-qrcode1",
+				"style": {
+					"navigationBarTitleText": "付款码",
+					"navigationBarTextStyle": "white",
+					"navigationBarBackgroundColor": "#18b566",
+					"enablePullDownRefresh": false
+				}
+
+			}]
 		}
-	    ,{
-            "path" : "pages/checkstand/pay-result",
-            "style" :                                                                                    
-            {
-				"navigationStyle":"custom",
-                "navigationBarTitleText": "支付结果",
-                "enablePullDownRefresh": false
-            }
-            
-        }
-        ,{
-            "path" : "pages/mine/my-balance/statistics",
-            "style" :                                                                                    
-            {
-                "navigationBarTitleText": "账单统计",
-                "enablePullDownRefresh": false
-            }
-            
-        }
-    ],
+
+	],
+	"preloadRule": {
+		"pages/gain/gain": {
+			"network": "all",
+			"packages": ["pagesB"]
+		},
+		"pages/mine/mine": {
+			"network": "all",
+			"packages": ["pagesA", "pagesC"]
+		}
+	},
 	"tabBar": {
 		"color": "#555555",
 		"backgroundColor": "#FFFFFF",

+ 0 - 22
pages/agent/myIncome/myIncome.vue

@@ -1,22 +0,0 @@
-<template>
-	<view>
-		
-	</view>
-</template>
-
-<script>
-	export default {
-		data() {
-			return {
-				
-			}
-		},
-		methods: {
-			
-		}
-	}
-</script>
-
-<style>
-
-</style>

+ 0 - 72
pages/agent/myPromote.vue

@@ -1,72 +0,0 @@
-<template>
-	<view>
-		<view class="search">
-			<u-search border-color="#fff" bg-color="#fff" placeholder="输入商家/商场/用户名称" v-model="keyword"></u-search>
-		</view>
-		<view class="content">
-			<view class="flex text-bold" style="padding:0 20rpx 0 40rpx">
-				<view class="area1">
-					加入时间
-				</view>
-				<view class="area2">
-					名称
-				</view>
-				<view class="area3">
-					推广类型
-				</view>
-			</view>
-			<view class="item" v-for="(item,index) in 4" :key="index">
-				<view class="area1 flex flex-direction justify-end padding-left-20" >
-					2021-08-10 18:10:32
-				</view>
-				<view class="area2">
-					黄明潘
-				</view>
-				<view class="area3">
-					推广商户
-				</view>
-			</view>
-		</view>
-	</view>
-</template>
-
-<script>
-	export default {
-		data() {
-			return {
-				
-			}
-		},
-		methods: {
-
-		}
-	}
-</script>
-
-<style lang="scss" scoped>
-	.search{
-		padding: 40rpx 30rpx 50rpx;
-	}
-	
-	.content{
-		.area1 {
-			width: 50%;
-		}
-		
-		.area2 {
-			width: 30%;
-		}
-		
-		.area3 {
-			width: 20%;
-		}
-		
-		.item{
-			margin-top: 20rpx;
-			background-color: #FFFFFF;
-			display: flex;
-			padding-top: 20rpx;
-			padding-bottom: 20rpx;
-		}
-	}
-</style>

+ 0 - 21
pages/agent/promote.vue

@@ -1,21 +0,0 @@
-<template>
-	<view>
-		
-	</view>
-</template>
-
-<script>
-	export default {
-		data() {
-			return {
-			}
-		},
-		methods: {
-			
-		}
-	}
-</script>
-
-<style>
-
-</style>

+ 3 - 7
pages/consume/comps/card1.vue

@@ -27,7 +27,7 @@
 							</block>
 							<block>
 								<text>月售</text>
-								<text>{{item.sales==-1?0:item.sales}}</text>
+								<text class="margin-left-10">{{item.sales==-1?0:item.sales}}</text>
 							</block>
 						</view>
 						<view>
@@ -41,12 +41,6 @@
 							{{item1}}
 						</view>
 					</view>
-					<view v-if="false" class="flex justify-start text-cut-2"
-						style="margin-top: 12rpx;width: 100%;height: 34rpx;">
-						<view class="line-red sm cu-tag" v-for="(item,index) in 5" :key="index">
-							首次光临减2元
-						</view>
-					</view>
 				</view>
 			</view>
 		</view>
@@ -125,6 +119,7 @@
 			.title {
 				font-size: 28rpx;
 				font-weight: 800;
+				
 			}
 	
 			.tag {
@@ -180,6 +175,7 @@
 				.title {
 					font-size: 30rpx;
 					font-weight: 800;
+					margin-bottom: 30rpx;
 				}
 	
 				.data {

+ 1 - 1
pages/consume/comps/tabsSwiper.vue

@@ -11,7 +11,7 @@
 				<text>保利中心</text>
 				<u-icon name="arrow-down-fill" size="20"></u-icon>
 			</view> -->
-			<view class="search-form" @click="$jump('/pages/search/search')">
+			<view class="search-form" @click="$jump('/pages/consume/search/search')">
 				<text class="cuIcon-search"></text>
 				<text class="margin-left-20">请输入搜索内容</text>
 			</view>

+ 132 - 0
pages/consume/search/comps/search-list.vue

@@ -0,0 +1,132 @@
+<template>
+	<view>
+		<view class="item">
+			<view class="card" @click="goDetail(item.id)" v-for="(item,index) in list" :key="index">
+				<view class="left">
+					<image :src="item.cover" mode=""></image>
+				</view>
+				<view class="right">
+					<view class="title text-cut-1">
+						<text class="">{{item.name}}</text>
+					</view>
+					<view class="data">
+						<view class="">
+							<block>
+								<u-icon name="star-fill" color="#ff7001" size="30"></u-icon>
+								<text
+									style="margin-left: 4rpx;margin-right: 20rpx;color: #ff7001;font-size: 26rpx;">{{item.score}}</text>
+							</block>
+							<block>
+								<text>月售</text>
+								<text class="margin-left-10">{{item.sales==-1?0:item.sales}}</text>
+							</block>
+						</view>
+						<view style="margin-right: 30rpx;">
+							<u-icon name="map-fill" color="#FF9447"></u-icon>
+							<text class="margin-left-10">{{distance(item.latitude,item.longitude)}} 米</text>
+						</view>
+					</view>
+					<view class="text-cut-2" style="width: 100%;height: 32rpx;margin-top: 14rpx;">
+						<view v-for="(item1,index1) in item.labelKey.split(',')" :key="index1" class="cu-tag sm "
+							style="background-color: #fff0d9;color: #ffa353;">
+							{{item1}}
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props:{
+			list:Array
+		},
+		data() {
+			return {
+				
+			}
+		},
+		computed:{
+			distance() {
+				return (lat1,lng1) => {
+					if (this.vuex_location!=null) {
+						let distance=this.$util.calculateDistance(lat1,lng1,this.vuex_location.latitude,this.vuex_location.longitude)
+						console.log(distance);
+						return distance
+					}else{
+						return 0
+					}
+					
+				};
+			},
+		},
+		methods: {
+			goDetail(id){
+				uni.navigateTo({
+					url:"/pages/consume/detail/shop-detail?id="+id
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.item {
+		display: flex;
+		flex-direction: column;
+		margin: 20rpx;
+		background-color: #FFFFFF;
+		border-bottom: 1rpx solid #EEEEEE;
+		padding-bottom: 10rpx;
+	
+		.card {
+			margin-bottom: 20rpx;
+			display: flex;
+			border-bottom: 1rpx solid #ececec;
+			
+			.left {
+				width: 30%;
+				
+				image {
+					width: 100%;
+					height: 180rpx;
+					border-radius: 12rpx;
+				}
+			}
+	
+			.right {
+				margin-left: 20rpx;
+				display: flex;
+				width: 70%;
+				flex-direction: column;
+	
+				.title {
+					margin-top: 6rpx;
+					margin-bottom: 30rpx;
+					font-size: 30rpx;
+					font-weight: 800;
+				}
+	
+				.data {
+					margin-top: 20rpx;
+					display: flex;
+					justify-content: space-between;
+					font-size: 24rpx;
+					color: #666666;
+				}
+	
+				.label {
+					margin-top: 10rpx;
+				}
+			}
+		}
+	
+		.card:last-child {
+			border: none;
+			margin-bottom: 0;
+			padding-bottom: 0;
+		}
+	}
+</style>

+ 135 - 0
pages/consume/search/list.vue

@@ -0,0 +1,135 @@
+<template>
+	<view>
+		<view class="item">
+			<view class="card" @click="goDetail(item.id)" v-for="(item,index) in list" :key="index">
+				<view class="left">
+					<image :src="item.cover" mode=""></image>
+				</view>
+				<view class="right">
+					<view class="title text-cut-1">
+						<text class="">{{item.name}}</text>
+					</view>
+					<view class="data">
+						<view class="">
+							<block>
+								<u-icon name="star-fill" color="#ff7001" size="30"></u-icon>
+								<text
+									style="margin-left: 4rpx;margin-right: 20rpx;color: #ff7001;font-size: 26rpx;">{{item.score}}</text>
+							</block>
+							<block>
+								<text>月售</text>
+								<text class="margin-left-10">{{item.sales==-1?0:item.sales}}</text>
+							</block>
+						</view>
+						<view style="margin-right: 30rpx;">
+							<u-icon name="map-fill" color="#FF9447"></u-icon>
+							<text class="margin-left-10">{{distance(item.latitude,item.longitude)}} 米</text>
+						</view>
+					</view>
+					<view class="text-cut-2" style="width: 100%;height: 32rpx;margin-top: 14rpx;">
+						<view v-for="(item1,index1) in item.labelKey.split(',')" :key="index1" class="cu-tag sm "
+							style="background-color: #fff0d9;color: #ffa353;">
+							{{item1}}
+						</view>
+					</view>
+					<view v-if="false" class="flex justify-start text-cut-2"
+						style="margin-top: 12rpx;width: 100%;height: 34rpx;">
+						<view class="line-red sm cu-tag" v-for="(item,index) in 5" :key="index">
+							首次光临减2元
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				list:[]
+			}
+		},
+		onLoad() {
+			this.fetchList()
+		},
+		computed:{
+			distance() {
+				return (lat1,lng1) => {
+					if (this.vuex_location!=null) {
+						let distance=this.$util.calculateDistance(lat1,lng1,this.vuex_location.latitude,this.vuex_location.longitude)
+						console.log(distance);
+						return distance
+					}else{
+						return 0
+					}
+					
+				};
+			},
+		},
+		methods: {
+			fetchList(){
+				this.$api.shop.list().then(res=>{
+					this.list=res.data.records
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.item {
+		display: flex;
+		flex-direction: column;
+		margin: 20rpx;
+		background-color: #FFFFFF;
+	
+		.card {
+			margin-bottom: 20rpx;
+			display: flex;
+			border-bottom: 1rpx solid #ececec;
+			
+			.left {
+				width: 30%;
+				
+				image {
+					width: 100%;
+					height: 180rpx;
+				}
+			}
+	
+			.right {
+				margin-left: 20rpx;
+				display: flex;
+				width: 70%;
+				flex-direction: column;
+	
+				.title {
+					margin-top: 10rpx;
+					margin-bottom: 20rpx;
+					font-size: 30rpx;
+					font-weight: 800;
+				}
+	
+				.data {
+					margin-top: 20rpx;
+					display: flex;
+					justify-content: space-between;
+					font-size: 24rpx;
+					color: #666666;
+				}
+	
+				.label {
+					margin-top: 10rpx;
+				}
+			}
+		}
+	
+		.card:last-child {
+			border: none;
+			margin-bottom: 0;
+			padding-bottom: 0;
+		}
+	}
+</style>

+ 172 - 0
pages/consume/search/search.vue

@@ -0,0 +1,172 @@
+<template>
+	<view class="bg-white" style="min-height: 100vh;">
+		<view class="cu-bar bg-white  ">
+			<view class="search-form round">
+				<text class="cuIcon-search" />
+				<input tyle="font-size: 34rpx;" v-model="searchKey" :adjust-position="false" type="text"
+					placeholder="请输入搜索内容" confirm-type="search" />
+				<text class="cuIcon-roundclose" style="font-size: 34rpx;color: #6c6c6c;" v-if="searchClose"
+					@click="clear" />
+			</view>
+			<view class="action" @click="bindsearch">
+				<view class="cu-btn round sm" style="background-color: #f0b363;padding: 28rpx 36rpx;">
+					搜 索
+				</view>
+			</view>
+		</view>
+		<view v-if="searchKey.length<=0||datalist==null">
+			<view class="search">
+				<view class="top">
+					<text class="left">历史搜索</text>
+					<view class="center" v-if="!$isEmpty(oldKeywordList)">
+						<text @click="delOldKeywordList" class="cuIcon-delete text-gray"
+							style="font-size: 34rpx;"></text>
+					</view>
+				</view>
+				<view class="list">
+					<view class="cu-tag "
+						@click="searchKey=item"
+						style="background-color: #f3f3f4;color: #6c6c6c;padding: 16rpx 26rpx;border-radius: 4rpx;margin-bottom: 16rpx;"
+						v-for="(item,index) in oldKeywordList" :key="index">
+						{{item}}
+					</view>
+				</view>
+			</view>
+		</view>
+		
+		<view class="" v-else>
+			<search-list  :list="datalist"></search-list>
+		</view>
+	</view>
+</template>
+
+<script>
+	import searchList from "./comps/search-list.vue"
+	export default {
+		components:{
+			searchList
+		},
+		data() {
+			return {
+				searchKey: '',
+				oldKeywordList: [],
+				datalist: null
+			}
+		},
+		onLoad() {
+			this.loadOldKeyword();
+		},
+		computed: {
+			searchClose: {
+				get() {
+					if (this.$isEmpty(this.searchKey)) {
+						return false
+					} else {
+						return true
+					}
+				}
+			}
+		},
+		methods: {
+			clear(){
+				this.searchKey= ''
+				this.datalist=null
+			},
+			delOldKeywordList() {
+				this.oldKeywordList = [];
+				uni.removeStorage({
+					key: 'OldKeys'
+				});
+			},
+			bindsearch() {
+				if (this.$isEmpty(this.searchKey)) {
+					return
+				}
+				//搜索todo
+				this.fetchList()
+				this.saveKeyword()
+			},
+			//加载历史搜索,自动读取本地Storage
+			loadOldKeyword() {
+				uni.getStorage({
+					key: 'OldKeys',
+					success: res => {
+						var OldKeys = JSON.parse(res.data);
+						this.oldKeywordList = OldKeys;
+					}
+				});
+			},
+			//保存关键字到历史记录
+			saveKeyword() {
+				let keyword = this.searchKey
+				uni.getStorage({
+					key: 'OldKeys',
+					success: res => {
+						//获取历史搜索词
+						var OldKeys = JSON.parse(res.data);
+						var findIndex = OldKeys.indexOf(keyword);
+						if (findIndex == -1) {
+							//如果历史搜索词里没有该输入的搜索词,就添加进数组头
+							OldKeys.unshift(keyword);
+						} else {
+							//如果有,就删除该搜索词并插进数组头
+							OldKeys.splice(findIndex, 1);
+							OldKeys.unshift(keyword);
+						}
+						//最多10个纪录,如果超过十条记录,就弹出最后一个元素
+						OldKeys.length > 10 && OldKeys.pop();
+						uni.setStorage({
+							key: 'OldKeys',
+							data: JSON.stringify(OldKeys)
+						});
+						this.oldKeywordList = OldKeys; //更新历史搜索
+					},
+					fail: e => {
+						var OldKeys = [keyword];
+						uni.setStorage({
+							key: 'OldKeys',
+							data: JSON.stringify(OldKeys)
+						});
+						this.oldKeywordList = OldKeys; //更新历史搜索
+					}
+				});
+			},
+			fetchList(){
+				let params={
+					name:this.searchKey
+				}
+				this.$api.shop.list(params).then(res=>{
+					this.datalist=res.data.records
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	page {
+		background-color: #FFFFFF;
+	}
+
+	.search {
+		padding: 20rpx;
+		margin: 0rpx 20rpx;
+
+		.top {
+			display: flex;
+			justify-content: space-between;
+
+			.left {
+				font-size: 28rpx;
+				font-weight: 800;
+			}
+		}
+
+		.list {
+			padding-top: 30rpx;
+			display: flex;
+			flex-wrap: wrap;
+			justify-content: flex-start;
+		}
+	}
+</style>

+ 1 - 1
pages/gain/comps/card1.vue

@@ -57,7 +57,7 @@
 			}
 		},
 		created() {
-			this.getActivityList()
+			// this.getActivityList()
 		},
 		methods: {
 			getActivityList() {

+ 53 - 13
pages/gain/comps/card2.vue

@@ -26,26 +26,32 @@
 		<view class="page" v-else>
 			<view class="card">
 				<view class="top">
-					<image src="https://thirdwx.qlogo.cn/mmopen/vi_32/q6EwR5U6zFp8bYbUWb9HmFc5Q3R88x13Q0ZtOcVK5lO8AmtibQDRcuMA2Y7CmyuMCr6icceM3QibIKN2icAAxSvjpA/132" ></image>
-					<view class="center text-lg" style="margin-left: 20rpx;margin-right: 10rpx;">黄明潘</view>
+					<image :src="info.agenterAvatar" ></image>
+					<view class="center text-lg" style="margin-left: 20rpx;margin-right: 10rpx;">{{info.agenterName}}</view>
 					<view class="center margin-left-10">
 						<view class="cu-tag btn-bg-color sm" style="border-radius: 20rpx 20rpx 20rpx 0;padding: 10rpx;">
-							代理商
+							{{info.agenterType}}
 						</view>
 					</view>
 				</view>
 				<view class="bottom">
 					<view >
 						<text>今日收益</text>
-						<text class="text-price">108.00</text>
+						<text class="text-price">{{info.todayEarning}}</text>
 					</view>
-					<view style="margin: 0 50rpx;">
+					<view @click="$jump('/pagesB/pages/agent/myIncome/myIncome')" style="margin: 0 50rpx;">
 						<text>总收益</text>
-						<text class="text-price">888.00</text>
+						<text class="text-price">{{info.totalEarning}}</text>
 					</view>
-					<view >
+					<view @click="$jump('/pagesB/pages/agent/myPromote')">
 						<text>我的推广</text>
-						<text >98</text>
+						<text >{{info.promoteNum}}</text>
+					</view>
+				</view>
+				
+				<view @click="$jump('/pagesB/pages/withdraw/withdraw')" class="btn-withdraw">
+					<view class="cu-btn round sm btn" style="font-size: 26rpx;">
+						提现
 					</view>
 				</view>
 			</view>
@@ -73,27 +79,38 @@
 	export default{
 		data(){
 			return{
+				info:{},
 				menuList:[
 					{
 						icon:'/static/agent/topromote.png',
 						name:'去推广',
-						path:''
+						path:'/pagesB/pages/agent/promote'
 					},
 					{
 						icon:'/static/agent/mypromote.png',
 						name:'我的推广',
-						path:'/pages/agent/myPromote'
+						path:'/pagesB/pages/agent/myPromote'
 					},
 					{
 						icon:'/static/agent/myearnings.png',
 						name:'我的收益',
-						path:''
+						path:'/pagesB/pages/agent/myIncome/myIncome'
+					},
+					{
+						icon:'/static/icon/bank1.png',
+						name:'我的银行卡',
+						path:'/pagesB/pages/userBank/userBank'
+					},
+					{
+						icon:'/static/icon/bank1.png',
+						name:'提现记录',
+						path:'/pagesB/pages/withdraw/records'
 					},
 				]
 			}
 		},
 		created() {
-			
+			this.fetchStatistics()
 		},
 		methods:{
 			login(){
@@ -103,12 +120,33 @@
 				uni.navigateTo({
 					url:"/pages/agent/agentDetail"
 				})
+			},
+			fetchStatistics(){
+				let params={
+					userId:this.vuex_userId
+				}
+				this.$api.agenter.statistics(params).then(res=>{
+					this.info=res.data
+				})
 			}
 		}
 	}
 </script>
 
 <style lang="scss" scoped>
+	
+	.btn-withdraw{
+		position: absolute;
+		right: -30rpx;
+		top: 26rpx;
+		
+		.btn{
+			padding: 16rpx 38rpx;
+			background-color: #FFE6CD;
+			color: #EF9944;
+		}
+	}
+	
 	.page{
 		background-color: #f6f6f6;
 		height: 80vh;
@@ -119,7 +157,9 @@
 		align-items: center;
 		
 		.card{
-			padding:30rpx 40rpx;
+			overflow: hidden;
+			position: relative;
+			padding:40rpx 40rpx;
 			width: 100%;
 			border-radius: 20rpx;
 			background-image: linear-gradient(#1F2349,#465274);

+ 7 - 3
pages/gain/gain.vue

@@ -12,10 +12,14 @@
 		</view>
 		<view style="height: 100%;">
 			<swiper style="height: 100%;" :current="current" @change="swiperChange" @animationfinish="animationfinish">
-				<swiper-item v-for="(item,index) in tabs" :key="index">
+				<swiper-item>
 					<scroll-view scroll-y style="height: 100%;">
-						<card1 v-if="current==0"></card1>
-						<card2 @login="showLogin" v-else></card2>
+						<card1 ></card1>
+					</scroll-view>
+				</swiper-item>
+				<swiper-item>
+					<scroll-view scroll-y style="height: 100%;">
+						<card2 @login="showLogin"></card2>
 					</scroll-view>
 				</swiper-item>
 			</swiper>

+ 31 - 26
pages/mine/mine.vue

@@ -1,6 +1,5 @@
 <template>
 	<view >
-
 		<!-- 扫一扫错误回调 -->
 		<u-modal title="扫描异常"  confirm-color="#dc9b21"  v-model="scanNotify.scan" :content="scanNotify.scanMessage"></u-modal>
 		<view style="position: relative;">
@@ -19,9 +18,6 @@
 							</block>
 						</view>
 					</view>
-					<!-- <view class="center padding-right-20">
-						<image style="width: 50rpx;height: 50rpx;" src="../../static/icon/setting.png"></image>
-					</view> -->
 				</view>
 				
 				<view class="info" v-else>
@@ -61,12 +57,12 @@
 			<text class="title">我的账户</text>
 			
 			<view class="menu">
-				<view class="item" @click="jump('/pages/mine/my-points/my-points?point='+userInfo.channelPoint)">
+				<view class="item" @click="jump('/pagesA/pages/my-points/my-points?point='+userInfo.channelPoint)">
 					<text>{{userInfo.channelPoint || 0}}</text>
 					通兑积分
 				</view>
 				
-				<view class="item" @click="jump('/pages/mine/my-balance/my-balance?pointValue='+userInfo.balance)">
+				<view class="item" @click="jump('/pagesA/pages/my-balance/my-balance?pointValue='+userInfo.balance)">
 					<text>{{userInfo.balance || 0}}</text>
 					积分余额
 				</view>
@@ -76,7 +72,7 @@
 		<view class="exchange">
 			<view class="top" style="padding: 20rpx 30rpx 10rpx;">
 				<text>积分通兑</text>
-				<text @click="jump('/pages/mine/my-points/exchange')">更多 ></text>
+				<text @click="jump('/pagesA/pages/my-points/exchange')">更多 ></text>
 			</view>
 			
 			<view class="menu-list">
@@ -166,36 +162,45 @@
 			this.toumindu = obj.scrollTop*0.006;
 		},
 		onLoad(options) {
+			uni.hideLoading()
 			//获取从收银台传过来的参数,处理新用户扫码进入收银台再进入首页的情况
 			if (!this.$isEmpty(options.query)) {
 				this.query=options.query
 			}
 		},
 		onShow() {
+			uni.hideLoading()
 			this.fetchStatic()
 			this.login()
 		},
 		methods: {
-			async init(){
-				await this.fetchUserInfo()
+			init(){
+				this.fetchUserInfo()
 			},
 			async fetchUserInfo(){
 				let params={
 					id:this.vuex_userId
 				}
-				let res=await this.$api.loginUser.detail(params)
-				if (this.$isEmpty(res.data)) {
-					//清除userId和手机号
-					this.$u.vuex('vuex_userId',null)
-					this.$u.vuex('vuex_phone',null)
-					this.$cache.remove('lifeData')
-					this.$cache.remove('userInfo')
-					//刷新当前页
-					this.login()
-				}else{
-					this.userInfo=res.data
-					this.$u.vuex('vuex_agenter_type',res.data.userType)
-				}
+				this.$api.loginUser.detail(params).then(res=>{
+					if (this.$isEmpty(res.data)) {
+						this.handeGetUserInfoError()
+					}else{
+						this.userInfo=res.data
+						this.$u.vuex('vuex_agenter_type',res.data.userType)
+					}
+				}).catch(err=>{
+					this.handeGetUserInfoError()
+				})
+				
+			},
+			handeGetUserInfoError(){
+				//清除userId和手机号
+				this.$u.vuex('vuex_userId',null)
+				this.$u.vuex('vuex_phone',null)
+				this.$cache.remove('lifeData')
+				this.$cache.remove('userInfo')
+				//刷新当前页
+				this.login()
 			},
 			// 登录 begin
 			showLogin(){
@@ -218,7 +223,7 @@
 				//处理跳回收银台的情况
 				if (!this.$isEmpty(this.query)) {
 					uni.redirectTo({
-						url:"/pages/checkstand/index?query="+this.query
+						url:"/pagesC/pages/checkstand/index?query="+this.query
 					})
 				}
 				// #endif
@@ -277,17 +282,17 @@
 					{
 						icon:'/static/icon/scan.png',
 						name:'扫一扫',
-						path:'/pages/checkstand/index'
+						path:'/pagesC/pages/checkstand/index'
 					},
 					{
 						icon:'/static/icon/pay.png',
 						name:'付款码',
-						path:'/pages/checkstand/pay-qrcode'
+						path:'/pagesC/pages/checkstand/pay-qrcode'
 					},
 					{
 						icon:'/static/icon/setting1.png',
 						name:'设置',
-						path:'/pages/mine/setting/setting'
+						path:'/pagesA/pages/setting/setting'
 					}
 				]
 				this.gainList=[

+ 0 - 117
pages/test/test.vue

@@ -1,117 +0,0 @@
-<template>
-	<view>
-		<view class="card">
-			<view class="top">
-				<view class="title center">
-					<image src="../../static/icon/yidong.png" mode=""></image>
-					<text>星巴克咖啡店</text>
-				</view>
-				<view class="detail">
-					<view class="">
-						<text>查看详情</text>
-						<text class="cuIcon-right margin-left-10"></text>
-					</view>
-				</view>
-			</view>
-			<view class="content">
-				<view class="">
-					<text class="label">申请人:</text>
-					<text class="value">张三</text>
-				</view>
-				<view class="padding-top-20">
-					<text class="label">时间:</text>
-					<text class="value">2021-08-02 10:10:35</text>
-				</view>
-			</view>
-			
-			<view class="bottom">
-				<view class="cu-btn round pass-btn" style="width: 140rpx;">
-					通过
-				</view>
-				<view class="cu-btn round refuse-btn" style="width: 140rpx;margin-left: 30rpx;">
-					拒绝
-				</view>
-			</view>
-		</view>
-	</view>
-</template>
-
-<script>
-	export default {
-		data() {
-			return {
-				
-			}
-		},
-		methods: {
-			
-		}
-	}
-</script>
-
-<style lang="scss" scoped>
-	.pass-btn{
-		background-color: 	#EF9944;
-		color: #FFFFFF;
-	}
-	
-	.refuse-btn{
-		background-color: #FFFFFF;
-		color: #EF9944;
-		border: 1rpx solid #EF9944;
-	}
-	
-	.card{
-		border-radius: 30rpx;
-		background-color: #FFFFFF;
-		display: flex;
-		flex-direction: column;
-		padding: 30rpx 36rpx;
-		margin: 20rpx 0;
-		position: relative;
-		
-		.top{
-			display: flex;
-			justify-content: space-between;
-			
-			.title{
-				color: #252525;
-				display: flex;
-				font-size: 34rpx;
-				font-weight: 800;
-				image{
-					margin-right: 10rpx;
-					width: 40rpx;
-					height: 40rpx;
-					border-radius: 50%;
-				}
-			}
-			
-			.detail{
-				display: flex;
-				flex-direction: column;
-				justify-content: flex-end;
-				color: #999999;
-				font-size: 26rpx;
-			}
-		}
-		
-		.content{
-			margin: 30rpx 20rpx;
-			
-			.label{
-				font-size: 28rpx;
-				color: #999999;
-			}
-			
-			.value{
-				color: #252525;
-			}
-		}
-		
-		.bottom{
-			display: flex;
-			justify-content: flex-end;
-		}
-	}
-</style>

+ 1 - 2
components/echarts/qiun-data-charts/qiun-data-charts.vue → pagesA/comps/echarts/qiun-data-charts/qiun-data-charts.vue

@@ -156,7 +156,6 @@
 </template>
 
 <script>
-import qiunLoading from "../qiun-loading/qiun-loading.vue"
 import qiunError from "../qiun-error/qiun-error.vue"
 import uChartsMp from '../u-charts/u-charts.js';
 import cfu from '../u-charts/config-ucharts.js';
@@ -234,7 +233,7 @@ function debounce(fn, wait) {
 export default {
   name: 'qiun-data-charts',
   components:{
-	  qiunLoading,qiunError
+	  qiunError
   },
   props: {
     type: {

+ 0 - 0
components/echarts/qiun-error/qiun-error.vue → pagesA/comps/echarts/qiun-error/qiun-error.vue


+ 0 - 0
components/echarts/u-charts/config-echarts.js → pagesA/comps/echarts/u-charts/config-echarts.js


+ 0 - 0
components/echarts/u-charts/config-ucharts.js → pagesA/comps/echarts/u-charts/config-ucharts.js


+ 0 - 0
components/echarts/u-charts/readme.md → pagesA/comps/echarts/u-charts/readme.md


+ 0 - 0
components/echarts/u-charts/u-charts.js → pagesA/comps/echarts/u-charts/u-charts.js


+ 10 - 2
pages/mine/bill-detail.vue → pagesA/pages/bill/bill-detail.vue

@@ -57,7 +57,7 @@
 				<view class="data text-center center">
 					<view style="padding: 0;">
 						<text>-</text>
-						<text>{{item.price}}</text>
+						<text>{{item.totalPrice}}</text>
 					</view>
 				</view>
 			</view>
@@ -68,7 +68,15 @@
 				</view>
 				<view class="item">
 					<text>交易状态</text>
-					<text>已付款</text>
+					<text>{{item.payStatus}}</text>
+				</view>
+				<view class="item">
+					<text>交易金额</text>
+					<text>{{item.price}}</text>
+				</view>
+				<view class="item">
+					<text>积分手续费</text>
+					<text>{{item.fee}}</text>
 				</view>
 				<view class="item">
 					<text>支付方式</text>

+ 6 - 5
pages/mine/my-balance/my-balance.vue → pagesA/pages/my-balance/my-balance.vue

@@ -17,7 +17,7 @@
 					<text>支出 ¥ 165.00 收入 ¥ 463.00</text>
 				</view> -->
 			</view>
-			<view class="right" @click="$jump('/pages/mine/my-balance/statistics')">
+			<view class="right" @click="$jump('/pagesA/pages/my-balance/statistics')">
 				<text>统计</text>
 				<text class="cuIcon-right"></text>
 			</view>
@@ -26,7 +26,7 @@
 		<view class="card">
 			<mescroll-body :height="height" ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption"
 				:up="upOption">
-				<view @click="$jump('/pages/mine/bill-detail?billDetail='+JSON.stringify(item)+'&type=balance')" class="item" hover-class="hoverClass"
+				<view @click="$jump('/pagesA/pages/bill/bill-detail?billDetail='+JSON.stringify(item)+'&type=balance')" class="item" hover-class="hoverClass"
 					v-for="(item,index) in list" :key="index">
 					<view class="area1">
 						<view class="item-padding">
@@ -38,7 +38,7 @@
 						</view>
 					</view>
 					<view  class="area3 text-area u-flex-1" >
-						<view class="item-padding" style="text-align: right;flex:1;font-size: 34rpx;color: #F39248;margin-right: 20rpx;">-¥{{item.price}}</view>
+						<view class="item-padding" style="text-align: right;flex:1;font-size: 34rpx;color: #F39248;margin-right: 20rpx;">-¥{{item.totalPrice}}</view>
 					</view>
 				</view>
 			</mescroll-body>
@@ -82,7 +82,7 @@
 			},
 			confirmTime(e){
 				this.defaultTime = e.year + '-' +e.month
-				this.downCallback()
+				this.mescroll.resetUpScroll();
 			},
 			upCallback(mescroll) {
 				let params = {
@@ -112,7 +112,8 @@
 			downCallback() {
 				setTimeout(() => {
 					this.mescroll.resetUpScroll();
-				}, 10)
+					this.$u.toast('刷新成功')
+				},500)
 			},
 		}
 	}

+ 3 - 3
pages/mine/my-balance/statistics.vue → pagesA/pages/my-balance/statistics.vue

@@ -41,7 +41,7 @@
 					<view class="area3 text-area u-flex-1">
 						<view class="item-padding"
 							style="text-align: right;flex:1;font-size: 34rpx;color: #F39248;margin-right: 20rpx;">
-							-¥{{item.price}}</view>
+							-¥{{item.totalPrice}}</view>
 					</view>
 				</view>
 				<view class="empty" style="margin-top: 80rpx;" v-if="$isEmpty(list)">
@@ -56,7 +56,7 @@
 
 <script>
 	import MescrollMixin from "@/components/mescroll-body/mescroll-mixins.js";
-	import qiunDataCharts from "@/components/echarts/qiun-data-charts/qiun-data-charts.vue"
+	import qiunDataCharts from "../../comps/echarts/qiun-data-charts/qiun-data-charts.vue"
 	export default {
 		mixins: [MescrollMixin],
 		components: {
@@ -97,7 +97,7 @@
 			},
 			async getData() {
 				let params={
-					queryColumn:'balance_num',
+					queryColumn:'total_price',
 					queryDate: this.$dateTime.format(new Date(this.defaultTime)),
 					payId:this.vuex_userId
 				}

+ 0 - 0
pages/mine/my-points/accredit.vue → pagesA/pages/my-points/accredit.vue


+ 0 - 0
pages/mine/my-points/exchange.vue → pagesA/pages/my-points/exchange.vue


+ 2 - 2
pages/mine/my-points/my-points.vue → pagesA/pages/my-points/my-points.vue

@@ -7,7 +7,7 @@
 				<text>{{point}}</text>
 			</view>
 			<view class="flex justify-center align-center">
-				<view class="cu-btn btn" hover-class="hoverClass1" @click="$jump('/pages/mine/my-points/accredit')">
+				<view class="cu-btn btn" hover-class="hoverClass1" @click="$jump('/pagesA/pages/my-points/accredit')">
 					积分授权
 				</view>
 			</view>
@@ -107,7 +107,7 @@
 			jump(item){
 				console.log(item);
 				uni.navigateTo({
-					url:'/pages/mine/bill-detail?billDetail='+JSON.stringify(item)+'&type=point'
+					url:'/pagesA/pages/bill/bill-detail='+JSON.stringify(item)+'&type=point'
 				})
 			},
 			confirmTime(e){

+ 0 - 0
pages/mine/myInfo.vue → pagesA/pages/myInfo.vue


+ 0 - 0
pages/mine/setting/pay-setting.vue → pagesA/pages/setting/pay-setting.vue


+ 0 - 0
pages/mine/setting/question.vue → pagesA/pages/setting/question.vue


+ 2 - 2
pages/mine/setting/setting.vue → pagesA/pages/setting/setting.vue

@@ -23,7 +23,7 @@
 					{
 						icon:'/static/setting/info.png',
 						name:'个人资料',
-						path:'/pages/mine/myInfo'
+						path:'/pagesA/pages/myInfo'
 					},
 					{
 						icon:'/static/setting/kefu.png',
@@ -33,7 +33,7 @@
 					{
 						icon:'/static/setting/tousu.png',
 						name:'投诉及反馈',
-						path:'/pages/mine/setting/question'
+						path:'/pagesA/pages/setting/question'
 					}
 				]
 			}

+ 447 - 0
pagesB/comps/amountInput/amountInput.vue

@@ -0,0 +1,447 @@
+<template>
+	<view class="keyboard-main" :style="{height:height+'px'}">
+		<view class="mask" @click.stop="hide" v-if="isShow"></view>
+		<view class='keyboard' :style="'margin-bottom:'+safeAreaBottom+'rpx'" :class="{noPadding:!isShow}">
+			<view class='key-row' v-if="isShow">
+				<view class='key-cell key-cell-num' @click.stop='_handleKeyPress(1)'>1</view>
+				<view class='key-cell key-cell-num' @click.stop='_handleKeyPress(2)'>2</view>
+				<view class='key-cell key-cell-num' @click.stop='_handleKeyPress(3)'>3</view>
+				<view class='key-cell  last-child key-cell-num' @click.stop="_handleKeyPress('delete')">
+					<image class="icon" src="/static/icon/del.png" mode="aspectFill"></image>
+				</view>
+			</view>
+			<view class='key-row' v-if="isShow">
+				<view class='key-cell key-cell-num' @click.stop='_handleKeyPress(4)'>4</view>
+				<view class='key-cell key-cell-num' @click.stop='_handleKeyPress(5)'>5</view>
+				<view class='key-cell key-cell-num' @click.stop='_handleKeyPress(6)'>6</view>
+				<view class='key-cell last-child'></view>
+			</view>
+			<view class='key-row' v-if="isShow">
+				<view class='key-cell key-cell-num' @click.stop='_handleKeyPress(7)'>7</view>
+				<view class='key-cell key-cell-num' @click.stop='_handleKeyPress(8)'>8</view>
+				<view class='key-cell key-cell-num' @click.stop='_handleKeyPress(9)'>9</view>
+				<view class='key-cell last-child '></view>
+			</view>
+			<view class="key-zero-and-point" v-if="isShow">
+				<view class="a zero key-cell-num" @click.stop='_handleKeyPress(0)'>0</view>
+				<view class="a point key-cell-num" @click.stop="_handleKeyPress('.')">.</view>
+				<view class='a last-child'>
+				</view>
+			</view>
+			
+			<view class='key-confirm key-cell-num' :class="{big:isShow,frist:fristShow}" :style="{'background':btnColor}"
+				@click.stop="_handleKeyPress('confirm')">
+				{{confirmText.substring(0,2)}}
+			</view>
+			
+			<view v-if="!isShow">
+				<view class="" style="height: 90rpx;"></view>
+				<view  class="footer-fixed center" style="bottom: 14%;">
+					<view @click="_handleKeyPress('confirm')" class="cu-btn df radius " :style="{'background':btnColor}" style="color: #FFFFFF;width: 90%;height: 90rpx;">
+						{{confirmText}}
+					</view>
+				</view>
+			</view>
+
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: "keyBoard",
+		props: {
+			confirmText: {
+				default: '充值',
+				type: String
+			},
+			btnColor: {
+				default: '#2B76EF',
+				type: String
+			},
+			placeholder: {
+				default: '请输入充值金额',
+				type: String
+			},
+			currency: {
+				default: '¥',
+				type: String
+			},
+			maxNumber: {
+				default: 10000000,
+				type: Number
+			},
+			fontSize: {
+				type: [String, Number],
+				default: 30
+			},
+			isBold: {
+				type: Boolean,
+				default: true
+			},
+			isFilter: {
+				type: Boolean,
+				default: true
+			}
+		},
+		data() {
+			return {
+				fristShow: true,
+				isShow: true,
+				size: 0,
+				height: 0,
+				allWidth: 0,
+				money: ''
+			}
+		},
+		created() {
+
+		},
+		computed: {
+			//ios底部安全区域
+			safeAreaBottom() {
+				let info = uni.getSystemInfoSync()
+				let safe = 20
+				if (
+					info && ['devtools', 'ios'].includes(info.platform) &&
+					info.statusBarHeight > safe
+				) {
+					return info.statusBarHeight - safe
+				}
+				return 0
+			}
+		},
+		watch: {
+			money(val) {
+				this.$emit('change', val);
+			}
+		},
+		methods: {
+			initialMoney(data){
+				this.money=data.toString()
+			},
+			show() {
+				this.isShow = true;
+			},
+			hide() {
+				this.isShow = false;
+			},
+			//处理按键
+			_handleKeyPress(num) {
+				uni.vibrateShort();
+				if (num == -1) return false;
+				switch (String(num)) {
+					//小数点
+					case '.':
+						this._handleDecimalPoint();
+						break;
+						//删除键
+					case 'delete':
+						this._handleDeleteKey();
+						break;
+						//确认键
+					case 'confirm':
+						this._handleConfirmKey();
+						break;
+					default:
+						this._handleNumberKey(num);
+						break;
+				}
+			},
+			//处理小数点函数
+			_handleDecimalPoint() {
+				//如果包含小数点,直接返回
+				if (this.money.indexOf('.') > -1) return false;
+				//如果小数点是第一位,补0
+				if (!this.money.length) {
+					this.money = '0.'
+				} else {
+					this.money = this.money + '.';
+				}
+			},
+			//处理删除键
+			_handleDeleteKey() {
+				let S = this.money;
+				//如果没有输入,直接返回
+				if (!S.length) return false;
+				//否则删除最后一个
+				this.money = S.substring(0, S.length - 1);
+			},
+			//处理数字
+			_handleNumberKey(num) {
+				if (Number(this.money + num) > this.maxNumber) {
+					return
+				}
+				let S = this.money;
+				//如果有小数点且小数点位数不小于2
+				if (S.indexOf('.') > -1 && S.substring(S.indexOf('.') + 1).length < 2)
+					this.money = S + num;
+				if (S.indexOf('.') > -1 && S.substring(S.indexOf('.') + 1).length >= 2)
+					return
+				//没有小数点
+				if (!(S.indexOf('.') > -1)) {
+					//如果第一位是0,只能输入小数点
+					if (num == 0 && S.length == 0)
+						this.money = '0.';
+					else {
+						if (S.length && Number(S.charAt(0)) === 0) return;
+						this.money = S + num;
+					}
+				}
+			},
+			//提交
+			_handleConfirmKey() {
+				let S = this.money;
+				//未输入
+				// if (!S.length || S == 0) {
+				// 	uni.showToast({
+				// 		title: this.placeholder,
+				// 		icon: 'none',
+				// 		duration: 1000
+				// 	});
+				// 	return false;
+				// }
+				//将 8. 这种转换成 8.00
+				if (S.indexOf('.') > -1 && S.indexOf('.') == (S.length - 1))
+					S = Number(S.substring(0, S.length - 1)).toFixed(2);
+				//保留两位
+				S = Number(S).toFixed(2);
+				this.$emit('confirm', parseFloat(S)); //提交参数
+			}
+		}
+	}
+</script>
+
+<style lang="less" scoped>
+	.data-content {
+		font-size: 32rpx;
+		font-weight: 800;
+		padding: 50rpx;
+	}
+
+	.keyboard-main {
+		width: 100%;
+		display: flex;
+		justify-content: flex-start;
+		align-items: flex-end;
+		padding: 20rpx 0;
+		box-sizing: initial;
+		color: #1B1B1B;
+
+		text {
+			font-size: 24px;
+			margin-right: 10px;
+		}
+	}
+
+	.keyboard-content {
+		width: 100%;
+		height: 30px;
+		position: relative;
+
+		.placeholder {
+			position: absolute;
+			top: 0;
+			left: 0;
+			width: 100%;
+			color: #c0c4cc;
+			display: flex;
+			justify-content: flex-start;
+			align-items: flex-end;
+		}
+
+		.input {
+			position: absolute;
+			top: 0;
+			left: 0;
+			width: 100%;
+			display: flex;
+			justify-content: flex-start;
+			align-items: flex-end;
+			font-size: 30px;
+			font-weight: bold;
+
+			&.zIndex {
+				z-index: 999;
+			}
+
+			#text {
+				display: flex;
+				justify-content: flex-start;
+				align-items: flex-end;
+			}
+
+			.line {
+				display: inline-block;
+				width: 4rpx;
+				height: 30px;
+				border: 2rpx;
+				background-color: #0063E5;
+				margin-left: 8rpx;
+				animation: cursorImg 1s infinite steps(1, start);
+
+				@keyframes cursorImg {
+
+					0%,
+					100% {
+						opacity: 0;
+					}
+
+					50% {
+						opacity: 1;
+					}
+				}
+			}
+		}
+	}
+
+	.mask {
+		position: fixed;
+		top: 0;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		z-index: 1;
+	}
+
+	.keyboard {
+		flex: 1;
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		width: 100%;
+		background-color: #f6f6f6;
+		padding: 20rpx;
+		transition: all 0.6s;
+		z-index: 9;
+		box-sizing: border-box;
+
+		&.noPadding {
+			padding: 0;
+			bottom: -100%;
+		}
+	}
+
+	.keyboard .key-row {
+		display: flex;
+		justify-content: space-between;
+		display: -webkit-flex;
+		position: relative;
+		box-sizing: border-box;
+	}
+
+	.keyboard .key-cell {
+		font-size: 18px;
+		width: 160rpx;
+		font-weight: 800;
+		height: 90rpx;
+		/* margin-right: 20rpx; */
+		margin-bottom: 20rpx;
+		background-color: #fff;
+		border-radius: 4rpx;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+
+		&.last-child {
+			/* margin-right: 0px; */
+			width: 170rpx;
+		}
+
+		.icon {
+			width: 50rpx;
+			height: 40rpx;
+		}
+	}
+
+	.keyboard .key-confirm {
+		position: fixed;
+		text-align: center;
+		/* min-height: 100rpx; */
+		width: 170rpx;
+		padding: 20rpx 30rpx;
+		border-radius: 6rpx;
+		box-sizing: border-box;
+		color: #FFFFFF;
+		z-index: 10;
+		right: 20rpx;
+		top: calc(100vh - 280rpx);
+		bottom: 200rpx;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		font-size: 20px;
+		letter-spacing: 4px;
+		font-weight: bold;
+		transition: all 0.3s;
+
+		&.big {
+			right: 20rpx;
+			bottom: 20rpx;
+			padding: 0 30rpx;
+			bottom: 20rpx;
+		}
+
+		&.frist {
+			position: absolute;
+			top: auto;
+			right: 20rpx;
+			height: 304rpx;
+			bottom: 18rpx;
+		}
+	}
+
+	.key-zero-and-point {
+		display: flex;
+		height: 80rpx;
+		justify-content: space-between;
+		align-items: center;
+		font-size: 20px;
+		font-weight: 800;
+
+		.zero {
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			width: 340rpx;
+			text-align: center;
+			background-color: #fff;
+			border-radius: 4rpx;
+			height: 100%;
+		}
+
+		.point {
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			background-color: #fff;
+			border-radius: 4rpx;
+			width: 160rpx;
+			height: 100%;
+		}
+
+		.last-child {
+			background-color: #fff;
+			border-radius: 8rpx;
+			width: 170rpx;
+			height: 100%;
+		}
+	}
+
+	.key-cell-num:active {
+		color: white;
+		background: black; //黑色
+		opacity: 0.1; //这里重要,就是通过这个透明度来设置
+	}
+
+	// .a:active,.key-confirm2:active{
+	// 	color: white;
+	// 	background: black;  //黑色
+	// 	opacity: 0.1;    //这里重要,就是通过这个透明度来设置
+	// }
+	#input-text {
+		width: max-content;
+		position: fixed;
+		bottom: -200%;
+	}
+</style>

+ 0 - 0
components/back.vue → pagesB/comps/back.vue


+ 55 - 0
pagesB/comps/mescroll-body/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
pagesB/comps/mescroll-body/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>

+ 90 - 0
pagesB/comps/mescroll-body/components/mescroll-empty.vue

@@ -0,0 +1,90 @@
+<!--空布局
+
+可作为独立的组件, 不使用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="option.btnText" class="empty-btn" @click="emptyClick">{{ option.btnText }}</view>
+	</view>
+</template>
+
+<script>
+// 引入全局配置
+import GlobalOption from './../mescroll-uni-option.js';
+export default {
+	props: {
+		// empty的配置项: 默认为GlobalOption.up.empty
+		option: {
+			type: Object,
+			default() {
+				return {};
+			}
+		}
+	},
+	// 使用computed获取配置,用于支持option的动态配置
+	computed: {
+		// 图标
+		icon() {
+			return this.option.icon == null ? GlobalOption.up.empty.icon : this.option.icon; // 此处不使用短路求值, 用于支持传空串不显示图标
+		},
+		// 文本提示
+		tip() {
+			return this.option.tip == null ? GlobalOption.up.empty.tip : this.option.tip; // 此处不使用短路求值, 用于支持传空串不显示文本提示
+		}
+	},
+	methods: {
+		// 点击按钮
+		emptyClick() {
+			this.$emit('emptyclick');
+		}
+	}
+};
+</script>
+
+<style>	
+/* 无任何数据的空布局 */
+.mescroll-empty {
+	box-sizing: border-box;
+	width: 100%;
+	padding: 300rpx 50rpx 0;
+	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
pagesB/comps/mescroll-body/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
pagesB/comps/mescroll-body/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
pagesB/comps/mescroll-body/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
pagesB/comps/mescroll-body/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);
+	}
+}

+ 348 - 0
pagesB/comps/mescroll-body/mescroll-body.vue

@@ -0,0 +1,348 @@
+<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 MescrollTop from './components/mescroll-top.vue';
+	// 引入兼容wxs(含renderjs)写法的mixins
+	import WxsMixin from './wxs/mixins.js';
+	
+	export default {
+		mixins: [WxsMixin],
+		components: {
+			MescrollEmpty,
+			MescrollTop
+		},
+		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 // 状态栏高度
+			};
+		},
+		props: {
+			down: Object, // 下拉刷新的参数配置
+			up: Object, // 上拉加载的参数配置
+			top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+			topbar: [Boolean, String], // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
+			bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+			safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
+			height: [String, Number], // 指定mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
+			bottombar:{ // 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效)
+				type: Boolean,
+				default: true
+			},
+			sticky: Boolean // 是否支持sticky,默认false; 当值配置true时,需避免在mescroll-body标签前面加非定位的元素,否则下拉区域无法会隐藏
+		},
+		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);
+					}
+				}
+			};
+
+			MeScroll.extend(diyOption, GlobalOption); // 混入全局的配置
+			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为滚动区域
+			// 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;
+			}
+		}
+	};
+</script>
+
+<style>
+	@import "./mescroll-body.css";
+	@import "./components/mescroll-down.css";
+	@import './components/mescroll-up.css';
+</style>

+ 65 - 0
pagesB/comps/mescroll-body/mescroll-mixins.js

@@ -0,0 +1,65 @@
+// mescroll-body 和 mescroll-uni 通用
+
+// import MescrollUni from "./mescroll-uni.vue";
+// import MescrollBody from "./mescroll-body.vue";
+
+const MescrollMixin = {
+	// components: { // 非H5端无法通过mixin注册组件, 只能在main.js中注册全局组件或具体界面中注册
+	// 	MescrollUni,
+	// 	MescrollBody
+	// },
+	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;

+ 36 - 0
pagesB/comps/mescroll-body/mescroll-uni-option.js

@@ -0,0 +1,36 @@
+// 全局配置
+// mescroll-body 和 mescroll-uni 通用
+const GlobalOption = {
+	down: {
+		// 其他down的配置参数也可以写,这里只展示了常用的配置:
+		textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
+		textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
+		textLoading: '加载中 ...', // 加载中的提示文本
+		textSuccess: '加载成功', // 加载成功的文本
+		textErr: '加载失败', // 加载失败的文本
+		beforeEndDelay: 100, // 延时结束的时长 (显示加载成功/失败的时长)
+		offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
+		native: false // 是否使用系统自带的下拉刷新; 默认false; 仅在mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
+	},
+	up: {
+		// 其他up的配置参数也可以写,这里只展示了常用的配置:
+		textLoading: '加载中 ...', // 加载中的提示文本
+		textNoMore: '到底啦', // 没有更多数据的提示文本
+		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: "/static/icon/empty5.png", // 图标路径 (建议放入static目录, 如 /static/img/mescroll-empty.png )
+			tip: '数据为空' // 提示
+		}
+	}
+}
+
+export default GlobalOption

+ 36 - 0
pagesB/comps/mescroll-body/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
pagesB/comps/mescroll-body/mescroll-uni.js

@@ -0,0 +1,799 @@
+/* mescroll
+ * version 1.3.3
+ * 2020-09-15 wenju
+ * https://www.mescroll.com
+ */
+
+export default function MeScroll(options, isScrollBody) {
+	let me = this;
+	me.version = '1.3.3'; // 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: 100, // 延时结束的时长 (显示加载成功/失败的时长)
+		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()
+}

+ 424 - 0
pagesB/comps/mescroll-body/mescroll-uni.vue

@@ -0,0 +1,424 @@
+<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 MescrollTop from './components/mescroll-top.vue';
+	// 引入兼容wxs(含renderjs)写法的mixins
+	import WxsMixin from './wxs/mixins.js';
+	
+	export default {
+		mixins: [WxsMixin],
+		components: {
+			MescrollEmpty,
+			MescrollTop
+		},
+		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 // 状态栏高度
+			}
+		},
+		props: {
+			down: Object, // 下拉刷新的参数配置
+			up: Object, // 上拉加载的参数配置
+			top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+			topbar: [Boolean, String], // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
+			bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+			safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
+			fixed: { // 是否通过fixed固定mescroll的高度, 默认true
+				type: Boolean,
+				default: true
+			},
+			height: [String, Number], // 指定mescroll的高度, 此项有值,则不使用fixed. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+			bottombar:{ // 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效)
+				type: Boolean,
+				default: true
+			}
+		},
+		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(){
+				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()
+					}
+				}
+			}
+
+			MeScroll.extend(diyOption, GlobalOption); // 混入全局的配置
+			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
+			// 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;
+			}
+		},
+		mounted() {
+			// 设置容器的高度
+			this.setClientHeight()
+		}
+	}
+</script>
+
+<style>
+	@import "./mescroll-uni.css";
+	@import "./components/mescroll-down.css";
+	@import './components/mescroll-up.css';
+</style>

+ 48 - 0
pagesB/comps/mescroll-body/mixins/mescroll-comp.js

@@ -0,0 +1,48 @@
+/**
+ * mescroll-body写在子组件时,需通过mescroll的mixins补充子组件缺少的生命周期
+ */
+const MescrollCompMixin = {
+	// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件 (一级)
+	onPageScroll(e) {
+		this.handlePageScroll(e)
+	},
+	onReachBottom() {
+		this.handleReachBottom()
+	},
+	// 当down的native: true时, 还需传递此方法进到子组件
+	onPullDownRefresh(){
+		this.handlePullDownRefresh()
+	},
+	// mescroll-body写在子子子...组件的情况 (多级)
+	data() {
+		return {
+			mescroll: {
+				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;

+ 59 - 0
pagesB/comps/mescroll-body/mixins/mescroll-more-item.js

@@ -0,0 +1,59 @@
+/**
+ * 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.isInit = true; // 标记为true
+				this.mescroll && this.mescroll.triggerDownScroll();
+			}
+		}
+	},
+	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.isInit = true; // 标记为true
+				this.mescroll.triggerDownScroll();
+			}
+		},
+	}
+}
+
+export default MescrollMoreItemMixin;

+ 74 - 0
pagesB/comps/mescroll-body/mixins/mescroll-more.js

@@ -0,0 +1,74 @@
+/**
+ * mescroll-body写在子组件时, 需通过mescroll的mixins补充子组件缺少的生命周期
+ */
+const MescrollMoreMixin = {
+	data() {
+		return {
+			tabIndex: 0, // 当前tab下标
+			mescroll: {
+				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;

+ 109 - 0
pagesB/comps/mescroll-body/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
pagesB/comps/mescroll-body/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
pagesB/comps/mescroll-body/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
+}

+ 0 - 0
components/u-city-select.vue → pagesB/comps/u-city-select.vue


+ 21 - 0
pagesB/comps/u-draw-poster/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019-PRESENT Anthony Fu<https://github.com/TuiMao233>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 230 - 0
pagesB/comps/u-draw-poster/README.md

@@ -0,0 +1,230 @@
+# 绘制海报工具简述
+
+- 创建绘制海报`canvas`矩形方法,内置了图片绘制,圆角矩形绘制,换行字体绘制等方法。
+- 接近原生开发体验,上手快,只需考虑业务逻辑,而不用考虑其他问题。
+- 拥有良好的语法架构,不会在绘制`uni/wx`矩形时陷入回调地狱。
+- 支持原生小程序,与`uniapp`多端应用。当是环境为原生小程序时,自动切换为性能更好的`type2d`绘制方式。
+- 将复杂的逻辑组合为简单的方法,扩展性强,可使用 `use|useCtx` 引入扩展。
+- 支持`typescript`,支持`vue3`模板,具体使用参考 [useDrawPoster](https://github.com/TuiMao233/u-draw-poster/tree/master/docs/use.md)。
+- 更加强大的图片绘制与裁剪方法(object-fit)
+- 支持声明式绘制扩展,自定义海报必备 具体使用参考 [draw-painter](https://tuimao233.gitee.io/mao-blog/wo-de-kuo-zhan/u-draw-poster/01-base-desc.html)
+
+api文档:[u-draw-poster](https://tuimao233.gitee.io/mao-blog/wo-de-kuo-zhan/u-draw-poster/01-base-desc.html)
+
+插件市场:[dcloud/u-draw-poster](https://ext.dcloud.net.cn/plugin?id=3237)
+
+**npm 安装插件**
+
+~~~
+npm i --save-dev u-draw-poster
+~~~
+
+## 1. 创建海报绘制工具
+
+~~~html
+<!-- #ifdef MP-WEIXIN -->
+<canvas id="canvas" type="2d" style="width:100px; height:100px" />
+<!-- #endif -->
+<!-- #ifndef MP-WEIXIN -->
+<canvas canvas-id="canvas" id="canvas" style="width:100px; height:100px" />
+<!-- #endif -->
+~~~
+
+~~~js
+// 注意:如果使用HBuilder引入, 需要引入 '@/js_sdk/u-draw-poster'
+import { useDrawPoster } from 'u-draw-poster'
+async onReady() {
+ // 传入选择器, 初始化绘制工具, 当微信小程序时, 将自动启用type2d绘制
+ const dp = await useDrawPoster("canvas", {
+   // 设置画布宽高
+   width: 100,
+   height: 100
+ })   
+}
+~~~
+
+## 2. 绘制任意内容
+~~~js
+// 绘制背景与文字
+dp.draw((ctx) => {
+    ctx.fillStyle = "#F4F4F4";
+    ctx.fillRect(0, 0, dp.canvas.width, dp.canvas.height);
+    ctx.textBaseline = "top";
+    ctx.textAlign = "start";
+    ctx.fillStyle = "white";
+    ctx.font = `bold ${22}px sans-serif`;
+    ctx.fillText('周先生', dp.canvas.width/2, 38.5);
+})
+// 绘制图片内容
+dp.draw(async (ctx) => {
+    await ctx.drawImage('...')
+})
+~~~
+值得注意的是, `draw`方法会自动的执行`ctx.save/ctx.restore`, 不需要人为操纵绘画栈.
+~~~js
+dp.draw((ctx) => {/* ... */})
+// 相当于
+ctx.save()
+/* ... */
+ctx.restore()
+~~~
+
+## 4. 进行绘制
+
+`dp.draw`并不会马上绘制,只是将该任务添加到了任务栈,需要使用`dp.awaitCreate`函数进行绘制,该函数在绘制完毕后将弹出所有任务。
+`dp.render`在非`2d`绘画中,执行绘画任务完毕后,将自动执行`ctx.draw`方法,并在 draw 绘画才算异步结束。
+
+~~~js
+dp.draw((ctx) => {/* ... */})
+dp.draw(async (ctx) => {/* ... */})
+// 由于每个任务都有可能会有异步的绘制任务, 所以得需要使用await等待绘制
+console.log("draw绘制状况:", await dp.render()); // draw绘制状况: [true]
+~~~
+
+[^为什么这么做]: 当全部同步绘制时,将会出现绘制时间保持不一致的情况。这样就会导致一个问题,绘制图层覆盖导致显示未达到预期效果,之所以设计为异步等待,也是为了绘制图层能保持一致顺序。
+
+## 5. 生成图片本地地址
+
+如需要保存为图片时,可以使用`dp.createImagePath` 进行创建图片本地地址,在由`wx`或`uni`的`api`进行保存。
+~~~js
+dp.draw(async (ctx) => {/* ... */})
+const result = await dp.render();
+const posterImgUrl = await dp.createImagePath();
+console.log("draw绘制状况:", result); // [true]
+console.log("绘制生成本地地址:", posterImgUrl); // ...tmp...
+~~~
+你也可以不使用`dp.render`方法,当调用`dp.createImagePath`时会自动检测任务列表,如果有则执行绘制任务后在创建地址。
+
+~~~js
+dp.draw(async (ctx) => {/* ... */})
+// 跳过drawPoster.awaitCreate直接生成地址
+const posterImgUrl = await dp.createImagePath();
+console.log("绘制生成本地地址:", posterImgUrl);
+~~~
+
+# 绘制扩展 API
+
+DrawPoster 在创建时,会自动的向`ctx(画笔)`添加/覆盖扩展方法,以便构建海报矩形。
+
+目前支持绘制图片、圆角图片、绘制裁剪图片(object-fit)、换行字体、圆角矩形、圆角矩形边框、绘制二维码。
+
+~~~js
+dp.draw(async (ctx) => {
+  // ctx.drawImage | ctx.drawRoundImage | ctx.fillWarpText | ....
+})
+~~~
+
+具体查看API文档:[u-draw-poster](http://tuimao233.gitee.io/mao-blog/my-extends/u-draw-poster/02-draw-extends.html)
+
+
+# 全局实例 API
+
+DrawPoster 的静态与扩展方法,除了最常用的:绘制节点、绘画构建、创建绘制、创建图片,以及还有另外的扩展功能:绘画构建、挂载全局扩展、挂载绘制扩展、全局画笔、等待绘制、停止绘画。
+
+具体查看API文档:[u-draw-poster](http://tuimao233.gitee.io/mao-blog/my-extends/u-draw-poster/03-gbl-example.html)
+
+# 使用建议
+
+## canvas 当做为生成工具
+
+canvas在海报生成中请当做一个生成工具来看待,它的作用仅是绘制出海报。应把生成得到的资源保存并使用,显示用image图片组件,原因是方便操作,例如调整大小,或是H5端长按保存或识别,所以canvas应将它放在看不见的地方。不能用display:none;overflow:hidden;隐藏,否则生成空白。这里推荐canvas的隐藏样式代码,该说明为 [uQRCode](https://github.com/Sansnn/uQRCode) 提供的说明,同样`u-draw-poster`也适用
+
+~~~css
+.canvas-hide {
+	/* 1 */
+	position: fixed;
+	right: 100vw;
+	bottom: 100vh;
+	/* 2 */
+	z-index: -9999;
+	/* 3 */
+	opacity: 0;
+}
+~~~
+
+## 支持重复调用 (单例模式)
+需要注意的是,创建绘制工具支持重复调用,当构建第一次的绘制工具后,重复构建将自动获取第一次的实例。不需要存入`this`中,其实`vue3`也不提倡使用`this`这个黑盒,甚至抛弃了使用`this`。
+~~~js
+data: () => ({}),
+// 不存入实例(推荐)
+method: {
+  async draw() {
+    const dp = await useDrawPoster("canvas")
+    //...
+  }
+},
+onReady() {
+ this.draw()
+ // 重复调用....
+ this.draw()
+}
+~~~
+~~~js
+// 存入实例(不推荐)
+data: () => ({
+  dp: null
+}),
+method: {
+  async draw() {
+    if (!this.dp) {
+      const dp = await useDrawPoster("canvas")
+      this.dp = dp
+    }
+    //...
+  }
+},
+onReady() {
+ this.draw()
+ // 重复调用....
+ this.draw()
+}
+~~~
+
+# 常见问题
+
+## 微信小程序手机浏览空白
+
+微信小程序绘制如果有图片绘制,手机浏览需要在后台添加`downloadFile`域名,并需要重启开发者工具。
+
+## 微信小程序无法真机调试
+
+https://developers.weixin.qq.com/community/develop/doc/000eece1640d608df21bb19055b000
+
+## 绘制完毕后没有效果
+
+注意`DrawPoster.build`无法检测你所选择`canvasId`的是否正确,所以一定要确保与`canvas-id`和`html`中的`canvas`相同,在小程序端,由于会自动切换为`type2d`,必须得加上动态编译。
+
+~~~html
+<!-- #ifdef MP-WEIXIN -->
+<canvas id="canvas" type="2d" style="width: 300px; height: 300px" />
+<!-- #endif -->
+<!-- #ifndef MP-WEIXIN -->
+<canvas canvas-id="canvas" id="canvas" style="width: 300px; height: 300px" />
+<!-- #endif -->
+~~~
+
+## 绘制多个图片加载慢
+
+如果觉得多个图片绘制`await`加载慢,可以使用`Promise.all`将一部分不需要处理图层覆盖的图片进行同步绘制。
+
+~~~js
+dp.draw(async (ctx) => {
+  // // 用户头像
+  await ctx.drawRoundImage(headImgUrl, 39, 790, 90, 90, 100);
+  await Promise.all([
+    ctx.drawImage('/static/logo1.png', 20, 20, 35, 35),
+    ctx.drawImage('/static/tp.png', 19, 86, 612, 459),
+    ctx.drawImage('/static/bw.png', 188, 559, 274, 50),
+    // // 用户二维码
+    ctx.drawImage(codeImgUrl, 518, 780, 92, 92),
+  ]);
+});
+~~~
+
+需要注意的是:`ctx.drawRoundImage`不可以放在`Promise.all`当中,由于`ctx.drawRoundImage`内部会调用`ctx.clip`方法,在`Promise.all`中会与其他图片绘制产生冲突。从而导致圆角失效。
+
+我的博客:[Mr.Mao'blog](https://tuimao233.gitee.io/mao-blog/)
+
+联系方式(邮箱):951416545@qq.com
+
+海报绘制QQ群:936377537

+ 9 - 0
pagesB/comps/u-draw-poster/core/debugginglog.d.ts

@@ -0,0 +1,9 @@
+import { DrawPosterResult } from '.';
+export declare class DebuggingLog {
+    private dp;
+    private $color;
+    constructor(dp: Partial<DrawPosterResult>);
+    log: (message: string, color?: string, ...args: any[]) => void;
+    success: (message: string, ...args: any[]) => void;
+    error: (message: string, ...args: any[]) => void;
+}

+ 21 - 0
pagesB/comps/u-draw-poster/core/debugginglog.js

@@ -0,0 +1,21 @@
+// eslint-disable-next-line eslint-comments/disable-enable-pair
+/* eslint-disable no-console */
+export class DebuggingLog {
+    dp;
+    $color = '#3489fd';
+    constructor(dp) {
+        this.dp = dp;
+    }
+    log = (message, color = this.$color, ...args) => {
+        if (!this.dp?.$options?.debug)
+            return;
+        console.log(`%c${this.dp.id} -> ${message}`, `color: ${color}`, ...args);
+    };
+    success = (message, ...args) => {
+        this.log(`🎉 ${message}`, '#19be6b', ...args);
+    };
+    error = (message, ...args) => {
+        this.log(`🎉 ${message}`, '#fa3534', ...args);
+    };
+}
+//# sourceMappingURL=debugginglog.js.map

+ 1 - 0
pagesB/comps/u-draw-poster/core/debugginglog.js.map

@@ -0,0 +1 @@
+{"version":3,"file":"debugginglog.js","sourceRoot":"","sources":["../../packages/core/debugginglog.ts"],"names":[],"mappings":"AACA,+DAA+D;AAC/D,+BAA+B;AAC/B,MAAM,OAAO,YAAY;IAGH;IAFZ,MAAM,GAAG,SAAS,CAAA;IAE1B,YAAoB,EAA6B;QAA7B,OAAE,GAAF,EAAE,CAA2B;IAAG,CAAC;IACrD,GAAG,GAAG,CAAC,OAAe,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,IAAW,EAAE,EAAE;QAC7D,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK;YAAE,OAAM;QACrC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC,EAAE,OAAO,OAAO,EAAE,EAAE,UAAU,KAAK,EAAE,EAAE,GAAG,IAAI,CAAC,CAAA;IAC1E,CAAC,CAAA;IACD,OAAO,GAAG,CAAC,OAAe,EAAE,GAAG,IAAW,EAAE,EAAE;QAC5C,IAAI,CAAC,GAAG,CAAC,MAAM,OAAO,EAAE,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,CAAA;IAC/C,CAAC,CAAA;IACD,KAAK,GAAG,CAAC,OAAe,EAAE,GAAG,IAAW,EAAE,EAAE;QAC1C,IAAI,CAAC,GAAG,CAAC,MAAM,OAAO,EAAE,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,CAAA;IAC/C,CAAC,CAAA;CACF"}

+ 97 - 0
pagesB/comps/u-draw-poster/core/index.d.ts

@@ -0,0 +1,97 @@
+/// <reference types="@dcloudio/types" />
+import { DrawPosterPlugin, DrawPosterUse } from './plugin';
+export declare type NonNullableCustom<T, N> = T extends N ? never : T;
+export declare type NonPick<T, K extends keyof T> = {
+    [P in NonNullableCustom<keyof T, K>]: T[P];
+};
+export interface DrawPosterOptions {
+    /** 查询字符串(必须), 注意不要写错对应canvas id */
+    selector: string;
+    /** 选取组件范围 */
+    componentThis?: any;
+    /** 绘制类型, 微信小程序自动切换为 '2d' */
+    type?: '2d' | 'context' | 'webgl';
+    /** 是否在绘制与创建时显示加载提示 */
+    loading?: boolean | {
+        /** 生成时加载文字 @default '绘制海报中...' */
+        render?: string;
+        /** 创建图片时加载文字 @default '生成图片中...' */
+        create?: string;
+    };
+    /** 是否开启调试模式 */
+    debug?: boolean;
+    /** 是否启动gcanvas(nvue) */
+    gcanvas?: boolean;
+    /** 画布宽度 */
+    width?: number;
+    /** 画布高度 */
+    height?: number;
+    /** 绘制扩展 */
+    plugins?: DrawPosterPlugin[];
+}
+export interface DrawPosterResult {
+    /** 绘制标识 */
+    readonly id: string;
+    /** 当前绘画插件 */
+    readonly plugins: DrawPosterPlugin[];
+    /** 源数据 */
+    readonly $options: DrawPosterOptions;
+    /** 画布(仅 2d 生效) */
+    readonly canvas: Canvas;
+    /** 画笔 */
+    readonly ctx: CanvasCtx;
+    /**
+     * 引入扩展
+     * 建议: 在构建配置中传入 `plugins`, 该引入方式无法触发 beforeMount
+     */
+    readonly use: DrawPosterUse;
+    /** 停止绘制(仅停止生成) */
+    readonly stop: () => void;
+    /** 创建一个绘制作用域 */
+    readonly draw: (func: (ctx: CanvasCtx) => Promise<void> | void) => void;
+    /** 将所有作用域渲染 */
+    readonly render: () => Promise<boolean[]>;
+    /** 生成图片地址 */
+    readonly createImagePath: (options?: CreateImagePathOptions) => Promise<string>;
+    /** 绘图原型(用于在 beforeMount 时自定义绘制原型) */
+    $drawPrototype?: {
+        canvas: Canvas;
+        ctx: CanvasCtx;
+    };
+    [key: string]: any;
+}
+export interface CanvasCtx extends UniApp.CanvasContext {
+    [key: string]: any;
+    createImageData: () => ImageData;
+    textAlign: CanvasTextDrawingStyles['textAlign'];
+    textBaseline: CanvasTextDrawingStyles['textBaseline'];
+    transform: CanvasTransform['transform'];
+}
+export interface Canvas {
+    width: number;
+    height: number;
+    getContext<K extends '2d' | 'webgl'>(contextType: K): K extends '2d' ? CanvasCtx : WebGLRenderingContext;
+    createImage(): {
+        src: string;
+        width: number;
+        height: number;
+        onload: () => void;
+        onerror: () => void;
+    };
+    requestAnimationFrame(callback: Function): number;
+    cancelAnimationFrame(requestID: number): void;
+    createImageData(): ImageData;
+    createPath2D(path: Path2D): Path2D;
+    toDataURL(type: string, encoderOptions: number): string;
+}
+export declare type Stacks = Array<() => Promise<boolean>>;
+export declare type CreateImagePathOptions = Partial<NonPick<UniApp.CanvasToTempFilePathOptions, 'canvasId' | 'complete' | 'success' | 'fail'>>;
+declare function useDrawPoster(selector: string, options?: Partial<NonPick<DrawPosterOptions, 'selector'>>): Promise<DrawPosterResult>;
+declare namespace useDrawPoster {
+    var use: DrawPosterUse;
+}
+declare function useDrawPoster(options: DrawPosterOptions): Promise<DrawPosterResult>;
+declare namespace useDrawPoster {
+    var use: DrawPosterUse;
+}
+export { useDrawPoster, DrawPosterPlugin };

+ 165 - 0
pagesB/comps/u-draw-poster/core/index.js

@@ -0,0 +1,165 @@
+import { isObject } from 'lodash';
+import { queryFields, UNI_PLATFORM } from '../utils';
+import { DebuggingLog } from './debugginglog';
+import { Plugins, globalUse } from './plugin';
+async function useDrawPoster(...args) {
+    const $options = (() => {
+        const _default = {
+            selector: '',
+            componentThis: undefined,
+            type: UNI_PLATFORM === 'mp-weixin' ? '2d' : 'context',
+            loading: false,
+            debug: false,
+            gcanvas: false
+        };
+        let _overrides;
+        if (isObject(args[0])) {
+            _overrides = args[0];
+        }
+        else if (isObject(args[1])) {
+            _overrides = args[1];
+            _overrides.selector = args[0];
+        }
+        else {
+            _overrides = { selector: args[0] };
+        }
+        const options = { ..._default, ..._overrides };
+        options.selector = options.selector.replace('#', '');
+        if (options.type === '2d') {
+            options.selector = `#${options.selector}`;
+        }
+        if (options.loading === true) {
+            options.loading = { render: '绘制海报中...', create: '生成图片中...' };
+        }
+        if (isObject(options.loading)) {
+            options.loading.render = options.loading?.render ?? '绘制海报中...';
+            options.loading.create = options.loading?.create ?? '生成图片中...';
+        }
+        if (!UNI_PLATFORM) {
+            console.warn('注意! draw-poster未开启uni条件编译! 当环境是微信小程序将不会动态切换为type=2d模式');
+            console.warn(`请在vue.config.js中的'transpileDependencies'中添加 'u-draw-poster' `);
+        }
+        return options;
+    })();
+    const pages = getCurrentPages();
+    const page = pages[pages.length - 1];
+    const dp = { $options };
+    const ps = new Plugins(dp);
+    const consola = new DebuggingLog(dp);
+    let stacks = [];
+    let isStop = false;
+    const build = async () => {
+        if (dp.$drawPrototype)
+            return dp.$drawPrototype;
+        const _nodeInfo = await queryFields($options.selector, $options.componentThis, {
+            node: true
+        });
+        const canvas = _nodeInfo?.node || {};
+        const ctx = canvas.getContext?.('2d') ||
+            uni.createCanvasContext($options.selector, $options.componentThis);
+        if (!canvas || !ctx || !$options.selector) {
+            throw new Error('DrawPoster Error: useDrawPoster to build drawPoster instance');
+        }
+        return { canvas, ctx };
+    };
+    const render = async () => {
+        if ($options.loading)
+            uni.showLoading({ title: $options.loading.render });
+        consola.log('绘制海报中...');
+        const tips = [];
+        for (const next of stacks) {
+            tips.push(await next());
+        }
+        stacks = [];
+        consola.log('绘制状况: ', undefined, tips);
+        if ($options.type === 'context') {
+            await new Promise((resolve) => {
+                dp.ctx.draw(true, resolve);
+            });
+        }
+        if ($options.loading)
+            uni.hideLoading();
+        return tips;
+    };
+    const createImagePath = async (_options_ = {}) => {
+        if (stacks.length > 0)
+            await dp.render();
+        if (isStop) {
+            isStop = false;
+            return Promise.reject();
+        }
+        if ($options.loading)
+            uni.showLoading({ title: $options.loading.create });
+        const options = _options_;
+        if ($options.type === '2d') {
+            ;
+            options.canvas = dp.canvas;
+        }
+        if ($options.type === 'context') {
+            ;
+            options.canvasId = dp.id;
+        }
+        return new Promise((resolve, reject) => {
+            options.success = (res) => {
+                resolve(res.tempFilePath);
+                $options.loading && uni.hideLoading();
+                consola.success('绘制成功', res);
+            };
+            options.fail = (err) => {
+                reject(err);
+                $options.loading && uni.hideLoading();
+                consola.success('绘制失败', err);
+            };
+            uni.canvasToTempFilePath(options);
+        });
+    };
+    const draw = async (func) => {
+        const length = stacks.length;
+        stacks.push(async () => {
+            try {
+                dp.ctx.save();
+                await func(dp.ctx);
+                dp.ctx.restore();
+                return true;
+            }
+            catch (error) {
+                if (!error?.message?.includes?.(`'nodeId' of undefined`))
+                    consola.error(`绘画栈(${length}),绘制错误:`, error);
+                return false;
+            }
+        });
+    };
+    const stop = () => {
+        stacks = [];
+        isStop = true;
+    };
+    if (page[`__dp_${dp.id}`])
+        return page[`__dp_${dp.id}`];
+    Object.defineProperty(dp, 'id', { get: () => $options.selector });
+    Object.defineProperty(dp, 'plugins', { get: () => ps.plugins });
+    ps.run('beforeMount');
+    const { canvas, ctx } = await build();
+    Object.defineProperty(dp, 'canvas', { get: () => canvas });
+    Object.defineProperty(dp, 'ctx', { get: () => ctx });
+    Object.defineProperty(dp, 'render', { get: () => render });
+    Object.defineProperty(dp, 'createImagePath', { get: () => createImagePath });
+    Object.defineProperty(dp, 'draw', { get: () => draw });
+    Object.defineProperty(dp, 'stop', { get: () => stop });
+    Object.defineProperty(dp, 'use', { get: () => ps.use });
+    dp.canvas.width = $options.width ?? 0;
+    dp.canvas.height = $options.height ?? 0;
+    ps.run('mounted');
+    $options.debug && consola.success('构建成功!', dp);
+    page[`__dp_${dp.id}`] = dp;
+    const _onUnload = page.onUnload;
+    page.onUnload = function () {
+        ps.run('beforeUnmount');
+        dp.stop();
+        _onUnload();
+        ps.run('unmounted');
+    };
+    return dp;
+}
+useDrawPoster.use = globalUse;
+export { useDrawPoster };
+//# sourceMappingURL=index.js.map

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
pagesB/comps/u-draw-poster/core/index.js.map


+ 37 - 0
pagesB/comps/u-draw-poster/core/plugin.d.ts

@@ -0,0 +1,37 @@
+import { DrawPosterResult, NonPick } from '.';
+export interface DrawPosterLifeCycle<I = DrawPosterResult, O = Record<string, any>> {
+    (instance: I, options?: O): void;
+}
+export interface DrawPosterLifeCycles {
+    /** 创建实例前 */
+    beforeMount?: DrawPosterLifeCycle<Partial<DrawPosterResult>>;
+    /** 创建实例后 */
+    mounted?: DrawPosterLifeCycle;
+    /** 卸载实例前 */
+    beforeUnmount?: DrawPosterLifeCycle;
+    /** 卸载实例后 */
+    unmounted?: DrawPosterLifeCycle;
+    /** 创建绘图前 */
+    beforeCreate?: DrawPosterLifeCycle;
+    /** 创建绘图后 */
+    created?: DrawPosterLifeCycle;
+}
+export interface DrawPosterPlugin extends DrawPosterLifeCycles {
+    /** 扩展名称 */
+    name: string;
+    [key: string]: any;
+}
+export interface DrawPosterUse {
+    (name: string, lifeCycle: DrawPosterLifeCycle): void;
+    (name: string, options: NonPick<DrawPosterPlugin, 'name'>): void;
+    (options: DrawPosterPlugin): void;
+}
+export declare const globalUse: DrawPosterUse;
+export declare class Plugins {
+    dp: Partial<DrawPosterResult>;
+    $plugins: DrawPosterPlugin[];
+    get plugins(): DrawPosterPlugin[];
+    constructor(dp: Partial<DrawPosterResult>);
+    use: (...args: any[]) => void;
+    run: (lifeCycleName: keyof DrawPosterLifeCycles) => void;
+}

+ 52 - 0
pagesB/comps/u-draw-poster/core/plugin.js

@@ -0,0 +1,52 @@
+import { isFunction, isObject, isString } from 'lodash';
+/**
+ * 对插件参数进行处理并引入
+ * @param plugins 插件列表
+ * @param args 参数
+ */
+const usePluginOptions = (plugins, ...args) => {
+    if (!args[0]) {
+        throw new Error('DrawPoster Error: plugins arguments required');
+    }
+    let _options = { name: '' };
+    if (isString(args[0]) && isFunction(args[1])) {
+        _options.name = args[0];
+        _options.mounted = args[1];
+    }
+    if (isString(args[0]) && isObject(args[1])) {
+        _options = { name: args[0], ...args[1] };
+    }
+    if (isObject(args[0])) {
+        _options = args[0];
+    }
+    if (![...globalPlugins, ...plugins].some((v) => _options.name === v.name)) {
+        plugins.push(_options);
+        return _options;
+    }
+    console.warn(`该扩展已存在: ${_options.name}`);
+};
+const globalPlugins = [];
+export const globalUse = (...args) => usePluginOptions(globalPlugins, ...args);
+export class Plugins {
+    dp;
+    $plugins = [];
+    get plugins() {
+        return [...globalPlugins, ...this.$plugins];
+    }
+    constructor(dp) {
+        this.dp = dp;
+        if (dp.$options?.plugins)
+            this.$plugins.push(...dp.$options?.plugins);
+    }
+    use = (...args) => {
+        const plugin = usePluginOptions(this.$plugins, ...args);
+        if (this.dp['canvas'])
+            plugin?.mounted?.(this.dp);
+    };
+    run = (lifeCycleName) => {
+        this.plugins.forEach((lifeCycle) => {
+            lifeCycle[lifeCycleName]?.(this.dp);
+        });
+    };
+}
+//# sourceMappingURL=plugin.js.map

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
pagesB/comps/u-draw-poster/core/plugin.js.map


+ 1 - 0
pagesB/comps/u-draw-poster/index.d.ts

@@ -0,0 +1 @@
+export * from './core';

+ 11 - 0
pagesB/comps/u-draw-poster/index.js

@@ -0,0 +1,11 @@
+import { useDrawPoster } from './core';
+import * as plugins from './plugins/preset';
+useDrawPoster.use(plugins.drawImage());
+useDrawPoster.use(plugins.drawImageFit());
+useDrawPoster.use(plugins.drawRoundImage());
+useDrawPoster.use(plugins.fillRoundRect());
+useDrawPoster.use(plugins.roundRect());
+useDrawPoster.use(plugins.strokeRoundRect());
+useDrawPoster.use(plugins.fillWarpText());
+export * from './core';
+//# sourceMappingURL=index.js.map

+ 1 - 0
pagesB/comps/u-draw-poster/index.js.map

@@ -0,0 +1 @@
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../packages/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAA;AACtC,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAA;AAE3C,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAA;AACtC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,CAAA;AACzC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,CAAA;AAC3C,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,CAAA;AAC1C,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAA;AACtC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC,CAAA;AAC5C,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,CAAA;AAEzC,cAAc,QAAQ,CAAA"}

+ 44 - 0
pagesB/comps/u-draw-poster/package.json

@@ -0,0 +1,44 @@
+{
+  "name": "u-draw-poster",
+  "version": "1.1.5",
+  "description": "绘制uniapp海报工具类",
+  "main": "dist/index.js",
+  "types": "dist/index.d.ts",
+  "scripts": {
+    "build": "esno scripts/build.ts",
+    "publish": "esno scripts/publish.ts",
+    "compress": "esno scripts/compress.ts",
+    "clean": "rimraf dcloud dist template/js_sdk"
+  },
+  "files": [
+    "dist",
+    "lib"
+  ],
+  "author": "Mr.Mao",
+  "license": "ISC",
+  "dependencies": {
+    "@dcloudio/types": "^2.5.1",
+    "@tuimao/uni-utils": "^0.3.0",
+    "@types/lodash-es": "^4.17.4",
+    "lodash": "^4.17.21",
+    "miniprogram-api-typings": "^3.1.3"
+  },
+  "prettier": "@tuimao/eslint/prettier.js",
+  "devDependencies": {
+    "@tuimao/eslint": "^1.2.0-bata.3",
+    "@types/archiver": "^5.1.1",
+    "@types/fs-extra": "^9.0.12",
+    "@types/node": "^14.14.9",
+    "archiver": "^5.3.0",
+    "assert": "^2.0.0",
+    "consola": "^2.15.3",
+    "eslint": "^7.32.0",
+    "esno": "^0.9.1",
+    "execa": "^5.1.1",
+    "fs-extra": "^10.0.0",
+    "rimraf": "^3.0.2",
+    "tern": "^0.24.3",
+    "typescript": "^4.0.5",
+    "vue": "^2.6.14"
+  }
+}

+ 17 - 0
pagesB/comps/u-draw-poster/plugins/drawImage/index.d.ts

@@ -0,0 +1,17 @@
+/// <reference types="@dcloudio/types" />
+import { DrawPosterPlugin } from '../../core/plugin';
+declare module '../../core' {
+    interface CanvasCtx {
+        /** 等待绘制图片
+         *
+         * 说明文档: https://tuimao233.gitee.io/mao-blog/my-extends/u-draw-poste
+         */
+        drawImage(url: string, dx?: number | undefined, dy?: number | undefined, dWidth?: number | undefined, dHeigt?: number | undefined, sx?: number | undefined, sy?: number | undefined, sWidth?: number | undefined, sHeight?: number | undefined): Promise<boolean>;
+        /**
+         * 绘制图片原型
+         */
+        drawImageProto: UniApp.CanvasContext['drawImage'];
+    }
+}
+declare const _default: () => DrawPosterPlugin;
+export default _default;

+ 43 - 0
pagesB/comps/u-draw-poster/plugins/drawImage/index.js

@@ -0,0 +1,43 @@
+import { downloadImgUrl } from './utils';
+const plugin = {
+    name: '__ctx-drawImage__',
+    mounted: ({ ctx, $options, canvas }) => {
+        ctx.drawImageProto = ctx.drawImage;
+        ctx.drawImage = async (url, sx, sy, sh, sw, dx, dy, dh, dw) => {
+            // 下载路径
+            const path = await downloadImgUrl(url);
+            // 标记当前绘画存在图片绘制
+            let result = false;
+            // 基本绘制方法, 如果是 fit 方式, 则传入所有参数, 不然则只传入四个参数
+            const baseDrawImage = (imageResource) => {
+                const isFit = typeof dx === 'number' && typeof dw === 'number';
+                if (isFit) {
+                    ctx.drawImageProto(imageResource, sx, sy, sh, sw, dx, dy, dh, dw);
+                }
+                else {
+                    ctx.drawImageProto(imageResource, sx, sy, sh, sw);
+                }
+            };
+            // 如果是 context 绘制方式, 则直接绘制
+            if ($options.type === 'context') {
+                baseDrawImage(path);
+                result = true;
+            }
+            // 如果是 type2d 绘制方式, 则等待图片绘制完毕
+            if ($options.type === '2d') {
+                result = await new Promise((resolve) => {
+                    const image = canvas.createImage();
+                    image.src = path;
+                    image.addEventListener('load', () => {
+                        baseDrawImage(image);
+                        resolve(true);
+                    });
+                    image.addEventListener('error', () => resolve(false));
+                });
+            }
+            return result;
+        };
+    }
+};
+export default () => plugin;
+//# sourceMappingURL=index.js.map

+ 1 - 0
pagesB/comps/u-draw-poster/plugins/drawImage/index.js.map

@@ -0,0 +1 @@
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../packages/plugins/drawImage/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AA0BxC,MAAM,MAAM,GAAqB;IAC/B,IAAI,EAAE,mBAAmB;IACzB,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE;QACrC,GAAG,CAAC,cAAc,GAAG,GAAG,CAAC,SAAS,CAAA;QAClC,GAAG,CAAC,SAAS,GAAG,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;YAC5D,OAAO;YACP,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,CAAA;YACtC,eAAe;YACf,IAAI,MAAM,GAAG,KAAK,CAAA;YAClB,0CAA0C;YAC1C,MAAM,aAAa,GAAG,CAAC,aAAqB,EAAE,EAAE;gBAC9C,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,QAAQ,IAAI,OAAO,EAAE,KAAK,QAAQ,CAAA;gBAC9D,IAAI,KAAK,EAAE;oBACT,GAAG,CAAC,cAAc,CAAC,aAAa,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;iBAClE;qBAAM;oBACL,GAAG,CAAC,cAAc,CAAC,aAAa,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;iBAClD;YACH,CAAC,CAAA;YACD,0BAA0B;YAC1B,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS,EAAE;gBAC/B,aAAa,CAAC,IAAI,CAAC,CAAA;gBACnB,MAAM,GAAG,IAAI,CAAA;aACd;YACD,6BAA6B;YAC7B,IAAI,QAAQ,CAAC,IAAI,KAAK,IAAI,EAAE;gBAC1B,MAAM,GAAG,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;oBACrC,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,EAAE,CAAA;oBAClC,KAAK,CAAC,GAAG,GAAG,IAAI,CACf;oBAAM,KAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE;wBAC1C,aAAa,CAAM,KAAK,CAAC,CAAA;wBACzB,OAAO,CAAC,IAAI,CAAC,CAAA;oBACf,CAAC,CAAC,CACD;oBAAM,KAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;gBAC/D,CAAC,CAAC,CAAA;aACH;YACD,OAAO,MAAM,CAAA;QACf,CAAC,CAAA;IACH,CAAC;CACF,CAAA;AAED,eAAe,GAAG,EAAE,CAAC,MAAM,CAAA"}

+ 7 - 0
pagesB/comps/u-draw-poster/plugins/drawImage/utils.d.ts

@@ -0,0 +1,7 @@
+/** 是否是base64本地地址 */
+export declare const isBaseUrl: (str: string) => boolean;
+/** 是否是小程序本地地址 */
+export declare const isTmpUrl: (str: string) => boolean;
+/** 是否是网络地址 */
+export declare const isNetworkUrl: (str: string) => boolean;
+export declare const downloadImgUrl: (url: string) => Promise<string>;

+ 27 - 0
pagesB/comps/u-draw-poster/plugins/drawImage/utils.js

@@ -0,0 +1,27 @@
+/** 是否是base64本地地址 */
+export const isBaseUrl = (str) => {
+    return /^\s*data:(?:[a-z]+\/[\d+.a-z-]+(?:;[a-z-]+=[\da-z-]+)?)?(?:;base64)?,([\s\w!$%&'()*+,./:;=?@~-]*?)\s*$/i.test(str);
+};
+/** 是否是小程序本地地址 */
+export const isTmpUrl = (str) => {
+    return /http:\/\/temp\/wx/.test(str);
+};
+/** 是否是网络地址 */
+export const isNetworkUrl = (str) => {
+    return /^(((ht|f)tps?):\/\/)?[\w-]+(\.[\w-]+)+([\w#%&+,./:=?@^~-]*[\w#%&+/=?@^~-])?$/.test(str);
+};
+// 下载指定地址图片, 如果不符合下载图片, 则直接返回
+export const downloadImgUrl = (url) => {
+    const isLocalFile = isBaseUrl(url) || isTmpUrl(url) || !isNetworkUrl(url);
+    return new Promise((resolve, reject) => {
+        if (isLocalFile) {
+            return resolve(url);
+        }
+        uni.downloadFile({
+            url,
+            success: (res) => resolve(res.tempFilePath),
+            fail: reject
+        });
+    });
+};
+//# sourceMappingURL=utils.js.map

+ 1 - 0
pagesB/comps/u-draw-poster/plugins/drawImage/utils.js.map

@@ -0,0 +1 @@
+{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../packages/plugins/drawImage/utils.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAAW,EAAE,EAAE;IACvC,OAAO,yGAAyG,CAAC,IAAI,CACnH,GAAG,CACJ,CAAA;AACH,CAAC,CAAA;AACD,iBAAiB;AACjB,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAE,EAAE;IACtC,OAAO,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACtC,CAAC,CAAA;AACD,cAAc;AACd,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAAW,EAAE,EAAE;IAC1C,OAAO,8EAA8E,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACjG,CAAC,CAAA;AAED,6BAA6B;AAC7B,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,GAAW,EAAmB,EAAE;IAC7D,MAAM,WAAW,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAA;IACzE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,WAAW,EAAE;YACf,OAAO,OAAO,CAAC,GAAG,CAAC,CAAA;SACpB;QACD,GAAG,CAAC,YAAY,CAAC;YACf,GAAG;YACH,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;YAC3C,IAAI,EAAE,MAAM;SACb,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA"}

+ 21 - 0
pagesB/comps/u-draw-poster/plugins/drawImageFit/index.d.ts

@@ -0,0 +1,21 @@
+import { DrawPosterPlugin } from '../../core/plugin';
+import { ObjectFit, ObjectPosition, Size } from './object-sizing';
+declare module '../../core' {
+    interface CanvasCtx {
+        /** 绘制 Object-Fit 模式图片
+         *
+         * 说明文档: https://tuimao233.gitee.io/mao-blog/my-extends/u-draw-poste
+         */
+        drawImageFit(url: string, opts?: ImageFitOption): Promise<boolean>;
+    }
+}
+export interface ImageFitOption {
+    radius?: number;
+    objectFit?: ObjectFit;
+    intrinsicSize?: Size;
+    specifiedSize?: Size;
+    intrinsicPosition?: ObjectPosition;
+    specifiedPosition?: [number, number];
+}
+declare const _default: () => DrawPosterPlugin;
+export default _default;

+ 35 - 0
pagesB/comps/u-draw-poster/plugins/drawImageFit/index.js

@@ -0,0 +1,35 @@
+import { concreteRect } from './object-sizing';
+import { promisify } from '../../utils';
+const plugin = {
+    name: '__ctx-drawImageFit__',
+    mounted: ({ ctx }) => {
+        ctx.drawImageFit = async (url, options) => {
+            const imageInfo = await promisify(uni.getImageInfo)({ src: url });
+            const style = {
+                radius: 0,
+                objectFit: 'cover',
+                intrinsicSize: { width: imageInfo?.width ?? 100, height: imageInfo?.height ?? 100 },
+                specifiedSize: { width: 100, height: 100 },
+                intrinsicPosition: ['center', 'center'],
+                specifiedPosition: [0, 0],
+                ...options
+            };
+            // 计算图片尺寸
+            const drawInfo = concreteRect(style, style.intrinsicSize, style.specifiedSize);
+            // 如有圆角, 则进行裁剪
+            if (style.radius > 0) {
+                ctx.save();
+                ctx.setFillStyle?.('transparent');
+                ctx.fillStyle = 'transparent';
+                ctx.fillRoundRect(style.specifiedPosition[0], style.specifiedPosition[1], style.specifiedSize.width, style.specifiedSize.height, style.radius);
+                ctx.clip();
+            }
+            const result = await ctx.drawImage(url, ...Object.values(drawInfo));
+            if (style.radius > 0)
+                ctx.restore();
+            return result;
+        };
+    }
+};
+export default () => plugin;
+//# sourceMappingURL=index.js.map

+ 1 - 0
pagesB/comps/u-draw-poster/plugins/drawImageFit/index.js.map

@@ -0,0 +1 @@
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../packages/plugins/drawImageFit/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAmC,MAAM,iBAAiB,CAAA;AAC/E,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAkBvC,MAAM,MAAM,GAAqB;IAC/B,IAAI,EAAE,sBAAsB;IAC5B,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE;QACnB,GAAG,CAAC,YAAY,GAAG,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;YACxC,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAA;YACjE,MAAM,KAAK,GAA6B;gBACtC,MAAM,EAAE,CAAC;gBACT,SAAS,EAAE,OAAO;gBAClB,aAAa,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,IAAI,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,IAAI,GAAG,EAAE;gBACnF,aAAa,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE;gBAC1C,iBAAiB,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC;gBACvC,iBAAiB,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;gBACzB,GAAG,OAAO;aACX,CAAA;YACD,SAAS;YACT,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,aAAa,EAAE,KAAK,CAAC,aAAa,CAAC,CAAA;YAC9E,cAAc;YACd,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;gBACpB,GAAG,CAAC,IAAI,EAAE,CAAA;gBACV,GAAG,CAAC,YAAY,EAAE,CAAC,aAAa,CAAC,CAAA;gBACjC,GAAG,CAAC,SAAS,GAAG,aAAa,CAAA;gBAC7B,GAAG,CAAC,aAAa,CACf,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAC1B,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAC1B,KAAK,CAAC,aAAa,CAAC,KAAK,EACzB,KAAK,CAAC,aAAa,CAAC,MAAM,EAC1B,KAAK,CAAC,MAAM,CACb,CAAA;gBACD,GAAG,CAAC,IAAI,EAAE,CAAA;aACX;YACD,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAA;YACnE,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;gBAAE,GAAG,CAAC,OAAO,EAAE,CAAA;YACnC,OAAO,MAAM,CAAA;QACf,CAAC,CAAA;IACH,CAAC;CACF,CAAA;AAED,eAAe,GAAG,EAAE,CAAC,MAAM,CAAA"}

+ 38 - 0
pagesB/comps/u-draw-poster/plugins/drawImageFit/object-sizing.d.ts

@@ -0,0 +1,38 @@
+export declare type ObjectFit = 'contain' | 'cover';
+export declare type ObjectPosition = ['left' | 'center' | 'right', 'top' | 'center' | 'bottom'];
+export interface Size {
+    width: number;
+    height: number;
+}
+/**
+ * 用于计算图片的宽高比例
+ * @see https://drafts.csswg.org/css-images-3/#sizing-terms
+ *
+ * ## 名词解释
+ * ### intrinsic dimensions
+ * 图片本身的尺寸
+ *
+ * ### specified size
+ * 用户指定的元素尺寸
+ *
+ * ### concrete object size
+ * 应用了 `objectFit` 之后图片的显示尺寸
+ *
+ * ### default object size
+ */
+export declare function concreteRect(style: {
+    /** @see https://developer.mozilla.org/zh-CN/docs/Web/CSS/object-fit */
+    objectFit?: ObjectFit;
+    /** @see https://developer.mozilla.org/zh-CN/docs/Web/CSS/object-position */
+    intrinsicPosition?: ObjectPosition;
+    specifiedPosition?: [number, number];
+}, intrinsicSize: Size, specifiedSize: Size): {
+    sx: number;
+    sy: number;
+    sw: number;
+    sh: number;
+    dx: number;
+    dy: number;
+    dw: number;
+    dh: number;
+};

+ 78 - 0
pagesB/comps/u-draw-poster/plugins/drawImageFit/object-sizing.js

@@ -0,0 +1,78 @@
+/**
+ * 用于计算图片的宽高比例
+ * @see https://drafts.csswg.org/css-images-3/#sizing-terms
+ *
+ * ## 名词解释
+ * ### intrinsic dimensions
+ * 图片本身的尺寸
+ *
+ * ### specified size
+ * 用户指定的元素尺寸
+ *
+ * ### concrete object size
+ * 应用了 `objectFit` 之后图片的显示尺寸
+ *
+ * ### default object size
+ */
+export function concreteRect(style, intrinsicSize, specifiedSize) {
+    const isContain = style.objectFit === 'contain';
+    const specifiedPosition = style.specifiedPosition || [0, 0];
+    // ratio 越大表示矩形越"扁"
+    const intrinsicRatio = intrinsicSize.width / intrinsicSize.height;
+    const specifiedRatio = specifiedSize.width / specifiedSize.height;
+    /** 图片原始尺寸与最终尺寸之比 */
+    let concreteScale = 1;
+    if ((intrinsicRatio > specifiedRatio && style.objectFit == 'contain') ||
+        (intrinsicRatio <= specifiedRatio && style.objectFit == 'cover'))
+        // 图片较"胖"时完整显示图片,图片较"瘦"时完全覆盖容器
+        // 这两种情况下有 concreteRect.width = specifiedSize.width
+        // 因为 concreteRect.width = intrinsicSize.width * concreteScale
+        // 所以:
+        concreteScale = specifiedSize.width / intrinsicSize.width;
+    else if ((intrinsicRatio > specifiedRatio && style.objectFit == 'cover') ||
+        (intrinsicRatio <= specifiedRatio && style.objectFit == 'contain'))
+        // 图片较"瘦"时完整显示图片,图片较"胖"时完全覆盖容器
+        // 这两种情况下有 concreteRect.height = specifiedSize.height
+        // 因为 concreteRect.height = intrinsicSize.height * concreteScale
+        // 所以:
+        concreteScale = specifiedSize.height / intrinsicSize.height;
+    else
+        throw new Error('Unkonwn concreteScale');
+    const concreteRectWidth = intrinsicSize.width * concreteScale;
+    const concreteRectHeight = intrinsicSize.height * concreteScale;
+    // 这里可以把 left top 的计算想象成投影
+    const xRelativeOrigin = { left: 0, center: 0.5, right: 1 }[style.intrinsicPosition?.[0] || 'center'];
+    const yRelativeOrigin = { top: 0, center: 0.5, bottom: 1 }[style.intrinsicPosition?.[1] || 'center'];
+    let concreteRectLeft = (specifiedSize.width - concreteRectWidth) * xRelativeOrigin;
+    let concreteRectTop = (specifiedSize.height - concreteRectHeight) * yRelativeOrigin;
+    if (isContain) {
+        concreteRectLeft += specifiedPosition[0];
+        concreteRectTop += specifiedPosition[1];
+    }
+    // 这里有两个坐标系,一个是 specified (dist) 的坐标系,一个是 intrinsic (src) 的坐标系
+    // 这里将两个坐标系的点位置进行变换
+    // 例: 带入 x=0, y=0, 得到的结果就是 specifiedRect 的左上角在 intrinsic 坐标系下的坐标位置
+    // 在 specified 坐标系下, intrinsic 的零点在 (concreteRectLeft, concreteRectTop), 缩放为 concreteScale
+    // 所以有 x_dist = x_src * concreteScale + concreteRectLeft
+    //        y_dist = y_src * concreteScale + concreteRectTop
+    const dist2src = (distX, distY) => [
+        /* srcX = */ (distX - concreteRectLeft) / concreteScale,
+        /* srcY = */ (distY - concreteRectTop) / concreteScale
+    ];
+    const [srcLeft, srcTop] = dist2src(0, 0);
+    // srcRight =  图片 specified 框右边在 src 坐标系下的 x 坐标
+    // srcBottom = 图片 specified 框下边在 src 坐标系下的 y 坐标
+    const [srcRight, srcBottom] = dist2src(specifiedSize.width, specifiedSize.height);
+    // 这里要对 src 和 disc 两个框进行约束
+    return {
+        sx: Math.max(srcLeft, 0),
+        sy: Math.max(srcTop, 0),
+        sw: Math.min(srcRight - srcLeft, intrinsicSize.width),
+        sh: Math.min(srcBottom - srcTop, intrinsicSize.height),
+        dx: isContain ? Math.max(concreteRectLeft, 0) : specifiedPosition[0],
+        dy: isContain ? Math.max(concreteRectTop, 0) : specifiedPosition[1],
+        dw: Math.min(concreteRectWidth, specifiedSize.width),
+        dh: Math.min(concreteRectHeight, specifiedSize.height)
+    };
+}
+//# sourceMappingURL=object-sizing.js.map

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
pagesB/comps/u-draw-poster/plugins/drawImageFit/object-sizing.js.map


+ 28 - 0
pagesB/comps/u-draw-poster/plugins/drawQrCode/index.d.ts

@@ -0,0 +1,28 @@
+import { DrawPosterPlugin } from '../../core/plugin';
+declare module '../../core' {
+    interface CanvasCtx {
+        /** 绘制二维码
+         *
+         * 说明文档: https://tuimao233.gitee.io/mao-blog/my-extends/u-draw-poste
+         */
+        drawQrCode(options: DrawQrCodeOptions): void;
+    }
+}
+/** 绘制二维码配置 */
+export interface DrawQrCodeOptions {
+    text: string;
+    x?: number;
+    y?: number;
+    size?: number;
+    margin?: number;
+    backgroundColor?: string;
+    foregroundColor?: string;
+}
+export declare const errorCorrectLevel: {
+    L: number;
+    M: number;
+    Q: number;
+    H: number;
+};
+declare const _default: () => DrawPosterPlugin;
+export default _default;

+ 12 - 0
pagesB/comps/u-draw-poster/plugins/drawQrCode/index.js

@@ -0,0 +1,12 @@
+import uQRCode from './uQRCode';
+const plugin = {
+    name: '__ctx-drawQrCode__',
+    mounted: ({ ctx, canvas }) => {
+        ctx.drawQrCode = (options) => {
+            uQRCode.make.call(uQRCode, canvas, ctx, options);
+        };
+    }
+};
+export const errorCorrectLevel = uQRCode.errorCorrectLevel;
+export default () => plugin;
+//# sourceMappingURL=index.js.map

+ 1 - 0
pagesB/comps/u-draw-poster/plugins/drawQrCode/index.js.map

@@ -0,0 +1 @@
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../packages/plugins/drawQrCode/index.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,WAAW,CAAA;AAqB/B,MAAM,MAAM,GAAqB;IAC/B,IAAI,EAAE,oBAAoB;IAC1B,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE;QAC3B,GAAG,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,EAAE;YAC3B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,CAAA;QAClD,CAAC,CAAA;IACH,CAAC;CACF,CAAA;AAED,MAAM,CAAC,MAAM,iBAAiB,GAK1B,OAAO,CAAC,iBAAiB,CAAA;AAE7B,eAAe,GAAG,EAAE,CAAC,MAAM,CAAA"}

+ 2 - 0
pagesB/comps/u-draw-poster/plugins/drawQrCode/uQRCode.d.ts

@@ -0,0 +1,2 @@
+export default uQRCode;
+declare let uQRCode: {};

+ 1075 - 0
pagesB/comps/u-draw-poster/plugins/drawQrCode/uQRCode.js

@@ -0,0 +1,1075 @@
+/* eslint-disable */
+//---------------------------------------------------------------------
+// github https://github.com/Sansnn/uQRCode
+//---------------------------------------------------------------------
+let uQRCode = {};
+(function () {
+    //---------------------------------------------------------------------
+    // QRCode for JavaScript
+    //
+    // Copyright (c) 2009 Kazuhiko Arase
+    //
+    // URL: http://www.d-project.com/
+    //
+    // Licensed under the MIT license:
+    //   http://www.opensource.org/licenses/mit-license.php
+    //
+    // The word "QR Code" is registered trademark of 
+    // DENSO WAVE INCORPORATED
+    //   http://www.denso-wave.com/qrcode/faqpatent-e.html
+    //
+    //---------------------------------------------------------------------
+    //---------------------------------------------------------------------
+    // QR8bitByte
+    //---------------------------------------------------------------------
+    function QR8bitByte(data) {
+        this.mode = QRMode.MODE_8BIT_BYTE;
+        this.data = data;
+    }
+    QR8bitByte.prototype = {
+        getLength: function (buffer) {
+            return this.data.length;
+        },
+        write: function (buffer) {
+            for (var i = 0; i < this.data.length; i++) {
+                // not JIS ...
+                buffer.put(this.data.charCodeAt(i), 8);
+            }
+        }
+    };
+    //---------------------------------------------------------------------
+    // QRCode
+    //---------------------------------------------------------------------
+    function QRCode(typeNumber, errorCorrectLevel) {
+        this.typeNumber = typeNumber;
+        this.errorCorrectLevel = errorCorrectLevel;
+        this.modules = null;
+        this.moduleCount = 0;
+        this.dataCache = null;
+        this.dataList = new Array();
+    }
+    QRCode.prototype = {
+        addData: function (data) {
+            var newData = new QR8bitByte(data);
+            this.dataList.push(newData);
+            this.dataCache = null;
+        },
+        isDark: function (row, col) {
+            if (row < 0 || this.moduleCount <= row || col < 0 || this.moduleCount <= col) {
+                throw new Error(row + "," + col);
+            }
+            return this.modules[row][col];
+        },
+        getModuleCount: function () {
+            return this.moduleCount;
+        },
+        make: function () {
+            // Calculate automatically typeNumber if provided is < 1
+            if (this.typeNumber < 1) {
+                var typeNumber = 1;
+                for (typeNumber = 1; typeNumber < 40; typeNumber++) {
+                    var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, this.errorCorrectLevel);
+                    var buffer = new QRBitBuffer();
+                    var totalDataCount = 0;
+                    for (var i = 0; i < rsBlocks.length; i++) {
+                        totalDataCount += rsBlocks[i].dataCount;
+                    }
+                    for (var i = 0; i < this.dataList.length; i++) {
+                        var data = this.dataList[i];
+                        buffer.put(data.mode, 4);
+                        buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber));
+                        data.write(buffer);
+                    }
+                    if (buffer.getLengthInBits() <= totalDataCount * 8)
+                        break;
+                }
+                this.typeNumber = typeNumber;
+            }
+            this.makeImpl(false, this.getBestMaskPattern());
+        },
+        makeImpl: function (test, maskPattern) {
+            this.moduleCount = this.typeNumber * 4 + 17;
+            this.modules = new Array(this.moduleCount);
+            for (var row = 0; row < this.moduleCount; row++) {
+                this.modules[row] = new Array(this.moduleCount);
+                for (var col = 0; col < this.moduleCount; col++) {
+                    this.modules[row][col] = null; //(col + row) % 3;
+                }
+            }
+            this.setupPositionProbePattern(0, 0);
+            this.setupPositionProbePattern(this.moduleCount - 7, 0);
+            this.setupPositionProbePattern(0, this.moduleCount - 7);
+            this.setupPositionAdjustPattern();
+            this.setupTimingPattern();
+            this.setupTypeInfo(test, maskPattern);
+            if (this.typeNumber >= 7) {
+                this.setupTypeNumber(test);
+            }
+            if (this.dataCache == null) {
+                this.dataCache = QRCode.createData(this.typeNumber, this.errorCorrectLevel, this.dataList);
+            }
+            this.mapData(this.dataCache, maskPattern);
+        },
+        setupPositionProbePattern: function (row, col) {
+            for (var r = -1; r <= 7; r++) {
+                if (row + r <= -1 || this.moduleCount <= row + r)
+                    continue;
+                for (var c = -1; c <= 7; c++) {
+                    if (col + c <= -1 || this.moduleCount <= col + c)
+                        continue;
+                    if ((0 <= r && r <= 6 && (c == 0 || c == 6)) ||
+                        (0 <= c && c <= 6 && (r == 0 || r == 6)) ||
+                        (2 <= r && r <= 4 && 2 <= c && c <= 4)) {
+                        this.modules[row + r][col + c] = true;
+                    }
+                    else {
+                        this.modules[row + r][col + c] = false;
+                    }
+                }
+            }
+        },
+        getBestMaskPattern: function () {
+            var minLostPoint = 0;
+            var pattern = 0;
+            for (var i = 0; i < 8; i++) {
+                this.makeImpl(true, i);
+                var lostPoint = QRUtil.getLostPoint(this);
+                if (i == 0 || minLostPoint > lostPoint) {
+                    minLostPoint = lostPoint;
+                    pattern = i;
+                }
+            }
+            return pattern;
+        },
+        createMovieClip: function (target_mc, instance_name, depth) {
+            var qr_mc = target_mc.createEmptyMovieClip(instance_name, depth);
+            var cs = 1;
+            this.make();
+            for (var row = 0; row < this.modules.length; row++) {
+                var y = row * cs;
+                for (var col = 0; col < this.modules[row].length; col++) {
+                    var x = col * cs;
+                    var dark = this.modules[row][col];
+                    if (dark) {
+                        qr_mc.beginFill(0, 100);
+                        qr_mc.moveTo(x, y);
+                        qr_mc.lineTo(x + cs, y);
+                        qr_mc.lineTo(x + cs, y + cs);
+                        qr_mc.lineTo(x, y + cs);
+                        qr_mc.endFill();
+                    }
+                }
+            }
+            return qr_mc;
+        },
+        setupTimingPattern: function () {
+            for (var r = 8; r < this.moduleCount - 8; r++) {
+                if (this.modules[r][6] != null) {
+                    continue;
+                }
+                this.modules[r][6] = (r % 2 == 0);
+            }
+            for (var c = 8; c < this.moduleCount - 8; c++) {
+                if (this.modules[6][c] != null) {
+                    continue;
+                }
+                this.modules[6][c] = (c % 2 == 0);
+            }
+        },
+        setupPositionAdjustPattern: function () {
+            var pos = QRUtil.getPatternPosition(this.typeNumber);
+            for (var i = 0; i < pos.length; i++) {
+                for (var j = 0; j < pos.length; j++) {
+                    var row = pos[i];
+                    var col = pos[j];
+                    if (this.modules[row][col] != null) {
+                        continue;
+                    }
+                    for (var r = -2; r <= 2; r++) {
+                        for (var c = -2; c <= 2; c++) {
+                            if (r == -2 || r == 2 || c == -2 || c == 2 ||
+                                (r == 0 && c == 0)) {
+                                this.modules[row + r][col + c] = true;
+                            }
+                            else {
+                                this.modules[row + r][col + c] = false;
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        setupTypeNumber: function (test) {
+            var bits = QRUtil.getBCHTypeNumber(this.typeNumber);
+            for (var i = 0; i < 18; i++) {
+                var mod = (!test && ((bits >> i) & 1) == 1);
+                this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod;
+            }
+            for (var i = 0; i < 18; i++) {
+                var mod = (!test && ((bits >> i) & 1) == 1);
+                this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
+            }
+        },
+        setupTypeInfo: function (test, maskPattern) {
+            var data = (this.errorCorrectLevel << 3) | maskPattern;
+            var bits = QRUtil.getBCHTypeInfo(data);
+            // vertical		
+            for (var i = 0; i < 15; i++) {
+                var mod = (!test && ((bits >> i) & 1) == 1);
+                if (i < 6) {
+                    this.modules[i][8] = mod;
+                }
+                else if (i < 8) {
+                    this.modules[i + 1][8] = mod;
+                }
+                else {
+                    this.modules[this.moduleCount - 15 + i][8] = mod;
+                }
+            }
+            // horizontal
+            for (var i = 0; i < 15; i++) {
+                var mod = (!test && ((bits >> i) & 1) == 1);
+                if (i < 8) {
+                    this.modules[8][this.moduleCount - i - 1] = mod;
+                }
+                else if (i < 9) {
+                    this.modules[8][15 - i - 1 + 1] = mod;
+                }
+                else {
+                    this.modules[8][15 - i - 1] = mod;
+                }
+            }
+            // fixed module
+            this.modules[this.moduleCount - 8][8] = (!test);
+        },
+        mapData: function (data, maskPattern) {
+            var inc = -1;
+            var row = this.moduleCount - 1;
+            var bitIndex = 7;
+            var byteIndex = 0;
+            for (var col = this.moduleCount - 1; col > 0; col -= 2) {
+                if (col == 6)
+                    col--;
+                while (true) {
+                    for (var c = 0; c < 2; c++) {
+                        if (this.modules[row][col - c] == null) {
+                            var dark = false;
+                            if (byteIndex < data.length) {
+                                dark = (((data[byteIndex] >>> bitIndex) & 1) == 1);
+                            }
+                            var mask = QRUtil.getMask(maskPattern, row, col - c);
+                            if (mask) {
+                                dark = !dark;
+                            }
+                            this.modules[row][col - c] = dark;
+                            bitIndex--;
+                            if (bitIndex == -1) {
+                                byteIndex++;
+                                bitIndex = 7;
+                            }
+                        }
+                    }
+                    row += inc;
+                    if (row < 0 || this.moduleCount <= row) {
+                        row -= inc;
+                        inc = -inc;
+                        break;
+                    }
+                }
+            }
+        }
+    };
+    QRCode.PAD0 = 0xEC;
+    QRCode.PAD1 = 0x11;
+    QRCode.createData = function (typeNumber, errorCorrectLevel, dataList) {
+        var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel);
+        var buffer = new QRBitBuffer();
+        for (var i = 0; i < dataList.length; i++) {
+            var data = dataList[i];
+            buffer.put(data.mode, 4);
+            buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber));
+            data.write(buffer);
+        }
+        // calc num max data.
+        var totalDataCount = 0;
+        for (var i = 0; i < rsBlocks.length; i++) {
+            totalDataCount += rsBlocks[i].dataCount;
+        }
+        if (buffer.getLengthInBits() > totalDataCount * 8) {
+            throw new Error("code length overflow. (" +
+                buffer.getLengthInBits() +
+                ">" +
+                totalDataCount * 8 +
+                ")");
+        }
+        // end code
+        if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
+            buffer.put(0, 4);
+        }
+        // padding
+        while (buffer.getLengthInBits() % 8 != 0) {
+            buffer.putBit(false);
+        }
+        // padding
+        while (true) {
+            if (buffer.getLengthInBits() >= totalDataCount * 8) {
+                break;
+            }
+            buffer.put(QRCode.PAD0, 8);
+            if (buffer.getLengthInBits() >= totalDataCount * 8) {
+                break;
+            }
+            buffer.put(QRCode.PAD1, 8);
+        }
+        return QRCode.createBytes(buffer, rsBlocks);
+    };
+    QRCode.createBytes = function (buffer, rsBlocks) {
+        var offset = 0;
+        var maxDcCount = 0;
+        var maxEcCount = 0;
+        var dcdata = new Array(rsBlocks.length);
+        var ecdata = new Array(rsBlocks.length);
+        for (var r = 0; r < rsBlocks.length; r++) {
+            var dcCount = rsBlocks[r].dataCount;
+            var ecCount = rsBlocks[r].totalCount - dcCount;
+            maxDcCount = Math.max(maxDcCount, dcCount);
+            maxEcCount = Math.max(maxEcCount, ecCount);
+            dcdata[r] = new Array(dcCount);
+            for (var i = 0; i < dcdata[r].length; i++) {
+                dcdata[r][i] = 0xff & buffer.buffer[i + offset];
+            }
+            offset += dcCount;
+            var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
+            var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1);
+            var modPoly = rawPoly.mod(rsPoly);
+            ecdata[r] = new Array(rsPoly.getLength() - 1);
+            for (var i = 0; i < ecdata[r].length; i++) {
+                var modIndex = i + modPoly.getLength() - ecdata[r].length;
+                ecdata[r][i] = (modIndex >= 0) ? modPoly.get(modIndex) : 0;
+            }
+        }
+        var totalCodeCount = 0;
+        for (var i = 0; i < rsBlocks.length; i++) {
+            totalCodeCount += rsBlocks[i].totalCount;
+        }
+        var data = new Array(totalCodeCount);
+        var index = 0;
+        for (var i = 0; i < maxDcCount; i++) {
+            for (var r = 0; r < rsBlocks.length; r++) {
+                if (i < dcdata[r].length) {
+                    data[index++] = dcdata[r][i];
+                }
+            }
+        }
+        for (var i = 0; i < maxEcCount; i++) {
+            for (var r = 0; r < rsBlocks.length; r++) {
+                if (i < ecdata[r].length) {
+                    data[index++] = ecdata[r][i];
+                }
+            }
+        }
+        return data;
+    };
+    //---------------------------------------------------------------------
+    // QRMode
+    //---------------------------------------------------------------------
+    var QRMode = {
+        MODE_NUMBER: 1 << 0,
+        MODE_ALPHA_NUM: 1 << 1,
+        MODE_8BIT_BYTE: 1 << 2,
+        MODE_KANJI: 1 << 3
+    };
+    //---------------------------------------------------------------------
+    // QRErrorCorrectLevel
+    //---------------------------------------------------------------------
+    var QRErrorCorrectLevel = {
+        L: 1,
+        M: 0,
+        Q: 3,
+        H: 2
+    };
+    //---------------------------------------------------------------------
+    // QRMaskPattern
+    //---------------------------------------------------------------------
+    var QRMaskPattern = {
+        PATTERN000: 0,
+        PATTERN001: 1,
+        PATTERN010: 2,
+        PATTERN011: 3,
+        PATTERN100: 4,
+        PATTERN101: 5,
+        PATTERN110: 6,
+        PATTERN111: 7
+    };
+    //---------------------------------------------------------------------
+    // QRUtil
+    //---------------------------------------------------------------------
+    var QRUtil = {
+        PATTERN_POSITION_TABLE: [
+            [],
+            [6, 18],
+            [6, 22],
+            [6, 26],
+            [6, 30],
+            [6, 34],
+            [6, 22, 38],
+            [6, 24, 42],
+            [6, 26, 46],
+            [6, 28, 50],
+            [6, 30, 54],
+            [6, 32, 58],
+            [6, 34, 62],
+            [6, 26, 46, 66],
+            [6, 26, 48, 70],
+            [6, 26, 50, 74],
+            [6, 30, 54, 78],
+            [6, 30, 56, 82],
+            [6, 30, 58, 86],
+            [6, 34, 62, 90],
+            [6, 28, 50, 72, 94],
+            [6, 26, 50, 74, 98],
+            [6, 30, 54, 78, 102],
+            [6, 28, 54, 80, 106],
+            [6, 32, 58, 84, 110],
+            [6, 30, 58, 86, 114],
+            [6, 34, 62, 90, 118],
+            [6, 26, 50, 74, 98, 122],
+            [6, 30, 54, 78, 102, 126],
+            [6, 26, 52, 78, 104, 130],
+            [6, 30, 56, 82, 108, 134],
+            [6, 34, 60, 86, 112, 138],
+            [6, 30, 58, 86, 114, 142],
+            [6, 34, 62, 90, 118, 146],
+            [6, 30, 54, 78, 102, 126, 150],
+            [6, 24, 50, 76, 102, 128, 154],
+            [6, 28, 54, 80, 106, 132, 158],
+            [6, 32, 58, 84, 110, 136, 162],
+            [6, 26, 54, 82, 110, 138, 166],
+            [6, 30, 58, 86, 114, 142, 170]
+        ],
+        G15: (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0),
+        G18: (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0),
+        G15_MASK: (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1),
+        getBCHTypeInfo: function (data) {
+            var d = data << 10;
+            while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) {
+                d ^= (QRUtil.G15 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15)));
+            }
+            return ((data << 10) | d) ^ QRUtil.G15_MASK;
+        },
+        getBCHTypeNumber: function (data) {
+            var d = data << 12;
+            while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) {
+                d ^= (QRUtil.G18 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18)));
+            }
+            return (data << 12) | d;
+        },
+        getBCHDigit: function (data) {
+            var digit = 0;
+            while (data != 0) {
+                digit++;
+                data >>>= 1;
+            }
+            return digit;
+        },
+        getPatternPosition: function (typeNumber) {
+            return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1];
+        },
+        getMask: function (maskPattern, i, j) {
+            switch (maskPattern) {
+                case QRMaskPattern.PATTERN000:
+                    return (i + j) % 2 == 0;
+                case QRMaskPattern.PATTERN001:
+                    return i % 2 == 0;
+                case QRMaskPattern.PATTERN010:
+                    return j % 3 == 0;
+                case QRMaskPattern.PATTERN011:
+                    return (i + j) % 3 == 0;
+                case QRMaskPattern.PATTERN100:
+                    return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0;
+                case QRMaskPattern.PATTERN101:
+                    return (i * j) % 2 + (i * j) % 3 == 0;
+                case QRMaskPattern.PATTERN110:
+                    return ((i * j) % 2 + (i * j) % 3) % 2 == 0;
+                case QRMaskPattern.PATTERN111:
+                    return ((i * j) % 3 + (i + j) % 2) % 2 == 0;
+                default:
+                    throw new Error("bad maskPattern:" + maskPattern);
+            }
+        },
+        getErrorCorrectPolynomial: function (errorCorrectLength) {
+            var a = new QRPolynomial([1], 0);
+            for (var i = 0; i < errorCorrectLength; i++) {
+                a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0));
+            }
+            return a;
+        },
+        getLengthInBits: function (mode, type) {
+            if (1 <= type && type < 10) {
+                // 1 - 9
+                switch (mode) {
+                    case QRMode.MODE_NUMBER:
+                        return 10;
+                    case QRMode.MODE_ALPHA_NUM:
+                        return 9;
+                    case QRMode.MODE_8BIT_BYTE:
+                        return 8;
+                    case QRMode.MODE_KANJI:
+                        return 8;
+                    default:
+                        throw new Error("mode:" + mode);
+                }
+            }
+            else if (type < 27) {
+                // 10 - 26
+                switch (mode) {
+                    case QRMode.MODE_NUMBER:
+                        return 12;
+                    case QRMode.MODE_ALPHA_NUM:
+                        return 11;
+                    case QRMode.MODE_8BIT_BYTE:
+                        return 16;
+                    case QRMode.MODE_KANJI:
+                        return 10;
+                    default:
+                        throw new Error("mode:" + mode);
+                }
+            }
+            else if (type < 41) {
+                // 27 - 40
+                switch (mode) {
+                    case QRMode.MODE_NUMBER:
+                        return 14;
+                    case QRMode.MODE_ALPHA_NUM:
+                        return 13;
+                    case QRMode.MODE_8BIT_BYTE:
+                        return 16;
+                    case QRMode.MODE_KANJI:
+                        return 12;
+                    default:
+                        throw new Error("mode:" + mode);
+                }
+            }
+            else {
+                throw new Error("type:" + type);
+            }
+        },
+        getLostPoint: function (qrCode) {
+            var moduleCount = qrCode.getModuleCount();
+            var lostPoint = 0;
+            // LEVEL1
+            for (var row = 0; row < moduleCount; row++) {
+                for (var col = 0; col < moduleCount; col++) {
+                    var sameCount = 0;
+                    var dark = qrCode.isDark(row, col);
+                    for (var r = -1; r <= 1; r++) {
+                        if (row + r < 0 || moduleCount <= row + r) {
+                            continue;
+                        }
+                        for (var c = -1; c <= 1; c++) {
+                            if (col + c < 0 || moduleCount <= col + c) {
+                                continue;
+                            }
+                            if (r == 0 && c == 0) {
+                                continue;
+                            }
+                            if (dark == qrCode.isDark(row + r, col + c)) {
+                                sameCount++;
+                            }
+                        }
+                    }
+                    if (sameCount > 5) {
+                        lostPoint += (3 + sameCount - 5);
+                    }
+                }
+            }
+            // LEVEL2
+            for (var row = 0; row < moduleCount - 1; row++) {
+                for (var col = 0; col < moduleCount - 1; col++) {
+                    var count = 0;
+                    if (qrCode.isDark(row, col))
+                        count++;
+                    if (qrCode.isDark(row + 1, col))
+                        count++;
+                    if (qrCode.isDark(row, col + 1))
+                        count++;
+                    if (qrCode.isDark(row + 1, col + 1))
+                        count++;
+                    if (count == 0 || count == 4) {
+                        lostPoint += 3;
+                    }
+                }
+            }
+            // LEVEL3
+            for (var row = 0; row < moduleCount; row++) {
+                for (var col = 0; col < moduleCount - 6; col++) {
+                    if (qrCode.isDark(row, col) &&
+                        !qrCode.isDark(row, col + 1) &&
+                        qrCode.isDark(row, col + 2) &&
+                        qrCode.isDark(row, col + 3) &&
+                        qrCode.isDark(row, col + 4) &&
+                        !qrCode.isDark(row, col + 5) &&
+                        qrCode.isDark(row, col + 6)) {
+                        lostPoint += 40;
+                    }
+                }
+            }
+            for (var col = 0; col < moduleCount; col++) {
+                for (var row = 0; row < moduleCount - 6; row++) {
+                    if (qrCode.isDark(row, col) &&
+                        !qrCode.isDark(row + 1, col) &&
+                        qrCode.isDark(row + 2, col) &&
+                        qrCode.isDark(row + 3, col) &&
+                        qrCode.isDark(row + 4, col) &&
+                        !qrCode.isDark(row + 5, col) &&
+                        qrCode.isDark(row + 6, col)) {
+                        lostPoint += 40;
+                    }
+                }
+            }
+            // LEVEL4
+            var darkCount = 0;
+            for (var col = 0; col < moduleCount; col++) {
+                for (var row = 0; row < moduleCount; row++) {
+                    if (qrCode.isDark(row, col)) {
+                        darkCount++;
+                    }
+                }
+            }
+            var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
+            lostPoint += ratio * 10;
+            return lostPoint;
+        }
+    };
+    //---------------------------------------------------------------------
+    // QRMath
+    //---------------------------------------------------------------------
+    var QRMath = {
+        glog: function (n) {
+            if (n < 1) {
+                throw new Error("glog(" + n + ")");
+            }
+            return QRMath.LOG_TABLE[n];
+        },
+        gexp: function (n) {
+            while (n < 0) {
+                n += 255;
+            }
+            while (n >= 256) {
+                n -= 255;
+            }
+            return QRMath.EXP_TABLE[n];
+        },
+        EXP_TABLE: new Array(256),
+        LOG_TABLE: new Array(256)
+    };
+    for (var i = 0; i < 8; i++) {
+        QRMath.EXP_TABLE[i] = 1 << i;
+    }
+    for (var i = 8; i < 256; i++) {
+        QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^
+            QRMath.EXP_TABLE[i - 5] ^
+            QRMath.EXP_TABLE[i - 6] ^
+            QRMath.EXP_TABLE[i - 8];
+    }
+    for (var i = 0; i < 255; i++) {
+        QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i;
+    }
+    //---------------------------------------------------------------------
+    // QRPolynomial
+    //---------------------------------------------------------------------
+    function QRPolynomial(num, shift) {
+        if (num.length == undefined) {
+            throw new Error(num.length + "/" + shift);
+        }
+        var offset = 0;
+        while (offset < num.length && num[offset] == 0) {
+            offset++;
+        }
+        this.num = new Array(num.length - offset + shift);
+        for (var i = 0; i < num.length - offset; i++) {
+            this.num[i] = num[i + offset];
+        }
+    }
+    QRPolynomial.prototype = {
+        get: function (index) {
+            return this.num[index];
+        },
+        getLength: function () {
+            return this.num.length;
+        },
+        multiply: function (e) {
+            var num = new Array(this.getLength() + e.getLength() - 1);
+            for (var i = 0; i < this.getLength(); i++) {
+                for (var j = 0; j < e.getLength(); j++) {
+                    num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j)));
+                }
+            }
+            return new QRPolynomial(num, 0);
+        },
+        mod: function (e) {
+            if (this.getLength() - e.getLength() < 0) {
+                return this;
+            }
+            var ratio = QRMath.glog(this.get(0)) - QRMath.glog(e.get(0));
+            var num = new Array(this.getLength());
+            for (var i = 0; i < this.getLength(); i++) {
+                num[i] = this.get(i);
+            }
+            for (var i = 0; i < e.getLength(); i++) {
+                num[i] ^= QRMath.gexp(QRMath.glog(e.get(i)) + ratio);
+            }
+            // recursive call
+            return new QRPolynomial(num, 0).mod(e);
+        }
+    };
+    //---------------------------------------------------------------------
+    // QRRSBlock
+    //---------------------------------------------------------------------
+    function QRRSBlock(totalCount, dataCount) {
+        this.totalCount = totalCount;
+        this.dataCount = dataCount;
+    }
+    QRRSBlock.RS_BLOCK_TABLE = [
+        // L
+        // M
+        // Q
+        // H
+        // 1
+        [1, 26, 19],
+        [1, 26, 16],
+        [1, 26, 13],
+        [1, 26, 9],
+        // 2
+        [1, 44, 34],
+        [1, 44, 28],
+        [1, 44, 22],
+        [1, 44, 16],
+        // 3
+        [1, 70, 55],
+        [1, 70, 44],
+        [2, 35, 17],
+        [2, 35, 13],
+        // 4		
+        [1, 100, 80],
+        [2, 50, 32],
+        [2, 50, 24],
+        [4, 25, 9],
+        // 5
+        [1, 134, 108],
+        [2, 67, 43],
+        [2, 33, 15, 2, 34, 16],
+        [2, 33, 11, 2, 34, 12],
+        // 6
+        [2, 86, 68],
+        [4, 43, 27],
+        [4, 43, 19],
+        [4, 43, 15],
+        // 7		
+        [2, 98, 78],
+        [4, 49, 31],
+        [2, 32, 14, 4, 33, 15],
+        [4, 39, 13, 1, 40, 14],
+        // 8
+        [2, 121, 97],
+        [2, 60, 38, 2, 61, 39],
+        [4, 40, 18, 2, 41, 19],
+        [4, 40, 14, 2, 41, 15],
+        // 9
+        [2, 146, 116],
+        [3, 58, 36, 2, 59, 37],
+        [4, 36, 16, 4, 37, 17],
+        [4, 36, 12, 4, 37, 13],
+        // 10		
+        [2, 86, 68, 2, 87, 69],
+        [4, 69, 43, 1, 70, 44],
+        [6, 43, 19, 2, 44, 20],
+        [6, 43, 15, 2, 44, 16],
+        // 11
+        [4, 101, 81],
+        [1, 80, 50, 4, 81, 51],
+        [4, 50, 22, 4, 51, 23],
+        [3, 36, 12, 8, 37, 13],
+        // 12
+        [2, 116, 92, 2, 117, 93],
+        [6, 58, 36, 2, 59, 37],
+        [4, 46, 20, 6, 47, 21],
+        [7, 42, 14, 4, 43, 15],
+        // 13
+        [4, 133, 107],
+        [8, 59, 37, 1, 60, 38],
+        [8, 44, 20, 4, 45, 21],
+        [12, 33, 11, 4, 34, 12],
+        // 14
+        [3, 145, 115, 1, 146, 116],
+        [4, 64, 40, 5, 65, 41],
+        [11, 36, 16, 5, 37, 17],
+        [11, 36, 12, 5, 37, 13],
+        // 15
+        [5, 109, 87, 1, 110, 88],
+        [5, 65, 41, 5, 66, 42],
+        [5, 54, 24, 7, 55, 25],
+        [11, 36, 12],
+        // 16
+        [5, 122, 98, 1, 123, 99],
+        [7, 73, 45, 3, 74, 46],
+        [15, 43, 19, 2, 44, 20],
+        [3, 45, 15, 13, 46, 16],
+        // 17
+        [1, 135, 107, 5, 136, 108],
+        [10, 74, 46, 1, 75, 47],
+        [1, 50, 22, 15, 51, 23],
+        [2, 42, 14, 17, 43, 15],
+        // 18
+        [5, 150, 120, 1, 151, 121],
+        [9, 69, 43, 4, 70, 44],
+        [17, 50, 22, 1, 51, 23],
+        [2, 42, 14, 19, 43, 15],
+        // 19
+        [3, 141, 113, 4, 142, 114],
+        [3, 70, 44, 11, 71, 45],
+        [17, 47, 21, 4, 48, 22],
+        [9, 39, 13, 16, 40, 14],
+        // 20
+        [3, 135, 107, 5, 136, 108],
+        [3, 67, 41, 13, 68, 42],
+        [15, 54, 24, 5, 55, 25],
+        [15, 43, 15, 10, 44, 16],
+        // 21
+        [4, 144, 116, 4, 145, 117],
+        [17, 68, 42],
+        [17, 50, 22, 6, 51, 23],
+        [19, 46, 16, 6, 47, 17],
+        // 22
+        [2, 139, 111, 7, 140, 112],
+        [17, 74, 46],
+        [7, 54, 24, 16, 55, 25],
+        [34, 37, 13],
+        // 23
+        [4, 151, 121, 5, 152, 122],
+        [4, 75, 47, 14, 76, 48],
+        [11, 54, 24, 14, 55, 25],
+        [16, 45, 15, 14, 46, 16],
+        // 24
+        [6, 147, 117, 4, 148, 118],
+        [6, 73, 45, 14, 74, 46],
+        [11, 54, 24, 16, 55, 25],
+        [30, 46, 16, 2, 47, 17],
+        // 25
+        [8, 132, 106, 4, 133, 107],
+        [8, 75, 47, 13, 76, 48],
+        [7, 54, 24, 22, 55, 25],
+        [22, 45, 15, 13, 46, 16],
+        // 26
+        [10, 142, 114, 2, 143, 115],
+        [19, 74, 46, 4, 75, 47],
+        [28, 50, 22, 6, 51, 23],
+        [33, 46, 16, 4, 47, 17],
+        // 27
+        [8, 152, 122, 4, 153, 123],
+        [22, 73, 45, 3, 74, 46],
+        [8, 53, 23, 26, 54, 24],
+        [12, 45, 15, 28, 46, 16],
+        // 28
+        [3, 147, 117, 10, 148, 118],
+        [3, 73, 45, 23, 74, 46],
+        [4, 54, 24, 31, 55, 25],
+        [11, 45, 15, 31, 46, 16],
+        // 29
+        [7, 146, 116, 7, 147, 117],
+        [21, 73, 45, 7, 74, 46],
+        [1, 53, 23, 37, 54, 24],
+        [19, 45, 15, 26, 46, 16],
+        // 30
+        [5, 145, 115, 10, 146, 116],
+        [19, 75, 47, 10, 76, 48],
+        [15, 54, 24, 25, 55, 25],
+        [23, 45, 15, 25, 46, 16],
+        // 31
+        [13, 145, 115, 3, 146, 116],
+        [2, 74, 46, 29, 75, 47],
+        [42, 54, 24, 1, 55, 25],
+        [23, 45, 15, 28, 46, 16],
+        // 32
+        [17, 145, 115],
+        [10, 74, 46, 23, 75, 47],
+        [10, 54, 24, 35, 55, 25],
+        [19, 45, 15, 35, 46, 16],
+        // 33
+        [17, 145, 115, 1, 146, 116],
+        [14, 74, 46, 21, 75, 47],
+        [29, 54, 24, 19, 55, 25],
+        [11, 45, 15, 46, 46, 16],
+        // 34
+        [13, 145, 115, 6, 146, 116],
+        [14, 74, 46, 23, 75, 47],
+        [44, 54, 24, 7, 55, 25],
+        [59, 46, 16, 1, 47, 17],
+        // 35
+        [12, 151, 121, 7, 152, 122],
+        [12, 75, 47, 26, 76, 48],
+        [39, 54, 24, 14, 55, 25],
+        [22, 45, 15, 41, 46, 16],
+        // 36
+        [6, 151, 121, 14, 152, 122],
+        [6, 75, 47, 34, 76, 48],
+        [46, 54, 24, 10, 55, 25],
+        [2, 45, 15, 64, 46, 16],
+        // 37
+        [17, 152, 122, 4, 153, 123],
+        [29, 74, 46, 14, 75, 47],
+        [49, 54, 24, 10, 55, 25],
+        [24, 45, 15, 46, 46, 16],
+        // 38
+        [4, 152, 122, 18, 153, 123],
+        [13, 74, 46, 32, 75, 47],
+        [48, 54, 24, 14, 55, 25],
+        [42, 45, 15, 32, 46, 16],
+        // 39
+        [20, 147, 117, 4, 148, 118],
+        [40, 75, 47, 7, 76, 48],
+        [43, 54, 24, 22, 55, 25],
+        [10, 45, 15, 67, 46, 16],
+        // 40
+        [19, 148, 118, 6, 149, 119],
+        [18, 75, 47, 31, 76, 48],
+        [34, 54, 24, 34, 55, 25],
+        [20, 45, 15, 61, 46, 16]
+    ];
+    QRRSBlock.getRSBlocks = function (typeNumber, errorCorrectLevel) {
+        var rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel);
+        if (rsBlock == undefined) {
+            throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + errorCorrectLevel);
+        }
+        var length = rsBlock.length / 3;
+        var list = new Array();
+        for (var i = 0; i < length; i++) {
+            var count = rsBlock[i * 3 + 0];
+            var totalCount = rsBlock[i * 3 + 1];
+            var dataCount = rsBlock[i * 3 + 2];
+            for (var j = 0; j < count; j++) {
+                list.push(new QRRSBlock(totalCount, dataCount));
+            }
+        }
+        return list;
+    };
+    QRRSBlock.getRsBlockTable = function (typeNumber, errorCorrectLevel) {
+        switch (errorCorrectLevel) {
+            case QRErrorCorrectLevel.L:
+                return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0];
+            case QRErrorCorrectLevel.M:
+                return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];
+            case QRErrorCorrectLevel.Q:
+                return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];
+            case QRErrorCorrectLevel.H:
+                return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];
+            default:
+                return undefined;
+        }
+    };
+    //---------------------------------------------------------------------
+    // QRBitBuffer
+    //---------------------------------------------------------------------
+    function QRBitBuffer() {
+        this.buffer = new Array();
+        this.length = 0;
+    }
+    QRBitBuffer.prototype = {
+        get: function (index) {
+            var bufIndex = Math.floor(index / 8);
+            return ((this.buffer[bufIndex] >>> (7 - index % 8)) & 1) == 1;
+        },
+        put: function (num, length) {
+            for (var i = 0; i < length; i++) {
+                this.putBit(((num >>> (length - i - 1)) & 1) == 1);
+            }
+        },
+        getLengthInBits: function () {
+            return this.length;
+        },
+        putBit: function (bit) {
+            var bufIndex = Math.floor(this.length / 8);
+            if (this.buffer.length <= bufIndex) {
+                this.buffer.push(0);
+            }
+            if (bit) {
+                this.buffer[bufIndex] |= (0x80 >>> (this.length % 8));
+            }
+            this.length++;
+        }
+    };
+    //---------------------------------------------------------------------
+    // Support Chinese
+    //---------------------------------------------------------------------
+    function utf16To8(text) {
+        var result = '';
+        var c;
+        for (var i = 0; i < text.length; i++) {
+            c = text.charCodeAt(i);
+            if (c >= 0x0001 && c <= 0x007F) {
+                result += text.charAt(i);
+            }
+            else if (c > 0x07FF) {
+                result += String.fromCharCode(0xE0 | c >> 12 & 0x0F);
+                result += String.fromCharCode(0x80 | c >> 6 & 0x3F);
+                result += String.fromCharCode(0x80 | c >> 0 & 0x3F);
+            }
+            else {
+                result += String.fromCharCode(0xC0 | c >> 6 & 0x1F);
+                result += String.fromCharCode(0x80 | c >> 0 & 0x3F);
+            }
+        }
+        return result;
+    }
+    uQRCode = {
+        errorCorrectLevel: QRErrorCorrectLevel,
+        defaults: {
+            size: 354,
+            margin: 0,
+            backgroundColor: '#ffffff',
+            foregroundColor: '#000000',
+            errorCorrectLevel: QRErrorCorrectLevel.H,
+            typeNumber: -1
+        },
+        make: function (canvas, ctx, options) {
+            var defaultOptions = {
+                x: 0,
+                y: 0,
+                text: options.text,
+                size: this.defaults.size,
+                margin: this.defaults.margin,
+                backgroundColor: this.defaults.backgroundColor,
+                foregroundColor: this.defaults.foregroundColor,
+                errorCorrectLevel: this.defaults.errorCorrectLevel,
+                typeNumber: this.defaults.typeNumber
+            };
+            if (options) {
+                for (var i in options) {
+                    defaultOptions[i] = options[i];
+                }
+            }
+            options = defaultOptions;
+            var qrcode = new QRCode(options.typeNumber, options.errorCorrectLevel);
+            qrcode.addData(utf16To8(options.text));
+            qrcode.make();
+            ctx.fill;
+            ctx.fillStyle = options.backgroundColor;
+            ctx.fillRect(options.x, options.y, options.size, options.size);
+            var tileW = (options.size - options.margin * 2) / qrcode.getModuleCount();
+            var tileH = tileW;
+            for (var row = 0; row < qrcode.getModuleCount(); row++) {
+                for (var col = 0; col < qrcode.getModuleCount(); col++) {
+                    var style = qrcode.isDark(row, col) ? options.foregroundColor : options.backgroundColor;
+                    ctx.fillStyle = style;
+                    var x = Math.round(col * tileW) + options.margin;
+                    var y = Math.round(row * tileH) + options.margin;
+                    var w = Math.ceil((col + 1) * tileW) - Math.floor(col * tileW);
+                    var h = Math.ceil((row + 1) * tileW) - Math.floor(row * tileW);
+                    ctx.fillRect(options.x + x, options.y + y, w, h);
+                }
+            }
+        }
+    };
+})();
+export default uQRCode;
+//# sourceMappingURL=uQRCode.js.map

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
pagesB/comps/u-draw-poster/plugins/drawQrCode/uQRCode.js.map


Некоторые файлы не были показаны из-за большого количества измененных файлов