zhangyuewww 2 年之前
當前提交
4bebb44622
共有 100 個文件被更改,包括 9540 次插入0 次删除
  1. 2 0
      .gitattributes
  2. 78 0
      .gitignore
  3. 145 0
      pom.xml
  4. 22 0
      sendGoodNotPlat.xml
  5. 641 0
      sql/unimall_tmp.sql
  6. 29 0
      uni_applet/App.vue
  7. 234 0
      uni_applet/README.md
  8. 87 0
      uni_applet/common/helper.js
  9. 143 0
      uni_applet/components/ossutil/base64.js
  10. 9 0
      uni_applet/components/ossutil/config.js
  11. 178 0
      uni_applet/components/ossutil/crypto.js
  12. 34 0
      uni_applet/components/ossutil/hmac.js
  13. 79 0
      uni_applet/components/ossutil/sha1.js
  14. 394 0
      uni_applet/components/ossutil/signature.js
  15. 85 0
      uni_applet/components/ossutil/uploadFile.js
  16. 12 0
      uni_applet/config/index.js
  17. 20 0
      uni_applet/index.html
  18. 141 0
      uni_applet/js_sdk/district.js
  19. 52 0
      uni_applet/js_sdk/filterCity.js
  20. 0 0
      uni_applet/js_sdk/js-amap/__MACOSX/amap-wx.130.js
  21. 31 0
      uni_applet/js_sdk/js-amap/amap-wx.130.js
  22. 1122 0
      uni_applet/js_sdk/qqmap-wx-jssdk.js
  23. 0 0
      uni_applet/js_sdk/qqmap-wx-jssdk.min.js
  24. 50 0
      uni_applet/js_sdk/share.js
  25. 272 0
      uni_applet/js_sdk/wa-permission/permission.js
  26. 594 0
      uni_applet/js_sdk/xfl-DateFormat/DateFormat.js
  27. 38 0
      uni_applet/main.js
  28. 25 0
      uni_applet/pages/demo/demo.vue
  29. 112 0
      uni_applet/pages/index/index.vue
  30. 19 0
      uni_applet/pages/my/my.vue
  31. 20 0
      uni_applet/static/customicons.css
  32. 二進制
      uni_applet/static/customicons.ttf
  33. 539 0
      uni_applet/static/icons/demo.css
  34. 257 0
      uni_applet/static/icons/demo_index.html
  35. 27 0
      uni_applet/static/icons/iconfont.css
  36. 0 0
      uni_applet/static/icons/iconfont.js
  37. 二進制
      uni_applet/static/icons/iconfont.ttf
  38. 二進制
      uni_applet/static/icons/iconfont.woff
  39. 二進制
      uni_applet/static/icons/iconfont.woff2
  40. 二進制
      uni_applet/static/imgs/bz.png
  41. 二進制
      uni_applet/static/imgs/cirlcle.png
  42. 二進制
      uni_applet/static/imgs/home.png
  43. 二進制
      uni_applet/static/imgs/location.png
  44. 二進制
      uni_applet/static/imgs/logo.png
  45. 二進制
      uni_applet/static/imgs/map.png
  46. 二進制
      uni_applet/static/imgs/messege.png
  47. 二進制
      uni_applet/static/imgs/mic.png
  48. 二進制
      uni_applet/static/imgs/mic1.png
  49. 二進制
      uni_applet/static/imgs/mic2.png
  50. 二進制
      uni_applet/static/imgs/mr.png
  51. 二進制
      uni_applet/static/imgs/my.png
  52. 二進制
      uni_applet/static/imgs/notavailable.png
  53. 二進制
      uni_applet/static/imgs/phone.png
  54. 二進制
      uni_applet/static/imgs/scan.png
  55. 二進制
      uni_applet/static/imgs/scancode.png
  56. 二進制
      uni_applet/static/imgs/scirlce.png
  57. 二進制
      uni_applet/static/imgs/search.png
  58. 二進制
      uni_applet/static/imgs/shade.png
  59. 二進制
      uni_applet/static/imgs/share.png
  60. 二進制
      uni_applet/static/imgs/smy.png
  61. 二進制
      uni_applet/static/imgs/stop.png
  62. 二進制
      uni_applet/static/imgs/swjj.png
  63. 二進制
      uni_applet/static/imgs/uni.png
  64. 二進制
      uni_applet/static/imgs/voice.png
  65. 二進制
      uni_applet/static/imgs/wjj.png
  66. 二進制
      uni_applet/static/imgs/xjt.png
  67. 28 0
      uni_applet/static/styles/index.scss
  68. 109 0
      uni_applet/store/index.js
  69. 3 0
      uni_applet/style/icons/iconfont.css
  70. 2 0
      uni_applet/uni.scss
  71. 10 0
      uni_applet/uni_modules/d-search-log/changelog.md
  72. 365 0
      uni_applet/uni_modules/d-search-log/components/d-search-log/d-search-log.vue
  73. 85 0
      uni_applet/uni_modules/d-search-log/components/d-search-log/pages/index/index.vue
  74. 二進制
      uni_applet/uni_modules/d-search-log/components/d-search-log/static/icon_img/del.png
  75. 二進制
      uni_applet/uni_modules/d-search-log/components/d-search-log/static/icon_img/del2.png
  76. 二進制
      uni_applet/uni_modules/d-search-log/components/d-search-log/static/icon_img/search.png
  77. 二進制
      uni_applet/uni_modules/d-search-log/components/d-search-log/static/icon_img/yan_close.png
  78. 二進制
      uni_applet/uni_modules/d-search-log/components/d-search-log/static/icon_img/yan_open.png
  79. 110 0
      uni_applet/uni_modules/d-search-log/readme.md
  80. 6 0
      uni_applet/uni_modules/lyuan-tx-asr/changelog.md
  81. 37 0
      uni_applet/uni_modules/lyuan-tx-asr/components/lyuan-tx-asr/asrauthentication.js
  82. 273 0
      uni_applet/uni_modules/lyuan-tx-asr/components/lyuan-tx-asr/cryptojs.js
  83. 374 0
      uni_applet/uni_modules/lyuan-tx-asr/components/lyuan-tx-asr/lyuan-tx-asr.vue
  84. 99 0
      uni_applet/uni_modules/lyuan-tx-asr/readme.md
  85. 6 0
      uni_applet/uni_modules/mescroll-uni/changelog.md
  86. 19 0
      uni_applet/uni_modules/mescroll-uni/components/mescroll-body/mescroll-body.css
  87. 400 0
      uni_applet/uni_modules/mescroll-uni/components/mescroll-body/mescroll-body.vue
  88. 47 0
      uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/beibei/components/mescroll-down.css
  89. 39 0
      uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/beibei/components/mescroll-down.vue
  90. 360 0
      uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/beibei/mescroll-body.vue
  91. 49 0
      uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/beibei/mescroll-uni-option.js
  92. 437 0
      uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/beibei/mescroll-uni.vue
  93. 44 0
      uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/xinlang/components/mescroll-down.css
  94. 53 0
      uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/xinlang/components/mescroll-down.vue
  95. 32 0
      uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/xinlang/components/mescroll-up.css
  96. 40 0
      uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/xinlang/components/mescroll-up.vue
  97. 380 0
      uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/xinlang/mescroll-body.vue
  98. 64 0
      uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/xinlang/mescroll-uni-option.js
  99. 462 0
      uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/xinlang/mescroll-uni.vue
  100. 116 0
      uni_applet/uni_modules/mescroll-uni/components/mescroll-empty/mescroll-empty.vue

+ 2 - 0
.gitattributes

@@ -0,0 +1,2 @@
+*.sql linguist-language=java
+*.html linguist-language=java

+ 78 - 0
.gitignore

@@ -0,0 +1,78 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+#/build/
+
+### VS Code ###
+.vscode/
+
+### maven ###
+.mvn
+mvnw
+mvnw.cmd
+
+###properties###
+/unimall-launcher/src/main/resources/application-dev.properties
+/unimall-launcher/src/main/resources/application-prd.properties
+
+### target ###
+/target/
+/unimall-admin-api/target/
+/unimall-app-api/target/
+/unimall-biz/target/
+/unimall-data/target/
+/unimall-core/target/
+/unimall-launcher/target/
+/unimall-plugin-core/target/
+/unimall-app/unpackage/dist/
+/unimall-app/unpackage/resources/
+/unimall-app/unpackage/debug/
+/unimall-admin/dist.zip
+
+###node_modules###
+/unimall-admin/node_modules/
+/unimall-app/node_modules/
+
+
+###logs###
+unimall.log
+unimall.log.*.gz
+unimall.log.*.tmp
+
+
+### plugins ###
+/plugins/
+/unimall-app/pages/plugins/
+/unimall-admin/src/views/plugins/
+
+### xcx ###
+.map/
+/unpackage/
+*.wxss
+*.json
+*.wxml
+xiaochengxu/unpackage/

+ 145 - 0
pom.xml

@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<packaging>pom</packaging>
+	<parent>
+		<groupId>org.springframework.boot</groupId>
+		<artifactId>spring-boot-starter-parent</artifactId>
+		<version>2.1.2.RELEASE</version>
+		<relativePath/> <!-- lookup parent from repository -->
+	</parent>
+	<groupId>com.iotechn</groupId>
+	<artifactId>unimall</artifactId>
+	<version>0.0.1-RELEASE</version>
+	<name>unimall</name>
+	<description>App,MiniProgram</description>
+
+	<properties>
+		<java.version>1.8</java.version>
+	</properties>
+
+	<repositories>
+		<repository>
+			<id>maven-ali</id>
+			<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
+			<releases>
+				<enabled>true</enabled>
+			</releases>
+			<snapshots>
+				<enabled>true</enabled>
+				<updatePolicy>always</updatePolicy>
+				<checksumPolicy>fail</checksumPolicy>
+			</snapshots>
+		</repository>
+	</repositories>
+
+	<modules>
+		<module>unimall-core</module>
+		<module>unimall-data</module>
+		<module>unimall-biz</module>
+		<module>unimall-app-api</module>
+		<module>unimall-admin-api</module>
+		<module>unimall-launcher</module>
+		<module>unimall-plugin-core</module>
+	</modules>
+
+	<dependencyManagement>
+		<dependencies>
+
+			<!-- http客户端 -->
+			<dependency>
+				<groupId>com.squareup.okhttp3</groupId>
+				<artifactId>okhttp</artifactId>
+				<version>3.10.0</version>
+			</dependency>
+
+			<!-- 良心云 sms -->
+			<dependency>
+				<groupId>com.github.qcloudsms</groupId>
+				<artifactId>qcloudsms</artifactId>
+				<version>1.0.5</version>
+			</dependency>
+
+			<!-- 阿里云 SMS -->
+			<dependency>
+				<groupId>com.aliyun</groupId>
+				<artifactId>aliyun-java-sdk-core</artifactId>
+				<version>4.1.1</version>
+			</dependency>
+
+			<!-- 阿里云对象存储 -->
+			<dependency>
+				<groupId>com.aliyun.oss</groupId>
+				<artifactId>aliyun-sdk-oss</artifactId>
+				<version>2.8.3</version>
+			</dependency>
+
+			<!-- mybatis plus 依赖 -->
+			<dependency>
+				<groupId>com.baomidou</groupId>
+				<artifactId>mybatis-plus-boot-starter</artifactId>
+				<version>2.2.0</version>
+			</dependency>
+
+			<dependency>
+				<groupId>com.baomidou</groupId>
+				<artifactId>mybatis-plus-support</artifactId>
+				<version>2.2.0</version>
+			</dependency>
+
+			<!-- mysql -->
+			<dependency>
+				<groupId>mysql</groupId>
+				<artifactId>mysql-connector-java</artifactId>
+				<version>8.0.15</version>
+			</dependency>
+
+			<!-- 阿里巴巴 数据源 Druid -->
+			<dependency>
+				<groupId>com.alibaba</groupId>
+				<artifactId>druid</artifactId>
+				<version>1.1.8</version>
+			</dependency>
+
+			<!-- lombok 工具 -->
+			<dependency>
+				<groupId>org.projectlombok</groupId>
+				<artifactId>lombok</artifactId>
+				<version>1.18.4</version>
+				<scope>provided</scope>
+			</dependency>
+
+			<!-- 阿里巴巴 json 解析 -->
+			<dependency>
+				<groupId>com.alibaba</groupId>
+				<artifactId>fastjson</artifactId>
+				<version>1.2.31</version>
+			</dependency>
+
+			<!-- 微信第三方封装Api -->
+			<dependency>
+				<groupId>com.github.binarywang</groupId>
+				<artifactId>weixin-java-pay</artifactId>
+				<version>3.3.0</version>
+				<exclusions>
+					<exclusion>
+						<artifactId>qrcode-utils</artifactId>
+						<groupId>com.github.binarywang</groupId>
+					</exclusion>
+				</exclusions>
+			</dependency>
+
+		</dependencies>
+	</dependencyManagement>
+
+	<!-- 发布maven私服 -->
+	<!--<distributionManagement>-->
+		<!--<repository>-->
+			<!--<id>iotechn-release</id>-->
+			<!--<name>unimall maven</name>-->
+			<!--<url>http://maven.iotechn.com/repository/maven-releases/</url>-->
+		<!--</repository>-->
+	<!--</distributionManagement>-->
+
+</project>

+ 22 - 0
sendGoodNotPlat.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="GBK"?>
+<request>
+    <functionID>dangdang.order.goods.send</functionID>
+    <time>2022-07-11 09:32:13</time>
+    <OrdersList>
+        <OrderInfo>
+            <orderID>45018706599</orderID>
+            <logisticsName>¼«ÍÃËÙµÝ</logisticsName>
+            <logisticsNameAb>JT</logisticsNameAb>
+            <logisticsTel>400-820-1666</logisticsTel>
+            <logisticsOrderID>JT3007364146966</logisticsOrderID>
+            <SendGoodsList>
+                <ItemInfo>
+                    <itemID>11162110282</itemID>
+                    <sendGoodsCount>1</sendGoodsCount>
+                    <belongProductsPromoID></belongProductsPromoID>
+                    <productItemId>45018706599001</productItemId>
+                </ItemInfo>
+            </SendGoodsList>
+        </OrderInfo>
+    </OrdersList>
+</request>

文件差異過大導致無法顯示
+ 641 - 0
sql/unimall_tmp.sql


+ 29 - 0
uni_applet/App.vue

@@ -0,0 +1,29 @@
+<script>
+	export default {
+
+		onLaunch: function() {
+			console.warn('当前组件仅支持 uni_modules 目录结构 ,请升级 HBuilderX 到 3.1.0 版本以上!')
+			console.log('App Launch')
+
+		},
+		onShow: function() {
+			console.log('App Show')
+		},
+		onHide: function() {
+			console.log('App Hide')
+		}
+	}
+</script>
+
+<style lang="scss">
+	/*每个页面公共css */
+	@import "@/uni_modules/uview-ui/index.scss";
+	@import '@/uni_modules/uni-scss/index.scss';
+	@import '@/static/styles/index.scss';
+	@import '@/style/icons/iconfont.css';
+	// 设置整个项目的背景色
+	page {
+		background-color: #f5f5f5;
+	}
+	
+</style>

+ 234 - 0
uni_applet/README.md

@@ -0,0 +1,234 @@
+## 上传图片
+
+  ```
+  <u-upload :fileList="fileList1" @afterRead="afterRead" @delete="deletePic" name="1" multiple :maxCount="1"></u-upload>
+import uploadImage from '@/components/ossutil/uploadFile.js';
+// 删除图片
+			deletePic(event) {
+				this[`fileList${event.name}`].splice(event.index, 1)
+			},
+			// 新增图片
+			async afterRead(event) {
+				// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
+				let lists = [].concat(event.file)
+				let fileListLen = this[`fileList${event.name}`].length
+				lists.map((item) => {
+					this[`fileList${event.name}`].push({
+						...item,
+						status: 'uploading',
+						message: '上传中'
+					})
+				})
+				for (let i = 0; i < lists.length; i++) {
+					const result = await this.uploadFilePromise(lists[i].url)
+					let item = this[`fileList${event.name}`][fileListLen]
+					this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
+						status: 'success',
+						message: '',
+						url: result
+					}))
+					fileListLen++
+				}
+			},
+			uploadFilePromise(res) {
+				return new Promise((resolve, reject) => {
+					uploadImage(res, 'cardImages/',
+						result => {
+							that.cardInfo.headSculpture = result
+							resolve(res)
+						}
+					)
+				})
+			}
+  ```
+
+## 上拉加载、下拉刷新
+
+```
+<mescroll-body ref="mescrollRef" @init="mescrollInit" @up="upCallback" @down="downCallback"></mescroll-body>
+import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
+export default {
+		mixins: [MescrollMixin],
+}
+
+data(){
+	return {
+		canReset:false
+	}
+}
+methods:{
+	mescrollInit(mescroll) {
+		this.mescroll = mescroll;
+	},
+	downCallback() {
+		this.mescroll.resetUpScroll()
+	},
+	upCallback(page) {
+		uni.showLoading({
+			title: '数据加载中'
+		})
+		this.$request.baseRequest('admin.unimall.circleManagementInfo', 'list', {
+			commonId: this.userInfo.id,
+			pageNum: page.num,
+			pageSize: page.size,
+		}, failres => {
+			console.log('res+++++', failres.errmsg)
+			this.$refs.uToast.show({
+				type: 'error',
+				message: failres.errmsg,
+			})
+			uni.hideLoading()
+		}).then(res => {
+			uni.hideLoading()
+			let curPageData = res.data.items;
+			let totalPage = res.data.total;
+			let curPageLen = curPageData.length;
+			this.mescroll.endByPage(curPageLen, totalPage);
+			console.log(res.data)
+			if (page.num == 1) this.circleList = []; //如果是第一页需手动置空列表
+			this.circleList = this.circleList.concat(curPageData); //追加新数据
+	},
+}
+
+onShow(){
+	this.$nextTick(function() {
+		this.canReset && this.mescroll.resetUpScroll() // 重置列表数据为第一页  
+		this.canReset && this.mescroll.scrollTo(0, 0) // 重置列表数据为第一页时,建议把滚动条也重置到顶部,避免无法再次翻页的问题  
+		this.canReset = true // 过滤第一次的onShow事件,避免初始化界面时重复触发upCallback, 无需配置auto:false
+	});
+}
+
+重置列表数据 this.mescroll.resetUpScroll()
+```
+
+## 工具类
+
+```
+1、时间戳格式化
+parseTime(gmtCreate)
+2、地址拆分省市区详细地址
+let _address = this.$helper.formatLocation("辽宁省营口市鲅鱼圈区日月大道8号")
+
+```
+
+## U-view 常用
+
+```
+1、toast提示
+<u-toast ref="uToast"></u-toast>
+this.$refs.uToast.show({
+	type: 'error/success',
+	message: "message",
+	complete() {
+	params.url && uni.navigateTo({
+		url: params.url
+	})
+}
+})
+
+2、Modal 模态框
+<template>
+	<view >
+		<u-modal :show="show" :title="title" :content='content' closeOnClickOverlay @confirm="confirm" @cancel="show==false" @close="show==false"></u-modal>
+		<u-button @click="show = true">打开</u-button>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			show:false,
+			title:'标题',
+			content:'uView的目标是成为uni-app生态最优秀的UI框架'
+		};
+	}
+};
+</script>
+
+```
+
+## 同步请求
+
+```
+async upCallback(page) {
+	await this.$request.baseRequest('admin.unimall.cardManagementInfo', 'list', {
+		commonId: this.userInfo.id
+	}, failres => {
+		console.log('res+++++', failres.errmsg)
+		this.$refs.uToast.show({
+			type: 'error',
+			message: failres.errmsg,
+		})
+	}).then(res => {
+		this.cardList = [res.data.items]
+	})
+
+	await this.$request.baseRequest('admin.unimall.circleManagementInfo', 'get', {
+		commonId: this.userInfo.id,
+		id: this.id
+	}, failres => {
+		console.log('res+++++', failres.errmsg)
+		this.$refs.uToast.show({
+			type: 'error',
+			message: failres.errmsg,
+		})
+		uni.hideLoading()
+	}).then(res => {
+		console.log(res.data)
+		this.dataObj = res.data
+
+	})
+	this.getCardList(page)
+},
+```
+
+## 公共样式
+```
+static/styles/index.scss
+
+.flex{
+	display: flex;
+	align-items: center;
+}
+.flex-row-center{
+	display: flex;
+	justify-content: center;
+}
+.flex-all-center{
+	display: flex;
+	justify-content: center;
+	align-items: center;
+}
+.flex-between{
+	display: flex;
+	justify-content: space-between;
+}
+.flex-evenly{
+	display: flex;
+	justify-content: space-evenly;
+}
+
+.relative{
+	position: relative;
+}
+.absolute{
+	position: absolute;
+}
+
+
+```
+
+## 阻止冒泡
+```
+@click.native.stop="delSearchVal"
+```
+
+## 同步请求
+```
+参考util下request.js
+调用
+async xxx(){
+	let _xx = await this.$request.syncInfo(this.userInfo)
+}
+```

+ 87 - 0
uni_applet/common/helper.js

@@ -0,0 +1,87 @@
+import * as config from '../config'
+const ossUploadUrl = 'https://taohaoliang.oss-cn-beijing.aliyuncs.com/';
+
+const contactCustomerService = (item) => {
+	console.log(item)
+	if (!item) {
+		item = '110'
+	}
+	console.log("联系客服")
+	uni.makePhoneCall({
+		phoneNumber: item,
+		success: (res) => {
+			console.log('调用成功!')
+		},
+		// 失败回调
+		fail: (res) => {
+			console.log('调用失败!')
+			contactCustomerService(item)
+		}
+	});
+}
+
+const logout = () => {
+	uni.removeStorage({
+		key: 'userInfo'
+	})
+}
+const formatLocation = (str) => {
+	let area = {}
+	let index11 = 0
+	let index1 = str.indexOf("省")
+	if (index1 == -1) {
+		index11 = str.indexOf("自治区")
+		if (index11 != -1) {
+			area.Province = str.substring(0, index11 + 3)
+		} else {
+			area.Province = str.substring(0, 0)
+		}
+	} else {
+		area.Province = str.substring(0, index1 + 1)
+	}
+
+	let index2 = str.indexOf("市")
+	if (index11 == -1) {
+		area.City = str.substring(index11 + 1, index2 + 1)
+	} else {
+		if (index11 == 0) {
+			area.City = str.substring(index1 + 1, index2 + 1)
+		} else {
+			area.City = str.substring(index11 + 3, index2 + 1)
+		}
+	}
+
+	let index3 = str.indexOf("区")
+	if (index3 == -1) {
+		index3 = str.indexOf("县")
+		if (index3 == -1) {
+			index3 = str.lastIndexOf("市")
+			if (index3 == -1) {
+				index3 = str.indexOf("镇")
+				if (index3 == -1) {
+
+				} else {
+					area.Country = str.substring(index2 + 1, index3 + 1)
+
+				}
+			} else {
+				area.Country = str.substring(index2 + 1, index3 + 1)
+			}
+		} else {
+			area.Country = str.substring(index2 + 1, index3 + 1)
+		}
+
+	} else {
+		area.Country = str.substring(index2 + 1, index3 + 1)
+	}
+	area.Village = str.substring(index3 + 1, str.length)
+	return area;
+}
+
+
+
+export default {
+logout,
+contactCustomerService,
+formatLocation
+}

+ 143 - 0
uni_applet/components/ossutil/base64.js

@@ -0,0 +1,143 @@
+
+var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+var base64DecodeChars = new Array(
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+    -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1);
+function encode(str) {
+    var out, i, len;
+    var c1, c2, c3;
+    len = str.length;
+    i = 0;
+    out = "";
+    while (i < len) {
+        c1 = str.charCodeAt(i++) & 0xff;
+        if (i == len) {
+            out += base64EncodeChars.charAt(c1 >> 2);
+            out += base64EncodeChars.charAt((c1 & 0x3) << 4);
+            out += "==";
+            break;
+        }
+        c2 = str.charCodeAt(i++);
+        if (i == len) {
+            out += base64EncodeChars.charAt(c1 >> 2);
+            out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
+            out += base64EncodeChars.charAt((c2 & 0xF) << 2);
+            out += "=";
+            break;
+        }
+        c3 = str.charCodeAt(i++);
+        out += base64EncodeChars.charAt(c1 >> 2);
+        out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
+        out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
+        out += base64EncodeChars.charAt(c3 & 0x3F);
+    }
+    return out;
+}
+function decode(str) {
+    var c1, c2, c3, c4;
+    var i, len, out;
+    len = str.length;
+    i = 0;
+    out = "";
+    while (i < len) {
+        /* c1 */
+        do {
+            c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
+        } while (i < len && c1 == -1);
+        if (c1 == -1)
+            break;
+        /* c2 */
+        do {
+            c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
+        } while (i < len && c2 == -1);
+        if (c2 == -1)
+            break;
+        out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
+        /* c3 */
+        do {
+            c3 = str.charCodeAt(i++) & 0xff;
+            if (c3 == 61)
+                return out;
+            c3 = base64DecodeChars[c3];
+        } while (i < len && c3 == -1);
+        if (c3 == -1)
+            break;
+        out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2));
+        /* c4 */
+        do {
+            c4 = str.charCodeAt(i++) & 0xff;
+            if (c4 == 61)
+                return out;
+            c4 = base64DecodeChars[c4];
+        } while (i < len && c4 == -1);
+        if (c4 == -1)
+            break;
+        out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
+    }
+    return out;
+}
+
+
+function utf16to8(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;
+}
+function utf8to16(str) {
+    var out, i, len, c;
+    var char2, char3;
+    out = "";
+    len = str.length;
+    i = 0;
+    while (i < len) {
+        c = str.charCodeAt(i++);
+        switch (c >> 4) {
+            case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+                // 0xxxxxxx
+                out += str.charAt(i - 1);
+                break;
+            case 12: case 13:
+                // 110x xxxx 10xx xxxx
+                char2 = str.charCodeAt(i++);
+                out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
+                break;
+            case 14:
+                // 1110 xxxx 10xx xxxx 10xx xxxx
+                char2 = str.charCodeAt(i++);
+                char3 = str.charCodeAt(i++);
+                out += String.fromCharCode(((c & 0x0F) << 12) |
+                    ((char2 & 0x3F) << 6) |
+                    ((char3 & 0x3F) << 0));
+                break;
+        }
+    }
+    return out;
+}
+
+
+module.exports = {
+    encode: encode,
+    decode: decode,
+    utf16to8: utf16to8,
+    utf8to16: utf8to16
+}

+ 9 - 0
uni_applet/components/ossutil/config.js

@@ -0,0 +1,9 @@
+var fileHost = 'https://taohaoliang.oss-cn-beijing.aliyuncs.com';//你的阿里云地址最后面跟上一个/   在你当前小程序的后台的uploadFile 合法域名也要配上这个域名
+var config = {
+   //aliyun OSS config
+  uploadImageUrl: `${fileHost}`, // 默认存在根目录,可根据需求改
+  AccessKeySecret: 'FpClTp4OVrRRtHEfi3lBOWUoLxKieW',        // AccessKeySecret 去你的阿里云上控制台上找
+  OSSAccessKeyId: 'LTAI4G9c14PgKvM23WZ9zrpc',         // AccessKeyId 去你的阿里云上控制台上找
+   timeout: 87600 //这个是上传文件时Policy的失效时间
+};
+module.exports = config

+ 178 - 0
uni_applet/components/ossutil/crypto.js

@@ -0,0 +1,178 @@
+const Crypto = {};
+
+(function(){
+
+var base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+
+// Crypto utilities
+var util = Crypto.util = {
+
+	// Bit-wise rotate left
+	rotl: function (n, b) {
+		return (n << b) | (n >>> (32 - b));
+	},
+
+	// Bit-wise rotate right
+	rotr: function (n, b) {
+		return (n << (32 - b)) | (n >>> b);
+	},
+
+	// Swap big-endian to little-endian and vice versa
+	endian: function (n) {
+
+		// If number given, swap endian
+		if (n.constructor == Number) {
+			return util.rotl(n,  8) & 0x00FF00FF |
+			       util.rotl(n, 24) & 0xFF00FF00;
+		}
+
+		// Else, assume array and swap all items
+		for (var i = 0; i < n.length; i++)
+			n[i] = util.endian(n[i]);
+		return n;
+
+	},
+
+	// Generate an array of any length of random bytes
+	randomBytes: function (n) {
+		for (var bytes = []; n > 0; n--)
+			bytes.push(Math.floor(Math.random() * 256));
+		return bytes;
+	},
+
+	// Convert a string to a byte array
+	stringToBytes: function (str) {
+		var bytes = [];
+		for (var i = 0; i < str.length; i++)
+			bytes.push(str.charCodeAt(i));
+		return bytes;
+	},
+
+	// Convert a byte array to a string
+	bytesToString: function (bytes) {
+		var str = [];
+		for (var i = 0; i < bytes.length; i++)
+			str.push(String.fromCharCode(bytes[i]));
+		return str.join("");
+	},
+
+	// Convert a string to big-endian 32-bit words
+	stringToWords: function (str) {
+		var words = [];
+		for (var c = 0, b = 0; c < str.length; c++, b += 8)
+			words[b >>> 5] |= str.charCodeAt(c) << (24 - b % 32);
+		return words;
+	},
+
+	// Convert a byte array to big-endian 32-bits words
+	bytesToWords: function (bytes) {
+		var words = [];
+		for (var i = 0, b = 0; i < bytes.length; i++, b += 8)
+			words[b >>> 5] |= bytes[i] << (24 - b % 32);
+		return words;
+	},
+
+	// Convert big-endian 32-bit words to a byte array
+	wordsToBytes: function (words) {
+		var bytes = [];
+		for (var b = 0; b < words.length * 32; b += 8)
+			bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
+		return bytes;
+	},
+
+	// Convert a byte array to a hex string
+	bytesToHex: function (bytes) {
+		var hex = [];
+		for (var i = 0; i < bytes.length; i++) {
+			hex.push((bytes[i] >>> 4).toString(16));
+			hex.push((bytes[i] & 0xF).toString(16));
+		}
+		return hex.join("");
+	},
+
+	// Convert a hex string to a byte array
+	hexToBytes: function (hex) {
+		var bytes = [];
+		for (var c = 0; c < hex.length; c += 2)
+			bytes.push(parseInt(hex.substr(c, 2), 16));
+		return bytes;
+	},
+
+	// Convert a byte array to a base-64 string
+	bytesToBase64: function (bytes) {
+
+		// Use browser-native function if it exists
+		// if (typeof btoa == "function") return btoa(util.bytesToString(bytes));
+
+		var base64 = [],
+		    overflow;
+
+		for (var i = 0; i < bytes.length; i++) {
+			switch (i % 3) {
+				case 0:
+					base64.push(base64map.charAt(bytes[i] >>> 2));
+					overflow = (bytes[i] & 0x3) << 4;
+					break;
+				case 1:
+					base64.push(base64map.charAt(overflow | (bytes[i] >>> 4)));
+					overflow = (bytes[i] & 0xF) << 2;
+					break;
+				case 2:
+					base64.push(base64map.charAt(overflow | (bytes[i] >>> 6)));
+					base64.push(base64map.charAt(bytes[i] & 0x3F));
+					overflow = -1;
+			}
+		}
+
+		// Encode overflow bits, if there are any
+		if (overflow != undefined && overflow != -1)
+			base64.push(base64map.charAt(overflow));
+
+		// Add padding
+		while (base64.length % 4 != 0) base64.push("=");
+
+		return base64.join("");
+
+	},
+
+	// Convert a base-64 string to a byte array
+	base64ToBytes: function (base64) {
+
+		// Use browser-native function if it exists
+		if (typeof atob == "function") return util.stringToBytes(atob(base64));
+
+		// Remove non-base-64 characters
+		base64 = base64.replace(/[^A-Z0-9+\/]/ig, "");
+
+		var bytes = [];
+
+		for (var i = 0; i < base64.length; i++) {
+			switch (i % 4) {
+				case 1:
+					bytes.push((base64map.indexOf(base64.charAt(i - 1)) << 2) |
+					           (base64map.indexOf(base64.charAt(i)) >>> 4));
+					break;
+				case 2:
+					bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & 0xF) << 4) |
+					           (base64map.indexOf(base64.charAt(i)) >>> 2));
+					break;
+				case 3:
+					bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & 0x3) << 6) |
+					           (base64map.indexOf(base64.charAt(i))));
+					break;
+			}
+		}
+
+		return bytes;
+
+	}
+
+};
+
+// Crypto mode namespace
+Crypto.mode = {};
+
+})();
+
+module.exports = Crypto;

+ 34 - 0
uni_applet/components/ossutil/hmac.js

@@ -0,0 +1,34 @@
+const Crypto = require('./crypto.js');
+
+(function(){
+
+// Shortcut
+var util = Crypto.util;
+
+Crypto.HMAC = function (hasher, message, key, options) {
+
+	// Allow arbitrary length keys
+	key = key.length > hasher._blocksize * 4 ?
+	      hasher(key, { asBytes: true }) :
+	      util.stringToBytes(key);
+
+	// XOR keys with pad constants
+	var okey = key,
+	    ikey = key.slice(0);
+	for (var i = 0; i < hasher._blocksize * 4; i++) {
+		okey[i] ^= 0x5C;
+		ikey[i] ^= 0x36;
+	}
+
+	var hmacbytes = hasher(util.bytesToString(okey) +
+	                       hasher(util.bytesToString(ikey) + message, { asString: true }),
+	                       { asBytes: true });
+	return options && options.asBytes ? hmacbytes :
+	       options && options.asString ? util.bytesToString(hmacbytes) :
+	       util.bytesToHex(hmacbytes);
+
+};
+
+})();
+
+module.exports = Crypto;

