zhang 1 år sedan
incheckning
70871020a3
100 ändrade filer med 15297 tillägg och 0 borttagningar
  1. 20 0
      .hbuilderx/launch.json
  2. 279 0
      App.vue
  3. 0 0
      README.md
  4. 276 0
      api/activity.js
  5. 66 0
      api/functionalUnit.js
  6. 29 0
      api/game.js
  7. 29 0
      api/index.js
  8. 64 0
      api/login.js
  9. 147 0
      api/order.js
  10. 194 0
      api/product.js
  11. 46 0
      api/set.js
  12. 199 0
      api/user.js
  13. 149 0
      api/wallet.js
  14. 37 0
      api/wx.js
  15. 57 0
      components/Loading/index.vue
  16. 120 0
      components/countDown/index.vue
  17. 18 0
      components/empty.vue
  18. 36 0
      components/emptyPage.vue
  19. 118 0
      components/home/index.vue
  20. 630 0
      components/jyf-parser/jyf-parser.vue
  21. 97 0
      components/jyf-parser/libs/CssHandler.js
  22. 535 0
      components/jyf-parser/libs/MpHtmlParser.js
  23. 80 0
      components/jyf-parser/libs/config.js
  24. 22 0
      components/jyf-parser/libs/handler.wxs
  25. 501 0
      components/jyf-parser/libs/trees.vue
  26. 421 0
      components/newlist/nowList.vue
  27. 68 0
      components/returnButton.vue
  28. 246 0
      components/seckill/seckill.vue
  29. 196 0
      components/share.vue
  30. 218 0
      components/ss-calendar/ss-calendar.vue
  31. 1201 0
      components/tki-qrcode/qrcode.js
  32. 210 0
      components/tki-qrcode/tki-qrcode.vue
  33. 122 0
      components/uni-badge/uni-badge.vue
  34. 182 0
      components/uni-countdown/uni-countdown.vue
  35. 188 0
      components/uni-countdown/uni-countdowns.vue
  36. 124 0
      components/uni-fav/uni-fav.vue
  37. 96 0
      components/uni-icons/icons.js
  38. 10 0
      components/uni-icons/uni-icons.vue
  39. 230 0
      components/uni-list-item/uni-list-item.vue
  40. 68 0
      components/uni-list/uni-list.vue
  41. 65 0
      components/uni-list/uni-refresh.vue
  42. 87 0
      components/uni-list/uni-refresh.wxs
  43. 194 0
      components/uni-load-more/uni-load-more.vue
  44. 396 0
      components/uni-notice-bar/uni-notice-bar.vue
  45. 198 0
      components/uni-number-box.vue
  46. 243 0
      components/uni-popup/uni-popup-dialog.vue
  47. 116 0
      components/uni-popup/uni-popup-message.vue
  48. 263 0
      components/uni-popup/uni-popup-ori.vue
  49. 282 0
      components/uni-popup/uni-popup-share.vue
  50. 263 0
      components/uni-popup/uni-popup.vue
  51. 141 0
      components/uni-rate/uni-rate.vue
  52. 244 0
      components/uni-steps/uni-steps.vue
  53. 279 0
      components/uni-transition/uni-transition.vue
  54. 226 0
      components/upload-images.vue
  55. 1356 0
      components/wangding-pickerAddress/data.js
  56. 103 0
      components/wangding-pickerAddress/wangding-pickerAddress.vue
  57. 18 0
      config/app.js
  58. 32 0
      config/cache.js
  59. 39 0
      libs/log.js
  60. 84 0
      libs/login.js
  61. 253 0
      libs/wechat.js
  62. 44 0
      main.js
  63. 87 0
      manifest.json
  64. 34 0
      node_modules/.package-lock.json
  65. 361 0
      node_modules/echarts/KEYS
  66. 222 0
      node_modules/echarts/LICENSE
  67. 5 0
      node_modules/echarts/NOTICE
  68. 98 0
      node_modules/echarts/README.md
  69. BIN
      node_modules/echarts/asset/logo.png
  70. 183 0
      node_modules/echarts/build/addHeader.js
  71. 114 0
      node_modules/echarts/build/build-i18n.js
  72. 210 0
      node_modules/echarts/build/build.js
  73. 176 0
      node_modules/echarts/build/config.js
  74. 67 0
      node_modules/echarts/build/dev-fast.js
  75. 30 0
      node_modules/echarts/build/nightly/post.js
  76. 72 0
      node_modules/echarts/build/nightly/prepare.js
  77. 428 0
      node_modules/echarts/build/pre-publish.js
  78. 231 0
      node_modules/echarts/build/preamble.js
  79. 164 0
      node_modules/echarts/build/source-release/prepareReleaseMaterials.js
  80. 26 0
      node_modules/echarts/build/source-release/template/announce-release.tpl
  81. 44 0
      node_modules/echarts/build/source-release/template/vote-release.tpl
  82. 23 0
      node_modules/echarts/build/source-release/template/vote-result.tpl
  83. 20 0
      node_modules/echarts/build/template/charts.d.ts
  84. 27 0
      node_modules/echarts/build/template/charts.js
  85. 20 0
      node_modules/echarts/build/template/components.d.ts
  86. 20 0
      node_modules/echarts/build/template/components.js
  87. 20 0
      node_modules/echarts/build/template/core.d.ts
  88. 20 0
      node_modules/echarts/build/template/core.js
  89. 20 0
      node_modules/echarts/build/template/features.d.ts
  90. 20 0
      node_modules/echarts/build/template/features.js
  91. 20 0
      node_modules/echarts/build/template/option.d.ts
  92. 20 0
      node_modules/echarts/build/template/renderers.d.ts
  93. 20 0
      node_modules/echarts/build/template/renderers.js
  94. 75 0
      node_modules/echarts/build/testDts.js
  95. 58 0
      node_modules/echarts/build/transform-dev.js
  96. 20 0
      node_modules/echarts/charts.d.ts
  97. 27 0
      node_modules/echarts/charts.js
  98. 20 0
      node_modules/echarts/components.d.ts
  99. 20 0
      node_modules/echarts/components.js
  100. 20 0
      node_modules/echarts/core.d.ts

+ 20 - 0
.hbuilderx/launch.json

@@ -0,0 +1,20 @@
+{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
+  // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
+    "version": "0.0",
+    "configurations": [{
+     	"default" : 
+     	{
+     		"launchtype" : "remote"
+     	},
+     	"h5" : 
+     	{
+     		"launchtype" : "remote"
+     	},
+     	"mp-weixin" : 
+     	{
+     		"launchtype" : "remote"
+     	},
+     	"type" : "uniCloud"
+     }
+    ]
+}

+ 279 - 0
App.vue

@@ -0,0 +1,279 @@
+<script>
+/**
+ * vuex管理登陆状态,具体可以参考官方登陆模板示例
+ */
+import { mapMutations } from 'vuex';
+// #ifdef H5
+import { weixindata, setRouter } from './utils/wxAuthorized';
+// #endif
+// #ifdef APP-PLUS
+import { getUpApp } from './utils/upApp.js';
+// #endif
+export default {
+	data() {
+		return {
+			/* 保存微信信息 */
+			appData: {}
+		};
+	},
+	methods: {
+		...mapMutations('user', ['setUserInfo', 'login', 'hasLogin'])
+	},
+	onLaunch: function(urlObj) {
+		let obj = this;
+		// 加载缓存中的用户信息
+		let userInfo = uni.getStorageSync('userInfo') || '';
+		// 判断是否拥有用户信息
+		if (userInfo.uid) {
+			//更新登陆状态
+			uni.getStorage({
+				key: 'userInfo',
+				success: res => {
+					obj.setUserInfo(res.data);
+					obj.login(res.data);
+				}
+			});
+		}
+		// #ifdef H5
+		// 保存路由对象
+		setRouter(this.$router);
+		//判断是否已经缓存浏览器
+		let bool = uni.getStorageSync('weichatBrowser') || '';
+		if (bool === '') {
+			//判断是否为微信浏览
+			bool = navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger';
+			// 保存当前是否为微信内核浏览器
+			uni.setStorageSync('weichatBrowser', bool);
+		}
+		if (bool) {
+			// 加载微信信息
+			weixindata();
+		}
+		// #endif
+		// #ifdef APP-PLUS
+		// 判断是否升级
+		getUpApp();
+		// 获取当前运行系统
+		let system = uni.getStorageSync('platform') || '';
+		if (!system) {
+			uni.setStorage({
+				key: 'platform',
+				data: uni.getSystemInfoSync().platform
+			});
+		}
+		// #endif
+	},
+	onShow: function() {
+		// 加载拦截
+		// console.log('App Show');
+	},
+	onHide: function() {
+		// console.log('App Hide');
+	}
+};
+</script>
+
+<style lang="scss">
+/*全局公共样式和字体图标*/
+@import '/static/css/cmy.css';
+view,
+scroll-view,
+swiper,
+swiper-item,
+cover-view,
+cover-image,
+icon,
+text,
+rich-text,
+progress,
+button,
+checkbox,
+form,
+input,
+label,
+radio,
+slider,
+switch,
+textarea,
+navigator,
+audio,
+camera,
+image,
+video {
+	box-sizing: border-box;
+}
+/* 骨架屏替代方案 */
+.Skeleton {
+	background: #f3f3f3;
+	padding: 20rpx 0;
+	border-radius: 8rpx;
+}
+
+/* 图片载入替代方案 */
+.image-wrapper {
+	font-size: 0;
+	background: #f3f3f3;
+	border-radius: 4px;
+	image {
+		width: 100%;
+		height: 100%;
+		transition: 0.6s;
+		opacity: 0;
+		&.loaded {
+			opacity: 1;
+		}
+	}
+}
+
+// 设置富文本中图片最大宽度
+uni-rich-text img {
+	max-width: 100% !important;
+}
+/*边框*/
+.b-b:after,
+.b-t:after {
+	position: absolute;
+	z-index: 3;
+	left: 0;
+	right: 0;
+	height: 0;
+	content: '';
+	transform: scaleY(0.5);
+	border-bottom: 1px solid $border-color-base;
+}
+
+.b-b:after {
+	bottom: 0;
+}
+
+.b-t:after {
+	top: 0;
+}
+
+/* button样式改写 */
+uni-button,
+button {
+	height: 80rpx;
+	line-height: 80rpx;
+	font-size: $font-lg + 2rpx;
+	font-weight: normal;
+
+	&.no-border:before,
+	&.no-border:after {
+		border: 0;
+	}
+}
+
+uni-button[type='default'],
+button[type='default'] {
+	color: $font-color-dark;
+}
+
+/* input 样式 */
+.input-placeholder {
+	color: #999999;
+}
+
+.placeholder {
+	color: #999999;
+}
+// 边距样式
+@for $i from 1 to 4 {
+	.margin-l-#{$i * 10} {
+		margin-left: $i * 10rpx !important;
+	}
+	.margin-r-#{$i * 10} {
+		margin-right: $i * 10rpx !important;
+	}
+	.margin-t-#{$i * 10} {
+		margin-top: $i * 10rpx !important;
+	}
+	.margin-b-#{$i * 10} {
+		margin-bottom: $i * 10rpx !important;
+	}
+	.margin-#{$i * 10} {
+		margin: $i * 10rpx !important;
+	}
+	.margin-v-#{$i * 10} {
+		margin-top: $i * 10rpx !important;
+		margin-bottom: $i * 10rpx !important;
+	}
+	.margin-c-#{$i * 10} {
+		margin-left: $i * 10rpx !important;
+		margin-right: $i * 10rpx !important;
+	}
+	.padding-l-#{$i * 10} {
+		padding-left: $i * 10rpx !important;
+	}
+	.padding-r-#{$i * 10} {
+		padding-right: $i * 10rpx !important;
+	}
+	.padding-t-#{$i * 10} {
+		padding-top: $i * 10rpx !important;
+	}
+	.padding-b-#{$i * 10} {
+		padding-bottom: $i * 10rpx !important;
+	}
+	.padding-#{$i * 10} {
+		padding: $i * 10rpx !important;
+	}
+	.padding-v-#{$i * 10} {
+		padding-top: $i * 10rpx !important;
+		padding-bottom: $i * 10rpx !important;
+	}
+	.padding-c-#{$i * 10} {
+		padding-left: $i * 10rpx !important;
+		padding-right: $i * 10rpx !important;
+	}
+}
+// 字体大小
+.font-size-sm {
+	font-size: $font-sm;
+}
+.font-size-base {
+	font-size: $font-base;
+}
+.font-size-lg {
+	font-size: $font-lg;
+}
+// 字体颜色
+.font-color-yellow {
+	color: $color-yellow;
+}
+.font-color-gray {
+	color: $color-gray;
+}
+.font-color-red {
+	color: $color-red;
+}
+// 边框颜色
+.border-color-yellow {
+	border: 1rpx solid $color-yellow;
+}
+
+// 修改默认背景颜色
+uni-page-wrapper {
+	background-color: $page-color-base;
+}
+page {
+	background-color: $page-color-base;
+	// 设置默认字体
+	font-family: PingFang SC, STHeitiSC-Light, Helvetica-Light, arial, sans-serif, Droid Sans Fallback;
+}
+	/*边框*/
+	.b-b:after,
+	.b-t:after {
+		position: absolute;
+		z-index: 3;
+		left: 0;
+		right: 0;
+		height: 0;
+		content: '';
+		transform: scaleY(0.5);
+		border-bottom: 1px solid $border-color-base;
+	}
+
+	.b-b:after {
+		bottom: 0;
+	}
+</style>

+ 0 - 0
README.md


+ 276 - 0
api/activity.js

@@ -0,0 +1,276 @@
+import request from "@/utils/request.js";
+/**
+ * 
+ * 所有活动接口 包括:拼团,砍价,秒杀
+ * 
+*/
+// 砍价产品详情
+export function getBargainDetail(data,id) {
+	return request({
+		url: '/api/bargain/detail/' + id,
+		method: 'get',
+		data
+	});
+}
+
+/**
+ * 砍价产品详情
+ */
+// export function getBargainDetail(id) {
+//   return request.get("bargain/detail/" + id);
+// }
+
+//砍价 砍价帮总人数、剩余金额、进度条、已经砍掉的价格
+export function postBargainHelpCount(data) {
+	return request({
+		url: '/api/bargain/help/count',
+		method: 'post',
+		data
+	});
+}
+
+/**
+ * 砍价 砍价帮总人数、剩余金额、进度条、已经砍掉的价格
+ */
+// export function postBargainHelpCount(data) {
+//   return request.post("bargain/help/count", data);
+// }
+
+//砍价 开启砍价用户信息
+export function postBargainStartUser(data) {
+	return request({
+		url: '/api/bargain/start/user',
+		method: 'post',
+		data
+	});
+}
+
+/**
+ * 砍价 开启砍价用户信息
+ */
+// export function postBargainStartUser(data) {
+//   return request.post("bargain/start/user", data);
+// }
+
+//砍价开启
+export function postBargainStart(data) {
+	return request({
+		url: '/api/bargain/start',
+		method: 'post',
+		data
+	});
+}
+
+/**
+ * 砍价开启
+ */
+// export function postBargainStart(bargainId) {
+//   return request.post("bargain/start", { bargainId: bargainId});
+// }
+
+// 砍价 砍掉金额
+export function postBargainHelpPrice(data) {
+	return request({
+		url: '/api/bargain/help/price',
+		method: 'post',
+		data
+	});
+}
+
+/**
+ * 砍价 砍掉金额
+ */
+// export function postBargainHelpPrice(data) {
+//   return request.post("bargain/help/price", data);
+// }
+
+// 砍价 帮助好友砍价
+export function postBargainHelp(data) {
+	return request({
+		url: '/api/bargain/help',
+		method: 'post',
+		data
+	});
+}
+
+/**
+ * 砍价 帮助好友砍价
+ */
+// export function postBargainHelp(data) {
+//   return request.post("bargain/help", data);
+// }
+
+// 砍价 砍价帮
+export function postBargainHelpList(data) {
+	return request({
+		url: '/api/bargain/help/list',
+		method: 'post',
+		data
+	});
+}
+
+
+/**
+ * 砍价 砍价帮
+ */
+// export function postBargainHelpList(data) {
+//   return request.post("bargain/help/list", data);
+// }
+
+// 砍价 观看/分享/参与次数
+export function postBargainShare(data) {
+	return request({
+		url: '/api/bargain/share',
+		method: 'post',
+		data
+	});
+}
+
+/**
+ * 砍价 观看/分享/参与次数
+ */
+// export function postBargainShare(bargainId) {
+//   return request.post("bargain/share", { bargainId: bargainId});
+// }
+
+// 砍价列表(已参与)
+export function getBargainUserList(data) {
+	return request({
+		url: '/api/bargain/user/list',
+		method: 'get',
+		data
+	});
+}
+
+/**
+ * 
+ * 砍价列表(已参与)
+ * @param object data
+*/
+// export function getBargainUserList(data){
+//   return request.get('bargain/user/list',data);
+// }
+
+// 砍价取消
+export function getBargainUserCancel(data) {
+	return request({
+		url: '/api/bargain/user/cancel',
+		method: 'post',
+		data
+	});
+}
+
+/**
+ * 砍价取消
+ */
+// export function getBargainUserCancel(data) {
+//   return request.post("/bargain/user/cancel", data);
+// }
+
+
+
+
+
+/**
+ * 拼团列表
+ * 
+*/
+export function getCombinationList(data) {
+  return request.get('combination/list', data,{noAuth:true});
+}
+
+/**
+ * 拼团详情
+ * 
+*/
+export function getCombinationDetail(id) {
+  return request.get('combination/detail/'+id);
+}
+
+/**
+ * 拼团 开团
+ */
+export function getCombinationPink(id) {
+  return request.get("combination/pink/" + id);
+}
+
+/**
+ * 拼团 取消开团
+ */
+export function postCombinationRemove(data) {
+  return request.post("combination/remove",data);
+}
+
+/**
+ * 砍价列表
+ */
+export function getBargainList(data) {
+  return request.get("bargain/list", data,{noAuth:true});
+}
+
+
+/**
+ * 
+ * 取消砍价
+ * @param int bargainId
+*/
+export function bargainUserCancel(bargainId){
+  return request.post('bargain/user/cancel', { bargainId: bargainId})
+}
+
+/**
+ * 秒杀产品时间区间
+ * 
+*/
+export function getSeckillIndexTime(){
+  return request.get('seckill/index',{},{noAuth:true});
+}
+
+/**
+ * 秒杀产品列表
+ * @param int time
+ * @param object data
+*/
+export function getSeckillList(time,data){
+  return request.get('seckill/list/'+time,data,{noAuth:true});
+}
+
+/**
+ * 秒杀产品详情
+ * @param int id
+*/
+export function getSeckillDetail(id){
+  return request.get('seckill/detail/'+id);
+}
+
+/**
+ * 砍价海报
+ * @param object data
+ * 
+*/
+export function getBargainPoster(data){
+  return request.post('bargain/poster',data)
+}
+
+/**
+ * 拼团海报
+ * @param object data
+ * 
+*/
+export function getCombinationPoster(data){
+  return request.post('combination/poster',data)
+}
+
+/**
+ * 获取秒杀小程序二维码
+ */
+export function seckillCode(id,data) {
+  return request.get("seckill/code/"+id,data);
+}
+
+/**
+ * 获取拼团小程序二维码
+ */
+export function scombinationCode(id) {
+  return request.get("combination/code/"+id);
+}

+ 66 - 0
api/functionalUnit.js

@@ -0,0 +1,66 @@
+import request from '@/utils/request'
+
+//获取优惠券列表
+export function getCouponsList(data,types) {
+	//优惠券状态 0全部 1未使用 2已使用
+	return request({
+		url: '/api/coupons/user/'+types,
+		method: 'get',
+		data
+	});
+}
+
+//领取优惠券
+export function setCoupons(data) {
+	return request({
+		url: '/api/coupon/receive',
+		method: 'post',
+		data
+	});
+}
+
+//获取可使用优惠券
+export function couponsOrder(data,price) {
+	return request({
+		url: '/api/coupons/order/'+price,
+		method: 'get',
+		data
+	});
+}
+
+
+//	积分列表
+export function integrallist(data) {
+	return request({
+		url: '/api/integral/list',
+		method: 'get',
+		data
+	});
+}
+
+
+// 获取签到列表
+export function signList(data) {
+	return request({
+		url: '/api/sign/list',
+		method: 'get',
+		data
+	});
+}
+// 点击签到
+export function integral(data) {
+	return request({
+		url: '/api/sign/integral',
+		method: 'post',
+		data
+	});
+}
+
+// 签到信息
+export function signUser(data) {
+	return request({
+		url: '/api/sign/user',
+		method: 'post',
+		data
+	});
+}

+ 29 - 0
api/game.js

@@ -0,0 +1,29 @@
+import request from '@/utils/request'
+
+
+// 互娱
+export function getGame(data) {
+	return request({
+		url: '/api/game/room/1',
+		method: 'get',
+		data
+	});
+}
+
+
+export function test(data) {
+	return request({
+		url: '/api/test',
+		method: 'get',
+		data
+	});
+}
+
+// 互娱  k线
+export default function SZlineData(data) {
+	return request({
+		url: '/api/kline/0',
+		method: 'get',
+		data
+	});
+}

+ 29 - 0
api/index.js

@@ -0,0 +1,29 @@
+import request from '@/utils/request'
+// 新品首发
+export function groom(data, id) {
+	return request({
+		url: '/api/groom/list/' + id,
+		method: 'get',
+		data
+	});
+}
+
+// 获取首页信息
+export function loadIndexs(data) {
+	return request({
+		url: '/api/index',
+		method: 'get',
+		data
+	});
+}
+
+export function enroll(data) {
+	return request({
+		url: '/api/enroll',
+		method: 'post',
+		data
+	});
+}
+
+
+

+ 64 - 0
api/login.js

@@ -0,0 +1,64 @@
+import request from '@/utils/request'
+
+// 登录
+export function login(data) {
+	return request({
+		url: '/api/login',
+		method: 'post',
+		data
+	});
+}
+// 注册
+export function register(data) {
+	return request({
+		url: '/api/register',
+		method: 'post',
+		data
+	});
+}
+// 验证码
+export function verify(data) {
+	// type=register为注册
+	// type=login为登录
+	return request({
+		url: '/api/register/verify',
+		method: 'post',
+		data
+	});
+}
+
+// 获取用户信息
+export function getUserInfo(data) {
+	return request({
+		url: '/api/userinfo',
+		method: 'get',
+		data
+	});
+}
+
+// 短信登录
+export function loginMobile(data) {
+	return request({
+		url: '/api/login/mobile',
+		method: 'post',
+		data
+	});
+}
+// #ifdef APP-PLUS
+// 微信授权登录
+export function loginWx(data) {
+	return request({
+		url: '/api/wechat/appauth',
+		method: 'get',
+		data
+	});
+}
+// #endif
+//绑定手机号
+export function bangding(data) {
+	return request({
+		url: '/api/binding',
+		method: 'POST',
+		data
+	});
+}

+ 147 - 0
api/order.js

@@ -0,0 +1,147 @@
+import request from '@/utils/request'
+import {upFilse} from '@/utils/request'
+// 订单确认
+export function confirm(data) {
+	return request({
+		url: '/api/order/confirm',
+		method: 'post',
+		data
+	});
+}
+//获取可使用优惠券
+export function couponsOrder(data,price) {
+	return request({
+		url: '/api/coupons/order/'+price,
+		method: 'get',
+		data
+	});
+}
+//获取优惠券列表
+export function getCouponsList(data,types) {
+	//优惠券状态 0全部 1未使用 2已使用
+	return request({
+		url: '/api/coupons/user/'+types,
+		method: 'get',
+		data
+	});
+}
+//提交评论
+export function order_comment(data) {
+	return request({
+		url: '/api/order/comment',
+		method: 'post',
+		data
+	});
+}
+//订单产品信息
+export function product(data) {
+	return request({
+		url: '/api/order/product',
+		method: 'post',
+		data
+	});
+}
+// 快递查询
+export function express_query(data) {
+	return request({
+		url: '/api/order/express_query',
+		method: 'get',
+		data
+	});
+}
+//上传图片
+export function upload(data) {
+	return upFilse({
+		url: '/api/upload/image',
+		method: 'post',
+		data
+	});
+}
+// 订单列表
+export function orderList(data) {
+	return request({
+		url: '/api/order/list',
+		method: 'get',
+		data
+	});
+}
+// 订单详细
+export function orderDetail(data,orderid) {
+	return request({
+		url: '/api/order/detail/'+orderid,
+		method: 'get',
+		data
+	});
+}
+
+// 取消订单
+export function orderCancel(data) {
+	return request({
+		url: '/api/order/cancel',
+		method: 'post',
+		data
+	});
+}
+
+// 删除订单
+export function orderDel(data) {
+	return request({
+		url: '/api/order/del',
+		method: 'get',
+		data
+	});
+}
+// 申请退款
+export function refund(data) {
+	return request({
+		url: '/api/order/refund/verify',
+		method: 'post',
+		data
+	});
+}
+// 退款理由列表
+export function refundReason(data) {
+	return request({
+		url: '/api/order/refund/reason',
+		method: 'get',
+		data
+	});
+}
+
+// 确认收货
+export function orderTake(data) {
+	return request({
+		url: '/api/order/take',
+		method: 'post',
+		data
+	});
+}
+
+// 订单支付
+export function orderPay(data) {
+	return request({
+		url: '/api/order/pay',
+		method: 'post',
+		data
+	});
+}
+
+// 创建订单
+export function createOrderkey(data,key) {
+	return request({
+		url: '/api/order/create/'+key,
+		method: 'post',
+		data
+	});
+}
+
+// 统计订单金额
+export function computedOrderkey(data) {
+	return request({
+		url: '/api/order/computed/'+data.orderkey,
+		method: 'post',
+		data
+	});
+}
+
+

+ 194 - 0
api/product.js

@@ -0,0 +1,194 @@
+import request from '@/utils/request'
+
+// 获取商品列表
+export function getProducts(data) {
+	// 	{
+	// 参数名称	是否必须	示例	备注
+	// sid			否			二级分类编号
+	// cid			否			一级分类编号(!)
+	// keyword		否			搜索
+	// priceOrder	否			价格排序
+	// salesOrder	否			销量排序
+	// news			否			是否新品
+	// page			否			分页参数起始值
+	// limit		否			分页数步长值
+	// }
+	return request({
+		url: '/api/products',
+		method: 'get',
+		data
+	});
+}
+// 获取商品详情
+export function goodsDetail(data, id) {
+	return request({
+		url: '/api/product/detail/' + id,
+		method: 'get',
+		data
+	});
+}
+
+// 砍价列表
+export function getBargainList(data) {
+	return request({
+		url: '/api/bargain/list',
+		method: 'get',
+		data
+	});
+}
+// 加入购物车
+export function cartAdd(data) {
+	return request({
+		url: '/api/cart/add',
+		method: 'post',
+		data
+	});
+}
+
+// 收藏商品
+export function collectAdd(data) {
+	return request({
+		url: '/api/collect/add',
+		method: 'post',
+		data
+	});
+}
+
+// 取消收藏商品
+export function collectDel(data) {
+	return request({
+		url: '/api/collect/del',
+		method: 'post',
+		data
+	});
+}
+
+// 获取搜搜关键字
+export function searchKeyword(data) {
+	return request({
+		url: '/api/search/keyword',
+		method: 'get',
+		data
+	});
+}
+
+// 获取热门分类信息
+export function groomList(data, type) {
+	// 获取产品类型 1 精品推荐 2 热门榜单 3首发新品 4促销单品
+	return request({
+		url: '/api/groom/list/' + type,
+		method: 'get',
+		data
+	});
+}
+
+
+// 获取秒杀商品详细
+export function seckillGoods(data, id) {
+	return request({
+		url: '/api/seckill/detail/' + id,
+		method: 'get',
+		data
+	});
+}
+
+// 获取拼团商品详细
+export function groupGoods(data, id) {
+	return request({
+		url: '/api/combination/detail/' + id,
+		method: 'get',
+		data
+	});
+}
+
+
+// 获取商品分类
+export function getCategoryList(data) {
+	return request({
+		url: '/api/category',
+		method: 'get',
+		data
+	});
+}
+
+
+// 获取拼团列表
+export function getCombinationList(data) {
+	return request({
+		url: '/api/combination/list',
+		method: 'get',
+		data
+	});
+}
+
+//取消拼团
+export function getCombinationLisRemove(data, id) {
+	return request({
+		url: '/api/combination/remove' + id,
+		method: 'get',
+		data
+	});
+}
+
+//拼团开团页面数据
+export function getCombinationLisPink(data, id) {
+	return request({
+		url: '/api/combination/pink/' + id,
+		method: 'get',
+		data
+	});
+}
+
+// 获取拼团海报
+export function getCombinationPoster(data) {
+	return request({
+		url: '/api/combination/poster',
+		method: 'post',
+		data
+	});
+}
+
+
+// 获取秒杀分类
+export function getSeckillClass(data) {
+	return request({
+		url: '/api/seckill/index',
+		method: 'get',
+		data
+	});
+}
+// 获取秒杀列表
+export function getSeckillList(data, id) {
+	return request({
+		url: '/api/seckill/list/' + id,
+		method: 'get',
+		data
+	});
+}
+
+
+// 产品评价数量和好评度
+export function reply_config(data, id) {
+	return request({
+		url: '/api/reply/config/' + id,
+		method: 'get',
+		data
+	});
+}
+// 获取产品评论
+export function reply_list(data, id) {
+	return request({
+		url: '/api/reply/list/' + id,
+		method: 'get',
+		data
+	});
+}
+
+// 分享海报 
+export function poster(data) {
+	return request({
+		url: '/api/product/poster',
+		method: 'post',
+		data
+	});
+}

+ 46 - 0
api/set.js

@@ -0,0 +1,46 @@
+import request from '@/utils/request'
+
+// 修改用户信息
+export function userEdit(data) {
+	return request({
+		url: '/api/user/edit',
+		method: 'post',
+		data
+	});
+}
+
+//退出登录
+export function logout(data) {
+	return request({
+		url: '/api/logout',
+		method: 'get',
+		data
+	});
+}
+//修改密码
+export function registerReset(data) {
+	return request({
+		url: '/api/register/reset',
+		method: 'post',
+		data
+	});
+}
+
+//绑定手机
+export function binding(data) {
+	return request({
+		url: '/api/binding',
+		method: 'post',
+		data
+	});
+}
+// #ifdef APP-PLUS
+//苹果生成账户
+export function applelogin(data) {
+	return request({
+		url: '/api/applelogin',
+		method: 'post',
+		data
+	});
+}
+// #endif

+ 199 - 0
api/user.js

@@ -0,0 +1,199 @@
+import request from '@/utils/request'
+
+// 获取用户信息
+export function getUserInfo(data) {
+	return request({
+		url: '/api/userinfo',
+		method: 'get',
+		data
+	});
+}
+
+
+		
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// 订单统计信息
+export function orderData(data) {
+	return request({
+		url: '/api/order/data',
+		method: 'get',
+		data
+	});
+}
+
+
+
+// 用户分享图
+export function spreadBanner(data) {
+	return request({
+		url: '/api/spread/banner',
+		method: 'get',
+		data
+	});
+}
+
+// 获取地址列表
+export function getAddressList(data) {
+	return request({
+		url: '/api/address/list',
+		method: 'get',
+		data
+	});
+}
+// 修改地址
+export function addressEdit(data) {
+	return request({
+		url: '/api/address/edit',
+		method: 'post',
+		data
+	});
+}
+// 删除地址
+export function addressDel(data) {
+	return request({
+		url: '/api/address/del',
+		method: 'post',
+		data
+	});
+}
+// 设为默认地址
+export function setAddressDefault(data) {
+	return request({
+		url: '/api/address/default/set',
+		method: 'post',
+		data
+	});
+}
+// 购物车列表
+export function getCartList(data) {
+	return request({
+		url: '/api/cart/list',
+		method: 'get',
+		data
+	});
+}
+
+// 修改购物车数量
+export function getCartNum(data) {
+	return request({
+		url: '/api/cart/num',
+		method: 'post',
+		data
+	});
+}
+//删除购物车
+export function cartDel(data) {
+	return request({
+		url: '/api/cart/del',
+		method: 'post',
+		data
+	});
+}
+//获取收藏夹列表
+export function getcollectList(data) {
+	return request({
+		url: '/api/collect/user',
+		method: 'get',
+		data
+	});
+}
+// 取消收藏
+export function delcollect(data) {
+	return request({
+		url: '/api/collect/del',
+		method: 'post',
+		data
+	});
+}
+
+
+//http://hmd.frp.liuniu946.com/api/brokerage/list?page=1&limit=10&pm=1
+export function getCommissionInfo(data) {
+	return request({
+		url: '/api/brokerage/list',
+		method: 'get',
+		data
+	});
+}
+
+//我的推广
+export function myspread(data) {
+	return request({
+		url: '/api/spread/people',
+		method: 'POST',
+		data
+	});
+}
+
+
+//
+export function sqdl(data) {
+	return request({
+		url: '/api/user/apply',
+		method: 'POST',
+		data
+	});
+}
+
+export function details(data, id) {
+	return request({
+		url: '/api/article/details/' + id,
+		method: 'get',
+		data
+	});
+}
+
+export function yjzye(data) {
+	return request({
+		url: '/api/user/transformation',
+		method: 'post',
+		data
+	})
+}
+
+export function jfzz(data) {
+	return request({
+		url: '/api/brokeragePriceTransfer',
+		method: 'post',
+		data
+	})
+}
+
+export function getLevelList(data) {
+	// api/give_level_list
+	return request({
+		url: '/api/give_level_list',
+		method: 'get',
+		data
+	})
+}

+ 149 - 0
api/wallet.js

@@ -0,0 +1,149 @@
+import request from '@/utils/request'
+
+// 获取用户消费记录
+export function spreadCommission(data, state) {
+	return request({
+		url: '/api/spread/commission/' + state,
+		method: 'get',
+		data
+	});
+}
+
+// 获取账户余额
+export function userBalance(data) {
+	return request({
+		url: '/api/user/balance',
+		method: 'get',
+		data
+	});
+}
+
+// 提现
+export function extractCash(data) {
+	return request({
+		url: '/api/extract/cash',
+		method: 'post',
+		data
+	});
+}
+
+// 提现信息
+export function extractBank(data) {
+	return request({
+		url: '/api/extract/bank',
+		method: 'get',
+		data
+	});
+}
+// #ifdef H5
+// 公众号充值
+export function rechargeWechat(data) {
+	return request({
+		url: '/api/recharge/wechat',
+		method: 'post',
+		data
+	});
+}
+// #endif
+// #ifdef MP
+// 小程序充值
+export function rechargeRoutine(data) {
+	return request({
+		url: '/api/recharge/routine',
+		method: 'post',
+		data
+	});
+}
+// #endif
+// 获取提现支付宝账号
+export function aliInfo(data) {
+	return request({
+		url: '/api/ali/info',
+		method: 'get',
+		data
+	});
+}
+//获取默认银行卡账号
+export function bankInfo(data) {
+	return request({
+		url: '/api/bank/info',
+		method: 'get',
+		data
+	});
+}
+// 保存提现支付宝账号
+export function setAliInfo(data) {
+	return request({
+		url: '/api/ali/edit',
+		method: 'post',
+		data
+	});
+}
+//保存默认银行卡账号
+export function setBankInfo(data) {
+	return request({
+		url: '/api/bank/edit',
+		method: 'post',
+		data
+	});
+}
+
+
+// 账户余额
+export function balance(data) {
+	return request({
+		url: '/api/user/balance',
+		method: 'get',
+		data
+	});
+}
+
+//
+export function yue(data) {
+	return request({
+		url: '/api/now_money/list',
+		method: 'get',
+		data
+	});
+}
+
+export function integral(data) {
+	return request({
+		url: '/api/integral/list',
+		method: 'get',
+		data
+	});
+}
+
+export function getBank(data) {
+	return request({
+		url: '/api/auction/pay_list',
+		method: 'get',
+		data
+	});
+}
+
+export function setBank(data) {
+	return request({
+		url: '/api/auction/pay',
+		method: 'post',
+		data
+	});
+}
+
+export function getRechargePrice(data) {
+	return request({
+		url: '/api/getRechargePrice',
+		method: 'post',
+		data
+	});
+}
+
+// 竞猜项目api
+export function qianBao(data) {
+	return request({
+		url: '/api/wallet',
+		method: 'get',
+		data
+	});
+}

+ 37 - 0
api/wx.js

@@ -0,0 +1,37 @@
+import request from '@/utils/request'
+// #ifdef H5
+// 微信分享信息
+export function share(data) {
+	return request({
+		url: '/api/share',
+		method: 'get',
+		data
+	});
+}
+//微信配置
+export function wechatConfig(data) {
+	return request({
+		url: '/api/wechat/config',
+		method: 'get',
+		data
+	});
+}
+// 微信code地址
+export function wechatAuth(data) {
+	return request({
+		url: '/api/wechat/auth',
+		method: 'get',
+		data
+	});
+}
+// #endif
+// #ifdef MP-WEIXIN
+// 微信code地址
+export function wechatMpAuth(data) {
+	return request({
+		url: '/api/wechat/mp_auth',
+		method: 'post',
+		data
+	});
+}
+// #endif

+ 57 - 0
components/Loading/index.vue

@@ -0,0 +1,57 @@
+<template>
+	<view>
+		<view class="Loads acea-row row-center-wrapper" v-if="loading && !loaded" style="margin-top: .2rem;">
+			<view v-if="loading">
+				<view class="iconfont icon-jiazai loading acea-row row-center-wrapper"></view>
+				正在加载中
+			</view>
+			<view v-else>
+				上拉加载更多
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: "Loading",
+		props: {
+			loaded: {
+				type: Boolean,
+				default: false
+			},
+			loading: {
+				type: Boolean,
+				default: false
+			}
+		}
+	};
+</script>
+<style>
+	.Loads {
+	  height: 80upx;
+	  font-size: 25upx;
+	  color: #000;
+	}
+	.Loads .iconfont {
+	  font-size: 30upx;
+	  margin-right: 10upx;
+	  height: 32upx;
+	  line-height: 32upx;
+	}
+	/*加载动画*/
+	@keyframes load {
+	  from {
+	    transform: rotate(0deg);
+	  }
+	  to {
+	    transform: rotate(360deg);
+	  }
+	}
+	.loadingpic {
+	  animation: load 3s linear 1s infinite;
+	}
+	.loading {
+	  animation: load linear 1s infinite;
+	}
+</style>

+ 120 - 0
components/countDown/index.vue

@@ -0,0 +1,120 @@
+<template>
+	<view class="time" :style="justifyLeft">
+		<text class="red" v-if="tipText">{{ tipText }}</text>
+		<text class="styleAll" v-if="isDay === true">{{ day }}</text>
+		<text class="timeTxt red" v-if="dayText">{{ dayText }}</text>
+		<text class="styleAll">{{ hour }}</text>
+		<text class="timeTxt red" v-if="hourText">{{ hourText }}</text>
+		<text class="styleAll">{{ minute }}</text>
+		<text class="timeTxt red" v-if="minuteText">{{ minuteText }}</text>
+		<text class="styleAll">{{ second }}</text>
+		<text class="timeTxt red" v-if="secondText">{{ secondText }}</text>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: "countDown",
+		props: {
+			justifyLeft: {
+				type: String,
+				default: ""
+			},
+			//距离开始提示文字
+			tipText: {
+				type: String,
+				default: "倒计时"
+			},
+			dayText: {
+				type: String,
+				default: "天"
+			},
+			hourText: {
+				type: String,
+				default: "时"
+			},
+			minuteText: {
+				type: String,
+				default: "分"
+			},
+			secondText: {
+				type: String,
+				default: "秒"
+			},
+			datatime: {
+				type: Number,
+				default: 0
+			},
+			isDay: {
+				type: Boolean,
+				default: true
+			}
+		},
+		data: function() {
+			return {
+				day: "00",
+				hour: "00",
+				minute: "00",
+				second: "00"
+			};
+		},
+		created: function() {
+			this.show_time();
+		},
+		mounted: function() {},
+		methods: {
+			show_time: function() {
+				let that = this;
+
+				function runTime() {
+					//时间函数
+					let intDiff = that.datatime - Date.parse(new Date()) / 1000; //获取数据中的时间戳的时间差;
+					let day = 0,
+						hour = 0,
+						minute = 0,
+						second = 0;
+					if (intDiff > 0) {
+						//转换时间
+						if (that.isDay === true) {
+							day = Math.floor(intDiff / (60 * 60 * 24));
+						} else {
+							day = 0;
+						}
+						hour = Math.floor(intDiff / (60 * 60)) - day * 24;
+						minute = Math.floor(intDiff / 60) - day * 24 * 60 - hour * 60;
+						second =
+							Math.floor(intDiff) -
+							day * 24 * 60 * 60 -
+							hour * 60 * 60 -
+							minute * 60;
+						if (hour <= 9) hour = "0" + hour;
+						if (minute <= 9) minute = "0" + minute;
+						if (second <= 9) second = "0" + second;
+						that.day = day;
+						that.hour = hour;
+						that.minute = minute;
+						that.second = second;
+					} else {
+						that.day = "00";
+						that.hour = "00";
+						that.minute = "00";
+						that.second = "00";
+					}
+				}
+				runTime();
+				setInterval(runTime, 1000);
+			}
+		}
+	};
+</script>
+
+<style>
+	.time{
+		display: flex;
+		justify-content: center;
+	} 
+	.red{
+		color: #fc4141;
+		margin: 0 4rpx;
+	}
+</style>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 18 - 0
components/empty.vue


+ 36 - 0
components/emptyPage.vue

@@ -0,0 +1,36 @@
+<template>
+	<view class="empty-box">
+		<image src="/static/images/empty-box.png"></image>
+		<view class="txt">{{title}}</view>
+	</view>
+</template>
+
+<script>
+	export default{
+		props: {
+			title: {
+				type: String,
+				default: '暂无记录',
+			},
+		},
+	}
+	
+</script>
+
+<style lang="scss">
+	.empty-box{
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		margin-top: 200rpx;
+		image{
+			width: 414rpx;
+			height: 240rpx;
+		}
+		.txt{
+			font-size: 26rpx;
+			color: #999;
+		}
+	}
+</style>

+ 118 - 0
components/home/index.vue

@@ -0,0 +1,118 @@
+<template>
+	<view style="touch-action: none;">
+		<view class="home" style="position:fixed;" :style="{ top: top + 'px', bottom: bottom }" id="right-nav" @touchmove.stop.prevent="setTouchMove">
+			<view class="homeCon bg-color-red" :class="homeActive === true ? 'on' : ''" v-if="homeActive">
+				<navigator hover-class='none' url='/pages/index/index' open-type='switchTab' class='iconfont icon-shouye-xianxing'></navigator>
+				<navigator hover-class='none' url='/pages/order_addcart/order_addcart' open-type='switchTab' class='iconfont icon-caigou-xianxing'></navigator>
+				<navigator hover-class='none' url='/pages/user/index' open-type='switchTab' class='iconfont icon-yonghu1'></navigator>
+			</view>
+			<view @click="open" class="pictrueBox">
+				<view class="pictrue">
+					<image :src="
+              homeActive === true
+                ? '/static/images/close.gif'
+                : '/static/images/open.gif'
+            "
+					 class="image" />
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	import {
+		mapGetters
+	} from "vuex";
+	export default {
+		name: "Home",
+		props: {},
+		data: function() {
+			return {
+				top: "",
+				bottom: ""
+			};
+		},
+		computed: mapGetters(["homeActive"]),
+		methods: {
+			setTouchMove(e) {
+				var that = this;
+				if (e.touches[0].clientY < 545 && e.touches[0].clientY > 66) {
+					that.top = e.touches[0].clientY
+					// that.setData({
+					// 	top: e.touches[0].clientY
+					// })
+				}
+			},
+			open: function() {
+				this.homeActive ?
+					this.$store.commit("CLOSE_HOME") :
+					this.$store.commit("OPEN_HOME");
+			}
+		},
+		created() {
+			this.bottom = "50px";
+		}
+	};
+</script>
+
+<style scoped>
+	.pictrueBox {
+		width: 130rpx;
+		height: 120rpx;
+	}
+
+	/*返回主页按钮*/
+	.home {
+		position: fixed;
+		color: white;
+		text-align: center;
+		z-index: 9999;
+		right: 15rpx;
+		display: flex;
+	}
+
+	.home .homeCon {
+		border-radius: 50rpx;
+		opacity: 0;
+		height: 0;
+		color: #e93323;
+		width: 0;
+	}
+
+	.home .homeCon.on {
+		opacity: 1;
+		animation: bounceInRight 0.5s cubic-bezier(0.215, 0.610, 0.355, 1.000);
+		width: 300rpx;
+		height: 86rpx;
+		margin-bottom: 20rpx;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		background: #f44939 !important;
+	}
+
+	.home .homeCon .iconfont {
+		font-size: 48rpx;
+		color: #fff;
+		display: inline-block;
+		margin: 0 auto;
+	}
+
+	.home .pictrue {
+		width: 86rpx;
+		height: 86rpx;
+		border-radius: 50%;
+		margin: 0 auto;
+	}
+
+	.home .pictrue .image {
+		width: 100%;
+		height: 100%;
+		border-radius: 50%;
+		transform: rotate(90deg);
+		ms-transform: rotate(90deg);
+		moz-transform: rotate(90deg);
+		webkit-transform: rotate(90deg);
+		o-transform: rotate(90deg);
+	}
+</style>

+ 630 - 0
components/jyf-parser/jyf-parser.vue

@@ -0,0 +1,630 @@
+<template>
+	<view>
+		<slot v-if="!nodes.length" />
+		<!--#ifdef APP-PLUS-NVUE-->
+		<web-view id="_top" ref="web" :style="'margin-top:-2px;height:'+height+'px'" @onPostMessage="_message" />
+		<!--#endif-->
+		<!--#ifndef APP-PLUS-NVUE-->
+		<view id="_top" :style="showAm+(selectable?';user-select:text;-webkit-user-select:text':'')">
+			<!--#ifdef H5 || MP-360-->
+			<div :id="'rtf'+uid"></div>
+			<!--#endif-->
+			<!--#ifndef H5 || MP-360-->
+			<trees :nodes="nodes" :lazyLoad="lazyLoad" :loading="loadingImg" />
+			<!--#endif-->
+		</view>
+		<!--#endif-->
+	</view>
+</template>
+
+<script>
+	// #ifndef H5 || APP-PLUS-NVUE || MP-360
+	import trees from './libs/trees';
+	var cache = {},
+		// #ifdef MP-WEIXIN || MP-TOUTIAO
+		fs = uni.getFileSystemManager ? uni.getFileSystemManager() : null,
+		// #endif
+		Parser = require('./libs/MpHtmlParser.js');
+	var dom;
+	// 计算 cache 的 key
+	function hash(str) {
+		for (var i = str.length, val = 5381; i--;)
+			val += (val << 5) + str.charCodeAt(i);
+		return val;
+	}
+	// #endif
+	// #ifdef H5 || APP-PLUS-NVUE || MP-360
+	var windowWidth = uni.getSystemInfoSync().windowWidth,
+		cfg = require('./libs/config.js');
+	// #endif
+	// #ifdef APP-PLUS-NVUE
+	var weexDom = weex.requireModule('dom');
+	// #endif
+	/**
+	 * Parser 富文本组件
+	 * @tutorial https://github.com/jin-yufeng/Parser
+	 * @property {String} html 富文本数据
+	 * @property {Boolean} autopause 是否在播放一个视频时自动暂停其他视频
+	 * @property {Boolean} autoscroll 是否自动给所有表格添加一个滚动层
+	 * @property {Boolean} autosetTitle 是否自动将 title 标签中的内容设置到页面标题
+	 * @property {Number} compress 压缩等级
+	 * @property {String} domain 图片、视频等链接的主域名
+	 * @property {Boolean} lazyLoad 是否开启图片懒加载
+	 * @property {String} loadingImg 图片加载完成前的占位图
+	 * @property {Boolean} selectable 是否开启长按复制
+	 * @property {Object} tagStyle 标签的默认样式
+	 * @property {Boolean} showWithAnimation 是否使用渐显动画
+	 * @property {Boolean} useAnchor 是否使用锚点
+	 * @property {Boolean} useCache 是否缓存解析结果
+	 * @event {Function} parse 解析完成事件
+	 * @event {Function} load dom 加载完成事件
+	 * @event {Function} ready 所有图片加载完毕事件
+	 * @event {Function} error 错误事件
+	 * @event {Function} imgtap 图片点击事件
+	 * @event {Function} linkpress 链接点击事件
+	 * @author JinYufeng
+	 * @version 20200728
+	 * @listens MIT
+	 */
+	export default {
+		name: 'parser',
+		data() {
+			return {
+				// #ifdef H5 || MP-360
+				uid: this._uid,
+				// #endif
+				// #ifdef APP-PLUS-NVUE
+				height: 1,
+				// #endif
+				// #ifndef APP-PLUS-NVUE
+				showAm: '',
+				// #endif
+				nodes: []
+			}
+		},
+		// #ifndef H5 || APP-PLUS-NVUE || MP-360
+		components: {
+			trees
+		},
+		// #endif
+		props: {
+			html: String,
+			autopause: {
+				type: Boolean,
+				default: true
+			},
+			autoscroll: Boolean,
+			autosetTitle: {
+				type: Boolean,
+				default: true
+			},
+			// #ifndef H5 || APP-PLUS-NVUE || MP-360
+			compress: Number,
+			loadingImg: String,
+			useCache: Boolean,
+			// #endif
+			domain: String,
+			lazyLoad: Boolean,
+			selectable: Boolean,
+			tagStyle: Object,
+			showWithAnimation: Boolean,
+			useAnchor: Boolean
+		},
+		watch: {
+			html(html) {
+				this.setContent(html);
+			}
+		},
+		created() {
+			// 图片数组
+			this.imgList = [];
+			this.imgList.each = function(f) {
+				for (var i = 0, len = this.length; i < len; i++)
+					this.setItem(i, f(this[i], i, this));
+			}
+			this.imgList.setItem = function(i, src) {
+				if (i == void 0 || !src) return;
+				// #ifndef MP-ALIPAY || APP-PLUS
+				// 去重
+				if (src.indexOf('http') == 0 && this.includes(src)) {
+					var newSrc = src.split('://')[0];
+					for (var j = newSrc.length, c; c = src[j]; j++) {
+						if (c == '/' && src[j - 1] != '/' && src[j + 1] != '/') break;
+						newSrc += Math.random() > 0.5 ? c.toUpperCase() : c;
+					}
+					newSrc += src.substr(j);
+					return this[i] = newSrc;
+				}
+				// #endif
+				this[i] = src;
+				// 暂存 data src
+				if (src.includes('data:image')) {
+					var filePath, info = src.match(/data:image\/(\S+?);(\S+?),(.+)/);
+					if (!info) return;
+					// #ifdef MP-WEIXIN || MP-TOUTIAO
+					filePath = `${wx.env.USER_DATA_PATH}/${Date.now()}.${info[1]}`;
+					fs && fs.writeFile({
+						filePath,
+						data: info[3],
+						encoding: info[2],
+						success: () => this[i] = filePath
+					})
+					// #endif
+					// #ifdef APP-PLUS
+					filePath = `_doc/parser_tmp/${Date.now()}.${info[1]}`;
+					var bitmap = new plus.nativeObj.Bitmap();
+					bitmap.loadBase64Data(src, () => {
+						bitmap.save(filePath, {}, () => {
+							bitmap.clear()
+							this[i] = filePath;
+						})
+					})
+					// #endif
+				}
+			}
+		},
+		mounted() {
+			// #ifdef H5 || MP-360
+			this.document = document.getElementById('rtf' + this._uid);
+			// #endif
+			// #ifndef H5 || APP-PLUS-NVUE || MP-360
+			if (dom) this.document = new dom(this);
+			// #endif
+			// #ifdef APP-PLUS-NVUE
+			this.document = this.$refs.web;
+			setTimeout(() => {
+				// #endif
+				if (this.html) this.setContent(this.html);
+				// #ifdef APP-PLUS-NVUE
+			}, 30)
+			// #endif
+		},
+		beforeDestroy() {
+			// #ifdef H5 || MP-360
+			if (this._observer) this._observer.disconnect();
+			// #endif
+			this.imgList.each(src => {
+				// #ifdef APP-PLUS
+				if (src && src.includes('_doc')) {
+					plus.io.resolveLocalFileSystemURL(src, entry => {
+						entry.remove();
+					});
+				}
+				// #endif
+				// #ifdef MP-WEIXIN || MP-TOUTIAO
+				if (src && src.includes(uni.env.USER_DATA_PATH))
+					fs && fs.unlink({
+						filePath: src
+					})
+				// #endif
+			})
+			clearInterval(this._timer);
+		},
+		methods: {
+			// 设置富文本内容
+			setContent(html, append) {
+				// #ifdef APP-PLUS-NVUE
+				if (!html)
+					return this.height = 1;
+				if (append)
+					this.$refs.web.evalJs("var b=document.createElement('div');b.innerHTML='" + html.replace(/'/g, "\\'") +
+						"';document.getElementById('parser').appendChild(b)");
+				else {
+					html =
+						'<meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"><style>html,body{width:100%;height:100%;overflow:hidden}body{margin:0}</style><base href="' +
+						this.domain + '"><div id="parser"' + (this.selectable ? '>' : ' style="user-select:none">') + this._handleHtml(html).replace(/\n/g, '\\n') +
+						'</div><script>"use strict";function e(e){if(window.__dcloud_weex_postMessage||window.__dcloud_weex_){var t={data:[e]};window.__dcloud_weex_postMessage?window.__dcloud_weex_postMessage(t):window.__dcloud_weex_.postMessage(JSON.stringify(t))}}document.body.onclick=function(){e({action:"click"})},' +
+						(this.showWithAnimation ? 'document.body.style.animation="_show .5s",' : '') +
+						'setTimeout(function(){e({action:"load",text:document.body.innerText,height:document.getElementById("parser").scrollHeight})},50);\x3c/script>';
+					this.$refs.web.evalJs("document.write('" + html.replace(/'/g, "\\'") + "');document.close()");
+				}
+				this.$refs.web.evalJs(
+					'var t=document.getElementsByTagName("title");t.length&&e({action:"getTitle",title:t[0].innerText});for(var o,n=document.getElementsByTagName("style"),r=1;o=n[r++];)o.innerHTML=o.innerHTML.replace(/body/g,"#parser");for(var a,c=document.getElementsByTagName("img"),s=[],i=0==c.length,d=0,l=0,g=0;a=c[l];l++)parseInt(a.style.width||a.getAttribute("width"))>' +
+					windowWidth + '&&(a.style.height="auto"),a.onload=function(){++d==c.length&&(i=!0)},a.onerror=function(){++d==c.length&&(i=!0),' + (cfg.errorImg ? 'this.src="' + cfg.errorImg + '",' : '') +
+					'e({action:"error",source:"img",target:this})},a.hasAttribute("ignore")||"A"==a.parentElement.nodeName||(a.i=g++,s.push(a.src),a.onclick=function(){e({action:"preview",img:{i:this.i,src:this.src}})});e({action:"getImgList",imgList:s});for(var u,m=document.getElementsByTagName("a"),f=0;u=m[f];f++)u.onclick=function(){var t,o=this.getAttribute("href");if("#"==o[0]){var n=document.getElementById(o.substr(1));n&&(t=n.offsetTop)}return e({action:"linkpress",href:o,offset:t}),!1};for(var h,y=document.getElementsByTagName("video"),v=0;h=y[v];v++)h.style.maxWidth="100%",h.onerror=function(){e({action:"error",source:"video",target:this})}' +
+					(this.autopause ? ',h.onplay=function(){for(var e,t=0;e=y[t];t++)e!=this&&e.pause()}' : '') +
+					';for(var _,p=document.getElementsByTagName("audio"),w=0;_=p[w];w++)_.onerror=function(){e({action:"error",source:"audio",target:this})};' +
+					(this.autoscroll ? 'for(var T,E=document.getElementsByTagName("table"),B=0;T=E[B];B++){var N=document.createElement("div");N.style.overflow="scroll",T.parentNode.replaceChild(N,T),N.appendChild(T)}' : '') +
+					'var x=document.getElementById("parser");clearInterval(window.timer),window.timer=setInterval(function(){i&&clearInterval(window.timer),e({action:"ready",ready:i,height:x.scrollHeight})},350)'
+				)
+				this.nodes = [1];
+				// #endif
+				// #ifdef H5 || MP-360
+				if (!html) {
+					if (this.rtf && !append) this.rtf.parentNode.removeChild(this.rtf);
+					return;
+				}
+				var div = document.createElement('div');
+				if (!append) {
+					if (this.rtf) this.rtf.parentNode.removeChild(this.rtf);
+					this.rtf = div;
+				} else {
+					if (!this.rtf) this.rtf = div;
+					else this.rtf.appendChild(div);
+				}
+				div.innerHTML = this._handleHtml(html, append);
+				for (var styles = this.rtf.getElementsByTagName('style'), i = 0, style; style = styles[i++];) {
+					style.innerHTML = style.innerHTML.replace(/body/g, '#rtf' + this._uid);
+					style.setAttribute('scoped', 'true');
+				}
+				// 懒加载
+				if (!this._observer && this.lazyLoad && IntersectionObserver) {
+					this._observer = new IntersectionObserver(changes => {
+						for (let item, i = 0; item = changes[i++];) {
+							if (item.isIntersecting) {
+								item.target.src = item.target.getAttribute('data-src');
+								item.target.removeAttribute('data-src');
+								this._observer.unobserve(item.target);
+							}
+						}
+					}, {
+						rootMargin: '500px 0px 500px 0px'
+					})
+				}
+				var _ts = this;
+				// 获取标题
+				var title = this.rtf.getElementsByTagName('title');
+				if (title.length && this.autosetTitle)
+					uni.setNavigationBarTitle({
+						title: title[0].innerText
+					})
+				// 图片处理
+				this.imgList.length = 0;
+				var imgs = this.rtf.getElementsByTagName('img');
+				for (let i = 0, j = 0, img; img = imgs[i]; i++) {
+					if (parseInt(img.style.width || img.getAttribute('width')) > windowWidth)
+						img.style.height = 'auto';
+					var src = img.getAttribute('src');
+					if (this.domain && src) {
+						if (src[0] == '/') {
+							if (src[1] == '/')
+								img.src = (this.domain.includes('://') ? this.domain.split('://')[0] : '') + ':' + src;
+							else img.src = this.domain + src;
+						} else if (!src.includes('://')) img.src = this.domain + '/' + src;
+					}
+					if (!img.hasAttribute('ignore') && img.parentElement.nodeName != 'A') {
+						img.i = j++;
+						_ts.imgList.push(img.src || img.getAttribute('data-src'));
+						img.onclick = function() {
+							var preview = true;
+							this.ignore = () => preview = false;
+							_ts.$emit('imgtap', this);
+							if (preview) {
+								uni.previewImage({
+									current: this.i,
+									urls: _ts.imgList
+								});
+							}
+						}
+					}
+					img.onerror = function() {
+						if (cfg.errorImg)
+							_ts.imgList[this.i] = this.src = cfg.errorImg;
+						_ts.$emit('error', {
+							source: 'img',
+							target: this
+						});
+					}
+					if (_ts.lazyLoad && this._observer && img.src && img.i != 0) {
+						img.setAttribute('data-src', img.src);
+						img.removeAttribute('src');
+						this._observer.observe(img);
+					}
+				}
+				// 链接处理
+				var links = this.rtf.getElementsByTagName('a');
+				for (var link of links) {
+					link.onclick = function() {
+						var jump = true,
+							href = this.getAttribute('href');
+						_ts.$emit('linkpress', {
+							href,
+							ignore: () => jump = false
+						});
+						if (jump && href) {
+							if (href[0] == '#') {
+								if (_ts.useAnchor) {
+									_ts.navigateTo({
+										id: href.substr(1)
+									})
+								}
+							} else if (href.indexOf('http') == 0 || href.indexOf('//') == 0)
+								return true;
+							else
+								uni.navigateTo({
+									url: href
+								})
+						}
+						return false;
+					}
+				}
+				// 视频处理
+				var videos = this.rtf.getElementsByTagName('video');
+				_ts.videoContexts = videos;
+				for (let video, i = 0; video = videos[i++];) {
+					video.style.maxWidth = '100%';
+					video.onerror = function() {
+						_ts.$emit('error', {
+							source: 'video',
+							target: this
+						});
+					}
+					video.onplay = function() {
+						if (_ts.autopause)
+							for (let item, i = 0; item = _ts.videoContexts[i++];)
+								if (item != this) item.pause();
+					}
+				}
+				// 音频处理
+				var audios = this.rtf.getElementsByTagName('audio');
+				for (var audio of audios)
+					audio.onerror = function() {
+						_ts.$emit('error', {
+							source: 'audio',
+							target: this
+						});
+					}
+				// 表格处理
+				if (this.autoscroll) {
+					var tables = this.rtf.getElementsByTagName('table');
+					for (var table of tables) {
+						let div = document.createElement('div');
+						div.style.overflow = 'scroll';
+						table.parentNode.replaceChild(div, table);
+						div.appendChild(table);
+					}
+				}
+				if (!append) this.document.appendChild(this.rtf);
+				this.$nextTick(() => {
+					this.nodes = [1];
+					this.$emit('load');
+				});
+				setTimeout(() => this.showAm = '', 500);
+				// #endif
+				// #ifndef APP-PLUS-NVUE
+				// #ifndef H5 || MP-360
+				var nodes;
+				if (!html) return this.nodes = [];
+				var parser = new Parser(html, this);
+				// 缓存读取
+				if (this.useCache) {
+					var hashVal = hash(html);
+					if (cache[hashVal])
+						nodes = cache[hashVal];
+					else {
+						nodes = parser.parse();
+						cache[hashVal] = nodes;
+					}
+				} else nodes = parser.parse();
+				this.$emit('parse', nodes);
+				if (append) this.nodes = this.nodes.concat(nodes);
+				else this.nodes = nodes;
+				if (nodes.length && nodes.title && this.autosetTitle)
+					uni.setNavigationBarTitle({
+						title: nodes.title
+					})
+				if (this.imgList) this.imgList.length = 0;
+				this.videoContexts = [];
+				this.$nextTick(() => {
+					(function f(cs) {
+						for (var i = cs.length; i--;) {
+							if (cs[i].top) {
+								cs[i].controls = [];
+								cs[i].init();
+								f(cs[i].$children);
+							}
+						}
+					})(this.$children)
+					this.$emit('load');
+				})
+				// #endif
+				var height;
+				clearInterval(this._timer);
+				this._timer = setInterval(() => {
+					// #ifdef H5 || MP-360
+					this.rect = this.rtf.getBoundingClientRect();
+					// #endif
+					// #ifndef H5 || MP-360
+					uni.createSelectorQuery().in(this)
+						.select('#_top').boundingClientRect().exec(res => {
+							if (!res) return;
+							this.rect = res[0];
+							// #endif
+							if (this.rect.height == height) {
+								this.$emit('ready', this.rect)
+								clearInterval(this._timer);
+							}
+							height = this.rect.height;
+							// #ifndef H5 || MP-360
+						});
+					// #endif
+				}, 350);
+				if (this.showWithAnimation && !append) this.showAm = 'animation:_show .5s';
+				// #endif
+			},
+			// 获取文本内容
+			getText(ns = this.nodes) {
+				var txt = '';
+				// #ifdef APP-PLUS-NVUE
+				txt = this._text;
+				// #endif
+				// #ifdef H5 || MP-360
+				txt = this.rtf.innerText;
+				// #endif
+				// #ifndef H5 || APP-PLUS-NVUE || MP-360
+				for (var i = 0, n; n = ns[i++];) {
+					if (n.type == 'text') txt += n.text.replace(/&nbsp;/g, '\u00A0').replace(/&lt;/g, '<').replace(/&gt;/g, '>')
+						.replace(/&amp;/g, '&');
+					else if (n.type == 'br') txt += '\n';
+					else {
+						// 块级标签前后加换行
+						var block = n.name == 'p' || n.name == 'div' || n.name == 'tr' || n.name == 'li' || (n.name[0] == 'h' && n.name[1] >
+							'0' && n.name[1] < '7');
+						if (block && txt && txt[txt.length - 1] != '\n') txt += '\n';
+						if (n.children) txt += this.getText(n.children);
+						if (block && txt[txt.length - 1] != '\n') txt += '\n';
+						else if (n.name == 'td' || n.name == 'th') txt += '\t';
+					}
+				}
+				// #endif
+				return txt;
+			},
+			// 锚点跳转
+			in (obj) {
+				if (obj.page && obj.selector && obj.scrollTop) this._in = obj;
+			},
+			navigateTo(obj) {
+				if (!this.useAnchor) return obj.fail && obj.fail('Anchor is disabled');
+				// #ifdef APP-PLUS-NVUE
+				if (!obj.id)
+					weexDom.scrollToElement(this.$refs.web);
+				else
+					this.$refs.web.evalJs('var pos=document.getElementById("' + obj.id +
+						'");if(pos)post({action:"linkpress",href:"#",offset:pos.offsetTop+' + (obj.offset || 0) + '})');
+				obj.success && obj.success();
+				// #endif
+				// #ifndef APP-PLUS-NVUE
+				var d = ' ';
+				// #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO
+				d = '>>>';
+				// #endif
+				var selector = uni.createSelectorQuery().in(this._in ? this._in.page : this).select((this._in ? this._in.selector :
+					'#_top') + (obj.id ? `${d}#${obj.id},${this._in?this._in.selector:'#_top'}${d}.${obj.id}` : '')).boundingClientRect();
+				if (this._in) selector.select(this._in.selector).scrollOffset().select(this._in.selector).boundingClientRect();
+				else selector.selectViewport().scrollOffset();
+				selector.exec(res => {
+					if (!res[0]) return obj.fail && obj.fail('Label not found')
+					var scrollTop = res[1].scrollTop + res[0].top - (res[2] ? res[2].top : 0) + (obj.offset || 0);
+					if (this._in) this._in.page[this._in.scrollTop] = scrollTop;
+					else uni.pageScrollTo({
+						scrollTop,
+						duration: 300
+					})
+					obj.success && obj.success();
+				})
+				// #endif
+			},
+			// 获取视频对象
+			getVideoContext(id) {
+				// #ifndef APP-PLUS-NVUE
+				if (!id) return this.videoContexts;
+				else
+					for (var i = this.videoContexts.length; i--;)
+						if (this.videoContexts[i].id == id) return this.videoContexts[i];
+				// #endif
+			},
+			// #ifdef H5 || APP-PLUS-NVUE || MP-360
+			_handleHtml(html, append) {
+				if (!append) {
+					// 处理 tag-style 和 userAgentStyles
+					var style = '<style>@keyframes _show{0%{opacity:0}100%{opacity:1}}img{max-width:100%}';
+					for (var item in cfg.userAgentStyles)
+						style += `${item}{${cfg.userAgentStyles[item]}}`;
+					for (item in this.tagStyle)
+						style += `${item}{${this.tagStyle[item]}}`;
+					style += '</style>';
+					html = style + html;
+				}
+				// 处理 rpx
+				if (html.includes('rpx'))
+					html = html.replace(/[0-9.]+\s*rpx/g, $ => (parseFloat($) * windowWidth / 750) + 'px');
+				return html;
+			},
+			// #endif
+			// #ifdef APP-PLUS-NVUE
+			_message(e) {
+				// 接收 web-view 消息
+				var d = e.detail.data[0];
+				switch (d.action) {
+					case 'load':
+						this.$emit('load');
+						this.height = d.height;
+						this._text = d.text;
+						break;
+					case 'getTitle':
+						if (this.autosetTitle)
+							uni.setNavigationBarTitle({
+								title: d.title
+							})
+						break;
+					case 'getImgList':
+						this.imgList.length = 0;
+						for (var i = d.imgList.length; i--;)
+							this.imgList.setItem(i, d.imgList[i]);
+						break;
+					case 'preview':
+						var preview = true;
+						d.img.ignore = () => preview = false;
+						this.$emit('imgtap', d.img);
+						if (preview)
+							uni.previewImage({
+								current: d.img.i,
+								urls: this.imgList
+							})
+						break;
+					case 'linkpress':
+						var jump = true,
+							href = d.href;
+						this.$emit('linkpress', {
+							href,
+							ignore: () => jump = false
+						})
+						if (jump && href) {
+							if (href[0] == '#') {
+								if (this.useAnchor)
+									weexDom.scrollToElement(this.$refs.web, {
+										offset: d.offset
+									})
+							} else if (href.includes('://'))
+								plus.runtime.openWeb(href);
+							else
+								uni.navigateTo({
+									url: href
+								})
+						}
+						break;
+					case 'error':
+						if (d.source == 'img' && cfg.errorImg)
+							this.imgList.setItem(d.target.i, cfg.errorImg);
+						this.$emit('error', {
+							source: d.source,
+							target: d.target
+						})
+						break;
+					case 'ready':
+						this.height = d.height;
+						if (d.ready) uni.createSelectorQuery().in(this).select('#_top').boundingClientRect().exec(res => {
+							this.rect = res[0];
+							this.$emit('ready', res[0]);
+						})
+						break;
+					case 'click':
+						this.$emit('click');
+						this.$emit('tap');
+				}
+			},
+			// #endif
+		}
+	}
+</script>
+
+<style>
+	@keyframes _show {
+		0% {
+			opacity: 0;
+		}
+
+		100% {
+			opacity: 1;
+		}
+	}
+
+	/* #ifdef MP-WEIXIN */
+	:host {
+		display: block;
+		overflow: scroll;
+		-webkit-overflow-scrolling: touch;
+	}
+
+	/* #endif */
+</style>

+ 97 - 0
components/jyf-parser/libs/CssHandler.js

@@ -0,0 +1,97 @@
+const cfg = require('./config.js'),
+	isLetter = c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+
+function CssHandler(tagStyle) {
+	var styles = Object.assign(Object.create(null), cfg.userAgentStyles);
+	for (var item in tagStyle)
+		styles[item] = (styles[item] ? styles[item] + ';' : '') + tagStyle[item];
+	this.styles = styles;
+}
+CssHandler.prototype.getStyle = function(data) {
+	this.styles = new parser(data, this.styles).parse();
+}
+CssHandler.prototype.match = function(name, attrs) {
+	var tmp, matched = (tmp = this.styles[name]) ? tmp + ';' : '';
+	if (attrs.class) {
+		var items = attrs.class.split(' ');
+		for (var i = 0, item; item = items[i]; i++)
+			if (tmp = this.styles['.' + item])
+				matched += tmp + ';';
+	}
+	if (tmp = this.styles['#' + attrs.id])
+		matched += tmp + ';';
+	return matched;
+}
+module.exports = CssHandler;
+
+function parser(data, init) {
+	this.data = data;
+	this.floor = 0;
+	this.i = 0;
+	this.list = [];
+	this.res = init;
+	this.state = this.Space;
+}
+parser.prototype.parse = function() {
+	for (var c; c = this.data[this.i]; this.i++)
+		this.state(c);
+	return this.res;
+}
+parser.prototype.section = function() {
+	return this.data.substring(this.start, this.i);
+}
+// 状态机
+parser.prototype.Space = function(c) {
+	if (c == '.' || c == '#' || isLetter(c)) {
+		this.start = this.i;
+		this.state = this.Name;
+	} else if (c == '/' && this.data[this.i + 1] == '*')
+		this.Comment();
+	else if (!cfg.blankChar[c] && c != ';')
+		this.state = this.Ignore;
+}
+parser.prototype.Comment = function() {
+	this.i = this.data.indexOf('*/', this.i) + 1;
+	if (!this.i) this.i = this.data.length;
+	this.state = this.Space;
+}
+parser.prototype.Ignore = function(c) {
+	if (c == '{') this.floor++;
+	else if (c == '}' && !--this.floor) this.state = this.Space;
+}
+parser.prototype.Name = function(c) {
+	if (cfg.blankChar[c]) {
+		this.list.push(this.section());
+		this.state = this.NameSpace;
+	} else if (c == '{') {
+		this.list.push(this.section());
+		this.Content();
+	} else if (c == ',') {
+		this.list.push(this.section());
+		this.Comma();
+	} else if (!isLetter(c) && (c < '0' || c > '9') && c != '-' && c != '_')
+		this.state = this.Ignore;
+}
+parser.prototype.NameSpace = function(c) {
+	if (c == '{') this.Content();
+	else if (c == ',') this.Comma();
+	else if (!cfg.blankChar[c]) this.state = this.Ignore;
+}
+parser.prototype.Comma = function() {
+	while (cfg.blankChar[this.data[++this.i]]);
+	if (this.data[this.i] == '{') this.Content();
+	else {
+		this.start = this.i--;
+		this.state = this.Name;
+	}
+}
+parser.prototype.Content = function() {
+	this.start = ++this.i;
+	if ((this.i = this.data.indexOf('}', this.i)) == -1) this.i = this.data.length;
+	var content = this.section();
+	for (var i = 0, item; item = this.list[i++];)
+		if (this.res[item]) this.res[item] += ';' + content;
+		else this.res[item] = content;
+	this.list = [];
+	this.state = this.Space;
+}

+ 535 - 0
components/jyf-parser/libs/MpHtmlParser.js

@@ -0,0 +1,535 @@
+/**
+ * html 解析器
+ * @tutorial https://github.com/jin-yufeng/Parser
+ * @version 20200728
+ * @author JinYufeng
+ * @listens MIT
+ */
+const cfg = require('./config.js'),
+	blankChar = cfg.blankChar,
+	CssHandler = require('./CssHandler.js'),
+	windowWidth = uni.getSystemInfoSync().windowWidth;
+var emoji;
+
+function MpHtmlParser(data, options = {}) {
+	this.attrs = {};
+	this.CssHandler = new CssHandler(options.tagStyle, windowWidth);
+	this.data = data;
+	this.domain = options.domain;
+	this.DOM = [];
+	this.i = this.start = this.audioNum = this.imgNum = this.videoNum = 0;
+	options.prot = (this.domain || '').includes('://') ? this.domain.split('://')[0] : 'http';
+	this.options = options;
+	this.state = this.Text;
+	this.STACK = [];
+	// 工具函数
+	this.bubble = () => {
+		for (var i = this.STACK.length, item; item = this.STACK[--i];) {
+			if (cfg.richOnlyTags[item.name]) {
+				if (item.name == 'table' && !Object.hasOwnProperty.call(item, 'c')) item.c = 1;
+				return false;
+			}
+			item.c = 1;
+		}
+		return true;
+	}
+	this.decode = (val, amp) => {
+		var i = -1,
+			j, en;
+		while (1) {
+			if ((i = val.indexOf('&', i + 1)) == -1) break;
+			if ((j = val.indexOf(';', i + 2)) == -1) break;
+			if (val[i + 1] == '#') {
+				en = parseInt((val[i + 2] == 'x' ? '0' : '') + val.substring(i + 2, j));
+				if (!isNaN(en)) val = val.substr(0, i) + String.fromCharCode(en) + val.substr(j + 1);
+			} else {
+				en = val.substring(i + 1, j);
+				if (cfg.entities[en] || en == amp)
+					val = val.substr(0, i) + (cfg.entities[en] || '&') + val.substr(j + 1);
+			}
+		}
+		return val;
+	}
+	this.getUrl = url => {
+		if (url[0] == '/') {
+			if (url[1] == '/') url = this.options.prot + ':' + url;
+			else if (this.domain) url = this.domain + url;
+		} else if (this.domain && url.indexOf('data:') != 0 && !url.includes('://'))
+			url = this.domain + '/' + url;
+		return url;
+	}
+	this.isClose = () => this.data[this.i] == '>' || (this.data[this.i] == '/' && this.data[this.i + 1] == '>');
+	this.section = () => this.data.substring(this.start, this.i);
+	this.parent = () => this.STACK[this.STACK.length - 1];
+	this.siblings = () => this.STACK.length ? this.parent().children : this.DOM;
+}
+MpHtmlParser.prototype.parse = function() {
+	if (emoji) this.data = emoji.parseEmoji(this.data);
+	for (var c; c = this.data[this.i]; this.i++)
+		this.state(c);
+	if (this.state == this.Text) this.setText();
+	while (this.STACK.length) this.popNode(this.STACK.pop());
+	return this.DOM;
+}
+// 设置属性
+MpHtmlParser.prototype.setAttr = function() {
+	var name = this.attrName.toLowerCase(),
+		val = this.attrVal;
+	if (cfg.boolAttrs[name]) this.attrs[name] = 'T';
+	else if (val) {
+		if (name == 'src' || (name == 'data-src' && !this.attrs.src)) this.attrs.src = this.getUrl(this.decode(val, 'amp'));
+		else if (name == 'href' || name == 'style') this.attrs[name] = this.decode(val, 'amp');
+		else if (name.substr(0, 5) != 'data-') this.attrs[name] = val;
+	}
+	this.attrVal = '';
+	while (blankChar[this.data[this.i]]) this.i++;
+	if (this.isClose()) this.setNode();
+	else {
+		this.start = this.i;
+		this.state = this.AttrName;
+	}
+}
+// 设置文本节点
+MpHtmlParser.prototype.setText = function() {
+	var back, text = this.section();
+	if (!text) return;
+	text = (cfg.onText && cfg.onText(text, () => back = true)) || text;
+	if (back) {
+		this.data = this.data.substr(0, this.start) + text + this.data.substr(this.i);
+		let j = this.start + text.length;
+		for (this.i = this.start; this.i < j; this.i++) this.state(this.data[this.i]);
+		return;
+	}
+	if (!this.pre) {
+		// 合并空白符
+		var flag, tmp = [];
+		for (let i = text.length, c; c = text[--i];)
+			if (!blankChar[c]) {
+				tmp.unshift(c);
+				if (!flag) flag = 1;
+			} else {
+				if (tmp[0] != ' ') tmp.unshift(' ');
+				if (c == '\n' && flag == void 0) flag = 0;
+			}
+		if (flag == 0) return;
+		text = tmp.join('');
+	}
+	this.siblings().push({
+		type: 'text',
+		text: this.decode(text)
+	});
+}
+// 设置元素节点
+MpHtmlParser.prototype.setNode = function() {
+	var node = {
+			name: this.tagName.toLowerCase(),
+			attrs: this.attrs
+		},
+		close = cfg.selfClosingTags[node.name];
+	if (this.options.nodes.length) node.type = 'node';
+	this.attrs = {};
+	if (!cfg.ignoreTags[node.name]) {
+		// 处理属性
+		var attrs = node.attrs,
+			style = this.CssHandler.match(node.name, attrs, node) + (attrs.style || ''),
+			styleObj = {};
+		if (attrs.id) {
+			if (this.options.compress & 1) attrs.id = void 0;
+			else if (this.options.useAnchor) this.bubble();
+		}
+		if ((this.options.compress & 2) && attrs.class) attrs.class = void 0;
+		switch (node.name) {
+			case 'a':
+			case 'ad': // #ifdef APP-PLUS
+			case 'iframe':
+				// #endif
+				this.bubble();
+				break;
+			case 'font':
+				if (attrs.color) {
+					styleObj['color'] = attrs.color;
+					attrs.color = void 0;
+				}
+				if (attrs.face) {
+					styleObj['font-family'] = attrs.face;
+					attrs.face = void 0;
+				}
+				if (attrs.size) {
+					var size = parseInt(attrs.size);
+					if (size < 1) size = 1;
+					else if (size > 7) size = 7;
+					var map = ['xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'];
+					styleObj['font-size'] = map[size - 1];
+					attrs.size = void 0;
+				}
+				break;
+			case 'embed':
+				// #ifndef APP-PLUS
+				var src = node.attrs.src || '',
+					type = node.attrs.type || '';
+				if (type.includes('video') || src.includes('.mp4') || src.includes('.3gp') || src.includes('.m3u8'))
+					node.name = 'video';
+				else if (type.includes('audio') || src.includes('.m4a') || src.includes('.wav') || src.includes('.mp3') || src.includes(
+						'.aac'))
+					node.name = 'audio';
+				else break;
+				if (node.attrs.autostart)
+					node.attrs.autoplay = 'T';
+				node.attrs.controls = 'T';
+				// #endif
+				// #ifdef APP-PLUS
+				this.bubble();
+				break;
+				// #endif
+			case 'video':
+			case 'audio':
+				if (!attrs.id) attrs.id = node.name + (++this[`${node.name}Num`]);
+				else this[`${node.name}Num`]++;
+				if (node.name == 'video') {
+					if (this.videoNum > 3)
+						node.lazyLoad = 1;
+					if (attrs.width) {
+						styleObj.width = parseFloat(attrs.width) + (attrs.width.includes('%') ? '%' : 'px');
+						attrs.width = void 0;
+					}
+					if (attrs.height) {
+						styleObj.height = parseFloat(attrs.height) + (attrs.height.includes('%') ? '%' : 'px');
+						attrs.height = void 0;
+					}
+				}
+				if (!attrs.controls && !attrs.autoplay) attrs.controls = 'T';
+				attrs.source = [];
+				if (attrs.src) {
+					attrs.source.push(attrs.src);
+					attrs.src = void 0;
+				}
+				this.bubble();
+				break;
+			case 'td':
+			case 'th':
+				if (attrs.colspan || attrs.rowspan)
+					for (var k = this.STACK.length, item; item = this.STACK[--k];)
+						if (item.name == 'table') {
+							item.c = void 0;
+							break;
+						}
+		}
+		if (attrs.align) {
+			styleObj['text-align'] = attrs.align;
+			attrs.align = void 0;
+		}
+		// 压缩 style
+		var styles = style.split(';');
+		style = '';
+		for (var i = 0, len = styles.length; i < len; i++) {
+			var info = styles[i].split(':');
+			if (info.length < 2) continue;
+			let key = info[0].trim().toLowerCase(),
+				value = info.slice(1).join(':').trim();
+			if (value[0] == '-' || value.includes('safe'))
+				style += `;${key}:${value}`;
+			else if (!styleObj[key] || value.includes('import') || !styleObj[key].includes('import'))
+				styleObj[key] = value;
+		}
+		if (node.name == 'img') {
+			if (attrs.src && !attrs.ignore) {
+				if (this.bubble())
+					attrs.i = (this.imgNum++).toString();
+				else attrs.ignore = 'T';
+			}
+			if (attrs.ignore) {
+				style += ';-webkit-touch-callout:none';
+				styleObj['max-width'] = '100%';
+			}
+			var width;
+			if (styleObj.width) width = styleObj.width;
+			else if (attrs.width) width = attrs.width.includes('%') ? attrs.width : attrs.width + 'px';
+			if (width) {
+				styleObj.width = width;
+				attrs.width = '100%';
+				if (parseInt(width) > windowWidth) {
+					styleObj.height = '';
+					if (attrs.height) attrs.height = void 0;
+				}
+			}
+			if (styleObj.height) {
+				attrs.height = styleObj.height;
+				styleObj.height = '';
+			} else if (attrs.height && !attrs.height.includes('%'))
+				attrs.height += 'px';
+		}
+		for (var key in styleObj) {
+			var value = styleObj[key];
+			if (!value) continue;
+			if (key.includes('flex') || key == 'order' || key == 'self-align') node.c = 1;
+			// 填充链接
+			if (value.includes('url')) {
+				var j = value.indexOf('(');
+				if (j++ != -1) {
+					while (value[j] == '"' || value[j] == "'" || blankChar[value[j]]) j++;
+					value = value.substr(0, j) + this.getUrl(value.substr(j));
+				}
+			}
+			// 转换 rpx
+			else if (value.includes('rpx'))
+				value = value.replace(/[0-9.]+\s*rpx/g, $ => parseFloat($) * windowWidth / 750 + 'px');
+			else if (key == 'white-space' && value.includes('pre') && !close)
+				this.pre = node.pre = true;
+			style += `;${key}:${value}`;
+		}
+		style = style.substr(1);
+		if (style) attrs.style = style;
+		if (!close) {
+			node.children = [];
+			if (node.name == 'pre' && cfg.highlight) {
+				this.remove(node);
+				this.pre = node.pre = true;
+			}
+			this.siblings().push(node);
+			this.STACK.push(node);
+		} else if (!cfg.filter || cfg.filter(node, this) != false)
+			this.siblings().push(node);
+	} else {
+		if (!close) this.remove(node);
+		else if (node.name == 'source') {
+			var parent = this.parent();
+			if (parent && (parent.name == 'video' || parent.name == 'audio') && node.attrs.src)
+				parent.attrs.source.push(node.attrs.src);
+		} else if (node.name == 'base' && !this.domain) this.domain = node.attrs.href;
+	}
+	if (this.data[this.i] == '/') this.i++;
+	this.start = this.i + 1;
+	this.state = this.Text;
+}
+// 移除标签
+MpHtmlParser.prototype.remove = function(node) {
+	var name = node.name,
+		j = this.i;
+	// 处理 svg
+	var handleSvg = () => {
+		var src = this.data.substring(j, this.i + 1);
+		if (!node.attrs.xmlns) src = ' xmlns="http://www.w3.org/2000/svg"' + src;
+		var i = j;
+		while (this.data[j] != '<') j--;
+		src = this.data.substring(j, i).replace("viewbox", "viewBox") + src;
+		var parent = this.parent();
+		if (node.attrs.width == '100%' && parent && (parent.attrs.style || '').includes('inline'))
+			parent.attrs.style = 'width:300px;max-width:100%;' + parent.attrs.style;
+		this.siblings().push({
+			name: 'img',
+			attrs: {
+				src: 'data:image/svg+xml;utf8,' + src.replace(/#/g, '%23'),
+				style: (/vertical[^;]+/.exec(node.attrs.style) || []).shift(),
+				ignore: 'T'
+			}
+		})
+	}
+	if (node.name == 'svg' && this.data[j] == '/') return handleSvg(this.i++);
+	while (1) {
+		if ((this.i = this.data.indexOf('</', this.i + 1)) == -1) {
+			if (name == 'pre' || name == 'svg') this.i = j;
+			else this.i = this.data.length;
+			return;
+		}
+		this.start = (this.i += 2);
+		while (!blankChar[this.data[this.i]] && !this.isClose()) this.i++;
+		if (this.section().toLowerCase() == name) {
+			// 代码块高亮
+			if (name == 'pre') {
+				this.data = this.data.substr(0, j + 1) + cfg.highlight(this.data.substring(j + 1, this.i - 5), node.attrs) + this.data
+					.substr(this.i - 5);
+				return this.i = j;
+			} else if (name == 'style')
+				this.CssHandler.getStyle(this.data.substring(j + 1, this.i - 7));
+			else if (name == 'title')
+				this.DOM.title = this.data.substring(j + 1, this.i - 7);
+			if ((this.i = this.data.indexOf('>', this.i)) == -1) this.i = this.data.length;
+			if (name == 'svg') handleSvg();
+			return;
+		}
+	}
+}
+// 节点出栈处理
+MpHtmlParser.prototype.popNode = function(node) {
+	// 空白符处理
+	if (node.pre) {
+		node.pre = this.pre = void 0;
+		for (let i = this.STACK.length; i--;)
+			if (this.STACK[i].pre)
+				this.pre = true;
+	}
+	var siblings = this.siblings(),
+		len = siblings.length,
+		childs = node.children;
+	if (node.name == 'head' || (cfg.filter && cfg.filter(node, this) == false))
+		return siblings.pop();
+	var attrs = node.attrs;
+	// 替换一些标签名
+	if (cfg.blockTags[node.name]) node.name = 'div';
+	else if (!cfg.trustTags[node.name]) node.name = 'span';
+	// 处理列表
+	if (node.c && (node.name == 'ul' || node.name == 'ol')) {
+		if ((node.attrs.style || '').includes('list-style:none')) {
+			for (let i = 0, child; child = childs[i++];)
+				if (child.name == 'li')
+					child.name = 'div';
+		} else if (node.name == 'ul') {
+			var floor = 1;
+			for (let i = this.STACK.length; i--;)
+				if (this.STACK[i].name == 'ul') floor++;
+			if (floor != 1)
+				for (let i = childs.length; i--;)
+					childs[i].floor = floor;
+		} else {
+			for (let i = 0, num = 1, child; child = childs[i++];)
+				if (child.name == 'li') {
+					child.type = 'ol';
+					child.num = ((num, type) => {
+						if (type == 'a') return String.fromCharCode(97 + (num - 1) % 26);
+						if (type == 'A') return String.fromCharCode(65 + (num - 1) % 26);
+						if (type == 'i' || type == 'I') {
+							num = (num - 1) % 99 + 1;
+							var one = ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'],
+								ten = ['X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'],
+								res = (ten[Math.floor(num / 10) - 1] || '') + (one[num % 10 - 1] || '');
+							if (type == 'i') return res.toLowerCase();
+							return res;
+						}
+						return num;
+					})(num++, attrs.type) + '.';
+				}
+		}
+	}
+	// 处理表格的边框
+	if (node.name == 'table') {
+		var padding = attrs.cellpadding,
+			spacing = attrs.cellspacing,
+			border = attrs.border;
+		if (node.c) {
+			this.bubble();
+			attrs.style = (attrs.style || '') + ';display:table';
+			if (!padding) padding = 2;
+			if (!spacing) spacing = 2;
+		}
+		if (border) attrs.style = `border:${border}px solid gray;${attrs.style || ''}`;
+		if (spacing) attrs.style = `border-spacing:${spacing}px;${attrs.style || ''}`;
+		if (border || padding || node.c)
+			(function f(ns) {
+				for (var i = 0, n; n = ns[i]; i++) {
+					if (n.type == 'text') continue;
+					var style = n.attrs.style || '';
+					if (node.c && n.name[0] == 't') {
+						n.c = 1;
+						style += ';display:table-' + (n.name == 'th' || n.name == 'td' ? 'cell' : (n.name == 'tr' ? 'row' : 'row-group'));
+					}
+					if (n.name == 'th' || n.name == 'td') {
+						if (border) style = `border:${border}px solid gray;${style}`;
+						if (padding) style = `padding:${padding}px;${style}`;
+					} else f(n.children || []);
+					if (style) n.attrs.style = style;
+				}
+			})(childs)
+		if (this.options.autoscroll) {
+			var table = Object.assign({}, node);
+			node.name = 'div';
+			node.attrs = {
+				style: 'overflow:scroll'
+			}
+			node.children = [table];
+		}
+	}
+	this.CssHandler.pop && this.CssHandler.pop(node);
+	// 自动压缩
+	if (node.name == 'div' && !Object.keys(attrs).length && childs.length == 1 && childs[0].name == 'div')
+		siblings[len - 1] = childs[0];
+}
+// 状态机
+MpHtmlParser.prototype.Text = function(c) {
+	if (c == '<') {
+		var next = this.data[this.i + 1],
+			isLetter = c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+		if (isLetter(next)) {
+			this.setText();
+			this.start = this.i + 1;
+			this.state = this.TagName;
+		} else if (next == '/') {
+			this.setText();
+			if (isLetter(this.data[++this.i + 1])) {
+				this.start = this.i + 1;
+				this.state = this.EndTag;
+			} else this.Comment();
+		} else if (next == '!' || next == '?') {
+			this.setText();
+			this.Comment();
+		}
+	}
+}
+MpHtmlParser.prototype.Comment = function() {
+	var key;
+	if (this.data.substring(this.i + 2, this.i + 4) == '--') key = '-->';
+	else if (this.data.substring(this.i + 2, this.i + 9) == '[CDATA[') key = ']]>';
+	else key = '>';
+	if ((this.i = this.data.indexOf(key, this.i + 2)) == -1) this.i = this.data.length;
+	else this.i += key.length - 1;
+	this.start = this.i + 1;
+	this.state = this.Text;
+}
+MpHtmlParser.prototype.TagName = function(c) {
+	if (blankChar[c]) {
+		this.tagName = this.section();
+		while (blankChar[this.data[this.i]]) this.i++;
+		if (this.isClose()) this.setNode();
+		else {
+			this.start = this.i;
+			this.state = this.AttrName;
+		}
+	} else if (this.isClose()) {
+		this.tagName = this.section();
+		this.setNode();
+	}
+}
+MpHtmlParser.prototype.AttrName = function(c) {
+	if (c == '=' || blankChar[c] || this.isClose()) {
+		this.attrName = this.section();
+		if (blankChar[c])
+			while (blankChar[this.data[++this.i]]);
+		if (this.data[this.i] == '=') {
+			while (blankChar[this.data[++this.i]]);
+			this.start = this.i--;
+			this.state = this.AttrValue;
+		} else this.setAttr();
+	}
+}
+MpHtmlParser.prototype.AttrValue = function(c) {
+	if (c == '"' || c == "'") {
+		this.start++;
+		if ((this.i = this.data.indexOf(c, this.i + 1)) == -1) return this.i = this.data.length;
+		this.attrVal = this.section();
+		this.i++;
+	} else {
+		for (; !blankChar[this.data[this.i]] && !this.isClose(); this.i++);
+		this.attrVal = this.section();
+	}
+	this.setAttr();
+}
+MpHtmlParser.prototype.EndTag = function(c) {
+	if (blankChar[c] || c == '>' || c == '/') {
+		var name = this.section().toLowerCase();
+		for (var i = this.STACK.length; i--;)
+			if (this.STACK[i].name == name) break;
+		if (i != -1) {
+			var node;
+			while ((node = this.STACK.pop()).name != name) this.popNode(node);
+			this.popNode(node);
+		} else if (name == 'p' || name == 'br')
+			this.siblings().push({
+				name,
+				attrs: {}
+			});
+		this.i = this.data.indexOf('>', this.i);
+		this.start = this.i + 1;
+		if (this.i == -1) this.i = this.data.length;
+		else this.state = this.Text;
+	}
+}
+module.exports = MpHtmlParser;

+ 80 - 0
components/jyf-parser/libs/config.js

@@ -0,0 +1,80 @@
+/* 配置文件 */
+var cfg = {
+	// 出错占位图
+	errorImg: null,
+	// 过滤器函数
+	filter: null,
+	// 代码高亮函数
+	highlight: null,
+	// 文本处理函数
+	onText: null,
+	// 实体编码列表
+	entities: {
+		quot: '"',
+		apos: "'",
+		semi: ';',
+		nbsp: '\xA0',
+		ensp: '\u2002',
+		emsp: '\u2003',
+		ndash: '–',
+		mdash: '—',
+		middot: '·',
+		lsquo: '‘',
+		rsquo: '’',
+		ldquo: '“',
+		rdquo: '”',
+		bull: '•',
+		hellip: '…'
+	},
+	blankChar: makeMap(' ,\xA0,\t,\r,\n,\f'),
+	boolAttrs: makeMap('allowfullscreen,autoplay,autostart,controls,ignore,loop,muted'),
+	// 块级标签,将被转为 div
+	blockTags: makeMap('address,article,aside,body,caption,center,cite,footer,header,html,nav,pre,section'),
+	// 将被移除的标签
+	ignoreTags: makeMap('area,base,canvas,frame,iframe,input,link,map,meta,param,script,source,style,svg,textarea,title,track,wbr'),
+	// 只能被 rich-text 显示的标签
+	richOnlyTags: makeMap('a,colgroup,fieldset,legend,table'),
+	// 自闭合的标签
+	selfClosingTags: makeMap('area,base,br,col,circle,ellipse,embed,frame,hr,img,input,line,link,meta,param,path,polygon,rect,source,track,use,wbr'),
+	// 信任的标签
+	trustTags: makeMap('a,abbr,ad,audio,b,blockquote,br,code,col,colgroup,dd,del,dl,dt,div,em,fieldset,h1,h2,h3,h4,h5,h6,hr,i,img,ins,label,legend,li,ol,p,q,source,span,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,title,ul,video'),
+	// 默认的标签样式
+	userAgentStyles: {
+		address: 'font-style:italic',
+		big: 'display:inline;font-size:1.2em',
+		blockquote: 'background-color:#f6f6f6;border-left:3px solid #dbdbdb;color:#6c6c6c;padding:5px 0 5px 10px',
+		caption: 'display:table-caption;text-align:center',
+		center: 'text-align:center',
+		cite: 'font-style:italic',
+		dd: 'margin-left:40px',
+		mark: 'background-color:yellow',
+		pre: 'font-family:monospace;white-space:pre;overflow:scroll',
+		s: 'text-decoration:line-through',
+		small: 'display:inline;font-size:0.8em',
+		u: 'text-decoration:underline'
+	}
+}
+
+function makeMap(str) {
+	var map = Object.create(null),
+		list = str.split(',');
+	for (var i = list.length; i--;)
+		map[list[i]] = true;
+	return map;
+}
+
+// #ifdef MP-WEIXIN
+if (wx.canIUse('editor')) {
+	cfg.blockTags.pre = void 0;
+	cfg.ignoreTags.rp = true;
+	Object.assign(cfg.richOnlyTags, makeMap('bdi,bdo,caption,rt,ruby'));
+	Object.assign(cfg.trustTags, makeMap('bdi,bdo,caption,pre,rt,ruby'));
+}
+// #endif
+
+// #ifdef APP-PLUS
+cfg.ignoreTags.iframe = void 0;
+Object.assign(cfg.trustTags, makeMap('embed,iframe'));
+// #endif
+
+module.exports = cfg;

+ 22 - 0
components/jyf-parser/libs/handler.wxs

@@ -0,0 +1,22 @@
+var inline = {
+	abbr: 1,
+	b: 1,
+	big: 1,
+	code: 1,
+	del: 1,
+	em: 1,
+	i: 1,
+	ins: 1,
+	label: 1,
+	q: 1,
+	small: 1,
+	span: 1,
+	strong: 1,
+	sub: 1,
+	sup: 1
+}
+module.exports = {
+	use: function(item) {
+		return !item.c && !inline[item.name] && (item.attrs.style || '').indexOf('display:inline') == -1
+	}
+}

+ 501 - 0
components/jyf-parser/libs/trees.vue

@@ -0,0 +1,501 @@
+<template>
+	<view :class="'interlayer '+(c||'')" :style="s">
+		<block v-for="(n, i) in nodes" v-bind:key="i">
+			<!--图片-->
+			<view v-if="n.name=='img'" :class="'_img '+n.attrs.class" :style="n.attrs.style" :data-attrs="n.attrs" @tap="imgtap">
+				<rich-text v-if="ctrl[i]!=0" :nodes="[{attrs:{src:loading&&(ctrl[i]||0)<2?loading:(lazyLoad&&!ctrl[i]?placeholder:(ctrl[i]==3?errorImg:n.attrs.src||'')),alt:n.attrs.alt||'',width:n.attrs.width||'',style:'-webkit-touch-callout:none;max-width:100%;display:block'+(n.attrs.height?';height:'+n.attrs.height:'')},name:'img'}]" />
+				<image class="_image" :src="lazyLoad&&!ctrl[i]?placeholder:n.attrs.src" :lazy-load="lazyLoad"
+				 :show-menu-by-longpress="!n.attrs.ignore" :data-i="i" :data-index="n.attrs.i" data-source="img" @load="loadImg"
+				 @error="error" />
+			</view>
+			<!--文本-->
+			<text v-else-if="n.type=='text'" decode>{{n.text}}</text>
+			<!--#ifndef MP-BAIDU-->
+			<text v-else-if="n.name=='br'">\n</text>
+			<!--#endif-->
+			<!--视频-->
+			<view v-else-if="((n.lazyLoad&&!n.attrs.autoplay)||(n.name=='video'&&!loadVideo))&&ctrl[i]==undefined" :id="n.attrs.id" :class="'_video '+(n.attrs.class||'')"
+			 :style="n.attrs.style" :data-i="i" @tap="_loadVideo" />
+			<video v-else-if="n.name=='video'" :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :autoplay="n.attrs.autoplay||ctrl[i]==0"
+			 :controls="n.attrs.controls" :loop="n.attrs.loop" :muted="n.attrs.muted" :poster="n.attrs.poster" :src="n.attrs.source[ctrl[i]||0]"
+			 :unit-id="n.attrs['unit-id']" :data-id="n.attrs.id" :data-i="i" data-source="video" @error="error" @play="play" />
+			<!--音频-->
+			<audio v-else-if="n.name=='audio'" :ref="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :author="n.attrs.author"
+			 :autoplay="n.attrs.autoplay" :controls="n.attrs.controls" :loop="n.attrs.loop" :name="n.attrs.name" :poster="n.attrs.poster"
+			 :src="n.attrs.source[ctrl[i]||0]" :data-i="i" :data-id="n.attrs.id" data-source="audio"
+			 @error.native="error" @play.native="play" />
+			<!--链接-->
+			<view v-else-if="n.name=='a'" :id="n.attrs.id" :class="'_a '+(n.attrs.class||'')" hover-class="_hover" :style="n.attrs.style"
+			 :data-attrs="n.attrs" @tap="linkpress">
+				<trees class="_span" c="_span" :nodes="n.children" />
+			</view>
+			<!--广告-->
+			<!--<ad v-else-if="n.name=='ad'" :class="n.attrs.class" :style="n.attrs.style" :unit-id="n.attrs['unit-id']" :appid="n.attrs.appid" :apid="n.attrs.apid" :type="n.attrs.type" :adpid="n.attrs.adpid" data-source="ad" @error="error" />-->
+			<!--列表-->
+			<view v-else-if="n.name=='li'" :id="n.attrs.id" :class="n.attrs.class" :style="(n.attrs.style||'')+';display:flex;flex-direction:row'">
+				<view v-if="n.type=='ol'" class="_ol-bef">{{n.num}}</view>
+				<view v-else class="_ul-bef">
+					<view v-if="n.floor%3==0" class="_ul-p1">█</view>
+					<view v-else-if="n.floor%3==2" class="_ul-p2" />
+					<view v-else class="_ul-p1" style="border-radius:50%">█</view>
+				</view>
+				<trees class="_li" c="_li" :nodes="n.children" :lazyLoad="lazyLoad" :loading="loading" />
+			</view>
+			<!--表格-->
+			<view v-else-if="n.name=='table'&&n.c" :id="n.attrs.id" :class="n.attrs.class" :style="(n.attrs.style||'')+';display:table'">
+				<view v-for="(tbody, o) in n.children" v-bind:key="o" :class="tbody.attrs.class" :style="(tbody.attrs.style||'')+(tbody.name[0]=='t'?';display:table-'+(tbody.name=='tr'?'row':'row-group'):'')">
+					<view v-for="(tr, p) in tbody.children" v-bind:key="p" :class="tr.attrs.class" :style="(tr.attrs.style||'')+(tr.name[0]=='t'?';display:table-'+(tr.name=='tr'?'row':'cell'):'')">
+						<trees v-if="tr.name=='td'" :nodes="tr.children" />
+						<trees v-else v-for="(td, q) in tr.children" v-bind:key="q" :class="td.attrs.class" :c="td.attrs.class" :style="(td.attrs.style||'')+(td.name[0]=='t'?';display:table-'+(td.name=='tr'?'row':'cell'):'')"
+						 :s="(td.attrs.style||'')+(td.name[0]=='t'?';display:table-'+(td.name=='tr'?'row':'cell'):'')" :nodes="td.children" />
+					</view>
+				</view>
+			</view>
+			<!--#ifdef APP-PLUS-->
+			<iframe v-else-if="n.name=='iframe'" :style="n.attrs.style" :allowfullscreen="n.attrs.allowfullscreen" :frameborder="n.attrs.frameborder"
+			 :width="n.attrs.width" :height="n.attrs.height" :src="n.attrs.src" />
+			<embed v-else-if="n.name=='embed'" :style="n.attrs.style" :width="n.attrs.width" :height="n.attrs.height" :src="n.attrs.src" />
+			<!--#endif-->
+			<!--富文本-->
+			<!--#ifdef MP-WEIXIN || MP-QQ || APP-PLUS-->
+			<rich-text v-else-if="handler.use(n)" :id="n.attrs.id" :class="'_p __'+n.name" :nodes="[n]" />
+			<!--#endif-->
+			<!--#ifndef MP-WEIXIN || MP-QQ || APP-PLUS-->
+			<rich-text v-else-if="!n.c" :id="n.attrs.id" :nodes="[n]" style="display:inline" />
+			<!--#endif-->
+			<trees v-else :class="(n.attrs.id||'')+' _'+n.name+' '+(n.attrs.class||'')" :c="(n.attrs.id||'')+' _'+n.name+' '+(n.attrs.class||'')"
+			 :style="n.attrs.style" :s="n.attrs.style" :nodes="n.children" :lazyLoad="lazyLoad" :loading="loading" />
+		</block>
+	</view>
+</template>
+<script module="handler" lang="wxs" src="./handler.wxs"></script>
+<script>
+	global.Parser = {};
+	import trees from './trees'
+	const errorImg = require('../libs/config.js').errorImg;
+	export default {
+		components: {
+			trees
+		},
+		name: 'trees',
+		data() {
+			return {
+				ctrl: [],
+				placeholder: 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="300" height="225"/>',
+				errorImg,
+				loadVideo: typeof plus == 'undefined',
+				// #ifndef MP-ALIPAY
+				c: '',
+				s: ''
+				// #endif
+			}
+		},
+		props: {
+			nodes: Array,
+			lazyLoad: Boolean,
+			loading: String,
+			// #ifdef MP-ALIPAY
+			c: String,
+			s: String
+			// #endif
+		},
+		mounted() {
+			for (this.top = this.$parent; this.top.$options.name != 'parser'; this.top = this.top.$parent);
+			this.init();
+		},
+		// #ifdef APP-PLUS
+		beforeDestroy() {
+			this.observer && this.observer.disconnect();
+		},
+		// #endif
+		methods: {
+			init() {
+				for (var i = this.nodes.length, n; n = this.nodes[--i];) {
+					if (n.name == 'img') {
+						this.top.imgList.setItem(n.attrs.i, n.attrs.src);
+						// #ifdef APP-PLUS
+						if (this.lazyLoad && !this.observer) {
+							this.observer = uni.createIntersectionObserver(this).relativeToViewport({
+								top: 500,
+								bottom: 500
+							});
+							setTimeout(() => {
+								this.observer.observe('._img', res => {
+									if (res.intersectionRatio) {
+										for (var j = this.nodes.length; j--;)
+											if (this.nodes[j].name == 'img')
+												this.$set(this.ctrl, j, 1);
+										this.observer.disconnect();
+									}
+								})
+							}, 0)
+						}
+						// #endif
+					} else if (n.name == 'video' || n.name == 'audio') {
+						var ctx;
+						if (n.name == 'video') {
+							ctx = uni.createVideoContext(n.attrs.id
+								// #ifndef MP-BAIDU
+								, this
+								// #endif
+							);
+						} else if (this.$refs[n.attrs.id])
+							ctx = this.$refs[n.attrs.id][0];
+						if (ctx) {
+							ctx.id = n.attrs.id;
+							this.top.videoContexts.push(ctx);
+						}
+					}
+				}
+				// #ifdef APP-PLUS
+				// APP 上避免 video 错位需要延时渲染
+				setTimeout(() => {
+					this.loadVideo = true;
+				}, 1000)
+				// #endif
+			},
+			play(e) {
+				var contexts = this.top.videoContexts;
+				if (contexts.length > 1 && this.top.autopause)
+					for (var i = contexts.length; i--;)
+						if (contexts[i].id != e.currentTarget.dataset.id)
+							contexts[i].pause();
+			},
+			imgtap(e) {
+				var attrs = e.currentTarget.dataset.attrs;
+				if (!attrs.ignore) {
+					var preview = true,
+						data = {
+							id: e.target.id,
+							src: attrs.src,
+							ignore: () => preview = false
+						};
+					global.Parser.onImgtap && global.Parser.onImgtap(data);
+					this.top.$emit('imgtap', data);
+					if (preview) {
+						var urls = this.top.imgList,
+							current = urls[attrs.i] ? parseInt(attrs.i) : (urls = [attrs.src], 0);
+						uni.previewImage({
+							current,
+							urls
+						})
+					}
+				}
+			},
+			loadImg(e) {
+				var i = e.currentTarget.dataset.i;
+				if (this.lazyLoad && !this.ctrl[i]) {
+					// #ifdef QUICKAPP-WEBVIEW
+					this.$set(this.ctrl, i, 0);
+					this.$nextTick(function() {
+						// #endif
+						// #ifndef APP-PLUS
+						this.$set(this.ctrl, i, 1);
+						// #endif
+						// #ifdef QUICKAPP-WEBVIEW
+					})
+					// #endif
+				} else if (this.loading && this.ctrl[i] != 2) {
+					// #ifdef QUICKAPP-WEBVIEW
+					this.$set(this.ctrl, i, 0);
+					this.$nextTick(function() {
+						// #endif
+						this.$set(this.ctrl, i, 2);
+						// #ifdef QUICKAPP-WEBVIEW
+					})
+					// #endif
+				}
+			},
+			linkpress(e) {
+				var jump = true,
+					attrs = e.currentTarget.dataset.attrs;
+				attrs.ignore = () => jump = false;
+				global.Parser.onLinkpress && global.Parser.onLinkpress(attrs);
+				this.top.$emit('linkpress', attrs);
+				if (jump) {
+					// #ifdef MP
+					if (attrs['app-id']) {
+						return uni.navigateToMiniProgram({
+							appId: attrs['app-id'],
+							path: attrs.path
+						})
+					}
+					// #endif
+					if (attrs.href) {
+						if (attrs.href[0] == '#') {
+							if (this.top.useAnchor)
+								this.top.navigateTo({
+									id: attrs.href.substring(1)
+								})
+						} else if (attrs.href.indexOf('http') == 0 || attrs.href.indexOf('//') == 0) {
+							// #ifdef APP-PLUS
+							plus.runtime.openWeb(attrs.href);
+							// #endif
+							// #ifndef APP-PLUS
+							uni.setClipboardData({
+								data: attrs.href,
+								success: () =>
+									uni.showToast({
+										title: '链接已复制'
+									})
+							})
+							// #endif
+						} else
+							uni.navigateTo({
+								url: attrs.href,
+								fail() {
+									uni.switchTab({
+										url: attrs.href,
+									})
+								}
+							})
+					}
+				}
+			},
+			error(e) {
+				var target = e.currentTarget,
+					source = target.dataset.source,
+					i = target.dataset.i;
+				if (source == 'video' || source == 'audio') {
+					// 加载其他 source
+					var index = this.ctrl[i] ? this.ctrl[i].i + 1 : 1;
+					if (index < this.nodes[i].attrs.source.length)
+						this.$set(this.ctrl, i, index);
+					if (e.detail.__args__)
+						e.detail = e.detail.__args__[0];
+				} else if (errorImg && source == 'img') {
+					this.top.imgList.setItem(target.dataset.index, errorImg);
+					this.$set(this.ctrl, i, 3);
+				}
+				this.top && this.top.$emit('error', {
+					source,
+					target,
+					errMsg: e.detail.errMsg
+				});
+			},
+			_loadVideo(e) {
+				this.$set(this.ctrl, e.target.dataset.i, 0);
+			}
+		}
+	}
+</script>
+
+<style>
+	/* 在这里引入自定义样式 */
+
+	/* 链接和图片效果 */
+	._a {
+		display: inline;
+		padding: 1.5px 0 1.5px 0;
+		color: #366092;
+		word-break: break-all;
+	}
+
+	._hover {
+		text-decoration: underline;
+		opacity: 0.7;
+	}
+
+	._img {
+		display: inline-block;
+		max-width: 100%;
+		overflow: hidden;
+	}
+
+	/* #ifdef MP-WEIXIN */
+	:host {
+		display: inline;
+	}
+
+	/* #endif */
+
+	/* #ifndef MP-ALIPAY || APP-PLUS */
+	.interlayer {
+		display: inherit;
+		flex-direction: inherit;
+		flex-wrap: inherit;
+		align-content: inherit;
+		align-items: inherit;
+		justify-content: inherit;
+		width: 100%;
+		white-space: inherit;
+	}
+
+	/* #endif */
+
+	._b,
+	._strong {
+		font-weight: bold;
+	}
+
+	/* #ifndef MP-ALIPAY */
+	._blockquote,
+	._div,
+	._p,
+	._ol,
+	._ul,
+	._li {
+		display: block;
+	}
+	
+	/* #endif */
+
+	._code {
+		font-family: monospace;
+	}
+
+	._del {
+		text-decoration: line-through;
+	}
+
+	._em,
+	._i {
+		font-style: italic;
+	}
+
+	._h1 {
+		font-size: 2em;
+	}
+
+	._h2 {
+		font-size: 1.5em;
+	}
+
+	._h3 {
+		font-size: 1.17em;
+	}
+
+	._h5 {
+		font-size: 0.83em;
+	}
+
+	._h6 {
+		font-size: 0.67em;
+	}
+
+	._h1,
+	._h2,
+	._h3,
+	._h4,
+	._h5,
+	._h6 {
+		display: block;
+		font-weight: bold;
+	}
+
+	._image {
+		display: block;
+		width: 100%;
+		height: 360px;
+		margin-top: -360px;
+		opacity: 0;
+	}
+
+	._ins {
+		text-decoration: underline;
+	}
+
+	._li {
+		flex: 1;
+		width: 0;
+	}
+
+	._ol-bef {
+		width: 36px;
+		margin-right: 5px;
+		text-align: right;
+	}
+
+	._ul-bef {
+		display: block;
+		margin: 0 12px 0 23px;
+		line-height: normal;
+	}
+
+	._ol-bef,
+	._ul-bef {
+		flex: none;
+		user-select: none;
+	}
+
+	._ul-p1 {
+		display: inline-block;
+		width: 0.3em;
+		height: 0.3em;
+		overflow: hidden;
+		line-height: 0.3em;
+	}
+
+	._ul-p2 {
+		display: inline-block;
+		width: 0.23em;
+		height: 0.23em;
+		border: 0.05em solid black;
+		border-radius: 50%;
+	}
+
+	._q::before {
+		content: '"';
+	}
+
+	._q::after {
+		content: '"';
+	}
+
+	._sub {
+		font-size: smaller;
+		vertical-align: sub;
+	}
+
+	._sup {
+		font-size: smaller;
+		vertical-align: super;
+	}
+
+	/* #ifdef MP-ALIPAY || APP-PLUS || QUICKAPP-WEBVIEW */
+	._abbr,
+	._b,
+	._code,
+	._del,
+	._em,
+	._i,
+	._ins,
+	._label,
+	._q,
+	._span,
+	._strong,
+	._sub,
+	._sup {
+		display: inline;
+	}
+
+	/* #endif */
+
+	/* #ifdef MP-WEIXIN || MP-QQ */
+	.__bdo,
+	.__bdi,
+	.__ruby,
+	.__rt {
+		display: inline-block;
+	}
+
+	/* #endif */
+	._video {
+		position: relative;
+		display: inline-block;
+		width: 300px;
+		height: 225px;
+		background-color: black;
+	}
+
+	._video::after {
+		position: absolute;
+		top: 50%;
+		left: 50%;
+		margin: -15px 0 0 -15px;
+		content: '';
+		border-color: transparent transparent transparent white;
+		border-style: solid;
+		border-width: 15px 0 15px 30px;
+	}
+</style>

+ 421 - 0
components/newlist/nowList.vue

@@ -0,0 +1,421 @@
+<template>
+	<view class="other">
+		<view class="other-1">大家还在拼</view>
+		
+		<view class="preferred_item" v-for="item in recommendedlist" @click.stop="ToKaiTuan(item)">
+			<view class="flex_item" style="overflow: hidden;">
+				<view class="tlist-img">
+					<view class="leftImgIcon">AA团</view>
+					<!-- <view class="leftImgIcon" v-if="sid == 129">达人团</view> -->
+					<view class="img"><image :src="item.image" mode="scaleToFill"></image></view>
+				</view>
+				<view class="tlist-img " v-for="imgItem in item.images">
+					<view class="img"><image :src="imgItem" mode="scaleToFill"></image></view>
+				</view>
+			</view>
+			<view class="goods_name">
+				<view class="goods_title flex_item">
+					<view class="text">{{ item.min_people }}人团</view>
+					<view class="title">{{ item.title }}</view>
+				</view>
+				<view class="goods-height">
+					<!-- <view class="goods_num clamp">{{ item.info }}</view> -->
+					<view class="flex goods-peplo">
+						<view class="goods-tip flex_item">
+							<view class="peplo">库存剩{{ item.percent | parseIntTo }}%</view>
+							<view class="make">{{ item.mark }}</view>
+						</view>
+						<view class="right flex_item">
+							<image src="/static/icon/hot.png" mode="aspectFill"></image>
+							<text>已拼{{ item.sales }}份</text>
+						</view>
+					</view>
+				</view>
+				<view class="price flex">
+					<view class="price_list">
+						<view class="price-red">
+							<text>单人仅付:</text>
+							<text class="moneyIcon">¥</text>
+							<text class="money">{{ item.price }}</text>
+							<text class="moneyType">/{{ item.unit_name }}</text>
+							<!-- <text class="outMoney">¥{{ item.product_price }}</text> -->
+						</view>
+					</view>
+					<view class="img position-relative" @click.stop="ToKaiTuan(item)">去开团</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		recommendedlist: {
+			type: Array,
+			default: function() {
+				return {
+				};
+			}
+		}
+	},
+	data() {
+		return {};
+	},
+	filters: {
+		parseIntTo(percent) {
+			percent = +percent * 100;
+			if (percent % 1 === 0) {
+				return percent;
+			} else {
+				percent = percent.toFixed(1);
+				return percent;
+			}
+		}
+	},
+	methods: {
+		// 去开团
+		ToKaiTuan(item) {
+			console.log(8754847)
+			let id = item.id;
+			uni.navigateTo({
+				url: '/pages/product/productGroup?id=' + id
+			});
+		},
+	}
+};
+</script>
+
+<style lang="scss">
+.other{
+		.other-1{
+			font-size:27rpx;
+			color:#333333;
+			line-height: 55rpx;
+		}
+		
+		.preferred_item {
+			width: 100%;
+			height: 100%;
+			padding: 25rpx 25rpx;
+			position: relative;
+			background-color: #FFFFFF;
+			border-radius: 15rpx;
+			margin-bottom: 15rpx;
+			.tlist-img {
+				width: 225rpx;
+				position: relative;
+				margin-right: 15rpx;
+				.leftImgIcon {
+					position: absolute;
+					top: 0;
+					left: 0;
+					font-size: 22rpx;
+					font-family: PingFangSC;
+					color: rgba(148, 71, 34, 1);
+					background: rgba(254, 242, 111, 1);
+					z-index: 99;
+					border-radius: 5rpx;
+					padding: 5rpx 10rpx;
+				}
+				.img {
+					width: 210rpx;
+					height: 210rpx;
+					image {
+						width: 100%;
+						height: 100%;
+						border-radius: 20rpx;
+					}
+				}
+				.stock {
+					margin-top: 13rpx;
+					font-size: 26rpx;
+					background: #fff1ee;
+					width: 100%;
+					color: #fb4912;
+					padding: 6rpx 0;
+					border-radius: 5rpx;
+					justify-content: center;
+					align-items: center;
+					position: absolute;
+					left: 0;
+					bottom: 0;
+					.img {
+						width: 20rpx;
+						height: 20rpx;
+						flex-shrink: 0;
+					}
+					.stock-num {
+						padding-left: 7rpx;
+						font-size: 22rpx;
+						border-radius: 5rpx;
+						height: 32rpx;
+						line-height: 32rpx;
+					}
+				}
+			}
+			.goods_name {
+				.goods_title {
+					padding-top: 15rpx;
+					color:rgba(0,0,0,1);
+					// white-space: nowrap;
+					// overflow: hidden;
+					// text-overflow: ellipsis;
+					font-size:32rpx;
+					color: $font-color-dark;
+					// height: 70rpx;
+					align-items: baseline;
+					.text{
+						border-radius: 8rpx;
+						border: 2rpx solid #FF1A27;
+						color: #FF1A27;
+						padding:0rpx 10rpx;
+						font-size: 26rpx !important;
+						margin-right: 15rpx;
+						
+					}
+					.title {
+						width: 80%;
+						overflow : hidden;
+						text-overflow: ellipsis;
+						display: -webkit-box;
+						-webkit-line-clamp: 2;
+						-webkit-box-orient: vertical;
+					}
+				}
+				.goods-height {
+					min-height: 60rpx;
+				}
+				.goods_num {
+					font-size: 26rpx;
+					color: #8f8f97;
+					padding-bottom: 15rpx;
+				}
+				.goods-peplo {
+					height: 45rpx;
+					margin-top: 15rpx;
+					.right {
+						color:#8e8e8e;
+						font-size: 24rpx;
+						width:195rpx;
+						image {
+							width: 30rpx;
+							height: 33rpx;
+							margin-right: 15rpx;
+						}
+					}
+					.goods-tip {
+						.peplo {
+							background:linear-gradient(14deg,rgba(255,116,37,1),rgba(255,30,41,1));
+							padding: 5rpx 10rpx;
+							color: #ffffff;
+							border-top-left-radius:8rpx;
+							border-bottom-left-radius: 8rpx;
+						}
+						.make {
+							background-color: #fef26f;
+							color: #944722;
+							border-top-right-radius: 8rpx;
+							border-bottom-right-radius: 8rpx;
+						}
+						.make,
+						.peplo {
+							font-size: $font-sm;
+							padding: 5rpx 10rpx;
+						}
+					}
+				}
+				.price {
+					font-size: 28rpx;
+					position: relative;
+					padding-top: 15rpx;
+					.price_list {
+						.price-red {
+							font-size: 30rpx !important;
+							font-family: Source Han Sans CN;
+							color: rgba(253, 27, 42, 1);
+							font-size: $font-base;
+							font-weight: bold;
+							.moneyIcon {
+								font-weight: normal !important; 
+							}
+							.money {
+								font-size: 58rpx;
+							}
+							.moneyType {
+								font-weight: 400;
+							}
+							.outMoney {
+								font-weight: 400;
+								text-decoration: line-through;
+								color: rgba(142, 142, 142, 1);
+							}
+						}
+						.price-green {
+							color: #2dbd59;
+							font-size: 26rpx !important;
+							font-weight: bold;
+							text {
+								background: linear-gradient(45deg, rgba(21, 197, 52, 1), rgba(21, 197, 52, 1));
+								color: #ffffff;
+								padding: 0rpx 10rpx;
+								border-radius: 7rpx;
+								font-size: 24rpx !important;
+								margin-left: 15rpx;
+							}
+						}
+					}
+					.img {
+						width: 265rpx;
+						height: 74rpx;
+						line-height: 74rpx;
+						// background:linear-gradient(14deg,rgba(255,116,37,1),rgba(255,30,41,1));
+						background: linear-gradient(270deg, rgba(181,116,242, 1) 0%, rgba(139,86,254, 1) 100%);
+						border-radius: 99rpx;
+						color: #ffffff;
+						font-size: $font-lg;
+						text-align: center;
+					}
+					.img1{
+						background-color: #D3D3D3;
+						width: 265rpx;
+						height: 74rpx;
+						line-height: 74rpx;
+						border-radius: 99rpx;
+						color: #ffffff;
+						font-size: $font-lg;
+						text-align: center;
+					}
+					.tomorrow {
+						background: #29a66e;
+						color: #ffffff;
+						border-radius: 25rpx;
+						padding: 10rpx 25rpx;
+					}
+				}
+			}
+		}
+		
+		
+		.other-2{
+			width: 100%;
+			background:#ffffff;
+			padding: 10rpx 15rpx ;
+			border-radius: 20rpx;
+			.content-row{
+				padding: 10rpx 0;
+				.row-1{
+					width: 210rpx;
+					position: relative;
+					margin-right: 20rpx;
+					text-align: center;
+					.row-1-1{
+						text-align: center;
+						height:40rpx;
+						background:#fff1ee;
+						border-radius:6rpx;
+						font-size:22rpx;
+						font-weight:500;
+						color:#fb4912;
+						line-height:40rpx;
+						image{
+							width: 23rpx;
+							height: 23rpx;
+							margin-right: 5rpx;
+						}
+					}
+					.img1{
+						width:170rpx;
+						height:170rpx;
+						border-radius:10rpx;
+					}
+					.img2{
+						position: absolute;
+						top: 0;
+						left: 16rpx;
+						width:80rpx;
+						height:32rpx;
+						border-radius:5px;
+					}
+				}
+				.row-2{
+					padding: 20rpx 0 20rpx 0;
+					width:calc(100% - 210rpx);
+					position: relative;
+					
+					border-bottom: 1px solid #EAEAEA;
+					.word-1{
+						font-size:32rpx;
+						font-weight:bold;
+						color:#141821;
+						margin-left: 10rpx;
+						overflow: hidden;
+						text-overflow: ellipsis;
+						white-space: nowrap;
+					}
+					.word-2{
+						margin-top: 10rpx;
+						font-size:24rpx;
+						color:#979797;
+					}
+					.word-3{
+						margin-top: 10rpx;
+						position: relative;
+						font-size:20rpx;
+						color:#ffffff;
+						image{
+							width: 235rpx;
+							height: 50rpx;
+						}
+						.word-3-1{
+							position: absolute;
+							top:8px;
+							left:2px;
+							width: 230rpx;
+							text-align: center;
+							.word-3-1-1{
+								display: inline-block;
+								color:#FD1B2A;
+								width: 50%;
+							}
+							
+						}
+					}
+					.word-4{
+						margin-top: 15rpx;
+						font-size:23rpx;
+						color:#fd1b2a;
+						margin-left: 10rpx;
+						text{
+							font-size:23rpx;
+							font-weight:bold;
+						}
+						.word-4-1{
+							font-size:36rpx;
+						}
+					}
+					.word-5{
+						margin-top: 15rpx;
+						font-size:21rpx;
+						color:#868686;
+					}
+					.button{
+						width:145rpx;
+						height:60rpx;
+						background:linear-gradient(14deg,#ff7425,#ff1e29);
+						border-radius:30rpx;
+						font-size:27rpx;
+						font-weight:bold;
+						color:#ffffff;
+						line-height:60rpx;
+						text-align: center;
+						
+						position: absolute;
+						bottom: 20rpx;
+						right: 0;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 68 - 0
components/returnButton.vue

@@ -0,0 +1,68 @@
+<template>
+	<view class="content">
+		<view class="retun-Box" @click="navTo('/pages/order/order?state=0')">
+			<image class="return-img" src=".././static/tabBar/dingdan.png"></image>
+			<view class="return-text">订单</view>
+		</view>
+		<view class="retun-Box" @click="GoHome">
+			<image class="return-img" src=".././static/tabBar/home.png" ></image>
+			<view class="return-text">首页</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		
+	},
+	data() {
+		return {
+			
+		}
+	},
+	methods: {
+		navTo(url) {
+			uni.navigateTo({
+				url: url
+			})
+		},
+		GoHome() {
+			// uni.navigateTo({
+			// 	url: '/pages/groupBooking/index'
+			// })
+			uni.switchTab({
+				url: '/pages/index/index'
+			})
+		}
+	}
+}
+</script>
+
+<style lang="scss">
+.content {
+	position: fixed;
+	right: 0;
+	bottom: 330rpx;
+	.retun-Box {
+		border-radius: 50%;
+		background: #FFFFFF;
+		width: 100rpx;
+		height: 100rpx;
+		margin-top: 30rpx;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		box-shadow: 0 6rpx 20rpx #888888;
+		.return-img {
+			width: 40rpx;
+			height: 40rpx;
+		}
+		.return-text {
+			font-size: $font-sm;
+			margin-top: 8rpx;
+		}
+	}
+}
+</style>

+ 246 - 0
components/seckill/seckill.vue

@@ -0,0 +1,246 @@
+<template>
+	<view class="seckill-section m-t" v-if="show">
+		<view class="s-header">
+			<view class="f-left-icon"></view>
+			<view class="tit-box"><text class="tit">限时秒杀</text></view>
+			<view class="tip-box">
+				<text class="tip" v-if="status == 1">{{ showTime }}点场结束</text>
+				<text class="tip" v-if="status == 2">距离下场开始</text>
+				<text class="tip" v-if="status == 0">当天活动已结束</text>
+				<uni-countdown v-if="status == 1 || status == 2" :show-day="false" :hour="stopTimeH" :minute="stopTimeM" :second="stopTimeS"></uni-countdown>
+			</view>
+			<view class="textNav iconfont iconenter" @click="navTo('/pages/product/seckill')">更多</view>
+		</view>
+		<scroll-view class="floor-list" scroll-x>
+			<view class="scoll-wrapper position-relative" @click="navTo('/pages/product/seckill')">
+				<view v-for="(item, index) in list" :key="index" class="floor-item">
+					<image class="list-image" :src="item.image" mode="aspectFill"></image>
+					<text class="title clamp">{{ item.title }}</text>
+					<text class="price">¥{{ item.price }}</text>
+				</view>
+				<view v-if="list.length == 0" class="floor-item ">
+					<image class="list-image" mode="aspectFill"></image>
+					<text class="title clamp"></text>
+					<text class="price"></text>
+				</view>
+				<view v-if="list.length == 0" class="noGoodsBg"><view>敬请期待</view></view>
+			</view>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+import uniCountdown from '@/components/uni-countdown/uni-countdown.vue';
+import { getSeckillList, getSeckillClass } from '@/api/product.js';
+import { timeComputed } from '@/utils/rocessor.js';
+export default {
+	components: {
+		uniCountdown
+	},
+	data() {
+		return {
+			list: [],
+			page: 1,
+			limit: 10,
+			showTime: '', //显示的时间
+			showTImeId: '', //显示时间id用于查询数据
+			stopTimeH: 0,
+			stopTimeM: 0,
+			stopTimeS: 0,
+			// 判断是否所有活动已经结束
+			stop: false, //活动是否已经结束
+			show: false, //是否显示活动
+			status: 0 //获取状态值1为有活动开始中 2为活动未开始 0为活动已经结束
+		};
+	},
+	created: function(e) {
+		// 载入分类
+		this.getClass();
+	},
+	methods: {
+		navTo(url) {
+			uni.navigateTo({
+				url
+			});
+		},
+		getList() {
+			getSeckillList(
+				{
+					page: this.page,
+					limit: this.limit
+				},
+				this.showTImeId
+			)
+				.then(e => {
+					this.list = e.data;
+				})
+				.catch(e => {
+					console.log(e);
+				});
+		},
+		getClass() {
+			let obj = this;
+			getSeckillClass({})
+				.then(({ data }) => {
+					let arr = data.seckillTime;
+					// 用于判断是否有数据
+					let showDate = false;
+					for (var i = 0; i < arr.length; i++) {
+						let ar = arr[i];
+						if (ar.status === 1 || ar.status === 2) {
+							obj.status = ar.status;
+							// 保存要显示的场次时间
+							obj.showTime = ar.time;
+							// 保存要显示活动商品的id
+							obj.showTImeId = ar.id;
+							// 保存当前状态值
+							// 计算倒计时时间
+							if (ar.status === 1) {
+								obj.timeComputed(ar.stop * 1000);
+							} else {
+								// 获取需要开始
+								let arTime = ar.time.split(':');
+								let h = arTime[0];
+								let m = arTime[1];
+								let time = new Date();
+								// 设置时间
+								time.setHours(h, m, 0);
+								obj.timeComputed(time.getTime());
+							}
+							// 获取商品列表
+							obj.getList();
+							// 保存当前有活动在举行
+							showDate = true;
+							// 任务查询结束跳出循环
+							break;
+						}
+					}
+					// 判断是否有活动
+					if (arr.length > 0) {
+						obj.show = true;
+					}
+					// 判断今天活动是否已经全部结束
+					if (!showDate) {
+						// 保存活动结束最后一个小时的活动商品
+						obj.showTImeId = arr[arr.length - 1].id;
+						// 活动已经结束
+						obj.status = 0;
+						// 获取结束时的商品
+						obj.getList();
+						console.log(obj.status);
+					}
+					// 如果所有场次均已经结束
+				})
+				.catch(e => {
+					uni.showModal({
+						title: JSON.stringify(e)
+					});
+				});
+		},
+		// 计算倒计时时间
+		timeComputed(da) {
+			let obj = this;
+			let stopTime = timeComputed(da)
+			obj.stopTimeH =stopTime.hours;
+			obj.stopTimeM = stopTime.minutes;
+			obj.stopTimeS =stopTime.seconds;
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+/* 秒杀专区 */
+.seckill-section {
+	padding: 4rpx 30rpx 24rpx;
+	.s-header {
+		display: flex;
+		align-items: center;
+		height: 92rpx;
+		line-height: 1;
+		.tit-box {
+			flex-shrink: 0;
+		}
+		.tit {
+			@extend %font-title;
+		}
+		.f-left-icon {
+			@extend %f-left-icon;
+		}
+		.textNav {
+			line-height: 1;
+			padding: 15rpx 0;
+			flex-shrink: 0;
+			flex-grow: 1;
+			min-width: 100rpx;
+		}
+		.tip-box {
+			flex-grow: 1;
+			display: flex;
+			justify-content: flex-start;
+			align-items: center;
+		}
+		.tip {
+			font-size: $font-sm;
+			color: $font-color-light;
+			padding-left: 10rpx;
+			padding-right: 10rpx;
+		}
+		.timer {
+			display: inline-block;
+			width: 40rpx;
+			height: 36rpx;
+			text-align: center;
+			line-height: 36rpx;
+			margin-right: 14rpx;
+			font-size: $font-sm + 2rpx;
+			color: #fff;
+			border-radius: 2px;
+			background: rgba(0, 0, 0, 0.8);
+		}
+		.iconenter {
+			font-size: $font-sm;
+			color: $font-color-light;
+			flex: 1;
+			text-align: right;
+		}
+	}
+	.floor-list {
+		white-space: nowrap;
+		background-color: white;
+		padding: 20rpx;
+		border-radius: 5rpx;
+		box-shadow: $box-shadow;
+	}
+	.scoll-wrapper {
+		display: flex;
+		align-items: flex-start;
+		.noGoodsBg {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			height: 100%;
+			position: absolute;
+			top: 0;
+			left: 0;
+			width: 100%;
+			color: $font-color-light;
+		}
+		.floor-item {
+			width: 150rpx;
+			margin-right: 20rpx;
+			font-size: $font-sm + 2rpx;
+			color: $font-color-dark;
+			line-height: 1.8;
+			.list-image {
+				width: 150rpx;
+				height: 150rpx;
+				border-radius: 6rpx;
+			}
+			.price {
+				color: $color-red;
+			}
+		}
+	}
+}
+</style>

+ 196 - 0
components/share.vue

@@ -0,0 +1,196 @@
+<template>
+	<view v-if="show" class="mask" @click="toggleMask" @touchmove.stop.prevent="stopPrevent" :style="{ backgroundColor: backgroundColor }">
+		<view
+			class="mask-content"
+			@click.stop.prevent="stopPrevent"
+			:style="[
+				{
+					height: config.height,
+					transform: transform
+				}
+			]"
+		>
+			<scroll-view class="view-content" scroll-y>
+				<view class="share-header">分享到</view>
+				<view class="share-list">
+					<view v-for="(item, index) in shareList" :key="index" class="share-item" @click="shareToFriend(item.text)">
+						<image class="itemImage" :src="item.icon" mode=""></image>
+						<text class="itemText">{{ item.text }}</text>
+					</view>
+				</view>
+			</scroll-view>
+			<view class="bottomButtom b-t" @click="toggleMask">取消</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			transform: 'translateY(50vh)',
+			timer: 0,
+			backgroundColor: 'rgba(0,0,0,0)',
+			show: false,
+			config: {}
+		};
+	},
+	props: {
+		contentHeight: {
+			type: Number,
+			default: 0
+		},
+		//是否是tabbar页面
+		hasTabbar: {
+			type: Boolean,
+			default: false
+		},
+		shareList: {
+			type: Array,
+			default: function() {
+				return [];
+			}
+		}
+	},
+	created() {
+		const height = uni.upx2px(this.contentHeight) + 'px';
+		this.config = {
+			height: height,
+			transform: `translateY(${height})`,
+			backgroundColor: 'rgba(0,0,0,.4)'
+		};
+		this.transform = this.config.transform;
+	},
+	methods: {
+		toggleMask() {
+			//防止高频点击
+			if (this.timer == 1) {
+				return;
+			}
+			this.timer = 1;
+			setTimeout(() => {
+				this.timer = 0;
+			}, 500);
+
+			if (this.show) {
+				this.transform = this.config.transform;
+				this.backgroundColor = 'rgba(0,0,0,0)';
+				setTimeout(() => {
+					this.show = false;
+					this.hasTabbar && uni.showTabBar();
+				}, 200);
+				return;
+			}
+
+			this.show = true;
+			//等待mask重绘完成执行
+			if (this.hasTabbar) {
+				uni.hideTabBar({
+					success: () => {
+						setTimeout(() => {
+							this.backgroundColor = this.config.backgroundColor;
+							this.transform = 'translateY(0px)';
+						}, 10);
+					}
+				});
+			} else {
+				setTimeout(() => {
+					this.backgroundColor = this.config.backgroundColor;
+					this.transform = 'translateY(0px)';
+				}, 10);
+			}
+		},
+		//防止冒泡和滚动穿透
+		stopPrevent() {},
+		//分享操作
+		shareToFriend(type) {
+			this.$api.msg(`分享给${type}`);
+			this.toggleMask();
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.mask {
+	position: fixed;
+	left: 0;
+	top: 0;
+	right: 0;
+	bottom: 0;
+	display: flex;
+	justify-content: center;
+	align-items: flex-end;
+	z-index: 998;
+	transition: 0.3s;
+	.bottomButtom {
+		position: absolute;
+		left: 0;
+		bottom: 0;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		width: 100%;
+		height: 90rpx;
+		background: #fff;
+		z-index: 9;
+		font-size: $font-base + 2rpx;
+		color: $font-color-dark;
+	}
+}
+.mask-content {
+	width: 100%;
+	height: 580rpx;
+	transition: 0.3s;
+	background: #fff;
+	&.has-bottom {
+		padding-bottom: 90rpx;
+	}
+	.view-content {
+		height: 100%;
+	}
+}
+.share-header {
+	height: 110rpx;
+	font-size: $font-base + 2rpx;
+	color: font-color-dark;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	padding-top: 10rpx;
+	&:before,
+	&:after {
+		content: '';
+		width: 240rpx;
+		heighg: 0;
+		border-top: 1px solid $border-color-base;
+		transform: scaleY(0.5);
+		margin-right: 30rpx;
+	}
+	&:after {
+		margin-left: 30rpx;
+		margin-right: 0;
+	}
+}
+.share-list {
+	display: flex;
+	flex-wrap: wrap;
+}
+.share-item {
+	min-width: 33.33%;
+	display: flex;
+	flex-direction: column;
+	justify-content: center;
+	align-items: center;
+	height: 180rpx;
+	.itemImage {
+		width: 80rpx;
+		height: 80rpx;
+		margin-bottom: 16rpx;
+	}
+	.itemText {
+		font-size: $font-base;
+		color: $font-color-base;
+	}
+}
+</style>

+ 218 - 0
components/ss-calendar/ss-calendar.vue

@@ -0,0 +1,218 @@
+<template>
+	<view class="calendar__wrap">
+		<view class="header">
+			<view class="current-date">{{ currentDate }}</view>
+		</view>
+		<view class="body">
+			<view class="weeks">
+				<view class="week__item" v-for="week in weeks" :key="week">{{ week }}</view>
+			</view>
+			<view class="day__list">
+				<view class="day__item" v-for="(item, index) in dateData" :key="index">
+					<view class="checked-box" :class="[checksClass]" v-if="item === 'checked'">
+						<text class="checked iconfont iconfavor" v-if="!checksIcon"></text>
+						<image v-else :src="checksIcon" mode="aspectFit"></image>
+						<view class="check_text" v-if="checkTextShow">{{ dayLoad(index) }}</view>
+					</view>
+					<text class="current-box" :class="[item === day ? (actionClass ? actionClass : 'current') : '']" v-else>{{ item }}</text>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		//选中的日期数据
+		checks: {
+			type: Array,
+			default() {
+				// 例子[1,2,3,4],表示本月1,2,3,4号4天被选中
+				return [];
+			}
+		},
+		// 选中物品的样式类
+		checksClass: {
+			type: String,
+			default: ''
+		},
+		// 选中时图标
+		checksIcon: {
+			type: String
+		},
+		// 选中时是否显示文字
+		checkTextShow: {
+			type: Boolean, 
+			default: false
+		},
+		// 表示当前日期的样式
+		actionClass: {
+			type: String,
+			default: ''
+		}
+	},
+	data() {
+		const { year, month, day } = this.getDate();
+		const dateData = this.getDateData(year, month);
+		const noDay = ( new Date(year,month-1,1)).getDay();
+		return {
+			year,
+			month,
+			day,
+			dateData,
+			noDay,//本月1号是星期几
+			weeks: ['日', '一', '二', '三', '四', '五', '六'],
+		};
+	},
+	computed: {
+		// 获取当前日期
+		currentDate() {
+			return `${this.year}-${this.format(this.month)}`;
+		}
+	},
+	watch: {
+		checks(val) {
+			const { year, month } = this.getDate();
+			const dateData = this.getDateData(year, month);
+			// 保存当前月份信息
+			this.dateData = dateData;
+		},
+	},
+	methods: {
+		// 重新计算时间
+		dayLoad: function(value) {
+			return value + 1 - this.noDay;
+		},
+		// 获取当前日期
+		getDate(current) {
+			const date = current ? new Date(current) : new Date();
+			const year = date.getFullYear();
+			// 月份值默认从0开始
+			const month = date.getMonth() + 1;
+			const day = date.getDate();
+			return {
+				year,
+				month,
+				day
+			};
+		},
+		// 日期处理
+		getDateData(year, month) {
+			// 新增月份时需要减少1个月
+			const date = new Date(year,month-1,1);
+			const firstDayWeek = date.getDay();
+			const data = [...this.getEmptys(firstDayWeek), ...this.getDays(firstDayWeek)];
+			return data;
+		},
+		// 查询日期列表有几个空格
+		getEmptys(count) {
+			let arr = [];
+			if (count) {
+				for (let i = 0; i < count; i++) {
+					arr.push('');
+				}
+			}
+			return arr;
+		},
+		getLastDay() {
+			let { year, month } = this.getDate();
+			month += 1;
+			if (month > 11) {
+				year += 1;
+				month = 1;
+			}
+			let firstDayTimeStamp = new Date(`${year}/${month}/1`).getTime();
+			let oneDayTimeStamp = 24 * 60 * 60 * 1000;
+			let lastDay = new Date(firstDayTimeStamp - oneDayTimeStamp).getDate();
+			return lastDay;
+		},
+		getDays() {
+			const lastDay = this.getLastDay();
+			const days = [];
+			for (let i = 1; i <= lastDay; i++) {
+				days.push(this.checks.includes(i) ? 'checked' : i);
+			}
+			return days;
+		},
+		format(num) {
+			return num < 10 ? `0${num}` : num;
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.calendar__wrap {
+	background-color: #fff;
+	color: $uni-text-color;
+	.header {
+		padding: 0 24rpx;
+		.current-date {
+			text-align: center;
+			font-size: $font-lg + 2rpx;
+			// border-bottom: 2rpx solid #eee;
+			padding: 32rpx 0;
+		}
+	}
+	.body {
+		.weeks {
+			display: flex;
+			font-size: $font-lg;
+			padding: 10rpx 0;
+			background-color: #f4f7ff;
+			.week__item {
+				flex: 1;
+				text-align: center;
+			}
+		}
+		.day__list {
+			display: flex;
+			flex-wrap: wrap;
+			.day__item {
+				display: flex;
+				justify-content: center;
+				width: 14.285%;
+				text-align: center;
+				padding: 30rpx 0;
+				font-size: 34rpx;
+				color: $font-color-light;
+				.checked-box,
+				.current-box {
+					display: flex;
+					align-items: center;
+					justify-content: center;
+					border-radius: 100%;
+					box-sizing: border-box;
+					position: relative;
+				}
+				image,
+				.checked-box,
+				.current-box,
+				.check_text {
+					width: 56rpx;
+					height: 56rpx;
+					line-height: 56rpx;
+				}
+				image,
+				.check_text {
+					position: absolute;
+					top: 0;
+					left: 0;
+				}
+				.checked {
+					font-size: 40rpx;
+					background-color: #3f9dff;
+					color: #fff;
+				}
+				.current {
+					padding: 12rpx;
+					background-color: $background-color;
+					color: #fff;
+					font-size: 28rpx;
+				}
+			}
+		}
+	}
+}
+</style>

+ 1201 - 0
components/tki-qrcode/qrcode.js

@@ -0,0 +1,1201 @@
+let QRCode = {};
+(function () {
+    /**
+     * 获取单个字符的utf8编码
+     * unicode BMP平面约65535个字符
+     * @param {num} code
+     * return {array}
+     */
+    function unicodeFormat8(code) {
+        // 1 byte
+        var c0, c1, c2;
+        if (code < 128) {
+            return [code];
+            // 2 bytes
+        } else if (code < 2048) {
+            c0 = 192 + (code >> 6);
+            c1 = 128 + (code & 63);
+            return [c0, c1];
+            // 3 bytes
+        } else {
+            c0 = 224 + (code >> 12);
+            c1 = 128 + (code >> 6 & 63);
+            c2 = 128 + (code & 63);
+            return [c0, c1, c2];
+        }
+    }
+    /**
+     * 获取字符串的utf8编码字节串
+     * @param {string} string
+     * @return {array}
+     */
+    function getUTF8Bytes(string) {
+        var utf8codes = [];
+        for (var i = 0; i < string.length; i++) {
+            var code = string.charCodeAt(i);
+            var utf8 = unicodeFormat8(code);
+            for (var j = 0; j < utf8.length; j++) {
+                utf8codes.push(utf8[j]);
+            }
+        }
+        return utf8codes;
+    }
+    /**
+     * 二维码算法实现
+     * @param {string} data              要编码的信息字符串
+     * @param {num} errorCorrectLevel 纠错等级
+     */
+    function QRCodeAlg(data, errorCorrectLevel) {
+        this.typeNumber = -1; //版本
+        this.errorCorrectLevel = errorCorrectLevel;
+        this.modules = null; //二维矩阵,存放最终结果
+        this.moduleCount = 0; //矩阵大小
+        this.dataCache = null; //数据缓存
+        this.rsBlocks = null; //版本数据信息
+        this.totalDataCount = -1; //可使用的数据量
+        this.data = data;
+        this.utf8bytes = getUTF8Bytes(data);
+        this.make();
+    }
+    QRCodeAlg.prototype = {
+        constructor: QRCodeAlg,
+        /**
+         * 获取二维码矩阵大小
+         * @return {num} 矩阵大小
+         */
+        getModuleCount: function () {
+            return this.moduleCount;
+        },
+        /**
+         * 编码
+         */
+        make: function () {
+            this.getRightType();
+            this.dataCache = this.createData();
+            this.createQrcode();
+        },
+        /**
+         * 设置二位矩阵功能图形
+         * @param  {bool} test 表示是否在寻找最好掩膜阶段
+         * @param  {num} maskPattern 掩膜的版本
+         */
+        makeImpl: function (maskPattern) {
+            this.moduleCount = this.typeNumber * 4 + 17;
+            this.modules = new Array(this.moduleCount);
+            for (var row = 0; row < this.moduleCount; row++) {
+                this.modules[row] = new Array(this.moduleCount);
+            }
+            this.setupPositionProbePattern(0, 0);
+            this.setupPositionProbePattern(this.moduleCount - 7, 0);
+            this.setupPositionProbePattern(0, this.moduleCount - 7);
+            this.setupPositionAdjustPattern();
+            this.setupTimingPattern();
+            this.setupTypeInfo(true, maskPattern);
+            if (this.typeNumber >= 7) {
+                this.setupTypeNumber(true);
+            }
+            this.mapData(this.dataCache, maskPattern);
+        },
+        /**
+         * 设置二维码的位置探测图形
+         * @param  {num} row 探测图形的中心横坐标
+         * @param  {num} col 探测图形的中心纵坐标
+         */
+        setupPositionProbePattern: function (row, col) {
+            for (var r = -1; r <= 7; r++) {
+                if (row + r <= -1 || this.moduleCount <= row + r) continue;
+                for (var c = -1; c <= 7; c++) {
+                    if (col + c <= -1 || this.moduleCount <= col + c) continue;
+                    if ((0 <= r && r <= 6 && (c == 0 || c == 6)) || (0 <= c && c <= 6 && (r == 0 || r == 6)) || (2 <= r && r <= 4 && 2 <= c && c <= 4)) {
+                        this.modules[row + r][col + c] = true;
+                    } else {
+                        this.modules[row + r][col + c] = false;
+                    }
+                }
+            }
+        },
+        /**
+         * 创建二维码
+         * @return {[type]} [description]
+         */
+        createQrcode: function () {
+            var minLostPoint = 0;
+            var pattern = 0;
+            var bestModules = null;
+            for (var i = 0; i < 8; i++) {
+                this.makeImpl(i);
+                var lostPoint = QRUtil.getLostPoint(this);
+                if (i == 0 || minLostPoint > lostPoint) {
+                    minLostPoint = lostPoint;
+                    pattern = i;
+                    bestModules = this.modules;
+                }
+            }
+            this.modules = bestModules;
+            this.setupTypeInfo(false, pattern);
+            if (this.typeNumber >= 7) {
+                this.setupTypeNumber(false);
+            }
+        },
+        /**
+         * 设置定位图形
+         * @return {[type]} [description]
+         */
+        setupTimingPattern: function () {
+            for (var r = 8; r < this.moduleCount - 8; r++) {
+                if (this.modules[r][6] != null) {
+                    continue;
+                }
+                this.modules[r][6] = (r % 2 == 0);
+                if (this.modules[6][r] != null) {
+                    continue;
+                }
+                this.modules[6][r] = (r % 2 == 0);
+            }
+        },
+        /**
+         * 设置矫正图形
+         * @return {[type]} [description]
+         */
+        setupPositionAdjustPattern: function () {
+            var pos = QRUtil.getPatternPosition(this.typeNumber);
+            for (var i = 0; i < pos.length; i++) {
+                for (var j = 0; j < pos.length; j++) {
+                    var row = pos[i];
+                    var col = pos[j];
+                    if (this.modules[row][col] != null) {
+                        continue;
+                    }
+                    for (var r = -2; r <= 2; r++) {
+                        for (var c = -2; c <= 2; c++) {
+                            if (r == -2 || r == 2 || c == -2 || c == 2 || (r == 0 && c == 0)) {
+                                this.modules[row + r][col + c] = true;
+                            } else {
+                                this.modules[row + r][col + c] = false;
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        /**
+         * 设置版本信息(7以上版本才有)
+         * @param  {bool} test 是否处于判断最佳掩膜阶段
+         * @return {[type]}      [description]
+         */
+        setupTypeNumber: function (test) {
+            var bits = QRUtil.getBCHTypeNumber(this.typeNumber);
+            for (var i = 0; i < 18; i++) {
+                var mod = (!test && ((bits >> i) & 1) == 1);
+                this.modules[Math.floor(i / 3)][i % 3 + this.moduleCount - 8 - 3] = mod;
+                this.modules[i % 3 + this.moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
+            }
+        },
+        /**
+         * 设置格式信息(纠错等级和掩膜版本)
+         * @param  {bool} test
+         * @param  {num} maskPattern 掩膜版本
+         * @return {}
+         */
+        setupTypeInfo: function (test, maskPattern) {
+            var data = (QRErrorCorrectLevel[this.errorCorrectLevel] << 3) | maskPattern;
+            var bits = QRUtil.getBCHTypeInfo(data);
+            // vertical
+            for (var i = 0; i < 15; i++) {
+                var mod = (!test && ((bits >> i) & 1) == 1);
+                if (i < 6) {
+                    this.modules[i][8] = mod;
+                } else if (i < 8) {
+                    this.modules[i + 1][8] = mod;
+                } else {
+                    this.modules[this.moduleCount - 15 + i][8] = mod;
+                }
+                // horizontal
+                var mod = (!test && ((bits >> i) & 1) == 1);
+                if (i < 8) {
+                    this.modules[8][this.moduleCount - i - 1] = mod;
+                } else if (i < 9) {
+                    this.modules[8][15 - i - 1 + 1] = mod;
+                } else {
+                    this.modules[8][15 - i - 1] = mod;
+                }
+            }
+            // fixed module
+            this.modules[this.moduleCount - 8][8] = (!test);
+        },
+        /**
+         * 数据编码
+         * @return {[type]} [description]
+         */
+        createData: function () {
+            var buffer = new QRBitBuffer();
+            var lengthBits = this.typeNumber > 9 ? 16 : 8;
+            buffer.put(4, 4); //添加模式
+            buffer.put(this.utf8bytes.length, lengthBits);
+            for (var i = 0, l = this.utf8bytes.length; i < l; i++) {
+                buffer.put(this.utf8bytes[i], 8);
+            }
+            if (buffer.length + 4 <= this.totalDataCount * 8) {
+                buffer.put(0, 4);
+            }
+            // padding
+            while (buffer.length % 8 != 0) {
+                buffer.putBit(false);
+            }
+            // padding
+            while (true) {
+                if (buffer.length >= this.totalDataCount * 8) {
+                    break;
+                }
+                buffer.put(QRCodeAlg.PAD0, 8);
+                if (buffer.length >= this.totalDataCount * 8) {
+                    break;
+                }
+                buffer.put(QRCodeAlg.PAD1, 8);
+            }
+            return this.createBytes(buffer);
+        },
+        /**
+         * 纠错码编码
+         * @param  {buffer} buffer 数据编码
+         * @return {[type]}
+         */
+        createBytes: function (buffer) {
+            var offset = 0;
+            var maxDcCount = 0;
+            var maxEcCount = 0;
+            var length = this.rsBlock.length / 3;
+            var rsBlocks = new Array();
+            for (var i = 0; i < length; i++) {
+                var count = this.rsBlock[i * 3 + 0];
+                var totalCount = this.rsBlock[i * 3 + 1];
+                var dataCount = this.rsBlock[i * 3 + 2];
+                for (var j = 0; j < count; j++) {
+                    rsBlocks.push([dataCount, totalCount]);
+                }
+            }
+            var dcdata = new Array(rsBlocks.length);
+            var ecdata = new Array(rsBlocks.length);
+            for (var r = 0; r < rsBlocks.length; r++) {
+                var dcCount = rsBlocks[r][0];
+                var ecCount = rsBlocks[r][1] - dcCount;
+                maxDcCount = Math.max(maxDcCount, dcCount);
+                maxEcCount = Math.max(maxEcCount, ecCount);
+                dcdata[r] = new Array(dcCount);
+                for (var i = 0; i < dcdata[r].length; i++) {
+                    dcdata[r][i] = 0xff & buffer.buffer[i + offset];
+                }
+                offset += dcCount;
+                var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
+                var rawPoly = new QRPolynomial(dcdata[r], rsPoly.getLength() - 1);
+                var modPoly = rawPoly.mod(rsPoly);
+                ecdata[r] = new Array(rsPoly.getLength() - 1);
+                for (var i = 0; i < ecdata[r].length; i++) {
+                    var modIndex = i + modPoly.getLength() - ecdata[r].length;
+                    ecdata[r][i] = (modIndex >= 0) ? modPoly.get(modIndex) : 0;
+                }
+            }
+            var data = new Array(this.totalDataCount);
+            var index = 0;
+            for (var i = 0; i < maxDcCount; i++) {
+                for (var r = 0; r < rsBlocks.length; r++) {
+                    if (i < dcdata[r].length) {
+                        data[index++] = dcdata[r][i];
+                    }
+                }
+            }
+            for (var i = 0; i < maxEcCount; i++) {
+                for (var r = 0; r < rsBlocks.length; r++) {
+                    if (i < ecdata[r].length) {
+                        data[index++] = ecdata[r][i];
+                    }
+                }
+            }
+            return data;
+
+        },
+        /**
+         * 布置模块,构建最终信息
+         * @param  {} data
+         * @param  {} maskPattern
+         * @return {}
+         */
+        mapData: function (data, maskPattern) {
+            var inc = -1;
+            var row = this.moduleCount - 1;
+            var bitIndex = 7;
+            var byteIndex = 0;
+            for (var col = this.moduleCount - 1; col > 0; col -= 2) {
+                if (col == 6) col--;
+                while (true) {
+                    for (var c = 0; c < 2; c++) {
+                        if (this.modules[row][col - c] == null) {
+                            var dark = false;
+                            if (byteIndex < data.length) {
+                                dark = (((data[byteIndex] >>> bitIndex) & 1) == 1);
+                            }
+                            var mask = QRUtil.getMask(maskPattern, row, col - c);
+                            if (mask) {
+                                dark = !dark;
+                            }
+                            this.modules[row][col - c] = dark;
+                            bitIndex--;
+                            if (bitIndex == -1) {
+                                byteIndex++;
+                                bitIndex = 7;
+                            }
+                        }
+                    }
+                    row += inc;
+                    if (row < 0 || this.moduleCount <= row) {
+                        row -= inc;
+                        inc = -inc;
+                        break;
+                    }
+                }
+            }
+        }
+    };
+    /**
+     * 填充字段
+     */
+    QRCodeAlg.PAD0 = 0xEC;
+    QRCodeAlg.PAD1 = 0x11;
+    //---------------------------------------------------------------------
+    // 纠错等级对应的编码
+    //---------------------------------------------------------------------
+    var QRErrorCorrectLevel = [1, 0, 3, 2];
+    //---------------------------------------------------------------------
+    // 掩膜版本
+    //---------------------------------------------------------------------
+    var QRMaskPattern = {
+        PATTERN000: 0,
+        PATTERN001: 1,
+        PATTERN010: 2,
+        PATTERN011: 3,
+        PATTERN100: 4,
+        PATTERN101: 5,
+        PATTERN110: 6,
+        PATTERN111: 7
+    };
+    //---------------------------------------------------------------------
+    // 工具类
+    //---------------------------------------------------------------------
+    var QRUtil = {
+        /*
+        每个版本矫正图形的位置
+         */
+        PATTERN_POSITION_TABLE: [
+            [],
+            [6, 18],
+            [6, 22],
+            [6, 26],
+            [6, 30],
+            [6, 34],
+            [6, 22, 38],
+            [6, 24, 42],
+            [6, 26, 46],
+            [6, 28, 50],
+            [6, 30, 54],
+            [6, 32, 58],
+            [6, 34, 62],
+            [6, 26, 46, 66],
+            [6, 26, 48, 70],
+            [6, 26, 50, 74],
+            [6, 30, 54, 78],
+            [6, 30, 56, 82],
+            [6, 30, 58, 86],
+            [6, 34, 62, 90],
+            [6, 28, 50, 72, 94],
+            [6, 26, 50, 74, 98],
+            [6, 30, 54, 78, 102],
+            [6, 28, 54, 80, 106],
+            [6, 32, 58, 84, 110],
+            [6, 30, 58, 86, 114],
+            [6, 34, 62, 90, 118],
+            [6, 26, 50, 74, 98, 122],
+            [6, 30, 54, 78, 102, 126],
+            [6, 26, 52, 78, 104, 130],
+            [6, 30, 56, 82, 108, 134],
+            [6, 34, 60, 86, 112, 138],
+            [6, 30, 58, 86, 114, 142],
+            [6, 34, 62, 90, 118, 146],
+            [6, 30, 54, 78, 102, 126, 150],
+            [6, 24, 50, 76, 102, 128, 154],
+            [6, 28, 54, 80, 106, 132, 158],
+            [6, 32, 58, 84, 110, 136, 162],
+            [6, 26, 54, 82, 110, 138, 166],
+            [6, 30, 58, 86, 114, 142, 170]
+        ],
+        G15: (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0),
+        G18: (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0),
+        G15_MASK: (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1),
+        /*
+        BCH编码格式信息
+         */
+        getBCHTypeInfo: function (data) {
+            var d = data << 10;
+            while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0) {
+                d ^= (QRUtil.G15 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15)));
+            }
+            return ((data << 10) | d) ^ QRUtil.G15_MASK;
+        },
+        /*
+        BCH编码版本信息
+         */
+        getBCHTypeNumber: function (data) {
+            var d = data << 12;
+            while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0) {
+                d ^= (QRUtil.G18 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18)));
+            }
+            return (data << 12) | d;
+        },
+        /*
+        获取BCH位信息
+         */
+        getBCHDigit: function (data) {
+            var digit = 0;
+            while (data != 0) {
+                digit++;
+                data >>>= 1;
+            }
+            return digit;
+        },
+        /*
+        获取版本对应的矫正图形位置
+         */
+        getPatternPosition: function (typeNumber) {
+            return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1];
+        },
+        /*
+        掩膜算法
+         */
+        getMask: function (maskPattern, i, j) {
+            switch (maskPattern) {
+                case QRMaskPattern.PATTERN000:
+                    return (i + j) % 2 == 0;
+                case QRMaskPattern.PATTERN001:
+                    return i % 2 == 0;
+                case QRMaskPattern.PATTERN010:
+                    return j % 3 == 0;
+                case QRMaskPattern.PATTERN011:
+                    return (i + j) % 3 == 0;
+                case QRMaskPattern.PATTERN100:
+                    return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0;
+                case QRMaskPattern.PATTERN101:
+                    return (i * j) % 2 + (i * j) % 3 == 0;
+                case QRMaskPattern.PATTERN110:
+                    return ((i * j) % 2 + (i * j) % 3) % 2 == 0;
+                case QRMaskPattern.PATTERN111:
+                    return ((i * j) % 3 + (i + j) % 2) % 2 == 0;
+                default:
+                    throw new Error("bad maskPattern:" + maskPattern);
+            }
+        },
+        /*
+        获取RS的纠错多项式
+         */
+        getErrorCorrectPolynomial: function (errorCorrectLength) {
+            var a = new QRPolynomial([1], 0);
+            for (var i = 0; i < errorCorrectLength; i++) {
+                a = a.multiply(new QRPolynomial([1, QRMath.gexp(i)], 0));
+            }
+            return a;
+        },
+        /*
+        获取评价
+         */
+        getLostPoint: function (qrCode) {
+            var moduleCount = qrCode.getModuleCount(),
+                lostPoint = 0,
+                darkCount = 0;
+            for (var row = 0; row < moduleCount; row++) {
+                var sameCount = 0;
+                var head = qrCode.modules[row][0];
+                for (var col = 0; col < moduleCount; col++) {
+                    var current = qrCode.modules[row][col];
+                    //level 3 评价
+                    if (col < moduleCount - 6) {
+                        if (current && !qrCode.modules[row][col + 1] && qrCode.modules[row][col + 2] && qrCode.modules[row][col + 3] && qrCode.modules[row][col + 4] && !qrCode.modules[row][col + 5] && qrCode.modules[row][col + 6]) {
+                            if (col < moduleCount - 10) {
+                                if (qrCode.modules[row][col + 7] && qrCode.modules[row][col + 8] && qrCode.modules[row][col + 9] && qrCode.modules[row][col + 10]) {
+                                    lostPoint += 40;
+                                }
+                            } else if (col > 3) {
+                                if (qrCode.modules[row][col - 1] && qrCode.modules[row][col - 2] && qrCode.modules[row][col - 3] && qrCode.modules[row][col - 4]) {
+                                    lostPoint += 40;
+                                }
+                            }
+                        }
+                    }
+                    //level 2 评价
+                    if ((row < moduleCount - 1) && (col < moduleCount - 1)) {
+                        var count = 0;
+                        if (current) count++;
+                        if (qrCode.modules[row + 1][col]) count++;
+                        if (qrCode.modules[row][col + 1]) count++;
+                        if (qrCode.modules[row + 1][col + 1]) count++;
+                        if (count == 0 || count == 4) {
+                            lostPoint += 3;
+                        }
+                    }
+                    //level 1 评价
+                    if (head ^ current) {
+                        sameCount++;
+                    } else {
+                        head = current;
+                        if (sameCount >= 5) {
+                            lostPoint += (3 + sameCount - 5);
+                        }
+                        sameCount = 1;
+                    }
+                    //level 4 评价
+                    if (current) {
+                        darkCount++;
+                    }
+                }
+            }
+            for (var col = 0; col < moduleCount; col++) {
+                var sameCount = 0;
+                var head = qrCode.modules[0][col];
+                for (var row = 0; row < moduleCount; row++) {
+                    var current = qrCode.modules[row][col];
+                    //level 3 评价
+                    if (row < moduleCount - 6) {
+                        if (current && !qrCode.modules[row + 1][col] && qrCode.modules[row + 2][col] && qrCode.modules[row + 3][col] && qrCode.modules[row + 4][col] && !qrCode.modules[row + 5][col] && qrCode.modules[row + 6][col]) {
+                            if (row < moduleCount - 10) {
+                                if (qrCode.modules[row + 7][col] && qrCode.modules[row + 8][col] && qrCode.modules[row + 9][col] && qrCode.modules[row + 10][col]) {
+                                    lostPoint += 40;
+                                }
+                            } else if (row > 3) {
+                                if (qrCode.modules[row - 1][col] && qrCode.modules[row - 2][col] && qrCode.modules[row - 3][col] && qrCode.modules[row - 4][col]) {
+                                    lostPoint += 40;
+                                }
+                            }
+                        }
+                    }
+                    //level 1 评价
+                    if (head ^ current) {
+                        sameCount++;
+                    } else {
+                        head = current;
+                        if (sameCount >= 5) {
+                            lostPoint += (3 + sameCount - 5);
+                        }
+                        sameCount = 1;
+                    }
+                }
+            }
+            // LEVEL4
+            var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
+            lostPoint += ratio * 10;
+            return lostPoint;
+        }
+
+    };
+    //---------------------------------------------------------------------
+    // QRMath使用的数学工具
+    //---------------------------------------------------------------------
+    var QRMath = {
+        /*
+        将n转化为a^m
+         */
+        glog: function (n) {
+            if (n < 1) {
+                throw new Error("glog(" + n + ")");
+            }
+            return QRMath.LOG_TABLE[n];
+        },
+        /*
+        将a^m转化为n
+         */
+        gexp: function (n) {
+            while (n < 0) {
+                n += 255;
+            }
+            while (n >= 256) {
+                n -= 255;
+            }
+            return QRMath.EXP_TABLE[n];
+        },
+        EXP_TABLE: new Array(256),
+        LOG_TABLE: new Array(256)
+
+    };
+    for (var i = 0; i < 8; i++) {
+        QRMath.EXP_TABLE[i] = 1 << i;
+    }
+    for (var i = 8; i < 256; i++) {
+        QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8];
+    }
+    for (var i = 0; i < 255; i++) {
+        QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i;
+    }
+    //---------------------------------------------------------------------
+    // QRPolynomial 多项式
+    //---------------------------------------------------------------------
+    /**
+     * 多项式类
+     * @param {Array} num   系数
+     * @param {num} shift a^shift
+     */
+    function QRPolynomial(num, shift) {
+        if (num.length == undefined) {
+            throw new Error(num.length + "/" + shift);
+        }
+        var offset = 0;
+        while (offset < num.length && num[offset] == 0) {
+            offset++;
+        }
+        this.num = new Array(num.length - offset + shift);
+        for (var i = 0; i < num.length - offset; i++) {
+            this.num[i] = num[i + offset];
+        }
+    }
+    QRPolynomial.prototype = {
+        get: function (index) {
+            return this.num[index];
+        },
+        getLength: function () {
+            return this.num.length;
+        },
+        /**
+         * 多项式乘法
+         * @param  {QRPolynomial} e 被乘多项式
+         * @return {[type]}   [description]
+         */
+        multiply: function (e) {
+            var num = new Array(this.getLength() + e.getLength() - 1);
+            for (var i = 0; i < this.getLength(); i++) {
+                for (var j = 0; j < e.getLength(); j++) {
+                    num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j)));
+                }
+            }
+            return new QRPolynomial(num, 0);
+        },
+        /**
+         * 多项式模运算
+         * @param  {QRPolynomial} e 模多项式
+         * @return {}
+         */
+        mod: function (e) {
+            var tl = this.getLength(),
+                el = e.getLength();
+            if (tl - el < 0) {
+                return this;
+            }
+            var num = new Array(tl);
+            for (var i = 0; i < tl; i++) {
+                num[i] = this.get(i);
+            }
+            while (num.length >= el) {
+                var ratio = QRMath.glog(num[0]) - QRMath.glog(e.get(0));
+
+                for (var i = 0; i < e.getLength(); i++) {
+                    num[i] ^= QRMath.gexp(QRMath.glog(e.get(i)) + ratio);
+                }
+                while (num[0] == 0) {
+                    num.shift();
+                }
+            }
+            return new QRPolynomial(num, 0);
+        }
+    };
+
+    //---------------------------------------------------------------------
+    // RS_BLOCK_TABLE
+    //---------------------------------------------------------------------
+    /*
+    二维码各个版本信息[块数, 每块中的数据块数, 每块中的信息块数]
+     */
+    var RS_BLOCK_TABLE = [
+        // L
+        // M
+        // Q
+        // H
+        // 1
+        [1, 26, 19],
+        [1, 26, 16],
+        [1, 26, 13],
+        [1, 26, 9],
+
+        // 2
+        [1, 44, 34],
+        [1, 44, 28],
+        [1, 44, 22],
+        [1, 44, 16],
+
+        // 3
+        [1, 70, 55],
+        [1, 70, 44],
+        [2, 35, 17],
+        [2, 35, 13],
+
+        // 4
+        [1, 100, 80],
+        [2, 50, 32],
+        [2, 50, 24],
+        [4, 25, 9],
+
+        // 5
+        [1, 134, 108],
+        [2, 67, 43],
+        [2, 33, 15, 2, 34, 16],
+        [2, 33, 11, 2, 34, 12],
+
+        // 6
+        [2, 86, 68],
+        [4, 43, 27],
+        [4, 43, 19],
+        [4, 43, 15],
+
+        // 7
+        [2, 98, 78],
+        [4, 49, 31],
+        [2, 32, 14, 4, 33, 15],
+        [4, 39, 13, 1, 40, 14],
+
+        // 8
+        [2, 121, 97],
+        [2, 60, 38, 2, 61, 39],
+        [4, 40, 18, 2, 41, 19],
+        [4, 40, 14, 2, 41, 15],
+
+        // 9
+        [2, 146, 116],
+        [3, 58, 36, 2, 59, 37],
+        [4, 36, 16, 4, 37, 17],
+        [4, 36, 12, 4, 37, 13],
+
+        // 10
+        [2, 86, 68, 2, 87, 69],
+        [4, 69, 43, 1, 70, 44],
+        [6, 43, 19, 2, 44, 20],
+        [6, 43, 15, 2, 44, 16],
+
+        // 11
+        [4, 101, 81],
+        [1, 80, 50, 4, 81, 51],
+        [4, 50, 22, 4, 51, 23],
+        [3, 36, 12, 8, 37, 13],
+
+        // 12
+        [2, 116, 92, 2, 117, 93],
+        [6, 58, 36, 2, 59, 37],
+        [4, 46, 20, 6, 47, 21],
+        [7, 42, 14, 4, 43, 15],
+
+        // 13
+        [4, 133, 107],
+        [8, 59, 37, 1, 60, 38],
+        [8, 44, 20, 4, 45, 21],
+        [12, 33, 11, 4, 34, 12],
+
+        // 14
+        [3, 145, 115, 1, 146, 116],
+        [4, 64, 40, 5, 65, 41],
+        [11, 36, 16, 5, 37, 17],
+        [11, 36, 12, 5, 37, 13],
+
+        // 15
+        [5, 109, 87, 1, 110, 88],
+        [5, 65, 41, 5, 66, 42],
+        [5, 54, 24, 7, 55, 25],
+        [11, 36, 12],
+
+        // 16
+        [5, 122, 98, 1, 123, 99],
+        [7, 73, 45, 3, 74, 46],
+        [15, 43, 19, 2, 44, 20],
+        [3, 45, 15, 13, 46, 16],
+
+        // 17
+        [1, 135, 107, 5, 136, 108],
+        [10, 74, 46, 1, 75, 47],
+        [1, 50, 22, 15, 51, 23],
+        [2, 42, 14, 17, 43, 15],
+
+        // 18
+        [5, 150, 120, 1, 151, 121],
+        [9, 69, 43, 4, 70, 44],
+        [17, 50, 22, 1, 51, 23],
+        [2, 42, 14, 19, 43, 15],
+
+        // 19
+        [3, 141, 113, 4, 142, 114],
+        [3, 70, 44, 11, 71, 45],
+        [17, 47, 21, 4, 48, 22],
+        [9, 39, 13, 16, 40, 14],
+
+        // 20
+        [3, 135, 107, 5, 136, 108],
+        [3, 67, 41, 13, 68, 42],
+        [15, 54, 24, 5, 55, 25],
+        [15, 43, 15, 10, 44, 16],
+
+        // 21
+        [4, 144, 116, 4, 145, 117],
+        [17, 68, 42],
+        [17, 50, 22, 6, 51, 23],
+        [19, 46, 16, 6, 47, 17],
+
+        // 22
+        [2, 139, 111, 7, 140, 112],
+        [17, 74, 46],
+        [7, 54, 24, 16, 55, 25],
+        [34, 37, 13],
+
+        // 23
+        [4, 151, 121, 5, 152, 122],
+        [4, 75, 47, 14, 76, 48],
+        [11, 54, 24, 14, 55, 25],
+        [16, 45, 15, 14, 46, 16],
+
+        // 24
+        [6, 147, 117, 4, 148, 118],
+        [6, 73, 45, 14, 74, 46],
+        [11, 54, 24, 16, 55, 25],
+        [30, 46, 16, 2, 47, 17],
+
+        // 25
+        [8, 132, 106, 4, 133, 107],
+        [8, 75, 47, 13, 76, 48],
+        [7, 54, 24, 22, 55, 25],
+        [22, 45, 15, 13, 46, 16],
+
+        // 26
+        [10, 142, 114, 2, 143, 115],
+        [19, 74, 46, 4, 75, 47],
+        [28, 50, 22, 6, 51, 23],
+        [33, 46, 16, 4, 47, 17],
+
+        // 27
+        [8, 152, 122, 4, 153, 123],
+        [22, 73, 45, 3, 74, 46],
+        [8, 53, 23, 26, 54, 24],
+        [12, 45, 15, 28, 46, 16],
+
+        // 28
+        [3, 147, 117, 10, 148, 118],
+        [3, 73, 45, 23, 74, 46],
+        [4, 54, 24, 31, 55, 25],
+        [11, 45, 15, 31, 46, 16],
+
+        // 29
+        [7, 146, 116, 7, 147, 117],
+        [21, 73, 45, 7, 74, 46],
+        [1, 53, 23, 37, 54, 24],
+        [19, 45, 15, 26, 46, 16],
+
+        // 30
+        [5, 145, 115, 10, 146, 116],
+        [19, 75, 47, 10, 76, 48],
+        [15, 54, 24, 25, 55, 25],
+        [23, 45, 15, 25, 46, 16],
+
+        // 31
+        [13, 145, 115, 3, 146, 116],
+        [2, 74, 46, 29, 75, 47],
+        [42, 54, 24, 1, 55, 25],
+        [23, 45, 15, 28, 46, 16],
+
+        // 32
+        [17, 145, 115],
+        [10, 74, 46, 23, 75, 47],
+        [10, 54, 24, 35, 55, 25],
+        [19, 45, 15, 35, 46, 16],
+
+        // 33
+        [17, 145, 115, 1, 146, 116],
+        [14, 74, 46, 21, 75, 47],
+        [29, 54, 24, 19, 55, 25],
+        [11, 45, 15, 46, 46, 16],
+
+        // 34
+        [13, 145, 115, 6, 146, 116],
+        [14, 74, 46, 23, 75, 47],
+        [44, 54, 24, 7, 55, 25],
+        [59, 46, 16, 1, 47, 17],
+
+        // 35
+        [12, 151, 121, 7, 152, 122],
+        [12, 75, 47, 26, 76, 48],
+        [39, 54, 24, 14, 55, 25],
+        [22, 45, 15, 41, 46, 16],
+
+        // 36
+        [6, 151, 121, 14, 152, 122],
+        [6, 75, 47, 34, 76, 48],
+        [46, 54, 24, 10, 55, 25],
+        [2, 45, 15, 64, 46, 16],
+
+        // 37
+        [17, 152, 122, 4, 153, 123],
+        [29, 74, 46, 14, 75, 47],
+        [49, 54, 24, 10, 55, 25],
+        [24, 45, 15, 46, 46, 16],
+
+        // 38
+        [4, 152, 122, 18, 153, 123],
+        [13, 74, 46, 32, 75, 47],
+        [48, 54, 24, 14, 55, 25],
+        [42, 45, 15, 32, 46, 16],
+
+        // 39
+        [20, 147, 117, 4, 148, 118],
+        [40, 75, 47, 7, 76, 48],
+        [43, 54, 24, 22, 55, 25],
+        [10, 45, 15, 67, 46, 16],
+
+        // 40
+        [19, 148, 118, 6, 149, 119],
+        [18, 75, 47, 31, 76, 48],
+        [34, 54, 24, 34, 55, 25],
+        [20, 45, 15, 61, 46, 16]
+    ];
+
+    /**
+     * 根据数据获取对应版本
+     * @return {[type]} [description]
+     */
+    QRCodeAlg.prototype.getRightType = function () {
+        for (var typeNumber = 1; typeNumber < 41; typeNumber++) {
+            var rsBlock = RS_BLOCK_TABLE[(typeNumber - 1) * 4 + this.errorCorrectLevel];
+            if (rsBlock == undefined) {
+                throw new Error("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + this.errorCorrectLevel);
+            }
+            var length = rsBlock.length / 3;
+            var totalDataCount = 0;
+            for (var i = 0; i < length; i++) {
+                var count = rsBlock[i * 3 + 0];
+                var dataCount = rsBlock[i * 3 + 2];
+                totalDataCount += dataCount * count;
+            }
+            var lengthBytes = typeNumber > 9 ? 2 : 1;
+            if (this.utf8bytes.length + lengthBytes < totalDataCount || typeNumber == 40) {
+                this.typeNumber = typeNumber;
+                this.rsBlock = rsBlock;
+                this.totalDataCount = totalDataCount;
+                break;
+            }
+        }
+    };
+
+    //---------------------------------------------------------------------
+    // QRBitBuffer
+    //---------------------------------------------------------------------
+    function QRBitBuffer() {
+        this.buffer = new Array();
+        this.length = 0;
+    }
+    QRBitBuffer.prototype = {
+        get: function (index) {
+            var bufIndex = Math.floor(index / 8);
+            return ((this.buffer[bufIndex] >>> (7 - index % 8)) & 1);
+        },
+        put: function (num, length) {
+            for (var i = 0; i < length; i++) {
+                this.putBit(((num >>> (length - i - 1)) & 1));
+            }
+        },
+        putBit: function (bit) {
+            var bufIndex = Math.floor(this.length / 8);
+            if (this.buffer.length <= bufIndex) {
+                this.buffer.push(0);
+            }
+            if (bit) {
+                this.buffer[bufIndex] |= (0x80 >>> (this.length % 8));
+            }
+            this.length++;
+        }
+    };
+
+
+
+    // xzedit
+    let qrcodeAlgObjCache = [];
+    /**
+     * 二维码构造函数,主要用于绘制
+     * @param  {参数列表} opt 传递参数
+     * @return {}
+     */
+    QRCode = function (opt) {
+        //设置默认参数
+        this.options = {
+            text: '',
+            size: 256,
+            correctLevel: 3,
+            background: '#ffffff',
+            foreground: '#000000',
+            pdground: '#000000',
+            image: '',
+            imageSize: 30,
+            canvasId: opt.canvasId,
+            context: opt.context,
+            usingComponents: opt.usingComponents,
+            showLoading: opt.showLoading,
+            loadingText: opt.loadingText,
+        };
+        if (typeof opt === 'string') { // 只编码ASCII字符串
+            opt = {
+                text: opt
+            };
+        }
+        if (opt) {
+            for (var i in opt) {
+                this.options[i] = opt[i];
+            }
+        }
+        //使用QRCodeAlg创建二维码结构
+        var qrCodeAlg = null;
+        for (var i = 0, l = qrcodeAlgObjCache.length; i < l; i++) {
+            if (qrcodeAlgObjCache[i].text == this.options.text && qrcodeAlgObjCache[i].text.correctLevel == this.options.correctLevel) {
+                qrCodeAlg = qrcodeAlgObjCache[i].obj;
+                break;
+            }
+        }
+        if (i == l) {
+            qrCodeAlg = new QRCodeAlg(this.options.text, this.options.correctLevel);
+            qrcodeAlgObjCache.push({
+                text: this.options.text,
+                correctLevel: this.options.correctLevel,
+                obj: qrCodeAlg
+            });
+        }
+        /**
+         * 计算矩阵点的前景色
+         * @param {Obj} config
+         * @param {Number} config.row 点x坐标
+         * @param {Number} config.col 点y坐标
+         * @param {Number} config.count 矩阵大小
+         * @param {Number} config.options 组件的options
+         * @return {String}
+         */
+        let getForeGround = function (config) {
+            var options = config.options;
+            if (options.pdground && (
+                (config.row > 1 && config.row < 5 && config.col > 1 && config.col < 5) ||
+                (config.row > (config.count - 6) && config.row < (config.count - 2) && config.col > 1 && config.col < 5) ||
+                (config.row > 1 && config.row < 5 && config.col > (config.count - 6) && config.col < (config.count - 2))
+            )) {
+                return options.pdground;
+            }
+            return options.foreground;
+        }
+        // 创建canvas
+        let createCanvas = function (options) {
+            if (options.showLoading) {
+                uni.showLoading({
+                    title: options.loadingText,
+                    mask: true
+                });
+            }
+            var ctx = uni.createCanvasContext(options.canvasId, options.context);
+            var count = qrCodeAlg.getModuleCount();
+            var ratioSize = options.size;
+            var ratioImgSize = options.imageSize;
+            //计算每个点的长宽
+            var tileW = (ratioSize / count).toPrecision(4);
+            var tileH = (ratioSize / count).toPrecision(4);
+            //绘制
+            for (var row = 0; row < count; row++) {
+                for (var col = 0; col < count; col++) {
+                    var w = (Math.ceil((col + 1) * tileW) - Math.floor(col * tileW));
+                    var h = (Math.ceil((row + 1) * tileW) - Math.floor(row * tileW));
+                    var foreground = getForeGround({
+                        row: row,
+                        col: col,
+                        count: count,
+                        options: options
+                    });
+                    ctx.setFillStyle(qrCodeAlg.modules[row][col] ? foreground : options.background);
+                    ctx.fillRect(Math.round(col * tileW), Math.round(row * tileH), w, h);
+                }
+            }
+            if (options.image) {
+                var x = Number(((ratioSize - ratioImgSize) / 2).toFixed(2));
+                var y = Number(((ratioSize - ratioImgSize) / 2).toFixed(2));
+                drawRoundedRect(ctx, x, y, ratioImgSize, ratioImgSize, 2, 6, true, true)
+                ctx.drawImage(options.image, x, y, ratioImgSize, ratioImgSize);
+                // 画圆角矩形
+                function drawRoundedRect(ctxi, x, y, width, height, r, lineWidth, fill, stroke) {
+                    ctxi.setLineWidth(lineWidth);
+                    ctxi.setFillStyle(options.background);
+                    ctxi.setStrokeStyle(options.background);
+                    ctxi.beginPath(); // draw top and top right corner 
+                    ctxi.moveTo(x + r, y);
+                    ctxi.arcTo(x + width, y, x + width, y + r, r); // draw right side and bottom right corner 
+                    ctxi.arcTo(x + width, y + height, x + width - r, y + height, r); // draw bottom and bottom left corner 
+                    ctxi.arcTo(x, y + height, x, y + height - r, r); // draw left and top left corner 
+                    ctxi.arcTo(x, y, x + r, y, r);
+                    ctxi.closePath();
+                    if (fill) {
+                        ctxi.fill();
+                    }
+                    if (stroke) {
+                        ctxi.stroke();
+                    }
+                }
+            }
+            setTimeout(() => {
+                ctx.draw(true, () => {
+                    // 保存到临时区域
+                    setTimeout(() => {
+                        uni.canvasToTempFilePath({
+                            width: options.width,
+                            height: options.height,
+                            destWidth: options.width,
+                            destHeight: options.height,
+                            canvasId: options.canvasId,
+                            quality: Number(1),
+                            success: function (res) {
+                                if (options.cbResult) {
+                                    options.cbResult(res.tempFilePath)
+                                }
+                            },
+                            fail: function (res) {
+                                if (options.cbResult) {
+                                    options.cbResult(res)
+                                }
+                            },
+                            complete: function () {
+                                if (options.showLoading){
+                                    uni.hideLoading();
+                                }
+                            },
+                        }, options.context);
+                    }, options.text.length + 100);
+                });
+            }, options.usingComponents ? 0 : 150);
+        }
+        createCanvas(this.options);
+        // 空判定
+        let empty = function (v) {
+            let tp = typeof v,
+                rt = false;
+            if (tp == "number" && String(v) == "") {
+                rt = true
+            } else if (tp == "undefined") {
+                rt = true
+            } else if (tp == "object") {
+                if (JSON.stringify(v) == "{}" || JSON.stringify(v) == "[]" || v == null) rt = true
+            } else if (tp == "string") {
+                if (v == "" || v == "undefined" || v == "null" || v == "{}" || v == "[]") rt = true
+            } else if (tp == "function") {
+                rt = false
+            }
+            return rt
+        }
+    };
+    QRCode.prototype.clear = function (fn) {
+        var ctx = uni.createCanvasContext(this.options.canvasId, this.options.context)
+        ctx.clearRect(0, 0, this.options.size, this.options.size)
+        ctx.draw(false, () => {
+            if (fn) {
+                fn()
+            }
+        })
+    };
+})()
+
+export default QRCode

+ 210 - 0
components/tki-qrcode/tki-qrcode.vue

@@ -0,0 +1,210 @@
+<template xlang="wxml" minapp="mpvue">
+	<view class="tki-qrcode">
+		<!-- #ifndef MP-ALIPAY -->
+		<canvas class="tki-qrcode-canvas" :canvas-id="cid" :style="{width:cpSize+'px',height:cpSize+'px'}" />
+		<!-- #endif -->
+		<!-- #ifdef MP-ALIPAY -->
+		<canvas :id="cid" :width="cpSize" :height="cpSize" class="tki-qrcode-canvas" />
+		<!-- #endif -->
+		<image v-show="show" :src="result" :style="{width:cpSize+'px',height:cpSize+'px'}" />
+	</view>
+</template>
+
+<script>
+import QRCode from "./qrcode.js"
+let qrcode
+export default {
+	name: "tki-qrcode",
+	props: {
+		cid: {
+			type: String,
+			default: 'tki-qrcode-canvas'
+		},
+		size: {
+			type: Number,
+			default: 200
+		},
+		unit: {
+			type: String,
+			default: 'upx'
+		},
+		show: {
+			type: Boolean,
+			default: true
+		},
+		val: {
+			type: String,
+			default: ''
+		},
+		background: {
+			type: String,
+			default: '#ffffff'
+		},
+		foreground: {
+			type: String,
+			default: '#000000'
+		},
+		pdground: {
+			type: String,
+			default: '#000000'
+		},
+		icon: {
+			type: String,
+			default: ''
+		},
+		iconSize: {
+			type: Number,
+			default: 40
+		},
+		lv: {
+			type: Number,
+			default: 3
+		},
+		onval: {
+			type: Boolean,
+			default: false
+		},
+		loadMake: {
+			type: Boolean,
+			default: false
+		},
+		usingComponents: {
+			type: Boolean,
+			default: true
+		},
+		showLoading: {
+			type: Boolean,
+			default: true
+		},
+		loadingText: {
+			type: String,
+			default: '二维码生成中'
+		},
+	},
+	data() {
+		return {
+			result: '',
+		}
+	},
+	methods: {
+		_makeCode() {
+			let that = this
+			if (!this._empty(this.val)) {
+				qrcode = new QRCode({
+					context: that, // 上下文环境
+					canvasId:that.cid, // canvas-id
+					usingComponents: that.usingComponents, // 是否是自定义组件
+					showLoading: that.showLoading, // 是否显示loading
+					loadingText: that.loadingText, // loading文字
+					text: that.val, // 生成内容
+					size: that.cpSize, // 二维码大小
+					background: that.background, // 背景色
+					foreground: that.foreground, // 前景色
+					pdground: that.pdground, // 定位角点颜色
+					correctLevel: that.lv, // 容错级别
+					image: that.icon, // 二维码图标
+					imageSize: that.iconSize,// 二维码图标大小
+					cbResult: function (res) { // 生成二维码的回调
+						that._result(res)
+					},
+				});
+			} else {
+				uni.showToast({
+					title: '二维码内容不能为空',
+					icon: 'none',
+					duration: 2000
+				});
+			}
+		},
+		_clearCode() {
+			this._result('')
+			qrcode.clear()
+		},
+		_saveCode() {
+			let that = this;
+			if (this.result != "") {
+				uni.saveImageToPhotosAlbum({
+					filePath: that.result,
+					success: function () {
+						uni.showToast({
+							title: '二维码保存成功',
+							icon: 'success',
+							duration: 2000
+						});
+					}
+				});
+			}
+		},
+		_result(res) {
+			this.result = res;
+			this.$emit('result', res)
+		},
+		_empty(v) {
+			let tp = typeof v,
+				rt = false;
+			if (tp == "number" && String(v) == "") {
+				rt = true
+			} else if (tp == "undefined") {
+				rt = true
+			} else if (tp == "object") {
+				if (JSON.stringify(v) == "{}" || JSON.stringify(v) == "[]" || v == null) rt = true
+			} else if (tp == "string") {
+				if (v == "" || v == "undefined" || v == "null" || v == "{}" || v == "[]") rt = true
+			} else if (tp == "function") {
+				rt = false
+			}
+			return rt
+		}
+	},
+	watch: {
+		size: function (n, o) {
+			if (n != o && !this._empty(n)) {
+				this.cSize = n
+				if (!this._empty(this.val)) {
+					setTimeout(() => {
+						this._makeCode()
+					}, 100);
+				}
+			}
+		},
+		val: function (n, o) {
+			if (this.onval) {
+				if (n != o && !this._empty(n)) {
+					setTimeout(() => {
+						this._makeCode()
+					}, 0);
+				}
+			}
+		}
+	},
+	computed: {
+		cpSize() {
+			if(this.unit == "upx"){
+				return uni.upx2px(this.size)
+			}else{
+				return this.size
+			}
+		}
+	},
+	mounted: function () {
+		if (this.loadMake) {
+			if (!this._empty(this.val)) {
+				setTimeout(() => {
+					this._makeCode()
+				}, 0);
+			}
+		}
+	},
+}
+</script>
+<style>
+.tki-qrcode {
+  position: relative;
+}
+.tki-qrcode-canvas {
+  position: fixed;
+  top: -99999upx;
+  left: -99999upx;
+  z-index: -99999;
+}
+</style>

+ 122 - 0
components/uni-badge/uni-badge.vue

@@ -0,0 +1,122 @@
+<template>
+	<text v-if="text" :class="inverted ? 'uni-badge--' + type + ' uni-badge--' + size + ' uni-badge--' + type + '-inverted' : 'uni-badge--' + type + ' uni-badge--' + size"
+	 class="uni-badge" :style="width" @click="onClick()">{{ text }}</text>
+</template>
+
+<script>
+	export default {
+		name: 'UniBadge',
+		props: {
+			type: {
+				type: String,
+				default: 'default'
+			},
+			inverted: {
+				type: Boolean,
+				default: false
+			},
+			text: {
+				type: [String,Number],
+				default: ''
+			},
+			size: {
+				// small.normal
+				type: String,
+				default: 'normal'
+			}
+		},
+		data() {
+			return {
+				width: `display: inline-block;width: ${String(this.text).length * 15 + 25}rpx`
+			};
+		},
+		methods: {
+			onClick() {
+				this.$emit('click');
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	$bage-size: 12px;
+	$bage-small: scale(0.8);
+	$bage-height: 40rpx;
+
+	.uni-badge {
+		/* #ifndef APP-PLUS */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		height: $bage-height;
+		line-height: $bage-height;
+		color: $uni-text-color;
+		border-radius: 100px;
+		background-color: $uni-bg-color-hover;
+		background-color: transparent;
+		text-align: center;
+		font-family: 'Helvetica Neue', Helvetica, sans-serif;
+		font-size: $bage-size;
+		padding: 0;
+	}
+
+	.uni-badge--inverted {
+		padding: 0 5px 0 0;
+		color: $uni-bg-color-hover;
+	}
+
+	.uni-badge--default {
+		color: $uni-text-color;
+		background-color: $uni-bg-color-hover;
+	}
+
+	.uni-badge--default-inverted {
+		color: $uni-text-color-grey;
+		background-color: transparent;
+	}
+
+	.uni-badge--primary {
+		color: $uni-text-color-inverse;
+		background-color: $uni-color-primary;
+	}
+
+	.uni-badge--primary-inverted {
+		color: $uni-color-primary;
+		background-color: transparent;
+	}
+
+	.uni-badge--success {
+		color: $uni-text-color-inverse;
+		background-color: $uni-color-success;
+	}
+
+	.uni-badge--success-inverted {
+		color: $uni-color-success;
+		background-color: transparent;
+	}
+
+	.uni-badge--warning {
+		color: $uni-text-color-inverse;
+		background-color: $uni-color-warning;
+	}
+
+	.uni-badge--warning-inverted {
+		color: $uni-color-warning;
+		background-color: transparent;
+	}
+
+	.uni-badge--error {
+		color: $uni-text-color-inverse;
+		background-color: $uni-color-error;
+	}
+
+	.uni-badge--error-inverted {
+		color: $uni-color-error;
+		background-color: transparent;
+	}
+
+	.uni-badge--small {
+		transform: $bage-small;
+		transform-origin: center center;
+	}
+</style>

+ 182 - 0
components/uni-countdown/uni-countdown.vue

@@ -0,0 +1,182 @@
+<template>
+	<view class="uni-countdown">
+		<text v-if="showDay" :style="{ borderColor: borderColor, color: color, backgroundColor: backgroundColor }" class="uni-countdown__number">{{ d }}</text>
+		<text v-if="showDay" :style="{ color: splitorColor }" class="uni-countdown__splitor">天</text>
+		<text :style="{ borderColor: borderColor, color: color, backgroundColor: backgroundColor }" class="uni-countdown__number">{{ h }}</text>
+		<text :style="{ color: splitorColor }" class="uni-countdown__splitor">{{ showColon ? ':' : '时' }}</text>
+		<text :style="{ borderColor: borderColor, color: color, backgroundColor: backgroundColor }" class="uni-countdown__number">{{ i }}</text>
+		<text :style="{ color: splitorColor }" class="uni-countdown__splitor">{{ showColon ? ':' : '分' }}</text>
+		<text :style="{ borderColor: borderColor, color: color, backgroundColor: backgroundColor }" class="uni-countdown__number">{{ s }}</text>
+		<text v-if="!showColon" :style="{ color: splitorColor }" class="uni-countdown__splitor">秒</text>
+	</view>
+</template>
+<script>
+	export default {
+		name: 'UniCountdown',
+		props: {
+			showDay: {
+				type: Boolean,
+				default: true
+			},
+			showColon: {
+				type: Boolean,
+				default: true
+			},
+			backgroundColor: {
+				type: String,
+				default: '#FFFFFF'
+			},
+			borderColor: {
+				type: String,
+				default: '#000000'
+			},
+			color: {
+				type: String,
+				default: '#000000'
+			},
+			splitorColor: {
+				type: String,
+				default: '#000000'
+			},
+			day:0,
+			hour:0,
+			minute:0,
+			second:0
+		},
+		data() {
+			return {
+				timer: null,
+				syncFlag: false,
+				d: '00',
+				h: '00',
+				i: '00',
+				s: '00',
+				leftTime: 0,
+				seconds: 0
+			}
+		},
+		watch: {
+			day(val) {
+				console.log(val,'day')
+				this.changeFlag()
+			},
+			hour(val) {
+				this.changeFlag()
+			},
+			minute(val) {
+				this.changeFlag()
+			},
+			second(val) {
+				this.changeFlag()
+			}
+		},
+		created: function(e) {
+			this.startData();
+		},
+		beforeDestroy() {
+			clearInterval(this.timer)
+		},
+		methods: {
+			toSeconds(day, hours, minutes, seconds) {
+				return day * 60 * 60 * 24 + hours * 60 * 60 + minutes * 60 + seconds
+			},
+			timeUp() {
+				clearInterval(this.timer)
+				this.$emit('timeup')
+			},
+			countDown() {
+				let seconds = this.seconds
+				let [day, hour, minute, second] = [0, 0, 0, 0]
+				if (seconds > 0) {
+					day = Math.floor(seconds / (60 * 60 * 24))
+					hour = Math.floor(seconds / (60 * 60)) - (day * 24)
+					minute = Math.floor(seconds / 60) - (day * 24 * 60) - (hour * 60)
+					second = Math.floor(seconds) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60)
+				} else {
+					this.timeUp()
+				}
+				hour = hour + (day*24)
+				if (day < 10) {
+					day = '0' + day
+				}
+				if (hour < 10) {
+					hour = '0' + hour
+				}
+				if (minute < 10) {
+					minute = '0' + minute
+				}
+				if (second < 10) {
+					second = '0' + second
+				}
+				this.d = day
+				this.h = hour
+				this.i = minute
+				this.s = second
+			},
+			startData() {
+				this.seconds = this.toSeconds(this.day, this.hour, this.minute, this.second)
+				if (this.seconds <= 0) {
+					return
+				}
+				this.countDown()
+				this.timer = setInterval(() => {
+					this.seconds--
+					if (this.seconds < 0) {
+						this.timeUp()
+						return
+					}
+					this.countDown()
+				}, 1000)
+			},
+			changeFlag() {
+				if (!this.syncFlag) {
+					this.seconds = this.toSeconds(this.day, this.hour, this.minute, this.second)
+					console.log(this.seconds)
+					this.startData();
+					this.syncFlag = true;
+				}
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	@import '~@/uni.scss';
+	$countdown-height: 40rpx;
+	$countdown-width: 40rpx;
+
+	.uni-countdown {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: flex-start;
+		position: relative;
+		top: 5rpx;
+		left: 15rpx;
+	}
+
+	.uni-countdown__splitor {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		line-height: $countdown-height;
+		padding: 5rpx;
+		font-size: $uni-font-size-sm;
+	}
+
+	.uni-countdown__number {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		align-items: center;
+		width: $countdown-width;
+		height: $countdown-height;
+		line-height: $countdown-height;
+		// margin: 5rpx;
+		text-align: center;
+		font-size: $uni-font-size-sm;
+		border-radius: 8rpx;
+	}
+</style>

+ 188 - 0
components/uni-countdown/uni-countdowns.vue

@@ -0,0 +1,188 @@
+<template>
+	<view class="uni-countdown">
+		<text v-if="showDay" :style="{ borderColor: borderColor, color: color, backgroundColor: backgroundColor }" class="uni-countdown__number">{{ d }}</text>
+		<text v-if="showDay" :style="{ color: splitorColor }" class="uni-countdown__splitor">天</text>
+		<text :style="{ borderColor: borderColor, color: color, backgroundColor: backgroundColor }" class="uni-countdown__number">{{ h }}</text>
+		<text :style="{ color: splitorColor }" class="uni-countdown__splitor">{{ showColon ? ':' : '时' }}</text>
+		<text :style="{ borderColor: borderColor, color: color, backgroundColor: backgroundColor }" class="uni-countdown__number">{{ i }}</text>
+		<text :style="{ color: splitorColor }" class="uni-countdown__splitor">{{ showColon ? ':' : '分' }}</text>
+		<text :style="{ borderColor: borderColor, color: color, backgroundColor: backgroundColor }" class="uni-countdown__number">{{ s }}</text>
+		<text v-if="!showColon" :style="{ color: splitorColor }" class="uni-countdown__splitor">秒</text>
+	</view>
+</template>
+<script>
+	export default {
+		name: 'UniCountdown',
+		props: {
+			showDay: {
+				type: Boolean,
+				default: true
+			},
+			showColon: {
+				type: Boolean,
+				default: true
+			},
+			backgroundColor: {
+				type: String,
+				default: '#FFFFFF'
+			},
+			borderColor: {
+				type: String,
+				default: '#000000'
+			},
+			color: {
+				type: String,
+				default: '#000000'
+			},
+			splitorColor: {
+				type: String,
+				default: '#000000'
+			},
+			day: {
+				type: Number,
+				default: 0
+			},
+			hour: {
+				type: Number,
+				default: 0
+			},
+			minute: {
+				type: Number,
+				default: 0
+			},
+			second: {
+				type: Number,
+				default: 0
+			}
+		},
+		data() {
+			return {
+				timer: null,
+				syncFlag: false,
+				d: '00',
+				h: '00',
+				i: '00',
+				s: '00',
+				leftTime: 0,
+				seconds: 0
+			}
+		},
+		watch: {
+			day(val) {
+				this.changeFlag()
+			},
+			hour(val) {
+				this.changeFlag()
+			},
+			minute(val) {
+				this.changeFlag()
+			},
+			second(val) {
+				this.changeFlag()
+			}
+		},
+		created: function(e) {
+			this.startData();
+		},
+		beforeDestroy() {
+			clearInterval(this.timer)
+		},
+		methods: {
+			toSeconds(day, hours, minutes, seconds) {
+				return day * 60 * 60 * 24 + hours * 60 * 60 + minutes * 60 + seconds
+			},
+			timeUp() {
+				clearInterval(this.timer)
+				this.$emit('timeup')
+			},
+			countDown() {
+				let seconds = this.seconds
+				let [day, hour, minute, second] = [0, 0, 0, 0]
+				if (seconds > 0) {
+					day = Math.floor(seconds / (60 * 60 * 24))
+					hour = Math.floor(seconds / (60 * 60)) - (day * 24)
+					minute = Math.floor(seconds / 60) - (day * 24 * 60) - (hour * 60)
+					second = Math.floor(seconds) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60)
+				} else {
+					this.timeUp()
+				}
+				if (day < 10) {
+					day = '0' + day
+				}
+				if (hour < 10) {
+					hour = '0' + hour
+				}
+				if (minute < 10) {
+					minute = '0' + minute
+				}
+				if (second < 10) {
+					second = '0' + second
+				}
+				this.d = day
+				this.h = hour
+				this.i = minute
+				this.s = second
+			},
+			startData() {
+				this.seconds = this.toSeconds(this.day, this.hour, this.minute, this.second)
+				if (this.seconds <= 0) {
+					return
+				}
+				this.countDown()
+				this.timer = setInterval(() => {
+					this.seconds--
+					if (this.seconds < 0) {
+						this.timeUp()
+						return
+					}
+					this.countDown()
+				}, 1000)
+			},
+			changeFlag() {
+				if (!this.syncFlag) {
+					this.seconds = this.toSeconds(this.day, this.hour, this.minute, this.second)
+					this.startData();
+					this.syncFlag = true;
+				}
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	@import '~@/uni.scss';
+	$countdown-height: 48rpx;
+	$countdown-width: 52rpx;
+
+	.uni-countdown {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: flex-start;
+		padding: 2rpx 0;
+	}
+
+	.uni-countdown__splitor {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		line-height: $countdown-height;
+		padding: 5rpx;
+		font-size: $uni-font-size-sm;
+	}
+
+	.uni-countdown__number {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		align-items: center;
+		width: $countdown-width;
+		height: $countdown-height;
+		line-height: $countdown-height;
+		margin: 5rpx;
+		text-align: center;
+		font-size: $uni-font-size-sm;
+	}
+</style>

+ 124 - 0
components/uni-fav/uni-fav.vue

@@ -0,0 +1,124 @@
+<template>
+	<view :class="[circle === true || circle === 'true' ? 'uni-fav--circle' : '']" :style="[{ backgroundColor: checked ? bgColorChecked : bgColor }]"
+	 @click="onClick" class="uni-fav">
+		<!-- #ifdef MP-ALIPAY -->
+		<view class="uni-fav-star" v-if="!checked && (star === true || star === 'true')">
+			<uni-icons :color="fgColor" :style="{color: checked ? fgColorChecked : fgColor}" size="14" type="star-filled" />
+		</view>
+		<!-- #endif -->
+		<!-- #ifndef MP-ALIPAY -->
+		<uni-icons :color="fgColor" :style="{color: checked ? fgColorChecked : fgColor}" class="uni-fav-star" size="14" type="star-filled"
+		 v-if="!checked && (star === true || star === 'true')" />
+		<!-- #endif -->
+		<text :style="{color: checked ? fgColorChecked : fgColor}" class="uni-fav-text">{{ checked ? contentText.contentFav : contentText.contentDefault }}</text>
+	</view>
+</template>
+
+<script>
+	import uniIcons from "../uni-icons/uni-icons.vue";
+	export default {
+		name: "UniFav",
+		components: {
+			uniIcons
+		},
+		props: {
+			star: {
+				type: [Boolean, String],
+				default: true
+			},
+			bgColor: {
+				type: String,
+				default: "#eeeeee"
+			},
+			fgColor: {
+				type: String,
+				default: "#666666"
+			},
+			bgColorChecked: {
+				type: String,
+				default: "#007aff"
+			},
+			fgColorChecked: {
+				type: String,
+				default: "#FFFFFF"
+			},
+			circle: {
+				type: [Boolean, String],
+				default: false
+			},
+			checked: {
+				type: Boolean,
+				default: false
+			},
+			contentText: {
+				type: Object,
+				default () {
+					return {
+						contentDefault: "收藏",
+						contentFav: "已收藏"
+					};
+				}
+			}
+		},
+		watch: {
+			checked() {
+				if (uni.report) {
+					if (this.checked) {
+						uni.report("收藏", "收藏");
+					} else {
+						uni.report("取消收藏", "取消收藏");
+					}
+				}
+			}
+		},
+		methods: {
+			onClick() {
+				this.$emit("click");
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	$fav-height: 25px;
+
+	.uni-fav {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		justify-content: center;
+		width: 60px;
+		height: $fav-height;
+		line-height: $fav-height;
+		text-align: center;
+		border-radius: 3px;
+	}
+
+	.uni-fav--circle {
+		border-radius: 30px;
+	}
+
+	.uni-fav-star {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		height: $fav-height;
+		line-height: 24px;
+		margin-right: 3px;
+		align-items: center;
+		justify-content: center;
+	}
+
+	.uni-fav-text {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		height: $fav-height;
+		line-height: $fav-height;
+		align-items: center;
+		justify-content: center;
+		font-size: $uni-font-size-base;
+	}
+</style>

+ 96 - 0
components/uni-icons/icons.js

@@ -0,0 +1,96 @@
+export default {
+	'contact': '\ue100',
+	'person': '\ue101',
+	'personadd': '\ue102',
+	'contact-filled': '\ue130',
+	'person-filled': '\ue131',
+	'personadd-filled': '\ue132',
+	'phone': '\ue200',
+	'email': '\ue201',
+	'chatbubble': '\ue202',
+	'chatboxes': '\ue203',
+	'phone-filled': '\ue230',
+	'email-filled': '\ue231',
+	'chatbubble-filled': '\ue232',
+	'chatboxes-filled': '\ue233',
+	'weibo': '\ue260',
+	'weixin': '\ue261',
+	'pengyouquan': '\ue262',
+	'chat': '\ue263',
+	'qq': '\ue264',
+	'videocam': '\ue300',
+	'camera': '\ue301',
+	'mic': '\ue302',
+	'location': '\ue303',
+	'mic-filled': '\ue332',
+	'speech': '\ue332',
+	'location-filled': '\ue333',
+	'micoff': '\ue360',
+	'image': '\ue363',
+	'map': '\ue364',
+	'compose': '\ue400',
+	'trash': '\ue401',
+	'upload': '\ue402',
+	'download': '\ue403',
+	'close': '\ue404',
+	'redo': '\ue405',
+	'undo': '\ue406',
+	'refresh': '\ue407',
+	'star': '\ue408',
+	'plus': '\ue409',
+	'minus': '\ue410',
+	'circle': '\ue411',
+	'checkbox': '\ue411',
+	'close-filled': '\ue434',
+	'clear': '\ue434',
+	'refresh-filled': '\ue437',
+	'star-filled': '\ue438',
+	'plus-filled': '\ue439',
+	'minus-filled': '\ue440',
+	'circle-filled': '\ue441',
+	'checkbox-filled': '\ue442',
+	'closeempty': '\ue460',
+	'refreshempty': '\ue461',
+	'reload': '\ue462',
+	'starhalf': '\ue463',
+	'spinner': '\ue464',
+	'spinner-cycle': '\ue465',
+	'search': '\ue466',
+	'plusempty': '\ue468',
+	'forward': '\ue470',
+	'back': '\ue471',
+	'left-nav': '\ue471',
+	'checkmarkempty': '\ue472',
+	'home': '\ue500',
+	'navigate': '\ue501',
+	'gear': '\ue502',
+	'paperplane': '\ue503',
+	'info': '\ue504',
+	'help': '\ue505',
+	'locked': '\ue506',
+	'more': '\ue507',
+	'flag': '\ue508',
+	'home-filled': '\ue530',
+	'gear-filled': '\ue532',
+	'info-filled': '\ue534',
+	'help-filled': '\ue535',
+	'more-filled': '\ue537',
+	'settings': '\ue560',
+	'list': '\ue562',
+	'bars': '\ue563',
+	'loop': '\ue565',
+	'paperclip': '\ue567',
+	'eye': '\ue568',
+	'arrowup': '\ue580',
+	'arrowdown': '\ue581',
+	'arrowleft': '\ue582',
+	'arrowright': '\ue583',
+	'arrowthinup': '\ue584',
+	'arrowthindown': '\ue585',
+	'arrowthinleft': '\ue586',
+	'arrowthinright': '\ue587',
+	'pulldown': '\ue588',
+	'closefill': '\ue589',
+	'sound': '\ue590',
+	'scan': '\ue612'
+}

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 10 - 0
components/uni-icons/uni-icons.vue


+ 230 - 0
components/uni-list-item/uni-list-item.vue

@@ -0,0 +1,230 @@
+<template>
+	<!-- #ifdef APP-NVUE -->
+	<cell>
+	<!-- #endif -->
+	<view :class="disabled ? 'uni-list-item--disabled' : ''" :hover-class="disabled || showSwitch ? '' : 'uni-list-item--hover'"
+	 class="uni-list-item" @click="onClick">
+		<view class="uni-list-item__container" :class="{'uni-list-item--first':isFirstChild}">
+			<view v-if="thumb" class="uni-list-item__icon">
+				<image :src="thumb" mode="aspectFit" class="uni-list-item__icon-img" />
+			</view>
+			<view v-else-if="showExtraIcon" class="uni-list-item__icon">
+				<uni-icons :color="extraIcon.color" :size="extraIcon.size" :type="extraIcon.type" class="uni-icon-wrapper" />
+			</view>
+			<view class="uni-list-item__content">
+				<slot></slot>
+				<text class="uni-list-item__content-title">{{ title }}</text>
+				<text v-if="note" class="uni-list-item__content-note">{{ note }}</text>
+			</view>
+			<view class="uni-list-item__slot">
+				<slot name="right" ></slot>
+			</view>
+			<view v-if="showBadge || showArrow || showSwitch" class="uni-list-item__extra">
+				<uni-badge v-if="showBadge" :type="badgeType" :text="badgeText" />
+				<switch class="itemSwitch" v-if="showSwitch" :color="switchColor" :disabled="disabled" :checked="switchChecked" @change="onSwitchChange" />
+				<uni-icons v-if="showArrow" :size="20" class="uni-icon-wrapper" color="#bbb" type="arrowright" />
+			</view>
+		</view>
+	</view>
+	<!-- #ifdef APP-NVUE -->
+	</cell>
+	<!-- #endif -->
+</template>
+
+<script>
+	import uniIcons from '../uni-icons/uni-icons.vue'
+	import uniBadge from '../uni-badge/uni-badge.vue'
+	export default {
+		name: 'UniListItem',
+		components: {
+			uniIcons,
+			uniBadge
+		},
+		props: {
+			title: {
+				type: String,
+				default: ''
+			}, // 列表标题
+			note: {
+				type: String,
+				default: ''
+			}, // 列表描述
+			disabled: {
+				// 是否禁用
+				type: [Boolean, String],
+				default: false
+			},
+			showArrow: {
+				// 是否显示箭头
+				type: [Boolean, String],
+				default: true
+			},
+			showBadge: {
+				// 是否显示数字角标
+				type: [Boolean, String],
+				default: false
+			},
+			showSwitch: {
+				// 是否显示Switch
+				type: [Boolean, String],
+				default: false
+			},
+			switchChecked: {
+				// Switch是否被选中
+				type: [Boolean, String],
+				default: false
+			},
+			switchColor:{
+				type:String,
+				default:''
+			},
+			badgeText: {
+				// badge内容
+				type: String,
+				default: ''
+			},
+			badgeType: {
+				// badge类型
+				type: String,
+				default: 'success'
+			},
+			thumb: {
+				// 缩略图
+				type: String,
+				default: ''
+			},
+			showExtraIcon: {
+				// 是否显示扩展图标
+				type: [Boolean, String],
+				default: false
+			},
+			extraIcon: {
+				type: Object,
+				default () {
+					return {
+						type: 'contact',
+						color: '#000000',
+						size: 20
+					}
+				}
+			}
+		},
+		inject: ['list'],
+		data() {
+			return {
+				isFirstChild: false
+			}
+		},
+		mounted() {
+			if (!this.list.firstChildAppend) {
+				this.list.firstChildAppend = true
+				this.isFirstChild = true
+			}
+		},
+		methods: {
+			onClick() {
+				this.$emit('click')
+			},
+			onSwitchChange(e) {
+				this.$emit('switchChange', e.detail)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import '~@/uni.scss';
+
+	$list-item-pd: $uni-spacing-col-lg $uni-spacing-row-lg;
+
+	.uni-list-item {
+		font-size: $uni-font-size-lg;
+		position: relative;
+		flex-direction: column;
+		justify-content: space-between;
+		padding-left: $uni-spacing-row-lg;
+	}
+
+	.uni-list-item--disabled {
+		opacity: 0.3;
+	}
+
+	.uni-list-item--hover {
+		background-color: $uni-bg-color-hover;
+	}
+
+	.uni-list-item__container {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		padding: $list-item-pd;
+		padding-left: 0;
+		flex: 1;
+		position: relative;
+		justify-content: space-between;
+		align-items: center;
+		border-top-color: $uni-border-color;
+		border-top-style: solid;
+		border-top-width: 1px;
+	}
+
+	.uni-list-item--first {
+		border-top-width: 0px;
+	}
+
+	.uni-list-item__content {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex: 1;
+		overflow: hidden;
+		flex-direction: column;
+		color: #3b4144;
+
+	}
+
+	.uni-list-item__content-title {
+		font-size: $uni-font-size-base;
+		color: #3b4144;
+		overflow: hidden;
+	}
+
+	.uni-list-item__content-note {
+		margin-top: 6rpx;
+		color: $uni-text-color-grey;
+		font-size: $uni-font-size-sm;
+		overflow: hidden;
+	}
+
+	.uni-list-item__extra {
+		// width: 25%;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: flex-end;
+		align-items: center;
+	}
+
+	.uni-list-item__icon {
+		margin-right: 18rpx;
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		display: flex;
+	}
+
+	.uni-list-item__icon-img {
+		height: $uni-img-size-base;
+		width: $uni-img-size-base;
+	}
+	// 修改switch默认大小
+	.itemSwitch{
+		transform: translateX(16rpx) scale(.84);
+	}
+	.uni-list-item__slot{
+		color: #909399;
+		font-size: 28rpx;
+	}
+</style>

+ 68 - 0
components/uni-list/uni-list.vue

@@ -0,0 +1,68 @@
+<template>
+	<!-- #ifndef APP-NVUE -->
+	<view class="uni-list">
+		<slot />
+	</view>
+	<!-- #endif -->
+	<!-- #ifdef APP-NVUE -->
+	<list class="uni-list" :enableBackToTop="enableBackToTop" loadmoreoffset="15" :scroll-y="scrollY" @loadmore="loadMore">
+		<slot />
+	</list>
+	<!-- #endif -->
+</template>
+
+<script>
+	export default {
+		name: 'UniList',
+		'mp-weixin': {
+			options: {
+				multipleSlots: false
+			}
+		},
+		props: {
+			enableBackToTop: {
+				type: [Boolean, String],
+				default: false
+			},
+			scrollY: {
+				type: [Boolean, String],
+				default: false
+			}
+		},
+		provide() {
+			return {
+				list: this
+			}
+		},
+		created() {
+			this.firstChildAppend = false
+		},
+		methods: {
+			loadMore(e) {
+				this.$emit("scrolltolower");
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.uni-list {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		background-color: $uni-bg-color;
+		position: relative;
+		flex-direction: column;
+		// border-bottom-color: $uni-border-color;
+		// border-bottom-style: solid;
+		// border-bottom-width: 1px;
+	}
+	/* #ifndef APP-NVUE */
+	.uni-list:before {
+		height: 0;
+	}
+	.uni-list:after {
+		height: 0;
+	}
+	/* #endif */
+
+</style>

+ 65 - 0
components/uni-list/uni-refresh.vue

@@ -0,0 +1,65 @@
+<template>
+    <!-- #ifdef APP-NVUE -->
+    <refresh :display="display" @refresh="onrefresh" @pullingdown="onpullingdown">
+        <slot />
+    </refresh>
+    <!-- #endif -->
+    <!-- #ifndef APP-NVUE -->
+    <view ref="uni-refresh" class="uni-refresh" v-show="isShow">
+        <slot />
+    </view>
+    <!-- #endif -->
+</template>
+
+<script>
+    export default {
+        name: 'UniRefresh',
+        props: {
+            display: {
+                type: [String],
+                default: "hide"
+            }
+        },
+        data() {
+            return {
+                pulling: false
+            }
+        },
+        computed: {
+            isShow() {
+                if (this.display === "show" || this.pulling === true) {
+                    return true;
+                }
+                return false;
+            }
+        },
+        created() {},
+        methods: {
+            onchange(value) {
+                this.pulling = value;
+            },
+            onrefresh(e) {
+                this.$emit("refresh", e);
+            },
+            onpullingdown(e) {
+                // #ifdef APP-NVUE
+                this.$emit("pullingdown", e);
+                // #endif
+                // #ifndef APP-NVUE
+                var detail = {
+                    viewHeight: 90,
+                    pullingDistance: e.height
+                }
+                this.$emit("pullingdown", detail);
+                // #endif
+            }
+        }
+    }
+</script>
+
+<style>
+    .uni-refresh {
+        height: 0;
+        overflow: hidden;
+    }
+</style>

+ 87 - 0
components/uni-list/uni-refresh.wxs

@@ -0,0 +1,87 @@
+var pullDown = {
+    threshold: 95,
+    maxHeight: 200,
+    callRefresh: 'onrefresh',
+    callPullingDown: 'onpullingdown',
+    refreshSelector: '.uni-refresh'
+};
+
+function ready(newValue, oldValue, ownerInstance, instance) {
+    var state = instance.getState()
+    state.canPullDown = newValue;
+    console.log(newValue);
+}
+
+function touchStart(e, instance) {
+    var state = instance.getState();
+    state.refreshInstance = instance.selectComponent(pullDown.refreshSelector);
+    state.canPullDown = (state.refreshInstance != null && state.refreshInstance != undefined);
+    if (!state.canPullDown) {
+        return
+    }
+
+    console.log("touchStart");
+
+    state.height = 0;
+    state.touchStartY = e.touches[0].pageY || e.changedTouches[0].pageY;
+    state.refreshInstance.setStyle({
+        'height': 0
+    });
+    state.refreshInstance.callMethod("onchange", true);
+}
+
+function touchMove(e, ownerInstance) {
+    var instance = e.instance;
+    var state = instance.getState();
+    if (!state.canPullDown) {
+        return
+    }
+
+    var oldHeight = state.height;
+    var endY = e.touches[0].pageY || e.changedTouches[0].pageY;
+    var height = endY - state.touchStartY;
+    if (height > pullDown.maxHeight) {
+        return;
+    }
+
+    var refreshInstance = state.refreshInstance;
+    refreshInstance.setStyle({
+        'height': height + 'px'
+    });
+
+    height = height < pullDown.maxHeight ? height : pullDown.maxHeight;
+    state.height = height;
+    refreshInstance.callMethod(pullDown.callPullingDown, {
+        height: height
+    });
+}
+
+function touchEnd(e, ownerInstance) {
+    var state = e.instance.getState();
+    if (!state.canPullDown) {
+        return
+    }
+
+    state.refreshInstance.callMethod("onchange", false);
+
+    var refreshInstance = state.refreshInstance;
+    if (state.height > pullDown.threshold) {
+        refreshInstance.callMethod(pullDown.callRefresh);
+        return;
+    }
+
+    refreshInstance.setStyle({
+        'height': 0
+    });
+}
+
+function propObserver(newValue, oldValue, instance) {
+    pullDown = newValue;
+}
+
+module.exports = {
+    touchmove: touchMove,
+    touchstart: touchStart,
+    touchend: touchEnd,
+    propObserver: propObserver
+}

+ 194 - 0
components/uni-load-more/uni-load-more.vue

@@ -0,0 +1,194 @@
+<template>
+	<view class="uni-load-more">
+		<view class="uni-load-more__img" v-show="status === 'loading' && showIcon">
+			<view class="load1 load">
+				<view class="item" :style="{background:color}"></view>
+				<view class="item" :style="{background:color}"></view>
+				<view class="item" :style="{background:color}"></view>
+				<view class="item" :style="{background:color}"></view>
+			</view>
+			<view class="load2 load">
+				<view class="item" :style="{background:color}"></view>
+				<view class="item"  :style="{background:color}"></view>
+				<view class="item" :style="{background:color}"></view>
+				<view class="item" :style="{background:color}"></view>
+			</view>
+			<view class="load3 load">
+				<view class="item" :style="{background:color}"></view>
+				<view class="item" :style="{background:color}"></view>
+				<view class="item" :style="{background:color}"></view>
+				<view class="item" :style="{background:color}"></view>
+			</view>
+		</view>
+		<text class="uni-load-more__text" :style="{color:color}">{{status === 'more' ? contentText.contentdown : (status === 'loading' ? contentText.contentrefresh : contentText.contentnomore)}}</text>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: "uni-load-more",
+		props: {
+			status: {
+				//上拉的状态:more-loading前;loading-loading中;noMore-没有更多了
+				type: String,
+				default: 'more'
+			},
+			showIcon: {
+				type: Boolean,
+				default: true
+			},
+			color: {
+				type: String,
+				default: "#777777"
+			},
+			contentText: {
+				type: Object,
+				default () {
+					return {
+						contentdown: "上拉显示更多",
+						contentrefresh: "正在加载...",
+						contentnomore: "没有更多数据了"
+					};
+				}
+			}
+		},
+		data() {
+			return {}
+		}
+	}
+</script>
+
+<style>
+	@charset "UTF-8";
+
+	.uni-load-more {
+		display: flex;
+		flex-direction: row;
+		height: 80upx;
+		align-items: center;
+		justify-content: center
+	}
+
+	.uni-load-more__text {
+		font-size: 28upx;
+		color: #999
+	}
+
+	.uni-load-more__img {
+		height: 24px;
+		width: 24px;
+		margin-right: 10px
+	}
+
+	.uni-load-more__img>.load {
+		position: absolute
+	}
+
+	.uni-load-more__img>.load .item {
+		width: 6px;
+		height: 2px;
+		border-top-left-radius: 1px;
+		border-bottom-left-radius: 1px;
+		background: #999;
+		position: absolute;
+		opacity: .2;
+		transform-origin: 50%;
+		animation: load 1.56s ease infinite
+	}
+
+	.uni-load-more__img>.load .item:nth-child(1) {
+		transform: rotate(90deg);
+		top: 2px;
+		left: 9px
+	}
+
+	.uni-load-more__img>.load .item:nth-child(2) {
+		transform: rotate(180deg);
+		top: 11px;
+		right: 0
+	}
+
+	.uni-load-more__img>.load .item:nth-child(3) {
+		transform: rotate(270deg);
+		bottom: 2px;
+		left: 9px
+	}
+
+	.uni-load-more__img>.load .item:nth-child(4) {
+		top: 11px;
+		left: 0
+	}
+
+	.load1,
+	.load2,
+	.load3 {
+		height: 24px;
+		width: 24px
+	}
+
+	.load2 {
+		transform: rotate(30deg)
+	}
+
+	.load3 {
+		transform: rotate(60deg)
+	}
+
+	.load1 .item:nth-child(1) {
+		animation-delay: 0s
+	}
+
+	.load2 .item:nth-child(1) {
+		animation-delay: .13s
+	}
+
+	.load3 .item:nth-child(1) {
+		animation-delay: .26s
+	}
+
+	.load1 .item:nth-child(2) {
+		animation-delay: .39s
+	}
+
+	.load2 .item:nth-child(2) {
+		animation-delay: .52s
+	}
+
+	.load3 .item:nth-child(2) {
+		animation-delay: .65s
+	}
+
+	.load1 .item:nth-child(3) {
+		animation-delay: .78s
+	}
+
+	.load2 .item:nth-child(3) {
+		animation-delay: .91s
+	}
+
+	.load3 .item:nth-child(3) {
+		animation-delay: 1.04s
+	}
+
+	.load1 .item:nth-child(4) {
+		animation-delay: 1.17s
+	}
+
+	.load2 .item:nth-child(4) {
+		animation-delay: 1.3s
+	}
+
+	.load3 .item:nth-child(4) {
+		animation-delay: 1.43s
+	}
+
+	@-webkit-keyframes load {
+		0% {
+			opacity: 1
+		}
+
+		100% {
+			opacity: .2
+		}
+	}
+</style>

+ 396 - 0
components/uni-notice-bar/uni-notice-bar.vue

@@ -0,0 +1,396 @@
+<template>
+	<view v-if="show" class="uni-noticebar" :style="{ backgroundColor: backgroundColor }" @click="onClick">
+		<!-- #ifdef MP-ALIPAY -->
+		<view v-if="showClose === true || showClose === 'true'" class="uni-noticebar-close" @click="close">
+			<uni-icons type="closefill" :color="color" size="12" />
+		</view>
+		<view v-if="showIcon === true || showIcon === 'true'" class="uni-noticebar-icon">
+			<uni-icons type="sound" :color="color" size="14" />
+		</view>
+		<!-- #endif -->
+		<!-- #ifndef MP-ALIPAY -->
+		<uni-icons v-if="showClose === true || showClose === 'true'" class="uni-noticebar-close" type="closefill" :color="color"
+		 size="12" @click="close" />
+		<uni-icons v-if="showIcon === true || showIcon === 'true'" class="uni-noticebar-icon" type="sound" :color="color"
+		 size="14" />
+		<!-- #endif -->
+		<view ref="textBox" class="uni-noticebar__content-wrapper" :class="{'uni-noticebar__content-wrapper--scrollable':scrollable, 'uni-noticebar__content-wrapper--single':!scrollable && (single || moreText)}">
+			<view :id="elIdBox" class="uni-noticebar__content" :class="{'uni-noticebar__content--scrollable':scrollable, 'uni-noticebar__content--single':!scrollable && (single || moreText)}">
+				<text :id="elId" ref="animationEle" class="uni-noticebar__content-text" :class="{'uni-noticebar__content-text--scrollable':scrollable,'uni-noticebar__content-text--single':!scrollable && (single || moreText)}"
+				 :style="{color:color, width:wrapWidth+'px', 'animationDuration': animationDuration, '-webkit-animationDuration': animationDuration ,animationPlayState: webviewHide?'paused':animationPlayState,'-webkit-animationPlayState':webviewHide?'paused':animationPlayState, animationDelay: animationDelay, '-webkit-animationDelay':animationDelay}">{{text}}</text>
+			</view>
+		</view>
+		<view v-if="showGetMore === true || showGetMore === 'true'" class="uni-noticebar__more" @click="clickMore">
+			<text v-if="moreText" :style="{ color: moreColor }" class="uni-noticebar__more-text">{{ moreText }}</text>
+			<uni-icons type="arrowright" :color="moreColor" size="14" />
+		</view>
+	</view>
+</template>
+
+<script>
+	import uniIcons from '../uni-icons/uni-icons.vue'
+	// #ifdef APP-NVUE
+	const dom = weex.requireModule('dom');
+	const animation = weex.requireModule('animation');
+	// #endif
+
+	/**
+	 * NoticeBar 自定义导航栏
+	 * @description 通告栏组件
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=30
+	 * @property {Number} speed 文字滚动的速度,默认100px/秒
+	 * @property {String} text 显示文字
+	 * @property {String} backgroundColor 背景颜色
+	 * @property {String} color 文字颜色
+	 * @property {String} moreColor 查看更多文字的颜色
+	 * @property {String} moreText 设置“查看更多”的文本
+	 * @property {Boolean} single = [true|false] 是否单行
+	 * @property {Boolean} scrollable = [true|false] 是否滚动,为true时,NoticeBar为单行
+	 * @property {Boolean} showIcon = [true|false] 是否显示左侧喇叭图标
+	 * @property {Boolean} showClose = [true|false] 是否显示左侧关闭按钮
+	 * @property {Boolean} showGetMore = [true|false] 是否显示右侧查看更多图标,为true时,NoticeBar为单行
+	 * @event {Function} click 点击 NoticeBar 触发事件
+	 * @event {Function} close 关闭 NoticeBar 触发事件
+	 * @event {Function} getmore 点击”查看更多“时触发事件
+	 */
+
+	export default {
+		name: 'UniNoticeBar',
+		components: {
+			uniIcons
+		},
+		props: {
+			text: {
+				type: String,
+				default: ''
+			},
+			moreText: {
+				type: String,
+				default: ''
+			},
+			backgroundColor: {
+				type: String,
+				default: '#fffbe8'
+			},
+			speed: {
+				// 默认1s滚动100px
+				type: Number,
+				default: 100
+			},
+			color: {
+				type: String,
+				default: '#de8c17'
+			},
+			moreColor: {
+				type: String,
+				default: '#999999'
+			},
+			single: {
+				// 是否单行
+				type: [Boolean, String],
+				default: false
+			},
+			scrollable: {
+				// 是否滚动,添加后控制单行效果取消
+				type: [Boolean, String],
+				default: false
+			},
+			showIcon: {
+				// 是否显示左侧icon
+				type: [Boolean, String],
+				default: false
+			},
+			showGetMore: {
+				// 是否显示右侧查看更多
+				type: [Boolean, String],
+				default: false
+			},
+			showClose: {
+				// 是否显示左侧关闭按钮
+				type: [Boolean, String],
+				default: false
+			}
+		},
+		data() {
+			const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}`
+			const elIdBox = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}`
+			return {
+				textWidth: 0,
+				boxWidth: 0,
+				wrapWidth: '',
+				webviewHide: false,
+				// #ifdef APP-NVUE
+				stopAnimation: false,
+				// #endif
+				elId: elId,
+				elIdBox: elIdBox,
+				show: true,
+				animationDuration: 'none',
+				animationPlayState: 'paused',
+				animationDelay: '0s'
+			}
+		},
+		mounted() {
+			// #ifdef APP-PLUS
+			var pages = getCurrentPages();
+			var page = pages[pages.length - 1];
+			var currentWebview = page.$getAppWebview();
+			currentWebview.addEventListener('hide',()=>{
+				this.webviewHide = true
+			})
+			currentWebview.addEventListener('show',()=>{
+				this.webviewHide = false
+			})
+			// #endif
+			this.$nextTick(() => {
+				this.initSize()
+			})
+		},
+		// #ifdef APP-NVUE
+		beforeDestroy() {
+			this.stopAnimation = true
+		},
+		// #endif
+		methods: {
+			initSize() {
+				if (this.scrollable) {
+					// #ifndef APP-NVUE
+					let query = [],
+						boxWidth = 0,
+						textWidth = 0;
+					let textQuery = new Promise((resolve, reject) => {
+						uni.createSelectorQuery()
+							// #ifndef MP-ALIPAY
+							.in(this)
+							// #endif
+							.select(`#${this.elId}`)
+							.boundingClientRect()
+							.exec(ret => {
+								this.textWidth = ret[0].width
+								resolve()
+							})
+					})
+					let boxQuery = new Promise((resolve, reject) => {
+						uni.createSelectorQuery()
+							// #ifndef MP-ALIPAY
+							.in(this)
+							// #endif
+							.select(`#${this.elIdBox}`)
+							.boundingClientRect()
+							.exec(ret => {
+								this.boxWidth = ret[0].width
+								resolve()
+							})
+					})
+					query.push(textQuery)
+					query.push(boxQuery)
+					Promise.all(query).then(() => {
+						this.animationDuration = `${this.textWidth / this.speed}s`
+						this.animationDelay = `-${this.boxWidth / this.speed}s`
+						setTimeout(() => {
+							this.animationPlayState = 'running'
+						}, 1000)
+					})
+					// #endif
+					// #ifdef APP-NVUE
+					dom.getComponentRect(this.$refs['animationEle'], (res) => {
+						let winWidth = uni.getSystemInfoSync().windowWidth
+						this.textWidth = res.size.width
+						animation.transition(this.$refs['animationEle'], {
+							styles: {
+								transform: `translateX(-${winWidth}px)`
+							},
+							duration: 0,
+							timingFunction: 'linear',
+							delay: 0
+						}, () => {
+							if (!this.stopAnimation) {
+								animation.transition(this.$refs['animationEle'], {
+									styles: {
+										transform: `translateX(-${this.textWidth}px)`
+									},
+									timingFunction: 'linear',
+									duration: (this.textWidth - winWidth) / this.speed * 1000,
+									delay: 1000
+								}, () => {
+									if (!this.stopAnimation) {
+										this.loopAnimation()
+									}
+								});
+							}
+						});
+					})
+					// #endif
+				}
+				// #ifdef APP-NVUE
+				if (!this.scrollable && (this.single || this.moreText)) {
+					dom.getComponentRect(this.$refs['textBox'], (res) => {
+						this.wrapWidth = res.size.width
+					})
+				}
+				// #endif
+			},
+			loopAnimation() {
+				// #ifdef APP-NVUE
+				animation.transition(this.$refs['animationEle'], {
+					styles: {
+						transform: `translateX(0px)`
+					},
+					duration: 0
+				}, () => {
+					if (!this.stopAnimation) {
+						animation.transition(this.$refs['animationEle'], {
+							styles: {
+								transform: `translateX(-${this.textWidth}px)`
+							},
+							duration: this.textWidth / this.speed * 1000,
+							timingFunction: 'linear',
+							delay: 0
+						}, () => {
+							if (!this.stopAnimation) {
+								this.loopAnimation()
+							}
+						});
+					}
+				});
+				// #endif
+			},
+			clickMore() {
+				this.$emit('getmore')
+			},
+			close() {
+				this.show = false;
+				this.$emit('close')
+			},
+			onClick() {
+				this.$emit('click')
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import '@/uni.scss';
+
+	.uni-noticebar {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		width: 100%;
+		box-sizing: border-box;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		padding: 6px 12px;
+		margin-bottom: 10px;
+	}
+
+	.uni-noticebar-close {
+		margin-right: 5px;
+	}
+
+	.uni-noticebar-icon {
+		margin-right: 5px;
+	}
+
+	.uni-noticebar__content-wrapper {
+		flex: 1;
+		flex-direction: column;
+		overflow: hidden;
+	}
+
+	.uni-noticebar__content-wrapper--single {
+		/* #ifndef APP-NVUE */
+		line-height: 18px;
+		/* #endif */
+	}
+
+	.uni-noticebar__content-wrapper--single,
+	.uni-noticebar__content-wrapper--scrollable {
+		flex-direction: row;
+	}
+
+	/* #ifndef APP-NVUE */
+	.uni-noticebar__content-wrapper--scrollable {
+		position: relative;
+		height: 18px;
+	}
+	/* #endif */
+
+	.uni-noticebar__content--scrollable {
+		/* #ifdef APP-NVUE */
+		flex: 0;
+		/* #endif */
+		/* #ifndef APP-NVUE */
+		flex: 1;
+		display: block;
+		overflow: hidden;
+		/* #endif */
+	}
+
+	.uni-noticebar__content--single {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		flex: none;
+		width: 100%;
+		justify-content: center;
+		/* #endif */
+	}
+
+	.uni-noticebar__content-text {
+		font-size: 14px;
+		line-height: 18px;
+		/* #ifndef APP-NVUE */
+		word-break: break-all;
+		/* #endif */
+	}
+
+	.uni-noticebar__content-text--single {
+		/* #ifdef APP-NVUE */
+		lines: 1;
+		/* #endif */
+		/* #ifndef APP-NVUE */
+		display: block;
+		width: 100%;
+		white-space: nowrap;
+		/* #endif */
+		overflow: hidden;
+		text-overflow: ellipsis;
+	}
+
+	.uni-noticebar__content-text--scrollable {
+		/* #ifdef APP-NVUE */
+		lines: 1;
+		padding-left: 750rpx;
+		/* #endif */
+		/* #ifndef APP-NVUE */
+		position: absolute;
+		display: block;
+		height: 18px;
+		line-height: 18px;
+		white-space: nowrap;
+		padding-left: 100%;
+		animation: notice 10s 0s linear infinite both;
+		animation-play-state: paused;
+		/* #endif */
+	}
+
+	.uni-noticebar__more {
+		/* #ifndef APP-NVUE */
+		display: inline-flex;
+		/* #endif */
+		flex-direction: row;
+		flex-wrap: nowrap;
+		align-items: center;
+		padding-left: 5px;
+	}
+
+	.uni-noticebar__more-text {
+		font-size: 14px;
+	}
+
+	@keyframes notice {
+		100% {
+			transform: translate3d(-100%, 0, 0);
+		}
+	}
+</style>

+ 198 - 0
components/uni-number-box.vue

@@ -0,0 +1,198 @@
+<template>
+	<view class="uni-numbox">
+		<view class="uni-numbox-minus" 
+			@click="_calcValue('subtract')"
+		>
+			<text class="iconfont iconmove" :class="minDisabled?'uni-numbox-disabled': ''" ></text>
+		</view>
+		<input 
+			class="uni-numbox-value" 
+			type="number"  
+			:disabled="disabled"
+			:value="inputValue" 
+			@blur="_onBlur"
+		>
+		<view 
+			class="uni-numbox-plus" 
+			@click="_calcValue('add')"
+		>
+			<text class="iconfont iconadd" :class="maxDisabled?'uni-numbox-disabled': ''" ></text>
+		</view>
+	</view>
+</template>
+<script>
+	export default {
+		name: 'uni-number-box',
+		props: {
+			isMax: {
+				type: Boolean,
+				default: false
+			},
+			isMin: {
+				type: Boolean,
+				default: false
+			},
+			index: {
+				type: Number,
+				default: 0
+			},
+			value: {
+				type: Number,
+				default: 0
+			},
+			min: {
+				type: Number,
+				default: -Infinity
+			},
+			max: {
+				type: Number,
+				default: Infinity
+			},
+			step: {
+				type: Number,
+				default: 1
+			},
+			disabled: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+				inputValue: this.value,
+				minDisabled: false,
+				maxDisabled: false
+			}
+		},
+		created(){
+			this.maxDisabled = this.isMax;
+			this.minDisabled = this.isMin;
+		},
+		computed: {
+
+		},
+		watch: {
+			inputValue(number) {
+				const data = {
+					number: number,
+					index: this.index
+				}
+				this.$emit('eventChange', data);
+			}
+		},
+		methods: {
+			_calcValue(type) {
+				const scale = this._getDecimalScale();
+				let value = this.inputValue * scale;
+				let newValue = 0;
+				let step = this.step * scale;
+				
+				if(type === 'subtract'){
+					newValue = value - step;
+					if (newValue <= this.min){
+						this.minDisabled = true;
+					}
+					if(newValue < this.min){
+						newValue = this.min
+					}
+					if(newValue < this.max && this.maxDisabled === true){
+						this.maxDisabled = false;
+					}
+				}else if(type === 'add'){
+					newValue = value + step;
+					if (newValue >= this.max){
+						this.maxDisabled = true;
+					}
+					if(newValue > this.max){
+						newValue = this.max
+					}
+					if(newValue > this.min && this.minDisabled === true){
+						this.minDisabled = false;
+					}
+				}
+				if(newValue === value){
+					return;
+				}
+				this.inputValue = newValue / scale;
+			},
+			_getDecimalScale() {
+				let scale = 1;
+				// 浮点型
+				if (~~this.step !== this.step) {
+					scale = Math.pow(10, (this.step + '').split('.')[1].length);
+				}
+				return scale;
+			},
+			_onBlur(event) {
+				let value = event.detail.value;
+				if (!value) {
+					this.inputValue = 0;
+					return
+				}
+				value = +value;
+				if (value > this.max) {
+					value = this.max;
+				} else if (value < this.min) {
+					value = this.min
+				}
+
+				this.inputValue = value
+			}
+		}
+	}
+</script>
+<style>
+	.uni-numbox {
+		/* position:absolute; */
+		/* left: 30rpx; */
+		/* bottom: 0; */
+		display: flex;
+		justify-content: flex-start;
+		align-items: center;
+		width:230rpx;
+		height: 70rpx;
+		background:#f5f5f5;
+	}
+
+	.uni-numbox-minus,
+	.uni-numbox-plus {
+		margin: 0;
+		background-color: #f5f5f5;
+		width: 70rpx;
+		height: 100%;
+		line-height: 70rpx;
+		text-align: center;
+		position: relative;
+	}
+	.uni-numbox-minus .yticon,
+	.uni-numbox-plus .yticon{
+		font-size: 36rpx;
+		color: #555;
+	}
+
+	.uni-numbox-minus {
+		border-right: none;
+		border-top-left-radius: 6rpx;
+		border-bottom-left-radius: 6rpx;
+	}
+
+	.uni-numbox-plus {
+		border-left: none;
+		border-top-right-radius: 6rpx;
+		border-bottom-right-radius: 6rpx;
+	}
+
+	.uni-numbox-value {
+		position: relative;
+		background-color: #f5f5f5;
+		width: 90rpx;
+		height: 50rpx;
+		text-align: center;
+		padding: 0;
+		font-size: 30rpx;
+	}
+
+	.uni-numbox-disabled.iconfont {
+		color: #d6d6d6;
+	}
+</style>

+ 243 - 0
components/uni-popup/uni-popup-dialog.vue

@@ -0,0 +1,243 @@
+<template>
+	<view class="uni-popup-dialog">
+		<view class="uni-dialog-title">
+			<text class="uni-dialog-title-text" :class="['uni-popup__'+dialogType]">{{title}}</text>
+		</view>
+		<view class="uni-dialog-content">
+			<text class="uni-dialog-content-text" v-if="mode === 'base'">{{content}}</text>
+			<input v-else class="uni-dialog-input" v-model="val" type="text" :placeholder="placeholder" :focus="focus" >
+		</view>
+		<view class="uni-dialog-button-group">
+			<view class="uni-dialog-button" @click="close">
+				<text class="uni-dialog-button-text">取消</text>
+			</view>
+			<view class="uni-dialog-button uni-border-left" @click="onOk">
+				<text class="uni-dialog-button-text uni-button-color">确定</text>
+			</view>
+		</view>
+
+	</view>
+</template>
+
+<script>
+	/**
+	 * PopUp 弹出层-对话框样式
+	 * @description 弹出层-对话框样式
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=329
+	 * @property {String} value input 模式下的默认值
+	 * @property {String} placeholder input 模式下输入提示
+	 * @property {String} type = [success|warning|info|error] 主题样式
+	 *  @value success 成功
+	 * 	@value warning 提示
+	 * 	@value info 消息
+	 * 	@value error 错误
+	 * @property {String} mode = [base|input] 模式、
+	 * 	@value base 基础对话框
+	 * 	@value input 可输入对话框
+	 * @property {String} content 对话框内容
+	 * @property {Boolean} beforeClose 是否拦截取消事件
+	 * @event {Function} confirm 点击确认按钮触发
+	 * @event {Function} close 点击取消按钮触发
+	 */
+
+	export default {
+		name: "uniPopupDialog",
+		props: {
+			value: {
+				type: [String, Number],
+				default: ''
+			},
+			placeholder: {
+				type: [String, Number],
+				default: '请输入内容'
+			},
+			/**
+			 * 对话框主题 success/warning/info/error	  默认 success
+			 */
+			type: {
+				type: String,
+				default: 'error'
+			},
+			/**
+			 * 对话框模式 base/input
+			 */
+			mode: {
+				type: String,
+				default: 'base'
+			},
+			/**
+			 * 对话框标题
+			 */
+			title: {
+				type: String,
+				default: '提示'
+			},
+			/**
+			 * 对话框内容
+			 */
+			content: {
+				type: String,
+				default: ''
+			},
+			/**
+			 * 拦截取消事件 ,如果拦截取消事件,必须监听close事件,执行 done()
+			 */
+			beforeClose: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+				dialogType: 'error',
+				focus: false,
+				val: ""
+			}
+		},
+		inject: ['popup'],
+		watch: {
+			type(val) {
+				this.dialogType = val
+			},
+			mode(val) {
+				if (val === 'input') {
+					this.dialogType = 'info'
+				}
+			},
+			value(val) {
+				this.val = val
+			}
+		},
+		created() {
+			// 对话框遮罩不可点击
+			this.popup.mkclick = false
+			if (this.mode === 'input') {
+				this.dialogType = 'info'
+				this.val = this.value
+			} else {
+				this.dialogType = this.type
+			}
+		},
+		mounted() {
+			this.focus = true
+		},
+		methods: {
+			/**
+			 * 点击确认按钮
+			 */
+			onOk() {
+				this.$emit('confirm', () => {
+					this.popup.close()
+					if (this.mode === 'input') this.val = this.value
+				}, this.mode === 'input' ? this.val : '')
+			},
+			/**
+			 * 点击取消按钮
+			 */
+			close() {
+				if (this.beforeClose) {
+					this.$emit('close', () => {
+						this.popup.close()
+					})
+					return
+				}
+				this.popup.close()
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.uni-popup-dialog {
+		width: 300px;
+		border-radius: 15px;
+		background-color: #fff;
+	}
+
+	.uni-dialog-title {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		padding-top: 15px;
+		padding-bottom: 5px;
+	}
+
+	.uni-dialog-title-text {
+		font-size: 16px;
+		font-weight: 500;
+	}
+
+	.uni-dialog-content {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		padding: 5px 15px 15px 15px;
+	}
+
+	.uni-dialog-content-text {
+		font-size: 14px;
+		color: #6e6e6e;
+	}
+
+	.uni-dialog-button-group {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		border-top-color: #f5f5f5;
+		border-top-style: solid;
+		border-top-width: 1px;
+	}
+
+	.uni-dialog-button {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+
+		flex: 1;
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		height: 45px;
+	}
+
+	.uni-border-left {
+		border-left-color: #f0f0f0;
+		border-left-style: solid;
+		border-left-width: 1px;
+	}
+
+	.uni-dialog-button-text {
+		font-size: 14px;
+	}
+
+	.uni-button-color {
+		color: $uni-color-primary;
+	}
+
+	.uni-dialog-input {
+		flex: 1;
+		font-size: 14px;
+	}
+
+	.uni-popup__success {
+		color: $uni-color-success;
+	}
+
+	.uni-popup__warn {
+		color: $uni-color-warning;
+	}
+
+	.uni-popup__error {
+		color: $uni-color-error;
+	}
+
+	.uni-popup__info {
+		color: #909399;
+	}
+</style>

+ 116 - 0
components/uni-popup/uni-popup-message.vue

@@ -0,0 +1,116 @@
+<template>
+	<view class="uni-popup-message" :class="'uni-popup__'+[type]">
+		<text class="uni-popup-message-text" :class="'uni-popup__'+[type]+'-text'">{{message}}</text>
+	</view>
+</template>
+
+<script>
+	
+	/**
+	 * PopUp 弹出层-消息提示
+	 * @description 弹出层-消息提示
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=329
+	 * @property {String} type = [success|warning|info|error] 主题样式
+	 *  @value success 成功
+	 * 	@value warning 提示
+	 * 	@value info 消息
+	 * 	@value error 错误
+	 * @property {String} message 消息提示文字
+	 * @property {String} duration 显示时间,设置为 0 则不会自动关闭
+	 */
+	
+	export default {
+		name: 'UniPopupMessage',
+		props: {
+			/**
+			 * 主题 success/warning/info/error	  默认 success
+			 */
+			type: {
+				type: String,
+				default: 'success'
+			},
+			/**
+			 * 消息文字
+			 */
+			message: {
+				type: String,
+				default: ''
+			},
+			/**
+			 * 显示时间,设置为 0 则不会自动关闭
+			 */
+			duration: {
+				type: Number,
+				default: 3000
+			}
+		},
+		inject: ['popup'],
+		data() {
+			return {}
+		},
+		created() {
+			this.popup.childrenMsg = this
+		},
+		methods: {
+			open() {
+				if (this.duration === 0) return
+				clearTimeout(this.popuptimer)
+				this.popuptimer = setTimeout(() => {
+					this.popup.close()
+				}, this.duration)
+			},
+			close() {
+				clearTimeout(this.popuptimer)
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.uni-popup-message {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		background-color: #e1f3d8;
+		padding: 10px 15px;
+		border-color: #eee;
+		border-style: solid;
+		border-width: 1px;
+	}
+	.uni-popup-message-text {
+		font-size: 14px;
+		padding: 0;
+	}
+
+	.uni-popup__success {
+		background-color: #e1f3d8;
+	}
+
+	.uni-popup__success-text {
+		color: #67C23A;
+	}
+
+	.uni-popup__warn {
+		background-color: #faecd8;
+	}
+
+	.uni-popup__warn-text {
+		color: #E6A23C;
+	}
+
+	.uni-popup__error {
+		background-color: #fde2e2;
+	}
+
+	.uni-popup__error-text {
+		color: #F56C6C;
+	}
+
+	.uni-popup__info {
+		background-color: #F2F6FC;
+	}
+
+	.uni-popup__info-text {
+		color: #909399;
+	}
+</style>

+ 263 - 0
components/uni-popup/uni-popup-ori.vue

@@ -0,0 +1,263 @@
+<template>
+	<view v-if="showPopup" class="uni-popup" @touchmove.stop.prevent="clear">
+		<uni-transition :mode-class="['fade']" :styles="maskClass" :duration="duration" :show="showTrans" @click="onTap" />
+		<uni-transition :mode-class="ani" :styles="transClass" :duration="duration" :show="showTrans" @click="onTap">
+			<view class="uni-popup__wrapper-box" @click.stop="clear">
+				<slot />
+			</view>
+		</uni-transition>
+	</view>
+</template>
+
+<script>
+	import uniTransition from '../uni-transition/uni-transition.vue'
+
+	/**
+	 * PopUp 弹出层
+	 * @description 弹出层组件,为了解决遮罩弹层的问题
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=329
+	 * @property {String} type = [top|center|bottom] 弹出方式
+	 * 	@value top 顶部弹出
+	 * 	@value center 中间弹出
+	 * 	@value bottom 底部弹出
+	 * @property {Boolean} animation = [ture|false] 是否开启动画
+	 * @property {Boolean} maskClick = [ture|false] 蒙版点击是否关闭弹窗
+	 * @event {Function} change 打开关闭弹窗触发,e={show: false}
+	 */
+
+	export default {
+		name: 'UniPopup',
+		components: {
+			uniTransition
+		},
+		props: {
+			// 开启动画
+			animation: {
+				type: Boolean,
+				default: true
+			},
+			// 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
+			type: {
+				type: String,
+				default: 'center'
+			},
+			// maskClick
+			maskClick: {
+				type: Boolean,
+				default: true
+			}
+		},
+		data() {
+			return {
+				duration: 300,
+				ani: [],
+				showPopup: false,
+				showTrans: false,
+				maskClass: {
+					'position': 'fixed',
+					'bottom': 0,
+					'top': 0,
+					'left': 0,
+					'right': 0,
+					'backgroundColor': 'rgba(0, 0, 0, 0.4)'
+				},
+				transClass: {
+					'position': 'fixed',
+					'left': 0,
+					'right': 0,
+				}
+			}
+		},
+		watch: {
+			type: {
+				handler: function(newVal) {
+					switch (this.type) {
+						case 'top':
+							this.ani = ['slide-top']
+							this.transClass = {
+								'position': 'fixed',
+								'left': 0,
+								'right': 0,
+							}
+							break
+						case 'bottom':
+							this.ani = ['slide-bottom']
+							this.transClass = {
+								'position': 'fixed',
+								'left': 0,
+								'right': 0,
+								'bottom': 0
+							}
+							break
+						case 'center':
+							this.ani = ['zoom-out', 'fade']
+							this.transClass = {
+								'position': 'fixed',
+								/* #ifndef APP-NVUE */
+								'display': 'flex',
+								'flexDirection': 'column',
+								/* #endif */
+								'bottom': 0,
+								'left': 0,
+								'right': 0,
+								'top': 0,
+								'justifyContent': 'center',
+								'alignItems': 'center'
+							}
+
+							break
+					}
+				},
+				immediate: true
+			}
+		},
+		created() {
+			if (this.animation) {
+				this.duration = 300
+			} else {
+				this.duration = 0
+			}
+		},
+		methods: {
+			clear(e) {
+				// TODO nvue 取消冒泡
+				e.stopPropagation()
+			},
+			open() {
+				this.showPopup = true
+				this.$nextTick(() => {
+					clearTimeout(this.timer)
+					this.timer = setTimeout(() => {
+						this.showTrans = true
+					}, 50);
+				})
+				this.$emit('change', {
+					show: true
+				})
+			},
+			close(type) {
+				this.showTrans = false
+				this.$nextTick(() => {
+					clearTimeout(this.timer)
+					this.timer = setTimeout(() => {
+						this.$emit('change', {
+							show: false
+						})
+						this.showPopup = false
+					}, 300)
+				})
+			},
+			onTap() {
+				if (!this.maskClick) return
+				this.close()
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.uni-popup {
+		position: fixed;
+		/* #ifdef H5 */
+		top: var(--window-top);
+		/* #endif */
+		/* #ifndef H5 */
+		top: 0;
+		/* #endif */
+		bottom: 0;
+		left: 0;
+		right: 0;
+		/* #ifndef APP-NVUE */
+		z-index: 99;
+		/* #endif */
+	}
+
+	.uni-popup__mask {
+		position: absolute;
+		top: 0;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		background-color: $uni-bg-color-mask;
+		opacity: 0;
+	}
+
+	.mask-ani {
+		transition-property: opacity;
+		transition-duration: 0.2s;
+	}
+
+	.uni-top-mask {
+		opacity: 1;
+	}
+
+	.uni-bottom-mask {
+		opacity: 1;
+	}
+
+	.uni-center-mask {
+		opacity: 1;
+	}
+
+	.uni-popup__wrapper {
+		/* #ifndef APP-NVUE */
+		display: block;
+		/* #endif */
+		position: absolute;
+	}
+
+	.top {
+		top: 0;
+		left: 0;
+		right: 0;
+		transform: translateY(-500px);
+	}
+
+	.bottom {
+		bottom: 0;
+		left: 0;
+		right: 0;
+		transform: translateY(500px);
+	}
+
+	.center {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		flex-direction: column;
+		/* #endif */
+		bottom: 0;
+		left: 0;
+		right: 0;
+		top: 0;
+		justify-content: center;
+		align-items: center;
+		transform: scale(1.2);
+		opacity: 0;
+	}
+
+	.uni-popup__wrapper-box {
+		/* #ifndef APP-NVUE */
+		display: block;
+		/* #endif */
+		position: relative;
+	}
+
+	.content-ani {
+		// transition: transform 0.3s;
+		transition-property: transform, opacity;
+		transition-duration: 0.2s;
+	}
+
+
+	.uni-top-content {
+		transform: translateY(0);
+	}
+
+	.uni-bottom-content {
+		transform: translateY(0);
+	}
+
+	.uni-center-content {
+		transform: scale(1);
+		opacity: 1;
+	}
+</style>

+ 282 - 0
components/uni-popup/uni-popup-share.vue

@@ -0,0 +1,282 @@
+<template>
+	<view>
+		<uni-popup-ori ref="showshare" type="bottom">
+			<view class="uni-share">
+				<text class="uni-share-title">分享到</text>
+				<view class="uni-share-content">
+					<view v-for="(item, index) in bottomData" :key="index" class="uni-share-content-box" @click="shareTo(item.name)">
+						<view class="uni-share-content-image"><image :src="item.icon" class="content-image" mode="widthFix" /></view>
+						<text class="uni-share-content-text">{{ item.text }}</text>
+					</view>
+				</view>
+				<text class="uni-share-btn" @click="cancel()">取消分享</text>
+			</view>
+		</uni-popup-ori>
+		<uni-popup-ori ref="showPast" type="center" class="popupPast">
+			<view class="backPop">
+				<view class="popPast">
+					<view class="popTitle">口令已复制</view>
+					<view class="popContent">
+						<view>{{ describe }}</view>
+					</view>
+					<view class="popBtn" @click="goWhere(1)" v-if="popType == 'wx'">
+						<!-- <image src="../../static/spend/wxin.png" mode="widthFix"></image> -->
+						<view>去微信粘贴给好友</view>
+					</view>
+					<view class="popBtn" @click="goWhere(2)" v-if="popType == 'timeline'">
+						<!-- <image src="../../static/spend/wechat.png" mode="widthFix"></image> -->
+						<view>粘贴到朋友圈</view>
+					</view>
+				</view>
+				<icon type="cancel" size="26" color="white" style="margin-top: 40rpx;" @click="cancelPo" />
+			</view>
+		</uni-popup-ori>
+	</view>
+</template>
+
+<script>
+import uniPopupOri from '@/components/uni-popup/uni-popup-ori.vue';
+import { mapState, mapMutations } from 'vuex';
+import { getActionPage } from '@/utils/loginUtils.js';
+export default {
+	name: 'SharePopup',
+	components: {
+		uniPopupOri
+	},
+	props: ['opt', 'type','option'],
+	data() {
+		return {
+			describe: '',
+			shareoption: '',
+			bottomData: [
+				{
+					text: '微信',
+					icon: '../../static/spend/wxin.png',
+					name: 'wx'
+				},
+				{
+					text: '朋友圈',
+					icon: '../../static/spend/wechat.png',
+					name: 'timeline'
+				}
+			],
+			popType: '',
+			uid: ''
+		};
+	},
+	computed: {
+		...mapState(['userInfo', 'baseURL'])
+	},
+	mounted() {},
+	methods: {
+		loadData() {
+			try {
+				let prePage = getActionPage();
+				var path = prePage.route;
+				this.uid = this.userInfo.uid;
+				//获取object转化成
+				var parm = '';
+				var i = 0;
+				var option = this.option; //其他页面传值
+				console.log(option,'option')
+				if(this.type == 4){
+					parm = parm + '?' + 'promo_code=' + option;
+				}else{
+					for (let item in option) {
+						//拼接参数
+						if (i == 0) {
+							parm = '?' + item + '=' + option[item];
+						} else {
+							parm = parm + '&' + item + '=' + option[item];
+						}
+						i++;
+					}
+				}
+				if(this.type == 4){
+					var url = 'pages/index/index' + parm;
+				}else{
+					var url = path + parm;
+				}
+				console.log(path,'path')
+				console.log(parm,'parm')
+				//用后台加密
+				//第一个参数是判断是不是我们的链接
+				//第二个参数是访问地址
+				//第三个参数是,类型,type:0商品,type=1拼团,type=2邀请注册,type=3邀请好友参团,type=4邀请好友助力
+				//第四个参数是share的id
+				console.log(option,'option')
+				if(this.type == 4){
+					this.describe = this.baseURL + '@' + url + '@' + this.type + '@' + this.uid + '@复制这段话进入美美赚,自动打开页面';
+				}else{
+					this.describe = this.baseURL + '@' + url + '@' + this.type + '@' + this.uid + '@复制这段话进入美美赚,自动打开页面';
+				}
+				console.log(this.describe);
+				let obj = this;
+				// #ifndef H5
+				uni.setClipboardData({
+					data: this.describe,
+					  success: function () {
+					        uni.hideToast();
+					    }
+				});
+				// #endif
+			} catch (e) {
+				console.log(e);
+				//TODO handle the exception
+			}
+		},
+		goWhere(type) {
+			this.$api.msg('复制成功');
+		},
+		cancelPo() {
+			this.$nextTick(() => {
+				this.$refs['showPast'].close();
+			});
+		},
+		shareTo(name) {
+			this.popType = name;
+			this.$nextTick(() => {
+				this.$refs.showPast.open();
+				this.$refs['showshare'].close();
+			});
+		},
+		cancel() {
+			this.$nextTick(() => {
+				this.$refs['showshare'].close();
+			});
+		},
+		open() {
+			this.$nextTick(() => {
+				this.$refs['showshare'].open();
+			});
+		}
+	}
+};
+</script>
+<style lang="scss" scoped>
+.backPop {
+	padding: 0rpx 25rpx;
+	display: flex;
+	flex-direction: column;
+	justify-content: center;
+	align-items: center;
+}
+.popupPast {
+	display: flex;
+	flex-direction: column;
+	justify-content: center;
+	align-items: center;
+	text-align: center;
+}
+.popPast {
+	display: flex;
+	flex-direction: column;
+	justify-content: center;
+	padding: 40rpx 30rpx;
+	width: 90%;
+	background-color: white;
+	border-radius: 18rpx;
+	align-items: center;
+	.popTitle {
+		color: #2f2f2f;
+		font-size: 32rpx;
+		font-weight: bold;
+		margin-bottom: 40rpx;
+	}
+	.popContent {
+		background-color: #f4f4f4;
+		padding: 30rpx 24rpx;
+		border-radius: 16rpx;
+		view {
+			font-size: 24rpx;
+			color: #939393;
+		}
+		margin-bottom: 40rpx;
+	}
+	.popBtn {
+		display: flex;
+		align-items: center;
+		padding: 20rpx 40rpx;
+		background-color: #04be02;
+		border-radius: 60rpx;
+		image {
+			width: 36rpx;
+		}
+		view {
+			color: white;
+			font-size: 36rpx;
+			margin-left: 10rpx;
+		}
+	}
+}
+/* 底部分享 */
+.uni-share {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	flex-direction: column;
+	/* #endif */
+	background-color: #fff;
+}
+
+.uni-share-title {
+	line-height: 60rpx;
+	font-size: 24rpx;
+	padding: 15rpx 0;
+	text-align: center;
+}
+
+.uni-share-content {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: row;
+	flex-wrap: wrap;
+	justify-content: center;
+	padding: 15px;
+}
+
+.uni-share-content-box {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: column;
+	align-items: center;
+	width: 200rpx;
+}
+
+.uni-share-content-image {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: row;
+	justify-content: center;
+	align-items: center;
+	width: 60rpx;
+	height: 60rpx;
+	overflow: hidden;
+	border-radius: 10rpx;
+}
+
+.content-image {
+	width: 60rpx;
+	height: 60rpx;
+}
+
+.uni-share-content-text {
+	font-size: 26rpx;
+	color: #333;
+	padding-top: 5px;
+	padding-bottom: 10px;
+}
+
+.uni-share-btn {
+	height: 90rpx;
+	line-height: 90rpx;
+	font-size: 14px;
+	border-top-color: #f5f5f5;
+	border-top-width: 1px;
+	border-top-style: solid;
+	text-align: center;
+	color: #666;
+}
+</style>

+ 263 - 0
components/uni-popup/uni-popup.vue

@@ -0,0 +1,263 @@
+<template>
+	<view v-if="showPopup" class="uni-popup" @touchmove.stop.prevent="clear">
+		<uni-transition :mode-class="['fade']" :styles="maskClass" :duration="duration" :show="showTrans" @click="onTap" />
+		<uni-transition :mode-class="ani" :styles="transClass" :duration="duration" :show="showTrans" @click="onTap">
+			<view class="uni-popup__wrapper-box" @click.stop="clear">
+				<slot />
+			</view>
+		</uni-transition>
+	</view>
+</template>
+
+<script>
+	import uniTransition from '../uni-transition/uni-transition.vue'
+
+	/**
+	 * PopUp 弹出层
+	 * @description 弹出层组件,为了解决遮罩弹层的问题
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=329
+	 * @property {String} type = [top|center|bottom] 弹出方式
+	 * 	@value top 顶部弹出
+	 * 	@value center 中间弹出
+	 * 	@value bottom 底部弹出
+	 * @property {Boolean} animation = [ture|false] 是否开启动画
+	 * @property {Boolean} maskClick = [ture|false] 蒙版点击是否关闭弹窗
+	 * @event {Function} change 打开关闭弹窗触发,e={show: false}
+	 */
+
+	export default {
+		name: 'UniPopup',
+		components: {
+			uniTransition
+		},
+		props: {
+			// 开启动画
+			animation: {
+				type: Boolean,
+				default: true
+			},
+			// 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
+			type: {
+				type: String,
+				default: 'center'
+			},
+			// maskClick
+			maskClick: {
+				type: Boolean,
+				default: true
+			}
+		},
+		data() {
+			return {
+				duration: 300,
+				ani: [],
+				showPopup: false,
+				showTrans: false,
+				maskClass: {
+					'position': 'fixed',
+					'bottom': 0,
+					'top': 0,
+					'left': 0,
+					'right': 0,
+					'backgroundColor': 'rgba(0, 0, 0, 0.4)'
+				},
+				transClass: {
+					'position': 'fixed',
+					'left': 0,
+					'right': 0,
+				}
+			}
+		},
+		watch: {
+			type: {
+				handler: function(newVal) {
+					switch (this.type) {
+						case 'top':
+							this.ani = ['slide-top']
+							this.transClass = {
+								'position': 'fixed',
+								'left': 0,
+								'right': 0,
+							}
+							break
+						case 'bottom':
+							this.ani = ['slide-bottom']
+							this.transClass = {
+								'position': 'fixed',
+								'left': 0,
+								'right': 0,
+								'bottom': 0
+							}
+							break
+						case 'center':
+							this.ani = ['zoom-out', 'fade']
+							this.transClass = {
+								'position': 'fixed',
+								/* #ifndef APP-NVUE */
+								'display': 'flex',
+								'flexDirection': 'column',
+								/* #endif */
+								'bottom': 0,
+								'left': 0,
+								'right': 0,
+								'top': 0,
+								'justifyContent': 'center',
+								'alignItems': 'center'
+							}
+
+							break
+					}
+				},
+				immediate: true
+			}
+		},
+		created() {
+			if (this.animation) {
+				this.duration = 300
+			} else {
+				this.duration = 0
+			}
+		},
+		methods: {
+			clear(e) {
+				// TODO nvue 取消冒泡
+				e.stopPropagation()
+			},
+			open() {
+				this.showPopup = true
+				this.$nextTick(() => {
+					clearTimeout(this.timer)
+					this.timer = setTimeout(() => {
+						this.showTrans = true
+					}, 50);
+				})
+				this.$emit('change', {
+					show: true
+				})
+			},
+			close(type) {
+				this.showTrans = false
+				this.$nextTick(() => {
+					clearTimeout(this.timer)
+					this.timer = setTimeout(() => {
+						this.$emit('change', {
+							show: false
+						})
+						this.showPopup = false
+					}, 300)
+				})
+			},
+			onTap() {
+				if (!this.maskClick) return
+				this.close()
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.uni-popup {
+		position: fixed;
+		/* #ifdef H5 */
+		top: var(--window-top);
+		/* #endif */
+		/* #ifndef H5 */
+		top: 0;
+		/* #endif */
+		bottom: 0;
+		left: 0;
+		right: 0;
+		/* #ifndef APP-NVUE */
+		z-index: 99;
+		/* #endif */
+	}
+
+	.uni-popup__mask {
+		position: absolute;
+		top: 0;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		background-color: $uni-bg-color-mask;
+		opacity: 0;
+	}
+
+	.mask-ani {
+		transition-property: opacity;
+		transition-duration: 0.2s;
+	}
+
+	.uni-top-mask {
+		opacity: 1;
+	}
+
+	.uni-bottom-mask {
+		opacity: 1;
+	}
+
+	.uni-center-mask {
+		opacity: 1;
+	}
+
+	.uni-popup__wrapper {
+		/* #ifndef APP-NVUE */
+		display: block;
+		/* #endif */
+		position: absolute;
+	}
+
+	.top {
+		top: 0;
+		left: 0;
+		right: 0;
+		transform: translateY(-500px);
+	}
+
+	.bottom {
+		bottom: 0;
+		left: 0;
+		right: 0;
+		transform: translateY(500px);
+	}
+
+	.center {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		flex-direction: column;
+		/* #endif */
+		bottom: 0;
+		left: 0;
+		right: 0;
+		top: 0;
+		justify-content: center;
+		align-items: center;
+		transform: scale(1.2);
+		opacity: 0;
+	}
+
+	.uni-popup__wrapper-box {
+		/* #ifndef APP-NVUE */
+		display: block;
+		/* #endif */
+		position: relative;
+	}
+
+	.content-ani {
+		// transition: transform 0.3s;
+		transition-property: transform, opacity;
+		transition-duration: 0.2s;
+	}
+
+
+	.uni-top-content {
+		transform: translateY(0);
+	}
+
+	.uni-bottom-content {
+		transform: translateY(0);
+	}
+
+	.uni-center-content {
+		transform: scale(1);
+		opacity: 1;
+	}
+</style>

+ 141 - 0
components/uni-rate/uni-rate.vue

@@ -0,0 +1,141 @@
+<template>
+	<view class="uni-rate">
+		<view :key="index" :style="{ marginLeft: margin + 'px' }" @click="_onClick(index)" class="uni-rate__icon" v-for="(star, index) in stars">
+			<uni-icons :color="color" :size="size" :type="isFill ? 'star-filled' : 'star'" />
+			<!-- #ifdef APP-NVUE -->
+			<view :style="{ width: star.activeWitch.replace('%','')*size/100+'px'}" class="uni-rate__icon-on">
+				<uni-icons style="text-align: left;" :color="activeColor" :size="size" type="star-filled" />
+			</view>
+			<!-- #endif -->
+			<!-- #ifndef APP-NVUE -->
+			<view :style="{ width: star.activeWitch,top:-size/2+'px' }" class="uni-rate__icon-on">
+				<uni-icons :color="activeColor" :size="size" type="star-filled" />
+			</view>
+			<!-- #endif -->
+		</view>
+	</view>
+</template>
+
+<script>
+	import uniIcons from "../uni-icons/uni-icons.vue";
+	export default {
+		name: "UniRate",
+		components: {
+			uniIcons
+		},
+		props: {
+			isFill: {
+				// 星星的类型,是否镂空
+				type: [Boolean, String],
+				default: true
+			},
+			color: {
+				// 星星的颜色
+				type: String,
+				default: "#ececec"
+			},
+			activeColor: {
+				// 星星选中状态颜色
+				type: String,
+				default: "#ffca3e"
+			},
+			size: {
+				// 星星的大小
+				type: [Number, String],
+				default: 24
+			},
+			value: {
+				// 当前评分
+				type: [Number, String],
+				default: 0
+			},
+			max: {
+				// 最大评分
+				type: [Number, String],
+				default: 5
+			},
+			margin: {
+				// 星星的间距
+				type: [Number, String],
+				default: 0
+			},
+			disabled: {
+				// 是否可点击
+				type: [Boolean, String],
+				default: false
+			}
+		},
+		data() {
+			return {
+				valueSync: ""
+			};
+		},
+		computed: {
+			stars() {
+				const value = this.valueSync ? this.valueSync : 0;
+				const starList = [];
+				const floorValue = Math.floor(value);
+				const ceilValue = Math.ceil(value);
+				// console.log("ceilValue: " + ceilValue);
+				// console.log("floorValue: " + floorValue);
+				for (let i = 0; i < this.max; i++) {
+					if (floorValue > i) {
+						starList.push({
+							activeWitch: "100%"
+						});
+					} else if (ceilValue - 1 === i) {
+						starList.push({
+							activeWitch: (value - floorValue) * 100 + "%"
+						});
+					} else {
+						starList.push({
+							activeWitch: "0"
+						});
+					}
+				}
+				console.log("starList[4]: " + starList[4].activeWitch);
+				return starList;
+			}
+		},
+		created() {
+			this.valueSync = Number(this.value);
+		},
+		methods: {
+			_onClick(index) {
+				if (this.disabled) {
+					return;
+				}
+				this.valueSync = index + 1;
+				this.$emit("change", {
+					value: this.valueSync
+				});
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.uni-rate {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		line-height: 0;
+		font-size: 0;
+		flex-direction: row;
+	}
+
+	.uni-rate__icon {
+		position: relative;
+		line-height: 0;
+		font-size: 0;
+	}
+
+	.uni-rate__icon-on {
+		overflow: hidden;
+		position: absolute;
+		top: 0;
+		left: 0;
+		line-height: 1;
+		text-align: left;
+	}
+</style>

+ 244 - 0
components/uni-steps/uni-steps.vue

@@ -0,0 +1,244 @@
+<template>
+	<view class="uni-steps">
+		<view :class="[direction==='column'?'uni-steps__column':'uni-steps__row']">
+			<view :class="[direction==='column'?'uni-steps__column-text-container':'uni-steps__row-text-container']">
+				<view v-for="(item,index) in options" :key="index" :class="[direction==='column'?'uni-steps__column-text':'uni-steps__row-text']">
+					<text :style="{color:index<=active?activeColor:deactiveColor}" :class="[direction==='column'?'uni-steps__column-title':'uni-steps__row-title']">{{item.status}}</text>
+					<text :style="{color:index<=active?activeColor:deactiveColor}" :class="[direction==='column'?'uni-steps__column-desc':'uni-steps__row-desc']">{{item.context}}</text>
+					<text :style="{color:index<=active?activeColor:deactiveColor}" :class="[direction==='column'?'uni-steps__column-desc':'uni-steps__row-desc']">{{item.time}}</text>
+				</view>
+			</view>
+			<view :class="[direction==='column'?'uni-steps__column-container':'uni-steps__row-container']">
+				<view :class="[direction==='column'?'uni-steps__column-line-item':'uni-steps__row-line-item']" v-for="(item,index) in options"
+				 :key="index">
+					<view :class="[direction==='column'?'uni-steps__column-line':'uni-steps__row-line',direction==='column'?'uni-steps__column-line--before':'uni-steps__row-line--before']"
+					 :style="{backgroundColor:index<=active&&index!==0?activeColor:index===0?'transparent':deactiveColor}"></view>
+					<view :class="[direction==='column'?'uni-steps__column-check':'uni-steps__row-check']" v-if="index === active">
+						<uni-icons :color="activeColor" type="checkbox-filled" size="14"></uni-icons>
+					</view>
+					<view :class="[direction==='column'?'uni-steps__column-circle':'uni-steps__row-circle']" v-else :style="{backgroundColor:index<active?activeColor:deactiveColor}"></view>
+					<view :class="[direction==='column'?'uni-steps__column-line':'uni-steps__row-line',direction==='column'?'uni-steps__column-line--after':'uni-steps__row-line--after']"
+					 :style="{backgroundColor:index<active&&index!==options.length-1?activeColor:index===options.length-1?'transparent':deactiveColor}"></view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import uniIcons from '../uni-icons/uni-icons.vue'
+	export default {
+		name: 'UniSteps',
+		components: {
+			uniIcons
+		},
+		props: {
+			direction: {
+				// 排列方向 row column
+				type: String,
+				default: 'row'
+			},
+			activeColor: {
+				// 激活状态颜色
+				type: String,
+				default: '#1aad19'
+			},
+			deactiveColor: {
+				// 未激活状态颜色
+				type: String,
+				default: '#999999'
+			},
+			active: {
+				// 当前步骤
+				type: Number,
+				default: 0
+			},
+			options: {
+				type: Array,
+				default () {
+					return []
+				}
+			} // 数据
+		},
+		data() {
+			return {}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.uni-steps {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		width: 100%;
+		/* #endif */
+		/* #ifdef APP-NVUE */
+		flex: 1;
+		/* #endif */
+		flex-direction: column;
+	}
+
+	.uni-steps__row {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+	}
+
+	.uni-steps__column {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row-reverse;
+	}
+
+	.uni-steps__row-text-container {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+	}
+
+	.uni-steps__column-text-container {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		flex: 1;
+	}
+
+	.uni-steps__row-text {
+		/* #ifndef APP-NVUE */
+		display: inline-flex;
+		/* #endif */
+		flex: 1;
+		flex-direction: column;
+	}
+
+	.uni-steps__column-text {
+		padding: 6px 0px;
+		border-bottom-style: solid;
+		border-bottom-width: 1px;
+		border-bottom-color: $uni-border-color;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+	}
+
+	.uni-steps__row-title {
+		font-size: $uni-font-size-base;
+		line-height: 16px;
+		text-align: center;
+	}
+
+	.uni-steps__column-title {
+		font-size: $uni-font-size-base;
+		text-align: left;
+		line-height: 18px;
+	}
+
+	.uni-steps__row-desc {
+		font-size: 12px;
+		line-height: 14px;
+		text-align: center;
+	}
+
+	.uni-steps__column-desc {
+		font-size: $uni-font-size-sm;
+		text-align: left;
+		line-height: 18px;
+	}
+
+	.uni-steps__row-container {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+	}
+
+	.uni-steps__column-container {
+		/* #ifndef APP-NVUE */
+		display: inline-flex;
+		/* #endif */
+		width: 30px;
+		flex-direction: column;
+	}
+
+	.uni-steps__row-line-item {
+		/* #ifndef APP-NVUE */
+		display: inline-flex;
+		/* #endif */
+		flex-direction: row;
+		flex: 1;
+		height: 14px;
+		line-height: 14px;
+		align-items: center;
+		justify-content: center;
+	}
+
+	.uni-steps__column-line-item {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		flex: 1;
+		align-items: center;
+		justify-content: center;
+	}
+
+	.uni-steps__row-line {
+		flex: 1;
+		height: 1px;
+		background-color: $uni-text-color-grey;
+	}
+
+	.uni-steps__column-line {
+		width: 1px;
+		background-color: $uni-text-color-grey;
+	}
+
+	.uni-steps__row-line--after {
+		transform: translateX(1px);
+	}
+
+	.uni-steps__column-line--after {
+		flex: 1;
+		transform: translate(0px, 1px);
+	}
+
+	.uni-steps__row-line--before {
+		transform: translateX(-1px);
+	}
+
+	.uni-steps__column-line--before {
+		height: 6px;
+		transform: translate(0px, -1px);
+	}
+
+	.uni-steps__row-circle {
+		width: 5px;
+		height: 5px;
+		border-radius: 100px;
+		background-color: $uni-text-color-grey;
+		margin: 0px 3px;
+	}
+
+	.uni-steps__column-circle {
+		width: 5px;
+		height: 5px;
+		border-radius: 100px;
+		background-color: $uni-text-color-grey;
+		margin: 4px 0px 5px 0px;
+	}
+
+	.uni-steps__row-check {
+		margin: 0px 6px;
+	}
+
+	.uni-steps__column-check {
+		height: 14px;
+		line-height: 14px;
+		margin: 2px 0px;
+	}
+</style>

+ 279 - 0
components/uni-transition/uni-transition.vue

@@ -0,0 +1,279 @@
+<template>
+	<view v-if="isShow" ref="ani" class="uni-transition" :class="[ani.in]" :style="'transform:' +transform+';'+stylesObject"
+	 @click="change">
+		 <slot></slot>
+	</view>
+</template>
+
+<script>
+	// #ifdef APP-NVUE
+	const animation = uni.requireNativePlugin('animation');
+	// #endif
+	/**
+	 * Transition 过渡动画
+	 * @description 简单过渡动画组件
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=985
+	 * @property {Boolean} show = [false|true] 控制组件显示或隐藏
+     * @property {Array} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
+     *  @value fade 渐隐渐出过渡
+     *  @value slide-top 由上至下过渡
+     *  @value slide-right 由右至左过渡
+     *  @value slide-bottom 由下至上过渡
+     *  @value slide-left 由左至右过渡
+     *  @value zoom-in 由小到大过渡
+     *  @value zoom-out 由大到小过渡
+	 * @property {Number} duration 过渡动画持续时间
+	 * @property {Object} styles 组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red`
+	 */
+	export default {
+		name: 'uniTransition',
+		props: {
+			show: {
+				type: Boolean,
+				default: false
+			},
+			modeClass: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			duration: {
+				type: Number,
+				default: 300
+			},
+			styles: {
+				type: Object,
+				default () {
+					return {}
+				}
+			}
+		},
+		data() {
+			return {
+				isShow: false,
+				transform: '',
+				ani: { in: '',
+					active: ''
+				}
+			};
+		},
+		watch: {
+			show: {
+				handler(newVal) {
+					if (newVal) {
+						this.open()
+					} else {
+						this.close()
+					}
+				},
+				immediate: true
+			}
+		},
+		computed: {
+			stylesObject() {
+				let styles = {
+					...this.styles,
+					'transition-duration': this.duration / 1000 + 's'
+				}
+				let transfrom = ''
+				for (let i in styles) {
+					let line = this.toLine(i)
+					transfrom += line + ':' + styles[i] + ';'
+				}
+				return transfrom
+			}
+		},
+		created() {
+			// this.timer = null
+			// this.nextTick = (time = 50) => new Promise(resolve => {
+			// 	clearTimeout(this.timer)
+			// 	this.timer = setTimeout(resolve, time)
+			// 	return this.timer
+			// });
+		},
+		methods: {
+			change() {
+				this.$emit('click', {
+					detail: this.isShow
+				})
+			},
+			open() {
+				clearTimeout(this.timer)
+				this.isShow = true
+				this.transform = ''
+				this.ani.in = ''
+				for (let i in this.getTranfrom(false)) {
+					if (i === 'opacity') {
+						this.ani.in = 'fade-in'
+					} else {
+						this.transform += `${this.getTranfrom(false)[i]} `
+					}
+				}
+				this.$nextTick(() => {
+					setTimeout(() => {
+						this._animation(true)
+					}, 50)
+				})
+
+			},
+			close(type) {
+				clearTimeout(this.timer)
+				this._animation(false)
+			},
+			_animation(type) {
+				let styles = this.getTranfrom(type)
+				// #ifdef APP-NVUE
+				if(!this.$refs['ani']) return
+				animation.transition(this.$refs['ani'].ref, {
+					styles,
+					duration: this.duration, //ms
+					timingFunction: 'ease',
+					needLayout: false,
+					delay: 0 //ms
+				}, () => {
+					if (!type) {
+						this.isShow = false
+					}
+					this.$emit('change', {
+						detail: this.isShow
+					})
+				})
+				// #endif
+				// #ifndef APP-NVUE
+				this.transform = ''
+				for (let i in styles) {
+					if (i === 'opacity') {
+						this.ani.in = `fade-${type?'out':'in'}`
+					} else {
+						this.transform += `${styles[i]} `
+					}
+				}
+				this.timer = setTimeout(() => {
+					if (!type) {
+						this.isShow = false
+					}
+					this.$emit('change', {
+						detail: this.isShow
+					})
+
+				}, this.duration)
+				// #endif
+
+			},
+			getTranfrom(type) {
+				let styles = {
+					transform: ''
+				}
+				this.modeClass.forEach((mode) => {
+					switch (mode) {
+						case 'fade':
+							styles.opacity = type ? 1 : 0
+							break;
+						case 'slide-top':
+							styles.transform += `translateY(${type?'0':'-100%'}) `
+							break;
+						case 'slide-right':
+							styles.transform += `translateX(${type?'0':'100%'}) `
+							break;
+						case 'slide-bottom':
+							styles.transform += `translateY(${type?'0':'100%'}) `
+							break;
+						case 'slide-left':
+							styles.transform += `translateX(${type?'0':'-100%'}) `
+							break;
+						case 'zoom-in':
+							styles.transform += `scale(${type?1:0.8}) `
+							break;
+						case 'zoom-out':
+							styles.transform += `scale(${type?1:1.2}) `
+							break;
+					}
+				})
+				return styles
+			},
+			_modeClassArr(type) {
+				let mode = this.modeClass
+				if (typeof(mode) !== "string") {
+					let modestr = ''
+					mode.forEach((item) => {
+						modestr += (item + '-' + type + ',')
+					})
+					return modestr.substr(0, modestr.length - 1)
+				} else {
+					return mode + '-' + type
+				}
+			},
+			// getEl(el) {
+			// 	console.log(el || el.ref || null);
+			// 	return el || el.ref || null
+			// },
+			toLine(name) {
+				return name.replace(/([A-Z])/g, "-$1").toLowerCase();
+			}
+		}
+	}
+</script>
+
+<style>
+	.uni-transition {
+		transition-timing-function: ease;
+		transition-duration: 0.3s;
+		transition-property: transform, opacity;
+	}
+
+	.fade-in {
+		opacity: 0;
+	}
+
+	.fade-active {
+		opacity: 1;
+	}
+
+	.slide-top-in {
+		/* transition-property: transform, opacity; */
+		transform: translateY(-100%);
+	}
+
+	.slide-top-active {
+		transform: translateY(0);
+		/* opacity: 1; */
+	}
+
+	.slide-right-in {
+		transform: translateX(100%);
+	}
+
+	.slide-right-active {
+		transform: translateX(0);
+	}
+
+	.slide-bottom-in {
+		transform: translateY(100%);
+	}
+
+	.slide-bottom-active {
+		transform: translateY(0);
+	}
+
+	.slide-left-in {
+		transform: translateX(-100%);
+	}
+
+	.slide-left-active {
+		transform: translateX(0);
+		opacity: 1;
+	}
+
+	.zoom-in-in {
+		transform: scale(0.8);
+	}
+
+	.zoom-out-active {
+		transform: scale(1);
+	}
+
+	.zoom-out-in {
+		transform: scale(1.2);
+	}
+</style>

+ 226 - 0
components/upload-images.vue

@@ -0,0 +1,226 @@
+<template>
+	<view class="upload-content">
+		<block v-for="(item, index) in imageList" :key="index">
+			<view class="upload-item">
+				<image class="upload-img" :src="item.filePath" mode="aspectFill" @click="previewImage(index)"></image>
+				<image class="upload-del-btn" 
+					@click="delImage(index)" 
+					src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAmCAYAAACoPemuAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RjNBODgzQjUwNDM5MTFFOUJDMjlGN0UwRTJGMjVCNjQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RjNBODgzQjYwNDM5MTFFOUJDMjlGN0UwRTJGMjVCNjQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGM0E4ODNCMzA0MzkxMUU5QkMyOUY3RTBFMkYyNUI2NCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGM0E4ODNCNDA0MzkxMUU5QkMyOUY3RTBFMkYyNUI2NCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PuYQTIAAAAHNSURBVHjazJgxasMwFIbjnsD4BDmCLxDQDZrNQ5bcILlBvXvpDRLIWmiHFkyH2t0LyVbwEkPpbE/eYlUCGYQax096UuIffkIgij7ryU/vyZuYy2e+Z56K70R8lsJcOfPn5AriMCvmZ2YKdMW8EQ/hRA9iEorwXlpZtObMRySQ6kysvrFWloHUEIcmUBuHUDLccmxQskErt7wyVLdy00tQ4Q2g5De2V9kNwWjffiNDA33f/42i6E13Qj5uNptlwJD+SyODuaqqqi/KVBTFOxSKA7VtW/NxSZI8AcbE2nuraZpvKgSBk6G4drsdBOwogz1CwAghH/JEl+BUKJ1VltPHHjoIAoeE4l53YFqb+RKcBSgqyqXhtxEKZwkKB3YOThYCqjPuCDoHZwGK3mGLtdPp5LmqTI1Dqe4p26EkNqA4iE6eg2z+iQ0okyQMATvYgLIIt9Y6knTyFBIudHqIq3BpmkL60VJ9O0sXZY8MBwSLtdNGEAQ/i8XiVXcz83EcEPDbuq/fzMdYWt+6GTmMsX2rh9q3TtsxNrzXhKt1rwg6rR1Dhdjqo7QMlWOvoWTF4imxQMRF7eaL8L5ohmwrLgDB8pCQc8DlcG7y538CDABJNGPqfaJgfgAAAABJRU5ErkJggg==" 
+					mode="scaleToFill">
+				</image>
+				<view class="upload-progress" v-if="item.progress < 100">{{item.progress}}%</view>
+			</view>
+		</block>
+		<view class="upload-add-btn" v-if="rduLength > 0" @click="chooseImage"></view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			imageList: []
+		};
+	},
+	props: {
+		url: {
+			type: String,
+			value: '' //上传接口地址
+		},
+		count: {
+			type: Number,
+			value: 4 //单次可选择的图片数量
+		},
+		length: {
+			type: Number,
+			value: 50 //可上传总数量
+		}
+	},
+	computed: {
+		rduLength(){
+			return this.length - this.imageList.length;
+		}
+	},
+	methods: {
+		//选择图片
+		chooseImage: function(){
+			uni.chooseImage({
+				count: this.rduLength < this.count ? this.rduLength : this.count, //最多可以选择的图片张数,默认9
+				sizeType: ['original', 'compressed'], //original 原图,compressed 压缩图,默认二者都有
+				sourceType: ['album'], //album 从相册选图,camera 使用相机,默认二者都有
+				success: (res)=> {
+					const images = res.tempFilePaths;
+					this.uploadFiles(images);
+				}
+			});
+		},
+		//上传图片
+		async uploadFiles(images){
+			this.imageList.push({
+				filePath: images[0],
+				progress: 0
+			});
+			uni.showLoading({
+				title: '请稍后..',
+				mask: true,
+			})
+			try{
+				const uploadUrl = await this.uploadImage(images[0]);
+			}catch(err){
+				console.log(err);
+				return;
+			}
+			
+			if(uploadUrl !== false){
+				images.splice(0, 1);
+				this.imageList[this.imageList.length - 1].src = uploadUrl;
+
+				//判断是否需要继续上传
+				if(images.length > 0 && this.rduLength > 0){
+					this.uploadFiles(images);
+				}else{
+					uni.hideLoading();
+				}
+			}else{
+				//上传失败处理
+				this.imageList.pop();
+				uni.hideLoading();
+				uni.showToast({
+					title: '上传中出现问题,已终止上传',
+					icon: 'none',
+					mask: true,
+					duration: 2000
+				});
+			}
+		},
+		uploadImage: function(file){
+			return new Promise((resolve, reject)=> {
+				//发送给后端的附加参数
+				const formData = {
+					thumb_mode: 1,  
+				};
+				this.uploadTask = uni.uploadFile({
+					url: this.url, 
+					filePath: file,
+					name: 'file',
+					formData: formData,
+					success(uploadFileResult){
+						const uploadFileRes = JSON.parse(uploadFileResult.data) || {};
+						if(uploadFileRes.status === 1 && uploadFileRes.data){
+							resolve(uploadFileRes.data);
+						}else{
+							reject('接口返回错误');
+						}
+					}, 
+					fail(){
+						reject('网络链接错误');
+					}
+				});
+				//上传进度
+				this.uploadTask.onProgressUpdate((progressRes)=> {
+					this.imageList[this.imageList.length - 1].progress = progressRes.progress;
+				});
+			});
+		},
+		//删除图片
+		delImage: function(index){
+			uni.showModal({
+				content: '确定要放弃这张图片么?',
+				success: (confirmRes)=> {
+					if (confirmRes.confirm) {
+						this.imageList.splice(index, 1);
+					} 
+				}
+			});
+		},
+		//预览图片
+		previewImage: function(index){
+			const urls = [];
+			this.imageList.forEach((item)=> {
+				urls.push(item.filePath);
+			})
+			uni.previewImage({
+				current: urls[index],
+				urls: urls,
+				indicator: "number"
+			})
+		}
+	}
+}
+</script>
+
+<style lang="scss">
+	.upload-content{
+		padding:24upx 0 0 28upx;
+		background-color: #fff;
+		overflow:hidden;
+	}
+	.upload-item{
+		position: relative;
+		float:left;
+		width:150upx;
+		height:150upx;
+		margin-right:30upx;
+		margin-bottom:30upx;
+		&:nth-child(4n){
+			margin-right:0;
+		}
+		.upload-img{
+			width:100%;
+			height:100%;
+			border-radius:8upx;
+		}
+		.upload-del-btn{
+			position: absolute;
+			right:-16upx;
+			top:-14upx;
+			width:36upx;
+			height:36upx;
+			border: 4upx solid #fff;
+			border-radius: 100px;
+		}
+		.upload-progress{
+			position: absolute;
+			left:0;
+			top:0;
+			display:flex;
+			align-items:center;
+			justify-content: center;
+			width:100%;
+			height:100%;
+			background-color: rgba(0,0,0,.4);
+			color:#fff;
+			font-size:24upx;
+			border-radius:8upx;
+		}
+	}
+	.upload-add-btn {
+		position: relative;
+		float:left;
+		width: 150upx;
+		height: 150upx;
+		z-index: 99;
+		border-radius:8upx;
+		background:#f9f9f9;
+		&:before,
+		&:after {
+			content: " ";
+			position: absolute;
+			top: 50%;
+			left: 50%;
+			-webkit-transform: translate(-50%, -50%);
+			transform: translate(-50%, -50%);
+			width: 4upx;
+			height: 60upx;
+			background-color: #d6d6d6;
+		}
+		&:after {
+			width: 60upx;
+			height: 4upx;
+		}
+		&:active {
+			background-color: #f7f7f7;
+		}
+	}
+
+</style>

+ 1356 - 0
components/wangding-pickerAddress/data.js

@@ -0,0 +1,1356 @@
+export default [{
+	"name": "北京市",
+	"city": [{
+		"name": "市辖区",
+		"area": ["东城区", "西城区", "朝阳区", "丰台区", "石景山区", "海淀区", "门头沟区", "房山区", "通州区", "顺义区", "昌平区", "大兴区",
+			"怀柔区", "平谷区", "密云区", "延庆区"
+		]
+	}]
+}, {
+	"name": "天津市",
+	"city": [{
+		"name": "市辖区",
+		"area": ["和平区", "河东区", "河西区", "南开区", "河北区", "红桥区", "东丽区", "西青区", "津南区", "北辰区", "武清区", "宝坻区", "滨海新区",
+			"宁河区", "静海区", "蓟州区"
+		]
+	}]
+}, {
+	"name": "河北省",
+	"city": [{
+		"name": "石家庄市",
+		"area": ["市辖区", "长安区", "桥西区", "新华区", "井陉矿区", "裕华区", "藁城区", "鹿泉区", "栾城区", "井陉县", "正定县", "行唐县", "灵寿县",
+			"高邑县", "深泽县", "赞皇县", "无极县", "平山县", "元氏县", "赵县", "晋州市", "新乐市"
+		]
+	}, {
+		"name": "唐山市",
+		"area": ["市辖区", "路南区", "路北区", "古冶区", "开平区", "丰南区", "丰润区", "曹妃甸区", "滦县", "滦南县", "乐亭县", "迁西县", "玉田县",
+			"遵化市", "迁安市"
+		]
+	}, {
+		"name": "秦皇岛市",
+		"area": ["市辖区", "海港区", "山海关区", "北戴河区", "抚宁区", "青龙满族自治县", "昌黎县", "卢龙县"]
+	}, {
+		"name": "邯郸市",
+		"area": ["市辖区", "邯山区", "丛台区", "复兴区", "峰峰矿区", "邯郸县", "临漳县", "成安县", "大名县", "涉县", "磁县", "肥乡县", "永年县",
+			"邱县", "鸡泽县", "广平县", "馆陶县", "魏县", "曲周县", "武安市"
+		]
+	}, {
+		"name": "邢台市",
+		"area": ["市辖区", "桥东区", "桥西区", "邢台县", "临城县", "内丘县", "柏乡县", "隆尧县", "任县", "南和县", "宁晋县", "巨鹿县", "新河县",
+			"广宗县", "平乡县", "威县", "清河县", "临西县", "南宫市", "沙河市"
+		]
+	}, {
+		"name": "保定市",
+		"area": ["市辖区", "竞秀区", "莲池区", "满城区", "清苑区", "徐水区", "涞水县", "阜平县", "定兴县", "唐县", "高阳县", "容城县", "涞源县",
+			"望都县", "安新县", "易县", "曲阳县", "蠡县", "顺平县", "博野县", "雄县", "涿州市", "安国市", "高碑店市"
+		]
+	}, {
+		"name": "张家口市",
+		"area": ["市辖区", "桥东区", "桥西区", "宣化区", "下花园区", "万全区", "崇礼区", "张北县", "康保县", "沽源县", "尚义县", "蔚县", "阳原县",
+			"怀安县", "怀来县", "涿鹿县", "赤城县"
+		]
+	}, {
+		"name": "承德市",
+		"area": ["市辖区", "双桥区", "双滦区", "鹰手营子矿区", "承德县", "兴隆县", "平泉县", "滦平县", "隆化县", "丰宁满族自治县", "宽城满族自治县",
+			"围场满族蒙古族自治县"
+		]
+	}, {
+		"name": "沧州市",
+		"area": ["市辖区", "新华区", "运河区", "沧县", "青县", "东光县", "海兴县", "盐山县", "肃宁县", "南皮县", "吴桥县", "献县", "孟村回族自治县",
+			"泊头市", "任丘市", "黄骅市", "河间市"
+		]
+	}, {
+		"name": "廊坊市",
+		"area": ["市辖区", "安次区", "广阳区", "固安县", "永清县", "香河县", "大城县", "文安县", "大厂回族自治县", "霸州市", "三河市"]
+	}, {
+		"name": "衡水市",
+		"area": ["市辖区", "桃城区", "冀州区", "枣强县", "武邑县", "武强县", "饶阳县", "安平县", "故城县", "景县", "阜城县", "深州市"]
+	}, {
+		"name": "直辖县",
+		"area": ["定州市", "辛集市"]
+	}]
+}, {
+	"name": "山西省",
+	"city": [{
+		"name": "太原市",
+		"area": ["市辖区", "小店区", "迎泽区", "杏花岭区", "尖草坪区", "万柏林区", "晋源区", "清徐县", "阳曲县", "娄烦县", "古交市"]
+	}, {
+		"name": "大同市",
+		"area": ["市辖区", "城区", "矿区", "南郊区", "新荣区", "阳高县", "天镇县", "广灵县", "灵丘县", "浑源县", "左云县", "大同县"]
+	}, {
+		"name": "阳泉市",
+		"area": ["市辖区", "城区", "矿区", "郊区", "平定县", "盂县"]
+	}, {
+		"name": "长治市",
+		"area": ["市辖区", "城区", "郊区", "长治县", "襄垣县", "屯留县", "平顺县", "黎城县", "壶关县", "长子县", "武乡县", "沁县", "沁源县",
+			"潞城市"
+		]
+	}, {
+		"name": "晋城市",
+		"area": ["市辖区", "城区", "沁水县", "阳城县", "陵川县", "泽州县", "高平市"]
+	}, {
+		"name": "朔州市",
+		"area": ["市辖区", "朔城区", "平鲁区", "山阴县", "应县", "右玉县", "怀仁县"]
+	}, {
+		"name": "晋中市",
+		"area": ["市辖区", "榆次区", "榆社县", "左权县", "和顺县", "昔阳县", "寿阳县", "太谷县", "祁县", "平遥县", "灵石县", "介休市"]
+	}, {
+		"name": "运城市",
+		"area": ["市辖区", "盐湖区", "临猗县", "万荣县", "闻喜县", "稷山县", "新绛县", "绛县", "垣曲县", "夏县", "平陆县", "芮城县", "永济市",
+			"河津市"
+		]
+	}, {
+		"name": "忻州市",
+		"area": ["市辖区", "忻府区", "定襄县", "五台县", "代县", "繁峙县", "宁武县", "静乐县", "神池县", "五寨县", "岢岚县", "河曲县", "保德县",
+			"偏关县", "原平市"
+		]
+	}, {
+		"name": "临汾市",
+		"area": ["市辖区", "尧都区", "曲沃县", "翼城县", "襄汾县", "洪洞县", "古县", "安泽县", "浮山县", "吉县", "乡宁县", "大宁县", "隰县",
+			"永和县", "蒲县", "汾西县", "侯马市", "霍州市"
+		]
+	}, {
+		"name": "吕梁市",
+		"area": ["市辖区", "离石区", "文水县", "交城县", "兴县", "临县", "柳林县", "石楼县", "岚县", "方山县", "中阳县", "交口县", "孝义市",
+			"汾阳市"
+		]
+	}]
+}, {
+	"name": "内蒙古自治区",
+	"city": [{
+		"name": "呼和浩特市",
+		"area": ["市辖区", "新城区", "回民区", "玉泉区", "赛罕区", "土默特左旗", "托克托县", "和林格尔县", "清水河县", "武川县"]
+	}, {
+		"name": "包头市",
+		"area": ["市辖区", "东河区", "昆都仑区", "青山区", "石拐区", "白云鄂博矿区", "九原区", "土默特右旗", "固阳县", "达尔罕茂明安联合旗"]
+	}, {
+		"name": "乌海市",
+		"area": ["市辖区", "海勃湾区", "海南区", "乌达区"]
+	}, {
+		"name": "赤峰市",
+		"area": ["市辖区", "红山区", "元宝山区", "松山区", "阿鲁科尔沁旗", "巴林左旗", "巴林右旗", "林西县", "克什克腾旗", "翁牛特旗", "喀喇沁旗",
+			"宁城县", "敖汉旗"
+		]
+	}, {
+		"name": "通辽市",
+		"area": ["市辖区", "科尔沁区", "科尔沁左翼中旗", "科尔沁左翼后旗", "开鲁县", "库伦旗", "奈曼旗", "扎鲁特旗", "霍林郭勒市"]
+	}, {
+		"name": "鄂尔多斯市",
+		"area": ["市辖区", "东胜区", "康巴什区", "达拉特旗", "准格尔旗", "鄂托克前旗", "鄂托克旗", "杭锦旗", "乌审旗", "伊金霍洛旗"]
+	}, {
+		"name": "呼伦贝尔市",
+		"area": ["市辖区", "海拉尔区", "扎赉诺尔区", "阿荣旗", "莫力达瓦达斡尔族自治旗", "鄂伦春自治旗", "鄂温克族自治旗", "陈巴尔虎旗", "新巴尔虎左旗",
+			"新巴尔虎右旗", "满洲里市", "牙克石市", "扎兰屯市", "额尔古纳市", "根河市"
+		]
+	}, {
+		"name": "巴彦淖尔市",
+		"area": ["市辖区", "临河区", "五原县", "磴口县", "乌拉特前旗", "乌拉特中旗", "乌拉特后旗", "杭锦后旗"]
+	}, {
+		"name": "乌兰察布市",
+		"area": ["市辖区", "集宁区", "卓资县", "化德县", "商都县", "兴和县", "凉城县", "察哈尔右翼前旗", "察哈尔右翼中旗", "察哈尔右翼后旗", "四子王旗",
+			"丰镇市"
+		]
+	}, {
+		"name": "兴安盟",
+		"area": ["乌兰浩特市", "阿尔山市", "科尔沁右翼前旗", "科尔沁右翼中旗", "扎赉特旗", "突泉县"]
+	}, {
+		"name": "锡林郭勒盟",
+		"area": ["二连浩特市", "锡林浩特市", "阿巴嘎旗", "苏尼特左旗", "苏尼特右旗", "东乌珠穆沁旗", "西乌珠穆沁旗", "太仆寺旗", "镶黄旗", "正镶白旗",
+			"正蓝旗", "多伦县"
+		]
+	}, {
+		"name": "阿拉善盟",
+		"area": ["阿拉善左旗", "阿拉善右旗", "额济纳旗"]
+	}]
+}, {
+	"name": "辽宁省",
+	"city": [{
+		"name": "沈阳市",
+		"area": ["市辖区", "和平区", "沈河区", "大东区", "皇姑区", "铁西区", "苏家屯区", "浑南区", "沈北新区", "于洪区", "辽中区", "康平县",
+			"法库县", "新民市"
+		]
+	}, {
+		"name": "大连市",
+		"area": ["市辖区", "中山区", "西岗区", "沙河口区", "甘井子区", "旅顺口区", "金州区", "普兰店区", "长海县", "瓦房店市", "庄河市"]
+	}, {
+		"name": "鞍山市",
+		"area": ["市辖区", "铁东区", "铁西区", "立山区", "千山区", "台安县", "岫岩满族自治县", "海城市"]
+	}, {
+		"name": "抚顺市",
+		"area": ["市辖区", "新抚区", "东洲区", "望花区", "顺城区", "抚顺县", "新宾满族自治县", "清原满族自治县"]
+	}, {
+		"name": "本溪市",
+		"area": ["市辖区", "平山区", "溪湖区", "明山区", "南芬区", "本溪满族自治县", "桓仁满族自治县"]
+	}, {
+		"name": "丹东市",
+		"area": ["市辖区", "元宝区", "振兴区", "振安区", "宽甸满族自治县", "东港市", "凤城市"]
+	}, {
+		"name": "锦州市",
+		"area": ["市辖区", "古塔区", "凌河区", "太和区", "黑山县", "义县", "凌海市", "北镇市"]
+	}, {
+		"name": "营口市",
+		"area": ["市辖区", "站前区", "西市区", "鲅鱼圈区", "老边区", "盖州市", "大石桥市"]
+	}, {
+		"name": "阜新市",
+		"area": ["市辖区", "海州区", "新邱区", "太平区", "清河门区", "细河区", "阜新蒙古族自治县", "彰武县"]
+	}, {
+		"name": "辽阳市",
+		"area": ["市辖区", "白塔区", "文圣区", "宏伟区", "弓长岭区", "太子河区", "辽阳县", "灯塔市"]
+	}, {
+		"name": "盘锦市",
+		"area": ["市辖区", "双台子区", "兴隆台区", "大洼区", "盘山县"]
+	}, {
+		"name": "铁岭市",
+		"area": ["市辖区", "银州区", "清河区", "铁岭县", "西丰县", "昌图县", "调兵山市", "开原市"]
+	}, {
+		"name": "朝阳市",
+		"area": ["市辖区", "双塔区", "龙城区", "朝阳县", "建平县", "喀喇沁左翼蒙古族自治县", "北票市", "凌源市"]
+	}, {
+		"name": "葫芦岛市",
+		"area": ["市辖区", "连山区", "龙港区", "南票区", "绥中县", "建昌县", "兴城市"]
+	}]
+}, {
+	"name": "吉林省",
+	"city": [{
+		"name": "长春市",
+		"area": ["市辖区", "南关区", "宽城区", "朝阳区", "二道区", "绿园区", "双阳区", "九台区", "农安县", "榆树市", "德惠市"]
+	}, {
+		"name": "吉林市",
+		"area": ["市辖区", "昌邑区", "龙潭区", "船营区", "丰满区", "永吉县", "蛟河市", "桦甸市", "舒兰市", "磐石市"]
+	}, {
+		"name": "四平市",
+		"area": ["市辖区", "铁西区", "铁东区", "梨树县", "伊通满族自治县", "公主岭市", "双辽市"]
+	}, {
+		"name": "辽源市",
+		"area": ["市辖区", "龙山区", "西安区", "东丰县", "东辽县"]
+	}, {
+		"name": "通化市",
+		"area": ["市辖区", "东昌区", "二道江区", "通化县", "辉南县", "柳河县", "梅河口市", "集安市"]
+	}, {
+		"name": "白山市",
+		"area": ["市辖区", "浑江区", "江源区", "抚松县", "靖宇县", "长白朝鲜族自治县", "临江市"]
+	}, {
+		"name": "松原市",
+		"area": ["市辖区", "宁江区", "前郭尔罗斯蒙古族自治县", "长岭县", "乾安县", "扶余市"]
+	}, {
+		"name": "白城市",
+		"area": ["市辖区", "洮北区", "镇赉县", "通榆县", "洮南市", "大安市"]
+	}, {
+		"name": "延边朝鲜族自治州",
+		"area": ["延吉市", "图们市", "敦化市", "珲春市", "龙井市", "和龙市", "汪清县", "安图县"]
+	}]
+}, {
+	"name": "黑龙江省",
+	"city": [{
+		"name": "哈尔滨市",
+		"area": ["市辖区", "道里区", "南岗区", "道外区", "平房区", "松北区", "香坊区", "呼兰区", "阿城区", "双城区", "依兰县", "方正县", "宾县",
+			"巴彦县", "木兰县", "通河县", "延寿县", "尚志市", "五常市"
+		]
+	}, {
+		"name": "齐齐哈尔市",
+		"area": ["市辖区", "龙沙区", "建华区", "铁锋区", "昂昂溪区", "富拉尔基区", "碾子山区", "梅里斯达斡尔族区", "龙江县", "依安县", "泰来县",
+			"甘南县", "富裕县", "克山县", "克东县", "拜泉县", "讷河市"
+		]
+	}, {
+		"name": "鸡西市",
+		"area": ["市辖区", "鸡冠区", "恒山区", "滴道区", "梨树区", "城子河区", "麻山区", "鸡东县", "虎林市", "密山市"]
+	}, {
+		"name": "鹤岗市",
+		"area": ["市辖区", "向阳区", "工农区", "南山区", "兴安区", "东山区", "兴山区", "萝北县", "绥滨县"]
+	}, {
+		"name": "双鸭山市",
+		"area": ["市辖区", "尖山区", "岭东区", "四方台区", "宝山区", "集贤县", "友谊县", "宝清县", "饶河县"]
+	}, {
+		"name": "大庆市",
+		"area": ["市辖区", "萨尔图区", "龙凤区", "让胡路区", "红岗区", "大同区", "肇州县", "肇源县", "林甸县", "杜尔伯特蒙古族自治县"]
+	}, {
+		"name": "伊春市",
+		"area": ["市辖区", "伊春区", "南岔区", "友好区", "西林区", "翠峦区", "新青区", "美溪区", "金山屯区", "五营区", "乌马河区", "汤旺河区",
+			"带岭区", "乌伊岭区", "红星区", "上甘岭区", "嘉荫县", "铁力市"
+		]
+	}, {
+		"name": "佳木斯市",
+		"area": ["市辖区", "向阳区", "前进区", "东风区", "郊区", "桦南县", "桦川县", "汤原县", "同江市", "富锦市", "抚远市"]
+	}, {
+		"name": "七台河市",
+		"area": ["市辖区", "新兴区", "桃山区", "茄子河区", "勃利县"]
+	}, {
+		"name": "牡丹江市",
+		"area": ["市辖区", "东安区", "阳明区", "爱民区", "西安区", "林口县", "绥芬河市", "海林市", "宁安市", "穆棱市", "东宁市"]
+	}, {
+		"name": "黑河市",
+		"area": ["市辖区", "爱辉区", "嫩江县", "逊克县", "孙吴县", "北安市", "五大连池市"]
+	}, {
+		"name": "绥化市",
+		"area": ["市辖区", "北林区", "望奎县", "兰西县", "青冈县", "庆安县", "明水县", "绥棱县", "安达市", "肇东市", "海伦市"]
+	}, {
+		"name": "大兴安岭地区",
+		"area": ["呼玛县", "塔河县", "漠河县"]
+	}]
+}, {
+	"name": "市辖区",
+	"city": [{
+		"name": "上海市",
+		"area": ["黄浦区", "徐汇区", "长宁区", "静安区", "普陀区", "虹口区", "杨浦区", "闵行区", "宝山区", "嘉定区", "浦东新区", "金山区", "松江区",
+			"青浦区", "奉贤区", "崇明区"
+		]
+	}]
+}, {
+	"name": "江苏省",
+	"city": [{
+		"name": "南京市",
+		"area": ["市辖区", "玄武区", "秦淮区", "建邺区", "鼓楼区", "浦口区", "栖霞区", "雨花台区", "江宁区", "六合区", "溧水区", "高淳区"]
+	}, {
+		"name": "无锡市",
+		"area": ["市辖区", "锡山区", "惠山区", "滨湖区", "梁溪区", "新吴区", "江阴市", "宜兴市"]
+	}, {
+		"name": "徐州市",
+		"area": ["市辖区", "鼓楼区", "云龙区", "贾汪区", "泉山区", "铜山区", "丰县", "沛县", "睢宁县", "新沂市", "邳州市"]
+	}, {
+		"name": "常州市",
+		"area": ["市辖区", "天宁区", "钟楼区", "新北区", "武进区", "金坛区", "溧阳市"]
+	}, {
+		"name": "苏州市",
+		"area": ["市辖区", "虎丘区", "吴中区", "相城区", "姑苏区", "吴江区", "常熟市", "张家港市", "昆山市", "太仓市"]
+	}, {
+		"name": "南通市",
+		"area": ["市辖区", "崇川区", "港闸区", "通州区", "海安县", "如东县", "启东市", "如皋市", "海门市"]
+	}, {
+		"name": "连云港市",
+		"area": ["市辖区", "连云区", "海州区", "赣榆区", "东海县", "灌云县", "灌南县"]
+	}, {
+		"name": "淮安市",
+		"area": ["市辖区", "淮安区", "淮阴区", "清江浦区", "洪泽区", "涟水县", "盱眙县", "金湖县"]
+	}, {
+		"name": "盐城市",
+		"area": ["市辖区", "亭湖区", "盐都区", "大丰区", "响水县", "滨海县", "阜宁县", "射阳县", "建湖县", "东台市"]
+	}, {
+		"name": "扬州市",
+		"area": ["市辖区", "广陵区", "邗江区", "江都区", "宝应县", "仪征市", "高邮市"]
+	}, {
+		"name": "镇江市",
+		"area": ["市辖区", "京口区", "润州区", "丹徒区", "丹阳市", "扬中市", "句容市"]
+	}, {
+		"name": "泰州市",
+		"area": ["市辖区", "海陵区", "高港区", "姜堰区", "兴化市", "靖江市", "泰兴市"]
+	}, {
+		"name": "宿迁市",
+		"area": ["市辖区", "宿城区", "宿豫区", "沭阳县", "泗阳县", "泗洪县"]
+	}]
+}, {
+	"name": "浙江省",
+	"city": [{
+		"name": "杭州市",
+		"area": ["市辖区", "上城区", "下城区", "江干区", "拱墅区", "西湖区", "滨江区", "萧山区", "余杭区", "富阳区", "桐庐县", "淳安县", "建德市",
+			"临安市"
+		]
+	}, {
+		"name": "宁波市",
+		"area": ["市辖区", "海曙区", "江东区", "江北区", "北仑区", "镇海区", "鄞州区", "象山县", "宁海县", "余姚市", "慈溪市", "奉化市"]
+	}, {
+		"name": "温州市",
+		"area": ["市辖区", "鹿城区", "龙湾区", "瓯海区", "洞头区", "永嘉县", "平阳县", "苍南县", "文成县", "泰顺县", "瑞安市", "乐清市"]
+	}, {
+		"name": "嘉兴市",
+		"area": ["市辖区", "南湖区", "秀洲区", "嘉善县", "海盐县", "海宁市", "平湖市", "桐乡市"]
+	}, {
+		"name": "湖州市",
+		"area": ["市辖区", "吴兴区", "南浔区", "德清县", "长兴县", "安吉县"]
+	}, {
+		"name": "绍兴市",
+		"area": ["市辖区", "越城区", "柯桥区", "上虞区", "新昌县", "诸暨市", "嵊州市"]
+	}, {
+		"name": "金华市",
+		"area": ["市辖区", "婺城区", "金东区", "武义县", "浦江县", "磐安县", "兰溪市", "义乌市", "东阳市", "永康市"]
+	}, {
+		"name": "衢州市",
+		"area": ["市辖区", "柯城区", "衢江区", "常山县", "开化县", "龙游县", "江山市"]
+	}, {
+		"name": "舟山市",
+		"area": ["市辖区", "定海区", "普陀区", "岱山县", "嵊泗县"]
+	}, {
+		"name": "台州市",
+		"area": ["市辖区", "椒江区", "黄岩区", "路桥区", "玉环县", "三门县", "天台县", "仙居县", "温岭市", "临海市"]
+	}, {
+		"name": "丽水市",
+		"area": ["市辖区", "莲都区", "青田县", "缙云县", "遂昌县", "松阳县", "云和县", "庆元县", "景宁畲族自治县", "龙泉市"]
+	}]
+}, {
+	"name": "安徽省",
+	"city": [{
+		"name": "合肥市",
+		"area": ["市辖区", "瑶海区", "庐阳区", "蜀山区", "包河区", "长丰县", "肥东县", "肥西县", "庐江县", "巢湖市"]
+	}, {
+		"name": "芜湖市",
+		"area": ["市辖区", "镜湖区", "弋江区", "鸠江区", "三山区", "芜湖县", "繁昌县", "南陵县", "无为县"]
+	}, {
+		"name": "蚌埠市",
+		"area": ["市辖区", "龙子湖区", "蚌山区", "禹会区", "淮上区", "怀远县", "五河县", "固镇县"]
+	}, {
+		"name": "淮南市",
+		"area": ["市辖区", "大通区", "田家庵区", "谢家集区", "八公山区", "潘集区", "凤台县", "寿县"]
+	}, {
+		"name": "马鞍山市",
+		"area": ["市辖区", "花山区", "雨山区", "博望区", "当涂县", "含山县", "和县"]
+	}, {
+		"name": "淮北市",
+		"area": ["市辖区", "杜集区", "相山区", "烈山区", "濉溪县"]
+	}, {
+		"name": "铜陵市",
+		"area": ["市辖区", "铜官区", "义安区", "郊区", "枞阳县"]
+	}, {
+		"name": "安庆市",
+		"area": ["市辖区", "迎江区", "大观区", "宜秀区", "怀宁县", "潜山县", "太湖县", "宿松县", "望江县", "岳西县", "桐城市"]
+	}, {
+		"name": "黄山市",
+		"area": ["市辖区", "屯溪区", "黄山区", "徽州区", "歙县", "休宁县", "黟县", "祁门县"]
+	}, {
+		"name": "滁州市",
+		"area": ["市辖区", "琅琊区", "南谯区", "来安县", "全椒县", "定远县", "凤阳县", "天长市", "明光市"]
+	}, {
+		"name": "阜阳市",
+		"area": ["市辖区", "颍州区", "颍东区", "颍泉区", "临泉县", "太和县", "阜南县", "颍上县", "界首市"]
+	}, {
+		"name": "宿州市",
+		"area": ["市辖区", "埇桥区", "砀山县", "萧县", "灵璧县", "泗县"]
+	}, {
+		"name": "六安市",
+		"area": ["市辖区", "金安区", "裕安区", "叶集区", "霍邱县", "舒城县", "金寨县", "霍山县"]
+	}, {
+		"name": "亳州市",
+		"area": ["市辖区", "谯城区", "涡阳县", "蒙城县", "利辛县"]
+	}, {
+		"name": "池州市",
+		"area": ["市辖区", "贵池区", "东至县", "石台县", "青阳县"]
+	}, {
+		"name": "宣城市",
+		"area": ["市辖区", "宣州区", "郎溪县", "广德县", "泾县", "绩溪县", "旌德县", "宁国市"]
+	}]
+}, {
+	"name": "福建省",
+	"city": [{
+		"name": "福州市",
+		"area": ["市辖区", "鼓楼区", "台江区", "仓山区", "马尾区", "晋安区", "闽侯县", "连江县", "罗源县", "闽清县", "永泰县", "平潭县", "福清市",
+			"长乐市"
+		]
+	}, {
+		"name": "厦门市",
+		"area": ["市辖区", "思明区", "海沧区", "湖里区", "集美区", "同安区", "翔安区"]
+	}, {
+		"name": "莆田市",
+		"area": ["市辖区", "城厢区", "涵江区", "荔城区", "秀屿区", "仙游县"]
+	}, {
+		"name": "三明市",
+		"area": ["市辖区", "梅列区", "三元区", "明溪县", "清流县", "宁化县", "大田县", "尤溪县", "沙县", "将乐县", "泰宁县", "建宁县", "永安市"]
+	}, {
+		"name": "泉州市",
+		"area": ["市辖区", "鲤城区", "丰泽区", "洛江区", "泉港区", "惠安县", "安溪县", "永春县", "德化县", "金门县", "石狮市", "晋江市", "南安市"]
+	}, {
+		"name": "漳州市",
+		"area": ["市辖区", "芗城区", "龙文区", "云霄县", "漳浦县", "诏安县", "长泰县", "东山县", "南靖县", "平和县", "华安县", "龙海市"]
+	}, {
+		"name": "南平市",
+		"area": ["市辖区", "延平区", "建阳区", "顺昌县", "浦城县", "光泽县", "松溪县", "政和县", "邵武市", "武夷山市", "建瓯市"]
+	}, {
+		"name": "龙岩市",
+		"area": ["市辖区", "新罗区", "永定区", "长汀县", "上杭县", "武平县", "连城县", "漳平市"]
+	}, {
+		"name": "宁德市",
+		"area": ["市辖区", "蕉城区", "霞浦县", "古田县", "屏南县", "寿宁县", "周宁县", "柘荣县", "福安市", "福鼎市"]
+	}]
+}, {
+	"name": "江西省",
+	"city": [{
+		"name": "南昌市",
+		"area": ["市辖区", "东湖区", "西湖区", "青云谱区", "湾里区", "青山湖区", "新建区", "南昌县", "安义县", "进贤县"]
+	}, {
+		"name": "景德镇市",
+		"area": ["市辖区", "昌江区", "珠山区", "浮梁县", "乐平市"]
+	}, {
+		"name": "萍乡市",
+		"area": ["市辖区", "安源区", "湘东区", "莲花县", "上栗县", "芦溪县"]
+	}, {
+		"name": "九江市",
+		"area": ["市辖区", "濂溪区", "浔阳区", "九江县", "武宁县", "修水县", "永修县", "德安县", "都昌县", "湖口县", "彭泽县", "瑞昌市", "共青城市",
+			"庐山市"
+		]
+	}, {
+		"name": "新余市",
+		"area": ["市辖区", "渝水区", "分宜县"]
+	}, {
+		"name": "鹰潭市",
+		"area": ["市辖区", "月湖区", "余江县", "贵溪市"]
+	}, {
+		"name": "赣州市",
+		"area": ["市辖区", "章贡区", "南康区", "赣县", "信丰县", "大余县", "上犹县", "崇义县", "安远县", "龙南县", "定南县", "全南县", "宁都县",
+			"于都县", "兴国县", "会昌县", "寻乌县", "石城县", "瑞金市"
+		]
+	}, {
+		"name": "吉安市",
+		"area": ["市辖区", "吉州区", "青原区", "吉安县", "吉水县", "峡江县", "新干县", "永丰县", "泰和县", "遂川县", "万安县", "安福县", "永新县",
+			"井冈山市"
+		]
+	}, {
+		"name": "宜春市",
+		"area": ["市辖区", "袁州区", "奉新县", "万载县", "上高县", "宜丰县", "靖安县", "铜鼓县", "丰城市", "樟树市", "高安市"]
+	}, {
+		"name": "抚州市",
+		"area": ["市辖区", "临川区", "南城县", "黎川县", "南丰县", "崇仁县", "乐安县", "宜黄县", "金溪县", "资溪县", "东乡县", "广昌县"]
+	}, {
+		"name": "上饶市",
+		"area": ["市辖区", "信州区", "广丰区", "上饶县", "玉山县", "铅山县", "横峰县", "弋阳县", "余干县", "鄱阳县", "万年县", "婺源县", "德兴市"]
+	}]
+}, {
+	"name": "山东省",
+	"city": [{
+		"name": "济南市",
+		"area": ["市辖区", "历下区", "市中区", "槐荫区", "天桥区", "历城区", "长清区", "平阴县", "济阳县", "商河县", "章丘市"]
+	}, {
+		"name": "青岛市",
+		"area": ["市辖区", "市南区", "市北区", "黄岛区", "崂山区", "李沧区", "城阳区", "胶州市", "即墨市", "平度市", "莱西市"]
+	}, {
+		"name": "淄博市",
+		"area": ["市辖区", "淄川区", "张店区", "博山区", "临淄区", "周村区", "桓台县", "高青县", "沂源县"]
+	}, {
+		"name": "枣庄市",
+		"area": ["市辖区", "市中区", "薛城区", "峄城区", "台儿庄区", "山亭区", "滕州市"]
+	}, {
+		"name": "东营市",
+		"area": ["市辖区", "东营区", "河口区", "垦利区", "利津县", "广饶县"]
+	}, {
+		"name": "烟台市",
+		"area": ["市辖区", "芝罘区", "福山区", "牟平区", "莱山区", "长岛县", "龙口市", "莱阳市", "莱州市", "蓬莱市", "招远市", "栖霞市", "海阳市"]
+	}, {
+		"name": "潍坊市",
+		"area": ["市辖区", "潍城区", "寒亭区", "坊子区", "奎文区", "临朐县", "昌乐县", "青州市", "诸城市", "寿光市", "安丘市", "高密市", "昌邑市"]
+	}, {
+		"name": "济宁市",
+		"area": ["市辖区", "任城区", "兖州区", "微山县", "鱼台县", "金乡县", "嘉祥县", "汶上县", "泗水县", "梁山县", "曲阜市", "邹城市"]
+	}, {
+		"name": "泰安市",
+		"area": ["市辖区", "泰山区", "岱岳区", "宁阳县", "东平县", "新泰市", "肥城市"]
+	}, {
+		"name": "威海市",
+		"area": ["市辖区", "环翠区", "文登区", "荣成市", "乳山市"]
+	}, {
+		"name": "日照市",
+		"area": ["市辖区", "东港区", "岚山区", "五莲县", "莒县"]
+	}, {
+		"name": "莱芜市",
+		"area": ["市辖区", "莱城区", "钢城区"]
+	}, {
+		"name": "临沂市",
+		"area": ["市辖区", "兰山区", "罗庄区", "河东区", "沂南县", "郯城县", "沂水县", "兰陵县", "费县", "平邑县", "莒南县", "蒙阴县", "临沭县"]
+	}, {
+		"name": "德州市",
+		"area": ["市辖区", "德城区", "陵城区", "宁津县", "庆云县", "临邑县", "齐河县", "平原县", "夏津县", "武城县", "乐陵市", "禹城市"]
+	}, {
+		"name": "聊城市",
+		"area": ["市辖区", "东昌府区", "阳谷县", "莘县", "茌平县", "东阿县", "冠县", "高唐县", "临清市"]
+	}, {
+		"name": "滨州市",
+		"area": ["市辖区", "滨城区", "沾化区", "惠民县", "阳信县", "无棣县", "博兴县", "邹平县"]
+	}, {
+		"name": "菏泽市",
+		"area": ["市辖区", "牡丹区", "定陶区", "曹县", "单县", "成武县", "巨野县", "郓城县", "鄄城县", "东明县"]
+	}]
+}, {
+	"name": "河南省",
+	"city": [{
+		"name": "郑州市",
+		"area": ["市辖区", "中原区", "二七区", "管城回族区", "金水区", "上街区", "惠济区", "中牟县", "巩义市", "荥阳市", "新密市", "新郑市",
+			"登封市"
+		]
+	}, {
+		"name": "开封市",
+		"area": ["市辖区", "龙亭区", "顺河回族区", "鼓楼区", "禹王台区", "金明区", "祥符区", "杞县", "通许县", "尉氏县", "兰考县"]
+	}, {
+		"name": "洛阳市",
+		"area": ["市辖区", "老城区", "西工区", "瀍河回族区", "涧西区", "吉利区", "洛龙区", "孟津县", "新安县", "栾川县", "嵩县", "汝阳县", "宜阳县",
+			"洛宁县", "伊川县", "偃师市"
+		]
+	}, {
+		"name": "平顶山市",
+		"area": ["市辖区", "新华区", "卫东区", "石龙区", "湛河区", "宝丰县", "叶县", "鲁山县", "郏县", "舞钢市", "汝州市"]
+	}, {
+		"name": "安阳市",
+		"area": ["市辖区", "文峰区", "北关区", "殷都区", "龙安区", "安阳县", "汤阴县", "滑县", "内黄县", "林州市"]
+	}, {
+		"name": "鹤壁市",
+		"area": ["市辖区", "鹤山区", "山城区", "淇滨区", "浚县", "淇县"]
+	}, {
+		"name": "新乡市",
+		"area": ["市辖区", "红旗区", "卫滨区", "凤泉区", "牧野区", "新乡县", "获嘉县", "原阳县", "延津县", "封丘县", "长垣县", "卫辉市", "辉县市"]
+	}, {
+		"name": "焦作市",
+		"area": ["市辖区", "解放区", "中站区", "马村区", "山阳区", "修武县", "博爱县", "武陟县", "温县", "沁阳市", "孟州市"]
+	}, {
+		"name": "濮阳市",
+		"area": ["市辖区", "华龙区", "清丰县", "南乐县", "范县", "台前县", "濮阳县"]
+	}, {
+		"name": "许昌市",
+		"area": ["市辖区", "魏都区", "许昌县", "鄢陵县", "襄城县", "禹州市", "长葛市"]
+	}, {
+		"name": "漯河市",
+		"area": ["市辖区", "源汇区", "郾城区", "召陵区", "舞阳县", "临颍县"]
+	}, {
+		"name": "三门峡市",
+		"area": ["市辖区", "湖滨区", "陕州区", "渑池县", "卢氏县", "义马市", "灵宝市"]
+	}, {
+		"name": "南阳市",
+		"area": ["市辖区", "宛城区", "卧龙区", "南召县", "方城县", "西峡县", "镇平县", "内乡县", "淅川县", "社旗县", "唐河县", "新野县", "桐柏县",
+			"邓州市"
+		]
+	}, {
+		"name": "商丘市",
+		"area": ["市辖区", "梁园区", "睢阳区", "民权县", "睢县", "宁陵县", "柘城县", "虞城县", "夏邑县", "永城市"]
+	}, {
+		"name": "信阳市",
+		"area": ["市辖区", "浉河区", "平桥区", "罗山县", "光山县", "新县", "商城县", "固始县", "潢川县", "淮滨县", "息县"]
+	}, {
+		"name": "周口市",
+		"area": ["市辖区", "川汇区", "扶沟县", "西华县", "商水县", "沈丘县", "郸城县", "淮阳县", "太康县", "鹿邑县", "项城市"]
+	}, {
+		"name": "驻马店市",
+		"area": ["市辖区", "驿城区", "西平县", "上蔡县", "平舆县", "正阳县", "确山县", "泌阳县", "汝南县", "遂平县", "新蔡县"]
+	}, {
+		"name": "直辖县",
+		"area": ["济源市"]
+	}]
+}, {
+	"name": "湖北省",
+	"city": [{
+		"name": "武汉市",
+		"area": ["市辖区", "江岸区", "江汉区", "硚口区", "汉阳区", "武昌区", "青山区", "洪山区", "东西湖区", "汉南区", "蔡甸区", "江夏区", "黄陂区",
+			"新洲区"
+		]
+	}, {
+		"name": "黄石市",
+		"area": ["市辖区", "黄石港区", "西塞山区", "下陆区", "铁山区", "阳新县", "大冶市"]
+	}, {
+		"name": "十堰市",
+		"area": ["市辖区", "茅箭区", "张湾区", "郧阳区", "郧西县", "竹山县", "竹溪县", "房县", "丹江口市"]
+	}, {
+		"name": "宜昌市",
+		"area": ["市辖区", "西陵区", "伍家岗区", "点军区", "猇亭区", "夷陵区", "远安县", "兴山县", "秭归县", "长阳土家族自治县", "五峰土家族自治县",
+			"宜都市", "当阳市", "枝江市"
+		]
+	}, {
+		"name": "襄阳市",
+		"area": ["市辖区", "襄城区", "樊城区", "襄州区", "南漳县", "谷城县", "保康县", "老河口市", "枣阳市", "宜城市"]
+	}, {
+		"name": "鄂州市",
+		"area": ["市辖区", "梁子湖区", "华容区", "鄂城区"]
+	}, {
+		"name": "荆门市",
+		"area": ["市辖区", "东宝区", "掇刀区", "京山县", "沙洋县", "钟祥市"]
+	}, {
+		"name": "孝感市",
+		"area": ["市辖区", "孝南区", "孝昌县", "大悟县", "云梦县", "应城市", "安陆市", "汉川市"]
+	}, {
+		"name": "荆州市",
+		"area": ["市辖区", "沙市区", "荆州区", "公安县", "监利县", "江陵县", "石首市", "洪湖市", "松滋市"]
+	}, {
+		"name": "黄冈市",
+		"area": ["市辖区", "黄州区", "团风县", "红安县", "罗田县", "英山县", "浠水县", "蕲春县", "黄梅县", "麻城市", "武穴市"]
+	}, {
+		"name": "咸宁市",
+		"area": ["市辖区", "咸安区", "嘉鱼县", "通城县", "崇阳县", "通山县", "赤壁市"]
+	}, {
+		"name": "随州市",
+		"area": ["市辖区", "曾都区", "随县", "广水市"]
+	}, {
+		"name": "恩施土家族苗族自治州",
+		"area": ["恩施市", "利川市", "建始县", "巴东县", "宣恩县", "咸丰县", "来凤县", "鹤峰县"]
+	}, {
+		"name": "直辖县",
+		"area": ["仙桃市", "潜江市", "天门市", "神农架林区"]
+	}]
+}, {
+	"name": "湖南省",
+	"city": [{
+		"name": "长沙市",
+		"area": ["市辖区", "芙蓉区", "天心区", "岳麓区", "开福区", "雨花区", "望城区", "长沙县", "宁乡县", "浏阳市"]
+	}, {
+		"name": "株洲市",
+		"area": ["市辖区", "荷塘区", "芦淞区", "石峰区", "天元区", "株洲县", "攸县", "茶陵县", "炎陵县", "醴陵市"]
+	}, {
+		"name": "湘潭市",
+		"area": ["市辖区", "雨湖区", "岳塘区", "湘潭县", "湘乡市", "韶山市"]
+	}, {
+		"name": "衡阳市",
+		"area": ["市辖区", "珠晖区", "雁峰区", "石鼓区", "蒸湘区", "南岳区", "衡阳县", "衡南县", "衡山县", "衡东县", "祁东县", "耒阳市", "常宁市"]
+	}, {
+		"name": "邵阳市",
+		"area": ["市辖区", "双清区", "大祥区", "北塔区", "邵东县", "新邵县", "邵阳县", "隆回县", "洞口县", "绥宁县", "新宁县", "城步苗族自治县",
+			"武冈市"
+		]
+	}, {
+		"name": "岳阳市",
+		"area": ["市辖区", "岳阳楼区", "云溪区", "君山区", "岳阳县", "华容县", "湘阴县", "平江县", "汨罗市", "临湘市"]
+	}, {
+		"name": "常德市",
+		"area": ["市辖区", "武陵区", "鼎城区", "安乡县", "汉寿县", "澧县", "临澧县", "桃源县", "石门县", "津市市"]
+	}, {
+		"name": "张家界市",
+		"area": ["市辖区", "永定区", "武陵源区", "慈利县", "桑植县"]
+	}, {
+		"name": "益阳市",
+		"area": ["市辖区", "资阳区", "赫山区", "南县", "桃江县", "安化县", "沅江市"]
+	}, {
+		"name": "郴州市",
+		"area": ["市辖区", "北湖区", "苏仙区", "桂阳县", "宜章县", "永兴县", "嘉禾县", "临武县", "汝城县", "桂东县", "安仁县", "资兴市"]
+	}, {
+		"name": "永州市",
+		"area": ["市辖区", "零陵区", "冷水滩区", "祁阳县", "东安县", "双牌县", "道县", "江永县", "宁远县", "蓝山县", "新田县", "江华瑶族自治县"]
+	}, {
+		"name": "怀化市",
+		"area": ["市辖区", "鹤城区", "中方县", "沅陵县", "辰溪县", "溆浦县", "会同县", "麻阳苗族自治县", "新晃侗族自治县", "芷江侗族自治县",
+			"靖州苗族侗族自治县", "通道侗族自治县", "洪江市"
+		]
+	}, {
+		"name": "娄底市",
+		"area": ["市辖区", "娄星区", "双峰县", "新化县", "冷水江市", "涟源市"]
+	}, {
+		"name": "湘西土家族苗族自治州",
+		"area": ["吉首市", "泸溪县", "凤凰县", "花垣县", "保靖县", "古丈县", "永顺县", "龙山县"]
+	}]
+}, {
+	"name": "广东省",
+	"city": [{
+		"name": "广州市",
+		"area": ["市辖区", "荔湾区", "越秀区", "海珠区", "天河区", "白云区", "黄埔区", "番禺区", "花都区", "南沙区", "从化区", "增城区"]
+	}, {
+		"name": "韶关市",
+		"area": ["市辖区", "武江区", "浈江区", "曲江区", "始兴县", "仁化县", "翁源县", "乳源瑶族自治县", "新丰县", "乐昌市", "南雄市"]
+	}, {
+		"name": "深圳市",
+		"area": ["市辖区", "罗湖区", "福田区", "南山区", "宝安区", "龙岗区", "盐田区"]
+	}, {
+		"name": "珠海市",
+		"area": ["市辖区", "香洲区", "斗门区", "金湾区"]
+	}, {
+		"name": "汕头市",
+		"area": ["市辖区", "龙湖区", "金平区", "濠江区", "潮阳区", "潮南区", "澄海区", "南澳县"]
+	}, {
+		"name": "佛山市",
+		"area": ["市辖区", "禅城区", "南海区", "顺德区", "三水区", "高明区"]
+	}, {
+		"name": "江门市",
+		"area": ["市辖区", "蓬江区", "江海区", "新会区", "台山市", "开平市", "鹤山市", "恩平市"]
+	}, {
+		"name": "湛江市",
+		"area": ["市辖区", "赤坎区", "霞山区", "坡头区", "麻章区", "遂溪县", "徐闻县", "廉江市", "雷州市", "吴川市"]
+	}, {
+		"name": "茂名市",
+		"area": ["市辖区", "茂南区", "电白区", "高州市", "化州市", "信宜市"]
+	}, {
+		"name": "肇庆市",
+		"area": ["市辖区", "端州区", "鼎湖区", "高要区", "广宁县", "怀集县", "封开县", "德庆县", "四会市"]
+	}, {
+		"name": "惠州市",
+		"area": ["市辖区", "惠城区", "惠阳区", "博罗县", "惠东县", "龙门县"]
+	}, {
+		"name": "梅州市",
+		"area": ["市辖区", "梅江区", "梅县区", "大埔县", "丰顺县", "五华县", "平远县", "蕉岭县", "兴宁市"]
+	}, {
+		"name": "汕尾市",
+		"area": ["市辖区", "城区", "海丰县", "陆河县", "陆丰市"]
+	}, {
+		"name": "河源市",
+		"area": ["市辖区", "源城区", "紫金县", "龙川县", "连平县", "和平县", "东源县"]
+	}, {
+		"name": "阳江市",
+		"area": ["市辖区", "江城区", "阳东区", "阳西县", "阳春市"]
+	}, {
+		"name": "清远市",
+		"area": ["市辖区", "清城区", "清新区", "佛冈县", "阳山县", "连山壮族瑶族自治县", "连南瑶族自治县", "英德市", "连州市"]
+	}, {
+		"name": "东莞市",
+		"area": ["东城街道办事处", "南城街道办事处", "万江街道办事处", "莞城街道办事处", "石碣镇", "石龙镇", "茶山镇", "石排镇", "企石镇", "横沥镇",
+			"桥头镇", "谢岗镇", "东坑镇", "常平镇", "寮步镇", "樟木头镇", "大朗镇", "黄江镇", "清溪镇", "塘厦镇", "凤岗镇", "大岭山镇", "长安镇",
+			"虎门镇", "厚街镇", "沙田镇", "道滘镇", "洪梅镇", "麻涌镇", "望牛墩镇", "中堂镇", "高埗镇", "松山湖管委会", "虎门港管委会", "东莞生态园"
+		]
+	}, {
+		"name": "中山市",
+		"area": ["石岐区街道办事处", "东区街道办事处", "火炬开发区街道办事处", "西区街道办事处", "南区街道办事处", "五桂山街道办事处", "小榄镇", "黄圃镇", "民众镇",
+			"东凤镇", "东升镇", "古镇镇", "沙溪镇", "坦洲镇", "港口镇", "三角镇", "横栏镇", "南头镇", "阜沙镇", "南朗镇", "三乡镇", "板芙镇",
+			"大涌镇", "神湾镇"
+		]
+	}, {
+		"name": "潮州市",
+		"area": ["市辖区", "湘桥区", "潮安区", "饶平县"]
+	}, {
+		"name": "揭阳市",
+		"area": ["市辖区", "榕城区", "揭东区", "揭西县", "惠来县", "普宁市"]
+	}, {
+		"name": "云浮市",
+		"area": ["市辖区", "云城区", "云安区", "新兴县", "郁南县", "罗定市"]
+	}]
+}, {
+	"name": "广西壮族自治区",
+	"city": [{
+		"name": "南宁市",
+		"area": ["市辖区", "兴宁区", "青秀区", "江南区", "西乡塘区", "良庆区", "邕宁区", "武鸣区", "隆安县", "马山县", "上林县", "宾阳县", "横县"]
+	}, {
+		"name": "柳州市",
+		"area": ["市辖区", "城中区", "鱼峰区", "柳南区", "柳北区", "柳江区", "柳城县", "鹿寨县", "融安县", "融水苗族自治县", "三江侗族自治县"]
+	}, {
+		"name": "桂林市",
+		"area": ["市辖区", "秀峰区", "叠彩区", "象山区", "七星区", "雁山区", "临桂区", "阳朔县", "灵川县", "全州县", "兴安县", "永福县", "灌阳县",
+			"龙胜各族自治县", "资源县", "平乐县", "荔浦县", "恭城瑶族自治县"
+		]
+	}, {
+		"name": "梧州市",
+		"area": ["市辖区", "万秀区", "长洲区", "龙圩区", "苍梧县", "藤县", "蒙山县", "岑溪市"]
+	}, {
+		"name": "北海市",
+		"area": ["市辖区", "海城区", "银海区", "铁山港区", "合浦县"]
+	}, {
+		"name": "防城港市",
+		"area": ["市辖区", "港口区", "防城区", "上思县", "东兴市"]
+	}, {
+		"name": "钦州市",
+		"area": ["市辖区", "钦南区", "钦北区", "灵山县", "浦北县"]
+	}, {
+		"name": "贵港市",
+		"area": ["市辖区", "港北区", "港南区", "覃塘区", "平南县", "桂平市"]
+	}, {
+		"name": "玉林市",
+		"area": ["市辖区", "玉州区", "福绵区", "容县", "陆川县", "博白县", "兴业县", "北流市"]
+	}, {
+		"name": "百色市",
+		"area": ["市辖区", "右江区", "田阳县", "田东县", "平果县", "德保县", "那坡县", "凌云县", "乐业县", "田林县", "西林县", "隆林各族自治县",
+			"靖西市"
+		]
+	}, {
+		"name": "贺州市",
+		"area": ["市辖区", "八步区", "平桂区", "昭平县", "钟山县", "富川瑶族自治县"]
+	}, {
+		"name": "河池市",
+		"area": ["市辖区", "金城江区", "南丹县", "天峨县", "凤山县", "东兰县", "罗城仫佬族自治县", "环江毛南族自治县", "巴马瑶族自治县", "都安瑶族自治县",
+			"大化瑶族自治县", "宜州市"
+		]
+	}, {
+		"name": "来宾市",
+		"area": ["市辖区", "兴宾区", "忻城县", "象州县", "武宣县", "金秀瑶族自治县", "合山市"]
+	}, {
+		"name": "崇左市",
+		"area": ["市辖区", "江州区", "扶绥县", "宁明县", "龙州县", "大新县", "天等县", "凭祥市"]
+	}]
+}, {
+	"name": "海南省",
+	"city": [{
+		"name": "海口市",
+		"area": ["市辖区", "秀英区", "龙华区", "琼山区", "美兰区"]
+	}, {
+		"name": "三亚市",
+		"area": ["市辖区", "海棠区", "吉阳区", "天涯区", "崖州区"]
+	}, {
+		"name": "三沙市",
+		"area": ["西沙群岛", "南沙群岛", "中沙群岛的岛礁及其海域"]
+	}, {
+		"name": "儋州市",
+		"area": ["那大镇", "和庆镇", "南丰镇", "大成镇", "雅星镇", "兰洋镇", "光村镇", "木棠镇", "海头镇", "峨蔓镇", "三都镇", "王五镇", "白马井镇",
+			"中和镇", "排浦镇", "东成镇", "新州镇", "国营西培农场", "国营西联农场", "国营蓝洋农场", "国营八一农场", "洋浦经济开发区", "华南热作学院"
+		]
+	}, {
+		"name": "省直辖县级行政区域",
+		"area": ["五指山市", "琼海市", "文昌市", "万宁市", "东方市", "定安县", "屯昌县", "澄迈县", "临高县", "白沙黎族自治县", "昌江黎族自治县",
+			"乐东黎族自治县", "陵水黎族自治县", "保亭黎族苗族自治县", "琼中黎族苗族自治县"
+		]
+	}]
+}, {
+	"name": "重庆市",
+	"city": [{
+		"name": "市辖区",
+		"area": ["万州区", "涪陵区", "渝中区", "大渡口区", "江北区", "沙坪坝区", "九龙坡区", "南岸区", "北碚区", "綦江区", "大足区", "渝北区",
+			"巴南区", "黔江区", "长寿区", "江津区", "合川区", "永川区", "南川区", "璧山区", "铜梁区", "潼南区", "荣昌区", "开州区"
+		]
+	}, {
+		"name": "县",
+		"area": ["梁平县", "城口县", "丰都县", "垫江县", "武隆县", "忠县", "云阳县", "奉节县", "巫山县", "巫溪县", "石柱土家族自治县",
+			"秀山土家族苗族自治县", "酉阳土家族苗族自治县", "彭水苗族土家族自治县"
+		]
+	}]
+}, {
+	"name": "四川省",
+	"city": [{
+		"name": "成都市",
+		"area": ["市辖区", "锦江区", "青羊区", "金牛区", "武侯区", "成华区", "龙泉驿区", "青白江区", "新都区", "温江区", "双流区", "金堂县", "郫县",
+			"大邑县", "蒲江县", "新津县", "都江堰市", "彭州市", "邛崃市", "崇州市", "简阳市"
+		]
+	}, {
+		"name": "自贡市",
+		"area": ["市辖区", "自流井区", "贡井区", "大安区", "沿滩区", "荣县", "富顺县"]
+	}, {
+		"name": "攀枝花市",
+		"area": ["市辖区", "东区", "西区", "仁和区", "米易县", "盐边县"]
+	}, {
+		"name": "泸州市",
+		"area": ["市辖区", "江阳区", "纳溪区", "龙马潭区", "泸县", "合江县", "叙永县", "古蔺县"]
+	}, {
+		"name": "德阳市",
+		"area": ["市辖区", "旌阳区", "中江县", "罗江县", "广汉市", "什邡市", "绵竹市"]
+	}, {
+		"name": "绵阳市",
+		"area": ["市辖区", "涪城区", "游仙区", "安州区", "三台县", "盐亭县", "梓潼县", "北川羌族自治县", "平武县", "江油市"]
+	}, {
+		"name": "广元市",
+		"area": ["市辖区", "利州区", "昭化区", "朝天区", "旺苍县", "青川县", "剑阁县", "苍溪县"]
+	}, {
+		"name": "遂宁市",
+		"area": ["市辖区", "船山区", "安居区", "蓬溪县", "射洪县", "大英县"]
+	}, {
+		"name": "内江市",
+		"area": ["市辖区", "市中区", "东兴区", "威远县", "资中县", "隆昌县"]
+	}, {
+		"name": "乐山市",
+		"area": ["市辖区", "市中区", "沙湾区", "五通桥区", "金口河区", "犍为县", "井研县", "夹江县", "沐川县", "峨边彝族自治县", "马边彝族自治县",
+			"峨眉山市"
+		]
+	}, {
+		"name": "南充市",
+		"area": ["市辖区", "顺庆区", "高坪区", "嘉陵区", "南部县", "营山县", "蓬安县", "仪陇县", "西充县", "阆中市"]
+	}, {
+		"name": "眉山市",
+		"area": ["市辖区", "东坡区", "彭山区", "仁寿县", "洪雅县", "丹棱县", "青神县"]
+	}, {
+		"name": "宜宾市",
+		"area": ["市辖区", "翠屏区", "南溪区", "宜宾县", "江安县", "长宁县", "高县", "珙县", "筠连县", "兴文县", "屏山县"]
+	}, {
+		"name": "广安市",
+		"area": ["市辖区", "广安区", "前锋区", "岳池县", "武胜县", "邻水县", "华蓥市"]
+	}, {
+		"name": "达州市",
+		"area": ["市辖区", "通川区", "达川区", "宣汉县", "开江县", "大竹县", "渠县", "万源市"]
+	}, {
+		"name": "雅安市",
+		"area": ["市辖区", "雨城区", "名山区", "荥经县", "汉源县", "石棉县", "天全县", "芦山县", "宝兴县"]
+	}, {
+		"name": "巴中市",
+		"area": ["市辖区", "巴州区", "恩阳区", "通江县", "南江县", "平昌县"]
+	}, {
+		"name": "资阳市",
+		"area": ["市辖区", "雁江区", "安岳县", "乐至县"]
+	}, {
+		"name": "阿坝藏族羌族自治州",
+		"area": ["马尔康市", "汶川县", "理县", "茂县", "松潘县", "九寨沟县", "金川县", "小金县", "黑水县", "壤塘县", "阿坝县", "若尔盖县", "红原县"]
+	}, {
+		"name": "甘孜藏族自治州",
+		"area": ["康定市", "泸定县", "丹巴县", "九龙县", "雅江县", "道孚县", "炉霍县", "甘孜县", "新龙县", "德格县", "白玉县", "石渠县", "色达县",
+			"理塘县", "巴塘县", "乡城县", "稻城县", "得荣县"
+		]
+	}, {
+		"name": "凉山彝族自治州",
+		"area": ["西昌市", "木里藏族自治县", "盐源县", "德昌县", "会理县", "会东县", "宁南县", "普格县", "布拖县", "金阳县", "昭觉县", "喜德县",
+			"冕宁县", "越西县", "甘洛县", "美姑县", "雷波县"
+		]
+	}]
+}, {
+	"name": "贵州省",
+	"city": [{
+		"name": "贵阳市",
+		"area": ["市辖区", "南明区", "云岩区", "花溪区", "乌当区", "白云区", "观山湖区", "开阳县", "息烽县", "修文县", "清镇市"]
+	}, {
+		"name": "六盘水市",
+		"area": ["钟山区", "六枝特区", "水城县", "盘县"]
+	}, {
+		"name": "遵义市",
+		"area": ["市辖区", "红花岗区", "汇川区", "播州区", "桐梓县", "绥阳县", "正安县", "道真仡佬族苗族自治县", "务川仡佬族苗族自治县", "凤冈县", "湄潭县",
+			"余庆县", "习水县", "赤水市", "仁怀市"
+		]
+	}, {
+		"name": "安顺市",
+		"area": ["市辖区", "西秀区", "平坝区", "普定县", "镇宁布依族苗族自治县", "关岭布依族苗族自治县", "紫云苗族布依族自治县"]
+	}, {
+		"name": "毕节市",
+		"area": ["市辖区", "七星关区", "大方县", "黔西县", "金沙县", "织金县", "纳雍县", "威宁彝族回族苗族自治县", "赫章县"]
+	}, {
+		"name": "铜仁市",
+		"area": ["市辖区", "碧江区", "万山区", "江口县", "玉屏侗族自治县", "石阡县", "思南县", "印江土家族苗族自治县", "德江县", "沿河土家族自治县",
+			"松桃苗族自治县"
+		]
+	}, {
+		"name": "黔西南布依族苗族自治州",
+		"area": ["兴义市", "兴仁县", "普安县", "晴隆县", "贞丰县", "望谟县", "册亨县", "安龙县"]
+	}, {
+		"name": "黔东南苗族侗族自治州",
+		"area": ["凯里市", "黄平县", "施秉县", "三穗县", "镇远县", "岑巩县", "天柱县", "锦屏县", "剑河县", "台江县", "黎平县", "榕江县", "从江县",
+			"雷山县", "麻江县", "丹寨县"
+		]
+	}, {
+		"name": "黔南布依族苗族自治州",
+		"area": ["都匀市", "福泉市", "荔波县", "贵定县", "瓮安县", "独山县", "平塘县", "罗甸县", "长顺县", "龙里县", "惠水县", "三都水族自治县"]
+	}]
+}, {
+	"name": "云南省",
+	"city": [{
+		"name": "昆明市",
+		"area": ["市辖区", "五华区", "盘龙区", "官渡区", "西山区", "东川区", "呈贡区", "晋宁县", "富民县", "宜良县", "石林彝族自治县", "嵩明县",
+			"禄劝彝族苗族自治县", "寻甸回族彝族自治县", "安宁市"
+		]
+	}, {
+		"name": "曲靖市",
+		"area": ["市辖区", "麒麟区", "沾益区", "马龙县", "陆良县", "师宗县", "罗平县", "富源县", "会泽县", "宣威市"]
+	}, {
+		"name": "玉溪市",
+		"area": ["市辖区", "红塔区", "江川区", "澄江县", "通海县", "华宁县", "易门县", "峨山彝族自治县", "新平彝族傣族自治县", "元江哈尼族彝族傣族自治县"]
+	}, {
+		"name": "保山市",
+		"area": ["市辖区", "隆阳区", "施甸县", "龙陵县", "昌宁县", "腾冲市"]
+	}, {
+		"name": "昭通市",
+		"area": ["市辖区", "昭阳区", "鲁甸县", "巧家县", "盐津县", "大关县", "永善县", "绥江县", "镇雄县", "彝良县", "威信县", "水富县"]
+	}, {
+		"name": "丽江市",
+		"area": ["市辖区", "古城区", "玉龙纳西族自治县", "永胜县", "华坪县", "宁蒗彝族自治县"]
+	}, {
+		"name": "普洱市",
+		"area": ["市辖区", "思茅区", "宁洱哈尼族彝族自治县", "墨江哈尼族自治县", "景东彝族自治县", "景谷傣族彝族自治县", "镇沅彝族哈尼族拉祜族自治县",
+			"江城哈尼族彝族自治县", "孟连傣族拉祜族佤族自治县", "澜沧拉祜族自治县", "西盟佤族自治县"
+		]
+	}, {
+		"name": "临沧市",
+		"area": ["市辖区", "临翔区", "凤庆县", "云县", "永德县", "镇康县", "双江拉祜族佤族布朗族傣族自治县", "耿马傣族佤族自治县", "沧源佤族自治县"]
+	}, {
+		"name": "楚雄彝族自治州",
+		"area": ["楚雄市", "双柏县", "牟定县", "南华县", "姚安县", "大姚县", "永仁县", "元谋县", "武定县", "禄丰县"]
+	}, {
+		"name": "红河哈尼族彝族自治州",
+		"area": ["个旧市", "开远市", "蒙自市", "弥勒市", "屏边苗族自治县", "建水县", "石屏县", "泸西县", "元阳县", "红河县", "金平苗族瑶族傣族自治县",
+			"绿春县", "河口瑶族自治县"
+		]
+	}, {
+		"name": "文山壮族苗族自治州",
+		"area": ["文山市", "砚山县", "西畴县", "麻栗坡县", "马关县", "丘北县", "广南县", "富宁县"]
+	}, {
+		"name": "西双版纳傣族自治州",
+		"area": ["景洪市", "勐海县", "勐腊县"]
+	}, {
+		"name": "大理白族自治州",
+		"area": ["大理市", "漾濞彝族自治县", "祥云县", "宾川县", "弥渡县", "南涧彝族自治县", "巍山彝族回族自治县", "永平县", "云龙县", "洱源县", "剑川县",
+			"鹤庆县"
+		]
+	}, {
+		"name": "德宏傣族景颇族自治州",
+		"area": ["瑞丽市", "芒市", "梁河县", "盈江县", "陇川县"]
+	}, {
+		"name": "怒江傈僳族自治州",
+		"area": ["泸水市", "福贡县", "贡山独龙族怒族自治县", "兰坪白族普米族自治县"]
+	}, {
+		"name": "迪庆藏族自治州",
+		"area": ["香格里拉市", "德钦县", "维西傈僳族自治县"]
+	}]
+}, {
+	"name": "西藏自治区",
+	"city": [{
+		"name": "拉萨市",
+		"area": ["市辖区", "城关区", "堆龙德庆区", "林周县", "当雄县", "尼木县", "曲水县", "达孜县", "墨竹工卡县"]
+	}, {
+		"name": "日喀则市",
+		"area": ["桑珠孜区", "南木林县", "江孜县", "定日县", "萨迦县", "拉孜县", "昂仁县", "谢通门县", "白朗县", "仁布县", "康马县", "定结县",
+			"仲巴县", "亚东县", "吉隆县", "聂拉木县", "萨嘎县", "岗巴县"
+		]
+	}, {
+		"name": "昌都市",
+		"area": ["卡若区", "江达县", "贡觉县", "类乌齐县", "丁青县", "察雅县", "八宿县", "左贡县", "芒康县", "洛隆县", "边坝县"]
+	}, {
+		"name": "林芝市",
+		"area": ["巴宜区", "工布江达县", "米林县", "墨脱县", "波密县", "察隅县", "朗县"]
+	}, {
+		"name": "山南市",
+		"area": ["市辖区", "乃东区", "扎囊县", "贡嘎县", "桑日县", "琼结县", "曲松县", "措美县", "洛扎县", "加查县", "隆子县", "错那县", "浪卡子县"]
+	}, {
+		"name": "那曲地区",
+		"area": ["那曲县", "嘉黎县", "比如县", "聂荣县", "安多县", "申扎县", "索县", "班戈县", "巴青县", "尼玛县", "双湖县"]
+	}, {
+		"name": "阿里地区",
+		"area": ["普兰县", "札达县", "噶尔县", "日土县", "革吉县", "改则县", "措勤县"]
+	}]
+}, {
+	"name": "陕西省",
+	"city": [{
+		"name": "西安市",
+		"area": ["市辖区", "新城区", "碑林区", "莲湖区", "灞桥区", "未央区", "雁塔区", "阎良区", "临潼区", "长安区", "高陵区", "蓝田县", "周至县",
+			"户县"
+		]
+	}, {
+		"name": "铜川市",
+		"area": ["市辖区", "王益区", "印台区", "耀州区", "宜君县"]
+	}, {
+		"name": "宝鸡市",
+		"area": ["市辖区", "渭滨区", "金台区", "陈仓区", "凤翔县", "岐山县", "扶风县", "眉县", "陇县", "千阳县", "麟游县", "凤县", "太白县"]
+	}, {
+		"name": "咸阳市",
+		"area": ["市辖区", "秦都区", "杨陵区", "渭城区", "三原县", "泾阳县", "乾县", "礼泉县", "永寿县", "彬县", "长武县", "旬邑县", "淳化县",
+			"武功县", "兴平市"
+		]
+	}, {
+		"name": "渭南市",
+		"area": ["市辖区", "临渭区", "华州区", "潼关县", "大荔县", "合阳县", "澄城县", "蒲城县", "白水县", "富平县", "韩城市", "华阴市"]
+	}, {
+		"name": "延安市",
+		"area": ["市辖区", "宝塔区", "安塞区", "延长县", "延川县", "子长县", "志丹县", "吴起县", "甘泉县", "富县", "洛川县", "宜川县", "黄龙县",
+			"黄陵县"
+		]
+	}, {
+		"name": "汉中市",
+		"area": ["市辖区", "汉台区", "南郑县", "城固县", "洋县", "西乡县", "勉县", "宁强县", "略阳县", "镇巴县", "留坝县", "佛坪县"]
+	}, {
+		"name": "榆林市",
+		"area": ["市辖区", "榆阳区", "横山区", "神木县", "府谷县", "靖边县", "定边县", "绥德县", "米脂县", "佳县", "吴堡县", "清涧县", "子洲县"]
+	}, {
+		"name": "安康市",
+		"area": ["市辖区", "汉滨区", "汉阴县", "石泉县", "宁陕县", "紫阳县", "岚皋县", "平利县", "镇坪县", "旬阳县", "白河县"]
+	}, {
+		"name": "商洛市",
+		"area": ["市辖区", "商州区", "洛南县", "丹凤县", "商南县", "山阳县", "镇安县", "柞水县"]
+	}]
+}, {
+	"name": "甘肃省",
+	"city": [{
+		"name": "兰州市",
+		"area": ["市辖区", "城关区", "七里河区", "西固区", "安宁区", "红古区", "永登县", "皋兰县", "榆中县"]
+	}, {
+		"name": "嘉峪关市",
+		"area": ["市辖区"]
+	}, {
+		"name": "金昌市",
+		"area": ["市辖区", "金川区", "永昌县"]
+	}, {
+		"name": "白银市",
+		"area": ["市辖区", "白银区", "平川区", "靖远县", "会宁县", "景泰县"]
+	}, {
+		"name": "天水市",
+		"area": ["市辖区", "秦州区", "麦积区", "清水县", "秦安县", "甘谷县", "武山县", "张家川回族自治县"]
+	}, {
+		"name": "武威市",
+		"area": ["市辖区", "凉州区", "民勤县", "古浪县", "天祝藏族自治县"]
+	}, {
+		"name": "张掖市",
+		"area": ["市辖区", "甘州区", "肃南裕固族自治县", "民乐县", "临泽县", "高台县", "山丹县"]
+	}, {
+		"name": "平凉市",
+		"area": ["市辖区", "崆峒区", "泾川县", "灵台县", "崇信县", "华亭县", "庄浪县", "静宁县"]
+	}, {
+		"name": "酒泉市",
+		"area": ["市辖区", "肃州区", "金塔县", "瓜州县", "肃北蒙古族自治县", "阿克塞哈萨克族自治县", "玉门市", "敦煌市"]
+	}, {
+		"name": "庆阳市",
+		"area": ["市辖区", "西峰区", "庆城县", "环县", "华池县", "合水县", "正宁县", "宁县", "镇原县"]
+	}, {
+		"name": "定西市",
+		"area": ["市辖区", "安定区", "通渭县", "陇西县", "渭源县", "临洮县", "漳县", "岷县"]
+	}, {
+		"name": "陇南市",
+		"area": ["市辖区", "武都区", "成县", "文县", "宕昌县", "康县", "西和县", "礼县", "徽县", "两当县"]
+	}, {
+		"name": "临夏回族自治州",
+		"area": ["临夏市", "临夏县", "康乐县", "永靖县", "广河县", "和政县", "东乡族自治县", "积石山保安族东乡族撒拉族自治县"]
+	}, {
+		"name": "甘南藏族自治州",
+		"area": ["合作市", "临潭县", "卓尼县", "舟曲县", "迭部县", "玛曲县", "碌曲县", "夏河县"]
+	}]
+}, {
+	"name": "青海省",
+	"city": [{
+		"name": "西宁市",
+		"area": ["市辖区", "城东区", "城中区", "城西区", "城北区", "大通回族土族自治县", "湟中县", "湟源县"]
+	}, {
+		"name": "海东市",
+		"area": ["乐都区", "平安区", "民和回族土族自治县", "互助土族自治县", "化隆回族自治县", "循化撒拉族自治县"]
+	}, {
+		"name": "海北藏族自治州",
+		"area": ["门源回族自治县", "祁连县", "海晏县", "刚察县"]
+	}, {
+		"name": "黄南藏族自治州",
+		"area": ["同仁县", "尖扎县", "泽库县", "河南蒙古族自治县"]
+	}, {
+		"name": "海南藏族自治州",
+		"area": ["共和县", "同德县", "贵德县", "兴海县", "贵南县"]
+	}, {
+		"name": "果洛藏族自治州",
+		"area": ["玛沁县", "班玛县", "甘德县", "达日县", "久治县", "玛多县"]
+	}, {
+		"name": "玉树藏族自治州",
+		"area": ["玉树市", "杂多县", "称多县", "治多县", "囊谦县", "曲麻莱县"]
+	}, {
+		"name": "海西蒙古族藏族自治州",
+		"area": ["格尔木市", "德令哈市", "乌兰县", "都兰县", "天峻县"]
+	}]
+}, {
+	"name": "宁夏回族自治区",
+	"city": [{
+		"name": "银川市",
+		"area": ["市辖区", "兴庆区", "西夏区", "金凤区", "永宁县", "贺兰县", "灵武市"]
+	}, {
+		"name": "石嘴山市",
+		"area": ["市辖区", "大武口区", "惠农区", "平罗县"]
+	}, {
+		"name": "吴忠市",
+		"area": ["市辖区", "利通区", "红寺堡区", "盐池县", "同心县", "青铜峡市"]
+	}, {
+		"name": "固原市",
+		"area": ["市辖区", "原州区", "西吉县", "隆德县", "泾源县", "彭阳县"]
+	}, {
+		"name": "中卫市",
+		"area": ["市辖区", "沙坡头区", "中宁县", "海原县"]
+	}]
+}, {
+	"name": "新疆维吾尔自治区",
+	"city": [{
+		"name": "乌鲁木齐市",
+		"area": ["市辖区", "天山区", "沙依巴克区", "新市区", "水磨沟区", "头屯河区", "达坂城区", "米东区", "乌鲁木齐县"]
+	}, {
+		"name": "克拉玛依市",
+		"area": ["市辖区", "独山子区", "克拉玛依区", "白碱滩区", "乌尔禾区"]
+	}, {
+		"name": "吐鲁番市",
+		"area": ["高昌区", "鄯善县", "托克逊县"]
+	}, {
+		"name": "哈密市",
+		"area": ["伊州区", "巴里坤哈萨克自治县", "伊吾县"]
+	}, {
+		"name": "昌吉回族自治州",
+		"area": ["昌吉市", "阜康市", "呼图壁县", "玛纳斯县", "奇台县", "吉木萨尔县", "木垒哈萨克自治县"]
+	}, {
+		"name": "博尔塔拉蒙古自治州",
+		"area": ["博乐市", "阿拉山口市", "精河县", "温泉县"]
+	}, {
+		"name": "巴音郭楞蒙古自治州",
+		"area": ["库尔勒市", "轮台县", "尉犁县", "若羌县", "且末县", "焉耆回族自治县", "和静县", "和硕县", "博湖县"]
+	}, {
+		"name": "阿克苏地区",
+		"area": ["阿克苏市", "温宿县", "库车县", "沙雅县", "新和县", "拜城县", "乌什县", "阿瓦提县", "柯坪县"]
+	}, {
+		"name": "克孜勒苏柯尔克孜自治州",
+		"area": ["阿图什市", "阿克陶县", "阿合奇县", "乌恰县"]
+	}, {
+		"name": "喀什地区",
+		"area": ["喀什市", "疏附县", "疏勒县", "英吉沙县", "泽普县", "莎车县", "叶城县", "麦盖提县", "岳普湖县", "伽师县", "巴楚县",
+			"塔什库尔干塔吉克自治县"
+		]
+	}, {
+		"name": "和田地区",
+		"area": ["和田市", "和田县", "墨玉县", "皮山县", "洛浦县", "策勒县", "于田县", "民丰县"]
+	}, {
+		"name": "伊犁哈萨克自治州",
+		"area": ["伊宁市", "奎屯市", "霍尔果斯市", "伊宁县", "察布查尔锡伯自治县", "霍城县", "巩留县", "新源县", "昭苏县", "特克斯县", "尼勒克县"]
+	}, {
+		"name": "塔城地区",
+		"area": ["塔城市", "乌苏市", "额敏县", "沙湾县", "托里县", "裕民县", "和布克赛尔蒙古自治县"]
+	}, {
+		"name": "阿勒泰地区",
+		"area": ["阿勒泰市", "布尔津县", "富蕴县", "福海县", "哈巴河县", "青河县", "吉木乃县"]
+	}, {
+		"name": "直辖县",
+		"area": ["石河子市", "阿拉尔市", "图木舒克市", "五家渠市", "铁门关市"]
+	}]
+}, {
+	"name": "香港特别行政区",
+	"city": [{
+		"name": "香港特别行政区",
+		"area": ["中西区", "东区", "九龙城区", "观塘区", "南区", "深水埗区", "湾仔区", "黄大仙区", "油尖旺区", "离岛区", "葵青区", "北区", "西贡区",
+			"沙田区", "屯门区", "大埔区", "荃湾区", "元朗区"
+		]
+	}]
+}, {
+	"name": "澳门特别行政区",
+	"city": [{
+		"name": "澳门特别行政区",
+		"area": ["澳门半岛", "凼仔", "路凼城", "路环"]
+	}]
+}, {
+	"name": "台湾",
+	"city": [{
+		"name": "彰化县",
+		"area": ["芳苑乡", "芬园乡", "福兴乡", "和美镇", "花坛乡", "鹿港镇", "埤头乡", "埔心乡", "埔盐乡", "伸港乡", "社头乡", "田尾乡", "田中镇",
+			"线西乡", "溪湖镇", "秀水乡", "溪州乡", "永靖乡", "员林市", "竹塘乡"
+		]
+	}, {
+		"name": "新北市",
+		"area": ["八里区", "板桥区", "贡寮区", "金山区", "林口区", "芦洲区", "坪林区", "平溪区", "瑞芳区", "三重区", "三峡区", "三芝区", "深坑区",
+			"石碇区", "石门区", "双溪区", "树林区", "泰山区", "淡水区", "土城区"
+		]
+	}, {
+		"name": "澎湖县",
+		"area": ["白沙乡", "湖西乡", "马公市", "七美乡", "望安乡", "西屿乡"]
+	}, {
+		"name": "屏东县",
+		"area": ["三地门乡", "狮子乡", "泰武乡", "万丹乡", "万峦乡", "雾臺乡", "新埤乡", "新园乡", "盐埔乡", "竹田乡", "长治乡", "潮州镇", "车城乡",
+			"春日乡", "东港镇", "枋寮乡", "枋山乡", "高树乡", "恆春镇", "佳冬乡"
+		]
+	}, {
+		"name": "臺中市",
+		"area": ["梧栖区", "乌日区", "新社区", "西屯区", "北屯区", "中区", "大肚区", "大甲区", "大里区", "大雅区", "大安区", "东势区", "东区",
+			"丰原区", "和平区", "后里区", "龙井区", "南屯区", "北区", "清水区"
+		]
+	}, {
+		"name": "臺南市",
+		"area": ["佳里区", "将军区", "六甲区", "柳营区", "龙崎区", "麻豆区", "南化区", "楠西区", "北区", "七股区", "仁德区", "善化区", "山上区",
+			"南区", "中西区", "下营区", "西港区", "新化区", "新市区", "新营区"
+		]
+	}, {
+		"name": "臺北市",
+		"area": ["北投区", "大同区", "大安区", "南港区", "内湖区", "士林区", "松山区", "万华区", "文山区", "信义区", "中山区", "中正区"]
+	}, {
+		"name": "臺东县",
+		"area": ["卑南乡", "长滨乡", "成功镇", "池上乡", "达仁乡", "大武乡", "东河乡", "关山镇", "海端乡", "金峰乡", "兰屿乡", "绿岛乡", "鹿野乡",
+			"太麻里乡", "臺东市", "延平乡"
+		]
+	}, {
+		"name": "桃园市",
+		"area": ["八德区", "大溪区", "大园区", "復兴区", "观音区", "龟山区", "龙潭区", "芦竹区", "平镇区", "桃园区", "新屋区", "杨梅区", "中坜区"]
+	}, {
+		"name": "宜兰县",
+		"area": ["大同乡", "钓鱼臺", "冬山乡", "礁溪乡", "罗东镇", "南澳乡", "三星乡", "苏澳镇", "头城镇", "五结乡", "宜兰市", "员山乡", "壮围乡"]
+	}, {
+		"name": "南投县",
+		"area": ["草屯镇", "国姓乡", "集集镇", "鹿谷乡", "名间乡", "南投市", "埔里镇", "仁爱乡", "水里乡", "信义乡", "鱼池乡", "中寮乡", "竹山镇"]
+	}, {
+		"name": "南海岛",
+		"area": ["东沙群岛", "南沙群岛"]
+	}, {
+		"name": "苗栗县",
+		"area": ["头屋乡", "西湖乡", "苑里镇", "造桥乡", "竹南镇", "卓兰镇", "大湖乡", "公馆乡", "后龙镇", "苗栗市", "南庄乡", "三湾乡", "三义乡",
+			"狮潭乡", "泰安乡", "铜锣乡", "通霄镇", "头份市"
+		]
+	}, {
+		"name": "嘉义市",
+		"area": ["东区", "西区"]
+	}, {
+		"name": "嘉义县",
+		"area": ["阿里山乡", "布袋镇", "大林镇", "大埔乡", "东石乡", "番路乡", "六脚乡", "鹿草乡", "梅山乡", "民雄乡", "朴子市", "水上乡", "太保市",
+			"溪口乡", "新港乡", "义竹乡", "中埔乡", "竹崎乡"
+		]
+	}, {
+		"name": "新竹市",
+		"area": ["东区", "北区"]
+	}, {
+		"name": "新竹县",
+		"area": ["峨眉乡", "关西镇", "横山乡", "湖口乡", "尖石乡", "芎林乡", "五峰乡", "新丰乡", "新埔镇", "竹北市", "竹东镇", "宝山乡", "北埔乡"]
+	}, {
+		"name": "花莲县",
+		"area": ["卓溪乡", "丰滨乡", "凤林镇", "富里乡", "光復乡", "花莲市", "吉安乡", "瑞穗乡", "寿丰乡", "万荣乡", "新城乡", "秀林乡", "玉里镇"]
+	}, {
+		"name": "高雄市",
+		"area": ["阿莲区", "大寮区", "大社区", "大树区", "凤山区", "冈山区", "鼓山区", "湖内区", "甲仙区", "苓雅区", "林园区", "六龟区", "路竹区",
+			"茂林区", "美浓区", "弥陀区", "那玛夏区", "楠梓区", "内门区", "鸟松区"
+		]
+	}, {
+		"name": "基隆市",
+		"area": ["安乐区", "暖暖区", "七堵区", "仁爱区", "信义区", "中山区", "中正区"]
+	}, {
+		"name": "金门县",
+		"area": ["金城镇", "金湖镇", "金宁乡", "金沙镇", "烈屿乡", "乌坵乡"]
+	}, {
+		"name": "连江县",
+		"area": ["北竿乡", "东引乡", "莒光乡", "南竿乡"]
+	}, {
+		"name": "云林县",
+		"area": ["褒忠乡", "北港镇", "莿桐乡", "大埤乡", "东势乡", "斗六市", "斗南镇", "二崙乡", "古坑乡", "虎尾镇", "口湖乡", "林内乡", "崙背乡",
+			"麦寮乡", "水林乡", "四湖乡", "臺西乡", "土库镇", "西螺镇", "元长乡"
+		]
+	}]
+}]

+ 103 - 0
components/wangding-pickerAddress/wangding-pickerAddress.vue

@@ -0,0 +1,103 @@
+<template>
+	<picker @change="bindPickerChange" @columnchange="columnchange" :range="array" range-key="name" :value="value" mode="multiSelector">
+		<slot></slot>
+	</picker>
+</template>
+
+<script>
+	import AllAddress from './data.js'
+	let selectVal = ['','',''];
+	
+	export default {
+		data() {
+			return{
+				value: [0,0,0],
+				array: [],
+				index: 0
+			}
+		},
+		created() {
+			this.initSelect()
+		},
+		methods:{
+			// 初始化地址选项
+			initSelect() {
+				this.updateSourceDate() // 更新源数据
+				.updateAddressDate() // 更新结果数据
+				.$forceUpdate()  // 触发双向绑定
+			},
+			// 地址控件改变控件
+			columnchange(d) {
+				this.updateSelectIndex(d.detail.column, d.detail.value) // 更新选择索引
+				.updateSourceDate() // 更新源数据
+				.updateAddressDate() // 更新结果数据
+				.$forceUpdate()  // 触发双向绑定
+			},
+			
+			/**
+			 * 更新源数据
+			 * */
+			updateSourceDate() {
+				this.array = []
+				this.array[0] = AllAddress.map(obj => {
+					return {
+						name: obj.name
+					}
+				})
+				this.array[1] = AllAddress[this.value[0]].city.map(obj => {
+					return {
+						name: obj.name
+					}
+				})
+				this.array[2] = AllAddress[this.value[0]].city[this.value[1]].area.map(obj => { 
+					return {
+						name: obj
+					}
+				})
+				return this
+			},
+			
+			/**
+			 * 更新索引
+			 * */
+			updateSelectIndex(column, value){
+				let arr = JSON.parse(JSON.stringify(this.value)) 
+				arr[column] = value
+				if(column === 0 ) {
+					arr[1] = 0
+					arr[2] = 0
+				}
+				if(column === 1 ) {
+					arr[2] = 0
+				}
+				this.value = arr
+				return this
+			},
+			
+			/**
+			 * 更新结果数据 
+			 * */
+			updateAddressDate() {
+				selectVal[0] = this.array[0][this.value[0]].name
+				selectVal[1] = this.array[1][this.value[1]].name 
+				selectVal[2] = this.array[2][this.value[2]].name 
+				return this
+			},
+			
+			/**
+			 * 点击确定
+			 * */
+			bindPickerChange(e) {
+				this.$emit('change', {
+					index: this.value,
+					data: selectVal
+				})
+				return this
+			}
+			
+		}
+	}
+</script>
+
+<style>
+</style>

+ 18 - 0
config/app.js

@@ -0,0 +1,18 @@
+module.exports = {
+	// 请求域名 格式: https://您的域名
+	
+	HTTP_REQUEST_URL:'http://base.liuniu946.com',
+
+	
+	
+	// #ifdef H5
+	// HTTP_REQUEST_URL: window.location.protocol+"//"+window.location.host,
+	// #endif
+	HEADER:{
+		'content-type': 'application/json'
+	},
+	// 回话密钥名称 请勿修改此配置
+	TOKENNAME: 'Authori-zation',
+	// 缓存时间 0 永久
+	EXPIRE:0,
+};

+ 32 - 0
config/cache.js

@@ -0,0 +1,32 @@
+module.exports = {
+	//token
+	LOGIN_STATUS: 'LOGIN_STATUS_TOKEN',
+	// uid
+	UID:'UID',
+	//�û�
+	USER_INFO: 'USER_INFO',
+	//token�����¼�
+	EXPIRES_TIME: 'EXPIRES_TIME',
+	//�Ƿ���Ȩ
+	WX_AUTH: 'WX_AUTH',
+	//���ں���Ȩcode
+	STATE_KEY: 'wx_authorize_state',
+	//�û�����
+	LOGINTYPE: 'loginType',
+	//���ں���ת����
+	BACK_URL: 'login_back_url',
+	// ����code
+	STATE_R_KEY: 'roution_authorize_state',
+	//��ȨlogoС����
+	LOGO_URL: 'LOGO_URL',
+	//模板缓存
+	SUBSCRIBE_MESSAGE: 'SUBSCRIBE_MESSAGE',
+
+	TIPS_KEY: 'TIPS_KEY',
+
+	SPREAD: 'spread',
+	//缓存经度
+	CACHE_LONGITUDE: 'LONGITUDE',
+	//缓存纬度
+	CACHE_LATITUDE: 'LATITUDE',
+}

+ 39 - 0
libs/log.js

@@ -0,0 +1,39 @@
+
+const logLength=100;//缓存存储上限
+const name = 'log';//缓存名字
+export function addLog (data,content='') {
+	let log = uni.getStorageSync(name)||[];
+	log.unshift({
+		title:data,
+		content:content
+	});
+	uni.setStorageSync(name,log);
+	initLog(log);
+}
+
+
+export function delLog () {
+	return uni.setStorageSync(name,'');
+}
+
+export function getLog () {
+	return uni.getStorageSync(name);
+}
+
+export function initLog (log) {
+	if(log.length>logLength){
+		const newarr = log.slice(log.length-logLength);
+		uni.setStorageSync(name,newarr);
+	}
+}
+export function showLog (log) {
+	
+	let str = '';
+	uni.getStorageSync(name).forEach((e) => {
+		str+=e.title+':'+JSON.stringify(e.content)
+	})
+	uni.showModal({
+		title:"日志",
+		content:str
+	})
+}

+ 84 - 0
libs/login.js

@@ -0,0 +1,84 @@
+import store from "../store";
+import Cache from '../utils/cache';
+// #ifdef H5 || APP-PLUS
+import {
+	isWeixin
+} from "../utils";
+import auth from './wechat';
+// #endif
+
+import {
+	LOGIN_STATUS,
+	USER_INFO,
+	EXPIRES_TIME,
+	STATE_R_KEY
+} from './../config/cache';
+
+function prePage() {
+	let pages = getCurrentPages();
+	let prePage = pages[pages.length - 2];
+	// #ifdef H5
+	return prePage;
+	// #endif
+	return prePage.$vm;
+}
+
+export function toLogin(push, pathLogin) {
+	// store.commit("LOGOUT");
+	let path = prePage();
+	if (path) {
+		path = path.router;
+		if (path == undefined) {
+			path = location.pathname;
+		}
+	}
+	// #ifdef H5
+	else {
+		path = location.pathname;
+	}
+	// #endif
+
+	if (!pathLogin)
+		pathLogin = '/page/users/login/index'
+	Cache.set('login_back_url', path);
+	// #ifdef H5 || APP-PLUS
+	if (isWeixin()) {
+		auth.oAuth();
+	} else {
+		if (path !== pathLogin) {
+			push ? uni.navigateTo({
+				url: '/pages/users/login/index'
+			}) : uni.reLaunch({
+				url: '/pages/users/login/index'
+			});
+		}
+	}
+	// #endif
+
+	// #ifdef MP 
+
+
+	// #endif
+}
+
+
+export function checkLogin() {
+	let token = Cache.get(LOGIN_STATUS);
+	let expiresTime = Cache.get(EXPIRES_TIME);
+	let newTime = Math.round(new Date() / 1000);
+	if (expiresTime < newTime || !token) {
+		Cache.clear(LOGIN_STATUS);
+		Cache.clear(EXPIRES_TIME);
+		Cache.clear(USER_INFO);
+		Cache.clear(STATE_R_KEY);
+		return false;
+	} else {
+		store.commit('UPDATE_LOGIN', token);
+		let userInfo = Cache.get(USER_INFO, true);
+		if (userInfo) {
+			store.commit('UPDATE_USERINFO', userInfo);
+		}
+		return true;
+	}
+
+}

+ 253 - 0
libs/wechat.js

@@ -0,0 +1,253 @@
+// #ifdef H5
+import WechatJSSDK from "@/plugin/jweixin-module/index.js";
+// #endif
+
+import {
+	wechatConfig,
+	wechatAuth
+} from "@/api/wx.js";
+import {
+	WX_AUTH,
+	STATE_KEY,
+	LOGINTYPE,
+	BACK_URL
+} from '@/config/cache';
+import {
+	parseQuery
+} from '@/utils';
+import store from '@/store';
+import Cache from '@/utils/cache';
+
+class AuthWechat {
+	// #ifdef H5
+	constructor() {
+		//微信实例化对象
+		this.instance = WechatJSSDK;
+		//是否实例化
+		this.status = false;
+
+		this.initConfig = {};
+
+	}
+	// #endif
+	
+	isAndroid(){
+		let u = navigator.userAgent;
+		return u.indexOf('Android') > -1 || u.indexOf('Adr') > -1;
+	}
+
+	/**
+	 * 初始化wechat(分享配置)
+	 */
+	wechat() {
+		return new Promise((resolve, reject) => {
+			// if (this.status && !this.isAndroid()) return resolve(this.instance);
+			wechatConfig()
+				.then(res => {
+					this.instance.config(res.data);
+					this.initConfig = res.data;
+					this.status = true;
+					this.instance.ready(() => {
+						resolve(this.instance);
+					})
+				}).catch(err => {
+					console.log(err);
+					this.status = false;
+					reject(err);
+				});
+		});
+	}
+
+	/**
+	 * 验证是否初始化
+	 */
+	verifyInstance() {
+		let that = this;
+		return new Promise((resolve, reject) => {
+			if (that.instance === null && !that.status) {
+				that.wechat().then(res => {
+					resolve(that.instance);
+				}).catch(() => {
+					return reject();
+				})
+			} else {
+				return resolve(that.instance);
+			}
+		})
+	}
+	// 微信公众号的共享地址
+	openAddress() {
+		return new Promise((resolve, reject) => {
+			this.wechat().then(wx => {
+				this.toPromise(wx.openAddress).then(res => {
+					resolve(res);
+				}).catch(err => {
+					reject(err);
+				});
+			}).catch(err => {
+				reject(err);
+			})
+		});
+	}
+
+	/**
+	 * 微信支付
+	 * @param {Object} config
+	 */
+	pay(config) {
+		return new Promise((resolve, reject) => {
+			this.wechat().then((wx) => {
+				this.toPromise(wx.chooseWXPay, config).then(res => {
+					resolve(res);
+				}).catch(res => {
+					reject(res);
+				});
+			}).catch(res => {
+				reject(res);
+			});
+		});
+	}
+
+	toPromise(fn, config = {}) {
+		return new Promise((resolve, reject) => {
+			fn({
+				...config,
+				success(res) {
+					resolve(res);
+				},
+				fail(err) {
+					reject(err);
+				},
+				complete(err) {
+					reject(err);
+				},
+				cancel(err) {
+					reject(err);
+				}
+			});
+		});
+	}
+
+	/**
+	 * 自动去授权
+	 */
+	oAuth() {
+		if (uni.getStorageSync(WX_AUTH) && store.state.app.token) return;
+		const {
+			code
+		} = parseQuery();
+		if (!code) return this.toAuth();
+	}
+
+	clearAuthStatus() {
+
+	}
+
+	/**
+	 * 授权登陆获取token
+	 * @param {Object} code
+	 */
+	auth(code) {
+		return new Promise((resolve, reject) => {
+			let loginType = Cache.get(LOGINTYPE);
+			wechatAuth(code, parseInt(Cache.get("spread")), loginType)
+				.then(({
+					data
+				}) => {
+					let expires_time = data.expires_time.substring(0, 19);
+					expires_time = expires_time.replace(/-/g, '/');
+					expires_time = new Date(expires_time).getTime();
+					let newTime = Math.round(new Date() / 1000);
+					store.commit("LOGIN", {
+						token: data.token,
+						time: expires_time - newTime
+					});
+					Cache.set(WX_AUTH, code);
+					Cache.clear(STATE_KEY);
+					loginType && Cache.clear(LOGINTYPE);
+					resolve();
+				})
+				.catch(reject);
+		});
+	}
+
+	/**
+	 * 获取跳转授权后的地址
+	 * @param {Object} appId
+	 */
+	getAuthUrl(appId) {
+		const redirect_uri = encodeURIComponent(
+			`${location.origin}/pages/auth/index?back_url=` +
+			encodeURIComponent(
+				encodeURIComponent(
+					uni.getStorageSync(BACK_URL) ?
+					uni.getStorageSync(BACK_URL) :
+					location.pathname + location.search
+				)
+			)
+		);
+		uni.removeStorageSync(BACK_URL);
+		const state = encodeURIComponent(
+			("" + Math.random()).split(".")[1] + "authorizestate"
+		);
+		uni.setStorageSync(STATE_KEY, state);
+		return `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirect_uri}&response_type=code&scope=snsapi_userinfo&state=${state}#wechat_redirect`;
+	}
+
+	/**
+	 * 跳转自动登陆
+	 */
+	toAuth() {
+		let that = this;
+		this.wechat().then(wx => {
+			location.href = this.getAuthUrl(that.initConfig.appId);
+		})
+	}
+
+	/**
+	 * 绑定事件
+	 * @param {Object} name 事件名
+	 * @param {Object} config 参数
+	 */
+	wechatEvevt(name, config) {
+		let that = this;
+		return new Promise((resolve, reject) => {
+			let configDefault = {
+				fail(res) {
+					console.log(res,11111);
+					if (that.instance) return reject({
+						is_ready: true,
+						wx: that.instance
+					});
+					that.verifyInstance().then(wx => {
+						return reject({
+							is_ready: true,
+							wx: wx
+						});
+					})
+				},
+				success(res) {
+					return resolve(res,2222);
+				}
+			};
+			Object.assign(configDefault, config);
+			that.wechat().then(wx => {
+				if (typeof name === 'object') {
+					name.forEach(item => {
+						wx[item] && wx[item](configDefault)
+					})
+				} else {
+					wx[name] && wx[name](configDefault)
+				}
+			})
+		});
+	}
+
+	isWeixin() {
+		return navigator.userAgent.toLowerCase().indexOf("micromessenger") !== -1;
+	}
+
+}
+
+export default new AuthWechat();
+

+ 44 - 0
main.js

@@ -0,0 +1,44 @@
+import Vue from 'vue'
+import store from './store'
+import App from './App'
+/**
+ *  所有测试用数据均存放于根目录json.js
+ *  
+ *  css部分使用了App.vue下的全局样式和iconfont图标,有需要图标库的可以留言。
+ *  示例使用了uni.scss下的变量, 除变量外已尽量移除特有语法,可直接替换为其他预处理器使用
+ */
+const msg = (title, duration=1500, mask=false, icon='none')=>{
+	//统一提示方便全局修改
+	if(Boolean(title) === false){
+		return;
+	}
+	uni.showToast({
+		title,
+		duration,
+		mask,
+		icon
+	});
+}
+
+const prePage = ()=>{
+	// 获取当前页面
+	let pages = getCurrentPages();
+	let prePage = pages[pages.length - 2];
+	// #ifdef H5
+	return prePage;
+	// #endif
+	return prePage.$vm;
+}
+
+
+Vue.config.productionTip = false
+Vue.prototype.$fire = new Vue();
+Vue.prototype.$store = store;
+Vue.prototype.$api = {msg, prePage};
+
+App.mpType = 'app'
+
+const app = new Vue({
+    ...App
+})
+app.$mount()

+ 87 - 0
manifest.json

@@ -0,0 +1,87 @@
+{
+    "name" : "jingcai",
+    "appid" : "__UNI__E0158B1",
+    "description" : "",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    "app-plus" : {
+        /* 5+App特有相关 */
+        "usingComponents" : true,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        "modules" : {},
+        /* 模块配置 */
+        "distribute" : {
+            /* 应用发布信息 */
+            "android" : {
+                /* android打包配置 */
+                "permissions" : [
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>",
+                    "<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>"
+                ],
+                "abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ]
+            },
+            "ios" : {
+                "idfa" : false
+            },
+            /* ios打包配置 */
+            "sdkConfigs" : {
+                "maps" : {},
+                "oauth" : {
+                    "weixin" : {
+                        "appid" : "",
+                        "appsecret" : "",
+                        "UniversalLinks" : ""
+                    }
+                }
+            }
+        }
+    },
+    /* SDK配置 */
+    "quickapp" : {},
+    /* 快应用特有相关 */
+    "mp-weixin" : {
+        /* 小程序特有相关 */
+        "usingComponents" : true,
+        "appid" : "",
+        "setting" : {
+            "urlCheck" : true,
+            "minified" : true
+        }
+    },
+    "h5" : {
+        "title" : "",
+        "domain" : "",
+        "router" : {
+            "base" : "/index/",
+            "mode" : "hash"
+        },
+        "devServer" : {
+            "proxy" : {
+                "/api" : {
+					// kline.ns.liuniu946.com
+                    "target" : "http://kline.ns.liuniu946.com/api/",
+                    // "changeOrigin": true,
+                    "pathRewrite" : {
+                        "/api" : "" // rewrite path
+                    }
+                }
+            }
+        }
+    }
+}

+ 34 - 0
node_modules/.package-lock.json

@@ -0,0 +1,34 @@
+{
+  "name": "chonggou",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "node_modules/echarts": {
+      "version": "5.4.3",
+      "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.4.3.tgz",
+      "integrity": "sha512-mYKxLxhzy6zyTi/FaEbJMOZU1ULGEQHaeIeuMR5L+JnJTpz+YR03mnnpBhbR4+UYJAgiXgpyTVLffPAjOTLkZA==",
+      "dependencies": {
+        "tslib": "2.3.0",
+        "zrender": "5.4.4"
+      }
+    },
+    "node_modules/tslib": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
+      "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
+    },
+    "node_modules/uqrcodejs": {
+      "version": "4.0.7",
+      "resolved": "https://registry.npmmirror.com/uqrcodejs/-/uqrcodejs-4.0.7.tgz",
+      "integrity": "sha512-84+aZmD2godCVI+93lxE3YUAPNY8zAJvNA7xRS7R7U+q57KzMDepBSfNCwoRUhWOfR6eHFoAOcHRPwsP6ka1cA=="
+    },
+    "node_modules/zrender": {
+      "version": "5.4.4",
+      "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.4.4.tgz",
+      "integrity": "sha512-0VxCNJ7AGOMCWeHVyTrGzUgrK4asT4ml9PEkeGirAkKNYXYzoPJCLvmyfdoOXcjTHPs10OZVMfD1Rwg16AZyYw==",
+      "dependencies": {
+        "tslib": "2.3.0"
+      }
+    }
+  }
+}

+ 361 - 0
node_modules/echarts/KEYS

@@ -0,0 +1,361 @@
+This file contains the PGP keys of various developers.
+Please don't use them for email unless you have to. Their main
+purpose is code signing.
+
+Examples of importing this file in your keystore:
+ gpg --import KEYS.txt
+ (need pgp and other examples here)
+
+Examples of adding your key to this file:
+ pgp -kxa <your name> and append it to this file.
+ (pgpk -ll <your name> && pgpk -xa <your name>) >> this file.
+ (gpg --list-sigs <your name>
+     && gpg --armor --export <your name>) >> this file.
+
+---------------------------------------
+pub   rsa4096 2018-04-23 [SC]
+      9B06D9B4FA37C4DD52725742747985D7E3CEB635
+uid           [ultimate] Su Shuang (CODE SIGNING KEY) <sushuang@apache.org>
+sig 3        747985D7E3CEB635 2018-04-23  Su Shuang (CODE SIGNING KEY) <sushuang@apache.org>
+sub   rsa4096 2018-04-23 [E]
+sig          747985D7E3CEB635 2018-04-23  Su Shuang (CODE SIGNING KEY) <sushuang@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQINBFrd5SYBEADoCBw12lsK1sxn3r879jI50GhRAg5vF0aBql0h2BIJ3d+oYYSm
+nIsK/XGpIk3t6ZhJRXK+le89t8a7vBsU+y0+3+OehxOV63du1wscQU9GPu7IfXhw
+V4YcsGK330+V/GiwBs3EX808fdQrdkfCsaGEJhKJbK2fldUcnNp3M1Y2+DVZqGmb
+I7fRJuEj/S9bcVGWnv40jBbMKjx/8LyP2dxZLyy1+whEUimU9em6Tj+SnyISe1I2
+sLa3lwhWer0rkrz0siGFTgDHaDvLlpL9TV34acj/FOon3XKMtx4neNVmkC3QVi0z
+PSlnX6EV8Fas9ylA4x9bdaUo6zUZKO533ASfC6uEibvE2XSRXYJ0xB2bThcQbkdl
+332JqD1TkyF/UQRel3pUm/bCsv2daKD98ZO+eCbvNNonrip2qXDwJJ5HzlXlThyR
+eN1Og90gXvYix4sbsZgNEIyYSaLri7/GjyMD34GCLQiV/kvc/foaC/hkvz6kVOiq
+/tMHY3KsGYAIF4Z9kuTCwJOwFqgfb+Y15bPRDK84uyCiRhtIubNWY7Euy4bBd3ul
+uazQ9LabBhZaa7HCOMssW+TaB+GondZJTiwnI6MCTJKrKtvb8kzcKR4mNf/dvF0O
+x7zwVBeklMKXjkpOtje/+/XOYKuD3g1BZ/+vrfMFPTZ7y7ASC2ylcKI0/QARAQAB
+tDJTdSBTaHVhbmcgKENPREUgU0lHTklORyBLRVkpIDxzdXNodWFuZ0BhcGFjaGUu
+b3JnPokCTgQTAQoAOBYhBJsG2bT6N8TdUnJXQnR5hdfjzrY1BQJa3eUmAhsDBQsJ
+CAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEHR5hdfjzrY13yIP+wS+Mh86IuIK+zG5
+qr/cncV541RxvIGbv5uCQEbFRIwtR8SJEyx2tu4pIgsaTu93hdwxHFCcOZT2IsXP
+meRWPfhaguDFQArdu4VdOfq2AbMqqByFWRsbwvF8CX8fGMPBCsMp0pzqp0px1uUr
+WlK5hBSVwDHWACElyJE7jmk5K+O7RmDUD2E/pgXid+SiU8W+k9vWj49nHAhStYTm
+SwVQA4Gl7jGCJY5jFwZIRD5/b8kVYjbJFl9CBDD2nOIytrGfMVlhp2OcT1f6yZvZ
+oY2nvWLBUF0SmQzlli3EW9zzsNAXDu3f81kqwa+kC2WqQ3s4bKZKQurN5sCWvoyX
+db+AWedArK+m3fH9y3JFIr5Lu1MwfbgfMfm9EZS4A+3DqLFIsLrmnzbGZ9FCkqsj
+TuvKWOP2H365xH44gHImYKZ92PDdLKE7XArVU5b9qtAimgCDsCjEiXTB4S3NVJGX
+R0RZCttKgnrLHwAad3TeLhktWcjH4TdxNCrNZsHLO9mklGyeM1IxKqba4OdHTmYX
+tYYlixSlAu5vSPa+vDkILRfyU87n9YD9RiVGmvy27IP7wdxSClJun6+9fviU2NpG
+FCkLZovYz8/Qht1c8yQZGscw3sa316m1nJz42Lo+p2s6AQZhZupu8bi/W85VHoxa
+roRO16i+mFr4bnbo2/jftB6UVVo7uQINBFrd5SYBEACVsgwBHz5cpBqZQVNS6o0W
+RUnWWNDiBYidNQNTWCF9NDF0HCh6oHecjjXQEPduvMPdzOPpawAkKMRG+7MlHiu/
+ugAq0RluoM3QzDZwvCPw+p/NTESZMqLvbHXEs2u6YCdIsFcTLXr2d+JBWDeGri0S
+YB4gjjQIVvDGqG0tDoW4JmqHHMZiJ6c+h2Rq+saHte0rctHcVAq4p5I8O1iJ1Mkg
+gKJ/TBsjPM5aK6ahPpIPPh48nbhpsLjKHwqB/UWdUcB/HUDa0YfV4JbJilEeeQFZ
+PzlP5SJaGyuEnTnhEwnoXpFetfMYi+Mxnc4VoSrQ3UOsVpD2Ii3haUjdKWTjukyn
+o3sCxvsBTQ8jyBtjjhLw1jfWJdHJ2WCDGVtQVuJ6Gx1GCV0XRbKDTWdIBnCkdKtU
+FY+VMt77oQ/ydeRsZDXhkdgBqqkvdiRHRyEFy72rx61cGTIKuKcWu0rJx8/LnVyi
+nOEk8K8mgNR8omnpFmkkStOtSDLjDb8WeIdigxwJ4wtQnLlLGWiAAVNnDDsqgGIB
+3rrR+/HKUa05CwKI1oIC7i4f7qkgfFUjjr1e496FDSq2tBTLukq/v5FpU6C0JSVq
+MeD5+UuGtSezBxQUdxV7caftIptopwWnx4bBjWSuk2FVCzWcYMnXNIbtfEbqMKuS
+mrpk4mOBNAV6XYzNcOHQqwARAQABiQI2BBgBCgAgFiEEmwbZtPo3xN1ScldCdHmF
+1+POtjUFAlrd5SYCGwwACgkQdHmF1+POtjXK4g//c7vJXmN0FtACspBJVrgsKrYj
+ha4c2PCEynfKSwhVXW3yHnQMwh8/bpQUs5bwCTWx27IEeBrfb03/X9tlx12koGvl
+LujaR7IP6xaqWpbh6rrfttOKGx3xKopJ4nHgNPIYN/ApflAacwyOd+/leWOjHrii
+JXbB60oc7FNvfQRREICLZyeAnzlAcEOVcWvBTngB0EDUZucKwkQtt0x3YvKetgQf
+EMFBAH4RUXG0ms85acX2rpi/kbdarFv6Hc2pzakoWDKNjHMMae1J8wQbPRaXx1NB
++xF362eLXZaxtvKdzs9Q03R46DY9cyQRofG5WNnZapgemEzPgixur8FYK5EPCQkh
+Y2FA0WUbZFIkO7pE7UNS5ZN5fHkkEhAFo4wV0uqWRVBpFrjKeBxtRkIaw7jLCHr5
+3EpkTusjT/529rEYIq9cGOTwf75AbKR1IZFxffEZYOU76y6SH0bINoYp0VxFJ/IR
+zy5CHqvyUQVUed5O/7UzkYx0IVBGk2wSwOtC7+iRptqj+kI9RCjGizhNe4hG3SUq
+1qkUGkQu6+skyXeFCR1PIAbQgleRNUQotsh/rfsfZpQOomBdvDRPT8ZcN5bjUIJ1
+5c4abryWPkun+BgZk+YFtYLbGZVJAUy2OtXRG5uYzeLc5ID+X5XwwtZOO4gSWMTh
+oQH7TsthVKvdZyjtZQg=
+=Uv8d
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub   rsa4096 2019-01-24 [SC]
+      1683FBD23F6DD36C0E52223507D78F777D2C0C27
+uid           [ultimate] Ovilia (CODE SIGNING KEY) <oviliazhang@gmail.com>
+sig 3        07D78F777D2C0C27 2019-01-24  Ovilia (CODE SIGNING KEY) <oviliazhang@gmail.com>
+sub   rsa4096 2019-01-24 [E]
+sig          07D78F777D2C0C27 2019-01-24  Ovilia (CODE SIGNING KEY) <oviliazhang@gmail.com>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQINBFxJWEYBEADYzZRcG+WIllHo8PloMv9pX2QZxmZiVJzM7Prgg8KlWfHnO68/
+7Et//hMA2zexJWweZwM0ffmjvcIIEre23De6KaA2htM/54aPoBweDAOBi34RsdR9
+kpN0RvipvJMMZKGB0tDSB3mLhWaiApDGMsysfJAgTaGsIISrC2+xLO/+HxgoEAIX
+a0BTJ+P3cOLPghBBaRtyKNWJjJ2e4XzlVM0T4bM06QmzC0qWTSufKqk1XAZTSOGU
+LXYESonSu/+kL2TCsKi90THNX69a9SBx3DAohbb5WKjXkYistSQi9S33jqZMIc7n
+I1kG1x39YxZiQwwszwbfa3/+qE3X0Qjp2k3fD7wa+qDnSpHTchqy8d71EN0wU6S/
+9vEiJ2e+gxN6WZetK9wl90P70Iu0rvLqSu+5EdkenvIbh6i4CR+Cer1Sky2z7rEY
+vmEjFNjV2ktvbu83RDofxp4ERSbZOwq8VMOWqj6Ft9mIWfw1OAoSkLCRchYFR1ue
+r+e3FuF01KlCXjTV4t24F7l5QO/bwexnmYuVTlSEo4PVZLJAv/UYSP0ngie5DawL
+z2RDCuRrROgtzcf84SaRxwcPNQ0h6EZlKZ4NFL7nl4rwbDsyZRdBqzQ5JPm6dbGe
+CZXCBA84ivcnK845flcsl7ITNjcfsLbeN9s6FMnYZgOHZh/ucmw2dL+5vQARAQAB
+tDFPdmlsaWEgKENPREUgU0lHTklORyBLRVkpIDxvdmlsaWF6aGFuZ0BnbWFpbC5j
+b20+iQJOBBMBCAA4FiEEFoP70j9t02wOUiI1B9ePd30sDCcFAlxJWEYCGwMFCwkI
+BwIGFQoJCAsCBBYCAwECHgECF4AACgkQB9ePd30sDCcgHA//be3mdnRU+jYCP3VU
+l/pcYnbxoIfAhf1Z2orVcN3/E6v2wDYvbvcV7EX/cqwMXBc0/CEVisGQ3zX5CM4/
+C/vwjAsPNPWsX8iyE/Mui/Ktl9tZqQ3/8hTOHe5RQIn0VQ5wIYmyh3Q42BI4vKK3
+BodV9PwONdRhQVJ15x1fp59wiPTqflcXJ0qdGml3JY4ULLFYh63MBV4as6pg/Qtb
+1enZmw8/Bgg6mhY6HiBI+v+8wAwdatwYuG33JdzhoPVbjsnovqAE+kMvOuxmVbK/
+q5dwdwFULbyHzojNAj7zg1zjtksawP8Uspc02JHr16pW3u48E2/uk6XCkTpFDJ09
+xqwtZyEGSobl/9BaDuidXQ9UDsrOIYuvBXO53vlVv1nwzyF7qUhNRNn1HdzIbEiV
+16CaYT5Soy4Xh5sFTFoIg0g/E8JquSgIEJN/NutqbQOHO4ldMxaDEgFp7dRJ/tqo
+CEJgahC/D16efbIUP2gVScYsJK3VYNjuEfnTu2qiR7XDXosG0zGOMGsr4xCuSx8y
+mwtrqRZdl4wfaHi2/QojJGAXwd1Q9WNBxYKuE31amAo7AxGKZ8QLZ9m0RwitG912
+yP7gsw9k/TA195GJiQ5W1qNTHa4gKXhzFtPqg7s9xhJOkb+GOk6tOCWzts1IJSXa
+oyGerp3bGP4Ho49nipEFjeiUKgW5Ag0EXElYRgEQAMbeZQMWRo9h6RgGm7eLCfz2
+K9Ro9yL0U0Jz8SmNz2I7YoYqg4idPV7D0gBym/502QsalQc427vE4QtJGlNPx8yH
+uXIKD0u9sGadO3wkz3WmPqyVMlAgdzjB9ddoWjeQDYTvJLO1eo4LtVUoSydoOs67
+bBNr9Wi2hIso60+cZGxczI+dTkqvgd+nSrhzG1+N1NPjpGqLUSvjWEZiu4NT1oVd
+4f8C6SpQNkgUbliomLE9Zv8Wkcj8RDU5je+dU8r4fKQy1GtDVGW89QXGKALwTg4F
+4/d+/qbF/ZhfZk3e6dxJV4Slmb+IKWUd5dcEYwXIdYXJuQu84CnEtsnQDsIUCc5V
+Qfk1E4SqEmc0gWsmTlsPKF51VdeDpbqQShGgt+xM65wCL7/JASnuEwr1Jt2pPRDq
+VF9s4APQJi/neuJh1A6RlHU6PFcPXmqjsglMdbfKdc0dzoOcc4OcSFPdAlX935L8
+Tlwrp2dy2ARNTSdCvbXx4Lj+Ru7tIUTjDqIFzRLBdppRU/NO6SpNMoIKkOwrjFYd
+H8nV9z6+nYHfJNR/FfT8LLx7ac/trYwDYWMJhk/h9taOszZ5OpQM4LOrWwyg2HA8
+80H95TcQ0c1/dp5OBfPSNfse75yBJrW0PwtQA3++38PHQQZVhO7J3Ha2Y9/MmLqU
+Ip+rhd38hfkHlkrwCr7tABEBAAGJAjYEGAEIACAWIQQWg/vSP23TbA5SIjUH1493
+fSwMJwUCXElYRgIbDAAKCRAH1493fSwMJ4GVD/9AS8YwflROUAodGe7jBHZ41oye
+4I8AX8iTP1qxww8ydeCBVCz3n3lvEHHP8JfVB0aJwiezUtt/1uV0bTFt9ycxyJS1
+5eIefOVN0wFEsj4pgQfBfSWxI0Yd97m+W1xg5h+aAN9W1MNH6rb1ktHCebW709Vf
+Bs+NfktKww98M134cQlmJSo1pBQEBzKaE5KEvLAiafluAPTkvafZfe+35QQdJAXx
+iLE/ZNJQ8L9lBYZaA5mM/NKNzeEqeSTwfvcIonY5sD2EsgBU/ux6QzjRV5EmteJr
+eg+bCWJnbVvZY/2LVru8NKDgfhTSMN0ocDLaWKW6aQO36TequQNdD09wasdSpQmV
+GoCydtdCVoetGdGm8SZvi6EUgAWH4eI3Su/19V8sVo3kHhJ1d575NJCFwTPvKAre
+s8wgU+7CgTojnMxFmb68p+lLe1qQheyXaa44WQ7d7hmXPIoe3EgMYtMc7tLcKccE
+upu7zWG7BNU97kpUw7nmHKalI/1fKEEAYQUmNm9mNVGKjLVNtuG8jw6Zq0vX1tP9
+mh+T3SMBEnsdzoQ+E31lIDNYTZaEHxt0XupNdjt+uEfASdrD3+8+jlWVkpO3FlZ0
+MhfLdHrk689ty11m+5HlrSU7O1I1wZkt/OlYsZmS1yIpD1hEnOuSjAuqm4D3s+YI
+B4WM8AJSCwl8WlZrRA==
+=wft0
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub   rsa4096 2020-08-06 [SC]
+      94BD178077672157652826758E44A82497382298
+uid             [ 绝对 ] Shen Yi (CODE SIGNING KEY\) <shenyi@apache.org>
+sig 3        8E44A82497382298 2020-08-06  Shen Yi (CODE SIGNING KEY\) <shenyi@apache.org>
+sub   rsa4096 2020-08-06 [E]
+sig          8E44A82497382298 2020-08-06  Shen Yi (CODE SIGNING KEY\) <shenyi@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQINBF8sDHUBEADScBNW9N9I7tu/ytMLp0XSQbyDO64iRsaAic/dnM4ffcZOl1AZ
+fbKTF2jI5ABVIl6mWBx5t8RE5XluyESfnB0au3fa0N1cb9bzjAqPiiTU5l9vF4Np
+u0517j8anqPYYk9n0HCVczaBQLavwa7ulUegnMCvO+WkrapkES3PzF/QDmHEh4iC
+FnPsayrhYvirg7Gwy6gkfkSZvp2jQIt2O3PQmffW1OsxwCf0uNIf4UrXxZ9gi6hc
+O/x1jNNpyfOBJY5es8feIsx+zQu/jZRL5AnLeuqYdODD/IdcT/AsSeFnMkIuYdKl
++S5DL23Rr5W47mCkRglauIOAFXnVd6cc3I0/TB+8+B1XOE7YBcslPytVmnc00Uwf
+f09a1WF7gTufCQAizIRShHLqSXA8Gebs42g5CLEC7k4v1Yojmwun5UFDlbxERQgj
+00hyDsGYv9Mwk5EokcpB/fyInRU0Niny6kk/siui/nvol0vcqBgwTqRJjfFByX8T
+ck11j7f3mUFq4z/PsVU4pQQpGyuiKLDQm7IJPAsJC/+s7aHAuMS/j3lpitM8j26A
+3x091RsxjfBrCusxb301rzw6F2g4bxTRueoPv9Ie8OW27uykqTgdnnCSjT5LQcN7
+H3dRmfk4UMU+QJTDhIdCzHyMnSGBVmlbbHIMIaoxnqzXFpO1+iGRQs8QcwARAQAB
+tC9TaGVuIFlpIChDT0RFIFNJR05JTkcgS0VZXCkgPHNoZW55aUBhcGFjaGUub3Jn
+PokCTgQTAQgAOBYhBJS9F4B3ZyFXZSgmdY5EqCSXOCKYBQJfLAx1AhsDBQsJCAcC
+BhUKCQgLAgQWAgMBAh4BAheAAAoJEI5EqCSXOCKYVkYP/1n0eL9d5EnDunqxo0dt
+HlfxLSx4l+edORXF+q9p0s7x33AktUZxMMNEbeAAgfrtC8sXg8bMa/NWHvmWVND7
+Qj8nJYVZ/jJSVwwXImsK6EdP8401UM1X3+z7uWy4KepJZQIVd6j8dxhW4QE74mlx
+CLBm9dK5rgxTjcNIKApscBJ6pP2eZBprHNdDW3ttaIMGBfz+nA3IpvH7ADgEkffP
+zc9BjiyCuff3q4qW1PnATJFEQCbBAxU13Y8S7pDRhHHDvuo/GNMAoKm8xWb9OzTz
+u8KistljvZWD1ZBjYxAYIKDqVyyUeH/aN134QsQyra++FFHkTiyYjpn/roSQm3Ww
+eQLXtRK0f12EpDb2pchxSrN3L4wRtzGj3I/u/7z6YXa8nuK29t8CDGTss4kBjDmQ
+2uYNAxFq6EylZU6QzaqvQgv/nhSuJFGlSY3v/4Q1MxB5rn68s2jegi/HXUIbFerf
+KgeJCN8nUtBiSIzVwMo0HMrrNyR4ZdCJa4bxzHspu6Fck4572AKxB3TNFkLYC0s+
+zOQ6b6l0bMgzH4HDj6C0k0+KtikK6Q2U1YXWu1T4MBu8Gq4weGEUDOxc0B1XywA2
+BE+cbOpjHi4lK3n1//RjUR+JL90RuD+JGCB8x2d+Ttm/c19S/KjQc8CsJ9JA5x1H
+wlHqg7br0XQQrbUedY65S6skuQINBF8sDHUBEAC99I/csLsLcrpNXB2JYh8XmtBc
+Vb6aSWCc7kowhdwuqjyXvHMkpy9RZz6hxEkk8XiZC+nrCcrr7DNNFNzh5gx30Ihm
+NyZybaawr/vn5O2Oe0BSTwuhIdk1XjpzDtqpcNT2Qui4eRx/OBcyyX9PJvicBfMq
+53ZNom/3NTZbsXp70uCV8eC97a7g7T+GymRS1u2x7I/Kp+/w0plG11bXnWg2A0EZ
+WHCnmQWBUpqSUW3syfuzqlCFDYWoyVkw2eNtIbhGv9knEKPtU9bewAbo1/2Jk1R2
+FVP5B3VvdY2huzQLzbzHB4zhsJCEjYnvzwPZ0WeIYHmTYJEAulTynBdv9GNX9sdM
+GNXS/ESTFUQDMXbgDBdwVxZOq1Gzwh+grN3lwpS/5wcsSuNhfEfvx37DyLKNiXMo
+5HS/g03kAmmIgH7IWUcM27ZyyKlpxj8ztFFUIdnIUX4biiZCBJnfMuWnNzJM7o/b
+T8PVEEM3wuUT5ih7yT4l/j5pV4WmEbgVdWSrbL/H77GuFHwXYiuzDyH1/E23Hedi
+crd8g47bV0jL1v0TwT4oHtEkAXIU5Nj2+z+ZKSl5SJ0I2tAy86hCpIn/rmbMmtws
+Ce/OHHOu2Mm5KBEK9SyLThMzqYrv5Zux9Xqre+P0LPk/tzxwdG87qKhU0xdPvn6y
+rGaC1OFCT3GmidZl2QARAQABiQI2BBgBCAAgFiEElL0XgHdnIVdlKCZ1jkSoJJc4
+IpgFAl8sDHUCGwwACgkQjkSoJJc4IphtBw/8DsvdVbaaVqMOe/S66R3zn5M22YKU
+AkhQvBQId4rTDUgTiSJ6Ll+Ascr1q2gFupb7iAM4BWAFQji4f8iH51sS9a6I6Oy8
+WK4ftFYDyQU0/hgaF2B0+QE0PN3/88ckBlL3KHhzw0ad/Y2Bp6CGGFNwI9xqC7XT
+t8Y+XCpv9buC7ZVpE/N/yF+2HvVhW7PG+5oB+Qc+Q/G0RK2QX7unOSqLc2pS/n4v
+mBqGc1KAe7iyxOo2Q2G+Q0XTK8g/BUMWACVOuYpOrvteyHJXIYv/VDRu+/pd81G0
+i6B063BzuaDRqwNngLOU6lNcDOgom6gWkCfkg1Nbr009rXyADIg/RHPX1TUAaoFn
+QH0YDIxWfyDvTJ7FgmLVCnXXc88T1du/ROAq5Y+opD3vcDX+egzbKR+oSGbaf6HL
+ASj0haconAOZ7V3sLO9WSITUODzHEUwOuOx+XtaW/JYTm47JeH2r83v+OmBNbAJg
+hT5KINI8iBvor3cUYKAor9ib1192ZHgBjPlrFDMntZZCqKyCvRGRktts4VcH09DD
+szVC2TEeuxgIMuUi73HebjX+fRefcSIkW30ehXVzN/7Ah1SK9IJc9hzVa2ZspUho
+Ias/zRyLSbzHrpCs6KVPLwzOQbyPmXNpjoYuGCq6NX54S7bf8Hn3X8SQmezozLhN
+krvOtK7UUytDTcY=
+=+SBy
+-----END PGP PUBLIC KEY BLOCK-----
+pub   rsa4096 2022-03-02 [SC]
+      8ACA4FC874B6B0836DFE70BB52514D7E7CFC32B6
+uid             [ 绝对 ] Zhang Wenli (CODE SIGNING KEY) <ovilia@apache.org>
+sig 3        52514D7E7CFC32B6 2022-03-02  Zhang Wenli (CODE SIGNING KEY) <ovilia@apache.org>
+sub   rsa4096 2022-03-02 [E]
+sig          52514D7E7CFC32B6 2022-03-02  Zhang Wenli (CODE SIGNING KEY) <ovilia@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQINBGIfP7kBEACe5lPqYdMuQsugMCFN8EdGAoFnytQJGHNIY6fBgIQv/CTTM6oM
+JW5pLERfmlvXs3SDIpdZVQp1JmUjs0SpKV4pDBwJq+bMzxiD0QD+7sZb/zadHBOR
+EfKFBij9lrrft/42FbsLrSA19FNalLniXp0NC8QBl+dLafy6ypPX7iSXCWvB/qiu
+XPFY6yJGi4Jt1vVnTeTz9k17y2oJNRl6eh4CLxuTJwLb11Fuhwy8gC0JWMXd52OF
+P6PcWWPWV5qA/UrtbnwQb0Z8+YiK/nDv5p0e2HOEB+Nnl9KdHIpDaP1dSE4hKkFK
+UjWBXzMSBJAwNObMBDGtiWzeU1kIIkHguEUNbJXLHzIWvNrYbuCYOSsdA4o7QNFr
+quy/Vt39+zu5R5znn1AgoUsCvfhMGKME5d2MDgKsyfh8LTHuqDkWZxj8zgMZxDrX
+p/KZBy/bSjii8V1vgoDl0NuJZrXNHrEGQglLiV7RzQBRfkAI4u+3gd+8Emeny0Ku
+GEXrB2dCj7OoDgR0TXmzZf4U8Stnhr4//Fgn76ca+9mOp6NeZpIvVIiJ0hK3QsUe
+gllD0yEJ7fHGQIX//qfymo+rWdvT+WXz6/251eDb+C9TYosj0lpeW0h4URywarvc
+Nqudz8UEVNe4hETtP7VpKjokEiNgj66T+WrbsBWjT1KWlkOhiVFO+FVV0wARAQAB
+tDJaaGFuZyBXZW5saSAoQ09ERSBTSUdOSU5HIEtFWSkgPG92aWxpYUBhcGFjaGUu
+b3JnPokCTgQTAQgAOBYhBIrKT8h0trCDbf5wu1JRTX58/DK2BQJiHz+5AhsDBQsJ
+CAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEFJRTX58/DK2SccP+wZbwZIu4nI1zzlX
+jljH7wLyvDT/hfEm/cBBvF+IgV/EYfMaNaphzsci1V0X8Dv4LmzsV8HS/pIscekM
+mV9Ua8Lyty0QHdFdcMaZPF0irJ59NXfXVu+SDB5NTVaEPhQHclChdyVQEpbv444p
+FwWtNc2JU7C33NtDnsoTECDKy22rP3E/4vti1OEKvaNPqJ7Cmed/fmjShEvoUl1U
+k34fZlTzAZS8FQk3oIvVZq91B9FekywAOLMTo0QFQdgbHpk3Pu2BQ3xaIwEdTu5n
+jypgx7ljK/1Siczo+VzH7uv5pGyVgeufI07OFOqoyC+gfAhXcZp8pBbVuRm5aO0O
+oyzOLm8qQ9TxXt5XtdZzdbgZ8uMr8ualgTj1XOU3Q8AY/BCZ3i7qqZEPY2lO4O/e
+spS2HGx158soggTH0m7EDx5jas5WS49pWxhZOAq4Z3hDSz3LFYTUOUgq1HJJS2b3
+l11rRaDiuxShpIgr5LfxmbCLL+cGmxcPZGEsJBCszEwhPNRqR5AwvRO2OONGsTel
+Y9PqJRT2+3KXgu/rvBnbAuIxaI8vIy1iP82rTxw8z8QK1qce6BIldho18yOVmCrC
+wLMB+snpVnXyaDKvcNJI3KnfiRA9RyKz13XHsykH02nI0c3O0zFW5Ob+HNCnzlgg
+vd1mG4jAwrTN+/fezrInfMu2YsQzuQINBGIfP7kBEADRINphJ2MWt8/FfacMhiVy
+3a9DKkI/w0xt2OFZuTxK7xAuGeNCJGVrRf/qxM82xR7IApDyxLIZn/+DzYMoFzQs
+r2XQR8sAy2/x8r42xUiSZUtfdztVN+QEu+qCgVYAY//qLZsrSfn0ezv51m/Dw2Q0
+k3euzR4/dbulTnt28z4T1BDnDyEWU7vE0m4qyrrQe9DHmC0iIkg3RY7u6/0UK+Ar
+W+IgLQZnZOwTc4GygFCMst8pWsfnLYpPGt3XSI5Om7OQ0Xf1nyLWBtmxJQRsbU5i
+hDLfR0KTARC8cjReFL1eoe9OT6NXJiQltTvDnrpWXN/3tYFakgPf1JrEHkllgHOM
+zM78/H7FgetIueTjem98Qju0/zvBxxd93kLrSkcLRP2QiD7cdIW9tqCrcKY7k06t
+EG+oVdvQA+W7V5wDxQ+8YYp9l+9ftBZNTXa9q/5e7/qzl4cIY4EPpe3eTxj2K9uM
+wsVtPPk48N819fSNDKXOEpqzTs12tniZC5NBsfB8ZduNmjDhcxRMJRA2RhQWRMG0
+knEsVBFkepnhlg6PhWE1fz9Q/YbmVTni4hSN6YFSpw2da6zpHqStXooSzfEw+IvT
+v4WUbHq9TA0zkPEdHn1s75blf8jO6s6XLGEZBKXM/PGO9QtjkYDOaePfpfoLgQEt
+TGHJSTLcEUS/HQLiqVFPpQARAQABiQI2BBgBCAAgFiEEispPyHS2sINt/nC7UlFN
+fnz8MrYFAmIfP7kCGwwACgkQUlFNfnz8MrY18w//QbqFYRLJLKoqfcZV55W2jtxX
+N71+GvY1DWAQByvcV1h9aChpVXyNjKmNiwAdBDam9RYnArmFQauFyEZpHfOdoEc0
+u+Wsllou/tomsqIMx5AuUpGyCrqPKFsKAuqA15/a6tbhEhDd5gIbSYRVlvNinKqm
+JyuPvfbiKQxo28yV7NMIPpSg9gGSkZiEWTGVQR5603EFnkhrS6n8VZFCKQLlSl1X
+VhyN2U/rjwRkDQUh6DSGMb6OHoeFCW00LqqiFoxtdBru9LYO5NYSbnZzicBsBnJ+
+rEqX0yfyDaSzC21wTH3ARf88CruVYerEPMs6lMDLlHlsdZX9VPxofvA7PGcNiiiI
+xkIfPsE1X5cdy7hnhdpPuWEsV4XoYEn1p3TpRdud2N6OZjZe/Jb6KaNmGbRnCl9L
+Hiftq4uZ8hgIdRMa1FdeXug3dwVyPp6HLjqA7q1mi/f69ywNYT8e1g2YrI1MNEL8
+TJqsONJX5Y5LRdUIdGfQ2KZOOlPqTb1ksdm9+xamLccUz3UCCqQS3GuufUjmLmoi
+WQBNQpzlLXaZtFworBRRXTeq7TYK5lqYCU+d46D1pc4TmFoLlCwdr7kY/taa5pip
+XmpgVv8kY1A+ONjCCk5kDNDWUZbYVEyvdihvUz765fpIoCFM2YfbB8J8fgInRfWB
+RWnk0btbWIvaznWpIWo=
+=QBYg
+-----END PGP PUBLIC KEY BLOCK-----
+pub   rsa4096 2022-03-28 [SC]
+      EFA5629C5F1FF8D33E016202F16C82C561221579
+uid           [ultimate] Zhongxiang Wang (CODE SIGNING KEY) <wangzx@apache.org>
+sig 3        F16C82C561221579 2022-03-28  Zhongxiang Wang (CODE SIGNING KEY) <wangzx@apache.org>
+sub   rsa4096 2022-03-28 [E]
+sig          F16C82C561221579 2022-03-28  Zhongxiang Wang (CODE SIGNING KEY) <wangzx@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQINBGJBdpIBEACtI4twpy36+vUMwpBQCgbpKzY+KtD95bcoMuy8IepzyQSq+Z2b
+mPfjUIA4e9hSvuPCXMkDTZo3Vj2MskzxsFmS+1Or/y0pfsmx0pgzDQ5voD0ayQo3
+EzDT2LbOOkCkPIpBVnQvh3LFk5/VIJCDqjHPyM6r5moWfmq9x7lfDwqhQrJryK9s
+/7EGvgZT2AR7e5TMVgP021t2HH9xfyp/zF+oZVUPSXnmy9j6yiNyu3DjgHwLY+4O
+RGUqhe+I8wq1l2nul0QW2BvLjouEXftf/Rx+X3k/TRVoWtH8RiJzkWZNjd8vyyDd
+cOYo8MxLEJtGDhnrhpsGYM2cYwvGET2mpy1FeX/U/CWfTKUALNxZ4e7GacRi8UeM
+YVp0ov22vskqYKxy0gTVHAoL/mfIcXuCxUw/s0sL01O/rP5lHwy6ghK4KZCTu/4d
+YTfQo8R9NFaBWY9odN3kxJ9ehLPczogtYPU9ThIzbUJ5NudYjh+2NAXEbx9lbfRC
+mR1DyihskYZ4j4FFOWqrke4flDW+lx7VgFb/Um9oQX1Bl7jKRgmlJIN+dNpJpi8w
+9a2DR/gFwxulLvsQPm/Mcki6Xb/Igscq7AZBgUKAtzLMdJuYglp1EUyYhGL6ylIf
+YivzUfNnd6Dvl52H/jLxnZemHy5wO7ZtmehSs3XcPLvM6azb+zCr6xne6QARAQAB
+tDZaaG9uZ3hpYW5nIFdhbmcgKENPREUgU0lHTklORyBLRVkpIDx3YW5nenhAYXBh
+Y2hlLm9yZz6JAk4EEwEIADgWIQTvpWKcXx/40z4BYgLxbILFYSIVeQUCYkF2kgIb
+AwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRDxbILFYSIVecLfD/9L/C5XBA5I
+Ub6jS6ozqkupIKdIdLbcXsXNL7cLCrs67zCldHl0t5iaVZF1/rfbwEyjWRD0W6Yx
+k4XPe2iOaOh4R0BBySptBKyK3tyMBOeGZxhtn5w5hZp9ikEbyq/TDK9XK8S+45Zs
+AlfzQ/B0fmihSaMyGNOS2m3kxOMwEOZVegZtiNM+ZSd10/K8Zf5mfdA7EjHLHiow
+WvFMV26gAnd4T7ZRGv7/ZmI0eWAxwdnDdlxE3JgpLfaLjbKWYVOPFxSyF749yFFL
+oRNcTK1Advlwf3jloWhFQU+9i/bsp+VZ7bG3ptfQvq7Nnm+TkVHpHB4FaMnezrJL
+5rKATGZapA9c5MLye0OGGfZAzfvbFsE4J7e1J6mgatjPbMoPjsYBHW5N89ZaBfbQ
+napQuGx2HrBSIzmIaoQqUwdsMaC9cfNx8IdSsbK31maXyY4cooQnGbt4hrALEcti
+DVZCty6NsTLruNk+kCIKLTgMdXYbvJTydNF8bGWppDaEUayRCyCUHf/UBhVhdLU6
+/jyNF141xlNUV5yXDlMGANrZ+26Bu8vufEpkiABihjh/DGQZpdqY9zEDR5sQmae+
+ij0CBG7SLtEFLY5bHsCxm5orSIil0eTAsNFkjn9JYvoil7WJNuV2TdWbSa+Fs+gM
+UmLLR5oUA3EM1T1BV4TICUevcoSZxdKkIrkCDQRiQXaSARAA6Ci/4XEq5CApLoIJ
+MO+HsmP1orppgqGY1hFM1saQ/1JkgOFjfXlGWNLSkymNpqapDIblHdeC8mXdZJSm
+Qeto8i+wEJI+iKl8iYm/KSt/OpfnxfqmMcFhYRczTDFUdp4/cidxCf1TTjyub1PL
+9Pu6TJ4pqJC4TJ1QYOGVZEsMk+Csg6n33sArmpD4YoZfCQy1unvweSr920A4Y5sJ
+jNn6ntGUhguAeHe165yHv2fIWJb/ur+9Kl/SYdD17I+oGW9EZzyNU/lwXs4/siqD
+nmTzdWQ+/NsfFAIJzVsEwp9687opNOXKlSpaLO8ACGx/nOMUnjfmG9tu4h3bkQtN
+SAALDKRn12V3nB7nqbOdSy2QgyFETn5gO64ZuWD/TSk/3P8Bp8AwHdNDKer3GqH7
+omA7VgKxbRhoeJMKWuihBRJ3y01u614QPgmheSzggGg+NVmwWbq5f8+nH20NVNjX
+dTRACCR/0IjRv2ZitNc48X+lNqMMXQdk9K+EpcQhy4fHAnwqc4iij+moKBBp513n
+mv7h+QWLVYjqOuA1yPLAUFxoYLBEQ1DoHTHCbJ3o6gHk8eiPgoIvtJIZNAc150aj
+scwXmk6KxyZwB4cFtFpzRYMfefDRS2O6t9+lkz83dBT9VKWISoRhh3JXaeoIRkk6
+/RvzPYzwGf3R5ouvwfaAXI4YOqEAEQEAAYkCNgQYAQgAIBYhBO+lYpxfH/jTPgFi
+AvFsgsVhIhV5BQJiQXaSAhsMAAoJEPFsgsVhIhV5AUgQAJN537gtlvtWkj6jPFQR
+hNuoCapc7XicBjtqSUlSg/vbWzPeayhSeX288shNJVmJTD5Wq2UfDuki6W6EEdBu
+pZnPX8xqhBjvOCgei3vZZPqEMKqCxAnbV9CVFJzJZh+u5SLnbOlYVuNh6fp1uaSi
+AcRDgyLaUYBYj14ge42aukQuzCWvdnMcn1fZdN84xnm/dXHTxrmphBJlTfVk2U0+
+bvieQNtqp7V7f18peMEoCBTqNjmDxebaTiyqcqAAWXV0bnH9TVIsjCDdT8HfsHAH
+8Pfn/Tw9WqhIRcvWA1Ld1wrMRHv7oOVzMsvvaBsxR4X4yhXBx2Nn2r/g0Rp5K+2R
+o9QLwPCa0P874LVMmdxdoBSC8GMigoj7R1lBIjyaM5v5ylTu8RVmDSul7xIjb6ek
+tWKjZ/ASFSnA+m5VMBF0Z9bA3v31KvsS4ZQtnXEcAIVrNFkBO9JZrwBPat0WVWx7
+/VQeh7PEtvsQhlKRlWY6xVdLq+DD3p/mHqpIH+YWaqhOa6sde8teN8UpSyp6F13a
+SVM1KUz1U6gH3WEu8aqOmJTVrHq5h3kBUrfiLpc3juBCjrAlY2iY3Fzi5VuBzbnT
+oEg8NMD8Wao5YN22JG30anrmYadZaghIwBz6rEuHmbf5MwcKoK349LptfHV4fhuq
+5B5E6LlMNPTCWmPzYtTm5qZK
+=bbcU
+-----END PGP PUBLIC KEY BLOCK-----
+pub   ed25519 2022-12-01 [SC] [expires: 2024-11-30]
+      016736F5612A13D1FD04AA45CC593BC1F4F4EB7A
+uid           [ultimate] susiwen <susiwen8@gmail.com>
+sig 3        CC593BC1F4F4EB7A 2022-12-01  susiwen <susiwen8@gmail.com>
+sub   cv25519 2022-12-01 [E] [expires: 2024-11-30]
+sig          CC593BC1F4F4EB7A 2022-12-01  susiwen <susiwen8@gmail.com>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mDMEY4jDBhYJKwYBBAHaRw8BAQdAlpaQNA7ARfkPVj6EoYARkkGPdLgOmulCwScl
+xGk3+8m0HHN1c2l3ZW4gPHN1c2l3ZW44QGdtYWlsLmNvbT6ImQQTFgoAQRYhBAFn
+NvVhKhPR/QSqRcxZO8H09Ot6BQJjiMMGAhsDBQkDwmcABQsJCAcCAiICBhUKCQgL
+AgQWAgMBAh4HAheAAAoJEMxZO8H09Ot6gcoBANBsCrZOwZtWCCQB2A6cy0or7q4c
+GdyMJbP7zT5tdAAuAQDI7dy5/KE5tklZmEHJZevQLWezs6yKi+31QxcNFh6FA7g4
+BGOIwwYSCisGAQQBl1UBBQEBB0A4z0jb/PpPRt/zILSBzl8XidMvvQAksexms4P4
+D74EcQMBCAeIfgQYFgoAJhYhBAFnNvVhKhPR/QSqRcxZO8H09Ot6BQJjiMMGAhsM
+BQkDwmcAAAoJEMxZO8H09Ot6hEABALEBaZSNzmx17PbubyiyvtaEISuzsv23RYwh
+4NRHP4BkAP475WSjwMns2hSairvPXULqAcqQnjytov7CU1hbMLvgDpgzBGOMr5EW
+CSsGAQQB2kcPAQEHQF85ZZTr9NstXxkToCrkVYwNuahidgRyv6S3zo2xTc6ZtC9z
+dXNpd2VuIChDT0RFIFNJR05JTkcgS0VZKSA8c3VzaXdlbjhAZ21haWwuY29tPoiT
+BBMWCgA7FiEEhBIjSy5LUgkGNSGQJZ0/SMJTSzwFAmOMr5ECGwMFCwkIBwICIgIG
+FQoJCAsCBBYCAwECHgcCF4AACgkQJZ0/SMJTSzyNaAD+P35MI4r5nUDDg97QKYNY
+m99MtUxTmcK/KGsrxYEZEDEA/jECGFvy/5WAhIRUTl4ExVsY3eBL/K2DaoTseW4a
+eVEPuDgEY4yvkRIKKwYBBAGXVQEFAQEHQKNPmeMoqbHBVs5xn0c+Tz/bPW0rDDbw
+Gt1pqdBMdmUvAwEIB4h4BBgWCgAgFiEEhBIjSy5LUgkGNSGQJZ0/SMJTSzwFAmOM
+r5ECGwwACgkQJZ0/SMJTSzxTzQD+MTFHjt7z78fdTqbbRA6isxPV84cAFQsX4cRx
+PRobcbkBAIwAkq+ddEycxZTdzaELpE08h/BLcScqbOl/ME1PTZ0H
+=3Tm4
+-----END PGP PUBLIC KEY BLOCK-----
+pub   ed25519 2023-03-15 [SC] [有效至:2025-03-14]
+      9C8B166777DB15AD1CC0FFBF715559B9217D4E5A
+uid             [ 绝对 ] zakwu <123537200@qq.com>
+sig 3        715559B9217D4E5A 2023-03-15  zakwu <123537200@qq.com>
+sub   cv25519 2023-03-15 [E] [有效至:2025-03-14]
+sig          715559B9217D4E5A 2023-03-15  zakwu <123537200@qq.com>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mDMEZBE+JRYJKwYBBAHaRw8BAQdA4US4FlrxvH2Ckj5NzIkeL5nd4NyDBrlpyERo
+KvlXn/C0GHpha3d1IDwxMjM1MzcyMDBAcXEuY29tPoiZBBMWCgBBFiEEnIsWZ3fb
+Fa0cwP+/cVVZuSF9TloFAmQRPiUCGwMFCQPCZwAFCwkIBwICIgIGFQoJCAsCBBYC
+AwECHgcCF4AACgkQcVVZuSF9TloeGAD/RjarHn34jh1NtJGi6Z8wv/XWESxyNH6g
+orBPlQ+yluEBAIinhY8j/XczJQUcj9cqpMB4m8R+/jEadbaBe9pQ3uAHuDgEZBE+
+JRIKKwYBBAGXVQEFAQEHQPa8rnpAhbsWw0VsCbYo1J+VeZXT/piqPpdducN3Wyh2
+AwEIB4h+BBgWCgAmFiEEnIsWZ3fbFa0cwP+/cVVZuSF9TloFAmQRPiUCGwwFCQPC
+ZwAACgkQcVVZuSF9Tlrc4QD/ZDd7OjcT9ShdARjcGoQ0jt6rEqL6n10V6caG+77a
+89wA/R+29UlbOXNAxcQHxph8WXUZhACDhKyNETgRsgHysZQJ
+=/6bg
+-----END PGP PUBLIC KEY BLOCK-----

+ 222 - 0
node_modules/echarts/LICENSE

@@ -0,0 +1,222 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+
+
+
+========================================================================
+Apache ECharts Subcomponents:
+
+The Apache ECharts project contains subcomponents with separate copyright
+notices and license terms. Your use of the source code for these
+subcomponents is also subject to the terms and conditions of the following
+licenses.
+
+BSD 3-Clause (d3.js):
+The following files embed [d3.js](https://github.com/d3/d3) BSD 3-Clause:
+    `/src/chart/treemap/treemapLayout.ts`,
+    `/src/chart/tree/layoutHelper.ts`,
+    `/src/chart/graph/forceHelper.ts`,
+    `/src/util/number.ts`
+See `/licenses/LICENSE-d3` for details of the license.

+ 5 - 0
node_modules/echarts/NOTICE

@@ -0,0 +1,5 @@
+Apache ECharts
+Copyright 2017-2023 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (https://www.apache.org/).

+ 98 - 0
node_modules/echarts/README.md

@@ -0,0 +1,98 @@
+# Apache ECharts
+
+<a href="https://echarts.apache.org/">
+    <img style="vertical-align: top;" src="./asset/logo.png?raw=true" alt="logo" height="50px">
+</a>
+
+Apache ECharts is a free, powerful charting and visualization library offering easy ways to add intuitive, interactive, and highly customizable charts to your commercial products. It is written in pure JavaScript and based on <a href="https://github.com/ecomfe/zrender">zrender</a>, which is a whole new lightweight canvas library.
+
+**[中文官网](https://echarts.apache.org/zh/index.html)** | **[ENGLISH HOMEPAGE](https://echarts.apache.org/en/index.html)**
+
+[![License](https://img.shields.io/npm/l/echarts?color=5470c6)](https://github.com/apache/echarts/blob/master/LICENSE) [![Latest npm release](https://img.shields.io/npm/v/echarts?color=91cc75)](https://www.npmjs.com/package/echarts) [![NPM downloads](https://img.shields.io/npm/dm/echarts.svg?label=npm%20downloads&style=flat&color=fac858)](https://www.npmjs.com/package/echarts) [![Contributors](https://img.shields.io/github/contributors/apache/echarts?color=3ba272)](https://github.com/apache/echarts/graphs/contributors)
+
+[![Build Status](https://github.com/apache/echarts/actions/workflows/ci.yml/badge.svg)](https://github.com/apache/echarts/actions/workflows/ci.yml)
+
+## Get Apache ECharts
+
+You may choose one of the following methods:
+
++ Download from the [official website](https://echarts.apache.org/download.html)
++ `npm install echarts --save`
++ CDN: [jsDelivr CDN](https://www.jsdelivr.com/package/npm/echarts?path=dist)
+
+## Docs
+
++ [Get Started](https://echarts.apache.org/handbook)
++ [API](https://echarts.apache.org/api.html)
++ [Option Manual](https://echarts.apache.org/option.html)
++ [Examples](https://echarts.apache.org/examples)
+
+## Get Help
+
++ [GitHub Issues](https://github.com/apache/echarts/issues) for bug report and feature requests
++ Email [dev@echarts.apache.org](mailto:dev@echarts.apache.org) for general questions
++ Subscribe to the [mailing list](https://echarts.apache.org/maillist.html) to get updated with the project
+
+## Build
+
+Build echarts source code:
+
+Execute the instructions in the root directory of the echarts:
+([Node.js](https://nodejs.org) is required)
+
+```shell
+# Install the dependencies from NPM:
+npm install
+
+# Rebuild source code immediately in watch mode when changing the source code.
+# It opens the `./test` directory, and you may open `-cases.html` to get the list
+# of all test cases.
+# If you wish to create a test case, run `npm run mktest:help` to learn more.
+npm run dev
+
+# Check the correctness of TypeScript code.
+npm run checktype
+
+# If intending to build and get all types of the "production" files:
+npm run release
+```
+
+Then the "production" files are generated in the `dist` directory.
+
+## Contribution
+
+Please refer to the [contributing](https://github.com/apache/echarts/blob/master/CONTRIBUTING.md) document if you wish to debug locally or make pull requests.
+
+## Resources
+
+### Awesome ECharts
+
+[https://github.com/ecomfe/awesome-echarts](https://github.com/ecomfe/awesome-echarts)
+
+### Extensions
+
++ [ECharts GL](https://github.com/ecomfe/echarts-gl) An extension pack of ECharts, which provides 3D plots, globe visualization, and WebGL acceleration.
+
++ [Liquidfill 水球图](https://github.com/ecomfe/echarts-liquidfill)
+
++ [Wordcloud 字符云](https://github.com/ecomfe/echarts-wordcloud)
+
++ [Extension for Baidu Map 百度地图扩展](https://github.com/apache/echarts/tree/master/extension-src/bmap) An extension provides a wrapper of Baidu Map Service SDK.
+
++ [vue-echarts](https://github.com/ecomfe/vue-echarts) ECharts component for Vue.js
+
++ [echarts-stat](https://github.com/ecomfe/echarts-stat) Statistics tool for ECharts
+
+## License
+
+ECharts is available under the Apache License V2.
+
+## Code of Conduct
+
+Please refer to [Apache Code of Conduct](https://www.apache.org/foundation/policies/conduct.html).
+
+## Paper
+
+Deqing Li, Honghui Mei, Yi Shen, Shuang Su, Wenli Zhang, Junting Wang, Ming Zu, Wei Chen.
+[ECharts: A Declarative Framework for Rapid Construction of Web-based Visualization](https://www.sciencedirect.com/science/article/pii/S2468502X18300068).
+Visual Informatics, 2018.

BIN
node_modules/echarts/asset/logo.png


+ 183 - 0
node_modules/echarts/build/addHeader.js

@@ -0,0 +1,183 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+
+const fs = require('fs');
+const preamble = require('./preamble');
+const pathTool = require('path');
+const chalk = require('chalk');
+
+// In the `.headerignore`, each line is a pattern in RegExp.
+// all relative path (based on the echarts base directory) is tested.
+// The pattern should match the relative path completely.
+const excludesPath = pathTool.join(__dirname, '../.headerignore');
+const ecBasePath = pathTool.join(__dirname, '../');
+
+const isVerbose = process.argv[2] === '--verbose';
+
+// const lists = [
+//     '../src/**/*.js',
+//     '../build/*.js',
+//     '../benchmark/src/*.js',
+//     '../benchmark/src/gulpfile.js',
+//     '../extension-src/**/*.js',
+//     '../extension/**/*.js',
+//     '../map/js/**/*.js',
+//     '../test/build/**/*.js',
+//     '../test/node/**/*.js',
+//     '../test/ut/core/*.js',
+//     '../test/ut/spe/*.js',
+//     '../test/ut/ut.js',
+//     '../test/*.js',
+//     '../theme/*.js',
+//     '../theme/tool/**/*.js',
+//     '../echarts.all.js',
+//     '../echarts.blank.js',
+//     '../echarts.common.js',
+//     '../echarts.simple.js',
+//     '../index.js',
+//     '../index.common.js',
+//     '../index.simple.js'
+// ];
+
+function run() {
+    const updatedFiles = [];
+    const passFiles = [];
+    const pendingFiles = [];
+
+    eachFile(function (absolutePath, fileExt) {
+        const fileStr = fs.readFileSync(absolutePath, 'utf-8');
+
+        const existLicense = preamble.extractLicense(fileStr, fileExt);
+
+        if (existLicense) {
+            passFiles.push(absolutePath);
+            return;
+        }
+
+        // Conside binary files, only add for files with known ext.
+        if (!preamble.hasPreamble(fileExt)) {
+            pendingFiles.push(absolutePath);
+            return;
+        }
+
+        fs.writeFileSync(absolutePath, preamble.addPreamble(fileStr, fileExt), 'utf-8');
+        updatedFiles.push(absolutePath);
+    });
+
+    console.log('\n');
+    console.log('----------------------------');
+    console.log(' Files that exists license: ');
+    console.log('----------------------------');
+    if (passFiles.length) {
+        if (isVerbose) {
+            passFiles.forEach(function (path) {
+                console.log(chalk.green(path));
+            });
+        }
+        else {
+            console.log(chalk.green(passFiles.length + ' files. (use argument "--verbose" see details)'));
+        }
+    }
+    else {
+        console.log('Nothing.');
+    }
+
+    console.log('\n');
+    console.log('--------------------');
+    console.log(' License added for: ');
+    console.log('--------------------');
+    if (updatedFiles.length) {
+        updatedFiles.forEach(function (path) {
+            console.log(chalk.green(path));
+        });
+    }
+    else {
+        console.log('Nothing.');
+    }
+
+    console.log('\n');
+    console.log('----------------');
+    console.log(' Pending files: ');
+    console.log('----------------');
+    if (pendingFiles.length) {
+        pendingFiles.forEach(function (path) {
+            console.log(chalk.red(path));
+        });
+    }
+    else {
+        console.log('Nothing.');
+    }
+
+    console.log('\nDone.');
+}
+
+function eachFile(visit) {
+
+    const excludePatterns = [];
+    const extReg = /\.([a-zA-Z0-9_-]+)$/;
+
+    prepareExcludePatterns();
+    travel('./');
+
+    function travel(relativePath) {
+        if (isExclude(relativePath)) {
+            return;
+        }
+
+        const absolutePath = pathTool.join(ecBasePath, relativePath);
+        const stat = fs.statSync(absolutePath);
+
+        if (stat.isFile()) {
+            visit(absolutePath, getExt(absolutePath));
+        }
+        else if (stat.isDirectory()) {
+            fs.readdirSync(relativePath).forEach(function (file) {
+                travel(pathTool.join(relativePath, file));
+            });
+        }
+    }
+
+    function prepareExcludePatterns() {
+        const content = fs.readFileSync(excludesPath, {encoding: 'utf-8'});
+        content.replace(/\r/g, '\n').split('\n').forEach(function (line) {
+            line = line.trim();
+            if (line && line.charAt(0) !== '#') {
+                excludePatterns.push(new RegExp(line));
+            }
+        });
+    }
+
+    function isExclude(relativePath) {
+        for (let i = 0; i < excludePatterns.length; i++) {
+            if (excludePatterns[i].test(relativePath)) {
+                return true;
+            }
+        }
+    }
+
+    function getExt(path) {
+        if (path) {
+            const mathResult = path.match(extReg);
+            return mathResult && mathResult[1];
+        }
+    }
+}
+
+run();

+ 114 - 0
node_modules/echarts/build/build-i18n.js

@@ -0,0 +1,114 @@
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+const fs = require('fs');
+const preamble = require('./preamble');
+const ts = require('typescript');
+const path = require('path');
+const fsExtra = require('fs-extra');
+
+const umdWrapperHead = `
+${preamble.js}
+/**
+ * AUTO-GENERATED FILE. DO NOT MODIFY.
+ */
+(function(root, factory) {
+    if (typeof define === 'function' && define.amd) {
+        // AMD. Register as an anonymous module.
+        define(['exports'], factory);
+    } else if (
+        typeof exports === 'object' &&
+        typeof exports.nodeName !== 'string'
+    ) {
+        // CommonJS
+        factory(exports);
+    } else {
+        // Browser globals
+        factory({});
+    }
+})(this, function(exports) {
+`;
+
+const umdWrapperHeadWithEcharts = `
+${preamble.js}
+/**
+ * AUTO-GENERATED FILE. DO NOT MODIFY.
+ */
+(function(root, factory) {
+    if (typeof define === 'function' && define.amd) {
+        // AMD. Register as an anonymous module.
+        define(['exports', 'echarts'], factory);
+    } else if (
+        typeof exports === 'object' &&
+        typeof exports.nodeName !== 'string'
+    ) {
+        // CommonJS
+        factory(exports, require('echarts/lib/echarts'));
+    } else {
+        // Browser globals
+        factory({}, root.echarts);
+    }
+})(this, function(exports, echarts) {
+`;
+
+const umdWrapperTail = `
+});`;
+
+async function buildI18nWrap() {
+    const targetDir = path.join(__dirname, '../i18n');
+    const sourceDir = path.join(__dirname, '../src/i18n');
+    const files = fs.readdirSync(sourceDir);
+    files.forEach(t => {
+        if(!t.startsWith('lang')) {
+            return;
+        }
+        const fileName = t.replace(/\.ts$/, '');
+        const type = fileName.replace(/^lang/, '');
+        const echartsRegister = `
+    echarts.registerLocale('${type}', localeObj);
+        `;
+        const pureExports = `
+    for (var key in localeObj) {
+        if (localeObj.hasOwnProperty(key)) {
+            exports[key] = localeObj[key];
+        }
+    }
+        `;
+        const code = fs.readFileSync(path.join(sourceDir, t), 'utf-8');
+        // const outputText = ts.transpileModule(code, {
+        //     module: ts.ModuleKind.CommonJS,
+        // }).outputText;
+        // Simple regexp replace is enough
+        const outputCode = code.replace(/export\s+?default/, 'var localeObj =')
+            .replace(/\/\*([\w\W]*?)\*\//, '');
+
+        fsExtra.ensureDirSync(targetDir);
+
+        fs.writeFileSync(path.join(targetDir, fileName + '.js'), umdWrapperHeadWithEcharts + outputCode + echartsRegister + umdWrapperTail, 'utf-8');
+        fs.writeFileSync(path.join(targetDir, fileName + '-obj.js'), umdWrapperHead + outputCode + pureExports + umdWrapperTail, 'utf-8');
+    })
+    console.log('i18n build completed');
+}
+
+buildI18nWrap();
+
+module.exports = {
+    buildI18n: buildI18nWrap
+};

+ 210 - 0
node_modules/echarts/build/build.js

@@ -0,0 +1,210 @@
+#!/usr/bin/env node
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+const fs = require('fs');
+const config = require('./config.js');
+const commander = require('commander');
+const chalk = require('chalk');
+const rollup = require('rollup');
+const prePublish = require('./pre-publish');
+const transformDEV = require('./transform-dev');
+
+async function run() {
+
+    /**
+     * Tips for `commander`:
+     * (1) If arg xxx not specified, `commander.xxx` is undefined.
+     *     Otherwise:
+     *      If '-x, --xxx', `commander.xxx` can only be true/false, even if '--xxx yyy' input.
+     *      If '-x, --xxx <some>', the 'some' string is required, or otherwise error will be thrown.
+     *      If '-x, --xxx [some]', the 'some' string is optional, that is, `commander.xxx` can be boolean or string.
+     * (2) `node ./build/build.js --help` will print helper info and exit.
+     */
+
+    let descIndent = '                                 ';
+    let egIndent = '    ';
+
+    commander
+        .usage('[options]')
+        .description([
+            'Build echarts and generate result files in directory `echarts/dist`.',
+            '',
+            '  For example:',
+            '',
+            egIndent + 'node build/build.js --prepublish'
+                + '\n' + descIndent + '# Only prepublish.',
+            egIndent + 'node build/build.js --type ""'
+                + '\n' + descIndent + '# Only generate `dist/echarts.js`.',
+            egIndent + 'node build/build.js --type common --min'
+                + '\n' + descIndent + '# Only generate `dist/echarts.common.min.js`.',
+            egIndent + 'node build/build.js --type simple --min'
+                + '\n' + descIndent + '# Only generate `dist/echarts-en.simple.min.js`.',
+        ].join('\n'))
+        .option(
+            '--prepublish',
+            'Build all for release'
+        )
+        .option(
+            '--min',
+            'Whether to compress the output file, and remove error-log-print code.'
+        )
+        .option(
+            '--type <type name>', [
+            'Can be "simple" or "common" or "all" (default). Or can be simple,common,all to build multiple. For example,',
+            descIndent + '`--type ""` or `--type "common"`.'
+        ].join('\n'))
+        .option(
+            '--format <format>',
+            'The format of output bundle. Can be "umd", "amd", "iife", "cjs", "esm".'
+        )
+        .parse(process.argv);
+
+    let isPrePublish = !!commander.prepublish;
+    let buildType = commander.type || 'all';
+
+    let opt = {
+        min: commander.min,
+        format: commander.format || 'umd'
+    };
+
+    validateIO(opt.input, opt.output);
+
+    if (isPrePublish) {
+        await prePublish();
+    }
+    else if (buildType === 'extension') {
+        const cfgs = [
+            config.createBMap(opt),
+            config.createDataTool(opt)
+        ];
+        await build(cfgs);
+    }
+    else if (buildType === 'myTransform') {
+        const cfgs = [
+            config.createMyTransform(opt)
+        ];
+        await build(cfgs);
+    }
+    else {
+        const types = buildType.split(',').map(a => a.trim());
+        const cfgs = types.map(type =>
+            config.createECharts({
+                ...opt,
+                type
+            })
+        );
+        await build(cfgs);
+    }
+}
+
+function checkBundleCode(cfg) {
+    // Make sure process.env.NODE_ENV is eliminated.
+    for (let output of cfg.output) {
+        let code = fs.readFileSync(output.file, {encoding: 'utf-8'});
+        if (!code) {
+            throw new Error(`${output.file} is empty`);
+        }
+        transformDEV.recheckDEV(code);
+        console.log(chalk.green.dim('Check code: correct.'));
+    }
+}
+
+function validateIO(input, output) {
+    if ((input != null && output == null)
+        || (input == null && output != null)
+    ) {
+        throw new Error('`input` and `output` must be both set.');
+    }
+}
+
+/**
+ * @param {Array.<Object>} configs A list of rollup configs:
+ *  See: <https://rollupjs.org/#big-list-of-options>
+ *  For example:
+ *  [
+ *      {
+ *          ...inputOptions,
+ *          output: [outputOptions],
+ *      },
+ *      ...
+ *  ]
+ */
+async function build(configs) {
+    console.log(chalk.yellow(`
+    NOTICE: If you are using 'npm run build'. Run 'npm run prepare' before build !!!
+`));
+
+    console.log(chalk.yellow(`
+    NOTICE: If you are using syslink on zrender. Run 'npm run prepare' in zrender first !!
+`));
+
+    for (let singleConfig of configs) {
+        console.log(
+            chalk.cyan.dim('\Bundling '),
+            chalk.cyan(singleConfig.input)
+        );
+
+        console.time('rollup build');
+        const bundle = await rollup.rollup(singleConfig);
+
+        for (let output of singleConfig.output) {
+            console.log(
+                chalk.green.dim('Created '),
+                chalk.green(output.file),
+                chalk.green.dim(' successfully.')
+            );
+
+            await bundle.write(output);
+
+        };
+        console.timeEnd('rollup build');
+
+        checkBundleCode(singleConfig);
+    }
+}
+
+async function main() {
+    try {
+        await run();
+    }
+    catch (err) {
+        console.log(chalk.red('BUILD ERROR!'));
+        // rollup parse error.
+        if (err) {
+            if (err.loc) {
+                console.warn(chalk.red(`${err.loc.file} (${err.loc.line}:${err.loc.column})`));
+                console.warn(chalk.red(err.message));
+            }
+            if (err.frame) {
+                console.warn(chalk.red(err.frame));
+            }
+            console.log(chalk.red(err ? err.stack : err));
+
+            err.id != null && console.warn(chalk.red(`id: ${err.id}`));
+            err.hook != null && console.warn(chalk.red(`hook: ${err.hook}`));
+            err.code != null && console.warn(chalk.red(`code: ${err.code}`));
+            err.plugin != null && console.warn(chalk.red(`plugin: ${err.plugin}`));
+        }
+        // console.log(err);
+    }
+}
+
+main();

+ 176 - 0
node_modules/echarts/build/config.js

@@ -0,0 +1,176 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+const nodeResolvePlugin = require('@rollup/plugin-node-resolve').default;
+const nodePath = require('path');
+const ecDir = nodePath.resolve(__dirname, '..');
+const {terser} = require('rollup-plugin-terser');
+const replace = require('@rollup/plugin-replace');
+const MagicString = require('magic-string');
+const preamble = require('./preamble');
+
+function createAddLicensePlugin(sourcemap) {
+    return {
+        renderChunk(code, chunk) {
+            const s = new MagicString(code);
+            s.prepend(preamble.js);
+            return {
+                code: s.toString(),
+                map: sourcemap ? s.generateMap({ hires: true }).toString() : null
+            };
+        }
+    }
+}
+
+function createOutputs(basename, { min }, commonOutputOpts) {
+    commonOutputOpts = {
+        format: 'umd',
+        ...commonOutputOpts
+    }
+    function createReplacePlugin(replacement) {
+        const plugin = replace({
+            'process.env.NODE_ENV': JSON.stringify(replacement)
+        });
+        // Remove transform hook. It will have warning when using in output
+        delete plugin.transform;
+        return plugin;
+    }
+    const output = [{
+        ...commonOutputOpts,
+        // Disable sourcemap in
+        sourcemap: true,
+        plugins: [
+            createReplacePlugin('development'),
+            createAddLicensePlugin(true)
+        ],
+        file: basename + '.js'
+    }];
+
+    if (min) {
+        output.push({
+            ...commonOutputOpts,
+            // Disable sourcemap in min file.
+            sourcemap: false,
+            // TODO preamble
+            plugins: [
+                createReplacePlugin('production'),
+                terser(),
+                createAddLicensePlugin(false)
+            ],
+            file: basename + '.min.js'
+        })
+    }
+    return output;
+}
+
+/**
+ * @param {Object} [opt]
+ * @param {string} [opt.type=''] 'all' or 'simple' or 'common', default is 'all'
+ * @param {boolean} [opt.sourcemap] If set, `opt.input` is required too, and `opt.type` is ignored.
+ * @param {string} [opt.format='umd'] If set, `opt.input` is required too, and `opt.type` is ignored.
+ * @param {string} [opt.min=false] If build minified output
+ * @param {boolean} [opt.addBundleVersion=false] Only for debug in watch, prompt that the two build is different.
+ */
+exports.createECharts = function (opt = {}) {
+    const srcType = opt.type !== 'all' ? '.' + opt.type : '';
+    const postfixType = srcType;
+    const format = opt.format || 'umd';
+    const postfixFormat = (format !== 'umd') ? '.' + format.toLowerCase() : '';
+
+    const input = nodePath.resolve(ecDir, `index${srcType}.js`);
+
+    return {
+        plugins: [nodeResolvePlugin()],
+        treeshake: {
+            moduleSideEffects: false
+        },
+
+        input: input,
+
+        output: createOutputs(
+            nodePath.resolve(ecDir, `dist/echarts${postfixFormat}${postfixType}`),
+            opt,
+            {
+                name: 'echarts',
+                // Ignore default exports, which is only for compitable code like:
+                // import echarts from 'echarts/lib/echarts';
+                exports: 'named',
+                format: format
+            }
+        )
+    };
+};
+
+exports.createBMap = function (opt) {
+    const input = nodePath.resolve(ecDir, `extension/bmap/bmap.js`);
+
+    return {
+        plugins: [nodeResolvePlugin()],
+        input: input,
+        external: ['echarts'],
+        output: createOutputs(
+            nodePath.resolve(ecDir, `dist/extension/bmap`),
+            opt,
+            {
+                name: 'bmap',
+                globals: {
+                    // For UMD `global.echarts`
+                    echarts: 'echarts'
+                }
+            }
+        )
+    };
+};
+
+exports.createDataTool = function (opt) {
+    let input = nodePath.resolve(ecDir, `extension/dataTool/index.js`);
+
+    return {
+        plugins: [nodeResolvePlugin()],
+        input: input,
+        external: ['echarts'],
+        output: createOutputs(
+            nodePath.resolve(ecDir, `dist/extension/dataTool`),
+            opt,
+            {
+                name: 'dataTool',
+                globals: {
+                    // For UMD `global.echarts`
+                    echarts: 'echarts'
+                }
+            }
+        )
+    };
+};
+
+exports.createMyTransform = function (opt) {
+    let input = nodePath.resolve(ecDir, `test/lib/myTransform/src/index.ts`);
+
+    return {
+        plugins: [nodeResolvePlugin()],
+        input: input,
+        output: createOutputs(
+            nodePath.resolve(ecDir, `test/lib/myTransform/dist/myTransform`),
+            opt,
+            {
+                name: 'myTransform'
+            }
+        )
+    };
+};

+ 67 - 0
node_modules/echarts/build/dev-fast.js

@@ -0,0 +1,67 @@
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+const path = require('path');
+const {build} = require('esbuild');
+const commander = require('commander');
+const outFilePath = path.resolve(__dirname, '../dist/echarts.js');
+
+const umdWrapperHead = `(function (root, factory) {
+    if (typeof define === 'function' && define.amd) {
+        // AMD. Register as an anonymous module.
+        define(['exports'], factory);
+    } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
+        // CommonJS
+        factory(exports);
+    } else {
+        // Browser globals
+        factory((root.echarts = {}));
+    }
+}(typeof self !== 'undefined' ? self : this, function (exports, b) {
+`;
+
+const umdWrapperTail = `
+}));`;
+
+build({
+    entryPoints: [path.resolve(__dirname, '../src/echarts.all.ts')],
+    outfile: outFilePath,
+    format: 'cjs',
+    sourcemap: true,
+    bundle: true,
+    banner: umdWrapperHead,
+    footer: umdWrapperTail,
+    define: {
+        'process.env.NODE_ENV': '"development"',
+        '__DEV__': 'true'
+    },
+    watch: {
+        async onRebuild(error) {
+            if (error) {
+                console.error('watch build failed:', error)
+            }
+            else {
+                console.log('Bundled with esbuild')
+            }
+        },
+    },
+}).then(async () => {
+    console.log('Bundled with esbuild')
+}).catch(e => console.error(e.toString()))

+ 30 - 0
node_modules/echarts/build/nightly/post.js

@@ -0,0 +1,30 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+const fs = require('fs');
+
+const root = __dirname + '/../../';
+const echartsPkg = JSON.parse(fs.readFileSync(root + 'package.json'), 'utf-8');
+const zrenderPkg = JSON.parse(fs.readFileSync(root + 'node_modules/zrender/package.json', 'utf-8'));
+
+const echartsCorePath = root + 'src/core/echarts.ts';
+const echartsCoreFile = fs.readFileSync(echartsCorePath, 'utf-8')
+    .replace(/export const version = '\S+'/, `export const version = '${echartsPkg.version}'`)
+    .replace(/(export const dependencies = {\s+zrender: ')\S+('\s+})/, `$1${zrenderPkg.version}$2`);
+fs.writeFileSync(echartsCorePath, echartsCoreFile, 'utf-8');

+ 72 - 0
node_modules/echarts/build/nightly/prepare.js

@@ -0,0 +1,72 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+
+const fs = require('fs');
+const packageJsonPath = __dirname + '/../../package.json';
+const nightlyPackageName = 'echarts-nightly';
+
+const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
+
+function updateVersion(version) {
+    const isNext = process.argv.includes('--next');
+    const parts = /(\d+)\.(\d+)\.(\d+)($|\-)/.exec(version);
+    if (!parts) {
+        throw new Error(`Invalid version number ${version}`);
+    }
+    // Add date to version.
+    const major = +parts[1];
+    let minor = +parts[2];
+    let patch = +parts[3];
+    const isStable = !parts[4];
+    if (isStable) {
+        // It's previous stable version. Dev version should be higher.
+        if (isNext) {
+            // Increase minor version for next branch.
+            minor++;
+            patch = 0;
+        }
+        else {
+            // Increase main version for master branch.
+            patch++;
+        }
+    }
+
+    const date = new Date().toISOString().replace(/:|T|\.|-/g, '').slice(0, 8);
+    return `${major}.${minor}.${patch}-dev.${date}`;
+}
+
+packageJson.name = nightlyPackageName;
+packageJson.version = updateVersion(packageJson.version);
+
+fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf-8');
+
+const readmePath = __dirname + '/../../README.md';
+const readmeAttention = `<h3>
+<p><em>⚠️ ATTENTION PLEASE</em></p>
+<p><em>This is nightly build of Apache ECharts. Please DON't use it in your production environment.</em></p>
+</h3>`;
+const readmeContent = fs.readFileSync(readmePath, 'utf-8');
+if (!readmeContent.includes(readmeAttention)) {
+    fs.writeFileSync(readmePath, `
+${readmeAttention}
+
+${readmeContent}
+`, 'utf-8');
+}

+ 428 - 0
node_modules/echarts/build/pre-publish.js

@@ -0,0 +1,428 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+/**
+ * [Create CommonJS files]:
+ * Compatible with prevoius folder structure: `echarts/lib` exists in `node_modules`
+ * (1) Build all files to CommonJS to `echarts/lib`.
+ * (2) Remove __DEV__.
+ * (3) Mount `echarts/src/export.js` to `echarts/lib/echarts.js`.
+ *
+ * [Create ESModule files]:
+ * Build all files to CommonJS to `echarts/esm`.
+ */
+
+const nodePath = require('path');
+const assert = require('assert');
+const fs = require('fs');
+const fsExtra = require('fs-extra');
+const chalk = require('chalk');
+const ts = require('typescript');
+const globby = require('globby');
+const transformDEVUtil = require('./transform-dev');
+const preamble = require('./preamble');
+const dts = require('@lang/rollup-plugin-dts').default;
+const rollup = require('rollup');
+const { transformImport } = require('zrender/build/transformImport');
+
+const ecDir = nodePath.resolve(__dirname, '..');
+const tmpDir = nodePath.resolve(ecDir, 'pre-publish-tmp');
+
+const tsConfig = readTSConfig();
+
+const autoGeneratedFileAlert = `
+/**
+ * AUTO-GENERATED FILE. DO NOT MODIFY.
+ */
+
+`;
+
+const mainSrcGlobby = {
+    patterns: [
+        'src/**/*.ts'
+    ],
+    cwd: ecDir
+};
+const extensionSrcGlobby = {
+    patterns: [
+        'extension-src/**/*.ts'
+    ],
+    cwd: ecDir
+};
+const extensionSrcDir = nodePath.resolve(ecDir, 'extension-src');
+const extensionESMDir = nodePath.resolve(ecDir, 'extension');
+
+const typesDir = nodePath.resolve(ecDir, 'types');
+const esmDir = 'lib';
+
+
+const compileWorkList = [
+    {
+        logLabel: 'main ts -> js-esm',
+        compilerOptionsOverride: {
+            module: 'ES2015',
+            rootDir: ecDir,
+            outDir: tmpDir,
+            // Generate types when buidling esm
+            declaration: true,
+            declarationDir: typesDir
+        },
+        srcGlobby: mainSrcGlobby,
+        transformOptions: {
+            filesGlobby: {patterns: ['**/*.js'], cwd: tmpDir},
+            preamble: preamble.js,
+            transformDEV: true
+        },
+        before: async function () {
+            fsExtra.removeSync(tmpDir);
+            fsExtra.removeSync(nodePath.resolve(ecDir, 'types'));
+            fsExtra.removeSync(nodePath.resolve(ecDir, esmDir));
+            fsExtra.removeSync(nodePath.resolve(ecDir, 'index.js'));
+            fsExtra.removeSync(nodePath.resolve(ecDir, 'index.blank.js'));
+            fsExtra.removeSync(nodePath.resolve(ecDir, 'index.common.js'));
+            fsExtra.removeSync(nodePath.resolve(ecDir, 'index.simple.js'));
+        },
+        after: async function () {
+            fs.renameSync(nodePath.resolve(tmpDir, 'src/echarts.all.js'), nodePath.resolve(ecDir, 'index.js'));
+            fs.renameSync(nodePath.resolve(tmpDir, 'src/echarts.blank.js'), nodePath.resolve(ecDir, 'index.blank.js'));
+            fs.renameSync(nodePath.resolve(tmpDir, 'src/echarts.common.js'), nodePath.resolve(ecDir, 'index.common.js'));
+            fs.renameSync(nodePath.resolve(tmpDir, 'src/echarts.simple.js'), nodePath.resolve(ecDir, 'index.simple.js'));
+            fs.renameSync(nodePath.resolve(tmpDir, 'src'), nodePath.resolve(ecDir, esmDir));
+
+            transformRootFolderInEntry(nodePath.resolve(ecDir, 'index.js'), esmDir);
+            transformRootFolderInEntry(nodePath.resolve(ecDir, 'index.blank.js'), esmDir);
+            transformRootFolderInEntry(nodePath.resolve(ecDir, 'index.common.js'), esmDir);
+            transformRootFolderInEntry(nodePath.resolve(ecDir, 'index.simple.js'), esmDir);
+
+            await transformLibFiles(nodePath.resolve(ecDir, esmDir), esmDir);
+            await transformLibFiles(nodePath.resolve(ecDir, 'types'), esmDir);
+            fsExtra.removeSync(tmpDir);
+        }
+    },
+    {
+        logLabel: 'extension ts -> js-esm',
+        compilerOptionsOverride: {
+            module: 'ES2015',
+            declaration: false,
+            rootDir: extensionSrcDir,
+            outDir: extensionESMDir
+        },
+        srcGlobby: extensionSrcGlobby,
+        transformOptions: {
+            filesGlobby: {patterns: ['**/*.js'], cwd: extensionESMDir},
+            preamble: preamble.js,
+            transformDEV: true
+        },
+        before: async function () {
+            fsExtra.removeSync(extensionESMDir);
+        },
+        after: async function () {
+            await transformLibFiles(extensionESMDir, 'lib');
+        }
+    }
+];
+
+
+
+/**
+ * @public
+ */
+module.exports = async function () {
+
+    for (let {
+        logLabel, compilerOptionsOverride, srcGlobby,
+        transformOptions, before, after
+    } of compileWorkList) {
+
+        process.stdout.write(chalk.green.dim(`[${logLabel}]: compiling ...`));
+
+        before && await before();
+
+        let srcPathList = await readFilePaths(srcGlobby);
+
+        await tsCompile(compilerOptionsOverride, srcPathList);
+
+        process.stdout.write(chalk.green.dim(` done \n`));
+
+        process.stdout.write(chalk.green.dim(`[${logLabel}]: transforming ...`));
+
+        await transformCode(transformOptions);
+
+        after && await after();
+
+        process.stdout.write(chalk.green.dim(` done \n`));
+    }
+
+    process.stdout.write(chalk.green.dim(`Generating entries ...`));
+    generateEntries();
+    process.stdout.write(chalk.green.dim(`Bundling DTS ...`));
+    await bundleDTS();
+
+    console.log(chalk.green.dim('All done.'));
+};
+
+async function runTsCompile(localTs, compilerOptions, srcPathList) {
+    // Must do it. becuase the value in tsconfig.json might be different from the inner representation.
+    // For example: moduleResolution: "NODE" => moduleResolution: 2
+    const {options, errors} = localTs.convertCompilerOptionsFromJson(compilerOptions, ecDir);
+
+    if (errors.length) {
+        let errMsg = 'tsconfig parse failed: '
+            + errors.map(error => error.messageText).join('. ')
+            + '\n compilerOptions: \n' + JSON.stringify(compilerOptions, null, 4);
+        assert(false, errMsg);
+    }
+
+    // See: https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API
+
+    let program = localTs.createProgram(srcPathList, options);
+    let emitResult = program.emit();
+
+    let allDiagnostics = localTs
+        .getPreEmitDiagnostics(program)
+        .concat(emitResult.diagnostics);
+
+    allDiagnostics.forEach(diagnostic => {
+        if (diagnostic.file) {
+            let {line, character} = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
+            let message = localTs.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
+            console.log(chalk.red(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`));
+        }
+        else {
+            console.log(chalk.red(localTs.flattenDiagnosticMessageText(diagnostic.messageText, '\n')));
+        }
+    });
+    if (allDiagnostics.length > 0) {
+        throw new Error('TypeScript Compile Failed')
+    }
+}
+module.exports.runTsCompile = runTsCompile;
+
+async function tsCompile(compilerOptionsOverride, srcPathList) {
+    assert(
+        compilerOptionsOverride
+        && compilerOptionsOverride.module
+        && compilerOptionsOverride.rootDir
+        && compilerOptionsOverride.outDir
+    );
+
+    let compilerOptions = {
+        ...tsConfig.compilerOptions,
+        ...compilerOptionsOverride,
+        sourceMap: false
+    };
+
+    runTsCompile(ts, compilerOptions, srcPathList);
+}
+
+/**
+ * Transform import/require path in the entry file to `esm` or `lib`.
+ */
+function transformRootFolderInEntry(entryFile, replacement) {
+    let code = fs.readFileSync(entryFile, 'utf-8');
+    // Simple regex replacement
+    // TODO More robust way?
+    assert(
+        !/(import\s+|from\s+|require\(\s*)["']\.\/echarts\./.test(code)
+        && !/(import\s+|from\s+|require\(\s*)["']echarts\./.test(code),
+        'Import echarts.xxx.ts is not supported.'
+    );
+    code = code.replace(/((import\s+|from\s+|require\(\s*)["'])\.\//g, `$1./${replacement}/`);
+    fs.writeFileSync(
+        entryFile,
+        // Also transform zrender.
+        singleTransformImport(code, replacement),
+        'utf-8'
+    );
+}
+
+/**
+ * Transform `zrender/src` to `zrender/lib` in all files
+ */
+async function transformLibFiles(rooltFolder, replacement) {
+    const files = await readFilePaths({
+        patterns: ['**/*.js', '**/*.d.ts'],
+        cwd: rooltFolder
+    });
+    // Simple regex replacement
+    // TODO More robust way?
+    for (let fileName of files) {
+        let code = fs.readFileSync(fileName, 'utf-8');
+        code = singleTransformImport(code, replacement);
+        // For lower ts version, not use import type
+        // TODO Use https://github.com/sandersn/downlevel-dts ?
+        // if (fileName.endsWith('.d.ts')) {
+        //     code = singleTransformImportType(code);
+        // }
+        fs.writeFileSync(fileName, code, 'utf-8');
+    }
+}
+
+/**
+ * 1. Transform zrender/src to zrender/lib
+ * 2. Add .js extensions
+ */
+function singleTransformImport(code, replacement) {
+    return transformImport(
+        code.replace(/([\"\'])zrender\/src\//g, `$1zrender/${replacement}/`),
+        (moduleName) => {
+            // Ignore 'tslib' and 'echarts' in the extensions.
+            if (moduleName === 'tslib' || moduleName === 'echarts') {
+                return moduleName;
+            }
+            else if (moduleName === 'zrender/lib/export') {
+                throw new Error('Should not import the whole zrender library.');
+            }
+            else if (moduleName.endsWith('.ts')) {
+                // Replace ts with js
+                return moduleName.replace(/\.ts$/, '.js');
+            }
+            else if (moduleName.endsWith('.js')) {
+                return moduleName;
+            }
+            else {
+                return moduleName + '.js'
+            }
+        }
+    );
+}
+
+// function singleTransformImportType(code) {
+//     return code.replace(/import\s+type\s+/g, 'import ');
+// }
+
+/**
+ * @param {Object} transformOptions
+ * @param {Object} transformOptions.filesGlobby {patterns: string[], cwd: string}
+ * @param {string} [transformOptions.preamble] See './preamble.js'
+ * @param {boolean} [transformOptions.transformDEV]
+ */
+async function transformCode({filesGlobby, preamble, transformDEV}) {
+
+    let filePaths = await readFilePaths(filesGlobby);
+
+    filePaths.map(filePath => {
+        let code = fs.readFileSync(filePath, 'utf8');
+
+        if (transformDEV) {
+            let result = transformDEVUtil.transform(code, false);
+            code = result.code;
+        }
+
+        code = autoGeneratedFileAlert + code;
+
+        if (preamble) {
+            code = preamble + code;
+        }
+
+        fs.writeFileSync(filePath, code, 'utf8');
+    });
+}
+
+async function readFilePaths({patterns, cwd}) {
+    assert(patterns && cwd);
+    return (
+        await globby(patterns, {cwd})
+    ).map(
+        srcPath => nodePath.resolve(cwd, srcPath)
+    );
+}
+
+async function bundleDTS() {
+
+    const outDir = nodePath.resolve(__dirname, '../types/dist');
+    const commonConfig = {
+        onwarn(warning, rollupWarn) {
+            // Not warn circular dependency
+            if (warning.code !== 'CIRCULAR_DEPENDENCY') {
+                rollupWarn(warning);
+            }
+        },
+        plugins: [
+            dts({
+                respectExternal: true
+            })
+//             {
+//                 generateBundle(options, bundle) {
+//                     for (let chunk of Object.values(bundle)) {
+//                         chunk.code = `
+// type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
+// ${chunk.code}`
+//                     }
+//                 }
+//             }
+        ]
+    };
+
+    // Bundle chunks.
+    const parts = [
+        'core', 'charts', 'components', 'renderers', 'option', 'features'
+    ];
+    const inputs = {};
+    parts.forEach(partName => {
+        inputs[partName] = nodePath.resolve(__dirname, `../types/src/export/${partName}.d.ts`)
+    });
+
+    const bundle = await rollup.rollup({
+        input: inputs,
+        ...commonConfig
+    });
+    let idx = 1;
+    await bundle.write({
+        dir: outDir,
+        minifyInternalExports: false,
+        manualChunks: (id) => {
+            // Only create one chunk.
+            return 'shared';
+        },
+        chunkFileNames: 'shared.d.ts'
+    });
+
+    // Bundle all in one
+    const bundleAllInOne = await rollup.rollup({
+        input: nodePath.resolve(__dirname, `../types/src/export/all.d.ts`),
+        ...commonConfig
+    });
+    await bundleAllInOne.write({
+        file: nodePath.resolve(outDir, 'echarts.d.ts')
+    });
+}
+
+function readTSConfig() {
+    // tsconfig.json may have comment string, which is invalid if
+    // using `require('tsconfig.json'). So we use a loose parser.
+    let filePath = nodePath.resolve(ecDir, 'tsconfig.json');
+    const tsConfigText = fs.readFileSync(filePath, {encoding: 'utf8'});
+    return (new Function(`return ( ${tsConfigText} )`))();
+}
+
+
+function generateEntries() {
+    ['charts', 'components', 'renderers', 'core', 'features'].forEach(entryName => {
+        if (entryName !== 'option') {
+            const jsCode = fs.readFileSync(nodePath.join(__dirname, `template/${entryName}.js`), 'utf-8');
+            fs.writeFileSync(nodePath.join(__dirname, `../${entryName}.js`), jsCode, 'utf-8');
+        }
+
+        const dtsCode = fs.readFileSync(nodePath.join(__dirname, `/template/${entryName}.d.ts`), 'utf-8');
+        fs.writeFileSync(nodePath.join(__dirname, `../${entryName}.d.ts`), dtsCode, 'utf-8');
+    });
+}
+
+module.exports.readTSConfig = readTSConfig;

+ 231 - 0
node_modules/echarts/build/preamble.js

@@ -0,0 +1,231 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+const cStyleComment = `
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+`;
+
+const hashComment = `
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+`;
+
+const mlComment = `
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+`;
+
+function hasPreamble(fileExt) {
+    return fileExt && preambleMap[fileExt];
+}
+
+function addPreamble(fileStr, fileExt) {
+    if (fileStr && fileExt) {
+        const addFn = addFns[fileExt];
+        const headStr = preambleMap[fileExt];
+        return addFn && headStr && addFn(headStr, fileStr);
+    }
+}
+
+const addFns = {
+
+    ts: function (headStr, fileStr) {
+        return headStr + fileStr;
+    },
+
+    js: function (headStr, fileStr) {
+        return headStr + fileStr;
+    },
+
+    css: function (headStr, fileStr) {
+        return headStr + fileStr;
+    },
+
+    java: function (headStr, fileStr) {
+        return headStr + fileStr;
+    },
+
+    yml: addShellLikeHeader,
+
+    yaml: addShellLikeHeader,
+
+    sh: addShellLikeHeader,
+
+    html: function (headStr, fileStr) {
+        // Git diff enables manual check.
+        let resultStr = fileStr.replace(/^\s*<!DOCTYPE\s[^<>]+>/i, '$&' + headStr);
+        // If no doctype
+        if (resultStr.length === fileStr.length) {
+            resultStr = headStr + fileStr;
+        }
+        return resultStr;
+    },
+
+    xml: xmlAddFn,
+
+    xsl: xmlAddFn
+};
+
+function addShellLikeHeader(headStr, fileStr) {
+    // Git diff enables manual check.
+    if (/^#\!/.test(fileStr)) {
+        const lines = fileStr.split('\n');
+        lines.splice(1, 0, headStr);
+        return lines.join('\n');
+    }
+    else {
+        return headStr + fileStr;
+    }
+}
+
+function xmlAddFn(headStr, fileStr) {
+    // Git diff enables manual check.
+    let resultStr = fileStr.replace(/^\s*<\?xml\s[^<>]+\?>/i, '$&' + headStr);
+    // If no <?xml version='1.0' ?>
+    if (resultStr.length === fileStr.length) {
+        resultStr = headStr + fileStr;
+    }
+    return resultStr;
+}
+
+const preambleMap = {
+    ts: cStyleComment,
+    js: cStyleComment,
+    css: cStyleComment,
+    java: cStyleComment,
+    yml: hashComment,
+    yaml: hashComment,
+    sh: hashComment,
+    html: mlComment,
+    xml: mlComment,
+    xsl: mlComment
+};
+
+const licenseReg = [
+    {name: 'Apache', reg: /apache (license|commons)/i},
+    {name: 'BSD', reg: /BSD/},
+    {name: 'LGPL', reg: /LGPL/},
+    {name: 'GPL', reg: /GPL/},
+    {name: 'Mozilla', reg: /mozilla public/i},
+    {name: 'MIT', reg: /mit license/i},
+    {name: 'BSD-d3', reg: /Copyright\s+\(c\)\s+2010-2015,\s+Michael\s+Bostock/i}
+];
+
+function extractLicense(fileStr, fileExt) {
+    let commentText = extractComment(fileStr.trim(), fileExt);
+    if (!commentText) {
+        return;
+    }
+    for (let i = 0; i < licenseReg.length; i++) {
+        if (licenseReg[i].reg.test(commentText)) {
+            return licenseReg[i].name;
+        }
+    }
+}
+
+const cStyleCommentReg = /\/\*[\S\s]*?\*\//;
+const hashCommentReg = /^\s*#.*$/gm;
+const mlCommentReg = /<\!\-\-[\S\s]*?\-\->/;
+const commentReg = {
+    ts: cStyleCommentReg,
+    js: cStyleCommentReg,
+    css: cStyleCommentReg,
+    java: cStyleCommentReg,
+    yml: hashCommentReg,
+    yaml: hashCommentReg,
+    sh: hashCommentReg,
+    html: mlCommentReg,
+    xml: mlCommentReg,
+    xsl: mlCommentReg
+};
+
+function extractComment(str, fileExt) {
+    const reg = commentReg[fileExt];
+
+    if (!fileExt || !reg || !str) {
+        return;
+    }
+
+    reg.lastIndex = 0;
+
+    if (fileExt === 'sh' || fileExt === 'yml' || fileExt === 'yaml') {
+        let result = str.match(reg);
+        return result && result.join('\n');
+    }
+    else {
+        let result = reg.exec(str);
+        return result && result[0];
+    }
+}
+
+module.exports = Object.assign({
+    extractLicense,
+    hasPreamble,
+    addPreamble
+}, preambleMap);

+ 164 - 0
node_modules/echarts/build/source-release/prepareReleaseMaterials.js

@@ -0,0 +1,164 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+
+// Prepare release materials like mail, release note
+
+const commander = require('commander');
+const fse = require('fs-extra');
+const pathTool = require('path');
+const https = require('https');
+
+commander
+    .usage('[options]')
+    .description([
+        'Generate source release'
+    ].join('\n'))
+    .option(
+        '--rcversion <version>',
+        'Release version'
+    )
+    .option(
+        '--commit <commit>',
+        'Hash of commit'
+    )
+    .option(
+        '--repo <repo>',
+        'Repo'
+    )
+    .option(
+        '--out <out>',
+        'Out directory. Default to be tmp/release-mail'
+    )
+    .parse(process.argv);
+
+const outDir = pathTool.resolve(process.cwd(), commander.out || 'tmp/release-materials');
+const releaseCommit = commander.commit;
+if (!releaseCommit) {
+    throw new Error('Release commit is required');
+}
+const repo = commander.repo || 'apache/echarts';
+
+let rcVersion = commander.rcversion + '';
+if (rcVersion.startsWith('v')) {   // tag may have v prefix, v5.1.0
+    rcVersion = rcVersion.substr(1);
+}
+if (rcVersion.indexOf('-rc.') < 0) {
+    throw new Error('Only rc version is accepeted.');
+}
+
+const parts = /(\d+)\.(\d+)\.(\d+)\-rc\.(\d+)/.exec(rcVersion);
+if (!parts) {
+    throw new Error(`Invalid version number ${rcVersion}`);
+}
+
+const major = +parts[1];
+const minor = +parts[2];
+const patch = +parts[3];
+const rc = +parts[4];
+
+const stableVersion = `${major}.${minor}.${patch}`;
+const releaseFullName = `Apache ECharts ${stableVersion} (release candidate ${rc})`;
+
+console.log('[Release Repo] ' + repo);
+console.log('[Release Verion] ' + rcVersion);
+console.log('[Release Commit] ' + releaseCommit);
+console.log('[Release Name] ' + releaseFullName);
+
+const voteTpl = fse.readFileSync(pathTool.join(__dirname, './template/vote-release.tpl'), 'utf-8');
+const voteResultTpl = fse.readFileSync(pathTool.join(__dirname, './template/vote-result.tpl'), 'utf-8');
+const announceTpl = fse.readFileSync(pathTool.join(__dirname, './template/announce-release.tpl'), 'utf-8');
+const voteUntil = new Date(+new Date() + (72 + 12) * 3600 * 1000);   // 3.5 day.
+
+fse.ensureDirSync(outDir);
+fse.writeFileSync(
+    pathTool.resolve(outDir, 'vote.txt'),
+    voteTpl.replace(/{{ECHARTS_RELEASE_VERSION}}/g, rcVersion)
+        .replace(/{{ECHARTS_RELEASE_VERSION_FULL_NAME}}/g, releaseFullName)
+        .replace(/{{ECHARTS_RELEASE_COMMIT}}/g, releaseCommit)
+        .replace(/{{VOTE_UNTIL}}/g, voteUntil.toISOString()),
+    'utf-8'
+);
+
+fse.ensureDirSync(outDir);
+fse.writeFileSync(
+    pathTool.resolve(outDir, 'vote-result.txt'),
+    voteResultTpl.replace(/{{ECHARTS_RELEASE_VERSION}}/g, rcVersion)
+        .replace(/{{ECHARTS_RELEASE_VERSION_FULL_NAME}}/g, releaseFullName),
+    'utf-8'
+);
+
+fse.writeFileSync(
+    pathTool.resolve(outDir, 'announce.txt'),
+    announceTpl.replace(/{{ECHARTS_RELEASE_VERSION}}/g, stableVersion)
+        .replace(/{{ECHARTS_RELEASE_COMMIT}}/g, releaseCommit),
+    'utf-8'
+);
+
+
+// Fetch RELEASE_NOTE
+https.get({
+    hostname: 'api.github.com',
+    path: `/repos/${repo}/releases`,
+    headers: {
+        'User-Agent': 'NodeJS'
+    }
+}, function (res) {
+    console.log(`https://api.github.com/repos/${repo}/releases`);
+    if (res.statusCode !== 200) {
+        console.error(`Failed to fetch releases ${res.statusCode}`);
+        res.resume();
+        return;
+    }
+
+    res.setEncoding('utf8');
+    let rawData = '';
+    res.on('data', (chunk) => {
+        rawData += chunk;
+    });
+    res.on('end', () => {
+        let releaseNote = '';
+
+        const releases = JSON.parse(rawData);
+        const found = releases.find(release => release.name === rcVersion);
+        if (!found) {
+            throw 'Can\'t found release';
+        }
+        else {
+            releaseNote = found.body.trim();
+            if (!releaseNote) {
+                throw 'Release description is empty';
+            }
+        }
+
+        const firstLine = releaseNote.split('\n')[0];
+        if (firstLine.indexOf(stableVersion) < 0) {
+            // Add version if release note is not start with version.
+        }
+        releaseNote = `## ${stableVersion}\n\n${releaseNote}`;
+
+        fse.writeFileSync(
+            pathTool.resolve(outDir, 'RELEASE_NOTE.txt'),
+            releaseNote,
+            'utf-8'
+        );
+    });
+  }).on('error', (e) => {
+      throw e;
+  });

+ 26 - 0
node_modules/echarts/build/source-release/template/announce-release.tpl

@@ -0,0 +1,26 @@
+--- Mail to: ---
+dev@echarts.apache.org
+------------------------------------------------------------------------------
+
+--- Subject: ---
+[ANNOUNCE] Release Apache ECharts {{ECHARTS_RELEASE_VERSION}}
+------------------------------------------------------------------------------
+
+
+Hi all,
+
+The Apache ECharts team is proud to announce Apache ECharts {{ECHARTS_RELEASE_VERSION}}.
+
+Apache ECharts is a powerful, interactive charting and data visualization library for browser.
+
+Download Links: https://echarts.apache.org/download.html
+
+Release Notes: https://echarts.apache.org/changelog.html
+
+Website: https://echarts.apache.org/
+
+
+ECharts Resources:
+- Issue: https://github.com/apache/echarts/issues
+- Build Guide: https://github.com/apache/echarts/blob/{{ECHARTS_RELEASE_VERSION}}/README.md
+- Mailing list: dev@echarts.apache.org

+ 44 - 0
node_modules/echarts/build/source-release/template/vote-release.tpl

@@ -0,0 +1,44 @@
+--- Mail To: ---
+dev@echarts.apache.org
+-----------------------------------------------------------
+
+--- Subject: ---
+[VOTE] Release {{ECHARTS_RELEASE_VERSION_FULL_NAME}}
+-----------------------------------------------------------
+
+Dear community,
+
+We are pleased to be calling this vote for the release of {{ECHARTS_RELEASE_VERSION_FULL_NAME}}.
+
+The release candidate to be voted over is available at:
+https://dist.apache.org/repos/dist/dev/echarts/{{ECHARTS_RELEASE_VERSION}}/
+
+The release candidate is signed with a GPG key available at:
+https://dist.apache.org/repos/dist/dev/echarts/KEYS
+
+The Git commit for this release is:
+https://gitbox.apache.org/repos/asf?p=echarts.git;a=commit;h={{ECHARTS_RELEASE_COMMIT}}
+
+The Release Note is available in:
+https://dist.apache.org/repos/dist/dev/echarts/{{ECHARTS_RELEASE_VERSION}}/RELEASE_NOTE.txt
+
+Build Guide:
+https://github.com/apache/echarts/blob/{{ECHARTS_RELEASE_VERSION}}/README.md#build
+
+NPM Install:
+npm i echarts@{{ECHARTS_RELEASE_VERSION}}
+https://www.npmjs.com/package/echarts/v/{{ECHARTS_RELEASE_VERSION}}
+
+Please vote on releasing this package as:
+{{ECHARTS_RELEASE_VERSION_FULL_NAME}}
+by "{{VOTE_UNTIL}}".
+
+[ ] +1 Release this package
+[ ] 0 I don't feel strongly about it, but don't object
+[ ] -1 Do not release this package because...
+
+Anyone can participate in testing and voting, not just committers, please
+feel free to try out the release candidate and provide your votes.
+
+A checklist for reference:
+https://cwiki.apache.org/confluence/display/ECHARTS/Apache+ECharts+Release+Checklist

+ 23 - 0
node_modules/echarts/build/source-release/template/vote-result.tpl

@@ -0,0 +1,23 @@
+--- Mail To: ---
+dev@echarts.apache.org
+-----------------------------------------------------------
+
+--- Subject: ---
+[RESULT] [VOTE] Release {{ECHARTS_RELEASE_VERSION_FULL_NAME}}
+-----------------------------------------------------------
+
+Thanks to all who voted or provided comments!
+
+We received ______NUMBER_OF_+1_VOTES______ +1 votes from the PMC members, and the release has PASSED:
+
++1 ______NAME______ (binding)
+
+Other votes from the community:
+
++1 ______NAME______
+
+Vote thread:
+https://lists.apache.org/thread/xxx
+
+I'm going to release the source release of Apache ECharts {{ECHARTS_RELEASE_VERSION}}.
+Thank you all for making this happen!

+ 20 - 0
node_modules/echarts/build/template/charts.d.ts

@@ -0,0 +1,20 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+export * from './types/dist/charts';

+ 27 - 0
node_modules/echarts/build/template/charts.js

@@ -0,0 +1,27 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+
+// In somehow. If we export like
+// export * as LineChart './chart/line/install'
+// The exported code will be transformed to
+// import * as LineChart_1 './chart/line/install'; export {LineChart_1 as LineChart};
+// Treeshaking in webpack will not work even if we configured sideEffects to false in package.json
+
+export * from './lib/export/charts.js';

+ 20 - 0
node_modules/echarts/build/template/components.d.ts

@@ -0,0 +1,20 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+export * from './types/dist/components';

+ 20 - 0
node_modules/echarts/build/template/components.js

@@ -0,0 +1,20 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+export * from './lib/export/components.js';

+ 20 - 0
node_modules/echarts/build/template/core.d.ts

@@ -0,0 +1,20 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+export * from './types/dist/core';

+ 20 - 0
node_modules/echarts/build/template/core.js

@@ -0,0 +1,20 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+export * from './lib/export/core.js';

+ 20 - 0
node_modules/echarts/build/template/features.d.ts

@@ -0,0 +1,20 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+export * from './types/dist/features';

+ 20 - 0
node_modules/echarts/build/template/features.js

@@ -0,0 +1,20 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+export * from './lib/export/features.js';

+ 20 - 0
node_modules/echarts/build/template/option.d.ts

@@ -0,0 +1,20 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+export * from './types/dist/option';

+ 20 - 0
node_modules/echarts/build/template/renderers.d.ts

@@ -0,0 +1,20 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+export * from './types/dist/renderers';

+ 20 - 0
node_modules/echarts/build/template/renderers.js

@@ -0,0 +1,20 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+export * from './lib/export/renderers.js';

+ 75 - 0
node_modules/echarts/build/testDts.js

@@ -0,0 +1,75 @@
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+const { TypeScriptVersion } = require('@definitelytyped/typescript-versions');
+const {
+    cleanTypeScriptInstalls,
+    installAllTypeScriptVersions,
+    typeScriptPath
+} = require('@definitelytyped/utils');
+const { runTsCompile } = require('./pre-publish');
+const globby = require('globby');
+const semver = require('semver');
+
+const MIN_VERSION = '3.5.0';
+
+async function installTs() {
+    // await cleanTypeScriptInstalls();
+    await installAllTypeScriptVersions();
+}
+
+async function runTests() {
+    const compilerOptions = {
+        declaration: false,
+        importHelpers: false,
+        sourceMap: false,
+        pretty: false,
+        removeComments: false,
+        allowJs: false,
+        outDir: __dirname + '/../test/types/tmp',
+        typeRoots: [__dirname + '/../types/dist'],
+        rootDir: __dirname + '/../test/types',
+
+        // Must pass in most strict cases
+        strict: true
+    };
+    const testsList = await globby(__dirname + '/../test/types/*.ts');
+
+    for (let version of TypeScriptVersion.shipped) {
+        if (semver.lt(version + '.0', MIN_VERSION)) {
+            continue;
+        }
+
+        console.log(`Testing ts version ${version}`);
+        const ts = require(typeScriptPath(version));
+        await runTsCompile(ts, compilerOptions, testsList);
+
+        console.log(`Finished test of ts version ${version}`);
+    }
+}
+
+async function main() {
+    await installTs();
+    await runTests();
+}
+
+module.exports = main;
+
+main();

+ 58 - 0
node_modules/echarts/build/transform-dev.js

@@ -0,0 +1,58 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+const babel = require('@babel/core');
+const parser = require('@babel/parser');
+
+function transformDEVPlugin ({types, template}) {
+    return {
+        visitor: {
+            Identifier: {
+                enter(path, state) {
+                    if (path.isIdentifier({ name: '__DEV__' }) && path.scope.hasGlobal('__DEV__')) {
+                        path.replaceWith(
+                            parser.parseExpression(state.opts.expr)
+                        );
+                    }
+                }
+            }
+        }
+    };
+};
+
+
+module.exports.transform = function (sourceCode, sourcemap, expr) {
+    let {code, map} = babel.transformSync(sourceCode, {
+        plugins: [ [transformDEVPlugin, {
+            expr: expr || 'process.env.NODE_ENV !== \'production\''
+        }] ],
+        compact: false,
+        sourceMaps: sourcemap
+    });
+
+    return {code, map};
+};
+
+/**
+ * @param {string} code
+ * @throws {Error} If check failed.
+ */
+module.exports.recheckDEV = function (code) {
+    return code.indexOf('process.env.NODE_ENV') >= 0;
+};

+ 20 - 0
node_modules/echarts/charts.d.ts

@@ -0,0 +1,20 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+export * from './types/dist/charts';

+ 27 - 0
node_modules/echarts/charts.js

@@ -0,0 +1,27 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+
+// In somehow. If we export like
+// export * as LineChart './chart/line/install'
+// The exported code will be transformed to
+// import * as LineChart_1 './chart/line/install'; export {LineChart_1 as LineChart};
+// Treeshaking in webpack will not work even if we configured sideEffects to false in package.json
+
+export * from './lib/export/charts.js';

+ 20 - 0
node_modules/echarts/components.d.ts

@@ -0,0 +1,20 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+export * from './types/dist/components';

+ 20 - 0
node_modules/echarts/components.js

@@ -0,0 +1,20 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+export * from './lib/export/components.js';

+ 20 - 0
node_modules/echarts/core.d.ts

@@ -0,0 +1,20 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+export * from './types/dist/core';

Vissa filer visades inte eftersom för många filer har ändrats