WangYiHan 11 months ago
commit
8693be6f5b
100 changed files with 20329 additions and 0 deletions
  1. 24 0
      .gitignore
  2. 20 0
      .hbuilderx/launch.json
  3. BIN
      .image/common/mall-feature.png
  4. BIN
      .image/common/mall-preview.png
  5. BIN
      .image/common/project-vs.png
  6. BIN
      .image/common/ruoyi-vue-pro-architecture.png
  7. BIN
      .image/common/ruoyi-vue-pro-biz.png
  8. BIN
      .image/common/yudao-cloud-architecture.png
  9. BIN
      .image/common/yudao-roadmap.png
  10. BIN
      .image/mall/会员详情.png
  11. BIN
      .image/mall/商品详情.png
  12. BIN
      .image/mall/营销中心.png
  13. BIN
      .image/mall/订单详情.png
  14. 194 0
      App.vue
  15. 55 0
      README.md
  16. 143 0
      api/activity.js
  17. 164 0
      api/api.js
  18. 33 0
      api/member/address.js
  19. 68 0
      api/member/auth.js
  20. 11 0
      api/member/level.js
  21. 6 0
      api/member/point.js
  22. 21 0
      api/member/signin.js
  23. 28 0
      api/member/user.js
  24. 247 0
      api/order.js
  25. 8 0
      api/pay/channel.js
  26. 11 0
      api/pay/order.js
  27. 21 0
      api/pay/wallet.js
  28. 8 0
      api/product/category.js
  29. 27 0
      api/product/comment.js
  30. 46 0
      api/product/favorite.js
  31. 24 0
      api/product/spu.js
  32. 19 0
      api/promotion/activity.js
  33. 38 0
      api/promotion/article.js
  34. 7 0
      api/promotion/banner.js
  35. 75 0
      api/promotion/bargain.js
  36. 53 0
      api/promotion/combination.js
  37. 37 0
      api/promotion/coupon.js
  38. 9 0
      api/promotion/decorate.js
  39. 31 0
      api/promotion/seckill.js
  40. 116 0
      api/public.js
  41. 191 0
      api/store.js
  42. 8 0
      api/system/area.js
  43. 6 0
      api/system/dict.js
  44. 27 0
      api/trade/afterSale.js
  45. 62 0
      api/trade/brokerage.js
  46. 33 0
      api/trade/cart.js
  47. 6 0
      api/trade/config.js
  48. 23 0
      api/trade/delivery.js
  49. 61 0
      api/trade/order.js
  50. 396 0
      api/user.js
  51. 145 0
      components/Authorize.vue
  52. 57 0
      components/Loading/index.vue
  53. 189 0
      components/addressWindow/index.vue
  54. 162 0
      components/countDown/index.vue
  55. 247 0
      components/couponListWindow/index.vue
  56. 50 0
      components/couponWindow/index.vue
  57. 260 0
      components/easy-upload/easy-upload.vue
  58. 40 0
      components/easy-upload/readme.md
  59. 37 0
      components/emptyPage.vue
  60. 140 0
      components/goodList/index.vue
  61. 107 0
      components/home/index.vue
  62. 814 0
      components/jyf-parser/jyf-parser.vue
  63. 102 0
      components/jyf-parser/libs/CssHandler.js
  64. 577 0
      components/jyf-parser/libs/MpHtmlParser.js
  65. 80 0
      components/jyf-parser/libs/config.js
  66. 35 0
      components/jyf-parser/libs/handler.sjs
  67. 44 0
      components/jyf-parser/libs/handler.wxs
  68. 476 0
      components/jyf-parser/libs/trees.vue
  69. 272 0
      components/login_mobile/index.vue
  70. 160 0
      components/login_mobile/routine_phone.vue
  71. 406 0
      components/mpvue-calendar/browser-style.css
  72. 524 0
      components/mpvue-calendar/calendarinit.js
  73. 15 0
      components/mpvue-calendar/icon.css
  74. 1153 0
      components/mpvue-calendar/mpvue-calendar.vue
  75. 394 0
      components/mpvue-calendar/style.css
  76. 160 0
      components/numberScroll.vue
  77. 136 0
      components/orderGoods/index.vue
  78. 1108 0
      components/order_addcart/index.vue
  79. 314 0
      components/payment/index.vue
  80. 151 0
      components/productConSwiper/index.vue
  81. 350 0
      components/productWindow/index.vue
  82. 115 0
      components/recommend/index.vue
  83. 43 0
      components/shareInfo/index.vue
  84. 75 0
      components/shareRedPackets/index.vue
  85. 52 0
      components/swipers/index.vue
  86. 165 0
      components/ucharts/component.vue
  87. 5658 0
      components/ucharts/ucharts.js
  88. 0 0
      components/ucharts/ucharts.min.js
  89. 546 0
      components/uni-calendar/calendar.js
  90. 152 0
      components/uni-calendar/uni-calendar-item.vue
  91. 434 0
      components/uni-calendar/uni-calendar.vue
  92. 327 0
      components/uni-calendar/util.js
  93. 156 0
      components/userEvaluation/index.vue
  94. 9 0
      components/vconsole.min.js
  95. 29 0
      config/app.js
  96. 34 0
      config/cache.js
  97. 8 0
      config/socket.js
  98. 1380 0
      js_sdk/Sansnn-uQRCode/uqrcode.js
  99. 22 0
      libs/apps.js
  100. 62 0
      libs/chat.js

+ 24 - 0
.gitignore

@@ -0,0 +1,24 @@
+.DS_Store
+node_modules
+/dist
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+build.sh
+.idea
+unpackage

+ 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"
+     }
+    ]
+}

BIN
.image/common/mall-feature.png


BIN
.image/common/mall-preview.png


BIN
.image/common/project-vs.png


BIN
.image/common/ruoyi-vue-pro-architecture.png


BIN
.image/common/ruoyi-vue-pro-biz.png


BIN
.image/common/yudao-cloud-architecture.png


BIN
.image/common/yudao-roadmap.png


BIN
.image/mall/会员详情.png


BIN
.image/mall/商品详情.png


BIN
.image/mall/营销中心.png


BIN
.image/mall/订单详情.png


+ 194 - 0
App.vue

@@ -0,0 +1,194 @@
+<script>
+	import { HTTP_REQUEST_URL } from './config/app';
+	import Auth from './libs/wechat.js';
+	import Routine from './libs/routine.js';
+	export default {
+		globalData: {
+			spid: 0,
+			code: 0,
+			isLogin: false,
+			userInfo: {},
+			MyMenus: [],
+			windowHeight: 0,
+			id: 0
+		},
+		onLaunch: function(option) {
+			let that = this;
+      // 获得全局的 window 高度
+			// #ifdef H5
+			uni.getSystemInfo({
+				success: function(res) {
+					// 首页没有title获取的整个页面的高度,里面的页面有原生标题要减掉就是视口的高度
+					// 状态栏是动态的可以拿到 标题栏是固定写死的是44px
+					let height = res.windowHeight - res.statusBarHeight - 44
+					// #ifdef H5
+					that.globalData.windowHeight = res.windowHeight + 'px'
+					// #endif
+				}
+			});
+			// #endif
+
+      // 获取导航高度;
+      uni.getSystemInfo({
+        success: function(res) {
+          that.globalData.navHeight = res.statusBarHeight * (750 / res.windowWidth) + 91;
+        }
+      });
+      // #ifdef MP
+      let menuButtonInfo = uni.getMenuButtonBoundingClientRect();
+      that.globalData.navH = menuButtonInfo.top * 2 + menuButtonInfo.height / 2;
+      // #endif
+
+			// #ifdef MP
+			if (HTTP_REQUEST_URL === '') {
+				console.error(
+					"请配置根目录下的config.js文件中的 'HTTP_REQUEST_URL'\n\n请修改开发者工具中【详情】->【AppID】改为自己的Appid\n\n请前往后台【小程序】->【小程序配置】填写自己的 appId and AppSecret"
+				);
+				return false;
+			}
+      // TODO 芋艿: 分销
+			if (option.query.hasOwnProperty('scene')) {
+				switch(option.scene){
+					case 1047: // 扫描小程序码
+					case 1048: // 长按图片识别小程序码
+					case 1049: // 手机相册选取小程序码
+					case 1001: // 直接进入小程序
+					let value = this.$util.getUrlParams(decodeURIComponent(option.query.scene));
+					let values = value.split(',');
+					if(values.length === 2){
+						let v1 = values[0].split(":");
+						if (v1[0] === 'pid') {
+							that.globalData.spid = v1[1];
+						} else{
+							that.globalData.id = v1[1];
+						}
+						let v2 = values[1].split(":");
+						if (v2[0] === 'pid') {
+							that.globalData.spid = v2[1];
+						}else{
+							that.globalData.id = v2[1];
+						}
+					}else{
+						that.globalData.spid = values[0].split(":")[1];
+					}
+					break;
+				}
+			}
+			// #endif
+
+			// #ifdef H5
+      // 静默登录的情况一:微信公众号
+			let snsapiBase = 'snsapi_base';
+			let urlData = location.pathname + location.search;
+			if (!that.$store.getters.isLogin && Auth.isWeixin()) {
+				const { code, state } = option.query;
+				if (code && code !== uni.getStorageSync('snsapiCode')
+          && location.pathname.indexOf('/pages/users/wechat_login/index') === -1) {
+          // 存储静默授权code
+					uni.setStorageSync('snsapiCode', code);
+					Auth.auth(code, state, that.$Cache.get('spread')).then(res => {
+            // TODO 芋艿:snRouter 的作用是啥
+            uni.setStorageSync('snRouter', decodeURIComponent(decodeURIComponent(option.query.back_url)));
+            // 跳转回去
+            location.replace(decodeURIComponent(decodeURIComponent(option.query.back_url)));
+          }).catch(error => {
+            this.$Cache.set('snsapiKey', code);
+            // TODO 芋艿:为什么没有 snsapiKey 就反复认证???没看懂
+            // if (!this.$Cache.has('snsapiKey')) {
+            // 	if (location.pathname.indexOf('/pages/users/wechat_login/index') === -1) {
+            // 		Auth.oAuth(snsapiBase, option.query.back_url);
+            // 	}
+            // }
+          });
+				} else {
+					if (!this.$Cache.has('snsapiKey')) {
+						if (location.pathname.indexOf('/pages/users/wechat_login/index') === -1) {
+							Auth.oAuth(snsapiBase, urlData);
+						}
+					}
+				}
+			} else {
+				if (option.query.back_url) {
+					location.replace(uni.getStorageSync('snRouter'));
+				}
+			}
+			// #endif
+
+			// #ifdef MP
+			// 静默登录的情况二:微信小程序
+			if (!this.$store.getters.isLogin) {
+				let spread = that.globalData.spid ? that.globalData.spid : 0;
+				Routine.getCode().then(code => {
+          Routine.authUserInfo(code, spread)
+            .then(res => {})
+        }).catch(res => {
+          uni.hideLoading();
+        });
+			}
+			// #endif
+		},
+		async mounted() {
+      // 读取用户的基本信息
+      if (this.$store.getters.isLogin && !this.$Cache.get('USER_INFO')) {
+        await this.$store.dispatch('USERINFO');
+      }
+		},
+		onShow: function() {
+			// #ifdef H5
+			uni.getSystemInfo({
+				success(e) {
+          // TODO 芋艿:这样是否合理???
+					/* 窗口宽度大于420px且不在PC页面且不在移动设备时跳转至 PC.html 页面 */
+					if (e.windowWidth > 420 && !window.top.isPC && !/iOS|Android/i.test(e.system)) {
+						// window.location.pathname = 'https://java.crmeb.net/';
+						/* 若你的项目未设置根目录(默认为 / 时),则使用下方代码 */
+						window.location.pathname = '/static/html/pc.html';
+					}
+				}
+			})
+			// #endif
+		}
+	}
+</script>
+<style>
+	@import url("@/plugin/animate/animate.min.css");
+	@import 'static/css/base.css';
+	@import 'static/iconfont/iconfont.css';
+	@import 'static/css/guildford.css';
+	@import 'static/css/style.scss';
+
+	/* 条件编译,仅在H5平台生效 */
+	// #ifdef H5
+	body::-webkit-scrollbar,
+	html::-webkit-scrollbar {
+		display: none;
+	}
+
+	// #endif
+	view {
+		box-sizing: border-box;
+	}
+
+	.bg-color-red {
+		background-color: #E93323 !important;
+	}
+
+	.syspadding {
+		padding-top: var(--status-bar-height);
+	}
+
+	.flex {
+		display: flex;
+	}
+
+	.uni-scroll-view::-webkit-scrollbar {
+		/* 隐藏滚动条,但依旧具备可以滚动的功能 */
+		display: none
+	}
+
+	::-webkit-scrollbar {
+		width: 0;
+		height: 0;
+		color: transparent;
+	}
+</style>

+ 55 - 0
README.md

@@ -0,0 +1,55 @@
+**严肃声明:现在、未来都不会有商业版本,所有代码全部开源!!**
+
+**「我喜欢写代码,乐此不疲」**  
+**「我喜欢做开源,以此为乐」**
+
+我 🐶 在上海艰苦奋斗,早中晚在 top3 大厂认真搬砖,夜里为开源做贡献。
+
+如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。
+
+## 🐶 新手必读
+
+* 演示地址:<https://doc.iocoder.cn/mall-preview/>
+* 启动文档:<https://doc.iocoder.cn/quick-start/>
+* 视频教程:<https://doc.iocoder.cn/video/>
+
+## 🐯 商城简介
+
+**芋道商城**,基于 [芋道开发平台](https://github.com/YunaiV/ruoyi-vue-pro) 构建,以开发者为中心,打造中国第一流的 Java 开源商城系统,全部开源,个人与企业可 100% 免费使用。
+
+> 有任何问题,或者想要的功能,可以在 Issues 中提给艿艿。
+>
+> 😜 给项目点点 Star 吧,这对我们真的很重要!
+
+![功能图](/.image/common/mall-feature.png)
+
+* 基于 uniapp 开发,支持微信小程序、微信公众号、H5 移动端,未来会支持支付宝小程序、抖音小程序等
+* 支持 SaaS 多租户,可满足商品、订单、支付、会员、优惠券、秒杀、拼团、砍价、分销、积分等多种经营需求
+
+## 🔥 后端架构
+
+支持 Spring Boot、Spring Cloud 两种架构:
+
+① Spring Boot 单体架构:<https://github.com/YunaiV/ruoyi-vue-pro>
+
+![架构图](/.image/common/ruoyi-vue-pro-architecture.png)
+
+② Spring Cloud 微服务架构:<https://github.com/YunaiV/yudao-cloud>
+
+![架构图](/.image/common/yudao-cloud-architecture.png)
+
+## 🐱 移动端预览
+
+![移动端预览](/.image/common/mall-preview.png)
+
+_前端基于 crmeb uniapp 经过授权重构,优化代码实现_
+
+## 🐶 管理端预览
+
+![会员详情](/.image/mall/会员详情.png)
+
+![商品详情](/.image/mall/商品详情.png)
+
+![订单详情](/.image/mall/订单详情.png)
+
+![营销中心](/.image/mall/营销中心.png)

+ 143 - 0
api/activity.js

@@ -0,0 +1,143 @@
+import request from "@/utils/request.js";
+/**
+ *
+ * 所有活动接口 包括:拼团,砍价,秒杀
+ *
+*/
+
+/**
+ * 拼团列表
+ *
+*/
+export function getCombinationList(data) {
+  return request.get('combination/list', data,{noAuth:true});
+}
+
+/**
+ * 拼团 开团
+ */
+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 object data
+*/
+export function getBargainUserList(data){
+  return request.get('bargain/record',data);
+}
+
+
+/**
+ *
+ * 取消砍价
+ * @param int bargainId
+*/
+export function bargainUserCancel(bargainId){
+  return request.post('bargain/user/cancel', { bargainId: bargainId})
+}
+
+/**
+ * 砍价产品详情
+ */
+export function getBargainDetail(id) {
+  return request.get("bargain/detail/" + id);
+}
+
+/**
+ * 用户砍价信息,注意自己的活动和别人的活动的区别
+ */
+export function getBargainUser(data) {
+  return request.get("bargain/user", data);
+}
+
+
+/**
+ * 砍价开启
+ */
+export function postBargainStart(bargainId) {
+  return request.post("bargain/start", { bargainId: bargainId});
+}
+
+/**
+ * 砍价 帮助好友砍价
+ */
+export function postBargainHelp(data) {
+  return request.post("bargain/help", data);
+}
+
+/**
+ * 砍价 砍掉金额
+ */
+export function postBargainHelpPrice(data) {
+  return request.post("bargain/help/price", data);
+}
+
+/**
+ * 砍价 砍价帮
+ */
+export function postBargainHelpList(params,data) {
+  return request.get("bargain/help/list?limit="+params.limit+"&page="+params.page, data,{});
+}
+
+/**
+ * 砍价海报
+ * @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 getBargainUserCancel(data) {
+  return request.post("/bargain/user/cancel", data);
+}
+
+/**
+ * 获取秒杀小程序二维码
+ */
+export function seckillCode(id,data) {
+  return request.get("seckill/code/"+id,data);
+}
+
+/**
+ * 获取拼团小程序二维码
+ */
+export function scombinationCode(id) {
+  return request.get("combination/code/"+id);
+}
+
+/**
+ * 首页砍价产品列表
+*/
+export function bargainHeaderApi(){
+  return request.get('bargain/header');
+}

+ 164 - 0
api/api.js

@@ -0,0 +1,164 @@
+import request from "@/utils/request.js";
+/**
+ * 公共接口 ,优惠券接口 , 行业此讯 , 手机号码注册
+ *
+*/
+
+/**
+ * 获取主页数据 无需授权
+ *
+*/
+export function getIndexData()
+{
+  return request.get("index",{},{ noAuth : true});
+}
+
+/**
+ * 获取登录授权login
+ *
+*/
+export function getLogo()
+{
+  return request.get('wechat/getLogo', {}, { noAuth : true});
+}
+
+
+/**
+ * 保存form_id
+ * @param string formId
+ */
+export function setFormId(formId) {
+  return request.post("wechat/set_form_id", { formId: formId});
+}
+
+/**
+ * 领取优惠券
+ * @param int couponId
+ *
+*/
+export function setCouponReceive(couponId){
+  return request.post('coupon/receive', { couponId: couponId});
+}
+/**
+ * 优惠券列表
+ * @param object data
+*/
+export function getCoupons(data){
+  return request.get('coupons',data,{noAuth:true})
+}
+
+/**
+ * 手机号+验证码登录接口
+ * @param object data
+*/
+export function loginMobile(data){
+  return request.post('login/mobile',data,{noAuth:true})
+}
+
+/**
+ * 获取短信KEY
+ * @param object phone
+*/
+export function verifyCode(){
+  return request.get('verify_code', {},{noAuth:true})
+}
+
+/**
+ * 验证码发送
+ * @param object phone
+*/
+export function registerVerify(phone){
+  return request.post('sendCode', { phone: phone },{noAuth:true},1)
+}
+
+/**
+ * 手机号注册
+ * @param object data
+ *
+*/
+export function phoneRegister(data){
+  return request.post('register',data,{noAuth:true});
+}
+
+/**
+ * 手机号修改密码
+ * @param object data
+ *
+*/
+export function phoneRegisterReset(data){
+  return request.post('register/reset',data,{noAuth:true})
+}
+
+/**
+ * 手机号+密码登录
+ * @param object data
+ *
+*/
+export function phoneLogin(data){
+  return request.post('login',data,{noAuth:true})
+}
+
+/**
+ * 切换H5登录
+ * @param object data
+*/
+// #ifdef MP
+export function switchH5Login(){
+  return request.post('switch_h5', { 'from':'routine'});
+}
+// #endif
+
+/*
+ * h5切换公众号登录
+ * */
+// #ifdef H5
+export function switchH5Login() {
+  return request.post("switch_h5", { 'from': "wechat" });
+}
+// #endif
+
+/**
+ * 换绑手机号
+ *
+*/
+export function bindingPhone(data){
+  return request.post('update/binding',data);
+}
+
+/**
+ * 换绑手机号校验
+ *
+*/
+export function bindingVerify(data){
+  return request.post('update/binding/verify',data);
+}
+
+/**
+ * 退出登錄
+ *
+*/
+export function logout(){
+  return request.get('logout');
+}
+
+/**
+ * 获取订阅消息id
+ */
+export function getTemlIds(data)
+{
+  return request.get('wechat/program/my/temp/list', data , { noAuth:true});
+}
+
+/**
+ * 获取小程序直播列表
+ */
+export function getLiveList(page,limit) {
+  return request.get('wechat/live', { page, limit}, { noAuth: true });
+}
+
+/**
+ * 获取小程序二维码
+ */
+export function getQrcode(data) {
+  return request.post('qrcode/get',data,{ noAuth: true });
+}

+ 33 - 0
api/member/address.js

@@ -0,0 +1,33 @@
+import request from "@/utils/request.js";
+
+// 获得用户收件地址
+export function getAddress(id) {
+  return request.get('app-api/member/address/get', {
+    id
+  });
+}
+
+// 获得默认的用户收件地址
+export function getDefaultUserAddress() {
+  return request.get('app-api/member/address/get-default', {});
+}
+
+// 获得用户收件地址列表
+export function getAddressList() {
+  return request.get('app-api/member/address/list', {});
+}
+
+// 创建用户收件地址
+export function createAddress(data) {
+  return request.post('app-api/member/address/create', data);
+}
+
+// 更新用户收件地址
+export function updateAddress(data) {
+  return request.put('app-api/member/address/update', data);
+}
+
+// 删除用户收件地址
+export function deleteAddress(id) {
+  return request.delete('app-api/member/address/delete?id=' + id, {});
+}

+ 68 - 0
api/member/auth.js

@@ -0,0 +1,68 @@
+import request from "@/utils/request.js";
+
+// 发送手机验证码
+export function sendSmsCode(mobile, scene) {
+  return request.post('app-api/member/auth/send-sms-code', {
+    mobile,
+    scene
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 校验手机验证码
+export function validateSmsCode(mobile, scene, code) {
+  return request.post('app-api/member/auth/validate-sms-code', {
+    mobile,
+    scene,
+    code
+  });
+}
+
+// 登出系统
+export function logout() {
+  return request.post('app-api/member/auth/logout');
+}
+
+// 使用手机 + 密码登录
+export function login(data) {
+  return request.post('app-api/member/auth/login', data, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 使用手机 + 验证码登录
+export function smsLogin(data) {
+  return request.post('app-api/member/auth/sms-login', data, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 社交快捷登录
+export function socialLogin(type, code, state) {
+  return request.post('app-api/member/auth/social-login', {
+    type,
+    code,
+    state
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 微信小程序的一键登录登录
+export function weixinMiniAppLogin(phoneCode, loginCode,state) {
+  return request.post('app-api/member/auth/weixin-mini-app-login', {
+    phoneCode,
+    loginCode,
+	  state
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 创建微信 JS SDK 初始化所需的签名
+export function createWeixinMpJsapiSignature(url) {
+  return request.post("app-api/member/auth/create-weixin-jsapi-signature?url=" + url, {}, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}

+ 11 - 0
api/member/level.js

@@ -0,0 +1,11 @@
+import request from "@/utils/request.js";
+
+// 获得会员等级列表
+export function getLevelList() {
+  return request.get('app-api/member/level/list');
+}
+
+// 获得会员经验记录分页
+export function getExperienceRecordPage() {
+  return request.get('app-api/member/experience-record/page');
+}

+ 6 - 0
api/member/point.js

@@ -0,0 +1,6 @@
+import request from "@/utils/request.js";
+
+// 获得用户积分记录分页
+export function getPointRecordPage() {
+  return request.get('app-api/member/point/record/page');
+}

+ 21 - 0
api/member/signin.js

@@ -0,0 +1,21 @@
+import request from "@/utils/request.js";
+
+// 获得签到规则列表
+export function getSignInConfigList() {
+  return request.get('app-api/member/sign-in/config/list');
+}
+
+// 获得个人签到统计
+export function getSignInRecordSummary() {
+  return request.get('app-api/member/sign-in/record/get-summary')
+}
+
+// 签到
+export function createSignInRecord() {
+  return request.post('app-api/member/sign-in/record/create')
+}
+
+// 获得签到记录分页
+export function getSignRecordPage() {
+  return request.get('app-api/member/sign-in/record/page');
+}

+ 28 - 0
api/member/user.js

@@ -0,0 +1,28 @@
+import request from "@/utils/request.js";
+
+// 获得基本信息
+export function getUserInfo() {
+  return request.get('app-api/member/user/get');
+}
+
+// 修改基本信息
+export function updateUser(data) {
+  return request.put('app-api/member/user/update', data);
+}
+
+// 修改用户手机
+export function updateUserMobile(data) {
+  return request.put('app-api/member/user/update-mobile', data);
+}
+
+// 修改用户密码
+export function updateUserPassword(data) {
+  return request.put('app-api/member/user/update-password', data);
+}
+
+// 重置密码
+export function resetUserPassword(data) {
+  return request.put('app-api/member/user/reset-password', data, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}

+ 247 - 0
api/order.js

@@ -0,0 +1,247 @@
+import request from "@/utils/request.js";
+
+/**
+ * 获取购物车列表
+ * @param numType boolean true 购物车数量,false=购物车产品数量
+ */
+export function getCartCounts(numType,type) {
+	return request.get("cart/count?numType=" + numType + "&type=" + type);
+}
+/**
+ * 获取购物车列表
+ * 
+ */
+export function getCartList(data) {
+	return request.get("cart/list", data);
+}
+
+/**
+ * 修改购物车数量
+ * @param int cartId  购物车id
+ * @param int number 修改数量
+ */
+export function changeCartNum(cartId, number) {
+	return request.post("cart/num", {
+		id: cartId,
+		number: number
+	}, {}, 1);
+}
+/**
+ * 清除购物车
+ * @param object ids join(',') 切割成字符串
+ */
+export function cartDel(ids) {
+	if (typeof ids === 'object')
+		ids = ids.join(',');
+	return request.post('cart/delete', {
+		ids: ids
+	}, {}, 1);
+}
+
+/**
+ * 购物车重选提交
+ * 
+ */
+export function getResetCart(data) {
+	return request.post('cart/resetcart', data);
+}
+
+/**
+ * 订单列表
+ * @param object data
+ */
+export function getOrderList(data) {
+	return request.get('order/list', data);
+}
+
+/**
+ * 订单产品信息
+ * @param string unique 
+ */
+export function orderProduct(data) {
+	return request.post('order/product', data);
+}
+
+/**
+ * 订单评价
+ * @param object data
+ * 
+ */
+export function orderComment(data) {
+	return request.post('order/comment', data);
+}
+
+/**
+ * 订单支付
+ * @param object data
+ */
+export function orderPay(data) {
+	return request.post('order/pay', data);
+}
+
+/**
+ * 订单统计数据
+ */
+export function orderData() {
+	return request.get('order/data')
+}
+
+/**
+ * 订单取消
+ * @param string id
+ * 
+ */
+export function orderCancel(id) {
+	return request.post('order/cancel', {
+		id: id
+	}, {}, 1);
+}
+
+/**
+ * 删除已完成订单
+ * @param string uni
+ * 
+ */
+export function orderDel(uni) {
+	return request.post('order/del', {
+		id: uni
+	}, {}, 1);
+}
+
+/**
+ * 订单详情
+ * @param string uni 
+ */
+export function getOrderDetail(uni) {
+	return request.get('order/detail/' + uni);
+}
+
+/**
+ * 再次下单
+ * @param string uni
+ * 
+ */
+export function orderAgain(uni) {
+	return request.post('order/again', {
+		orderNo: uni
+	});
+}
+
+/**
+ * 订单收货
+ * @param string uni
+ * 
+ */
+export function orderTake(uni) {
+	return request.post('order/take', {
+		id: uni
+	}, {}, 1);
+}
+
+/**
+ * 订单查询物流信息
+ * @returns {*}
+ */
+export function express(uni) {
+	return request.get("order/express/" + uni);
+}
+
+/**
+ * 获取退款理由
+ * 
+ */
+export function ordeRefundReason() {
+	return request.get('order/refund/reason');
+}
+
+/**
+ * 订单退款审核
+ * @param object data
+ */
+export function orderRefundVerify(data) {
+	return request.post('order/refund', data);
+}
+
+/**
+ * 订单确认获取订单详细信息
+ * @param string cartId
+ */
+export function orderConfirm(cartId, isNew, addAgain,secKill,combination,bargain) {
+	return request.post('order/confirm', {
+		cartIds: cartId,
+		isNew: isNew,
+		addAgain: addAgain,
+		secKill: secKill,
+		combination:combination,
+		bargain:bargain
+	});
+}
+
+/**
+ * 获取当前金额能使用的优惠券
+ * @param string price
+ * 
+ */
+export function getCouponsOrderPrice(preOrderNo) {
+	return request.get(`coupons/order/${preOrderNo}`)
+}
+
+/**
+ * 订单创建
+ * @param string key
+ * @param object data
+ * 
+ */
+export function orderCreate(data) {
+	return request.post('order/create', data);
+}
+
+/**
+ * 计算订单金额
+ * @param key
+ * @param data
+ * @returns {*}
+ */
+export function postOrderComputed(data) {
+	return request.post("order/computed/price", data);
+}
+
+/**
+ * 微信订单支付
+ * @param object data
+ */
+export function wechatOrderPay(data) {
+	return request.post('pay/payment', data);
+}
+
+/**
+ * 微信查询支付结果
+ * @param object data
+ */
+export function wechatQueryPayResult(data) {
+	return request.get('pay/queryPayResult?orderNo=' + data);
+}
+
+/**
+ * 申请退款商品详情
+ * @param object data
+ */
+export function applyRefund(orderId) {
+	return request.get(`order/apply/refund/${orderId}`);
+}
+
+/**
+ * 预下单
+ * @param object data
+ */
+export function preOrderApi(data) {
+	return request.post(`order/pre/order`, data);
+}
+
+/**
+ * 加载预下单
+ * @param object preOrderNo
+ */
+export function loadPreOrderApi(preOrderNo) {
+	return request.get(`order/load/pre/${preOrderNo}`);
+}

+ 8 - 0
api/pay/channel.js

@@ -0,0 +1,8 @@
+import request from "@/utils/request.js";
+
+// 获得指定应用的开启的支付渠道编码列表
+export function getEnableChannelCodeList(appId) {
+  return request.get("app-api/pay/channel/get-enable-code-list", {
+    appId
+  });
+}

+ 11 - 0
api/pay/order.js

@@ -0,0 +1,11 @@
+import request from "@/utils/request.js";
+
+export function getOrder(id) {
+  return request.get("app-api/pay/order/get", {
+    id
+  });
+}
+
+export function submitOrder(data) {
+  return request.post("app-api/pay/order/submit", data);
+}

+ 21 - 0
api/pay/wallet.js

@@ -0,0 +1,21 @@
+import request from "@/utils/request.js";
+
+// 获取钱包
+export function getPayWallet() {
+  return request.get("app-api/pay/wallet/get");
+}
+
+// 获得钱包流水分页
+export function getWalletTransactionPage(data) {
+  return request.get("app-api/pay/wallet-transaction/page", data);
+}
+
+// 获得钱包充值套餐列表
+export function getWalletRechargePackageList() {
+  return request.get("app-api/pay/wallet-recharge-package/list");
+}
+
+// 创建钱包充值记录(发起充值)
+export function createWalletRecharge(data) {
+  return request.post("app-api/pay/wallet-recharge/create", data);
+}

+ 8 - 0
api/product/category.js

@@ -0,0 +1,8 @@
+import request from "@/utils/request.js";
+
+// 查询分类列表
+export function getCategoryList() {
+  return request.get('app-api/product/category/list', {}, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}

+ 27 - 0
api/product/comment.js

@@ -0,0 +1,27 @@
+import request from "@/utils/request.js";
+
+// 获得商品的评价统计
+export function getCommentStatistics(spuId) {
+  return request.get('app-api/product/comment/statistics', {
+    spuId
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得最近的 n 条商品评价
+export function getCommentList(spuId, count) {
+  return request.get('app-api/product/comment/list', {
+    spuId,
+    count
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得商品评价分页
+export function getCommentPage(data) {
+  return request.get('app-api/product/comment/page', data, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}

+ 46 - 0
api/product/favorite.js

@@ -0,0 +1,46 @@
+import request from "@/utils/request.js";
+
+// 检查是否收藏过商品
+export function isFavoriteExists(spuId) {
+  return request.get('app-api/product/favorite/exits', {
+    spuId
+  });
+}
+
+// 获得商品收藏数量
+export function getFavoriteCount() {
+  return request.get('app-api/product/favorite/get-count');
+}
+
+// 添加商品收藏
+export function createFavorite(spuId) {
+  return request.post('app-api/product/favorite/create', {
+    spuId
+  });
+}
+
+// 添加多个商品收藏
+export function createFavoriteList(spuIds) {
+  return request.post('app-api/product/favorite/create-list', {
+    spuIds
+  });
+}
+
+// 取消商品收藏
+export function deleteFavorite(spuId) {
+  return request.delete('app-api/product/favorite/delete', {
+    spuId
+  });
+}
+
+// 获得商品收藏分页
+export function getFavoritePage(data) {
+  return request.get('app-api/product/favorite/page', data);
+}
+
+// 取消多个商品收藏
+export function deleteFavoriteList(spuIds) {
+  return request.delete('app-api/product/favorite/delete-list', {
+    spuIds
+  });
+}

+ 24 - 0
api/product/spu.js

@@ -0,0 +1,24 @@
+import request from "@/utils/request.js";
+
+// 获得商品 SPU 列表
+export function getSpuList(recommendType) {
+  return request.get('app-api/product/spu/list', {
+    recommendType
+  });
+}
+
+// 获得商品 SPU 分页
+export function getSpuPage(data) {
+  return request.get('app-api/product/spu/page', data, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 查询商品
+export function getSpuDetail(id) {
+  return request.get('app-api/product/spu/get-detail', {
+    id
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}

+ 19 - 0
api/promotion/activity.js

@@ -0,0 +1,19 @@
+import request from "@/utils/request.js";
+
+// 获得单个商品,近期参与的每个活动
+export function getActivityListBySpuId(spuId) {
+  return request.get("app-api/promotion/activity/list-by-spu-id", {
+    spuId
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得多个商品,近期参与的每个活动
+export function getActivityListBySpuIds(spuIds) {
+  return request.get("app-api/promotion/activity/list-by-spu-ids", {
+    spuIds: spuIds && spuIds.length > 0 ? spuIds.join(',') : undefined
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}

+ 38 - 0
api/promotion/article.js

@@ -0,0 +1,38 @@
+import request from "@/utils/request.js";
+
+// 获得文章分类列表
+export function getArticleCategoryList() {
+  return request.get("app-api/promotion/article-category/list", {}, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得文章列表
+export function getArticleList(data) {
+  return request.get("app-api/promotion/article/list", data, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得文章分页
+export function getArticlePage(data) {
+  return request.get("app-api/promotion/article/page", data, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得文章
+export function getArticle(id) {
+  return request.get("app-api/promotion/article/get", {
+    id
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 增加文章浏览量
+export function addArticleBrowseCount(id) {
+  return request.put("app-api/promotion/article/add-browse-count?id=" + id, {}, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}

+ 7 - 0
api/promotion/banner.js

@@ -0,0 +1,7 @@
+import request from "@/utils/request.js";
+
+export function getBannerList(position) {
+  return request.get("app-api/promotion/banner/list", {
+    position
+  });
+}

+ 75 - 0
api/promotion/bargain.js

@@ -0,0 +1,75 @@
+import request from "@/utils/request.js";
+
+// 获得砍价活动列表
+export function getBargainActivityList(count) {
+  return request.get("app-api/promotion/bargain-activity/list", {
+    count
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得砍价活动详情
+export function getBargainActivityDetail(id) {
+  return request.get("app-api/promotion/bargain-activity/get-detail", {
+    id
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得砍价活动分页
+export function getBargainActivityPage(count) {
+  return request.get("app-api/promotion/bargain-activity/page", {
+    count
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得砍价记录的概要信息
+export function getBargainRecordSummary() {
+  return request.get("app-api/promotion/bargain-record/get-summary", {}, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得砍价记录的分页
+export function getBargainRecordPage(data) {
+  return request.get("app-api/promotion/bargain-record/page", data, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得砍价活动详情
+export function getBargainRecordDetail(id, activityId) {
+  return request.get("app-api/promotion/bargain-record/get-detail", {
+    id: id > 0 ? id : undefined,
+    activityId: activityId > 0 ? activityId : undefined
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 创建砍价记录(参与拼团活动)
+export function createBargainRecord(activityId) {
+  return request.post("app-api/promotion/bargain-record/create", {
+    activityId
+  });
+}
+
+// 创建砍价助力(给拼团记录砍一刀)
+export function createBargainHelp(recordId) {
+  return request.post("app-api/promotion/bargain-help/create", {
+    recordId: recordId
+  });
+}
+
+// 获得砍价助力列表
+export function getBargainHelpList(recordId) {
+  return request.get("app-api/promotion/bargain-help/list", {
+    recordId: recordId
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}

+ 53 - 0
api/promotion/combination.js

@@ -0,0 +1,53 @@
+import request from "@/utils/request.js";
+
+// 获得拼团活动列表
+export function getCombinationActivityList(count) {
+  return request.get("app-api/promotion/combination-activity/list", {
+    count
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得拼团活动分页
+export function getCombinationActivityPage(data) {
+  return request.get("app-api/promotion/combination-activity/page", data, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得拼团活动明细
+export function getCombinationActivity(id) {
+  return request.get("app-api/promotion/combination-activity/get-detail", {
+    id
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得最近 n 条拼团记录(团长发起的)
+export function getHeadCombinationRecordList(activityId, status, count) {
+  return request.get("app-api/promotion/combination-record/get-head-list", {
+    activityId,
+    status,
+    count
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得拼团记录明细
+export function getCombinationRecordDetail(id) {
+  return request.get("app-api/promotion/combination-record/get-detail", {
+    id
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得拼团记录的概要信息
+export function getCombinationRecordSummary() {
+  return request.get("app-api/promotion/combination-record/get-summary", {}, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}

+ 37 - 0
api/promotion/coupon.js

@@ -0,0 +1,37 @@
+import request from "@/utils/request.js";
+
+// 领取优惠券
+export function takeCoupon(templateId) {
+  return request.post("app-api/promotion/coupon/take", {
+    templateId
+  });
+}
+
+// 获得匹配指定商品的优惠劵列表
+export function getMatchCouponList(data) {
+  return request.get("app-api/promotion/coupon/match-list", data);
+}
+
+// 获得用户优惠劵列表
+export function getCouponPage(data) {
+  return request.get("app-api/promotion/coupon/page", data);
+}
+
+// 获得优惠劵模板分页
+export function getCouponTemplatePage(data) {
+  return request.get("app-api/promotion/coupon-template/page", data, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得优惠劵模板列表
+export function getCouponTemplateList(data) {
+  return request.get("app-api/promotion/coupon-template/list", data, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得未使用的优惠劵数量
+export function getUnusedCouponCount() {
+  return request.get("app-api/promotion/coupon/get-unused-count");
+}

+ 9 - 0
api/promotion/decorate.js

@@ -0,0 +1,9 @@
+import request from "@/utils/request.js";
+
+export function getDecorateComponentListByPage(page) {
+  return request.get("app-api/promotion/decorate/list", {
+    page
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}

+ 31 - 0
api/promotion/seckill.js

@@ -0,0 +1,31 @@
+import request from "@/utils/request.js";
+
+// 获得秒杀时间段列表
+export function getSeckillConfigList() {
+  return request.get("app-api/promotion/seckill-config/list", {}, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得当前秒杀活动
+export function getNowSeckillActivity() {
+  return request.get("app-api/promotion/seckill-activity/get-now", {}, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得秒杀活动分页
+export function getSeckillActivityPage(data) {
+  return request.get("app-api/promotion/seckill-activity/page", data, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}
+
+// 获得秒杀活动明细
+export function getSeckillActivity(id) {
+  return request.get("app-api/promotion/seckill-activity/get-detail", {
+    id
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}

+ 116 - 0
api/public.js

@@ -0,0 +1,116 @@
+import request from "@/utils/request.js";
+import wechat from "@/libs/wechat.js";
+import {
+	toLogin,
+	checkLogin
+} from '../libs/login';
+/**
+ * 获取微信公众号js配置
+ * @returns {*}
+ */
+export function getWechatConfig() {
+  return request.get("wechat/config",{ url: encodeURIComponent(wechat.signLink()) },{ noAuth: true });
+}
+
+/**
+ * 获取微信sdk配置
+ * @returns {*}
+ */
+export function wechatAuth(code, spread) {
+	var reg=/^[0-9]+.?[0-9]*$/; //判断字符串是否为数字 ,判断正整数用/^[1-9]+[0-9]*]*$/
+	spread = reg.test(spread) ? spread : 0;
+  return request.get(
+    "wechat/authorize/login?code=" + code + "&spread_spid=" + spread, {},
+    { noAuth: true }
+  );
+}
+
+/**
+ * 获取登录授权login
+ *
+*/
+export function getLogo()
+{
+	// wechat/get_logo
+  return request.get('wechat/getLogo', {}, { noAuth : true});
+}
+
+/**
+ * 小程序用户登录
+ * @param data object 小程序用户登录信息
+ */
+export function login(code,data) {
+  return request.post("wechat/authorize/program/login?code="+code, data, { noAuth : true });
+}
+/**
+ * 分享
+ * @returns {*}
+ */
+export function getShare() {
+  return request.get("share", {}, { noAuth: true });
+}
+
+/**
+ * 获取关注海报
+ * @returns {*}
+ */
+export function follow() {
+  return request.get("wechat/follow", {}, { noAuth: true });
+}
+
+/**
+ * 获取图片base64
+ * @retins {*}
+ * */
+export function imageBase64(image) {
+  return request.post("qrcode/base64",image,{ noAuth: true },1);
+}
+
+/**
+ * 自动复制口令功能
+ * @returns {*}
+ */
+export function copyWords() {
+  return request.get("copy_words", {}, { noAuth: true });
+}
+
+/**
+ * 首页 获取客服地址
+ * @returns {*}
+ */
+export function kefuConfig() {
+  return request.get("config", {}, { noAuth: true });
+}
+
+/**
+ * 微信(公众号,小程序)绑定手机号
+ * @param {Object} data
+ */
+export function getUserPhone(data){
+	return request.post('wechat/register/binding/phone',data,{noAuth : true});
+}
+
+/**
+ * APP微信登录
+ * @param {Object} data
+ */
+export function appAuth(data) {
+	return request.post("wechat/authorize/app/login", data, { noAuth : true });
+}
+
+/**
+ * 苹果登录
+ * @param {Object} data
+ */
+export function appleLogin(data) {
+	return request.post("ios/login", data, { noAuth : true });
+}
+
+
+/**
+ * 苹果绑定手机号
+ * @param {Object} data
+ */
+export function iosBinding(data) {
+	return request.post("ios/binding/phone", data, { noAuth : true });
+}

+ 191 - 0
api/store.js

@@ -0,0 +1,191 @@
+import request from "@/utils/request.js";
+
+/**
+ * 获取产品详情
+ * @param int id
+ * 
+ */
+export function getProductDetail(id, type) {
+	return request.get('product/detail/' + id + '?type=' + type, {}, {
+		noAuth: true
+	});
+}
+
+/**
+ * 产品分享二维码 推广员
+ * @param int id
+ */
+// #ifndef MP
+export function getProductCode(id) {
+	return request.get('product/code/' + id, {});
+}
+// #endif
+// #ifdef MP
+export function getProductCode(id) {
+	return request.get('product/code/' + id, {
+		user_type: 'routine'
+	});
+}
+// #endif
+
+/**
+ * 添加收藏
+ * @param int id
+ * @param string category product=普通产品,product_seckill=秒杀产品
+ */
+export function collectAdd(id, category) {
+	return request.post('collect/add', {
+		id: id,
+		'category': category === undefined ? 'product' : category
+	});
+}
+
+/**
+ * 取消收藏产品
+ * @param int id
+ */
+export function collectDel(proId) {
+	return request.post(`collect/cancel/${proId}`);
+}
+
+
+/**
+ * 删除收藏产品
+ * @param string id
+ */
+export function collectDelete(ids) {
+	return request.post(`collect/delete`,ids);
+}
+
+/**
+ * 购车添加
+ * 
+ */
+export function postCartAdd(data) {
+	return request.post('cart/save', data, {});
+}
+
+/**
+ * 获取分类列表
+ * 
+ */
+export function getCategoryList() {
+	return request.get('category', {}, {
+		noAuth: true
+	});
+}
+
+/**
+ * 获取产品列表
+ * @param object data
+ */
+export function getProductslist(data) {
+	return request.get('products', data, {
+		noAuth: true
+	});
+}
+
+/**
+ * 获取推荐产品
+ * 
+ */
+export function getProductHot(page, limit) {
+	return request.get("product/hot", {
+		page: page === undefined ? 1 : page,
+		limit: limit === undefined ? 4 : limit
+	}, {
+		noAuth: true
+	});
+}
+/**
+ * 批量收藏
+ * 
+ * @param object id  产品编号 join(',') 切割成字符串
+ * @param string category 
+ */
+export function collectAll(id, category) {
+	return request.post('collect/all', {
+		id: id,
+		category: category === undefined ? 'product' : category
+	});
+}
+
+/**
+ * 首页产品的轮播图和产品信息
+ * @param int type 
+ * 
+ */
+export function getGroomList(type, data) {
+	return request.get('index/product/' + type, data, {
+		noAuth: true
+	});
+}
+
+/**
+ * 获取收藏列表
+ * @param object data
+ */
+export function getCollectUserList(data) {
+	return request.get('collect/user', data)
+}
+
+/**
+ * 获取产品评论
+ * @param int id
+ * @param object data
+ * 
+ */
+export function getReplyList(id, data) {
+	return request.get('reply/list/' + id, data,{
+		noAuth: true
+	})
+}
+
+/**
+ * 产品评价数量和好评度
+ * @param int id
+ */
+export function getReplyConfig(id) {
+	return request.get('reply/config/' + id,{},{
+		noAuth: true
+	});
+}
+
+/**
+ * 获取搜索关键字获取
+ * 
+ */
+export function getSearchKeyword() {
+	return request.get('search/keyword', {}, {
+		noAuth: true
+	});
+}
+
+/**
+ * 门店列表
+ * @returns {*}
+ */
+export function storeListApi(data) {
+	return request.post("store/list", data, {}, 1);
+}
+
+
+/**
+ * 优品推荐
+ * @param object data
+ */
+export function getProductGood() {
+	return request.get('product/good');
+}
+
+/**
+ * 详情页产品评论
+ * @param int id
+ * @param object data
+ * 
+ */
+export function getReplyProduct(id) {
+	return request.get('reply/product/' + id, {
+		noAuth: true
+	})
+}

+ 8 - 0
api/system/area.js

@@ -0,0 +1,8 @@
+import request from "@/utils/request.js";
+
+// 获得地区树
+export function getAreaTree() {
+  return request.get("app-api/system/area/tree", {}, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}

+ 6 - 0
api/system/dict.js

@@ -0,0 +1,6 @@
+import request from "@/utils/request.js";
+
+// 根据字典类型查询字典数据信息
+export function getDicts(dictType) {
+  return request.get('app-api/system/dict-data/type?type=' + dictType);
+}

+ 27 - 0
api/trade/afterSale.js

@@ -0,0 +1,27 @@
+import request from "@/utils/request.js";
+
+export function getAfterSale(id) {
+  return request.get("app-api/trade/after-sale/get", {
+    id
+  });
+}
+
+export function getApplyingAfterSaleCount() {
+  return request.get("app-api/trade/after-sale/get-applying-count");
+}
+
+export function getAfterSalePage(data) {
+  return request.get("app-api/trade/after-sale/page", data);
+}
+
+export function createAfterSale(data) {
+  return request.post("app-api/trade/after-sale/create", data);
+}
+
+export function cancelAfterSale(id) {
+  return request.delete("app-api/trade/after-sale/cancel?id=" + id);
+}
+
+export function deliveryAfterSale(data) {
+  return request.put("app-api/trade/after-sale/delivery", data);
+}

+ 62 - 0
api/trade/brokerage.js

@@ -0,0 +1,62 @@
+import request from "@/utils/request.js";
+
+// 获得个人分销信息
+export function getBrokerageUser() {
+  return request.get('app-api/trade/brokerage-user/get');
+}
+
+// 获得个人分销信息
+export function bindBrokerageUser(bindUserId) {
+  return request.put('app-api/trade/brokerage-user/bind', {
+    bindUserId
+  });
+}
+
+// 获得个人分销统计
+export function getBrokerageUserSummary() {
+  return request.get('app-api/trade/brokerage-user/get-summary');
+}
+
+// 获得下级分销统计分页
+export function getBrokerageUserChildSummaryPage(data) {
+  return request.get('app-api/trade/brokerage-user/child-summary-page', data);
+}
+
+// 获得分销用户排行分页(基于用户量)
+export function getBrokerageUserRankPageByUserCount(data) {
+  return request.get('app-api/trade/brokerage-user/rank-page-by-user-count', data);
+}
+
+// 获得分销用户排行(基于佣金)
+export function getBrokerageUserRankByPrice(data) {
+  return request.get('app-api/trade/brokerage-user/get-rank-by-price', data);
+}
+
+// 获得分销用户排行分页(基于佣金)
+export function getBrokerageUserRankPageByPrice(data) {
+  return request.get('app-api/trade/brokerage-user/rank-page-by-price', data);
+}
+
+// 获得分销提现分页记录
+export function getBrokerageWithdrawPage(data) {
+  return request.get('app-api/trade/brokerage-withdraw/page', data);
+}
+
+// 创建分销提现
+export function createBrokerageWithdraw(data) {
+  return request.post('app-api/trade/brokerage-withdraw/create', data);
+}
+
+// 获得分销记录分页
+export function getBrokerageRecordPage(data) {
+  return request.get('app-api/trade/brokerage-record/page', data);
+}
+
+// 获得商品的分销金额
+export function getProductBrokeragePrice(spuId) {
+  return request.get('app-api/trade/brokerage-record/get-product-brokerage-price', {
+    spuId
+  }, {
+    noAuth: true // TODO 芋艿:后续要做调整
+  });
+}

+ 33 - 0
api/trade/cart.js

@@ -0,0 +1,33 @@
+import request from "@/utils/request.js";
+
+export function addCart(data) {
+  return request.post("app-api/trade/cart/add", data);
+}
+
+export function updateCartCount(data) {
+  return request.put("app-api/trade/cart/update-count", data);
+}
+
+export function updateCartSelected(data) {
+  return request.put("app-api/trade/cart/update-selected", data);
+}
+
+export function resetCart(data) {
+  return request.put("app-api/trade/cart/reset", data);
+}
+
+export function getCartCount() {
+  return request.get("app-api/trade/cart/get-count");
+}
+
+export function getCartCountMap() {
+  return request.get("app-api/trade/cart/get-count-map");
+}
+
+export function getCartList() {
+  return request.get("app-api/trade/cart/list");
+}
+
+export function deleteCart(ids) {
+  return request.delete("app-api/trade/cart/delete?ids=" + ids.join(','));
+}

+ 6 - 0
api/trade/config.js

@@ -0,0 +1,6 @@
+import request from "@/utils/request.js";
+
+// 获得交易配置
+export function getTradeConfig() {
+  return request.get("app-api/trade/config/get");
+}

+ 23 - 0
api/trade/delivery.js

@@ -0,0 +1,23 @@
+import request from "@/utils/request.js";
+
+// 获得配送配置
+export function getDeliveryConfig() {
+  return request.get("app-api/trade/delivery/config/get", {});
+}
+
+// 获得自提门店列表
+export function getDeliveryPickUpStoreList(data) {
+  return request.get("app-api/trade/delivery/pick-up-store/list", data);
+}
+
+// 获得自提门店
+export function getDeliveryPickUpStore(id) {
+  return request.get("app-api/trade/delivery/pick-up-store/get", {
+    id
+  });
+}
+
+// 获得快递公司列表
+export function getDeliveryExpressList() {
+  return request.get("app-api/trade/delivery/express/list");
+}

+ 61 - 0
api/trade/order.js

@@ -0,0 +1,61 @@
+import request from "@/utils/request.js";
+
+export function settlementOrder(data) {
+  const data2 = {
+    ...data,
+  }
+  // 解决 SpringMVC 接受 List<Item> 参数的问题
+  delete data2.items
+  for (let i = 0; i < data.items.length; i++) {
+    data2['items[' + i + '].skuId'] = data.items[i].skuId;
+    data2['items[' + i + '].count'] = data.items[i].count;
+    data2['items[' + i + '].cartId'] = data.items[i].cartId;
+  }
+  return request.get("app-api/trade/order/settlement", data2);
+}
+
+export function createOrder(data) {
+  return request.post("app-api/trade/order/create", data);
+}
+
+export function getOrderDetail(id) {
+  return request.get("app-api/trade/order/get-detail", {
+    id
+  });
+}
+
+export function getOrderCount() {
+  return request.get("app-api/trade/order/get-count");
+}
+
+export function getOrderPage(data) {
+  return request.get("app-api/trade/order/page", data);
+}
+
+export function receiveOrder(id) {
+  return request.put("app-api/trade/order/receive?id=" + id, {});
+}
+
+export function cancelOrder(id) {
+  return request.delete("app-api/trade/order/cancel?id=" + id, {});
+}
+
+export function deleteOrder(id) {
+  return request.delete("app-api/trade/order/delete?id=" + id, {});
+}
+
+export function getOrderItem(id) {
+  return request.get("app-api/trade/order/item/get", {
+    id
+  });
+}
+
+export function createOrderItemComment(data) {
+  return request.post("app-api/trade/order/item/create-comment", data);
+}
+
+export function getOrderExpressTrackList(id) {
+  return request.get("app-api/trade/order/get-express-track-list", {
+    id
+  });
+}

+ 396 - 0
api/user.js

@@ -0,0 +1,396 @@
+import request from "@/utils/request.js";
+
+/**
+ * 获取用户信息
+ */
+export function getUserInfo(){
+  return request.get('user');
+}
+
+/**
+ * 设置用户分享
+ */
+export function userShare(){
+  return request.post('user/share');
+}
+
+/**
+ * h5用户登录
+ * @param data object 用户账号密码
+ */
+export function loginH5(data) {
+  return request.post("login", data, { noAuth : true });
+}
+
+/**
+ * h5用户手机号登录
+ * @param data object 用户手机号 也只能
+ */
+export function loginMobile(data) {
+  return request.post("login/mobile", data, { noAuth : true });
+}
+
+/**
+ * 验证码key
+ */
+export function getCodeApi() {
+  return request.get("verify_code", {}, { noAuth: true });
+}
+
+/**
+ * h5用户发送验证码
+ * @param data object 用户手机号
+ */
+export function registerVerify(phone){
+  return request.post('sendCode', { phone: phone },{noAuth:true},1)
+}
+
+/**
+ * h5用户手机号注册
+ * @param data object 用户手机号 验证码 密码
+ */
+export function register(data) {
+  return request.post("register", data, { noAuth : true });
+}
+
+/**
+ * 用户手机号修改密码
+ * @param data object 用户手机号 验证码 密码
+ */
+export function registerReset(data) {
+  return request.post("register/reset", data, { noAuth: true });
+}
+
+/**
+ * 获取用户中心菜单
+ *
+ */
+export function getMenuList() {
+  return request.get("menu/user");
+}
+
+/*
+ * 签到用户信息
+ * */
+export function postSignUser(sign) {
+  return request.post("user/sign/user", sign);
+}
+
+/**
+ * 获取签到配置
+ *
+*/
+export function getSignConfig(){
+  return request.get('user/sign/config')
+}
+
+/**
+ * 获取签到列表
+ * @param object data
+*/
+export function getSignList(data){
+  return request.get('user/sign/list',data);
+}
+
+/**
+ * 用户签到
+*/
+export function setSignIntegral(){
+  return request.get('user/sign/integral')
+}
+
+/**
+ * 签到列表(年月)
+ * @param object data
+ *
+*/
+export function getSignMonthList(data){
+  return request.get('user/sign/month',data)
+}
+
+/**
+ * 活动状态
+ *
+*/
+export function userActivity(){
+  return request.get('user/activity');
+}
+
+/*
+ * 余额明细(types|2=全部,1=支出,2=收入)
+ * */
+export function getCommissionInfo(data) {
+  return request.get("spread/commission/detail", data);
+}
+
+/*
+ * 提现记录 getCountApi
+ * */
+export function getRecordApi(q) {
+  return request.get("extract/record", q);
+}
+
+/*
+ * 提现总金额
+ * */
+export function getCountApi() {
+  return request.get("extract/totalMoney");
+}
+
+/*
+ * 积分记录
+ * */
+export function getIntegralList(q) {
+  return request.get("integral/list", q);
+}
+
+/**
+ * 获取分销海报图片
+ *
+*/
+export function spreadBanner(data){
+	return request.get('user/spread/banner',data);
+}
+
+/**
+ *
+ * 获取推广用户一级和二级
+ * @param object data
+*/
+export function spreadPeople(data){
+  return request.get('spread/people',data);
+}
+
+/**
+ *
+ * 推广佣金/提现总和
+ * @param int type
+*/
+export function spreadCount(type){
+  return request.get('spread/count/'+type);
+}
+
+/*
+ * 推广数据 当前佣金 提现总金额
+ * */
+export function getSpreadInfo() {
+  return request.get("commission");
+}
+
+
+/**
+ *
+ * 推广订单
+ * @param object data
+*/
+export function spreadOrder(data){
+  return request.get('spread/order',data);
+}
+
+/*
+ * 获取推广人排行
+ * */
+export function getRankList(q) {
+  return request.get("rank", q);
+}
+
+/*
+ * 获取佣金排名
+ * */
+export function getBrokerageRank(q) {
+  return request.get("brokerage_rank", q);
+}
+
+/**
+ * 提现申请
+ * @param object data
+*/
+export function extractCash(data){
+  return request.post('extract/cash',data)
+}
+
+/**
+ * 会员等级列表
+ *
+*/
+export function userLevelGrade(){
+  return request.get('user/level/grade');
+}
+
+/**
+ * 获取某个等级任务
+ * @param int id 任务id
+*/
+export function userLevelTask(id){
+  return request.get('user/level/task/'+id);
+}
+
+/**
+ * 检查用户是否可以成为会员
+ *
+*/
+export function userLevelDetection(){
+  return request.get('user/level/detection');
+}
+
+/**
+ * 获取单个地址
+ * @param int id
+*/
+export function getAddressDetail(id){
+  return request.get('address/detail/'+id);
+}
+
+/**
+ * 修改用户信息
+ * @param object
+*/
+export function userEdit(data){
+  return request.post('user/edit',data);
+}
+
+/*
+ * 退出登录
+ * */
+export function getLogout() {
+  return request.get("logout");
+}
+/**
+ * 小程序充值
+ *
+*/
+export function rechargeRoutine(data){
+  return request.post('recharge/routine',data)
+}
+/*
+ * 公众号充值
+ * */
+export function rechargeWechat(data) {
+  return request.post("recharge/wechat", data);
+}
+
+/*
+ * app微信充值
+ * */
+export function appWechat(data) {
+  return request.post("recharge/wechat/app", data);
+}
+
+/*
+ * 余额充值
+ * */
+export function transferIn(data) {
+  return request.post("recharge/transferIn", data,{},1);
+}
+
+/**
+ * 获取默认地址
+ *
+*/
+export function getAddressDefault(){
+  return request.get('address/default');
+}
+
+/**
+ * 充值金额选择
+ */
+export function getRechargeApi() {
+  return request.get("recharge/index");
+}
+
+/**
+ * 登录记录
+ */
+export function setVisit(data)
+{
+  return request.post('user/set_visit', {...data}, { noAuth:true});
+}
+
+/**
+ * 客服列表
+ */
+export function serviceList() {
+  return request.get("user/service/lst");
+}
+/**
+ * 客服详情
+ */
+export function getChatRecord(to_uid, data) {
+  return request.get("user/service/record/" + to_uid, data);
+}
+
+/**
+ * 静默绑定推广人
+ * @param {Object} puid
+ */
+export function spread(puid)
+{
+	return request.get("user/bindSpread?spreadPid="+ puid);
+}
+
+/**
+ * 当前用户在佣金排行第几名
+ *
+ */
+export function brokerageRankNumber(data)
+{
+	return request.get("user/brokerageRankNumber",data);
+}
+
+/**
+ * 会员等级经验值;
+ *
+ */
+export function getlevelInfo()
+{
+	return request.get("user/level/grade");
+}
+
+/**
+ * 经验值明细;
+ *
+ */
+export function getlevelExpList(data)
+{
+	return request.get("user/expList",data);
+}
+
+/**
+ * 经验值明细;
+ *
+ */
+export function getuserDalance()
+{
+	return request.get("user/balance");
+}
+
+
+/**
+ * 账单记录;
+ *
+ */
+export function getBillList(data)
+{
+	return request.get("recharge/bill/record",data);
+}
+
+/*
+ * 积分中心详情
+ * */
+export function postIntegralUser() {
+  return request.get("integral/user");
+}
+
+/*
+ * 立即提现 冻结期、冻结佣金、可提现佣金、最低可提现金额
+ * */
+export function extractUser() {
+  return request.get("extract/user");
+}
+
+/*
+ * 推广人统计页 推广人数(一级+二级)、一级人数、二级人数
+ * */
+export function spreadPeoCount() {
+  return request.get("spread/people/count");
+}

+ 145 - 0
components/Authorize.vue

@@ -0,0 +1,145 @@
+<template>
+	<view>
+		<view class='Popup' v-if='isShowAuth'>
+		   <image :src='logoUrl'></image>
+		   <view class='title'>授权提醒</view>
+		   <view class='tip'>请授权头像等信息,以便为您提供更好的服务</view>
+		   <view class='bottom flex'>
+		      <view class='item' @click='close'>随便逛逛</view>
+			  <!-- #ifdef APP-PLUS -->
+			  <button class='item grant' @click="setUserInfo">去授权</button>
+			  <!-- #endif -->
+			  <!-- #ifdef MP -->
+			  <button class='item grant'  type="primary" open-type="getUserInfo" lang="zh_CN" @getuserinfo="setUserInfo">去授权</button>
+			  <!-- #endif -->
+		   </view>
+		</view>
+		<view class='mask' v-if='isShowAuth' @click='close'></view>
+	</view>
+</template>
+
+<script>
+	const app = getApp();
+	import Cache from '../utils/cache';
+	import { getLogo } from '../api/public';
+	import { LOGO_URL } from '../config/cache';
+	import { mapGetters } from 'vuex';
+	import Routine from '../libs/routine';
+	
+	export default {
+		name:'Authorize',
+		props:{
+			isAuto:{
+				type:Boolean,
+				default:true
+			},
+			isGoIndex:{
+				type:Boolean,
+				default:true
+			},
+			isShowAuth:{
+				type:Boolean,
+				default:false
+			}
+		},
+		data(){
+			return {
+				logoUrl:''
+			}
+		},
+		computed:mapGetters(['isLogin','userInfo']),
+		watch:{
+			isLogin(n){
+				n === true && this.$emit('onLoadFun',this.userInfo);
+			}
+		},
+		created() {
+			this.getLogoUrl();
+			this.setAuthStatus();
+		},
+		methods:{
+			setAuthStatus(){
+				Routine.authorize().then(res=>{
+					if(res.islogin === false)
+						this.setUserInfo();
+					else
+						this.$emit('onLoadFun',this.userInfo);
+				}).catch(res=>{
+					if (this.isAuto) 
+						this.$emit('authColse',true);
+				})
+			},
+			getUserInfo(code){
+				Routine.getUserInfo().then(res=>{
+					let userInfo = res.userInfo
+					userInfo.code = code;
+					userInfo.spread_spid = app.globalData.spid;//获取推广人ID
+					userInfo.spread_code = app.globalData.code;//获取推广人分享二维码ID
+					userInfo.avatar  = userInfo.userInfo.avatarUrl;
+					userInfo.city  = userInfo.userInfo.city;
+					userInfo.country  = userInfo.userInfo.country;
+					userInfo.nickName  = userInfo.userInfo.nickName;
+					userInfo.province  = userInfo.userInfo.province;
+					userInfo.sex  = userInfo.userInfo.gender;
+					Routine.authUserInfo(code,userInfo).then(res=>{
+						uni.hideLoading();
+						this.$emit('authColse',false);
+						this.$emit('onLoadFun',this.userInfo);
+					}).catch(res=>{
+						uni.hideLoading();
+						uni.showToast({
+							title:res.message,
+							icon:'none',
+							duration:2000
+						});
+					})
+				}).catch(res =>{
+					uni.hideLoading();
+				})
+			},
+			setUserInfo(){
+				uni.showLoading({title:'正在登录中'});
+				Routine.getCode().then(code=>{
+					this.getUserInfo(code);
+				}).catch(res=>{
+					uni.hideLoading();
+				})
+			},
+			getLogoUrl(){
+				let that = this;
+				if (Cache.has(LOGO_URL)) {
+					this.logoUrl = Cache.get(LOGO_URL);
+					return;
+				}
+				getLogo().then(res=>{
+					that.logoUrl = res.data.logoUrl
+					Cache.set(LOGO_URL,that.logoUrl);
+				})
+			},
+			close(){
+				let pages = getCurrentPages(), currPage  = pages[pages.length - 1];
+				if(this.isGoIndex){
+					uni.switchTab({url:'/pages/index/index'});
+				} else {
+					this.$emit('authColse',false);
+				}
+				// if (currPage && currPage.isShowAuth != undefined){
+				// 	currPage.isShowAuth = true;
+				// }
+			},
+		}
+	}
+</script>
+
+<style scoped lang='scss'>
+	.Popup{width:500rpx;background-color:#fff;position:fixed;top:50%;left:50%;margin-left:-250rpx;transform:translateY(-50%);z-index:320;}
+	.Popup image{width:150rpx;height:150rpx;margin:-67rpx auto 0 auto;display:block;border: 8rpx solid #fff;border-radius: 50%}
+	.Popup .title{font-size:28rpx;color:#000;text-align:center;margin-top: 30rpx}
+	.Popup .tip{font-size:22rpx;color:#555;padding:0 24rpx;margin-top:25rpx;}
+	.Popup .bottom .item{width:50%;height:80rpx;background-color:#eeeeee;text-align:center;line-height:80rpx;font-size:24rpx;color:#666;margin-top:54rpx;}
+	.Popup .bottom .item.on{width: 100%}
+	.flex{display:flex;}
+	.Popup .bottom .item.grant{font-size:28rpx;color:#fff;font-weight:bold;background-color:$theme-color;border-radius:0;padding:0;}
+	.mask{position:fixed;top:0;right:0;left:0;bottom:0;background-color:rgba(0,0,0,0.65);z-index:310;}
+	
+</style>

+ 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>

+ 189 - 0
components/addressWindow/index.vue

@@ -0,0 +1,189 @@
+<template>
+	<view>
+		<view class="address-window" :class="address.address ? 'on' : ''">
+			<view class='title'>选择地址<text class='iconfont icon-guanbi' @tap='close'></text></view>
+			<view class='list'>
+				<view class='item acea-row row-between-wrapper' :class='active === index?"font-color":""'
+              v-for="(item,index) in addressList" @tap='tapAddress(index,item.id)' :key='index'>
+					<text class='iconfont icon-ditu' :class='active===index?"font-color":""' />
+					<view class='address'>
+						<view class='name' :class='active==index?"font-color":""'>
+              {{ item.name }}
+              <text class='phone'>{{ item.mobile }}</text>
+            </view>
+						<view class='line1'>{{ item.areaName }} {{ item.detailAddress }}</view>
+					</view>
+					<text class='iconfont icon-complete' :class='active === index?"font-color":""' />
+				</view>
+			</view>
+			<!-- 无地址 -->
+			<view class='pictrue' v-if="!is_loading && !addressList.length">
+				<image src='../../static/images/noAddress.png'></image>
+			</view>
+			<view class='addressBnt bg-color' @tap='goAddressPages'>选择其地址</view>
+		</view>
+		<view class='mask' catchtouchmove="true" :hidden='!address.address' @tap='close' />
+	</view>
+</template>
+
+<script>
+  import * as AddressApi from '@/api/member/address.js';
+  export default {
+		props: {
+			pagesUrl: {
+				type: String,
+				default: '',
+			},
+			address: {
+				type: Object,
+				default: function() {
+					return {
+						address: true, // 是否打开
+						addressId: 0,
+					};
+				}
+			},
+			isLog: {
+				type: Boolean,
+				default: false,
+			}
+		},
+		data() {
+			return {
+				active: 0, // 激活的 addressList 下标
+				is_loading: true,
+				addressList: []
+			};
+		},
+		methods: {
+      /**
+       * 选中某个地址
+       */
+			tapAddress: function(e, addressid) {
+				this.active = e;
+				for (let i = 0; i < this.addressList.length; i++) {
+					if (this.addressList[i].id === addressid) {
+            this.$emit('OnChangeAddress', this.addressList[i]);
+            break
+          }
+				}
+			},
+			close: function() {
+				this.$emit('changeClose');
+			},
+			goAddressPages: function() {
+				this.$emit('changeClose');
+				uni.navigateTo({
+					url: this.pagesUrl
+				});
+			},
+			getAddressList: function() {
+        AddressApi.getAddressList().then(res => {
+					const addressList = res.data;
+					this.$set(this, 'addressList', addressList);
+					this.is_loading = false;
+          // 处理默认选中项
+					if (!this.address.addressId) {
+            return;
+          }
+          let defaultAddress = {};
+          for (let i = 0; i < addressList.length; i++) {
+						if (addressList[i].id === this.address.addressId) {
+							this.active = i;
+							defaultAddress = this.addressList[i];
+						}
+					}
+				})
+			}
+		}
+	}
+</script>
+<style scoped lang="scss">
+	.address-window {
+		background-color: #fff;
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		width: 100%;
+		z-index: 101;
+		transform: translate3d(0, 100%, 0);
+		transition: all .3s cubic-bezier(.25, .5, .5, .9);
+	}
+
+	.address-window.on {
+		transform: translate3d(0, 0, 0);
+	}
+
+	.address-window .title {
+		font-size: 32rpx;
+		font-weight: bold;
+		text-align: center;
+		height: 123rpx;
+		line-height: 123rpx;
+		position: relative;
+	}
+
+	.address-window .title .iconfont {
+		position: absolute;
+		right: 30rpx;
+		color: #8a8a8a;
+		font-size: 35rpx;
+	}
+
+	.address-window .list .item {
+		margin-left: 30rpx;
+		padding-right: 30rpx;
+		border-bottom: 1px solid #eee;
+		height: 129rpx;
+		font-size: 25rpx;
+		color: #333;
+	}
+
+	.address-window .list .item .iconfont {
+		font-size: 37rpx;
+		color: #2c2c2c;
+	}
+
+	.address-window .list .item .iconfont.icon-complete {
+		font-size: 30rpx;
+		color: #fff;
+	}
+
+	.address-window .list .item .address {
+		width: 560rpx;
+	}
+
+	.address-window .list .item .address .name {
+		font-size: 28rpx;
+		font-weight: bold;
+		color: #282828;
+		margin-bottom: 4rpx;
+	}
+
+	.address-window .list .item .address .name .phone {
+		margin-left: 18rpx;
+	}
+
+	.address-window .addressBnt {
+		font-size: 30rpx;
+		font-weight: bold;
+		color: #fff;
+		width: 690rpx;
+		height: 86rpx;
+		border-radius: 43rpx;
+		text-align: center;
+		line-height: 86rpx;
+		margin: 85rpx auto;
+	}
+
+	.address-window .pictrue {
+		width: 414rpx;
+		height: 336rpx;
+		margin: 0 auto;
+	}
+
+	.address-window .pictrue image {
+		width: 100%;
+		height: 100%;
+	}
+</style>

+ 162 - 0
components/countDown/index.vue

@@ -0,0 +1,162 @@
+<template>
+	<view class="time" :style="justifyLeft">
+		<text class="" v-if="tipText">{{ tipText }}</text>
+		<text class="styleAll p6" v-if="isDay === true" :style="{background:bgColor.bgColor,color:bgColor.Color}">{{ day }}{{bgColor.isDay?'天':''}}</text>
+		<text class="timeTxt" v-if="dayText" :style="{width:bgColor.timeTxtwidth,color:bgColor.bgColor}">{{ dayText }}</text>
+		<text class="styleAll" :class='isCol?"timeCol":""' :style="{background:bgColor.bgColor,color:bgColor.Color,width:bgColor.width}">{{ hour }}</text>
+		<text class="timeTxt" v-if="hourText" :class='isCol?"whit":""' :style="{width:bgColor.timeTxtwidth,color:bgColor.bgColor}">{{ hourText }}</text>
+		<text class="styleAll" :class='isCol?"timeCol":""' :style="{background:bgColor.bgColor,color:bgColor.Color,width:bgColor.width}">{{ minute }}</text>
+		<text class="timeTxt" v-if="minuteText" :class='isCol?"whit":""' :style="{width:bgColor.timeTxtwidth,color:bgColor.bgColor}">{{ minuteText }}</text>
+		<text class="styleAll" :class='isCol?"timeCol":""' :style="{background:bgColor.bgColor,color:bgColor.Color,width:bgColor.width}">{{ second }}</text>
+		<text class="timeTxt" 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
+			},
+			isCol: {
+				type: Boolean,
+				default: false
+			},
+			bgColor: {
+				type: Object,
+				default: null
+			}
+		},
+		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 scoped>
+	.p6{
+		padding: 0 8rpx;
+	}
+	.styleAll{
+		/* color: #fff; */
+		font-size: 24rpx;
+		height: 36rpx;
+		line-height: 36rpx;
+		border-radius: 6rpx;
+		text-align: center;
+		/* padding: 0 6rpx; */
+	}
+	.timeTxt{
+		    text-align: center;
+		    /* width: 16rpx; */
+		    height: 36rpx;
+		    line-height: 36rpx;
+		    display: inline-block;
+	}
+	.whit{
+		color: #fff !important;
+	}
+	.time {
+		display: flex;
+		justify-content: center;
+	}
+
+	.red {
+		color: #fc4141;
+		margin: 0 4rpx;
+	}
+
+	.timeCol {
+		/* width: 40rpx;
+		height: 40rpx;
+		line-height: 40rpx;
+		text-align:center;
+		border-radius: 6px;
+		background: #fff;
+		font-size: 24rpx; */
+		color: #E93323;
+	}
+</style>

+ 247 - 0
components/couponListWindow/index.vue

@@ -0,0 +1,247 @@
+<template>
+	<view>
+		<view class='coupon-list-window' :class='coupon.coupon === true?"on":""'>
+			<view v-if="!orderShow"  class="nav acea-row row-around">
+				<view :class="['acea-row', 'row-middle', type === 1 ? 'on' : '']" @click="setType(1)">通用券</view>
+				<view :class="['acea-row', 'row-middle', type === 2 ? 'on' : '']" @click="setType(2)">商品券</view>
+				<view :class="['acea-row', 'row-middle', type === 3 ? 'on' : '']" @click="setType(3)">品类券</view>
+			</view>
+			<view class='coupon-list' :style="{'margin-top':!orderShow?'0':'50rpx'}">
+				<block v-if="coupon.list.length">
+					<view class='item acea-row row-center-wrapper' v-for="(item,index) in coupon.list"
+						@click="getCouponUser(index, item.id)" :key='index'>
+            <!-- 金额 -->
+						<view class='money acea-row row-column row-center-wrapper' :class='item.canTake ? "" : "moneyGray"'>
+							<view>¥
+                <text v-if="item.discountType === 1" class='num'>{{ fen2yuan(item.discountPrice) }}</text>
+                <text v-else class='num'>{{ (item.discountPercent / 10.0).toFixed(1) }} 折</text>
+              </view>
+							<view class="pic-num">满 {{ fen2yuan(item.usePrice) }} 元可用</view>
+						</view>
+						<view class='text'>
+              <!-- 类型 -->
+							<view class='condition line2'>
+								<span class='line-title' :class='item.isUse?"gray":""' v-if='type === 1'>通用</span>
+								<span class='line-title' :class='item.isUse?"gray":""'
+									v-else-if='type === 3'>品类</span>
+								<span class='line-title' :class='item.isUse?"gray":""' v-else>商品</span>
+								<span>{{item.name}}</span>
+							</view>
+              <!-- 领取类型 -->
+							<view class='data acea-row row-between-wrapper'>
+								<view v-if="item.validityType > 1">领取后 {{ item.fixedEndTerm }} 天内可用</view>
+								<view v-else>
+									{{ formatDate(item.validStartTime) + " - " + formatDate(item.validEndTime) }}
+								</view>
+								<view class='bnt bg-color' v-if="item.canTake">{{coupon.statusTile || '立即领取'}}</view>
+								<view class='bnt gray' v-else>{{item.use_title || '已领取'}}</view>
+							</view>
+						</view>
+					</view>
+				</block>
+				<!-- 无优惠券 -->
+				<view class='pictrue' v-else>
+					<image src='../../static/images/noCoupon.png'></image>
+				</view>
+			</view>
+		</view>
+		<view class='mask' catchtouchmove="true" :hidden='!coupon.coupon' @click='close'></view>
+	</view>
+</template>
+
+<script>
+  import * as Util from '@/utils/util.js';
+  import dayjs from "@/plugin/dayjs/dayjs.min.js";
+  import * as CouponApi from '@/api/promotion/coupon.js';
+  export default {
+		props: {
+			openType: {
+				type: Number, // 打开状态 0=领取优惠券, 1=使用优惠券
+				default: 0,
+			},
+			coupon: {
+				type: Object,
+				default: function() {
+					return {};
+				}
+			},
+			orderShow: {
+				type: String, // 下单页面使用优惠券组件不展示 tab 切换页
+				default: function() {
+					return '';
+				}
+			}
+		},
+		data() {
+			return {
+        type: 1 // 使用类型
+			};
+		},
+		methods: {
+			close: function() {
+				this.type = 1
+				this.$emit('ChangCouponsClose');
+			},
+      /**
+       * 选择优惠劵
+       */
+			getCouponUser: function(index, id) {
+        // 领取优惠劵时,如果已经领取,则直接跳过
+				let list = this.coupon.list;
+				switch (this.openType) {
+					case 0: // 领取优惠券
+            if (!list[index].canTake) {
+              return;
+            }
+            CouponApi.takeCoupon(id).then(res => {
+              this.$util.Tips({
+								title: "领取成功"
+							});
+              this.$emit('ChangCoupons', list[index]);
+						})
+						break;
+					case 1: // 使用优惠劵
+            // TODO 芋艿:需要额外把不可用优惠劵的样式做了;
+            if (list[index].match === false) {
+              alert('该优惠劵无法使用,原因:' + list[index].description);
+              return;
+            }
+            this.$emit('ChangCoupons', index);
+						break;
+				}
+			},
+			setType: function(type) {
+				this.type = type;
+				this.$emit('tabCouponType', type);
+			},
+
+      fen2yuan(price) {
+        return Util.fen2yuan(price)
+      },
+      formatDate: function(date) {
+        return dayjs(date).format("YYYY-MM-DD");
+      }
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.coupon-list-window {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		width: 100%;
+		background-color: #f5f5f5;
+		border-radius: 16rpx 16rpx 0 0;
+		z-index: 555;
+		transform: translate3d(0, 100%, 0);
+		transition: all .3s cubic-bezier(.25, .5, .5, .9);
+	}
+
+	.coupon-list-window.on {
+		transform: translate3d(0, 0, 0);
+	}
+
+	.coupon-list-window .title {
+		height: 124rpx;
+		width: 100%;
+		text-align: center;
+		line-height: 124rpx;
+		font-size: 32rpx;
+		font-weight: bold;
+		position: relative;
+	}
+
+	.coupon-list-window .title .iconfont {
+		position: absolute;
+		right: 30rpx;
+		top: 50%;
+		transform: translateY(-50%);
+		font-size: 35rpx;
+		color: #8a8a8a;
+		font-weight: normal;
+	}
+
+	.coupon-list-window .coupon-list {
+		margin: 0 0 30rpx 0;
+		height: 823rpx;
+		overflow: auto;
+		padding-top: 30rpx;
+	}
+
+	.coupon-list-window .pictrue {
+		width: 414rpx;
+		height: 336rpx;
+		margin: 208rpx auto;
+	}
+
+	.coupon-list-window .pictrue image {
+		width: 100%;
+		height: 100%;
+	}
+
+	.pic-num {
+		color: #fff;
+		font-size: 24rpx;
+	}
+
+	.line-title {
+		width: 90rpx;
+		padding: 0 10rpx;
+		box-sizing: border-box;
+		background: rgba(255, 247, 247, 1);
+		border: 1px solid rgba(232, 51, 35, 1);
+		opacity: 1;
+		border-radius: 20rpx;
+		font-size: 20rpx;
+		color: #E83323;
+		margin-right: 12rpx;
+	}
+
+	.line-title.gray {
+		border-color: #BBB;
+		color: #bbb;
+		background-color: #F5F5F5;
+	}
+
+	.nav {
+		// position: absolute;
+		// top: 0;
+		// left: 0;
+		width: 100%;
+		height: 96rpx;
+		border-bottom: 2rpx solid #F5F5F5;
+		border-top-left-radius: 16rpx;
+		border-top-right-radius: 16rpx;
+		background-color: #FFFFFF;
+		font-size: 30rpx;
+		color: #999999;
+	}
+
+	.nav .acea-row {
+		border-top: 5rpx solid transparent;
+		border-bottom: 5rpx solid transparent;
+	}
+
+	.nav .acea-row.on {
+		border-bottom-color: #E93323;
+		color: #282828;
+	}
+
+	.nav .acea-row:only-child {
+		border-bottom-color: transparent;
+	}
+
+	.occupy {
+		height: 106rpx;
+	}
+
+	.coupon-list .item {
+		margin-bottom: 20rpx;
+		box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.06);
+	}
+
+	.coupon-list .item .money {
+		font-weight: normal;
+	}
+</style>

File diff suppressed because it is too large
+ 50 - 0
components/couponWindow/index.vue


+ 260 - 0
components/easy-upload/easy-upload.vue

@@ -0,0 +1,260 @@
+<template>
+	<view>
+		<view class="upload">
+			<block v-for="(upload,index) in uploads" :key="index">
+				<view class="uplode-file">
+					<image v-if="types == 'image'" class="uploade-img" :src="upload" :data-src="upload" @tap="previewImage"></image>
+					<image v-if="types == 'image'" class="clear-one-icon" :src="clearIcon" @tap="delImage(index)"></image>
+					<video v-if="types == 'video'" class="uploade-img" :src="upload" controls>
+						<cover-image v-if="types == 'video'" class="clear-one-icon" :src="clearIcon" @tap="delImage(index)"></cover-image>
+					</video>
+				</view>
+			</block>
+			<view class="uploader-input-box" v-if="uploads.length < uploadCount">
+				<view class="uploader-input" @tap="chooseUploads"></view>
+			</view>
+		</view>	
+		<button type="primary" v-if="types == 'image'" @tap="upload">上传</button>
+	</view>
+</template>
+
+<script>
+	export default{
+		props: {
+			types: {
+				type: String,
+				default: 'image'
+			},
+			dataList: {
+				type: Array,
+				default: function() {
+					return []
+				}
+			},
+			clearIcon: {
+				type: String,
+				default: 'http://img1.imgtn.bdimg.com/it/u=451604666,2295832001&fm=26&gp=0.jpg'
+			},
+			uploadUrl: {
+				type: String,
+				default: ''
+			},
+			deleteUrl: {
+				type: String,
+				default: ''
+			},
+			uploadCount: {
+				type: Number,
+				default: 1
+			},
+			//上传图片大小 默认3M
+			upload_max: {
+				type: Number,
+				default: 3
+			}
+		},
+		data(){
+			return {
+				//上传的图片地址
+				uploadImages: [],
+				//展示的图片地址
+				uploads: [],
+				// 超出限制数组
+				exceeded_list: [],
+			}
+		},
+		watch:{
+			dataList() {
+				this.uploads = this.dataList
+			}
+		},
+		methods:{
+			previewImage (e) {
+				var current = e.target.dataset.src
+				uni.previewImage({
+					current: current,
+					urls: this.dataList
+				})
+			},
+			chooseUploads(){
+				switch (this.types){
+					case 'image': 
+						uni.chooseImage({
+							count: this.uploadCount - this.uploads.length, //默认9
+							sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
+							sourceType: ['album', 'camera'], //从相册选择
+							success: (res) => {
+								for(let i = 0; i< res.tempFiles.length; i++){
+									if(Math.ceil(res.tempFiles[i].size / 1024) < this.upload_max * 1024){
+										this.uploads.push(res.tempFiles[i].path)
+										this.uploadImages.push(res.tempFiles[i].path)
+									}else {
+										this.exceeded_list.push(i === 0 ? 1 : i + 1);
+										uni.showModal({
+											title: '提示',
+											content: `第${[...new Set(this.exceeded_list)].join(',')}张图片超出限制${this.upload_max}MB,已过滤`
+										});
+									}
+								}
+							},
+							fail: (err) => {
+								uni.showModal({
+									content: JSON.stringify(err)
+								});
+							}
+						});
+					break;
+					case 'video' :
+						uni.chooseVideo({
+							sourceType: ['camera', 'album'],
+							success: (res) => {
+								if(Math.ceil(res.size / 1024) < this.upload_max * 1024){
+									this.uploads.push(res.tempFilePath)
+									uni.uploadFile({
+										url: this.uploadUrl, //仅为示例,非真实的接口地址
+										filePath: res.tempFilePath,
+										name: 'file',
+										//请求参数
+										formData: {
+											'user': 'test'
+										},
+										success: (uploadFileRes) => {
+											this.$emit('successVideo',uploadFileRes)
+										}
+									});
+								}else {
+									uni.showModal({
+										title: '提示',
+										content: `第${[...new Set(this.exceeded_list)].join(',')}张视频超出限制${this.upload_max}MB,已过滤`
+									});
+								}
+							},
+							fail: (err) => {
+								uni.showModal({
+									content: JSON.stringify(err)
+								});
+							}
+						});
+					break;
+				}
+			},
+			delImage(index){
+				if(this.uploads[index].substring(0,4) !== 'http'){
+					this.uploads.splice(index,1)
+					return;
+				};
+				if(!this.deleteUrl) {
+					uni.showModal({
+						content: '请填写删除接口'
+					});
+					return;
+				};
+				uni.request({
+					url: this.deleteUrl,
+					method: 'DELETE',
+					data: {
+						image: this.dataList[index]
+					},
+					success: res => {
+						this.uploads.splice(index,1)
+					},
+				});
+			},
+			upload(){
+				if(!this.uploadUrl) {
+					uni.showModal({
+						content: '请填写上传接口'
+					});
+					return;
+				};
+				for (let i of this.uploadImages) {
+					uni.uploadFile({
+						url: this.uploadUrl, //仅为示例,非真实的接口地址
+						filePath: i,
+						name: 'file',
+						//请求参数
+						formData: {
+							'user': 'test'
+						},
+						success: (uploadFileRes) => {
+							this.$emit('successImage',uploadFileRes)
+						}
+					});
+				}
+			}
+		}
+	}
+</script>
+
+<style scoped>
+	.upload {
+		display: flex;
+		flex-direction: row;
+		flex-wrap: wrap;
+	}
+	.uplode-file {
+		margin: 10upx;
+		width: 210upx;
+		height: 210upx;
+		position: relative;
+	}
+	.uploade-img {
+		display: block;
+		width: 210upx;
+		height: 210upx;
+	}
+	.clear-one{
+		position: absolute;
+		top: -10rpx;
+		right: 0;
+	}
+	.clear-one-icon{
+		position: absolute;
+		width: 20px;
+		height: 20px;
+		top: 0;
+		right: 0;
+		z-index: 9;
+	}
+	.uploader-input-box {
+		position: relative;
+		margin:10upx;
+		width: 208upx;
+		height: 208upx;
+		border: 2upx solid #D9D9D9;
+	}
+	.uploader-input-box:before,
+	.uploader-input-box:after {
+		content: " ";
+		position: absolute;
+		top: 50%;
+		left: 50%;
+		-webkit-transform: translate(-50%, -50%);
+		transform: translate(-50%, -50%);
+		background-color: #D9D9D9;
+	}
+	.uploader-input-box:before {
+		width: 4upx;
+		height: 79upx;
+	}
+	.uploader-input-box:after {
+		width: 79upx;
+		height: 4upx;
+	}
+	.uploader-input-box:active {
+		border-color: #999999;
+	}
+	.uploader-input-box:active:before,
+	.uploader-input-box:active:after {
+		background-color: #999999;
+	}
+	.uploader-input {
+		position: absolute;
+		z-index: 1;
+		top: 0;
+		left: 0;
+		width: 100%;
+		height: 100%;
+		opacity: 0;
+	}
+</style>

+ 40 - 0
components/easy-upload/readme.md

@@ -0,0 +1,40 @@
+### easy-upload 组件
+
+使用方法
+```js
+	<easy-upload
+	:dataList="imageList" uploadUrl="http://localhost:3000/upload" :types="category"
+	deleteUrl='http://localhost:3000/upload' :uploadCount="6"
+	@successImage="successImage" @successVideo="successvideo"
+	></easy-upload>
+	
+	//先引入组件
+    import easyUpload from '@/components/easy-upload/easy-upload.vue'
+    //注册组件
+    components:{
+        easyUpload
+    }
+	
+	//使用 easycom 可以直接使用
+
+```
+
+|  参数   | 类型  | 是否必填 | 参数描述
+|  ----  | ----  | ---- | ----
+| types  | String | 否 | 上传类型 image/video
+| dataList  | Array | 否 | 图片/视频数据展示
+| clearIcon  | String | 否 | 删除图标(可以换成自己本地图片)
+| uploadUrl  | String | 否 | 上传的接口
+| deleteUrl  | String | 否 | 删除的接口
+| uploadCount  | Number | 否 | 上传图片最大个数(默认为一张)
+| upload_max  | Number | 否 | 上传大小(默认为3M)
+| upload_max  | Number | 否 | 上传大小(默认为3M)
+| upload_max  | Number | 否 | 上传大小(默认为3M)
+
+|  事件  | 是否必填 | 参数描述
+|  ---- | ---- | ----
+| successImage  | 否 | 上传图片成功事件
+| successVideo  |  否 | 上传视频成功回调
+
+示例项目中有服务端代码 (node.js)
+

+ 37 - 0
components/emptyPage.vue

@@ -0,0 +1,37 @@
+<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;
+		padding-bottom: 60rpx;
+		image{
+			width: 414rpx;
+			height: 240rpx;
+		}
+		.txt{
+			font-size: 26rpx;
+			color: #999;
+		}
+	}
+</style>

+ 140 - 0
components/goodList/index.vue

@@ -0,0 +1,140 @@
+<template>
+	<view class='goodList'>
+		<block v-for="(item,index) in bastList" :key="index">
+			<view @click="goDetail(item)" class='item acea-row row-between-wrapper' hover-class="none">
+				<view class='pictrue'>
+					<image :src='item.picUrl'></image>
+					<span class="pictrue_log pictrue_log_class" v-if="item.activityList && item.activityList[0] && item.activityList[0].type === 1">秒杀</span>
+					<span class="pictrue_log pictrue_log_class" v-if="item.activityList && item.activityList[0] && item.activityList[0].type === 2">砍价</span>
+					<span class="pictrue_log pictrue_log_class" v-if="item.activityList && item.activityList[0] && item.activityList[0].type === 3">拼团</span>
+				</view>
+				<view class='underline'>
+					<view class='text'>
+						<view class='line1'>{{ item.name }}</view>
+						<view class='money font-color'>¥<text class='num'>{{ fen2yuan(item.price) }}</text></view>
+						<view class='vip-money acea-row row-middle' v-if="item.vipPrice > 0">¥{{ fen2yuan(item.price - item.vipPrice) }}
+							<image src='../../static/images/vip.png'></image><text class='num'>已售{{ item.salesCount }}{{item.unitName}}</text>
+						</view>
+						<view class='vip-money acea-row row-middle' v-else><text class='num'>已售{{ item.salesCount }}{{item.unitName}}</text></view>
+					</view>
+				</view>
+				<view class='iconfont icon-gouwuche cart-color acea-row row-center-wrapper'></view>
+			</view>
+		</block>
+	</view>
+</template>
+
+<script>
+	import {mapGetters} from "vuex";
+	import { goShopDetail } from '@/libs/order.js'
+  import * as Util from '@/utils/util.js';
+  export default {
+		computed: mapGetters(['uid']),
+		props: {
+			status: {
+				type: Number,
+				default: 0,
+			},
+			bastList: {
+				type: Array,
+				default: function() {
+					return [];
+				}
+			}
+		},
+		methods: {
+			goDetail(item) {
+				goShopDetail(item,this.uid).then(() =>{
+					uni.navigateTo({
+						url:`/pages/goods_details/index?id=${item.id}`
+					})
+				})
+			},
+      fen2yuan(price) {
+        return Util.fen2yuan(price)
+      }
+		}
+	}
+</script>
+<style scoped lang='scss'>
+	.goodList .item {
+		position: relative;
+		padding-left: 30rpx;
+	}
+
+	.goodList .item .pictrue {
+		width: 180rpx;
+		height: 180rpx;
+		position: relative;
+	}
+
+	.goodList .item .pictrue image {
+		width: 100%;
+		height: 100%;
+		border-radius: 6rpx;
+	}
+
+	.goodList .item .pictrue .numPic {
+		position: absolute;
+		left: 7rpx;
+		top: 7rpx;
+		width: 50rpx;
+		height: 50rpx;
+		border-radius: 50%;
+	}
+
+	.goodList .item .underline {
+		padding: 30rpx 30rpx 30rpx 0;
+		border-bottom: 1px solid #f5f5f5;
+	}
+
+	.goodList .item:nth-last-child(1) .underline {
+		border-bottom: 0;
+	}
+
+	.goodList .item .text {
+		font-size: 30rpx;
+		color: #222;
+		width: 489rpx;
+	}
+
+	.goodList .item .text .money {
+		font-size: 26rpx;
+		font-weight: bold;
+		margin-top: 50rpx;
+	}
+
+	.goodList .item .text .money .num {
+		font-size: 34rpx;
+	}
+
+	.goodList .item .text .vip-money {
+		font-size: 24rpx;
+		color: #282828;
+		font-weight: bold;
+		margin-top: 15rpx;
+	}
+
+	.goodList .item .text .vip-money image {
+		width: 46rpx;
+		height: 21rpx;
+		margin: 0 22rpx 0 5rpx;
+	}
+
+	.goodList .item .text .vip-money .num {
+		font-size: 22rpx;
+		color: #aaa;
+		font-weight: normal;
+		margin: -2rpx 0 0 0;
+	}
+
+	.goodList .item .iconfont {
+		position: absolute;
+		right: 30rpx;
+		width: 50rpx;
+		height: 50rpx;
+		border-radius: 50%;
+		font-size: 30rpx;
+		bottom: 38rpx;
+	}
+</style>

+ 107 - 0
components/home/index.vue

@@ -0,0 +1,107 @@
+<template>
+	<view style="touch-action: none;">
+		<view class="home" style="position:fixed;" :style="{ top: top + 'px'}" 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 hover-class='none' url='/pages/order_addcart/order_addcart' open-type='switchTab'
+                   class='iconfont icon-caigou-xianxing' />
+				<navigator hover-class='none' url='/pages/user/index' open-type='switchTab'
+                   class='iconfont icon-yonghu1' />
+			</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: "500"
+			};
+		},
+		computed: mapGetters(["homeActive"]),
+		methods: {
+			setTouchMove(e) {
+				if (e.touches[0].clientY < 545 && e.touches[0].clientY > 66) {
+					this.top = e.touches[0].clientY
+				}
+			},
+			open: function() {
+				this.homeActive ?
+					this.$store.commit("CLOSE_HOME") :
+					this.$store.commit("OPEN_HOME");
+			}
+		}
+	};
+</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: $theme-color;
+		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>

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

@@ -0,0 +1,814 @@
+<!--
+  parser 主模块组件
+  github:https://github.com/jin-yufeng/Parser 
+  docs:https://jin-yufeng.github.io/Parser
+  插件市场:https://ext.dcloud.net.cn/plugin?id=805
+  author:JinYufeng
+  update:2020/04/14
+-->
+<template>
+	<view>
+		<slot v-if="!nodes.length" />
+		<!--#ifdef APP-PLUS-NVUE-->
+		<web-view id="top" ref="web" :src="src" :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':'')" :animation="scaleAm" @tap="_tap"
+		 @touchstart="_touchstart" @touchmove="_touchmove">
+			<!--#ifdef H5-->
+			<div :id="'rtf'+uid"></div>
+			<!--#endif-->
+			<!--#ifndef H5-->
+			<trees :nodes="nodes" :lazy-load="lazyLoad" :loadVideo="loadVideo" />
+			<image v-for="(item, index) in imgs" v-bind:key="index" :id="index" :src="item" hidden @load="_load" />
+			<!--#endif-->
+		</view>
+		<!--#endif-->
+	</view>
+</template>
+
+<script>
+	// #ifndef H5 || APP-PLUS-NVUE
+	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 document; // document 补丁包 https://jin-yufeng.github.io/Parser/#/instructions?id=document
+	// 计算 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
+	var rpx = uni.getSystemInfoSync().screenWidth / 750,
+		cfg = require('./libs/config.js');
+	// #endif
+	// #ifdef APP-PLUS-NVUE
+	var dom = weex.requireModule('dom');
+	// #endif
+	export default {
+		name: 'parser',
+		data() {
+			return {
+				// #ifdef APP-PLUS
+				loadVideo: false,
+				// #endif
+				// #ifdef H5
+				uid: this._uid,
+				// #endif
+				// #ifdef APP-PLUS-NVUE
+				src: '',
+				height: 1,
+				// #endif
+				// #ifndef APP-PLUS-NVUE
+				scaleAm: '',
+				showAm: '',
+				imgs: [],
+				// #endif
+				nodes: []
+			}
+		},
+		// #ifndef H5 || APP-PLUS-NVUE
+		components: {
+			trees
+		},
+		// #endif
+		props: {
+			'html': null,
+			// #ifndef MP-ALIPAY
+			'autopause': {
+				type: Boolean,
+				default: true
+			},
+			// #endif
+			'autosetTitle': {
+				type: Boolean,
+				default: true
+			},
+			// #ifndef H5 || APP-PLUS-NVUE
+			'compress': Number,
+			'useCache': Boolean,
+			'xml': Boolean,
+			// #endif
+			'domain': String,
+			// #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
+			'gestureZoom': Boolean,
+			// #endif
+			// #ifdef MP-WEIXIN || MP-QQ || H5 || APP-PLUS
+			'lazyLoad': Boolean,
+			// #endif
+			'selectable': Boolean,
+			'tagStyle': Object,
+			'showWithAnimation': Boolean,
+			'useAnchor': Boolean
+		},
+		watch: {
+			html(html) {
+				this.setContent(html);
+			}
+		},
+		mounted() {
+			// 图片数组
+			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 = '';
+					for (var j = 0, 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
+				}
+			}
+			if (this.html) this.setContent(this.html);
+		},
+		beforeDestroy() {
+			// #ifdef H5
+			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: {
+			// #ifdef H5 || APP-PLUS-NVUE
+			_Dom2Str(nodes) {
+				var str = '';
+				for (var node of nodes) {
+					if (node.type == 'text')
+						str += node.text;
+					else {
+						str += ('<' + node.name);
+						for (var attr in node.attrs || {})
+							str += (' ' + attr + '="' + node.attrs[attr] + '"');
+						if (!node.children || !node.children.length) str += '>';
+						else str += ('>' + this._Dom2Str(node.children) + '</' + node.name + '>');
+					}
+				}
+				return str;
+			},
+			_handleHtml(html, append) {
+				if (typeof html != 'string') html = this._Dom2Str(html.nodes || html);
+				// 处理 rpx
+				if (html.includes('rpx'))
+					html = html.replace(/[0-9.]+\s*rpx/g, $ => parseFloat($) * rpx + 'px');
+				if (!append) {
+					// 处理 tag-style 和 userAgentStyles
+					var style = '<style>@keyframes show{0%{opacity:0}100%{opacity:1}}';
+					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;
+				}
+				return html;
+			},
+			// #endif
+			setContent(html, append) {
+				// #ifdef APP-PLUS-NVUE
+				if (!html) {
+					this.src = '';
+					this.height = 1;
+					return;
+				}
+				if (append) return;
+				plus.io.resolveLocalFileSystemURL('_doc', entry => {
+					entry.getDirectory('parser_tmp', {
+						create: true
+					}, entry => {
+						var fileName = Date.now() + '.html';
+						entry.getFile(fileName, {
+							create: true
+						}, entry => {
+							entry.createWriter(writer => {
+								writer.onwriteend = () => {
+									this.nodes = [1];
+									this.src = '_doc/parser_tmp/' + fileName;
+									this.$nextTick(function() {
+										entry.remove();
+									})
+								}
+								html =
+									'<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1' +
+									(this.selectable ? '' : ',user-scalable=no') +
+									'"><script type="text/javascript" src="https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js"></' +
+									'script><base href="' + this.domain + '">' + this._handleHtml(html) +
+									'<script>"use strict";function post(t){uni.postMessage({data:t})}' +
+									(this.showWithAnimation ? 'document.body.style.animation="show .5s",' : '') +
+									'document.addEventListener("UniAppJSBridgeReady",function(){post({action:"load",text:document.body.innerText});var t=document.getElementsByTagName("title");t.length&&post({action:"getTitle",title:t[0].innerText});for(var e,o=document.getElementsByTagName("img"),n=[],i=0,r=0;e=o[i];i++)e.onerror=function(){post({action:"error",source:"img",target:this})},e.hasAttribute("ignore")||"A"==e.parentElement.nodeName||(e.i=r++,n.push(e.src),e.onclick=function(){post({action:"preview",img:{i:this.i,src:this.src}})});post({action:"getImgList",imgList:n});for(var a,s=document.getElementsByTagName("a"),c=0;a=s[c];c++)a.onclick=function(){var t,e=this.getAttribute("href");if("#"==e[0]){var r=document.getElementById(e.substr(1));r&&(t=r.offsetTop)}return post({action:"linkpress",href:e,offset:t}),!1};;for(var u,m=document.getElementsByTagName("video"),d=0;u=m[d];d++)u.style.maxWidth="100%",u.onerror=function(){post({action:"error",source:"video",target:this})}' +
+									(this.autopause ? ',u.onplay=function(){for(var t,e=0;t=m[e];e++)t!=this&&t.pause()}' : '') +
+									';for(var g,l=document.getElementsByTagName("audio"),p=0;g=l[p];p++)g.onerror=function(){post({action:"error",source:"audio",target:this})};window.onload=function(){post({action:"ready",height:document.body.scrollHeight})}});</' +
+									'script>';
+								writer.write(html);
+							});
+						})
+					})
+				})
+				// #endif
+				// #ifdef H5
+				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: '900px 0px 900px 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++) {
+					img.style.maxWidth = '100%';
+					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() {
+						_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('audios');
+				for (var audio of audios)
+					audio.onerror = function() {
+						_ts.$emit('error', {
+							source: 'audio',
+							target: this
+						});
+					}
+				this.document = this.rtf;
+				if (!append) document.getElementById('rtf' + this._uid).appendChild(this.rtf);
+				this.$nextTick(() => {
+					this.nodes = [1];
+					this.$emit('load');
+				})
+				setTimeout(() => this.showAm = '', 500);
+				// #endif
+				// #ifndef H5 || APP-PLUS-NVUE
+				var nodes;
+				if (!html)
+					return this.nodes = [];
+				else if (typeof html == 'string') {
+					let 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);
+				} else if (Object.prototype.toString.call(html) == '[object Array]') {
+					// 非本插件产生的 array 需要进行一些转换
+					if (html.length && html[0].PoweredBy != 'Parser') {
+						let parser = new Parser(html, this);
+						(function f(ns) {
+							for (var i = 0, n; n = ns[i]; i++) {
+								if (n.type == 'text') continue;
+								n.attrs = n.attrs || {};
+								for (var item in n.attrs)
+									if (typeof n.attrs[item] != 'string') n.attrs[item] = n.attrs[item].toString();
+								parser.matchAttr(n, parser);
+								if (n.children && n.children.length) {
+									parser.STACK.push(n);
+									f(n.children);
+									parser.popNode(parser.STACK.pop());
+								} else n.children = void 0;
+							}
+						})(html);
+					}
+					nodes = html;
+				} else if (typeof html == 'object' && html.nodes) {
+					nodes = html.nodes;
+					console.warn('错误的 html 类型:object 类型已废弃');
+				} else
+					return console.warn('错误的 html 类型:' + typeof html);
+				// #ifdef APP-PLUS
+				this.loadVideo = false;
+				// #endif
+				if (document) this.document = new document(this.nodes, 'nodes', this);
+				if (append) this.nodes = this.nodes.concat(nodes);
+				else this.nodes = nodes;
+				if (nodes.length && nodes[0].title && this.autosetTitle)
+					uni.setNavigationBarTitle({
+						title: nodes[0].title
+					})
+				this.$nextTick(() => {
+					this.imgList.length = 0;
+					this.videoContexts = [];
+					// #ifdef MP-TOUTIAO
+					setTimeout(() => {
+						// #endif
+						var f = (cs) => {
+							for (let i = 0, c; c = cs[i++];) {
+								if (c.$options.name == 'trees') {
+									for (var j = c.nodes.length, item; item = c.nodes[--j];) {
+										if (item.c) continue;
+										if (item.name == 'img') {
+											this.imgList.setItem(item.attrs.i, item.attrs.src);
+											// #ifndef MP-ALIPAY
+											if (!c.observer && !c.imgLoad && item.attrs.i != '0') {
+												if (this.lazyLoad && uni.createIntersectionObserver) {
+													c.observer = uni.createIntersectionObserver(c);
+													c.observer.relativeToViewport({
+														top: 900,
+														bottom: 900
+													}).observe('._img', () => {
+														c.imgLoad = true;
+														c.observer.disconnect();
+													})
+												} else
+													c.imgLoad = true;
+											}
+											// #endif
+										}
+										// #ifndef MP-ALIPAY
+										else if (item.name == 'video') {
+											var ctx = uni.createVideoContext(item.attrs.id, c);
+											ctx.id = item.attrs.id;
+											this.videoContexts.push(ctx);
+										}
+										// #endif
+										// #ifdef MP-BAIDU || MP-ALIPAY || APP-PLUS
+										if (item.attrs && item.attrs.id) {
+											this.anchors = this.anchors || [];
+											this.anchors.push({
+												id: item.attrs.id,
+												node: c
+											})
+										}
+										// #endif
+									}
+								}
+								if (c.$children.length)
+									f(c.$children)
+							}
+						}
+						f(this.$children);
+						// #ifdef MP-TOUTIAO
+					}, 200)
+					this.$emit('load');
+					// #endif
+					// #ifdef APP-PLUS
+					setTimeout(() => {
+						this.loadVideo = true;
+					}, 3000);
+					// #endif
+				})
+				// #endif
+				// #ifndef APP-PLUS-NVUE
+				var height;
+				clearInterval(this._timer);
+				this._timer = setInterval(() => {
+					// #ifdef H5
+					var res = [this.rtf.getBoundingClientRect()];
+					// #endif
+					// #ifndef H5
+					// #ifdef APP-PLUS
+					uni.createSelectorQuery().in(this)
+					// #endif
+					// #ifndef APP-PLUS
+					this.createSelectorQuery()
+						// #endif
+						.select('#top').boundingClientRect().exec(res => {
+							// #endif
+							this.width = res[0].width;
+							if (res[0].height == height) {
+								this.$emit('ready', res[0])
+								clearInterval(this._timer);
+							}
+							height = res[0].height;
+							// #ifndef H5
+						});
+					// #endif
+				}, 350)
+				if (this.showWithAnimation && !append) this.showAm = 'animation:show .5s';
+				// #endif
+			},
+			getText(ns = this.nodes) {
+				// #ifdef APP-PLUS-NVUE
+				return this._text;
+				// #endif
+				// #ifdef H5
+				return this.rtf.innerText;
+				// #endif
+				// #ifndef H5 || APP-PLUS-NVUE
+				var txt = '';
+				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';
+					}
+				}
+				return txt;
+				// #endif
+			},
+			navigateTo(obj) {
+				if (!this.useAnchor)
+					return obj.fail && obj.fail({
+						errMsg: 'Anchor is disabled'
+					})
+				// #ifdef APP-PLUS-NVUE
+				if (!obj.id)
+					dom.scrollToElement(this.$refs.web);
+				else
+					this.$refs.web.evalJs('var pos=document.getElementById("' + obj.id +
+						'");if(pos)post({action:"linkpress",href:"#",offset:pos.offsetTop})');
+				return obj.success && obj.success({
+					errMsg: 'pageScrollTo:ok'
+				});
+				// #endif
+				// #ifdef H5
+				if (!obj.id) {
+					window.scrollTo(0, this.rtf.offsetTop);
+					return obj.success && obj.success({
+						errMsg: 'pageScrollTo:ok'
+					});
+				}
+				var target = document.getElementById(obj.id);
+				if (!target) return obj.fail && obj.fail({
+					errMsg: 'Label not found'
+				});
+				obj.scrollTop = this.rtf.offsetTop + target.offsetTop;
+				uni.pageScrollTo(obj);
+				// #endif
+				// #ifndef H5
+				var Scroll = (selector, component) => {
+					uni.createSelectorQuery().in(component ? component : this).select(selector).boundingClientRect().selectViewport()
+						.scrollOffset()
+						.exec(res => {
+							if (!res || !res[0])
+								return obj.fail && obj.fail({
+									errMsg: 'Label not found'
+								});
+							obj.scrollTop = res[1].scrollTop + res[0].top;
+							uni.pageScrollTo(obj);
+						})
+				}
+				if (!obj.id) Scroll('#top');
+				else {
+					// #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
+					Scroll('#top >>> #' + obj.id + ', #top >>> .' + obj.id);
+					// #endif
+					// #ifdef MP-BAIDU || MP-ALIPAY || APP-PLUS
+					for (var anchor of this.anchors)
+						if (anchor.id == obj.id)
+							Scroll('#' + obj.id + ', .' + obj.id, anchor.node);
+					// #endif
+				}
+				// #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
+			},
+			// 预加载
+			preLoad(html, num) {
+				// #ifdef H5 || APP-PLUS-NVUE
+				if (html.constructor == Array)
+					html = this._Dom2Str(html);
+				var script = "var contain=document.createElement('div');contain.innerHTML='" + html.replace(/'/g, "\\'") +
+					"';for(var imgs=contain.querySelectorAll('img'),i=imgs.length-1;i>=" + num +
+					";i--)imgs[i].removeAttribute('src');";
+				// #endif
+				// #ifdef APP-PLUS-NVUE
+				this.$refs.web.evalJs(script);
+				// #endif
+				// #ifdef H5
+				eval(script);
+				// #endif
+				// #ifndef H5 || APP-PLUS-NVUE
+				if (typeof html == 'string') {
+					var id = hash(html);
+					html = new Parser(html, this).parse();
+					cache[id] = html;
+				}
+				var wait = [];
+				(function f(ns) {
+					for (var i = 0, n; n = ns[i++];) {
+						if (n.name == 'img' && n.attrs.src && !wait.includes(n.attrs.src))
+							wait.push(n.attrs.src);
+						f(n.children || []);
+					}
+				})(html);
+				if (num) wait = wait.slice(0, num);
+				this._wait = (this._wait || []).concat(wait);
+				if (!this.imgs) this.imgs = this._wait.splice(0, 15);
+				else if (this.imgs.length < 15)
+					this.imgs = this.imgs.concat(this._wait.splice(0, 15 - this.imgs.length));
+				// #endif
+			},
+			// #ifdef APP-PLUS-NVUE
+			_message(e) {
+				// 接收 web-view 消息
+				var data = e.detail.data[0];
+				if (data.action == 'load') {
+					this.$emit('load');
+					this._text = data.text;
+				} else if (data.action == 'getTitle') {
+					if (this.autosetTitle)
+						uni.setNavigationBarTitle({
+							title: data.title
+						})
+				} else if (data.action == 'getImgList') {
+					this.imgList.length = 0;
+					for (var i = data.imgList.length; i--;)
+						this.imgList.setItem(i, data.imgList[i]);
+				} else if (data.action == 'preview') {
+					var preview = true;
+					data.img.ignore = () => preview = false;
+					this.$emit('imgtap', data.img);
+					if (preview)
+						uni.previewImage({
+							current: data.img.i,
+							urls: this.imgList
+						})
+				} else if (data.action == 'linkpress') {
+					var jump = true,
+						href = data.href;
+					this.$emit('linkpress', {
+						href,
+						ignore: () => jump = false
+					})
+					if (jump && href) {
+						if (href[0] == '#') {
+							if (this.useAnchor)
+								dom.scrollToElement(this.$refs.web, {
+									offset: data.offset
+								})
+						} else if (href.includes('://'))
+							plus.runtime.openWeb(href);
+						else
+							uni.navigateTo({
+								url: href
+							})
+					}
+				} else if (data.action == 'error')
+					this.$emit('error', {
+						source: data.source,
+						target: data.target
+					})
+				else if (data.action == 'ready') {
+					this.height = data.height;
+					this.$nextTick(() => {
+						uni.createSelectorQuery().in(this).select('#top').boundingClientRect().exec(res => {
+							this.rect = res[0];
+							this.$emit('ready', res[0]);
+						})
+					})
+				}
+			},
+			// #endif
+			// #ifndef APP-PLUS-NVUE
+			// #ifndef H5
+			_load(e) {
+				if (this._wait.length)
+					this.$set(this.imgs, e.target.id, this._wait.shift());
+			},
+			// #endif
+			_tap(e) {
+				// #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
+				if (this.gestureZoom && e.timeStamp - this._lastT < 300) {
+					var initY = e.touches[0].pageY - e.currentTarget.offsetTop;
+					if (this._zoom) {
+						this._scaleAm.translateX(0).scale(1).step();
+						uni.pageScrollTo({
+							scrollTop: (initY + this._initY) / 2 - e.touches[0].clientY,
+							duration: 400
+						})
+					} else {
+						var initX = e.touches[0].pageX - e.currentTarget.offsetLeft;
+						this._initY = initY;
+						this._scaleAm = uni.createAnimation({
+							transformOrigin: `${initX}px ${this._initY}px 0`,
+							timingFunction: 'ease-in-out'
+						});
+						// #ifdef MP-TOUTIAO
+						this._scaleAm.opacity(1);
+						// #endif
+						this._scaleAm.scale(2).step();
+						this._tMax = initX / 2;
+						this._tMin = (initX - this.width) / 2;
+						this._tX = 0;
+					}
+					this._zoom = !this._zoom;
+					this.scaleAm = this._scaleAm.export();
+				}
+				this._lastT = e.timeStamp;
+				// #endif
+			},
+			_touchstart(e) {
+				// #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
+				if (e.touches.length == 1)
+					this._initX = this._lastX = e.touches[0].pageX;
+				// #endif
+			},
+			_touchmove(e) {
+				// #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
+				var diff = e.touches[0].pageX - this._lastX;
+				if (this._zoom && e.touches.length == 1 && Math.abs(diff) > 20) {
+					this._lastX = e.touches[0].pageX;
+					if ((this._tX <= this._tMin && diff < 0) || (this._tX >= this._tMax && diff > 0))
+						return;
+					this._tX += (diff * Math.abs(this._lastX - this._initX) * 0.05);
+					if (this._tX < this._tMin) this._tX = this._tMin;
+					if (this._tX > this._tMax) this._tX = this._tMax;
+					this._scaleAm.translateX(this._tX).step();
+					this.scaleAm = this._scaleAm.export();
+				}
+				// #endif
+			}
+			// #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>

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

@@ -0,0 +1,102 @@
+/*
+  解析和匹配 Css 的选择器
+  github:https://github.com/jin-yufeng/Parser
+  docs:https://jin-yufeng.github.io/Parser
+  author:JinYufeng
+  update:2020/03/15
+*/
+var cfg = require('./config.js');
+class CssHandler {
+	constructor(tagStyle) {
+		var styles = Object.assign({}, cfg.userAgentStyles);
+		for (var item in tagStyle)
+			styles[item] = (styles[item] ? styles[item] + ';' : '') + tagStyle[item];
+		this.styles = styles;
+	}
+	getStyle = data => this.styles = new CssParser(data, this.styles).parse();
+	match(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;
+class CssParser {
+	constructor(data, init) {
+		this.data = data;
+		this.floor = 0;
+		this.i = 0;
+		this.list = [];
+		this.res = init;
+		this.state = this.Space;
+	}
+	parse() {
+		for (var c; c = this.data[this.i]; this.i++)
+			this.state(c);
+		return this.res;
+	}
+	section = () => this.data.substring(this.start, this.i);
+	isLetter = c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+	// 状态机
+	Space(c) {
+		if (c == '.' || c == '#' || this.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;
+	}
+	Comment() {
+		this.i = this.data.indexOf('*/', this.i) + 1;
+		if (!this.i) this.i = this.data.length;
+		this.state = this.Space;
+	}
+	Ignore(c) {
+		if (c == '{') this.floor++;
+		else if (c == '}' && !--this.floor) this.state = this.Space;
+	}
+	Name(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 (!this.isLetter(c) && (c < '0' || c > '9') && c != '-' && c != '_')
+			this.state = this.Ignore;
+	}
+	NameSpace(c) {
+		if (c == '{') this.Content();
+		else if (c == ',') this.Comma();
+		else if (!cfg.blankChar[c]) this.state = this.Ignore;
+	}
+	Comma() {
+		while (cfg.blankChar[this.data[++this.i]]);
+		if (this.data[this.i] == '{') this.Content();
+		else {
+			this.start = this.i--;
+			this.state = this.Name;
+		}
+	}
+	Content() {
+		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;
+	}
+}

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

@@ -0,0 +1,577 @@
+/*
+  将 html 解析为适用于小程序 rich-text 的 DOM 结构
+  github:https://github.com/jin-yufeng/Parser
+  docs:https://jin-yufeng.github.io/Parser
+  author:JinYufeng
+  update:2020/04/13
+*/
+var cfg = require('./config.js'),
+	blankChar = cfg.blankChar,
+	CssHandler = require('./CssHandler.js'),
+	{
+		screenWidth,
+		system
+	} = wx.getSystemInfoSync();
+// #ifdef MP-BAIDU || MP-ALIPAY || MP-TOUTIAO
+var entities = {
+	lt: '<',
+	gt: '>',
+	amp: '&',
+	quot: '"',
+	apos: "'",
+	nbsp: '\xA0',
+	ensp: '\u2002',
+	emsp: '\u2003',
+	ndash: '–',
+	mdash: '—',
+	middot: '·',
+	lsquo: '‘',
+	rsquo: '’',
+	ldquo: '“',
+	rdquo: '”',
+	bull: '•',
+	hellip: '…',
+	permil: '‰',
+	copy: '©',
+	reg: '®',
+	trade: '™',
+	times: '×',
+	divide: '÷',
+	cent: '¢',
+	pound: '£',
+	yen: '¥',
+	euro: '€',
+	sect: '§'
+};
+// #endif
+var emoji; // emoji 补丁包 https://jin-yufeng.github.io/Parser/#/instructions?id=emoji
+class MpHtmlParser {
+	constructor(data, options = {}) {
+		this.attrs = {};
+		this.compress = options.compress;
+		this.CssHandler = new CssHandler(options.tagStyle, screenWidth);
+		this.data = data;
+		this.domain = options.domain;
+		this.DOM = [];
+		this.i = this.start = this.audioNum = this.imgNum = this.videoNum = 0;
+		this.protocol = this.domain && this.domain.includes('://') ? this.domain.split('://')[0] : '';
+		this.state = this.Text;
+		this.STACK = [];
+		this.useAnchor = options.useAnchor;
+		this.xml = options.xml;
+	}
+	parse() {
+		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());
+		// #ifdef MP-BAIDU || MP-TOUTIAO
+		// 将顶层标签的一些样式提取出来给 rich-text
+		(function f(ns) {
+			for (var i = ns.length, n; n = ns[--i];) {
+				if (n.type == 'text') continue;
+				if (!n.c) {
+					var style = n.attrs.style;
+					if (style) {
+						var j, k, res;
+						if ((j = style.indexOf('display')) != -1)
+							res = style.substring(j, (k = style.indexOf(';', j)) == -1 ? style.length : k);
+						if ((j = style.indexOf('float')) != -1)
+							res += ';' + style.substring(j, (k = style.indexOf(';', j)) == -1 ? style.length : k);
+						n.attrs.contain = res;
+					}
+				} else f(n.children);
+			}
+		})(this.DOM);
+		// #endif
+		if (this.DOM.length) {
+			this.DOM[0].PoweredBy = 'Parser';
+			if (this.title) this.DOM[0].title = this.title;
+		}
+		return this.DOM;
+	}
+	// 设置属性
+	setAttr() {
+		var name = this.getName(this.attrName);
+		if (cfg.trustAttrs[name]) {
+			if (!this.attrVal) {
+				if (cfg.boolAttrs[name]) this.attrs[name] = 'T';
+			} else if (name == 'src') this.attrs[name] = this.getUrl(this.attrVal.replace(/&amp;/g, '&'));
+			else this.attrs[name] = this.attrVal;
+		}
+		this.attrVal = '';
+		while (blankChar[this.data[this.i]]) this.i++;
+		if (this.isClose()) this.setNode();
+		else {
+			this.start = this.i;
+			this.state = this.AttrName;
+		}
+	}
+	// 设置文本节点
+	setText() {
+		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 tmp = [];
+			for (let i = text.length, c; c = text[--i];)
+				if (!blankChar[c] || (!blankChar[tmp[0]] && (c = ' '))) tmp.unshift(c);
+			text = tmp.join('');
+			if (text == ' ') return;
+		}
+		// 处理实体
+		var siblings = this.siblings(),
+			i = -1,
+			j, en;
+		while (1) {
+			if ((i = text.indexOf('&', i + 1)) == -1) break;
+			if ((j = text.indexOf(';', i + 2)) == -1) break;
+			if (text[i + 1] == '#') {
+				en = parseInt((text[i + 2] == 'x' ? '0' : '') + text.substring(i + 2, j));
+				if (!isNaN(en)) text = text.substr(0, i) + String.fromCharCode(en) + text.substring(j + 1);
+			} else {
+				en = text.substring(i + 1, j);
+				// #ifdef MP-WEIXIN || MP-QQ || APP-PLUS
+				if (en == 'nbsp') text = text.substr(0, i) + '\xA0' + text.substr(j + 1); // 解决 &nbsp; 失效
+				else if (en != 'lt' && en != 'gt' && en != 'amp' && en != 'ensp' && en != 'emsp' && en != 'quot' && en != 'apos') {
+					i && siblings.push({
+						type: 'text',
+						text: text.substr(0, i)
+					})
+					siblings.push({
+						type: 'text',
+						text: `&${en};`,
+						en: 1
+					})
+					text = text.substr(j + 1);
+					i = -1;
+				}
+				// #endif
+				// #ifdef MP-BAIDU || MP-ALIPAY || MP-TOUTIAO
+				if (entities[en]) text = text.substr(0, i) + entities[en] + text.substr(j + 1);
+				// #endif
+			}
+		}
+		text && siblings.push({
+			type: 'text',
+			text
+		})
+	}
+	// 设置元素节点
+	setNode() {
+		var node = {
+				name: this.tagName.toLowerCase(),
+				attrs: this.attrs
+			},
+			close = cfg.selfClosingTags[node.name] || (this.xml && this.data[this.i] == '/');
+		this.attrs = {};
+		if (!cfg.ignoreTags[node.name]) {
+			this.matchAttr(node);
+			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.STACK[this.STACK.length - 1],
+					attrs = node.attrs;
+				if (parent && attrs.src)
+					if (parent.name == 'video' || parent.name == 'audio')
+						parent.attrs.source.push(attrs.src);
+					else {
+						var i, media = attrs.media;
+						if (parent.name == 'picture' && !parent.attrs.src && !(attrs.src.indexOf('.webp') && system.includes('iOS')) &&
+							(!media || (media.includes('px') &&
+								(((i = media.indexOf('min-width')) != -1 && (i = media.indexOf(':', i + 8)) != -1 && screenWidth > parseInt(
+										media.substr(i + 1))) ||
+									((i = media.indexOf('max-width')) != -1 && (i = media.indexOf(':', i + 8)) != -1 && screenWidth < parseInt(
+										media.substr(i + 1)))))))
+							parent.attrs.src = 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;
+	}
+	// 移除标签
+	remove(node) {
+		var name = node.name,
+			j = 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.getName(this.section()) == 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.title = this.data.substring(j + 1, this.i - 7);
+				if ((this.i = this.data.indexOf('>', this.i)) == -1) this.i = this.data.length;
+				// 处理 svg
+				if (name == 'svg') {
+					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) + src;
+					var parent = this.STACK[this.STACK.length - 1];
+					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'),
+							ignore: 'T'
+						}
+					})
+				}
+				return;
+			}
+		}
+	}
+	// 处理属性
+	matchAttr(node) {
+		var attrs = node.attrs,
+			style = this.CssHandler.match(node.name, attrs, node) + (attrs.style || ''),
+			styleObj = {};
+		if (attrs.id) {
+			if (this.compress & 1) attrs.id = void 0;
+			else if (this.useAnchor) this.bubble();
+		}
+		if ((this.compress & 2) && attrs.class) attrs.class = void 0;
+		switch (node.name) {
+			case 'img':
+				if (attrs['data-src']) {
+					attrs.src = attrs.src || attrs['data-src'];
+					attrs['data-src'] = void 0;
+				}
+				if (attrs.src && !attrs.ignore) {
+					if (this.bubble()) attrs.i = (this.imgNum++).toString();
+					else attrs.ignore = 'T';
+				}
+				break;
+			case 'a':
+			case 'ad':
+			// #ifdef APP-PLUS
+			case 'iframe':
+			case 'embed':
+			// #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 'video':
+			case 'audio':
+				if (!attrs.id) attrs.id = node.name + (++this[`${node.name}Num`]);
+				else this[`${node.name}Num`]++;
+				if (node.name == 'video') {
+					if (attrs.width) {
+						style = `width:${parseFloat(attrs.width) + (attrs.width.includes('%') ? '%' : 'px')};${style}`;
+						attrs.width = void 0;
+					}
+					if (attrs.height) {
+						style = `height:${parseFloat(attrs.height) + (attrs.height.includes('%') ? '%' : 'px')};${style}`;
+						attrs.height = void 0;
+					}
+					if (this.videoNum > 3) node.lazyLoad = true;
+				}
+				attrs.source = [];
+				if (attrs.src) attrs.source.push(attrs.src);
+				if (!attrs.controls && !attrs.autoplay)
+					console.warn(`存在没有 controls 属性的 ${node.name} 标签,可能导致无法播放`, node);
+				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.replace(/&quot;/g, '"').replace(/&amp;/g, '&').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.includes('-webkit') || value.includes('-moz') || value.includes('-ms') || value.includes('-o') || value
+				.includes(
+					'safe'))
+				style += `;${key}:${value}`;
+			else if (!styleObj[key] || value.includes('import') || !styleObj[key].includes('import'))
+				styleObj[key] = value;
+		}
+		if (node.name == 'img' && parseInt(styleObj.width || attrs.width) > screenWidth)
+			styleObj.height = 'auto';
+		for (var key in styleObj) {
+			var value = styleObj[key];
+			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($) * screenWidth / 750 + 'px');
+			else if (key == 'white-space' && value.includes('pre'))
+				this.pre = node.pre = true;
+			style += `;${key}:${value}`;
+		}
+		style = style.substr(1);
+		if (style) attrs.style = style;
+	}
+	// 节点出栈处理
+	popNode(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;
+		}
+		if (node.name == 'head' || (cfg.filter && cfg.filter(node, this) == false))
+			return this.siblings().pop();
+		var attrs = node.attrs;
+		// 替换一些标签名
+		if (node.name == 'picture') {
+			node.name = 'img';
+			if (!attrs.src && (node.children[0] || '').name == 'img')
+				attrs.src = node.children[0].attrs.src;
+			if (attrs.src && !attrs.ignore)
+				attrs.i = (this.imgNum++).toString();
+			return node.children = void 0;
+		}
+		if (cfg.blockTags[node.name]) node.name = 'div';
+		else if (!cfg.trustTags[node.name]) node.name = 'span';
+		// 处理列表
+		if (node.c) {
+			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 = node.children.length; i--;)
+						node.children[i].floor = floor;
+			} else if (node.name == 'ol') {
+				for (let i = 0, num = 1, child; child = node.children[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();
+				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)
+				(function f(ns) {
+					for (var i = 0, n; n = ns[i]; i++) {
+						if (n.name == 'th' || n.name == 'td') {
+							if (border) n.attrs.style = `border:${border}px solid gray;${n.attrs.style}`;
+							if (padding) n.attrs.style = `padding:${padding}px;${n.attrs.style}`;
+						} else f(n.children || []);
+					}
+				})(node.children)
+		}
+		this.CssHandler.pop && this.CssHandler.pop(node);
+		// 自动压缩
+		if (node.name == 'div' && !Object.keys(attrs).length) {
+			var siblings = this.siblings();
+			if (node.children.length == 1 && node.children[0].name == 'div')
+				siblings[siblings.length - 1] = node.children[0];
+		}
+	}
+	// 工具函数
+	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;
+	}
+	getName = val => this.xml ? val : val.toLowerCase();
+	getUrl(url) {
+		if (url[0] == '/') {
+			if (url[1] == '/') url = this.protocol + ':' + 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;
+	}
+	isClose = () => this.data[this.i] == '>' || (this.data[this.i] == '/' && this.data[this.i + 1] == '>');
+	section = () => this.data.substring(this.start, this.i);
+	siblings = () => this.STACK.length ? this.STACK[this.STACK.length - 1].children : this.DOM;
+	// 状态机
+	Text(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 == '!') {
+				this.setText();
+				this.Comment();
+			}
+		}
+	}
+	Comment() {
+		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;
+	}
+	TagName(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();
+		}
+	}
+	AttrName(c) {
+		var blank = blankChar[c];
+		if (blank) {
+			this.attrName = this.section();
+			c = this.data[this.i];
+		}
+		if (c == '=') {
+			if (!blank) this.attrName = this.section();
+			while (blankChar[this.data[++this.i]]);
+			this.start = this.i--;
+			this.state = this.AttrValue;
+		} else if (blank) this.setAttr();
+		else if (this.isClose()) {
+			this.attrName = this.section();
+			this.setAttr();
+		}
+	}
+	AttrValue(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();
+	}
+	EndTag(c) {
+		if (blankChar[c] || c == '>' || c == '/') {
+			var name = this.getName(this.section());
+			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);
+			} 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 @@
+/* 配置文件 */
+// #ifdef MP-WEIXIN
+const canIUse = wx.canIUse('editor'); // 高基础库标识,用于兼容
+// #endif
+module.exports = {
+	// 过滤器函数
+	filter: null,
+	// 代码高亮函数
+	highlight: null,
+	// 文本处理函数
+	onText: null,
+	blankChar: makeMap(' ,\xA0,\t,\r,\n,\f'),
+	// 块级标签,将被转为 div
+	blockTags: makeMap('address,article,aside,body,caption,center,cite,footer,header,html,nav,section' + (
+		// #ifdef MP-WEIXIN
+		canIUse ? '' :
+		// #endif
+		',pre')),
+	// 将被移除的标签
+	ignoreTags: makeMap(
+		'area,base,basefont,canvas,command,frame,input,isindex,keygen,link,map,meta,param,script,source,style,svg,textarea,title,track,use,wbr'
+		// #ifdef MP-WEIXIN
+		+ (canIUse ? ',rp' : '')
+		// #endif
+		// #ifndef APP-PLUS
+		+ ',embed,iframe'
+		// #endif
+	),
+	// 只能被 rich-text 显示的标签
+	richOnlyTags: makeMap('a,colgroup,fieldset,legend,picture,table'
+		// #ifdef MP-WEIXIN
+		+ (canIUse ? ',bdi,bdo,caption,rt,ruby' : '')
+		// #endif
+	),
+	// 自闭合的标签
+	selfClosingTags: makeMap(
+		'area,base,basefont,br,col,circle,ellipse,embed,frame,hr,img,input,isindex,keygen,line,link,meta,param,path,polygon,rect,source,track,use,wbr'
+	),
+	// 信任的属性
+	trustAttrs: makeMap(
+		'align,alt,app-id,author,autoplay,border,cellpadding,cellspacing,class,color,colspan,controls,data-src,dir,face,height,href,id,ignore,loop,media,muted,name,path,poster,rowspan,size,span,src,start,style,type,unit-id,width,xmlns'
+	),
+	// bool 型的属性
+	boolAttrs: makeMap('autoplay,controls,ignore,loop,muted'),
+	// 信任的标签
+	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'
+		// #ifdef MP-WEIXIN
+		+ (canIUse ? ',bdi,bdo,caption,pre,rt,ruby' : '')
+		// #endif
+		// #ifdef APP-PLUS
+		+ ',embed,iframe'
+		// #endif
+	),
+	// 默认的标签样式
+	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',
+		img: 'max-width:100%',
+		mark: 'background-color:yellow',
+		picture: 'max-width:100%',
+		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 = {},
+		list = str.split(',');
+	for (var i = list.length; i--;)
+		map[list[i]] = true;
+	return map;
+}

+ 35 - 0
components/jyf-parser/libs/handler.sjs

@@ -0,0 +1,35 @@
+var inlineTags = {
+	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
+}
+export default {
+	// 从顶层标签的样式中取出一些给 rich-text
+	getStyle: function(style) {
+		if (style) {
+			var i, j, res = '';
+			if ((i = style.indexOf('display')) != -1)
+				res = style.substring(i, (j = style.indexOf(';', i)) == -1 ? style.length : j);
+			if ((i = style.indexOf('float')) != -1)
+				res += ';' + style.substring(i, (j = style.indexOf(';', i)) == -1 ? style.length : j);
+			return res;
+		}
+	},
+	getNode: function(item) {
+		return [item];
+	},
+	// 是否通过 rich-text 显示
+	useRichText: function(item) {
+		return !item.c && !inlineTags[item.name] && (item.attrs.style || '').indexOf('display:inline') == -1;
+	}
+}

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

@@ -0,0 +1,44 @@
+var inlineTags = {
+	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
+}
+module.exports = {
+	// 从顶层标签的样式中取出一些给 rich-text
+	getStyle: function(style) {
+		if (style) {
+			var i, j, res = '';
+			if ((i = style.indexOf('display')) != -1)
+				res = style.substring(i, (j = style.indexOf(';', i)) == -1 ? style.length : j);
+			if ((i = style.indexOf('float')) != -1)
+				res += ';' + style.substring(i, (j = style.indexOf(';', i)) == -1 ? style.length : j);
+			return res;
+		}
+	},
+	// 处理懒加载
+	getNode: function(item, imgLoad) {
+		if (!imgLoad && item.attrs.i != '0') {
+			var img = {
+				name: 'img',
+				attrs: JSON.parse(JSON.stringify(item.attrs))
+			}
+			delete img.attrs.src;
+			img.attrs.style += ';width:20px;height:20px';
+			return [img];
+		} else return [item];
+	},
+	// 是否通过 rich-text 显示
+	useRichText: function(item) {
+		return !item.c && !inlineTags[item.name] && (item.attrs.style || '').indexOf('display:inline') == -1;
+	}
+}

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

@@ -0,0 +1,476 @@
+<!--
+  trees 递归显示组件
+  github:https://github.com/jin-yufeng/Parser 
+  docs:https://jin-yufeng.github.io/Parser
+  插件市场:https://ext.dcloud.net.cn/plugin?id=805
+  author:JinYufeng
+  update:2020/04/13
+-->
+<template>
+	<view class="interlayer">
+		<block v-for="(n, index) in nodes" v-bind:key="index">
+			<!--图片-->
+			<!--#ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY || APP-PLUS-->
+			<rich-text v-if="n.name=='img'" :id="n.attrs.id" class="_img" :style="''+handler.getStyle(n.attrs.style)" :nodes="handler.getNode(n,!lazyLoad||imgLoad)"
+			 :data-attrs="n.attrs" @tap="imgtap" @longpress="imglongtap" />
+			<!--#endif-->
+			<!--#ifdef MP-BAIDU || MP-TOUTIAO-->
+			<rich-text v-if="n.name=='img'" :id="n.attrs.id" class="_img" :style="n.attrs.contain" :nodes='[n]' :data-attrs="n.attrs"
+			 @tap="imgtap" @longpress="imglongtap" />
+			<!--#endif-->
+			<!--文本-->
+			<!--#ifdef MP-WEIXIN || MP-QQ || APP-PLUS-->
+			<rich-text v-else-if="n.decode" class="_entity" :nodes="[n]"></rich-text>
+			<!--#endif-->
+			<text v-else-if="n.type=='text'" decode>{{n.text}}</text>
+			<text v-else-if="n.name=='br'">\n</text>
+			<!--视频-->
+			<view v-else-if="n.name=='video'">
+				<view v-if="(!loadVideo||n.lazyLoad)&&!(controls[n.attrs.id]&&controls[n.attrs.id].play)" :id="n.attrs.id" :class="'_video '+(n.attrs.class||'')"
+				 :style="n.attrs.style" @tap="_loadVideo" />
+				<video v-else :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :autoplay="n.attrs.autoplay||(controls[n.attrs.id]&&controls[n.attrs.id].play)"
+				 :controls="n.attrs.controls" :loop="n.attrs.loop" :muted="n.attrs.muted" :poster="n.attrs.poster" :src="n.attrs.source[(controls[n.attrs.id]&&controls[n.attrs.id].index)||0]"
+				 :unit-id="n.attrs['unit-id']" :data-id="n.attrs.id" data-from="video" data-source="source" @error="error" @play="play" />
+			</view>
+			<!--音频-->
+			<audio v-else-if="n.name=='audio'" :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[(controls[n.attrs.id]&&controls[n.attrs.id].index)||0]"
+			 :data-id="n.attrs.id" data-from="audio" data-source="source" @error="error" @play="play" />
+			<!--链接-->
+			<view v-else-if="n.name=='a'" :class="'_a '+(n.attrs.class||'')" hover-class="_hover" :style="n.attrs.style"
+			 :data-attrs="n.attrs" @tap="linkpress">
+				<trees class="_span" :nodes="n.children" />
+			</view>
+			<!--广告(按需打开注释)-->
+			<!--#ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO-->
+			<!--<ad v-else-if="n.name=='ad'" :class="n.attrs.class" :style="n.attrs.style" :unit-id="n.attrs['unit-id']"
+			 data-from="ad" @error="error" />-->
+			<!--#endif-->
+			<!--#ifdef MP-BAIDU-->
+			<!--<ad v-else-if="n.name=='ad'" :class="n.attrs.class" :style="n.attrs.style" :appid="n.attrs.appid"
+			 :apid="n.attrs.apid" :type="n.attrs.type" data-from="ad" @error="error" />-->
+			<!--#endif-->
+			<!--#ifdef APP-PLUS-->
+			<!--<ad v-else-if="n.name=='ad'" :class="n.attrs.class" :style="n.attrs.style" :adpid="n.attrs.adpid"
+			 data-from="ad" @error="error" />-->
+			<!--#endif-->
+			<!--列表-->
+			<view v-else-if="n.name=='li'" :id="n.attrs.id" :class="n.attrs.class" :style="(n.attrs.style||'')+';display:flex'">
+				<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>
+				<!--#ifdef MP-ALIPAY-->
+				<view class="_li">
+					<trees :nodes="n.children" />
+				</view>
+				<!--#endif-->
+				<!--#ifndef MP-ALIPAY-->
+				<trees class="_li" :nodes="n.children" :lazyLoad="lazyLoad" :loadVideo="loadVideo" />
+				<!--#endif-->
+			</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, i) in n.children" v-bind:key="i" :class="tbody.attrs.class" :style="(tbody.attrs.style||'')+(tbody.name[0]=='t'?';display:table-'+(tbody.name=='tr'?'row':'row-group'):'')">
+					<view v-for="(tr, j) in tbody.children" v-bind:key="j" :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" :lazyLoad="lazyLoad" :loadVideo="loadVideo" />
+						<block v-else>
+							<!--#ifdef MP-ALIPAY-->
+							<view v-for="(td, k) in tr.children" v-bind:key="k" :class="td.attrs.class" :style="(td.attrs.style||'')+(td.name[0]=='t'?';display:table-'+(td.name=='tr'?'row':'cell'):'')">
+								<trees :nodes="td.children" />
+							</view>
+							<!--#endif-->
+							<!--#ifndef MP-ALIPAY-->
+							<trees v-for="(td, k) in tr.children" v-bind:key="k" :class="td.attrs.class" :style="(td.attrs.style||'')+(td.name[0]=='t'?';display:table-'+(td.name=='tr'?'row':'cell'):'')"
+							 :nodes="td.children" :lazyLoad="lazyLoad" :loadVideo="loadVideo" />
+							<!--#endif-->
+						</block>
+					</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 || MP-ALIPAY || APP-PLUS-->
+			<rich-text v-else-if="handler.useRichText(n)" :id="n.attrs.id" :class="'_p __'+n.name" :nodes="[n]" />
+			<!--#endif-->
+			<!--#ifdef MP-BAIDU || MP-TOUTIAO-->
+			<rich-text v-else-if="!(n.c||n.continue)" :id="n.attrs.id" :class="_p" :style="n.attrs.contain" :nodes="[n]" />
+			<!--#endif-->
+			<!--#ifdef MP-ALIPAY-->
+			<view v-else :id="n.attrs.id" :class="'_'+n.name+' '+(n.attrs.class||'')" :style="n.attrs.style">
+				<trees :nodes="n.children" />
+			</view>
+			<!--#endif-->
+			<!--#ifndef MP-ALIPAY-->
+			<trees v-else :class="(n.attrs.id||'')+' _'+n.name+' '+(n.attrs.class||'')" :style="n.attrs.style" :nodes="n.children"
+			 :lazyLoad="lazyLoad" :loadVideo="loadVideo" />
+			<!--#endif-->
+		</block>
+	</view>
+</template>
+<script module="handler" lang="wxs" src="./handler.wxs"></script>
+<script module="handler" lang="sjs" src="./handler.sjs"></script>
+<script>
+	global.Parser = {};
+	import trees from './trees'
+	export default {
+		components: {
+			trees
+		},
+		name: 'trees',
+		data() {
+			return {
+				controls: {},
+				// #ifdef MP-WEIXIN || MP-QQ || APP-PLUS
+				imgLoad: false,
+				// #endif
+				// #ifndef APP-PLUS
+				loadVideo: true
+				// #endif
+			}
+		},
+		props: {
+			nodes: Array,
+			// #ifdef MP-WEIXIN || MP-QQ || H5 || APP-PLUS
+			lazyLoad: Boolean,
+			// #endif
+			// #ifdef APP-PLUS
+			loadVideo: Boolean
+			// #endif
+		},
+		mounted() {
+			// 获取顶层组件
+			this.top = this.$parent;
+			while (this.top.$options.name != 'parser') {
+				if (this.top.top) {
+					this.top = this.top.top;
+					break;
+				}
+				this.top = this.top.$parent;
+			}
+		},
+		// #ifdef MP-WEIXIN || MP-QQ || APP-PLUS
+		beforeDestroy() {
+			if (this.observer)
+				this.observer.disconnect();
+		},
+		// #endif
+		methods: {
+			// #ifndef MP-ALIPAY
+			play(e) {
+				if (this.top.videoContexts.length > 1 && this.top.autopause)
+					for (var i = this.top.videoContexts.length; i--;)
+						if (this.top.videoContexts[i].id != e.currentTarget.dataset.id)
+							this.top.videoContexts[i].pause();
+			},
+			// #endif
+			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
+						})
+					}
+				}
+			},
+			imglongtap(e) {
+				var attrs = e.item.dataset.attrs;
+				if (!attrs.ignore)
+					this.top.$emit('imglongtap', {
+						id: e.target.id,
+						src: attrs.src
+					})
+			},
+			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
+							})
+					}
+				}
+			},
+			error(e) {
+				var context, target = e.currentTarget,
+					source = target.dataset.from;
+				if (source == 'video' || source == 'audio') {
+					// 加载其他 source
+					var index = this.controls[target.id] ? this.controls[target.id].index + 1 : 1;
+					if (index < target.dataset.source.length)
+						this.$set(this.controls, target.id + '.index', index);
+					if (source == 'video') context = uni.createVideoContext(target.id, this);
+				}
+				this.top && this.top.$emit('error', {
+					source,
+					target,
+					errMsg: e.detail.errMsg,
+					errCode: e.detail.errCode,
+					context
+				});
+			},
+			_loadVideo(e) {
+				this.$set(this.controls, e.currentTarget.id, {
+					play: true,
+					index: 0
+				})
+			}
+		}
+	}
+</script>
+
+<style>
+	/* 在这里引入自定义样式 */
+
+	/* 链接和图片效果 */
+	._a {
+		display: inline;
+		color: #366092;
+		word-break: break-all;
+		padding: 1.5px 0 1.5px 0;
+	}
+
+	._hover {
+		opacity: 0.7;
+		text-decoration: underline;
+	}
+
+	._img {
+		display: inline-block;
+		text-indent: 0;
+	}
+
+	/* #ifdef MP-WEIXIN */
+	:host {
+		display: inline;
+	}
+
+	/* #endif */
+
+	/* #ifdef MP */
+	.interlayer {
+		align-content: inherit;
+		align-items: inherit;
+		display: inherit;
+		flex-direction: inherit;
+		flex-wrap: inherit;
+		justify-content: inherit;
+		width: 100%;
+		white-space: inherit;
+	}
+
+	/* #endif */
+
+	._b,
+	._strong {
+		font-weight: bold;
+	}
+
+	._blockquote,
+	._div,
+	._p,
+	._ol,
+	._ul,
+	._li {
+		display: block;
+	}
+
+	._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;
+	}
+
+	._ins {
+		text-decoration: underline;
+	}
+
+	._li {
+		flex: 1;
+		width: 0;
+	}
+
+	._ol-bef {
+		margin-right: 5px;
+		text-align: right;
+		width: 36px;
+	}
+
+	._ul-bef {
+		line-height: normal;
+		margin: 0 12px 0 23px;
+	}
+
+	._ol-bef,
+	._ul_bef {
+		flex: none;
+		user-select: none;
+	}
+
+	._ul-p1 {
+		display: inline-block;
+		height: 0.3em;
+		line-height: 0.3em;
+		overflow: hidden;
+		width: 0.3em;
+	}
+
+	._ul-p2 {
+		border: 0.05em solid black;
+		border-radius: 50%;
+		display: inline-block;
+		height: 0.23em;
+		width: 0.23em;
+	}
+
+	._q::before {
+		content: '"';
+	}
+
+	._q::after {
+		content: '"';
+	}
+
+	._sub {
+		font-size: smaller;
+		vertical-align: sub;
+	}
+
+	._sup {
+		font-size: smaller;
+		vertical-align: super;
+	}
+
+	/* #ifndef MP-WEIXIN */
+	._abbr,
+	._b,
+	._code,
+	._del,
+	._em,
+	._i,
+	._ins,
+	._label,
+	._q,
+	._span,
+	._strong,
+	._sub,
+	._sup {
+		display: inline;
+	}
+
+	/* #endif */
+
+	/* #ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY */
+	.__bdo,
+	.__bdi,
+	.__ruby,
+	.__rt,
+	._entity {
+		display: inline-block;
+	}
+
+	/* #endif */
+	._video {
+		background-color: black;
+		display: inline-block;
+		height: 225px;
+		position: relative;
+		width: 300px;
+	}
+
+	._video::after {
+		border-color: transparent transparent transparent white;
+		border-style: solid;
+		border-width: 15px 0 15px 30px;
+		content: '';
+		left: 50%;
+		margin: -15px 0 0 -15px;
+		position: absolute;
+		top: 50%;
+	}
+</style>

+ 272 - 0
components/login_mobile/index.vue

@@ -0,0 +1,272 @@
+<template>
+	<view v-if="isUp">
+		<view class="mobile-bg" v-if="isShow" @click="close"></view>
+		<view class="mobile-mask animated" :class="{slideInUp:isUp}" :style="{position:isPos?'fixed':'static'}">
+			<view class="input-item">
+				<input type="text" v-model="account" placeholder="输入手机号" />
+			</view>
+			<view class="input-item">
+				<input type="text" v-model="codeNum" placeholder="输入验证码" />
+				<button class="code" :disabled="disabled" @click="code">{{text}}</button>
+			</view>
+			<view class="sub_btn" @click="loginBtn">{{(!userInfo.phone && isLogin) || (userInfo.phone && isLogin)?'立即绑定':'立即登录'}}</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	const app = getApp();
+	import sendVerifyCode from "@/mixins/SendVerifyCode";
+	import {mapGetters} from "vuex";
+  import * as AuthApi from "@/api/member/auth";
+  import * as UserApi from "@/api/member/user";
+  import * as BrokerageAPI from '@/api/trade/brokerage.js'
+	import { iosBinding } from '@/api/public';
+	const BACK_URL = "login_back_url";
+	export default {
+		name: 'login_mobile',
+		computed: mapGetters(['userInfo','isLogin']),
+		props: {
+			isUp: {
+				type: Boolean,
+				default: false,
+			},
+			authKey: {
+				type: String,
+				default: '',
+			},
+      socialCode: {
+        type: String,
+        default: '',
+      },
+      socialState: {
+        type: String,
+        default: '',
+      },
+			isShow: {
+				type: Boolean,
+				default: true
+			},
+			isPos: {
+				type: Boolean,
+				default: true
+			},
+			appleShow: {
+				type: String,
+				default: ''
+			},
+			platform: {
+				type: String,
+				default: '',
+			}
+		},
+		data() {
+			return {
+				account: '',
+				codeNum: '',
+				isApp: 0
+			}
+		},
+		mixins: [sendVerifyCode],
+		methods: {
+			// 获取验证码
+			async code() {
+				if (!this.account) {
+          return this.$util.Tips({
+            title: '请填写手机号码'
+          });
+        }
+				if (!/^1(3|4|5|7|8|9|6)\d{9}$/i.test(this.account)) {
+          return this.$util.Tips({
+            title: '请输入正确的手机号码'
+          });
+        }
+        await AuthApi.sendSmsCode(this.account, 1)
+          .then(res => {
+            this.$util.Tips({title:res.message});
+            this.sendCode();
+          })
+          .catch(err => {
+            return this.$util.Tips({
+              title: err
+            });
+          });
+			},
+			close() {
+				this.$emit('close', false)
+			},
+			// 登录
+			loginBtn() {
+				if (!this.account) {
+          return this.$util.Tips({
+            title: '请填写手机号码'
+          });
+        }
+				if (!/^1(3|4|5|7|8|9|6)\d{9}$/i.test(this.account)) {
+          return this.$util.Tips({
+            title: '请输入正确的手机号码'
+          });
+        }
+				if (!this.codeNum) return this.$util.Tips({
+					title: '请填写验证码'
+				});
+				if (!/^[\w\d]+$/i.test(this.codeNum)) {
+          return this.$util.Tips({
+            title: '请输入正确的验证码'
+          });
+        }
+				uni.showLoading({
+					title: !this.userInfo.phone && this.isLogin?'正在绑定中':'正在登录中'
+				});
+				if (!this.userInfo.phone && this.isLogin) {
+          // TODO 芋艿:不晓得它要搞啥哈???
+					iosBinding({
+						captcha: this.codeNum,
+						phone: this.account
+					}).then(res => {
+						this.$util.Tips({
+							title: '绑定手机号成功',
+							icon: 'success'
+						}, {
+							tab: 3
+						})
+            this.isApp = 1;
+            this.getUserInfo();
+					}).catch(error => {
+						uni.hideLoading()
+            this.$util.Tips({
+							title: error
+						})
+					})
+				} else {
+          AuthApi.smsLogin({
+            mobile: this.account,
+            code: this.codeNum,
+            socialType: 31,
+            socialCode: this.socialCode,
+            socialState: this.socialState
+					}).then(res => {
+            // TODO 芋艿:refreshToken 机制
+            let data = res.data;
+            this.$store.commit("LOGIN", {
+              'token': res.data.accessToken
+            });
+            this.getUserInfo(data);
+            this.bindBrokerUser();
+					}).catch(error => {
+						uni.hideLoading()
+						this.$util.Tips({
+							title: error
+						})
+					})
+				}
+			},
+			/**
+			 * 获取个人用户信息
+			 */
+			getUserInfo: function(data) {
+        this.$store.commit("SETUID", data.userId);
+        this.$store.commit("OPENID", data.openid);
+        UserApi.getUserInfo().then(res => {
+					uni.hideLoading();
+          this.$store.commit("UPDATE_USERINFO", res.data);
+          // 调回登录前页面
+          // #ifdef MP
+					this.$util.Tips({
+						title: '登录成功',
+						icon: 'success'
+					}, {
+						tab: 3
+					})
+					this.close()
+					// #endif
+					// #ifdef H5
+					this.$emit('wechatPhone', true)
+					// #endif
+				});
+			},
+      bindBrokerUser() {
+        const spread = this.$Cache.get("spread");
+        if (spread > 0) {
+          BrokerageAPI.bindBrokerageUser(spread)
+        }
+      }
+		}
+	}
+</script>
+
+<style lang="stylus" scoped>
+	.mobile-bg {
+		position: fixed;
+		left: 0;
+		top: 0;
+		width: 100%;
+		height: 100%;
+		background: rgba(0, 0, 0, 0.5);
+	}
+
+	.isPos {
+		position: static;
+	}
+
+	.mobile-mask {
+		z-index: 20;
+		// position: fixed;
+		left: 0;
+		bottom: 0;
+		width: 100%;
+		padding: 67rpx 30rpx;
+		background: #fff;
+
+		.input-item {
+			display: flex;
+			justify-content: space-between;
+			width: 100%;
+			height: 86rpx;
+			margin-bottom: 38rpx;
+
+			input {
+				flex: 1;
+				display: block;
+				height: 100%;
+				padding-left: 40rpx;
+				border-radius: 43rpx;
+				border: 1px solid #DCDCDC;
+			}
+
+			.code {
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				width: 220rpx;
+				height: 86rpx;
+				margin-left: 30rpx;
+				background: rgba(233, 51, 35, 0.05);
+				font-size: 28rpx;
+				color: $theme-color;
+				border-radius: 43rpx;
+
+				&[disabled] {
+					background: rgba(0, 0, 0, 0.05);
+					color: #999;
+				}
+			}
+		}
+
+		.sub_btn {
+			width: 690rpx;
+			height: 86rpx;
+			line-height: 86rpx;
+			margin-top: 60rpx;
+			background: #E93323;
+			border-radius: 43rpx;
+			color: #fff;
+			font-size: 28rpx;
+			text-align: center;
+		}
+	}
+
+	.animated {
+		animation-duration: .4s
+	}
+</style>

+ 160 - 0
components/login_mobile/routine_phone.vue

@@ -0,0 +1,160 @@
+<template>
+	<view v-if="isPhoneBox">
+		<view class="mobile-bg" @click="close"></view>
+		<view class="mobile-mask animated" :class="{slideInUp:isUp}">
+			<view class="info-box">
+				<image :src="logoUrl"></image>
+				<view class="title">获取授权</view>
+				<view class="txt">获取微信的手机号授权</view>
+			</view>
+			<button class="sub_btn" open-type="getPhoneNumber" @getphonenumber="getphonenumber">获取微信手机号</button>
+		</view>
+	</view>
+</template>
+<script>
+	const app = getApp();
+	import Routine from '@/libs/routine';
+	import {
+		loginMobile,
+		registerVerify,
+		getCodeApi,
+		getUserInfo
+	} from "@/api/user";
+	import { getLogo, getUserPhone } from '@/api/public';
+	export default{
+		name:'routine_phone',
+		props:{
+			isPhoneBox:{
+				type:Boolean,
+				default:false,
+			},
+			logoUrl:{
+				type:String,
+				default:'',
+			},
+			authKey:{
+				type:String,
+				default:'',
+			}
+		},
+		data(){
+			return {
+				keyCode:'',
+				account:'',
+				codeNum:'',
+				isStatus:false
+			}
+		},
+		mounted() {
+		},
+		methods:{
+			// #ifdef MP
+			// 小程序获取手机号码
+			getphonenumber(e){
+				uni.showLoading({ title: '加载中' });
+				Routine.getCode()
+					.then(code => {
+						this.getUserPhoneNumber(e.detail.encryptedData, e.detail.iv, code);
+					})
+					.catch(error => {
+						uni.hideLoading();
+					});
+			},
+			// 小程序获取手机号码回调
+			getUserPhoneNumber(encryptedData, iv, code) {
+				getUserPhone({
+					encryptedData: encryptedData,
+					iv: iv,
+					code: code,
+					key:this.authKey,
+					type: 'routine'
+				})
+					.then(res => {
+						this.$store.commit('LOGIN', {
+							token: res.data.token
+						});
+						this.$store.commit("SETUID", res.data.uid);
+						this.getUserInfo();
+					})
+					.catch(res => {
+						uni.hideLoading();
+						this.$util.Tips({
+							title: res
+						});
+					});
+			},
+			/**
+			 * 获取个人用户信息
+			 */
+			getUserInfo: function() {
+				let that = this;
+				getUserInfo().then(res => {
+					uni.hideLoading();
+					that.userInfo = res.data
+					that.$store.commit("UPDATE_USERINFO", res.data);
+					that.isStatus = true
+					this.close()
+				});
+			},
+			// #endif
+			close(){
+				this.$emit('close',{isStatus:this.isStatus})
+			}
+		}
+	}
+	
+</script>
+
+<style lang="scss">
+	.mobile-bg{
+		position: fixed;
+		left: 0;
+		top: 0;
+		width: 100%;
+		height: 100%;
+		background: rgba(0,0,0,0.5);
+	}
+	.mobile-mask {
+		z-index: 20;
+		position: fixed;
+		left: 0;
+		bottom: 0;
+		width: 100%;
+		padding: 67rpx 30rpx;
+		background: #fff;
+		.info-box{
+			display:flex;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+			image{
+				width: 150rpx;
+				height: 150rpx;
+				border-radius: 10rpx;
+			}
+			.title{
+				margin-top: 30rpx;
+				margin-bottom: 20rpx;
+				font-size: 36rpx;
+			}
+			.txt{
+				font-size: 30rpx;
+				color: #868686;
+			}
+		}
+		.sub_btn{
+			width: 690rpx;
+			height: 86rpx;
+			line-height: 86rpx;
+			margin-top: 60rpx;
+			background: $theme-color;
+			border-radius: 43rpx;
+			color: #fff;
+			font-size: 28rpx;
+			text-align: center;
+		}
+	}
+	.animated{
+		animation-duration:.4s
+	}
+</style>

+ 406 - 0
components/mpvue-calendar/browser-style.css

@@ -0,0 +1,406 @@
+.mpvue-calendar {
+    margin:auto;
+    width: 100%;
+    min-width:350px;
+    background: #fff;
+    user-select:none;
+    position: relative;
+}
+.calendar-tools{
+    height:40px;
+    font-size: 20px;
+    line-height: 40px;
+    color:#5e7a88;
+    box-shadow: 0px 4px 8px rgba(25, 47, 89, 0.1);
+    margin-bottom: 20px;
+    border-top: 1px solid rgba(200, 200, 200, .1);
+}
+.calendar-tools span{
+    cursor: pointer;
+}
+.calendar-prev{
+    width: 14.28571429%;
+    float:left;
+    text-align: center;
+}
+.calendar-prev img, .calendar-next img{
+    width: 34px;
+    height: 34px;
+}
+.calendar-info{
+    font-size:16px;
+    line-height: 1.3;
+    text-align: center;
+    width: 220px;
+    margin: 0 auto;
+}
+.calendar-info>div.mc-month{
+    margin:auto;
+    height:24px;
+    width:100px;
+    text-align: center;
+    color:#5e7a88;
+    overflow: hidden;
+    position: relative;
+}
+.calendar-info>div.mc-month .mc-month-inner{
+    position: absolute;
+    left:0;
+    top:0;
+    height:480px;
+}
+.month-transition{
+    transition:top .5s cubic-bezier(0.075, 0.82, 0.165, 1);
+}
+.calendar-info .mc-month-text{
+    display:block;
+    font-size:28px;
+    height:40px;
+    width:200px;
+    overflow:hidden;
+    text-align:center;
+}
+.calendar-info>div.mc-month .mc-month-inner>span{
+    display: block;
+    font-size: 14px;
+    height:24px;
+    line-height:24px;
+    width:100px;
+    overflow: hidden;
+    text-align: center;
+}
+.calendar-info>div.mc-year{
+    font-size:10px;
+    line-height: 1;
+    color:#999;
+}
+.calendar-next{
+    width: 14.28571429%;
+    float:right;
+    text-align: center;
+}
+.mpvue-calendar table {
+    clear: both;
+    width: 100%;
+    margin-bottom:10px;
+    border-collapse: collapse;
+    color: #444444;
+}
+.mpvue-calendar td {
+    margin:2px !important;
+    padding: 4px;
+    width: 14.28571429%;
+    box-sizing: border-box;
+    text-align: center;
+    vertical-align: middle;
+    font-size:14px;
+    cursor: pointer;
+    position: relative;
+    vertical-align: top;
+}
+.mpvue-calendar td.mc-week{
+    font-size:10px;
+    pointer-events:none !important;
+    cursor: default !important;
+}
+.mpvue-calendar td.disabled {
+    color: #ccc;
+}
+.mpvue-calendar td.disabled div{
+    color: #ccc;
+}
+.mpvue-calendar td span{
+    display:block;
+    height:100%;
+    width:100%;
+    margin:0px auto;
+    border-radius:50%;
+    position: relative;
+    z-index: 3;
+}
+.mpvue-calendar td:not(.disabled) span.mc-date-red{
+    color:#ea6151;
+}
+.mc-today{
+    color: #3b75fb;
+}
+.mpvue-calendar td.selected span{
+    background-color: #3b75fb;
+    color: #fff;
+    border-radius: 50%;
+}
+.mpvue-calendar td .mc-text{
+    box-sizing: border-box;
+    height: 0.7em;
+    overflow: hidden;
+    text-overflow:ellipsis;
+    white-space: nowrap;
+    position: absolute;
+    bottom: 0px;
+    left: 0;
+    right: 0;
+    text-align: center;
+    font-size: 0.7em;
+    line-height: 0.7em;
+    z-index: 4;
+}
+.mpvue-calendar td .isGregorianFestival,
+.mpvue-calendar td .isTerm,
+.mpvue-calendar td .isLunarFestival{
+    color:#ea6151;
+}
+.mpvue-calendar td.selected span.mc-date-red{
+    background-color: #3b75fb;
+    color: #fff;
+}
+.selected .mc-text {
+    color: #fff !important;
+}
+.mpvue-calendar .lunarStyle .mc-text{
+    overflow: visible;
+    bottom: 20%;
+}
+.mpvue-calendar thead td {
+    text-transform: uppercase;
+    height:30px;
+    vertical-align: middle;
+}
+.mc-head {
+    margin-bottom: 10px;
+}
+.mc-head div {
+    overflow: hidden;
+}
+.mc-head-box div {
+    flex:1;
+    text-align: center;
+    font-size: 18px;
+}
+.mc-head-box {
+    display: flex;
+    flex-direction: row;
+    justify-content: center;
+    align-content: space-between
+}
+.mc-body {
+    padding-bottom: 20px;
+}
+.mc-body tr {
+    display: flex;
+    flex-direction: row;
+    justify-content: center;
+    align-content: space-between;
+}
+.mc-dot {
+    width: 8px;
+    height: 8px;
+    background-color: #ea6151;
+    border-radius: 50%;
+    position: absolute;
+    bottom: -4px;
+    left: 50%;
+    margin-left: -4px;
+    z-index: 5;
+}
+.remark-text {
+    box-sizing: border-box;
+    height: 0.7em;
+    overflow: hidden;
+    text-overflow:ellipsis;
+    white-space: nowrap;
+    position: absolute;
+    bottom: 0px;
+    left: 0;
+    right: 0;
+    text-align: center;
+    font-size: 0.7em;
+    line-height: 0.7em;
+    z-index: 5;
+}
+.slot-element{
+    line-height: normal;
+    position: absolute;
+    z-index: 5;
+}
+.mpvue-calendar-change{
+    position: absolute;
+    left:0px;
+    top:42px;
+    right:0px;
+    bottom:0px;
+    background:#fff;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    flex-wrap:wrap;
+    overflow: auto;
+    transition:all .5s cubic-bezier(0.075, 0.82, 0.165, 1);
+    opacity: 0;
+    pointer-events: none;
+    transform: translateY(-10px);
+    z-index: 9;
+}
+.mpvue-calendar-change.show{
+    opacity: 1;
+    pointer-events: auto;
+    transform: translateY(0px);
+}
+.mpvue-calendar-change span{
+    margin:4px 2%;
+    display: inline-block;
+    line-height: 30px;
+    border-radius: 20px;
+    text-align:center;
+    color:#999;
+    width: 20%;
+    float: left;
+    text-align: center;
+    border-radius: 40px;
+    box-sizing: border-box;
+    margin-bottom: 4%;
+    cursor: pointer;
+}
+.mpvue-calendar-change span.active{
+    background-color: #587dff;
+    box-shadow: 2px 2px 2px rgba(88, 125, 255, 0.7);
+    color:#fff;
+}
+.mpvue-calendar-change .calendar-week-switch-months{
+    height: 100%;
+}
+.mpvue-calendar-change .calendar-week-switch-months span {
+    margin-bottom: 10px;
+    margin-top: 0px;
+}
+.calendar-years, .calendar-months{
+    height: 50%;
+    width: 100%;
+    padding: 10px;
+    box-sizing: border-box;
+    position: relative;
+}
+.calendar-years:after {
+    content: '';
+    display: block;
+    width: 86%;
+    height: 1px;
+    background-color: #eee;
+    position: absolute;
+    bottom: 2%;
+    left: 7%;
+}
+/*range background*/
+.mc-range-mode .selected .mc-range-bg{
+    content: '';
+    display: block;
+    width: 150%;
+    height: 100%;
+    background-color: #01a1ed;
+    position: relative;
+    top: -100%;
+    left: 50%;
+}
+.mpvue-calendar .mc-range-mode .selected .calendar-date{
+    background-color: transparent;
+}
+.mpvue-calendar .mc-range-mode .mc-range-row-last .calendar-date, .mpvue-calendar .mc-range-mode .mc-range-row-first .calendar-date{
+    border-radius: 4px;
+    background-color: #01a1ed;
+}
+.mpvue-calendar .mc-range-mode .mc-range-month-first.selected .calendar-date, .mpvue-calendar .mc-range-mode .mc-range-month-last.selected .calendar-date{
+    background-color: #01a1ed;
+    border-radius: 4px;
+}
+.mc-range-mode .mc-range-month-last .mc-range-bg{
+    background-color: transparent;
+    border-radius: 4px;
+}
+.mc-range-mode .mc-range-end .mc-range-bg, .mc-range-mode .mc-range-row-last .mc-range-bg{
+    display: none;
+}
+.mc-range-row-first.mc-range-end .mc-range-bg{
+    display: block;
+    margin-left: -50%;
+    width: 50%;
+    border-radius: 4px;
+}
+.mpvue-calendar .mc-range-row-first.mc-range-end.month-first-date .mc-range-bg{
+    margin-left: 0px;
+}
+.mc-range-row-last.mc-range-begin .mc-range-bg{
+    display: block;
+    width: 50%;
+    border-radius: 4px;
+}
+.mpvue-calendar .mc-range-mode .selected.mc-range-second-to-last span{
+    background-color: #01a1ed;
+    border-radius: 4px;
+}
+.mc-range-begin.mc-range-second-to-last{
+    background-color: #01a1ed;
+    border-radius: 4px;
+}
+.mpvue-calendar .mc-range-mode .mc-range-end span.calendar-date, .mpvue-calendar .mc-range-mode .mc-range-begin span.calendar-date{
+    background-color: #3b75fb;
+    color: #fff;
+    border-radius: 50%;
+}
+.mpvue-calendar .mc-range-mode .month-last-date.mc-range-begin .mc-range-bg{
+    display: block;
+    width: 50%;
+    border-radius: 4px;
+}
+.mpvue-calendar .mc-range-mode .month-first-date.mc-range-end .mc-range-bg{
+    display: block;
+    width: 50%;
+    border-radius: 4px;
+    left: 0px;
+}
+.calendar-wrapper .mpvue-calendar .mc-range-mode .mc-range-select-one div.mc-range-bg{
+    display: none;
+}
+.mc-range-mode .mc-range-second-to-last .mc-range-bg{
+    border-radius: 0px 25% 25% 0px;
+}
+.mc-today-element .calendar-date{
+    background-color: rgba(25, 47, 89, 0.1);
+    border-radius: 4px;
+}
+/*week switch*/
+.mpvue-calendar .mc-range-mode.week-switch .month-last-date.mc-range-begin .mc-range-bg{
+    width: 150%;
+    border-radius: 0px 20% 20% 0px;
+}
+.mpvue-calendar .mc-range-mode.week-switch .mc-range-month-last .mc-range-bg{
+    background-color: #01a1ed;
+    border-radius: 0px 20% 20% 0px;
+}
+/*month range*/
+.mpvue-calendar .month-range-mode{
+  border-bottom: 1px solid #f2f2f2;
+  position: relative;
+}
+.mpvue-calendar .mc-month-range-mode-head{
+  box-shadow: 0 4px 8px rgba(25,47,89,.1);
+  padding: 8px 0px;
+  position: sticky;
+  top: 0px;
+  background-color: #fff;
+  z-index: 9;
+}
+.month-range-mode .month-rang-head {
+  text-align: left;
+  margin: 10px 0px;
+  padding-left: 10px;
+}
+.month-range-mode .mc-last-month, .month-range-mode .mc-next-month{
+  opacity: 0 !important;
+}
+.month-text-background{
+  position: absolute;
+  font-size: 140px;
+  width: 100%;
+  height: 100%;
+  text-align: center;
+  line-height: 2.4;
+}

+ 524 - 0
components/mpvue-calendar/calendarinit.js

@@ -0,0 +1,524 @@
+/**
+ * @1900-2100区间内的公历、农历互转
+ * @charset UTF-8
+ * @Author  Jea杨(JJonline@JJonline.Cn)
+ * @Time    2014-7-21
+ * @Time    2016-8-13 Fixed 2033hex、Attribution Annals
+ * @Time    2016-9-25 Fixed lunar LeapMonth Param Bug
+ * @Version 1.0.2
+ * @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
+ * @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
+ */
+var calendar = {
+
+  /**
+   * 农历1900-2100的润大小信息表
+   * @Array Of Property
+   * @return Hex
+   */
+  lunarInfo:[0x04bd8,0x04ae0,0x0a570,0x054d5,0x0d260,0x0d950,0x16554,0x056a0,0x09ad0,0x055d2,//1900-1909
+    0x04ae0,0x0a5b6,0x0a4d0,0x0d250,0x1d255,0x0b540,0x0d6a0,0x0ada2,0x095b0,0x14977,//1910-1919
+    0x04970,0x0a4b0,0x0b4b5,0x06a50,0x06d40,0x1ab54,0x02b60,0x09570,0x052f2,0x04970,//1920-1929
+    0x06566,0x0d4a0,0x0ea50,0x06e95,0x05ad0,0x02b60,0x186e3,0x092e0,0x1c8d7,0x0c950,//1930-1939
+    0x0d4a0,0x1d8a6,0x0b550,0x056a0,0x1a5b4,0x025d0,0x092d0,0x0d2b2,0x0a950,0x0b557,//1940-1949
+    0x06ca0,0x0b550,0x15355,0x04da0,0x0a5b0,0x14573,0x052b0,0x0a9a8,0x0e950,0x06aa0,//1950-1959
+    0x0aea6,0x0ab50,0x04b60,0x0aae4,0x0a570,0x05260,0x0f263,0x0d950,0x05b57,0x056a0,//1960-1969
+    0x096d0,0x04dd5,0x04ad0,0x0a4d0,0x0d4d4,0x0d250,0x0d558,0x0b540,0x0b6a0,0x195a6,//1970-1979
+    0x095b0,0x049b0,0x0a974,0x0a4b0,0x0b27a,0x06a50,0x06d40,0x0af46,0x0ab60,0x09570,//1980-1989
+    0x04af5,0x04970,0x064b0,0x074a3,0x0ea50,0x06b58,0x055c0,0x0ab60,0x096d5,0x092e0,//1990-1999
+    0x0c960,0x0d954,0x0d4a0,0x0da50,0x07552,0x056a0,0x0abb7,0x025d0,0x092d0,0x0cab5,//2000-2009
+    0x0a950,0x0b4a0,0x0baa4,0x0ad50,0x055d9,0x04ba0,0x0a5b0,0x15176,0x052b0,0x0a930,//2010-2019
+    0x07954,0x06aa0,0x0ad50,0x05b52,0x04b60,0x0a6e6,0x0a4e0,0x0d260,0x0ea65,0x0d530,//2020-2029
+    0x05aa0,0x076a3,0x096d0,0x04afb,0x04ad0,0x0a4d0,0x1d0b6,0x0d250,0x0d520,0x0dd45,//2030-2039
+    0x0b5a0,0x056d0,0x055b2,0x049b0,0x0a577,0x0a4b0,0x0aa50,0x1b255,0x06d20,0x0ada0,//2040-2049
+    /**Add By JJonline@JJonline.Cn**/
+    0x14b63,0x09370,0x049f8,0x04970,0x064b0,0x168a6,0x0ea50, 0x06b20,0x1a6c4,0x0aae0,//2050-2059
+    0x0a2e0,0x0d2e3,0x0c960,0x0d557,0x0d4a0,0x0da50,0x05d55,0x056a0,0x0a6d0,0x055d4,//2060-2069
+    0x052d0,0x0a9b8,0x0a950,0x0b4a0,0x0b6a6,0x0ad50,0x055a0,0x0aba4,0x0a5b0,0x052b0,//2070-2079
+    0x0b273,0x06930,0x07337,0x06aa0,0x0ad50,0x14b55,0x04b60,0x0a570,0x054e4,0x0d160,//2080-2089
+    0x0e968,0x0d520,0x0daa0,0x16aa6,0x056d0,0x04ae0,0x0a9d4,0x0a2d0,0x0d150,0x0f252,//2090-2099
+    0x0d520],//2100
+
+  /**
+   * 公历每个月份的天数普通表
+   * @Array Of Property
+   * @return Number
+   */
+  solarMonth:[31,28,31,30,31,30,31,31,30,31,30,31],
+
+  /**
+   * 天干地支之天干速查表
+   * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
+   * @return Cn string
+   */
+  Gan:["\u7532","\u4e59","\u4e19","\u4e01","\u620a","\u5df1","\u5e9a","\u8f9b","\u58ec","\u7678"],
+
+  /**
+   * 天干地支之地支速查表
+   * @Array Of Property
+   * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
+   * @return Cn string
+   */
+  Zhi:["\u5b50","\u4e11","\u5bc5","\u536f","\u8fb0","\u5df3","\u5348","\u672a","\u7533","\u9149","\u620c","\u4ea5"],
+
+  /**
+   * 天干地支之地支速查表<=>生肖
+   * @Array Of Property
+   * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
+   * @return Cn string
+   */
+  Animals:["\u9f20","\u725b","\u864e","\u5154","\u9f99","\u86c7","\u9a6c","\u7f8a","\u7334","\u9e21","\u72d7","\u732a"],
+
+  /**
+   * 24节气速查表
+   * @Array Of Property
+   * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
+   * @return Cn string
+   */
+  solarTerm:["\u5c0f\u5bd2","\u5927\u5bd2","\u7acb\u6625","\u96e8\u6c34","\u60ca\u86f0","\u6625\u5206","\u6e05\u660e","\u8c37\u96e8","\u7acb\u590f","\u5c0f\u6ee1","\u8292\u79cd","\u590f\u81f3","\u5c0f\u6691","\u5927\u6691","\u7acb\u79cb","\u5904\u6691","\u767d\u9732","\u79cb\u5206","\u5bd2\u9732","\u971c\u964d","\u7acb\u51ac","\u5c0f\u96ea","\u5927\u96ea","\u51ac\u81f3"],
+
+  /**
+   * 1900-2100各年的24节气日期速查表
+   * @Array Of Property
+   * @return 0x string For splice
+   */
+  sTermInfo:['9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e','97bcf97c3598082c95f8c965cc920f',
+    '97bd0b06bdb0722c965ce1cfcc920f','b027097bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f','97bd0b06bdb0722c965ce1cfcc920f','b027097bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e','97bcf97c359801ec95f8c965cc920f','97bd0b06bdb0722c965ce1cfcc920f',
+    'b027097bd097c36b0b6fc9274c91aa','9778397bd19801ec9210c965cc920e','97b6b97bd19801ec95f8c965cc920f',
+    '97bd09801d98082c95f8e1cfcc920f','97bd097bd097c36b0b6fc9210c8dc2','9778397bd197c36c9210c9274c91aa',
+    '97b6b97bd19801ec95f8c965cc920e','97bd09801d98082c95f8e1cfcc920f','97bd097bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa','97b6b97bd19801ec95f8c965cc920e','97bcf97c3598082c95f8e1cfcc920f',
+    '97bd097bd097c36b0b6fc9210c8dc2','9778397bd097c36c9210c9274c91aa','97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c3598082c95f8c965cc920f','97bd097bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e','97bcf97c3598082c95f8c965cc920f','97bd097bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e','97bcf97c359801ec95f8c965cc920f',
+    '97bd097bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f','97bd097bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e','97bcf97c359801ec95f8c965cc920f','97bd097bd07f595b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9210c8dc2','9778397bd19801ec9210c9274c920e','97b6b97bd19801ec95f8c965cc920f',
+    '97bd07f5307f595b0b0bc920fb0722','7f0e397bd097c36b0b6fc9210c8dc2','9778397bd097c36c9210c9274c920e',
+    '97b6b97bd19801ec95f8c965cc920f','97bd07f5307f595b0b0bc920fb0722','7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa','97b6b97bd19801ec9210c965cc920e','97bd07f1487f595b0b0bc920fb0722',
+    '7f0e397bd097c36b0b6fc9210c8dc2','9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f595b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e','97bcf7f1487f595b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e','97bcf7f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f531b0b0bb0b6fb0722','7f0e397bd07f595b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c9274c920e','97bcf7f0e47f531b0b0bb0b6fb0722','7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa','97b6b97bd197c36c9210c9274c920e','97bcf7f0e47f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722','9778397bd097c36b0b6fc9210c8dc2','9778397bd097c36c9210c9274c920e',
+    '97b6b7f0e47f531b0723b0b6fb0722','7f0e37f5307f595b0b0bc920fb0722','7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36b0b70c9274c91aa','97b6b7f0e47f531b0723b0b6fb0721','7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2','9778397bd097c36b0b6fc9274c91aa','97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f595b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa','97b6b7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa','97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722','7f0e397bd07f595b0b0bc920fb0722','9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0787b0721','7f0e27f0e47f531b0b0bb0b6fb0722','7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa','97b6b7f0e47f149b0723b0787b0721','7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722','9778397bd097c36b0b6fc9210c8dc2','977837f0e37f149b0723b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722','7f0e37f5307f595b0b0bc920fb0722','7f0e397bd097c35b0b6fc9210c8dc2',
+    '977837f0e37f14998082b0787b0721','7f07e7f0e47f531b0723b0b6fb0721','7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2','977837f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722','977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722',
+    '977837f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722','977837f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722','7f0e397bd07f595b0b0bc920fb0722','977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f149b0723b0787b0721','7f0e27f0e47f531b0b0bb0b6fb0722','7f0e397bd07f595b0b0bc920fb0722',
+    '977837f0e37f14998082b0723b06bd','7f07e7f0e37f149b0723b0787b0721','7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722','977837f0e37f14898082b0723b02d5','7ec967f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722','7f0e37f1487f595b0b0bb0b6fb0722','7f0e37f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721','7f07e7f0e47f531b0723b0b6fb0722','7f0e37f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e37f14898082b0723b02d5','7ec967f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e37f1487f531b0b0bb0b6fb0722','7f0e37f0e37f14898082b072297c35','7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722','7f0e37f0e37f14898082b072297c35',
+    '7ec967f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35','7ec967f0e37f14998082b0787b06bd','7f07e7f0e47f149b0723b0787b0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722','7f0e37f0e366aa89801eb072297c35','7ec967f0e37f14998082b0723b06bd',
+    '7f07e7f0e47f149b0723b0787b0721','7f0e27f0e47f531b0723b0b6fb0722','7f0e37f0e366aa89801eb072297c35',
+    '7ec967f0e37f14998082b0723b06bd','7f07e7f0e37f14998083b0787b0721','7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35','7ec967f0e37f14898082b0723b02d5','7f07e7f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722','7f0e36665b66aa89801e9808297c35','665f67f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721','7f07e7f0e47f531b0723b0b6fb0722','7f0e36665b66a449801e9808297c35',
+    '665f67f0e37f14898082b0723b02d5','7ec967f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e36665b66a449801e9808297c35','665f67f0e37f14898082b072297c35','7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721','7f0e26665b66a449801e9808297c35','665f67f0e37f1489801eb072297c35',
+    '7ec967f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722'],
+
+  /**
+   * 数字转中文速查表
+   * @Array Of Property
+   * @trans ['日','一','二','三','四','五','六','七','八','九','十']
+   * @return Cn string
+   */
+  nStr1:["\u65e5","\u4e00","\u4e8c","\u4e09","\u56db","\u4e94","\u516d","\u4e03","\u516b","\u4e5d","\u5341"],
+
+  /**
+   * 日期转农历称呼速查表
+   * @Array Of Property
+   * @trans ['初','十','廿','卅']
+   * @return Cn string
+   */
+  nStr2:["\u521d","\u5341","\u5eff","\u5345"],
+
+  /**
+   * 月份转农历称呼速查表
+   * @Array Of Property
+   * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
+   * @return Cn string
+   */
+  nStr3:["\u6b63","\u4e8c","\u4e09","\u56db","\u4e94","\u516d","\u4e03","\u516b","\u4e5d","\u5341","\u51ac","\u814a"],
+
+  /**
+   * 返回农历y年一整年的总天数
+   * @param lunar Year
+   * @return Number
+   * @eg:var count = calendar.lYearDays(1987) ;//count=387
+   */
+  lYearDays:function(y) {
+    var i, sum = 348;
+    for(i=0x8000; i>0x8; i>>=1) { sum += (calendar.lunarInfo[y-1900] & i)? 1: 0; }
+    return(sum+calendar.leapDays(y));
+  },
+
+  /**
+   * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0
+   * @param lunar Year
+   * @return Number (0-12)
+   * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
+   */
+  leapMonth:function(y) { //闰字编码 \u95f0
+    return(calendar.lunarInfo[y-1900] & 0xf);
+  },
+
+  /**
+   * 返回农历y年闰月的天数 若该年没有闰月则返回0
+   * @param lunar Year
+   * @return Number (0、29、30)
+   * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
+   */
+  leapDays:function(y) {
+    if(calendar.leapMonth(y))  {
+      return((calendar.lunarInfo[y-1900] & 0x10000)? 30: 29);
+    }
+    return(0);
+  },
+
+  /**
+   * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法
+   * @param lunar Year
+   * @return Number (-1、29、30)
+   * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
+   */
+  monthDays:function(y,m) {
+    if(m>12 || m<1) {return -1}//月份参数从1至12,参数错误返回-1
+    return( (calendar.lunarInfo[y-1900] & (0x10000>>m))? 30: 29 );
+  },
+
+  /**
+   * 返回公历(!)y年m月的天数
+   * @param solar Year
+   * @return Number (-1、28、29、30、31)
+   * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
+   */
+  solarDays:function(y,m) {
+    if(m>12 || m<1) {return -1} //若参数错误 返回-1
+    var ms = m-1;
+    if(ms==1) { //2月份的闰平规律测算后确认返回28或29
+      return(((y%4 == 0) && (y%100 != 0) || (y%400 == 0))? 29: 28);
+    }else {
+      return(calendar.solarMonth[ms]);
+    }
+  },
+
+  /**
+   * 农历年份转换为干支纪年
+   * @param  lYear 农历年的年份数
+   * @return Cn string
+   */
+  toGanZhiYear:function(lYear) {
+    var ganKey = (lYear - 3) % 10;
+    var zhiKey = (lYear - 3) % 12;
+    if(ganKey == 0) ganKey = 10;//如果余数为0则为最后一个天干
+    if(zhiKey == 0) zhiKey = 12;//如果余数为0则为最后一个地支
+    return calendar.Gan[ganKey-1] + calendar.Zhi[zhiKey-1];
+
+  },
+
+  /**
+   * 公历月、日判断所属星座
+   * @param  cMonth [description]
+   * @param  cDay [description]
+   * @return Cn string
+   */
+  toAstro:function(cMonth,cDay) {
+    var s   = "\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf";
+    var arr = [20,19,21,21,21,22,23,23,23,23,22,22];
+    return s.substr(cMonth*2 - (cDay < arr[cMonth-1] ? 2 : 0),2) + "\u5ea7";//座
+  },
+
+  /**
+   * 传入offset偏移量返回干支
+   * @param offset 相对甲子的偏移量
+   * @return Cn string
+   */
+  toGanZhi:function(offset) {
+    return calendar.Gan[offset%10] + calendar.Zhi[offset%12];
+  },
+
+  /**
+   * 传入公历(!)y年获得该年第n个节气的公历日期
+   * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起
+   * @return day Number
+   * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
+   */
+  getTerm:function(y,n) {
+    if(y<1900 || y>2100) {return -1;}
+    if(n<1 || n>24) {return -1;}
+    var _table = calendar.sTermInfo[y-1900];
+    var _info = [
+      parseInt('0x'+_table.substr(0,5)).toString() ,
+      parseInt('0x'+_table.substr(5,5)).toString(),
+      parseInt('0x'+_table.substr(10,5)).toString(),
+      parseInt('0x'+_table.substr(15,5)).toString(),
+      parseInt('0x'+_table.substr(20,5)).toString(),
+      parseInt('0x'+_table.substr(25,5)).toString()
+    ];
+    var _calday = [
+      _info[0].substr(0,1),
+      _info[0].substr(1,2),
+      _info[0].substr(3,1),
+      _info[0].substr(4,2),
+
+      _info[1].substr(0,1),
+      _info[1].substr(1,2),
+      _info[1].substr(3,1),
+      _info[1].substr(4,2),
+
+      _info[2].substr(0,1),
+      _info[2].substr(1,2),
+      _info[2].substr(3,1),
+      _info[2].substr(4,2),
+
+      _info[3].substr(0,1),
+      _info[3].substr(1,2),
+      _info[3].substr(3,1),
+      _info[3].substr(4,2),
+
+      _info[4].substr(0,1),
+      _info[4].substr(1,2),
+      _info[4].substr(3,1),
+      _info[4].substr(4,2),
+
+      _info[5].substr(0,1),
+      _info[5].substr(1,2),
+      _info[5].substr(3,1),
+      _info[5].substr(4,2),
+    ];
+    return parseInt(_calday[n-1]);
+  },
+
+  /**
+   * 传入农历数字月份返回汉语通俗表示法
+   * @param lunar month
+   * @return Cn string
+   * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
+   */
+  toChinaMonth:function(m) { // 月 => \u6708
+    if(m>12 || m<1) {return -1} //若参数错误 返回-1
+    var s = calendar.nStr3[m-1];
+    s+= "\u6708";//加上月字
+    return s;
+  },
+
+  /**
+   * 传入农历日期数字返回汉字表示法
+   * @param lunar day
+   * @return Cn string
+   * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
+   */
+  toChinaDay:function(d){ //日 => \u65e5
+    var s;
+    switch (d) {
+      case 10:
+        s = '\u521d\u5341'; break;
+      case 20:
+        s = '\u4e8c\u5341'; break;
+        break;
+      case 30:
+        s = '\u4e09\u5341'; break;
+        break;
+      default :
+        s = calendar.nStr2[Math.floor(d/10)];
+        s += calendar.nStr1[d%10];
+    }
+    return(s);
+  },
+
+  /**
+   * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
+   * @param y year
+   * @return Cn string
+   * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
+   */
+  getAnimal: function(y) {
+    return calendar.Animals[(y - 4) % 12]
+  },
+
+  /**
+   * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
+   * @param y  solar year
+   * @param m  solar month
+   * @param d  solar day
+   * @return JSON object
+   * @eg:console.log(calendar.solar2lunar(1987,11,01));
+   */
+  solar2lunar:function (y,m,d) { //参数区间1900.1.31~2100.12.31
+    if(y<1900 || y>2100) {return -1;}//年份限定、上限
+    if(y==1900&&m==1&&d<31) {return -1;}//下限
+    if(!y) { //未传参  获得当天
+      var objDate = new Date();
+    }else {
+      var objDate = new Date(y,parseInt(m)-1,d)
+    }
+    var i, leap=0, temp=0;
+    //修正ymd参数
+    var y = objDate.getFullYear(),m = objDate.getMonth()+1,d = objDate.getDate();
+    var offset   = (Date.UTC(objDate.getFullYear(),objDate.getMonth(),objDate.getDate()) - Date.UTC(1900,0,31))/86400000;
+    for(i=1900; i<2101 && offset>0; i++) { temp=calendar.lYearDays(i); offset-=temp; }
+    if(offset<0) { offset+=temp; i--; }
+
+    //是否今天
+    var isTodayObj = new Date(),isToday=false;
+    if(isTodayObj.getFullYear()==y && isTodayObj.getMonth()+1==m && isTodayObj.getDate()==d) {
+      isToday = true;
+    }
+    //星期几
+    var nWeek = objDate.getDay(),cWeek = calendar.nStr1[nWeek];
+    if(nWeek==0) {nWeek =7;}//数字表示周几顺应天朝周一开始的惯例
+    //农历年
+    var year = i;
+
+    var leap = calendar.leapMonth(i); //闰哪个月
+    var isLeap = false;
+
+    //效验闰月
+    for(i=1; i<13 && offset>0; i++) {
+      //闰月
+      if(leap>0 && i==(leap+1) && isLeap==false){
+        --i;
+        isLeap = true; temp = calendar.leapDays(year); //计算农历闰月天数
+      }
+      else{
+        temp = calendar.monthDays(year, i);//计算农历普通月天数
+      }
+      //解除闰月
+      if(isLeap==true && i==(leap+1)) { isLeap = false; }
+      offset -= temp;
+    }
+
+    if(offset==0 && leap>0 && i==leap+1)
+      if(isLeap){
+        isLeap = false;
+      }else{
+        isLeap = true; --i;
+      }
+    if(offset<0){ offset += temp; --i; }
+    //农历月
+    var month   = i;
+    //农历日
+    var day     = offset + 1;
+
+    //天干地支处理
+    var sm      =   m-1;
+    var gzY     =   calendar.toGanZhiYear(year);
+
+    //月柱 1900年1月小寒以前为 丙子月(60进制12)
+    var firstNode   = calendar.getTerm(year,(m*2-1));//返回当月「节」为几日开始
+    var secondNode  = calendar.getTerm(year,(m*2));//返回当月「节」为几日开始
+
+    //依据12节气修正干支月
+    var gzM     =   calendar.toGanZhi((y-1900)*12+m+11);
+    if(d>=firstNode) {
+      gzM     =   calendar.toGanZhi((y-1900)*12+m+12);
+    }
+
+    //传入的日期的节气与否
+    var isTerm = false;
+    var Term   = null;
+    if(firstNode==d) {
+      isTerm  = true;
+      Term    = calendar.solarTerm[m*2-2];
+    }
+    if(secondNode==d) {
+      isTerm  = true;
+      Term    = calendar.solarTerm[m*2-1];
+    }
+    //日柱 当月一日与 1900/1/1 相差天数
+    var dayCyclical = Date.UTC(y,sm,1,0,0,0,0)/86400000+25567+10;
+    var gzD = calendar.toGanZhi(dayCyclical+d-1);
+    //该日期所属的星座
+    var astro = calendar.toAstro(m,d);
+
+    return {'lYear':year,'lMonth':month,'lDay':day,'Animal':calendar.getAnimal(year),'IMonthCn':(isLeap?"\u95f0":'')+calendar.toChinaMonth(month),'IDayCn':calendar.toChinaDay(day),'cYear':y,'cMonth':m,'cDay':d,'gzYear':gzY,'gzMonth':gzM,'gzDay':gzD,'isToday':isToday,'isLeap':isLeap,'nWeek':nWeek,'ncWeek':"\u661f\u671f"+cWeek,'isTerm':isTerm,'Term':Term,'astro':astro};
+  },
+
+  /**
+   * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON
+   * @param y  lunar year
+   * @param m  lunar month
+   * @param d  lunar day
+   * @param isLeapMonth  lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
+   * @return JSON object
+   * @eg:console.log(calendar.lunar2solar(1987,9,10));
+   */
+  lunar2solar:function(y,m,d,isLeapMonth) {   //参数区间1900.1.31~2100.12.1
+    var isLeapMonth = !!isLeapMonth;
+    var leapOffset  = 0;
+    var leapMonth   = calendar.leapMonth(y);
+    var leapDay     = calendar.leapDays(y);
+    if(isLeapMonth&&(leapMonth!=m)) {return -1;}//传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
+    if(y==2100&&m==12&&d>1 || y==1900&&m==1&&d<31) {return -1;}//超出了最大极限值
+    var day  = calendar.monthDays(y,m);
+    var _day = day;
+    //bugFix 2016-9-25
+    //if month is leap, _day use leapDays method
+    if(isLeapMonth) {
+      _day = calendar.leapDays(y,m);
+    }
+    if(y < 1900 || y > 2100 || d > _day) {return -1;}//参数合法性效验
+
+    //计算农历的时间差
+    var offset = 0;
+    for(var i=1900;i<y;i++) {
+      offset+=calendar.lYearDays(i);
+    }
+    var leap = 0,isAdd= false;
+    for(var i=1;i<m;i++) {
+      leap = calendar.leapMonth(y);
+      if(!isAdd) {//处理闰月
+        if(leap<=i && leap>0) {
+          offset+=calendar.leapDays(y);isAdd = true;
+        }
+      }
+      offset+=calendar.monthDays(y,i);
+    }
+    //转换闰月农历 需补充该年闰月的前一个月的时差
+    if(isLeapMonth) {offset+=day;}
+    //1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
+    var stmap   =   Date.UTC(1900,1,30,0,0,0);
+    var calObj  =   new Date((offset+d-31)*86400000+stmap);
+    var cY      =   calObj.getUTCFullYear();
+    var cM      =   calObj.getUTCMonth()+1;
+    var cD      =   calObj.getUTCDate();
+
+    return calendar.solar2lunar(cY,cM,cD);
+  }
+};
+
+export default calendar

+ 15 - 0
components/mpvue-calendar/icon.css

@@ -0,0 +1,15 @@
+@font-face {font-family: "iconfont";
+    src: url('data:font/truetype;charset=utf-8;base64,d09GRgABAAAAAASEAAsAAAAABuwAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFY7d0f0Y21hcAAAAYAAAABTAAABhmJUzs9nbHlmAAAB1AAAALcAAADIzC0F5mhlYWQAAAKMAAAALwAAADYS7IZUaGhlYQAAArwAAAAcAAAAJAfeA4RobXR4AAAC2AAAAAwAAAAMDAAAAGxvY2EAAALkAAAACAAAAAgANgBkbWF4cAAAAuwAAAAfAAAAIAEOACluYW1lAAADDAAAAUUAAAJtPlT+fXBvc3QAAARUAAAALQAAAEOUPjuMeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2BkYWCcwMDKwMHUyXSGgYGhH0IzvmYwYuRgYGBiYGVmwAoC0lxTGByeMj5jYm7438AQw9zA0AAUZgTJAQDdSgvleJztkMERgDAIBPdIzMOxEB8W5MvuTRsRMHbhzSwHR/IBWIDiHE4FXYjQ6akyL6yZ13zT3IXd6jYGfO6S75q7xT81fm1Z9zlZXOsl+j5BD35IDU4AeJwVzUEOwUAYBeD/mfxTEso/mkEJoYluqgtajUjYsHEOSytncROJK/QErjNMd+8lL+8jEP3eqq8uNCPiokK1L4t1ihzVCSPMEekAep3mQCh4tpVm95JWz2QGj3iyiN3LZMJBODU49IMuDxk32YifuI89X4+xqwcsHUtEjfVVWt1p5MvWNs9RY4RowNLrJ7Tq8ZKNN7wmvMp8jBc7PDpW3RPfPrISV5ss4QEO9pxcdrix/gMMIyHOAHicY2BkYGAAYmNBZtV4fpuvDNwsDCBw/dlCBQT9fzMLA3MKkMvBwAQSBQDyNAlAAHicY2BkYGBu+N/AEMPCAAJAkpEBFTADAEcJAmwEAAAABAAAAAQAAAAAAAAAADYAZHicY2BkYGBgZpBlANEMDExAzAWEDAz/wXwGAAuHATgAeJxlj01OwzAQhV/6B6QSqqhgh+QFYgEo/RGrblhUavdddN+mTpsqiSPHrdQDcB6OwAk4AtyAO/BIJ5s2lsffvHljTwDc4Acejt8t95E9XDI7cg0XuBeuU38QbpBfhJto41W4Rf1N2MczpsJtdGF5g9e4YvaEd2EPHXwI13CNT+E69S/hBvlbuIk7/Aq30PHqwj7mXle4jUcv9sdWL5xeqeVBxaHJIpM5v4KZXu+Sha3S6pxrW8QmU4OgX0lTnWlb3VPs10PnIhVZk6oJqzpJjMqt2erQBRvn8lGvF4kehCblWGP+tsYCjnEFhSUOjDFCGGSIyujoO1Vm9K+xQ8Jee1Y9zed0WxTU/3OFAQL0z1xTurLSeTpPgT1fG1J1dCtuy56UNJFezUkSskJe1rZUQuoBNmVXjhF6XNGJPyhnSP8ACVpuyAAAAHicY2BigAAuBuyAmZGJkZmRhYEnKzMxryS/tDgjMS+dC8qpzC9lYAAAiPIJlAAAAA==');
+}
+
+.iconfont {
+    font-family:"iconfont" !important;
+    font-size:16px;
+    font-style:normal;
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-arrow-right:before { content: "\e602"; }
+
+.icon-arrow-left:before { content: "\e501"; }

+ 1153 - 0
components/mpvue-calendar/mpvue-calendar.vue

@@ -0,0 +1,1153 @@
+<template>
+  <div class="mpvue-calendar" ref="calendar">
+    <div class="calendar-tools" v-if="!isMonthRange">
+      <div class="calendar-prev" @click="prev">
+        <img :src="arrowLeft" v-if="!!arrowLeft">
+        <i class="iconfont icon-arrow-left" v-else></i>
+      </div>
+      <div class="calendar-next"  @click="next">
+        <img :src="arrowRight" v-if="!!arrowRight">
+        <i class="iconfont icon-arrow-right" v-else></i>
+      </div>
+      <div class="calendar-info" @click.stop="changeYear">
+        <div class="mc-month">
+          <div :class="['mc-month-inner', oversliding ? '' : 'month-transition']" :style="{'top': monthPosition + unit}" v-if="isIos">
+            <span v-for="(m, i) in monthsLoop" :key="i" >{{m}}</span>
+          </div>
+          <div class="mc-month-text" v-else>{{monthText}}</div>
+        </div>
+        <div class="mc-year">{{year}}</div>
+      </div>
+    </div>
+    <table cellpadding="5">
+      <div class="mc-head" :class="['mc-head', {'mc-month-range-mode-head': isMonthRange}]">
+        <div class="mc-head-box">
+          <div v-for="(week, index) in weeks" :key="index" class="mc-week">{{week}}</div>
+        </div>
+      </div>
+      <div :class="['mc-body', {'mc-range-mode': range, 'week-switch': weekSwitch && !isMonthRange, 'month-range-mode': isMonthRange}]" v-for="(days, index) in monthRangeDays" :key='index'>
+        <div class="month-rang-head" v-if="isMonthRange">{{rangeOfMonths[index][2]}}</div>
+        <tr v-for="(day,k1) in days" :key="k1" :class="{'gregorianStyle': !lunar}">
+          <td v-for="(child,k2) in day" :key="k2" :class="[{'selected': child.selected, 'mc-today-element': child.isToday, 'disabled': child.disabled, 'mc-range-select-one': rangeBgHide && child.selected, 'lunarStyle': lunar, 'mc-range-row-first': k2 === 0 && child.selected, 'month-last-date': child.lastDay, 'month-first-date': 1 === child.day, 'mc-range-row-last': k2 === 6 && child.selected, 'mc-last-month': child.lastMonth, 'mc-next-month': child.nextMonth}, child.className, child.rangeClassName]" @click="select(k1, k2, child, $event, index)" class="mc-day" :style="itemStyle">
+            <span v-if="showToday.show && child.isToday" class="mc-today calendar-date">{{showToday.text}}</span>
+            <span :class="[{'mc-date-red': k2 === (monFirst ? 5 : 0) || k2 === 6}, 'calendar-date']" v-else>{{child.day}}</span>
+            <div class="slot-element" v-if="!!child.content">{{child.content}}</div>
+            <div class="mc-text remark-text" v-if="child.eventName && !clean">{{child.eventName}}</div>
+            <div class="mc-dot" v-if="child.eventName && clean"></div>
+            <div class="mc-text" :class="{'isLunarFestival': child.isAlmanac || child.isLunarFestival,'isGregorianFestival': child.isGregorianFestival,'isTerm': child.isTerm}" v-if="lunar && (!child.eventName || clean)">{{child.almanac || child.lunar}}</div>
+            <div class="mc-range-bg" v-if="range && child.selected"/>
+          </td>
+        </tr>
+      </div>
+    </table>
+    <div class="mpvue-calendar-change" :class="{'show': yearsShow}">
+      <div class="calendar-years" v-if="!weekSwitch">
+        <span v-for="y in years" :key="y" @click.stop="selectYear(y)" :class="{'active': y === year}">{{y}}</span>
+      </div>
+      <div :class="['calendar-months', {'calendar-week-switch-months': weekSwitch}]">
+        <span v-for="(m, i) in months" :key="m" @click.stop="changeMonth(i)" :class="{'active': i === month}">{{m}}</span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+  import calendar from './calendarinit.js';
+  import './icon.css';
+  const isBrowser = !!window;
+  const now = new Date();
+  const todayString = [now.getFullYear(), now.getMonth() + 1, now.getDate()].join('-');
+  export default {
+    props: {
+      multi: {
+        type: Boolean,
+        default: false
+      },
+      arrowLeft: {
+        type: String,
+        default: ''
+      },
+      arrowRight: {
+        type: String,
+        default: ''
+      },
+      clean: {
+        type: Boolean,
+        default: false
+      },
+      now: {
+        type: [String, Boolean],
+        default: true
+      },
+      range:{
+        type: Boolean,
+        default: false
+      },
+      completion:{
+        type: Boolean,
+        default: false
+      },
+      value: {
+        type: Array,
+        default: function(){
+          return []
+        }
+      },
+      begin:  {
+        type: Array,
+        default: function(){
+          return []
+        }
+      },
+      end:  {
+        type: Array,
+        default: function(){
+          return []
+        }
+      },
+      zero:{
+        type: Boolean,
+        default: false
+      },
+      disabled:{
+        type: Array,
+        default: function(){
+          return []
+        }
+      },
+      almanacs:{
+        type: Object,
+        default: function(){
+          return {}
+        }
+      },
+      tileContent:{
+        type: Array,
+        default: function(){
+          return []
+        }
+      },
+      lunar: {
+        type: Boolean,
+        default: false
+      },
+      monFirst: {
+        type: Boolean,
+        default: false
+      },
+      weeks: {
+        type: Array,
+        default:function(){
+          return this.monFirst ? ['一', '二', '三', '四', '五', '六', '日'] : ['日', '一', '二', '三', '四', '五', '六']
+        }
+      },
+      months:{
+        type: Array,
+        default:function(){
+          return ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月']
+        }
+      },
+      events:  {
+        type: Object,
+        default: function(){
+          return {}
+        }
+      },
+      weekSwitch: {
+        type: Boolean,
+        default: false
+      },
+      monthRange: {
+        type: Array,
+        default: function(){
+          return []
+        }
+      },
+      responsive: {
+        type: Boolean,
+        default: false
+      },
+      rangeMonthFormat: {
+        type: String,
+        default: ''
+      }
+    },
+    data() {
+      return {
+        years:[],
+        yearsShow:false,
+        year: 0,
+        month: 0,
+        monthPosition: 0,
+        day: 0,
+        days: [],
+        multiDays:[],
+        today: [],
+        handleMultiDay: [],
+        firstRender: true,
+        isIos: true,
+        showToday: {},
+        monthText: '',
+        festival:{
+          lunar:{
+            "1-1":"春节",
+            "1-15":"元宵节",
+            "2-2":"龙头节",
+            "5-5":"端午节",
+            "7-7":"七夕节",
+            "7-15":"中元节",
+            "8-15":"中秋节",
+            "9-9":"重阳节",
+            "10-1":"寒衣节",
+            "10-15":"下元节",
+            "12-8":"腊八节",
+            "12-23":"小年",
+          },
+          gregorian:{
+            "1-1":"元旦",
+            "2-14":"情人节",
+            "3-8":"妇女节",
+            "3-12":"植树节",
+            "5-1":"劳动节",
+            "5-4":"青年节",
+            "6-1":"儿童节",
+            "7-1":"建党节",
+            "8-1":"建军节",
+            "9-10":"教师节",
+            "10-1":"国庆节",
+            "12-24":"平安夜",
+            "12-25":"圣诞节",
+          },
+        },
+        rangeBegin: [],
+        rangeEnd: [],
+        multiDaysData: [],
+        monthsLoop: [],
+        itemWidth: 50,
+        unit: isBrowser ? 'px' : 'rpx',
+        positionH: isBrowser ? -24 : -40,
+        monthIndex: 0,
+        oversliding: false,
+        rangeBgHide: false,
+        monthRangeDays: [],
+        rangeOfMonths: [],
+        monthDays: [],
+        weekIndex: 0,
+        startWeekIndex: 0,
+        positionWeek: true,
+        isMonthRange: false,
+      }
+    },
+    computed: {
+      itemStyle() {
+        return {width: this.itemWidth + 'px', height: this.itemWidth + 'px', fontSize: this.itemWidth / 4 + 'px', lineHeight: this.lunar ? (this.itemWidth / 1.5 + 'px') : (this.itemWidth + 'px')}
+      }
+    },
+    watch:{
+      events() {
+        if (this.isRendeRangeMode()) return;
+        this.render(this.year, this.month, '_WATCHRENDER_', 'events');
+      },
+      disabled() {
+        if (this.isRendeRangeMode()) return;
+        this.render(this.year, this.month, '_WATCHRENDER_', 'disabled');
+      },
+      value() {
+        if (this.isRendeRangeMode('_WATCHRENDERVALUE_')) return;
+        const value = this.value;
+        let year = value[0], month = value[1] - 1;
+        if (this.multi) {
+          year = value[value.length - 1][0];
+          month = value[value.length - 1][1] - 1;
+        } else if (this.range) {
+          if (this.isUserSelect) {
+            year = this.year;
+            month = this.month;
+            this.isUserSelect = false;
+          } else {
+            year = value[0][0];
+            month = value[0][1] - 1;
+            const day = value[0][2];
+            return this.render(year, month, '_WATCHRENDERVALUE_', [year, month, day]);
+          }
+        }
+        this.render(year, month, '_WATCHRENDERVALUE_');
+      },
+      tileContent() {
+        if (this.isRendeRangeMode()) return;
+        this.render(this.year, this.month, '_WATCHRENDER_', 'tileContent');
+      },
+      almanacs() {
+        if (this.isRendeRangeMode()) return;
+        this.render(this.year, this.month, '_WATCHRENDER_', 'almanacs');
+      },
+      monthRange() {
+        if (this.isRendeRangeMode()) return;
+        this.render(this.year, this.month, '_WATCHRENDER_', 'almanacs');
+      },
+      responsive() {
+        if (this.responsive) this.addResponsiveListener();
+      }
+    },
+    created() {
+      this.isMonthRange = !!this.monthRange.length;
+      const loopArray = this.months.concat();
+      loopArray.unshift(this.months[this.months.length - 1]);
+      loopArray.push(this.months[0]);
+      this.monthsLoop = loopArray;
+      this.monthsLoopCopy = this.monthsLoop.concat();
+    },
+    mounted() {
+      const self = this;
+      this.resize();
+      if (!isBrowser) {
+        wx.getSystemInfo({
+          success: function(res) {
+            self.isIos = (res.system.split(' ') || [])[0] === 'iOS';
+          }
+        });
+      } else if (this.responsive) {
+        this.addResponsiveListener();
+      }
+      this.oversliding = true;
+      this.initRender = true;
+      this.init();
+    },
+    beforeDestroy() {
+      if (isBrowser) {
+        window.removeEventListener('resize', this.resize)
+      }
+    },
+    methods: {
+      init() {
+        let now = new Date();
+        this.year = now.getFullYear();
+        this.month = now.getMonth();
+        this.day = now.getDate();
+        this.monthIndex = this.month + 1;
+        if (this.value.length || this.multi) {
+          if (this.range) {
+            this.year = Number(this.value[0][0]);
+            this.month = this.value[0][1] - 1;
+            this.day = Number(this.value[0][2]);
+            const yearEnd = Number(this.value[1][0]);
+            const monthEnd = this.value[1][1] - 1;
+            const dayEnd = this.value[1][2];
+            this.rangeBegin = [this.year, this.month, this.day];
+            this.rangeEnd = [yearEnd, monthEnd, dayEnd];
+          } else if (this.multi) {
+            this.multiDays = this.value;
+            const handleMultiDay = this.handleMultiDay;
+            if (this.firstRender) {
+              this.firstRender = false;
+              const thatYear = (this.value[0] || [])[0];
+              const thatMonth = (this.value[0] || [])[1];
+              if (isFinite(thatYear) && isFinite(thatMonth)) {
+                this.month = parseInt(thatMonth) - 1;
+                this.year = parseInt(thatYear);
+              }
+            } else if (this.handleMultiDay.length) {
+              this.month = parseInt(handleMultiDay[handleMultiDay.length - 1][1]) - 1;
+              this.year = parseInt(handleMultiDay[handleMultiDay.length - 1][0]);
+              this.handleMultiDay = [];
+            } else {
+              this.month = parseInt(this.value[this.value.length - 1][1]) - 1;
+              this.year = parseInt(this.value[this.value.length - 1][0]);
+            }
+            this.day = parseInt((this.value[0] || [])[2]);
+          } else {
+            this.year = parseInt(this.value[0]);
+            this.month = parseInt(this.value[1]) - 1;
+            this.day = parseInt(this.value[2]);
+          }
+        }
+        this.updateHeadMonth();
+        if (this.isRendeRangeMode()) return;
+        this.render(this.year, this.month);
+      },
+      renderOption(year, month, i, playload) {
+        const weekSwitch = this.monthRange.length ? false : this.weekSwitch;
+        const seletSplit = this.value;
+        const isMonthModeCurrentMonth = !weekSwitch && !playload;
+        const disabledFilter = (disabled) => {
+          return disabled.find(v => {
+            const dayArr = v.split('-');
+            return year === Number(dayArr[0]) && month === (dayArr[1] - 1) && i === Number(dayArr[2]);
+          });
+        };
+        if (this.range) {
+          const lastDay = new Date(year, month + 1, 0).getDate() === i ? {lastDay: true} : null;
+          const options = Object.assign(
+            {day: i},
+            this.getLunarInfo(year, month + 1, i),
+            this.getEvents(year, month + 1, i),
+            lastDay
+          );
+          const {date, day} = options;
+          const copyRangeBegin = this.rangeBegin.concat();
+          const copyRangeEnd = this.rangeEnd.concat();
+          copyRangeBegin[1] = copyRangeBegin[1] + 1;
+          copyRangeEnd[1] = copyRangeEnd[1] + 1;
+          if (weekSwitch || isMonthModeCurrentMonth) {
+            (copyRangeEnd.join('-') === date) && (options.rangeClassName = 'mc-range-end');
+            (copyRangeBegin.join('-') === date) && (options.rangeClassName = 'mc-range-begin');
+          }
+          if (year === copyRangeEnd[0] && (month + 1) === copyRangeEnd[1] && day === (copyRangeEnd[2] - 1)) {
+            options.rangeClassName = options.rangeClassName ? ['mc-range-begin', 'mc-range-second-to-last'] : 'mc-range-second-to-last';
+          }
+          if (this.rangeBegin.length) {
+            const beginTime = +new Date(this.rangeBegin[0], this.rangeBegin[1], this.rangeBegin[2]);
+            const endTime = +new Date(this.rangeEnd[0], this.rangeEnd[1], this.rangeEnd[2]);
+            const stepTime = +new Date(year, month, i);
+            if (beginTime <= stepTime && endTime >= stepTime) {
+              options.selected = true;
+            }
+          }
+          if (this.begin.length) {
+            const beginTime = +new Date(parseInt(this.begin[0]), parseInt(this.begin[1]) - 1, parseInt(this.begin[2]));
+            if (beginTime > +new Date(year, month, i)) {
+              options.disabled = true;
+            }
+          }
+          if (this.end.length) {
+            let endTime = Number(new Date(parseInt(this.end[0]), parseInt(this.end[1]) - 1, parseInt(this.end[2])));
+            if (endTime <  Number(new Date(year, month, i))) {
+              options.disabled = true;
+            }
+          }
+          if (playload && !weekSwitch) {
+            options.disabled = true;
+          } else if (this.disabled.length && disabledFilter(this.disabled)) {
+            options.disabled = true;
+          }
+          const monthFirstDay = year + '-' + (month + 1) + '-' + 1;
+          const monthLastDay = year + '-' + (month + 1) + '-' + new Date(year, month + 1, 0).getDate();
+          (monthFirstDay === date && options.selected && !options.rangeClassName) && (options.rangeClassName = 'mc-range-month-first');
+          (monthLastDay === date && options.selected && !options.rangeClassName) && (options.rangeClassName = 'mc-range-month-last');
+          this.isCurrentMonthToday(options) && (options.isToday = true);
+          (!weekSwitch && playload) && (options.selected = false);
+          return options;
+        } else if(this.multi) {
+          let options;
+          if (this.value.find(v => year === v[0] && month === v[1]-1 && i === v[2])){
+            options = Object.assign(
+              {day: i, selected: true},
+              this.getLunarInfo(year, month + 1, i),
+              this.getEvents(year, month + 1, i)
+            );
+          } else {
+            options = Object.assign(
+              {day: i, selected: false},
+              this.getLunarInfo(year, month + 1, i),
+              this.getEvents(year, month + 1, i)
+            );
+            if (this.begin.length) {
+              const beginTime = +new Date(parseInt(this.begin[0]), parseInt(this.begin[1]) - 1, parseInt(this.begin[2]));
+              if (beginTime > +(new Date(year, month, i))) {
+                options.disabled = true;
+              }
+            }
+            if (this.end.length){
+              const endTime = +new Date(parseInt(this.end[0]), parseInt(this.end[1]) - 1, parseInt(this.end[2]));
+              if (endTime < +(new Date(year, month, i))) {
+                options.disabled = true;
+              }
+            }
+            if (playload && !weekSwitch) {
+              options.disabled = true;
+            } else if (this.disabled.length && disabledFilter(this.disabled)) {
+              options.disabled = true;
+            }
+          }
+          if (options.selected && this.multiDaysData.length !== this.value.length) {
+            this.multiDaysData.push(options);
+          }
+          this.isCurrentMonthToday(options) && (options.isToday = true);
+          (!weekSwitch && playload) && (options.selected = false);
+          return options;
+        } else {
+          const options = {};
+          const monthHuman = month + 1;
+          if (seletSplit[0] === year && seletSplit[1] === monthHuman && seletSplit[2] === i) {
+            Object.assign(
+              options,
+              {day: i, selected: true},
+              this.getLunarInfo(year, monthHuman, i),
+              this.getEvents(year, monthHuman, i)
+            );
+          } else {
+            Object.assign(
+              options,
+              {day: i, selected: false},
+              this.getLunarInfo(year, monthHuman, i),
+              this.getEvents(year, monthHuman, i)
+            );
+            if (this.begin.length) {
+              const beginTime = +new Date(parseInt(this.begin[0]), parseInt(this.begin[1]) - 1, parseInt(this.begin[2]));
+              if (beginTime > Number(new Date(year, month, i))) {
+                options.disabled = true;
+              }
+            }
+            if (this.end.length){
+              const endTime = +new Date(parseInt(this.end[0]), parseInt(this.end[1]) - 1, parseInt(this.end[2]));
+              if (endTime < +(new Date(year, month, i))) {
+                options.disabled = true;
+              }
+            }
+            if (playload && !weekSwitch) {
+              options.disabled = true;
+            } else if (this.disabled.length && disabledFilter(this.disabled)) {
+              options.disabled = true;
+            }
+          }
+          this.isCurrentMonthToday(options) && (options.isToday = true);
+          (!weekSwitch && playload) && (options.selected = false);
+          return options;
+        }
+      },
+      isCurrentMonthToday(options) {
+        const isToday = todayString === options.date;
+        if (!isToday) return false;
+        return this.weekSwitch ? isToday : (Number(todayString.split('-')[1]) === this.month + 1);
+      },
+      watchRender(type) {
+        const weekSwitch = this.weekSwitch;
+        const daysDeepCopy = JSON.parse(JSON.stringify(this.monthDays));
+        if (type === 'events') {
+          const events = this.events || {};
+          Object.keys(events).forEach(value => {
+            daysDeepCopy.some(v => v.some(vv => {
+              if (vv.date === value) {
+                vv.eventName = events[value];
+                return true;
+              }
+            }))
+          });
+          this.monthDays = daysDeepCopy;
+        } else if (type === 'disabled') {
+          const disabled = this.disabled || [];
+          disabled.forEach(value => {
+            daysDeepCopy.some(v => v.some(vv => {
+              if (vv.date === value) {
+                vv.disabled = true;
+                return true;
+              }
+            }))
+          });
+        } else if (type === 'almanacs') {
+          const almanacs = this.almanacs || {};
+          Object.keys(almanacs).forEach(value => {
+            daysDeepCopy.some(v => v.some(vv => {
+              if (vv.date.slice(5, 20) === value) {
+                vv.lunar = almanacs[value];
+                return true;
+              }
+            }))
+          });
+        } else if (type === 'tileContent') {
+          const tileContent = this.tileContent || [];
+          tileContent.forEach(value => {
+            daysDeepCopy.some(v => v.some(vv => {
+              if (vv.date === value.date) {
+                vv.className = value.className;
+                vv.content = value.content;
+                return true;
+              }
+            }))
+          });
+        }
+        if (weekSwitch) {
+          this.monthDays = daysDeepCopy;
+          this.days = [daysDeepCopy[this.weekIndex]];
+          this.monthRangeDays = [this.days];
+        } else {
+          this.days = daysDeepCopy;
+          this.monthRangeDays = [this.days];
+        }
+      },
+      render(y, m, renderer, payload) {
+        const weekSwitch = this.weekSwitch;
+        const isCustomRender = renderer === 'CUSTOMRENDER';
+        const isWatchRenderValue = renderer === '_WATCHRENDERVALUE_';
+        this.year = y;
+        this.month = m;
+        if (renderer === '_WATCHRENDER_') return this.watchRender(payload);
+        if (this.range && isWatchRenderValue) {
+          if (!Array.isArray((this.value || [])[0])) {
+            this.rangeBegin = [];
+            this.rangeEnd = [];
+          } else {
+            this.rangeBegin = [this.value[0][0], this.value[0][1] - 1, this.value[0][2]];
+            this.rangeEnd = [this.value[1][0], this.value[1][1] - 1, this.value[1][2]];
+          }
+        }
+        if (isWatchRenderValue && weekSwitch) {
+          this.positionWeek = true;
+        }
+        if (isCustomRender) {
+          this.year = y;
+          this.month = m;
+          this.positionWeek = true;
+          if (weekSwitch && !payload) {
+            this.startWeekIndex = 0;
+            this.weekIndex = 0;
+          }
+          this.updateHeadMonth();
+        }
+        let firstDayOfMonth = new Date(y, m, 1).getDay();
+        const lastDateOfMonth = new Date(y, m + 1, 0).getDate();
+        let lastDayOfLastMonth = new Date(y, m, 0).getDate();
+        this.year = y;
+        let i = 1, line = 0, temp = [], nextMonthPushDays = 1;
+        for (i; i <= lastDateOfMonth; i++) {
+          let day = new Date(y, m, i).getDay();
+          let k;
+          if (day === 0) {
+            temp[line] = [];
+          } else if (i === 1) {
+            temp[line] = [];
+            k = lastDayOfLastMonth - firstDayOfMonth + 1;
+            for (let j = 0; j < firstDayOfMonth; j++) { //generate prev month surplus option
+              temp[line].push(Object.assign(
+                this.renderOption(this.computedPrevYear(y, m), this.computedPrevMonth(false, m), k, 'prevMonth'),
+                {lastMonth: true}
+              ));
+              k++;
+            }
+          }
+
+          temp[line].push(this.renderOption(y, m, i)); //generate current month option
+
+          if (day === 6 && i < lastDateOfMonth) {
+            line++;
+          } else if (i === lastDateOfMonth) {
+            let k = 1;
+            const lastDateOfMonthLength = this.monFirst ? 7 : 6;
+            for (let d = day; d < lastDateOfMonthLength; d++) { //generate next month surplus option
+              temp[line].push(Object.assign(
+                this.renderOption(this.computedNextYear(y, m), this.computedNextMonth(false, m), k, 'nextMonth'),
+                {nextMonth: true}
+              ));
+              k++;
+            }
+            nextMonthPushDays = k;
+          }
+        }
+        const completion = this.completion;
+        if (this.monFirst) {
+          if (!firstDayOfMonth) {
+            let lastMonthDay = lastDayOfLastMonth;
+            const LastMonthItems = [];
+            for (let i = 1; i <= 7; i++) {
+              LastMonthItems.unshift(Object.assign(
+                this.renderOption(this.computedPrevYear(y, m), this.computedPrevMonth(false, m), lastMonthDay, 'prevMonth'),
+                {lastMonth: true}
+              ));
+              lastMonthDay --;
+            }
+            temp.unshift(LastMonthItems);
+          }
+          temp.forEach((item, index) => {
+            if (!index) {
+              return item.splice(0, 1);
+            };
+            temp[index-1].length < 7 && temp[index-1].push(item.splice(0, 1)[0]);
+          });
+          if (this.isMonthRange && temp[temp.length - 1][0].nextMonth) {
+            temp.splice(temp.length - 1, 1); //if the first day of last line is nextMonth, delete this line
+          }
+          if (!completion && !weekSwitch) {
+            const lastIndex = temp.length - 1;
+            const secondToLastIndex = lastIndex - 1;
+            const differentMonth = temp[lastIndex][0].date.split('-')[1] !== temp[secondToLastIndex][6].date.split('-')[1];
+            differentMonth && temp.splice(lastIndex, 1);
+          }
+        }
+        if (completion && !weekSwitch && temp.length <= 5 && nextMonthPushDays > 0) {
+          for (let i = temp.length; i<=5; i++) {
+            temp[i] = [];
+            let start = nextMonthPushDays + (i - line -1) * 7;
+            for (let d = start; d <= start + 6; d++) {
+              temp[i].push(Object.assign(
+                {day: d, disabled: true,  nextMonth: true},
+                this.getLunarInfo(this.computedNextYear(), this.computedNextMonth(true), d),
+                this.getEvents(this.computedNextYear(), this.computedNextMonth(true), d)
+              ));
+            }
+          }
+        }
+        if (this.tileContent.length) {
+          temp.forEach((item, index) => {
+            item.forEach((v, i) => {
+              const contents = this.tileContent.find(val => val.date === v.date);
+              if (contents) {
+                const {className, content} = contents || {};
+                v.className = className;
+                v.content = content;
+              }
+            });
+          });
+        }
+        if (weekSwitch) {
+          const tempLength = temp.length;
+          const lastLineMonth = temp[tempLength - 1][0].date.split('-')[1]; // last line month
+          const secondLastMonth = temp[tempLength - 2][0].date.split('-')[1]; // second-to-last line month
+          lastLineMonth !== secondLastMonth && temp.splice(tempLength - 1, 1);
+        }
+        this.monthDays = temp;
+        if (weekSwitch && !this.isMonthRange) {
+          if (this.positionWeek) {
+            let payloadDay = '';
+            let searchIndex = true;
+            if (Array.isArray(payload)) { //range
+              payloadDay = [payload[0], payload[1] + 1, payload[2]].join('-');
+            } else if (this.multi || isWatchRenderValue) {
+              if (this.thisTimeSelect) {
+                payloadDay = this.thisTimeSelect;
+              } else {
+                payloadDay = this.multi ? this.value[this.value.length - 1].join('-') : this.value.join('-') ;
+              }
+            }
+            if (payload === 'SETTODAY') {
+              payloadDay = todayString;
+            } else if (isCustomRender) {
+              if (typeof payload === 'string') {
+                payloadDay = [y, Number(m) + 1, payload].join('-');
+                searchIndex = true;
+              } else if (typeof payload === 'number') {
+                const setIndex = payload > temp.length ? temp.length - 1 : payload;
+                this.startWeekIndex = setIndex;
+                this.weekIndex = setIndex;
+                this.positionWeek = false;
+                searchIndex = false;
+              }
+            }
+            const positionDay = payloadDay || todayString;
+            if (searchIndex) {
+              temp.some((v, i) => {
+                const isWeekNow = v.find(vv => vv.date === positionDay);
+                if (isWeekNow) {
+                  this.startWeekIndex = i;
+                  this.weekIndex = i;
+                  return true;
+                }
+              });
+            }
+            this.positionWeek = false;
+          }
+          this.days = [temp[this.startWeekIndex]];
+          if (this.initRender) {
+            this.setMonthRangeofWeekSwitch();
+            this.initRender = false;
+          }
+        } else {
+          this.days = temp;
+        }
+        const todayText = '今';
+        if (typeof this.now === 'boolean' && !this.now) {
+          this.showToday = {show: false};
+        } else if (typeof this.now === 'string') {
+          this.showToday = {
+            show: true,
+            text: this.now || todayText
+          };
+        } else {
+          this.showToday = {
+            show: true,
+            text: todayText
+          };
+        }
+        this.monthRangeDays = [this.days];
+        isWatchRenderValue && this.updateHeadMonth();
+        return this.days;
+      },
+      rendeRange(renderer) {
+        const range = [];
+        const self = this;
+        const monthRange = this.monthRange;
+        function formatDateText(fYear, fMonth) {
+          const reg = /([y]+)(.*?)([M]+)(.*?)$/i;
+          const rangeMonthFormat = self.rangeMonthFormat || 'yyyy-MM';
+          reg.exec(rangeMonthFormat);
+          return String(fYear).substring(4 - RegExp.$1.length) + RegExp.$2 + String(fMonth).substring(2 - RegExp.$3.length) + RegExp.$4;
+        }
+        if (monthRange[0] === monthRange[1]) {
+          const [y, m] = monthRange[0].split('-');
+          range.push([Number(y), Number(m), formatDateText(y, m)])
+        } else {
+          const monthRangeOfStart = monthRange[0].split('-');
+          const monthRangeOfEnd = monthRange[1].split('-');
+          let startYear = +monthRangeOfStart[0];
+          let startMonth = +monthRangeOfStart[1];
+          let endYear = +monthRangeOfEnd[0];
+          let endtMonth = +monthRangeOfEnd[1] > 12 ? 12 : +monthRangeOfEnd[1];
+          while (startYear < endYear || startMonth <= endtMonth) {
+            range.push([startYear, startMonth, formatDateText(startYear, startMonth)]);
+            if (startMonth === 12 && startYear !== endYear) {
+              startYear++;
+              startMonth = 0;
+            }
+            startMonth++;
+          }
+        }
+        this.rangeOfMonths = range;
+
+        const monthsRange = range.map(item => {
+          const [yearParam, monthParam] = item;
+          return this.render(yearParam, monthParam - 1, renderer);
+        });
+        this.monthRangeDays = monthsRange;
+      },
+      isRendeRangeMode(renderer) {
+        this.isMonthRange = !!this.monthRange.length;
+        if (this.isMonthRange) {
+          this.rendeRange(renderer);
+          return true;
+        }
+      },
+      renderer(y, m, w) {
+        const renderY = y || this.year;
+        const renderM = typeof parseInt(m) === 'number' ? (m - 1) : this.month;
+        this.initRender = true;
+        this.render(renderY, renderM, 'CUSTOMRENDER', w);
+        !this.weekSwitch && (this.monthsLoop = this.monthsLoopCopy.concat());
+      },
+      computedPrevYear(year, month) {
+        let value = year;
+        if((month - 1) < 0){
+          value--;
+        }
+        return value;
+      },
+      computedPrevMonth(isString, month) {
+        let value = month;
+        if((month - 1) < 0){
+          value = 11;
+        } else {
+          value--;
+        }
+        if(isString) {
+          return value + 1;
+        }
+        return value;
+      },
+      computedNextYear(year, month) {
+        let value = year;
+        if((month + 1) > 11){
+          value++;
+        }
+        return value;
+      },
+      computedNextMonth(isString, month) {
+        let value = month;
+        if((month + 1) > 11){
+          value = 0;
+        } else {
+          value++;
+        }
+        if(isString) {
+          return value + 1;
+        }
+        return value;
+      },
+      getLunarInfo(y, m, d) {
+        let lunarInfo = calendar.solar2lunar(y, m, d);
+        let yearEve = '';
+        if (lunarInfo.lMonth === 12 && lunarInfo.lDay === calendar.monthDays(lunarInfo.lYear, 12)) {
+          yearEve = '除夕';
+        }
+        let lunarValue = lunarInfo.IDayCn;
+        let Term = lunarInfo.Term;
+        let isLunarFestival = false;
+        let isGregorianFestival = false;
+        if(this.festival.lunar[lunarInfo.lMonth + "-" + lunarInfo.lDay]) {
+          lunarValue = this.festival.lunar[lunarInfo.lMonth + "-" + lunarInfo.lDay];
+          isLunarFestival = true;
+        } else if(this.festival.gregorian[m + "-" + d]) {
+          lunarValue = this.festival.gregorian[m + "-" + d];
+          isGregorianFestival = true;
+        }
+        const lunarInfoObj = {
+          date: `${y}-${m}-${d}`,
+          lunar: yearEve || Term || lunarValue,
+          isLunarFestival: isLunarFestival,
+          isGregorianFestival: isGregorianFestival,
+          isTerm: !!yearEve || lunarInfo.isTerm
+        };
+        if (Object.keys(this.almanacs).length) {
+          Object.assign(lunarInfoObj, {
+            almanac: this.almanacs[m + "-" + d] || '',
+            isAlmanac: !!this.almanacs[m + "-" + d]
+          });
+        }
+        return lunarInfoObj;
+      },
+      getEvents(y, m, d){
+        if(Object.keys(this.events).length == 0) return false;
+        let eventName = this.events[y + "-" + m + "-" + d];
+        let data = {};
+        if(eventName!=undefined){
+          data.eventName = eventName;
+        }
+        return data;
+      },
+      prev(e) {
+        e && e.stopPropagation();
+        if (this.isMonthRange) return;
+        const weekSwitch = this.weekSwitch;
+        const changeMonth = (changed) => {
+          if (this.monthIndex === 1) {
+            this.oversliding = false;
+            this.month = 11;
+            this.year = parseInt(this.year) - 1;
+            this.monthIndex = this.monthIndex - 1;
+          } else if (this.monthIndex === 0) {
+            this.oversliding = true;
+            this.monthIndex = 12;
+            setTimeout(() => this.prev(e), 50);
+            return this.updateHeadMonth('custom');
+          } else if (this.monthIndex === 13) {
+            this.month = 11;
+            this.year = parseInt(this.year) - 1;
+            this.monthIndex = this.monthIndex - 1;
+          } else {
+            this.oversliding = false;
+            this.month = parseInt(this.month) - 1;
+            this.monthIndex = this.monthIndex - 1;
+          }
+          this.updateHeadMonth('custom');
+          this.render(this.year, this.month);
+          (typeof changed === 'function') && changed();
+          const weekIndex = weekSwitch ? this.weekIndex : undefined;
+          this.$emit('prev', this.year, this.month + 1, weekIndex);
+        }
+        if (!this.weekSwitch) return changeMonth();
+        const changeWeek = () => {
+          this.weekIndex = this.weekIndex - 1;
+          this.days = [this.monthDays[this.weekIndex]];
+          this.monthRangeDays = [this.days];
+          this.setMonthRangeofWeekSwitch();
+          this.$emit('prev', this.year, this.month + 1, this.weekIndex);
+        }
+        const currentWeek = (this.days[0] || [])[0] || {};
+        if (currentWeek.lastMonth || currentWeek.day === 1) {
+          const monthChenged = () => {
+            const lastMonthLength = this.monthDays.length;
+            const startWeekIndex = currentWeek.lastMonth ? lastMonthLength - 1: lastMonthLength;
+            this.startWeekIndex = startWeekIndex;
+            this.weekIndex = startWeekIndex;
+            changeWeek();
+          }
+          changeMonth(monthChenged);
+        } else {
+          changeWeek();
+        }
+      },
+      next(e) {
+        e && e.stopPropagation();
+        if (this.isMonthRange) return;
+        const weekSwitch = this.weekSwitch;
+        const changeMonth = () => {
+          if (this.monthIndex === 12) {
+            this.oversliding = false;
+            this.month = 0;
+            this.year = parseInt(this.year) + 1;
+            this.monthIndex = this.monthIndex + 1;
+          } else if (this.monthIndex === 0 && this.month === 11) {
+            this.oversliding = false;
+            this.month = 0;
+            this.year = parseInt(this.year) + 1;
+            this.monthIndex = this.monthIndex + 1;
+          } else if (this.monthIndex === 13) {
+            this.oversliding = true;
+            this.monthIndex = 1;
+            setTimeout(() => this.next(e), 50);
+            return this.updateHeadMonth('custom');
+          } else {
+            this.oversliding = false;
+            this.month = parseInt(this.month) + 1;
+            this.monthIndex = this.monthIndex + 1;
+          }
+          this.updateHeadMonth('custom');
+          this.render(this.year, this.month);
+          const weekIndex = weekSwitch ? this.weekIndex : undefined;
+          this.$emit('next', this.year, this.month + 1, weekIndex);
+        }
+        if (!this.weekSwitch) return changeMonth();
+        const changeWeek = () => {
+          this.weekIndex = this.weekIndex + 1;
+          this.days = [this.monthDays[this.weekIndex]];
+          this.monthRangeDays = [this.days];
+          this.setMonthRangeofWeekSwitch();
+          this.$emit('next', this.year, this.month + 1, this.weekIndex);
+        }
+        const currentWeek = (this.days[0] || [])[6] || {};
+        if (currentWeek.nextMonth || currentWeek.day === (new Date(this.year, this.month + 1, 0).getDate())) {
+          const startWeekIndex = currentWeek.nextMonth ? 1 : 0;
+          this.startWeekIndex = startWeekIndex;
+          this.weekIndex = startWeekIndex;
+          changeMonth();
+        } else {
+          changeWeek();
+        }
+      },
+      select(k1, k2, data, e, monthIndex) {
+        e && e.stopPropagation();
+        const weekSwitch = this.weekSwitch;
+        if (data.lastMonth && !weekSwitch) {
+          return this.prev(e);
+        } else if (data.nextMonth && !weekSwitch) {
+          return this.next(e);
+        }
+        if (data.disabled) return;
+        (data || {}).event = (this.events || {})[data.date] || '';
+        const {selected, day, date} = data;
+        const selectedDates = date.split('-');
+        const selectYear = Number(selectedDates[0]);
+        const selectMonth = selectedDates[1] - 1;
+        const selectMonthHuman = Number(selectedDates[1]);
+        const selectDay = Number(selectedDates[2]);;
+        if (this.range) {
+          this.isUserSelect = true;
+          if (this.rangeBegin.length === 0 || this.rangeEndTemp !== 0) {
+            this.rangeBegin = [selectYear, selectMonth, selectDay];
+            this.rangeBeginTemp = this.rangeBegin;
+            this.rangeEnd = [selectYear, selectMonth, selectDay];
+            this.thisTimeSelect = this.rangeEnd;
+            this.rangeEndTemp = 0;
+          } else {
+            this.rangeEnd = [selectYear, selectMonth, selectDay];
+            this.thisTimeSelect = [selectYear, selectMonth, selectDay];
+            if (this.rangeBegin.join('-') === this.rangeEnd.join('-')) {
+              return this.rangeEndTemp = 0;
+            }
+            this.rangeEndTemp = 1;
+            if (+new Date(this.rangeEnd[0], this.rangeEnd[1], this.rangeEnd[2]) < +new Date(this.rangeBegin[0], this.rangeBegin[1], this.rangeBegin[2])) {
+              this.rangeBegin = this.rangeEnd;
+              this.rangeEnd = this.rangeBeginTemp;
+            }
+            const rangeDate = (date) => {
+              return date.map((v, k) =>{
+                const value = k === 1 ? v + 1 : v;
+                return this.zero ? this.zeroPad(value) : value;
+              });
+            }
+            const begin = rangeDate(this.rangeBegin);
+            const end = rangeDate(this.rangeEnd);
+            this.value.splice(0, 1, begin)
+            this.value.splice(1, 1, end)
+            this.$emit('select', begin, end);
+          }
+          this.rangeBgHide = !this.rangeEndTemp || (this.rangeBegin.join('-') === this.rangeEnd.join('-'));
+          this.positionWeek = true;
+          if (this.isMonthRange) {
+            this.rendeRange();
+          } else {
+            this.render(this.year, this.month, undefined, this.thisTimeSelect);
+          }
+        } else if (this.multi) {
+          const filterDayIndex = this.value.findIndex(v => v.join('-') === date);
+          if(~filterDayIndex) {
+            this.handleMultiDay = this.value.splice(filterDayIndex, 1);
+          } else {
+            this.value.push([Number(Number(selectedDates[0])), Number(selectedDates[1]), day]);
+          }
+          this.days[k1][k2].selected = !selected;
+          if (this.monthDays[k1][k2].selected) {
+            this.multiDaysData.push(data);
+          } else {
+            this.multiDaysData = this.multiDaysData.filter(item => item.date !== date);
+          }
+          this.thisTimeSelect = date;
+          this.$emit('select', this.value, this.multiDaysData);
+        } else {
+          const currentSelected = this.value.join('-');
+          this.monthRangeDays.some(value => value.some(v => !!v.find(vv => {
+            if (vv.date === currentSelected) {
+              vv.selected = false;
+              return true;
+            }
+          })));
+          this.monthRangeDays[monthIndex][k1][k2].selected = true;
+          this.day = day;
+          const selectDate = [selectYear, selectMonthHuman, selectDay];
+          this.value[0] = selectYear;
+          this.value[1] = selectMonthHuman;
+          this.value[2] = selectDay;
+          this.today = [k1, k2];
+          this.$emit('select', selectDate, data);
+        }
+      },
+      changeYear() {
+        if(this.yearsShow) {
+          this.yearsShow = false;
+          return false;
+        }
+        this.yearsShow = true;
+        this.years = [];
+        for (let i = this.year - 5; i < this.year + 7; i++){
+          this.years.push(i);
+        }
+      },
+      changeMonth(value) {
+        this.oversliding && (this.oversliding = false);
+        this.yearsShow = false;
+        this.month = value;
+        this.render(this.year, this.month, 'CUSTOMRENDER', 0);
+        this.updateHeadMonth();
+        this.weekSwitch && this.setMonthRangeofWeekSwitch();
+        this.$emit('selectMonth', this.month + 1, this.year);
+      },
+      selectYear(value) {
+        this.yearsShow = false;
+        this.year = value;
+        this.render(this.year, this.month);
+        this.$emit('selectYear', value);
+      },
+      setToday() {
+        const now = new Date();
+        this.year = now.getFullYear();
+        this.month = now.getMonth();
+        this.day = now.getDate();
+        this.positionWeek = true;
+        this.render(this.year, this.month, undefined, 'SETTODAY');
+        this.updateHeadMonth();
+      },
+      setMonthRangeofWeekSwitch() {
+        this.monthsLoop = this.monthsLoopCopy.concat();
+        this.days[0].reduce((prev, current) => {
+          if (!prev) return;
+          const prveDate = ((prev || {}).date || '').split('-');
+          const prevYear = prveDate[0];
+          const prevMonth = prveDate[1];
+          const currentMonth = ((current || {}).date || '').split('-')[1];
+          if (prevMonth === currentMonth) {
+            return current;
+          } else {
+            const prevMonthText = this.months[prevMonth - 1];
+            const currentMonthText = this.months[currentMonth - 1];
+            this.monthsLoop[this.monthIndex] = prevMonthText + '~' + currentMonthText;
+          }
+        });
+      },
+      dateInfo(y, m, d) {
+        return calendar.solar2lunar(y, m, d);
+      },
+      zeroPad(n) {
+        return String(n < 10 ? '0' + n : n)
+      },
+      updateHeadMonth(type) {
+        if (!type) this.monthIndex = this.month + 1;
+        this.monthPosition = this.monthIndex * this.positionH;
+        this.monthText = this.months[this.month];
+      },
+      addResponsiveListener() {
+        window.addEventListener('resize', this.resize);
+      },
+      resize() {
+        const calendar = this.$refs.calendar;
+        this.itemWidth = (calendar.clientWidth/7 - 4).toFixed(5);
+      }
+    }
+  }
+</script>

+ 394 - 0
components/mpvue-calendar/style.css

@@ -0,0 +1,394 @@
+.mpvue-calendar {
+  margin:auto;
+  width: 100%;
+  min-width:300rpx;
+  background: #fff;
+  user-select:none;
+  position: relative;
+}
+.calendar-tools{
+  height:40px;
+  font-size: 20px;
+  line-height: 40px;
+  color:#5e7a88;
+  box-shadow: 0rpx 4rpx 8rpx rgba(25, 47, 89, 0.1);
+  margin-bottom: 30rpx;
+  border-top: 1px solid rgba(200, 200, 200, .1);
+}
+.calendar-prev{
+  width: 14.28571429%;
+  float:left;
+  text-align: center;
+}
+.calendar-prev img, .calendar-next img{
+  width: 34rpx;
+  height: 34rpx;
+}
+.calendar-info{
+  padding-top: 3px;
+  font-size:16px;
+  line-height: 1.3;
+  text-align: center;
+  width: 220rpx;
+  margin: 0 auto;
+}
+.calendar-info>div.mc-month{
+  margin:auto;
+  height:40rpx;
+  width:100px;
+  text-align: center;
+  color:#5e7a88;
+  overflow: hidden;
+  position: relative;
+}
+.calendar-info>div.mc-month .mc-month-inner{
+  position: absolute;
+  left:0;
+  top:0;
+  height:480rpx;
+}
+.month-transition{
+  transition:top .5s cubic-bezier(0.075, 0.82, 0.165, 1);
+}
+.calendar-info .mc-month-text{
+  display:block;
+  font-size:28rpx;
+  height:40rpx;
+  width:200rpx;
+  overflow:hidden;
+  text-align:center;
+}
+.calendar-info>div.mc-month .mc-month-inner>span{
+  display: block;
+  font-size: 14px;
+  height:20px;
+  width:100px;
+  overflow: hidden;
+  text-align: center;
+}
+.calendar-info>div.mc-year{
+  font-size:10px;
+  line-height: 1;
+  color:#999;
+}
+.calendar-next{
+  width: 14.28571429%;
+  float:right;
+  text-align: center;
+}
+.mpvue-calendar table {
+  clear: both;
+  width: 100%;
+  margin-bottom:10px;
+  border-collapse: collapse;
+  color: #444444;
+}
+.mpvue-calendar td {
+  margin:2px !important;
+  padding:0px 0;
+  width: 14.28571429%;
+  height:88rpx;
+  text-align: center;
+  vertical-align: middle;
+  font-size:14px;
+  line-height: 125%;
+  cursor: pointer;
+  position: relative;
+  vertical-align: top;
+}
+.mpvue-calendar td.mc-week{
+  font-size:10px;
+  pointer-events:none !important;
+  cursor: default !important;
+}
+.mpvue-calendar td.disabled {
+  color: #ccc;
+}
+.mpvue-calendar td.disabled div{
+  color: #ccc;
+}
+.mpvue-calendar td span{
+  display:block;
+  height:76rpx;
+  width:76rpx;
+  font-size: 28rpx;
+  line-height:76rpx;
+  margin:0px auto;
+  position: relative;
+  z-index: 3;
+}
+.mpvue-calendar td:not(.disabled) span.mc-date-red{
+  color:#ea6151;
+}
+.mc-today{
+  color: #3b75fb;
+}
+.mpvue-calendar td.selected span{
+  background-color: #3b75fb;
+  color: #fff;
+  border-radius:50%;
+}
+.mpvue-calendar td .mc-text{
+  position: absolute;
+  top:28px;
+  left:0;
+  right:0;
+  text-align: center;
+  padding:2px;
+  font-size:20rpx;
+  line-height: 1.2;
+  color:#444;
+  z-index: 4;
+}
+.mpvue-calendar td .isGregorianFestival,
+.mpvue-calendar td .isTerm,
+.mpvue-calendar td .isLunarFestival{
+  color:#ea6151;
+}
+.mpvue-calendar td.selected span.mc-date-red{
+  background-color: #3b75fb;
+  color: #fff;
+}
+.selected .mc-text {
+  color: #fff !important;
+}
+.mpvue-calendar .lunarStyle span{
+  width: 80rpx;
+  height: 80rpx;
+  line-height:54rpx;
+}
+.mpvue-calendar .lunarStyle .mc-text{
+  top: 44rpx;
+}
+.mpvue-calendar thead td {
+  text-transform: uppercase;
+  height:30px;
+  vertical-align: middle;
+}
+.mc-head {
+  margin-bottom: 20rpx;
+}
+.mc-head div {
+  overflow: hidden;
+}
+.mc-head-box div {
+  flex:1;
+  text-align: center;
+}
+.mc-head-box {
+  display: flex;
+  flex-direction: row;
+  justify-content: center;
+  align-content: space-between
+}
+.mc-head-box div {
+  font-size: 28rpx;
+}
+.mc-body tr {
+  display: flex;
+  flex-direction: row;
+  justify-content: center;
+  align-content: space-between
+}
+.mc-dot {
+  width: 10rpx;
+  height: 10rpx;
+  background-color: #ea6151;
+  border-radius: 50%;
+  margin: 0 auto;
+  margin-top: 5rpx;
+  position: absolute;
+  bottom: -5rpx;
+  left: 50%;
+  margin-left: -5rpx;
+  z-index: 5;
+}
+.remark-text {
+  padding-left: 8rpx;
+  padding-right: 8rpx;
+  box-sizing: border-box;
+  height: 34rpx;
+  overflow: hidden;
+  text-overflow:ellipsis;
+  white-space: nowrap;
+}
+.slot-element{
+  line-height: normal;
+  position: absolute;
+  z-index: 5;
+}
+.mpvue-calendar-change{
+  position: absolute;
+  left:0rpx;
+  top:85rpx;
+  right:0rpx;
+  bottom:0rpx;
+  background:#fff;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  flex-wrap:wrap;
+  overflow: auto;
+  transition:all .5s cubic-bezier(0.075, 0.82, 0.165, 1);
+  opacity: 0;
+  pointer-events: none;
+  transform: translateY(-10px);
+  z-index: 9;
+}
+.mpvue-calendar-change.show{
+  opacity: 1;
+  pointer-events: auto;
+  transform: translateY(0px);
+}
+.mpvue-calendar-change span{
+  margin:4px 2%;
+  display: inline-block;
+  line-height: 30px;
+  border-radius: 20px;
+  text-align:center;
+  color:#999;
+  width: 20%;
+  float: left;
+  text-align: center;
+  border-radius: 40px;
+  box-sizing: border-box;
+  margin-bottom: 4%;
+}
+.mpvue-calendar-change span.active{
+  background-color: #587dff;
+  box-shadow: 2px 2px 2px rgba(88, 125, 255, 0.7);
+  color:#fff;
+}
+.mpvue-calendar-change .calendar-week-switch-months{
+  height: 100%;
+  padding: 10rpx 20rpx;
+}
+.mpvue-calendar-change .calendar-week-switch-months span {
+  margin-bottom: 20rpx;
+  margin-top: 0px;
+  font-size: 26rpx;
+  line-height: 40rpx;
+}
+.calendar-years, .calendar-months{
+  height: 50%;
+  width: 100%;
+  padding: 10px;
+  box-sizing: border-box;
+  position: relative;
+}
+.calendar-years:after {
+  content: '';
+  display: block;
+  width: 86%;
+  height: 1rpx;
+  background-color: #eee;
+  position: absolute;
+  bottom: 2%;
+  left: 7%;
+}
+/*range background*/
+.mc-range-mode .selected .mc-range-bg{
+  content: '';
+  display: block;
+  width: 110rpx;
+  height: 80rpx;
+  background-color: #01a1ed;
+  position: absolute;
+  top: 0rpx;
+  left: 50%;
+}
+.mpvue-calendar .mc-range-mode .selected .calendar-date{
+  background-color: transparent;
+}
+.mpvue-calendar .mc-range-mode .mc-range-row-last span.calendar-date, .mpvue-calendar .mc-range-mode .mc-range-row-first span.calendar-date{
+  border-radius: 6rpx;
+  background-color: #01a1ed;
+}
+.mpvue-calendar .mc-range-mode .mc-range-month-first.selected .calendar-date, .mpvue-calendar .mc-range-mode .mc-range-month-last.selected .calendar-date{
+  border-radius: 6rpx;
+  background-color: #01a1ed;
+}
+.mc-range-mode .mc-range-month-last .mc-range-bg{
+  background-color: transparent;
+  border-radius: 6rpx;
+}
+.mc-range-mode .mc-range-end .mc-range-bg, .mc-range-mode .mc-range-row-last .mc-range-bg{
+  display: none;
+}
+.mpvue-calendar .mc-range-mode .mc-range-end span.calendar-date, .mpvue-calendar .mc-range-mode .mc-range-begin span.calendar-date{
+  background-color: #3b75fb;
+  color: #fff;
+  border-radius: 50%;
+}
+.mc-range-mode .mc-range-row-first.mc-range-end .mc-range-bg{
+  display: block;
+  border-radius: 6rpx;
+  width: 40rpx;
+  left: 5px;
+}
+.mpvue-calendar .mc-range-row-first.mc-range-end.month-first-date .mc-range-bg{
+  margin-left: 0px;
+}
+.mc-range-mode .mc-range-row-last.mc-range-begin .mc-range-bg{
+  display: block;
+  border-radius: 4rpx;
+  width: 40rpx;
+  right: 10px;
+}
+.mpvue-calendar .mc-range-mode .month-last-date.mc-range-begin .mc-range-bg{
+  display: block;
+  width: 40rpx;
+  border-radius: 6rpx;
+}
+.mpvue-calendar .mc-range-mode .month-first-date.mc-range-end .mc-range-bg{
+  display: block;
+  width: 40rpx;
+  border-radius: 6rpx;
+  left: 10rpx;
+}
+.mpvue-calendar .mc-range-mode .mc-range-select-one div.mc-range-bg{
+  display: none !important;
+}
+.mc-body .mc-today-element .calendar-date{
+  background-color: rgba(25, 47, 89, 0.1);
+  border-radius: 6rpx;
+}
+/*week switch*/
+.mpvue-calendar .mc-range-mode.week-switch .month-last-date.mc-range-begin .mc-range-bg{
+  width: 130%;
+  border-radius: 0px 20% 20% 0px;
+}
+.mpvue-calendar .mc-range-mode.week-switch .mc-range-month-last .mc-range-bg{
+  background-color: #01a1ed;
+  border-radius: 0px 20% 20% 0px;
+}
+/*month range*/
+.mpvue-calendar .month-range-mode{
+  border-bottom: 1px solid #f2f2f2;
+  position: relative;
+}
+.mpvue-calendar .mc-month-range-mode-head{
+  box-shadow: 0 4px 8px rgba(25,47,89,.1);
+  padding: 15rpx 0rpx;
+  position: sticky;
+  top: 0px;
+  background-color: #fff;
+  z-index: 9;
+}
+.month-range-mode .month-rang-head {
+  text-align: left;
+  margin: 20rpx 0px;
+  padding-left: 40rpx;
+  font-size: 28rpx;
+}
+.month-range-mode .mc-last-month, .month-range-mode .mc-next-month{
+  opacity: 0 !important;
+}
+.month-text-background{
+  position: absolute;
+  font-size: 140px;
+  width: 100%;
+  height: 100%;
+  text-align: center;
+  line-height: 2.4;
+}

+ 160 - 0
components/numberScroll.vue

@@ -0,0 +1,160 @@
+<template>
+	<view class="number-box">
+		<block v-for="(myIndex, index) in indexArr" :key="index">
+			<swiper class="swiper" vertical="true" :current="myIndex" circular="true"
+				v-bind:style="{color:color,width:myIndex == 10  ? '14rpx' : myIndex == 1 ? '22rpx' : width+'rpx',height:height+'rpx',lineHeight:fontSize+'rpx',fontSize:fontSize+'rpx',fontWeight: fontWeight}">
+				<swiper-item>
+					<view class="swiper-item">0</view>
+				</swiper-item>
+				<swiper-item>
+					<view class="swiper-item">1</view>
+				</swiper-item>
+				<swiper-item>
+					<view class="swiper-item">2</view>
+				</swiper-item>
+				<swiper-item>
+					<view class="swiper-item">3</view>
+				</swiper-item>
+				<swiper-item>
+					<view class="swiper-item">4</view>
+				</swiper-item>
+				<swiper-item>
+					<view class="swiper-item">5</view>
+				</swiper-item>
+				<swiper-item>
+					<view class="swiper-item">6</view>
+				</swiper-item>
+				<swiper-item>
+					<view class="swiper-item">7</view>
+				</swiper-item>
+				<swiper-item>
+					<view class="swiper-item">8</view>
+				</swiper-item>
+				<swiper-item>
+					<view class="swiper-item">9</view>
+				</swiper-item>
+				<swiper-item>
+					<view class="swiper-item">.</view>
+				</swiper-item>
+			</swiper>
+		</block>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			num: [String, Number],
+			color: {
+				type: String,
+				default: '#000000'
+			},
+			width: {
+				type: String,
+				default: '30'
+			},
+			height: {
+				type: String,
+				default: '30'
+			},
+			fontSize: {
+				type: String,
+				default: '30'
+			},
+			fontWeight: {
+				type: [String, Number],
+				default: 500
+			}
+		},
+		data() {
+			return {
+				indexArr: []
+			};
+		},
+		created() {
+			let {
+				num
+			} = this;
+			let arr = new Array(num.toString().length);
+			arr.fill(0);
+			this.indexArr = arr;
+		},
+		watch: {
+			num: function(val, oldVal) {
+				// 处理新老数据长度不一样的情况
+				let arr = Array.prototype.slice.apply(this.indexArr);
+				let newLen = val.toString().length;
+				let oldLen = oldVal.toString().length;
+				if (newLen > oldLen) {
+					for (let i = 0; i < newLen - oldLen; i++) {
+						arr.push(0);
+					}
+					this.indexArr = arr;
+				}
+				if (newLen < oldLen) {
+					for (let i = 0; i < oldLen - newLen; i++) {
+						arr.pop();
+					}
+					this.indexArr = arr;
+				}
+				this.numChange(val);
+			}
+		},
+		mounted() {
+			//定时器作用:app显示数字滚动
+			this._time = setTimeout(() => {
+				this.numChange(this.num);
+				clearTimeout(this._time);
+			}, 50);
+		},
+		methods: {
+			/**
+			 * 数字改变
+			 * @value 数字
+			 */
+			numChange(num) {
+				this.$nextTick(() => {
+					let {
+						indexArr
+					} = this;
+					let copyIndexArr = Array.prototype.slice.apply(indexArr);
+					let _num = num.toString();
+					for (let i = 0; i < _num.length; i++) {
+						if (_num[i] === '.') {
+							copyIndexArr[i] = 10;
+						} else {
+							copyIndexArr[i] = Number(_num[i]);
+						}
+					}
+					this.indexArr = copyIndexArr;
+				})
+			}
+		}
+	};
+</script>
+
+<style lang="scss">
+	.number-box {
+		display: flex;
+		flex-wrap: wrap;
+		justify-content: center;
+	}
+
+	.swiper {
+		position: relative;
+		// 	line-height: 30upx;
+		// 	width: 30upx;
+		// 	height: 30upx;
+		// 	font-size: 30upx;
+		// 	background: red;
+	}
+
+	.swiper:after {
+		content: '';
+		position: absolute;
+		left: 0;
+		top: 0;
+		width: 100%;
+		height: 100%;
+	}
+</style>

+ 136 - 0
components/orderGoods/index.vue

@@ -0,0 +1,136 @@
+<template>
+	<view class="orderGoods borRadius14">
+		<view class='total'>共{{ totalNmu }}件商品</view>
+		<view class='goodWrapper pad30'>
+			<view class='item acea-row row-between-wrapper' v-for="(item,index) in cartInfo" :key="index"
+				@click="jumpCon(item.spuId)">
+				<view class='pictrue'>
+					<image :src='item.picUrl' />
+				</view>
+				<view class='text'>
+					<view class='acea-row row-between-wrapper'>
+						<view class='name line1'>{{ item.spuName }}</view>
+						<view class='num'>x {{ item.count }}</view>
+					</view>
+					<view class='attr line1'>
+            <text v-for="(property, propertyIndex) in item.properties" :key="propertyIndex" style="padding-right: 10rpx;">
+              {{ property.valueName }} 
+            </text>
+          </view>
+					<view class='money font-color'>¥{{ fen2yuan(item.price) }}</view>
+          <!-- 售后状态 -->
+          <!-- TODO 芋艿:这样式不太合理;应该顺着向右对齐 -->
+          <view class="evaluate" style="right: 60px;" v-if="afterSale" @click.stop="afterSaleTap(item)">
+            {{
+              item.afterSaleStatus === 0 ? '申请退款' :
+                item.afterSaleStatus === 10 ? '退款中' : '退款成功'
+            }}
+          </view>
+          <!-- 评价状态 -->
+					<view class='evaluate' v-if='item.commentStatus === false && evaluate === 2' @click.stop="evaluateTap(item)">评价</view>
+					<view class='evaluate' v-else-if="item.replyStatus === true">已评价</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+  import * as Util from '@/utils/util.js';
+	export default {
+		props: {
+			evaluate: {
+				type: Number,
+				default: 0, // 是否开启评价功能 0 - 不开启;2 - 开启
+			},
+      afterSale: { // 是否开启售后功能
+        type: Boolean,
+        default: false,
+      },
+			cartInfo: {
+				type: Array,
+				default: function() {
+					return [];
+				}
+			},
+			orderId: {
+				type: String,
+				default: '',
+			},
+			jump: {
+				type: Boolean,
+				default: false,
+			},
+			productType: {
+				type: Number,
+				default: function() {
+					return 0;
+				}
+			}
+		},
+		data() {
+			return {
+				totalNmu: 0 // 商品数量
+			};
+		},
+		watch: {
+			cartInfo: function(nVal, oVal) {
+				let num = 0
+				nVal.forEach((item, index) => {
+					num += item.count
+				})
+				this.totalNmu =  num
+			}
+		},
+		methods: {
+			evaluateTap(item) {
+				uni.navigateTo({
+					url: "/pages/users/goods_comment_con/index?orderItemId=" + item.id
+				})
+			},
+      afterSaleTap(item) {
+        if (item.afterSaleStatus === 0) {
+          uni.navigateTo({
+            url: "/pages/users/goods_return/index?orderId=" + item.orderId + '&orderItemId=' + item.id
+          })
+          return;
+        }
+        uni.navigateTo({
+          url: "/pages/users/user_return_detail/index?id=" + item.afterSaleId
+        })
+      },
+			jumpCon: function(id) {
+				let type = this.productType === 0 ?'normal':'video'
+				if (this.jump) {
+					uni.navigateTo({
+						url: `/pages/goods_details/index?id=${id}&type=${type}`
+					})
+				}
+			},
+
+      fen2yuan(price) {
+        return Util.fen2yuan(price)
+      },
+		}
+	}
+</script>
+<style scoped lang="scss">
+	.orderGoods {
+		background-color: #fff;
+		margin-top: 15rpx;
+	}
+
+	.orderGoods .total {
+		width: 100%;
+		height: 86rpx;
+		padding: 0 24rpx;
+		border-bottom: 2rpx solid #f0f0f0;
+		font-size: 30rpx;
+		color: #282828;
+		line-height: 86rpx;
+		box-sizing: border-box;
+	}
+
+	.pictrue image {
+		background: #f4f4f4;
+	}
+</style>

+ 1108 - 0
components/order_addcart/index.vue

@@ -0,0 +1,1108 @@
+<template>
+	<view>
+		<view class='shoppingCart copy-data' v-if="showCartList">
+			<!-- <view class='labelNav acea-row row-around'>
+				<view class='item'><text class='iconfont icon-xuanzhong'></text>100%正品保证</view>
+				<view class='item'><text class='iconfont icon-xuanzhong'></text>所有商品精挑细选</view>
+				<view class='item'><text class='iconfont icon-xuanzhong'></text>售后无忧</view>
+			</view>
+			 -->
+			<view class="borRadius14 cartBox">
+				<view
+					v-if="(cartList.valid.length === 0 && cartList.invalid.length === 0) || (cartList.valid.length > 0)"
+					class='nav acea-row row-between-wrapper'>
+					<view>购物数量 <text class='num font-color'>{{cartCount}}</text></view>
+					<view v-if="cartList.valid.length > 0 || cartList.invalid.length > 0"
+						class='administrate acea-row row-center-wrapper' @click='manage'>{{ footerswitch ? '管理' : '取消'}}
+					</view>
+				</view>
+				<view v-if="cartList.valid.length > 0 || cartList.invalid.length > 0" class="pad30">
+					<!-- 有效的购物车 -->
+					<view class='list'>
+						<checkbox-group @change="checkboxChange">
+							<block v-for="(item,index) in cartList.valid" :key="index">
+								<view class='item acea-row row-between-wrapper'>
+									<!-- #ifndef MP -->
+									<checkbox :value="item.id.toString()" :checked="item.selected"
+										:disabled="!item.canChecked && footerswitch" style="margin-right: 10rpx;" />
+									<!-- #endif -->
+									<!-- #ifdef MP -->
+									<checkbox :value="item.id.toString()" :checked="item.selected"
+										:disabled="!item.canChecked && footerswitch" />
+									<!-- #endif -->
+									<navigator :url='"/pages/goods_details/index?id=" + item.spu.id' hover-class='none'
+										class='picTxt acea-row row-between-wrapper'>
+										<view class='pictrue'>
+											<image v-if="item.sku" :src='item.sku.picUrl' />
+											<image v-else :src='item.spu.picUrl' />
+										</view>
+										<view class='text'>
+											<view class='line1' :class="item.canChecked?'':'reColor'">
+												{{ item.spu.name }}
+											</view>
+											<view class='infor line1' v-if="item.sku">属性:
+												<text v-for="property in item.sku.properties"
+													style="padding-left: 2px">{{property.valueName}}</text>
+											</view>
+											<view class='money' v-if="item.canChecked">¥{{ fen2yuan(item.sku.price) }}
+											</view>
+											<view class="reElection acea-row row-between-wrapper" v-else>
+												<view class="title">请重新选择商品规格</view>
+												<view class="reBnt cart-color acea-row row-center-wrapper"
+													@click.stop="reElection(item)">重选</view>
+											</view>
+										</view>
+										<view class='carnum acea-row row-center-wrapper' v-if="item.canChecked">
+											<view class="reduce" :class="item.numSub ? 'on' : ''"
+												@click.stop='subCart(index)'>-</view>
+											<view class='num'>{{ item.count }}</view>
+											<view class="plus" :class="item.numAdd ? 'on' : ''"
+												@click.stop='addCart(index)'>+</view>
+										</view>
+									</navigator>
+								</view>
+							</block>
+						</checkbox-group>
+					</view>
+					<!-- 无效的购物车 -->
+					<view v-if="cartList.invalid.length > 0" class='invalidGoods borRadius14' :style="cartList.valid.length === 0 && cartList.invalid.length > 0
+                  ? 'position: relative;z-index: 111;top: -120rpx;'
+                  :'position: static;'">
+						<view class='goodsNav acea-row row-between-wrapper'>
+							<view v-if="cartList.invalid.length > 1 || cartList.valid.length > 0" @click='goodsOpen'>
+								<text class='iconfont' :class='goodsHidden ? "icon-xiangxia":"icon-xiangshang"' /> 失效商品
+							</view>
+							<view v-else>失效商品</view>
+							<view class='del' @click='unsetCart'>
+								<text class='iconfont icon-shanchu1' />清空
+							</view>
+						</view>
+						<view class='goodsList' :hidden='goodsHidden'>
+							<block v-for="(item,index) in cartList.invalid" :key='index'>
+								<view class='item acea-row row-between-wrapper'>
+									<view class='invalid'>失效</view>
+									<view class='picTxt acea-row row-between-wrapper'>
+										<view class='pictrue'>
+											<image v-if="item.sku" :src='item.sku.picUrl' />
+											<image v-else :src='item.spu.picUrl' />
+										</view>
+										<view class='text acea-row row-column-between'>
+											<view class='line1 name'>{{ item.spu.name }}</view>
+											<view class='infor line1' v-if="item.sku.properties">属性:
+												<text v-for="property in item.sku.properties"
+													style="padding-left: 2px">{{property.valueName}}</text>
+											</view>
+											<view class='acea-row row-between-wrapper'>
+												<view class='end'>该商品已失效</view>
+											</view>
+										</view>
+									</view>
+								</view>
+							</block>
+						</view>
+					</view>
+				</view>
+				<!-- 热门推荐 -->
+				<view class='noCart' v-if="cartList.valid.length === 0 && cartList.invalid.length === 0 && canShow">
+					<view class='pictrue'>
+						<image src='../../static/images/noCart.png' />
+					</view>
+					<recommend :hostProduct='hostProduct'></recommend>
+				</view>
+			</view>
+		</view>
+		<!-- 管理操作 -->
+		<view class='footer acea-row row-between-wrapper' @click="aaa">
+			<view>
+				<checkbox-group @change="checkboxAllChange">
+					<checkbox value="all" :checked="!!isAllSelect" />
+					<text class='checkAll'>全选({{selectValue.length}})</text>
+				</checkbox-group>
+			</view>
+			<view class='money acea-row row-middle' v-if="footerswitch">
+				<text class='font-color'>¥{{ fen2yuan(selectCountPrice) }}</text>
+				<form @submit="subOrder" report-submit='true'>
+					<button class='placeOrder bg-color' formType="submit">立即下单</button>
+				</form>
+			</view>
+			<view class='button acea-row row-middle' v-else>
+				<form @submit="subCollect" report-submit='true'>
+					<button class='bnt cart-color' formType="submit">收藏</button>
+				</form>
+				<form @submit="subDel" report-submit='true'>
+					<button class='bnt' formType="submit">删除</button>
+				</form>
+			</view>
+		</view>
+		<!-- SKU 不可用的商品,重新选择 SKU -->
+		<productWindow :attr="attr" :isShow='1' :iSplus='1' :iScart='1' @ChangeAttr="ChangeAttr"
+			@ChangeCartNum="ChangeCartNum" @iptCartNum="iptCartNum" @close="closeAttr" @goCat="reGoCat" />
+		<!-- <view class="uni-p-b-96" />
+		<view class="uni-p-b-98" /> -->
+	</view>
+</template>
+
+<script>
+	let sysHeight = 0
+	import {
+		toLogin
+	} from '@/libs/login.js';
+	import {
+		mapGetters
+	} from "vuex";
+	import recommend from '@/components/recommend';
+	import productWindow from '@/components/productWindow';
+	import * as TradeCartApi from '@/api/trade/cart.js';
+	import * as ProductSpuApi from '@/api/product/spu.js'
+	import * as ProductFavoriteApi from '@/api/product/favorite.js';
+	import * as Util from '@/utils/util.js';
+	import * as ProductUtil from '@/utils/product.js';
+	import * as PromotionActivityApi from '@/api/promotion/activity.js';
+	export default {
+		components: {
+			recommend,
+			productWindow,
+		},
+		data() {
+			return {
+				sysHeight: sysHeight,
+				goodsHidden: false,
+				footerswitch: true,
+				hostProduct: [],
+				hotPage: 1,
+				hotLimit: 10,
+				hotScroll: false,
+
+				// 购物车列表
+				cartList: { // 购物车列表
+					valid: [], // 有效
+					invalid: [] // 无效
+				},
+				isAllSelect: false, // 是否全选
+				selectValue: [], // 选中的数据
+				selectCountPrice: 0.00, // 选中的金额
+				cartCount: 0, // 选中的商品数量
+				canShow: false, // 是否可展示
+
+				// 重选
+				cartId: 0, // 重选的 cart id
+				product_id: 0, // 重选的 SPU 编号
+				attr: { // productWindow 组件,使用该属性
+					cartAttr: false, // 是否打开属性的选择弹出
+					// ↓↓↓ 属性数组,结构为:id = 属性编号;name = 属性编号的名字;values[].id = 属性值的编号,values[].name = 属性值的名字;index = 选中的属性值的名字
+					properties: [],
+					productSelect: {} // 选中的 SKU
+				},
+				spu: {}, // 商品 SPU 详情
+				skuMap: [], // 商品 SKU Map
+				attrValue: '', // 已选属性名的拼接,例如说 红色,大 这样的格式
+				showCartList: false, //是否显示购物车list
+			};
+		},
+		computed: mapGetters(['isLogin']),
+		created() {
+			console.log('我执行了')
+		},
+		created: function(options) {
+			console.log(222)
+			if (!this.isLogin) {
+				toLogin();
+			}
+		},
+		mounted: function() {
+			console.log(1111)
+			this.canShow = false
+			if (this.isLogin === true) {
+				// 加载热门商品
+				this.hotPage = 1;
+				this.hotLimit = 10;
+				this.hostProduct = [];
+				this.hotScroll = false;
+				this.loadend = false;
+				this.getHostProduct()
+
+				// 加载购物车列表
+				this.footerswitch = true;
+				this.hotScroll = false;
+				this.cartList = {
+					valid: [],
+					invalid: []
+				};
+				this.isAllSelect = false; // 全选
+				this.selectValue = []; // 选中的数据
+				this.selectCountPrice = 0.00;
+				this.cartCount = 0;
+				this.getCartList();
+			}
+		},
+		methods: {
+			/**
+			 * 修改购物车
+			 */
+			reGoCat: function() {
+				const productSelect = this.skuMap[this.attrValue];
+				// 如果有属性,没有选择,提示用户选择
+				if (this.attr.properties.length &&
+					productSelect === undefined) {
+					return this.$util.Tips({
+						title: "产品库存不足,请选择其它"
+					});
+				}
+
+				TradeCartApi.resetCart({
+					id: this.cartId,
+					skuId: this.attr.productSelect.id,
+					count: this.attr.productSelect.cart_num
+				}).then(res => {
+					this.attr.cartAttr = false;
+					this.$util.Tips({
+						title: this.$t(`添加购物车成功`)
+					});
+					this.cartList.valid = [];
+					this.getCartList();
+				}).catch(res => {
+					return this.$util.Tips({
+						title: res
+					});
+				});
+			},
+			aaa() {
+				console.log('我执行了点击btn')
+				this.showCartList = true
+				this.$emit('showMask')
+			},
+			/**
+			 * 重选 SKU
+			 */
+			reElection: function(item) {
+				this.getGoodsDetails(item)
+			},
+			/**
+			 * 获取产品详情
+			 */
+			getGoodsDetails: function(item) {
+				uni.showLoading({
+					title: '加载中',
+					mask: true
+				});
+				this.cartId = item.id;
+				this.product_id = item.spu.id;
+				ProductSpuApi.getSpuDetail(item.spu.id).then(res => {
+					uni.hideLoading();
+					this.attr.cartAttr = true;
+					let spu = res.data;
+					let properties = ProductUtil.convertProductPropertyList(res.data.skus);
+					let skuMap = ProductUtil.convertProductSkuMap(res.data.skus);
+					// 设置变量
+					this.$set(this, 'spu', spu);
+					this.$set(this.attr, 'properties', properties);
+					this.$set(this, 'skuMap', skuMap);
+					this.selectDefaultSku();
+				}).catch(err => {
+					uni.hideLoading();
+				})
+			},
+			/**
+			 * 关闭 productWindow 弹窗
+			 */
+			closeAttr: function() {
+				this.$set(this.attr, "cartAttr", false);
+			},
+			/**
+			 * 属性变动赋值
+			 *
+			 * @param newSkuKey 新的 skuKey
+			 * @param propertyIndex properties 的下标
+			 * @param valueIndex values 的下标
+			 */
+			ChangeAttr: function(newSkuKey, propertyIndex, valueIndex) {
+				// SKU
+				let sku = this.skuMap[newSkuKey];
+				if (!sku) {
+					return;
+				}
+				this.$set(this.attr.productSelect, "id", sku.id);
+				this.$set(this.attr.productSelect, "picUrl", sku.picUrl);
+				this.$set(this.attr.productSelect, "price", sku.price);
+				this.$set(this.attr.productSelect, "stock", sku.stock);
+				this.$set(this.attr.productSelect, "cart_num", 1);
+				// SKU 关联属性
+				this.$set(this.attr.properties[propertyIndex], 'index',
+					this.attr.properties[propertyIndex].values[valueIndex].name);
+				this.$set(this, "attrValue", newSkuKey);
+			},
+			/**
+			 * 查找默认选中的 sku,设置到 attr.productSelect 中
+			 *
+			 * 先找有库存的 SKU,否则找第一个 SKU
+			 */
+			selectDefaultSku: function() {
+				const properties = this.attr.properties;
+				// 获得选中的属性值的名字,例如说 "黑色,大",则 skuKey = ["黑色", "大"]
+				let skuKey = undefined;
+				for (let key in this.skuMap) {
+					if (this.skuMap[key].stock > 0) {
+						skuKey = key.split(",");
+						break;
+					}
+				}
+				if (!skuKey) { // 如果找不到,则选中第一个
+					skuKey = Object.keys(this.skuMap)[0].split(",");
+				}
+				// 使用 index 属性表示当前选中的,值为属性值的名字
+				for (let i = 0; i < properties.length; i++) {
+					this.$set(properties[i], "index", skuKey[i]);
+				}
+
+				let sku = this.skuMap[skuKey.join(",")];
+				if (!sku) {
+					return
+				}
+				this.$set(this.attr.productSelect, "spuName", this.spu.name);
+				this.$set(this.attr.productSelect, "id", sku.id);
+				this.$set(this.attr.productSelect, "picUrl", sku.picUrl);
+				this.$set(this.attr.productSelect, "price", sku.price);
+				this.$set(this.attr.productSelect, "stock", sku.stock);
+				this.$set(this.attr.productSelect, "cart_num", 1);
+				this.$set(this, "attrValue", skuKey.join(","));
+			},
+			/**
+			 * 购物车数量加和数量减
+			 *
+			 * @param changeValue true 增加;false 减少
+			 */
+			ChangeCartNum: function(changeValue) {
+				/// 获取当前 sku
+				let sku = this.attr.productSelect;
+				if (!sku) {
+					return;
+				}
+
+				// 设置数量
+				let stock = sku.stock || 0;
+				if (changeValue) {
+					sku.cart_num++;
+					if (sku.cart_num > stock) {
+						this.$set(this.attr.productSelect, "cart_num", stock);
+					}
+				} else {
+					sku.cart_num--;
+					if (sku.cart_num < 1) {
+						this.$set(this.attr.productSelect, "cart_num", 1);
+					}
+				}
+			},
+			/**
+			 * 购物车手动填写
+			 */
+			iptCartNum: function(number) {
+				this.$set(this.attr.productSelect, 'cart_num', number ? number : 1);
+			},
+			/**
+			 * 删除选中的商品
+			 */
+			subDel: function(event) {
+				const selectValue = this.selectValue;
+				if (selectValue.length === 0) {
+					return this.$util.Tips({
+						title: '请选择产品'
+					});
+				}
+				TradeCartApi.deleteCart(selectValue).then(res => {
+					this.getCartList();
+				});
+			},
+			getSelectValueProductId: function() {
+				const validList = this.cartList.valid;
+				const selectValue = this.selectValue;
+				let productId = [];
+				if (selectValue.length > 0) {
+					for (let index in validList) {
+						if (this.inArray(validList[index].id, selectValue)) {
+							productId.push(validList[index].spu.id);
+						}
+					}
+				}
+				return productId;
+			},
+			/**
+			 * 收藏选中的商品
+			 */
+			subCollect: function(event) {
+				const spuIds = this.getSelectValueProductId();
+				if (spuIds.length === 0) {
+					return this.$util.Tips({
+						title: '请选择产品'
+					});
+				}
+
+				ProductFavoriteApi.createFavoriteList(spuIds).then(res => {
+					return this.$util.Tips({
+						title: '收藏成功',
+						icon: 'success'
+					});
+				}).catch(err => {
+					return that.$util.Tips({
+						title: err
+					});
+				});
+			},
+			/**
+			 * 立即下单
+			 */
+			subOrder: function(event) {
+				const selectValue = this.selectValue;
+				if (selectValue.length === 0) {
+					return this.$util.Tips({
+						title: '请选择产品'
+					});
+				}
+				uni.navigateTo({
+					url: '/pages/users/order_confirm/index?cartIds=' + selectValue.join(',')
+				});
+			},
+			/**
+			 * 预下单
+			 */
+			getPreOrder: function() {
+				let shoppingCartId = this.selectValue.map(item => {
+					return {
+						"shoppingCartId": Number(item)
+					}
+				})
+				this.$Order.getPreOrder("shoppingCart", shoppingCartId);
+			},
+			/**
+			 * 全选 / 全不选
+			 */
+			checkboxAllChange: function(event) {
+				let value = event.detail.value;
+				if (value.length > 0) {
+					this.setAllSelectValue(true)
+				} else {
+					this.setAllSelectValue(false)
+				}
+			},
+			/**
+			 * 全选 / 全不选
+			 */
+			setAllSelectValue: function(selected) {
+				const valid = this.cartList.valid;
+				const values = [];
+				for (const item of valid) {
+					values.push(item.id.toString());
+				}
+				TradeCartApi.updateCartSelected({
+					ids: values,
+					selected: selected
+				}).then(res => {
+					this.getCartList()
+				})
+			},
+			/**
+			 * 更新是否选中
+			 */
+			checkboxChange: function(event) {
+				// 由于 uniapp 不支持直接通过 event 获取到变更的值,所以只能通过比对
+				const valid = this.cartList.valid;
+				const oldValues = [];
+				for (const item of valid) {
+					if (item.canChecked && item.selected) {
+						oldValues.push(item.id.toString());
+					} else if (!this.footerswitch && item.selected) {
+						oldValues.push(item.id.toString());
+					}
+				}
+				const newValues = event.detail.value;
+
+				// 情况一:选中
+				const selectedItem = newValues.find(item => !oldValues.includes(item));
+				if (selectedItem) {
+					TradeCartApi.updateCartSelected({
+						ids: [selectedItem],
+						selected: true
+					}).then(res => {
+						this.getCartList()
+					})
+					return;
+				}
+				// 情况二:取消选中
+				const cancelSelectedItem = oldValues.find(item => !newValues.includes(item));
+				if (cancelSelectedItem) {
+					TradeCartApi.updateCartSelected({
+						ids: [cancelSelectedItem],
+						selected: false
+					}).then(res => {
+						this.getCartList()
+					})
+				}
+			},
+			/**
+			 * 合计金额、数量
+			 */
+			switchSelect: function() {
+				// 是否全选
+				const validList = this.cartList.valid;
+				const selectValue = [];
+				let isAllSelect = true;
+				if (validList && validList.length > 0) {
+					for (const item of validList) {
+						if (item.canChecked) {
+							if (item.selected) {
+								selectValue.push(item.id);
+							} else {
+								isAllSelect = false;
+							}
+						} else {
+							if (!this.footerswitch && item.selected) {
+								selectValue.push(item.id);
+							}
+							if (!this.footerswitch && !item.selected) {
+								isAllSelect = false;
+							}
+						}
+					}
+				}
+				this.selectValue = selectValue;
+				this.isAllSelect = isAllSelect;
+
+				// 合计金额
+				let selectCountPrice = 0.00;
+				let cartCount = 0
+				for (let index in validList) {
+					if (this.inArray(validList[index].id, selectValue) &&
+						validList[index].sku) {
+						selectCountPrice = this.$util.$h.Add(selectCountPrice, this.$util.$h.Mul(validList[index]
+							.count, validList[index].sku.price))
+						cartCount += validList[index].count
+					}
+				}
+				this.selectCountPrice = selectCountPrice;
+				this.cartCount = cartCount;
+			},
+			/**
+			 * 减少购买数量
+			 */
+			subCart: function(index) {
+				let item = this.cartList.valid[index];
+				if (item.count < 1) {
+					this.$util.Tips({
+						title: '商品数量不能减少!'
+					});
+					return;
+				}
+				// 本地先修改状态
+				item.count = Number(item.count) - 1;
+				console.log(item.count,8888)
+				if (item.count <= 1) {
+					item.count = 1;
+					item.numSub = true;
+				} else {
+					item.numSub = false;
+					item.numAdd = false;
+				}
+				// 提交修改
+				this.setCartNum(item.id, item.count);
+			},
+			/**
+			 * 增加购买数量
+			 */
+			addCart: function(index) {
+				// 本地先修改状态
+				let item = this.cartList.valid[index];
+				item.count = Number(item.count) + 1
+				if (item.count >= item.sku.stock) {
+					item.count = item.sku.stock;
+					item.numAdd = true;
+					item.numSub = false;
+				} else {
+					item.numAdd = false;
+					item.numSub = false;
+				}
+				// 提交修改
+				this.setCartNum(item.id, item.count);
+			},
+			/**
+			 * 修改购物项为指定数量
+			 */
+			setCartNum(cartId, cartNum) {
+				TradeCartApi.updateCartCount({
+					id: cartId,
+					count: cartNum
+				}).then(res => {
+					// 加载最新状态
+					this.getCartList()
+				});
+			},
+			getCartData() {
+				return new Promise((resolve, reject) => {
+					TradeCartApi.getCartList().then((res) => {
+						resolve(res.data);
+					}).catch(function(err) {
+						this.canShow = true;
+						this.$util.Tips({
+							title: err
+						});
+					})
+				});
+			},
+			async getCartList() {
+				console.log('我是组件购物车')
+				uni.showLoading({
+					title: '加载中',
+					mask: true
+				});
+				// 加载购物车
+				const cartList = await this.getCartData();
+				const validList = cartList.validList;
+				const invalidList = cartList.invalidList;
+
+				// 有效的购物车
+				if (validList.length > 0) {
+					for (let index in validList) {
+						// 设置是否可减少(到底,不可减少)
+						validList[index].numSub = validList[index].cartNum === 1;
+						// 设置是否可添加(到顶,不可添加)
+						const sku = validList[index].sku;
+						validList[index].numAdd = sku && validList[index].count < sku.stock;
+						// 设置为选中,并添加到 selectValue 数组中
+						// why?库存不足时,可以引导选择该 SPU 对应的其它 SKU。而 invalidList 是 SPU 不存在或者库存彻底不足
+						if (sku && sku.stock > 0) {
+							validList[index].canChecked = true; // 是否可选中:是
+						} else {
+							validList[index].canChecked = false; // 是否可选中:否
+						}
+					}
+				}
+				this.$Cache
+				this.$emit('cartList', validList);
+				this.$set(this.cartList, 'valid', validList);
+				console.log(this.cartList,888)
+				this.switchSelect();
+
+				// 无效的购物车
+				this.$set(this.cartList, 'invalid', invalidList);
+
+				// 标记加载结束
+				this.canShow = true;
+				uni.hideLoading();
+			},
+
+			/**
+			 * 获得“热门推荐”
+			 */
+			getHostProduct: function() {
+				ProductSpuApi.getSpuPage({
+					recommendType: 'hot',
+					pageNo: this.hotPage,
+					pageSize: this.hotLimit
+				}).then(res => {
+					const good_list = res.data.list;
+					this.hotPage++
+					this.hotScroll = good_list.length < this.hotLimit
+
+					// 设置营销活动
+					const spuIds = good_list.map(item => item.id);
+					if (spuIds.length > 0) {
+						PromotionActivityApi.getActivityListBySpuIds(spuIds).then(res => {
+							ProductUtil.setActivityList(good_list, res.data);
+							this.hostProduct = this.hostProduct.concat(
+								good_list) // 放在此处,避免 Vue 监控不到数组里的元素变化
+						});
+					}
+				});
+			},
+			goodsOpen: function() {
+				this.goodsHidden = !this.goodsHidden;
+			},
+			/**
+			 * 切换到管理
+			 */
+			manage: function() {
+				this.footerswitch = !this.footerswitch;
+				this.switchSelect();
+			},
+			/**
+			 * 清空
+			 */
+			unsetCart: function() {
+				const ids = [];
+				for (let i = 0, len = this.cartList.invalid.length; i < len; i++) {
+					ids.push(this.cartList.invalid[i].id);
+				}
+				TradeCartApi.deleteCart(ids).then(res => {
+					this.$util.Tips({
+						title: '清除成功'
+					});
+					this.getCartList()
+				}).catch(res => {});
+			},
+
+			fen2yuan(price) {
+				return Util.fen2yuan(price)
+			},
+			inArray: function(search, array) {
+				for (let i in array) {
+					if (array[i] === search) {
+						return true;
+					}
+				}
+				return false;
+			},
+		},
+		onReachBottom() {
+			if (this.cartList.valid.length === 0 &&
+				this.cartList.invalid.length === 0 &&
+				this.hotPage != 1) {
+				this.getHostProduct();
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.invalidClas {
+		position: relative;
+		z-index: 111;
+		top: -120rpx;
+	}
+
+	.invalidClasNO {
+		position: static;
+		margin-top: 15px;
+	}
+
+	.cartBox {
+		// background-color: #fff;
+	}
+
+	.shoppingCart {
+		/* #ifdef H5 */
+		// padding-bottom: 0;
+		// padding-bottom: constant(safe-area-inset-bottom);
+		// padding-bottom: env(safe-area-inset-bottom);
+		/* #endif */
+	}
+
+	.shoppingCart .labelNav {
+		height: 178rpx;
+		padding: 30rpx 30rpx 0 30rpx;
+		font-size: 22rpx;
+		color: #fff;
+		position: fixed;
+		left: 0;
+		width: 100%;
+		box-sizing: border-box;
+		background-color: $theme-color;
+		z-index: 5;
+		top: 0;
+	}
+
+	.shoppingCart .labelNav .item .iconfont {
+		font-size: 25rpx;
+		margin-right: 10rpx;
+	}
+
+	.shoppingCart .nav {
+		width: 92%;
+		height: 90rpx;
+		background-color: #fff;
+		padding: 0 24rpx;
+		-webkit-box-sizing: border-box;
+		box-sizing: border-box;
+		font-size: 28rpx;
+		color: #282828;
+		// position: fixed;
+		left: 30rpx;
+		z-index: 6;
+		top: 94rpx;
+		border-top-left-radius: 14rpx;
+		border-top-right-radius: 14rpx;
+	}
+
+	.shoppingCart .nav .num {
+		margin-left: 12rpx;
+	}
+
+	.shoppingCart .nav .administrate {
+		font-size: 28rpx;
+		color: #333333;
+	}
+
+	.shoppingCart .noCart {
+		margin-top: 171rpx;
+		background-color: #fff;
+		padding-top: 0.1rpx;
+	}
+
+	.shoppingCart .noCart .pictrue {
+		width: 414rpx;
+		height: 336rpx;
+		margin: 78rpx auto 56rpx auto;
+	}
+
+	.shoppingCart .noCart .pictrue image {
+		width: 100%;
+		height: 100%;
+	}
+
+	.shoppingCart .list {
+		width: 100%;
+		// margin-top: 178rpx;
+		overflow: hidden;
+		border-bottom-left-radius: 14rpx;
+		border-bottom-right-radius: 14rpx;
+	}
+
+	.shoppingCart .list .item {
+		padding: 24rpx;
+		background-color: #fff;
+	}
+
+	.shoppingCart .list .item .picTxt {
+		width: 582rpx;
+		position: relative;
+	}
+
+	.shoppingCart .list .item .picTxt .pictrue {
+		width: 160rpx;
+		height: 160rpx;
+	}
+
+	.shoppingCart .list .item .picTxt .pictrue image {
+		width: 100%;
+		height: 100%;
+		border-radius: 6rpx;
+	}
+
+	.shoppingCart .list .item .picTxt .text {
+		width: 396rpx;
+		font-size: 28rpx;
+		color: #282828;
+	}
+
+	.shoppingCart .list .item .picTxt .text .reColor {
+		color: #999;
+	}
+
+	.shoppingCart .list .item .picTxt .text .reElection {
+		margin-top: 20rpx;
+	}
+
+	.shoppingCart .list .item .picTxt .text .reElection .title {
+		font-size: 24rpx;
+	}
+
+	.shoppingCart .list .item .picTxt .text .reElection .reBnt {
+		width: 120rpx;
+		height: 46rpx;
+		border-radius: 23rpx;
+		font-size: 26rpx;
+	}
+
+	.shoppingCart .list .item .picTxt .text .infor {
+		font-size: 24rpx;
+		color: #999999;
+		margin-top: 16rpx;
+	}
+
+	.shoppingCart .list .item .picTxt .text .money {
+		font-size: 32rpx;
+		color: #E93323;
+		margin-top: 28rpx;
+		font-weight: 600;
+	}
+
+	.shoppingCart .list .item .picTxt .carnum {
+		height: 47rpx;
+		position: absolute;
+		bottom: 7rpx;
+		right: 0;
+	}
+
+	.shoppingCart .list .item .picTxt .carnum view {
+		border: 1rpx solid #a4a4a4;
+		width: 66rpx;
+		text-align: center;
+		height: 100%;
+		line-height: 44rpx;
+		font-size: 28rpx;
+		color: #a4a4a4;
+	}
+
+	.shoppingCart .list .item .picTxt .carnum .reduce {
+		border-right: 0;
+		border-radius: 3rpx 0 0 3rpx;
+		border-radius: 22rpx 0rpx 0rpx 22rpx;
+		font-size: 34rpx;
+		line-height: 40rpx;
+	}
+
+	.shoppingCart .list .item .picTxt .carnum .reduce.on {
+		border-color: #e3e3e3;
+		color: #dedede;
+	}
+
+	.shoppingCart .list .item .picTxt .carnum .plus {
+		border-left: 0;
+		border-radius: 0 3rpx 3rpx 0;
+		border-radius: 0rpx 22rpx 22rpx 0rpx;
+		font-size: 34rpx;
+		line-height: 40rpx;
+	}
+
+	.shoppingCart .list .item .picTxt .carnum .num {
+		color: #282828;
+	}
+
+	.shoppingCart .invalidGoods {
+		background-color: #fff;
+		margin-top: 30rpx;
+		/* #ifdef MP */
+		margin-top: 140rpx;
+		/* #endif */
+
+	}
+
+	.shoppingCart .invalidGoods .goodsNav {
+		width: 100%;
+		height: 90rpx;
+		padding: 0 24rpx;
+		box-sizing: border-box;
+		font-size: 28rpx;
+		color: #333333;
+	}
+
+	.shoppingCart .invalidGoods .goodsNav .iconfont {
+		color: #424242;
+		font-size: 28rpx;
+		margin-right: 17rpx;
+	}
+
+	.shoppingCart .invalidGoods .goodsNav .del {
+		font-size: 26rpx;
+		color: #333;
+	}
+
+	.shoppingCart .invalidGoods .goodsNav .del .icon-shanchu1 {
+		color: #333;
+		font-size: 33rpx;
+		vertical-align: -2rpx;
+		margin-right: 8rpx;
+	}
+
+	.shoppingCart .invalidGoods .goodsList .item {
+		padding: 24rpx;
+	}
+
+	.shoppingCart .invalidGoods .goodsList .picTxt {
+		width: 576rpx;
+	}
+
+	.shoppingCart .invalidGoods .goodsList .item .invalid {
+		font-size: 22rpx;
+		color: #CCCCCC;
+		height: 36rpx;
+		border-radius: 3rpx;
+		text-align: center;
+		line-height: 36rpx;
+	}
+
+	.shoppingCart .invalidGoods .goodsList .item .pictrue {
+		width: 160rpx;
+		height: 160rpx;
+	}
+
+	.shoppingCart .invalidGoods .goodsList .item .pictrue image {
+		width: 100%;
+		height: 100%;
+		border-radius: 6rpx;
+	}
+
+	.shoppingCart .invalidGoods .goodsList .item .text {
+		width: 396rpx;
+		font-size: 28rpx;
+		color: #999;
+		height: 140rpx;
+	}
+
+	.shoppingCart .invalidGoods .goodsList .item .text .name {
+		width: 100%;
+	}
+
+	.shoppingCart .invalidGoods .goodsList .item .text .infor {
+		font-size: 24rpx;
+	}
+
+	.shoppingCart .invalidGoods .goodsList .item .text .end {
+		font-size: 26rpx;
+		color: #bbb;
+	}
+
+	.footer {
+		z-index: 9;
+		width: 100%;
+		height: 100rpx;
+		background-color: #fff;
+		position: fixed;
+		padding: 0 24rpx;
+		box-sizing: border-box;
+		border-top: 1rpx solid #eee;
+		// border-bottom: 1px solid #EEEEEE;
+		/* #ifdef H5 */
+		bottom: 98rpx;
+		/* #endif */
+		/* #ifdef MP */
+		bottom: 0;
+		/* #endif */
+		/* #ifndef MP */
+		// bottom: 98rpx;
+		// bottom: calc(98rpx+ constant(safe-area-inset-bottom)); ///兼容 IOS<11.2/
+		// bottom: calc(98rpx + env(safe-area-inset-bottom)); ///兼容 IOS>11.2/
+		/* #endif */
+	}
+
+	.footer .checkAll {
+		font-size: 28rpx;
+		color: #282828;
+		margin-left: 14rpx;
+	}
+
+	.footer .money {
+		font-size: 30rpx;
+
+		.font-color {
+			font-weight: 600;
+		}
+	}
+
+	.footer .placeOrder {
+		color: #fff;
+		font-size: 30rpx;
+		width: 226rpx;
+		height: 70rpx;
+		border-radius: 50rpx;
+		text-align: center;
+		line-height: 70rpx;
+		margin-left: 22rpx;
+	}
+
+	.footer .button .bnt {
+		font-size: 28rpx;
+		color: #999;
+		border-radius: 50rpx;
+		border: 1px solid #999;
+		width: 160rpx;
+		height: 60rpx;
+		text-align: center;
+		line-height: 60rpx;
+	}
+
+	.footer .button form~form {
+		margin-left: 17rpx;
+	}
+
+	.uni-p-b-96 {
+		height: 96rpx;
+	}
+</style>

+ 314 - 0
components/payment/index.vue

@@ -0,0 +1,314 @@
+<template>
+	<view>
+		<view class="payment" :class="pay_close ? 'on' : ''">
+			<view class="title acea-row row-center-wrapper">
+				选择付款方式<text class="iconfont icon-guanbi" @click='close'></text>
+			</view>
+			<view class="item acea-row row-between-wrapper" @click='goPay(item.number || 0 , item.value)'
+				v-for="(item,index) in payMode" :key="index">
+				<view class="left acea-row row-between-wrapper">
+					<view class="iconfont" :class="item.icon"></view>
+					<view class="text">
+						<view class="name">{{item.name}}</view>
+						<view class="info" v-if="item.number">
+							{{item.title}} <span class="money">¥{{ item.number }}</span>
+						</view>
+						<view class="info" v-else>{{item.title}}</view>
+					</view>
+				</view>
+				<view class="iconfont icon-xiangyou"></view>
+			</view>
+		</view>
+		<view class="mask" @click='close' v-if="pay_close"></view>
+	</view>
+</template>
+
+<script>
+	import {
+		orderPay,
+		wechatOrderPay,
+		wechatQueryPayResult
+	} from '@/api/order.js';
+	import {
+		mapGetters
+	} from "vuex";
+	export default {
+		props: {
+			payMode: {
+				type: Array,
+				default: function() {
+					return [];
+				}
+			},
+			pay_close: {
+				type: Boolean,
+				default: false,
+			},
+			order_id: {
+				type: String,
+				default: ''
+			},
+			totalPrice: {
+				type: String,
+				default: '0'
+			}
+		},
+		data() {
+			return {
+
+			};
+		},
+		computed: mapGetters(['systemPlatform']),
+		methods: {
+			close: function() {
+				this.$emit('onChangeFun', {
+					action: 'payClose'
+				});
+			},
+			goPay: function(number, paytype) {
+				let that = this;
+				let goPages = '/pages/order_pay_status/index?order_id=' + that.order_id;
+				if (!that.order_id) return that.$util.Tips({
+					title: '请选择要支付的订单'
+				});
+				if (paytype == 'yue' && parseFloat(number) < parseFloat(that.totalPrice)) return that.$util.Tips({
+					title: '余额不足!'
+				});
+				uni.showLoading({
+					title: '支付中'
+				});
+				wechatOrderPay({
+					orderNo: that.order_id,
+					// #ifdef MP
+					payChannel: 'routine',
+					// #endif
+					// #ifdef H5
+					payChannel: that.$wechat.isWeixin() ? 'public' : 'weixinh5',
+					// #endif
+					payType: paytype
+				}).then(res => {
+					let jsConfig = res.data.jsConfig;
+					that.order_id = res.data.orderNo;
+					switch (res.data.payType) {
+						case 'weixin':
+							// #ifdef MP
+							uni.requestPayment({
+								timeStamp: jsConfig.timeStamp,
+								nonceStr: jsConfig.nonceStr,
+								package: jsConfig.packages,
+								signType: jsConfig.signType,
+								paySign: jsConfig.paySign,
+								success: function(ress) {
+									uni.hideLoading();
+									wechatQueryPayResult(that.order_id).then(res => {
+										uni.hideLoading();
+										return that.$util.Tips({
+											title: "支付成功",
+											icon: 'success'
+										}, () => {
+											that.$emit('onChangeFun', {
+												action: 'pay_complete'
+											});
+										});
+									}).cache(err => {
+										uni.hideLoading();
+										return that.$util.Tips({
+											title: err
+										});
+									})
+								},
+								fail: function(e) {
+									uni.hideLoading();
+									return that.$util.Tips({
+										title: '取消支付'
+									}, () => {
+										that.$emit('onChangeFun', {
+											action: 'pay_fail'
+										});
+									});
+								},
+								complete: function(e) {
+									uni.hideLoading();
+									if (e.errMsg == 'requestPayment:cancel') return that.$util
+										.Tips({
+											title: '取消支付'
+										}, () => {
+											that.$emit('onChangeFun', {
+												action: 'pay_fail'
+											});
+										});
+								},
+							})
+							// #endif
+							// #ifdef H5
+							let datas = {
+								timestamp: jsConfig.timeStamp,
+								nonceStr: jsConfig.nonceStr,
+								package: jsConfig.packages,
+								signType: jsConfig.signType,
+								paySign: jsConfig.paySign
+							};
+							that.$wechat.pay(datas).then(res => {
+								if (res.errMsg == 'chooseWXPay:cancel') {
+									uni.hideLoading();
+									return that.$util.Tips({
+										title: '支付失败'
+									});
+								} else {
+									wechatQueryPayResult(that.order_id).then(res => {
+										uni.hideLoading();
+										return that.$util.Tips({
+											title: "支付成功",
+											icon: 'success'
+										}, () => {
+											that.$emit('onChangeFun', {
+												action: 'pay_complete'
+											});
+										});
+									}).cache(err => {
+										uni.hideLoading();
+										return that.$util.Tips({
+											title: err
+										});
+									})
+								}
+
+							}).cache(errW => {
+								uni.hideLoading();
+								return that.$util.Tips({
+									title: errW
+								});
+							})
+							// #endif
+							break;
+						case 'yue':
+							uni.hideLoading();
+							return that.$util.Tips({
+								title: '余额支付成功',
+								icon: 'success'
+							}, () => {
+								that.$emit('onChangeFun', {
+									action: 'pay_complete'
+								});
+							});
+							break;
+						case 'weixinh5':
+							uni.hideLoading();
+							location.replace(jsConfig.mwebUrl + '&redirect_url=' + window.location.protocol +
+								'//' + window.location.host + goPages + '&status=1');
+							return that.$util.Tips({
+								title: "支付中",
+								icon: 'success'
+							}, () => {
+								that.$emit('onChangeFun', {
+									action: 'pay_complete'
+								});
+							});
+							break;
+					}
+				}).catch(err => {
+					uni.hideLoading();
+					return that.$util.Tips({
+						title: err
+					}, () => {
+						that.$emit('onChangeFun', {
+							action: 'pay_fail'
+						});
+					});
+				})
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.payment {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		width: 100%;
+		border-radius: 16rpx 16rpx 0 0;
+		background-color: #fff;
+		padding-bottom: 60rpx;
+		z-index: 99;
+		transition: all 0.3s cubic-bezier(0.25, 0.5, 0.5, 0.9);
+		transform: translate3d(0, 100%, 0);
+	}
+
+	.payment.on {
+		transform: translate3d(0, 0, 0);
+	}
+
+	.payment .title {
+		text-align: center;
+		height: 123rpx;
+		font-size: 32rpx;
+		color: #282828;
+		font-weight: bold;
+		padding-right: 30rpx;
+		margin-left: 30rpx;
+		position: relative;
+		border-bottom: 1rpx solid #eee;
+	}
+
+	.payment .title .iconfont {
+		position: absolute;
+		right: 30rpx;
+		top: 50%;
+		transform: translateY(-50%);
+		font-size: 43rpx;
+		color: #8a8a8a;
+		font-weight: normal;
+	}
+
+	.payment .item {
+		border-bottom: 1rpx solid #eee;
+		height: 130rpx;
+		margin-left: 30rpx;
+		padding-right: 30rpx;
+	}
+
+	.payment .item .left {
+		width: 610rpx;
+	}
+
+	.payment .item .left .text {
+		width: 540rpx;
+	}
+
+	.payment .item .left .text .name {
+		font-size: 32rpx;
+		color: #282828;
+	}
+
+	.payment .item .left .text .info {
+		font-size: 24rpx;
+		color: #999;
+	}
+
+	.payment .item .left .text .info .money {
+		color: #ff9900;
+	}
+
+	.payment .item .left .iconfont {
+		font-size: 45rpx;
+		color: #09bb07;
+	}
+
+	.payment .item .left .iconfont.icon-zhifubao {
+		color: #00aaea;
+	}
+
+	.payment .item .left .iconfont.icon-yuezhifu {
+		color: #ff9900;
+	}
+
+	.payment .item .left .iconfont.icon-yuezhifu1 {
+		color: #eb6623;
+	}
+
+	.payment .item .iconfont {
+		font-size: 0.3rpx;
+		color: #999;
+	}
+</style>

+ 151 - 0
components/productConSwiper/index.vue

@@ -0,0 +1,151 @@
+<template>
+	<view class='product-bg'>
+		<swiper :indicator-dots="indicatorDots" indicator-active-color="#e93323" :autoplay="autoplay"
+			:circular="circular" :interval="interval" :duration="duration" @change="change">
+
+      <!-- 视频 -->
+			<swiper-item v-if="videoline">
+				<view class="item">
+					<view v-show="!controls" style="width:100%;height:100% ">
+						<video id="myVideo" :src='videoline' objectFit="cover" controls style="width:100%;height:100% "
+							show-center-play-btn show-mute-btn="true" auto-pause-if-navigate :custom-cache="false"
+							:enable-progress-gesture="false" :poster="imgUrls[0]" @pause="videoPause"></video>
+					</view>
+					<view class="poster" v-show="controls">
+						<image class="image" :src="imgUrls[0]"></image>
+					</view>
+					<view class="stop" v-show="controls" @tap="bindPause">
+						<image class="image" src="../../static/images/stop.png"></image>
+					</view>
+				</view>
+			</swiper-item>
+
+      <!-- 图片 -->
+      <block v-for="(item,index) in imgUrls" :key='index'>
+				<swiper-item>
+					<image :src="item" class="slide-image" />
+				</swiper-item>
+			</block>
+		</swiper>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			imgUrls: {
+				type: Array,
+				default: function() {
+					return [];
+				}
+			},
+			videoline: {
+				type: String,
+				value: ""
+			}
+		},
+		data() {
+			return {
+				indicatorDots: true,
+				circular: true,
+				autoplay: true,
+				interval: 3000,
+				duration: 500,
+				currents: "1",
+				controls: true,
+				isPlay:true,
+				videoContext:''
+			};
+		},
+		mounted() {
+			if (this.videoline) {
+				this.imgUrls.shift()
+			}
+		},
+		methods: {
+			videoPause(e){
+			},
+			bindPause: function() {
+				this.videoContext.play();
+				this.$set(this, 'controls', false)
+				this.autoplay = false
+			},
+			change: function(e) {
+				this.$set(this, 'currents', e.detail.current + 1);
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.product-bg {
+		width: 100%;
+		height: 975rpx;
+		position: relative;
+	}
+
+	.product-bg swiper {
+		width: 100%;
+		height: 100%;
+		position: relative;
+	}
+
+	.product-bg .slide-image {
+		width: 100%;
+		height: 100%;
+	}
+
+	.product-bg .pages {
+		position: absolute;
+		background-color: #fff;
+		height: 34rpx;
+		padding: 0 10rpx;
+		border-radius: 3rpx;
+		right: 30rpx;
+		bottom: 30rpx;
+		line-height: 34rpx;
+		font-size: 24rpx;
+		color: #050505;
+	}
+
+	#myVideo {
+		width: 100%;
+		height: 100%
+	}
+
+	.product-bg .item {
+		position: relative;
+		width: 100%;
+		height: 100%;
+	}
+
+	.product-bg .item .poster {
+		position: absolute;
+		top: 0;
+		left: 0;
+		height: 750rpx;
+		width: 100%;
+		z-index: 9;
+	}
+
+	.product-bg .item .poster .image {
+		width: 100%;
+		height: 100%;
+	}
+
+	.product-bg .item .stop {
+		position: absolute;
+		top: 50%;
+		left: 50%;
+		width: 136rpx;
+		height: 136rpx;
+		margin-top: -68rpx;
+		margin-left: -68rpx;
+		z-index: 9;
+	}
+
+	.product-bg .item .stop .image {
+		width: 100%;
+		height: 100%;
+	}
+</style>

+ 350 - 0
components/productWindow/index.vue

@@ -0,0 +1,350 @@
+<template>
+	<view>
+		<view class="product-window"
+			:class="(attr.cartAttr === true ? 'on' : '') + ' ' + (iSbnt ? 'join':'') + ' ' + (iScart ? 'joinCart':'')">
+			<view class="textpic acea-row row-between-wrapper">
+				<view class="pictrue">
+					<image :src="attr.productSelect.picUrl"></image>
+				</view>
+				<view class="text">
+					<view class="line1">
+						{{ attr.productSelect.spuName }}
+					</view>
+					<view class="money font-color">
+						¥<text class="num">{{ fen2yuan(attr.productSelect.price) }}</text>
+						<text class="stock" v-if='isShow'>库存: {{ attr.productSelect.stock }}</text>
+						<text class='stock' v-if="attr.productSelect.limitCount > 0">限购: {{ attr.productSelect.limitCount }}</text>
+					</view>
+				</view>
+				<view class="iconfont icon-guanbi" @click="close"></view>
+			</view>
+			<view class="rollTop">
+        <!-- 属性 key + value 列表 -->
+				<view class="productWinList">
+					<view class="item" v-for="(property, propertyIndex) in attr.properties" :key="propertyIndex">
+						<view class="title">{{ property.name }}</view>
+						<view class="listn acea-row row-middle">
+							<view class="itemn" :class="property.index === value.name ? 'on' : ''"
+                    v-for="(value, valueIndex) in property.values" :key="valueIndex"
+                    @click="clickProperty(propertyIndex, valueIndex)">
+								{{ value.name }}
+							</view>
+						</view>
+					</view>
+				</view>
+				<view class="cart acea-row row-between-wrapper">
+					<view class="title">数量</view>
+					<view class="carnum acea-row row-left">
+						<view class="item reduce" :class="attr.productSelect.cart_num <= 1 ? 'on' : ''"
+							@click="CartNumDes">
+							-
+						</view>
+						<view class='item num'>
+							<input type="number" v-model="attr.productSelect.cart_num"
+								data-name="productSelect.cart_num"
+								@input="bindCode(attr.productSelect.cart_num)" />
+						</view>
+						<view v-if="iSplus" class="item plus" :class="attr.productSelect.cart_num >= attr.productSelect.stock ? 'on'  : ''" @click="CartNumAdd">
+							+
+						</view>
+						<view v-else class='item plus'
+							:class='(attr.productSelect.cart_num >= attr.productSelect.stock)
+							|| (attr.productSelect.cart_num >= attr.productSelect.limitCount)
+							? "on":""'
+							@click='CartNumAdd'>+</view>
+					</view>
+				</view>
+			</view>
+      <!-- TODO 芋艿:拼团 -->
+			<view class="joinBnt bg-color" v-if="iSbnt && attr.productSelect.stock > 0"
+            @click="goCat">我要参团</view>
+			<view class="joinBnt on"
+            v-else-if="iSbnt && attr.productSelect.stock <= 0">已售罄</view>
+			<!-- TODO 芋艿:购物车 -->
+      <view class="joinBnt bg-color" v-if="iScart && attr.productSelect.stock"
+            @click="goCat">确定</view>
+			<view class="joinBnt on"
+            v-else-if="(iScart && !attr.productSelect.stock)">已售罄</view>
+		</view>
+		<view class="mask" @touchmove.prevent :hidden="attr.cartAttr === false" @click="close"></view>
+	</view>
+</template>
+
+<script>
+  import * as Util from '@/utils/util.js';
+	export default {
+		props: {
+			attr: {
+				type: Object,
+				default: () => {}
+			},
+			isShow: { // 是否展示库存
+				type: Number,
+				value: 0
+			},
+			iSbnt: { // 是否拼团商品
+				type: Number,
+				value: 0
+			},
+			iSplus: { // 是否允许 + - 操作
+				type: Number,
+				value: 0
+			},
+			iScart: { // 是否来自购物车界面
+				type: Number,
+				value: 0
+			}
+		},
+		data() {
+			return {};
+		},
+		mounted() {},
+		methods: {
+			goCat: function() {
+				this.$emit('goCat');
+			},
+			/**
+			 * 购物车手动输入数量
+			 */
+			bindCode: function(e) {
+        // TODO 芋艿:【优化】方法名的处理;是否可去掉这个事件,统一处理
+				this.$emit('iptCartNum', this.attr.productSelect.cart_num);
+			},
+			close: function() {
+        this.$emit('close');
+      },
+			CartNumDes: function() {
+        // TODO 芋艿:【优化】方法名的处理;是否可去掉这个事件,统一处理
+				this.$emit('ChangeCartNum', false);
+			},
+			CartNumAdd: function() {
+        // TODO 芋艿:【优化】方法名的处理;是否可去掉这个事件,统一处理
+				this.$emit('ChangeCartNum', true);
+			},
+      /**
+       * 选中某个规格属性
+       *
+       * @param propertyIndex properties 的下标
+       * @param valueIndex values 的下标
+       */
+			clickProperty: function(propertyIndex, valueIndex) {
+        this.$set(this.attr.properties[propertyIndex], 'index', this.attr.properties[propertyIndex].values[valueIndex].name);
+				let newSkuKey = this.getCheckedValueNames().join(",");
+        // TODO 芋艿:【优化】修改下 ChangeAttr 名字,改成 selectSku 更合适
+        this.$emit("ChangeAttr", newSkuKey, propertyIndex, valueIndex);
+			},
+      /**
+       * 获取被选中属性值的数组
+       *
+       * @returns {*[]} 例如说,['红色', '大']
+       */
+			getCheckedValueNames: function() {
+				let properties = this.attr.properties;
+				let valueNames = [];
+				for (let i = 0; i < properties.length; i++) {
+					for (let j = 0; j < properties[i].values.length; j++) {
+						if (properties[i].index === properties[i].values[j].name) {
+							valueNames.push(properties[i].values[j].name);
+						}
+					}
+				}
+				return valueNames;
+			},
+
+      fen2yuan(price) {
+        return Util.fen2yuan(price)
+      }
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.product-window {
+		position: fixed;
+		bottom: 0;
+		width: 100%;
+		left: 0;
+		background-color: #fff;
+		z-index: 77;
+		border-radius: 16rpx 16rpx 0 0;
+		padding-bottom: 140rpx;
+		transform: translate3d(0, 100%, 0);
+		transition: all .3s cubic-bezier(.25, .5, .5, .9);
+	}
+
+	.product-window.on {
+		transform: translate3d(0, 0, 0);
+	}
+
+	.product-window.join {
+		padding-bottom: 30rpx;
+	}
+
+	.product-window.joinCart {
+		padding-bottom: 30rpx;
+		z-index: 999;
+	}
+
+	.product-window .textpic {
+		padding: 0 130rpx 0 30rpx;
+		margin-top: 29rpx;
+		position: relative;
+	}
+
+	.product-window .textpic .pictrue {
+		width: 150rpx;
+		height: 150rpx;
+	}
+
+	.product-window .textpic .pictrue image {
+		width: 100%;
+		height: 100%;
+		border-radius: 10rpx;
+	}
+
+	.product-window .textpic .text {
+		width: 410rpx;
+		font-size: 32rpx;
+		color: #333333;
+	}
+
+	.product-window .textpic .text .money {
+		font-size: 24rpx;
+		margin-top: 40rpx;
+	}
+
+	.product-window .textpic .text .money .num {
+		font-size: 36rpx;
+	}
+
+	.product-window .textpic .text .money .stock {
+		color: #999;
+		margin-left: 18rpx;
+	}
+
+	.product-window .textpic .iconfont {
+		position: absolute;
+		right: 30rpx;
+		top: -5rpx;
+		font-size: 35rpx;
+		color: #8a8a8a;
+	}
+
+	.product-window .rollTop {
+		max-height: 500rpx;
+		overflow: auto;
+		margin-top: 36rpx;
+	}
+
+	.product-window .productWinList .item~.item {
+		margin-top: 36rpx;
+	}
+
+	.product-window .productWinList .item .title {
+		font-size: 30rpx;
+		color: #999;
+		padding: 0 30rpx;
+	}
+
+	.product-window .productWinList .item .listn {
+		padding: 0 30rpx 0 16rpx;
+	}
+
+	.product-window .productWinList .item .listn .itemn {
+		border: 1px solid #F2F2F2;
+		font-size: 26rpx;
+		color: #282828;
+		padding: 7rpx 33rpx;
+		border-radius: 40rpx;
+		margin: 20rpx 0 0 14rpx;
+		background-color: #F2F2F2;
+	}
+
+	.product-window .productWinList .item .listn .itemn.on {
+		color: $theme-color;
+		background: rgba(255, 244, 243, 1);
+		border-color: $theme-color;
+	}
+
+	.product-window .productWinList .item .listn .itemn.limit {
+		color: #999;
+		text-decoration: line-through;
+	}
+
+	.product-window .cart {
+		margin-top: 36rpx;
+		padding: 0 30rpx;
+	}
+
+	.product-window .cart .title {
+		font-size: 30rpx;
+		color: #999;
+	}
+
+	.product-window .cart .carnum {
+		height: 54rpx;
+		margin-top: 24rpx;
+	}
+
+	.product-window .cart .carnum view {
+		// border: 1px solid #a4a4a4;
+		width: 84rpx;
+		text-align: center;
+		height: 100%;
+		line-height: 54rpx;
+		color: #282828;
+		font-size: 45rpx;
+	}
+
+	.product-window .cart .carnum .reduce {
+		border-right: 0;
+		border-radius: 6rpx 0 0 6rpx;
+		line-height: 48rpx;
+	}
+
+	.product-window .cart .carnum .reduce.on {
+		// border-color: #e3e3e3;
+		color: #DEDEDE;
+		font-size: 44rpx;
+	}
+
+	.product-window .cart .carnum .plus {
+		border-left: 0;
+		border-radius: 0 6rpx 6rpx 0;
+		line-height: 46rpx;
+	}
+
+	.product-window .cart .carnum .plus.on {
+		border-color: #e3e3e3;
+		color: #dedede;
+	}
+
+	.product-window .cart .carnum .num {
+		background: rgba(242, 242, 242, 1);
+		color: #282828;
+		font-size: 28rpx;
+		border-radius: 12rpx;
+		line-height: 29px;
+		height: 54rpx;
+
+		input {
+			display: -webkit-inline-box;
+		}
+	}
+
+	.product-window .joinBnt {
+		font-size: 30rpx;
+		width: 620rpx;
+		height: 86rpx;
+		border-radius: 50rpx;
+		text-align: center;
+		line-height: 86rpx;
+		color: #fff;
+		margin: 21rpx auto 0 auto;
+	}
+
+	.product-window .joinBnt.on {
+		background-color: #bbb;
+		color: #fff;
+	}
+</style>

+ 115 - 0
components/recommend/index.vue

@@ -0,0 +1,115 @@
+<template>
+	<view class='recommend'>
+		<view class='title acea-row row-center-wrapper'>
+			<text class='iconfont icon-zhuangshixian'></text>
+			<text class='name'>热门推荐</text>
+			<text class='iconfont icon-zhuangshixian lefticon'></text>
+		</view>
+		<view class='recommendList acea-row row-between-wrapper'>
+			<view class='item' v-for="(item,index) in hostProduct" :key="index" hover-class='none' @tap="goDetail(item)">
+				<view class='pictrue'>
+					<image :src='item.picUrl'></image>
+					<span class="pictrue_log_big pictrue_log_class" v-if="item.activityList && item.activityList[0] && item.activityList[0].type === 1">秒杀</span>
+					<span class="pictrue_log_big pictrue_log_class" v-if="item.activityList && item.activityList[0] && item.activityList[0].type === 2">砍价</span>
+					<span class="pictrue_log_big pictrue_log_class" v-if="item.activityList && item.activityList[0] && item.activityList[0].type === 3">拼团</span>
+				</view>
+				<view class='name line1'>{{ item.name }}</view>
+				<view class='money font-color'>¥<text class='num'>{{ fen2yuan(item.price) }}</text></view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	import { mapGetters } from "vuex";
+	import { goShopDetail } from '@/libs/order.js'
+  import * as Util from '@/utils/util.js';
+  export default {
+	  computed: mapGetters(['uid']),
+		props: {
+			hostProduct: {
+				type: Array,
+				default: function() {
+					return [];
+				}
+			}
+		},
+		methods: {
+			goDetail(item){
+				goShopDetail(item,this.uid).then(() => {
+					uni.navigateTo({
+						url:`/pages/goods_details/index?id=${item.id}`
+					})
+				})
+			},
+      fen2yuan(price) {
+        return Util.fen2yuan(price)
+      }
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.recommend {
+		background-color: #fff;
+	}
+
+	.recommend .title {
+		height: 135rpx;
+		line-height: 135rpx;
+		font-size: 28rpx;
+		color: #282828;
+	}
+
+	.recommend .title .name {
+		margin: 0 28rpx;
+	}
+
+	.recommend .title .iconfont {
+		font-size: 170rpx;
+		color: #454545;
+	}
+
+	.recommend .title .iconfont.lefticon {
+		transform: rotate(180deg);
+	}
+
+	.recommend .recommendList {
+		padding: 0 30rpx;
+		/* #ifdef H5 */
+		padding-bottom: 50rpx;
+		/* #endif */
+	}
+
+	.recommend .recommendList .item {
+		width: 335rpx;
+		margin-bottom: 30rpx;
+	}
+
+	.recommend .recommendList .item .pictrue {
+		position: relative;
+		width: 100%;
+		height: 335rpx;
+	}
+
+	.recommend .recommendList .item .pictrue image {
+		width: 100%;
+		height: 100%;
+		border-radius: 14rpx;
+	}
+
+	.recommend .recommendList .item .name {
+		font-size: 28rpx;
+		color: #282828;
+		margin-top: 20rpx;
+	}
+
+	.recommend .recommendList .item .money {
+		font-size: 20rpx;
+		margin-top: 8rpx;
+		font-weight: 600;
+	}
+
+	.recommend .recommendList .item .money .num {
+		font-size: 28rpx;
+	}
+</style>

+ 43 - 0
components/shareInfo/index.vue

@@ -0,0 +1,43 @@
+<template>
+	<view v-if="shareInfoStatus" class="poster-first">
+	    <view class="mask-share">
+			<image src="/static/images/share-info.png" @click="shareInfoClose" @touchmove.stop.prevent="false" />
+	    </view>
+	  </view>
+</template>
+<script>
+export default {
+	props: {
+		 shareInfoStatus: {
+        type: Boolean,
+        default:false,
+      }
+	},
+  data: function() {
+    return {};
+  },
+  mounted: function() {},
+  methods: {
+    shareInfoClose: function() {
+      this.$emit("setShareInfoStatus");
+    }
+  }
+};
+</script>
+<style scoped lang="scss">
+	.poster-first {
+	  overscroll-behavior: contain;
+	}
+	.mask-share {
+	  position: fixed;
+	  top: 0;
+	  left: 0;
+	  right: 0;
+	  bottom: 0;
+	  z-index: 99;
+	}
+	.mask-share image {
+	  width: 100%;
+	  height:100%;
+	}
+</style>

+ 75 - 0
components/shareRedPackets/index.vue

@@ -0,0 +1,75 @@
+<template>
+	<view class='sharing-packets' :class='!enabled ? "on":""'>
+	   <view class='iconfont icon-guanbi' @click="closeShare"></view>
+	   <view class='line'></view>
+	   <view class='sharing-con' @click='goShare'>
+	      <image src='../../static/images/red-packets.png'></image>
+	      <view class='text font-color'>
+	        <view>会员分享返</view>
+	        <view class='money' v-if="sharePacket.brokerageMinPrice && sharePacket.brokerageMaxPrice">
+            <text class='label'>¥</text>{{ fen2yuan(sharePacket.brokerageMinPrice) }}~{{ fen2yuan(sharePacket.brokerageMaxPrice) }}
+          </view>
+          <view class='money' v-else-if="sharePacket.brokerageMinPrice">
+            <text class='label'>¥</text>{{ fen2yuan(sharePacket.brokerageMinPrice) }}
+          </view>
+          <view class='money' v-else-if="sharePacket.brokerageMaxPrice">
+            <text class='label'>¥</text>{{ fen2yuan(sharePacket.brokerageMaxPrice) }}
+          </view>
+	        <view class='tip'>下单即返佣金</view>
+	        <view class='shareBut'>立即分享</view>
+	      </view>
+	   </view>
+	</view>
+</template>
+
+<script>
+  import * as Util from '@/utils/util.js';
+	export default {
+		props: {
+      sharePacket: {
+        type: Object,
+        default: function() {
+          return {
+            enabled: true,
+            brokerageMinPrice: undefined,
+            brokerageMaxPrice: undefined,
+          }
+        }
+      }
+		},
+		data() {
+			return {
+			};
+		},
+    computed: {
+      enabled() {
+        return this.sharePacket.enabled && (this.sharePacket.brokerageMinPrice || this.sharePacket.brokerageMaxPrice)
+      }
+    },
+		methods: {
+			closeShare:function(){
+        this.$emit('closeChange');
+      },
+      goShare:function(){
+        this.$emit('listenerActionSheet');
+      },
+      fen2yuan(price) {
+        return Util.fen2yuan(price)
+      }
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.sharing-packets{position:fixed;left:30rpx;bottom:200rpx;z-index:5;transition:all 0.3s ease-in-out 0s;opacity:1;transform: scale(1);}
+	.sharing-packets.on{transform: scale(0);opacity:0;}
+	.sharing-packets .iconfont{width:44rpx;height:44rpx;border-radius:50%;text-align:center;line-height:44rpx;background-color:#999;font-size:20rpx;color:#fff;margin:0 auto;box-sizing:border-box;padding-left:1px;}
+	.sharing-packets .line{width:2rpx;height:40rpx;background-color:#999;margin:0 auto;}
+	.sharing-packets .sharing-con{width:187rpx;height:210rpx;position:relative;}
+	.sharing-packets .sharing-con image{width:100%;height:100%;}
+	.sharing-packets .sharing-con .text{position:absolute;top:30rpx;font-size:20rpx;width:100%;text-align:center;}
+	.sharing-packets .sharing-con .text .money{font-size:32rpx;font-weight:bold;margin-top:5rpx;white-space: nowrap}
+	.sharing-packets .sharing-con .text .money .label{font-size:20rpx;}
+	.sharing-packets .sharing-con .text .tip{font-size:18rpx;color:#999;margin-top:5rpx;}
+	.sharing-packets .sharing-con .text .shareBut{font-size:22rpx;color:#fff;margin-top:18rpx;height:50rpx;line-height:50rpx;}
+</style>

+ 52 - 0
components/swipers/index.vue

@@ -0,0 +1,52 @@
+<template>
+	    <view class='swiper'>
+	        <swiper :autoplay="autoplay" :circular="circular" :interval="interval" :duration="duration" @change="swiperChange">
+	            <block v-for="(item,index) in imgUrls" :key="index">
+	              <swiper-item>
+	                <navigator :url="item.link" style='width:100%;height:100%;' hover-class='none'><image :src="item.img" class="slide-image"/></navigator>
+	              </swiper-item>
+	            </block>
+	        </swiper>
+	        <view class="dots acea-row">
+	          <view class="dot" :class="index == currentSwiper ? 'active' : ''" v-for="(item,index) in imgUrls" :key="index"></view>
+	        </view>
+	    </view>
+</template>
+
+	<script>
+		export default {
+			
+			props: {
+				 imgUrls: {
+				 	type: Array,
+				 	default: function(){
+				 		return [];
+				 	}
+				 }
+			},
+			data() {
+				return {
+					circular: true,
+					    autoplay: true,
+					    interval: 3000,
+					    duration: 500,
+					    currentSwiper: 0
+				};
+			},
+			
+			methods: {
+				swiperChange: function (e) {
+					 this.currentSwiper = e.detail.current
+				    }
+			}
+		}
+	</script>
+
+<style scoped lang="scss">
+	.swiper{width:100%;height:282rpx;position:relative;}
+	.swiper swiper{width:100%;height:100%;position:relative;}
+	.swiper swiper .slide-image{width:100%;height:100%;}
+	.swiper .dots{position:absolute;right:40rpx;bottom:20rpx;}
+	.swiper .dots .dot{width:12rpx;height:12rpx;border:2rpx solid #fff;border-radius:50%;margin-right:15rpx;}
+	.swiper .dots .dot.active{border-color:$theme-color;background-color:$theme-color;}
+</style>

+ 165 - 0
components/ucharts/component.vue

@@ -0,0 +1,165 @@
+<template>
+	<canvas v-if="canvasId" :id="canvasId" :canvasId="canvasId" :style="{'width':cWidth*pixelRatio+'px','height':cHeight*pixelRatio+'px', 'transform': 'scale('+(1/pixelRatio)+')','margin-left':-cWidth*(pixelRatio-1)/2+'px','margin-top':-cHeight*(pixelRatio-1)/2+'px'}"
+	 @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd" @error="error">
+	</canvas>
+</template>
+
+<script>
+	import uCharts from './u-charts.js';
+	var canvases = {};
+	
+	export default {
+		props: {
+			chartType: {
+				required: true,
+				type: String,
+				default: 'column'
+			},
+			opts: {
+				required: true,
+				type: Object,
+				default () {
+					return null;
+				},
+			},
+			canvasId: {
+				type: String,
+				default: 'u-canvas',
+			},
+			cWidth: {
+				default: 375,
+			},
+			cHeight: {
+				default: 250,
+			},
+			pixelRatio: {
+				type: Number,
+				default: 1,
+			},
+		},
+		mounted() {
+			this.init();
+		},
+		methods: {
+			init() {
+				switch (this.chartType) {
+					case 'column':
+						this.initColumnChart();
+						break;
+					case 'line':
+						this.initLineChart();
+						break;
+					default:
+						break;
+				}
+			},
+			initColumnChart() {
+				canvases[this.canvasId] = new uCharts({
+					$this: this,
+					canvasId: this.canvasId,
+					type: 'column',
+					legend: true,
+					fontSize: 11,
+					background: '#FFFFFF',
+					pixelRatio: this.pixelRatio,
+					animation: true,
+					categories: this.opts.categories,
+					series: this.opts.series,
+					enableScroll: true,
+					xAxis: {
+						disableGrid: true,
+						itemCount: 4,
+						scrollShow: true
+					},
+					yAxis: {
+						//disabled:true
+					},
+					dataLabel: true,
+					width: this.cWidth * this.pixelRatio,
+					height: this.cHeight * this.pixelRatio,
+					extra: {
+						column: {
+							type: 'group',
+						}
+					}
+				});
+			},
+			initLineChart() {
+				canvases[this.canvasId] = new uCharts({
+					$this: this,
+					canvasId: this.canvasId,
+					type: 'line',
+					fontSize: 11,
+					legend: true,
+					dataLabel: false,
+					dataPointShape: true,
+					background: '#FFFFFF',
+					pixelRatio: this.pixelRatio,
+					categories: this.opts.categories,
+					series: this.opts.series,
+					animation: true,
+					enableScroll: true,
+					xAxis: {
+						type: 'grid',
+						gridColor: '#CCCCCC',
+						gridType: 'dash',
+						dashLength: 8,
+						itemCount: 4,
+						scrollShow: true
+					},
+					yAxis: {
+						gridType: 'dash',
+						gridColor: '#CCCCCC',
+						dashLength: 8,
+						splitNumber: 5,
+						min: 10,
+						max: 180,
+						format: (val) => {
+							return val.toFixed(0) + '元'
+						}
+					},
+					width: this.cWidth * this.pixelRatio,
+					height: this.cHeight * this.pixelRatio,
+					extra: {
+						line: {
+							type: 'straight'
+						}
+					}
+				});
+			},
+			// 这里仅作为示例传入两个参数,cid为canvas-id,newdata为更新的数据,需要更多参数请自行修改
+			changeData(cid,newdata) {
+				canvases[cid].updateData({
+					series: newdata.series,
+					categories: newdata.categories
+				});
+			},
+			touchStart(e) {
+				canvases[this.canvasId].showToolTip(e, {
+					format: function(item, category) {
+						return category + ' ' + item.name + ':' + item.data
+					}
+				});
+				canvases[this.canvasId].scrollStart(e);
+			},
+			touchMove(e) {
+				canvases[this.canvasId].scroll(e);
+			},
+			touchEnd(e) {
+				canvases[this.canvasId].scrollEnd(e);
+			},
+			error(e) {
+				console.log(e)
+			}
+		},
+	};
+</script>
+
+<style scoped>
+	.charts {
+		width: 100%;
+		height: 100%;
+		flex: 1;
+		background-color: #FFFFFF;
+	}
+</style>

+ 5658 - 0
components/ucharts/ucharts.js

@@ -0,0 +1,5658 @@
+/*
+ * uCharts v1.9.3.20190922
+ * uni-app平台高性能跨全端图表,支持H5、APP、小程序(微信/支付宝/百度/头条/QQ/360)
+ * Copyright (c) 2019 QIUN秋云 https://www.ucharts.cn All rights reserved.
+ * Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
+ * 
+ * uCharts官方网站
+ * https://www.uCharts.cn
+ * 
+ * 开源地址:
+ * https://gitee.com/uCharts/uCharts
+ * 
+ * uni-app插件市场地址:
+ * http://ext.dcloud.net.cn/plugin?id=271
+ * 
+ */
+
+'use strict';
+
+var config = {
+  yAxisWidth: 15,
+  yAxisSplit: 5,
+  xAxisHeight: 15,
+  xAxisLineHeight: 15,
+  legendHeight: 15,
+  yAxisTitleWidth: 15,
+  padding: [10, 10, 10, 10],
+  pixelRatio: 1,
+  rotate: false,
+  columePadding: 3,
+  fontSize: 13,
+  //dataPointShape: ['diamond', 'circle', 'triangle', 'rect'],
+  dataPointShape: ['circle', 'circle', 'circle', 'circle'],
+  colors: ['#1890ff', '#2fc25b', '#facc14', '#f04864', '#8543e0', '#90ed7d'],
+  pieChartLinePadding: 15,
+  pieChartTextPadding: 5,
+  xAxisTextPadding: 3,
+  titleColor: '#333333',
+  titleFontSize: 20,
+  subtitleColor: '#999999',
+  subtitleFontSize: 15,
+  toolTipPadding: 3,
+  toolTipBackground: '#000000',
+  toolTipOpacity: 0.7,
+  toolTipLineHeight: 20,
+  radarLabelTextMargin: 15,
+  gaugeLabelTextMargin: 15
+};
+
+let assign = function (target, ...varArgs) {
+    if (target == null) {
+        throw new TypeError('Cannot convert undefined or null to object');
+    }
+    if (!varArgs || varArgs.length <= 0) {
+        return target;
+    }
+    // 深度合并对象
+    function deepAssign(obj1, obj2) {
+        for (let key in obj2) {
+            obj1[key] = obj1[key] && obj1[key].toString() === "[object Object]" ?
+                deepAssign(obj1[key], obj2[key]) : obj1[key] = obj2[key];
+        }
+        return obj1;
+    }
+
+    varArgs.forEach(val => {
+        target = deepAssign(target, val);
+    });
+    return target;
+};
+
+var util = {
+  toFixed: function toFixed(num, limit) {
+    limit = limit || 2;
+    if (this.isFloat(num)) {
+      num = num.toFixed(limit);
+    }
+    return num;
+  },
+  isFloat: function isFloat(num) {
+    return num % 1 !== 0;
+  },
+  approximatelyEqual: function approximatelyEqual(num1, num2) {
+    return Math.abs(num1 - num2) < 1e-10;
+  },
+  isSameSign: function isSameSign(num1, num2) {
+    return Math.abs(num1) === num1 && Math.abs(num2) === num2 || Math.abs(num1) !== num1 && Math.abs(num2) !== num2;
+  },
+  isSameXCoordinateArea: function isSameXCoordinateArea(p1, p2) {
+    return this.isSameSign(p1.x, p2.x);
+  },
+  isCollision: function isCollision(obj1, obj2) {
+    obj1.end = {};
+    obj1.end.x = obj1.start.x + obj1.width;
+    obj1.end.y = obj1.start.y - obj1.height;
+    obj2.end = {};
+    obj2.end.x = obj2.start.x + obj2.width;
+    obj2.end.y = obj2.start.y - obj2.height;
+    var flag = obj2.start.x > obj1.end.x || obj2.end.x < obj1.start.x || obj2.end.y > obj1.start.y || obj2.start.y < obj1.end.y;
+    return !flag;
+  }
+};
+
+//兼容H5点击事件
+function getH5Offset(e) {
+  e.mp = {
+    changedTouches: []
+  };
+  e.mp.changedTouches.push({
+    x: e.offsetX,
+    y: e.offsetY
+  });
+  return e;
+}
+
+// hex 转 rgba
+function hexToRgb(hexValue, opc) {
+  var rgx = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
+  var hex = hexValue.replace(rgx, function(m, r, g, b) {
+    return r + r + g + g + b + b;
+  });
+  var rgb = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+  var r = parseInt(rgb[1], 16);
+  var g = parseInt(rgb[2], 16);
+  var b = parseInt(rgb[3], 16);
+  return 'rgba(' + r + ',' + g + ',' + b + ',' + opc + ')';
+}
+
+function findRange(num, type, limit) {
+  if (isNaN(num)) {
+    throw new Error('[uCharts] unvalid series data!');
+  }
+  limit = limit || 10;
+  type = type ? type : 'upper';
+  var multiple = 1;
+  while (limit < 1) {
+    limit *= 10;
+    multiple *= 10;
+  }
+  if (type === 'upper') {
+    num = Math.ceil(num * multiple);
+  } else {
+    num = Math.floor(num * multiple);
+  }
+  while (num % limit !== 0) {
+    if (type === 'upper') {
+      num++;
+    } else {
+      num--;
+    }
+  }
+  return num / multiple;
+}
+
+function calCandleMA(dayArr, nameArr, colorArr, kdata) {
+  let seriesTemp = [];
+  for (let k = 0; k < dayArr.length; k++) {
+    let seriesItem = {
+      data: [],
+      name: nameArr[k],
+      color: colorArr[k]
+    };
+    for (let i = 0, len = kdata.length; i < len; i++) {
+      if (i < dayArr[k]) {
+        seriesItem.data.push(null);
+        continue;
+      }
+      let sum = 0;
+      for (let j = 0; j < dayArr[k]; j++) {
+        sum += kdata[i - j][1];
+      }
+      seriesItem.data.push(+(sum / dayArr[k]).toFixed(3));
+    }
+    seriesTemp.push(seriesItem);
+  }
+  return seriesTemp;
+}
+
+function calValidDistance(self,distance, chartData, config, opts) {
+  var dataChartAreaWidth = opts.width - opts.area[1] - opts.area[3];
+  var dataChartWidth = chartData.eachSpacing * (opts.chartData.xAxisData.xAxisPoints.length-1);
+  var validDistance = distance;
+  if (distance >= 0) {
+    validDistance = 0;
+		self.event.trigger('scrollLeft');
+  } else if (Math.abs(distance) >= dataChartWidth - dataChartAreaWidth) {
+    validDistance = dataChartAreaWidth - dataChartWidth;
+		self.event.trigger('scrollRight');
+  }
+  return validDistance;
+}
+
+function isInAngleRange(angle, startAngle, endAngle) {
+  function adjust(angle) {
+    while (angle < 0) {
+      angle += 2 * Math.PI;
+    }
+    while (angle > 2 * Math.PI) {
+      angle -= 2 * Math.PI;
+    }
+    return angle;
+  }
+  angle = adjust(angle);
+  startAngle = adjust(startAngle);
+  endAngle = adjust(endAngle);
+  if (startAngle > endAngle) {
+    endAngle += 2 * Math.PI;
+    if (angle < startAngle) {
+      angle += 2 * Math.PI;
+    }
+  }
+  return angle >= startAngle && angle <= endAngle;
+}
+
+function calRotateTranslate(x, y, h) {
+  var xv = x;
+  var yv = h - y;
+  var transX = xv + (h - yv - xv) / Math.sqrt(2);
+  transX *= -1;
+  var transY = (h - yv) * (Math.sqrt(2) - 1) - (h - yv - xv) / Math.sqrt(2);
+  return {
+    transX: transX,
+    transY: transY
+  };
+}
+
+function createCurveControlPoints(points, i) {
+
+  function isNotMiddlePoint(points, i) {
+    if (points[i - 1] && points[i + 1]) {
+      return points[i].y >= Math.max(points[i - 1].y, points[i + 1].y) || points[i].y <= Math.min(points[i - 1].y,points[i + 1].y);
+    } else {
+      return false;
+    }
+  }
+	function isNotMiddlePointX(points, i) {
+	  if (points[i - 1] && points[i + 1]) {
+	    return points[i].x >= Math.max(points[i - 1].x, points[i + 1].x) || points[i].x <= Math.min(points[i - 1].x,points[i + 1].x);
+	  } else {
+	    return false;
+	  }
+	}
+  var a = 0.2;
+  var b = 0.2;
+  var pAx = null;
+  var pAy = null;
+  var pBx = null;
+  var pBy = null;
+  if (i < 1) {
+    pAx = points[0].x + (points[1].x - points[0].x) * a;
+    pAy = points[0].y + (points[1].y - points[0].y) * a;
+  } else {
+    pAx = points[i].x + (points[i + 1].x - points[i - 1].x) * a;
+    pAy = points[i].y + (points[i + 1].y - points[i - 1].y) * a;
+  }
+
+  if (i > points.length - 3) {
+    var last = points.length - 1;
+    pBx = points[last].x - (points[last].x - points[last - 1].x) * b;
+    pBy = points[last].y - (points[last].y - points[last - 1].y) * b;
+  } else {
+    pBx = points[i + 1].x - (points[i + 2].x - points[i].x) * b;
+    pBy = points[i + 1].y - (points[i + 2].y - points[i].y) * b;
+  }
+  if (isNotMiddlePoint(points, i + 1)) {
+    pBy = points[i + 1].y;
+  }
+  if (isNotMiddlePoint(points, i)) {
+    pAy = points[i].y;
+  }
+	if (isNotMiddlePointX(points, i + 1)) {
+	  pBx = points[i + 1].x;
+	}
+	if (isNotMiddlePointX(points, i)) {
+	  pAx = points[i].x;
+	}
+	if (pAy >= Math.max(points[i].y, points[i + 1].y) || pAy <= Math.min(points[i].y, points[i + 1].y)) {
+	pAy = points[i].y;
+	}
+	if (pBy >= Math.max(points[i].y, points[i + 1].y) || pBy <= Math.min(points[i].y, points[i + 1].y)) {
+	pBy = points[i + 1].y;
+	}
+	if (pAx >= Math.max(points[i].x, points[i + 1].x) || pAx <= Math.min(points[i].x, points[i + 1].x)) {
+	pAx = points[i].x;
+	}
+	if (pBx >= Math.max(points[i].x, points[i + 1].x) || pBx <= Math.min(points[i].x, points[i + 1].x)) {
+	pBx = points[i + 1].x;
+	}
+  return {
+    ctrA: {
+      x: pAx,
+      y: pAy
+    },
+    ctrB: {
+      x: pBx,
+      y: pBy
+    }
+  };
+}
+
+function convertCoordinateOrigin(x, y, center) {
+  return {
+    x: center.x + x,
+    y: center.y - y
+  };
+}
+
+function avoidCollision(obj, target) {
+  if (target) {
+    // is collision test
+    while (util.isCollision(obj, target)) {
+      if (obj.start.x > 0) {
+        obj.start.y--;
+      } else if (obj.start.x < 0) {
+        obj.start.y++;
+      } else {
+        if (obj.start.y > 0) {
+          obj.start.y++;
+        } else {
+          obj.start.y--;
+        }
+      }
+    }
+  }
+  return obj;
+}
+
+function fillSeries(series, opts, config) {
+  var index = 0;
+  return series.map(function(item) {
+    if (!item.color) {
+      item.color = config.colors[index];
+      index = (index + 1) % config.colors.length;
+    }
+    if (!item.index) {
+      item.index = 0;
+    }
+    if (!item.type) {
+      item.type = opts.type;
+    }
+    if (typeof item.show == "undefined") {
+      item.show = true;
+    }
+    if (!item.type) {
+      item.type = opts.type;
+    }
+    if (!item.pointShape) {
+      item.pointShape = "circle";
+    }
+    if (!item.legendShape) {
+      switch (item.type) {
+        case 'line':
+          item.legendShape = "line";
+          break;
+        case 'column':
+          item.legendShape = "rect";
+          break;
+        case 'area':
+          item.legendShape = "triangle";
+          break;
+        default:
+          item.legendShape = "circle";
+      }
+    }
+    return item;
+  });
+}
+
+function getDataRange(minData, maxData) {
+  var limit = 0;
+  var range = maxData - minData;
+  if (range >= 10000) {
+    limit = 1000;
+  } else if (range >= 1000) {
+    limit = 100;
+  } else if (range >= 100) {
+    limit = 10;
+  } else if (range >= 10) {
+    limit = 5;
+  } else if (range >= 1) {
+    limit = 1;
+  } else if (range >= 0.1) {
+    limit = 0.1;
+  } else if (range >= 0.01) {
+    limit = 0.01;
+  } else if (range >= 0.001) {
+    limit = 0.001;
+  } else if (range >= 0.0001) {
+    limit = 0.0001;
+  } else if (range >= 0.00001) {
+    limit = 0.00001;
+  } else {
+    limit = 0.000001;
+  }
+  return {
+    minRange: findRange(minData, 'lower', limit),
+    maxRange: findRange(maxData, 'upper', limit)
+  };
+}
+
+function measureText(text) {
+  var fontSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : config.fontSize;
+  text = String(text);
+  var text = text.split('');
+  var width = 0;
+  for (let i = 0; i < text.length; i++) {
+    let item = text[i];
+    if (/[a-zA-Z]/.test(item)) {
+      width += 7;
+    } else if (/[0-9]/.test(item)) {
+      width += 5.5;
+    } else if (/\./.test(item)) {
+      width += 2.7;
+    } else if (/-/.test(item)) {
+      width += 3.25;
+    } else if (/[\u4e00-\u9fa5]/.test(item)) {
+      width += 10;
+    } else if (/\(|\)/.test(item)) {
+      width += 3.73;
+    } else if (/\s/.test(item)) {
+      width += 2.5;
+    } else if (/%/.test(item)) {
+      width += 8;
+    } else {
+      width += 10;
+    }
+  }
+  return width * fontSize / 10;
+}
+
+function dataCombine(series) {
+  return series.reduce(function(a, b) {
+    return (a.data ? a.data : a).concat(b.data);
+  }, []);
+}
+
+function dataCombineStack(series, len) {
+  var sum = new Array(len);
+  for (var j = 0; j < sum.length; j++) {
+    sum[j] = 0;
+  }
+  for (var i = 0; i < series.length; i++) {
+    for (var j = 0; j < sum.length; j++) {
+      sum[j] += series[i].data[j];
+    }
+  }
+  return series.reduce(function(a, b) {
+    return (a.data ? a.data : a).concat(b.data).concat(sum);
+  }, []);
+}
+
+function getTouches(touches, opts, e) {
+  let x, y;
+  if (touches.clientX) {
+    if (opts.rotate) {
+      y = opts.height - touches.clientX * opts.pixelRatio;
+      x = (touches.pageY - e.currentTarget.offsetTop - (opts.height / opts.pixelRatio / 2) * (opts.pixelRatio - 1)) *
+        opts.pixelRatio;
+    } else {
+      x = touches.clientX * opts.pixelRatio;
+      y = (touches.pageY - e.currentTarget.offsetTop - (opts.height / opts.pixelRatio / 2) * (opts.pixelRatio - 1)) *
+        opts.pixelRatio;
+    }
+  } else {
+    if (opts.rotate) {
+      y = opts.height - touches.x * opts.pixelRatio;
+      x = touches.y * opts.pixelRatio;
+    } else {
+      x = touches.x * opts.pixelRatio;
+      y = touches.y * opts.pixelRatio;
+    }
+  }
+  return {
+    x: x,
+    y: y
+  }
+}
+
+function getSeriesDataItem(series, index) {
+  var data = [];
+  for (let i = 0; i < series.length; i++) {
+    let item = series[i];
+    if (item.data[index] !== null && typeof item.data[index] !== 'undefined' && item.show) {
+      let seriesItem = {};
+      seriesItem.color = item.color;
+      seriesItem.type = item.type;
+      seriesItem.style = item.style;
+      seriesItem.pointShape = item.pointShape;
+      seriesItem.disableLegend = item.disableLegend;
+      seriesItem.name = item.name;
+      seriesItem.show = item.show;
+      seriesItem.data = item.format ? item.format(item.data[index]) : item.data[index];
+      data.push(seriesItem);
+    }
+  }
+  return data;
+}
+
+function getMaxTextListLength(list) {
+  var lengthList = list.map(function(item) {
+    return measureText(item);
+  });
+  return Math.max.apply(null, lengthList);
+}
+
+function getRadarCoordinateSeries(length) {
+  var eachAngle = 2 * Math.PI / length;
+  var CoordinateSeries = [];
+  for (var i = 0; i < length; i++) {
+    CoordinateSeries.push(eachAngle * i);
+  }
+
+  return CoordinateSeries.map(function(item) {
+    return -1 * item + Math.PI / 2;
+  });
+}
+
+function getToolTipData(seriesData, calPoints, index, categories) {
+  var option = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
+
+  var textList = seriesData.map(function(item) {
+		let titleText=[];
+		if(categories){
+			titleText=categories;
+		}else{
+			titleText=item.data;
+		}
+    return {
+      text: option.format ? option.format(item, titleText[index]) : item.name + ': ' + item.data,
+      color: item.color
+    };
+  });
+  var validCalPoints = [];
+  var offset = {
+    x: 0,
+    y: 0
+  };
+  for (let i = 0; i < calPoints.length; i++) {
+    let points = calPoints[i];
+    if (typeof points[index] !== 'undefined' && points[index] !== null) {
+      validCalPoints.push(points[index]);
+    }
+  }
+  for (let i = 0; i < validCalPoints.length; i++) {
+    let item = validCalPoints[i];
+    offset.x = Math.round(item.x);
+    offset.y += item.y;
+  }
+  offset.y /= validCalPoints.length;
+  return {
+    textList: textList,
+    offset: offset
+  };
+}
+
+function getMixToolTipData(seriesData, calPoints, index, categories) {
+  var option = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
+  var textList = seriesData.map(function(item) {
+    return {
+      text: option.format ? option.format(item, categories[index]) : item.name + ': ' + item.data,
+      color: item.color,
+      disableLegend: item.disableLegend ? true : false
+    };
+  });
+  textList = textList.filter(function(item) {
+    if (item.disableLegend !== true) {
+      return item;
+    }
+  });
+  var validCalPoints = [];
+  var offset = {
+    x: 0,
+    y: 0
+  };
+  for (let i = 0; i < calPoints.length; i++) {
+    let points = calPoints[i];
+    if (typeof points[index] !== 'undefined' && points[index] !== null) {
+      validCalPoints.push(points[index]);
+    }
+  }
+  for (let i = 0; i < validCalPoints.length; i++) {
+    let item = validCalPoints[i];
+    offset.x = Math.round(item.x);
+    offset.y += item.y;
+  }
+  offset.y /= validCalPoints.length;
+  return {
+    textList: textList,
+    offset: offset
+  };
+}
+
+function getCandleToolTipData(series, seriesData, calPoints, index, categories, extra) {
+  var option = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : {};
+  let upColor = extra.color.upFill;
+  let downColor = extra.color.downFill;
+  //颜色顺序为开盘,收盘,最低,最高
+  let color = [upColor, upColor, downColor, upColor];
+  var textList = [];
+  let text0 = {
+    text: categories[index],
+    color: null
+  };
+  textList.push(text0);
+  seriesData.map(function(item) {
+    if (index == 0 && item.data[1] - item.data[0] < 0) {
+      color[1] = downColor;
+    } else {
+      if (item.data[0] < series[index - 1][1]) {
+        color[0] = downColor;
+      }
+      if (item.data[1] < item.data[0]) {
+        color[1] = downColor;
+      }
+      if (item.data[2] > series[index - 1][1]) {
+        color[2] = upColor;
+      }
+      if (item.data[3] < series[index - 1][1]) {
+        color[3] = downColor;
+      }
+    }
+    let text1 = {
+      text: '开盘:' + item.data[0],
+      color: color[0]
+    };
+    let text2 = {
+      text: '收盘:' + item.data[1],
+      color: color[1]
+    };
+    let text3 = {
+      text: '最低:' + item.data[2],
+      color: color[2]
+    };
+    let text4 = {
+      text: '最高:' + item.data[3],
+      color: color[3]
+    };
+    textList.push(text1, text2, text3, text4);
+  });
+  var validCalPoints = [];
+  var offset = {
+    x: 0,
+    y: 0
+  };
+  for (let i = 0; i < calPoints.length; i++) {
+    let points = calPoints[i];
+    if (typeof points[index] !== 'undefined' && points[index] !== null) {
+      validCalPoints.push(points[index]);
+    }
+  }
+  offset.x = Math.round(validCalPoints[0][0].x);
+  return {
+    textList: textList,
+    offset: offset
+  };
+}
+
+function filterSeries(series) {
+  let tempSeries = [];
+  for (let i = 0; i < series.length; i++) {
+    if (series[i].show == true) {
+      tempSeries.push(series[i])
+    }
+  }
+  return tempSeries;
+}
+
+function findCurrentIndex(currentPoints, calPoints, opts, config) {
+  var offset = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
+  var currentIndex = -1;
+  var spacing = opts.chartData.eachSpacing/2;
+	let xAxisPoints=[];
+	if(calPoints.length>0){
+		if(opts.type=='candle'){
+			for(let i=0;i<calPoints[0].length;i++){
+				xAxisPoints.push(calPoints[0][i][0].x)
+			}
+		}else{
+			for(let i=0;i<calPoints[0].length;i++){
+				xAxisPoints.push(calPoints[0][i].x)
+			}
+		}
+		if((opts.type=='line' || opts.type=='area') && opts.xAxis.boundaryGap=='justify'){
+		  spacing = opts.chartData.eachSpacing/2;
+		}
+		if(!opts.categories){
+			spacing=0
+		}
+		if (isInExactChartArea(currentPoints, opts, config)) {
+		  xAxisPoints.forEach(function(item, index) {
+		    if (currentPoints.x + offset + spacing > item) {
+		      currentIndex = index;
+		    }
+		  });
+		}
+	}
+  return currentIndex;
+}
+
+function findLegendIndex(currentPoints, legendData, opts) {
+  let currentIndex = -1;
+  if (isInExactLegendArea(currentPoints, legendData.area)) {
+    let points = legendData.points;
+    let index = -1;
+    for (let i = 0, len = points.length; i < len; i++) {
+      let item = points[i];
+      for (let j = 0; j < item.length; j++) {
+        index += 1;
+        let area = item[j]['area'];
+        if (currentPoints.x > area[0] && currentPoints.x < area[2] && currentPoints.y > area[1] && currentPoints.y < area[3]) {
+          currentIndex = index;
+          break;
+        }
+      }
+    }
+    return currentIndex;
+  }
+  return currentIndex;
+}
+
+function isInExactLegendArea(currentPoints, area) {
+  return currentPoints.x > area.start.x && currentPoints.x < area.end.x && currentPoints.y > area.start.y &&
+    currentPoints.y < area.end.y;
+}
+
+function isInExactChartArea(currentPoints, opts, config) {
+  return currentPoints.x <= opts.width - opts.area[1] + 10 && currentPoints.x >= opts.area[3] -10 && currentPoints.y >= opts.area[0] && currentPoints.y <= opts.height - opts.area[2];
+}
+
+function findRadarChartCurrentIndex(currentPoints, radarData, count) {
+  var eachAngleArea = 2 * Math.PI / count;
+  var currentIndex = -1;
+  if (isInExactPieChartArea(currentPoints, radarData.center, radarData.radius)) {
+    var fixAngle = function fixAngle(angle) {
+      if (angle < 0) {
+        angle += 2 * Math.PI;
+      }
+      if (angle > 2 * Math.PI) {
+        angle -= 2 * Math.PI;
+      }
+      return angle;
+    };
+
+    var angle = Math.atan2(radarData.center.y - currentPoints.y, currentPoints.x - radarData.center.x);
+    angle = -1 * angle;
+    if (angle < 0) {
+      angle += 2 * Math.PI;
+    }
+
+    var angleList = radarData.angleList.map(function(item) {
+      item = fixAngle(-1 * item);
+
+      return item;
+    });
+
+    angleList.forEach(function(item, index) {
+      var rangeStart = fixAngle(item - eachAngleArea / 2);
+      var rangeEnd = fixAngle(item + eachAngleArea / 2);
+      if (rangeEnd < rangeStart) {
+        rangeEnd += 2 * Math.PI;
+      }
+      if (angle >= rangeStart && angle <= rangeEnd || angle + 2 * Math.PI >= rangeStart && angle + 2 * Math.PI <=
+        rangeEnd) {
+        currentIndex = index;
+      }
+    });
+  }
+
+  return currentIndex;
+}
+
+function findFunnelChartCurrentIndex(currentPoints, funnelData) {
+  var currentIndex = -1;
+  for (var i = 0, len = funnelData.series.length; i < len; i++) {
+    var item = funnelData.series[i];
+    if (currentPoints.x > item.funnelArea[0] && currentPoints.x < item.funnelArea[2] && currentPoints.y > item.funnelArea[1] && currentPoints.y < item.funnelArea[3]) {
+      currentIndex = i;
+      break;
+    }
+  }
+  return currentIndex;
+}
+
+function findWordChartCurrentIndex(currentPoints, wordData) {
+  var currentIndex = -1;
+  for (var i = 0, len = wordData.length; i < len; i++) {
+    var item = wordData[i];
+    if (currentPoints.x > item.area[0] && currentPoints.x < item.area[2] && currentPoints.y > item.area[1] && currentPoints.y < item.area[3]) {
+      currentIndex = i;
+      break;
+    }
+  }
+  return currentIndex;
+}
+
+function findMapChartCurrentIndex(currentPoints, opts) {
+  var currentIndex = -1;
+  var cData=opts.chartData.mapData;
+  var data=opts.series;
+  var tmp=pointToCoordinate(currentPoints.y, currentPoints.x,cData.bounds,cData.scale,cData.xoffset,cData.yoffset);
+  var poi=[tmp.x, tmp.y];
+  for (var i = 0, len = data.length; i < len; i++) {
+    var item = data[i].geometry.coordinates;
+    if(isPoiWithinPoly(poi,item)){
+      currentIndex = i;
+      break;
+    }
+  }
+  return currentIndex;
+}
+
+function findPieChartCurrentIndex(currentPoints, pieData) {
+  var currentIndex = -1;
+  if (isInExactPieChartArea(currentPoints, pieData.center, pieData.radius)) {
+    var angle = Math.atan2(pieData.center.y - currentPoints.y, currentPoints.x - pieData.center.x);
+    angle = -angle;
+    for (var i = 0, len = pieData.series.length; i < len; i++) {
+      var item = pieData.series[i];
+      if (isInAngleRange(angle, item._start_, item._start_ + item._proportion_ * 2 * Math.PI)) {
+        currentIndex = i;
+        break;
+      }
+    }
+  }
+
+  return currentIndex;
+}
+
+function isInExactPieChartArea(currentPoints, center, radius) {
+  return Math.pow(currentPoints.x - center.x, 2) + Math.pow(currentPoints.y - center.y, 2) <= Math.pow(radius, 2);
+}
+
+function splitPoints(points) {
+  var newPoints = [];
+  var items = [];
+  points.forEach(function(item, index) {
+    if (item !== null) {
+      items.push(item);
+    } else {
+      if (items.length) {
+        newPoints.push(items);
+      }
+      items = [];
+    }
+  });
+  if (items.length) {
+    newPoints.push(items);
+  }
+
+  return newPoints;
+}
+
+function calLegendData(series, opts, config, chartData) {
+  let legendData = {
+    area: {
+      start: {
+        x: 0,
+        y: 0
+      },
+      end: {
+        x: 0,
+        y: 0
+      },
+      width: 0,
+      height: 0,
+      wholeWidth: 0,
+      wholeHeight: 0
+    },
+    points: [],
+    widthArr: [],
+    heightArr: []
+  };
+  if (opts.legend.show === false) {
+    chartData.legendData = legendData;
+    return legendData;
+  }
+
+  let padding = opts.legend.padding;
+  let margin = opts.legend.margin;
+  let fontSize = opts.legend.fontSize;
+  let shapeWidth = 15 * opts.pixelRatio;
+  let shapeRight = 5 * opts.pixelRatio;
+  let lineHeight = Math.max(opts.legend.lineHeight * opts.pixelRatio, fontSize);
+  if (opts.legend.position == 'top' || opts.legend.position == 'bottom') {
+    let legendList = [];
+    let widthCount = 0;
+    let widthCountArr = [];
+    let currentRow = [];
+    for (let i = 0; i < series.length; i++) {
+      let item = series[i];
+      let itemWidth = shapeWidth + shapeRight + measureText(item.name || 'undefined', fontSize) + opts.legend.itemGap;
+      if (widthCount + itemWidth > opts.width - opts.padding[1] - opts.padding[3]) {
+        legendList.push(currentRow);
+        widthCountArr.push(widthCount - opts.legend.itemGap);
+        widthCount = itemWidth;
+        currentRow = [item];
+      } else {
+        widthCount += itemWidth;
+        currentRow.push(item);
+      }
+    }
+    if (currentRow.length) {
+      legendList.push(currentRow);
+      widthCountArr.push(widthCount - opts.legend.itemGap);
+      legendData.widthArr = widthCountArr;
+      let legendWidth = Math.max.apply(null, widthCountArr);
+      switch (opts.legend.float) {
+        case 'left':
+          legendData.area.start.x = opts.padding[3];
+          legendData.area.end.x = opts.padding[3] + 2 * padding;
+          break;
+        case 'right':
+          legendData.area.start.x = opts.width - opts.padding[1] - legendWidth - 2 * padding;
+          legendData.area.end.x = opts.width - opts.padding[1];
+          break;
+        default:
+          legendData.area.start.x = (opts.width - legendWidth) / 2 - padding;
+          legendData.area.end.x = (opts.width + legendWidth) / 2 + padding;
+      }
+      legendData.area.width = legendWidth + 2 * padding;
+      legendData.area.wholeWidth = legendWidth + 2 * padding;
+      legendData.area.height = legendList.length * lineHeight + 2 * padding;
+      legendData.area.wholeHeight = legendList.length * lineHeight + 2 * padding + 2 * margin;
+      legendData.points = legendList;
+    }
+  } else {
+    let len = series.length;
+    let maxHeight = opts.height - opts.padding[0] - opts.padding[2] - 2 * margin - 2 * padding;
+    let maxLength = Math.min(Math.floor(maxHeight / lineHeight), len);
+    legendData.area.height = maxLength * lineHeight + padding * 2;
+    legendData.area.wholeHeight = maxLength * lineHeight + padding * 2;
+    switch (opts.legend.float) {
+      case 'top':
+        legendData.area.start.y = opts.padding[0] + margin;
+        legendData.area.end.y = opts.padding[0] + margin + legendData.area.height;
+        break;
+      case 'bottom':
+        legendData.area.start.y = opts.height - opts.padding[2] - margin - legendData.area.height;
+        legendData.area.end.y = opts.height - opts.padding[2] - margin;
+        break;
+      default:
+        legendData.area.start.y = (opts.height - legendData.area.height) / 2;
+        legendData.area.end.y = (opts.height + legendData.area.height) / 2;
+    }
+    let lineNum = len % maxLength === 0 ? len / maxLength : Math.floor((len / maxLength) + 1);
+    let currentRow = [];
+    for (let i = 0; i < lineNum; i++) {
+      let temp = series.slice(i * maxLength, i * maxLength + maxLength);
+      currentRow.push(temp);
+    }
+
+    legendData.points = currentRow;
+
+    if (currentRow.length) {
+      for (let i = 0; i < currentRow.length; i++) {
+        let item = currentRow[i];
+        let maxWidth = 0;
+        for (let j = 0; j < item.length; j++) {
+          let itemWidth = shapeWidth + shapeRight + measureText(item[j].name || 'undefined', fontSize) + opts.legend.itemGap;
+          if (itemWidth > maxWidth) {
+            maxWidth = itemWidth;
+          }
+        }
+        legendData.widthArr.push(maxWidth);
+        legendData.heightArr.push(item.length * lineHeight + padding * 2);
+      }
+      let legendWidth = 0
+      for (let i = 0; i < legendData.widthArr.length; i++) {
+        legendWidth += legendData.widthArr[i];
+      }
+      legendData.area.width = legendWidth - opts.legend.itemGap + 2 * padding;
+      legendData.area.wholeWidth = legendData.area.width + padding;
+    }
+  }
+
+  switch (opts.legend.position) {
+    case 'top':
+      legendData.area.start.y = opts.padding[0] + margin;
+      legendData.area.end.y = opts.padding[0] + margin + legendData.area.height;
+      break;
+    case 'bottom':
+      legendData.area.start.y = opts.height - opts.padding[2] - legendData.area.height - margin;
+      legendData.area.end.y = opts.height - opts.padding[2] - margin;
+      break;
+    case 'left':
+      legendData.area.start.x = opts.padding[3];
+      legendData.area.end.x = opts.padding[3] + legendData.area.width;
+      break;
+    case 'right':
+      legendData.area.start.x = opts.width - opts.padding[1] - legendData.area.width;
+      legendData.area.end.x = opts.width - opts.padding[1];
+      break;
+  }
+  chartData.legendData = legendData;
+  return legendData;
+}
+
+function calCategoriesData(categories, opts, config, eachSpacing) {
+  var result = {
+    angle: 0,
+    xAxisHeight: config.xAxisHeight
+  };
+  var categoriesTextLenth = categories.map(function(item) {
+    return measureText(item,opts.xAxis.fontSize||config.fontSize);
+  });
+  var maxTextLength = Math.max.apply(this, categoriesTextLenth);
+
+  if (opts.xAxis.rotateLabel == true && maxTextLength + 2 * config.xAxisTextPadding > eachSpacing) {
+    result.angle = 45 * Math.PI / 180;
+    result.xAxisHeight = 2 * config.xAxisTextPadding + maxTextLength * Math.sin(result.angle);
+  }
+  return result;
+}
+
+function getXAxisTextList(series, opts, config) {
+  var index = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : -1;
+  var data = dataCombine(series);
+  var sorted = [];
+  // remove null from data
+  data = data.filter(function(item) {
+    //return item !== null;
+    if (typeof item === 'object' && item !== null) {
+      if (item.constructor == Array) {
+        return item !== null;
+      } else {
+        return item.value !== null;
+      }
+    } else {
+      return item !== null;
+    }
+  });
+  data.map(function(item) {
+    if (typeof item === 'object') {
+      if (item.constructor == Array) {
+				if(opts.type=='candle'){
+					item.map(function(subitem) {
+					  sorted.push(subitem);
+					})
+				}else{
+					sorted.push(item[0]);
+				}
+      } else {
+        sorted.push(item.value);
+      }
+    } else {
+      sorted.push(item);
+    }
+  })
+	
+  var minData = 0;
+  var maxData = 0;
+  if (sorted.length > 0) {
+    minData = Math.min.apply(this, sorted);
+    maxData = Math.max.apply(this, sorted);
+  }
+  //为了兼容v1.9.0之前的项目
+  if(index>-1){
+    if (typeof opts.xAxis.data[index].min === 'number') {
+      minData = Math.min(opts.xAxis.data[index].min, minData);
+    }
+    if (typeof opts.xAxis.data[index].max === 'number') {
+      maxData = Math.max(opts.xAxis.data[index].max, maxData);
+    }
+  }else{
+    if (typeof opts.xAxis.min === 'number') {
+      minData = Math.min(opts.xAxis.min, minData);
+    }
+    if (typeof opts.xAxis.max === 'number') {
+      maxData = Math.max(opts.xAxis.max, maxData);
+    }
+  }
+  
+
+  if (minData === maxData) {
+    var rangeSpan = maxData || 10;
+    maxData += rangeSpan;
+  }
+
+  //var dataRange = getDataRange(minData, maxData);
+  var minRange = minData;
+  var maxRange = maxData;
+
+  var range = [];
+  var eachRange = (maxRange - minRange) / opts.xAxis.splitNumber;
+
+  for (var i = 0; i <= opts.xAxis.splitNumber; i++) {
+    range.push(minRange + eachRange * i);
+  }
+  return range;
+}
+
+function calXAxisData(series, opts, config){
+    var result = {
+        angle: 0,
+        xAxisHeight: config.xAxisHeight
+    };
+
+    result.ranges = getXAxisTextList(series, opts, config);
+    result.rangesFormat = result.ranges.map(function(item){
+        item = opts.xAxis.format? opts.xAxis.format(item):util.toFixed(item, 2);
+        return item;
+    });
+		
+    var xAxisScaleValues = result.ranges.map(function (item) {
+        // 如果刻度值是浮点数,则保留两位小数
+        item = util.toFixed(item, 2);
+        // 若有自定义格式则调用自定义的格式化函数
+        item = opts.xAxis.format ? opts.xAxis.format(Number(item)) : item;
+        return item;
+    });
+
+    result = Object.assign(result,getXAxisPoints(xAxisScaleValues, opts, config));
+    // 计算X轴刻度的属性譬如每个刻度的间隔,刻度的起始点\结束点以及总长
+    var eachSpacing = result.eachSpacing;
+
+    var textLength = xAxisScaleValues.map(function (item) {
+        return measureText(item);
+    });
+    
+    // get max length of categories text
+    var maxTextLength = Math.max.apply(this, textLength);
+
+    // 如果刻度值文本内容过长,则将其逆时针旋转45°
+    if (maxTextLength + 2 * config.xAxisTextPadding > eachSpacing) {
+        result.angle = 45 * Math.PI / 180;
+        result.xAxisHeight = 2 * config.xAxisTextPadding + maxTextLength * Math.sin(result.angle);
+    }
+
+    if (opts.xAxis.disabled === true) {
+        result.xAxisHeight = 0;
+    }
+
+    return result;
+}
+
+function getRadarDataPoints(angleList, center, radius, series, opts) {
+  var process = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 1;
+
+  var radarOption = opts.extra.radar || {};
+  radarOption.max = radarOption.max || 0;
+  var maxData = Math.max(radarOption.max, Math.max.apply(null, dataCombine(series)));
+
+  var data = [];
+  for (let i = 0; i < series.length; i++) {
+    let each = series[i];
+    let listItem = {};
+    listItem.color = each.color;
+		listItem.legendShape = each.legendShape;
+		listItem.pointShape = each.pointShape;
+    listItem.data = [];
+    each.data.forEach(function(item, index) {
+      let tmp = {};
+      tmp.angle = angleList[index];
+
+      tmp.proportion = item / maxData;
+      tmp.position = convertCoordinateOrigin(radius * tmp.proportion * process * Math.cos(tmp.angle), radius * tmp.proportion *
+        process * Math.sin(tmp.angle), center);
+      listItem.data.push(tmp);
+    });
+
+    data.push(listItem);
+  }
+
+  return data;
+}
+
+function getPieDataPoints(series, radius) {
+  var process = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
+
+  var count = 0;
+  var _start_ = 0;
+  for (let i = 0; i < series.length; i++) {
+    let item = series[i];
+    item.data = item.data === null ? 0 : item.data;
+    count += item.data;
+  }
+  for (let i = 0; i < series.length; i++) {
+    let item = series[i];
+    item.data = item.data === null ? 0 : item.data;
+    if (count === 0) {
+      item._proportion_ = 1 / series.length * process;
+    } else {
+      item._proportion_ = item.data / count * process;
+    }
+    item._radius_ = radius;
+  }
+  for (let i = 0; i < series.length; i++) {
+    let item = series[i];
+    item._start_ = _start_;
+    _start_ += 2 * item._proportion_ * Math.PI;
+  }
+
+  return series;
+}
+
+function getFunnelDataPoints(series, radius) {
+  var process = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
+  series = series.sort(function(a,b){return parseInt(b.data)-parseInt(a.data);});
+  for (let i = 0; i < series.length; i++) {
+    series[i].radius = series[i].data/series[0].data*radius*process;
+    series[i]._proportion_ = series[i].data/series[0].data;
+  }
+  return series.reverse();
+}
+
+function getRoseDataPoints(series, type, minRadius, radius) {
+  var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
+  var count = 0;
+  var _start_ = 0;
+
+  var dataArr = [];
+  for (let i = 0; i < series.length; i++) {
+    let item = series[i];
+    item.data = item.data === null ? 0 : item.data;
+    count += item.data;
+    dataArr.push(item.data);
+  }
+  
+  var minData = Math.min.apply(null, dataArr);
+  var maxData = Math.max.apply(null, dataArr);
+  var radiusLength = radius - minRadius;
+  
+  for (let i = 0; i < series.length; i++) {
+    let item = series[i];
+    item.data = item.data === null ? 0 : item.data;
+    if (count === 0 || type == 'area') {
+      item._proportion_ = item.data / count * process;
+      item._rose_proportion_ = 1 / series.length * process;
+    } else {
+      item._proportion_ = item.data / count * process;
+      item._rose_proportion_ = item.data / count * process;
+    }
+    item._radius_ = minRadius + radiusLength * ((item.data - minData) / (maxData - minData));
+  }
+  for (let i = 0; i < series.length; i++) {
+    let item = series[i];
+    item._start_ = _start_;
+    _start_ += 2 * item._rose_proportion_ * Math.PI;
+  }
+
+  return series;
+}
+
+function getArcbarDataPoints(series, arcbarOption) {
+  var process = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
+  if (process == 1) {
+    process = 0.999999;
+  }
+  for (let i = 0; i < series.length; i++) {
+    let item = series[i];
+    item.data = item.data === null ? 0 : item.data;
+    let totalAngle;
+    if (arcbarOption.type == 'circle') {
+      totalAngle = 2;
+    } else {
+			if (arcbarOption.endAngle < arcbarOption.startAngle) {
+			  totalAngle = 2 + arcbarOption.endAngle - arcbarOption.startAngle;
+			} else{
+			  totalAngle = arcbarOption.startAngle - arcbarOption.endAngle;
+			}
+    }
+    item._proportion_ = totalAngle * item.data * process + arcbarOption.startAngle;
+    if (item._proportion_ >= 2) {
+      item._proportion_ = item._proportion_ % 2;
+    }
+  }
+  return series;
+}
+
+function getGaugeAxisPoints(categories, startAngle, endAngle) {
+  let totalAngle = startAngle - endAngle + 1;
+  let tempStartAngle = startAngle;
+  for (let i = 0; i < categories.length; i++) {
+    categories[i].value = categories[i].value === null ? 0 : categories[i].value;
+    categories[i]._startAngle_ = tempStartAngle;
+    categories[i]._endAngle_ = totalAngle * categories[i].value + startAngle;
+    if (categories[i]._endAngle_ >= 2) {
+      categories[i]._endAngle_ = categories[i]._endAngle_ % 2;
+    }
+    tempStartAngle = categories[i]._endAngle_;
+  }
+  return categories;
+}
+
+function getGaugeDataPoints(series, categories, gaugeOption) {
+  let process = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
+  for (let i = 0; i < series.length; i++) {
+    let item = series[i];
+    item.data = item.data === null ? 0 : item.data;
+    if (gaugeOption.pointer.color == 'auto') {
+      for (let i = 0; i < categories.length; i++) {
+        if (item.data <= categories[i].value) {
+          item.color = categories[i].color;
+          break;
+        }
+      }
+    } else {
+      item.color = gaugeOption.pointer.color;
+    }
+    let totalAngle = gaugeOption.startAngle - gaugeOption.endAngle + 1;
+    item._endAngle_ = totalAngle * item.data + gaugeOption.startAngle;
+    item._oldAngle_ = gaugeOption.oldAngle;
+    if (gaugeOption.oldAngle < gaugeOption.endAngle) {
+      item._oldAngle_ += 2;
+    }
+    if (item.data >= gaugeOption.oldData) {
+      item._proportion_ = (item._endAngle_ - item._oldAngle_) * process + gaugeOption.oldAngle;
+    } else {
+      item._proportion_ = item._oldAngle_ - (item._oldAngle_ - item._endAngle_) * process;
+    }
+    if (item._proportion_ >= 2) {
+      item._proportion_ = item._proportion_ % 2;
+    }
+  }
+  return series;
+}
+
+function getPieTextMaxLength(series) {
+  series = getPieDataPoints(series);
+  let maxLength = 0;
+  for (let i = 0; i < series.length; i++) {
+    let item = series[i];
+    let text = item.format ? item.format(+item._proportion_.toFixed(2)) : util.toFixed(item._proportion_ * 100) + '%';
+    maxLength = Math.max(maxLength, measureText(text));
+  }
+
+  return maxLength;
+}
+
+function fixColumeData(points, eachSpacing, columnLen, index, config, opts) {
+  return points.map(function(item) {
+    if (item === null) {
+      return null;
+    }
+    item.width = Math.ceil((eachSpacing - 2 * config.columePadding) / columnLen);
+
+    if (opts.extra.column && opts.extra.column.width && +opts.extra.column.width > 0) {
+      item.width = Math.min(item.width, +opts.extra.column.width);
+    }
+    if (item.width <= 0) {
+      item.width = 1;
+    }
+    item.x += (index + 0.5 - columnLen / 2) * item.width;
+    return item;
+  });
+}
+
+function fixColumeMeterData(points, eachSpacing, columnLen, index, config, opts, border) {
+  return points.map(function(item) {
+    if (item === null) {
+      return null;
+    }
+    item.width = Math.ceil((eachSpacing - 2 * config.columePadding) / 2);
+
+    if (opts.extra.column && opts.extra.column.width && +opts.extra.column.width > 0) {
+      item.width = Math.min(item.width, +opts.extra.column.width);
+    }
+
+    if (index > 0) {
+      item.width -= 2 * border;
+    }
+    return item;
+  });
+}
+
+function fixColumeStackData(points, eachSpacing, columnLen, index, config, opts, series) {
+
+  return points.map(function(item, indexn) {
+
+    if (item === null) {
+      return null;
+    }
+    item.width = Math.ceil((eachSpacing - 2 * config.columePadding) / 2);
+
+    if (opts.extra.column && opts.extra.column.width && +opts.extra.column.width > 0) {
+      item.width = Math.min(item.width, +opts.extra.column.width);
+    }
+    return item;
+  });
+}
+
+function getXAxisPoints(categories, opts, config) {
+  var spacingValid = opts.width - opts.area[1] - opts.area[3];
+  var dataCount = opts.enableScroll ? Math.min(opts.xAxis.itemCount, categories.length) : categories.length;
+  if((opts.type=='line' || opts.type=='area') && dataCount>1 && opts.xAxis.boundaryGap=='justify'){
+    dataCount -=1;
+  }
+  var eachSpacing = spacingValid / dataCount;
+
+  var xAxisPoints = [];
+  var startX = opts.area[3];
+  var endX = opts.width - opts.area[1];
+  categories.forEach(function(item, index) {
+    xAxisPoints.push(startX + index * eachSpacing);
+  });
+  if(opts.xAxis.boundaryGap !=='justify'){
+    if (opts.enableScroll === true) {
+      xAxisPoints.push(startX + categories.length * eachSpacing);
+    } else {
+      xAxisPoints.push(endX);
+    }
+  }
+  return {
+    xAxisPoints: xAxisPoints,
+    startX: startX,
+    endX: endX,
+    eachSpacing: eachSpacing
+  };
+}
+
+function getCandleDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config) {
+  var process = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 1;
+  var points = [];
+  var validHeight = opts.height - opts.area[0] - opts.area[2];
+  data.forEach(function(item, index) {
+    if (item === null) {
+      points.push(null);
+    } else {
+      var cPoints = [];
+      item.forEach(function(items, indexs) {
+        var point = {};
+        point.x = xAxisPoints[index] + Math.round(eachSpacing / 2);
+        var value = items.value || items;
+        var height = validHeight * (value - minRange) / (maxRange - minRange);
+        height *= process;
+        point.y = opts.height - Math.round(height) - opts.area[2];
+        cPoints.push(point);
+      });
+      points.push(cPoints);
+    }
+  });
+
+  return points;
+}
+
+function getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config) {
+  var process = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 1;
+  var boundaryGap='center';
+  if (opts.type == 'line'||opts.type == 'area'){
+    boundaryGap=opts.xAxis.boundaryGap;
+  }
+  var points = [];
+  var validHeight = opts.height - opts.area[0] - opts.area[2];
+	var validWidth = opts.width - opts.area[1] - opts.area[3];
+  data.forEach(function(item, index) {
+    if (item === null) {
+      points.push(null);
+    } else {
+      var point = {};
+      point.color = item.color;
+      point.x = xAxisPoints[index];
+      var value = item;
+      if (typeof item === 'object' && item !== null) {
+				if (item.constructor == Array) {
+					let xranges,xminRange,xmaxRange;
+					xranges = [].concat(opts.chartData.xAxisData.ranges);
+					xminRange = xranges.shift();
+					xmaxRange = xranges.pop();
+				  value = item[1];
+					point.x = opts.area[3]+ validWidth * (item[0] - xminRange) / (xmaxRange - xminRange);
+				} else {
+				  value = item.value;
+				}
+      }
+			if(boundaryGap=='center'){
+			  point.x += Math.round(eachSpacing / 2);
+			}
+      var height = validHeight * (value - minRange) / (maxRange - minRange);
+      height *= process;
+      point.y = opts.height - Math.round(height) - opts.area[2];
+      points.push(point);
+    }
+  });
+
+  return points;
+}
+
+function getStackDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, seriesIndex, stackSeries) {
+  var process = arguments.length > 9 && arguments[9] !== undefined ? arguments[9] : 1;
+  var points = [];
+  var validHeight = opts.height - opts.area[0] - opts.area[2];
+
+  data.forEach(function(item, index) {
+    if (item === null) {
+      points.push(null);
+    } else {
+      var point = {};
+      point.color = item.color;
+      point.x = xAxisPoints[index] + Math.round(eachSpacing / 2);
+
+      if (seriesIndex > 0) {
+        var value = 0;
+        for (let i = 0; i <= seriesIndex; i++) {
+          value += stackSeries[i].data[index];
+        }
+        var value0 = value - item;
+        var height = validHeight * (value - minRange) / (maxRange - minRange);
+        var height0 = validHeight * (value0 - minRange) / (maxRange - minRange);
+      } else {
+        var value = item;
+        var height = validHeight * (value - minRange) / (maxRange - minRange);
+        var height0 = 0;
+      }
+      var heightc = height0;
+      height *= process;
+      heightc *= process;
+      point.y = opts.height - Math.round(height) - opts.area[2];
+      point.y0 = opts.height - Math.round(heightc) - opts.area[2];
+      points.push(point);
+    }
+  });
+
+  return points;
+}
+
+function getYAxisTextList(series, opts, config, stack) {
+  var index = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : -1;
+  var data;
+  if (stack == 'stack') {
+    data = dataCombineStack(series, opts.categories.length);
+  } else {
+    data = dataCombine(series);
+  }
+  var sorted = [];
+  // remove null from data
+  data = data.filter(function(item) {
+    //return item !== null;
+    if (typeof item === 'object' && item !== null) {
+      if (item.constructor == Array) {
+        return item !== null;
+      } else {
+        return item.value !== null;
+      }
+    } else {
+      return item !== null;
+    }
+  });
+  data.map(function(item) {
+    if (typeof item === 'object') {
+      if (item.constructor == Array) {
+				if(opts.type=='candle'){
+					item.map(function(subitem) {
+					  sorted.push(subitem);
+					})
+				}else{
+					sorted.push(item[1]);
+				}
+      } else {
+        sorted.push(item.value);
+      }
+    } else {
+      sorted.push(item);
+    }
+  })
+	
+  var minData = 0;
+  var maxData = 0;
+  if (sorted.length > 0) {
+    minData = Math.min.apply(this, sorted);
+    maxData = Math.max.apply(this, sorted);
+  }
+  //为了兼容v1.9.0之前的项目
+  if(index>-1){
+    if (typeof opts.yAxis.data[index].min === 'number') {
+      minData = Math.min(opts.yAxis.data[index].min, minData);
+    }
+    if (typeof opts.yAxis.data[index].max === 'number') {
+      maxData = Math.max(opts.yAxis.data[index].max, maxData);
+    }
+  }else{
+    if (typeof opts.yAxis.min === 'number') {
+      minData = Math.min(opts.yAxis.min, minData);
+    }
+    if (typeof opts.yAxis.max === 'number') {
+      maxData = Math.max(opts.yAxis.max, maxData);
+    }
+  }
+  
+
+  if (minData === maxData) {
+    var rangeSpan = maxData || 10;
+    maxData += rangeSpan;
+  }
+
+  var dataRange = getDataRange(minData, maxData);
+  var minRange = dataRange.minRange;
+  var maxRange = dataRange.maxRange;
+
+  var range = [];
+  var eachRange = (maxRange - minRange) / opts.yAxis.splitNumber;
+
+  for (var i = 0; i <= opts.yAxis.splitNumber; i++) {
+    range.push(minRange + eachRange * i);
+  }
+  return range.reverse();
+}
+
+function calYAxisData(series, opts, config) {
+  //堆叠图重算Y轴
+  var columnstyle = assign({}, {
+    type: ""
+  }, opts.extra.column);
+  //如果是多Y轴,重新计算
+  var YLength = opts.yAxis.data.length;
+  var newSeries=new Array(YLength);
+  if(YLength>0){
+    for(let i=0;i<YLength;i++){
+      newSeries[i]=[];
+      for(let j=0;j<series.length;j++){
+        if(series[j].index == i){
+          newSeries[i].push(series[j]);
+        }
+      }
+    }
+    var rangesArr =new Array(YLength);
+    var rangesFormatArr = new Array(YLength);
+    var yAxisWidthArr =new Array(YLength);
+		
+    for(let i=0;i<YLength;i++){
+      let yData = opts.yAxis.data[i];
+			//如果总开关不显示,强制每个Y轴为不显示
+			if(opts.yAxis.disabled == true){
+				yData.disabled = true;
+			}
+			rangesArr[i]=getYAxisTextList(newSeries[i], opts, config, columnstyle.type,i);
+			let yAxisFontSizes = yData.fontSize || config.fontSize;
+			yAxisWidthArr[i] = {position:yData.position?yData.position:'left',width:0};
+			rangesFormatArr[i]= rangesArr[i].map(function(items) {
+				items = util.toFixed(items, 6);
+				items = yData.format ? yData.format(Number(items)) : items;
+				yAxisWidthArr[i].width = Math.max(yAxisWidthArr[i].width, measureText(items, yAxisFontSizes) + 5);
+				return items;
+			});
+			let calibration= yData.calibration? 4*opts.pixelRatio : 0 ;
+			yAxisWidthArr[i].width += calibration +3*opts.pixelRatio;
+      if (yData.disabled === true) {
+        yAxisWidthArr[i].width=0;
+      }
+    }
+    
+  }else{
+    var rangesArr =new Array(1);
+    var rangesFormatArr = new Array(1);
+    var yAxisWidthArr =new Array(1);
+		rangesArr[0] = getYAxisTextList(series, opts, config, columnstyle.type);
+		yAxisWidthArr[0] = {position:'left',width:0};
+		var yAxisFontSize = opts.yAxis.fontSize || config.fontSize;
+		rangesFormatArr[0] = rangesArr[0].map(function(item) {
+			item = util.toFixed(item, 6);
+			item = opts.yAxis.format ? opts.yAxis.format(Number(item)) : item;
+			yAxisWidthArr[0].width = Math.max(yAxisWidthArr[0].width, measureText(item, yAxisFontSize) + 5);
+			return item;
+		});
+		yAxisWidthArr[0].width += 3*opts.pixelRatio;
+		if (opts.yAxis.disabled === true) {
+		  yAxisWidthArr[0] = {position:'left',width:0};
+		  opts.yAxis.data[0]={disabled:true};
+		}else{
+			opts.yAxis.data[0]={disabled:false,position:'left',max:opts.yAxis.max,min:opts.yAxis.min,format:opts.yAxis.format};
+		}
+    
+  }
+
+  return {
+    rangesFormat: rangesFormatArr,
+    ranges: rangesArr,
+    yAxisWidth: yAxisWidthArr
+  };
+  
+}
+
+function calTooltipYAxisData(point, series, opts, config, eachSpacing) {
+  let ranges = [].concat(opts.chartData.yAxisData.ranges);
+  let spacingValid = opts.height - opts.area[0] - opts.area[2];
+  let minAxis = opts.area[0];
+  let items=[];
+  for(let i=0;i<ranges.length;i++){
+    let maxVal = ranges[i].shift();
+    let minVal = ranges[i].pop();
+    let item = maxVal - (maxVal - minVal) * (point - minAxis) / spacingValid;
+    item = opts.yAxis.data[i].format ? opts.yAxis.data[i].format(Number(item)) : item.toFixed(0);
+    items.push(String(item))
+  }
+  return items;
+}
+
+function calMarkLineData(points, opts) {
+  let minRange, maxRange;
+  let spacingValid = opts.height - opts.area[0] - opts.area[2];
+  for (let i = 0; i < points.length; i++) {
+    points[i].yAxisIndex = points[i].yAxisIndex ? points[i].yAxisIndex:0;
+    let range = [].concat(opts.chartData.yAxisData.ranges[points[i].yAxisIndex]);
+    minRange = range.pop();
+    maxRange = range.shift();
+    let height = spacingValid * (points[i].value - minRange) / (maxRange - minRange);
+    points[i].y = opts.height - Math.round(height) - opts.area[2];
+  }
+  return points;
+}
+
+function contextRotate(context, opts) {
+  if (opts.rotateLock !== true) {
+    context.translate(opts.height, 0);
+    context.rotate(90 * Math.PI / 180);
+  } else if (opts._rotate_ !== true) {
+    context.translate(opts.height, 0);
+    context.rotate(90 * Math.PI / 180);
+    opts._rotate_ = true;
+  }
+}
+
+function drawPointShape(points, color, shape, context, opts) {
+  context.beginPath();
+	if(opts.dataPointShapeType == 'hollow'){
+		context.setStrokeStyle(color);
+		context.setFillStyle(opts.background);
+		context.setLineWidth(2 * opts.pixelRatio);
+	}else{
+		context.setStrokeStyle("#ffffff");
+		context.setFillStyle(color);
+		context.setLineWidth(1 * opts.pixelRatio);
+	}
+  if (shape === 'diamond') {
+    points.forEach(function(item, index) {
+      if (item !== null) {
+        context.moveTo(item.x, item.y - 4.5);
+        context.lineTo(item.x - 4.5, item.y);
+        context.lineTo(item.x, item.y + 4.5);
+        context.lineTo(item.x + 4.5, item.y);
+        context.lineTo(item.x, item.y - 4.5);
+      }
+    });
+  } else if (shape === 'circle') {
+    points.forEach(function(item, index) {
+      if (item !== null) {
+        context.moveTo(item.x + 2.5 * opts.pixelRatio, item.y);
+        context.arc(item.x, item.y, 3 * opts.pixelRatio, 0, 2 * Math.PI, false);
+      }
+    });
+  } else if (shape === 'rect') {
+    points.forEach(function(item, index) {
+      if (item !== null) {
+        context.moveTo(item.x - 3.5, item.y - 3.5);
+        context.rect(item.x - 3.5, item.y - 3.5, 7, 7);
+      }
+    });
+  } else if (shape === 'triangle') {
+    points.forEach(function(item, index) {
+      if (item !== null) {
+        context.moveTo(item.x, item.y - 4.5);
+        context.lineTo(item.x - 4.5, item.y + 4.5);
+        context.lineTo(item.x + 4.5, item.y + 4.5);
+        context.lineTo(item.x, item.y - 4.5);
+      }
+    });
+  }
+  context.closePath();
+  context.fill();
+  context.stroke();
+}
+
+function drawRingTitle(opts, config, context, center) {
+  var titlefontSize = opts.title.fontSize || config.titleFontSize;
+  var subtitlefontSize = opts.subtitle.fontSize || config.subtitleFontSize;
+  var title = opts.title.name || '';
+  var subtitle = opts.subtitle.name || '';
+  var titleFontColor = opts.title.color || config.titleColor;
+  var subtitleFontColor = opts.subtitle.color || config.subtitleColor;
+  var titleHeight = title ? titlefontSize : 0;
+  var subtitleHeight = subtitle ? subtitlefontSize : 0;
+  var margin = 5;
+
+  if (subtitle) {
+    var textWidth = measureText(subtitle, subtitlefontSize);
+    var startX = center.x - textWidth / 2 + (opts.subtitle.offsetX || 0);
+    var startY = center.y + subtitlefontSize / 2 + (opts.subtitle.offsetY || 0);
+    if (title) {
+      startY += (titleHeight + margin) / 2;
+    }
+    context.beginPath();
+    context.setFontSize(subtitlefontSize);
+    context.setFillStyle(subtitleFontColor);
+    context.fillText(subtitle, startX, startY);
+    context.closePath();
+    context.stroke();
+  }
+  if (title) {
+    var _textWidth = measureText(title, titlefontSize);
+    var _startX = center.x - _textWidth / 2 + (opts.title.offsetX || 0);
+    var _startY = center.y + titlefontSize / 2 + (opts.title.offsetY || 0);
+    if (subtitle) {
+      _startY -= (subtitleHeight + margin) / 2;
+    }
+    context.beginPath();
+    context.setFontSize(titlefontSize);
+    context.setFillStyle(titleFontColor);
+    context.fillText(title, _startX, _startY);
+    context.closePath();
+    context.stroke();
+  }
+}
+
+function drawPointText(points, series, config, context) {
+  // 绘制数据文案
+  var data = series.data;
+  points.forEach(function(item, index) {
+    if (item !== null) {
+      //var formatVal = series.format ? series.format(data[index]) : data[index];
+      context.beginPath();
+      context.setFontSize(series.textSize || config.fontSize);
+      context.setFillStyle(series.textColor || '#666666');
+      var value = data[index]
+      if (typeof data[index] === 'object' && data[index] !== null) {
+				if (data[index].constructor == Array) {
+					value = data[index][1];
+				}else{
+					value = data[index].value
+				}
+      }
+      var formatVal = series.format ? series.format(value) : value;
+      context.fillText(String(formatVal), item.x - measureText(formatVal, series.textSize || config.fontSize) / 2, item.y -4);
+      context.closePath();
+      context.stroke();
+    }
+  });
+
+}
+
+function drawGaugeLabel(gaugeOption, radius, centerPosition, opts, config, context) {
+  radius -= gaugeOption.width / 2 + config.gaugeLabelTextMargin;
+
+  let totalAngle = gaugeOption.startAngle - gaugeOption.endAngle + 1;
+  let splitAngle = totalAngle / gaugeOption.splitLine.splitNumber;
+  let totalNumber = gaugeOption.endNumber - gaugeOption.startNumber;
+  let splitNumber = totalNumber / gaugeOption.splitLine.splitNumber;
+  let nowAngle = gaugeOption.startAngle;
+  let nowNumber = gaugeOption.startNumber;
+  for (let i = 0; i < gaugeOption.splitLine.splitNumber + 1; i++) {
+    var pos = {
+      x: radius * Math.cos(nowAngle * Math.PI),
+      y: radius * Math.sin(nowAngle * Math.PI)
+    };
+    var labelText = gaugeOption.labelFormat ? gaugeOption.labelFormat(nowNumber) : nowNumber;
+    pos.x += centerPosition.x - measureText(labelText) / 2;
+    pos.y += centerPosition.y;
+    var startX = pos.x;
+    var startY = pos.y;
+    context.beginPath();
+    context.setFontSize(config.fontSize);
+    context.setFillStyle(gaugeOption.labelColor || '#666666');
+    context.fillText(labelText, startX, startY + config.fontSize / 2);
+    context.closePath();
+    context.stroke();
+
+    nowAngle += splitAngle;
+    if (nowAngle >= 2) {
+      nowAngle = nowAngle % 2;
+    }
+    nowNumber += splitNumber;
+  }
+
+}
+
+function drawRadarLabel(angleList, radius, centerPosition, opts, config, context) {
+  var radarOption = opts.extra.radar || {};
+  radius += config.radarLabelTextMargin;
+
+  angleList.forEach(function(angle, index) {
+    var pos = {
+      x: radius * Math.cos(angle),
+      y: radius * Math.sin(angle)
+    };
+    var posRelativeCanvas = convertCoordinateOrigin(pos.x, pos.y, centerPosition);
+    var startX = posRelativeCanvas.x;
+    var startY = posRelativeCanvas.y;
+    if (util.approximatelyEqual(pos.x, 0)) {
+      startX -= measureText(opts.categories[index] || '') / 2;
+    } else if (pos.x < 0) {
+      startX -= measureText(opts.categories[index] || '');
+    }
+    context.beginPath();
+    context.setFontSize(config.fontSize);
+    context.setFillStyle(radarOption.labelColor || '#666666');
+    context.fillText(opts.categories[index] || '', startX, startY + config.fontSize / 2);
+    context.closePath();
+    context.stroke();
+  });
+
+}
+
+function drawPieText(series, opts, config, context, radius, center) {
+  var lineRadius = config.pieChartLinePadding;
+  var textObjectCollection = [];
+  var lastTextObject = null;
+
+  var seriesConvert = series.map(function(item) {
+    var text = item.format ? item.format(+item._proportion_.toFixed(2)) : util.toFixed(item._proportion_.toFixed(4) * 100) +'%';
+    if(item._rose_proportion_) item._proportion_=item._rose_proportion_;
+    var arc = 2 * Math.PI - (item._start_ + 2 * Math.PI * item._proportion_ / 2);
+    var color = item.color;
+    var radius = item._radius_;
+    return {
+      arc: arc,
+      text: text,
+      color: color,
+      radius: radius,
+      textColor: item.textColor,
+      textSize: item.textSize,
+    };
+  });
+  for (let i = 0; i < seriesConvert.length; i++) {
+    let item = seriesConvert[i];
+    // line end
+    let orginX1 = Math.cos(item.arc) * (item.radius + lineRadius);
+    let orginY1 = Math.sin(item.arc) * (item.radius + lineRadius);
+
+    // line start
+    let orginX2 = Math.cos(item.arc) * item.radius;
+    let orginY2 = Math.sin(item.arc) * item.radius;
+
+    // text start
+    let orginX3 = orginX1 >= 0 ? orginX1 + config.pieChartTextPadding : orginX1 - config.pieChartTextPadding;
+    let orginY3 = orginY1;
+    let textWidth = measureText(item.text,item.textSize||config.fontSize);
+    let startY = orginY3;
+
+    if (lastTextObject && util.isSameXCoordinateArea(lastTextObject.start, {
+        x: orginX3
+      })) {
+      if (orginX3 > 0) {
+        startY = Math.min(orginY3, lastTextObject.start.y);
+      } else if (orginX1 < 0) {
+        startY = Math.max(orginY3, lastTextObject.start.y);
+      } else {
+        if (orginY3 > 0) {
+          startY = Math.max(orginY3, lastTextObject.start.y);
+        } else {
+          startY = Math.min(orginY3, lastTextObject.start.y);
+        }
+      }
+    }
+    if (orginX3 < 0) {
+      orginX3 -= textWidth;
+    }
+
+    let textObject = {
+      lineStart: {
+        x: orginX2,
+        y: orginY2
+      },
+      lineEnd: {
+        x: orginX1,
+        y: orginY1
+      },
+      start: {
+        x: orginX3,
+        y: startY
+      },
+      width: textWidth,
+      height: config.fontSize,
+      text: item.text,
+      color: item.color,
+      textColor: item.textColor,
+      textSize: item.textSize
+    };
+    lastTextObject = avoidCollision(textObject, lastTextObject);
+    textObjectCollection.push(lastTextObject);
+  }
+
+  for (let i = 0; i < textObjectCollection.length; i++) {
+    let item = textObjectCollection[i];
+    let lineStartPoistion = convertCoordinateOrigin(item.lineStart.x, item.lineStart.y, center);
+    let lineEndPoistion = convertCoordinateOrigin(item.lineEnd.x, item.lineEnd.y, center);
+    let textPosition = convertCoordinateOrigin(item.start.x, item.start.y, center);
+    context.setLineWidth(1 * opts.pixelRatio);
+    context.setFontSize(config.fontSize);
+    context.beginPath();
+    context.setStrokeStyle(item.color);
+    context.setFillStyle(item.color);
+    context.moveTo(lineStartPoistion.x, lineStartPoistion.y);
+    let curveStartX = item.start.x < 0 ? textPosition.x + item.width : textPosition.x;
+    let textStartX = item.start.x < 0 ? textPosition.x - 5 : textPosition.x + 5;
+    context.quadraticCurveTo(lineEndPoistion.x, lineEndPoistion.y, curveStartX, textPosition.y);
+    context.moveTo(lineStartPoistion.x, lineStartPoistion.y);
+    context.stroke();
+    context.closePath();
+    context.beginPath();
+    context.moveTo(textPosition.x + item.width, textPosition.y);
+    context.arc(curveStartX, textPosition.y, 2, 0, 2 * Math.PI);
+    context.closePath();
+    context.fill();
+    context.beginPath();
+    context.setFontSize(item.textSize || config.fontSize);
+    context.setFillStyle(item.textColor || '#666666');
+    context.fillText(item.text, textStartX, textPosition.y + 3);
+    context.closePath();
+    context.stroke();
+    context.closePath();
+  }
+}
+
+function drawToolTipSplitLine(offsetX, opts, config, context) {
+  var toolTipOption = opts.extra.tooltip || {};
+  toolTipOption.gridType = toolTipOption.gridType == undefined ? 'solid' : toolTipOption.gridType;
+  toolTipOption.dashLength = toolTipOption.dashLength == undefined ? 4 : toolTipOption.dashLength;
+  var startY = opts.area[0];
+  var endY = opts.height - opts.area[2];
+
+  if (toolTipOption.gridType == 'dash') {
+    context.setLineDash([toolTipOption.dashLength, toolTipOption.dashLength]);
+  }
+  context.setStrokeStyle(toolTipOption.gridColor || '#cccccc');
+  context.setLineWidth(1 * opts.pixelRatio);
+  context.beginPath();
+  context.moveTo(offsetX, startY);
+  context.lineTo(offsetX, endY);
+  context.stroke();
+  context.setLineDash([]);
+
+  if (toolTipOption.xAxisLabel) {
+    let labelText = opts.categories[opts.tooltip.index];
+    context.setFontSize(config.fontSize);
+    let textWidth = measureText(labelText, config.fontSize);
+
+    let textX = offsetX - 0.5 * textWidth;
+    let textY = endY;
+    context.beginPath();
+    context.setFillStyle(hexToRgb(toolTipOption.labelBgColor || config.toolTipBackground, toolTipOption.labelBgOpacity || config.toolTipOpacity));
+    context.setStrokeStyle(toolTipOption.labelBgColor || config.toolTipBackground);
+    context.setLineWidth(1 * opts.pixelRatio);
+    context.rect(textX - config.toolTipPadding, textY, textWidth + 2 * config.toolTipPadding, config.fontSize + 2 * config.toolTipPadding);
+    context.closePath();
+    context.stroke();
+    context.fill();
+
+    context.beginPath();
+    context.setFontSize(config.fontSize);
+    context.setFillStyle(toolTipOption.labelFontColor || config.fontColor);
+    context.fillText(String(labelText), textX, textY + config.toolTipPadding + config.fontSize);
+    context.closePath();
+    context.stroke();
+  }
+}
+
+function drawMarkLine(opts, config, context) {
+  let markLineOption = assign({}, {
+    type: 'solid',
+    dashLength: 4,
+    data: []
+  }, opts.extra.markLine);
+  let startX = opts.area[3];
+  let endX = opts.width - opts.area[1];
+  let points = calMarkLineData(markLineOption.data, opts);
+
+  for (let i = 0; i < points.length; i++) {
+    let item = assign({}, {
+      lineColor: '#DE4A42',
+      showLabel: false,
+      labelFontColor: '#666666',
+      labelBgColor: '#DFE8FF',
+      labelBgOpacity: 0.8,
+      yAxisIndex: 0
+    }, points[i]);
+
+    if (markLineOption.type == 'dash') {
+      context.setLineDash([markLineOption.dashLength, markLineOption.dashLength]);
+    }
+    context.setStrokeStyle(item.lineColor);
+    context.setLineWidth(1 * opts.pixelRatio);
+    context.beginPath();
+    context.moveTo(startX, item.y);
+    context.lineTo(endX, item.y);
+    context.stroke();
+    context.setLineDash([]);
+    if (item.showLabel) {
+      let labelText = opts.yAxis.format ? opts.yAxis.format(Number(item.value)) : item.value;
+      context.setFontSize(config.fontSize);
+      let textWidth = measureText(labelText, config.fontSize);
+      let bgStartX = opts.padding[3] + config.yAxisTitleWidth - config.toolTipPadding;
+      let bgEndX = Math.max(opts.area[3], textWidth + config.toolTipPadding * 2);
+      let bgWidth = bgEndX - bgStartX;
+
+      let textX = bgStartX + (bgWidth - textWidth) / 2;
+      let textY = item.y;
+      context.setFillStyle(hexToRgb(item.labelBgColor, item.labelBgOpacity));
+      context.setStrokeStyle(item.labelBgColor);
+      context.setLineWidth(1 * opts.pixelRatio);
+      context.beginPath();
+      context.rect(bgStartX, textY - 0.5 * config.fontSize - config.toolTipPadding, bgWidth, config.fontSize + 2 * config.toolTipPadding);
+      context.closePath();
+      context.stroke();
+      context.fill();
+
+      context.beginPath();
+      context.setFontSize(config.fontSize);
+      context.setFillStyle(item.labelFontColor);
+      context.fillText(String(labelText), textX, textY + 0.5 * config.fontSize);
+      context.stroke();
+    }
+  }
+}
+
+function drawToolTipHorizentalLine(opts, config, context, eachSpacing, xAxisPoints) {
+  var toolTipOption = assign({}, {
+    gridType: 'solid',
+    dashLength: 4
+  }, opts.extra.tooltip);
+
+  var startX = opts.area[3];
+  var endX = opts.width - opts.area[1];
+
+  if (toolTipOption.gridType == 'dash') {
+    context.setLineDash([toolTipOption.dashLength, toolTipOption.dashLength]);
+  }
+  context.setStrokeStyle(toolTipOption.gridColor || '#cccccc');
+  context.setLineWidth(1 * opts.pixelRatio);
+  context.beginPath();
+  context.moveTo(startX, opts.tooltip.offset.y);
+  context.lineTo(endX, opts.tooltip.offset.y);
+  context.stroke();
+  context.setLineDash([]);
+
+  if (toolTipOption.yAxisLabel) {
+    let labelText = calTooltipYAxisData(opts.tooltip.offset.y, opts.series, opts, config, eachSpacing);
+    let widthArr = opts.chartData.yAxisData.yAxisWidth;
+    let tStartLeft=opts.area[3];
+    let tStartRight=opts.width-opts.area[1];
+    for(let i=0;i<labelText.length;i++){
+      context.setFontSize(config.fontSize);
+      let textWidth = measureText(labelText[i], config.fontSize);
+      let bgStartX,bgEndX,bgWidth;
+      if(widthArr[i].position == 'left'){
+        bgStartX = tStartLeft - widthArr[i].width;
+        bgEndX = Math.max(bgStartX, bgStartX + textWidth + config.toolTipPadding * 2);
+      }else{
+        bgStartX = tStartRight;
+        bgEndX = Math.max(bgStartX + widthArr[i].width, bgStartX + textWidth + config.toolTipPadding * 2);
+      }
+      bgWidth = bgEndX - bgStartX;
+      
+      let textX = bgStartX + (bgWidth - textWidth) / 2;
+      let textY = opts.tooltip.offset.y;
+      context.beginPath();
+      context.setFillStyle(hexToRgb(toolTipOption.labelBgColor || config.toolTipBackground, toolTipOption.labelBgOpacity || config.toolTipOpacity));
+      context.setStrokeStyle(toolTipOption.labelBgColor || config.toolTipBackground);
+      context.setLineWidth(1 * opts.pixelRatio);
+      context.rect(bgStartX, textY - 0.5 * config.fontSize - config.toolTipPadding, bgWidth, config.fontSize + 2 * config.toolTipPadding);
+      context.closePath();
+      context.stroke();
+      context.fill();
+      
+      context.beginPath();
+      context.setFontSize(config.fontSize);
+      context.setFillStyle(toolTipOption.labelFontColor || config.fontColor);
+      context.fillText(labelText[i], textX, textY + 0.5 * config.fontSize);
+      context.closePath();
+      context.stroke();
+      if(widthArr[i].position == 'left'){
+        tStartLeft -=(widthArr[i].width + opts.yAxis.padding);
+      }else{
+        tStartRight +=widthArr[i].width+ opts.yAxis.padding;
+      }
+    }
+  }
+}
+
+function drawToolTipSplitArea(offsetX, opts, config, context, eachSpacing) {
+  var toolTipOption = assign({}, {
+    activeBgColor: '#000000',
+    activeBgOpacity: 0.08
+  }, opts.extra.tooltip);
+  var startY = opts.area[0];
+  var endY = opts.height - opts.area[2];
+  context.beginPath();
+  context.setFillStyle(hexToRgb(toolTipOption.activeBgColor, toolTipOption.activeBgOpacity));
+  context.rect(offsetX - eachSpacing / 2, startY, eachSpacing, endY - startY);
+  context.closePath();
+  context.fill();
+}
+
+function drawToolTip(textList, offset, opts, config, context, eachSpacing, xAxisPoints) {
+  var toolTipOption = assign({}, {
+		showBox:true,
+    bgColor: '#000000',
+    bgOpacity: 0.7,
+    fontColor: '#FFFFFF'
+  }, opts.extra.tooltip);
+  var legendWidth = 4 * opts.pixelRatio;
+  var legendMarginRight = 5 * opts.pixelRatio;
+  var arrowWidth = 8 * opts.pixelRatio;
+  var isOverRightBorder = false;
+  if (opts.type == 'line' || opts.type == 'area' || opts.type == 'candle' || opts.type == 'mix') {
+    drawToolTipSplitLine(opts.tooltip.offset.x, opts, config, context);
+  }
+
+  offset = assign({
+    x: 0,
+    y: 0
+  }, offset);
+  offset.y -= 8 * opts.pixelRatio;
+  var textWidth = textList.map(function(item) {
+    return measureText(item.text, config.fontSize);
+  });
+  var toolTipWidth = legendWidth + legendMarginRight + 4 * config.toolTipPadding + Math.max.apply(null, textWidth);
+  var toolTipHeight = 2 * config.toolTipPadding + textList.length * config.toolTipLineHeight;
+
+	if(toolTipOption.showBox == false){ return }
+  // if beyond the right border
+  if (offset.x - Math.abs(opts._scrollDistance_) + arrowWidth + toolTipWidth > opts.width) {
+    isOverRightBorder = true;
+  }
+  if (toolTipHeight + offset.y > opts.height) {
+    offset.y = opts.height - toolTipHeight;
+  }
+  // draw background rect
+  context.beginPath();
+  context.setFillStyle(hexToRgb(toolTipOption.bgColor || config.toolTipBackground, toolTipOption.bgOpacity || config.toolTipOpacity));
+  if (isOverRightBorder) {
+    context.moveTo(offset.x, offset.y + 10 * opts.pixelRatio);
+    context.lineTo(offset.x - arrowWidth, offset.y + 10 * opts.pixelRatio - 5 * opts.pixelRatio);
+    context.lineTo(offset.x - arrowWidth, offset.y);
+    context.lineTo(offset.x - arrowWidth - Math.round(toolTipWidth), offset.y);
+    context.lineTo(offset.x - arrowWidth - Math.round(toolTipWidth), offset.y + toolTipHeight);
+    context.lineTo(offset.x - arrowWidth, offset.y + toolTipHeight);
+    context.lineTo(offset.x - arrowWidth, offset.y + 10 * opts.pixelRatio + 5 * opts.pixelRatio);
+    context.lineTo(offset.x, offset.y + 10 * opts.pixelRatio);
+  } else {
+    context.moveTo(offset.x, offset.y + 10 * opts.pixelRatio);
+    context.lineTo(offset.x + arrowWidth, offset.y + 10 * opts.pixelRatio - 5 * opts.pixelRatio);
+    context.lineTo(offset.x + arrowWidth, offset.y);
+    context.lineTo(offset.x + arrowWidth + Math.round(toolTipWidth), offset.y);
+    context.lineTo(offset.x + arrowWidth + Math.round(toolTipWidth), offset.y + toolTipHeight);
+    context.lineTo(offset.x + arrowWidth, offset.y + toolTipHeight);
+    context.lineTo(offset.x + arrowWidth, offset.y + 10 * opts.pixelRatio + 5 * opts.pixelRatio);
+    context.lineTo(offset.x, offset.y + 10 * opts.pixelRatio);
+  }
+
+  context.closePath();
+  context.fill();
+
+  // draw legend
+  textList.forEach(function(item, index) {
+    if (item.color !== null) {
+      context.beginPath();
+      context.setFillStyle(item.color);
+      var startX = offset.x + arrowWidth + 2 * config.toolTipPadding;
+      var startY = offset.y + (config.toolTipLineHeight - config.fontSize) / 2 + config.toolTipLineHeight * index +
+        config.toolTipPadding + 1;
+      if (isOverRightBorder) {
+        startX = offset.x - toolTipWidth - arrowWidth + 2 * config.toolTipPadding;
+      }
+      context.fillRect(startX, startY, legendWidth, config.fontSize);
+      context.closePath();
+    }
+  });
+
+  // draw text list
+
+  textList.forEach(function(item, index) {
+    var startX = offset.x + arrowWidth + 2 * config.toolTipPadding + legendWidth + legendMarginRight;
+    if (isOverRightBorder) {
+      startX = offset.x - toolTipWidth - arrowWidth + 2 * config.toolTipPadding + +legendWidth + legendMarginRight;
+    }
+    var startY = offset.y + (config.toolTipLineHeight - config.fontSize) / 2 + config.toolTipLineHeight * index +
+      config.toolTipPadding;
+    context.beginPath();
+    context.setFontSize(config.fontSize);
+    context.setFillStyle(toolTipOption.fontColor);
+    context.fillText(item.text, startX, startY + config.fontSize);
+    context.closePath();
+    context.stroke();
+  });
+}
+
+function drawYAxisTitle(title, opts, config, context) {
+  var startX = config.xAxisHeight + (opts.height - config.xAxisHeight - measureText(title)) / 2;
+  context.save();
+  context.beginPath();
+  context.setFontSize(config.fontSize);
+  context.setFillStyle(opts.yAxis.titleFontColor || '#333333');
+  context.translate(0, opts.height);
+  context.rotate(-90 * Math.PI / 180);
+  context.fillText(title, startX, opts.padding[3] + 0.5 * config.fontSize);
+  context.closePath();
+  context.stroke();
+  context.restore();
+}
+
+function drawColumnDataPoints(series, opts, config, context) {
+  let process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
+  let xAxisData = opts.chartData.xAxisData,
+    xAxisPoints = xAxisData.xAxisPoints,
+    eachSpacing = xAxisData.eachSpacing;
+  let columnOption = assign({}, {
+    type: 'group',
+    width: eachSpacing / 2,
+    meter: {
+      border: 4,
+      fillColor: '#FFFFFF'
+    }
+  }, opts.extra.column);
+  
+  let calPoints = [];
+  context.save();
+	
+	let leftNum=-2;
+	let rightNum=xAxisPoints.length+2;
+	
+  if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
+    context.translate(opts._scrollDistance_, 0);
+		leftNum=Math.floor(-opts._scrollDistance_/eachSpacing)-2;
+		rightNum=leftNum+opts.xAxis.itemCount+4;
+  }
+  if (opts.tooltip && opts.tooltip.textList && opts.tooltip.textList.length && process === 1) {
+    drawToolTipSplitArea(opts.tooltip.offset.x, opts, config, context, eachSpacing);
+  }
+	
+  series.forEach(function(eachSeries, seriesIndex) {
+    let ranges,minRange,maxRange;
+    ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
+    minRange = ranges.pop();
+    maxRange = ranges.shift();
+    
+    var data = eachSeries.data;
+    switch (columnOption.type) {
+      case 'group':
+        var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
+        var tooltipPoints = getStackDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, seriesIndex, series, process);
+        calPoints.push(tooltipPoints);
+        points = fixColumeData(points, eachSpacing, series.length, seriesIndex, config, opts);
+				for(let i=0;i<points.length;i++){
+					let item=points[i];
+          if (item !== null && i>leftNum && i<rightNum) {
+            context.beginPath();
+            context.setStrokeStyle(item.color || eachSeries.color);
+            context.setLineWidth(1)
+            context.setFillStyle(item.color || eachSeries.color);
+            var startX = item.x - item.width / 2;
+            var height = opts.height - item.y - opts.area[2];
+            context.moveTo(startX, item.y);
+            context.lineTo(startX+item.width-2,item.y);
+            context.lineTo(startX+item.width-2,opts.height - opts.area[2]);
+            context.lineTo(startX,opts.height - opts.area[2]);
+            context.lineTo(startX,item.y);
+            context.closePath();
+            context.stroke();
+            context.fill();
+          }
+        };
+        break;
+      case 'stack':
+        // 绘制堆叠数据图
+        var points = getStackDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, seriesIndex, series, process);
+        calPoints.push(points);
+        points = fixColumeStackData(points, eachSpacing, series.length, seriesIndex, config, opts, series);
+
+        for(let i=0;i<points.length;i++){
+        	let item=points[i];
+          if (item !== null && i>leftNum && i<rightNum) {
+            context.beginPath();
+            context.setFillStyle(item.color || eachSeries.color);
+            var startX = item.x - item.width / 2 + 1;
+            var height = opts.height - item.y - opts.area[2];
+            var height0 = opts.height - item.y0 - opts.area[2];
+            if (seriesIndex > 0) {
+              height -= height0;
+            }
+            context.moveTo(startX, item.y);
+            context.fillRect(startX, item.y, item.width - 2, height);
+            context.closePath();
+            context.fill();
+          }
+        };
+        break;
+      case 'meter':
+        // 绘制温度计数据图
+        var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
+        calPoints.push(points);
+        points = fixColumeMeterData(points, eachSpacing, series.length, seriesIndex, config, opts, columnOption.meter.border);
+        if (seriesIndex == 0) {
+          for(let i=0;i<points.length;i++){
+          	let item=points[i];
+            if (item !== null && i>leftNum && i<rightNum) {
+              //画背景颜色
+              context.beginPath();
+              context.setFillStyle(columnOption.meter.fillColor);
+              var startX = item.x - item.width / 2;
+              var height = opts.height - item.y - opts.area[2];
+              context.moveTo(startX, item.y);
+              context.fillRect(startX, item.y, item.width, height);
+              context.closePath();
+              context.fill();
+              //画边框线
+              if (columnOption.meter.border > 0) {
+                context.beginPath();
+                context.setStrokeStyle(eachSeries.color);
+                context.setLineWidth(columnOption.meter.border * opts.pixelRatio);
+                context.moveTo(startX + columnOption.meter.border * 0.5, item.y + height);
+                context.lineTo(startX + columnOption.meter.border * 0.5, item.y + columnOption.meter.border * 0.5);
+                context.lineTo(startX + item.width - columnOption.meter.border * 0.5, item.y + columnOption.meter.border * 0.5);
+                context.lineTo(startX + item.width - columnOption.meter.border * 0.5, item.y + height);
+                context.stroke();
+              }
+            }
+          };
+        } else {
+          for(let i=0;i<points.length;i++){
+          	let item=points[i];
+            if (item !== null && i>leftNum && i<rightNum) {
+              context.beginPath();
+              context.setFillStyle(item.color || eachSeries.color);
+              var startX = item.x - item.width / 2;
+              var height = opts.height - item.y - opts.area[2];
+              context.moveTo(startX, item.y);
+              context.fillRect(startX, item.y, item.width, height);
+              context.closePath();
+              context.fill();
+            }
+          };
+        }
+        break;
+    }
+  });
+
+  if (opts.dataLabel !== false && process === 1) {
+    series.forEach(function(eachSeries, seriesIndex) {
+      let ranges,minRange,maxRange;
+        ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
+        minRange = ranges.pop();
+        maxRange = ranges.shift();
+      var data = eachSeries.data;
+      switch (columnOption.type) {
+        case 'group':
+          var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
+          points = fixColumeData(points, eachSpacing, series.length, seriesIndex, config, opts);
+          drawPointText(points, eachSeries, config, context);
+          break;
+        case 'stack':
+          var points = getStackDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, seriesIndex, series, process);
+          drawPointText(points, eachSeries, config, context);
+          break;
+        case 'meter':
+          var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
+          drawPointText(points, eachSeries, config, context);
+          break;
+      }
+    });
+  }
+
+  context.restore();
+
+  return {
+    xAxisPoints: xAxisPoints,
+    calPoints: calPoints,
+    eachSpacing: eachSpacing
+  };
+}
+
+function drawCandleDataPoints(series, seriesMA, opts, config, context) {
+  var process = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 1;
+  var candleOption = assign({}, {
+    color: {},
+    average: {}
+  }, opts.extra.candle);
+  candleOption.color = assign({}, {
+    upLine: '#f04864',
+    upFill: '#f04864',
+    downLine: '#2fc25b',
+    downFill: '#2fc25b'
+  }, candleOption.color);
+  candleOption.average = assign({}, {
+    show: false,
+    name: [],
+    day: [],
+    color: config.colors
+  }, candleOption.average);
+  opts.extra.candle = candleOption;
+
+  let xAxisData = opts.chartData.xAxisData,
+    xAxisPoints = xAxisData.xAxisPoints,
+    eachSpacing = xAxisData.eachSpacing;
+
+  let calPoints = [];
+
+  context.save();
+	
+	let leftNum=-2;
+	let rightNum=xAxisPoints.length+2;
+	let leftSpace=0;
+	let rightSpace=opts.width+eachSpacing;
+	
+  if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
+    context.translate(opts._scrollDistance_, 0);
+		leftNum=Math.floor(-opts._scrollDistance_/eachSpacing)-2;
+		rightNum=leftNum+opts.xAxis.itemCount+4;
+		leftSpace=-opts._scrollDistance_-eachSpacing+opts.area[3];
+		rightSpace=leftSpace+(opts.xAxis.itemCount+4)*eachSpacing;
+  }
+
+  //画均线
+  if (candleOption.average.show) {
+    seriesMA.forEach(function(eachSeries, seriesIndex) {
+      let ranges,minRange,maxRange;
+      ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
+      minRange = ranges.pop();
+      maxRange = ranges.shift();
+
+      var data = eachSeries.data;
+      var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
+      var splitPointList = splitPoints(points);
+			
+			for(let i=0;i<splitPointList.length;i++){
+				let points=splitPointList[i];
+				context.beginPath();
+				context.setStrokeStyle(eachSeries.color);
+				context.setLineWidth(1);
+				if (points.length === 1) {
+					context.moveTo(points[0].x, points[0].y);
+					context.arc(points[0].x, points[0].y, 1, 0, 2 * Math.PI);
+				} else {
+					context.moveTo(points[0].x, points[0].y);
+					let startPoint=0;
+					for(let j=0;j<points.length;j++){
+						let item=points[j];
+						if(startPoint==0 && item.x > leftSpace){
+							context.moveTo(item.x, item.y);
+							startPoint=1;
+						}
+						if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
+							var ctrlPoint = createCurveControlPoints(points, j - 1);
+							context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y,item.x, item.y);
+						}
+					}
+					context.moveTo(points[0].x, points[0].y);
+				}
+				context.closePath();
+				context.stroke();
+      }
+    });
+  }
+  //画K线
+  series.forEach(function(eachSeries, seriesIndex) {
+    let ranges,minRange,maxRange;
+    ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
+    minRange = ranges.pop();
+    maxRange = ranges.shift();
+    var data = eachSeries.data;
+    var points = getCandleDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
+    calPoints.push(points);
+    var splitPointList = splitPoints(points);
+
+		for(let i=0;i<splitPointList[0].length;i++){
+			if(i>leftNum && i<rightNum){
+				let item=splitPointList[0][i];
+				context.beginPath();
+				//如果上涨
+				if (data[i][1] - data[i][0] > 0) {
+					context.setStrokeStyle(candleOption.color.upLine);
+					context.setFillStyle(candleOption.color.upFill);
+					context.setLineWidth(1 * opts.pixelRatio);
+					context.moveTo(item[3].x, item[3].y); //顶点
+					context.lineTo(item[1].x, item[1].y); //收盘中间点
+					context.lineTo(item[1].x - eachSpacing / 4, item[1].y); //收盘左侧点
+					context.lineTo(item[0].x - eachSpacing / 4, item[0].y); //开盘左侧点
+					context.lineTo(item[0].x, item[0].y); //开盘中间点
+					context.lineTo(item[2].x, item[2].y); //底点
+					context.lineTo(item[0].x, item[0].y); //开盘中间点
+					context.lineTo(item[0].x + eachSpacing / 4, item[0].y); //开盘右侧点
+					context.lineTo(item[1].x + eachSpacing / 4, item[1].y); //收盘右侧点
+					context.lineTo(item[1].x, item[1].y); //收盘中间点
+					context.moveTo(item[3].x, item[3].y); //顶点
+				} else {
+					context.setStrokeStyle(candleOption.color.downLine);
+					context.setFillStyle(candleOption.color.downFill);
+					context.setLineWidth(1 * opts.pixelRatio);
+					context.moveTo(item[3].x, item[3].y); //顶点
+					context.lineTo(item[0].x, item[0].y); //开盘中间点
+					context.lineTo(item[0].x - eachSpacing / 4, item[0].y); //开盘左侧点
+					context.lineTo(item[1].x - eachSpacing / 4, item[1].y); //收盘左侧点
+					context.lineTo(item[1].x, item[1].y); //收盘中间点
+					context.lineTo(item[2].x, item[2].y); //底点
+					context.lineTo(item[1].x, item[1].y); //收盘中间点
+					context.lineTo(item[1].x + eachSpacing / 4, item[1].y); //收盘右侧点
+					context.lineTo(item[0].x + eachSpacing / 4, item[0].y); //开盘右侧点
+					context.lineTo(item[0].x, item[0].y); //开盘中间点
+					context.moveTo(item[3].x, item[3].y); //顶点
+				}
+				context.closePath();
+				context.fill();
+				context.stroke();
+			}
+    }
+  });
+
+  context.restore();
+
+  return {
+    xAxisPoints: xAxisPoints,
+    calPoints: calPoints,
+    eachSpacing: eachSpacing
+  };
+}
+
+function drawAreaDataPoints(series, opts, config, context) {
+  var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
+  var areaOption = assign({},{
+    type: 'straight',
+    opacity: 0.2,
+    addLine: false,
+    width: 2,
+		gradient:false
+  },opts.extra.area);
+
+  let xAxisData = opts.chartData.xAxisData,
+    xAxisPoints = xAxisData.xAxisPoints,
+    eachSpacing = xAxisData.eachSpacing;
+
+  let endY = opts.height - opts.area[2];
+  let calPoints = [];
+
+  context.save();
+	let leftSpace=0;
+	let rightSpace=opts.width+eachSpacing;
+  if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
+    context.translate(opts._scrollDistance_, 0);
+		leftSpace=-opts._scrollDistance_-eachSpacing+opts.area[3];
+		rightSpace=leftSpace+(opts.xAxis.itemCount+4)*eachSpacing;
+  }
+
+  series.forEach(function(eachSeries, seriesIndex) {
+    let ranges,minRange,maxRange;
+    ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
+    minRange = ranges.pop();
+    maxRange = ranges.shift();
+    let data = eachSeries.data;
+    let points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
+    calPoints.push(points);
+
+    let splitPointList = splitPoints(points);
+    for (let i = 0; i < splitPointList.length; i++) {
+      let points = splitPointList[i];
+      // 绘制区域数
+      context.beginPath();
+      context.setStrokeStyle(hexToRgb(eachSeries.color, areaOption.opacity));
+			if(areaOption.gradient){
+				let gradient = context.createLinearGradient(0, opts.area[0], 0, opts.height-opts.area[2]);
+				gradient.addColorStop('0', hexToRgb(eachSeries.color, areaOption.opacity));
+				gradient.addColorStop('1.0',hexToRgb("#FFFFFF", 0.1));
+				context.setFillStyle(gradient);
+			}else{
+				context.setFillStyle(hexToRgb(eachSeries.color, areaOption.opacity));
+			}
+      context.setLineWidth(areaOption.width * opts.pixelRatio);
+      if (points.length > 1) {
+        let firstPoint = points[0];
+        let lastPoint = points[points.length - 1];
+        context.moveTo(firstPoint.x, firstPoint.y);
+				let startPoint=0;
+        if (areaOption.type === 'curve') {
+					for(let j=0;j<points.length;j++){
+						let item=points[j];
+						if(startPoint==0 && item.x > leftSpace){
+							context.moveTo(item.x, item.y);
+							startPoint=1;
+						}
+            if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
+              let ctrlPoint = createCurveControlPoints(points, j - 1);
+              context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y,item.x, item.y);
+            }
+          };
+        } else {
+					for(let j=0;j<points.length;j++){
+						let item=points[j];
+						if(startPoint==0 && item.x > leftSpace){
+							context.moveTo(item.x, item.y);
+							startPoint=1;
+						}
+					  if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
+              context.lineTo(item.x, item.y);
+            }
+          };
+        }
+
+        context.lineTo(lastPoint.x, endY);
+        context.lineTo(firstPoint.x, endY);
+        context.lineTo(firstPoint.x, firstPoint.y);
+      } else {
+        let item = points[0];
+        context.moveTo(item.x - eachSpacing / 2, item.y);
+        context.lineTo(item.x + eachSpacing / 2, item.y);
+        context.lineTo(item.x + eachSpacing / 2, endY);
+        context.lineTo(item.x - eachSpacing / 2, endY);
+        context.moveTo(item.x - eachSpacing / 2, item.y);
+      }
+      context.closePath();
+      context.fill();
+
+      //画连线
+      if (areaOption.addLine) {
+				if (eachSeries.lineType == 'dash') {
+					let dashLength = eachSeries.dashLength?eachSeries.dashLength:8;
+					dashLength *= opts.pixelRatio;
+				  context.setLineDash([dashLength, dashLength]);
+				}
+        context.beginPath();
+        context.setStrokeStyle(eachSeries.color);
+        context.setLineWidth(areaOption.width * opts.pixelRatio);
+        if (points.length === 1) {
+          context.moveTo(points[0].x, points[0].y);
+          context.arc(points[0].x, points[0].y, 1, 0, 2 * Math.PI);
+        } else {
+          context.moveTo(points[0].x, points[0].y);
+					let startPoint=0;
+          if (areaOption.type === 'curve') {
+            for(let j=0;j<points.length;j++){
+            	let item=points[j];
+            	if(startPoint==0 && item.x > leftSpace){
+            		context.moveTo(item.x, item.y);
+            		startPoint=1;
+            	}
+              if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
+                let ctrlPoint = createCurveControlPoints(points, j - 1);
+                context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y,item.x,item.y);
+              }
+            };
+          } else {
+            for(let j=0;j<points.length;j++){
+            	let item=points[j];
+            	if(startPoint==0 && item.x > leftSpace){
+            		context.moveTo(item.x, item.y);
+            		startPoint=1;
+            	}
+              if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
+                context.lineTo(item.x, item.y);
+              }
+            };
+          }
+          context.moveTo(points[0].x, points[0].y);
+        }
+        context.stroke();
+				context.setLineDash([]);
+      }
+    }
+
+    //画点
+    if (opts.dataPointShape !== false) {
+      drawPointShape(points, eachSeries.color, eachSeries.pointShape, context, opts);
+    }
+
+  });
+
+  if (opts.dataLabel !== false && process === 1) {
+    series.forEach(function(eachSeries, seriesIndex) {
+      let ranges,minRange,maxRange;
+      ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
+      minRange = ranges.pop();
+      maxRange = ranges.shift();
+      var data = eachSeries.data;
+      var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
+      drawPointText(points, eachSeries, config, context);
+    });
+  }
+
+  context.restore();
+
+  return {
+    xAxisPoints: xAxisPoints,
+    calPoints: calPoints,
+    eachSpacing: eachSpacing
+  };
+}
+
+function drawLineDataPoints(series, opts, config, context) {
+  var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
+  var lineOption = assign({},{
+		type: 'straight',
+		width: 2
+	},opts.extra.line);
+	lineOption.width *=opts.pixelRatio;
+	
+  let xAxisData = opts.chartData.xAxisData,
+    xAxisPoints = xAxisData.xAxisPoints,
+    eachSpacing = xAxisData.eachSpacing;
+  var calPoints = [];
+
+  context.save();
+	let leftSpace=0;
+	let rightSpace=opts.width+eachSpacing;
+  if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
+    context.translate(opts._scrollDistance_, 0);
+		leftSpace=-opts._scrollDistance_-eachSpacing+opts.area[3];
+		rightSpace=leftSpace+(opts.xAxis.itemCount+4)*eachSpacing;
+  }
+
+  series.forEach(function(eachSeries, seriesIndex) {
+    let ranges,minRange,maxRange;
+    ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
+    minRange = ranges.pop();
+    maxRange = ranges.shift();
+    var data = eachSeries.data;
+    var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
+    calPoints.push(points);
+    var splitPointList = splitPoints(points);
+		
+		if (eachSeries.lineType == 'dash') {
+			let dashLength = eachSeries.dashLength?eachSeries.dashLength:8;
+			dashLength *= opts.pixelRatio;
+		  context.setLineDash([dashLength, dashLength]);
+		}
+		context.beginPath();
+		context.setStrokeStyle(eachSeries.color);
+		context.setLineWidth(lineOption.width);
+		
+    splitPointList.forEach(function(points, index) {
+			
+      if (points.length === 1) {
+        context.moveTo(points[0].x, points[0].y);
+        context.arc(points[0].x, points[0].y, 1, 0, 2 * Math.PI);
+      } else {
+        context.moveTo(points[0].x, points[0].y);
+				let startPoint=0;
+        if (lineOption.type === 'curve') {
+          for(let j=0;j<points.length;j++){
+          	let item=points[j];
+          	if(startPoint==0 && item.x > leftSpace){
+          		context.moveTo(item.x, item.y);
+          		startPoint=1;
+          	}
+            if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
+              var ctrlPoint = createCurveControlPoints(points, j - 1);
+              context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y,item.x, item.y);
+            }
+          };
+        } else {
+          for(let j=0;j<points.length;j++){
+          	let item=points[j];
+          	if(startPoint==0 && item.x > leftSpace){
+          		context.moveTo(item.x, item.y);
+          		startPoint=1;
+          	}
+            if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
+              context.lineTo(item.x, item.y);
+            }
+          };
+        }
+        context.moveTo(points[0].x, points[0].y);
+      }
+      
+    });
+		
+		context.stroke();
+		context.setLineDash([]);
+		
+    if (opts.dataPointShape !== false) {
+      drawPointShape(points, eachSeries.color, eachSeries.pointShape, context, opts);
+    }
+  });
+
+  if (opts.dataLabel !== false && process === 1) {
+    series.forEach(function(eachSeries, seriesIndex) {
+      let ranges,minRange,maxRange;
+      ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
+      minRange = ranges.pop();
+      maxRange = ranges.shift();
+      var data = eachSeries.data;
+      var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
+      drawPointText(points, eachSeries, config, context);
+    });
+  }
+
+  context.restore();
+
+  return {
+    xAxisPoints: xAxisPoints,
+    calPoints: calPoints,
+    eachSpacing: eachSpacing
+  };
+}
+
+function drawMixDataPoints(series, opts, config, context) {
+  let process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
+  
+  let xAxisData = opts.chartData.xAxisData,
+    xAxisPoints = xAxisData.xAxisPoints,
+    eachSpacing = xAxisData.eachSpacing;
+
+  let endY = opts.height - opts.area[2];
+  let calPoints = [];
+
+  var columnIndex = 0;
+  var columnLength = 0;
+  series.forEach(function(eachSeries, seriesIndex) {
+    if (eachSeries.type == 'column') {
+      columnLength += 1;
+    }
+  });
+  context.save();
+	let leftNum=-2;
+	let rightNum=xAxisPoints.length+2;
+	let leftSpace=0;
+	let rightSpace=opts.width+eachSpacing;
+  if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
+    context.translate(opts._scrollDistance_, 0);
+		leftNum=Math.floor(-opts._scrollDistance_/eachSpacing)-2;
+		rightNum=leftNum+opts.xAxis.itemCount+4;
+		leftSpace=-opts._scrollDistance_-eachSpacing+opts.area[3];
+		rightSpace=leftSpace+(opts.xAxis.itemCount+4)*eachSpacing;
+  }
+
+  series.forEach(function(eachSeries, seriesIndex) {
+    let ranges,minRange,maxRange;
+    
+		ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
+		minRange = ranges.pop();
+		maxRange = ranges.shift();
+
+    var data = eachSeries.data;
+    var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
+    calPoints.push(points);
+
+    // 绘制柱状数据图
+    if (eachSeries.type == 'column') {
+      points = fixColumeData(points, eachSpacing, columnLength, columnIndex, config, opts);
+      for(let i=0;i<points.length;i++){
+      	let item=points[i];
+        if (item !== null && i>leftNum && i<rightNum) {
+          context.beginPath();
+          context.setStrokeStyle(item.color || eachSeries.color);
+          context.setLineWidth(1)
+          context.setFillStyle(item.color || eachSeries.color);
+          var startX = item.x - item.width / 2;
+          var height = opts.height - item.y - opts.area[2];
+          context.moveTo(startX, item.y);
+          context.moveTo(startX, item.y);
+          context.lineTo(startX+item.width-2,item.y);
+          context.lineTo(startX+item.width-2,opts.height - opts.area[2]);
+          context.lineTo(startX,opts.height - opts.area[2]);
+          context.lineTo(startX,item.y);
+          context.closePath();
+          context.stroke();
+          context.fill();
+          context.closePath();
+          context.fill();
+        }
+      }
+      columnIndex += 1;
+    }
+
+    //绘制区域图数据
+
+    if (eachSeries.type == 'area') {
+      let splitPointList = splitPoints(points);
+      for (let i = 0; i < splitPointList.length; i++) {
+        let points = splitPointList[i];
+        // 绘制区域数据
+        context.beginPath();
+        context.setStrokeStyle(eachSeries.color);
+        context.setFillStyle(hexToRgb(eachSeries.color, 0.2));
+        context.setLineWidth(2 * opts.pixelRatio);
+        if (points.length > 1) {
+          var firstPoint = points[0];
+          let lastPoint = points[points.length - 1];
+          context.moveTo(firstPoint.x, firstPoint.y);
+					let startPoint=0;
+          if (eachSeries.style === 'curve') {
+            for(let j=0;j<points.length;j++){
+            	let item=points[j];
+            	if(startPoint==0 && item.x > leftSpace){
+            		context.moveTo(item.x, item.y);
+            		startPoint=1;
+            	}
+              if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
+                var ctrlPoint = createCurveControlPoints(points, j - 1);
+                context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y, item.x, item.y);
+              }
+            };
+          } else {
+            for(let j=0;j<points.length;j++){
+            	let item=points[j];
+            	if(startPoint==0 && item.x > leftSpace){
+            		context.moveTo(item.x, item.y);
+            		startPoint=1;
+            	}
+              if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
+                context.lineTo(item.x, item.y);
+              }
+            };
+          }
+          context.lineTo(lastPoint.x, endY);
+          context.lineTo(firstPoint.x, endY);
+          context.lineTo(firstPoint.x, firstPoint.y);
+        } else {
+          let item = points[0];
+          context.moveTo(item.x - eachSpacing / 2, item.y);
+          context.lineTo(item.x + eachSpacing / 2, item.y);
+          context.lineTo(item.x + eachSpacing / 2, endY);
+          context.lineTo(item.x - eachSpacing / 2, endY);
+          context.moveTo(item.x - eachSpacing / 2, item.y);
+        }
+        context.closePath();
+        context.fill();
+      }
+    }
+
+    // 绘制折线数据图
+    if (eachSeries.type == 'line') {
+      var splitPointList = splitPoints(points);
+      splitPointList.forEach(function(points, index) {
+				if (eachSeries.lineType == 'dash') {
+					let dashLength = eachSeries.dashLength?eachSeries.dashLength:8;
+					dashLength *= opts.pixelRatio;
+				  context.setLineDash([dashLength, dashLength]);
+				}
+        context.beginPath();
+        context.setStrokeStyle(eachSeries.color);
+        context.setLineWidth(2 * opts.pixelRatio);
+        if (points.length === 1) {
+          context.moveTo(points[0].x, points[0].y);
+          context.arc(points[0].x, points[0].y, 1, 0, 2 * Math.PI);
+        } else {
+          context.moveTo(points[0].x, points[0].y);
+					let startPoint=0;
+          if (eachSeries.style == 'curve') {
+            for(let j=0;j<points.length;j++){
+            	let item=points[j];
+            	if(startPoint==0 && item.x > leftSpace){
+            		context.moveTo(item.x, item.y);
+            		startPoint=1;
+            	}
+              if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
+                var ctrlPoint = createCurveControlPoints(points, j - 1);
+                context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y,item.x,item.y);
+              }
+            }
+          } else {
+            for(let j=0;j<points.length;j++){
+            	let item=points[j];
+            	if(startPoint==0 && item.x > leftSpace){
+            		context.moveTo(item.x, item.y);
+            		startPoint=1;
+            	}
+              if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
+                context.lineTo(item.x, item.y);
+              }
+            }
+          }
+          context.moveTo(points[0].x, points[0].y);
+        }
+        context.stroke();
+				context.setLineDash([]);
+      });
+    }
+
+    // 绘制点数据图
+    if (eachSeries.type == 'point') {
+			eachSeries.addPoint = true;
+    }
+
+    if (eachSeries.addPoint == true && eachSeries.type !== 'column' ) {
+      drawPointShape(points, eachSeries.color, eachSeries.pointShape, context, opts);
+    }
+  });
+  if (opts.dataLabel !== false && process === 1) {
+    var columnIndex = 0;
+    series.forEach(function(eachSeries, seriesIndex) {
+      let ranges,minRange,maxRange;
+      
+			ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
+			minRange = ranges.pop();
+			maxRange = ranges.shift();
+				
+      var data = eachSeries.data;
+      var points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
+      if (eachSeries.type !== 'column') {
+        drawPointText(points, eachSeries, config, context);
+      } else {
+        points = fixColumeData(points, eachSpacing, columnLength, columnIndex, config, opts);
+        drawPointText(points, eachSeries, config, context);
+        columnIndex += 1;
+      }
+
+    });
+  }
+
+  context.restore();
+
+  return {
+    xAxisPoints: xAxisPoints,
+    calPoints: calPoints,
+    eachSpacing: eachSpacing,
+  }
+}
+
+function drawToolTipBridge(opts, config, context, process, eachSpacing, xAxisPoints) {
+  var toolTipOption = opts.extra.tooltip || {};
+  if (toolTipOption.horizentalLine && opts.tooltip && process === 1 && (opts.type == 'line' || opts.type == 'area' || opts.type == 'column' || opts.type == 'candle' || opts.type == 'mix')) {
+    drawToolTipHorizentalLine(opts, config, context, eachSpacing, xAxisPoints)
+  }
+  context.save();
+  if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
+    context.translate(opts._scrollDistance_, 0);
+  }
+  if (opts.tooltip && opts.tooltip.textList && opts.tooltip.textList.length && process === 1) {
+    drawToolTip(opts.tooltip.textList, opts.tooltip.offset, opts, config, context, eachSpacing, xAxisPoints);
+  }
+  context.restore();
+
+}
+
+function drawXAxis(categories, opts, config, context) {
+
+  let xAxisData = opts.chartData.xAxisData,
+    xAxisPoints = xAxisData.xAxisPoints,
+    startX = xAxisData.startX,
+    endX = xAxisData.endX,
+    eachSpacing = xAxisData.eachSpacing;
+  var boundaryGap='center';
+  if (opts.type == 'line'||opts.type == 'area'){
+    boundaryGap=opts.xAxis.boundaryGap;
+  }
+  var startY = opts.height - opts.area[2];
+  var endY = opts.area[0];
+
+  //绘制滚动条
+  if (opts.enableScroll && opts.xAxis.scrollShow) {
+    var scrollY = opts.height - opts.area[2] + config.xAxisHeight;
+    var scrollScreenWidth = endX - startX;
+    var scrollTotalWidth = eachSpacing * (xAxisPoints.length - 1);
+    var scrollWidth = scrollScreenWidth * scrollScreenWidth / scrollTotalWidth;
+    var scrollLeft = 0;
+    if (opts._scrollDistance_) {
+      scrollLeft = -opts._scrollDistance_ * (scrollScreenWidth) / scrollTotalWidth;
+    }
+    context.beginPath();
+    context.setLineCap('round');
+    context.setLineWidth(6 * opts.pixelRatio);
+    context.setStrokeStyle(opts.xAxis.scrollBackgroundColor || "#EFEBEF");
+    context.moveTo(startX, scrollY);
+    context.lineTo(endX, scrollY);
+    context.stroke();
+    context.closePath();
+    context.beginPath();
+    context.setLineCap('round');
+    context.setLineWidth(6 * opts.pixelRatio);
+    context.setStrokeStyle(opts.xAxis.scrollColor || "#A6A6A6");
+    context.moveTo(startX + scrollLeft, scrollY);
+    context.lineTo(startX + scrollLeft + scrollWidth, scrollY);
+    context.stroke();
+    context.closePath();
+    context.setLineCap('butt');
+  }
+
+  context.save();
+
+  if (opts._scrollDistance_ && opts._scrollDistance_ !== 0) {
+    context.translate(opts._scrollDistance_, 0);
+  }
+	
+	//绘制X轴刻度线
+	if (opts.xAxis.calibration === true) {
+		context.setStrokeStyle(opts.xAxis.gridColor || "#cccccc");
+		context.setLineCap('butt');
+		context.setLineWidth(1 * opts.pixelRatio);
+	  xAxisPoints.forEach(function(item, index) {
+	    if (index > 0) {
+	      context.beginPath();
+	      context.moveTo(item - eachSpacing / 2, startY);
+	      context.lineTo(item - eachSpacing / 2, startY + 3 * opts.pixelRatio);
+	      context.closePath();
+	      context.stroke();
+	    }
+	  });
+	}
+	//绘制X轴网格
+  if (opts.xAxis.disableGrid !== true) {
+    context.setStrokeStyle(opts.xAxis.gridColor || "#cccccc");
+    context.setLineCap('butt');
+    context.setLineWidth(1 * opts.pixelRatio);
+    if (opts.xAxis.gridType == 'dash') {
+      context.setLineDash([opts.xAxis.dashLength, opts.xAxis.dashLength]);
+    }
+		opts.xAxis.gridEval = opts.xAxis.gridEval || 1;
+		xAxisPoints.forEach(function(item, index) {
+			if (index % opts.xAxis.gridEval == 0) {
+				context.beginPath();
+				context.moveTo(item, startY);
+				context.lineTo(item, endY);
+				context.stroke();
+			}
+		});
+    context.setLineDash([]);
+  }
+  
+
+  //绘制X轴文案
+  if (opts.xAxis.disabled !== true) {
+    // 对X轴列表做抽稀处理
+    //默认全部显示X轴标签
+    let maxXAxisListLength = categories.length;
+    //如果设置了X轴单屏数量
+    if (opts.xAxis.labelCount) {
+      //如果设置X轴密度
+      if (opts.xAxis.itemCount) {
+        maxXAxisListLength = Math.ceil(categories.length / opts.xAxis.itemCount * opts.xAxis.labelCount);
+      } else {
+        maxXAxisListLength = opts.xAxis.labelCount;
+      }
+      maxXAxisListLength -= 1;
+    }
+
+    let ratio = Math.ceil(categories.length / maxXAxisListLength);
+
+    let newCategories = [];
+    let cgLength = categories.length;
+    for (let i = 0; i < cgLength; i++) {
+      if (i % ratio !== 0) {
+        newCategories.push("");
+      } else {
+        newCategories.push(categories[i]);
+      }
+    }
+    newCategories[cgLength - 1] = categories[cgLength - 1];
+
+    var xAxisFontSize = opts.xAxis.fontSize || config.fontSize;
+    if (config._xAxisTextAngle_ === 0) {
+      newCategories.forEach(function(item, index) {
+        var offset = - measureText(String(item), xAxisFontSize) / 2;
+        if(boundaryGap == 'center'){
+          offset+=eachSpacing / 2;
+        }
+        var scrollHeight=0;
+        if(opts.xAxis.scrollShow){
+          scrollHeight=6*opts.pixelRatio;
+        }
+        context.beginPath();
+        context.setFontSize(xAxisFontSize);
+        context.setFillStyle(opts.xAxis.fontColor || '#666666');
+        context.fillText(String(item), xAxisPoints[index] + offset, startY + xAxisFontSize + (config.xAxisHeight - scrollHeight - xAxisFontSize) / 2);
+        context.closePath();
+        context.stroke();
+      });
+
+    } else {
+      newCategories.forEach(function(item, index) {
+        context.save();
+        context.beginPath();
+        context.setFontSize(xAxisFontSize);
+        context.setFillStyle(opts.xAxis.fontColor || '#666666');
+        var textWidth = measureText(String(item),xAxisFontSize);
+        var offset = - textWidth;
+        if(boundaryGap == 'center'){
+          offset+=eachSpacing / 2;
+        }
+        var _calRotateTranslate = calRotateTranslate(xAxisPoints[index] + eachSpacing / 2, startY + xAxisFontSize / 2 + 5, opts.height),
+          transX = _calRotateTranslate.transX,
+          transY = _calRotateTranslate.transY;
+
+        context.rotate(-1 * config._xAxisTextAngle_);
+        context.translate(transX, transY);
+        context.fillText(String(item), xAxisPoints[index] + offset, startY + xAxisFontSize + 5);
+        context.closePath();
+        context.stroke();
+        context.restore();
+      });
+    }
+  }
+  context.restore();
+	
+	//绘制X轴轴线
+  if(opts.xAxis.axisLine){
+    context.beginPath();
+    context.setStrokeStyle(opts.xAxis.axisLineColor);
+    context.setLineWidth(1 * opts.pixelRatio);
+    context.moveTo(startX,opts.height-opts.area[2]);
+    context.lineTo(endX,opts.height-opts.area[2]);
+    context.stroke();
+  }
+}
+
+function drawYAxisGrid(categories, opts, config, context) {
+  if (opts.yAxis.disableGrid === true) {
+    return;
+  }
+  let spacingValid = opts.height - opts.area[0] - opts.area[2];
+  let eachSpacing = spacingValid / opts.yAxis.splitNumber;
+  let startX = opts.area[3];
+  let xAxisPoints = opts.chartData.xAxisData.xAxisPoints,
+    xAxiseachSpacing = opts.chartData.xAxisData.eachSpacing;
+  let TotalWidth = xAxiseachSpacing * (xAxisPoints.length - 1);
+  let endX = startX + TotalWidth;
+
+  let points = [];
+  for (let i = 0; i < opts.yAxis.splitNumber + 1; i++) {
+    points.push(opts.height - opts.area[2] - eachSpacing * i);
+  }
+
+  context.save();
+  if (opts._scrollDistance_ && opts._scrollDistance_ !== 0) {
+    context.translate(opts._scrollDistance_, 0);
+  }
+
+  if (opts.yAxis.gridType == 'dash') {
+    context.setLineDash([opts.yAxis.dashLength, opts.yAxis.dashLength]);
+  }
+  context.setStrokeStyle(opts.yAxis.gridColor);
+  context.setLineWidth(1 * opts.pixelRatio);
+  points.forEach(function(item, index) {
+    context.beginPath();
+    context.moveTo(startX, item);
+    context.lineTo(endX, item);
+    context.stroke();
+  });
+  context.setLineDash([]);
+
+  context.restore();
+}
+
+function drawYAxis(series, opts, config, context) {
+  if (opts.yAxis.disabled === true) {
+    return;
+  }
+  var spacingValid = opts.height - opts.area[0] - opts.area[2];
+  var eachSpacing = spacingValid / opts.yAxis.splitNumber;
+  var startX = opts.area[3];
+  var endX = opts.width - opts.area[1];
+  var endY = opts.height - opts.area[2];
+  var fillEndY = endY + config.xAxisHeight;
+  if (opts.xAxis.scrollShow) {
+    fillEndY -= 3 * opts.pixelRatio;
+  }
+	if (opts.xAxis.rotateLabel){
+		fillEndY = opts.height - opts.area[2]+3;
+	}
+  // set YAxis background
+  context.beginPath();
+  context.setFillStyle(opts.background || '#ffffff');
+  if (opts._scrollDistance_ < 0) {
+    context.fillRect(0, 0, startX, fillEndY);
+  }
+  if(opts.enableScroll == true){
+    context.fillRect(endX, 0, opts.width, fillEndY);
+  }
+  context.closePath();
+  context.stroke();
+
+  var points = [];
+  for (let i = 0; i <= opts.yAxis.splitNumber; i++) {
+    points.push(opts.area[0] + eachSpacing * i);
+  }
+
+  let tStartLeft=opts.area[3];
+  let tStartRight=opts.width-opts.area[1];
+
+  for (let i = 0; i < opts.yAxis.data.length; i++) {
+    let yData = opts.yAxis.data[i];
+    if(yData.disabled !== true){
+      let rangesFormat = opts.chartData.yAxisData.rangesFormat[i];
+      let yAxisFontSize = yData.fontSize || config.fontSize;
+      let yAxisWidth = opts.chartData.yAxisData.yAxisWidth[i];
+      //画Y轴刻度及文案
+      rangesFormat.forEach(function(item, index) {
+        var pos = points[index] ? points[index] : endY;
+        context.beginPath();
+        context.setFontSize(yAxisFontSize);
+        context.setLineWidth(1*opts.pixelRatio);
+        context.setStrokeStyle(yData.axisLineColor||'#cccccc');
+        context.setFillStyle(yData.fontColor|| '#666666');
+        if(yAxisWidth.position=='left'){
+          context.fillText(String(item), tStartLeft - yAxisWidth.width , pos + yAxisFontSize / 2);
+          //画刻度线
+          if(yData.calibration==true){
+            context.moveTo(tStartLeft,pos);
+            context.lineTo(tStartLeft - 3*opts.pixelRatio,pos);
+          }
+        }else{
+          context.fillText(String(item), tStartRight + 4*opts.pixelRatio, pos + yAxisFontSize / 2);
+          //画刻度线
+          if(yData.calibration==true){
+            context.moveTo(tStartRight,pos);
+            context.lineTo(tStartRight + 3*opts.pixelRatio,pos);
+          }
+        }
+        context.closePath();
+        context.stroke();
+      });
+      //画Y轴轴线
+      if (yData.axisLine!==false) {
+        context.beginPath();
+        context.setStrokeStyle(yData.axisLineColor||'#cccccc');
+        context.setLineWidth(1 * opts.pixelRatio);
+        if(yAxisWidth.position=='left'){
+          context.moveTo(tStartLeft,opts.height-opts.area[2]);
+          context.lineTo(tStartLeft,opts.area[0]);
+        }else{
+          context.moveTo(tStartRight,opts.height-opts.area[2]);
+          context.lineTo(tStartRight,opts.area[0]);
+        }
+        context.stroke();
+      }
+			
+      //画Y轴标题
+      if (opts.yAxis.showTitle) {
+				
+        let titleFontSize = yData.titleFontSize || config.fontSize;
+        let title = yData.title;
+        context.beginPath();
+        context.setFontSize(titleFontSize);
+        context.setFillStyle(yData.titleFontColor || '#666666');
+        if(yAxisWidth.position=='left'){
+          context.fillText(title, tStartLeft - measureText(title,titleFontSize)/2, opts.area[0]-10*opts.pixelRatio);
+        }else{
+          context.fillText(title,tStartRight - measureText(title,titleFontSize)/2, opts.area[0]-10*opts.pixelRatio);
+        }
+        context.closePath();
+        context.stroke();
+      }
+      if(yAxisWidth.position=='left'){
+        tStartLeft -=(yAxisWidth.width + opts.yAxis.padding);
+      }else{
+        tStartRight +=yAxisWidth.width+ opts.yAxis.padding;
+      }
+    }
+  }
+}
+
+function drawLegend(series, opts, config, context, chartData) {
+  if (opts.legend.show === false) {
+    return;
+  }
+  let legendData = chartData.legendData;
+  let legendList = legendData.points;
+  let legendArea = legendData.area;
+  let padding = opts.legend.padding;
+  let fontSize = opts.legend.fontSize;
+  let shapeWidth = 15 * opts.pixelRatio;
+  let shapeRight = 5 * opts.pixelRatio;
+  let itemGap = opts.legend.itemGap;
+  let lineHeight = Math.max(opts.legend.lineHeight * opts.pixelRatio, fontSize);
+
+  //画背景及边框
+  context.beginPath();
+  context.setLineWidth(opts.legend.borderWidth);
+  context.setStrokeStyle(opts.legend.borderColor);
+  context.setFillStyle(opts.legend.backgroundColor);
+  context.moveTo(legendArea.start.x, legendArea.start.y);
+  context.rect(legendArea.start.x, legendArea.start.y, legendArea.width, legendArea.height);
+  context.closePath();
+  context.fill();
+  context.stroke();
+
+  legendList.forEach(function(itemList, listIndex) {
+    let width = 0;
+    let height = 0;
+    width = legendData.widthArr[listIndex];
+    height = legendData.heightArr[listIndex];
+    let startX = 0;
+    let startY = 0;
+    if (opts.legend.position == 'top' || opts.legend.position == 'bottom') {
+      startX = legendArea.start.x + (legendArea.width - width) / 2;
+      startY = legendArea.start.y + padding + listIndex * lineHeight;
+    } else {
+      if (listIndex == 0) {
+        width = 0;
+      } else {
+        width = legendData.widthArr[listIndex - 1];
+      }
+      startX = legendArea.start.x + padding + width;
+      startY = legendArea.start.y + padding + (legendArea.height - height) / 2;
+    }
+
+    context.setFontSize(config.fontSize);
+    for (let i = 0; i < itemList.length; i++) {
+      let item = itemList[i];
+      item.area = [0, 0, 0, 0];
+      item.area[0] = startX;
+      item.area[1] = startY;
+      item.area[3] = startY + lineHeight;
+      context.beginPath();
+      context.setLineWidth(1 * opts.pixelRatio);
+      context.setStrokeStyle(item.show ? item.color : opts.legend.hiddenColor);
+      context.setFillStyle(item.show ? item.color : opts.legend.hiddenColor);
+      switch (item.legendShape) {
+        case 'line':
+          context.moveTo(startX, startY + 0.5 * lineHeight - 2 * opts.pixelRatio);
+          context.fillRect(startX, startY + 0.5 * lineHeight - 2 * opts.pixelRatio, 15 * opts.pixelRatio, 4 * opts.pixelRatio);
+          break;
+        case 'triangle':
+          context.moveTo(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight - 5 * opts.pixelRatio);
+          context.lineTo(startX + 2.5 * opts.pixelRatio, startY + 0.5 * lineHeight + 5 * opts.pixelRatio);
+          context.lineTo(startX + 12.5 * opts.pixelRatio, startY + 0.5 * lineHeight + 5 * opts.pixelRatio);
+          context.lineTo(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight - 5 * opts.pixelRatio);
+          break;
+        case 'diamond':
+          context.moveTo(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight - 5 * opts.pixelRatio);
+          context.lineTo(startX + 2.5 * opts.pixelRatio, startY + 0.5 * lineHeight);
+          context.lineTo(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight + 5 * opts.pixelRatio);
+          context.lineTo(startX + 12.5 * opts.pixelRatio, startY + 0.5 * lineHeight);
+          context.lineTo(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight - 5 * opts.pixelRatio);
+          break;
+        case 'circle':
+          context.moveTo(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight);
+          context.arc(startX + 7.5 * opts.pixelRatio, startY + 0.5 * lineHeight, 5 * opts.pixelRatio, 0, 2 * Math.PI);
+          break;
+        case 'rect':
+          context.moveTo(startX, startY + 0.5 * lineHeight - 5 * opts.pixelRatio);
+          context.fillRect(startX, startY + 0.5 * lineHeight - 5 * opts.pixelRatio, 15 * opts.pixelRatio, 10 * opts.pixelRatio);
+          break;
+        default:
+          context.moveTo(startX, startY + 0.5 * lineHeight - 5 * opts.pixelRatio);
+          context.fillRect(startX, startY + 0.5 * lineHeight - 5 * opts.pixelRatio, 15 * opts.pixelRatio, 10 * opts.pixelRatio);
+      }
+      context.closePath();
+      context.fill();
+      context.stroke();
+
+      startX += shapeWidth + shapeRight;
+      let fontTrans = 0.5 * lineHeight + 0.5 * fontSize - 2;
+      context.beginPath();
+      context.setFontSize(fontSize);
+      context.setFillStyle(item.show ? opts.legend.fontColor : opts.legend.hiddenColor);
+      context.fillText(item.name, startX, startY + fontTrans);
+      context.closePath();
+      context.stroke();
+      if (opts.legend.position == 'top' || opts.legend.position == 'bottom') {
+        startX += measureText(item.name, fontSize) + itemGap;
+        item.area[2] = startX;
+      } else {
+        item.area[2] = startX + measureText(item.name, fontSize) + itemGap;;
+        startX -= shapeWidth + shapeRight;
+        startY += lineHeight;
+      }
+    }
+  });
+}
+
+function drawPieDataPoints(series, opts, config, context) {
+  var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
+  var pieOption = assign({}, {
+    activeOpacity: 0.5,
+    activeRadius: 10 * opts.pixelRatio,
+    offsetAngle: 0,
+    labelWidth: 15 * opts.pixelRatio,
+    ringWidth: 0,
+    border:false,
+    borderWidth:2,
+    borderColor:'#FFFFFF'
+  }, opts.extra.pie);
+  var centerPosition = {
+    x: opts.area[3] + (opts.width - opts.area[1] - opts.area[3]) / 2,
+    y: opts.area[0] + (opts.height - opts.area[0] - opts.area[2]) / 2
+  };
+  if (config.pieChartLinePadding == 0) {
+    config.pieChartLinePadding = pieOption.activeRadius;
+  }
+
+  var radius = Math.min((opts.width - opts.area[1] - opts.area[3]) / 2 - config.pieChartLinePadding - config.pieChartTextPadding - config._pieTextMaxLength_, (opts.height - opts.area[0] - opts.area[2]) / 2 - config.pieChartLinePadding - config.pieChartTextPadding);
+
+  series = getPieDataPoints(series, radius, process);
+
+  var activeRadius = pieOption.activeRadius;
+
+  series = series.map(function(eachSeries) {
+    eachSeries._start_ += (pieOption.offsetAngle) * Math.PI / 180;
+    return eachSeries;
+  });
+  series.forEach(function(eachSeries, seriesIndex) {
+    if (opts.tooltip) {
+      if (opts.tooltip.index == seriesIndex) {
+        context.beginPath();
+        context.setFillStyle(hexToRgb(eachSeries.color, opts.extra.pie.activeOpacity || 0.5));
+        context.moveTo(centerPosition.x, centerPosition.y);
+        context.arc(centerPosition.x, centerPosition.y, eachSeries._radius_ + activeRadius, eachSeries._start_,
+          eachSeries._start_ + 2 *
+          eachSeries._proportion_ * Math.PI);
+        context.closePath();
+        context.fill();
+      }
+    }
+    context.beginPath();
+    context.setLineWidth(pieOption.borderWidth * opts.pixelRatio);
+    context.lineJoin = "round";
+    context.setStrokeStyle(pieOption.borderColor);
+    context.setFillStyle(eachSeries.color);
+    context.moveTo(centerPosition.x, centerPosition.y);
+    context.arc(centerPosition.x, centerPosition.y, eachSeries._radius_, eachSeries._start_, eachSeries._start_ + 2 * eachSeries._proportion_ * Math.PI);
+    context.closePath();
+    context.fill();
+    if (pieOption.border == true) {
+      context.stroke();
+    }
+  });
+
+  if (opts.type === 'ring') {
+    var innerPieWidth = radius * 0.6;
+    if (typeof opts.extra.pie.ringWidth === 'number' && opts.extra.pie.ringWidth > 0) {
+      innerPieWidth = Math.max(0, radius - opts.extra.pie.ringWidth);
+    }
+    context.beginPath();
+    context.setFillStyle(opts.background || '#ffffff');
+    context.moveTo(centerPosition.x, centerPosition.y);
+    context.arc(centerPosition.x, centerPosition.y, innerPieWidth, 0, 2 * Math.PI);
+    context.closePath();
+    context.fill();
+  }
+
+  if (opts.dataLabel !== false && process === 1) {
+    var valid = false;
+    for (var i = 0, len = series.length; i < len; i++) {
+      if (series[i].data > 0) {
+        valid = true;
+        break;
+      }
+    }
+
+    if (valid) {
+      drawPieText(series, opts, config, context, radius, centerPosition);
+    }
+  }
+
+  if (process === 1 && opts.type === 'ring') {
+    drawRingTitle(opts, config, context, centerPosition);
+  }
+
+  return {
+    center: centerPosition,
+    radius: radius,
+    series: series
+  };
+}
+
+function drawRoseDataPoints(series, opts, config, context) {
+  var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
+  var roseOption = assign({}, {
+    type: 'area',
+    activeOpacity: 0.5,
+    activeRadius: 10 * opts.pixelRatio,
+    offsetAngle: 0,
+    labelWidth: 15 * opts.pixelRatio,
+    border:false,
+    borderWidth:2,
+    borderColor:'#FFFFFF'
+  }, opts.extra.rose);
+  if (config.pieChartLinePadding == 0) {
+    config.pieChartLinePadding = roseOption.activeRadius;
+  }
+  var centerPosition = {
+    x: opts.area[3] + (opts.width - opts.area[1] - opts.area[3]) / 2,
+    y: opts.area[0] + (opts.height - opts.area[0] - opts.area[2]) / 2
+  };
+   var radius = Math.min((opts.width - opts.area[1] - opts.area[3]) / 2 - config.pieChartLinePadding - config.pieChartTextPadding - config._pieTextMaxLength_, (opts.height - opts.area[0] - opts.area[2]) / 2 - config.pieChartLinePadding - config.pieChartTextPadding);
+  var minRadius = roseOption.minRadius || radius * 0.5;
+
+  series = getRoseDataPoints(series, roseOption.type, minRadius, radius, process);
+
+  var activeRadius = roseOption.activeRadius;
+
+  series = series.map(function(eachSeries) {
+    eachSeries._start_ += (roseOption.offsetAngle || 0) * Math.PI / 180;
+    return eachSeries;
+  });
+
+  series.forEach(function(eachSeries, seriesIndex) {
+    if (opts.tooltip) {
+      if (opts.tooltip.index == seriesIndex) {
+        context.beginPath();
+        context.setFillStyle(hexToRgb(eachSeries.color, roseOption.activeOpacity || 0.5));
+        context.moveTo(centerPosition.x, centerPosition.y);
+        context.arc(centerPosition.x, centerPosition.y, activeRadius + eachSeries._radius_, eachSeries._start_,
+          eachSeries._start_ + 2 * eachSeries._rose_proportion_ * Math.PI);
+        context.closePath();
+        context.fill();
+      }
+    }
+    context.beginPath();
+    context.setLineWidth(roseOption.borderWidth * opts.pixelRatio);
+    context.lineJoin = "round";
+    context.setStrokeStyle(roseOption.borderColor);
+    context.setFillStyle(eachSeries.color);
+    context.moveTo(centerPosition.x, centerPosition.y);
+    context.arc(centerPosition.x, centerPosition.y, eachSeries._radius_, eachSeries._start_, eachSeries._start_ + 2 *
+      eachSeries._rose_proportion_ * Math.PI);
+    context.closePath();
+    context.fill();
+    if (roseOption.border == true) {
+      context.stroke();
+    }
+  });
+
+  if (opts.dataLabel !== false && process === 1) {
+    var valid = false;
+    for (var i = 0, len = series.length; i < len; i++) {
+      if (series[i].data > 0) {
+        valid = true;
+        break;
+      }
+    }
+
+    if (valid) {
+      drawPieText(series, opts, config, context, radius, centerPosition);
+    }
+  }
+
+  return {
+    center: centerPosition,
+    radius: radius,
+    series: series
+  };
+}
+
+function drawArcbarDataPoints(series, opts, config, context) {
+  var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
+  var arcbarOption = assign({}, {
+    startAngle: 0.75,
+    endAngle: 0.25,
+    type: 'default',
+    width: 12 * opts.pixelRatio,
+		gap:2 * opts.pixelRatio
+  }, opts.extra.arcbar);
+
+  series = getArcbarDataPoints(series, arcbarOption, process);
+	
+  var centerPosition;
+	if(arcbarOption.center){
+		centerPosition=arcbarOption.center;
+	}else{
+		centerPosition= {
+		  x: opts.width / 2,
+		  y: opts.height / 2
+		};
+	}
+	
+  var radius;
+	if(arcbarOption.radius){
+		radius=arcbarOption.radius;
+	}else{
+		radius = Math.min(centerPosition.x, centerPosition.y);
+		radius -= 5 * opts.pixelRatio;
+		radius -= arcbarOption.width / 2;
+	}
+	
+  for (let i = 0; i < series.length; i++) {
+    let eachSeries = series[i];
+		//背景颜色
+		context.setLineWidth(arcbarOption.width);
+		context.setStrokeStyle(arcbarOption.backgroundColor || '#E9E9E9');
+		context.setLineCap('round');
+		context.beginPath();
+		if (arcbarOption.type == 'default') {
+		  context.arc(centerPosition.x, centerPosition.y, radius-(arcbarOption.width+arcbarOption.gap)*i, arcbarOption.startAngle * Math.PI, arcbarOption.endAngle * Math.PI, false);
+		} else {
+		  context.arc(centerPosition.x, centerPosition.y, radius-(arcbarOption.width+arcbarOption.gap)*i, 0, 2 * Math.PI, false);
+		}
+		context.stroke();
+		//进度条
+    context.setLineWidth(arcbarOption.width);
+    context.setStrokeStyle(eachSeries.color);
+    context.setLineCap('round');
+    context.beginPath();
+    context.arc(centerPosition.x, centerPosition.y, radius-(arcbarOption.width+arcbarOption.gap)*i, arcbarOption.startAngle * Math.PI, eachSeries._proportion_ * Math.PI, false);
+    context.stroke();
+  }
+
+  drawRingTitle(opts, config, context, centerPosition);
+
+  return {
+    center: centerPosition,
+    radius: radius,
+    series: series
+  };
+}
+
+function drawGaugeDataPoints(categories, series, opts, config, context) {
+  var process = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 1;
+  var gaugeOption = assign({}, {
+		type:'default',
+    startAngle: 0.75,
+    endAngle: 0.25,
+    width: 15,
+    splitLine: {
+      fixRadius: 0,
+      splitNumber: 10,
+      width: 15,
+      color: '#FFFFFF',
+      childNumber: 5,
+      childWidth: 5
+    },
+    pointer: {
+      width: 15,
+      color: 'auto'
+    }
+  }, opts.extra.gauge);
+
+  if (gaugeOption.oldAngle == undefined) {
+    gaugeOption.oldAngle = gaugeOption.startAngle;
+  }
+  if (gaugeOption.oldData == undefined) {
+    gaugeOption.oldData = 0;
+  }
+  categories = getGaugeAxisPoints(categories, gaugeOption.startAngle, gaugeOption.endAngle);
+
+  var centerPosition = {
+    x: opts.width / 2,
+    y: opts.height / 2
+  };
+  var radius = Math.min(centerPosition.x, centerPosition.y);
+  radius -= 5 * opts.pixelRatio;
+  radius -= gaugeOption.width / 2;
+  var innerRadius = radius - gaugeOption.width;
+	var totalAngle=0;
+	
+	//判断仪表盘的样式:default百度样式,progress新样式
+	if(gaugeOption.type == 'progress'){
+		
+		//## 第一步画中心圆形背景和进度条背景
+		//中心圆形背景
+		var pieRadius = radius - gaugeOption.width*3;
+		context.beginPath();
+		let gradient = context.createLinearGradient(centerPosition.x, centerPosition.y-pieRadius, centerPosition.x , centerPosition.y+pieRadius);
+		//配置渐变填充(起点:中心点向上减半径;结束点中心点向下加半径)
+		gradient.addColorStop('0', hexToRgb(series[0].color, 0.3));
+		gradient.addColorStop('1.0',hexToRgb("#FFFFFF", 0.1));
+		context.setFillStyle(gradient);
+		context.arc(centerPosition.x, centerPosition.y, pieRadius, 0, 2*Math.PI, false);
+		context.fill();
+		//画进度条背景
+		context.setLineWidth(gaugeOption.width);
+		context.setStrokeStyle(hexToRgb(series[0].color, 0.3));
+		context.setLineCap('round');
+		context.beginPath();
+		context.arc(centerPosition.x, centerPosition.y, innerRadius , gaugeOption.startAngle * Math.PI, gaugeOption.endAngle *Math.PI, false);
+		context.stroke();
+		
+		//## 第二步画刻度线
+		totalAngle = gaugeOption.startAngle - gaugeOption.endAngle + 1;
+		let splitAngle = totalAngle / gaugeOption.splitLine.splitNumber;
+		let childAngle = totalAngle / gaugeOption.splitLine.splitNumber / gaugeOption.splitLine.childNumber;
+		let startX = -radius - gaugeOption.width * 0.5 - gaugeOption.splitLine.fixRadius;
+		let endX = -radius - gaugeOption.width - gaugeOption.splitLine.fixRadius + gaugeOption.splitLine.width;
+		context.save();
+		context.translate(centerPosition.x, centerPosition.y);
+		context.rotate((gaugeOption.startAngle - 1) * Math.PI);
+		let len = gaugeOption.splitLine.splitNumber * gaugeOption.splitLine.childNumber + 1;
+		let proc = series[0].data * process;
+		for (let i = 0; i < len; i++) {
+		  context.beginPath();
+			//刻度线随进度变色
+			if(proc>(i/len)){
+				context.setStrokeStyle(hexToRgb(series[0].color, 1));
+			}else{
+				context.setStrokeStyle(hexToRgb(series[0].color, 0.3));
+			}
+		  context.setLineWidth(3 * opts.pixelRatio);
+		  context.moveTo(startX, 0);
+		  context.lineTo(endX, 0);
+		  context.stroke();
+		  context.rotate(childAngle * Math.PI);
+		}
+		context.restore();
+		
+		//## 第三步画进度条
+		series = getArcbarDataPoints(series, gaugeOption, process);
+		context.setLineWidth(gaugeOption.width);
+		context.setStrokeStyle(series[0].color);
+		context.setLineCap('round');
+		context.beginPath();
+		context.arc(centerPosition.x, centerPosition.y, innerRadius , gaugeOption.startAngle * Math.PI, series[0]._proportion_ *Math.PI, false);
+		context.stroke();
+		
+		//## 第四步画指针
+		let pointerRadius = radius - gaugeOption.width*2.5;
+		context.save();
+		context.translate(centerPosition.x, centerPosition.y);
+		context.rotate((series[0]._proportion_ - 1) * Math.PI);
+		context.beginPath();
+		context.setLineWidth(gaugeOption.width/3);
+		let gradient3 = context.createLinearGradient(0, -pointerRadius*0.6, 0 , pointerRadius*0.6);
+		gradient3.addColorStop('0', hexToRgb('#FFFFFF', 0));
+		gradient3.addColorStop('0.5', hexToRgb(series[0].color, 1));
+		gradient3.addColorStop('1.0', hexToRgb('#FFFFFF', 0));
+		context.setStrokeStyle(gradient3);
+		context.arc(0, 0, pointerRadius , 0.85* Math.PI, 1.15 * Math.PI, false);
+		context.stroke();
+		context.beginPath();
+		context.setLineWidth(1);
+		context.setStrokeStyle(series[0].color);
+		context.setFillStyle(series[0].color);
+		context.moveTo(-pointerRadius-gaugeOption.width/3/2,-4);
+		context.lineTo(-pointerRadius-gaugeOption.width/3/2-4,0);
+		context.lineTo(-pointerRadius-gaugeOption.width/3/2,4);
+		context.lineTo(-pointerRadius-gaugeOption.width/3/2,-4);
+		context.stroke();
+		context.fill();
+		context.restore();
+		
+	//default百度样式
+	}else{
+		//画背景
+		context.setLineWidth(gaugeOption.width);
+		context.setLineCap('butt');
+		for (let i = 0; i < categories.length; i++) {
+		  let eachCategories = categories[i];
+		  context.beginPath();
+		  context.setStrokeStyle(eachCategories.color);
+		  context.arc(centerPosition.x, centerPosition.y, radius, eachCategories._startAngle_ * Math.PI, eachCategories._endAngle_ *Math.PI, false);
+		  context.stroke();
+		}
+		context.save();
+		
+		//画刻度线
+		totalAngle = gaugeOption.startAngle - gaugeOption.endAngle + 1;
+		let splitAngle = totalAngle / gaugeOption.splitLine.splitNumber;
+		let childAngle = totalAngle / gaugeOption.splitLine.splitNumber / gaugeOption.splitLine.childNumber;
+		let startX = -radius - gaugeOption.width * 0.5 - gaugeOption.splitLine.fixRadius;
+		let endX = -radius - gaugeOption.width * 0.5 - gaugeOption.splitLine.fixRadius + gaugeOption.splitLine.width;
+		let childendX = -radius - gaugeOption.width * 0.5 - gaugeOption.splitLine.fixRadius + gaugeOption.splitLine.childWidth;
+		
+		context.translate(centerPosition.x, centerPosition.y);
+		context.rotate((gaugeOption.startAngle - 1) * Math.PI);
+		
+		for (let i = 0; i < gaugeOption.splitLine.splitNumber + 1; i++) {
+		  context.beginPath();
+		  context.setStrokeStyle(gaugeOption.splitLine.color);
+		  context.setLineWidth(2 * opts.pixelRatio);
+		  context.moveTo(startX, 0);
+		  context.lineTo(endX, 0);
+		  context.stroke();
+		  context.rotate(splitAngle * Math.PI);
+		}
+		context.restore();
+		
+		context.save();
+		context.translate(centerPosition.x, centerPosition.y);
+		context.rotate((gaugeOption.startAngle - 1) * Math.PI);
+		
+		for (let i = 0; i < gaugeOption.splitLine.splitNumber * gaugeOption.splitLine.childNumber + 1; i++) {
+		  context.beginPath();
+		  context.setStrokeStyle(gaugeOption.splitLine.color);
+		  context.setLineWidth(1 * opts.pixelRatio);
+		  context.moveTo(startX, 0);
+		  context.lineTo(childendX, 0);
+		  context.stroke();
+		  context.rotate(childAngle * Math.PI);
+		}
+		context.restore();
+		
+		//画指针
+		series = getGaugeDataPoints(series, categories, gaugeOption, process);
+		
+		for (let i = 0; i < series.length; i++) {
+		  let eachSeries = series[i];
+		  context.save();
+		  context.translate(centerPosition.x, centerPosition.y);
+		  context.rotate((eachSeries._proportion_ - 1) * Math.PI);
+		  context.beginPath();
+		  context.setFillStyle(eachSeries.color);
+		  context.moveTo(gaugeOption.pointer.width, 0);
+		  context.lineTo(0, -gaugeOption.pointer.width / 2);
+		  context.lineTo(-innerRadius, 0);
+		  context.lineTo(0, gaugeOption.pointer.width / 2);
+		  context.lineTo(gaugeOption.pointer.width, 0);
+		  context.closePath();
+		  context.fill();
+		  context.beginPath();
+		  context.setFillStyle('#FFFFFF');
+		  context.arc(0, 0, gaugeOption.pointer.width / 6, 0, 2 * Math.PI, false);
+		  context.fill();
+		  context.restore();
+		}
+		
+		if (opts.dataLabel !== false) {
+		  drawGaugeLabel(gaugeOption, radius, centerPosition, opts, config, context);
+		}
+	}
+	
+	//画仪表盘标题,副标题
+  drawRingTitle(opts, config, context, centerPosition);
+
+  if (process === 1 && opts.type === 'gauge') {
+    opts.extra.gauge.oldAngle = series[0]._proportion_;
+    opts.extra.gauge.oldData = series[0].data;
+  }
+  return {
+    center: centerPosition,
+    radius: radius,
+    innerRadius: innerRadius,
+    categories: categories,
+    totalAngle: totalAngle
+  };
+}
+
+function drawRadarDataPoints(series, opts, config, context) {
+  var process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
+  var radarOption = assign({},{
+    gridColor: '#cccccc',
+    labelColor: '#666666',
+    opacity: 0.2,
+		gridCount:3
+  },opts.extra.radar);
+  
+  var coordinateAngle = getRadarCoordinateSeries(opts.categories.length);
+  
+  var centerPosition = {
+    x: opts.area[3] + (opts.width - opts.area[1] - opts.area[3]) / 2,
+    y: opts.area[0] + (opts.height - opts.area[0] - opts.area[2]) / 2
+  };
+
+  var radius = Math.min(centerPosition.x - (getMaxTextListLength(opts.categories) + config.radarLabelTextMargin),
+    centerPosition.y - config.radarLabelTextMargin);
+  //TODO逻辑不对
+  radius -= opts.padding[1];
+
+  // draw grid
+  context.beginPath();
+  context.setLineWidth(1 * opts.pixelRatio);
+  context.setStrokeStyle(radarOption.gridColor);
+  coordinateAngle.forEach(function(angle) {
+    var pos = convertCoordinateOrigin(radius * Math.cos(angle), radius * Math.sin(angle), centerPosition);
+    context.moveTo(centerPosition.x, centerPosition.y);
+    context.lineTo(pos.x, pos.y);
+  });
+  context.stroke();
+  context.closePath();
+  // draw split line grid
+
+  var _loop = function _loop(i) {
+    var startPos = {};
+    context.beginPath();
+    context.setLineWidth(1 * opts.pixelRatio);
+    context.setStrokeStyle(radarOption.gridColor);
+    coordinateAngle.forEach(function(angle, index) {
+      var pos = convertCoordinateOrigin(radius / radarOption.gridCount * i * Math.cos(angle), radius / radarOption.gridCount * i * Math.sin(angle), centerPosition);
+      if (index === 0) {
+        startPos = pos;
+        context.moveTo(pos.x, pos.y);
+      } else {
+        context.lineTo(pos.x, pos.y);
+      }
+    });
+    context.lineTo(startPos.x, startPos.y);
+    context.stroke();
+    context.closePath();
+  };
+
+  for (var i = 1; i <= radarOption.gridCount; i++) {
+    _loop(i);
+  }
+
+  var radarDataPoints = getRadarDataPoints(coordinateAngle, centerPosition, radius, series, opts, process);
+
+  radarDataPoints.forEach(function(eachSeries, seriesIndex) {
+    // 绘制区域数据
+    context.beginPath();
+    context.setFillStyle(hexToRgb(eachSeries.color, radarOption.opacity));
+    eachSeries.data.forEach(function(item, index) {
+      if (index === 0) {
+        context.moveTo(item.position.x, item.position.y);
+      } else {
+        context.lineTo(item.position.x, item.position.y);
+      }
+    });
+    context.closePath();
+    context.fill();
+
+    if (opts.dataPointShape !== false) {
+      var points = eachSeries.data.map(function(item) {
+        return item.position;
+      });
+      drawPointShape(points, eachSeries.color, eachSeries.pointShape, context, opts);
+    }
+  });
+  // draw label text
+  drawRadarLabel(coordinateAngle, radius, centerPosition, opts, config, context);
+
+  return {
+    center: centerPosition,
+    radius: radius,
+    angleList: coordinateAngle
+  };
+}
+
+function normalInt(min, max, iter) {
+    iter = iter==0?1:iter;
+    var arr = [];
+    for (var i = 0; i < iter; i++) {
+        arr[i] = Math.random();
+    };
+    return  Math.floor(arr.reduce(function(i,j){return i+j})/iter*(max-min))+min;  
+};
+
+function collisionNew(area,points,width,height){
+    var isIn=false;
+    for(let i=0;i<points.length;i++){
+      if(points[i].area){
+        if(area[3]<points[i].area[1]||area[0]>points[i].area[2]||area[1]>points[i].area[3]||area[2]<points[i].area[0]){
+          if(area[0]<0 || area[1]<0 || area[2]>width || area[3]>height){
+            isIn=true;
+            break;
+          }else{
+            isIn=false;
+          }
+        }else{
+          isIn=true;
+          break;
+        }
+      }
+    }
+    return isIn;
+};
+
+function getBoundingBox(data) {
+  var bounds = {}, coords;
+  bounds.xMin = 180;
+  bounds.xMax = 0;
+  bounds.yMin = 90;
+  bounds.yMax = 0
+  for (var i = 0; i < data.length; i++) {
+      var coorda = data[i].geometry.coordinates
+      for (var k = 0; k < coorda.length; k++) {
+          coords = coorda[k];
+          if (coords.length == 1) {
+              coords = coords[0]
+          }
+          for (var j = 0; j < coords.length; j++) {
+              var longitude = coords[j][0];
+              var latitude = coords[j][1];
+              var point = {
+                  x: longitude, 
+                  y: latitude 
+              }
+              bounds.xMin = bounds.xMin < point.x ? bounds.xMin : point.x;
+              bounds.xMax = bounds.xMax > point.x ? bounds.xMax : point.x;
+              bounds.yMin = bounds.yMin < point.y ? bounds.yMin : point.y;
+              bounds.yMax = bounds.yMax > point.y ? bounds.yMax : point.y;
+          }
+      }
+  }
+  return bounds;
+}
+
+function coordinateToPoint(latitude, longitude,bounds,scale,xoffset,yoffset) {
+  return {
+      x: (longitude - bounds.xMin) * scale+xoffset,
+      y: (bounds.yMax - latitude) * scale+yoffset
+  };
+}
+
+function pointToCoordinate(pointY, pointX,bounds,scale,xoffset,yoffset) {
+  return {
+      x: (pointX-xoffset)/scale+bounds.xMin,
+      y: bounds.yMax - (pointY-yoffset)/scale
+  };
+}
+
+function isRayIntersectsSegment(poi,s_poi,e_poi){
+      if (s_poi[1]==e_poi[1]){return false;} 
+      if (s_poi[1]>poi[1] && e_poi[1]>poi[1]){return false;}
+      if (s_poi[1]<poi[1] && e_poi[1]<poi[1]){return false;}
+      if (s_poi[1]==poi[1] && e_poi[1]>poi[1]){return false;}
+      if (e_poi[1]==poi[1] && s_poi[1]>poi[1]){return false;}
+      if (s_poi[0]<poi[0] && e_poi[1]<poi[1]){return false;}
+      let xseg=e_poi[0]-(e_poi[0]-s_poi[0])*(e_poi[1]-poi[1])/(e_poi[1]-s_poi[1]); 
+      if (xseg<poi[0]){
+        return false;
+      }else{
+        return true;
+      }
+} 
+
+function isPoiWithinPoly(poi,poly){
+  let sinsc=0;
+  for (let i=0;i<poly.length;i++){
+    let epoly=poly[i][0];
+    if (poly.length == 1) {
+      epoly = poly[i][0]
+    }
+    for(let j=0;j<epoly.length-1;j++){
+      let s_poi=epoly[j];
+      let e_poi=epoly[j+1];
+      if (isRayIntersectsSegment(poi,s_poi,e_poi)){
+        sinsc+=1;
+      }
+    }
+  }
+  
+  if(sinsc%2==1){
+    return true;
+  }else{
+    return false;
+  }
+}
+
+
+function drawMapDataPoints(series, opts, config, context) {
+  var mapOption=assign({},{
+    border:true,
+    borderWidth:1,
+    borderColor:'#666666',
+    fillOpacity:0.6,
+    activeBorderColor:'#f04864',
+    activeFillColor:'#facc14',
+    activeFillOpacity:1
+  },opts.extra.map);
+  var coords, point;
+  var data = series;
+  var bounds= getBoundingBox(data);
+  var xScale = opts.width / Math.abs(bounds.xMax - bounds.xMin);
+  var yScale = opts.height / Math.abs(bounds.yMax - bounds.yMin);
+  var scale = xScale < yScale ? xScale : yScale;
+  var xoffset=opts.width/2-Math.abs(bounds.xMax - bounds.xMin)/2*scale;
+  var yoffset=opts.height/2-Math.abs(bounds.yMax - bounds.yMin)/2*scale;
+  context.beginPath();
+  context.clearRect(0, 0, opts.width, opts.height);
+  context.setFillStyle(opts.background||'#FFFFFF');
+  context.rect(0,0,opts.width,opts.height);
+  context.fill();
+  for (var i = 0; i < data.length; i++) {
+    context.beginPath();
+    context.setLineWidth(mapOption.borderWidth * opts.pixelRatio);
+    context.setStrokeStyle(mapOption.borderColor);
+    context.setFillStyle(hexToRgb(series[i].color, mapOption.fillOpacity));
+    if (opts.tooltip) {
+      if (opts.tooltip.index == i ) {
+        context.setStrokeStyle(mapOption.activeBorderColor);
+        context.setFillStyle(hexToRgb(mapOption.activeFillColor, mapOption.activeFillOpacity));
+      }
+    }
+    var coorda = data[i].geometry.coordinates
+    for (var k = 0; k < coorda.length; k++) {
+      coords = coorda[k];
+      if (coords.length == 1) {
+        coords = coords[0]
+      }
+      for (var j = 0; j < coords.length; j++) {
+        point = coordinateToPoint(coords[j][1], coords[j][0],bounds,scale,xoffset,yoffset)
+        if (j === 0) {
+          context.beginPath();
+          context.moveTo(point.x, point.y);
+        } else {
+          context.lineTo(point.x, point.y);
+        }
+      }
+      context.fill();
+      if(mapOption.border == true){
+        context.stroke();
+      }
+    }
+    if(opts.dataLabel == true){
+      var centerPoint = data[i].properties.centroid;
+      if(centerPoint){
+        point = coordinateToPoint(centerPoint[1], centerPoint[0],bounds,scale,xoffset,yoffset);
+        let fontSize=data[i].textSize||config.fontSize;
+        let text=data[i].properties.name;
+        context.beginPath();
+        context.setFontSize(fontSize)
+        context.setFillStyle(data[i].textColor||'#666666')
+        context.fillText(text, point.x-measureText(text,fontSize)/2, point.y+fontSize/2);
+        context.closePath();
+        context.stroke();
+      }
+    }
+  }
+  opts.chartData.mapData={
+    bounds:bounds,
+    scale:scale,
+    xoffset:xoffset,
+    yoffset:yoffset
+  }
+  drawToolTipBridge(opts, config, context,1);
+  context.draw();
+}
+
+function getWordCloudPoint(opts,type){
+  let points = opts.series.sort(function(a,b){return parseInt(b.textSize)-parseInt(a.textSize);});
+  switch (type) {
+    case 'normal':
+      for (let i = 0; i < points.length; i++) {
+        let text = points[i].name;
+        let tHeight = points[i].textSize;
+        let tWidth = measureText(text,tHeight);
+        let x,y;
+        let area;
+        let breaknum=0;
+        while(true) {
+            breaknum++;
+            x = normalInt(-opts.width/2, opts.width/2,5) - tWidth/2;
+            y = normalInt(-opts.height/2, opts.height/2,5) + tHeight/2;
+            area=[x-5+opts.width/2,y-5-tHeight+opts.height/2,x+tWidth+5+opts.width/2,y+5+opts.height/2];
+            let isCollision = collisionNew(area,points,opts.width,opts.height);
+            if (!isCollision) break;
+            if (breaknum==1000){
+              area=[-100,-100,-100,-100];
+              break;
+            }
+        };
+        points[i].area=area;
+      }
+    break;
+    case 'vertical':
+      function Spin(){
+        //获取均匀随机值,是否旋转,旋转的概率为(1-0.5)
+        if (Math.random()>0.7) {
+            return true;
+        }else {return false};
+      };
+      for (let i = 0; i < points.length; i++) { 
+        let text = points[i].name;
+        let tHeight = points[i].textSize;
+        let tWidth = measureText(text,tHeight);
+        let isSpin = Spin(); 
+        let x,y,area,areav;
+        let breaknum=0;
+        while(true) {
+          breaknum++;
+          let isCollision;
+          if (isSpin) {
+              x = normalInt(-opts.width/2, opts.width/2,5) - tWidth/2;
+              y = normalInt(-opts.height/2, opts.height/2,5)+tHeight/2;
+              area=[y-5-tWidth+opts.width/2,(-x-5+opts.height/2),y+5+opts.width/2,(-x+tHeight+5+opts.height/2)];
+              areav=[opts.width-(opts.width/2-opts.height/2)-(-x+tHeight+5+opts.height/2)-5,(opts.height/2-opts.width/2)+(y-5-tWidth+opts.width/2)-5,opts.width-(opts.width/2-opts.height/2)-(-x+tHeight+5+opts.height/2)+tHeight,(opts.height/2-opts.width/2)+(y-5-tWidth+opts.width/2)+tWidth+5];
+              isCollision = collisionNew(areav,points,opts.height,opts.width);
+          }else{
+            x = normalInt(-opts.width/2, opts.width/2,5) - tWidth/2;
+            y = normalInt(-opts.height/2, opts.height/2,5)+tHeight/2;
+            area=[x-5+opts.width/2,y-5-tHeight+opts.height/2,x+tWidth+5+opts.width/2,y+5+opts.height/2];
+            isCollision = collisionNew(area,points,opts.width,opts.height);
+          } 
+          if (!isCollision) break;
+          if (breaknum==1000){
+            area=[-1000,-1000,-1000,-1000];
+            break;
+          }
+        };
+        if (isSpin) {
+          points[i].area=areav;
+          points[i].areav=area;
+        }else{
+          points[i].area=area;
+        }
+        points[i].rotate=isSpin;
+      };
+    break;
+  }
+  return points;
+}
+
+
+function drawWordCloudDataPoints(series, opts, config, context) {
+  let process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
+  let wordOption = assign({},{
+    type: 'normal',
+    autoColors: true
+  },opts.extra.word);
+  
+  context.beginPath();
+  context.setFillStyle(opts.background||'#FFFFFF');
+  context.rect(0,0,opts.width,opts.height);
+  context.fill();
+  context.save();
+  let points = opts.chartData.wordCloudData;
+  context.translate(opts.width/2,opts.height/2);
+  
+  for(let i=0;i<points.length;i++){
+      context.save();
+      if(points[i].rotate){
+        context.rotate(90 * Math.PI / 180);
+      }
+      let text = points[i].name;
+      let tHeight = points[i].textSize;
+      let tWidth = measureText(text,tHeight);
+      context.beginPath();
+      context.setStrokeStyle(points[i].color);
+      context.setFillStyle(points[i].color);
+      context.setFontSize(tHeight);
+      if(points[i].rotate){
+        if(points[i].areav[0]>0){
+          if (opts.tooltip) {
+            if (opts.tooltip.index == i) {
+              context.strokeText(text,(points[i].areav[0]+5-opts.width/2)*process-tWidth*(1-process)/2,(points[i].areav[1]+5+tHeight-opts.height/2)*process);
+              }else{
+                context.fillText(text,(points[i].areav[0]+5-opts.width/2)*process-tWidth*(1-process)/2,(points[i].areav[1]+5+tHeight-opts.height/2)*process);
+              }
+          }else{
+            context.fillText(text,(points[i].areav[0]+5-opts.width/2)*process-tWidth*(1-process)/2,(points[i].areav[1]+5+tHeight-opts.height/2)*process);
+          } 
+        }
+      }else{
+        if(points[i].area[0]>0){
+          if (opts.tooltip) {
+            if (opts.tooltip.index == i) {
+              context.strokeText(text,(points[i].area[0]+5-opts.width/2)*process-tWidth*(1-process)/2,(points[i].area[1]+5+tHeight-opts.height/2)*process);
+            }else{
+              context.fillText(text,(points[i].area[0]+5-opts.width/2)*process-tWidth*(1-process)/2,(points[i].area[1]+5+tHeight-opts.height/2)*process);
+            }
+          }else{
+            context.fillText(text,(points[i].area[0]+5-opts.width/2)*process-tWidth*(1-process)/2,(points[i].area[1]+5+tHeight-opts.height/2)*process);
+          }
+            
+        }
+      }
+      
+      context.stroke();
+      context.restore();
+  }
+  context.restore();
+}
+
+function drawFunnelDataPoints(series, opts, config, context) {
+  let process = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 1;
+  let funnelOption = assign({},{
+    activeWidth:10,
+    activeOpacity:0.3,
+    border:false,
+    borderWidth:2,
+    borderColor:'#FFFFFF',
+    fillOpacity:1,
+    labelAlign:'right'
+  },opts.extra.funnel);
+  let eachSpacing = (opts.height - opts.area[0] - opts.area[2])/series.length;
+  let centerPosition = {
+    x: opts.area[3] + (opts.width - opts.area[1] - opts.area[3]) / 2,
+    y: opts.height-opts.area[2]
+  };
+  let activeWidth = funnelOption.activeWidth;
+  let radius = Math.min((opts.width - opts.area[1] - opts.area[3]) / 2 - activeWidth, (opts.height - opts.area[0] - opts.area[2]) / 2 - activeWidth);
+  series = getFunnelDataPoints(series, radius, process);
+  context.save();
+  context.translate(centerPosition.x,centerPosition.y);
+  for(let i=0;i<series.length;i++){
+    if(i==0){
+      if (opts.tooltip) {
+        if (opts.tooltip.index == i) {
+          context.beginPath();
+          context.setFillStyle(hexToRgb(series[i].color, funnelOption.activeOpacity));
+          context.moveTo(-activeWidth, 0);
+          context.lineTo(-series[i].radius-activeWidth, -eachSpacing);
+          context.lineTo(series[i].radius+activeWidth, -eachSpacing);
+          context.lineTo(activeWidth, 0);
+          context.lineTo(-activeWidth, 0);
+          context.closePath();
+          context.fill();
+        }
+      }
+      series[i].funnelArea=[centerPosition.x-series[i].radius,centerPosition.y-eachSpacing,centerPosition.x+series[i].radius,centerPosition.y];
+      context.beginPath();
+      context.setLineWidth(funnelOption.borderWidth * opts.pixelRatio);
+      context.setStrokeStyle(funnelOption.borderColor);
+      context.setFillStyle(hexToRgb(series[i].color, funnelOption.fillOpacity));
+      context.moveTo(0, 0);
+      context.lineTo(-series[i].radius, -eachSpacing);
+      context.lineTo(series[i].radius, -eachSpacing);
+      context.lineTo(0, 0);
+      context.closePath();
+      context.fill();
+      if(funnelOption.border == true){
+        context.stroke();
+      }
+    }else{
+      if (opts.tooltip) {
+        if (opts.tooltip.index == i) {
+          context.beginPath();
+          context.setFillStyle(hexToRgb(series[i].color, funnelOption.activeOpacity));
+          context.moveTo(0, 0);
+          context.lineTo(-series[i-1].radius-activeWidth, 0);
+          context.lineTo(-series[i].radius-activeWidth, -eachSpacing);
+          context.lineTo(series[i].radius+activeWidth, -eachSpacing);
+          context.lineTo(series[i-1].radius+activeWidth, 0);
+          context.lineTo(0, 0);
+          context.closePath();
+          context.fill();
+        }
+      }
+      series[i].funnelArea=[centerPosition.x-series[i].radius,centerPosition.y-eachSpacing*(i+1),centerPosition.x+series[i].radius,centerPosition.y-eachSpacing*i];
+      context.beginPath();
+      context.setLineWidth(funnelOption.borderWidth * opts.pixelRatio);
+      context.setStrokeStyle(funnelOption.borderColor);
+      context.setFillStyle(hexToRgb(series[i].color, funnelOption.fillOpacity));
+      context.moveTo(0, 0);
+      context.lineTo(-series[i-1].radius, 0);
+      context.lineTo(-series[i].radius, -eachSpacing);
+      context.lineTo(series[i].radius, -eachSpacing);
+      context.lineTo(series[i-1].radius, 0);
+      context.lineTo(0, 0);
+      context.closePath();
+      context.fill();
+      if(funnelOption.border == true){
+        context.stroke();
+      }
+    }
+    context.translate(0,-eachSpacing)
+  }
+  context.restore();
+  
+  if (opts.dataLabel !== false && process === 1) {
+    drawFunnelText(series, opts, context, eachSpacing, funnelOption.labelAlign, activeWidth, centerPosition);
+  }
+  
+  return {
+    center: centerPosition,
+    radius: radius,
+    series: series
+  };
+}
+
+function drawFunnelText(series, opts, context, eachSpacing, labelAlign,activeWidth, centerPosition){
+  for(let i=0;i<series.length;i++){
+    let item = series[i];
+    let startX,endX,startY,fontSize;
+    let text = item.format ? item.format(+item._proportion_.toFixed(2)) : util.toFixed(item._proportion_ * 100) +'%';
+    if(labelAlign == 'right'){
+      if(i==0){
+        startX=(item.funnelArea[2]+centerPosition.x)/2;
+      }else{
+        startX=(item.funnelArea[2]+series[i-1].funnelArea[2])/2;
+      }
+      endX=startX+activeWidth*2;
+      startY=item.funnelArea[1]+eachSpacing/2;
+      fontSize = item.textSize || opts.fontSize;
+      context.setLineWidth(1 * opts.pixelRatio);
+      context.setStrokeStyle(item.color);
+      context.setFillStyle(item.color);
+      context.beginPath();
+      context.moveTo(startX,startY );
+      context.lineTo(endX,startY);
+      context.stroke();
+      context.closePath();
+      context.beginPath();
+      context.moveTo(endX, startY);
+      context.arc(endX, startY, 2, 0, 2 * Math.PI);
+      context.closePath();
+      context.fill();
+      context.beginPath();
+      context.setFontSize(fontSize);
+      context.setFillStyle(item.textColor || '#666666');
+      context.fillText(text, endX+5, startY + fontSize/2 -2);
+      context.closePath();
+      context.stroke();
+      context.closePath();
+    }else{
+      if(i==0){
+        startX=(item.funnelArea[0]+centerPosition.x)/2;
+      }else{
+        startX=(item.funnelArea[0]+series[i-1].funnelArea[0])/2;
+      }
+      endX=startX-activeWidth*2;
+      startY=item.funnelArea[1]+eachSpacing/2;
+      fontSize = item.textSize || opts.fontSize;
+      context.setLineWidth(1 * opts.pixelRatio);
+      context.setStrokeStyle(item.color);
+      context.setFillStyle(item.color);
+      context.beginPath();
+      context.moveTo(startX,startY );
+      context.lineTo(endX,startY);
+      context.stroke();
+      context.closePath();
+      context.beginPath();
+      context.moveTo(endX, startY);
+      context.arc(endX, startY, 2, 0, 2 * Math.PI);
+      context.closePath();
+      context.fill();
+      context.beginPath();
+      context.setFontSize(fontSize);
+      context.setFillStyle(item.textColor || '#666666');
+      context.fillText(text, endX-5-measureText(text), startY + fontSize/2 -2);
+      context.closePath();
+      context.stroke();
+      context.closePath();
+    }
+    
+  }
+}
+
+
+function drawCanvas(opts, context) {
+  context.draw();
+}
+
+var Timing = {
+  easeIn: function easeIn(pos) {
+    return Math.pow(pos, 3);
+  },
+  easeOut: function easeOut(pos) {
+    return Math.pow(pos - 1, 3) + 1;
+  },
+  easeInOut: function easeInOut(pos) {
+    if ((pos /= 0.5) < 1) {
+      return 0.5 * Math.pow(pos, 3);
+    } else {
+      return 0.5 * (Math.pow(pos - 2, 3) + 2);
+    }
+  },
+  linear: function linear(pos) {
+    return pos;
+  }
+};
+
+function Animation(opts) {
+  this.isStop = false;
+  opts.duration = typeof opts.duration === 'undefined' ? 1000 : opts.duration;
+  opts.timing = opts.timing || 'linear';
+  var delay = 17;
+
+  function createAnimationFrame() {
+    if (typeof setTimeout !== 'undefined') {
+      return function(step, delay) {
+        setTimeout(function() {
+          var timeStamp = +new Date();
+          step(timeStamp);
+        }, delay);
+      };
+    } else if (typeof requestAnimationFrame !== 'undefined') {
+      return requestAnimationFrame;
+    } else {
+      return function(step) {
+        step(null);
+      };
+    }
+  };
+  var animationFrame = createAnimationFrame();
+  var startTimeStamp = null;
+  var _step = function step(timestamp) {
+    if (timestamp === null || this.isStop === true) {
+      opts.onProcess && opts.onProcess(1);
+      opts.onAnimationFinish && opts.onAnimationFinish();
+      return;
+    }
+    if (startTimeStamp === null) {
+      startTimeStamp = timestamp;
+    }
+    if (timestamp - startTimeStamp < opts.duration) {
+      var process = (timestamp - startTimeStamp) / opts.duration;
+      var timingFunction = Timing[opts.timing];
+      process = timingFunction(process);
+
+      opts.onProcess && opts.onProcess(process);
+      animationFrame(_step, delay);
+    } else {
+      opts.onProcess && opts.onProcess(1);
+      opts.onAnimationFinish && opts.onAnimationFinish();
+    }
+  };
+  _step = _step.bind(this);
+  animationFrame(_step, delay);
+}
+
+// stop animation immediately
+// and tigger onAnimationFinish
+Animation.prototype.stop = function() {
+  this.isStop = true;
+};
+
+function drawCharts(type, opts, config, context) {
+  var _this = this;
+  var series = opts.series;
+  var categories = opts.categories;
+  series = fillSeries(series, opts, config);
+  var duration = opts.animation ? opts.duration : 0;
+  _this.animationInstance && _this.animationInstance.stop();
+  var seriesMA = null;
+  if (type == 'candle') {
+    let average = assign({}, opts.extra.candle.average);
+    if (average.show) {
+      seriesMA = calCandleMA(average.day, average.name, average.color, series[0].data);
+      seriesMA = fillSeries(seriesMA, opts, config);
+      opts.seriesMA = seriesMA;
+    } else if (opts.seriesMA) {
+      seriesMA = opts.seriesMA = fillSeries(opts.seriesMA, opts, config);
+    } else {
+      seriesMA = series;
+    }
+  } else {
+    seriesMA = series;
+  }
+
+  /* 过滤掉show=false的series */
+  opts._series_ = series = filterSeries(series);
+
+  //重新计算图表区域
+
+  opts.area = new Array(4);
+  //复位绘图区域
+  for (let j = 0; j < 4; j++) {
+    opts.area[j] = opts.padding[j];
+  }
+
+  //通过计算三大区域:图例、X轴、Y轴的大小,确定绘图区域
+  var _calLegendData = calLegendData(seriesMA, opts, config, opts.chartData),
+    legendHeight = _calLegendData.area.wholeHeight,
+    legendWidth = _calLegendData.area.wholeWidth;
+    
+  switch (opts.legend.position) {
+    case 'top':
+      opts.area[0] += legendHeight;
+      break;
+    case 'bottom':
+      opts.area[2] += legendHeight;
+      break;
+    case 'left':
+      opts.area[3] += legendWidth;
+      break;
+    case 'right':
+      opts.area[1] += legendWidth;
+      break;
+  }
+
+  let _calYAxisData = {},yAxisWidth = 0;
+  if (opts.type === 'line' || opts.type === 'column' || opts.type === 'area' || opts.type === 'mix' || opts.type === 'candle') {
+    _calYAxisData = calYAxisData(series, opts, config);
+    yAxisWidth = _calYAxisData.yAxisWidth;
+    //如果显示Y轴标题
+    if(opts.yAxis.showTitle){
+      let maxTitleHeight=0;
+      for(let i=0;i<opts.yAxis.data.length;i++){
+        maxTitleHeight = Math.max(maxTitleHeight,opts.yAxis.data[i].titleFontSize?opts.yAxis.data[i].titleFontSize:config.fontSize)
+      }
+      opts.area[0] += (maxTitleHeight+6)*opts.pixelRatio;
+    }
+    let rightIndex=0,leftIndex=0;
+    //计算主绘图区域左右位置
+    for(let i=0;i<yAxisWidth.length;i++){
+      if(yAxisWidth[i].position=='left'){
+        if(leftIndex>0){
+          opts.area[3] += yAxisWidth[i].width + opts.yAxis.padding;
+        }else{
+          opts.area[3] += yAxisWidth[i].width;
+        }
+        leftIndex +=1;
+      }else{
+        if(rightIndex>0){
+          opts.area[1] += yAxisWidth[i].width + opts.yAxis.padding;
+        }else{
+          opts.area[1] += yAxisWidth[i].width;
+        }
+        rightIndex +=1;
+      }
+    }
+  }else{
+    config.yAxisWidth = yAxisWidth;
+  }
+  opts.chartData.yAxisData = _calYAxisData;
+
+  if (opts.categories && opts.categories.length) {
+    opts.chartData.xAxisData = getXAxisPoints(opts.categories, opts, config);
+    let _calCategoriesData = calCategoriesData(opts.categories, opts, config, opts.chartData.xAxisData.eachSpacing),
+      xAxisHeight = _calCategoriesData.xAxisHeight,
+      angle = _calCategoriesData.angle;
+    config.xAxisHeight = xAxisHeight;
+    config._xAxisTextAngle_ = angle;
+    opts.area[2] += xAxisHeight;
+    opts.chartData.categoriesData = _calCategoriesData;
+  }else{
+		if (opts.type === 'line' || opts.type === 'area' || opts.type === 'points') {
+			opts.chartData.xAxisData = calXAxisData(series, opts, config);
+			categories=opts.chartData.xAxisData.rangesFormat;
+			let _calCategoriesData = calCategoriesData(categories, opts, config, opts.chartData.xAxisData.eachSpacing),
+			  xAxisHeight = _calCategoriesData.xAxisHeight,
+			  angle = _calCategoriesData.angle;
+			config.xAxisHeight = xAxisHeight;
+			config._xAxisTextAngle_ = angle;
+			opts.area[2] += xAxisHeight;
+			opts.chartData.categoriesData = _calCategoriesData;
+		}else{
+			opts.chartData.xAxisData={
+				xAxisPoints: []
+			};
+		}
+	}
+  //计算右对齐偏移距离
+  if (opts.enableScroll && opts.xAxis.scrollAlign == 'right' && opts._scrollDistance_ === undefined) {
+    let offsetLeft = 0,
+      xAxisPoints = opts.chartData.xAxisData.xAxisPoints,
+      startX = opts.chartData.xAxisData.startX,
+      endX = opts.chartData.xAxisData.endX,
+      eachSpacing = opts.chartData.xAxisData.eachSpacing;
+    let totalWidth = eachSpacing * (xAxisPoints.length - 1);
+    let screenWidth = endX - startX;
+    offsetLeft = screenWidth - totalWidth;
+    _this.scrollOption = {
+      currentOffset: offsetLeft,
+      startTouchX: offsetLeft,
+      distance: 0,
+      lastMoveTime: 0
+    };
+    opts._scrollDistance_ = offsetLeft;
+  }
+
+  if (type === 'pie' || type === 'ring' || type === 'rose') {
+    config._pieTextMaxLength_ = opts.dataLabel === false ? 0 : getPieTextMaxLength(seriesMA);
+  }
+
+  switch (type) {
+    case 'word':
+      let wordOption = assign({},{
+        type: 'normal',
+        autoColors: true
+      },opts.extra.word);
+      if(opts.updateData==true || opts.updateData==undefined){
+        opts.chartData.wordCloudData=getWordCloudPoint(opts,wordOption.type);
+      }
+      this.animationInstance = new Animation({
+        timing: 'easeInOut',
+        duration: duration,
+        onProcess: function(process) {
+          context.clearRect(0, 0, opts.width, opts.height);
+          if (opts.rotate) {
+            contextRotate(context, opts);
+          }
+          drawWordCloudDataPoints(series, opts, config, context,process);
+          drawCanvas(opts, context);
+        },
+        onAnimationFinish: function onAnimationFinish() {
+          _this.event.trigger('renderComplete');
+        }
+      });
+    break;
+    case 'map':
+      context.clearRect(0, 0, opts.width, opts.height);
+      drawMapDataPoints(series, opts, config, context);
+    break;
+    case 'funnel':
+      this.animationInstance = new Animation({
+        timing: 'easeInOut',
+        duration: duration,
+        onProcess: function(process) {
+          context.clearRect(0, 0, opts.width, opts.height);
+          if (opts.rotate) {
+            contextRotate(context, opts);
+          }
+          opts.chartData.funnelData = drawFunnelDataPoints(series, opts, config, context, process);
+          drawLegend(opts.series, opts, config, context, opts.chartData);
+          drawToolTipBridge(opts, config, context, process);
+          drawCanvas(opts, context);
+        },
+        onAnimationFinish: function onAnimationFinish() {
+          _this.event.trigger('renderComplete');
+        }
+      });
+    break;
+    case 'line':
+      this.animationInstance = new Animation({
+        timing: 'easeIn',
+        duration: duration,
+        onProcess: function onProcess(process) {
+          context.clearRect(0, 0, opts.width, opts.height);
+          if (opts.rotate) {
+            contextRotate(context, opts);
+          }
+          drawYAxisGrid(categories, opts, config, context);
+          drawXAxis(categories, opts, config, context);
+          var _drawLineDataPoints = drawLineDataPoints(series, opts, config, context, process),
+            xAxisPoints = _drawLineDataPoints.xAxisPoints,
+            calPoints = _drawLineDataPoints.calPoints,
+            eachSpacing = _drawLineDataPoints.eachSpacing;
+          opts.chartData.xAxisPoints = xAxisPoints;
+          opts.chartData.calPoints = calPoints;
+          opts.chartData.eachSpacing = eachSpacing;
+          drawYAxis(series, opts, config, context);
+          if (opts.enableMarkLine !== false && process === 1) {
+            drawMarkLine(opts, config, context);
+          }
+          drawLegend(opts.series, opts, config, context, opts.chartData);
+          drawToolTipBridge(opts, config, context, process, eachSpacing, xAxisPoints);
+          drawCanvas(opts, context);
+
+        },
+        onAnimationFinish: function onAnimationFinish() {
+          _this.event.trigger('renderComplete');
+        }
+      });
+      break;
+    case 'mix':
+      this.animationInstance = new Animation({
+        timing: 'easeIn',
+        duration: duration,
+        onProcess: function onProcess(process) {
+          context.clearRect(0, 0, opts.width, opts.height);
+          if (opts.rotate) {
+            contextRotate(context, opts);
+          }
+          drawYAxisGrid(categories, opts, config, context);
+          drawXAxis(categories, opts, config, context);
+          var _drawMixDataPoints = drawMixDataPoints(series, opts, config, context, process),
+            xAxisPoints = _drawMixDataPoints.xAxisPoints,
+            calPoints = _drawMixDataPoints.calPoints,
+            eachSpacing = _drawMixDataPoints.eachSpacing;
+          opts.chartData.xAxisPoints = xAxisPoints;
+          opts.chartData.calPoints = calPoints;
+          opts.chartData.eachSpacing = eachSpacing;
+          drawYAxis(series, opts, config, context);
+          if (opts.enableMarkLine !== false && process === 1) {
+            drawMarkLine(opts, config, context);
+          }
+          drawLegend(opts.series, opts, config, context, opts.chartData);
+          drawToolTipBridge(opts, config, context, process, eachSpacing, xAxisPoints);
+          drawCanvas(opts, context);
+        },
+        onAnimationFinish: function onAnimationFinish() {
+          _this.event.trigger('renderComplete');
+        }
+      });
+      break;
+    case 'column':
+      this.animationInstance = new Animation({
+        timing: 'easeIn',
+        duration: duration,
+        onProcess: function onProcess(process) {
+          context.clearRect(0, 0, opts.width, opts.height);
+          if (opts.rotate) {
+            contextRotate(context, opts);
+          }
+          drawYAxisGrid(categories, opts, config, context);
+          drawXAxis(categories, opts, config, context);
+          var _drawColumnDataPoints = drawColumnDataPoints(series, opts, config, context, process),
+            xAxisPoints = _drawColumnDataPoints.xAxisPoints,
+            calPoints = _drawColumnDataPoints.calPoints,
+            eachSpacing = _drawColumnDataPoints.eachSpacing;
+          opts.chartData.xAxisPoints = xAxisPoints;
+          opts.chartData.calPoints = calPoints;
+          opts.chartData.eachSpacing = eachSpacing;
+          drawYAxis(series, opts, config, context);
+          if (opts.enableMarkLine !== false && process === 1) {
+            drawMarkLine(opts, config, context);
+          }
+          drawLegend(opts.series, opts, config, context, opts.chartData);
+          drawToolTipBridge(opts, config, context, process, eachSpacing, xAxisPoints);
+          drawCanvas(opts, context);
+        },
+        onAnimationFinish: function onAnimationFinish() {
+          _this.event.trigger('renderComplete');
+        }
+      });
+      break;
+    case 'area':
+      this.animationInstance = new Animation({
+        timing: 'easeIn',
+        duration: duration,
+        onProcess: function onProcess(process) {
+          context.clearRect(0, 0, opts.width, opts.height);
+          if (opts.rotate) {
+            contextRotate(context, opts);
+          }
+          drawYAxisGrid(categories, opts, config, context);
+          drawXAxis(categories, opts, config, context);
+          var _drawAreaDataPoints = drawAreaDataPoints(series, opts, config, context, process),
+            xAxisPoints = _drawAreaDataPoints.xAxisPoints,
+            calPoints = _drawAreaDataPoints.calPoints,
+            eachSpacing = _drawAreaDataPoints.eachSpacing;
+          opts.chartData.xAxisPoints = xAxisPoints;
+          opts.chartData.calPoints = calPoints;
+          opts.chartData.eachSpacing = eachSpacing;
+          drawYAxis(series, opts, config, context);
+          if (opts.enableMarkLine !== false && process === 1) {
+            drawMarkLine(opts, config, context);
+          }
+          drawLegend(opts.series, opts, config, context, opts.chartData);
+          drawToolTipBridge(opts, config, context, process, eachSpacing, xAxisPoints);
+          drawCanvas(opts, context);
+        },
+        onAnimationFinish: function onAnimationFinish() {
+          _this.event.trigger('renderComplete');
+        }
+      });
+      break;
+    case 'ring':
+    case 'pie':
+      this.animationInstance = new Animation({
+        timing: 'easeInOut',
+        duration: duration,
+        onProcess: function onProcess(process) {
+          context.clearRect(0, 0, opts.width, opts.height);
+          if (opts.rotate) {
+            contextRotate(context, opts);
+          }
+          opts.chartData.pieData = drawPieDataPoints(series, opts, config, context, process);
+          drawLegend(opts.series, opts, config, context, opts.chartData);
+          drawToolTipBridge(opts, config, context, process);
+          drawCanvas(opts, context);
+        },
+        onAnimationFinish: function onAnimationFinish() {
+          _this.event.trigger('renderComplete');
+        }
+      });
+      break;
+    case 'rose':
+      this.animationInstance = new Animation({
+        timing: 'easeInOut',
+        duration: duration,
+        onProcess: function onProcess(process) {
+          context.clearRect(0, 0, opts.width, opts.height);
+          if (opts.rotate) {
+            contextRotate(context, opts);
+          }
+          opts.chartData.pieData = drawRoseDataPoints(series, opts, config, context, process);
+          drawLegend(opts.series, opts, config, context, opts.chartData);
+          drawToolTipBridge(opts, config, context, process);
+          drawCanvas(opts, context);
+        },
+        onAnimationFinish: function onAnimationFinish() {
+          _this.event.trigger('renderComplete');
+        }
+      });
+      break;
+    case 'radar':
+      this.animationInstance = new Animation({
+        timing: 'easeInOut',
+        duration: duration,
+        onProcess: function onProcess(process) {
+          context.clearRect(0, 0, opts.width, opts.height);
+          if (opts.rotate) {
+            contextRotate(context, opts);
+          }
+          opts.chartData.radarData = drawRadarDataPoints(series, opts, config, context, process);
+          drawLegend(opts.series, opts, config, context, opts.chartData);
+          drawToolTipBridge(opts, config, context, process);
+          drawCanvas(opts, context);
+        },
+        onAnimationFinish: function onAnimationFinish() {
+          _this.event.trigger('renderComplete');
+        }
+      });
+      break;
+    case 'arcbar':
+      this.animationInstance = new Animation({
+        timing: 'easeInOut',
+        duration: duration,
+        onProcess: function onProcess(process) {
+          context.clearRect(0, 0, opts.width, opts.height);
+          if (opts.rotate) {
+            contextRotate(context, opts);
+          }
+          opts.chartData.arcbarData = drawArcbarDataPoints(series, opts, config, context, process);
+          drawCanvas(opts, context);
+        },
+        onAnimationFinish: function onAnimationFinish() {
+          _this.event.trigger('renderComplete');
+        }
+      });
+      break;
+    case 'gauge':
+      this.animationInstance = new Animation({
+        timing: 'easeInOut',
+        duration: duration,
+        onProcess: function onProcess(process) {
+          context.clearRect(0, 0, opts.width, opts.height);
+          if (opts.rotate) {
+            contextRotate(context, opts);
+          }
+          opts.chartData.gaugeData = drawGaugeDataPoints(categories, series, opts, config, context, process);
+          drawCanvas(opts, context);
+        },
+        onAnimationFinish: function onAnimationFinish() {
+          _this.event.trigger('renderComplete');
+        }
+      });
+      break;
+    case 'candle':
+      this.animationInstance = new Animation({
+        timing: 'easeIn',
+        duration: duration,
+        onProcess: function onProcess(process) {
+          context.clearRect(0, 0, opts.width, opts.height);
+          if (opts.rotate) {
+            contextRotate(context, opts);
+          }
+          drawYAxisGrid(categories, opts, config, context);
+          drawXAxis(categories, opts, config, context);
+          var _drawCandleDataPoints = drawCandleDataPoints(series, seriesMA, opts, config, context, process),
+            xAxisPoints = _drawCandleDataPoints.xAxisPoints,
+            calPoints = _drawCandleDataPoints.calPoints,
+            eachSpacing = _drawCandleDataPoints.eachSpacing;
+          opts.chartData.xAxisPoints = xAxisPoints;
+          opts.chartData.calPoints = calPoints;
+          opts.chartData.eachSpacing = eachSpacing;
+          drawYAxis(series, opts, config, context);
+          if (opts.enableMarkLine !== false && process === 1) {
+            drawMarkLine(opts, config, context);
+          }
+          if (seriesMA) {
+            drawLegend(seriesMA, opts, config, context, opts.chartData);
+          } else {
+            drawLegend(opts.series, opts, config, context, opts.chartData);
+          }
+          drawToolTipBridge(opts, config, context, process, eachSpacing, xAxisPoints);
+          drawCanvas(opts, context);
+        },
+        onAnimationFinish: function onAnimationFinish() {
+          _this.event.trigger('renderComplete');
+        }
+      });
+      break;
+  }
+}
+
+// simple event implement
+
+function Event() {
+  this.events = {};
+}
+
+Event.prototype.addEventListener = function(type, listener) {
+  this.events[type] = this.events[type] || [];
+  this.events[type].push(listener);
+};
+
+Event.prototype.trigger = function() {
+  for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+    args[_key] = arguments[_key];
+  }
+
+  var type = args[0];
+  var params = args.slice(1);
+  if (!!this.events[type]) {
+    this.events[type].forEach(function(listener) {
+      try {
+        listener.apply(null, params);
+      } catch (e) {
+        console.error(e);
+      }
+    });
+  }
+};
+
+var Charts = function Charts(opts) {
+  opts.pixelRatio = opts.pixelRatio ? opts.pixelRatio : 1;
+  opts.fontSize = opts.fontSize ? opts.fontSize * opts.pixelRatio : 13 * opts.pixelRatio;
+  opts.title = assign({}, opts.title);
+  opts.subtitle = assign({}, opts.subtitle);
+  opts.duration = opts.duration ? opts.duration : 1000;
+  opts.yAxis = assign({}, {
+    data:[],
+    showTitle:false,
+    disabled:false,
+    disableGrid:false,
+    splitNumber:5,
+    gridType: 'solid',
+    dashLength: 4 * opts.pixelRatio,
+    gridColor:'#cccccc',
+    padding:10,
+    fontColor:'#666666'
+  }, opts.yAxis);
+  opts.yAxis.dashLength *= opts.pixelRatio;
+  opts.yAxis.padding *= opts.pixelRatio;
+  opts.xAxis = assign({}, {
+    rotateLabel: false,
+    type: 'calibration',
+    gridType: 'solid',
+    dashLength: 4,
+    scrollAlign: 'left',
+    boundaryGap:'center',
+    axisLine:true,
+    axisLineColor:'#cccccc'
+  }, opts.xAxis);
+  opts.xAxis.dashLength *= opts.pixelRatio;
+  opts.legend = assign({}, {
+    show: true,
+    position: 'bottom',
+    float: 'center',
+    backgroundColor: 'rgba(0,0,0,0)',
+    borderColor: 'rgba(0,0,0,0)',
+    borderWidth: 0,
+    padding: 5,
+    margin: 5,
+    itemGap: 10,
+    fontSize: opts.fontSize,
+    lineHeight: opts.fontSize,
+    fontColor: '#333333',
+    format: {},
+    hiddenColor: '#CECECE'
+  }, opts.legend);
+  opts.legend.borderWidth = opts.legend.borderWidth * opts.pixelRatio;
+  opts.legend.itemGap = opts.legend.itemGap * opts.pixelRatio;
+  opts.legend.padding = opts.legend.padding * opts.pixelRatio;
+  opts.legend.margin = opts.legend.margin * opts.pixelRatio;
+  opts.extra = assign({}, opts.extra);
+  opts.rotate = opts.rotate ? true : false;
+  opts.animation = opts.animation ? true : false;
+	opts.rotate = opts.rotate ? true : false;
+
+  let config$$1 = JSON.parse(JSON.stringify(config));
+  config$$1.colors = opts.colors ? opts.colors : config$$1.colors;
+  config$$1.yAxisTitleWidth = opts.yAxis.disabled !== true && opts.yAxis.title ? config$$1.yAxisTitleWidth : 0;
+  if (opts.type == 'pie' || opts.type == 'ring') {
+    config$$1.pieChartLinePadding = opts.dataLabel === false ? 0 : opts.extra.pie.labelWidth * opts.pixelRatio || config$$1.pieChartLinePadding * opts.pixelRatio;
+  }
+  if (opts.type == 'rose') {
+    config$$1.pieChartLinePadding = opts.dataLabel === false ? 0 : opts.extra.rose.labelWidth * opts.pixelRatio || config$$1.pieChartLinePadding * opts.pixelRatio;
+  }
+  config$$1.pieChartTextPadding = opts.dataLabel === false ? 0 : config$$1.pieChartTextPadding * opts.pixelRatio;
+  config$$1.yAxisSplit = opts.yAxis.splitNumber ? opts.yAxis.splitNumber : config.yAxisSplit;
+
+  //屏幕旋转
+  config$$1.rotate = opts.rotate;
+  if (opts.rotate) {
+    let tempWidth = opts.width;
+    let tempHeight = opts.height;
+    opts.width = tempHeight;
+    opts.height = tempWidth;
+  }
+
+  //适配高分屏
+  opts.padding = opts.padding ? opts.padding : config$$1.padding;
+  for (let i = 0; i < 4; i++) {
+    opts.padding[i] *= opts.pixelRatio;
+  }
+  config$$1.yAxisWidth = config.yAxisWidth * opts.pixelRatio;
+  config$$1.xAxisHeight = config.xAxisHeight * opts.pixelRatio;
+  if (opts.enableScroll && opts.xAxis.scrollShow) {
+    config$$1.xAxisHeight += 6 * opts.pixelRatio;
+  }
+  config$$1.xAxisLineHeight = config.xAxisLineHeight * opts.pixelRatio;
+  config$$1.fontSize = opts.fontSize;
+  config$$1.titleFontSize = config.titleFontSize * opts.pixelRatio;
+  config$$1.subtitleFontSize = config.subtitleFontSize * opts.pixelRatio;
+  config$$1.toolTipPadding = config.toolTipPadding * opts.pixelRatio;
+  config$$1.toolTipLineHeight = config.toolTipLineHeight * opts.pixelRatio;
+  config$$1.columePadding = config.columePadding * opts.pixelRatio;
+  opts.$this = opts.$this ? opts.$this : this;
+  
+  this.context = uni.createCanvasContext(opts.canvasId, opts.$this);
+  /* 兼容原生H5
+  this.context = document.getElementById(opts.canvasId).getContext("2d");
+  this.context.setStrokeStyle = function(e){ return this.strokeStyle=e; }
+  this.context.setLineWidth = function(e){ return this.lineWidth=e; }
+  this.context.setLineCap = function(e){ return this.lineCap=e; }
+  this.context.setFontSize = function(e){ return this.font=e+"px sans-serif"; }
+  this.context.setFillStyle = function(e){ return this.fillStyle=e; }
+  this.context.draw = function(){ }
+  */
+
+  opts.chartData = {};
+  this.event = new Event();
+  this.scrollOption = {
+    currentOffset: 0,
+    startTouchX: 0,
+    distance: 0,
+    lastMoveTime: 0
+  };
+
+  this.opts = opts;
+  this.config = config$$1;
+
+  drawCharts.call(this, opts.type, opts, config$$1, this.context);
+};
+
+Charts.prototype.updateData = function() {
+  let data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+  this.opts = assign({}, this.opts, data);
+  this.opts.updateData = true;
+  let scrollPosition = data.scrollPosition || 'current';
+  switch (scrollPosition) {
+    case 'current':
+      this.opts._scrollDistance_ = this.scrollOption.currentOffset;
+      break;
+    case 'left':
+      this.opts._scrollDistance_ = 0;
+      this.scrollOption = {
+        currentOffset: 0,
+        startTouchX: 0,
+        distance: 0,
+        lastMoveTime: 0
+      };
+      break;
+    case 'right':
+      let _calYAxisData = calYAxisData(this.opts.series, this.opts, this.config),
+        yAxisWidth = _calYAxisData.yAxisWidth;
+      this.config.yAxisWidth = yAxisWidth;
+      let offsetLeft = 0;
+      let _getXAxisPoints0 = getXAxisPoints(this.opts.categories, this.opts, this.config),
+        xAxisPoints = _getXAxisPoints0.xAxisPoints,
+        startX = _getXAxisPoints0.startX,
+        endX = _getXAxisPoints0.endX,
+        eachSpacing = _getXAxisPoints0.eachSpacing;
+      let totalWidth = eachSpacing * (xAxisPoints.length - 1);
+      let screenWidth = endX - startX;
+      offsetLeft = screenWidth - totalWidth;
+      this.scrollOption = {
+        currentOffset: offsetLeft,
+        startTouchX: offsetLeft,
+        distance: 0,
+        lastMoveTime: 0
+      };
+      this.opts._scrollDistance_ = offsetLeft;
+      break;
+  }
+  drawCharts.call(this, this.opts.type, this.opts, this.config, this.context);
+};
+
+Charts.prototype.zoom = function() {
+  var val = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.opts.xAxis.itemCount;
+  if (this.opts.enableScroll !== true) {
+    console.log('请启用滚动条后使用!')
+    return;
+  }
+  //当前屏幕中间点
+  let centerPoint = Math.round(Math.abs(this.scrollOption.currentOffset) / this.opts.chartData.eachSpacing) + Math.round(
+    this.opts.xAxis.itemCount / 2);
+  this.opts.animation = false;
+  this.opts.xAxis.itemCount = val.itemCount;
+  //重新计算x轴偏移距离
+  let _calYAxisData = calYAxisData(this.opts.series, this.opts, this.config),
+    yAxisWidth = _calYAxisData.yAxisWidth;
+  this.config.yAxisWidth = yAxisWidth;
+  let offsetLeft = 0;
+  let _getXAxisPoints0 = getXAxisPoints(this.opts.categories, this.opts, this.config),
+    xAxisPoints = _getXAxisPoints0.xAxisPoints,
+    startX = _getXAxisPoints0.startX,
+    endX = _getXAxisPoints0.endX,
+    eachSpacing = _getXAxisPoints0.eachSpacing;
+  let centerLeft = eachSpacing * centerPoint;
+  let screenWidth = endX - startX;
+  let MaxLeft = screenWidth - eachSpacing * (xAxisPoints.length - 1);
+  offsetLeft = screenWidth / 2 - centerLeft;
+  if (offsetLeft > 0) {
+    offsetLeft = 0;
+  }
+  if (offsetLeft < MaxLeft) {
+    offsetLeft = MaxLeft;
+  }
+  this.scrollOption = {
+    currentOffset: offsetLeft,
+    startTouchX: offsetLeft,
+    distance: 0,
+    lastMoveTime: 0
+  };
+  this.opts._scrollDistance_ = offsetLeft;
+  drawCharts.call(this, this.opts.type, this.opts, this.config, this.context);
+};
+
+Charts.prototype.stopAnimation = function() {
+  this.animationInstance && this.animationInstance.stop();
+};
+
+Charts.prototype.addEventListener = function(type, listener) {
+  this.event.addEventListener(type, listener);
+};
+
+Charts.prototype.getCurrentDataIndex = function(e) {
+  var touches = null;
+  if (e.changedTouches) {
+    touches = e.changedTouches[0];
+  } else {
+    touches = e.mp.changedTouches[0];
+  }
+  if (touches) {
+    let _touches$ = getTouches(touches, this.opts, e);
+    if (this.opts.type === 'pie' || this.opts.type === 'ring' || this.opts.type === 'rose') {
+      return findPieChartCurrentIndex({
+        x: _touches$.x,
+        y: _touches$.y
+      }, this.opts.chartData.pieData);
+    } else if (this.opts.type === 'radar') {
+      return findRadarChartCurrentIndex({
+        x: _touches$.x,
+        y: _touches$.y
+      }, this.opts.chartData.radarData, this.opts.categories.length);
+    } else if (this.opts.type === 'funnel') {
+      return findFunnelChartCurrentIndex({
+        x: _touches$.x,
+        y: _touches$.y
+      }, this.opts.chartData.funnelData);
+    } else if (this.opts.type === 'map') {
+      return findMapChartCurrentIndex({
+        x: _touches$.x,
+        y: _touches$.y
+      }, this.opts);
+    }else if (this.opts.type === 'word') {
+      return findWordChartCurrentIndex({
+        x: _touches$.x,
+        y: _touches$.y
+      }, this.opts.chartData.wordCloudData);
+    } else {
+      return findCurrentIndex({
+        x: _touches$.x,
+        y: _touches$.y
+      }, this.opts.chartData.calPoints, this.opts, this.config, Math.abs(this.scrollOption.currentOffset));
+    }
+  }
+  return -1;
+};
+
+Charts.prototype.getLegendDataIndex = function(e) {
+  var touches = null;
+  if (e.changedTouches) {
+    touches = e.changedTouches[0];
+  } else {
+    touches = e.mp.changedTouches[0];
+  }
+  if (touches) {
+    let _touches$ = getTouches(touches, this.opts, e);
+    return findLegendIndex({
+      x: _touches$.x,
+      y: _touches$.y
+    }, this.opts.chartData.legendData);
+  }
+  return -1;
+};
+
+Charts.prototype.touchLegend = function(e) {
+  var option = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+  var touches = null;
+  if (e.changedTouches) {
+    touches = e.changedTouches[0];
+  } else {
+    touches = e.mp.changedTouches[0];
+  }
+  if (touches) {
+    var _touches$ = getTouches(touches, this.opts, e);
+    var index = this.getLegendDataIndex(e);
+    if (index >= 0) {
+      this.opts.series[index].show = !this.opts.series[index].show;
+      this.opts.animation = option.animation ? true : false;
+			this.opts._scrollDistance_= this.scrollOption.currentOffset;
+      drawCharts.call(this, this.opts.type, this.opts, this.config, this.context);
+    }
+  }
+
+};
+
+Charts.prototype.showToolTip = function(e) {
+  var option = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+  var touches = null;
+  if (e.changedTouches) {
+    touches = e.changedTouches[0];
+  } else {
+    touches = e.mp.changedTouches[0];
+  }
+  if (!touches) {
+    console.log("touchError");
+  }
+  var _touches$ = getTouches(touches, this.opts, e);
+  var currentOffset = this.scrollOption.currentOffset;
+  var opts = assign({}, this.opts, {
+    _scrollDistance_: currentOffset,
+    animation: false
+  });
+  if (this.opts.type === 'line' || this.opts.type === 'area' || this.opts.type === 'column') {
+    var index = option.index==undefined? this.getCurrentDataIndex(e):option.index ;
+    if (index > -1) {
+      var seriesData = getSeriesDataItem(this.opts.series, index);
+      if (seriesData.length !== 0) {
+        var _getToolTipData = getToolTipData(seriesData, this.opts.chartData.calPoints, index, this.opts.categories,option),
+          textList = _getToolTipData.textList,
+          offset = _getToolTipData.offset;
+        offset.y = _touches$.y;
+        opts.tooltip = {
+          textList: option.textList?option.textList:textList,
+          offset: offset,
+          option: option,
+          index: index
+        };
+      }
+    }
+    drawCharts.call(this, opts.type, opts, this.config, this.context);
+  }
+  if (this.opts.type === 'mix') {
+    var index = option.index==undefined? this.getCurrentDataIndex(e):option.index ;
+    if (index > -1) {
+      var currentOffset = this.scrollOption.currentOffset;
+      var opts = assign({}, this.opts, {
+        _scrollDistance_: currentOffset,
+        animation: false
+      });
+      var seriesData = getSeriesDataItem(this.opts.series, index);
+      if (seriesData.length !== 0) {
+        var _getMixToolTipData = getMixToolTipData(seriesData, this.opts.chartData.calPoints, index, this.opts.categories,option),
+          textList = _getMixToolTipData.textList,
+          offset = _getMixToolTipData.offset;
+        offset.y = _touches$.y;
+        opts.tooltip = {
+          textList: option.textList?option.textList:textList,
+          offset: offset,
+          option: option,
+          index: index
+        };
+      }
+    }
+    drawCharts.call(this, opts.type, opts, this.config, this.context);
+  }
+  if (this.opts.type === 'candle') {
+    var index = option.index==undefined? this.getCurrentDataIndex(e):option.index ;
+    if (index > -1) {
+      var currentOffset = this.scrollOption.currentOffset;
+      var opts = assign({}, this.opts, {
+        _scrollDistance_: currentOffset,
+        animation: false
+      });
+      var seriesData = getSeriesDataItem(this.opts.series, index);
+      if (seriesData.length !== 0) {
+        var _getToolTipData = getCandleToolTipData(this.opts.series[0].data, seriesData, this.opts.chartData.calPoints,
+            index, this.opts.categories, this.opts.extra.candle, option),
+          textList = _getToolTipData.textList,
+          offset = _getToolTipData.offset;
+        offset.y = _touches$.y;
+        opts.tooltip = {
+          textList: option.textList?option.textList:textList,
+          offset: offset,
+          option: option,
+          index: index
+        };
+      }
+    }
+    drawCharts.call(this, opts.type, opts, this.config, this.context);
+  }
+  if (this.opts.type === 'pie' || this.opts.type === 'ring' || this.opts.type === 'rose'||this.opts.type === 'funnel' ) {
+    var index = option.index==undefined? this.getCurrentDataIndex(e):option.index ;
+    if (index > -1) {
+      var currentOffset = this.scrollOption.currentOffset;
+      var opts = assign({}, this.opts, {
+        _scrollDistance_: currentOffset,
+        animation: false
+      });
+      var seriesData = this.opts._series_[index];
+      var textList = [{
+        text: option.format ? option.format(seriesData) : seriesData.name + ': ' + seriesData.data,
+        color: seriesData.color
+      }];
+      var offset = {
+        x: _touches$.x,
+        y: _touches$.y
+      };
+      opts.tooltip = {
+        textList: option.textList?option.textList:textList,
+        offset: offset,
+        option: option,
+        index: index
+      };
+    }
+    drawCharts.call(this, opts.type, opts, this.config, this.context);
+  }
+  if (this.opts.type === 'map'||this.opts.type === 'word') {
+    var index = option.index==undefined? this.getCurrentDataIndex(e):option.index ;
+    if (index > -1) {
+      var currentOffset = this.scrollOption.currentOffset;
+      var opts = assign({}, this.opts, {
+        _scrollDistance_: currentOffset,
+        animation: false
+      });
+      var seriesData = this.opts._series_[index];
+      var textList = [{
+        text: option.format ? option.format(seriesData) : seriesData.properties.name ,
+        color: seriesData.color
+      }];
+      var offset = {
+        x: _touches$.x,
+        y: _touches$.y
+      };
+      opts.tooltip = {
+        textList: option.textList?option.textList:textList,
+        offset: offset,
+        option: option,
+        index: index
+      };
+    }
+    opts.updateData = false;
+    drawCharts.call(this, opts.type, opts, this.config, this.context);
+  }
+  if (this.opts.type === 'radar') {
+    var index = option.index==undefined? this.getCurrentDataIndex(e):option.index ;
+    if (index > -1) {
+      var currentOffset = this.scrollOption.currentOffset;
+      var opts = assign({}, this.opts, {
+        _scrollDistance_: currentOffset,
+        animation: false
+      });
+      var seriesData = getSeriesDataItem(this.opts.series, index);
+      if (seriesData.length !== 0) {
+        var textList = seriesData.map(function(item) {
+          return {
+            text: option.format ? option.format(item) : item.name + ': ' + item.data,
+            color: item.color
+          };
+        });
+        var offset = {
+          x: _touches$.x,
+          y: _touches$.y
+        };
+        opts.tooltip = {
+          textList: option.textList?option.textList:textList,
+          offset: offset,
+          option: option,
+          index: index
+        };
+      }
+    }
+    drawCharts.call(this, opts.type, opts, this.config, this.context);
+  }
+};
+
+Charts.prototype.translate = function(distance) {
+  this.scrollOption = {
+    currentOffset: distance,
+    startTouchX: distance,
+    distance: 0,
+    lastMoveTime: 0
+  };
+  let opts = assign({}, this.opts, {
+    _scrollDistance_: distance,
+    animation: false
+  });
+  drawCharts.call(this, this.opts.type, opts, this.config, this.context);
+};
+
+Charts.prototype.scrollStart = function(e) {
+  var touches = null;
+  if (e.changedTouches) {
+    touches = e.changedTouches[0];
+  } else {
+    touches = e.mp.changedTouches[0];
+  }
+  var _touches$ = getTouches(touches, this.opts, e);
+  if (touches && this.opts.enableScroll === true) {
+    this.scrollOption.startTouchX = _touches$.x;
+  }
+};
+
+Charts.prototype.scroll = function(e) {
+  if (this.scrollOption.lastMoveTime === 0) {
+    this.scrollOption.lastMoveTime = Date.now();
+  }
+  let Limit = this.opts.extra.touchMoveLimit || 20;
+  let currMoveTime = Date.now();
+  let duration = currMoveTime - this.scrollOption.lastMoveTime;
+  if (duration < Math.floor(1000 / Limit)) return;
+  this.scrollOption.lastMoveTime = currMoveTime;
+  var touches = null;
+  if (e.changedTouches) {
+    touches = e.changedTouches[0];
+  } else {
+    touches = e.mp.changedTouches[0];
+  }
+  if (touches && this.opts.enableScroll === true) {
+    var _touches$ = getTouches(touches, this.opts, e);
+    var _distance;
+    _distance = _touches$.x - this.scrollOption.startTouchX;
+    var currentOffset = this.scrollOption.currentOffset;
+    var validDistance = calValidDistance(this,currentOffset + _distance, this.opts.chartData, this.config, this.opts);
+    this.scrollOption.distance = _distance = validDistance - currentOffset;
+    var opts = assign({}, this.opts, {
+      _scrollDistance_: currentOffset + _distance,
+      animation: false
+    });
+    drawCharts.call(this, opts.type, opts, this.config, this.context);
+    return currentOffset + _distance;
+  }
+};
+
+Charts.prototype.scrollEnd = function(e) {
+  if (this.opts.enableScroll === true) {
+    var _scrollOption = this.scrollOption,
+      currentOffset = _scrollOption.currentOffset,
+      distance = _scrollOption.distance;
+    this.scrollOption.currentOffset = currentOffset + distance;
+    this.scrollOption.distance = 0;
+  }
+};
+if (typeof module === "object" && typeof module.exports === "object") {
+  module.exports = Charts;
+  //export default Charts;//建议使用nodejs的module导出方式,如报错请使用export方式导出
+}

File diff suppressed because it is too large
+ 0 - 0
components/ucharts/ucharts.min.js


+ 546 - 0
components/uni-calendar/calendar.js

@@ -0,0 +1,546 @@
+/**
+* @1900-2100区间内的公历、农历互转
+* @charset UTF-8
+* @github  https://github.com/jjonline/calendar.js
+* @Author  Jea杨(JJonline@JJonline.Cn)
+* @Time    2014-7-21
+* @Time    2016-8-13 Fixed 2033hex、Attribution Annals
+* @Time    2016-9-25 Fixed lunar LeapMonth Param Bug
+* @Time    2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year
+* @Version 1.0.3
+* @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
+* @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
+*/
+/* eslint-disable */
+var calendar = {
+
+  /**
+      * 农历1900-2100的润大小信息表
+      * @Array Of Property
+      * @return Hex
+      */
+  lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909
+    0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
+    0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
+    0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
+    0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
+    0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
+    0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
+    0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
+    0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
+    0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
+    0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
+    0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
+    0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
+    0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
+    0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
+    /** Add By JJonline@JJonline.Cn**/
+    0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
+    0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
+    0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
+    0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
+    0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
+    0x0d520], // 2100
+
+  /**
+      * 公历每个月份的天数普通表
+      * @Array Of Property
+      * @return Number
+      */
+  solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+
+  /**
+      * 天干地支之天干速查表
+      * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
+      * @return Cn string
+      */
+  Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'],
+
+  /**
+      * 天干地支之地支速查表
+      * @Array Of Property
+      * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
+      * @return Cn string
+      */
+  Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'],
+
+  /**
+      * 天干地支之地支速查表<=>生肖
+      * @Array Of Property
+      * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
+      * @return Cn string
+      */
+  Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'],
+
+  /**
+      * 24节气速查表
+      * @Array Of Property
+      * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
+      * @return Cn string
+      */
+  solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'],
+
+  /**
+      * 1900-2100各年的24节气日期速查表
+      * @Array Of Property
+      * @return 0x string For splice
+      */
+  sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f',
+    '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f',
+    'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f',
+    '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa',
+    '97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f',
+    '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f',
+    '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f',
+    '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
+    '97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722',
+    '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
+    '97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2',
+    '977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35',
+    '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd',
+    '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35',
+    '7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35',
+    '665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35',
+    '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'],
+
+  /**
+      * 数字转中文速查表
+      * @Array Of Property
+      * @trans ['日','一','二','三','四','五','六','七','八','九','十']
+      * @return Cn string
+      */
+  nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'],
+
+  /**
+      * 日期转农历称呼速查表
+      * @Array Of Property
+      * @trans ['初','十','廿','卅']
+      * @return Cn string
+      */
+  nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'],
+
+  /**
+      * 月份转农历称呼速查表
+      * @Array Of Property
+      * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
+      * @return Cn string
+      */
+  nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'],
+
+  /**
+      * 返回农历y年一整年的总天数
+      * @param lunar Year
+      * @return Number
+      * @eg:var count = calendar.lYearDays(1987) ;//count=387
+      */
+  lYearDays: function (y) {
+    var i; var sum = 348
+    for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 }
+    return (sum + this.leapDays(y))
+  },
+
+  /**
+      * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0
+      * @param lunar Year
+      * @return Number (0-12)
+      * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
+      */
+  leapMonth: function (y) { // 闰字编码 \u95f0
+    return (this.lunarInfo[y - 1900] & 0xf)
+  },
+
+  /**
+      * 返回农历y年闰月的天数 若该年没有闰月则返回0
+      * @param lunar Year
+      * @return Number (0、29、30)
+      * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
+      */
+  leapDays: function (y) {
+    if (this.leapMonth(y)) {
+      return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29)
+    }
+    return (0)
+  },
+
+  /**
+      * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法
+      * @param lunar Year
+      * @return Number (-1、29、30)
+      * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
+      */
+  monthDays: function (y, m) {
+    if (m > 12 || m < 1) { return -1 }// 月份参数从1至12,参数错误返回-1
+    return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29)
+  },
+
+  /**
+      * 返回公历(!)y年m月的天数
+      * @param solar Year
+      * @return Number (-1、28、29、30、31)
+      * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
+      */
+  solarDays: function (y, m) {
+    if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
+    var ms = m - 1
+    if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29
+      return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28)
+    } else {
+      return (this.solarMonth[ms])
+    }
+  },
+
+  /**
+     * 农历年份转换为干支纪年
+     * @param  lYear 农历年的年份数
+     * @return Cn string
+     */
+  toGanZhiYear: function (lYear) {
+    var ganKey = (lYear - 3) % 10
+    var zhiKey = (lYear - 3) % 12
+    if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干
+    if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支
+    return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1]
+  },
+
+  /**
+     * 公历月、日判断所属星座
+     * @param  cMonth [description]
+     * @param  cDay [description]
+     * @return Cn string
+     */
+  toAstro: function (cMonth, cDay) {
+    var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf'
+    var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
+    return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座
+  },
+
+  /**
+      * 传入offset偏移量返回干支
+      * @param offset 相对甲子的偏移量
+      * @return Cn string
+      */
+  toGanZhi: function (offset) {
+    return this.Gan[offset % 10] + this.Zhi[offset % 12]
+  },
+
+  /**
+      * 传入公历(!)y年获得该年第n个节气的公历日期
+      * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起
+      * @return day Number
+      * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
+      */
+  getTerm: function (y, n) {
+    if (y < 1900 || y > 2100) { return -1 }
+    if (n < 1 || n > 24) { return -1 }
+    var _table = this.sTermInfo[y - 1900]
+    var _info = [
+      parseInt('0x' + _table.substr(0, 5)).toString(),
+      parseInt('0x' + _table.substr(5, 5)).toString(),
+      parseInt('0x' + _table.substr(10, 5)).toString(),
+      parseInt('0x' + _table.substr(15, 5)).toString(),
+      parseInt('0x' + _table.substr(20, 5)).toString(),
+      parseInt('0x' + _table.substr(25, 5)).toString()
+    ]
+    var _calday = [
+      _info[0].substr(0, 1),
+      _info[0].substr(1, 2),
+      _info[0].substr(3, 1),
+      _info[0].substr(4, 2),
+
+      _info[1].substr(0, 1),
+      _info[1].substr(1, 2),
+      _info[1].substr(3, 1),
+      _info[1].substr(4, 2),
+
+      _info[2].substr(0, 1),
+      _info[2].substr(1, 2),
+      _info[2].substr(3, 1),
+      _info[2].substr(4, 2),
+
+      _info[3].substr(0, 1),
+      _info[3].substr(1, 2),
+      _info[3].substr(3, 1),
+      _info[3].substr(4, 2),
+
+      _info[4].substr(0, 1),
+      _info[4].substr(1, 2),
+      _info[4].substr(3, 1),
+      _info[4].substr(4, 2),
+
+      _info[5].substr(0, 1),
+      _info[5].substr(1, 2),
+      _info[5].substr(3, 1),
+      _info[5].substr(4, 2)
+    ]
+    return parseInt(_calday[n - 1])
+  },
+
+  /**
+      * 传入农历数字月份返回汉语通俗表示法
+      * @param lunar month
+      * @return Cn string
+      * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
+      */
+  toChinaMonth: function (m) { // 月 => \u6708
+    if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
+    var s = this.nStr3[m - 1]
+    s += '\u6708'// 加上月字
+    return s
+  },
+
+  /**
+      * 传入农历日期数字返回汉字表示法
+      * @param lunar day
+      * @return Cn string
+      * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
+      */
+  toChinaDay: function (d) { // 日 => \u65e5
+    var s
+    switch (d) {
+      case 10:
+        s = '\u521d\u5341'; break
+      case 20:
+        s = '\u4e8c\u5341'; break
+        break
+      case 30:
+        s = '\u4e09\u5341'; break
+        break
+      default :
+        s = this.nStr2[Math.floor(d / 10)]
+        s += this.nStr1[d % 10]
+    }
+    return (s)
+  },
+
+  /**
+      * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
+      * @param y year
+      * @return Cn string
+      * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
+      */
+  getAnimal: function (y) {
+    return this.Animals[(y - 4) % 12]
+  },
+
+  /**
+      * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
+      * @param y  solar year
+      * @param m  solar month
+      * @param d  solar day
+      * @return JSON object
+      * @eg:console.log(calendar.solar2lunar(1987,11,01));
+      */
+  solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31
+    // 年份限定、上限
+    if (y < 1900 || y > 2100) {
+      return -1// undefined转换为数字变为NaN
+    }
+    // 公历传参最下限
+    if (y == 1900 && m == 1 && d < 31) {
+      return -1
+    }
+    // 未传参  获得当天
+    if (!y) {
+      var objDate = new Date()
+    } else {
+      var objDate = new Date(y, parseInt(m) - 1, d)
+    }
+    var i; var leap = 0; var temp = 0
+    // 修正ymd参数
+    var y = objDate.getFullYear()
+    var m = objDate.getMonth() + 1
+    var d = objDate.getDate()
+    var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000
+    for (i = 1900; i < 2101 && offset > 0; i++) {
+      temp = this.lYearDays(i)
+      offset -= temp
+    }
+    if (offset < 0) {
+      offset += temp; i--
+    }
+
+    // 是否今天
+    var isTodayObj = new Date()
+    var isToday = false
+    if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) {
+      isToday = true
+    }
+    // 星期几
+    var nWeek = objDate.getDay()
+    var cWeek = this.nStr1[nWeek]
+    // 数字表示周几顺应天朝周一开始的惯例
+    if (nWeek == 0) {
+      nWeek = 7
+    }
+    // 农历年
+    var year = i
+    var leap = this.leapMonth(i) // 闰哪个月
+    var isLeap = false
+
+    // 效验闰月
+    for (i = 1; i < 13 && offset > 0; i++) {
+      // 闰月
+      if (leap > 0 && i == (leap + 1) && isLeap == false) {
+        --i
+        isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数
+      } else {
+        temp = this.monthDays(year, i)// 计算农历普通月天数
+      }
+      // 解除闰月
+      if (isLeap == true && i == (leap + 1)) { isLeap = false }
+      offset -= temp
+    }
+    // 闰月导致数组下标重叠取反
+    if (offset == 0 && leap > 0 && i == leap + 1) {
+      if (isLeap) {
+        isLeap = false
+      } else {
+        isLeap = true; --i
+      }
+    }
+    if (offset < 0) {
+      offset += temp; --i
+    }
+    // 农历月
+    var month = i
+    // 农历日
+    var day = offset + 1
+    // 天干地支处理
+    var sm = m - 1
+    var gzY = this.toGanZhiYear(year)
+
+    // 当月的两个节气
+    // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
+    var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始
+    var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始
+
+    // 依据12节气修正干支月
+    var gzM = this.toGanZhi((y - 1900) * 12 + m + 11)
+    if (d >= firstNode) {
+      gzM = this.toGanZhi((y - 1900) * 12 + m + 12)
+    }
+
+    // 传入的日期的节气与否
+    var isTerm = false
+    var Term = null
+    if (firstNode == d) {
+      isTerm = true
+      Term = this.solarTerm[m * 2 - 2]
+    }
+    if (secondNode == d) {
+      isTerm = true
+      Term = this.solarTerm[m * 2 - 1]
+    }
+    // 日柱 当月一日与 1900/1/1 相差天数
+    var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
+    var gzD = this.toGanZhi(dayCyclical + d - 1)
+    // 该日期所属的星座
+    var astro = this.toAstro(m, d)
+
+    return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro }
+  },
+
+  /**
+      * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON
+      * @param y  lunar year
+      * @param m  lunar month
+      * @param d  lunar day
+      * @param isLeapMonth  lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
+      * @return JSON object
+      * @eg:console.log(calendar.lunar2solar(1987,9,10));
+      */
+  lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1
+    var isLeapMonth = !!isLeapMonth
+    var leapOffset = 0
+    var leapMonth = this.leapMonth(y)
+    var leapDay = this.leapDays(y)
+    if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
+    if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值
+    var day = this.monthDays(y, m)
+    var _day = day
+    // bugFix 2016-9-25
+    // if month is leap, _day use leapDays method
+    if (isLeapMonth) {
+      _day = this.leapDays(y, m)
+    }
+    if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验
+
+    // 计算农历的时间差
+    var offset = 0
+    for (var i = 1900; i < y; i++) {
+      offset += this.lYearDays(i)
+    }
+    var leap = 0; var isAdd = false
+    for (var i = 1; i < m; i++) {
+      leap = this.leapMonth(y)
+      if (!isAdd) { // 处理闰月
+        if (leap <= i && leap > 0) {
+          offset += this.leapDays(y); isAdd = true
+        }
+      }
+      offset += this.monthDays(y, i)
+    }
+    // 转换闰月农历 需补充该年闰月的前一个月的时差
+    if (isLeapMonth) { offset += day }
+    // 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
+    var stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
+    var calObj = new Date((offset + d - 31) * 86400000 + stmap)
+    var cY = calObj.getUTCFullYear()
+    var cM = calObj.getUTCMonth() + 1
+    var cD = calObj.getUTCDate()
+
+    return this.solar2lunar(cY, cM, cD)
+  }
+}
+
+export default calendar

+ 152 - 0
components/uni-calendar/uni-calendar-item.vue

@@ -0,0 +1,152 @@
+<template>
+	<view class="uni-calendar-item__weeks-box" :class="{
+		'uni-calendar-item--disable':weeks.disable,
+		'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
+		'uni-calendar-item--checked':(calendar.fullDate === weeks.fullDate && !weeks.isDay) ,
+		'uni-calendar-item--multiple': weeks.multiple
+		}"
+	 @click="choiceDate(weeks)">
+		<view class="uni-calendar-item__weeks-box-item">
+			<text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
+			<text class="uni-calendar-item__weeks-box-text" :class="{
+				'uni-calendar-item--isDay-text': weeks.isDay,
+				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
+				'uni-calendar-item--multiple': weeks.multiple,
+				'uni-calendar-item--disable':weeks.disable,
+				}">{{weeks.date}}</text>
+			<text v-if="!lunar&&!weeks.extraInfo && weeks.isDay" class="uni-calendar-item__weeks-lunar-text" :class="{
+				'uni-calendar-item--isDay-text':weeks.isDay,
+				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
+				'uni-calendar-item--multiple': weeks.multiple,
+				}">今天</text>
+			<text v-if="lunar&&!weeks.extraInfo" class="uni-calendar-item__weeks-lunar-text" :class="{
+				'uni-calendar-item--isDay-text':weeks.isDay,
+				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
+				'uni-calendar-item--multiple': weeks.multiple,
+				'uni-calendar-item--disable':weeks.disable,
+				}">{{weeks.isDay?'今天': (weeks.lunar.IDayCn === '初一'?weeks.lunar.IMonthCn:weeks.lunar.IDayCn)}}</text>
+			<text v-if="weeks.extraInfo&&weeks.extraInfo.info" class="uni-calendar-item__weeks-lunar-text" :class="{
+				'uni-calendar-item--extra':weeks.extraInfo.info,
+				'uni-calendar-item--isDay-text':weeks.isDay,
+				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
+				'uni-calendar-item--multiple': weeks.multiple,
+				'uni-calendar-item--disable':weeks.disable,
+				}">{{weeks.extraInfo.info}}</text>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			weeks: {
+				type: Object,
+				default () {
+					return {}
+				}
+			},
+			calendar: {
+				type: Object,
+				default: () => {
+					return {}
+				}
+			},
+			selected: {
+				type: Array,
+				default: () => {
+					return []
+				}
+			},
+			lunar: {
+				type: Boolean,
+				default: false
+			}
+		},
+		methods: {
+			choiceDate(weeks) {
+				this.$emit('change', weeks)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.uni-calendar-item__weeks-box {
+		flex: 1;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+	}
+
+	.uni-calendar-item__weeks-box-text {
+		font-size: $uni-font-size-base;
+		color: $uni-text-color;
+	}
+
+	.uni-calendar-item__weeks-lunar-text {
+		font-size: $uni-font-size-sm;
+		color: $uni-text-color;
+	}
+
+	.uni-calendar-item__weeks-box-item {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		width: 100rpx;
+		height: 100rpx;
+	}
+
+	.uni-calendar-item__weeks-box-circle {
+		position: absolute;
+		top: 5px;
+		right: 5px;
+		width: 8px;
+		height: 8px;
+		border-radius: 8px;
+		background-color: $uni-color-error;
+
+	}
+
+	.uni-calendar-item--disable {
+		background-color: rgba(249, 249, 249, $uni-opacity-disabled);
+		color: $uni-text-color-disable;
+	}
+
+	.uni-calendar-item--isDay-text {
+		color: $uni-color-primary;
+	}
+
+	.uni-calendar-item--isDay {
+		background-color: $uni-color-primary;
+		opacity: 0.8;
+		color: #fff;
+	}
+
+	.uni-calendar-item--extra {
+		color: $uni-color-error;
+		opacity: 0.8;
+	}
+
+	.uni-calendar-item--checked {
+		background-color: $uni-color-primary;
+		color: #fff;
+		opacity: 0.8;
+	}
+
+	.uni-calendar-item--multiple {
+		background-color: $uni-color-primary;
+		color: #fff;
+		opacity: 0.8;
+	}
+</style>

+ 434 - 0
components/uni-calendar/uni-calendar.vue

@@ -0,0 +1,434 @@
+<template>
+	<view class="uni-calendar" @touchmove.stop.prevent="clean">
+		<view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" @click="clean"></view>
+		<view v-if="insert || show" class="uni-calendar__content" :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow}">
+			<view v-if="!insert" class="uni-calendar__header uni-calendar--fixed-top">
+				<view class="uni-calendar__header-btn-box" @click="close">
+					<text class="uni-calendar__header-text uni-calendar--fixed-width">取消</text>
+				</view>
+				<view class="uni-calendar__header-btn-box" @click="confirm">
+					<text class="uni-calendar__header-text uni-calendar--fixed-width">确定</text>
+				</view>
+			</view>
+			<view class="uni-calendar__header">
+				<view class="uni-calendar__header-btn-box" @click="pre">
+					<view class="uni-calendar__header-btn uni-calendar--left"></view>
+				</view>
+				<text class="uni-calendar__header-text">{{ (nowDate.year||'') +'年'+( nowDate.month||'') +'月'}}</text>
+				<view class="uni-calendar__header-btn-box" @click="next">
+					<view class="uni-calendar__header-btn uni-calendar--right"></view>
+				</view>
+				<text class="uni-calendar__backtoday" @click="backtoday">回到今天</text>
+			</view>
+			<view class="uni-calendar__box">
+				<view v-if="showMonth" class="uni-calendar__box-bg">
+					<text class="uni-calendar__box-bg-text">{{nowDate.month}}</text>
+				</view>
+				<view class="uni-calendar__weeks">
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">日</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">一</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">二</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">三</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">四</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">五</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">六</text>
+					</view>
+				</view>
+				<view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
+					<view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
+						<uni-calendar-item :weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar" @change="choiceDate"></uni-calendar-item>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import Calendar from './util.js';
+	import uniCalendarItem from './uni-calendar-item.vue'
+	export default {
+		components: {
+			uniCalendarItem
+		},
+		props: {
+			/**
+			 * 当前日期
+			 */
+			date: {
+				type: String,
+				default: ''
+			},
+			/**
+			 * 打点日期
+			 */
+			selected: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			/**
+			 * 是否开启阴历日期
+			 */
+			lunar: {
+				type: Boolean,
+				default: false
+			},
+			/**
+			 * 开始时间
+			 */
+			startDate: {
+				type: String,
+				default: ''
+			},
+			/**
+			 * 结束时间
+			 */
+			endDate: {
+				type: String,
+				default: ''
+			},
+			/**
+			 * 范围
+			 */
+			range: {
+				type: Boolean,
+				default: false
+			},
+			/**
+			 * 插入
+			 */
+			insert: {
+				type: Boolean,
+				default: true
+			},
+			/**
+			 * 是否显示月份背景
+			 */
+			showMonth: {
+				type: Boolean,
+				default: true
+			}
+		},
+		data() {
+			return {
+				show: false,
+				weeks: [],
+				calendar: {},
+				nowDate: '',
+				aniMaskShow: false
+			}
+		},
+		watch: {
+			selected(newVal) {
+				this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
+				this.weeks = this.cale.weeks
+			}
+		},
+		created() {
+			// 获取日历方法实例
+			this.cale = new Calendar({
+				date: this.date,
+				selected: this.selected,
+				startDate: this.startDate,
+				endDate: this.endDate,
+				range: this.range,
+			})
+			this.init(this.cale.date.fullDate)
+		},
+		methods: {
+			// 取消穿透
+			clean() {},
+			init(date) {
+				this.weeks = this.cale.weeks
+				this.nowDate = this.calendar = this.cale.getInfo(date)
+			},
+			open() {
+				this.show = true
+				this.$nextTick(() => {
+					setTimeout(()=>{
+						this.aniMaskShow = true
+					},50)
+				})
+			},
+			close() {
+				this.aniMaskShow = false
+				this.$nextTick(() => {
+					setTimeout(() => {
+						this.show = false
+					}, 300)
+				})
+			},
+			confirm() {
+				this.setEmit('confirm')
+				this.close()
+			},
+			change() {
+				if (!this.insert) return
+				this.setEmit('change')
+			},
+			monthSwitch() {
+				let {
+					year,
+					month
+				} = this.nowDate
+				this.$emit('monthSwitch', {
+					year,
+					month: Number(month)
+				})
+			},
+			setEmit(name) {
+				let {
+					year,
+					month,
+					date,
+					fullDate,
+					lunar,
+					extraInfo
+				} = this.calendar
+				this.$emit(name, {
+					range: this.cale.multipleStatus,
+					year,
+					month,
+					date,
+					fulldate: fullDate,
+					lunar,
+					extraInfo: extraInfo || {}
+				})
+			},
+			choiceDate(weeks) {
+				if (weeks.disable) return
+				this.calendar = weeks
+				// 设置多选
+				this.cale.setMultiple(this.calendar.fullDate)
+				this.weeks = this.cale.weeks
+				this.change()
+			},
+			backtoday() {
+				this.cale.setDate(this.date)
+				this.weeks = this.cale.weeks
+				this.nowDate = this.calendar = this.cale.getInfo(this.date)
+				this.change()
+			},
+			pre() {
+				const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
+				this.setDate(preDate)
+				this.monthSwitch()
+
+			},
+			next() {
+				const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
+				this.setDate(nextDate)
+				this.monthSwitch()
+			},
+			setDate(date) {
+				this.cale.setDate(date)
+				this.weeks = this.cale.weeks
+				this.nowDate = this.cale.getInfo(date)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.uni-calendar {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+	}
+
+	.uni-calendar__mask {
+		position: fixed;
+		bottom: 0;
+		top: 0;
+		left: 0;
+		right: 0;
+		background-color: $uni-bg-color-mask;
+		transition-property: opacity;
+		transition-duration: 0.3s;
+		opacity: 0;
+		/* #ifndef APP-NVUE */
+		z-index: 99;
+		/* #endif */
+	}
+
+	.uni-calendar--mask-show {
+		opacity: 1
+	}
+
+	.uni-calendar--fixed {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		transition-property: transform;
+		transition-duration: 0.3s;
+		transform: translateY(460px);
+		/* #ifndef APP-NVUE */
+		z-index: 99;
+		/* #endif */
+	}
+
+	.uni-calendar--ani-show {
+		transform: translateY(0);
+	}
+
+	.uni-calendar__content {
+		background-color: #fff;
+	}
+
+	.uni-calendar__header {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		height: 50px;
+		border-bottom-color: $uni-border-color;
+		border-bottom-style: solid;
+		border-bottom-width: 1px;
+	}
+
+	.uni-calendar--fixed-top {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: space-between;
+		border-top-color: $uni-border-color;
+		border-top-style: solid;
+		border-top-width: 1px;
+	}
+
+	.uni-calendar--fixed-width {
+		width: 50px;
+		// padding: 0 15px;
+	}
+
+	.uni-calendar__backtoday {
+		position: absolute;
+		right: 0;
+		top: 25rpx;
+		padding: 0 5px;
+		padding-left: 10px;
+		height: 25px;
+		line-height: 25px;
+		font-size: 12px;
+		border-top-left-radius: 25px;
+		border-bottom-left-radius: 25px;
+		color: $uni-text-color;
+		background-color: $uni-bg-color-hover;
+	}
+
+	.uni-calendar__header-text {
+		text-align: center;
+		width: 100px;
+		font-size: $uni-font-size-base;
+		color: $uni-text-color;
+	}
+
+	.uni-calendar__header-btn-box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		justify-content: center;
+		width: 50px;
+		height: 50px;
+	}
+
+	.uni-calendar__header-btn {
+		width: 10px;
+		height: 10px;
+		border-left-color: $uni-text-color-placeholder;
+		border-left-style: solid;
+		border-left-width: 2px;
+		border-top-color: $uni-color-subtitle;
+		border-top-style: solid;
+		border-top-width: 2px;
+	}
+
+	.uni-calendar--left {
+		transform: rotate(-45deg);
+	}
+
+	.uni-calendar--right {
+		transform: rotate(135deg);
+	}
+
+
+	.uni-calendar__weeks {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+	}
+
+	.uni-calendar__weeks-item {
+		flex: 1;
+	}
+
+	.uni-calendar__weeks-day {
+		flex: 1;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		height: 45px;
+		border-bottom-color: #F5F5F5;
+		border-bottom-style: solid;
+		border-bottom-width: 1px;
+	}
+	.uni-calendar__weeks-day-text {
+		font-size: 14px;
+	}
+
+	.uni-calendar__box {
+		position: relative;
+	}
+
+	.uni-calendar__box-bg {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		align-items: center;
+		position: absolute;
+		top: 0;
+		left: 0;
+		right: 0;
+		bottom: 0;
+	}
+
+	.uni-calendar__box-bg-text {
+		font-size: 200px;
+		font-weight: bold;
+		color: $uni-text-color-grey;
+		opacity: 0.1;
+		text-align: center;
+		/* #ifndef APP-NVUE */
+		line-height: 1;
+		/* #endif */
+	}
+</style>

+ 327 - 0
components/uni-calendar/util.js

@@ -0,0 +1,327 @@
+import CALENDAR from './calendar.js'
+
+class Calendar {
+	constructor({
+		date,
+		selected,
+		startDate,
+		endDate,
+		range
+	} = {}) {
+		// 当前日期
+		this.date = this.getDate(date) // 当前初入日期
+		// 打点信息
+		this.selected = selected || [];
+		// 范围开始
+		this.startDate = startDate
+		// 范围结束
+		this.endDate = endDate
+		this.range = range
+		// 多选状态
+		this.multipleStatus = {
+			before: '',
+			after: '',
+			data: []
+		}
+		// 每周日期
+		this.weeks = {}
+
+		this._getWeek(this.date.fullDate)
+	}
+
+	/**
+	 * 获取任意时间
+	 */
+	getDate(date, AddDayCount = 0, str = 'day') {
+		if (!date) {
+			date = new Date()
+		}
+		if (typeof date !== 'object') {
+			date = date.replace(/-/g, '/')
+		}
+		const dd = new Date(date)
+		switch (str) {
+			case 'day':
+				dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
+				break
+			case 'month':
+				if (dd.getDate() === 31) {
+					dd.setDate(dd.getDate() + AddDayCount)
+				} else {
+					dd.setMonth(dd.getMonth() + AddDayCount) // 获取AddDayCount天后的日期
+				}
+				break
+			case 'year':
+				dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
+				break
+		}
+		const y = dd.getFullYear()
+		const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0
+		const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0
+		return {
+			fullDate: y + '-' + m + '-' + d,
+			year: y,
+			month: m,
+			date: d,
+			day: dd.getDay()
+		}
+	}
+
+
+	/**
+	 * 获取上月剩余天数
+	 */
+	_getLastMonthDays(firstDay, full) {
+		let dateArr = []
+		for (let i = firstDay; i > 0; i--) {
+			const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate()
+			dateArr.push({
+				date: beforeDate,
+				month: full.month - 1,
+				lunar: this.getlunar(full.year, full.month - 1, beforeDate),
+				disable: true
+			})
+		}
+		return dateArr
+	}
+	/**
+	 * 获取本月天数
+	 */
+	_currentMonthDys(dateData, full) {
+		let dateArr = []
+		let fullDate = this.date.fullDate
+		for (let i = 1; i <= dateData; i++) {
+			let isinfo = false
+			let nowDate = full.year + '-' + (full.month < 10 ?
+				full.month : full.month) + '-' + (i < 10 ?
+				'0' + i : i)
+			// 是否今天
+			let isDay = fullDate === nowDate
+			// 获取打点信息
+			let info = this.selected && this.selected.find((item) => {
+				if (this.dateEqual(nowDate, item.date)) {
+					return item
+				}
+			})
+
+			// 日期禁用
+			let disableBefore = true
+			let disableAfter = true
+			if (this.startDate) {
+				let dateCompBefore = this.dateCompare(this.startDate, fullDate)
+				disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
+			}
+
+			if (this.endDate) {
+				let dateCompAfter = this.dateCompare(fullDate, this.endDate)
+				disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
+			}
+
+			let multiples = this.multipleStatus.data
+			let checked = false
+			let multiplesStatus = -1
+			if (this.range) {
+				if (multiples) {
+					multiplesStatus = multiples.findIndex((item) => {
+						return this.dateEqual(item, nowDate)
+					})
+				}
+				if (multiplesStatus !== -1) {
+					checked = true
+				}
+			}
+
+			let data = {
+				fullDate: nowDate,
+				year: full.year,
+				date: i,
+				multiple: this.range ? checked : false,
+				month: full.month,
+				lunar: this.getlunar(full.year, full.month, i),
+				disable: !disableBefore || !disableAfter,
+				isDay
+			}
+			if (info) {
+				data.extraInfo = info
+			}
+
+			dateArr.push(data)
+		}
+		return dateArr
+	}
+	/**
+	 * 获取下月天数
+	 */
+	_getNextMonthDays(surplus, full) {
+		let dateArr = []
+		for (let i = 1; i < surplus + 1; i++) {
+			dateArr.push({
+				date: i,
+				month: Number(full.month) + 1,
+				lunar: this.getlunar(full.year, Number(full.month) + 1, i),
+				disable: true
+			})
+		}
+		return dateArr
+	}
+	/**
+	 * 设置日期
+	 * @param {Object} date
+	 */
+	setDate(date) {
+		this._getWeek(date)
+	}
+	/**
+	 * 获取当前日期详情
+	 * @param {Object} date
+	 */
+	getInfo(date) {
+		if (!date) {
+			date = new Date()
+		}
+		const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate)
+		return dateInfo
+	}
+
+	/**
+	 * 比较时间大小
+	 */
+	dateCompare(startDate, endDate) {
+		// 计算截止时间
+		startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
+		// 计算详细项的截止时间
+		endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
+		if (startDate <= endDate) {
+			return true
+		} else {
+			return false
+		}
+	}
+
+	/**
+	 * 比较时间是否相等
+	 */
+	dateEqual(before, after) {
+		// 计算截止时间
+		before = new Date(before.replace('-', '/').replace('-', '/'))
+		// 计算详细项的截止时间
+		after = new Date(after.replace('-', '/').replace('-', '/'))
+		if (before.getTime() - after.getTime() === 0) {
+			return true
+		} else {
+			return false
+		}
+	}
+
+
+	/**
+	 * 获取日期范围内所有日期
+	 * @param {Object} begin
+	 * @param {Object} end
+	 */
+	geDateAll(begin, end) {
+		var arr = []
+		var ab = begin.split('-')
+		var ae = end.split('-')
+		var db = new Date()
+		db.setFullYear(ab[0], ab[1] - 1, ab[2])
+		var de = new Date()
+		de.setFullYear(ae[0], ae[1] - 1, ae[2])
+		var unixDb = db.getTime() - 24 * 60 * 60 * 1000
+		var unixDe = de.getTime() - 24 * 60 * 60 * 1000
+		for (var k = unixDb; k <= unixDe;) {
+			k = k + 24 * 60 * 60 * 1000
+			arr.push(this.getDate(new Date(parseInt(k))).fullDate)
+		}
+		return arr
+	}
+	/**
+	 * 计算阴历日期显示
+	 */
+	getlunar(year, month, date) {
+		return CALENDAR.solar2lunar(year, month, date)
+	}
+	/**
+	 * 设置打点
+	 */
+	setSelectInfo(data, value) {
+		this.selected = value
+		this._getWeek(data)
+	}
+
+	/**
+	 *  获取多选状态
+	 */
+	setMultiple(fullDate) {
+		let {
+			before,
+			after
+		} = this.multipleStatus
+		if (!this.range) return
+		if (before && after) {
+			this.multipleStatus.before = ''
+			this.multipleStatus.after = ''
+			this.multipleStatus.data = []
+			this._getWeek(fullDate)
+		} else {
+			if (!before) {
+				this.multipleStatus.before = fullDate
+			} else {
+				this.multipleStatus.after = fullDate
+				if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
+					this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
+				} else {
+					this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
+				}
+				this._getWeek(fullDate)
+			}
+		}
+	}
+
+	/**
+	 * 获取每周数据
+	 * @param {Object} dateData
+	 */
+	_getWeek(dateData) {
+		const {
+			fullDate,
+			year,
+			month,
+			date,
+			day
+		} = this.getDate(dateData)
+		let firstDay = new Date(year, month - 1, 1).getDay()
+		let currentDay = new Date(year, month, 0).getDate()
+		let dates = {
+			lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
+			currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数
+			nextMonthDays: [], // 下个月开始几天
+			weeks: []
+		}
+		let canlender = []
+		const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
+		dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData))
+		canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
+		let weeks = {}
+		// 拼接数组  上个月开始几天 + 本月天数+ 下个月开始几天
+		for (let i = 0; i < canlender.length; i++) {
+			if (i % 7 === 0) {
+				weeks[parseInt(i / 7)] = new Array(7)
+			}
+			weeks[parseInt(i / 7)][i % 7] = canlender[i]
+		}
+		this.canlender = canlender
+		this.weeks = weeks
+	}
+
+	//静态方法
+	// static init(date) {
+	// 	if (!this.instance) {
+	// 		this.instance = new Calendar(date);
+	// 	}
+	// 	return this.instance;
+	// }
+}
+
+
+export default Calendar

+ 156 - 0
components/userEvaluation/index.vue

@@ -0,0 +1,156 @@
+<template>
+	<view class="evaluateWtapper" v-if="reply.length > 0">
+		<view class="evaluateItem" v-for="(item, index) in reply" :key="index">
+			<view class="pic-text acea-row">
+				<view class="pictrue">
+					<image :src="item.userAvatar"></image>
+				</view>
+				<view class="content">
+					<view>
+						<view class="acea-row row-between">
+							<view class="acea-row">
+								<view class="name line1">{{ item.userNickname }}</view>
+								<view class="start" :class="'star' + item.scores"></view>
+							</view>
+							<view class="time">{{ formatDate(item.createTime) }}</view>
+						</view>
+						<view class="sku">规格:
+              <text v-for="(property, propertyIndex) in item.skuProperties" :key="propertyIndex" style="padding-right: 2px">
+                {{ property.valueName }}
+              </text>
+            </view>
+					</view>
+					<view class="evaluate-infor">{{ item.content }}</view>
+					<view class="imgList acea-row" v-if="item.picUrls && item.picUrls.length > 0">
+						<view class="pictrue" v-for="(picUrl, indexn) in item.picUrls" :key="indexn">
+							<image :src="picUrl" class="image" @click='getpreviewImage(index, indexn)'></image>
+						</view>
+					</view>
+					<view class="reply" v-if="item.replyContent">
+						<text class="font-color">店小二</text>:{{ item.replyContent }}
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+  import dayjs from "@/plugin/dayjs/dayjs.min.js";
+	export default {
+		props: {
+			reply: {
+				type: Array,
+				default: []
+			}
+		},
+		data: function() {
+			return {};
+		},
+		methods: {
+			getpreviewImage: function(indexw, indexn) {
+				uni.previewImage({
+					urls: this.reply[indexw].picUrls,
+					current: this.reply[indexw].picUrls[indexn]
+				});
+			},
+      formatDate: function(date) {
+        return dayjs(date).format("YYYY-MM-DD HH:mm:ss");
+      }
+		}
+	}
+</script>
+<style scoped lang='scss'>
+	.evaluateWtapper .evaluateItem {
+		background-color: #fff;
+		padding: 24rpx;
+		border-bottom-left-radius: 14rpx;
+		border-bottom-right-radius: 14rpx;
+	}
+
+	.evaluateWtapper .evaluateItem~.evaluateItem {
+		border-top: 1rpx solid #f5f5f5;
+	}
+
+	.evaluateWtapper .evaluateItem .pic-text {
+		font-size: 26rpx;
+		color: #282828;
+		.content{
+			width: 84%;
+			margin-left: 20rpx;
+		}
+	}
+
+	.evaluateWtapper .evaluateItem .pic-text .pictrue {
+		width: 62rpx;
+		height: 62rpx;
+	}
+
+	.evaluateWtapper .evaluateItem .pic-text .pictrue image {
+		width: 100%;
+		height: 100%;
+		border-radius: 50%;
+	}
+
+	.evaluateWtapper .evaluateItem .pic-text .name {
+		max-width: 450rpx;
+	}
+
+	.evaluateWtapper .evaluateItem .time {
+		font-size: 24rpx;
+		color: #999999;
+
+	}
+	.sku{
+		font-size: 24rpx;
+		color: #999999;
+		margin: 10rpx 0;
+	}
+	.evaluateWtapper .evaluateItem .evaluate-infor {
+		font-size: 28rpx;
+		color: #333;
+		margin-bottom: 14rpx;
+	}
+
+	.evaluateWtapper .evaluateItem .imgList {/*
+		padding: 0 24rpx;
+		margin-top: 16rpx; */
+	}
+
+	.evaluateWtapper .evaluateItem .imgList .pictrue {
+		width: 102rpx;
+		height: 102rpx;
+		margin-right: 14rpx;
+		border-radius: 14rpx;
+		margin-bottom: 16rpx;
+		/* margin: 0 0 15rpx 15rpx; */
+	}
+
+	.evaluateWtapper .evaluateItem .imgList .pictrue image {
+		width: 100%;
+		height: 100%;
+		background-color: #f7f7f7;
+		border-radius: 14rpx;
+	}
+
+	.evaluateWtapper .evaluateItem .reply {
+		font-size: 26rpx;
+		color: #454545;
+		background-color: #f7f7f7;
+		border-radius: 14rpx;
+		margin: 20rpx 30rpx 0 0rpx;
+		padding: 20rpx;
+		position: relative;
+	}
+
+	.evaluateWtapper .evaluateItem .reply::before {
+		content: "";
+		width: 0;
+		height: 0;
+		border-left: 20rpx solid transparent;
+		border-right: 20rpx solid transparent;
+		border-bottom: 30rpx solid #f7f7f7;
+		position: absolute;
+		top: -14rpx;
+		left: 40rpx;
+	}
+</style>

File diff suppressed because it is too large
+ 9 - 0
components/vconsole.min.js


+ 29 - 0
config/app.js

@@ -0,0 +1,29 @@
+
+let domain = 'http://apif.java.crmeb.net'
+// let domain = 'http://127.0.0.1:48080'
+
+module.exports = {
+	// 请求域名 格式: https://您的域名
+	// #ifdef MP
+		HTTP_REQUEST_URL: domain,
+	// #endif
+	// #ifdef H5
+		//H5接口是浏览器地址
+		// HTTP_REQUEST_URL: window.location.protocol+"//"+window.location.host,
+		// http://api.java.crmeb.net:20001
+		HTTP_REQUEST_URL: domain,
+	// #endif
+
+	HEADER:{
+		'content-type': 'application/json'
+	},
+	HEADERPARAMS:{
+		'content-type': 'application/x-www-form-urlencoded'
+	},
+	// 回话密钥名称 请勿修改此配置
+	TOKENNAME: 'Authori-zation',
+	// 缓存时间 0 永久
+	EXPIRE:0,
+	//分页最多显示条数
+	LIMIT: 10
+};

+ 34 - 0
config/cache.js

@@ -0,0 +1,34 @@
+module.exports = {
+        //token
+        LOGIN_STATUS: 'LOGIN_STATUS_TOKEN',
+        // uid
+        UID:'UID',
+        // openid
+        OPENID: 'OPENID',
+        //用户信息
+        USER_INFO: 'USER_INFO',
+        //token 过期时间
+        EXPIRES_TIME: 'EXPIRES_TIME',
+        //微信授权
+        WX_AUTH: 'WX_AUTH',
+        //微信授权状态
+        STATE_KEY: 'wx_authorize_state',
+        //登录回调地址
+        BACK_URL: 'login_back_url',
+        // 小程序授权状态
+        STATE_R_KEY: 'roution_authorize_state',
+        //logo Url
+        LOGO_URL: 'LOGO_URL',
+        //模板缓存
+        // SUBSCRIBE_MESSAGE: 'SUBSCRIBE_MESSAGE',
+
+        TIPS_KEY: 'TIPS_KEY',
+
+        SPREAD: 'spread',
+        //缓存经度
+        CACHE_LONGITUDE: 'LONGITUDE',
+        //缓存纬度
+        CACHE_LATITUDE: 'LATITUDE',
+		 //app手机信息
+		PLATFORM: 'systemPlatform'
+}

+ 8 - 0
config/socket.js

@@ -0,0 +1,8 @@
+module.exports = {
+  // Socket链接 暂不做配置
+  WSS_SERVER_URL:'',
+  // Socket调试模式
+  SERVER_DEBUG:true,
+  // 心跳间隔
+  PINGINTERVAL:3000
+}

+ 1380 - 0
js_sdk/Sansnn-uQRCode/uqrcode.js

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

+ 22 - 0
libs/apps.js

@@ -0,0 +1,22 @@
+import { appAuth } from '../api/public';
+
+class Apps{
+	/**
+	 * 授权登录获取token
+	 * @param {Object} code
+	 */
+	authApp(code) {
+		return new Promise((resolve, reject) => {
+			appAuth(code,{'spread_spid': 0})
+				.then(({
+					data
+				}) => {
+					resolve(data);
+					Cache.set(WX_AUTH, code);
+					Cache.clear(STATE_KEY);
+				})
+				.catch(reject);
+		});
+	}
+}
+export default new Apps();

+ 62 - 0
libs/chat.js

@@ -0,0 +1,62 @@
+import $store from "@/store";
+import { VUE_APP_WS_URL } from "@/utils/index.js";
+
+const Socket = function() {
+  this.ws = new WebSocket(wss(VUE_APP_WS_URL));
+  this.ws.onopen = this.onOpen.bind(this);
+  this.ws.onerror = this.onError.bind(this);
+  this.ws.onmessage = this.onMessage.bind(this);
+  this.ws.onclose = this.onClose.bind(this);
+};
+
+function wss(wsSocketUrl) {
+    let ishttps = document.location.protocol == 'https:';
+    if (ishttps) {
+        return wsSocketUrl.replace('ws:', 'wss:');
+    } else {
+        return wsSocketUrl.replace('wss:', 'ws:');
+    }
+}
+
+Socket.prototype = {
+  vm(vm) {
+    this.vm = vm;
+  },
+  close() {
+    clearInterval(this.timer);
+    this.ws.close();
+  },
+  onOpen: function() {
+    console.log("ws open");
+    this.init();
+    this.send({
+      type: "login",
+      data: $store.state.app.token
+    });
+    this.vm.$emit("socket_open");
+  },
+  init: function() {
+    var that = this;
+    this.timer = setInterval(function() {
+      that.send({ type: "ping" });
+    }, 10000);
+  },
+  send: function(data) {
+    return this.ws.send(JSON.stringify(data));
+  },
+  onMessage: function(res) {
+    const { type, data = {} } = JSON.parse(res.data);
+    this.vm.$emit(type, data);
+  },
+  onClose: function() {
+    clearInterval(this.timer);
+  },
+  onError: function(e) {
+    console.log(e);
+    this.vm.$emit("socket_error", e);
+  }
+};
+
+Socket.prototype.constructor = Socket;
+
+export default Socket;

Some files were not shown because too many files changed in this diff