+ 79 - 0
uni_applet/components/ossutil/sha1.js

@@ -0,0 +1,79 @@
+const Crypto = require('./crypto.js');
+
+(function(){
+
+// Shortcut
+var util = Crypto.util;
+
+// Public API
+var SHA1 = Crypto.SHA1 = function (message, options) {
+	var digestbytes = util.wordsToBytes(SHA1._sha1(message));
+	return options && options.asBytes ? digestbytes :
+	       options && options.asString ? util.bytesToString(digestbytes) :
+	       util.bytesToHex(digestbytes);
+};
+
+// The core
+SHA1._sha1 = function (message) {
+
+	var m  = util.stringToWords(message),
+	    l  = message.length * 8,
+	    w  =  [],
+	    H0 =  1732584193,
+	    H1 = -271733879,
+	    H2 = -1732584194,
+	    H3 =  271733878,
+	    H4 = -1009589776;
+
+	// Padding
+	m[l >> 5] |= 0x80 << (24 - l % 32);
+	m[((l + 64 >>> 9) << 4) + 15] = l;
+
+	for (var i = 0; i < m.length; i += 16) {
+
+		var a = H0,
+		    b = H1,
+		    c = H2,
+		    d = H3,
+		    e = H4;
+
+		for (var j = 0; j < 80; j++) {
+
+			if (j < 16) w[j] = m[i + j];
+			else {
+				var n = w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16];
+				w[j] = (n << 1) | (n >>> 31);
+			}
+
+			var t = ((H0 << 5) | (H0 >>> 27)) + H4 + (w[j] >>> 0) + (
+			         j < 20 ? (H1 & H2 | ~H1 & H3) + 1518500249 :
+			         j < 40 ? (H1 ^ H2 ^ H3) + 1859775393 :
+			         j < 60 ? (H1 & H2 | H1 & H3 | H2 & H3) - 1894007588 :
+			                  (H1 ^ H2 ^ H3) - 899497514);
+
+			H4 =  H3;
+			H3 =  H2;
+			H2 = (H1 << 30) | (H1 >>> 2);
+			H1 =  H0;
+			H0 =  t;
+
+		}
+
+		H0 += a;
+		H1 += b;
+		H2 += c;
+		H3 += d;
+		H4 += e;
+
+	}
+
+	return [H0, H1, H2, H3, H4];
+
+};
+
+// Package private blocksize
+SHA1._blocksize = 16;
+
+})();
+
+module.exports = Crypto;

+ 394 - 0
uni_applet/components/ossutil/signature.js

@@ -0,0 +1,394 @@
+class Handwriting {
+  // 内置数据
+  ctx = '';
+  canvasWidth = 300;
+  canvasHeight = 900;
+  linePrack = []; //划线轨迹 ; 生成线条的实际点
+  currentLine = [];
+  transparent = 1; // 透明度
+  pressure = 0.5; // 默认压力
+  smoothness = 100; //顺滑度,用60的距离来计算速度
+  lineSize = 1.5; // 笔记倍数
+  lineMin = 0.5; // 最小笔画半径
+  lineMax = 2; // 最大笔画半径
+  currentPoint = {};
+  firstTouch = true; // 第一次触发
+  radius = 1; //画圆的半径
+  cutArea = {
+    top: 0,
+    right: 0,
+    bottom: 0,
+    left: 0
+  }; //裁剪区域
+  lastPoint = 0;
+  chirography = []; //笔迹
+  startY = 0;
+  deltaY = 0;
+  startValue = 0;
+  constructor(opts) {
+    this.lineColor = opts.lineColor || '#1A1A1A' // 颜色
+    this.slideValue = opts.slideValue || 50
+		this.canvasName = opts.canvasName || 'handWriting'
+    this.init()
+  }
+  init() {
+    this.ctx = uni.createCanvasContext(this.canvasName)
+    var query = uni.createSelectorQuery();
+    query.select('.handCenter').boundingClientRect(rect => {
+      console.log(rect)
+	  if(rect){
+		 this.canvasWidth = rect.width;
+		 this.canvasHeight = rect.height; 
+	  }
+      
+    }).exec();
+    this.selectSlideValue(this.slideValue);
+  }
+
+  // 笔迹开始
+  uploadScaleStart(event) {
+		let e = event.mp
+    if (e.type != 'touchstart') return false;
+    this.ctx.setFillStyle(this.lineColor); // 初始线条设置颜色
+    this.ctx.setGlobalAlpha(this.transparent); // 设置半透明
+    this.currentPoint = {
+      x: e.touches[0].x,
+      y: e.touches[0].y
+    }
+    this.currentLine.unshift({
+      time: new Date().getTime(),
+      dis: 0,
+      x: this.currentPoint.x,
+      y: this.currentPoint.y
+    })
+    if (this.firstTouch) {
+      this.cutArea = {
+        top: this.currentPoint.y,
+        right: this.currentPoint.x,
+        bottom: this.currentPoint.y,
+        left: this.currentPoint.x
+      }
+      this.firstTouch = false
+    }
+    this.pointToLine(this.currentLine);
+  }
+  // 笔迹移动
+  uploadScaleMove(event) {
+		let e = event.mp
+    if (e.type != 'touchmove') return false;
+    if (e.cancelable) {
+      // 判断默认行为是否已经被禁用
+      if (!e.defaultPrevented) {
+        e.preventDefault();
+      }
+    }
+    let point = {
+      x: e.touches[0].x,
+      y: e.touches[0].y
+    }
+    //测试裁剪
+    if (point.y < this.cutArea.top) {
+      this.cutArea.top = point.y;
+    }
+    if (point.y < 0) this.cutArea.top = 0;
+
+    if (point.x > this.cutArea.right) {
+      this.cutArea.right = point.x;
+    }
+    if (this.canvasWidth - point.x <= 0) {
+      this.cutArea.right = this.canvasWidth;
+    }
+    if (point.y > this.cutArea.bottom) {
+      this.cutArea.bottom = point.y;
+    }
+    if (this.canvasHeight - point.y <= 0) {
+      this.cutArea.bottom = this.canvasHeight;
+    }
+    if (point.x < this.cutArea.left) {
+      this.cutArea.left = point.x;
+    }
+    if (point.x < 0) this.cutArea.left = 0;
+
+    this.lastPoint = this.currentPoint;
+    this.currentPoint = point
+    this.currentLine.unshift({
+      time: new Date().getTime(),
+      dis: this.distance(this.currentPoint, this.lastPoint, 'move'),
+      x: point.x,
+      y: point.y
+    })
+    this.pointToLine(this.currentLine);
+  }
+  // 笔迹结束
+  uploadScaleEnd(event) {
+		let e = event.mp
+    if (e.type != 'touchend') return 0;
+    let point = {
+      x: e.changedTouches[0].x,
+      y: e.changedTouches[0].y
+    }
+		
+    this.lastPoint = this.currentPoint;
+    this.currentPoint = point
+    this.currentLine.unshift({
+      time: new Date().getTime(),
+      dis: this.distance(this.currentPoint, this.lastPoint, 'end'),
+      x: point.x,
+      y: point.y
+    })
+    if (this.currentLine.length > 2) {
+      var info = (this.currentLine[0].time - this.currentLine[this.currentLine.length - 1].time) / this.currentLine.length;
+      //$("#info").text(info.toFixed(2));
+    }
+    //一笔结束,保存笔迹的坐标点,清空,当前笔迹
+    //增加判断是否在手写区域;
+    this.pointToLine(this.currentLine);
+    var currentChirography = {
+      lineSize: this.lineSize,
+      lineColor: this.lineColor
+    };
+    this.chirography.unshift(currentChirography);
+    this.linePrack.unshift(this.currentLine);
+    this.currentLine = []
+  }
+  retDraw() {
+    this.ctx.clearRect(0, 0, 700, 730)
+    this.ctx.draw()
+  }
+
+  //画两点之间的线条;参数为:line,会绘制最近的开始的两个点;
+  pointToLine(line) {
+    this.calcBethelLine(line);
+    // this.calcBethelLine1(line);
+    return;
+  }
+  //计算插值的方式;
+  calcBethelLine(line) {
+    if (line.length <= 1) {
+      line[0].r = this.radius;
+      return;
+    }
+    let x0, x1, x2, y0, y1, y2, r0, r1, r2, len, lastRadius, dis = 0,
+      time = 0,
+      curveValue = 0.5;
+    if (line.length <= 2) {
+      x0 = line[1].x
+      y0 = line[1].y
+      x2 = line[1].x + (line[0].x - line[1].x) * curveValue;
+      y2 = line[1].y + (line[0].y - line[1].y) * curveValue;
+      //x2 = line[1].x;
+      //y2 = line[1].y;
+      x1 = x0 + (x2 - x0) * curveValue;
+      y1 = y0 + (y2 - y0) * curveValue;;
+
+    } else {
+      x0 = line[2].x + (line[1].x - line[2].x) * curveValue;
+      y0 = line[2].y + (line[1].y - line[2].y) * curveValue;
+      x1 = line[1].x;
+      y1 = line[1].y;
+      x2 = x1 + (line[0].x - x1) * curveValue;
+      y2 = y1 + (line[0].y - y1) * curveValue;
+    }
+    //从计算公式看,三个点分别是(x0,y0),(x1,y1),(x2,y2) ;(x1,y1)这个是控制点,控制点不会落在曲线上;实际上,这个点还会手写获取的实际点,却落在曲线上
+    len = this.distance({
+      x: x2,
+      y: y2
+    }, {
+      x: x0,
+      y: y0
+    }, 'calc');
+    lastRadius = this.radius;
+    for (let n = 0; n < line.length - 1; n++) {
+      dis += line[n].dis;
+      time += line[n].time - line[n + 1].time;
+      if (dis > this.smoothness) break;
+    }
+    this.radius = Math.min(time / len * this.pressure + this.lineMin, this.lineMax) * this.lineSize
+    line[0].r = this.radius;
+    //计算笔迹半径;
+    if (line.length <= 2) {
+      r0 = (lastRadius + this.radius) / 2;
+      r1 = r0;
+      r2 = r1;
+      //return;
+    } else {
+      r0 = (line[2].r + line[1].r) / 2;
+      r1 = line[1].r;
+      r2 = (line[1].r + line[0].r) / 2;
+    }
+    let n = 5;
+    let point = [];
+    for (let i = 0; i < n; i++) {
+      let t = i / (n - 1);
+      let x = (1 - t) * (1 - t) * x0 + 2 * t * (1 - t) * x1 + t * t * x2;
+      let y = (1 - t) * (1 - t) * y0 + 2 * t * (1 - t) * y1 + t * t * y2;
+      let r = lastRadius + (this.radius - lastRadius) / n * i;
+      point.push({
+        x: x,
+        y: y,
+        r: r
+      });
+      if (point.length == 3) {
+        let a = this.ctaCalc(point[0].x, point[0].y, point[0].r, point[1].x, point[1].y, point[1].r, point[2].x, point[2].y, point[2].r);
+        a[0].color = this.lineColor;
+        this.bethelDraw(a, 1);
+        point = [{
+          x: x,
+          y: y,
+          r: r
+        }];
+      }
+    }
+  }
+  //求两点之间距离
+  distance(a, b, type) {
+    let x = b.x - a.x;
+    let y = b.y - a.y;
+    return Math.sqrt(x * x + y * y) * 5;
+  }
+  ctaCalc(x0, y0, r0, x1, y1, r1, x2, y2, r2) {
+    let a = [],
+      vx01, vy01, norm, n_x0, n_y0, vx21, vy21, n_x2, n_y2;
+    vx01 = x1 - x0;
+    vy01 = y1 - y0;
+    norm = Math.sqrt(vx01 * vx01 + vy01 * vy01 + 0.0001) * 2;
+    vx01 = vx01 / norm * r0;
+    vy01 = vy01 / norm * r0;
+    n_x0 = vy01;
+    n_y0 = -vx01;
+    vx21 = x1 - x2;
+    vy21 = y1 - y2;
+    norm = Math.sqrt(vx21 * vx21 + vy21 * vy21 + 0.0001) * 2;
+    vx21 = vx21 / norm * r2;
+    vy21 = vy21 / norm * r2;
+    n_x2 = -vy21;
+    n_y2 = vx21;
+    a.push({
+      mx: x0 + n_x0,
+      my: y0 + n_y0,
+      color: "#1A1A1A"
+    });
+    a.push({
+      c1x: x1 + n_x0,
+      c1y: y1 + n_y0,
+      c2x: x1 + n_x2,
+      c2y: y1 + n_y2,
+      ex: x2 + n_x2,
+      ey: y2 + n_y2
+    });
+    a.push({
+      c1x: x2 + n_x2 - vx21,
+      c1y: y2 + n_y2 - vy21,
+      c2x: x2 - n_x2 - vx21,
+      c2y: y2 - n_y2 - vy21,
+      ex: x2 - n_x2,
+      ey: y2 - n_y2
+    });
+    a.push({
+      c1x: x1 - n_x2,
+      c1y: y1 - n_y2,
+      c2x: x1 - n_x0,
+      c2y: y1 - n_y0,
+      ex: x0 - n_x0,
+      ey: y0 - n_y0
+    });
+    a.push({
+      c1x: x0 - n_x0 - vx01,
+      c1y: y0 - n_y0 - vy01,
+      c2x: x0 + n_x0 - vx01,
+      c2y: y0 + n_y0 - vy01,
+      ex: x0 + n_x0,
+      ey: y0 + n_y0
+    });
+    a[0].mx = a[0].mx.toFixed(1);
+    a[0].mx = parseFloat(a[0].mx);
+    a[0].my = a[0].my.toFixed(1);
+    a[0].my = parseFloat(a[0].my);
+    for (let i = 1; i < a.length; i++) {
+      a[i].c1x = a[i].c1x.toFixed(1);
+      a[i].c1x = parseFloat(a[i].c1x);
+      a[i].c1y = a[i].c1y.toFixed(1);
+      a[i].c1y = parseFloat(a[i].c1y);
+      a[i].c2x = a[i].c2x.toFixed(1);
+      a[i].c2x = parseFloat(a[i].c2x);
+      a[i].c2y = a[i].c2y.toFixed(1);
+      a[i].c2y = parseFloat(a[i].c2y);
+      a[i].ex = a[i].ex.toFixed(1);
+      a[i].ex = parseFloat(a[i].ex);
+      a[i].ey = a[i].ey.toFixed(1);
+      a[i].ey = parseFloat(a[i].ey);
+    }
+    return a;
+  }
+  bethelDraw(point, is_fill, color) {
+    this.ctx.beginPath();
+    this.ctx.moveTo(point[0].mx, point[0].my);
+    if (undefined != color) {
+      this.ctx.setFillStyle(color);
+      this.ctx.setStrokeStyle(color);
+    } else {
+      this.ctx.setFillStyle(point[0].color);
+      this.ctx.setStrokeStyle(point[0].color);
+    }
+    for (let i = 1; i < point.length; i++) {
+      this.ctx.bezierCurveTo(point[i].c1x, point[i].c1y, point[i].c2x, point[i].c2y, point[i].ex, point[i].ey);
+    }
+    this.ctx.stroke();
+    if (undefined != is_fill) {
+      this.ctx.fill(); //填充图形 ( 后绘制的图形会覆盖前面的图形, 绘制时注意先后顺序 )
+    }
+    this.ctx.draw(true)
+  }
+
+  selectColorEvent(lineColor) {
+    this.lineColor = lineColor;
+  }
+
+  selectSlideValue(slideValue) {
+    switch (slideValue) {
+      case 0:
+        this.lineSize = 0.1;
+        this.lineMin = 0.1;
+        this.lineMax = 0.1;
+        break;
+      case 25:
+        this.lineSize = 1;
+        this.lineMin = 0.5;
+        this.lineMax = 2;
+        break;
+      case 50:
+        this.lineSize = 1.5;
+        this.lineMin = 1;
+        this.lineMax = 3;
+        break;
+      case 75:
+        this.lineSize = 1.5;
+        this.lineMin = 2;
+        this.lineMax = 3.5;
+        break;
+      case 100:
+        this.lineSize = 3;
+        this.lineMin = 2;
+        this.lineMax = 3.5;
+        break;
+    }
+  }
+	
+	saveCanvas(){
+		 return new Promise((resolve,rej) => {
+			uni.canvasToTempFilePath({
+				canvasId: this.canvasName,
+				success: function(res) {
+					console.log(res.tempFilePath)
+					resolve(res.tempFilePath);
+				},
+				 fail:function(err){
+					 console.log('图片生成失败:'+err)
+					 rej(err);
+				 }
+			})
+		})	
+	}	
+}
+
+export default Handwriting;

+ 85 - 0
uni_applet/components/ossutil/uploadFile.js

@@ -0,0 +1,85 @@
+const env = require('./config.js'); //配置文件,在这文件里配置你的OSS keyId和KeySecret,timeout:87600;
+
+const base64 = require('./base64.js');//Base64,hmac,sha1,crypto相关算法
+require('./hmac.js');
+require('./sha1.js');
+const Crypto = require('./crypto.js');
+
+/*
+ *上传文件到阿里云oss
+ *@param - filePath :图片的本地资源路径
+ *@param - dir:表示要传到哪个目录下
+ *@param - successc:成功回调
+ *@param - failc:失败回调
+ */ 
+const uploadFile = function (filePath, dir, successc, failc) {
+  if (!filePath || filePath.length < 9) {
+    uni.showModal({
+      title: '图片错误',
+      content: '请重试',
+      showCancel: false,
+    })
+    return;
+  }
+  //图片名字 可以自行定义,     这里是采用当前的时间戳 + 150内的随机数来给图片命名的
+  const aliyunFileKey = dir + new Date().getTime() + Math.floor(Math.random() * 150) + '.png';
+  
+  const aliyunServerURL = env.uploadImageUrl;//OSS地址,需要https
+  const accessid = env.OSSAccessKeyId;
+  const policyBase64 = getPolicyBase64();
+  const signature = getSignature(policyBase64);//获取签名
+ 
+  uni.uploadFile({
+    url: aliyunServerURL,//开发者服务器 url
+    filePath: filePath,//要上传文件资源的路径
+    name: 'file',//必须填file
+    formData: {
+      'key': aliyunFileKey,
+      'policy': policyBase64,
+      'OSSAccessKeyId': accessid,
+      'signature': signature,
+      'success_action_status': '200',
+    },
+    success: function (res) {
+			console.log(res);
+      if (res.statusCode != 200) {
+        failc(new Error('上传错误:' + JSON.stringify(res)))
+        return;
+      }
+       successc(aliyunServerURL+"/"+aliyunFileKey);
+    },
+    fail: function (err) {
+      err.wxaddinfo = aliyunServerURL;
+      failc(err);
+    },
+  })
+}
+
+const getPolicyBase64 = function () {
+  let date = new Date();
+  date.setHours(date.getHours() + env.timeout);
+  let srcT = date.toISOString();
+  const policyText = {
+    "expiration": srcT, //设置该Policy的失效时间,超过这个失效时间之后,就没有办法通过这个policy上传文件了 
+    "conditions": [
+      ["content-length-range", 0, 5 * 1024 * 1024] // 设置上传文件的大小限制,5mb
+    ]
+  };
+
+  const policyBase64 = base64.encode(JSON.stringify(policyText));
+	console.log(policyBase64);
+  return policyBase64;
+}
+
+const getSignature = function (policyBase64) {
+  const accesskey = env.AccessKeySecret;
+
+  const bytes = Crypto.HMAC(Crypto.SHA1, policyBase64, accesskey, {
+    asBytes: true
+  });
+  const signature = Crypto.util.bytesToBase64(bytes);
+console.log(signature);
+  return signature;
+}
+
+module.exports = uploadFile;

+ 12 - 0
uni_applet/config/index.js

@@ -0,0 +1,12 @@
+const dev = {
+	// baseUrlNew: 'http://192.168.110.72:8182',
+	// baseUrlNew: 'http://192.168.110.138:8182',
+	// baseUrlNew: 'http://192.168.110.82:8182',
+	baseUrlNew: 'https://cardapi.eliangeyun.com',
+	h5Appid: 'wxb66b599f7f61b46f',
+	debug: false
+}
+
+export function def() {
+	return dev
+}

+ 20 - 0
uni_applet/index.html

@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <script>
+      var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
+        CSS.supports('top: constant(a)'))
+      document.write(
+        '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
+        (coverSupport ? ', viewport-fit=cover' : '') + '" />')
+    </script>
+    <title></title>
+    <!--preload-links-->
+    <!--app-context-->
+  </head>
+  <body>
+    <div id="app"><!--app-html--></div>
+    <script type="module" src="/main.js"></script>
+  </body>
+</html>

+ 141 - 0
uni_applet/js_sdk/district.js

@@ -0,0 +1,141 @@
+import twData from './taiwan.json'
+import { formatCity } from './filterCity'
+export default {
+	/**
+	 * 按省的城市编码排序
+	 */
+	orderByCityCode(a, b) {
+	  return parseInt(a.value) - parseInt(b.value)
+	},
+	/**
+	 * 自定义app数据格式
+	 * @param {Array} addressList 高德地图api返回的城市数据
+	 * @param {boolean} noFilter 是否过滤省市区
+	 * @param {boolean} allProvice 是否显示全省
+	 * @param {boolean} allCity 是否显示全市
+	 * @param {boolean} allCountry 是否显示全国
+	 */
+	buildAppJSONData({
+		addressList, 
+		noFilter = false,
+		allProvince = false,
+		allCity = false,
+		allCountry = false,
+	}) {
+	  let array = []
+	  get(addressList, array, noFilter)
+		
+		array.forEach(item => {
+		  if (item.value === '710000') {
+		    if (item.children.length === 0) {
+		      item.children = twData
+		    }
+		  }
+		})
+		array.sort(this.orderByCityCode) // 加入台湾区域,并按省份城市代码排序
+		
+		const specialCitys = ['北京', '上海', '天津', '重庆', '香港', '澳门']
+		if (array.length > 0) {
+			array.forEach(item => {
+				if (allCity) {
+					item.children.forEach(city => {
+						city.children.unshift({
+							value: '0', label: '全市', children: []
+						})
+					})
+				}
+				if (allProvince) {
+		      if (specialCitys.indexOf(item.label) === -1) {
+		        item.children.unshift({
+		        	value: '0', label: '全省', children: [
+		        		{value: '0', label: '全省', children: []}
+		        	]
+		        })
+		      }
+				}
+			})
+			if (allCountry) {
+				array.unshift({
+					value: '0', label: '全国', children: [
+						{value: '0', label: '全国', children: [
+							{value: '0', label: '全国', children: []}
+						]}
+					]
+				})
+			}
+		}
+	  return array
+		
+	  function get(districts, children, noFilter) {
+	    districts.forEach((item, index) => {
+	      children.push({
+	        value: noFilter === true ? item.adcode : item.name,
+	        label: noFilter === true ? formatCity(item.name) : item.name
+	      })
+	      if (item.districts.length != 0) {
+	        children[children.length - 1].children = []
+	        if (item.adcode === item.districts[0].adcode) {
+	          children[children.length - 1].children = [
+	            {
+	              value: noFilter === true ? item.adcode : item.name,
+	              label: noFilter === true ? formatCity(item.name) : item.name
+	            }
+	          ]      
+	        } else {
+	          get(item.districts, children[children.length - 1].children, noFilter)
+	        }
+	      } else {
+	        // 特别处理香港,市区统一
+	        if (parseInt(item.adcode) > 810000 && parseInt(item.adcode) < 820000) {
+	          children[children.length - 1].children = [
+	            {
+	             value: noFilter === true ? item.adcode : item.name,
+	             label: noFilter === true ? formatCity(item.name) : item.name
+	            }
+	          ]
+	        }
+	        // 特别处理澳门,市区统一
+	        if (parseInt(item.adcode) > 820000 && parseInt(item.adcode) < 830000) {
+	          children[children.length - 1].children = [
+	            {
+	              value: noFilter === true ? item.adcode : item.name,
+	              label: noFilter === true ? formatCity(item.name) : item.name
+	            }
+	          ]
+	        }
+	        // 特别处理台湾
+	        if (parseInt(item.adcode) === 710000) {
+	          children[children.length - 1].children = []
+	        }
+	      }
+	    })
+	  }
+	},
+	getGaoDeData(key) {
+		if (key) {
+			return new Promise((resolve, reject) => {
+				uni.request({
+					method: 'get',
+					url: `https://restapi.amap.com/v3/config/district?subdistrict=3&key=${key}`,
+					success: res => {
+						if (res.statusCode === 200) {
+							// 构造匹配app,otms框架的城市JSON文件
+							// const data = this.buildAppJSONData(, false, false)
+							resolve(res.data.districts[0].districts)
+						} else {
+							reject(res)
+						}
+					},
+					fail: res => {
+						reject(res)
+					}
+				})
+			})
+		} else {
+			uni.showModal({
+				title: '提示',
+				content: '请输入高德key'
+			})
+		}
+	}
+}

+ 52 - 0
uni_applet/js_sdk/filterCity.js

@@ -0,0 +1,52 @@
+/**
+ * 过滤城市字段
+ * 去掉省市区自治区等文字,匹配tms的地址库,兼容老数据 
+ */
+
+const cityReg = /省|市|自治区|自治州|自治县|特别行政区|壮族|回族|维吾尔/g
+
+const areaReg = /回族自治县|满族自治县|满族蒙古族自治县|蒙古族自治县|蒙古自治州|朝鲜族自治州|朝鲜族自治县|畲族自治县|土家族自治县|土家族苗族自治州|侗族自治县|苗族侗族自治县|苗族侗族自治州|苗族自治县|瑶族自治县|壮族瑶族自治县|各族自治县|毛南族自治县|仫佬族自治县|黎族自治县|黎族苗族自治县|苗族土家族自治县|土家族苗族自治县|羌族自治县|彝族自治县|彝族自治州|藏族自治县|藏族羌族自治州|仡佬族苗族自治县|布依族苗族自治州|苗族布依族自治县|布依族苗族自治县|布依族苗族自治州|水族自治县|彝族回族苗族自治县|傈僳族自治州|独龙族怒族自治县|白族普米族自治县|哈尼族彝族自治州|苗族瑶族傣族自治县|傣族自治州|彝族傣族自治县|哈尼族彝族傣族自治县|白族自治州|彝族自治县|彝族回族自治县|彝族自治县|彝族自治州|藏族自治州|傈僳族自治县|纳西族自治县|壮族苗族自治州|彝族哈尼族拉祜族自治县|哈尼族彝族自治县|傣族彝族自治县|哈尼族彝族自治县|哈尼族自治县|拉祜族自治县|傣族拉祜族佤族自治县|佤族自治县|傣族佤族自治县|拉祜族佤族布朗族傣族自治县|回族彝族自治县|彝族苗族自治县|傣族景颇族自治州|哈萨克族自治县|裕固族自治县|保安族东乡族撒拉族自治县|回族土族自治县|撒拉族自治县|土族自治县|蒙古族藏族自治州|蒙古族藏族自治州直辖|哈萨克自治县|哈萨克自治州|锡伯自治县|塔吉克自治县|柯尔克孜自治州/g
+
+const countyReg = /区|县|经济开发区/g
+
+export const formatCity = (name) => {
+  var tempName = name
+  if (tempName.length > 3) {
+    if (tempName.indexOf('城区') > -1) {
+      tempName = tempName.replace('城区', '')
+    }
+    if (tempName.indexOf('地区') > -1) {
+      tempName = tempName.replace('地区', '')
+    }
+  }
+  if (tempName.length > 2) {
+    if (tempName.indexOf('和田') === -1) {
+      tempName = tempName.replace(areaReg, "")
+      if (tempName.indexOf('市') > -1 && tempName.indexOf('市') !== tempName.length - 1) { // 市不是出现在最后一位,不过滤
+        if (tempName.indexOf('市市') > -1) { // 如果出现两个市,如津市市,只去掉第一个市字
+          tempName = tempName.replace('市', "")
+        } else {
+          console.log('保留市字:' + tempName)
+        }
+      } else {
+        tempName = tempName.replace(cityReg, "")
+      }
+      // 过滤剩余的x族自治县
+      tempName = tempName.replace('族自治县', "")
+      // 过滤区县
+      if (tempName.length > 2) {
+        if (tempName.indexOf('县区') > -1) {
+          tempName = tempName.replace('区', "")
+        } else {
+          if (tempName.length > 3 && tempName.indexOf('新区') > -1) {
+            console.log('保留新区:' + tempName)
+          } else {
+            tempName = tempName.replace(countyReg, "")
+          }
+        }
+        // tempName = tempName.replace(countyReg, "")
+      }
+    }
+  }
+  return tempName
+}

+ 0 - 0
uni_applet/js_sdk/js-amap/__MACOSX/amap-wx.130.js


+ 31 - 0
uni_applet/js_sdk/js-amap/amap-wx.130.js

@@ -0,0 +1,31 @@
+function AMapWX(a){this.key=a.key;this.requestConfig={key:a.key,s:"rsx",platform:"WXJS",appname:a.key,sdkversion:"1.2.0",logversion:"2.0"};this.MeRequestConfig={key:a.key,serviceName:"https://restapi.amap.com/rest/me"}}
+AMapWX.prototype.getWxLocation=function(a,b){wx.getLocation({type:"gcj02",success:function(c){c=c.longitude+","+c.latitude;wx.setStorage({key:"userLocation",data:c});b(c)},fail:function(c){wx.getStorage({key:"userLocation",success:function(d){d.data&&b(d.data)}});a.fail({errCode:"0",errMsg:c.errMsg||""})}})};
+AMapWX.prototype.getMEKeywordsSearch=function(a){if(!a.options)return a.fail({errCode:"0",errMsg:"\u7f3a\u5c11\u5fc5\u8981\u53c2\u6570"});var b=a.options,c=this.MeRequestConfig,d={key:c.key,s:"rsx",platform:"WXJS",appname:a.key,sdkversion:"1.2.0",logversion:"2.0"};b.layerId&&(d.layerId=b.layerId);b.keywords&&(d.keywords=b.keywords);b.city&&(d.city=b.city);b.filter&&(d.filter=b.filter);b.sortrule&&(d.sortrule=b.sortrule);b.pageNum&&(d.pageNum=b.pageNum);b.pageSize&&(d.pageSize=b.pageSize);b.sig&&(d.sig=
+b.sig);wx.request({url:c.serviceName+"/cpoint/datasearch/local",data:d,method:"GET",header:{"content-type":"application/json"},success:function(e){(e=e.data)&&e.status&&"1"===e.status&&0===e.code?a.success(e.data):a.fail({errCode:"0",errMsg:e})},fail:function(e){a.fail({errCode:"0",errMsg:e.errMsg||""})}})};
+AMapWX.prototype.getMEIdSearch=function(a){if(!a.options)return a.fail({errCode:"0",errMsg:"\u7f3a\u5c11\u5fc5\u8981\u53c2\u6570"});var b=a.options,c=this.MeRequestConfig,d={key:c.key,s:"rsx",platform:"WXJS",appname:a.key,sdkversion:"1.2.0",logversion:"2.0"};b.layerId&&(d.layerId=b.layerId);b.id&&(d.id=b.id);b.sig&&(d.sig=b.sig);wx.request({url:c.serviceName+"/cpoint/datasearch/id",data:d,method:"GET",header:{"content-type":"application/json"},success:function(e){(e=e.data)&&e.status&&"1"===e.status&&
+0===e.code?a.success(e.data):a.fail({errCode:"0",errMsg:e})},fail:function(e){a.fail({errCode:"0",errMsg:e.errMsg||""})}})};
+AMapWX.prototype.getMEPolygonSearch=function(a){if(!a.options)return a.fail({errCode:"0",errMsg:"\u7f3a\u5c11\u5fc5\u8981\u53c2\u6570"});var b=a.options,c=this.MeRequestConfig,d={key:c.key,s:"rsx",platform:"WXJS",appname:a.key,sdkversion:"1.2.0",logversion:"2.0"};b.layerId&&(d.layerId=b.layerId);b.keywords&&(d.keywords=b.keywords);b.polygon&&(d.polygon=b.polygon);b.filter&&(d.filter=b.filter);b.sortrule&&(d.sortrule=b.sortrule);b.pageNum&&(d.pageNum=b.pageNum);b.pageSize&&(d.pageSize=b.pageSize);
+b.sig&&(d.sig=b.sig);wx.request({url:c.serviceName+"/cpoint/datasearch/polygon",data:d,method:"GET",header:{"content-type":"application/json"},success:function(e){(e=e.data)&&e.status&&"1"===e.status&&0===e.code?a.success(e.data):a.fail({errCode:"0",errMsg:e})},fail:function(e){a.fail({errCode:"0",errMsg:e.errMsg||""})}})};
+AMapWX.prototype.getMEaroundSearch=function(a){if(!a.options)return a.fail({errCode:"0",errMsg:"\u7f3a\u5c11\u5fc5\u8981\u53c2\u6570"});var b=a.options,c=this.MeRequestConfig,d={key:c.key,s:"rsx",platform:"WXJS",appname:a.key,sdkversion:"1.2.0",logversion:"2.0"};b.layerId&&(d.layerId=b.layerId);b.keywords&&(d.keywords=b.keywords);b.center&&(d.center=b.center);b.radius&&(d.radius=b.radius);b.filter&&(d.filter=b.filter);b.sortrule&&(d.sortrule=b.sortrule);b.pageNum&&(d.pageNum=b.pageNum);b.pageSize&&
+(d.pageSize=b.pageSize);b.sig&&(d.sig=b.sig);wx.request({url:c.serviceName+"/cpoint/datasearch/around",data:d,method:"GET",header:{"content-type":"application/json"},success:function(e){(e=e.data)&&e.status&&"1"===e.status&&0===e.code?a.success(e.data):a.fail({errCode:"0",errMsg:e})},fail:function(e){a.fail({errCode:"0",errMsg:e.errMsg||""})}})};
+AMapWX.prototype.getGeo=function(a){var b=this.requestConfig,c=a.options;b={key:this.key,extensions:"all",s:b.s,platform:b.platform,appname:this.key,sdkversion:b.sdkversion,logversion:b.logversion};c.address&&(b.address=c.address);c.city&&(b.city=c.city);c.batch&&(b.batch=c.batch);c.sig&&(b.sig=c.sig);wx.request({url:"https://restapi.amap.com/v3/geocode/geo",data:b,method:"GET",header:{"content-type":"application/json"},success:function(d){(d=d.data)&&d.status&&"1"===d.status?a.success(d):a.fail({errCode:"0",
+errMsg:d})},fail:function(d){a.fail({errCode:"0",errMsg:d.errMsg||""})}})};
+AMapWX.prototype.getRegeo=function(a){function b(d){var e=c.requestConfig;wx.request({url:"https://restapi.amap.com/v3/geocode/regeo",data:{key:c.key,location:d,extensions:"all",s:e.s,platform:e.platform,appname:c.key,sdkversion:e.sdkversion,logversion:e.logversion},method:"GET",header:{"content-type":"application/json"},success:function(g){if(g.data.status&&"1"==g.data.status){g=g.data.regeocode;var h=g.addressComponent,f=[],k=g.roads[0].name+"\u9644\u8fd1",m=d.split(",")[0],n=d.split(",")[1];if(g.pois&&
+g.pois[0]){k=g.pois[0].name+"\u9644\u8fd1";var l=g.pois[0].location;l&&(m=parseFloat(l.split(",")[0]),n=parseFloat(l.split(",")[1]))}h.provice&&f.push(h.provice);h.city&&f.push(h.city);h.district&&f.push(h.district);h.streetNumber&&h.streetNumber.street&&h.streetNumber.number?(f.push(h.streetNumber.street),f.push(h.streetNumber.number)):f.push(g.roads[0].name);f=f.join("");a.success([{iconPath:a.iconPath,width:a.iconWidth,height:a.iconHeight,name:f,desc:k,longitude:m,latitude:n,id:0,regeocodeData:g}])}else a.fail({errCode:g.data.infocode,
+errMsg:g.data.info})},fail:function(g){a.fail({errCode:"0",errMsg:g.errMsg||""})}})}var c=this;a.location?b(a.location):c.getWxLocation(a,function(d){b(d)})};
+AMapWX.prototype.getWeather=function(a){function b(g){var h="base";a.type&&"forecast"==a.type&&(h="all");wx.request({url:"https://restapi.amap.com/v3/weather/weatherInfo",data:{key:d.key,city:g,extensions:h,s:e.s,platform:e.platform,appname:d.key,sdkversion:e.sdkversion,logversion:e.logversion},method:"GET",header:{"content-type":"application/json"},success:function(f){if(f.data.status&&"1"==f.data.status)if(f.data.lives){if((f=f.data.lives)&&0<f.length){f=f[0];var k={city:{text:"\u57ce\u5e02",data:f.city},
+weather:{text:"\u5929\u6c14",data:f.weather},temperature:{text:"\u6e29\u5ea6",data:f.temperature},winddirection:{text:"\u98ce\u5411",data:f.winddirection+"\u98ce"},windpower:{text:"\u98ce\u529b",data:f.windpower+"\u7ea7"},humidity:{text:"\u6e7f\u5ea6",data:f.humidity+"%"}};k.liveData=f;a.success(k)}}else f.data.forecasts&&f.data.forecasts[0]&&a.success({forecast:f.data.forecasts[0]});else a.fail({errCode:f.data.infocode,errMsg:f.data.info})},fail:function(f){a.fail({errCode:"0",errMsg:f.errMsg||""})}})}
+function c(g){wx.request({url:"https://restapi.amap.com/v3/geocode/regeo",data:{key:d.key,location:g,extensions:"all",s:e.s,platform:e.platform,appname:d.key,sdkversion:e.sdkversion,logversion:e.logversion},method:"GET",header:{"content-type":"application/json"},success:function(h){if(h.data.status&&"1"==h.data.status){h=h.data.regeocode;if(h.addressComponent)var f=h.addressComponent.adcode;else h.aois&&0<h.aois.length&&(f=h.aois[0].adcode);b(f)}else a.fail({errCode:h.data.infocode,errMsg:h.data.info})},
+fail:function(h){a.fail({errCode:"0",errMsg:h.errMsg||""})}})}var d=this,e=d.requestConfig;a.city?b(a.city):d.getWxLocation(a,function(g){c(g)})};
+AMapWX.prototype.getPoiAround=function(a){function b(e){e={key:c.key,location:e,s:d.s,platform:d.platform,appname:c.key,sdkversion:d.sdkversion,logversion:d.logversion};a.querytypes&&(e.types=a.querytypes);a.querykeywords&&(e.keywords=a.querykeywords);wx.request({url:"https://restapi.amap.com/v3/place/around",data:e,method:"GET",header:{"content-type":"application/json"},success:function(g){if(g.data.status&&"1"==g.data.status){if((g=g.data)&&g.pois){for(var h=[],f=0;f<g.pois.length;f++){var k=0==
+f?a.iconPathSelected:a.iconPath;h.push({latitude:parseFloat(g.pois[f].location.split(",")[1]),longitude:parseFloat(g.pois[f].location.split(",")[0]),iconPath:k,width:22,height:32,id:f,name:g.pois[f].name,address:g.pois[f].address})}a.success({markers:h,poisData:g.pois})}}else a.fail({errCode:g.data.infocode,errMsg:g.data.info})},fail:function(g){a.fail({errCode:"0",errMsg:g.errMsg||""})}})}var c=this,d=c.requestConfig;a.location?b(a.location):c.getWxLocation(a,function(e){b(e)})};
+AMapWX.prototype.getStaticmap=function(a){function b(e){c.push("location="+e);a.zoom&&c.push("zoom="+a.zoom);a.size&&c.push("size="+a.size);a.scale&&c.push("scale="+a.scale);a.markers&&c.push("markers="+a.markers);a.labels&&c.push("labels="+a.labels);a.paths&&c.push("paths="+a.paths);a.traffic&&c.push("traffic="+a.traffic);e="https://restapi.amap.com/v3/staticmap?"+c.join("&");a.success({url:e})}var c=[];c.push("key="+this.key);var d=this.requestConfig;c.push("s="+d.s);c.push("platform="+d.platform);
+c.push("appname="+d.appname);c.push("sdkversion="+d.sdkversion);c.push("logversion="+d.logversion);a.location?b(a.location):this.getWxLocation(a,function(e){b(e)})};
+AMapWX.prototype.getInputtips=function(a){var b=Object.assign({},this.requestConfig);a.location&&(b.location=a.location);a.keywords&&(b.keywords=a.keywords);a.type&&(b.type=a.type);a.city&&(b.city=a.city);a.citylimit&&(b.citylimit=a.citylimit);wx.request({url:"https://restapi.amap.com/v3/assistant/inputtips",data:b,method:"GET",header:{"content-type":"application/json"},success:function(c){c&&c.data&&c.data.tips&&a.success({tips:c.data.tips})},fail:function(c){a.fail({errCode:"0",errMsg:c.errMsg||
+""})}})};
+AMapWX.prototype.getDrivingRoute=function(a){var b=Object.assign({},this.requestConfig);a.origin&&(b.origin=a.origin);a.destination&&(b.destination=a.destination);a.strategy&&(b.strategy=a.strategy);a.waypoints&&(b.waypoints=a.waypoints);a.avoidpolygons&&(b.avoidpolygons=a.avoidpolygons);a.avoidroad&&(b.avoidroad=a.avoidroad);wx.request({url:"https://restapi.amap.com/v3/direction/driving",data:b,method:"GET",header:{"content-type":"application/json"},success:function(c){c&&c.data&&c.data.route&&a.success({paths:c.data.route.paths,
+taxi_cost:c.data.route.taxi_cost||""})},fail:function(c){a.fail({errCode:"0",errMsg:c.errMsg||""})}})};
+AMapWX.prototype.getWalkingRoute=function(a){var b=Object.assign({},this.requestConfig);a.origin&&(b.origin=a.origin);a.destination&&(b.destination=a.destination);wx.request({url:"https://restapi.amap.com/v3/direction/walking",data:b,method:"GET",header:{"content-type":"application/json"},success:function(c){c&&c.data&&c.data.route&&a.success({paths:c.data.route.paths})},fail:function(c){a.fail({errCode:"0",errMsg:c.errMsg||""})}})};
+AMapWX.prototype.getTransitRoute=function(a){var b=Object.assign({},this.requestConfig);a.origin&&(b.origin=a.origin);a.destination&&(b.destination=a.destination);a.strategy&&(b.strategy=a.strategy);a.city&&(b.city=a.city);a.cityd&&(b.cityd=a.cityd);wx.request({url:"https://restapi.amap.com/v3/direction/transit/integrated",data:b,method:"GET",header:{"content-type":"application/json"},success:function(c){c&&c.data&&c.data.route&&(c=c.data.route,a.success({distance:c.distance||"",taxi_cost:c.taxi_cost||
+"",transits:c.transits}))},fail:function(c){a.fail({errCode:"0",errMsg:c.errMsg||""})}})};
+AMapWX.prototype.getRidingRoute=function(a){var b=Object.assign({},this.requestConfig);a.origin&&(b.origin=a.origin);a.destination&&(b.destination=a.destination);wx.request({url:"https://restapi.amap.com/v3/direction/riding",data:b,method:"GET",header:{"content-type":"application/json"},success:function(c){c&&c.data&&c.data.route&&a.success({paths:c.data.route.paths})},fail:function(c){a.fail({errCode:"0",errMsg:c.errMsg||""})}})};module.exports.AMapWX=AMapWX;

+ 1122 - 0
uni_applet/js_sdk/qqmap-wx-jssdk.js

@@ -0,0 +1,1122 @@
+/**
+ * 微信小程序JavaScriptSDK
+ * 
+ * @version 1.2
+ * @date 2019-03-06
+ */
+
+var ERROR_CONF = {
+    KEY_ERR: 311,
+    KEY_ERR_MSG: 'key格式错误',
+    PARAM_ERR: 310,
+    PARAM_ERR_MSG: '请求参数信息有误',
+    SYSTEM_ERR: 600,
+    SYSTEM_ERR_MSG: '系统错误',
+    WX_ERR_CODE: 1000,
+    WX_OK_CODE: 200
+};
+var BASE_URL = 'https://apis.map.qq.com/ws/';
+var URL_SEARCH = BASE_URL + 'place/v1/search';
+var URL_SUGGESTION = BASE_URL + 'place/v1/suggestion';
+var URL_GET_GEOCODER = BASE_URL + 'geocoder/v1/';
+var URL_CITY_LIST = BASE_URL + 'district/v1/list';
+var URL_AREA_LIST = BASE_URL + 'district/v1/getchildren';
+var URL_DISTANCE = BASE_URL + 'distance/v1/';
+var URL_DIRECTION = BASE_URL + 'direction/v1/';
+var MODE = {
+  driving: 'driving',
+  transit: 'transit'
+};
+var EARTH_RADIUS = 6378136.49;
+var Utils = {
+  /**
+  * md5加密方法
+  * 版权所有©2011 Sebastian Tschan,https://blueimp.net
+  */
+  safeAdd(x, y) {
+    var lsw = (x & 0xffff) + (y & 0xffff);
+    var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+    return (msw << 16) | (lsw & 0xffff);
+  },
+  bitRotateLeft(num, cnt) {
+    return (num << cnt) | (num >>> (32 - cnt));
+  },
+  md5cmn(q, a, b, x, s, t) {
+    return this.safeAdd(this.bitRotateLeft(this.safeAdd(this.safeAdd(a, q), this.safeAdd(x, t)), s), b);
+  },
+  md5ff(a, b, c, d, x, s, t) {
+    return this.md5cmn((b & c) | (~b & d), a, b, x, s, t);
+  },
+  md5gg(a, b, c, d, x, s, t) {
+    return this.md5cmn((b & d) | (c & ~d), a, b, x, s, t);
+  },
+  md5hh(a, b, c, d, x, s, t) {
+    return this.md5cmn(b ^ c ^ d, a, b, x, s, t);
+  },
+  md5ii(a, b, c, d, x, s, t) {
+    return this.md5cmn(c ^ (b | ~d), a, b, x, s, t);
+  },
+  binlMD5(x, len) {
+    /* append padding */
+    x[len >> 5] |= 0x80 << (len % 32);
+    x[((len + 64) >>> 9 << 4) + 14] = len;
+
+    var i;
+    var olda;
+    var oldb;
+    var oldc;
+    var oldd;
+    var a = 1732584193;
+    var b = -271733879;
+    var c = -1732584194;
+    var d = 271733878;
+
+    for (i = 0; i < x.length; i += 16) {
+      olda = a;
+      oldb = b;
+      oldc = c;
+      oldd = d;
+
+      a = this.md5ff(a, b, c, d, x[i], 7, -680876936);
+      d = this.md5ff(d, a, b, c, x[i + 1], 12, -389564586);
+      c = this.md5ff(c, d, a, b, x[i + 2], 17, 606105819);
+      b = this.md5ff(b, c, d, a, x[i + 3], 22, -1044525330);
+      a = this.md5ff(a, b, c, d, x[i + 4], 7, -176418897);
+      d = this.md5ff(d, a, b, c, x[i + 5], 12, 1200080426);
+      c = this.md5ff(c, d, a, b, x[i + 6], 17, -1473231341);
+      b = this.md5ff(b, c, d, a, x[i + 7], 22, -45705983);
+      a = this.md5ff(a, b, c, d, x[i + 8], 7, 1770035416);
+      d = this.md5ff(d, a, b, c, x[i + 9], 12, -1958414417);
+      c = this.md5ff(c, d, a, b, x[i + 10], 17, -42063);
+      b = this.md5ff(b, c, d, a, x[i + 11], 22, -1990404162);
+      a = this.md5ff(a, b, c, d, x[i + 12], 7, 1804603682);
+      d = this.md5ff(d, a, b, c, x[i + 13], 12, -40341101);
+      c = this.md5ff(c, d, a, b, x[i + 14], 17, -1502002290);
+      b = this.md5ff(b, c, d, a, x[i + 15], 22, 1236535329);
+
+      a = this.md5gg(a, b, c, d, x[i + 1], 5, -165796510);
+      d = this.md5gg(d, a, b, c, x[i + 6], 9, -1069501632);
+      c = this.md5gg(c, d, a, b, x[i + 11], 14, 643717713);
+      b = this.md5gg(b, c, d, a, x[i], 20, -373897302);
+      a = this.md5gg(a, b, c, d, x[i + 5], 5, -701558691);
+      d = this.md5gg(d, a, b, c, x[i + 10], 9, 38016083);
+      c = this.md5gg(c, d, a, b, x[i + 15], 14, -660478335);
+      b = this.md5gg(b, c, d, a, x[i + 4], 20, -405537848);
+      a = this.md5gg(a, b, c, d, x[i + 9], 5, 568446438);
+      d = this.md5gg(d, a, b, c, x[i + 14], 9, -1019803690);
+      c = this.md5gg(c, d, a, b, x[i + 3], 14, -187363961);
+      b = this.md5gg(b, c, d, a, x[i + 8], 20, 1163531501);
+      a = this.md5gg(a, b, c, d, x[i + 13], 5, -1444681467);
+      d = this.md5gg(d, a, b, c, x[i + 2], 9, -51403784);
+      c = this.md5gg(c, d, a, b, x[i + 7], 14, 1735328473);
+      b = this.md5gg(b, c, d, a, x[i + 12], 20, -1926607734);
+
+      a = this.md5hh(a, b, c, d, x[i + 5], 4, -378558);
+      d = this.md5hh(d, a, b, c, x[i + 8], 11, -2022574463);
+      c = this.md5hh(c, d, a, b, x[i + 11], 16, 1839030562);
+      b = this.md5hh(b, c, d, a, x[i + 14], 23, -35309556);
+      a = this.md5hh(a, b, c, d, x[i + 1], 4, -1530992060);
+      d = this.md5hh(d, a, b, c, x[i + 4], 11, 1272893353);
+      c = this.md5hh(c, d, a, b, x[i + 7], 16, -155497632);
+      b = this.md5hh(b, c, d, a, x[i + 10], 23, -1094730640);
+      a = this.md5hh(a, b, c, d, x[i + 13], 4, 681279174);
+      d = this.md5hh(d, a, b, c, x[i], 11, -358537222);
+      c = this.md5hh(c, d, a, b, x[i + 3], 16, -722521979);
+      b = this.md5hh(b, c, d, a, x[i + 6], 23, 76029189);
+      a = this.md5hh(a, b, c, d, x[i + 9], 4, -640364487);
+      d = this.md5hh(d, a, b, c, x[i + 12], 11, -421815835);
+      c = this.md5hh(c, d, a, b, x[i + 15], 16, 530742520);
+      b = this.md5hh(b, c, d, a, x[i + 2], 23, -995338651);
+
+      a = this.md5ii(a, b, c, d, x[i], 6, -198630844);
+      d = this.md5ii(d, a, b, c, x[i + 7], 10, 1126891415);
+      c = this.md5ii(c, d, a, b, x[i + 14], 15, -1416354905);
+      b = this.md5ii(b, c, d, a, x[i + 5], 21, -57434055);
+      a = this.md5ii(a, b, c, d, x[i + 12], 6, 1700485571);
+      d = this.md5ii(d, a, b, c, x[i + 3], 10, -1894986606);
+      c = this.md5ii(c, d, a, b, x[i + 10], 15, -1051523);
+      b = this.md5ii(b, c, d, a, x[i + 1], 21, -2054922799);
+      a = this.md5ii(a, b, c, d, x[i + 8], 6, 1873313359);
+      d = this.md5ii(d, a, b, c, x[i + 15], 10, -30611744);
+      c = this.md5ii(c, d, a, b, x[i + 6], 15, -1560198380);
+      b = this.md5ii(b, c, d, a, x[i + 13], 21, 1309151649);
+      a = this.md5ii(a, b, c, d, x[i + 4], 6, -145523070);
+      d = this.md5ii(d, a, b, c, x[i + 11], 10, -1120210379);
+      c = this.md5ii(c, d, a, b, x[i + 2], 15, 718787259);
+      b = this.md5ii(b, c, d, a, x[i + 9], 21, -343485551);
+
+      a = this.safeAdd(a, olda);
+      b = this.safeAdd(b, oldb);
+      c = this.safeAdd(c, oldc);
+      d = this.safeAdd(d, oldd);
+    }
+    return [a, b, c, d];
+  },
+  binl2rstr(input) {
+    var i;
+    var output = '';
+    var length32 = input.length * 32;
+    for (i = 0; i < length32; i += 8) {
+      output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xff);
+    }
+    return output;
+  },
+  rstr2binl(input) {
+    var i;
+    var output = [];
+    output[(input.length >> 2) - 1] = undefined;
+    for (i = 0; i < output.length; i += 1) {
+      output[i] = 0;
+    }
+    var length8 = input.length * 8;
+    for (i = 0; i < length8; i += 8) {
+      output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << (i % 32);
+    }
+    return output;
+  },
+  rstrMD5(s) {
+    return this.binl2rstr(this.binlMD5(this.rstr2binl(s), s.length * 8));
+  },
+  rstrHMACMD5(key, data) {
+    var i;
+    var bkey = this.rstr2binl(key);
+    var ipad = [];
+    var opad = [];
+    var hash;
+    ipad[15] = opad[15] = undefined;
+    if (bkey.length > 16) {
+      bkey = this.binlMD5(bkey, key.length * 8);
+    }
+    for (i = 0; i < 16; i += 1) {
+      ipad[i] = bkey[i] ^ 0x36363636;
+      opad[i] = bkey[i] ^ 0x5c5c5c5c;
+    }
+    hash = this.binlMD5(ipad.concat(this.rstr2binl(data)), 512 + data.length * 8);
+    return this.binl2rstr(this.binlMD5(opad.concat(hash), 512 + 128));
+  },
+  rstr2hex(input) {
+    var hexTab = '0123456789abcdef';
+    var output = '';
+    var x;
+    var i;
+    for (i = 0; i < input.length; i += 1) {
+      x = input.charCodeAt(i);
+      output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f);
+    }
+    return output;
+  },
+  str2rstrUTF8(input) {
+    return unescape(encodeURIComponent(input));
+  },
+  rawMD5(s) {
+    return this.rstrMD5(this.str2rstrUTF8(s));
+  },
+  hexMD5(s) {
+    return this.rstr2hex(this.rawMD5(s));
+  },
+  rawHMACMD5(k, d) {
+    return this.rstrHMACMD5(this.str2rstrUTF8(k), str2rstrUTF8(d));
+  },
+  hexHMACMD5(k, d) {
+    return this.rstr2hex(this.rawHMACMD5(k, d));
+  },
+
+  md5(string, key, raw) {
+    if (!key) {
+      if (!raw) {
+        return this.hexMD5(string);
+      }
+      return this.rawMD5(string);
+    }
+    if (!raw) {
+      return this.hexHMACMD5(key, string);
+    }
+    return this.rawHMACMD5(key, string);
+  },
+  /**
+   * 得到md5加密后的sig参数
+   * @param {Object} requestParam 接口参数
+   * @param {String} sk签名字符串
+   * @param {String} featrue 方法名
+   * @return 返回加密后的sig参数
+   */
+  getSig(requestParam, sk, feature, mode) {
+    var sig = null;
+    var requestArr = [];
+    Object.keys(requestParam).sort().forEach(function(key){
+      requestArr.push(key + '=' + requestParam[key]);
+    });
+    if (feature == 'search') {
+      sig = '/ws/place/v1/search?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'suggest') {
+      sig = '/ws/place/v1/suggestion?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'reverseGeocoder') {
+      sig = '/ws/geocoder/v1/?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'geocoder') {
+      sig = '/ws/geocoder/v1/?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'getCityList') {
+      sig = '/ws/district/v1/list?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'getDistrictByCityId') {
+      sig = '/ws/district/v1/getchildren?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'calculateDistance') {
+      sig = '/ws/distance/v1/?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'direction') {
+      sig = '/ws/direction/v1/' + mode + '?' + requestArr.join('&') + sk;
+    }
+    sig = this.md5(sig);
+    return sig;
+  },
+    /**
+     * 得到终点query字符串
+     * @param {Array|String} 检索数据
+     */
+    location2query(data) {
+        if (typeof data == 'string') {
+            return data;
+        }
+        var query = '';
+        for (var i = 0; i < data.length; i++) {
+            var d = data[i];
+            if (!!query) {
+                query += ';';
+            }
+            if (d.location) {
+                query = query + d.location.lat + ',' + d.location.lng;
+            }
+            if (d.latitude && d.longitude) {
+                query = query + d.latitude + ',' + d.longitude;
+            }
+        }
+        return query;
+    },
+
+    /**
+     * 计算角度
+     */
+    rad(d) {
+      return d * Math.PI / 180.0;
+    },  
+    /**
+     * 处理终点location数组
+     * @return 返回终点数组
+     */
+    getEndLocation(location){
+      var to = location.split(';');
+      var endLocation = [];
+      for (var i = 0; i < to.length; i++) {
+        endLocation.push({
+          lat: parseFloat(to[i].split(',')[0]),
+          lng: parseFloat(to[i].split(',')[1])
+        })
+      }
+      return endLocation;
+    },
+
+    /**
+     * 计算两点间直线距离
+     * @param a 表示纬度差
+     * @param b 表示经度差
+     * @return 返回的是距离,单位m
+     */
+    getDistance(latFrom, lngFrom, latTo, lngTo) {
+      var radLatFrom = this.rad(latFrom);
+      var radLatTo = this.rad(latTo);
+      var a = radLatFrom - radLatTo;
+      var b = this.rad(lngFrom) - this.rad(lngTo);
+      var distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLatFrom) * Math.cos(radLatTo) * Math.pow(Math.sin(b / 2), 2)));
+      distance = distance * EARTH_RADIUS;
+      distance = Math.round(distance * 10000) / 10000;
+      return parseFloat(distance.toFixed(0));
+    },
+    /**
+     * 使用微信接口进行定位
+     */
+    getWXLocation(success, fail, complete) {
+        wx.getLocation({
+            type: 'gcj02',
+            success: success,
+            fail: fail,
+            complete: complete
+        });
+    },
+
+    /**
+     * 获取location参数
+     */
+    getLocationParam(location) {
+        if (typeof location == 'string') {
+            var locationArr = location.split(',');
+            if (locationArr.length === 2) {
+                location = {
+                    latitude: location.split(',')[0],
+                    longitude: location.split(',')[1]
+                };
+            } else {
+                location = {};
+            }
+        }
+        return location;
+    },
+
+    /**
+     * 回调函数默认处理
+     */
+    polyfillParam(param) {
+        param.success = param.success || function () { };
+        param.fail = param.fail || function () { };
+        param.complete = param.complete || function () { };
+    },
+
+    /**
+     * 验证param对应的key值是否为空
+     * 
+     * @param {Object} param 接口参数
+     * @param {String} key 对应参数的key
+     */
+    checkParamKeyEmpty(param, key) {
+        if (!param[key]) {
+            var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + key +'参数格式有误');
+            param.fail(errconf);
+            param.complete(errconf);
+            return true;
+        }
+        return false;
+    },
+
+    /**
+     * 验证参数中是否存在检索词keyword
+     * 
+     * @param {Object} param 接口参数
+     */
+    checkKeyword(param){
+        return !this.checkParamKeyEmpty(param, 'keyword');
+    },
+
+    /**
+     * 验证location值
+     * 
+     * @param {Object} param 接口参数
+     */
+    checkLocation(param) {
+        var location = this.getLocationParam(param.location);
+        if (!location || !location.latitude || !location.longitude) {
+            var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + ' location参数格式有误');
+            param.fail(errconf);
+            param.complete(errconf);
+            return false;
+        }
+        return true;
+    },
+
+    /**
+     * 构造错误数据结构
+     * @param {Number} errCode 错误码
+     * @param {Number} errMsg 错误描述
+     */
+    buildErrorConfig(errCode, errMsg) {
+        return {
+            status: errCode,
+            message: errMsg
+        };
+    },
+
+    /**
+     * 
+     * 数据处理函数
+     * 根据传入参数不同处理不同数据
+     * @param {String} feature 功能名称
+     * search 地点搜索
+     * suggest关键词提示
+     * reverseGeocoder逆地址解析
+     * geocoder地址解析
+     * getCityList获取城市列表:父集
+     * getDistrictByCityId获取区县列表:子集
+     * calculateDistance距离计算
+     * @param {Object} param 接口参数
+     * @param {Object} data 数据
+     */
+    handleData(param,data,feature){
+      if (feature == 'search') {
+        var searchResult = data.data;
+        var searchSimplify = [];
+        for (var i = 0; i < searchResult.length; i++) {
+          searchSimplify.push({
+            id: searchResult[i].id || null,
+            title: searchResult[i].title || null,
+            latitude: searchResult[i].location && searchResult[i].location.lat || null,
+            longitude: searchResult[i].location && searchResult[i].location.lng || null,
+            address: searchResult[i].address || null,
+            category: searchResult[i].category || null,
+            tel: searchResult[i].tel || null,
+            adcode: searchResult[i].ad_info && searchResult[i].ad_info.adcode || null,
+            city: searchResult[i].ad_info && searchResult[i].ad_info.city || null,
+            district: searchResult[i].ad_info && searchResult[i].ad_info.district || null,
+            province: searchResult[i].ad_info && searchResult[i].ad_info.province || null
+          })
+        }
+        param.success(data, {
+          searchResult: searchResult,
+          searchSimplify: searchSimplify
+        })
+      } else if (feature == 'suggest') {
+        var suggestResult = data.data;
+        var suggestSimplify = [];
+        for (var i = 0; i < suggestResult.length; i++) {
+          suggestSimplify.push({
+            adcode: suggestResult[i].adcode || null,
+            address: suggestResult[i].address || null,
+            category: suggestResult[i].category || null,
+            city: suggestResult[i].city || null,
+            district: suggestResult[i].district || null,
+            id: suggestResult[i].id || null,
+            latitude: suggestResult[i].location && suggestResult[i].location.lat || null,
+            longitude: suggestResult[i].location && suggestResult[i].location.lng || null,
+            province: suggestResult[i].province || null,
+            title: suggestResult[i].title || null,
+            type: suggestResult[i].type || null
+          })
+        }
+        param.success(data, {
+          suggestResult: suggestResult,
+          suggestSimplify: suggestSimplify
+          })
+      } else if (feature == 'reverseGeocoder') {
+        var reverseGeocoderResult = data.result;
+        var reverseGeocoderSimplify = {
+          address: reverseGeocoderResult.address || null,
+          latitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lat || null,
+          longitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lng || null,
+          adcode: reverseGeocoderResult.ad_info && reverseGeocoderResult.ad_info.adcode || null,
+          city: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.city || null,
+          district: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.district || null,
+          nation: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.nation || null,
+          province: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.province || null,
+          street: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.street || null,
+          street_number: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.street_number || null,
+          recommend: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses.recommend || null,
+          rough: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses.rough || null
+        };
+        if (reverseGeocoderResult.pois) {//判断是否返回周边poi
+          var pois = reverseGeocoderResult.pois;
+          var poisSimplify = [];
+          for (var i = 0;i < pois.length;i++) {
+            poisSimplify.push({
+              id: pois[i].id || null,
+              title: pois[i].title || null,
+              latitude: pois[i].location && pois[i].location.lat || null,
+              longitude: pois[i].location && pois[i].location.lng || null,
+              address: pois[i].address || null,
+              category: pois[i].category || null,
+              adcode: pois[i].ad_info && pois[i].ad_info.adcode || null,
+              city: pois[i].ad_info && pois[i].ad_info.city || null,
+              district: pois[i].ad_info && pois[i].ad_info.district || null,
+              province: pois[i].ad_info && pois[i].ad_info.province || null
+            })
+          }
+          param.success(data,{
+            reverseGeocoderResult: reverseGeocoderResult,
+            reverseGeocoderSimplify: reverseGeocoderSimplify,
+            pois: pois,
+            poisSimplify: poisSimplify
+          })
+        } else {
+          param.success(data, {
+            reverseGeocoderResult: reverseGeocoderResult,
+            reverseGeocoderSimplify: reverseGeocoderSimplify
+          })
+        }
+      } else if (feature == 'geocoder') {
+        var geocoderResult = data.result;
+        var geocoderSimplify = {
+          title: geocoderResult.title || null,
+          latitude: geocoderResult.location && geocoderResult.location.lat || null,
+          longitude: geocoderResult.location && geocoderResult.location.lng || null,
+          adcode: geocoderResult.ad_info && geocoderResult.ad_info.adcode || null,
+          province: geocoderResult.address_components && geocoderResult.address_components.province || null,
+          city: geocoderResult.address_components && geocoderResult.address_components.city || null,
+          district: geocoderResult.address_components && geocoderResult.address_components.district || null,
+          street: geocoderResult.address_components && geocoderResult.address_components.street || null,
+          street_number: geocoderResult.address_components && geocoderResult.address_components.street_number || null,
+          level: geocoderResult.level || null
+        };
+        param.success(data,{
+          geocoderResult: geocoderResult,
+          geocoderSimplify: geocoderSimplify
+        });
+      } else if (feature == 'getCityList') {
+        var provinceResult = data.result[0];
+        var cityResult = data.result[1];
+        var districtResult = data.result[2];
+        param.success(data,{
+          provinceResult: provinceResult,
+          cityResult: cityResult,
+          districtResult: districtResult
+        });
+      } else if (feature == 'getDistrictByCityId') {
+        var districtByCity = data.result[0];
+        param.success(data, districtByCity);
+      } else if (feature == 'calculateDistance') {
+        var calculateDistanceResult = data.result.elements;  
+        var distance = [];
+        for (var i = 0; i < calculateDistanceResult.length; i++){
+          distance.push(calculateDistanceResult[i].distance);
+        }   
+        param.success(data, {
+          calculateDistanceResult: calculateDistanceResult,
+          distance: distance
+          });
+      } else if (feature == 'direction') {
+        var direction = data.result.routes;
+        param.success(data,direction);
+      } else {
+        param.success(data);
+      }
+    },
+
+    /**
+     * 构造微信请求参数,公共属性处理
+     * 
+     * @param {Object} param 接口参数
+     * @param {Object} param 配置项
+     * @param {String} feature 方法名
+     */
+    buildWxRequestConfig(param, options, feature) {
+        var that = this;
+        options.header = { "content-type": "application/json" };
+        options.method = 'GET';
+        options.success = function (res) {
+            var data = res.data;
+            if (data.status === 0) {
+              that.handleData(param, data, feature);
+            } else {
+                param.fail(data);
+            }
+        };
+        options.fail = function (res) {
+            res.statusCode = ERROR_CONF.WX_ERR_CODE;
+            param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
+        };
+        options.complete = function (res) {
+            var statusCode = +res.statusCode;
+            switch(statusCode) {
+                case ERROR_CONF.WX_ERR_CODE: {
+                    param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
+                    break;
+                }
+                case ERROR_CONF.WX_OK_CODE: {
+                    var data = res.data;
+                    if (data.status === 0) {
+                        param.complete(data);
+                    } else {
+                        param.complete(that.buildErrorConfig(data.status, data.message));
+                    }
+                    break;
+                }
+                default:{
+                    param.complete(that.buildErrorConfig(ERROR_CONF.SYSTEM_ERR, ERROR_CONF.SYSTEM_ERR_MSG));
+                }
+
+            }
+        };
+        return options;
+    },
+
+    /**
+     * 处理用户参数是否传入坐标进行不同的处理
+     */
+    locationProcess(param, locationsuccess, locationfail, locationcomplete) {
+        var that = this;
+        locationfail = locationfail || function (res) {
+            res.statusCode = ERROR_CONF.WX_ERR_CODE;
+            param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
+        };
+        locationcomplete = locationcomplete || function (res) {
+            if (res.statusCode == ERROR_CONF.WX_ERR_CODE) {
+                param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
+            }
+        };
+        if (!param.location) {
+            that.getWXLocation(locationsuccess, locationfail, locationcomplete);
+        } else if (that.checkLocation(param)) {
+            var location = Utils.getLocationParam(param.location);
+            locationsuccess(location);
+        }
+    }
+};
+
+
+class QQMapWX {
+
+    /**
+     * 构造函数
+     * 
+     * @param {Object} options 接口参数,key 为必选参数
+     */
+    constructor(options) {
+        if (!options.key) {
+            throw Error('key值不能为空');
+        }
+        this.key = options.key;
+    };
+
+    /**
+     * POI周边检索
+     *
+     * @param {Object} options 接口参数对象
+     * 
+     * 参数对象结构可以参考
+     * @see http://lbs.qq.com/webservice_v1/guide-search.html
+     */
+    search(options) {
+        var that = this;
+        options = options || {};
+
+        Utils.polyfillParam(options);
+
+        if (!Utils.checkKeyword(options)) {
+            return;
+        }
+
+        var requestParam = {
+            keyword: options.keyword,
+            orderby: options.orderby || '_distance',
+            page_size: options.page_size || 10,
+            page_index: options.page_index || 1,
+            output: 'json',
+            key: that.key
+        };
+
+        if (options.address_format) {
+            requestParam.address_format = options.address_format;
+        }
+
+        if (options.filter) {
+            requestParam.filter = options.filter;
+        }
+
+        var distance = options.distance || "1000";
+        var auto_extend = options.auto_extend || 1;
+        var region = null;
+        var rectangle = null;
+
+        //判断城市限定参数
+        if (options.region) {
+          region = options.region;
+        }
+
+        //矩形限定坐标(暂时只支持字符串格式)
+        if (options.rectangle) {
+          rectangle = options.rectangle;
+        }
+
+        var locationsuccess = function (result) {        
+          if (region && !rectangle) {
+            //城市限定参数拼接
+            requestParam.boundary = "region(" + region + "," + auto_extend + "," + result.latitude + "," + result.longitude + ")";
+            if (options.sig) {
+              requestParam.sig = Utils.getSig(requestParam, options.sig, 'search');
+            }
+          } else if (rectangle && !region) {
+            //矩形搜索
+            requestParam.boundary = "rectangle(" + rectangle + ")";
+            if (options.sig) {
+              requestParam.sig = Utils.getSig(requestParam, options.sig, 'search');
+            }
+            } else {
+              requestParam.boundary = "nearby(" + result.latitude + "," + result.longitude + "," + distance + "," + auto_extend + ")";
+            if (options.sig) {
+              requestParam.sig = Utils.getSig(requestParam, options.sig, 'search');
+            }
+            }            
+            wx.request(Utils.buildWxRequestConfig(options, {
+                url: URL_SEARCH,
+                data: requestParam
+            }, 'search'));
+        };
+        Utils.locationProcess(options, locationsuccess);
+    };
+
+    /**
+     * sug模糊检索
+     *
+     * @param {Object} options 接口参数对象
+     * 
+     * 参数对象结构可以参考
+     * http://lbs.qq.com/webservice_v1/guide-suggestion.html
+     */
+    getSuggestion(options) {
+        var that = this;
+        options = options || {};
+        Utils.polyfillParam(options);
+
+        if (!Utils.checkKeyword(options)) {
+            return;
+        }
+
+        var requestParam = {
+            keyword: options.keyword,
+            region: options.region || '全国',
+            region_fix: options.region_fix || 0,
+            policy: options.policy || 0,
+            page_size: options.page_size || 10,//控制显示条数
+            page_index: options.page_index || 1,//控制页数
+            get_subpois : options.get_subpois || 0,//返回子地点
+            output: 'json',
+            key: that.key
+        };
+        //长地址
+        if (options.address_format) {
+          requestParam.address_format = options.address_format;
+        }
+        //过滤
+        if (options.filter) {
+          requestParam.filter = options.filter;
+        }
+        //排序
+        if (options.location) {
+          var locationsuccess = function (result) {
+            requestParam.location = result.latitude + ',' + result.longitude;
+            if (options.sig) {
+              requestParam.sig = Utils.getSig(requestParam, options.sig, 'suggest');
+            }
+            wx.request(Utils.buildWxRequestConfig(options, {
+              url: URL_SUGGESTION,
+              data: requestParam
+            }, "suggest"));      
+          };
+          Utils.locationProcess(options, locationsuccess);
+        } else {
+          if (options.sig) {
+            requestParam.sig = Utils.getSig(requestParam, options.sig, 'suggest');
+          }
+          wx.request(Utils.buildWxRequestConfig(options, {
+            url: URL_SUGGESTION,
+            data: requestParam
+          }, "suggest"));      
+        }        
+    };
+
+    /**
+     * 逆地址解析
+     *
+     * @param {Object} options 接口参数对象
+     * 
+     * 请求参数结构可以参考
+     * http://lbs.qq.com/webservice_v1/guide-gcoder.html
+     */
+    reverseGeocoder(options) {
+        var that = this;
+        options = options || {};
+        Utils.polyfillParam(options);
+        var requestParam = {
+            coord_type: options.coord_type || 5,
+            get_poi: options.get_poi || 0,
+            output: 'json',
+            key: that.key
+        };
+        if (options.poi_options) {
+            requestParam.poi_options = options.poi_options
+        }
+
+        var locationsuccess = function (result) {
+            requestParam.location = result.latitude + ',' + result.longitude;
+          if (options.sig) {
+            requestParam.sig = Utils.getSig(requestParam, options.sig, 'reverseGeocoder');
+          }
+            wx.request(Utils.buildWxRequestConfig(options, {
+                url: URL_GET_GEOCODER,
+                data: requestParam
+            }, 'reverseGeocoder'));
+        };
+        Utils.locationProcess(options, locationsuccess);
+    };
+
+    /**
+     * 地址解析
+     *
+     * @param {Object} options 接口参数对象
+     * 
+     * 请求参数结构可以参考
+     * http://lbs.qq.com/webservice_v1/guide-geocoder.html
+     */
+    geocoder(options) {
+        var that = this;
+        options = options || {};
+        Utils.polyfillParam(options);
+
+        if (Utils.checkParamKeyEmpty(options, 'address')) {
+            return;
+        }
+
+        var requestParam = {
+            address: options.address,
+            output: 'json',
+            key: that.key
+        };
+
+        //城市限定
+        if (options.region) {
+          requestParam.region = options.region;
+        }
+
+        if (options.sig) {
+          requestParam.sig = Utils.getSig(requestParam, options.sig, 'geocoder');
+        }
+
+        wx.request(Utils.buildWxRequestConfig(options, {
+            url: URL_GET_GEOCODER,
+            data: requestParam
+        },'geocoder'));
+    };
+
+
+    /**
+     * 获取城市列表
+     *
+     * @param {Object} options 接口参数对象
+     * 
+     * 请求参数结构可以参考
+     * http://lbs.qq.com/webservice_v1/guide-region.html
+     */
+    getCityList(options) {
+        var that = this;
+        options = options || {};
+        Utils.polyfillParam(options);
+        var requestParam = {
+            output: 'json',
+            key: that.key
+        };
+
+        if (options.sig) {
+          requestParam.sig = Utils.getSig(requestParam, options.sig, 'getCityList');
+        }
+
+        wx.request(Utils.buildWxRequestConfig(options, {
+            url: URL_CITY_LIST,
+            data: requestParam
+        },'getCityList'));
+    };
+
+    /**
+     * 获取对应城市ID的区县列表
+     *
+     * @param {Object} options 接口参数对象
+     * 
+     * 请求参数结构可以参考
+     * http://lbs.qq.com/webservice_v1/guide-region.html
+     */
+    getDistrictByCityId(options) {
+        var that = this;
+        options = options || {};
+        Utils.polyfillParam(options);
+
+        if (Utils.checkParamKeyEmpty(options, 'id')) {
+            return;
+        }
+
+        var requestParam = {
+            id: options.id || '',
+            output: 'json',
+            key: that.key
+        };
+
+        if (options.sig) {
+          requestParam.sig = Utils.getSig(requestParam, options.sig, 'getDistrictByCityId');
+        }
+
+        wx.request(Utils.buildWxRequestConfig(options, {
+            url: URL_AREA_LIST,
+            data: requestParam
+        },'getDistrictByCityId'));
+    };
+
+    /**
+     * 用于单起点到多终点的路线距离(非直线距离)计算:
+     * 支持两种距离计算方式:步行和驾车。
+     * 起点到终点最大限制直线距离10公里。
+     *
+     * 新增直线距离计算。
+     * 
+     * @param {Object} options 接口参数对象
+     * 
+     * 请求参数结构可以参考
+     * http://lbs.qq.com/webservice_v1/guide-distance.html
+     */
+    calculateDistance(options) {
+        var that = this;
+        options = options || {};
+        Utils.polyfillParam(options);
+
+        if (Utils.checkParamKeyEmpty(options, 'to')) {
+            return;
+        }
+
+        var requestParam = {
+            mode: options.mode || 'walking',
+            to: Utils.location2query(options.to),
+            output: 'json',
+            key: that.key
+        };
+
+        if (options.from) {
+          options.location = options.from;
+        }
+
+        //计算直线距离
+        if(requestParam.mode == 'straight'){        
+          var locationsuccess = function (result) {
+            var locationTo = Utils.getEndLocation(requestParam.to);//处理终点坐标
+            var data = {
+              message:"query ok",
+              result:{
+                elements:[]
+              },
+              status:0
+            };
+            for (var i = 0; i < locationTo.length; i++) {
+              data.result.elements.push({//将坐标存入
+                distance: Utils.getDistance(result.latitude, result.longitude, locationTo[i].lat, locationTo[i].lng),
+                duration:0,
+                from:{
+                  lat: result.latitude,
+                  lng:result.longitude
+                },
+                to:{
+                  lat: locationTo[i].lat,
+                  lng: locationTo[i].lng
+                }
+              });            
+            }
+            var calculateResult = data.result.elements;
+            var distanceResult = [];
+            for (var i = 0; i < calculateResult.length; i++) {
+              distanceResult.push(calculateResult[i].distance);
+            }  
+            return options.success(data,{
+              calculateResult: calculateResult,
+              distanceResult: distanceResult
+            });
+          };
+          
+          Utils.locationProcess(options, locationsuccess);
+        } else {
+          var locationsuccess = function (result) {
+            requestParam.from = result.latitude + ',' + result.longitude;
+            if (options.sig) {
+              requestParam.sig = Utils.getSig(requestParam, options.sig, 'calculateDistance');
+            }
+            wx.request(Utils.buildWxRequestConfig(options, {
+              url: URL_DISTANCE,
+              data: requestParam
+            },'calculateDistance'));
+          };
+
+          Utils.locationProcess(options, locationsuccess);
+        }      
+    };
+
+  /**
+   * 路线规划:
+   * 
+   * @param {Object} options 接口参数对象
+   * 
+   * 请求参数结构可以参考
+   * https://lbs.qq.com/webservice_v1/guide-road.html
+   */
+  direction(options) {
+    var that = this;
+    options = options || {};
+    Utils.polyfillParam(options);
+
+    if (Utils.checkParamKeyEmpty(options, 'to')) {
+      return;
+    }
+
+    var requestParam = {
+      output: 'json',
+      key: that.key
+    };
+
+    //to格式处理
+    if (typeof options.to == 'string') {
+      requestParam.to = options.to;
+    } else {
+      requestParam.to = options.to.latitude + ',' + options.to.longitude;
+    }
+    //初始化局部请求域名
+    var SET_URL_DIRECTION = null;
+    //设置默认mode属性
+    options.mode = options.mode || MODE.driving;
+
+    //设置请求域名
+    SET_URL_DIRECTION = URL_DIRECTION + options.mode;
+
+    if (options.from) {
+      options.location = options.from;
+    }
+
+    if (options.mode == MODE.driving) {
+      if (options.from_poi) {
+        requestParam.from_poi = options.from_poi;
+      }
+      if (options.heading) {
+        requestParam.heading = options.heading;
+      }
+      if (options.speed) {
+        requestParam.speed = options.speed;
+      }
+      if (options.accuracy) {
+        requestParam.accuracy = options.accuracy;
+      }
+      if (options.road_type) {
+        requestParam.road_type = options.road_type;
+      }
+      if (options.to_poi) {
+        requestParam.to_poi = options.to_poi;
+      }
+      if (options.from_track) {
+        requestParam.from_track = options.from_track;
+      }
+      if (options.waypoints) {
+        requestParam.waypoints = options.waypoints;
+      }
+      if (options.policy) {
+        requestParam.policy = options.policy;
+      }
+      if (options.plate_number) {
+        requestParam.plate_number = options.plate_number;
+      }
+    }
+
+    if (options.mode == MODE.transit) {
+      if (options.departure_time) {
+        requestParam.departure_time = options.departure_time;
+      }
+      if (options.policy) {
+        requestParam.policy = options.policy;
+      }
+    } 
+
+    var locationsuccess = function (result) {
+      requestParam.from = result.latitude + ',' + result.longitude;
+      if (options.sig) {
+        requestParam.sig = Utils.getSig(requestParam, options.sig, 'direction',options.mode);
+      }
+      wx.request(Utils.buildWxRequestConfig(options, {
+        url: SET_URL_DIRECTION,
+        data: requestParam
+      }, 'direction'));
+    };
+
+    Utils.locationProcess(options, locationsuccess);
+  }
+};
+
+module.exports = QQMapWX;

文件差異過大導致無法顯示
+ 0 - 0
uni_applet/js_sdk/qqmap-wx-jssdk.min.js


+ 50 - 0
uni_applet/js_sdk/share.js

@@ -0,0 +1,50 @@
+export default {
+	data() {
+		return {
+
+		}
+	},
+	onLoad: function() {
+		wx.showShareMenu({
+			withShareTicket: true,
+			menus: ["shareAppMessage", "shareTimeline"]
+		})
+	},
+	onShareAppMessage(res) {
+		console.log(1111)
+		let that = this;
+		// let imageUrl = that.shareUrl || '';
+		let imageUrl = 'https://taohaoliang.oss-cn-beijing.aliyuncs.com/pcfiles/min1.png'
+		if (res.from === 'button') {
+			console.log(2222)
+		//这块需要传参,不然链接地址进去获取不到数据
+			// let path = `/` + that.$scope.route + `?item=` + that.$scope.options.item;
+			let path = `/pages/cardHolder/scanCodeAddCard`
+			return {
+				title: '商品分享~',
+				path: path,
+				imageUrl: imageUrl,
+				content:'这里是分享内容啊!',
+				desc:"这里是自定义描述啊!"
+			};
+		}
+		if (res.from === 'menu') {
+			return {
+				title: '商通线上商城',
+				path: '/pages/tabBarPro/index/index',
+				imageUrl: imageUrl
+			};
+		}
+	},
+	// 分享到朋友圈
+	onShareTimeline() {
+		return {
+			title: '名片box',
+			path: '/pages/cardHolder/cardHolder',
+			imageUrl: 'https://taohaoliang.oss-cn-beijing.aliyuncs.com/pcfiles/%E5%90%8D%E7%89%87box.png'
+		};
+	},
+	methods: {
+
+	}
+}

+ 272 - 0
uni_applet/js_sdk/wa-permission/permission.js

@@ -0,0 +1,272 @@
+/**
+ * 本模块封装了Android、iOS的应用权限判断、打开应用权限设置界面、以及位置系统服务是否开启
+ */
+
+var isIos
+// #ifdef APP-PLUS
+isIos = (plus.os.name == "iOS")
+// #endif
+
+// 判断推送权限是否开启
+function judgeIosPermissionPush() {
+	var result = false;
+	var UIApplication = plus.ios.import("UIApplication");
+	var app = UIApplication.sharedApplication();
+	var enabledTypes = 0;
+	if (app.currentUserNotificationSettings) {
+		var settings = app.currentUserNotificationSettings();
+		enabledTypes = settings.plusGetAttribute("types");
+		console.log("enabledTypes1:" + enabledTypes);
+		if (enabledTypes == 0) {
+			console.log("推送权限没有开启");
+		} else {
+			result = true;
+			console.log("已经开启推送功能!")
+		}
+		plus.ios.deleteObject(settings);
+	} else {
+		enabledTypes = app.enabledRemoteNotificationTypes();
+		if (enabledTypes == 0) {
+			console.log("推送权限没有开启!");
+		} else {
+			result = true;
+			console.log("已经开启推送功能!")
+		}
+		console.log("enabledTypes2:" + enabledTypes);
+	}
+	plus.ios.deleteObject(app);
+	plus.ios.deleteObject(UIApplication);
+	return result;
+}
+
+// 判断定位权限是否开启
+function judgeIosPermissionLocation() {
+	var result = false;
+	var cllocationManger = plus.ios.import("CLLocationManager");
+	var status = cllocationManger.authorizationStatus();
+	result = (status != 2)
+	console.log("定位权限开启:" + result);
+	// 以下代码判断了手机设备的定位是否关闭,推荐另行使用方法 checkSystemEnableLocation
+	/* var enable = cllocationManger.locationServicesEnabled();
+	var status = cllocationManger.authorizationStatus();
+	console.log("enable:" + enable);
+	console.log("status:" + status);
+	if (enable && status != 2) {
+		result = true;
+		console.log("手机定位服务已开启且已授予定位权限");
+	} else {
+		console.log("手机系统的定位没有打开或未给予定位权限");
+	} */
+	plus.ios.deleteObject(cllocationManger);
+	return result;
+}
+
+// 判断麦克风权限是否开启
+function judgeIosPermissionRecord() {
+	var result = false;
+	var avaudiosession = plus.ios.import("AVAudioSession");
+	var avaudio = avaudiosession.sharedInstance();
+	var permissionStatus = avaudio.recordPermission();
+	console.log("permissionStatus:" + permissionStatus);
+	if (permissionStatus == 1684369017 || permissionStatus == 1970168948) {
+		console.log("麦克风权限没有开启");
+	} else {
+		result = true;
+		console.log("麦克风权限已经开启");
+	}
+	plus.ios.deleteObject(avaudiosession);
+	return result;
+}
+
+// 判断相机权限是否开启
+function judgeIosPermissionCamera() {
+	var result = false;
+	var AVCaptureDevice = plus.ios.import("AVCaptureDevice");
+	var authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide');
+	console.log("authStatus:" + authStatus);
+	if (authStatus == 3) {
+		result = true;
+		console.log("相机权限已经开启");
+	} else {
+		console.log("相机权限没有开启");
+	}
+	plus.ios.deleteObject(AVCaptureDevice);
+	return result;
+}
+
+// 判断相册权限是否开启
+function judgeIosPermissionPhotoLibrary() {
+	var result = false;
+	var PHPhotoLibrary = plus.ios.import("PHPhotoLibrary");
+	var authStatus = PHPhotoLibrary.authorizationStatus();
+	console.log("authStatus:" + authStatus);
+	if (authStatus == 3) {
+		result = true;
+		console.log("相册权限已经开启");
+	} else {
+		console.log("相册权限没有开启");
+	}
+	plus.ios.deleteObject(PHPhotoLibrary);
+	return result;
+}
+
+// 判断通讯录权限是否开启
+function judgeIosPermissionContact() {
+	var result = false;
+	var CNContactStore = plus.ios.import("CNContactStore");
+	var cnAuthStatus = CNContactStore.authorizationStatusForEntityType(0);
+	if (cnAuthStatus == 3) {
+		result = true;
+		console.log("通讯录权限已经开启");
+	} else {
+		console.log("通讯录权限没有开启");
+	}
+	plus.ios.deleteObject(CNContactStore);
+	return result;
+}
+
+// 判断日历权限是否开启
+function judgeIosPermissionCalendar() {
+	var result = false;
+	var EKEventStore = plus.ios.import("EKEventStore");
+	var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(0);
+	if (ekAuthStatus == 3) {
+		result = true;
+		console.log("日历权限已经开启");
+	} else {
+		console.log("日历权限没有开启");
+	}
+	plus.ios.deleteObject(EKEventStore);
+	return result;
+}
+
+// 判断备忘录权限是否开启
+function judgeIosPermissionMemo() {
+	var result = false;
+	var EKEventStore = plus.ios.import("EKEventStore");
+	var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(1);
+	if (ekAuthStatus == 3) {
+		result = true;
+		console.log("备忘录权限已经开启");
+	} else {
+		console.log("备忘录权限没有开启");
+	}
+	plus.ios.deleteObject(EKEventStore);
+	return result;
+}
+
+// Android权限查询
+function requestAndroidPermission(permissionID) {
+	return new Promise((resolve, reject) => {
+		plus.android.requestPermissions(
+			[permissionID], // 理论上支持多个权限同时查询,但实际上本函数封装只处理了一个权限的情况。有需要的可自行扩展封装
+			function(resultObj) {
+				var result = 0;
+				for (var i = 0; i < resultObj.granted.length; i++) {
+					var grantedPermission = resultObj.granted[i];
+					console.log('已获取的权限:' + grantedPermission);
+					result = 1
+				}
+				for (var i = 0; i < resultObj.deniedPresent.length; i++) {
+					var deniedPresentPermission = resultObj.deniedPresent[i];
+					console.log('拒绝本次申请的权限:' + deniedPresentPermission);
+					result = 0
+				}
+				for (var i = 0; i < resultObj.deniedAlways.length; i++) {
+					var deniedAlwaysPermission = resultObj.deniedAlways[i];
+					console.log('永久拒绝申请的权限:' + deniedAlwaysPermission);
+					result = -1
+				}
+				resolve(result);
+				// 若所需权限被拒绝,则打开APP设置界面,可以在APP设置界面打开相应权限
+				// if (result != 1) {
+				// gotoAppPermissionSetting()
+				// }
+			},
+			function(error) {
+				console.log('申请权限错误:' + error.code + " = " + error.message);
+				resolve({
+					code: error.code,
+					message: error.message
+				});
+			}
+		);
+	});
+}
+
+// 使用一个方法,根据参数判断权限
+function judgeIosPermission(permissionID) {
+	if (permissionID == "location") {
+		return judgeIosPermissionLocation()
+	} else if (permissionID == "camera") {
+		return judgeIosPermissionCamera()
+	} else if (permissionID == "photoLibrary") {
+		return judgeIosPermissionPhotoLibrary()
+	} else if (permissionID == "record") {
+		return judgeIosPermissionRecord()
+	} else if (permissionID == "push") {
+		return judgeIosPermissionPush()
+	} else if (permissionID == "contact") {
+		return judgeIosPermissionContact()
+	} else if (permissionID == "calendar") {
+		return judgeIosPermissionCalendar()
+	} else if (permissionID == "memo") {
+		return judgeIosPermissionMemo()
+	}
+	return false;
+}
+
+// 跳转到**应用**的权限页面
+function gotoAppPermissionSetting() {
+	if (isIos) {
+		var UIApplication = plus.ios.import("UIApplication");
+		var application2 = UIApplication.sharedApplication();
+		var NSURL2 = plus.ios.import("NSURL");
+		// var setting2 = NSURL2.URLWithString("prefs:root=LOCATION_SERVICES");		
+		var setting2 = NSURL2.URLWithString("app-settings:");
+		application2.openURL(setting2);
+
+		plus.ios.deleteObject(setting2);
+		plus.ios.deleteObject(NSURL2);
+		plus.ios.deleteObject(application2);
+	} else {
+		// console.log(plus.device.vendor);
+		var Intent = plus.android.importClass("android.content.Intent");
+		var Settings = plus.android.importClass("android.provider.Settings");
+		var Uri = plus.android.importClass("android.net.Uri");
+		var mainActivity = plus.android.runtimeMainActivity();
+		var intent = new Intent();
+		intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+		var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
+		intent.setData(uri);
+		mainActivity.startActivity(intent);
+	}
+}
+
+// 检查系统的设备服务是否开启
+// var checkSystemEnableLocation = async function () {
+function checkSystemEnableLocation() {
+	if (isIos) {
+		var result = false;
+		var cllocationManger = plus.ios.import("CLLocationManager");
+		var result = cllocationManger.locationServicesEnabled();
+		console.log("系统定位开启:" + result);
+		plus.ios.deleteObject(cllocationManger);
+		return result;
+	} else {
+		var context = plus.android.importClass("android.content.Context");
+		var locationManager = plus.android.importClass("android.location.LocationManager");
+		var main = plus.android.runtimeMainActivity();
+		var mainSvr = main.getSystemService(context.LOCATION_SERVICE);
+		var result = mainSvr.isProviderEnabled(locationManager.GPS_PROVIDER);
+		console.log("系统定位开启:" + result);
+		return result
+	}
+}
+
+module.exports = {
+	judgeIosPermission: judgeIosPermission,
+	requestAndroidPermission: requestAndroidPermission,
+	checkSystemEnableLocation: checkSystemEnableLocation,
+	gotoAppPermissionSetting: gotoAppPermissionSetting
+}

+ 594 - 0
uni_applet/js_sdk/xfl-DateFormat/DateFormat.js

@@ -0,0 +1,594 @@
+
+
+/**
+ * 日期格式转换模块,还可以从字符串中提取日期,同时提供了几个数字格式转换函数。 
+ * 2019.5.12  v1.0.0
+ * 
+## 背景
+当需要将日期转换为指定的格式时,或者需要从某个字符串中按照指定的格式提取日期,均可使用本模块。
+
+## 使用说明  
+格式标记的含意:   
+yyyy-四位阿拉伯数字的年份,如: 2018  
+YYYY-四位中文数字的年份,如: 二零一八  
+yy-两位阿拉伯数字的年份,如: 18  
+YY-两位中文数字的年份,如: 一八  
+mm-一位或两位阿拉伯数字的月数,如: 3 、 12   
+0m-两位阿拉伯数字的月数,如: 03 、 12  
+MM-一位或两位中文数字的月数,如:  三、 十二   
+dd-一位或两位阿拉伯数字的日数,如: 3 、 12   
+0d-两位阿拉伯数字的日数,如: 03 、 12、28   
+DD-中文数字的日数,如:  三、 十二、二十八   
+hh-一位或两位阿拉伯数字的小时数,如: 3 、 12   
+0h-两位阿拉伯数字的小时数,如: 03 、 12   
+HH-中文数字的小时数,如:  三、 十二、二十三   
+ff-一位或两位阿拉伯数字的分钟数,如: 3 、 12、56  
+0f-两位阿拉伯数字的分钟数,如: 03 、 12  
+0F-中文数字的分钟,如:  零三、 十二、二十三  
+FF-中文数字的分钟,如:  三、 十二、二十三   
+ss-一位或两位阿拉伯数字的秒数,如: 3 、 12   
+0s-两位阿拉伯数字的秒数,如: 03 、 12   
+0S-中文数字的秒数,如:  零三、 十二、二十三   
+SS-中文数字的秒数,如:  三、 十二、二十三   
+w-阿拉伯数字的星期数,如:  1、6、7  
+W-中文数字的星期数,如:  一、三、六、日   
+WT-中文数字的星期数,如:  一、三、六、天   
+  
+### 属性  
+这些属性是可读可写的,如果直接更改,则会自动计算其关联的其它值。  
+dataformat.year; //年 [1000, 3000]  
+dataformat.moth; //月 [1, 12]  
+dataformat.day; //日 [1, 31]  
+dataformat.hour; //时 [0, 23]  
+dataformat.minute; //分 [0, 59]  
+dataformat.second; //秒 [0, 59]  
+dataformat.mscond; //毫秒 [0, 999]  
+dataformat.week; //星期 [1, 7]  
+dataformat.timestamp; //时间戳  
+
+### 构造函数
+构造函数内部直接调用的 setTime(ms, formatStr);
+  
+### toString(formatStr)  
+  
+使用举例:   
+1. toString() //结果: '2014-11-7 03:08:01'  
+2. toString('yyyy-mm-dd hh:ff:ss') //结果: '2014-11-11 13:12:34'  
+3. toString('yyyy年mm月dd日') //结果: '2014年11月11日'  
+4. toString('yy年mm月dd日') //结果: '14年11月11日'  
+5. toString('YY年MM月DD日') //结果: '一四年十一月八日'  
+6. toString('星期W') //结果: '星期日'  或 '星期三'
+7. toString('星期WT')  //结果: '星期天'  或 '星期三'
+8. toString('星期w') //结果: '星期1'   或 '星期7'
+9. toString('hh:0f:ss') //结果: '3:04:5'  
+10. toString('date') //结果: '2014-11-7'  
+11. toString('time') //结果: '03:08:01'  
+  
+### setTime(ms, formtStr)  
+设置时间。如果存在 formatStr, 则表示从字符串 ms 中按 formatStr中的规则提取时间。  
+  
+使用举例:  
+1. setTime( new Date() ); //传入一个时间对象  
+2. setTime( new Date().getTime() ); //传入一个毫秒数  
+3. setTime( new Date().getTime() ); //传入一个毫秒数  
+4. setTime( new Date().getTime() + '' ); //传入一个毫秒数的字符串  
+5. setTime(); //传入其它的值或不传入,则创建当前时间  
+6. setTime(str, formatStr); //传入两个参数,第一个为字符串,第二个为格式规则,表示按照规则从字符串中提取时间  
+7. setTime('今天是2014年8月9号,天气特别好,但明天是2014年8月10号,....', '明天是yyyy年mm月dd号');  // 得到  2014.8.10 0:0:0
+  
+### fillChar(num, len, char)  
+字符填充  
+  
+使用举例:   
+1. fillChar(44, 5, '*'); //结果: '***44'   
+2. fillChar(44, 5); //结果: '00044'   
+3. fillChar('44', 5); //结果: '   44'   
+4. fillChar('aaa', 5); //结果: '   aa'   
+5. fillChar('aaa', 5, '$'); //结果: '$$aaa'   
+5. fillChar('aaa', 1, '$'); //结果: 'aaa'  //指定的长度太小,不做任何操作。  
+  
+### perNumToChinese(num)  
+将阿拉伯数字每一位对应转换为中文的格式。  
+  
+使用举例:   
+1. perNumToChinese(34); //结果: '三四'  
+2. perNumToChinese(3874); //结果: '三八七四'  
+3. perNumToChinese('3874'); //结果: '三八七四'  
+4. perNumToChinese('404'); //结果: '四零四'    
+5. perNumToChinese('0  04   '); //结果: '零零四'  
+6. perNumToChinese('  '); //结果: ''  
+  
+### perChineseToNum(numStr)
+将中文的格式的数字每一位对应转换为阿拉伯数字。 是perNumToChinese(num)的逆操作。  
+  
+使用举例:   
+1. perChineseToNum('三四'); //结果: 34  
+2. perChineseToNum('三八七四'); //结果: 3874  
+3. perChineseToNum('四零四'); //结果: 404  
+4. perChineseToNum('零零四'); //结果: 4  
+5. perChineseToNum('  '); //结果: NaN  
+  
+### numToChinese(num)  
+将阿拉伯数字转换为中文的格式 , 最多只能处理13位数(万亿)  
+  
+使用举例:   
+1. numToChinese(0); //结果: '零'  
+2. numToChinese(5); //结果: '五'  
+3. numToChinese(16); //结果: '十六'  
+4. numToChinese(34); //结果: '三十四'  
+5. numToChinese(106); //结果: '一百零六'  
+6. numToChinese(886); //结果: '八百八十六'  
+7. numToChinese(1004); //结果: '一千零四'  
+8. numToChinese(1000); //结果: '一千'  
+9. numToChinese(9904); //结果: '九千九百零四'  
+10. numToChinese(19904); //结果: '一万九千九百零四'  
+11. numToChinese(10004); //结果: '一万零四'  
+12. numToChinese(10000); //结果: '一万'  
+13. numToChinese(100404); //结果: '十万零四百零四'  
+14. numToChinese(9000000); //结果: '九百万'  
+15. numToChinese(90000000); //结果: '九千万'  
+16. numToChinese(900000000); //结果: '九亿'  
+17. numToChinese(9000000000); //结果: '九十亿'  
+18. numToChinese(9020030401); //结果: '九十亿零二千零三万零四百零一'  
+19. numToChinese(90000000000); //结果: '九百亿'  
+20. numToChinese(900000000000); //结果: '九千亿'  
+21. numToChinese(9000000000000); //结果: '九万亿'  
+22. numToChinese(90000000000000); //结果: undefined  
+  
+  
+### chineseToNum (numStr)
+将中文的格式的数字转换为阿拉伯数字, 最多只能处理13位数(万亿)。 是 numToChinese(num) 函数的逆操作。  
+  
+使用举例:   
+1. chineseToNum('十六'); //结果: 16  
+2. chineseToNum('一万九千九百零四'); //结果: 19904  
+3. chineseToNum('十万零四百零四'); //结果: 100404   
+4. chineseToNum('九十亿零二千零三万零四百零一'); //结果: 9020030401  
+ */
+export default class DateFormat{
+    constructor(ms, formatStr){
+        this.setTime(ms, formatStr);
+    }
+
+    set year(y){ if(this.y !== y){ this.__date.setFullYear(y); this.setTime(this.__date); }  }
+    get year(){ return this.y; }
+
+    set month(y){ if(this.m !== y){ this.__date.setMonth(y-1); this.setTime(this.__date); }  }
+    get month(){ return this.m; }
+
+    set day(y){ if(this.d !== y){ this.__date.setDate(y); this.setTime(this.__date); }  }
+    get day(){ return this.d; }
+
+    set hour(y){ if(this.h !== y){ this.__date.setHours(y); this.setTime(this.__date); }  }
+    get hour(){ return this.h; }
+
+    set minute(y){ if(this.f !== y){ this.__date.setMinutes(y); this.setTime(this.__date); }  }
+    get minute(){ return this.f; }
+
+    set second(y){ if(this.s !== y){ this.__date.setSeconds(y); this.setTime(this.__date); }  }
+    get second(){ return this.s; }
+
+    set msecond(y){ if(this.ms !== y){ this.__date.setMilliseconds(y); this.setTime(this.__date); }  }
+    get msecond(){ return this.ms; }
+
+    get week(){ return this.w; }
+
+     setTime(ms, formatStr){  //切换时间
+        if(formatStr && typeof ms === 'string'){
+            let result;
+            if(this.__analysis){
+                result = this.__analysis(ms, formatStr);
+            }
+            if(!result){
+                console.log('匹配时间失败,默认设置为当前时间');
+                ms = '';
+            }else{
+                ms = result;
+            }
+        }
+        let date;
+        if(ms instanceof Date){
+            date = ms;
+        }else if(typeof ms === 'number'){
+            date = new Date(ms);
+        }else if( parseInt(ms) > 0 ){
+            date = new Date(parseInt(ms));
+        }else{
+            if(typeof ms === 'object' && ms.y && ms.m && ms.d){
+                date = new Date(ms.y, ms.m-1, ms.d, ms.h || 0, ms.f || 0, ms.s || 0, ms.ms || 0);
+            }else{
+                date = new Date();
+            }
+        }
+
+        this.y = date.getFullYear(), //年 [1000, 3000]
+        this.m = date.getMonth() + 1, //月 [1, 12]
+        this.d = date.getDate(), //日 [1, 31]
+        this.h = date.getHours(), //时 [0, 23]
+        this.f = date.getMinutes(), //分 [0, 59]
+        this.s = date.getSeconds(), //秒 [0, 59]
+        this.ms = date.getMilliseconds(), //秒 [0, 9999]
+        this.w = date.getDay(); //星期 [1, 7]
+        if(this.w === 0){  //0表示星期天
+            this.w = 7;
+        }
+        this.timestamp = date.getTime(); //时间戳
+
+        this.__date = date;
+        
+        return this;
+     }
+
+/***************************************************** 时间格式化 *********************************************** */
+     toString(formatStr){  //将时间转换为格式的字符串
+         if(formatStr){
+            if(formatStr === 'time'){
+                //03:00:01
+                return this.fillChar(this.h, 2) + ':' + this.fillChar(this.f, 2) + ':' + this.fillChar(this.s, 2);
+            }else if(formatStr === 'date'){
+                //2014-11-7
+                return this.y + '-' + this.m + '-' + this.d;
+            }else{
+               return this.__replaceTime(formatStr);
+            }
+            
+        }else{
+            //2014-11-7 03:00:01
+            return this.y + '-' + this.m + '-' + this.d + ' ' + this.fillChar(this.h, 2) + ':' + this.fillChar(this.f, 2) + ':' + this.fillChar(this.s, 2);
+        }
+     }
+
+     valueOf(){
+         return this.toString();
+     }
+ 
+     __replaceTime(format){
+        var str = 
+        'y{4}|Y{4}|y{2}|Y{2}|mm|MM|0m|dd|DD|0d|hh|HH|0h|ff|FF|0F|0f|ss|SS|0S|0s|w|WT|W';
+        var reg = new RegExp(str, 'g');
+
+
+        format = format.replace(reg, (mstr)=>{
+           switch(mstr){
+              case 'yyyy': return this.y;                        // [0000, 9999]
+              case 'YYYY': return this.perNumToChinese(this.y);  // [零零零零-九九九九]   
+              case 'yy': return ('' + this.y).slice(-2);         // [00, 99]
+              case 'YY': return this.perNumToChinese( +('' + this.y).slice(-2) ); // [零零-九九]    
+              case 'mm': return this.m;                          // [1, 12]
+              case 'MM': return this.numToChinese(this.m);       // [一-十二]    
+              case '0m': return this.m < 10 ? '0'+ this.m : this.m; // [01, 12]
+              case 'dd': return this.d;                          // [1, 31]
+              case 'DD': return this.numToChinese(this.d);       // [一-三十一]  
+              case '0d': return this.d < 10 ? '0' + this.d : this.d; // [01, 31]
+              case 'hh': return this.h;                          // [0, 24]
+              case 'HH': return this.numToChinese(this.h);       // [零-二十四]  
+              case '0h': return this.h < 10 ? '0' + this.h : this.h; // [00, 24]
+              case 'ff': return this.f;                          // [0, 60]
+              case 'FF': return this.numToChinese(this.f);       // [零-六十]
+              case '0F': return this.f<10&&this.f>0 ? ('零' + this.numToChinese(this.f) ):this.numToChinese(this.f); // [零零-六十]   
+              case '0f': return this.f < 10 ? '0' + this.f : this.f; // [00, 60]
+              case 'ss': return this.s;                          // [0, 60]
+              case 'SS': return this.numToChinese(this.s);       // [零-六十]
+              case '0S': return this.s<10&&this.s>0 ? ('零' + this.numToChinese(this.s) ):this.numToChinese(this.s); // [零零-六十]    
+              case '0s': return this.s < 10 ? '0' + this.s : this.s;  // [00, 60]
+              case 'w': return this.w;                           // [7]
+              case 'W': return this.w == 7 ? '日' : this.numToChinese(this.w);  // [天|日]
+              case 'WT': return this.w == 7 ? '天' : this.numToChinese(this.w);  // [天|日]
+           }
+        });
+         return format;
+     }
+
+/***************************************************** 时间提取 *********************************************** */
+     __analysis(str, rule){  //从指定的字符串中根据规则提取日期和时间。
+         let resultObj;
+        if(typeof rule === 'function'){
+            resultObj = rule(str);
+            if(!resultObj){
+                return null;
+            }
+        }else{
+            resultObj = {};
+            var str11 = 
+            'y{4}|Y{4}|y{2}|Y{2}|mm|MM|0m|dd|DD|0d|hh|HH|0h|ff|FF|0F|0f|ss|SS|0S|0s|w|WT|W';
+            var reg = new RegExp(str11, 'g');
+
+            // rule = 'yyyy年mm月'
+            var namesArr = [null];   // [null, 'yyyy', 'mm']
+            rule = rule.replace(reg, (mstr)=>{
+                var result = this.__ANALYREGSTR[mstr];
+                if(result){
+                    namesArr.push(mstr);
+                    return `(${result})`;
+                }
+             });
+            //  rule = '(\\d{4})年([1-9]|1[012])月'
+
+            var resultArr = str.match( new RegExp(rule) );
+             if(resultArr){
+                for(let i=1,val; i<namesArr.length; i++){
+                    val = resultArr[i];
+                    if(val == null){
+                        continue;
+                    }
+                    switch(namesArr[i]){
+                        case 'yyyy': resultObj.y = +val; break;                        // [0000, 9999]
+                        case 'YYYY': resultObj.y = this.perChineseToNum(val); break;   // [零零零零-九九九九]   
+                        case 'yy': resultObj.y = +val + 2000; break;          // [00, 99]
+                        case 'YY': resultObj.y = this.perChineseToNum(val) + 2000; break;  // [零零-九九]    
+                        case 'mm': resultObj.m = +val;    break;                        // [1, 12]
+                        case 'MM': resultObj.m = this.chineseToNum(val);  break;       // [一-十二]    
+                        case '0m': resultObj.m = +val;  break; // [01, 12]
+                        case 'dd': resultObj.d = +val; break;                           // [1, 31]
+                        case 'DD': resultObj.d = this.chineseToNum(val); break;        // [一-三十一]  
+                        case '0d': resultObj.d = +val; break;  // [01, 31]
+                        case 'hh': resultObj.h = +val;  break;                           // [0, 24]
+                        case 'HH': resultObj.h = this.chineseToNum(val); break;        // [零-二十四]  
+                        case '0h': resultObj.h = +val; break;  // [00, 24]
+                        case 'ff': resultObj.f = +val;   break;                         // [0, 60]
+                        case 'FF': resultObj.f = this.chineseToNum(val); break;        // [零-六十]
+                        case '0F': resultObj.f = this.chineseToNum(val); break;  // [零零-六十]   
+                        case '0f': resultObj.f = +val; break;   // [00, 60]
+                        case 'ss': resultObj.s = +val;   break;                         // [0, 60]
+                        case 'SS': resultObj.s = this.chineseToNum(val);  break;       // [零-六十]
+                        case '0S': resultObj.s = this.chineseToNum(val); break;  // [零零-六十]    
+                        case '0s': resultObj.s = +val; break;    // [00, 60]
+                        case 'w': resultObj.w = 7; break;                             // [7]
+                        case 'W': resultObj.w = 7; break;      // [天|日]
+                        case 'WT': resultObj.w = 7; break;      // [天|日]
+                    }
+                }
+             }else{
+                return null;
+             }
+             
+        }
+        //   'yyyy年mm月'  '(\\d{4})年([1-9]|1[012])月'
+        return resultObj;
+     }
+
+     __ANALYREGSTR = {
+         'yyyy': '\\d{4}',                   // [0000, 9999]
+         'YYYY': '[零一二三四五六七八九]{4}',              // [零零零零-九九九九]    
+         'yy': '\\d{2}',                     // [00, 99]
+         'YY': '[零一二三四五六七八九]{2}',                // [零零-九九]    
+         'mm': '[1-9]|1[012]',               // [1, 12]
+         'MM': '十[一二]?|[一二三四五六七八九]',// [一-十二]    
+         '0m': '[0][1-9]|1[012]',             // [01, 12]
+         'dd': '3[01]|[12]0|[12]?[1-9]',            //  [1, 31]
+         'DD': '三十[一]?|二?十[一二三四五六七八九]|[零一二三四五六七八九十]',     // [一-三十一]  
+         '0d': '[012][0-9]|3[01]',            //  [01, 31]
+         'hh': '2[0-4]|[1]?[0-9]',            //  [0, 24]
+         'HH': '二十[一二三]?|十[一二三四五六七八九]|[零一二三四五六七八九十]',   // [零-二十四]  
+         '0h': '[01][0-9]|2[0-4]',             // [00, 24]
+         'ff': '60|[1-5]?[0-9]',               // [0, 60]
+         'FF': '[二三四五]十[一二三四五六七八九]|十[一二三四五六七八九]|[二三四五六]十|[零一二三四五六七八九十]',    // [零-六十]
+         '0F': '零[零一二三四五六七八九]|十[一二三四五六七八九]|[二三四五]十[一二三四五六七八九]|[二三四五六]十|十', // [零零-六十]     
+         '0f': '[0-5][0-9]|60',               // [00, 60]
+         'ss': '60|[1-5]?[0-9]',              // [0, 60]
+         'SS': '[二三四五]十[一二三四五六七八九]|十[一二三四五六七八九]|[二三四五六]十|[零一二三四五六七八九十]',    // [零-六十] 
+         '0S': '零[零一二三四五六七八九]|十[一二三四五六七八九]|[二三四五]十[一二三四五六七八九]|[二三四五六]十|十', // [零零-六十]    
+         '0s': '[0-5][0-9]|60',               // [00, 60]
+         'w': '7',                            // [7]
+         'W': '日',                        //[天|日]
+         'WT': '天',                        //[天|日]
+     }
+
+/***************************************************** 字符填充部分 *********************************************** */
+      /**
+     * 在一个字符前面补充指定个数的字符。
+     * <br><font color="red">ref: null</font> <br>
+     * @public
+     * @param {string|number} num -  要操作的数字或字符串。
+     * @param {number} len -  要保留的最大长度。如果指定的长度不比传入的参数的本身的长度大,则不做任何操作。
+     * @param {string} [char=' '|'0'] -  当长度不足时,要在最前面补足的字符。第一个参数为数字时,默认为0,否则默认为' '。
+     * @example
+       1. fillChar(44, 5, '*'); //结果: '***44' 
+       2. fillChar(44, 5); //结果: '00044' 
+       3. fillChar('44', 5); //结果: '   44' 
+       4. fillChar('aaa', 5); //结果: '   aa' 
+       5. fillChar('aaa', 5, '$'); //结果: '$$aaa' 
+       5. fillChar('aaa', 1, '$'); //结果: 'aaa'  //指定的长度太小,不做任何操作。
+     */
+    fillChar(num, len, char){
+        if(typeof num === 'number'){
+            char = char || '0';
+        }else{
+            char = char || ' ';
+        }
+        let str = '' + num, charStr = '';
+
+        if(str.length < len){
+            for(let i=0, len1=len-str.length; i<len1; i++){
+                charStr += char;
+            }
+            return charStr + str;
+        }
+
+        return str;
+    }
+
+/***************************************************** 数字转换部分 *********************************************** */
+
+    __SIZEPOW = {'十': 1, '百': 2, '千': 3, '万': 4, '亿': 8};
+    /** 
+     * 将中文的格式的数字转换为阿拉伯数字, 最多只能处理13位数(万亿)。 是 numToChinese(num) 函数的逆操作。 
+     *  @example
+        1. chineseToNum('十六'); //结果: 16  
+        2. chineseToNum('一万九千九百零四'); //结果: 19904  
+        3. chineseToNum('十万零四百零四'); //结果: 100404   
+        4. chineseToNum('九十亿零二千零三万零四百零一'); //结果: 9020030401  
+     */
+    chineseToNum (numStr){   //目前只能处理万亿以下的数据
+        if(numStr.length === 1){
+            return this.perChineseToNum(numStr);
+        }
+        if(numStr.length === 2 && numStr[0] === '十'){
+            return this.perChineseToNum(numStr[1]) + 10;
+        }
+
+        let arr;
+        if(!numStr.match(/[亿万]/)){
+            arr = numStr.split('零'); 
+            let num = 0, val = 0;
+            for(let i=0, str=''; i<arr.length; i++){
+                str = arr[i];
+                for(let j=0; j<str.length; j++){
+                    if(str[j].match(/[一二三四五六七八九]/)){
+                        num += val;
+                        val = 1;
+                        val *= this.perChineseToNum(str[j]);
+                    }else{
+                        val *= Math.pow(10, this.__SIZEPOW[str[j]] )
+                    }
+                }
+            }
+            num += val;
+            return num;
+        }else{
+            arr = numStr.split('亿');
+            if(arr.length > 1){
+                var arr1 = arr[1].split('万');
+                return this.chineseToNum(arr[0])*Math.pow(10, 8) + this.chineseToNum(arr1[0])*10000 + this.chineseToNum(arr1[1]);
+            }else{
+                return this.chineseToNum(arr[0])*Math.pow(10, 8) + this.chineseToNum(arr[1]);
+            }
+        }
+    }
+
+     /** 
+     * 将中文的格式的数字每一位对应转换为阿拉伯数字。 是perNumToChinese(num)的逆操作。  
+     *  @example
+    1. perChineseToNum('三四'); //结果: 34  
+    2. perChineseToNum('三八七四'); //结果: 3874  
+    3. perChineseToNum('四零四'); //结果: 404  
+    4. perChineseToNum('零零四'); //结果: 4  
+    5. perChineseToNum('  '); //结果: NaN  
+     */
+    perChineseToNum(numStr){
+        return +numStr.replace(/[零一二三四五六七八九十\s]/g, (str)=>{
+            var index =  this.__CHINANUM.indexOf(str);
+            return index == null ? '' : index;
+        });
+    }
+
+    /**
+    * 将阿拉伯数字转换为对应的汉字, 最多只能处理13位数(万亿)。
+    * <br><font color="red">ref: this.numToChina()、 this.weiNum() </font> <br>
+    * @public
+    * @param {number} num - 要转换的数字
+    * @example
+        1. numToChinese(0); //结果: '零'  
+        2. numToChinese(5); //结果: '五'  
+        3. numToChinese(16); //结果: '十六'  
+        4. numToChinese(34); //结果: '三十四'  
+        5. numToChinese(106); //结果: '一百零六'  
+        6. numToChinese(886); //结果: '八百八十六'  
+        7. numToChinese(1004); //结果: '一千零四'  
+        8. numToChinese(1000); //结果: '一千'  
+        9. numToChinese(9904); //结果: '九千九百零四'  
+        10. numToChinese(19904); //结果: '一万九千九百零四'  
+        11. numToChinese(10004); //结果: '一万零四'  
+        12. numToChinese(10000); //结果: '一万'  
+        13. numToChinese(100404); //结果: '十万零四百零四'  
+        14. numToChinese(9000000); //结果: '九百万'  
+        15. numToChinese(90000000); //结果: '九千万'  
+        16. numToChinese(900000000); //结果: '九亿'  
+        17. numToChinese(9000000000); //结果: '九十亿'  
+        18. numToChinese(9020030401); //结果: '九十亿零二千零三万零四百零一'  
+        19. numToChinese(90000000000); //结果: '九百亿'  
+        20. numToChinese(900000000000); //结果: '九千亿'  
+        21. numToChinese(9000000000000); //结果: '九万亿'  
+        22. numToChinese(90000000000000); //结果: undefined  
+    */
+    numToChinese (num){
+        let numStr = '' + num;
+        let len = numStr.length, result;
+        if(num <= 10){ //处理0~9
+            return this.__CHINANUM[num]; 
+        }else if(num < 20){ //处理10~20
+            return '十' + this.__CHINANUM[numStr[1]]; 
+        }else if(len < 6){ //处理5位数(万)及以下
+            if(!this.__WEIARR){
+                this.__WEIARR = this.__WEISTR.split(', ');
+            }
+            let lastStr, resultStr = '', index;
+            for(let i=0; i<len; i++){
+                lastStr = this.__CHINANUM[numStr[i]];
+                if(numStr[i] === '0'){
+                    index = null;
+                    for(let j=i+1; j<len; j++){
+                        if( numStr[j] !== '0' ){
+                            index = j - 1;
+                            break;
+                        }
+                    }
+                    if(index === null){
+                        break;
+                    }else{
+                        i = index;
+                        resultStr += '零';
+                    }
+                }else{
+                    resultStr += ( lastStr + this.__WEIARR[len - i - 1] );
+                }
+            }
+            return resultStr;
+        }else if(len < 9){ //处理8位数(千万)及以下
+            let low4Str = numStr.slice(-4), heigh4Str = numStr.slice(0, -4), isZero = '';
+            isZero = heigh4Str.slice(-1) === '0' ? '零' : ''; //必须用这个,如504000,应该是 五十万零四千 而不是 五十万四千
+            if(isZero){
+                low4Str = +low4Str;
+                if(low4Str === 0){
+                    isZero = '';
+                }
+            }
+            if(+heigh4Str !== 0){
+                isZero = '万' + isZero;
+            }
+            result = this.numToChinese( +heigh4Str ) + isZero  + this.numToChinese( +low4Str );
+            return result.slice(-1) === '零' ? result.slice(0, -1) : result;
+
+        }else if(len < 14){ //处理13位数(万亿)及以下
+            let low8Str = numStr.slice(-8), heigh4Str = numStr.slice(0, -8),  isZero = '';
+            isZero = heigh4Str.slice(-1) === '0' ? '零' : ''; //必须用这个
+            if(isZero){
+                low8Str = +low8Str;
+                if(low8Str === 0){
+                    isZero = '';
+                }
+            }
+            if(+heigh4Str !== 0){
+                isZero = '亿' + isZero;
+            }
+            result = this.numToChinese( +heigh4Str ) + isZero  + this.numToChinese( +low8Str );
+            return result.slice(-1) === '零' ? result.slice(0, -1) : result;
+        }
+    }
+
+    /**
+     * 按数字的位数将每一位数字转换为中文(注意,可以转换多位数字)
+     * <br><font color="red">ref: this.numToChina() </font> <br>
+     * @public
+     * @param {number|string} num - 要转换的数字
+     * @example
+    1. perNumToChinese(34); //结果: '三四'
+    2. perNumToChinese(3874); //结果: '三八七四'
+    3. perNumToChinese('3874'); //结果: '三八七四'
+    4. perNumToChinese('404'); //结果: '四零四'
+    5. perNumToChinese('0  04   '); //结果: '零零四'
+    6. perNumToChinese('  '); //结果: ''
+    */
+    perNumToChinese(num){
+        return ('' + num).replace(/[\d\s]/g, (index)=>{
+            var result = this.__CHINANUM[index];
+            return result ? result : '';
+        });
+    }
+    __CHINANUM =  '零一二三四五六七八九十'; 
+    __WEISTR =', 十, 百, 千, 万, 十万, 百万, 千万, 亿, 十亿, 百亿, 千亿, 万亿, 十万亿, 百万亿, 千万亿, 亿亿, 十亿亿, 百亿亿, 千亿亿, 万亿亿';
+    __WEIARR = null;
+ 
+ }
+
+

+ 38 - 0
uni_applet/main.js

@@ -0,0 +1,38 @@
+
+// #ifndef VUE3
+import Vue from 'vue'
+import App from './App'
+
+Vue.config.productionTip = false
+import request from '@/util/request.js'
+import helper from '@/common/helper.js'
+import WXBizDataCrypt from '@/util/WXBizDataCrypt.js'
+import uView from '@/uni_modules/uview-ui'
+import store from './store'
+import share from '@/js_sdk/share.js'
+Vue.mixin(share)
+Vue.use(uView)
+import {parseTime} from '@/util/util'
+Vue.prototype.$request = request
+Vue.prototype.$helper = helper
+Vue.prototype.$store = store
+Vue.prototype.parseTime = parseTime
+Vue.prototype.$WXBizDataCrypt = WXBizDataCrypt
+App.mpType = 'app'
+
+const app = new Vue({
+    ...App
+})
+app.$mount()
+// #endif
+
+// #ifdef VUE3
+import { createSSRApp } from 'vue'
+import App from './App.vue'
+export function createApp() {
+  const app = createSSRApp(App)
+  return {
+    app
+  }
+}
+// #endif

+ 25 - 0
uni_applet/pages/demo/demo.vue

@@ -0,0 +1,25 @@
+<template>
+	<view>
+		<view class="block">
+			<button class="shareBtn" type="default" data-name="shareBtn" open-type="share">
+				<image style='width:108rpx;height:108rpx;' src="@/static/imgs/shade.png" mode="aspectFill">
+				</image>
+				<view>分享微信好友</view>
+			</button>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				
+			};
+		}
+	}
+</script>
+
+<style lang="scss">
+
+</style>

+ 112 - 0
uni_applet/pages/index/index.vue

@@ -0,0 +1,112 @@
+<template>
+	<view>
+		<mescroll-uni :up="upOption" :down="downOption" ref="mescrollRef" @up="upCallback" @down="downCallback"
+			height="1060">
+			<view v-for='(item,index) in list' class="item flex">
+
+			</view>
+		</mescroll-uni>
+	</view>
+</template>
+
+<script>
+	var that;
+	import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
+	export default {
+		mixins: [MescrollMixin],
+		data() {
+			return {
+				downOption: {
+					auto: false,
+					textColor: '#bbb'
+				},
+				upOption: {
+					page: {
+						size: 10 // 每页数据的数量,默认10
+					},
+					auto: false,
+					noMoreSize: 1,
+					textNoMore: '没有更多了~',
+					textColor: '#bbb'
+				},
+				userInfo: {
+					head: '',
+					nickname: '',
+					phone: '',
+				},
+				list: [{
+						name: '1'
+					},
+					{
+						name: '2'
+					},
+					{
+						name: '3'
+					}, {
+						name: '4'
+					}, {
+						name: '5'
+					}, {
+						name: '6'
+					}, {
+						name: '7'
+					}, {
+						name: '8'
+					}, {
+						name: '9'
+					}, {
+						name: '10'
+					}, {
+						name: '11'
+					}
+				]
+			};
+		},
+		onLoad() {
+			that = this
+		},
+		onShow() {
+			 this.$nextTick(() => {
+			        this.mescroll.resetUpScroll()
+			    })
+		},
+
+		methods: {
+
+			downCallback() {
+				this.mescroll.resetUpScroll()
+
+			},
+			upCallback(page) {
+				that.mescroll.endBySize(0, 0)
+				// this.$request.baseRequest('admin.unimall.cardHolderInfo', 'list', {
+				// 	page: page.num,
+				// 	limit: page.size,
+				// }, failres => {
+				// 	uni.hideLoading()
+				// 	console.log('res+++++', failres.errmsg)
+				// 	uni.showToast({
+				// 		icon: "none",
+				// 		title: failres.errmsg,
+				// 		duration: 3000
+				// 	});
+				// }).then(res => {
+				// 	console.log(res)
+				// 	if (page.num == 1) this.list = [];
+				// 	let curPageLen = res.data.items.length;
+				// 	let totalPage = res.data.total;
+				// 	this.list = res.data.items
+				// 	this.$nextTick(() => {
+				// 		that.mescroll.endBySize(curPageLen, totalPage)
+				// 	});
+
+				// 	uni.hideLoading()
+				// })
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+
+</style>

+ 19 - 0
uni_applet/pages/my/my.vue

@@ -0,0 +1,19 @@
+<template>
+	<view>
+		
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				
+			};
+		}
+	}
+</script>
+
+<style lang="scss">
+
+</style>

+ 20 - 0
uni_applet/static/customicons.css

@@ -0,0 +1,20 @@
+@font-face {
+  font-family: "customicons"; /* Project id 2878519 */
+  src:url('/static/customicons.ttf') format('truetype');
+}
+
+.customicons {
+  font-family: "customicons" !important;
+}
+
+.youxi:before {
+  content: "\e60e";
+}
+
+.wenjian:before {
+  content: "\e60f";
+}
+
+.zhuanfa:before {
+  content: "\e610";
+}

二進制
uni_applet/static/customicons.ttf


+ 539 - 0
uni_applet/static/icons/demo.css

@@ -0,0 +1,539 @@
+/* Logo 字体 */
+@font-face {
+  font-family: "iconfont logo";
+  src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
+  src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
+    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
+    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
+    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
+}
+
+.logo {
+  font-family: "iconfont logo";
+  font-size: 160px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+/* tabs */
+.nav-tabs {
+  position: relative;
+}
+
+.nav-tabs .nav-more {
+  position: absolute;
+  right: 0;
+  bottom: 0;
+  height: 42px;
+  line-height: 42px;
+  color: #666;
+}
+
+#tabs {
+  border-bottom: 1px solid #eee;
+}
+
+#tabs li {
+  cursor: pointer;
+  width: 100px;
+  height: 40px;
+  line-height: 40px;
+  text-align: center;
+  font-size: 16px;
+  border-bottom: 2px solid transparent;
+  position: relative;
+  z-index: 1;
+  margin-bottom: -1px;
+  color: #666;
+}
+
+
+#tabs .active {
+  border-bottom-color: #f00;
+  color: #222;
+}
+
+.tab-container .content {
+  display: none;
+}
+
+/* 页面布局 */
+.main {
+  padding: 30px 100px;
+  width: 960px;
+  margin: 0 auto;
+}
+
+.main .logo {
+  color: #333;
+  text-align: left;
+  margin-bottom: 30px;
+  line-height: 1;
+  height: 110px;
+  margin-top: -50px;
+  overflow: hidden;
+  *zoom: 1;
+}
+
+.main .logo a {
+  font-size: 160px;
+  color: #333;
+}
+
+.helps {
+  margin-top: 40px;
+}
+
+.helps pre {
+  padding: 20px;
+  margin: 10px 0;
+  border: solid 1px #e7e1cd;
+  background-color: #fffdef;
+  overflow: auto;
+}
+
+.icon_lists {
+  width: 100% !important;
+  overflow: hidden;
+  *zoom: 1;
+}
+
+.icon_lists li {
+  width: 100px;
+  margin-bottom: 10px;
+  margin-right: 20px;
+  text-align: center;
+  list-style: none !important;
+  cursor: default;
+}
+
+.icon_lists li .code-name {
+  line-height: 1.2;
+}
+
+.icon_lists .icon {
+  display: block;
+  height: 100px;
+  line-height: 100px;
+  font-size: 42px;
+  margin: 10px auto;
+  color: #333;
+  -webkit-transition: font-size 0.25s linear, width 0.25s linear;
+  -moz-transition: font-size 0.25s linear, width 0.25s linear;
+  transition: font-size 0.25s linear, width 0.25s linear;
+}
+
+.icon_lists .icon:hover {
+  font-size: 100px;
+}
+
+.icon_lists .svg-icon {
+  /* 通过设置 font-size 来改变图标大小 */
+  width: 1em;
+  /* 图标和文字相邻时,垂直对齐 */
+  vertical-align: -0.15em;
+  /* 通过设置 color 来改变 SVG 的颜色/fill */
+  fill: currentColor;
+  /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
+      normalize.css 中也包含这行 */
+  overflow: hidden;
+}
+
+.icon_lists li .name,
+.icon_lists li .code-name {
+  color: #666;
+}
+
+/* markdown 样式 */
+.markdown {
+  color: #666;
+  font-size: 14px;
+  line-height: 1.8;
+}
+
+.highlight {
+  line-height: 1.5;
+}
+
+.markdown img {
+  vertical-align: middle;
+  max-width: 100%;
+}
+
+.markdown h1 {
+  color: #404040;
+  font-weight: 500;
+  line-height: 40px;
+  margin-bottom: 24px;
+}
+
+.markdown h2,
+.markdown h3,
+.markdown h4,
+.markdown h5,
+.markdown h6 {
+  color: #404040;
+  margin: 1.6em 0 0.6em 0;
+  font-weight: 500;
+  clear: both;
+}
+
+.markdown h1 {
+  font-size: 28px;
+}
+
+.markdown h2 {
+  font-size: 22px;
+}
+
+.markdown h3 {
+  font-size: 16px;
+}
+
+.markdown h4 {
+  font-size: 14px;
+}
+
+.markdown h5 {
+  font-size: 12px;
+}
+
+.markdown h6 {
+  font-size: 12px;
+}
+
+.markdown hr {
+  height: 1px;
+  border: 0;
+  background: #e9e9e9;
+  margin: 16px 0;
+  clear: both;
+}
+
+.markdown p {
+  margin: 1em 0;
+}
+
+.markdown>p,
+.markdown>blockquote,
+.markdown>.highlight,
+.markdown>ol,
+.markdown>ul {
+  width: 80%;
+}
+
+.markdown ul>li {
+  list-style: circle;
+}
+
+.markdown>ul li,
+.markdown blockquote ul>li {
+  margin-left: 20px;
+  padding-left: 4px;
+}
+
+.markdown>ul li p,
+.markdown>ol li p {
+  margin: 0.6em 0;
+}
+
+.markdown ol>li {
+  list-style: decimal;
+}
+
+.markdown>ol li,
+.markdown blockquote ol>li {
+  margin-left: 20px;
+  padding-left: 4px;
+}
+
+.markdown code {
+  margin: 0 3px;
+  padding: 0 5px;
+  background: #eee;
+  border-radius: 3px;
+}
+
+.markdown strong,
+.markdown b {
+  font-weight: 600;
+}
+
+.markdown>table {
+  border-collapse: collapse;
+  border-spacing: 0px;
+  empty-cells: show;
+  border: 1px solid #e9e9e9;
+  width: 95%;
+  margin-bottom: 24px;
+}
+
+.markdown>table th {
+  white-space: nowrap;
+  color: #333;
+  font-weight: 600;
+}
+
+.markdown>table th,
+.markdown>table td {
+  border: 1px solid #e9e9e9;
+  padding: 8px 16px;
+  text-align: left;
+}
+
+.markdown>table th {
+  background: #F7F7F7;
+}
+
+.markdown blockquote {
+  font-size: 90%;
+  color: #999;
+  border-left: 4px solid #e9e9e9;
+  padding-left: 0.8em;
+  margin: 1em 0;
+}
+
+.markdown blockquote p {
+  margin: 0;
+}
+
+.markdown .anchor {
+  opacity: 0;
+  transition: opacity 0.3s ease;
+  margin-left: 8px;
+}
+
+.markdown .waiting {
+  color: #ccc;
+}
+
+.markdown h1:hover .anchor,
+.markdown h2:hover .anchor,
+.markdown h3:hover .anchor,
+.markdown h4:hover .anchor,
+.markdown h5:hover .anchor,
+.markdown h6:hover .anchor {
+  opacity: 1;
+  display: inline-block;
+}
+
+.markdown>br,
+.markdown>p>br {
+  clear: both;
+}
+
+
+.hljs {
+  display: block;
+  background: white;
+  padding: 0.5em;
+  color: #333333;
+  overflow-x: auto;
+}
+
+.hljs-comment,
+.hljs-meta {
+  color: #969896;
+}
+
+.hljs-string,
+.hljs-variable,
+.hljs-template-variable,
+.hljs-strong,
+.hljs-emphasis,
+.hljs-quote {
+  color: #df5000;
+}
+
+.hljs-keyword,
+.hljs-selector-tag,
+.hljs-type {
+  color: #a71d5d;
+}
+
+.hljs-literal,
+.hljs-symbol,
+.hljs-bullet,
+.hljs-attribute {
+  color: #0086b3;
+}
+
+.hljs-section,
+.hljs-name {
+  color: #63a35c;
+}
+
+.hljs-tag {
+  color: #333333;
+}
+
+.hljs-title,
+.hljs-attr,
+.hljs-selector-id,
+.hljs-selector-class,
+.hljs-selector-attr,
+.hljs-selector-pseudo {
+  color: #795da3;
+}
+
+.hljs-addition {
+  color: #55a532;
+  background-color: #eaffea;
+}
+
+.hljs-deletion {
+  color: #bd2c00;
+  background-color: #ffecec;
+}
+
+.hljs-link {
+  text-decoration: underline;
+}
+
+/* 代码高亮 */
+/* PrismJS 1.15.0
+https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
+/**
+ * prism.js default theme for JavaScript, CSS and HTML
+ * Based on dabblet (http://dabblet.com)
+ * @author Lea Verou
+ */
+code[class*="language-"],
+pre[class*="language-"] {
+  color: black;
+  background: none;
+  text-shadow: 0 1px white;
+  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
+  text-align: left;
+  white-space: pre;
+  word-spacing: normal;
+  word-break: normal;
+  word-wrap: normal;
+  line-height: 1.5;
+
+  -moz-tab-size: 4;
+  -o-tab-size: 4;
+  tab-size: 4;
+
+  -webkit-hyphens: none;
+  -moz-hyphens: none;
+  -ms-hyphens: none;
+  hyphens: none;
+}
+
+pre[class*="language-"]::-moz-selection,
+pre[class*="language-"] ::-moz-selection,
+code[class*="language-"]::-moz-selection,
+code[class*="language-"] ::-moz-selection {
+  text-shadow: none;
+  background: #b3d4fc;
+}
+
+pre[class*="language-"]::selection,
+pre[class*="language-"] ::selection,
+code[class*="language-"]::selection,
+code[class*="language-"] ::selection {
+  text-shadow: none;
+  background: #b3d4fc;
+}
+
+@media print {
+
+  code[class*="language-"],
+  pre[class*="language-"] {
+    text-shadow: none;
+  }
+}
+
+/* Code blocks */
+pre[class*="language-"] {
+  padding: 1em;
+  margin: .5em 0;
+  overflow: auto;
+}
+
+:not(pre)>code[class*="language-"],
+pre[class*="language-"] {
+  background: #f5f2f0;
+}
+
+/* Inline code */
+:not(pre)>code[class*="language-"] {
+  padding: .1em;
+  border-radius: .3em;
+  white-space: normal;
+}
+
+.token.comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+  color: slategray;
+}
+
+.token.punctuation {
+  color: #999;
+}
+
+.namespace {
+  opacity: .7;
+}
+
+.token.property,
+.token.tag,
+.token.boolean,
+.token.number,
+.token.constant,
+.token.symbol,
+.token.deleted {
+  color: #905;
+}
+
+.token.selector,
+.token.attr-name,
+.token.string,
+.token.char,
+.token.builtin,
+.token.inserted {
+  color: #690;
+}
+
+.token.operator,
+.token.entity,
+.token.url,
+.language-css .token.string,
+.style .token.string {
+  color: #9a6e3a;
+  background: hsla(0, 0%, 100%, .5);
+}
+
+.token.atrule,
+.token.attr-value,
+.token.keyword {
+  color: #07a;
+}
+
+.token.function,
+.token.class-name {
+  color: #DD4A68;
+}
+
+.token.regex,
+.token.important,
+.token.variable {
+  color: #e90;
+}
+
+.token.important,
+.token.bold {
+  font-weight: bold;
+}
+
+.token.italic {
+  font-style: italic;
+}
+
+.token.entity {
+  cursor: help;
+}

+ 257 - 0
uni_applet/static/icons/demo_index.html

@@ -0,0 +1,257 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8"/>
+  <title>iconfont Demo</title>
+  <link rel="shortcut icon" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg" type="image/x-icon"/>
+  <link rel="icon" type="image/svg+xml" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg"/>
+  <link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
+  <link rel="stylesheet" href="demo.css">
+  <link rel="stylesheet" href="iconfont.css">
+  <script src="iconfont.js"></script>
+  <!-- jQuery -->
+  <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
+  <!-- 代码高亮 -->
+  <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
+  <style>
+    .main .logo {
+      margin-top: 0;
+      height: auto;
+    }
+
+    .main .logo a {
+      display: flex;
+      align-items: center;
+    }
+
+    .main .logo .sub-title {
+      margin-left: 0.5em;
+      font-size: 22px;
+      color: #fff;
+      background: linear-gradient(-45deg, #3967FF, #B500FE);
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+    }
+  </style>
+</head>
+<body>
+  <div class="main">
+    <h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank">
+      <img width="200" src="https://img.alicdn.com/imgextra/i3/O1CN01Mn65HV1FfSEzR6DKv_!!6000000000514-55-tps-228-59.svg">
+      
+    </a></h1>
+    <div class="nav-tabs">
+      <ul id="tabs" class="dib-box">
+        <li class="dib active"><span>Unicode</span></li>
+        <li class="dib"><span>Font class</span></li>
+        <li class="dib"><span>Symbol</span></li>
+      </ul>
+      
+      <a href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=4001868" target="_blank" class="nav-more">查看项目</a>
+      
+    </div>
+    <div class="tab-container">
+      <div class="content unicode" style="display: block;">
+          <ul class="icon_lists dib-box">
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe669;</span>
+                <div class="name">朋友圈</div>
+                <div class="code-name">&amp;#xe669;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe66c;</span>
+                <div class="name">微信</div>
+                <div class="code-name">&amp;#xe66c;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe759;</span>
+                <div class="name">通知</div>
+                <div class="code-name">&amp;#xe759;</div>
+              </li>
+          
+          </ul>
+          <div class="article markdown">
+          <h2 id="unicode-">Unicode 引用</h2>
+          <hr>
+
+          <p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
+          <ul>
+            <li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
+            <li>默认情况下不支持多色,直接添加多色图标会自动去色。</li>
+          </ul>
+          <blockquote>
+            <p>注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)</p>
+          </blockquote>
+          <p>Unicode 使用步骤如下:</p>
+          <h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
+<pre><code class="language-css"
+>@font-face {
+  font-family: 'iconfont';
+  src: url('iconfont.woff2?t=1681436897668') format('woff2'),
+       url('iconfont.woff?t=1681436897668') format('woff'),
+       url('iconfont.ttf?t=1681436897668') format('truetype');
+}
+</code></pre>
+          <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
+<pre><code class="language-css"
+>.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+</code></pre>
+          <h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
+<pre>
+<code class="language-html"
+>&lt;span class="iconfont"&gt;&amp;#x33;&lt;/span&gt;
+</code></pre>
+          <blockquote>
+            <p>"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
+          </blockquote>
+          </div>
+      </div>
+      <div class="content font-class">
+        <ul class="icon_lists dib-box">
+          
+          <li class="dib">
+            <span class="icon iconfont icon-pengyouquan"></span>
+            <div class="name">
+              朋友圈
+            </div>
+            <div class="code-name">.icon-pengyouquan
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-weixin"></span>
+            <div class="name">
+              微信
+            </div>
+            <div class="code-name">.icon-weixin
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-tongzhi"></span>
+            <div class="name">
+              通知
+            </div>
+            <div class="code-name">.icon-tongzhi
+            </div>
+          </li>
+          
+        </ul>
+        <div class="article markdown">
+        <h2 id="font-class-">font-class 引用</h2>
+        <hr>
+
+        <p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p>
+        <p>与 Unicode 使用方式相比,具有如下特点:</p>
+        <ul>
+          <li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
+          <li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li>
+        </ul>
+        <p>使用步骤如下:</p>
+        <h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
+<pre><code class="language-html">&lt;link rel="stylesheet" href="./iconfont.css"&gt;
+</code></pre>
+        <h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
+<pre><code class="language-html">&lt;span class="iconfont icon-xxx"&gt;&lt;/span&gt;
+</code></pre>
+        <blockquote>
+          <p>"
+            iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
+        </blockquote>
+      </div>
+      </div>
+      <div class="content symbol">
+          <ul class="icon_lists dib-box">
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-pengyouquan"></use>
+                </svg>
+                <div class="name">朋友圈</div>
+                <div class="code-name">#icon-pengyouquan</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-weixin"></use>
+                </svg>
+                <div class="name">微信</div>
+                <div class="code-name">#icon-weixin</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-tongzhi"></use>
+                </svg>
+                <div class="name">通知</div>
+                <div class="code-name">#icon-tongzhi</div>
+            </li>
+          
+          </ul>
+          <div class="article markdown">
+          <h2 id="symbol-">Symbol 引用</h2>
+          <hr>
+
+          <p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
+            这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:</p>
+          <ul>
+            <li>支持多色图标了,不再受单色限制。</li>
+            <li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> 来调整样式。</li>
+            <li>兼容性较差,支持 IE9+,及现代浏览器。</li>
+            <li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
+          </ul>
+          <p>使用步骤如下:</p>
+          <h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
+<pre><code class="language-html">&lt;script src="./iconfont.js"&gt;&lt;/script&gt;
+</code></pre>
+          <h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
+<pre><code class="language-html">&lt;style&gt;
+.icon {
+  width: 1em;
+  height: 1em;
+  vertical-align: -0.15em;
+  fill: currentColor;
+  overflow: hidden;
+}
+&lt;/style&gt;
+</code></pre>
+          <h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
+<pre><code class="language-html">&lt;svg class="icon" aria-hidden="true"&gt;
+  &lt;use xlink:href="#icon-xxx"&gt;&lt;/use&gt;
+&lt;/svg&gt;
+</code></pre>
+          </div>
+      </div>
+
+    </div>
+  </div>
+  <script>
+  $(document).ready(function () {
+      $('.tab-container .content:first').show()
+
+      $('#tabs li').click(function (e) {
+        var tabContent = $('.tab-container .content')
+        var index = $(this).index()
+
+        if ($(this).hasClass('active')) {
+          return
+        } else {
+          $('#tabs li').removeClass('active')
+          $(this).addClass('active')
+
+          tabContent.hide().eq(index).fadeIn()
+        }
+      })
+    })
+  </script>
+</body>
+</html>

+ 27 - 0
uni_applet/static/icons/iconfont.css

@@ -0,0 +1,27 @@
+@font-face {
+  font-family: "iconfont"; /* Project id 4001868 */
+  src: url('iconfont.woff2?t=1681436897668') format('woff2'),
+       url('iconfont.woff?t=1681436897668') format('woff'),
+       url('iconfont.ttf?t=1681436897668') format('truetype');
+}
+
+.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-pengyouquan:before {
+  content: "\e669";
+}
+
+.icon-weixin:before {
+  content: "\e66c";
+}
+
+.icon-tongzhi:before {
+  content: "\e759";
+}
+

文件差異過大導致無法顯示
+ 0 - 0
uni_applet/static/icons/iconfont.js


二進制
uni_applet/static/icons/iconfont.ttf


二進制
uni_applet/static/icons/iconfont.woff


二進制
uni_applet/static/icons/iconfont.woff2


二進制
uni_applet/static/imgs/bz.png


二進制
uni_applet/static/imgs/cirlcle.png


二進制
uni_applet/static/imgs/home.png


二進制
uni_applet/static/imgs/location.png


二進制
uni_applet/static/imgs/logo.png


二進制
uni_applet/static/imgs/map.png


二進制
uni_applet/static/imgs/messege.png


二進制
uni_applet/static/imgs/mic.png


二進制
uni_applet/static/imgs/mic1.png


二進制
uni_applet/static/imgs/mic2.png


二進制
uni_applet/static/imgs/mr.png


二進制
uni_applet/static/imgs/my.png


二進制
uni_applet/static/imgs/notavailable.png


二進制
uni_applet/static/imgs/phone.png


二進制
uni_applet/static/imgs/scan.png


二進制
uni_applet/static/imgs/scancode.png


二進制
uni_applet/static/imgs/scirlce.png


二進制
uni_applet/static/imgs/search.png


二進制
uni_applet/static/imgs/shade.png


二進制
uni_applet/static/imgs/share.png


二進制
uni_applet/static/imgs/smy.png


二進制
uni_applet/static/imgs/stop.png


二進制
uni_applet/static/imgs/swjj.png


二進制
uni_applet/static/imgs/uni.png


二進制
uni_applet/static/imgs/voice.png


二進制
uni_applet/static/imgs/wjj.png


二進制
uni_applet/static/imgs/xjt.png


+ 28 - 0
uni_applet/static/styles/index.scss

@@ -0,0 +1,28 @@
+.flex{
+	display: flex;
+	align-items: center;
+}
+.flex-row-center{
+	display: flex;
+	justify-content: center;
+}
+.flex-all-center{
+	display: flex;
+	justify-content: center;
+	align-items: center;
+}
+.flex-between{
+	display: flex;
+	justify-content: space-between;
+}
+.flex-evenly{
+	display: flex;
+	justify-content: space-evenly;
+}
+
+.relative{
+	position: relative;
+}
+.absolute{
+	position: absolute;
+}

+ 109 - 0
uni_applet/store/index.js

@@ -0,0 +1,109 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+Vue.use(Vuex)
+let lifeData = {};
+
+try {
+	// 尝试获取本地是否存在lifeData变量,第一次启动APP时是不存在的
+	lifeData = uni.getStorageSync('lifeData');
+} catch (e) {}
+
+// 需要永久存储,且下次APP启动需要取出的,在state中的变量名
+let saveStateKeys = ['userInfo', 'goods',
+	'isHaveService'
+];
+
+// 保存变量到本地存储中
+const saveLifeData = function(key, value) {
+	// 判断变量名是否在需要存储的数组中
+	if (saveStateKeys.indexOf(key) != -1) {
+		// 获取本地存储的lifeData对象,将变量添加到对象中
+		let tmp = uni.getStorageSync('lifeData');
+		// 第一次打开APP,不存在lifeData变量,故放一个{}空对象
+		tmp = tmp ? tmp : {};
+		// if(key == 'contractTip' && tmp[key] < value){
+		// 	uni.vibrateLong({
+		// 	    success: function () {
+		// 	        console.log('震动成功');
+		// 	    }
+		// 	});
+		// }
+		// if(key == 'taskTip' && tmp[key] < value){
+		// 	uni.vibrateLong({
+		// 	    success: function () {
+		// 	        console.log('震动成功');
+		// 	    }
+		// 	});
+		// }
+		tmp[key] = value;
+		// 执行这一步后,所有需要存储的变量,都挂载在本地的lifeData对象中
+		uni.setStorageSync('lifeData', tmp);
+	}
+}
+
+
+const store = new Vuex.Store({
+	modules: {},
+	state: {
+		sysinfo: uni.getSystemInfoSync(), //系统信息
+		hasLogin: false,
+		isHaveService: false,
+		goods: []
+	},
+	mutations: {
+		$uStore(state, payload) {
+			// 判断是否多层级调用,state中为对象存在的情况,诸如user.info.score = 1
+			let nameArr = payload.name.split('.');
+			let saveKey = '';
+			let len = nameArr.length;
+			if (nameArr.length >= 2) {
+				let obj = state[nameArr[0]];
+				for (let i = 1; i < len - 1; i++) {
+					obj = obj[nameArr[i]];
+				}
+				obj[nameArr[len - 1]] = payload.value;
+				saveKey = nameArr[0];
+			} else {
+				// 单层级变量,在state就是一个普通变量的情况
+				state[payload.name] = payload.value;
+				saveKey = payload.name;
+			}
+			// 保存变量到本地,见顶部函数定义
+			saveLifeData(saveKey, state[saveKey])
+		},
+		login(state, provider) {
+			state.hasLogin = true;
+			state.userInfo = provider;
+			uni.setStorageSync('userInfo', provider)
+		},
+
+		logout(state) {
+			state.hasLogin = false;
+			state.userInfo = {};
+			uni.removeStorageSync('userInfo')
+		},
+
+		goodCar(state, provider) {
+			
+			state.goods = provider;
+			uni.setStorageSync('goods', provider)
+		},
+		goodService(state, provider) {
+			state.isHaveService = provider;
+			uni.setStorageSync('isHaveService', provider)
+		},
+
+
+
+	},
+	actions: {
+
+	},
+	getters: {
+		getUserInfo(state) {
+			return state.userInfo
+		}
+	}
+})
+
+export default store

文件差異過大導致無法顯示
+ 3 - 0
uni_applet/style/icons/iconfont.css


+ 2 - 0
uni_applet/uni.scss

@@ -0,0 +1,2 @@
+@import '@/uni_modules/uni-scss/variables.scss';
+@import '@/uni_modules/uview-ui/theme.scss';

+ 10 - 0
uni_applet/uni_modules/d-search-log/changelog.md

@@ -0,0 +1,10 @@
+## 1.0.2(2022-06-15)
+1.增加:组件接收上个页面传递的参数 进行搜索。
+## 1.0.1(2022-06-14)
+1.优化逻辑 
+2.增加完整说明文档 +案例
+## 1.0.0(2022-05-29)
+````
+1.重构整个搜索页面
+2.只测了vue2的微信小程序+h5端,v3没测
+````

+ 365 - 0
uni_applet/uni_modules/d-search-log/components/d-search-log/d-search-log.vue

@@ -0,0 +1,365 @@
+<template>
+	<view class="dSLComVue">
+		
+		<view class="dSLComVueTop">
+			<view class="dSLComVueTopLeft">
+				<image class="dSLComVueTopLeftImg" style="width: 30rpx;height: 30rpx;flex: none;" src="@/static/imgs/card/search.png"></image>
+				<input  class="dSLComVueTopLeftInput" v-model="search_input" type="text" :placeholder="placeholder" @blur="onClickInput" />
+				<view class="dSLComVueTopLeftDel" v-if="search_input" @click="onClickDel">
+					<!-- <u-icon name="close" size="20"></u-icon> -->
+					<!-- <image src="/static/icon_img/del.png" class="dSLComVueTopLeftDelImg" style="width: 35rpx;height: 35rpx;flex: none;" ></image> -->
+				</view>
+				<image @click='onClickvoice' src="@/static/imgs/card/voice.png" class="dSLComVueTopLeftDelImg" style="width: 30rpx;height: 39rpx;flex: none;" ></image>
+			</view>
+			<view class="dSLComVueTopRight">
+				<view class="dSLComVueTopRightBtn" @click="onClickInput" >{{search_name?search_name:'搜索'}}</view>
+			</view>
+		</view>
+		
+		<view v-if="is_show_more" class="dSLComVue">
+			<view class="dSLComVueTitle" >
+				<text>历史搜索</text>
+				<view style="width: 100rpx;height: 60rpx;display: flex;align-items: center;" @click="onClickDelAll">
+					<!-- <u-icon name="trash-fill" size="30"></u-icon> -->
+					<image src="@/static/imgs/card/del.png" style="width: 27rpx;height: 32rpx;margin-right: 35rpx;"></image>
+				</view>
+			</view>
+			
+			<view v-if="search_list_old.length==0" class="mytext">暂无</view>
+			<view class="dSLComVueLog" v-if="search_list_old.length>0">
+				<text class="dSLComVueLogBox mytext" v-for="(item,index) in search_list_old" :key="index" @click="onClickInputValue(item)">
+					{{item?item:'无'}}
+				</text>
+			</view>
+			
+			<view class="dSLComVueTitle" v-if="search_list_hot.length>0" style="margin-top: 20rpx;">
+				<text>热门搜索</text>
+				<view style="width: 100rpx;height: 60rpx;display: flex;align-items: center;justify-content: flex-end;" @click="onClickShowHot">
+					<image :src="is_hot_show?'/static/icon_img/yan_open.png':'/static/icon_img/yan_close.png'" style="width: 30rpx;height: 30rpx;margin-right: 35rpx;"></image>
+				</view>
+			</view>
+			
+			<view class="dSLComVueLog" v-if="search_list_hot.length>0 && is_hot_show">
+				<text class="dSLComVueLogBox mytext" v-for="(item,index) in search_list_hot" :key="index" @click="onClickInputValue(item)">
+					{{item?item:'无'}}
+				</text>
+			</view>
+		</view>
+		
+		
+		
+		
+		
+		
+		
+		
+	</view>
+</template>
+
+
+<script>
+	export default {
+		data() {
+			return {
+				search_list_old:[],
+				search_input:'',
+				is_hot_show:true,
+			}
+		},
+		created() {
+			this._init_search()
+			
+		},
+		props: {
+			is_show_more: {
+				value: Boolean,
+				default: true
+			},
+			search_icon:{
+				value: String,
+				default: ''
+			},
+			search_list_old_man_num: {
+				value: Number,
+				default: 10
+			},
+			search_list_hot: {
+				value: Array,
+				default: []
+			},
+			//HM修改 定义默认搜索关键词(水印文字)
+			placeholder:{
+				value: String,
+				default: '请输入搜索内容'
+			},
+			store_key:{
+				value: String,
+				default: 'search_list'
+			},
+			
+			color_border:{
+				value: String,
+				default: ''
+			},
+			color_text:{
+				value: String,
+				default: '#30c66c'
+			},
+			search_name:{
+				value: String,
+				default: '搜索'
+			},
+			input_text:{
+				value: String,
+				default: ''
+			},
+		},
+		watch: {
+			search_list_hot(newVal) {
+				if (newVal) {
+					this.search_list_hot = newVal
+				}
+			},
+			input_text(newVal) {
+				if (newVal) {
+					this.search_input = newVal
+					// this.search_input = search_input.toLowerCase()
+					
+					this.saveKeyword()
+				}
+			},
+			
+		},
+		methods:{
+			onClickInput(){
+				this.saveKeyword()
+			},
+			onClickvoice(){
+				this.$emit('onSearchvoiceApi', '');
+			},
+			onClickInputValue(search_input) {
+				if (!search_input) {
+					return false;
+				}
+				
+				this.search_input = search_input.toLowerCase()
+				
+				this.saveKeyword()
+			},
+			_init_search() {
+				var that = this
+				
+				var store_key = that.store_key
+				
+				uni.getStorage({
+					key: store_key,
+					success: (res) => {
+						var old_data = JSON.parse(res.data);
+						that.search_list_old = old_data; //更新历史搜索
+						// console.log('____________________dev')
+						// console.log(that.search_list_old)
+						// console.log('____________________dev')
+					}
+				});
+			},
+			saveKeyword() {
+				var that = this
+				
+				// console.log('搜索的名称为:'+this.search_input)
+				if (!this.search_input) {
+					return false;
+				}
+				
+				this.search_input = this.search_input.toLowerCase()
+				
+				var search_input = this.search_input
+				
+				var store_key = that.store_key
+				uni.getStorage({
+					key: store_key,
+					success: (res) => {
+						var old_data = JSON.parse(res.data);
+						var findIndex = old_data.indexOf(search_input);
+						if (findIndex == -1) {
+							old_data.unshift(search_input);
+						} else {
+							old_data.splice(findIndex, 1);
+							old_data.unshift(search_input);
+						}
+						//最多10个纪录
+						old_data.length > that.search_list_old_man_num && old_data.pop();
+						uni.setStorage({
+							key: store_key,
+							data: JSON.stringify(old_data)
+						});
+						that.search_list_old = old_data; //更新历史搜索
+						
+						
+						// console.log('____________________dev')
+						// console.log(that.search_list_old)
+						// console.log('____________________dev')
+					},
+					fail: (e) => {
+						var old_data = [search_input];
+						uni.setStorage({
+							key: store_key,
+							data: JSON.stringify(old_data)
+						});
+						that.search_list_old = old_data; //更新历史搜索
+						// console.log('____________________dev')
+						// console.log(that.search_list_old)
+						// console.log('____________________dev')
+					}
+				});
+				//	向父级发送搜索的事件
+				that.$emit('onSearchNameApi', this.search_input);
+				//
+			},
+			onClickDel() {
+				this.search_input = ''
+				this.$emit('onSearchNameApi', '');
+			},
+			
+			onClickDelAll() {
+				var that = this
+				console.log('---全部删除-log--')
+				uni.showModal({
+					title:'提示',
+					content:'确定全部删除',
+					success(res) {
+						if (res.confirm) {
+							uni.removeStorageSync(that.store_key)
+							that.search_list_old = []
+							// console.log('______清除全部搜索记录______向上级发送事件')
+							that.$emit('onClickDelAllApi', '');
+						}
+					}
+				})
+			},
+			//
+			onClickShowHot() {
+				this.is_hot_show = this.is_hot_show==true?false:true
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.myborder {
+		border: 1rpx solid #60DF9D;
+		color: #30c66c;
+	}
+	.mytext {
+		color: #8799a3;
+	}
+	.dSLComVue {
+		width: 100%;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		padding: 20rpx;
+		.dSLComVueTop {
+			width: 90%;
+			height: 100rpx;
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			border-radius: 40rpx;
+			.dSLComVueTopLeft {
+				flex: 1;
+				height: 80rpx;
+				background-color: #F0F0F0;
+				border-radius: 50rpx;
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				padding: 0 3%;
+				// background-color: #ccc;
+				border: 1rpx solid #F0F0F0;
+				
+				.dSLComVueTopLeftImg {
+					flex: 1;
+					height: 80rpx;
+				}
+				.dSLComVueTopLeftInput {
+					flex: 1;
+					height: 80rpx;
+					padding: 0 20rpx;
+					
+				}
+				
+				.dSLComVueTopLeftDel {
+					width: 70rpx;
+					height: 60rpx;
+					display: flex;
+					align-items: center;
+					justify-content: flex-end;
+				}
+			}
+			.dSLComVueTopRight {
+				width: 100rpx;
+				margin-left: 10rpx;
+				height: 60rpx;
+				display: flex;
+				align-items: center;
+				justify-content: flex-end;
+				.dSLComVueTopRightBtn {
+					width: 120rpx;
+					height: 60rpx;
+					border-radius: 30rpx;
+					// color: red;
+					display: flex;
+					// color: #ff00ff;
+					// border:1rpx solid #ff00ff;
+					align-items: center;
+					justify-content: center;
+				}
+			}
+			
+		}
+		
+		.dSLComVueTitle {
+			width: 100%;
+			height: 80rpx;
+			display: flex;
+			font-weight: 600;
+			font-size: 26rpx;
+			align-items: center;
+			// background-color: #30c66c;
+			align-items: center;
+			justify-content: space-between;
+			padding-left: 5%;
+		}
+		.dSLComVueTitle text{
+			position:relative;
+			font-size:32rpx;
+		}
+		.dSLComVueTitle text::before{
+			content:'';
+			display:block;
+			position:absolute;
+			width: 3px;
+			height: 16px;
+			left: -6px;
+			top: 4px;
+			background:#4977FC;
+		}
+		.dSLComVueLog {
+			width: 90%;
+			display: flex;
+			flex-wrap: wrap;
+			justify-content: flex-start;
+			.dSLComVueLogBox {
+				line-height: 55rpx;
+				background-color: #e8e8e8;
+				padding: 0rpx 30rpx;
+				border-radius: 10rpx;
+				flex: none;
+				margin: 10rpx 25rpx 15rpx 0;
+				// border: 1rpx solid #ccc
+			}
+			
+			
+		}
+	}
+</style>

+ 85 - 0
uni_applet/uni_modules/d-search-log/components/d-search-log/pages/index/index.vue

@@ -0,0 +1,85 @@
+
+<template>
+	<view>
+		
+		<cu-custom bgColor="my-theme-bg" :isBack="false">
+			<block slot="backText">返回</block>
+			<block slot="content">搜索页面</block>
+		</cu-custom>
+		
+		<d-search-log 
+		:color_border="color_border"
+		:color_text="color_border"
+		:search_list_hot="search_list_hot"
+		:store_key="store_key"
+		:input_text="input_text"
+		@onClickDelAllApi="onClickDelAll"
+		@onSearchNameApi="onSearchName"
+		></d-search-log>
+		
+		
+		
+	</view>
+</template>
+
+<script>
+	import dSearchLog from '@/uni_modules/d-search-log/components/d-search-log/d-search-log.vue'
+	import dAlert from '@/uni_modules/d-alert/components/d-alert/d-alert.vue'
+	
+	export default {
+		components: {
+			"dSearchLog": dSearchLog,
+			"dAlert": dAlert,
+		},
+		computed:{
+			
+		},
+
+		data() {
+			return {
+				color_border:"#ff00ff",
+				search_list_hot:[],
+				store_key:'search_list',
+				input_text:''
+			}
+		},
+		onLoad() {
+			var that = this
+			if (true) {
+				setTimeout(function(){
+					that.input_text = 'dev'
+				},1000)
+			}
+			
+			
+			this.search_list_hot = [
+				'手机','电脑','河南老君山','三亚一游','北京环球影城','杭州西湖','保定驴肉火烧','保定狼牙山玻璃栈道'
+			]
+			
+			// this._open_tan()
+		},
+		methods: {
+			onClickDelAll() {
+				console.log('[父级接收事件]:删除全部搜索记录')
+			},
+			onSearchName(e) {
+				console.log('[父级接收事件]:点击搜索:'+e)
+			},
+			
+		
+			 
+			// 
+		}
+	}
+</script>
+
+<style>
+	page {
+		/* background-color: #f2f2f2; */
+	}
+	.my-theme-bg {
+		background: linear-gradient(117deg,#60DF9D,#31CB7B);
+		color:#fff;
+
+	}
+</style>

二進制
uni_applet/uni_modules/d-search-log/components/d-search-log/static/icon_img/del.png


二進制
uni_applet/uni_modules/d-search-log/components/d-search-log/static/icon_img/del2.png


二進制
uni_applet/uni_modules/d-search-log/components/d-search-log/static/icon_img/search.png


二進制
uni_applet/uni_modules/d-search-log/components/d-search-log/static/icon_img/yan_close.png


二進制
uni_applet/uni_modules/d-search-log/components/d-search-log/static/icon_img/yan_open.png


+ 110 - 0
uni_applet/uni_modules/d-search-log/readme.md

@@ -0,0 +1,110 @@
+## 【2023-搜索页面-可能是最好用的插件】
+## d-search-log
+
+
+````
+
+弹框插件: https://ext.dcloud.net.cn/plugin?id=2708
+
+日历插件: https://ext.dcloud.net.cn/plugin?id=2779
+
+````
+
+
+
+````
+
+1. /pages/index/index.vue 为案例文件
+
+2. 素材图片的话 复制到   /static/目录下
+
+
+````
+
+### 或者直接复制下边代码
+
+
+````
+
+
+
+<template>
+	<view>
+		
+		<cu-custom bgColor="my-theme-bg" :isBack="false">
+			<block slot="backText">返回</block>
+			<block slot="content">搜索页面</block>
+		</cu-custom>
+		
+		<d-search-log 
+		:color_border="color_border"
+		:color_text="color_border"
+		:search_list_hot="search_list_hot"
+		:store_key="store_key"
+		@onClickDelAllApi="onClickDelAll"
+		@onSearchNameApi="onSearchName"
+		></d-search-log>
+		
+		
+	</view>
+</template>
+
+<script>
+	import dSearchLog from '@/uni_modules/d-search-log/components/d-search-log/d-search-log.vue'
+	
+	export default {
+		components: {
+			"dSearchLog": dSearchLog
+		},
+		computed:{
+			
+		},
+
+		data() {
+			return {
+				color_border:"#ff00ff",
+				search_list_hot:[],
+				store_key:'search_list',
+			}
+		},
+		onLoad() {
+			
+			this.search_list_hot = [
+				'手机','电脑','河南老君山','三亚一游','北京环球影城','杭州西湖','保定驴肉火烧','保定狼牙山玻璃栈道'
+			]
+		},
+		methods: {
+			onClickDelAll() {
+				console.log('[父级接收事件]:删除全部搜索记录')
+			},
+			onSearchName(e) {
+				console.log('[父级接收事件]:点击搜索:'+e)
+			}
+		}
+	}
+</script>
+
+<style>
+	
+	.my-theme-bg {
+		background: linear-gradient(117deg,#60DF9D,#31CB7B);
+		color:#fff;
+		/* background: linear-gradient(117deg,#23C0F7,#ff661e); */
+		/* background: linear-gradient(90deg, #FFAA57, #23C0F7); */
+		/* background: linear-gradient(90deg, #F37749, #FFAA57); */
+		/* #FFAA57 */
+		/* background-image: linear-gradient(45deg, #39b54a, #8dc63f); */
+		/* color: #ffffff; */
+		/* background-color: #f37b1d; */
+		/* color: #ffffff; */
+		/* background-color: #ffffff; */
+		/* color: #666666; */
+		/* linear-gradient(-27deg, #33CB80, #28D0AF) */
+		/* background-image: line=ar-gradient(-27deg,#33CB80,#28D0AF); */
+		/* color: #ffffff; */
+		/* background-image: line=ar-gradient(-27deg,#fff,#fff); */
+		/* background-image: line=ar-gradient(-27deg,#23C0F7,#23C0F7); */
+		/* color: #000; */
+	}
+</style>
+````

+ 6 - 0
uni_applet/uni_modules/lyuan-tx-asr/changelog.md

@@ -0,0 +1,6 @@
+## 1.0.2(2021-10-12)
+新增透明度设置,参数名:opacity , 默认0.4 
+## 1.0.1(2021-10-11)
+新增语音文件回调
+## 1.0.0(2021-09-17)
+* 初次提交

+ 37 - 0
uni_applet/uni_modules/lyuan-tx-asr/components/lyuan-tx-asr/asrauthentication.js

@@ -0,0 +1,37 @@
+import CryptoJS from './cryptojs.js'
+/** 获取签名 start */
+
+function toUint8Array(wordArray) {
+	// Shortcuts
+	const words = wordArray.words;
+	const sigBytes = wordArray.sigBytes;
+
+	// Convert
+	const u8 = new Uint8Array(sigBytes);
+	for (let i = 0; i < sigBytes; i++) {
+		u8[i] = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
+	}
+	return u8;
+}
+
+function Uint8ArrayToString(fileData) {
+	let dataString = '';
+	for (let i = 0; i < fileData.length; i++) {
+		dataString += String.fromCharCode(fileData[i]);
+	}
+	return dataString;
+}
+// 签名函数示例
+function signCallback(signStr, secretKey) { 
+	const hash = CryptoJS.HmacSHA1(signStr, secretKey);
+	const bytes = Uint8ArrayToString(toUint8Array(hash));
+	
+	return uni.arrayBufferToBase64(toUint8Array(hash));
+}
+
+/** 获取签名 end */
+
+
+export default {
+	signCallback,
+}

+ 273 - 0
uni_applet/uni_modules/lyuan-tx-asr/components/lyuan-tx-asr/cryptojs.js

@@ -0,0 +1,273 @@
+  
+/*
+ * [js-sha1]
+ *
+ * @version 0.6.0
+ * @copyright H, J-C 2018-9-28
+ * @license MIT
+ */
+
+var CryptoJS = function (g, l) {
+	var e = {}, d = e.lib = {}, m = function () { }, k = d.Base = {
+		extend: function (a) {
+			m.prototype = this;
+			var c = new m;
+			a && c.mixIn(a);
+			c.hasOwnProperty("init") || (c.init = function () {
+			c.$super.init.apply(this, arguments)
+			});
+			c.init.prototype = c;
+			c.$super = this;
+			return c
+		},
+		create: function () {
+			var a = this.extend();
+			a.init.apply(a, arguments);
+			return a
+		},
+		init: function () { },
+		mixIn: function (a) {
+			for (var c in a) a.hasOwnProperty(c) && (this[c] = a[c]);
+			a.hasOwnProperty("toString") && (this.toString = a.toString)
+		},
+		clone: function () {
+			return this.init.prototype.extend(this)
+		}
+	},
+	p = d.WordArray = k.extend({
+		init: function (a, c) {
+			a = this.words = a || [];
+			this.sigBytes = c != l ? c : 4 * a.length
+		},
+		toString: function (a) {
+		  	return (a || n).stringify(this)
+		},
+		concat: function (a) {
+			var c = this.words,
+				q = a.words,
+				f = this.sigBytes;
+			a = a.sigBytes;
+			this.clamp();
+			if (f % 4)
+				for (var b = 0; b < a; b++) c[f + b >>> 2] |= (q[b >>> 2] >>> 24 - 8 * (b % 4) & 255) << 24 - 8 * ((f + b) % 4);
+			else if (65535 < q.length)
+				for (b = 0; b < a; b += 4) c[f + b >>> 2] = q[b >>> 2];
+			else c.push.apply(c, q);
+			this.sigBytes += a;
+			return this
+		},
+		clamp: function () {
+			var a = this.words,
+			c = this.sigBytes;
+			a[c >>> 2] &= 4294967295 << 32 - 8 * (c % 4);
+			a.length = g.ceil(c / 4)
+		},
+		clone: function () {
+			var a = k.clone.call(this);
+			a.words = this.words.slice(0);
+			return a
+		},
+		random: function (a) {
+			for (var c = [], b = 0; b < a; b += 4) c.push(4294967296 * g.random() | 0);
+			return new p.init(c, a)
+		}
+	}),
+	b = e.enc = {}, n = b.Hex = {
+		stringify: function (a) {
+			var c = a.words;
+			a = a.sigBytes;
+			for (var b = [], f = 0; f < a; f++) {
+				var d = c[f >>> 2] >>> 24 - 8 * (f % 4) & 255;
+				b.push((d >>> 4).toString(16));
+				b.push((d & 15).toString(16))
+			}
+			return b.join("")
+		},
+		parse: function (a) {
+			for (var c = a.length, b = [], f = 0; f < c; f += 2) b[f >>> 3] |= parseInt(a.substr(f, 2), 16) << 24 - 4 * (f % 8);
+			return new p.init(b, c / 2)
+		}
+	}, j = b.Latin1 = {
+		stringify: function (a) {
+		  var c = a.words;
+		  a = a.sigBytes;
+		  for (var b = [], f = 0; f < a; f++) b.push(String.fromCharCode(c[f >>> 2] >>> 24 - 8 * (f % 4) & 255));
+		  return b.join("")
+		},
+		parse: function (a) {
+		  for (var c = a.length, b = [], f = 0; f < c; f++) b[f >>> 2] |= (a.charCodeAt(f) & 255) << 24 - 8 * (f % 4);
+		  return new p.init(b, c)
+		}
+	}, h = b.Utf8 = {
+		stringify: function (a) {
+			try {
+				return decodeURIComponent(escape(j.stringify(a)))
+			} catch (c) {
+				throw Error("Malformed UTF-8 data");
+			}
+		},
+		parse: function (a) {
+		  	return j.parse(unescape(encodeURIComponent(a)))
+		}
+	},
+	r = d.BufferedBlockAlgorithm = k.extend({
+		reset: function () {
+			this._data = new p.init;
+			this._nDataBytes = 0
+		},
+		_append: function (a) {
+			"string" == typeof a && (a = h.parse(a));
+			this._data.concat(a);
+			this._nDataBytes += a.sigBytes
+		},
+		_process: function (a) {
+			var c = this._data,
+			b = c.words,
+			f = c.sigBytes,
+			d = this.blockSize,
+			e = f / (4 * d),
+			e = a ? g.ceil(e) : g.max((e | 0) - this._minBufferSize, 0);
+			a = e * d;
+			f = g.min(4 * a, f);
+			if (a) {
+				for (var k = 0; k < a; k += d) this._doProcessBlock(b, k);
+				k = b.splice(0, a);
+				c.sigBytes -= f
+			}
+			return new p.init(k, f)
+		},
+		clone: function () {
+			var a = k.clone.call(this);
+			a._data = this._data.clone();
+			return a
+		},
+		_minBufferSize: 0
+	});
+	d.Hasher = r.extend({
+		cfg: k.extend(),
+		init: function (a) {
+			this.cfg = this.cfg.extend(a);
+			this.reset()
+		},
+		reset: function () {
+			r.reset.call(this);
+			this._doReset()
+		},
+		update: function (a) {
+			this._append(a);
+			this._process();
+			return this
+		},
+		finalize: function (a) {
+			a && this._append(a);
+			return this._doFinalize()
+		},
+		blockSize: 16,
+		_createHelper: function (a) {
+			return function (b, d) {
+			return (new a.init(d)).finalize(b)
+			}
+		},
+		_createHmacHelper: function (a) {
+			return function (b, d) {
+			return (new s.HMAC.init(a, d)).finalize(b)
+			}
+		}
+	});
+	var s = e.algo = {};
+	return e
+}(Math);
+
+(function () {
+	var g = CryptoJS,
+		l = g.lib,
+		e = l.WordArray,
+		d = l.Hasher,
+		m = [],
+		l = g.algo.SHA1 = d.extend({
+			_doReset: function () {
+				this._hash = new e.init([1732584193, 4023233417, 2562383102, 271733878, 3285377520])
+			},
+			_doProcessBlock: function (d, e) {
+				for (var b = this._hash.words, n = b[0], j = b[1], h = b[2], g = b[3], l = b[4], a = 0; 80 > a; a++) {
+					if (16 > a) m[a] = d[e + a] | 0;
+					else {
+					var c = m[a - 3] ^ m[a - 8] ^ m[a - 14] ^ m[a - 16];
+					m[a] = c << 1 | c >>> 31
+					}
+					c = (n << 5 | n >>> 27) + l + m[a];
+					c = 20 > a ? c + ((j & h | ~j & g) + 1518500249) : 40 > a ? c + ((j ^ h ^ g) + 1859775393) : 60 > a ? c + ((j & h | j & g | h & g) - 1894007588) : c + ((j ^ h ^ g) - 899497514);
+					l = g;
+					g = h;
+					h = j << 30 | j >>> 2;
+					j = n;
+					n = c
+				}
+				b[0] = b[0] + n | 0;
+				b[1] = b[1] + j | 0;
+				b[2] = b[2] + h | 0;
+				b[3] = b[3] + g | 0;
+				b[4] = b[4] + l | 0
+			},
+			_doFinalize: function () {
+				var d = this._data,
+					e = d.words,
+					b = 8 * this._nDataBytes,
+					g = 8 * d.sigBytes;
+				e[g >>> 5] |= 128 << 24 - g % 32;
+				e[(g + 64 >>> 9 << 4) + 14] = Math.floor(b / 4294967296);
+				e[(g + 64 >>> 9 << 4) + 15] = b;
+				d.sigBytes = 4 * e.length;
+				this._process();
+				return this._hash
+			},
+			clone: function () {
+				var e = d.clone.call(this);
+				e._hash = this._hash.clone();
+				return e
+			}
+	});
+	g.SHA1 = d._createHelper(l);
+	g.HmacSHA1 = d._createHmacHelper(l)
+})();
+
+(function () {
+	var g = CryptoJS,
+		l = g.enc.Utf8;
+		g.algo.HMAC = g.lib.Base.extend({
+		init: function (e, d) {
+			e = this._hasher = new e.init;
+			"string" == typeof d && (d = l.parse(d));
+			var g = e.blockSize,
+			k = 4 * g;
+			d.sigBytes > k && (d = e.finalize(d));
+			d.clamp();
+			for (var p = this._oKey = d.clone(), b = this._iKey = d.clone(), n = p.words, j = b.words, h = 0; h < g; h++) n[h] ^= 1549556828, j[h] ^= 909522486;
+			p.sigBytes = b.sigBytes = k;
+			this.reset()
+		},
+		reset: function () {
+			var e = this._hasher;
+			e.reset();
+			e.update(this._iKey)
+		},
+		update: function (e) {
+			this._hasher.update(e);
+			return this
+		},
+		finalize: function (e) {
+			var d = this._hasher;
+			e = d.finalize(e);
+			d.reset();
+			return d.finalize(this._oKey.clone().concat(e))
+		}
+	})
+})();
+  
+  //使用算法
+  // var key = "f7205fffe445421fdssdfsdfdsfs"
+  // var sha1_result = CryptoJS.HmacSHA1("helloword", key)
+  // console.log('-------',sha1_result.toString())
+  
+
+export default CryptoJS;

+ 374 - 0
uni_applet/uni_modules/lyuan-tx-asr/components/lyuan-tx-asr/lyuan-tx-asr.vue

@@ -0,0 +1,374 @@
+<template>
+	<view class="asr-container flex flex-direction " v-show="isShow" :class="isAnimation ? 'animation-slide-bottom' : 'animation-slide-bottom-hidden'">
+		<view class="flex1" :style="'background-color: rgba(0,0,0,' + opacity + ');'"></view>
+		<view class="bg-white shadow shadow-warp radius padding-bottom shadow-blur">
+			<view class="cu-bar">
+				<view class="action"><button class="cu-btn" @click="cancel">取消</button></view>
+				<view class="content"></view>
+				<view class="action"><button class="cu-btn bg-main" @click="ok">确定</button></view>
+			</view>
+
+			<view class="padding">
+				<view class=" bg-gray padding border">{{ content }}</view>
+			</view>
+			<view class="flex  align-center justify-center">
+				<view v-if="status" class="flex1 margin-lr asr-playing"></view>
+				<view class=" flex justify-center flex-direction align-center">
+					<view
+						style="border: 2px solid #007AFF;"
+						class="cu-avatar xl round bg-white   "
+						:class="status ? 'startBtn' : ''"
+						@touchstart="ontouchstart"
+						@touchend="ontouchend"
+					>
+						<view :class="status ? 'cuIcon-stop text-blue' : 'cuIcon-playfill text-blue'"></view>
+					</view>
+					<view class=" text-gray padding">
+						<text v-if="status == 0">长按开始</text>
+						<text v-else-if="status == 1">引擎初始化</text>
+						<text v-else-if="status == 2">语音识别启动中</text>
+						<text v-else-if="status == 3">正在识别 {{ durationClock }}</text>
+					</view>
+				</view>
+				<view v-if="status" class="flex1 margin-lr asr-playing" style=""></view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+import asrauthentication from './asrauthentication.js';
+export default {
+	name: 'lyuan-tx-asr',
+	props: {
+		secretKey: { type: String },
+		secretId: { type: String },
+		appId: { type: Number | String },
+		uploadFile: { type: Function },
+		opacity: { type: Number, default: 0.4 }
+	},
+	data() {
+		return {
+			socket: null,
+			recorder: null,
+			status: 0,
+			timer: null,
+			content: '',
+			message: '',
+			duration: 0,
+			durationTimer: null,
+			isShow: false,
+			isAnimation: false
+		};
+	},
+	computed: {
+		durationClock: function() {
+			let minute = Math.floor(this.duration / 60);
+			if (minute < 10) minute = '0' + minute.toString();
+			let seconds = this.duration % 60;
+			if (seconds < 10) seconds = '0' + seconds.toString();
+			return minute + ':' + seconds;
+		}
+	},
+	methods: {
+		show: function() {
+			if (!this.appId || !this.secretId || !this.secretKey) {
+				uni.showToast({
+					icon: 'none',
+					title: '缺少腾讯配置参数'
+				});
+				return;
+			}
+
+			// #ifdef H5
+			uni.showToast({
+				icon: 'none',
+				title: '暂不支持H5平台'
+			});
+			return;
+			// #endif
+			this.status = 0;
+			this.content = '';
+			this.isShow = true;
+			this.isAnimation = true;
+		},
+		hide: function() {
+			this.stop();
+			this.isAnimation = false;
+			setTimeout(() => {
+				this.isShow = false;
+			}, 500);
+		},
+		ok: function() {
+			this.$emit('change', this.content);
+			this.hide();
+		},
+		cancel: function() {
+			this.hide();
+		},
+		ontouchstart(e) {
+			console.log('touch start');
+			if (this.timer) clearTimeout(this.timer);
+			this.timer = setTimeout(() => {
+				this.start();
+				this.timer = null;
+			}, 600);
+		},
+		ontouchend(e) {
+			console.log('touch end');
+			if (this.timer) {
+				console.log('清除定时器');
+				clearTimeout(this.timer);
+				this.timer = null;
+			}
+			this.stop();
+		},
+		getUrl: function() {
+			const timestamp = parseInt(new Date().getTime() / 1000) - 1;
+			const params = {
+				secretid: this.secretId,
+				timestamp: timestamp,
+				expired: timestamp + 60 * 60,
+				nonce: timestamp,
+				engine_model_type: '16k_zh',
+				voice_id: timestamp.toString(),
+				voice_format: 8
+			};
+			const url =
+				'asr.cloud.tencent.com/asr/v2/' +
+				this.appId +
+				'?' +
+				Object.keys(params)
+					.sort(function(a, b) {
+						return a.localeCompare(b);
+					})
+					.map(key => {
+						return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
+					})
+					.join('&');
+			const signature = asrauthentication.signCallback(url, this.secretKey);
+			return url + '&signature=' + signature;
+		},
+		startTimer: function() {
+			this.duration = 0;
+			this.durationTimer = setInterval(() => {
+				this.duration += 1;
+			}, 1000);
+		},
+		stopTimer: function() {
+			if (this.durationTimer) {
+				clearInterval(this.durationTimer);
+				this.durationTimer = null;
+			}
+		},
+		start: function() {
+			// #ifdef APP-PLUS
+			this.startRecorder();
+			// #endif
+			// #ifdef MP-WEIXIN
+			this.startConnect();
+			// #endif
+		},
+		stop: function() {
+			// #ifdef APP-PLUS
+			this.stopRecorder();
+			// #endif
+			// #ifdef MP-WEIXIN
+			this.stopConnect();
+			// #endif
+		},
+		startConnect: function() {
+			this.status = 1;
+
+			const socket = (this.socket = uni.connectSocket({
+				url: 'wss://' + this.getUrl(),
+				success: data => {
+					console.log('socket connect result ', data);
+				},
+				fail: e => {
+					this.loading(JSON.stringify(e));
+				}
+			}));
+
+			socket.onOpen(() => {
+				console.log('socket open');
+			});
+			socket.onMessage(({ data }) => {
+				console.log('socket message', data);
+				if (typeof data === 'string') data = JSON.parse(data);
+				if (data.code == 0 && data.result) {
+					if (data.result.voice_text_str) {
+						console.log('识别成功:' + data.result.voice_text_str);
+						this.content = data.result.voice_text_str;
+					}
+				} else if (data.code == 0) {
+					this.status = 2;
+					this.startRecorder();
+				} else {
+					this.loading(data.message);
+				}
+			});
+			socket.onClose(e => {
+				console.log('socket close');
+				this.stopRecorder();
+				this.socket = null;
+			});
+			socket.onError(e => {
+				console.log('socket error', e);
+				this.stopRecorder();
+				this.socket = null;
+			});
+		},
+		stopConnect: function() {
+			if (this.socket) {
+				this.socket.close();
+			}
+		},
+		startRecorder: function() {
+			if (this.recorder == null) {
+				const recorder = (this.recorder = uni.getRecorderManager());
+				recorder.onFrameRecorded(({ isLastFrame, frameBuffer }) => {
+					if (this.socket) {
+						this.socket.send({
+							data: frameBuffer
+						});
+						if (isLastFrame) {
+							this.socket.send({
+								data: JSON.stringify({
+									type: 'end'
+								})
+							});
+						}
+					}
+				});
+				recorder.onError(({ errMsg }) => {
+					console.log('recorder error', errMsg);
+					if (errMsg != "operateRecorder:fail:audio is stop, don't stop record again") {
+						this.loading('启动失败:' + errMsg);
+						this.stopConnect();
+					}
+				});
+				recorder.onStart(() => {
+					console.log('recorder start');
+					//this.loading('正在识别');
+					this.status = 3;
+					this.startTimer();
+				});
+				recorder.onStop(({ tempFilePath }) => {
+					console.log('recorder stop', tempFilePath);
+
+					// #ifdef APP-PLUS
+					if (this.uploadFile)
+						this.uploadFile(tempFilePath)
+							.then(res => {
+								this.content = res;
+								this.$emit('fileChange', { file: tempFilePath, content: res });
+							})
+							.catch(e => {
+								console.log(e);
+							});
+
+					// #endif
+					// #ifdef MP-WEIXIN
+					this.$emit('fileChange', { file: tempFilePath, content: this.content });
+					// #endif
+				});
+				recorder.onPause(e => {
+					console.log('recorder pause');
+				});
+			}
+
+			this.recorder.start({
+				duration: 60 * 1000,
+				format: 'mp3',
+				frameSize: 1.25,
+				sampleRate: 16000,
+				numberOfChannels: 1
+			});
+		},
+		stopRecorder: function() {
+			this.stopTimer();
+
+			if (this.status != 0) {
+				this.status = 0;
+			}
+
+			if (this.recorder) {
+				this.recorder.stop();
+			}
+		},
+		loading(title) {
+			uni.showToast({
+				icon: 'none',
+				title: title
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.asr-container {
+	height: calc(100vh - var(--window-top));
+	width: 750rpx;
+	position: fixed;
+	bottom: 0;
+}
+
+.asr-playing {
+	background: url(data:image/gif;base64,R0lGODlhDgAOAIABACWbJP///yH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpFNjUzM0MxOTMxMEQxMUU2OEUwRkQ0NTk5RTVERjg2OCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpFNjUzM0MxQTMxMEQxMUU2OEUwRkQ0NTk5RTVERjg2OCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkU2NTMzQzE3MzEwRDExRTY4RTBGRDQ1OTlFNURGODY4IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkU2NTMzQzE4MzEwRDExRTY4RTBGRDQ1OTlFNURGODY4Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkECSgAAQAsAAAAAA4ADgAAAh6Mj6mrAIwcPJLJuu6zDwesfZ0ziiZZhuiZkiyrtgUAIfkECSgAAQAsAAAAAA4ADgAAAh6Mj6nL7QjAiocqGyjO0ujugR8kSmN0mmrKcez3qgUAIfkEBSgAAQAsAAAAAA4ADgAAAh2Mj6nL7Q8VAHAua3CmWgcLTtz4jWGJiuSppitQAAA7)
+		repeat-x;
+	height: 60rpx;
+	background-size: contain;
+}
+
+.startBtn {
+	transition: all 0.3s;
+	cursor: pointer;
+
+	&:hover {
+		filter: contrast(1.1);
+	}
+
+	&:active {
+		filter: contrast(0.9);
+	}
+
+	&::before,
+	&::after {
+		content: '';
+		position: absolute;
+		top: -10px;
+		left: -10px;
+		right: -10px;
+		bottom: -10px;
+		border: 2px solid #007aff;
+		transition: all 0.5s;
+		animation: clippath 3s infinite linear;
+		border-radius: 10px;
+	}
+
+	&::after {
+		animation: clippath 3s infinite -1.5s linear;
+	}
+}
+
+@keyframes clippath {
+	0%,
+	100% {
+		clip-path: inset(0 0 98% 0);
+	}
+
+	25% {
+		clip-path: inset(0 98% 0 0);
+	}
+
+	50% {
+		clip-path: inset(98% 0 0 0);
+	}
+
+	75% {
+		clip-path: inset(0 0 0 98%);
+	}
+}
+</style>

+ 99 - 0
uni_applet/uni_modules/lyuan-tx-asr/readme.md

@@ -0,0 +1,99 @@
+## 腾讯小程序实时语音识别, app使用语音文件识别
+
+## [官方文档](https://cloud.tencent.com/document/product/1093/48982)
+  
+
+* 微信小程序: 支持实时音频识别
+* App:录音后通过后台Api进行语音文件识别。
+
+为省事样式直接使用的color ui 的样式 , 适合引入了ColorUi的项目使用。
+
+
+```vue
+<template>
+	<view>
+		<button @click="open">打开</button>
+		<lyuan-tx-asr
+			ref="asr"
+			:uploadMethod="uploadFile" 
+			@change="asrChange"		
+				@fileChange="fileChange"
+			appId=""
+			secretId=""
+			secretKey=""
+		></lyuan-tx-asr>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {};
+	},
+
+	methods: {
+		open: function() {
+			this.$refs.asr.show();
+		},
+		asrChange: function(res) {
+			console.log('语音识别确认结果:' + res);
+		},
+		fileChange: function({file,content}) {
+			console.log('录音文件', file);
+		},
+		uploadFile: function(tempFilePath) {
+			return new Promise((resolve, reject) => {
+				//调用你的接口把音频文件转为文字
+				this.$minApi
+					.upload('txasr/SentenceRecognition', null, tempFilePath)
+					.then(res => {
+						if (res.code == 1) {
+							resolve(res.data.Result);
+						} else {
+							reject(e);
+						}
+					})
+					.catch(e => {
+						reject(e);
+					});
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss"></style>
+
+
+```
+
+
+.net core 使用腾讯sdk语音转文字参考
+```csharp
+ using (var ms = new MemoryStream())
+{
+    var file = Request.Form.Files[0];
+    file.CopyTo(ms);
+    var bytes = ms.ToArray();
+    Credential cred = new Credential
+    {
+        SecretId = "",
+        SecretKey = ""
+    };
+    ClientProfile clientProfile = new ClientProfile();
+    HttpProfile httpProfile = new HttpProfile {Endpoint = ("asr.tencentcloudapi.com")};
+    clientProfile.HttpProfile = httpProfile;
+    AsrClient client = new AsrClient(cred, "", clientProfile);
+    SentenceRecognitionRequest req = new SentenceRecognitionRequest();
+    req.ProjectId = 0;
+    req.SubServiceType = 2;
+    req.EngSerViceType = "16k_zh";
+    req.SourceType = 1;
+    req.VoiceFormat = "mp3";
+    req.UsrAudioKey = Guid.NewGuid().ToString();
+    req.Data = Convert.ToBase64String(bytes);
+    req.DataLen = bytes.Length;
+    SentenceRecognitionResponse resp = client.SentenceRecognitionSync(req);
+    return resp;
+}
+```

+ 6 - 0
uni_applet/uni_modules/mescroll-uni/changelog.md

@@ -0,0 +1,6 @@
+## 1.3.7(2021-04-13)
+1. 新增`mescroll-swiper-sticky.vue`的示例, 轮播吸顶菜单导航  
+2. 新增`mescroll-empty.vue`的示例, 单独使用空布局组件  
+3. 简化tabs在具体项目中的使用,并简化对应的示例  
+4. mescroll-uni 支持动态禁止滚动的属性 disableScroll (注: mescroll-body不支持)  
+-by 小瑾同学

+ 19 - 0
uni_applet/uni_modules/mescroll-uni/components/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);
+	}
+}

+ 400 - 0
uni_applet/uni_modules/mescroll-uni/components/mescroll-body/mescroll-body.vue

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

+ 47 - 0
uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/beibei/components/mescroll-down.css

@@ -0,0 +1,47 @@
+/*下拉刷新--标语*/
+.mescroll-downwarp .downwarp-slogan{
+	display: block;
+	width: 420rpx;
+	height: 168rpx;
+	margin: auto;
+}
+/*下拉刷新--向下进度动画*/
+.mescroll-downwarp .downwarp-progress{
+	display: inline-block;
+	width: 40rpx;
+	height: 40rpx;
+	border: none;
+	margin: auto;
+	background-size: contain;
+	background-repeat: no-repeat;
+	background-position: center;
+	background-image: url(https://www.mescroll.com/img/beibei/mescroll-progress.png);
+	transition: all 300ms;
+}
+/*下拉刷新--进度条*/
+.mescroll-downwarp .downwarp-loading{
+	display: inline-block;
+	width: 32rpx;
+	height: 32rpx;
+	border-radius: 50%;
+	border: 2rpx solid #FF8095;
+	border-bottom-color: transparent;
+}
+/*下拉刷新--吉祥物*/
+.mescroll-downwarp .downwarp-mascot{
+	position: absolute;
+	right: 16rpx;
+	bottom: 0;
+	width: 100rpx;
+	height: 100rpx;
+	background-size: contain;
+	background-repeat: no-repeat;
+	animation: animMascot .6s steps(1,end) infinite;
+}
+@keyframes animMascot {
+	0% {background-image: url(https://www.mescroll.com/img/beibei/mescroll-bb1.png)}
+	25% {background-image: url(https://www.mescroll.com/img/beibei/mescroll-bb2.png)}
+	50% {background-image: url(https://www.mescroll.com/img/beibei/mescroll-bb3.png)}
+	75% {background-image: url(https://www.mescroll.com/img/beibei/mescroll-bb4.png)}
+	100% {background-image: url(https://www.mescroll.com/img/beibei/mescroll-bb1.png)}
+}

+ 39 - 0
uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/beibei/components/mescroll-down.vue

@@ -0,0 +1,39 @@
+<!-- 下拉刷新区域 -->
+<template>
+	<view v-if="mOption.use" class="mescroll-downwarp" :style="{'background':mOption.bgColor,'color':mOption.textColor}">
+		<view class="downwarp-content">
+			<image class="downwarp-slogan" src="https://www.mescroll.com/img/beibei/mescroll-slogan.jpg?v=1" mode="widthFix"/>
+			<view v-if="isDownLoading" class="downwarp-loading mescroll-rotate"></view>
+			<view v-else class="downwarp-progress" :style="{'transform':downRotate}"></view>
+			<view class="downwarp-mascot"></view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		option: Object , // down的配置项
+		type: Number // 下拉状态(inOffset:1, outOffset:2, showLoading:3, endDownScroll:4)
+	},
+	computed: {
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
+		mOption(){
+			return this.option || {}
+		},
+		// 是否在加载中
+		isDownLoading(){
+			return this.type === 3
+		},
+		// 旋转的角度
+		downRotate(){
+			return this.type === 2 ? 'rotate(180deg)' : 'rotate(0deg)'
+		}
+	}
+};
+</script>
+
+<style>
+@import "../../../mescroll-uni/components/mescroll-down.css";
+@import "./mescroll-down.css";
+</style>

+ 360 - 0
uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/beibei/mescroll-body.vue

@@ -0,0 +1,360 @@
+<template>
+	<view 
+		class="mescroll-body mescroll-render-touch" 
+		:style="{'minHeight':minHeight, 'padding-top': padTop, 'padding-bottom': padBottom}" 
+		:class="{'mescorll-sticky': sticky}"
+		@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"></mescroll-down> -->
+			<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
+				<view class="downwarp-content">
+					<image class="downwarp-slogan" src="https://www.mescroll.com/img/beibei/mescroll-slogan.jpg?v=1" mode="widthFix"/>
+					<view v-if="isDownLoading" class="downwarp-loading mescroll-rotate"></view>
+					<view v-else class="downwarp-progress" :style="{'transform':downRotate}"></view>
+					<view class="downwarp-mascot"></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端生效) -->
+		<!-- #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="../../mescroll-uni/wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
+<!-- #endif -->
+
+<!-- app, h5使用renderjs -->
+<!-- #ifdef APP-PLUS || H5 -->
+<script module="renderBiz" lang="renderjs">
+	import renderBiz from '../../mescroll-uni/wxs/renderjs.js';
+	export default {
+		mixins: [renderBiz]
+	}
+</script>
+<!-- #endif -->
+
+<script>
+	import MeScroll from '../../mescroll-uni/mescroll-uni.js';
+	import MescrollTop from '../../mescroll-uni/components/mescroll-top.vue';
+	import WxsMixin from '../../mescroll-uni/wxs/mixins.js';
+	import mescrollI18n from '../../mescroll-uni/mescroll-i18n.js';
+	import GlobalOption from './mescroll-uni-option.js';
+	
+	export default {
+		mixins: [WxsMixin],
+		components: {
+			MescrollTop
+		},
+		data() {
+			return {
+				mescroll: null, // mescroll实例
+				downHight: 0, //下拉刷新: 容器高度
+				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, // 上拉加载的参数配置
+			i18n: 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 this.downLoadType === 2 ? 'rotate(180deg)' : 'rotate(0deg)'
+			}
+		},
+		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组件时,此行不可删)
+					},
+					showLoading(mescroll, downHight) {
+						vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+					},
+					endDownScroll() {
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						if(vm.downResetTimer) {clearTimeout(vm.downResetTimer); vm.downResetTimer = null} // 移除重置倒计时
+						vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,避免下次inOffset不及时显示textInOffset
+							if(vm.downLoadType === 4) vm.downLoadType = 0
+						},300)
+					},
+					// 派发下拉刷新的回调
+					callback: function(mescroll) {
+						vm.$emit('down', mescroll);
+					}
+				},
+				// 上拉加载的配置
+				up: {
+					// 显示加载中的回调
+					showLoading() {
+						vm.upLoadType = 1;
+					},
+					// 显示无更多数据的回调
+					showNoMore() {
+						vm.upLoadType = 2;
+					},
+					// 隐藏上拉加载的回调
+					hideUpScroll(mescroll) {
+						vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
+					},
+					// 空布局
+					empty: {
+						onShow(isShow) {
+							// 显示隐藏的回调
+							vm.isShowEmpty = isShow;
+						}
+					},
+					// 回到顶部
+					toTop: {
+						onShow(isShow) {
+							// 显示隐藏的回调
+							vm.isShowToTop = isShow;
+						}
+					},
+					// 派发上拉加载的回调
+					callback: function(mescroll) {
+						vm.$emit('up', mescroll);
+					}
+				}
+			};
+
+			let i18nType = mescrollI18n.getType() // 当前语言类型
+			let i18nOption = {type: i18nType} // 国际化配置
+			MeScroll.extend(i18nOption, vm.i18n) // 具体页面的国际化配置
+			MeScroll.extend(i18nOption, GlobalOption.i18n) // 全局的国际化配置
+			MeScroll.extend(diyOption, i18nOption[i18nType]); // 混入国际化配置
+			MeScroll.extend(diyOption, {down:GlobalOption.down, up:GlobalOption.up}); // 混入全局的配置
+			let myOption = JSON.parse(JSON.stringify({down: vm.down,up: vm.up})); // 深拷贝,避免对props的影响
+			MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
+
+			// 初始化MeScroll对象
+			vm.mescroll = new MeScroll(myOption, true); // 传入true,标记body为滚动区域
+			// 挂载语言包
+			vm.mescroll.i18n = i18nOption;
+			// init回调mescroll对象
+			vm.$emit('init', vm.mescroll);
+
+			// 设置高度
+			const sys = uni.getSystemInfoSync();
+			if (sys.windowHeight) vm.windowHeight = sys.windowHeight;
+			if (sys.windowBottom) vm.windowBottom = sys.windowBottom;
+			if (sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
+			// 使down的bottomOffset生效
+			vm.mescroll.setBodyHeight(sys.windowHeight);
+
+			// 因为使用的是page的scroll,这里需自定义scrollTo
+			vm.mescroll.resetScrollTo((y, t) => {
+				if(typeof y === 'string'){
+					// 滚动到指定view (y为css选择器)
+					setTimeout(()=>{ // 延时确保view已渲染; 不使用$nextTick
+						let selector;
+						if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
+							selector = '#'+y // 不带#和. 则默认为id选择器
+						}else{
+							selector = y
+							// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
+							if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
+								selector = y.split('>>>')[1].trim()
+							}
+							// #endif
+						}
+						uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
+							if (rect) {
+								let top = rect.top
+								top += vm.mescroll.getScrollTop()
+								uni.pageScrollTo({
+									scrollTop: top,
+									duration: t
+								})
+							} else{
+								console.error(selector + ' does not exist');
+							}
+						}).exec()
+					},30)
+				} else{
+					// 滚动到指定位置 (y必须为数字)
+					uni.pageScrollTo({
+						scrollTop: y,
+						duration: t
+					})
+				}
+			});
+
+			// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
+			if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
+				vm.mescroll.optUp.toTop.safearea = vm.safearea;
+			}
+			
+			// 全局配置监听
+			uni.$on("setMescrollGlobalOption", options=>{
+				if(!options) return;
+				let i18nType = options.i18n ? options.i18n.type : null
+				if(i18nType && vm.mescroll.i18n.type != i18nType){
+					vm.mescroll.i18n.type = i18nType
+					mescrollI18n.setType(i18nType)
+					MeScroll.extend(options, vm.mescroll.i18n[i18nType])
+				}
+				if(options.down){
+					let down = MeScroll.extend({}, options.down)
+					vm.mescroll.optDown = MeScroll.extend(down, vm.mescroll.optDown)
+				}
+				if(options.up){
+					let up = MeScroll.extend({}, options.up)
+					vm.mescroll.optUp = MeScroll.extend(up, vm.mescroll.optUp)
+				}
+			})
+		},
+		destroyed() {
+			// 注销全局配置监听
+			uni.$off("setMescrollGlobalOption")
+		}
+	};
+</script>
+
+<style>
+	@import "../../mescroll-body/mescroll-body.css";
+	@import "../../mescroll-uni/components/mescroll-down.css";
+	@import "../../mescroll-uni/components/mescroll-up.css";
+	@import "./components/mescroll-down.css";
+</style>

+ 49 - 0
uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/beibei/mescroll-uni-option.js

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

+ 437 - 0
uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/beibei/mescroll-uni.vue

@@ -0,0 +1,437 @@
+<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"></mescroll-down> -->
+					<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
+						<view class="downwarp-content">
+							<image class="downwarp-slogan" src="https://www.mescroll.com/img/beibei/mescroll-slogan.jpg?v=1" mode="widthFix"/>
+							<view v-if="isDownLoading" class="downwarp-loading mescroll-rotate"></view>
+							<view v-else class="downwarp-progress" :style="{'transform':downRotate}"></view>
+							<view class="downwarp-mascot"></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端生效) -->
+				<!-- #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="../../mescroll-uni/wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
+<!-- #endif -->
+
+<!-- app, h5使用renderjs -->
+<!-- #ifdef APP-PLUS || H5 -->
+<script module="renderBiz" lang="renderjs">
+	import renderBiz from '../../mescroll-uni/wxs/renderjs.js';
+	export default {
+		mixins: [renderBiz]
+	}
+</script>
+<!-- #endif -->
+
+<script>
+	import MeScroll from '../../mescroll-uni/mescroll-uni.js';
+	import MescrollTop from '../../mescroll-uni/components/mescroll-top.vue';
+	import WxsMixin from '../../mescroll-uni/wxs/mixins.js';
+	import mescrollI18n from '../../mescroll-uni/mescroll-i18n.js';
+	import GlobalOption from './mescroll-uni-option.js';
+	
+	export default {
+		mixins: [WxsMixin],
+		components: {
+			MescrollTop
+		},
+		data() {
+			return {
+				mescroll: null, // mescroll实例
+				viewId: 'id_' + Math.random().toString(36).substr(2,16), // 随机生成mescroll的id(不能数字开头,否则找不到元素)
+				downHight: 0, //下拉刷新: 容器高度
+				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, // 上拉加载的参数配置
+			i18n: 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
+			},
+			disableScroll: Boolean // 是否禁止滚动
+		},
+		computed: {
+			// 是否使用fixed定位 (当height有值,则不使用)
+			isFixed(){
+				return !this.height && this.fixed
+			},
+			// mescroll的高度
+			scrollHeight(){
+				if (this.isFixed) {
+					return "auto"
+				} else if(this.height){
+					return this.toPx(this.height) + 'px'
+				}else{
+					return "100%"
+				}
+			},
+			// 下拉布局往下偏移的距离 (px)
+			numTop() {
+				return this.toPx(this.top)
+			},
+			fixedTop() {
+				return this.isFixed ? (this.numTop + this.windowTop) + 'px' : 0
+			},
+			padTop() {
+				return !this.isFixed ? this.numTop + 'px' : 0
+			},
+			// 上拉布局往上偏移 (px)
+			numBottom() {
+				return this.toPx(this.bottom)
+			},
+			fixedBottom() {
+				return this.isFixed ? (this.numBottom + this.windowBottom) + 'px' : 0
+			},
+			padBottom() {
+				return !this.isFixed ? this.numBottom + 'px' : 0
+			},
+			// 是否为重置下拉的状态
+			isDownReset(){
+				return this.downLoadType===3 || this.downLoadType===4
+			},
+			// 过渡
+			transition() {
+				return this.isDownReset ? 'transform 300ms' : ''
+			},
+			translateY() {
+				return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : '' // transform会使fixed失效,需注意把fixed元素写在mescroll之外
+			},
+			// 列表是否可滑动
+			scrollable(){
+				if(this.disableScroll) return false
+				return this.downLoadType===0 || this.isDownReset
+			},
+			// 是否在加载中
+			isDownLoading(){
+				return this.downLoadType === 3
+			},
+			// 旋转的角度
+			downRotate(){
+				return this.downLoadType === 2 ? 'rotate(180deg)' : 'rotate(0deg)'
+			}
+		},
+		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组件时,此行不可删)
+					},
+					showLoading(mescroll, downHight) {
+						vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+					},
+					endDownScroll() {
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						vm.downResetTimer && clearTimeout(vm.downResetTimer)
+						vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,以便置空this.transition,避免iOS小程序列表渲染不完整
+							if(vm.downLoadType===4) vm.downLoadType = 0
+						},300)
+					},
+					// 派发下拉刷新的回调
+					callback: function(mescroll) {
+						vm.$emit('down', mescroll)
+					}
+				},
+				// 上拉加载的配置
+				up: {
+					// 显示加载中的回调
+					showLoading() {
+						vm.upLoadType = 1;
+					},
+					// 显示无更多数据的回调
+					showNoMore() {
+						vm.upLoadType = 2;
+					},
+					// 隐藏上拉加载的回调
+					hideUpScroll(mescroll) {
+						vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
+					},
+					// 空布局
+					empty: {
+						onShow(isShow) { // 显示隐藏的回调
+							vm.isShowEmpty = isShow;
+						}
+					},
+					// 回到顶部
+					toTop: {
+						onShow(isShow) { // 显示隐藏的回调
+							vm.isShowToTop = isShow;
+						}
+					},
+					// 派发上拉加载的回调
+					callback: function(mescroll) {
+						vm.$emit('up', mescroll);
+						// 更新容器的高度 (多mescroll的情况)
+						vm.setClientHeight()
+					}
+				}
+			}
+
+			let i18nType = mescrollI18n.getType() // 当前语言类型
+			let i18nOption = {type: i18nType} // 国际化配置
+			MeScroll.extend(i18nOption, vm.i18n) // 具体页面的国际化配置
+			MeScroll.extend(i18nOption, GlobalOption.i18n) // 全局的国际化配置
+			MeScroll.extend(diyOption, i18nOption[i18nType]); // 混入国际化配置
+			MeScroll.extend(diyOption, {down:GlobalOption.down, up:GlobalOption.up}); // 混入全局的配置
+			let myOption = JSON.parse(JSON.stringify({'down': vm.down,'up': vm.up})) // 深拷贝,避免对props的影响
+			MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
+
+			// 初始化MeScroll对象
+			vm.mescroll = new MeScroll(myOption);
+			vm.mescroll.viewId = vm.viewId; // 附带id
+			// 挂载语言包
+			vm.mescroll.i18n = i18nOption;
+			// init回调mescroll对象
+			vm.$emit('init', vm.mescroll);
+			
+			// 设置高度
+			const sys = uni.getSystemInfoSync();
+			if(sys.windowTop) vm.windowTop = sys.windowTop;
+			if(sys.windowBottom) vm.windowBottom = sys.windowBottom;
+			if(sys.windowHeight) vm.windowHeight = sys.windowHeight;
+			if(sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
+			// 使down的bottomOffset生效
+			vm.mescroll.setBodyHeight(sys.windowHeight);
+
+			// 因为使用的是scrollview,这里需自定义scrollTo
+			vm.mescroll.resetScrollTo((y, t) => {
+				vm.scrollAnim = (t !== 0); // t为0,则不使用动画过渡
+				if(typeof y === 'string'){
+					// 小程序不支持slot里面的scroll-into-view, 统一使用计算的方式实现
+					vm.getClientInfo(function(rect){
+						let mescrollTop = rect.top // mescroll到顶部的距离
+						let selector;
+						if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
+							selector = '#'+y // 不带#和. 则默认为id选择器
+						}else{
+							selector = y
+							// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
+							if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
+								selector = y.split('>>>')[1].trim()
+							}
+							// #endif
+						}
+						uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
+							if (rect) {
+								let curY = vm.mescroll.getScrollTop()
+								let top = rect.top - mescrollTop
+								top += curY
+								if(!vm.isFixed) top -= vm.numTop
+								vm.scrollTop = curY;
+								vm.$nextTick(function() {
+									vm.scrollTop = top
+								})
+							} else{
+								console.error(selector + ' does not exist');
+							}
+						}).exec()
+					})
+					return;
+				}
+				let curY = vm.mescroll.getScrollTop()
+				if (t === 0 || t === 300) { // 当t使用默认配置的300时,则使用系统自带的动画过渡
+					vm.scrollTop = curY;
+					vm.$nextTick(function() {
+						vm.scrollTop = y
+					})
+				} else {
+					vm.mescroll.getStep(curY, y, step => { // 此写法可支持配置t
+						vm.scrollTop = step
+					}, t)
+				}
+			})
+			
+			// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
+			if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
+				vm.mescroll.optUp.toTop.safearea = vm.safearea;
+			}
+			// 全局配置监听
+			uni.$on("setMescrollGlobalOption", options=>{
+				if(!options) return;
+				let i18nType = options.i18n ? options.i18n.type : null
+				if(i18nType && vm.mescroll.i18n.type != i18nType){
+					vm.mescroll.i18n.type = i18nType
+					mescrollI18n.setType(i18nType)
+					MeScroll.extend(options, vm.mescroll.i18n[i18nType])
+				}
+				if(options.down){
+					let down = MeScroll.extend({}, options.down)
+					vm.mescroll.optDown = MeScroll.extend(down, vm.mescroll.optDown)
+				}
+				if(options.up){
+					let up = MeScroll.extend({}, options.up)
+					vm.mescroll.optUp = MeScroll.extend(up, vm.mescroll.optUp)
+				}
+			})
+		},
+		mounted() {
+			// 设置容器的高度
+			this.setClientHeight()
+		},
+		destroyed() {
+			// 注销全局配置监听
+			uni.$off("setMescrollGlobalOption")
+		}
+	}
+</script>
+
+<style>
+	@import "../../mescroll-uni/mescroll-uni.css";
+	@import "../../mescroll-uni/components/mescroll-down.css";
+	@import "../../mescroll-uni/components/mescroll-up.css";
+	@import "./components/mescroll-down.css";
+</style>

+ 44 - 0
uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/xinlang/components/mescroll-down.css

@@ -0,0 +1,44 @@
+/*下拉刷新--上下箭头*/
+.mescroll-downwarp .downwarp-arrow {
+	display: inline-block;
+	width: 20px;
+	height: 20px;
+	margin: 10px;
+	background-image: url(https://www.mescroll.com/img/xinlang/mescroll-arrow.png);
+	background-size: contain;
+	vertical-align: middle;
+	transition: all 300ms;
+}
+
+/*下拉刷新--旋转进度条*/
+.mescroll-downwarp .downwarp-progress{
+	width: 36px;
+	height: 36px;
+	border: none;
+	margin: auto;
+	background-size: contain;
+	animation: progressRotate 0.6s steps(6, start) infinite;
+}
+@keyframes progressRotate {
+	0% {
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress1.png);
+	}
+	16% {
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress2.png);
+	}
+	32% {
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress3.png);
+	}
+	48% {
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress4.png);
+	}
+	64% {
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress5.png);
+	}
+	80% {
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress6.png);
+	}
+	100% {
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress1.png);
+	}
+}

+ 53 - 0
uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/xinlang/components/mescroll-down.vue

@@ -0,0 +1,53 @@
+<!-- 下拉刷新区域 -->
+<template>
+	<view v-if="mOption.use" class="mescroll-downwarp" :style="{'background':mOption.bgColor,'color':mOption.textColor}">
+		<view class="downwarp-content">
+			<view v-if="isDownLoading" class="downwarp-progress"></view>
+			<view v-else class="downwarp-arrow" :style="{ 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)
+	},
+	computed: {
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
+		mOption() {
+			return this.option || {};
+		},
+		// 是否在加载中
+		isDownLoading() {
+			return this.type === 3;
+		},
+		// 旋转的角度
+		downRotate() {
+			return this.type === 2 ? 'rotate(-180deg)' : 'rotate(0deg)';
+		},
+		// 文本提示
+		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-uni/components/mescroll-down.css';
+@import './mescroll-down.css';
+</style>

+ 32 - 0
uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/xinlang/components/mescroll-up.css

@@ -0,0 +1,32 @@
+/*上拉加载--旋转进度条*/
+.mescroll-upwarp .upwarp-progress {
+	width: 36px;
+	height: 36px;
+	border: none;
+	margin: auto;
+	background-size: contain;
+	animation: progressRotate 0.6s steps(6, start) infinite;
+}
+@keyframes progressRotate {
+	0% {
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress1.png);
+	}
+	16% {
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress2.png);
+	}
+	32% {
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress3.png);
+	}
+	48% {
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress4.png);
+	}
+	64% {
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress5.png);
+	}
+	80% {
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress6.png);
+	}
+	100% {
+		background-image: url(https://www.mescroll.com/img/xinlang/mescroll-progress1.png);
+	}
+}

+ 40 - 0
uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/xinlang/components/mescroll-up.vue

@@ -0,0 +1,40 @@
+<!-- 上拉加载区域 -->
+<template>
+	<view class="mescroll-upwarp" :style="{'background':mOption.bgColor,'color':mOption.textColor}">
+		<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
+		<view v-show="isUpLoading">
+			<view class="upwarp-progress mescroll-rotate"></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(没有更多了,显示END文本提示),3(没有更多了,不显示END文本提示)
+	},
+	computed: {
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
+		mOption() {
+			return this.option || {};
+		},
+		// 加载中
+		isUpLoading() {
+			return this.type === 1;
+		},
+		// 没有更多了
+		isUpNoMore() {
+			return this.type === 2;
+		}
+	}
+};
+</script>
+
+<style>
+@import '../../../mescroll-uni/components/mescroll-up.css';
+@import './mescroll-up.css';
+</style>

+ 380 - 0
uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/xinlang/mescroll-body.vue

@@ -0,0 +1,380 @@
+<template>
+	<view 
+		class="mescroll-body mescroll-render-touch" 
+		:style="{'minHeight':minHeight, 'padding-top': padTop, 'padding-bottom': padBottom}" 
+		:class="{'mescorll-sticky': sticky}"
+		@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"></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 v-if="isDownLoading" class="downwarp-progress"></view>
+					<view v-else class="downwarp-arrow" :style="{ 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 && downLoadType !== 3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
+			<view 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"></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端生效) -->
+		<!-- #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="../../mescroll-uni/wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
+<!-- #endif -->
+
+<!-- app, h5使用renderjs -->
+<!-- #ifdef APP-PLUS || H5 -->
+<script module="renderBiz" lang="renderjs">
+	import renderBiz from '../../mescroll-uni/wxs/renderjs.js';
+	export default {
+		mixins: [renderBiz]
+	}
+</script>
+<!-- #endif -->
+
+<script>
+	import MeScroll from '../../mescroll-uni/mescroll-uni.js';
+	import MescrollTop from '../../mescroll-uni/components/mescroll-top.vue';
+	import WxsMixin from '../../mescroll-uni/wxs/mixins.js';
+	import mescrollI18n from '../../mescroll-uni/mescroll-i18n.js';
+	import GlobalOption from './mescroll-uni-option.js';
+
+	export default {
+		mixins: [WxsMixin],
+		components: {
+			MescrollTop
+		},
+		data() {
+			return {
+				mescroll: null, // mescroll实例
+				downHight: 0, //下拉刷新: 容器高度
+				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, // 上拉加载的参数配置
+			i18n: 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 this.downLoadType === 2 ? 'rotate(-180deg)' : 'rotate(0deg)';
+			},
+			// 文本提示
+			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组件时,此行不可删)
+					},
+					showLoading(mescroll, downHight) {
+						vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+					},
+					beforeEndDownScroll(mescroll){
+						vm.downLoadType = 4; 
+						return mescroll.optDown.beforeEndDelay // 延时结束的时长
+					},
+					endDownScroll() {
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						if(vm.downResetTimer) {clearTimeout(vm.downResetTimer); vm.downResetTimer = null} // 移除重置倒计时
+						vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,避免下次inOffset不及时显示textInOffset
+							if(vm.downLoadType === 4) vm.downLoadType = 0
+						},300)
+					},
+					// 派发下拉刷新的回调
+					callback: function(mescroll) {
+						vm.$emit('down', mescroll);
+					}
+				},
+				// 上拉加载的配置
+				up: {
+					// 显示加载中的回调
+					showLoading() {
+						vm.upLoadType = 1;
+					},
+					// 显示无更多数据的回调
+					showNoMore() {
+						vm.upLoadType = 2;
+					},
+					// 隐藏上拉加载的回调
+					hideUpScroll(mescroll) {
+						vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
+					},
+					// 空布局
+					empty: {
+						onShow(isShow) {
+							// 显示隐藏的回调
+							vm.isShowEmpty = isShow;
+						}
+					},
+					// 回到顶部
+					toTop: {
+						onShow(isShow) {
+							// 显示隐藏的回调
+							vm.isShowToTop = isShow;
+						}
+					},
+					// 派发上拉加载的回调
+					callback: function(mescroll) {
+						vm.$emit('up', mescroll);
+					}
+				}
+			};
+
+			let i18nType = mescrollI18n.getType() // 当前语言类型
+			let i18nOption = {type: i18nType} // 国际化配置
+			MeScroll.extend(i18nOption, vm.i18n) // 具体页面的国际化配置
+			MeScroll.extend(i18nOption, GlobalOption.i18n) // 全局的国际化配置
+			MeScroll.extend(diyOption, i18nOption[i18nType]); // 混入国际化配置
+			MeScroll.extend(diyOption, {down:GlobalOption.down, up:GlobalOption.up}); // 混入全局的配置
+			let myOption = JSON.parse(JSON.stringify({down: vm.down,up: vm.up})); // 深拷贝,避免对props的影响
+			MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
+
+			// 初始化MeScroll对象
+			vm.mescroll = new MeScroll(myOption, true); // 传入true,标记body为滚动区域
+			// 挂载语言包
+			vm.mescroll.i18n = i18nOption;
+			// init回调mescroll对象
+			vm.$emit('init', vm.mescroll);
+
+			// 设置高度
+			const sys = uni.getSystemInfoSync();
+			if (sys.windowHeight) vm.windowHeight = sys.windowHeight;
+			if (sys.windowBottom) vm.windowBottom = sys.windowBottom;
+			if (sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
+			// 使down的bottomOffset生效
+			vm.mescroll.setBodyHeight(sys.windowHeight);
+			
+			// 因为使用的是page的scroll,这里需自定义scrollTo
+			vm.mescroll.resetScrollTo((y, t) => {
+				if(typeof y === 'string'){
+					// 滚动到指定view (y为css选择器)
+					setTimeout(()=>{ // 延时确保view已渲染; 不使用$nextTick
+						let selector;
+						if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
+							selector = '#'+y // 不带#和. 则默认为id选择器
+						}else{
+							selector = y
+							// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
+							if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
+								selector = y.split('>>>')[1].trim()
+							}
+							// #endif
+						}
+						uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
+							if (rect) {
+								let top = rect.top
+								top += vm.mescroll.getScrollTop()
+								uni.pageScrollTo({
+									scrollTop: top,
+									duration: t
+								})
+							} else{
+								console.error(selector + ' does not exist');
+							}
+						}).exec()
+					},30)
+				} else{
+					// 滚动到指定位置 (y必须为数字)
+					uni.pageScrollTo({
+						scrollTop: y,
+						duration: t
+					})
+				}
+			});
+			
+			// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
+			if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
+				vm.mescroll.optUp.toTop.safearea = vm.safearea;
+			}
+			
+			// 全局配置监听
+			uni.$on("setMescrollGlobalOption", options=>{
+				if(!options) return;
+				let i18nType = options.i18n ? options.i18n.type : null
+				if(i18nType && vm.mescroll.i18n.type != i18nType){
+					vm.mescroll.i18n.type = i18nType
+					mescrollI18n.setType(i18nType)
+					MeScroll.extend(options, vm.mescroll.i18n[i18nType])
+				}
+				if(options.down){
+					let down = MeScroll.extend({}, options.down)
+					vm.mescroll.optDown = MeScroll.extend(down, vm.mescroll.optDown)
+				}
+				if(options.up){
+					let up = MeScroll.extend({}, options.up)
+					vm.mescroll.optUp = MeScroll.extend(up, vm.mescroll.optUp)
+				}
+			})
+		},
+		destroyed() {
+			// 注销全局配置监听
+			uni.$off("setMescrollGlobalOption")
+		}
+	};
+</script>
+
+<style>
+	@import "../../mescroll-uni/mescroll-uni.css";
+	@import "../../mescroll-uni/components/mescroll-down.css";
+	@import "../../mescroll-uni/components/mescroll-up.css";
+	@import "./components/mescroll-down.css";
+	@import "./components/mescroll-up.css";
+</style>

+ 64 - 0
uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/xinlang/mescroll-uni-option.js

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

+ 462 - 0
uni_applet/uni_modules/mescroll-uni/components/mescroll-diy/xinlang/mescroll-uni.vue

@@ -0,0 +1,462 @@
+<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"></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 v-if="isDownLoading" class="downwarp-progress"></view>
+							<view v-else class="downwarp-arrow" :style="{ 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 && downLoadType !== 3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
+					<view 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"></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端生效) -->
+				<!-- #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="../../mescroll-uni/wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
+<!-- #endif -->
+
+<!-- app, h5使用renderjs -->
+<!-- #ifdef APP-PLUS || H5 -->
+<script module="renderBiz" lang="renderjs">
+	import renderBiz from '../../mescroll-uni/wxs/renderjs.js';
+	export default {
+		mixins: [renderBiz]
+	}
+</script>
+<!-- #endif -->
+
+<script>
+	import MeScroll from '../../mescroll-uni/mescroll-uni.js';
+	import MescrollTop from '../../mescroll-uni/components/mescroll-top.vue';
+	import WxsMixin from '../../mescroll-uni/wxs/mixins.js';
+	import mescrollI18n from '../../mescroll-uni/mescroll-i18n.js';
+	import GlobalOption from './mescroll-uni-option.js';
+	
+	export default {
+		mixins: [WxsMixin],
+		components: {
+			MescrollTop
+		},
+		data() {
+			return {
+				mescroll: null, // mescroll实例
+				viewId: 'id_' + Math.random().toString(36).substr(2,16), // 随机生成mescroll的id(不能数字开头,否则找不到元素)
+				downHight: 0, //下拉刷新: 容器高度
+				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, // 上拉加载的参数配置
+			i18n: 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
+			},
+			disableScroll: Boolean // 是否禁止滚动
+		},
+		computed: {
+			// 是否使用fixed定位 (当height有值,则不使用)
+			isFixed(){
+				return !this.height && this.fixed
+			},
+			// mescroll的高度
+			scrollHeight(){
+				if (this.isFixed) {
+					return "auto"
+				} else if(this.height){
+					return this.toPx(this.height) + 'px'
+				}else{
+					return "100%"
+				}
+			},
+			// 下拉布局往下偏移的距离 (px)
+			numTop() {
+				return this.toPx(this.top)
+			},
+			fixedTop() {
+				return this.isFixed ? (this.numTop + this.windowTop) + 'px' : 0
+			},
+			padTop() {
+				return !this.isFixed ? this.numTop + 'px' : 0
+			},
+			// 上拉布局往上偏移 (px)
+			numBottom() {
+				return this.toPx(this.bottom)
+			},
+			fixedBottom() {
+				return this.isFixed ? (this.numBottom + this.windowBottom) + 'px' : 0
+			},
+			padBottom() {
+				return !this.isFixed ? this.numBottom + 'px' : 0
+			},
+			// 是否为重置下拉的状态
+			isDownReset(){
+				return this.downLoadType===3 || this.downLoadType===4
+			},
+			// 过渡
+			transition() {
+				return this.isDownReset ? 'transform 300ms' : ''
+			},
+			translateY() {
+				return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : '' // transform会使fixed失效,需注意把fixed元素写在mescroll之外
+			},
+			// 列表是否可滑动
+			scrollable(){
+				if(this.disableScroll) return false
+				return this.downLoadType===0 || this.isDownReset
+			},
+			// 是否在加载中
+			isDownLoading() {
+				return this.downLoadType === 3;
+			},
+			// 旋转的角度
+			downRotate() {
+				return this.downLoadType === 2 ? 'rotate(-180deg)' : 'rotate(0deg)';
+			},
+			// 文本提示
+			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组件时,此行不可删)
+					},
+					showLoading(mescroll, downHight) {
+						vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+					},
+					beforeEndDownScroll(mescroll){
+						vm.downLoadType = 4; 
+						return mescroll.optDown.beforeEndDelay // 延时结束的时长
+					},
+					endDownScroll() {
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						vm.downResetTimer && clearTimeout(vm.downResetTimer)
+						vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,以便置空this.transition,避免iOS小程序列表渲染不完整
+							if(vm.downLoadType===4) vm.downLoadType = 0
+						},300)
+					},
+					// 派发下拉刷新的回调
+					callback: function(mescroll) {
+						vm.$emit('down', mescroll)
+					}
+				},
+				// 上拉加载的配置
+				up: {
+					// 显示加载中的回调
+					showLoading() {
+						vm.upLoadType = 1;
+					},
+					// 显示无更多数据的回调
+					showNoMore() {
+						vm.upLoadType = 2;
+					},
+					// 隐藏上拉加载的回调
+					hideUpScroll(mescroll) {
+						vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
+					},
+					// 空布局
+					empty: {
+						onShow(isShow) { // 显示隐藏的回调
+							vm.isShowEmpty = isShow;
+						}
+					},
+					// 回到顶部
+					toTop: {
+						onShow(isShow) { // 显示隐藏的回调
+							vm.isShowToTop = isShow;
+						}
+					},
+					// 派发上拉加载的回调
+					callback: function(mescroll) {
+						vm.$emit('up', mescroll);
+						// 更新容器的高度 (多mescroll的情况)
+						vm.setClientHeight()
+					}
+				}
+			}
+
+			let i18nType = mescrollI18n.getType() // 当前语言类型
+			let i18nOption = {type: i18nType} // 国际化配置
+			MeScroll.extend(i18nOption, vm.i18n) // 具体页面的国际化配置
+			MeScroll.extend(i18nOption, GlobalOption.i18n) // 全局的国际化配置
+			MeScroll.extend(diyOption, i18nOption[i18nType]); // 混入国际化配置
+			MeScroll.extend(diyOption, {down:GlobalOption.down, up:GlobalOption.up}); // 混入全局的配置
+			let myOption = JSON.parse(JSON.stringify({
+				'down': vm.down,
+				'up': vm.up
+			})) // 深拷贝,避免对props的影响
+			MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
+
+			// 初始化MeScroll对象
+			vm.mescroll = new MeScroll(myOption);
+			vm.mescroll.viewId = vm.viewId; // 附带id
+			// 挂载语言包
+			vm.mescroll.i18n = i18nOption;
+			// init回调mescroll对象
+			vm.$emit('init', vm.mescroll);
+			
+			// 设置高度
+			const sys = uni.getSystemInfoSync();
+			if(sys.windowTop) vm.windowTop = sys.windowTop;
+			if(sys.windowBottom) vm.windowBottom = sys.windowBottom;
+			if(sys.windowHeight) vm.windowHeight = sys.windowHeight;
+			if(sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
+			// 使down的bottomOffset生效
+			vm.mescroll.setBodyHeight(sys.windowHeight);
+
+			// 因为使用的是scrollview,这里需自定义scrollTo
+			vm.mescroll.resetScrollTo((y, t) => {
+				vm.scrollAnim = (t !== 0); // t为0,则不使用动画过渡
+				if(typeof y === 'string'){
+					// 小程序不支持slot里面的scroll-into-view, 统一使用计算的方式实现
+					vm.getClientInfo(function(rect){
+						let mescrollTop = rect.top // mescroll到顶部的距离
+						let selector;
+						if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
+							selector = '#'+y // 不带#和. 则默认为id选择器
+						}else{
+							selector = y
+							// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
+							if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
+								selector = y.split('>>>')[1].trim()
+							}
+							// #endif
+						}
+						uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
+							if (rect) {
+								let curY = vm.mescroll.getScrollTop()
+								let top = rect.top - mescrollTop
+								top += curY
+								if(!vm.isFixed) top -= vm.numTop
+								vm.scrollTop = curY;
+								vm.$nextTick(function() {
+									vm.scrollTop = top
+								})
+							} else{
+								console.error(selector + ' does not exist');
+							}
+						}).exec()
+					})
+					return;
+				}
+				let curY = vm.mescroll.getScrollTop()
+				if (t === 0 || t === 300) { // 当t使用默认配置的300时,则使用系统自带的动画过渡
+					vm.scrollTop = curY;
+					vm.$nextTick(function() {
+						vm.scrollTop = y
+					})
+				} else {
+					vm.mescroll.getStep(curY, y, step => { // 此写法可支持配置t
+						vm.scrollTop = step
+					}, t)
+				}
+			})
+			
+			// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
+			if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
+				vm.mescroll.optUp.toTop.safearea = vm.safearea;
+			}
+			
+			// 全局配置监听
+			uni.$on("setMescrollGlobalOption", options=>{
+				if(!options) return;
+				let i18nType = options.i18n ? options.i18n.type : null
+				if(i18nType && vm.mescroll.i18n.type != i18nType){
+					vm.mescroll.i18n.type = i18nType
+					mescrollI18n.setType(i18nType)
+					MeScroll.extend(options, vm.mescroll.i18n[i18nType])
+				}
+				if(options.down){
+					let down = MeScroll.extend({}, options.down)
+					vm.mescroll.optDown = MeScroll.extend(down, vm.mescroll.optDown)
+				}
+				if(options.up){
+					let up = MeScroll.extend({}, options.up)
+					vm.mescroll.optUp = MeScroll.extend(up, vm.mescroll.optUp)
+				}
+			})
+		},
+		mounted() {
+			// 设置容器的高度
+			this.setClientHeight()
+		},
+		destroyed() {
+			// 注销全局配置监听
+			uni.$off("setMescrollGlobalOption")
+		}
+	}
+</script>
+
+<style>
+	@import "../../mescroll-uni/mescroll-uni.css";
+	@import "../../mescroll-uni/components/mescroll-down.css";
+	@import "../../mescroll-uni/components/mescroll-up.css";
+	@import "./components/mescroll-down.css";
+	@import "./components/mescroll-up.css";
+</style>

+ 116 - 0
uni_applet/uni_modules/mescroll-uni/components/mescroll-empty/mescroll-empty.vue

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

部分文件因文件數量過多而無法顯示