lhl vor 3 Tagen
Commit
8baf55880c
100 geänderte Dateien mit 32892 neuen und 0 gelöschten Zeilen
  1. 25 0
      .gitignore
  2. 333 0
      App.vue
  3. 28 0
      androidPrivacy.json
  4. 264 0
      api/activity.js
  5. 233 0
      api/admin.js
  6. 348 0
      api/api.js
  7. 138 0
      api/community.js
  8. 343 0
      api/order.js
  9. 70 0
      api/points_mall.js
  10. 214 0
      api/product.js
  11. 124 0
      api/public.js
  12. 589 0
      api/store.js
  13. 778 0
      api/user.js
  14. 111 0
      components/BaseMoney.vue
  15. 66 0
      components/Loading/index.vue
  16. 160 0
      components/PriceChange/index.vue
  17. 204 0
      components/WaterfallsFlow/WaterfallsFlow.vue
  18. 358 0
      components/WaterfallsFlowItem/WaterfallsFlowItem.vue
  19. 181 0
      components/adc/index.vue
  20. 605 0
      components/addInvoicing/index.vue
  21. 346 0
      components/addcartWindow/index.vue
  22. 190 0
      components/addressWindow/index.vue
  23. 85 0
      components/alert/index.vue
  24. 266 0
      components/areaWindow/index.vue
  25. 319 0
      components/bindmobile.vue
  26. 140 0
      components/cash/index.vue
  27. 388 0
      components/checkCoupon/index.vue
  28. 178 0
      components/checkDelivery/index.vue
  29. 318 0
      components/combinNav/index.vue
  30. 481 0
      components/comment.vue
  31. 147 0
      components/copyPassword/index.vue
  32. 174 0
      components/countDown/index.vue
  33. 238 0
      components/couponListWindow/index.vue
  34. 124 0
      components/customTab.vue
  35. 153 0
      components/discountDetails/index.vue
  36. 185 0
      components/easy-loadimage/easy-loadimage.vue
  37. 290 0
      components/eidtUserModal/index.vue
  38. 45 0
      components/emptyPage.vue
  39. 216 0
      components/freightGuarantee/index.vue
  40. 179 0
      components/goodList/index.vue
  41. 187 0
      components/guide/index.vue
  42. 116 0
      components/home/index.vue
  43. 59 0
      components/index.vue
  44. 291 0
      components/invoiceGoods/index.vue
  45. 825 0
      components/jyf-parser/jyf-parser.vue
  46. 111 0
      components/jyf-parser/libs/CssHandler.js
  47. 586 0
      components/jyf-parser/libs/MpHtmlParser.js
  48. 89 0
      components/jyf-parser/libs/config.js
  49. 44 0
      components/jyf-parser/libs/handler.sjs
  50. 53 0
      components/jyf-parser/libs/handler.wxs
  51. 488 0
      components/jyf-parser/libs/trees.vue
  52. 98 0
      components/kefuIcon/index.vue
  53. 214 0
      components/mentioned.vue
  54. 406 0
      components/mpvue-calendar/browser-style.css
  55. 533 0
      components/mpvue-calendar/calendarinit.js
  56. 15 0
      components/mpvue-calendar/icon.css
  57. 1162 0
      components/mpvue-calendar/mpvue-calendar.vue
  58. 394 0
      components/mpvue-calendar/style.css
  59. 290 0
      components/orderCoupon/index.vue
  60. 361 0
      components/orderGoods/index.vue
  61. 258 0
      components/passwordPopup/index.vue
  62. 466 0
      components/payment/index.vue
  63. 152 0
      components/plantConSwiper/index.vue
  64. 238 0
      components/plantWaterfallsFlow/WaterfallsFlow.vue
  65. 249 0
      components/plantWaterfallsFlowItem/WaterfallsFlowItem.vue
  66. 203 0
      components/privacyAgreementPopup/index.vue
  67. 199 0
      components/productConSwiper/index.vue
  68. 463 0
      components/productWindow/index.vue
  69. 114 0
      components/recommend/index.vue
  70. 446 0
      components/rightSlider.vue
  71. 283 0
      components/rightSlider/index.vue
  72. 328 0
      components/rightSlidera.vue
  73. 58 0
      components/shareInfo/index.vue
  74. 159 0
      components/shareRedPackets/index.vue
  75. 298 0
      components/shopList/index.vue
  76. 199 0
      components/skeleton/index.vue
  77. 114 0
      components/specs/index.vue
  78. 60 0
      components/swipers/index.vue
  79. 207 0
      components/timeSlot/index.vue
  80. 168 0
      components/timeranges/index.vue
  81. 248 0
      components/tui-skeleton.vue
  82. 171 0
      components/ucharts/component.vue
  83. 5658 0
      components/ucharts/ucharts.js
  84. 0 0
      components/ucharts/ucharts.min.js
  85. 546 0
      components/uni-calendar/calendar.js
  86. 161 0
      components/uni-calendar/uni-calendar-item.vue
  87. 444 0
      components/uni-calendar/uni-calendar.vue
  88. 337 0
      components/uni-calendar/util.js
  89. 168 0
      components/userEvaluation/index.vue
  90. 117 0
      components/userNameSwiper/userNameSwiper.vue
  91. 9 0
      components/vconsole.min.js
  92. 12 0
      components/verify/utils/ase.js
  93. 446 0
      components/verify/verify.vue
  94. 475 0
      components/verify/verifyPoint/verifyPoint.vue
  95. 579 0
      components/verify/verifySlider/verifySlider.vue
  96. 1376 0
      components/yq-avatar/yq-avatar.vue
  97. 1210 0
      components/zb-code/qrcode.js
  98. 219 0
      components/zb-code/zb-code.vue
  99. 56 0
      config/app.js
  100. 42 0
      config/cache.js

+ 25 - 0
.gitignore

@@ -0,0 +1,25 @@
+.DS_Store
+node_modules
+/dist
+.hbuilderx
+
+# 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

+ 333 - 0
App.vue

@@ -0,0 +1,333 @@
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import {
+		checkLogin
+	} from "./libs/login";
+	import {
+		HTTP_REQUEST_URL
+	} from './config/app';
+	import {
+		getconfig,
+		history
+	} from '@/api/public.js'
+	import Routine from './libs/routine.js';
+	import Cache from '@/utils/cache';
+	export default {
+		globalData: {
+			spid: 0,
+			code: 0,
+			isLogin: false,
+			userInfo: {},
+			MyMenus: [],
+			balance_func_status: 0, //余额开关
+			recharge_switch: 0, // 充值开关
+			store_user_min_recharge: 0, //最小充值
+			yue_pay_status: 0, //余额支付开关
+			alipay_open: 0, //支付宝支付开关
+			routine_logo: '', //首页logo
+			share_pic: '',
+			site_logo: '',
+			site_name: '', //名称
+			fid: '', //一级分类id
+			uid: '',
+			hide_mer_status: 0,
+			member_status: 0,
+			copy_command_status: 0, //是否开启自动获取剪切板内容
+			arrival_notice: 0, //是否开启到货通知
+			is_phone_login: 0,
+			auto_arrival: 0,
+			mer_location: 0,
+			statusBarHeight:0,
+			mer_location: 0,
+			store_street_theme: 1,
+			sys_intention_agree: '',
+			copyright_status: '',
+			copyright_context: '',
+			copyright_image: '',
+			open_update_info: 0,
+			recommend_switch: 0,
+			svip_switch_status: 0,
+			community_reply_status: 0,
+			community_reply_auth: 0,
+			margin_ico_switch: 0,
+			margin_ico: '',
+			community_app_switch: [],
+			first_avatar_switch: "",
+			wechat_phone_switch: "",
+			navigation: {},
+			imgColor: '',
+			...uni.getStorageSync('GLOBAL_DATA') || {}
+		},
+		onLaunch: function(option) {
+			uni.hideTabBar();
+			this.globalData.statusBarHeight = uni.getSystemInfoSync().statusBarHeight + 'px';
+			this.globalData.uid = this.$store.state.app.uid
+			let that = this;
+			// #ifdef MP
+			if (HTTP_REQUEST_URL == '') {
+				console.error(
+					"请配置根目录下的config.js文件中的 'HTTP_REQUEST_URL'\n\n请修改开发者工具中【详情】->【AppID】改为自己的Appid\n\n请前往后台【小程序】->【小程序配置】填写自己的 appId and AppSecret"
+				);
+				return false;
+			}
+			if (option.query.hasOwnProperty('scene')) {
+				switch (option.scene) {
+					//扫描小程序码
+					case 1047:
+						let val = that.$util.getUrlParams(decodeURIComponent(option.query.scene));
+						that.globalData.code = val;
+						that.globalData.uid = val
+						break;
+						//长按图片识别小程序码
+					case 1048:
+						that.globalData.code = option.query.scene;
+						break;
+						//手机相册选取小程序码
+					case 1049:
+						that.globalData.code = option.query.scene;
+						break;
+						//直接进入小程序
+					case 1001:
+						that.globalData.spid = option.query.scene;
+						break;
+				}
+			}
+			// #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;
+			const version = uni.getSystemInfoSync().SDKVersion
+			if (Routine.compareVersion(version, '2.21.2') >= 0) {
+				that.$Cache.set('MP_VERSION_ISNEW', true)
+			} else {
+				that.$Cache.set('MP_VERSION_ISNEW', false)
+			}
+			// #endif
+			that.getConfigData()
+		},
+		onShow() {
+			let that = this
+			that.$store.commit('SETUUID', uni.getStorageSync('uuid') || that.randomString());
+			// 记录H5和公众号
+			if (this.$store.state.app.token) {
+				// 浏览记录
+				// #ifdef H5
+				history({
+					page: location.pathname + location.search,
+				}).then(() => {});
+				//#endif
+			};
+			// #ifndef H5
+			setTimeout(()=>{
+				if(that.globalData.copy_command_status == 1){
+					uni.getClipboardData({
+					    success: function (res) {
+							if(/^(\/@[1-9]{1}).*\*\//.test(res.data)){
+								that.$store.commit("PARSE_PWD", res.data)
+							}
+					  },fail: function (res) {
+							// 内容获取失败
+						}
+					})
+				}
+			},1500)
+			// #endif	
+		},
+		methods: {
+			randomString(len) {
+		//   len = len || 32;
+		//   var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';    /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
+		//   var maxPos = $chars.length;
+		//   var pwd = '';
+		//   for (var i = 0; i < len; i++) {
+		//     pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
+		//   }
+				let pwd = (Date.now()).toString()
+				uni.setStorageSync('uuid', pwd)
+		  return pwd;
+			},
+			setOpenShare: function(data) {
+				let that = this;
+				let href = location.href;
+				href = href.indexOf("?") === -1 ? href + "?spid=" + this.globalData.uid : href + "&spid=" + this.globalData.uid;
+				if (that.$wechat.isWeixin()) {
+					let configAppMessage = {
+						desc: data.share_info,
+						title: data.share_title,
+						link: href,
+						imgUrl: data.share_pic
+					};
+					that.$wechat.wechatEvevt(["updateAppMessageShareData", "updateTimelineShareData"], configAppMessage);
+				}
+			},
+			// 获取配置
+			getConfigData(){
+				getconfig().then(res => {
+					uni.$emit('update', res.data)
+					this.$store.commit('GLOBAL_DATA', res.data);
+					this.globalData.balance_func_status = res.data.balance_func_status
+					this.globalData.recharge_switch = res.data.recharge_switch
+					this.globalData.routine_logo = res.data.routine_logo
+					this.globalData.share_pic = res.data.share_pic
+					this.globalData.community_reply_status = res.data.community_reply_status
+					this.globalData.site_logo = res.data.site_logo
+					this.globalData.login_logo = res.data.login_logo
+					this.globalData.site_name = res.data.site_name
+					this.globalData.store_user_min_recharge = res.data.store_user_min_recharge
+					this.globalData.yue_pay_status = res.data.yue_pay_status
+					this.globalData.sys_intention_agree = res.data.sys_intention_agree
+					this.globalData.mer_intention_open = res.data.mer_intention_open
+					this.globalData.alipay_open = res.data.alipay_open
+					this.globalData.hide_mer_status = res.data.hide_mer_status
+					this.globalData.mer_location = res.data.mer_location
+					this.globalData.procudt_increase_status = res.data.procudt_increase_status
+					this.globalData.auto_arrival = res.data.sys_extension_type
+					this.globalData.member_status = res.data.member_status
+					this.globalData.copy_command_status = res.data.copy_command_status
+					this.globalData.is_phone_login = res.data.is_phone_login
+					this.globalData.mer_location = res.data.mer_location
+					this.globalData.store_street_theme = res.data.store_street_theme
+					this.globalData.copyright_status = res.data.copyright_status
+					this.globalData.copyright_image = res.data.copyright_image
+					this.globalData.copyright_context = res.data.copyright_context
+					this.globalData.open_update_info = res.data.open_update_info
+					this.globalData.recommend_switch = res.data.recommend_switch
+					this.globalData.svip_switch_status = res.data.svip_switch_status
+					this.globalData.navigation = res.data.navigation
+					this.globalData.community_app_switch = res.data.community_app_switch
+					this.globalData.community_reply_auth = res.data.community_reply_auth
+					this.globalData.margin_ico_switch = res.data.margin_ico_switch
+					this.globalData.first_avatar_switch = res.data.first_avatar_switch
+					this.globalData.wechat_phone_switch = res.data.wechat_phone_switch 
+					this.$Cache.set('BIND_PHONE', res.data.wechat_phone_switch) //是否开启强制绑定手机号
+					this.globalData.margin_ico = res.data.margin_ico
+					this.globalData.community_auth = res.data.community_auth
+					this.$Cache.set('WECHAT_APPID', res.data.wechat_config_appid)
+					this.$store.commit("VIEW_COLOR", res.data.global_theme.theme)
+					this.$store.commit("KEY_COLOR",'_' + res.data.global_theme.type)
+					try {
+						uni.setStorageSync('SUBSCRIBE_MESSAGE', res.data.tempid);
+					} catch (e) {
+						// error
+					}
+					// #ifdef H5
+					this.setOpenShare(res.data);
+					// #endif
+				}).catch(err => {});
+			}
+		},
+		onHide: function() {
+			//console.log('App Hide')
+		},
+		watch: {
+			// 记录H5和公众号
+			$route(n) {
+				if (this.$store.state.app.token) {
+					// 浏览记录
+					history({
+						page: location.pathname + location.search,
+					}).then(() => {});
+				}
+			},
+		}
+	}
+</script>
+<style lang="scss">
+	/* #ifndef APP-PLUS-NVUE || APP-NVUE */
+	@import "@/plugin/animate/animate.min.css";
+	@import 'static/css/base.css';
+	@import 'static/iconfont/iconfont.css';
+	@import 'static/css/style.scss';
+	.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;
+	}
+	::-moz-scrollbar {
+		width: 0;
+		height: 0;
+		color: transparent;
+	}
+	.empty-txt {
+		line-height: 100rpx;
+		font-size: 22rpx;
+		color: #999;
+		text-align: center;
+	}
+	.product-con .conter img {
+		display: block;
+	}
+	.open-location {
+		height: 100vh;
+	}
+	uni-tabbar{
+		bottom: 0;
+	}
+	/*#endif*/
+	// 圆形指示点
+	.circular {
+		/deep/.uni-swiper-dot,/deep/.wx-swiper-dot {
+			width: 10rpx;
+			height: 10rpx;
+			background: rgba(0, 0, 0, .4);
+		}
+		/deep/.uni-swiper-dot-active,/deep/.wx-swiper-dot-active {
+			background: #fff;
+		}
+	}
+	// 方形指示点
+	.square {
+		/deep/.uni-swiper-dot,/deep/.wx-swiper-dot {
+			width: 20rpx;
+			height: 5rpx;
+			border-radius: 3rpx;
+			background: rgba(0, 0, 0, .4);
+		}
+		/deep/.uni-swiper-dot-active,/deep/.wx-swiper-dot-active {
+			background: #fff;
+		}
+	}
+	.nodoc{
+		/deep/.uni-swiper-dot,/deep/.wx-swiper-dot {
+			display: none;
+		}
+	}
+	/deep/.swiper.dot0 .uni-swiper-dots,/deep/.swiper.dot0 .wx-swiper-dots{
+		left: 130rpx;
+	}
+	/deep/.swiper.dot1 .uni-swiper-dots,/deep/.swiper.dot1 .wx-swiper-dots{
+		left: 50%;
+	}
+	/deep/.swiper.dot2 .uni-swiper-dots,/deep/.swiper.dot2 .wx-swiper-dots{
+		right: 130rpx;
+		left: auto;
+	}
+</style>

+ 28 - 0
androidPrivacy.json

@@ -0,0 +1,28 @@
+{
+    "version" : "1",
+    "prompt" : "template",
+    "title" : "服务协议与隐私政策",
+    "message" : "\t请务必审慎阅读、充分理解“服务协议与 隐私政策”各条款,包括但不限于:为了 向你提供即时通讯、内容分享等服务,我 们需要收集你的设备信息、操作日志等个 人信息。你可以在“设置”中查看、变更、删除个人信息并管理你的授权。<br/>\r\r
+\t你可以阅读 <a href=\"https://请修改为自己的域名/register.html\">《用户协议》</a> 与 <a href=\"https://请修改为自己的域名/protocol.html\">《隐私政策》</a>了解详细信息。如你同意,请点击“我同意”开始接受我们的服务。",
+    "buttonAccept" : "同意并接受",
+    "buttonRefuse" : "暂不同意",
+    "second" : {
+        "title" : "确认提示",
+        "message" : "进入应用前,你需先同意<a href=\"https://请修改为自己的域名/register.html\">《用户协议》</a> 与 <a href=\"https://请修改为自己的域名/protocol.html\">《隐私政策》</a>,否则将退出应用。",
+        "buttonAccept" : "同意并继续",
+        "buttonRefuse" : "退出应用"
+    },
+    "styles" : {
+        "backgroundColor" : "#fff",
+        "borderRadius" : "5px",
+        "title" : {
+            "color" : "#000"
+        },
+        "buttonAccept" : {
+            "color" : "#fff"
+        },
+        "buttonRefuse" : {
+            "color" : "#ccc"
+        }
+    }
+}

+ 264 - 0
api/activity.js

@@ -0,0 +1,264 @@
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+import request from "@/utils/request.js";
+
+/**
+ * 拼团列表
+ * 
+ */
+export function getCombinationList(data) {
+	return request.get('store/product/group/lst', data, {
+		noAuth: true
+	});
+}
+/**
+ * 拼团参与人
+ * 
+ */
+export function getCombinationUser(data) {
+	return request.get('store/product/group/count', data, {
+		noAuth: true
+	});
+}
+/**
+ * 活动分类
+ * 
+ */
+export function getActivitycategory(type) {
+	return request.get('product/spu/active/category/' + type, {}, {
+		noAuth: true
+	});
+}
+
+/**
+ * 拼团详情
+ * 
+ */
+export function getCombinationDetail(id) {
+	return request.get('store/product/group/detail/' + id, {}, {
+		noAuth: true
+	});
+}
+
+/**
+ * 拼团 开团
+ */
+export function getCombinationPink(id) {
+	return request.get("store/product/group/get/" + id);
+}
+
+
+/**
+ * 拼团 取消开团
+ */
+export function postCombinationRemove(data) {
+	return request.post("store/product/group/cancel", data);
+}
+
+/**
+ * 秒杀产品详情
+ * @param int id
+ */
+export function getSeckillDetail(id) {
+	return request.get('store/product/seckill/detail/' + id, {}, {
+		noAuth: true
+	});
+}
+
+
+/**
+ * 拼团海报
+ * @param object data
+ * 
+ */
+export function getCombinationPoster(data) {
+	return request.post('combination/poster', data)
+}
+
+
+
+/**
+ * 获取秒杀小程序二维码
+ */
+export function seckillCode(id, data) {
+	return request.get("seckill/code/" + id, data);
+}
+
+/**
+ * 获取拼团小程序二维码
+ */
+export function scombinationCode(id) {
+	return request.get("combination/code/" + id);
+}
+/**
+ * 秒杀产品时间区间
+ * 
+ */
+export function getSeckillIndexTime() {
+	return request.get('store/product/seckill/select', {}, {
+		noAuth: true
+	});
+}
+/**
+ * 秒杀产品列表
+ * @param int time
+ * @param object data
+ */
+export function getSeckillList(data) {
+	return request.get('store/product/seckill/lst', data, {
+		noAuth: true
+	});
+
+}
+/**
+ * 预售列表
+ */
+export function getPresellList(data) {
+	return request.get('store/product/presell/lst', data, {
+		noAuth: true
+	});
+}
+/**
+ * 助力列表
+ */
+export function getAssistList(data) {
+	return request.get('store/product/assist/lst', data, {
+		noAuth: true
+	});
+}
+
+/**
+ * 助力列表 -- 发起助力
+ */
+export function initiateAssistApi(id) {
+	return request.post('store/product/assist/create/' + id);
+}
+/**
+ * 助力详情
+ */
+export function getAssistDetail(id,data) {
+	return request.get('store/product/assist/detail/' + id,data);
+}
+/**
+ * 助力好友
+ */
+export function assistHelpList(id, data) {
+	return request.get('store/product/assist/user/' + id, data);
+}
+/**
+ * 预售协议
+ */
+export function presellAgreement() {
+	return request.get('store/product/presell/agree');
+}
+/**
+ * 已助力成功数据
+ */
+export function assistUserData(data) {
+	return request.get('store/product/assist/count', data, {
+		noAuth: true
+	});
+}
+/**
+ * 为好友助力
+ */
+export function postAssistHelp(id) {
+	return request.post('store/product/assist/set/' + id);
+}
+/**
+ * 获取助力查看分享次数
+ */
+export function getAssistUser(id) {
+	return request.get('store/product/assist/share/' + id);
+}
+/**
+ * 助力记录列表
+ */
+export function getBargainUserList(data) {
+	return request.get('store/product/assist/set/lst', data);
+}
+/**
+ * 助力记录列表 -- 取消
+ */
+export function getBargainUserCancel(id) {
+	return request.post('store/product/assist/set/delete/' + id);
+}
+/**
+ * 活动专题列表
+ */
+export function getTopicList(id, data) {
+	return request.get(`activity/lst/${id}`, data, {
+		noAuth: true
+	});
+}
+/**
+ * 活动专题详情
+ */
+export function getTopicDetail(id) {
+	return request.get(`activity/info/${id}`,{}, {
+		noAuth: true
+	});
+}
+/**
+ * 活动专题商品
+ */
+export function getTopicProLst(data) {
+	return request.get(`product/spu/labels`,data, {
+		noAuth: true
+	});
+}
+/**
+ * 获取本地服务商户列表
+ */
+export function getMerchantServiceLst(data) {
+	return request.get(`store/merchant/local`,data, {
+		noAuth: true
+	});
+}
+/**
+ * 获取优惠券列表
+ */
+export function getCouponLst(data) {
+	return request.get(`coupon/getlst`,data, {
+		noAuth: true
+	});
+}
+/**
+ * 获取优惠券列表
+ */
+export function getNewPeopleCouponLst(data) {
+	return request.get(`coupon/new_people`,data, {
+		noAuth: true
+	});
+}
+/**
+ * 获取热卖排行列表
+ */
+export function hotRankingApi(data) {
+	return request.get(`product/spu/get_hot_ranking`,data, {
+		noAuth: true
+	});
+}
+/**
+ * 热卖排行列表分类
+ */
+export function spuTop(data) {
+	return request.get(`store/product/category/hotranking`, {}, {
+		noAuth: true
+	});
+}
+/**
+ * 热卖排行列表
+ */
+export function spuTopList(data) {
+	return request.get(`product/spu/get_hot_ranking`, data, {
+		noAuth: true
+	});
+}

+ 233 - 0
api/admin.js

@@ -0,0 +1,233 @@
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+import request from "@/utils/request.js";
+
+/**
+ * 统计数据
+ */
+export function getStatisticsInfo() {
+  return request.get("admin/order/statistics", {}, { login: true });
+}
+/**
+ * 订单月统计
+ */
+export function getStatisticsMonth(where) {
+  return request.get("admin/order/data", where, { login: true });
+}
+/**
+ * 订单月统计
+ */
+export function getAdminOrderList(where) {
+  return request.get("admin/order/list", where, { login: true });
+}
+/**
+ * 订单改价
+ */
+export function setAdminOrderPrice(merId,id,data) {
+  return request.post("admin/"+merId+"/price/"+id, data, { login: true });
+}
+/**
+ * 订单备注
+ */
+export function setAdminOrderRemark(merId,id,data) {
+  return request.post("admin/"+merId+"/mark/" +id, data, { login: true });
+}
+/**
+ * 订单详情
+ */
+export function getAdminOrderDetail(merId, orderId) {
+  return request.get("admin/"+merId+"/order/" + orderId, {}, { login: true });
+}
+/**
+ * 订单发货信息获取
+ */
+export function getAdminOrderDelivery(orderId) {
+  return request.get( "admin/order/delivery/gain/" + orderId,{},{ login: true });
+}
+/**
+ * 订单发货保存
+ */
+export function setAdminOrderDelivery(merId,id,data) {
+  return request.post("admin/"+merId+"/delivery/"+ id, data, { login: true });
+}
+/**
+ * 订单统计图
+ */
+export function getStatisticsTime(data) {
+  return request.get("admin/order/time", data, { login: true });
+}
+/**
+ * 线下付款订单确认付款
+ */
+export function setOfflinePay(merId, data) {
+  return request.post("admin/"+merId+"/order/offline", data, { login: true });
+}
+/**
+ * 订单确认退款
+ */
+export function setOrderRefund(merId,data) {
+  return request.post("admin/"+merId+"/order/refund", data, { login: true });
+}
+
+/**
+ * 获取快递公司
+ * @returns {*}
+ */
+export function getLogistics() {
+  return request.get("logistics", {}, { login: false });
+}
+/**
+ * 订单核销
+ * @returns {*}
+ */
+export function orderVerific(merId, id, data) {
+	return request.post(`verifier/${merId}/${id}`,data);
+}
+/**
+ * 核销订单详情
+ * @returns {*}
+ */
+export function verifierOrder(mer_id,code) {
+  return request.get("verifier/"+mer_id+"/order/"+code);
+}
+/**
+ * 订单统计数
+ * @returns {*}
+ */
+export function orderStatistics(mer_id) {
+  return request.get("admin/"+mer_id+"/statistics");
+}
+/**
+ * 每日成交额
+ * @returns {*}
+ */
+export function orderPrice(where, mer_id) {
+  return request.get("admin/"+mer_id+"/order_price", where, { login: true });
+}
+/**
+ * 订单列表
+ * @returns {*}
+ */
+export function getOrderList(where, merId) {
+  return request.get(`admin/${merId}/order_list`, where, { login: true });
+}
+/**
+ * 退款订单列表
+ * @returns {*}
+ */
+export function getRefundOrderList(where, merId) {
+  return request.get(`server/${merId}/refund/lst`, where, { login: true });
+}
+/**
+ * 营业额统计
+ * @returns {*}
+ */
+export function turnoverStatistics(where, merId) {
+  return request.get(`admin/${merId}/pay_price`, where, { login: true });
+}
+/**
+ * 订单统计
+ * @returns {*}
+ */
+export function orderNumberStatistics(where, merId) {
+  return request.get(`admin/${merId}/pay_number`, where, { login: true });
+}
+/**
+ * 获取订单打印默认配置
+ * @returns {*}
+ */
+export function orderDeliveryInfo(merId) {
+  return request.get(`admin/${merId}/mer_form`);
+}
+/**
+ * 获取电子面单列表
+ * @returns {*}
+ */
+export function orderExportTemp(data) {
+  return request.get("store/expr/temps", data);
+}
+/**
+ * 是否开始电子面单和同城配送
+ * @returns {*}
+ */
+export function getTempAndDelivery(merId) {
+  return request.get(`admin/${merId}/delivery_config`);
+}
+/**
+ * 获取同城配送门店列表
+ * @returns {*}
+ */
+export function getDeliveryStoreLst(merId) {
+  return request.get(`admin/${merId}/delivery_options`);
+}
+/**
+ * 退款订单信息
+ * @returns {*}
+ */
+export function getRefundOrderInfo(merId,id) {
+  return request.get(`server/${merId}/refund/get/${id}`);
+}
+/**
+ * 提交退款订单信息
+ * @returns {*}
+ */
+export function refundOrderSubmit(merId,id,data) {
+  return request.post(`server/${merId}/refund/status/${id}`,data, { login: true });
+}
+/**
+ * 退款单确认收货
+ * @returns {*}
+ */
+export function refundOrderReceive(merId,id) {
+  return request.post(`server/${merId}/refund/confirm/${id}`,{}, { login: true });
+}
+/**
+ * 退款单详情
+ */
+export function getRefundOrderDetail(merId, orderId) {
+  return request.get(`server/${merId}/refund/detail/${orderId}`,{}, { login: true });
+}
+/**
+ * 添加退款单备注信息
+ */
+export function setRefundMark(merId, orderId, data) {
+  return request.post(`server/${merId}/refund/mark/${orderId}`,data, { login: true });
+}
+/**
+ * 去核销
+ * @param object data
+*/
+export function orderCancellation(merId,id){
+  return request.post(`admin/${merId}/verify/${id}`);
+}
+/**
+ * 手动退款 -- 订单详情
+ */
+export function getRefundOrderApi(merId, orderId) {
+  return request.get(`server/${merId}/refund/check/${orderId}`,{}, { login: true });
+}
+/**
+ * 手动退款 -- 确认退款
+ */
+export function confirmRefundApi(merId, data) {
+  return request.post(`server/${merId}/refund/create`,data, { login: true });
+}
+/**
+ * 手动退款 -- 计算退款金额
+ */
+export function computeRefundPrice(merId,data) {
+  return request.post(`server/${merId}/refund/compute`,data, { login: true });
+}
+/**
+ * 扫码上传 -- 上传图片
+ */
+export function scanUpload(field,token,data) {
+  return request.post(`scan_upload/image/${field}/${token}`,data, { login: true });
+}

+ 348 - 0
api/api.js

@@ -0,0 +1,348 @@
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+import request from "@/utils/request.js";
+/**
+ * 公共接口 ,优惠券接口 , 行业此讯 , 手机号码注册
+ * 
+*/
+/**
+ * 获取主页数据 无需授权
+ * 
+*/
+export function getIndexData()
+{
+  return request.get("common/home",{},{ noAuth : true});
+}
+/**
+ * 获取app版本
+ */
+export function getAppVersion() {
+  return request.get('appVersion',{},{noAuth:true});
+}
+/**
+ * 获取登录授权login
+ * 
+*/
+export function getLogo()
+{
+  return request.get('wechat/get_logo', {}, { 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);
+}
+/**
+ * 商铺优惠券列表
+ * @param object data
+*/
+export function getShopCoupons(id){
+  return request.get('coupon/store/'+id,{},{ noAuth: true})
+}
+/**
+ * 商品优惠券列表
+ * @param object data
+*/
+export function getCoupons(data){
+  return request.get('coupon/product',data,{ noAuth: true})
+}
+/**
+ * 我的优惠券
+ * @param int types 0全部  1未使用 2已使用
+*/
+export function getUserCoupons(data){
+  return request.get('coupon/list',data)
+}
+/**
+ * 文章分类列表
+ * 
+*/
+export function getArticleCategoryList(){
+  return request.get('article/category/lst',{},{noAuth:true})
+}
+/**
+ * 文章列表
+ * @param int cid
+ * 
+*/
+export function getArticleList(cid,data){
+  return request.get('article/lst/' + cid, data,{noAuth:true})
+}
+/**
+ * 文章 热门列表
+ * 
+*/
+export function getArticleHotList(){
+  return request.get('article/hot/list',{},{noAuth:true});
+}
+/**
+ * 文章 轮播列表
+ * 
+*/
+export function getArticleBannerList(){
+  return request.get('article/banner/list',{},{noAuth:true})
+}
+/**
+ * 文章详情
+ * @param int id 
+ * 
+*/
+export function getArticleDetails(id){
+  return request.get('article/detail/'+id,{},{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, reset, key, code){
+  return request.post('register/verify', { phone: phone, type: reset === undefined ? 'reset' : reset, key: key, code: code },{noAuth:true})
+}
+/**
+ * 手机号注册
+ * @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切换公众号登陆 */
+export function switchH5Login(data) {
+  return request.post("user/switch", data);
+}
+/** 绑定手机号 */
+export function bindingPhone(data){
+  return request.post('user/binding',data);
+}
+/** 修改手机号 */
+export function modifyPhone(data){
+  return request.post('user/change/phone',data);
+}
+/** 修改密码 */
+export function modifyPassword(data){
+  return request.post('user/change/password',data);
+}
+/** 退出登錄 */
+export function logout(){
+  return request.get('logout');
+}
+/** 获取订阅消息id */
+export function getTemlIds(){
+  return request.get('wechat/teml_ids', {}, { noAuth:true});
+}
+/** 首页拼团数据 */
+export function pink(){
+  return request.get('pink', {}, { noAuth:true});
+}
+/** 获取城市信息 */
+export function getCity() {
+  return request.get('system/city/lst', { }, { noAuth: true });
+}
+export function getCityV2(pid) {
+	return request.get('v2/system/city/lst/'+pid, {}, {noAuth: true});
+}
+export function getCityList(address){
+	return request.get('v2/system/city', {address}, {noAuth: true});
+}
+/** 获取小程序直播列表 */
+export function getLiveList(page,limit) {
+  return request.get('wechat/live', { page, limit}, { noAuth: true });
+}
+/* APP登录 */
+export function wechatAppAuth(data) {
+  return request.post("auth/app", data, { noAuth: true });
+}
+/* APPLE登录 */
+export function appleAppAuth(data) {
+  return request.post("auth/apple", data, { noAuth: true });
+}
+/* 小程序获取手机号解密 */
+export function appletsDecrypt(data) {
+  return request.post("user/mp/binding", data);
+}
+/**
+ * 获取首页DIY;
+ */
+export function getDiy(data) {
+  return request.get('diy',data,{ noAuth: true });
+}
+/**
+ * 获取首页微页面;
+ */
+export function getPageDiy(data) {
+	return request.get(`micro`, data, {
+		noAuth: true
+	});
+}
+/**
+ * 滑块信息
+ * @param {Object} data
+ */
+export function getAjcaptcha(data) {
+	return request.get("ajcaptcha", data, {
+		noAuth: true
+	});
+}
+/**
+ * 滑块验证
+ * @param {Object} data
+ */
+export function ajcaptchaCheck(data) {
+	return request.post("ajcheck", data, {
+		noAuth: true
+	});
+}
+/**
+ * 滑块信息
+ * @param {Object} data
+ */
+export function getOpenAdv() {
+	return request.get("open_screen", {}, {
+		noAuth: true
+	});
+}
+/**
+ * diy-秒杀
+ * @param {Object} data
+ */
+export function getSeckillData(data) {
+	return request.get("diy/seckill", data, {
+		noAuth: true
+	});
+}
+/**
+ * diy-预售
+ * @param {Object} data
+ */
+export function getPresellData(data) {
+	return request.get("diy/presell", data, {
+		noAuth: true
+	});
+}
+/**
+ * diy-助力
+ * @param {Object} data
+ */
+export function getAssistData(data) {
+	return request.get("diy/assist",data, {
+		noAuth: true
+	});
+}
+/**
+ * diy-拼团
+ * @param {Object} data
+ */
+export function getCombinationData(data) {
+	return request.get("diy/group", data, {
+		noAuth: true
+	});
+}
+/**
+ * diy-商品列表
+ * @param {Object} data
+ */
+export function getProductData(data) {
+	return request.get("diy/spu", data, {
+		noAuth: true
+	});
+}
+/**
+ * diy-社区列表
+ * @param {Object} data
+ */
+export function graphicLstData(data) {
+	return request.get("diy/community",data, {
+		noAuth: true
+	});
+}
+/**
+ * diy-商户列表
+ * @param {Object} data
+ */
+export function storeList(data) {
+	return request.get("diy/store",data, {
+		noAuth: true
+	});
+}
+/**
+ * diy-优惠券列表
+ * @param {Object} data
+ */
+export function getCouponData(data) {
+	return request.get("diy/coupon", data, {
+		noAuth: true
+	});
+}
+/**
+ * diy-小程序直播
+ * @param {Object} data
+ */
+export function getLiveData(data) {
+	return request.get("diy/broadcast", data, {
+		noAuth: true
+	});
+}
+/**
+ * diy-热卖排行
+ * @param {Object} data
+ */
+export function hotRankingList(data) {
+	return request.get("diy/hot_top", data, {
+		noAuth: true
+	});
+}
+/**
+ * diy-首页二级分类
+ * @param {Object} data
+ */
+export function getCateData(data) {
+	return request.get("diy/category", data, {
+		noAuth: true
+	});
+}

+ 138 - 0
api/community.js

@@ -0,0 +1,138 @@
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+import request from "@/utils/request.js";
+
+/** 获取话题分类*/
+export function getTopicList() {
+	return request.get('community/category/lst', {}, {
+		noAuth: true
+	});
+}
+/**已购商品列表*/
+export function boughtLstApi(data) {
+	return request.get('community/pay_product/lst', data);
+}
+/** 收藏图文列表*/
+export function collectLstApi(data) {
+	return request.get('community/rela_product/lst', data);
+}
+/** 浏览图文列表*/
+export function browseLstApi(data) {
+	return request.get('community/hist_product/lst', data);
+}
+/** 发布图文*/
+export function createPlantApi(data) {
+	return request.post('community/create', data);
+}
+/*图文编辑*/
+export function updatePlantApi(id, data) {
+	return request.post(`community/update/${id}`, data);
+}
+/*删除图文*/
+export function deletePlantApi(id) {
+	return request.post(`community/delete/${id}`);
+}
+/** 图文详情*/
+export function plantDetailApi(id) {
+	return request.get(`community/show/${id}`, {}, {
+		noAuth: true
+	});
+}
+/** 订单关联图文商品*/
+export function orderAssociatePlantApi(orderId) {
+	return request.get(`community/order/${orderId}`, {}, {
+		noAuth: true
+	});
+}
+/**评论列表*/
+export function replyLstApi(id, data) {
+	return request.get(`community/${id}/reply`, data, {
+		noAuth: true
+	});
+}
+/**发布评论*/
+export function replyCreateApi(id, data) {
+	return request.post(`community/reply/create/${id}`, data);
+}
+/**点赞评论*/
+export function starCommentApi(id, data) {
+	return request.post(`community/reply/start/${id}`, data);
+}
+/**关注用户*/
+export function followAuthorApi(id, data) {
+	return request.post(`community/fans/${id}`, data);
+}
+/**全部图文列表*/
+export function graphicLstApi(data) {
+	return request.get(`community/lst`, data, {
+		noAuth: true
+	});
+}
+/**视频列表*/
+export function videoList(data) {
+	return request.get(`community/video_lst`, data, {
+		noAuth: true
+	});
+}
+/**自己的视频列表*/
+export function myVideoList(id,data) {
+	return request.get(`community/user/community_video/${id}`, data, {
+		noAuth: true
+	});
+}
+/**文章点赞*/
+export function graphicStartApi(id, status) {
+	return request.post(`community/start/${id}`, status);
+}
+/**用户详情*/
+export function userInfoApi(id) {
+	return request.get(`community/user/info/${id}`);
+}
+/**关注人的文章列表*/
+export function focusArticleLst(data) {
+	return request.get(`community/focuslst`, data, {
+		noAuth: true
+	});
+}
+/**用户的文章列表*/
+export function userArticleLst(id, data) {
+	return request.get(`community/user/community/${id}`, data);
+}
+/**赞过的图文*/
+export function starArticleLst(data) {
+	return request.get(`community/start/lst`, data);
+}
+/**我的关注*/
+export function myFocusLst(data) {
+	return request.get(`community/focus/lst`, data);
+}
+/**我的粉丝*/
+export function myFansLst(data) {
+	return request.get(`community/fans/lst`, data);
+}
+/**热门搜索*/
+export function hotSearchLst() {
+	return request.get(`common/commuunity/hot_keyword`,{}, {
+		noAuth: true
+	});
+}
+/**热门搜索*/
+export function graphicProApi(id) {
+	return request.get(`product/spu/get/${id}`, {}, {
+		noAuth: true
+	});
+}
+/*视频分享二维码*/
+ 
+export function getVideoCode(id, data) {
+	return request.get(`community/qrcode/${id}`, data, {
+		noAuth: true
+	});
+}

+ 343 - 0
api/order.js

@@ -0,0 +1,343 @@
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+import request from "@/utils/request.js";
+
+/**
+ * 获取购物车列表
+ * @param numType boolean true 购物车数量,false=购物车产品数量
+ */
+export function getCartCounts() {
+  return request.get("user/cart/count");
+}
+/**
+ * 获取购物车列表
+ * 
+ */
+export function getCartList() {
+  return request.get("user/cart/lst");
+}
+
+/**
+ * 修改购物车数量
+ * @param int cartId  购物车id
+ * @param int number 修改数量
+ */
+export function changeCartNum(cartId, data) {
+  return request.post("user/cart/change/"+cartId,data);
+}
+/**
+ * 清除购物车
+ * @param object ids
+*/
+export function cartDel(data){
+  return request.post('user/cart/delete', data);
+}
+/**
+ * 购物车商品属性
+ * @param object ids
+*/
+export function cartProductAttr(id){
+  return request.get(`store/product/get_attr_value/${id}`);
+}
+/**
+ * 订单列表
+ * @param object data
+*/
+export function getOrderList(data){
+  return request.get('order/list',data);
+}
+
+/**
+ * 订单产品信息
+ * @param string unique 
+*/
+export function orderProduct(orderId){
+  return request.get('reply/product/'+orderId);
+}
+
+/**
+ * 订单评价
+ * @param object data
+ * 
+*/
+export function orderComment(id,data){
+  return request.post('reply/'+id,data);
+}
+
+/**
+ * 订单支付
+ * @param object data
+*/
+export function orderPay(id,data){
+  return request.post('order/pay/'+id,data);
+}
+
+/**
+ * 积分商品订单支付
+ * @param object data
+*/
+export function integralOrderPay(id,data){
+  return request.post('order/points/pay/'+id,data);
+}
+/**
+ * 订单统计数据
+*/
+export function orderData(){
+  return request.get('order/number')
+}
+
+/**
+ * 订单取消
+ * @param string id
+ * 
+*/
+// export function orderCancel(id){
+//   return request.post('order/cancel',{id:id});
+// }
+
+/**
+ * 未支付订单取消
+ * @param string id
+ * 
+*/
+export function unOrderCancel(id){
+  return request.post('order/cancel/'+id);
+}
+
+/**
+ * 删除已完成订单
+ * @param string uni
+ * 
+*/
+export function orderDel(id){
+  return request.post('order/del/'+id);
+}
+
+/**
+ * 订单详情
+ * @param string uni 
+*/
+export function getOrderDetail(uni){
+  return request.get('order/detail/'+uni);
+}
+
+/**
+ * 订单详情
+ * @param string uni 
+*/
+export function groupOrderDetail(uni){
+  return request.get('order/group_order_detail/'+uni);
+}
+
+// 支付状态订单
+export function getPayOrder(uni){
+  return request.get('order/status/'+uni);
+}
+
+/**
+ * 再次下单
+ * @param string uni
+ * 
+*/
+export function orderAgain(data){
+  return request.post('user/cart/again',data);
+}
+
+/**
+ * 订单收货
+ * @param string uni
+ * 
+*/
+export function orderTake(uni){
+  return request.post('order/take/'+uni);
+}
+
+/**
+ * 订单查询物流信息
+ * @returns {*}
+ */
+export function express(id) {
+  return request.post("order/express/" + id);
+}
+/**
+ * 退款单查询物流信息
+ * @returns {*}
+ */
+export function refundOrderExpress(merId,id) {
+  return request.get(`server/${merId}/refund/express/${id}`);
+}
+/**
+ * 获取退款理由
+ * 
+*/
+export function ordeRefundReason(){
+  return request.get('order/refund/reason');
+}
+
+/**
+ * 订单退款审核
+ * @param object data
+*/
+export function orderRefundVerify(data){
+  return request.post('order/refund/verify',data);
+}
+
+/**
+ * 订单确认获取订单详细信息
+ * @param string cartId
+*/
+export function orderConfirm(data){
+  return request.post('order/check', data);
+}
+/**
+ * 订单确认获取订单详细信息
+ * @param string cartId
+*/
+export function getOrderConfirm(data){
+  return request.post('v2/order/check', data);
+}
+/**
+ * 获取当前金额能使用的优惠卷
+ * @param string price
+ * 
+*/
+export function getCouponsOrderPrice(price, data){
+  return request.get('coupons/order/' + price, data)
+}
+
+
+
+/**
+ * 计算订单金额
+ * @param key
+ * @param data
+ * @returns {*}
+ */
+export function postOrderComputed(key, data) {
+  return request.post("/order/computed/" + key, data);
+}
+
+// 生成订单
+export function orderCreate(data) {
+	return request.post("order/create",data,{ noAuth : true });
+}
+// 新的生成订单
+export function createOrder(data) {
+	return request.post("v2/order/create",data,{ noAuth : true });
+}
+// 未支付订单
+export function groupOrderList(data) {
+	return request.get("order/group_order_list",data,{ noAuth : true });
+}
+
+// 批量退款列表
+export function refundBatch(id) {
+	return request.get("refund/batch_product/"+id,{ noAuth : true });
+}
+
+// 退款商品
+export function refundProduct(id,data) {
+	return request.get("refund/product/"+id,data,{ noAuth : true });
+}
+
+// 申请退款
+export function refundApply(id,data) {
+	return request.post("refund/apply/"+id,data,{ noAuth : true });
+}
+
+// 退款理由
+export function refundMessage() {
+	return request.get("common/refund_message",{ noAuth : true });
+}
+
+// 退款列表
+export function refundList(data) {
+	return request.get("refund/list",data,{ noAuth : true });
+}
+
+// 退款详情
+export function refundDetail(id) {
+	return request.get("refund/detail/"+id,{ noAuth : true });
+}
+
+// 物流列表
+export function expressList() {
+	return request.get("common/express");
+}
+
+// 退回商品提交
+export function refundBackGoods(id,data) {
+	return request.post("refund/back_goods/"+id,data,{ noAuth : true });
+}
+
+// 退款记录删除
+export function refundDel(id) {
+	return request.post("refund/del/"+id,{ noAuth : true });
+}
+
+// 退款记录删除
+export function refundExpress(id) {
+	return request.get("refund/express/"+id,{ noAuth : true });
+}
+
+// 核销二维码
+export function verifyCode(id) {
+	return request.get("order/verify_code/"+id);
+}
+/**
+ * 预售尾款支付
+ * @param object data
+*/
+export function presellOrderPay(id,data){
+  return request.post('presell/pay/'+id,data);
+}
+
+/**
+ * 发票订单
+ * @param object data
+*/
+export function receiptOrder(data){
+  return request.get('user/receipt/order',data);
+}
+/**
+ * 发票订单
+ * @param object data
+*/
+export function getReceiptOrder(id){
+  return request.get('user/receipt/order/'+id);
+}
+/**
+ * 发票订单
+ * @param object data
+*/
+export function getCallBackUrlApi(key){
+  return request.get('common/pay_key/'+key, {},{ noAuth : true});
+}
+/**
+ * 发票订单
+ * @param object data
+*/
+export function develiveryDetail(id){
+  return request.get(`order/delivery/${id}`);
+}
+/**
+ * 订单申请开票
+ * @param object data
+*/
+export function applyInvoiceApi(id, data){
+  return request.post(`order/receipt/${id}`, data);
+}
+/**
+ * 退款单取消申请
+ * @param object data
+*/
+export function refundCancelApi(id){
+  return request.post(`refund/cancel/${id}`);
+}

+ 70 - 0
api/points_mall.js

@@ -0,0 +1,70 @@
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+import request from "@/utils/request.js";
+/**
+ * 积分商城--banner和金刚区
+ */
+export function getIntegralHome() {
+	return request.get("points/home",{},{noAuth: true});
+}
+/**
+ * 积分商城--积分兑换区间
+ */
+export function getIntegralScope() {
+	return request.get("points/scope",{},{noAuth: true});
+}
+/**
+ * 积分商城--商品列表
+ */
+export function getIntegralGoodsList(data) {
+	return request.get("points/lst",data,{noAuth: true});
+}
+/**
+ * 积分商城--商品列表
+ */
+export function getIntegralProductDetail(id) {
+	return request.get(`points/detail/${id}`,{},{noAuth: true});
+}
+/**
+ * 积分商城--生成订单
+ */
+export function integralOrderConfirm(data) {
+	return request.post(`order/v3/check`,data,{noAuth: true});
+}
+/**
+ * 积分商城--下单
+ */
+export function integralOrderCreate(data) {
+	return request.post(`order/v3/create`,data,{noAuth: true});
+}
+/**
+ * 积分商城--订单列表
+ */
+export function integralOrderList(data) {
+	return request.get(`points/order/lst`,data,{noAuth: true});
+}
+/**
+ * 积分商城--订单详情
+ */
+export function integralOrderDetail(id) {
+	return request.get(`points/order/detail/${id}`,{},{noAuth: true});
+}
+/**
+ * 积分商城--订单详情
+ */
+export function integralOrderTake(id) {
+	return request.post(`points/order/take/${id}`,{},{noAuth: true});
+}
+/**
+ * 积分商城--删除
+ */
+export function integralOrderDelete(id) {
+	return request.post(`points/order/deleate/${id}`,{},{noAuth: true});
+}

+ 214 - 0
api/product.js

@@ -0,0 +1,214 @@
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+import request from "@/utils/request.js";
+
+/**
+ * 商品列表
+ */
+export function productLstApi(merId, data) {
+	return request.get(`server/${merId}/product/lst`, data, { login: true });
+}
+
+/**
+ * 商品列表 -- 删除
+ */
+export function productDeleteApi(merId, id) {
+	return request.post(`server/${merId}/product/delete/${id}`, {}, { login: true });
+}
+/**
+ * 商品列表 -- 上下架
+ */
+export function productOffApi(merId, id, data) {
+	return request.post(`server/${merId}/product/status/${id}`, data, { login: true });
+}
+
+/*
+ 售罄商品 -- 列表头部
+*/
+
+export function productTitle(merId, data) {
+	return request.get(`server/${merId}/product/title`, data, { login: true });
+}
+
+/*
+	回收站商品,彻底删除
+*/
+
+export function productDestory(merId, id, data) {
+	return request.post(`server/${merId}/product/destory/${id}`, data, { login: true });
+}
+
+/*
+	还原
+*/
+
+export function productRestore(merId, id) {
+	return request.post(`server/${merId}/product/restore/${id}`, {}, { login: true });
+}
+
+/*
+	添加或修改商品, 获取商品品牌
+*/
+
+export function categoryBrandlist(merId) {
+	return request.get(`server/${merId}/category/brandlist`, {}, { login: true });
+}
+
+/*
+	添加修改商品, 获取商户分类
+*/
+
+export function categorySelect(merId) {
+	return request.get(`server/${merId}/category/select`, {}, { login: true })
+}
+
+/*
+	添加修改商品, 获取平台分类
+*/
+
+export function categoryList(merId) {
+	return request.get(`server/${merId}/category/list`, {}, { login: true })
+}
+
+/*
+	商品管理, 添加商品
+*/
+
+export function productCreate(merId, data) {
+	return request.post(`server/${merId}/product/create`, data, { login: true })
+}
+/**
+ * 商品列表 -- 设置推荐
+ */
+export function productRecommendApi(merId, id, data) {
+	return request.post(`server/${merId}/product/good/${id}`, data, { login: true });
+}
+
+/*
+	获取规格列表
+*/
+
+export function attrList(merId, data) {
+	return request.get(`server/${merId}/attr/lst`, data, { login: true })
+}
+/*
+	请求运费模板
+*/
+export function templateList(merId, data) {
+	return request.get(`server/${merId}/template/lst`, data, { login: true })
+}
+/*
+	运费模板 -- 删除
+*/
+export function templateDelete(merId, data) {
+	return request.post(`server/${merId}/template/delete`, data, { login: true })
+}
+/**
+ * 运费模板 -- 详情
+ */
+export function templateDetail(merId, id) {
+	return request.get(`server/${merId}/template/detail/${id}`, {}, { login: true });
+}
+/*
+	运费模板 -- 添加
+*/
+export function templateCreate(merId, data) {
+	return request.post(`server/${merId}/template/create`, data, { login: true });
+}
+/*
+	运费模板 -- 编辑
+*/
+export function templateUpdate(merId, id, data) {
+	return request.post(`server/${merId}/template/update/${id}`, data, { login: true });
+}
+/**
+ * 店铺分类 -- 列表
+ */
+export function storeClassifyLst(merId) {
+	return request.get(`server/${merId}/category/lst`, {}, { login: true });
+}
+/**
+ * 店铺分类 -- 列表(删除)
+ */
+export function storeClassifyDel(merId, id) {
+	return request.post(`server/${merId}/category/delete/${id}`, {}, { login: true });
+}
+/**
+ * 上级分类 -- 商户列表
+ */
+export function merClassifyLst(merId) {
+	return request.get(`server/${merId}/category/select`, {}, { login: true });
+}
+/**
+ * 店铺分类 -- 添加
+ */
+export function merClassifyAdd(merId, data) {
+	return request.post(`server/${merId}/category/create`, data, { login: true });
+}
+/**
+ * 店铺分类 -- 详情
+ */
+export function merClassifyDetail(merId, id) {
+	return request.get(`server/${merId}/category/detail/${id}`, {}, { login: true });
+}
+/**
+ * 店铺分类 -- 编辑
+ */
+export function merClassifyUpdate(merId, id, data) {
+	return request.post(`server/${merId}/category/update/${id}`, data, { login: true });
+}
+/**
+ * 商品规格项目 -- 列表
+ */
+export function specificationLst(merId, data) {
+	return request.get(`server/${merId}/attr/lst`, data, { login: true });
+}
+/**
+ * 商品规格项目 -- 删除
+ */
+export function specificationDel(merId, data) {
+	return request.post(`server/${merId}/attr/delete`, data, { login: true });
+}
+/**
+ * 商品规格项目 -- 添加
+ */
+export function specificationAdd(merId, data) {
+	return request.post(`server/${merId}/attr/create`, data, { login: true });
+}
+/**
+ * 商品规格项目 -- 编辑
+ */
+export function specificationUpdate(merId, id, data) {
+	return request.post(`server/${merId}/attr/update/${id}`, data, { login: true });
+}
+/**
+ * 商品规格项目 -- 详情
+ */
+export function specificationDetail(merId, id) {
+	return request.get(`server/${merId}/attr/detail/${id}`, {}, { login: true });
+}
+/*
+	请求商品详情
+*/ 
+export function productDetail(merId, id) {
+	return request.get(`server/${merId}/product/detail/${id}`, {}, {login: true});
+}
+/*
+	编辑商品
+*/ 
+export function productUpdate(merId, id, data) {
+	return request.post(`server/${merId}/product/update/${id}`, data, { login: true });
+}
+/*
+	添加商品 -- 获取送货方式信息
+*/ 
+export function getDeliveryType(merId) {
+	return request.get(`server/${merId}/product/config`, {}, { login: true });
+}

+ 124 - 0
api/public.js

@@ -0,0 +1,124 @@
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+import request from "@/utils/request.js";
+import wechat from "@/libs/wechat.js";
+
+/**
+ * 获取微信sdk配置
+ * @returns {*}
+ */
+export function getWechatConfig() {
+	return request.get(
+		"wechat/config", {
+			url: wechat.signLink()
+		}, {
+			noAuth: true
+		}
+	);
+}
+
+/**
+ * 获取微信sdk配置
+ * @returns {*}
+ */
+export function wechatAuth(code, spread, login_type) {
+	return request.get(
+		"auth/wechat", {
+			code,
+			spread,
+			login_type
+		}, {
+			noAuth: true
+		}
+	);
+}
+
+export function commonAuth(data) {
+	return request.post(
+		"auth", data, {
+			noAuth: true
+		}
+	);
+}
+/**
+ * 获取登录授权login
+ * 
+ */
+export function getLogo() {
+	return request.get('wechat/get_logo', {}, {
+		noAuth: true
+	});
+}
+/**
+ * 小程序用户登录
+ * @param data object 小程序用户登陆信息
+ */
+export function login(data) {
+	return request.post("auth/mp", 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, code) {
+	return request.post(
+		"common/base64", {
+			image: image,
+			code: code
+		}, {
+			noAuth: true
+		}
+	);
+}
+// 配置
+export function getconfig(data) {
+	return request.get("config",data,{noAuth: true});
+}
+// 浏览记录
+export function history(data) {
+	return request.post("common/visit",data);
+}
+export function getSubscribe(){
+	return request.get("subscribe", {}, {
+		noAuth: true
+	});
+}
+export function getVersion() {
+	return request.get("version",{},{noAuth: true});
+}
+/**
+ * 获取组件底部菜单
+ * @param data object 获取组件底部菜单
+ */
+export function getNavigation(data) {
+	return request.get("navigation", data, {
+		noAuth: true
+	});
+}

+ 589 - 0
api/store.js

@@ -0,0 +1,589 @@
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+import request from "@/utils/request.js";
+/**
+ * 获取产品详情
+ * @param int id
+ * 
+ */
+export function getProductDetail(id) {
+	return request.get('store/product/detail/' + id, {}, {
+		noAuth: true
+	});
+}
+/**
+ * 获取产品详情参数
+ * @param int id
+ * 
+ */
+export function getProductParmas(id) {
+	return request.get('store/product/show/' + id, {}, {
+		noAuth: true
+	});
+}
+/**
+ * 获取店铺推荐商品
+ * @param int id
+ * 
+ */
+export function getStoreRecommend(id) {
+	return request.get('store/product/good_list/' + id, {}, {
+		noAuth: true
+	});
+}
+/**
+ * 获取预览商品详情
+ * @param int id
+ * 
+ */
+export function getPreviewProDetail(data) {
+	return request.get('store/product/preview', data, {
+		noAuth: true
+	});
+}
+/**
+ * 产品分享二维码 推广员
+ * @param int id
+ */
+export function getProductCode(id, data) {
+	return request.get('store/product/qrcode/' + id, data, {
+		noAuth: true
+	});
+}
+/**
+ * 添加收藏
+ * @param int id
+ * @param string category product=普通产品,product_seckill=秒杀产品
+ */
+export function collectAdd(data) {
+	return request.post('user/relation/create', data);
+}
+/**
+ * 删除收藏产品
+ * @param int id
+ * @param string category product=普通产品,product_seckill=秒杀产品
+ */
+export function collectDel(data) {
+	return request.post('user/relation/delete', data);
+}
+/**
+ * 购车添加
+ * 
+ */
+export function postCartAdd(data) {
+	return request.post('user/cart/create', data);
+}
+/**
+ * 获取分类列表
+ * 
+ */
+export function getCategoryList() {
+	return request.get('store/product/category/lst', {}, {
+		noAuth: true
+	});
+}
+/**
+ * 获取产品列表
+ * @param object data
+ */
+export function getProductslist(data) {
+	if (data.brand_id && Array.isArray(data.brand_id)) {
+		data = {
+			...data
+		}
+		data.brand_id = data.brand_id.toString()
+	}
+	return request.get('product/spu/lst', data, {
+		noAuth: true
+	});
+}
+/**
+ * 获取优惠券商品列表
+ * @param object data
+ */
+export function getCouponProductlist(data) {
+	if (data.brand_id && Array.isArray(data.brand_id)) {
+		data = {
+			...data
+		}
+		data.brand_id = data.brand_id.toString()
+	}
+	return request.get('product/spu/coupon_product', data, {
+		noAuth: true
+	});
+}
+/**
+ * 获取品牌列表
+ * @param object data
+ */
+export function getBrandlist(data) {
+	return request.get('store/product/brand/lst', data, {
+		noAuth: true
+	});
+}
+/**
+ * 获取推荐产品
+ * 
+ */
+export function getProductHot(page, limit) {
+	return request.get("product/spu/recommend", {
+		page: page === undefined ? 1 : page,
+		limit: limit === undefined ? 10 : limit
+	}, {
+		noAuth: true
+	});
+}
+/**
+ * 获取商户推荐产品
+ * 
+ */
+export function getMerProductHot(id,data) {
+	return request.get(`product/spu/recommend`, {
+		page: data.page === undefined ? 1 : data.page,
+		limit: data.limit === undefined ? 10 : data.limit,
+		mer_id: id || ''
+	}, {
+		noAuth: true
+	});
+}
+/**
+ * 批量收藏
+ * 
+ * @param object id
+ * @param string category 
+ */
+export function collectAll(data) {
+	return request.post('user/relation/batch/create', data);
+}
+/**
+ * 首页产品的轮播图和产品信息
+ * @param int type 
+ * 
+ */
+export function getGroomList(type, data) {
+	return request.get('product/spu/hot/' + type, data, {
+		noAuth: true
+	});
+}
+/**
+ * 获取商品收藏列表
+ * @param object data
+ */
+export function getCollectUserList(data) {
+	return request.get('user/relation/product/lst', data)
+}
+/**
+ * 获取商品收藏列表 -- 删除
+ * @param object data
+ */
+export function userCollectDel(data) {
+	return request.post('user/relation/batch/delete', data)
+}
+/**
+ * 获取产品评论
+ * @param int id
+ * @param object data
+ * 
+ */
+export function getReplyList(id, data) {
+	return request.get('store/product/reply/lst/' + id, data, {
+		noAuth: true
+	})
+}
+/**
+ * 产品评价数量和好评度
+ * @param int id
+ */
+export function getReplyConfig(id) {
+	return request.get('reply/config/' + id);
+}
+/**
+ * 获取搜索关键字获取
+ * 
+ */
+export function getSearchKeyword() {
+	return request.get('common/hot_keyword', {}, {
+		noAuth: true
+	});
+}
+/**
+ * 今日热搜列表
+ * 
+ */
+export function getTodayHotSearch() {
+	return request.get('product/spu/hot_lst', {}, {
+		noAuth: true
+	});
+}
+/**
+ * 热销排行分类
+ * 
+ */
+export function getHotRanking() {
+	return request.get('product/spu/hot_top', {}, {
+		noAuth: true
+	});
+}
+/**
+ * 商户列表
+ * @returns {*}
+ */
+export function storeMerchantList(data) {
+	return request.get("store/merchant/lst", data, {
+		noAuth: true
+	});
+}
+/**
+ * 获取商铺详情
+ * @param {Object} id 商铺id
+ * @param {Object} data 商铺数据
+ */
+export function getStoreDetail(id, data) {
+	return request.get("store/merchant/detail/" + id, data, {
+		noAuth: true
+	});
+}
+/**
+ * 获取商铺商品列表
+ * @param {Object} id 商铺 id
+ * @param {Object} data 商铺商品列表数据
+ */
+export function getStoreGoods(id, data) {
+	return request.get("product/spu/merchant/" + id, data, {
+		noAuth: true
+	});
+}
+/**
+ * 获取商铺分类列表
+ * @param {Object} id 商铺 id
+ * @param {Object} data
+ */
+export function getStoreCategory(id, data) {
+	return request.get("store/merchant/category/lst/" + id, data, {
+		noAuth: true
+	});
+}
+/**
+ * 关注商铺
+ * @param {Object} type_id 商铺 id
+ */
+export function followStore(type_id) {
+	return request.post("user/relation/create", {
+		type: 10,
+		type_id: type_id
+	});
+}
+/**
+ * 取消商铺关注
+ * @param {Object} type_id 商铺 id
+ */
+export function unfollowStore(type_id) {
+	return request.post("user/relation/delete", {
+		type: 10,
+		type_id: type_id
+	});
+}
+/**
+ * 获取商铺优惠券
+ * @param {Object} id
+ */
+export function getStoreCoupon(id) {
+	return request.get("coupon/store/" + id, {
+		noAuth: true
+	});
+}
+/**
+ * 获取商铺优惠券
+ */
+export function getMerchantLst(data) {
+	return request.get("user/relation/merchant/lst", data, {
+		noAuth: true
+	});
+}
+/**
+ * 物流信息
+ */
+export function express(id) {
+	return request.post("ordero/express/" + id, {
+		noAuth: true
+	});
+}
+/**
+ * 子集分类
+ * @returns {*}
+ */
+export function storeCategory(pid) {
+	return request.get("store/product/category", pid, {
+		noAuth: true
+	});
+}
+/**
+ * 分销说明
+ * @returns {*}
+ */
+export function bagExplain() {
+	return request.get("store/product/bag/explain");
+}
+/**
+ * 分销礼包推荐列表
+ * @returns {*}
+ */
+export function bagRecommend() {
+	return request.get("product/spu/bag/recommend");
+}
+/**
+ * 分销礼包列表
+ * @returns {*}
+ */
+export function productBag(data) {
+
+	return request.get("product/spu/bag", data, {
+		noAuth: true
+	});
+}
+/**
+ * 商铺二维码
+ * @returns {*}
+ */
+export function merchantQrcode(id, data) {
+	return request.get("store/merchant/qrcode/" + id, data, {
+		noAuth: true
+	});
+}
+/**
+ * 推荐商品
+ * @returns {*}
+ */
+export function merchantProduct(id, data) {
+	if (data.brand_id && Array.isArray(data.brand_id)) {
+		data = {
+			...data
+		}
+		data.brand_id = data.brand_id.toString()
+	}
+	return request.get("product/spu/merchant/" + id, data, {
+		noAuth: true
+	});
+}
+/**
+ * 推荐商品banner
+ * @returns {*}
+ */
+export function getHotBanner(type) {
+	return request.get("common/hot_banner/" + type, {}, {
+		noAuth: true
+	});
+}
+/**
+ * 商户入驻表单
+ * @returns {*}
+ */
+export function create(data) {
+	return request.post("intention/create", data);
+}
+/**
+ * 商户入驻短信验证码
+ * @returns {*}
+ */
+export function verify(data) {
+	return request.post("auth/verify", data);
+}
+/**
+ * 获取秒杀商品详情
+ * @param int id
+ * 
+ */
+export function getSeckillProductDetail(id,data) {
+	return request.get('store/product/seckill/detail/' + id, data, {
+		noAuth: true
+	});
+}
+/**
+ * 获取秒杀商品详情参数
+ * @param int id
+ * 
+ */
+// export function getSeckillProductParmas(id) {
+// 	return request.get('store/product/seckill/show/' + id, {}, {
+// 		noAuth: true
+// 	});
+// }
+/**
+ * 直播推荐列表
+ * @returns {*}
+ */
+export function getLiveList(data) {
+	return request.get(`broadcast/hot`, data, {
+		noAuth: true
+	});
+}
+/**
+ * 直播列表
+ * @returns {*}
+ */
+export function getBroadcastListApi(data) {
+	return request.get("broadcast/lst", data, {
+		noAuth: true
+	});
+}
+/**
+ * 商户分类
+ * @returns {*}
+ */
+export function merClassifly() {
+	return request.get("intention/cate", {}, {
+		noAuth: true
+	});
+}
+/**
+ * 获取预售商品详情
+ * @param int id
+ * 
+ */
+export function getPresellProductDetail(id) {
+	return request.get('store/product/presell/detail/' + id, {}, {
+		noAuth: true
+	});
+}
+/**
+ * 获取商户申请记录
+ * @param int id
+ * 
+ */
+export function getApplicationRecordList(data) {
+	return request.get('intention/lst', data);
+}
+/**
+ * 获取商户申请详情
+ * @param int id
+ * 
+ */
+export function getGoodsDetails(id) {
+	return request.get('intention/detail/' + id, {});
+}
+/**
+ * 修改入驻信息
+ * @param int id
+ * 
+ */
+export function updateGoodsRecord(id, data) {
+	return request.post('intention/update/' + id, data);
+}
+/**
+ * 获取定位详细地址
+ * @param int id
+ * 
+ */
+export function getGeocoder(data) {
+	return request.get(`lbs/geocoder?location=${data.lat},${data.long}`, {}, {
+		noAuth: true
+	});
+}
+/**
+ * 获取店铺类型
+ * @param int id
+ * 
+ */
+export function getStoreTypeApi(data) {
+	return request.get('intention/type', data, {
+		noAuth: true
+	});
+}
+/**
+ * 获取参数列表
+ * @param int id
+ * 
+ */
+export function getParmasList(data) {
+	return request.get('product/spu/params', data, {
+		noAuth: true
+	});
+}
+/**
+ * 获取参数值列表
+ * @param int id
+ * 
+ */
+export function getParmasValue(id) {
+	return request.get(`product/spu/params_value/${id}`, {}, {
+		noAuth: true
+	});
+}
+/**
+ * 到货通知
+ * 
+ */
+export function arrivalNoticeApi(data) {
+	return request.post('store/product/increase_take',data);
+}
+/*
+  获取图片验证码
+*/ 
+export function getCaptcha() {
+	return request.get('captcha');
+}
+/*
+	获取店铺资质
+*/
+export function storeCertificate(data) {
+	return request.post(`store/certificate/${data.merId}`, data)
+}
+/**
+ * 本地服务列表
+ * @returns {*}
+ */
+export function storeServiceList(id, data) {
+	return request.get(`product/spu/local/${id}`, data, {
+		noAuth: true
+	});
+}
+/**
+ * 复制口令
+ * @returns {*}
+ */
+export function copyPasswordApi(data) {
+	return request.get(`product/spu/copy`, data, {
+		noAuth: true
+	});
+}
+/**
+ * 口令搜索
+ * @returns {*}
+ */
+export function copyPasswordSearch(data) {
+	return request.get(`command/copy`, data, {
+		noAuth: true
+	});
+}
+/**
+ * 套餐列表
+ * @returns {*}
+ */
+export function getDiscountsLst(data) {
+	return request.get(`discounts/lst`, data, {
+		noAuth: true
+	});
+}
+/**
+ * 套餐--立即购买
+ * @returns {*}
+ */
+export function discountsCartAdd(data) {
+	return request.post('user/cart/batchCreate', data);
+}
+/**
+ * 商品--价格说明
+ * @returns {*}
+ */
+export function priceRuleApi(id) {
+	return request.get(`store/product/price_rule/${id}`, {}, {
+		noAuth: true
+	});
+}

+ 778 - 0
api/user.js

@@ -0,0 +1,778 @@
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+import request from "@/utils/request.js";
+import Cache from '@/utils/cache'
+/**
+ * 获取用户信息
+ * 
+ */
+export function getUserInfo() {
+	return request.get('user');
+}
+/**
+ * 头像
+ * 
+ */
+export function editAvatar(data) {
+	return request.post('user/change/info',data);
+}
+
+// 修改昵称
+export function updateInfo(data) {
+	return request.post('user/change/avatar',data);
+}
+/**
+ * h5用户登录
+ * @param data object 用户账号密码
+ */
+export function loginH5(data) {
+	return request.post("auth/login", data, {
+		noAuth: true
+	});
+}
+/**
+ * h5用户手机号登录
+ * @param data object 用户手机号 也只能
+ */
+export function loginMobile(data) {
+	return request.post("auth/smslogin", data, {
+		noAuth: true
+	});
+}
+/**
+ * h5用户手机号登录
+ * @param data object
+ */
+export function loginMpPhone(data) {
+	return request.post("auth/mp_phone", data, {
+		noAuth: true
+	});
+}
+/**
+ * h5获取登录授权auth_token
+ * @param data object 
+ */
+export function getAuthType(data) {
+	return request.post("auth/mp_login_type", data, {
+		noAuth: true
+	});
+}
+/**
+ * 验证码key
+ */
+export function getCodeApi() {
+	return request.get("verify_code", {}, {
+		noAuth: true
+	});
+}
+
+/**
+ * h5用户发送验证码
+ * @param data object 用户手机号
+ */
+export function registerVerify(data) {
+	return request.post("auth/verify", data, {
+		noAuth: true
+	});
+}
+/**
+ * h5用户手机号注册
+ * @param data object 用户手机号 验证码 密码
+ */
+export function register(data) {
+	return request.post("auth/register", data, {
+		noAuth: true
+	});
+}
+
+/**
+ * 用户手机号修改密码
+ * @param data object 用户手机号 验证码 密码
+ */
+export function registerReset(data) {
+	return request.post("/register/reset", data, {
+		noAuth: true
+	});
+}
+/**
+ * 用户手机号忘记密码
+ */
+export function registerForget(data) {
+	return request.post("user/change_pwd", data, {
+		noAuth: true
+	});
+}
+/**
+ * 获取用户中心菜单
+ *
+ */
+export function getMenuList() {
+	return request.get("common/menus",{},{noAuth: true});
+}
+/*
+ * 签到用户信息
+ * */
+export function getSignUser() {
+	return request.get("user/sign/info");
+}
+
+/**
+ * 获取签到配置
+ * 
+ */
+export function getSignConfig() {
+	return request.get('sign/config')
+}
+/**
+ * 获取签到列表
+ * @param object data
+ */
+export function getSignList(data) {
+	return request.get('user/sign/lst', data);
+}
+/**
+ * 用户签到
+ */
+export function setSignIntegral() {
+	return request.post('user/sign/create')
+}
+
+/**
+ * 签到列表(年月)
+ * @param object data
+ * 
+ */
+export function getSignMonthList(data) {
+	return request.get('user/sign/month', data)
+}
+/**
+ * 活动状态
+ * 
+ */
+export function userActivity() {
+	return request.get('user/activity');
+}
+/*
+ * 资金明细(types|0=全部,1=消费,2=充值,3=返佣)
+ * */
+export function getCommissionInfo(q, types) {
+	return request.get("user/bill", q);
+}
+/*
+ * 提现列表
+ * */
+export function extractLst(data) {
+	return request.get("user/extract/lst", data);
+}
+/*
+ * 积分记录
+ * */
+export function getIntegralList(data) {
+	return request.get("user/integral/lst", data);
+}
+
+/*
+ * 消费券记录
+ * */
+export function getCashList(data) {
+	return request.get("user/cash/lst", data);
+}
+
+/**
+ * 获取分销海报图片
+ * 
+ */
+export function spreadBanner() {
+	//#ifdef H5
+	return request.get('user/spread_image', {
+		type: 'wechat'
+	});
+	//#endif
+	//#ifdef MP
+	return request.get('user/spread_image', {
+		type: 'routine'
+	});
+	//#endif
+}
+/**
+ *
+ * 获取推广用户一级和二级
+ * @param object data
+ */
+export function spreadPeople(data) {
+	return request.get('user/spread_list', 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('user/spread_order', data);
+}
+/*
+ * 获取推广人排行
+ * */
+export function getRankList(data) {
+	return request.get("user/spread_top", data);
+}
+/*
+ * 获取佣金排名
+ * */
+export function getBrokerageRank(q) {
+	return request.get("user/brokerage_top", q);
+}
+/**
+ * 提现申请
+ * @param object data
+ */
+export function extractCash(data) {
+	return request.post('user/extract/create', data)
+}
+/**
+ * 提现银行/提现最低金额
+ * 
+ */
+export function extractBank() {
+	return request.get('user/extract/banklst');
+}
+/**
+ * 会员等级列表
+ * 
+ */
+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 object data
+ */
+export function getAddressList(data) {
+	return request.get('user/address/lst', data);
+}
+/**
+ * 设置默认地址
+ * @param int id
+ */
+export function setAddressDefault(id) {
+	return request.post('user/address/update/' + id)
+}
+/**
+ * 修改 添加地址
+ * @param object data
+ */
+export function editAddress(data) {
+	return request.post('user/address/create', data);
+}
+/**
+ * 删除地址
+ * @param int id
+ * 
+ */
+export function delAddress(id) {
+	return request.post('user/address/delete/' + id)
+}
+/**
+ * 获取单个地址
+ * @param int id 
+ */
+export function getAddressDetail(id) {
+	return request.get('user/address/detail/' + id);
+}
+/**
+ * 修改用户信息
+ * @param object
+ */
+export function userEdit(data) {
+	return request.post('user/edit', data);
+}
+/*
+ * 退出登录
+ * */
+export function getLogout() {
+	return request.post("logout");
+}
+/**
+ * 佣金转入
+ * 
+ */
+export function rechargeBrokerage(data) {
+	return request.post('user/recharge/brokerage', data)
+}
+/**
+ * 小程序充值
+ * 
+ */
+export function rechargeRoutine(data) {
+	return request.post('recharge/routine', data)
+}
+/*
+ * 公众号充值
+ * */
+export function rechargeWechat(data) {
+	return request.post("user/recharge", data);
+}
+/**
+ * 获取默认地址
+ * 
+ */
+export function getAddressDefault() {
+	return request.get('address/default');
+}
+/**
+ * 充值金额选择
+ */
+export function getRechargeApi() {
+	return request.get("common/recharge_quota");
+}
+/**
+ * 登陆记录
+ */
+export function setVisit(data) {
+	return request.post('user/set_visit', { ...data
+	}, {
+		noAuth: true
+	});
+}
+/**
+ * 客服列表
+ */
+export function serviceList(data) {
+	return request.get("service/list", data);
+}
+/**
+ * 客服列表
+ */
+export function serviceLogin(key, data) {
+	return request.post("service/scan_login/" + key, data);
+}
+/**
+ * 客服获取客户列表
+ */
+export function serviceUserList(mer_id, data) {
+	return request.get("service/user_list/"+mer_id, data);
+}
+/**
+ * 用户获取聊天记录详情
+ */
+export function getChatRecord(to_uid, data) {
+	return request.get("service/history/" + to_uid, data);
+}
+/**
+ * 客服获取聊天记录详情
+ */
+export function getMerHistory(userid, mer_id, data) {
+	return request.get("service/mer_history/" + mer_id + '/' + userid, data);
+}
+/**
+ * 静默绑定推广人
+ * @param {Object} puid
+ */
+export function spread(puid) {
+	Cache.set("spread", puid || 0);
+	return request.post("user/spread", {
+		spread_spid: puid
+	});
+}
+/**
+ * 反馈类型
+ */
+export function feedbackType() {
+	return request.get("common/feedback_type");
+}
+/**
+ * 提交反馈
+ */
+export function feedback(data) {
+	return request.post("user/feedback", { ...data
+	});
+}
+/**
+ * 反馈列表
+ */
+export function feedbackList(data) {
+	return request.get("user/feedback/list", data);
+}
+/**
+ * 反馈列表
+ */
+export function feedbackDetail(id) {
+	return request.get("user/feedback/detail/" + id);
+}
+/**
+ * 浏览记录
+ */
+export function historyList(data) {
+	return request.get("user/history", data);
+}
+/**
+ * 删除浏览记录
+ */
+export function historyDelete(id) {
+	return request.post("user/history/delete/" + id);
+}
+/**
+ * 批量删除浏览记录
+ */
+export function historyBatchDelete(data) {
+	return request.post("user/history/batch/delete", data);
+}
+/**
+ * 批量收藏浏览记录
+ */
+export function historyBatchCollect(data) {
+	return request.post("user/relation/batch/create", data);
+}
+/**
+ * 佣金记录
+ */
+export function brokerage_list(data) {
+	return request.get("user/brokerage_list", data);
+}
+/**
+ * 佣金数据
+ */
+export function spreadInfo() {
+	return request.get("user/spread_info");
+}
+// 图片验证码
+export function getCaptcha() {
+	return request.get('captcha', {}, {
+		noAuth: true
+	});
+}
+// 用户账户列表
+export function userAcc() {
+	return request.get('user/account', {}, {
+		noAuth: true
+	});
+}
+// 创建发票
+export function invoiceSave(data) {
+	return request.post('user/receipt/create', data);
+}
+// 编辑发票
+export function invoiceUpdate(id, data) {
+	return request.post('user/receipt/update/' + id, data);
+}
+// 获取默认发票
+export function invoiceDefault(id) {
+	return request.post('user/receipt/is_default/' + id);
+}
+// 发票抬头--列表
+export function invoice(data) {
+	return request.get('user/receipt/lst', data);
+}
+// 发票抬头--删除
+export function invoiceDelete(id) {
+	return request.post('user/receipt/delete/' + id);
+}
+// 发票--详情
+export function invoiceDetail(id) {
+	return request.get('user/receipt/detail/' + id);
+}
+/**
+ * 新版分享海报信息获取
+ * 
+ */
+export function spreadMsg(data) {
+	return request.get('user/v2/spread_image', data);
+}
+/**
+ * 图片链接转base64
+ * 
+ */
+export function imgToBase(data) {
+	return request.post('common/base64', data, {noAuth: true});
+}
+/**
+ * 获取协议
+ * 
+ */
+export function getAgreementApi(key) {
+	return request.get('agreement/'+key,{},{noAuth: true});
+}
+/**
+ * 获取协议
+ * 
+ */
+export function getIntegralInfo() {
+	return request.get('user/integral/info');
+}
+/**
+ * 获取店铺列表
+ * 
+ */
+export function getStoreList(data) {
+	return request.get('user/services', data);
+}
+/*
+	获取佣金说明
+*/
+export function commissionDescription() {
+	return request.get('agreement/sys_extension_agree')
+}
+/*
+	获取用户分销等级信息
+*/
+export function getBrokerageInfo() {
+	return request.get('user/brokerage/info')
+}
+/*
+	获取用户分销等级表格数据
+*/
+export function getBrokerageGrade() {
+	return request.get('user/brokerage/all')
+}
+/*
+	分销员升级提醒
+*/
+export function brokerageNotice(data) {
+	return request.get(`user/brokerage/notice`, data)
+}
+/*
+	口令解析
+*/
+export function pwdResolution(data) {
+	return request.get(`command/copy?key=${data}`)
+}
+/*
+	获取佣金说明
+*/
+export function getInstructions(key) {
+	return request.get(`agreement/${key}`)
+}
+/*
+	会员信息
+*/
+export function memberInfo() {
+	return request.get('user/member/info')
+}
+/**
+ * 成长值记录
+ * @param object data
+ * 
+ */
+export function growthValueRecord(data) {
+	return request.get('user/member/log', data)
+}
+/**
+ * 协议规则列表
+ * @param object data
+ * 
+ */
+export function cacheLst() {
+	return request.get('agreement_lst',{}, {noAuth: true})
+}
+/**
+ * 协议规则列表对应的数据
+ * @param object data
+ * 
+ */
+export function cacheInfo(key) {
+	return request.get(`agreement/${key}`,{}, {noAuth: true})
+}
+/**
+ * 注销账户
+ * @param object data
+ * 
+ */
+export function userOut(data) {
+	return request.post(`user/cancel`, data)
+}
+/**
+ * 获取聊天用户信息
+ * @param object data
+ * 
+ */
+export function serviceUser(merId, uid) {
+	return request.get(`service/user/${merId}/${uid}`)
+}
+/**
+ * 保存聊天用户备注
+ * @param object data
+ * 
+ */
+export function serviceSaveMark(merId, uid, mark) {
+	return request.post(`service/mark/${merId}/${uid}`, {mark})
+}
+/**
+ * 获取会员卡类型
+ * @param object data
+ * 
+ */
+export function memberCard() {
+	return request.get(`svip/pay_lst`)
+}
+/**
+ * 开通付费会员--支付
+ * @param object data
+ * 
+ */
+export function memberCardCreate(id, data) {
+	return request.post(`svip/pay/${id}`, data)
+}
+/**
+ * 付费会员权益
+ * @param object data
+ * 
+ */
+export function memberEquity() {
+	return request.get(`svip/user_info`, {}, {noAuth: true})
+}
+/**
+ * 付费会员优惠券
+ * @param object data
+ * 
+ */
+export function memberCouponLst() {
+	return request.get(`svip/coupon_lst`, {}, {noAuth: true})
+}
+/**
+ * 付费会员优惠券--领取
+ * @param object data
+ * 
+ */
+export function receiveMemberCoupon(id) {
+	return request.post(`svip/coupon_receive/${id}`)
+}
+/**
+ * 付费会员--会员商品
+ * @param object data
+ * 
+ */
+export function groomList(data) {
+	return request.get(`svip/product_lst`, data, {noAuth: true})
+}
+/**
+ * 客服聊天--撤回消息
+ * @param object data
+ * 
+ */
+export function chatReverstApi(id) {
+	return request.post(`service/recall/${id}`)
+}
+/**
+ * 客服聊天--判断店铺是否有在线客服
+ * @param object data
+ * 
+ */
+export function hasServiceApi(id) {
+	return request.get(`has_service/${id}`)
+}
+/**
+ * 银行卡提现--银行卡信息
+ * @param object data
+ * 
+ */
+export function getBankInfo() {
+	return request.get(`user/extract/history_bank`)
+}
+/**
+ * 用户设置--信息
+ * @param object data
+ * 
+ */
+export function getUserSetting() {
+	return request.get(`user/fields/info`)
+}
+/**
+ * 用户设置--修改
+ * @param object data
+ * 
+ */
+export function userSettingEdit(data) {
+	return request.post(`user/fields/save`,  data)
+}
+/**
+ * 报名活动--详情
+ * @param object data
+ * 
+ */
+export function registrateDetail(id) {
+	return request.get(`system/form/detail/${id}`,{}, {noAuth: true})
+}
+/**
+ * 报名活动--提交数据
+ * @param object data
+ * 
+ */
+export function registrateCreate(id,data) {
+	return request.post(`user/form/create/${id}`, data)
+}
+/**
+ * 报名活动--列表
+ * @param object data
+ * 
+ */
+export function getRechargeList(data) {
+	return request.get(`system/form/lst`, data, {noAuth: true})
+}
+/**
+ * 报名活动--记录
+ * @param object data
+ * 
+ */
+export function getRechargeRecordList(data) {
+	return request.get(`user/form/lst`, data)
+}
+/**
+ * 报名活动--分享海报
+ * @param object data
+ * 
+ */
+export function registratePoster(id) {
+	return request.get(`system/form/share_posters/${id}`, {})
+}
+/**
+ * 报名活动--提交记录详情
+ * @param object data
+ * 
+ */
+export function registrateRecordDetail(id) {
+	return request.get(`user/form/show/${id}`)
+}
+/**
+ * 系统表单--表单详情数据
+ * @param object data
+ * 
+ */
+export function systemFormData(id) {
+	return request.get(`system/form/info/${id}`, {}, {noAuth: true})
+}

+ 111 - 0
components/BaseMoney.vue

@@ -0,0 +1,111 @@
+<template>
+	<view :class="{ line: line, weight: weight }"
+		:style="{color: '#' + incolor,fontWeight: fontWeight, display:(discount || inline) ? 'inline-block' : 'block', fontFamily: line?'Futura-Light':'Futura' }"
+		class="base-money">
+		<text v-if="!discount" class="symbol" :style="{'font-size': symbolSize +'rpx'}">¥</text><text class="integer"
+			:style="{'font-size': integerSize +'rpx'}">{{ integer }}</text>
+		<text v-if="digits && decimal != '00' && decimal != '0'" class="decimal"
+			:style="{'font-size': decimalSize +'rpx'}">.{{ decimal }}</text>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	export default {
+		name: 'BaseMoney',
+		props: {
+			// 小数位数,为0则不显示
+			digits: {
+				type: Number,
+				default: 2
+			},
+			fontWeight: {
+				type: Number | String,
+				default: 'inherit'
+			},
+			money: {
+				type: String | Number,
+				default: ""
+			},
+			// 删除线
+			line: {
+				type: Boolean,
+				default: false
+			},
+			// 粗体
+			weight: {
+				type: Boolean,
+				default: false
+			},
+			incolor: {
+				type: String,
+				default: '424242'
+			},
+			symbolSize: {
+				type: String,
+				default: '20'
+			},
+			integerSize: {
+				type: String,
+				default: '26'
+			},
+			decimalSize: {
+				type: String,
+				default: '24'
+			},
+			discount: {
+				type: Boolean,
+				default: false
+			},
+			inline: {
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+				integer: 0,
+				decimal: 0
+			};
+		},
+		watch: {
+			money: {
+				handler(newValue, oldValue) {
+					let value = Number(newValue).toFixed(this.digits);
+					value = value.split('.');
+					this.integer = value[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
+					if (value[1]) {
+						this.decimal = (value[1].length == 2 && value[1].charAt(1) != 0) ? value[1] : (value[1].charAt(
+							0) || 0);
+					}
+
+				},
+				immediate: true
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.base-money {
+		font-family: Futura;
+		&.line {
+			.symbol,.decimal,.integer{
+				text-decoration: line-through;
+			}
+		}
+
+		&.weight {
+			font-weight: 500;
+		}
+	}
+	
+</style>

+ 66 - 0
components/Loading/index.vue

@@ -0,0 +1,66 @@
+<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>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	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>

+ 160 - 0
components/PriceChange/index.vue

@@ -0,0 +1,160 @@
+<template>
+  <view>
+    <view class="priceChange" :class="change === true ? 'on' : ''">
+      <view class="priceTitle" :style="{ 'background-image': `url(${domain}/static/images/pricetitle.jpg)`}">
+        {{ status == 0 ? "一键改价" :  status == 1  ?   "订单备注" : "立即退款" }}
+        <span class="iconfont icon-guanbi" @click="close"></span>
+      </view>
+     <view class="listChange" v-if="status == 0">
+        <view class="item acea-row row-between-wrapper">
+          <view>商品总价(¥)</view>
+          <view class="money">
+					<input
+						type="digit"
+						class="color_black"
+						v-model="orderInfo.total_price"
+						@input="setValue"
+					/>
+          </view>
+        </view>
+        <view class="item acea-row row-between-wrapper">
+          <view>实际支付邮费(¥)</view>
+          <view class="money">
+					<input
+						type="digit"
+						class="color_black"
+						v-model="orderInfo.pay_postage"
+						@input="setValue"/>
+          </view>
+        </view>
+        <view class="item acea-row row-between-wrapper">
+          <view>实际支付金额(¥)</view>
+          <view class="money">
+					{{ Number(orderInfo.total_price) + Number(orderInfo.pay_postage) - Number(orderInfo.coupon_price) - Number(orderInfo.integral_price)- Number(orderInfo.svip_discount)}}<span class="iconfont icon-suozi"></span>
+          </view>
+        </view>
+				<view v-if="orderInfo.coupon_price>0 || orderInfo.svip_discount>0" class="item acea-row row-between-wrapper">
+					<view>优惠金额(¥)</view>
+					<view class="money">
+					{{ Number(orderInfo.coupon_price) + Number(orderInfo.svip_discount) }}<span class="iconfont icon-suozi"></span>
+					</view>  
+				</view>
+				<view v-if="orderInfo.integral_price>0" class="item acea-row row-between-wrapper">
+					<view>积分抵扣(¥)</view>
+					<view class="money">
+					{{ orderInfo.integral_price }}<span class="iconfont icon-suozi"></span>
+					</view>  
+				</view>
+      </view>
+      <view class="listChange" v-else>
+        <textarea
+          :placeholder="orderInfo.remark ? orderInfo.remark : '请填写备注信息...'"
+          v-model="remark"
+        ></textarea>
+      </view>
+      <view class="modify" @click="save">
+        {{ status == 0 ? "立即修改" : "确认提交" }}
+      </view>
+    </view>
+    <view class="mask" @touchmove.prevent v-show="change === true"></view>
+  </view>
+</template>
+
+<script>
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------	
+import { HTTP_REQUEST_URL } from '@/config/app';
+export default {
+  name: "PriceChange",
+  components: {},
+  props: {
+    change: Boolean,
+    orderInfo: Object,
+    status: String
+  },
+  data: function() {
+    return {
+			domain: HTTP_REQUEST_URL,
+      focus: false,
+      price: 0,
+			actual_price: 0,
+      refund_price: 0,
+      remark: ""
+    };
+  },
+  watch: {
+    orderInfo: function(nVal) {
+      this.price = this.orderInfo.pay_price;
+			this.actual_price = this.orderInfo.total_price + this.orderInfo.pay_postage - this.orderInfo.coupon_price
+      this.refund_price = this.orderInfo.pay_price;
+      this.remark = this.orderInfo.remark;
+    }
+  },
+  mounted: function() {
+	  
+	},
+  methods: {
+    priceChange: function() {
+      this.focus = true;
+    },
+    close: function() {
+      this.price = this.orderInfo.pay_price;
+      this.$emit("closechange", false);
+    },
+		setValue: function(){
+			this.price = this.orderInfo.total_price + this.orderInfo.pay_postage - this.orderInfo.coupon_price
+		},
+		save: function() {
+			let that = this;
+			that.$emit("savePrice", {
+				price: that.price,
+				refund_price: that.refund_price,
+				type: 1,
+				remark: that.remark,
+				orderInfo: that.orderInfo
+			});
+		},
+		refuse: function() {
+			let that = this;
+			that.$emit("savePrice", {
+				price: that.price,
+				refund_price: that.refund_price,
+				type: 2,
+				remark: that.remark
+			});
+		}
+	}
+};
+</script>
+<style>
+.priceChange{position:fixed;width:580upx;background-color:#fff;border-radius:10upx;top:50%;left:50%;margin-left:-290upx;margin-top:-335upx;z-index:666;transition:all 0.3s ease-in-out 0s;transform: scale(0);opacity:0;}
+.priceChange.on{opacity:1;transform: scale(1);}
+.priceChange .priceTitle{background-repeat:no-repeat;background-size:100% 100%;width:100%;height:160upx;border-radius:10upx 10upx 0 0;text-align:center;font-size:40upx;color:#fff;line-height:160upx;position:relative;}
+.priceChange .priceTitle .iconfont{position:absolute;font-size:40upx;right:26upx;top:23upx;width:40upx;height:40upx;line-height:40upx;}
+.priceChange .listChange{padding:0 40upx;}
+.priceChange .listChange textarea{box-sizing: border-box;}
+.priceChange .listChange .item{height:103upx;border-bottom:1px solid #e3e3e3;font-size:32upx;color:#333;}
+.priceChange .listChange .item .money{color:#666;width:200upx;text-align:right;}
+.priceChange .listChange .item .money .color_black{color: #333;}
+.priceChange .listChange .item .money .iconfont{font-size:32upx;margin-left:20upx;}
+.priceChange .listChange .item .money input{width:100%;height:100%;text-align:right;color:#ccc;}
+.priceChange .listChange .item .money input.on{color:#666;}
+.priceChange .modify{font-size:32upx;color:#fff;width:490upx;height:90upx;text-align:center;line-height:90upx;border-radius:45upx;background-color:#2291f8;margin:53upx auto;}
+.priceChange .modify1{font-size:32upx;color:#312b2b;width:490upx;height:90upx;text-align:center;line-height:90upx;border-radius:45upx;background-color:#eee;margin:30upx auto 0 auto;}
+.priceChange .listChange textarea {
+  border: 1px solid #eee;
+  width: 100%;
+  height: 200upx;
+  margin-top: 50upx;
+  border-radius: 10upx;
+  color: #333;
+  padding: 20upx;
+}
+</style>

+ 204 - 0
components/WaterfallsFlow/WaterfallsFlow.vue

@@ -0,0 +1,204 @@
+<template>
+    <view :class="'wf-page wf-page'+type">
+        <!--    left    -->
+        <view>
+            <view id="left" v-if="leftList.length">
+                <view v-for="(item,index) in leftList" :key="index"
+                      class="wf-item" @tap="itemTap(item)">
+                    <WaterfallsFlowItem :item="item" :isStore="isStore" :type="type" @goShop="goShop"/>
+                </view>
+            </view>
+        </view>
+        <!--    right    -->
+        <view>
+            <view id="right" v-if="rightList.length">
+                <view v-for="(item,index) in rightList" :key="index"
+                      class="wf-item" @tap="itemTap(item)">					  
+                    <WaterfallsFlowItem :item="item" :isStore="isStore" :type="type" @goShop="goShop"/>
+                </view>
+            </view>
+        </view>
+    </view>
+</template>
+
+<script>
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+import WaterfallsFlowItem from '../WaterfallsFlowItem/WaterfallsFlowItem.vue'
+
+export default {
+    components: {
+        WaterfallsFlowItem
+    },
+    props: {
+        // 瀑布流列表
+        wfList: {
+            type: Array,
+            require: true
+        },
+        updateNum: {
+            type: Number,
+            default: 10
+        },
+		type: {
+			type: Number,
+			default: 0			
+		},
+		isStore: {
+			type: [String, Number],
+			default: '1'
+		},
+    },
+    data() {
+        return {
+            allList: [],       // 全部列表
+            leftList: [],      // 左边列表
+            rightList: [],     // 右边列表
+            mark: 0,           // 列表标记
+            boxHeight: [],     // 下标0和1分别为左列和右列高度
+        };
+    },
+    watch: {
+        // 监听列表数据变化
+        wfList:  {
+			handler(nVal,oVal){
+				// 如果数据为空或新的列表数据少于旧的列表数据(通常为下拉刷新或切换排序或使用筛选器),初始化变量
+				if (!this.wfList.length ||
+				    (this.wfList.length === this.updateNum && this.wfList.length <= this.allList.length)) {
+				    this.allList = [];
+				    this.leftList = [];
+				    this.rightList = [];
+				    this.boxHeight = [];
+				    this.mark = 0;
+				}
+				
+				// 如果列表有值,调用waterfall方法
+				if (this.wfList.length) {
+				    this.allList = this.wfList;
+					this.leftList = [];
+					this.rightList = [];
+					this.boxHeight = [];
+					this.allList.forEach((v, i) => {
+						if(this.allList.length < 3 || (this.allList.length <= 7  && this.allList.length - i > 1) || (this.allList.length > 7 && this.allList.length - i > 2)) {
+							if(i % 2){
+								this.rightList.push(v);
+							}else{
+								this.leftList.push(v);
+							}
+						}
+					});
+					if(this.allList.length < 3){
+						this.mark = this.allList.length+1;
+					}else if(this.allList.length <= 7){
+						this.mark = this.allList.length - 1;
+					}else{
+						this.mark = this.allList.length - 2;
+					}
+					if(this.mark < this.allList.length){
+						this.waterFall()
+					}
+				}
+			},
+			immediate: true,
+			deep:true
+        },
+		mounted(){
+		},
+
+        // 监听标记,当标记发生变化,则执行下一个item排序
+        mark() {
+            const len = this.allList.length;
+            if (this.mark < len && this.mark !== 0 && this.boxHeight.length) {
+                this.waterFall();
+            }
+        }
+    },
+    methods: {
+        // 瀑布流排序
+        waterFall() {
+            const i = this.mark;
+            if (i == 0) {
+                // 初始化,从左边开始插入
+                this.leftList.push(this.allList[i]);
+                // 更新左边列表高度
+                this.getViewHeight(0);
+            } else if (i == 1) {
+                // 第二个item插入,默认为右边插入
+                this.rightList.push(this.allList[i]);
+                // 更新右边列表高度
+                this.getViewHeight(1);
+            } else {
+                // 根据左右列表高度判断下一个item应该插入哪边
+                if(!this.boxHeight.length){
+                	this.rightList.length < this.leftList.length 
+                	? this.rightList.push(this.allList[i])
+                	: this.leftList.push(this.allList[i]);
+                } else {
+                	const leftOrRight = this.boxHeight[0] > this.boxHeight[1] ? 1 : 0;
+                	if (leftOrRight) {
+                	    this.rightList.push(this.allList[i])
+                	} else {
+                	    this.leftList.push(this.allList[i])
+                	}
+                }
+				// 更新插入列表高度
+				this.getViewHeight();
+            }
+        },
+        // 获取列表高度
+        getViewHeight() {
+            // 使用nextTick,确保页面更新结束后,再请求高度
+            this.$nextTick(() => {
+            	setTimeout(()=>{
+            		uni.createSelectorQuery().in(this).select('#right').boundingClientRect(res => {
+            				res ? this.boxHeight[1] = res.height : '';
+            			uni.createSelectorQuery().in(this).select('#left').boundingClientRect(res => {
+            				res ? this.boxHeight[0] = res.height : '';	
+            				this.mark = this.mark + 1;				
+            			}).exec();
+            		}).exec();
+            	},100)               
+            })
+        },
+        // item点击
+        itemTap(item) {
+            this.$emit('itemTap', item)
+        },
+		// item点击
+		goShop(item) {
+		    this.$emit('goShop', item)
+		}
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+$page-padding: 10px;
+$grid-gap: 10px;
+
+.wf-page {
+    display: grid;
+    grid-template-columns: 1fr 1fr;
+    grid-gap: $grid-gap;
+}
+.wf-item {
+    width: calc((100vw - 2 * #{$page-padding} - #{$grid-gap}) / 2);
+    padding-bottom: $grid-gap;
+}
+.wf-page1 .wf-item{
+	margin-top: 20rpx;
+	background-color: #fff;
+	border-radius: 20rpx;
+	padding-bottom: 0;
+}
+.wf-item-page{
+	padding-bottom: 20rpx;
+}
+</style>

+ 358 - 0
components/WaterfallsFlowItem/WaterfallsFlowItem.vue

@@ -0,0 +1,358 @@
+<template>
+    <view v-if="type == 0" class="wf-item-page wf-page0" :style="viewColor">
+		<view class='pictrue'>
+			<easy-loadimage mode="widthFix" :image-src="item.image"></easy-loadimage>
+			<view v-if="item.stock == 0" class="sell_out">已售罄</view>
+			<view v-if="item.border_pic" :style="{ backgroundImage: `url(${item.border_pic})` }" class="border-picture"></view>
+		</view> 
+		<view class="text">
+			<view class='name line2'>{{item.store_name}}</view>			
+			<view class="acea-row row-middle">
+				<view class='money'>¥<text class='num'>{{item.price}}</text></view>				
+			</view>
+			<view v-if="item.show_svip_info && item.show_svip_info.show_svip_price && item.svip_price" class="acea-row row-middle svip">
+				<text class='vip-money'>¥{{item.svip_price}}</text>
+				<view class="vipImg">
+					<image :src="`${domain}/static/images/svip.png`"></image>
+				</view>
+			</view>
+			<view class="item_tags">
+				<text v-if="item.product_type == 0 && item.merchant.type_name" class="font-bg-red b-color">{{item.merchant.type_name}}</text>
+				<text v-else-if="item.product_type == 0 && item.merchant.is_trader" class="font-bg-red b-color">自营</text>
+				<text v-if="item.product_type != 0" :class="'font_bg-red type'+item.product_type">{{item.product_type == 1 ? "秒杀" : item.product_type == 2 ? "预售" : item.product_type == 3 ? "助力" : item.product_type == 4 ? "拼团" : ""}}</text>
+				<text class="tags_item ticket" v-if="item.issetCoupon">领券</text>
+				<text class="tags_item delivery" v-if="item.delivery_free == 1">包邮</text>
+			</view>
+		</view>
+    </view>
+	<view v-else-if="type == 1" class="wf-page1" :style="viewColor">
+		<view class='pictrue'>
+			<easy-loadimage mode="widthFix" :image-src="item.image"></easy-loadimage>
+			<view v-if="item.stock == 0" class="sell_out">已售罄</view>
+			<view v-if="item.border_pic" :style="{ backgroundImage: `url(${item.border_pic})` }" class="border-picture"></view>
+		</view>
+		<view class='text'>
+			<view class='name line2'>{{item.store_name}}</view>
+			<view class='money'>
+				¥<text class='num'>{{item.price}}</text>	
+			</view>
+			<view v-if="item.show_svip_info&&item.show_svip_info.show_svip && item.show_svip_info.show_svip_price" class="acea-row row-middle svip">
+				<text class='vip-money'>¥{{item.svip_price}}</text>
+				<view class="vipImg">
+					<image :src="`${domain}/static/images/svip.png`"></image>
+				</view>
+			</view>
+			<view class="item_tags acea-row">
+				<text v-if="item.merchant.type_name && item.product_type == 0" class="font-bg-red b-color">{{item.merchant.type_name}}</text>
+				<text v-else-if="item.merchant.is_trader && item.product_type == 0" class="font-bg-red b-color">自营</text>
+				<text v-if="item.product_type != 0" :class="'font_bg-red type'+item.product_type">{{item.product_type == 1 ? "秒杀" : item.product_type == 2 ? "预售" : item.product_type == 3 ? "助力" : item.product_type == 4 ? "拼团" : ""}}</text>
+				<text class="tags_item ticket" v-if="item.issetCoupon">领券</text>
+				<text class="tags_item delivery" v-if="item.delivery_free == 1">包邮</text>
+			</view>
+			<view class="score">{{item.rate}}评分 {{item.reply_count}}条评论</view>
+			<view class="company" v-if="item.merchant" @click.stop="goShop(item.merchant.mer_id)">
+				<text class="line1">{{item.merchant.mer_name}}</text>
+				<view class="flex" v-if="isStore != '1'">
+					进店
+					<text class="iconfont icon-xiangyou"></text>
+				</view>
+			</view>
+		</view>
+		<!-- 返佣 -->
+		<block v-if="item.max_extension>0 && (item.product_type == 0 || item.product_type == 2)">
+			<view class="foot-bar">
+				<text class="iconfont icon-fenxiang"></text>
+				最高赚 ¥{{item.max_extension}}
+			</view>
+		</block>
+	</view>	
+</template>
+
+<script>
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+import easyLoadimage from '@/components/easy-loadimage/easy-loadimage.vue'
+import {mapGetters} from "vuex";
+import { HTTP_REQUEST_URL } from '@/config/app';
+export default {
+	components:{easyLoadimage},
+	computed: mapGetters(['viewColor']),
+    props: {
+			item: {
+				type: Object,
+				require: true
+			},
+		type: {
+			type: Number,
+			default: 0
+		},
+		isStore: {
+			type: [String, Number],
+			default: '1'
+		},
+		isLogin: {
+			type: Boolean,
+			require: false
+		}
+  },
+	data(){
+		return {
+			domain: HTTP_REQUEST_URL,
+		}
+	},
+	methods: {
+		goShop(id) {
+			this.$emit('goShop', id);
+		},
+		authOpen(){
+			this.$emit('authOpen');
+		},
+		followToggle(item){
+			this.$emit('followToggle', item);
+		}
+	}	
+}
+
+</script>
+<style lang="scss" scoped>
+.wf-item-page {
+	background: #fff;
+	overflow: hidden;
+	border-radius: 16rpx;
+	padding-bottom: 20rpx;
+}
+.wf-page0 .coupon{
+	background:rgba(255,248,247,1);
+	border:1px solid rgba(233,51,35,1);
+	border-radius:4rpx;
+	font-size:20rpx;
+	margin-left: 18rpx;
+	padding: 1rpx 4rpx;
+}
+.wf-page0 .pictrue{
+	width: 100%!important;
+	height: 345rpx;
+	position: relative;
+	/deep/image,/deep/.easy-loadimage,uni-image{
+		height: 345rpx;
+		border-radius: 16rpx 16rpx 0 0;
+	}
+	.border-picture {
+		position: absolute;
+		top: 0;
+		left: 0;
+		width: 100%;
+		height: 100%;
+		border-radius: 16rpx 16rpx 0 0;
+		background: center/cover no-repeat;
+	}
+}
+ .loadfail-img{
+		width: 100%;
+    height: 360rpx;
+}
+.svip{
+	margin: 5rpx 0 15rpx;
+}
+.vip-money {
+	color: #282828;
+	font-size: 22rpx;
+	margin-left: 6rpx;
+	font-weight: bold;
+}
+.vipImg {
+	width: 65rpx;
+	height: 28rpx;
+	margin-left: 4rpx;
+	image {
+		width: 100%;
+		height: 100%;
+		display: block;
+	}
+}
+.wf-page0 .name {
+	color: #282828;
+	margin: 20rpx 0 10rpx 0;
+	font-size: 13px;
+	overflow:hidden; 
+	text-overflow: ellipsis; 
+	display: -webkit-box; 
+	-webkit-line-clamp: 2; 
+	-webkit-box-orient: vertical;
+	text-align: left;
+}
+.wf-page0 .text{
+	padding: 0 20rpx;
+}	
+.wf-page0 .money {
+	font-size: 20rpx;
+	font-weight: bold;
+	color: var(--view-priceColor);
+}
+.b-color {
+	background-color: var(--view-theme);
+	border: 1px solid var(--view-theme);
+}
+.wf-page0 .money .num {
+	font-size: 34rpx;
+}
+.wf-page1 .wf-item{
+	.name{
+		font-size: 13px;
+		overflow:hidden; 
+		text-overflow: ellipsis; 
+		display: -webkit-box; 
+		-webkit-line-clamp: 2; 
+		-webkit-box-orient: vertical;
+		text-align: left;
+	}
+}
+.wf-page1 .pictrue {
+	position: relative;
+	height: 345rpx;
+	width: 100%!important;
+	/deep/image,/deep/.easy-loadimage,uni-image{
+		height: 345rpx;
+		border-radius: 20rpx 20rpx 0 0;
+	}
+	.border-picture {
+		position: absolute;
+		top: 0;
+		left: 0;
+		width: 100%;
+		height: 100%;
+		border-radius: 20rpx 20rpx 0 0;
+		background: center/cover no-repeat;
+	}
+	
+}
+.sell_out {
+	display: flex;
+	width: 150rpx;
+	height: 150rpx;
+	align-items: center;
+	justify-content: center;
+	border-radius: 100%;
+	background: rgba(0,0,0,.6);
+	color: #fff;
+	font-size: 30rpx;
+	position: absolute;
+	top: 50%;
+	left: 50%;
+	margin: -75rpx 0 0 -75rpx;
+	&::before{
+		content: "";
+		display: block;
+		width: 140rpx;
+		height: 140rpx;
+		border-radius: 100%;
+		border: 1px dashed #fff;
+		position: absolute;
+		top: 5rpx;
+		left: 5rpx;
+	}
+}
+.loading-img{
+	height: 345rpx;
+  max-height: 360rpx;
+}
+.wf-page1 .text {
+	padding: 20rpx 17rpx 26rpx 17rpx;
+	font-size: 30rpx;
+	color: #222;
+}
+.wf-page1 .text .money {
+	display: flex;
+	align-items: center;
+	font-size: 26rpx;
+	font-weight: bold;
+	margin-top: 8rpx;
+	color: var(--view-priceColor);
+}
+.wf-page1 .text .money .num {
+	font-size: 34rpx;
+}
+.item_tags{
+	margin-top: 8rpx;
+	display: flex;
+}
+.item_tags .tags_item {
+	display: flex;
+	font-size: 20rpx;
+	text-align: center;
+	border-radius: 5rpx;
+	padding: 0 4rpx;
+	height: 28rpx;
+	align-items: center;
+	justify-content: center;
+	margin-right: 8rpx;
+}
+.item_tags .tags_item.ticket{
+	color: var(--view-theme);
+	border: 1px solid var(--view-theme);
+}
+.item_tags .tags_item.delivery{
+	color: #FF9000;
+	border: 1px solid #FF9000;
+}
+.wf-page1 .text .money .ticket-big {
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	max-width: 163rpx;
+	padding: 0 6rpx;
+	height: 28rpx;
+	margin-left: 10rpx;
+	background-image: url(~static/images/yh.png);
+	background-size: 100% 100%;
+	font-size: 20rpx;
+	font-weight: normal;
+}
+.wf-page1 .text .score {
+	margin-top: 10rpx;
+	color: #737373;
+	font-size: 20rpx;
+}
+.wf-page1 .text .company {
+	display: flex;
+	align-items: center;
+	color: #737373;
+	font-size: 20rpx;
+	margin-top: 10rpx;
+	.line1{
+		max-width: 200rpx;
+	}
+	.flex {
+		display: flex;
+		align-items: center;
+		margin-left: 10rpx;
+		color: #282828;
+		.iconfont {
+			font-size: 16rpx;
+			margin-top: 4rpx;
+		}
+	}
+}
+.foot-bar {
+	width: 100%;
+	height: 52rpx;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	background-image: linear-gradient(-90deg, var(--view-bntColor21) 0%, var(--view-bntColor22) 100%);
+	border-radius: 0px 0px 16rpx 16rpx;
+	color: #fff;
+	font-size: 24rpx;
+	.icon-fenxiang {
+		font-size: 24rpx;
+		margin-right: 10rpx;
+	}
+}
+</style>

+ 181 - 0
components/adc/index.vue

@@ -0,0 +1,181 @@
+<template>
+	<view>
+		<view class="address-window" :class="address.address==true?'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":""'></text>
+					<view class='address'>
+						<view class='name' :class='active==index?"font-color":""'>{{item.real_name}}<text class='phone'>{{item.phone}}</text></view>
+						<view class='line1'>{{item.province}}{{item.city}}{{item.district}}{{item.detail}}</view>
+					</view>
+					<text class='iconfont icon-complete' :class='active==index?"font-color":""'></text>
+				</view>
+			</view>
+			<!-- 无地址 -->
+			<view class='pictrue' v-if="!is_loading && !addressList.length">
+				<image :src="`${domain}/static/images/noAddress.png`"></image>
+			</view>
+			<view class='addressBnt bg-color' @tap='goAddressPages'>选择其地址</view>
+		</view>
+		<view class='mask' catchtouchmove="true" :hidden='address.address==false' @tap='close'></view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { getAddressList } from '@/api/user.js';
+	import { HTTP_REQUEST_URL } from '@/config/app';
+	import { toLogin } from '@/libs/login.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 {
+				domain: HTTP_REQUEST_URL,
+				active: 0,
+				//地址列表
+				addressList: [],
+				is_loading: true
+			};
+		},
+		methods: {
+			tapAddress: function(e, addressid) {
+				this.active = e;
+				this.$emit('OnChangeAddress', addressid);
+			},
+			close: function() {
+				this.$emit('changeClose');
+				this.$emit('changeTextareaStatus');
+			},
+			goAddressPages: function() {
+				this.$emit('changeClose');
+				this.$emit('changeTextareaStatus');
+				uni.navigateTo({
+					url: this.pagesUrl
+				});
+			},
+			getAddressList: function() {
+				let that = this;
+				getAddressList({
+					page: 1,
+					limit: 5
+				}).then(res => {
+					let addressList = res.data;
+					//处理默认选中项
+					for (let i = 0, leng = addressList.length; i < leng; i++) {
+						if (addressList[i].id == that.address.addressId) {
+							that.active = i;
+						}
+					}
+					that.$set(that, 'addressList', addressList);
+					that.is_loading = false;
+				})
+			}
+		}
+	}
+</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>

+ 605 - 0
components/addInvoicing/index.vue

@@ -0,0 +1,605 @@
+<template>
+	<view :style="viewColor">
+		<view class="add_invoicing" :class="invoice.invoice==true?'on':''">
+			<view class='title'>选择发票<text class='iconfont icon-guanbi' @tap='close'></text></view>
+			<form @submit="formSubmit" report-submit="true">
+				<view class="panel">
+					<view v-if="receipt_title_type == '1'" class="acea-row row-middle">
+						<view>发票类型</view>
+						<input name="receipt_type" :value="typeName" disabled="true" />
+					</view>
+					<view v-if="receipt_title_type == '2'" class="acea-row row-middle">
+						<view>发票类型</view>
+						<input name="receipt_type" :value="typeName" disabled="true" />
+						<text class="iconfont icon-xiangyou" @click.stop="callType"></text>
+					</view>
+					<view class="acea-row row-middle">
+						<view>抬头类型</view>
+						<radio-group name="receipt_title_type" @change="changeHeader">
+							<label>
+								<radio value="1" :checked="receipt_title_type == '1' ? true : false" /><text>个人</text>
+							</label>
+							<label>
+								<radio value="2" :checked="receipt_title_type == '2' ? true : false" /><text>企业</text>
+							</label>
+						</radio-group>
+					</view>
+					<view class="acea-row row-middle">
+						<view>发票抬头</view>
+						<input name="receipt_title" :value="receipt_title" :maxlength="20" placeholder="需要开具发票的企业名称"/>
+						<text class="iconfont icon-xiangyou" @click="callTitle" ></text>
+					</view>
+					<view v-show="receipt_title_type == '2'" class="acea-row row-middle">
+						<view>税号</view>
+						<input name="duty_paragraph" :value="duty_paragraph"  placeholder="纳税人识别号" />
+					</view>
+					<view class="acea-row row-middle">
+						<view>邮箱</view>
+						<input name="email" :value="email"  placeholder="您的联系邮箱" />
+					</view>
+				</view>
+				<view v-show="receipt_title_type == '2' && receipt_type == '2'" class="panel">
+					<view class="acea-row row-middle" style="border-top: 1px solid #EEEEEE;">
+						<view>开户银行</view>
+						<input name="bank_name" :value="bank_name"  placeholder="您的开户银行" />
+					</view>
+					<view class="acea-row row-middle">
+						<view>银行账号</view>
+						<input name="bank_code" :value="bank_code"  placeholder="您的银行账号" />
+					</view>
+					<view class="acea-row row-middle">
+						<view>企业地址</view>
+						<input name="address" :value="address"  placeholder="您所在的企业地址" />
+					</view>
+					<view class="acea-row row-middle">
+						<view>企业电话</view>
+						<input name="tel" :value="tel"  placeholder="您的企业电话" />
+					</view>
+				</view>
+				<view class="btn-wrap">
+					<button class="button" form-type="submit">提交申请</button>
+					<button class="back" @tap="noInvoice">不开发票</button>
+				</view>
+			</form>
+			<view :class="{ mask: popupType || popupTitle }"></view>
+			<view class="popup" :class="{ on: popupType }">
+				<view class="title">发票类型选择<text class="iconfont icon-guanbi" @click="closeType"></text></view>
+				<scroll-view scroll-y="true">
+					<radio-group name="invoice-type" @change="changeType">
+						<label v-for="item in invoiceTypeList" :key="item.type" class="acea-row row-middle">
+							<view class="text">
+								<view>{{ item.name }}</view>
+								<view class="info">{{ item.info }}</view>
+							</view>
+							<radio :value="item.type" :checked="receipt_type == item.type ? true : false" />
+						</label>
+					</radio-group>
+				</scroll-view>
+				<button @tap="closeType">确定</button>
+			</view>
+			<view class="popup" :class="{ on: popupTitle }">
+				<view class="title">抬头选择<text class="iconfont icon-guanbi" @click="closeTitle"></text></view>
+				<scroll-view v-if="invoiceList.length > 0" scroll-y="true">
+					<radio-group name="invoice-title" @change="changeTitle">
+						<template v-for="item in invoiceList">
+							<label :key="item.user_receipt_id" class="acea-row row-middle">
+								<view class="text">
+									<view class="acea-row row-middle">
+										<view class="name">{{ item.receipt_title }}</view>
+										<view v-if="item.is_default" class="label">默认</view>
+									</view>
+									<view class="type" :class="{ special: item.receipt_type == '2'}">{{ item.receipt_type == 1 ? '普通发票' : '专用发票'}}</view>
+								</view>
+								<radio :value="item.user_receipt_id" :checked="item.user_receipt_id == invoice_id ? true : false" />
+							</label>
+						</template>
+					</radio-group>
+				</scroll-view>
+				<view v-else class="nothing">
+					<image :src="`${domain}/static/images/noInvoice.png`"></image>
+					<view class="nothing_text">您还没有添加发票信息哟~</view>
+				</view>
+				<button v-if="invoice.add" @tap="addTitle">添加新的抬头</button>
+				<button v-else @tap="close" class="btn-default">不开发票</button>
+			</view>			
+		</view>		
+		<view class='mask' catchtouchmove="true" :hidden='invoice.invoice==false' @tap='close'></view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { invoiceDefault, invoice, invoiceDetail } from '@/api/user.js';
+	import { mapGetters } from "vuex";
+	import { HTTP_REQUEST_URL } from '@/config/app';
+	export default {
+		props: {			
+			invoice: {
+				type: Object,
+				default: function() {
+					return {
+						invoice: false,
+						mer_id: 0,
+					};
+				}
+			}			
+		},
+		computed: { ...mapGetters(['viewColor'])},
+		data() {
+			return {
+				domain: HTTP_REQUEST_URL,
+				id: '',
+				receipt_title_type: '1',
+				receipt_type: '1',
+				drawer_phone: '',
+				receipt_title: '',
+				duty_paragraph: '',
+				tel: '',
+				address: '',
+				bank_name: '',
+				bank_code: '',
+				is_default: 0,
+				email: '',
+				isDefault: [],
+				typeName: '增值税电子普通发票',
+				popupType: false,
+				popupTitle: false,
+				invoiceTypeList: [{
+						type: '1',
+						name: '增值税电子普通发票',
+						info: '默认发送至所提供的电子邮件'
+					},
+					{
+						type: '2',
+						name: '增值税专用发票',
+						info: '纸质发票开出后将以邮寄形式交付'
+					}
+				],
+				special_invoice: true,
+				invoice_func: true,
+				invoiceList: [],
+				invoice_checked: '',
+				invoice_id: '',
+				order_id: '',
+				news: '',
+				cartId: '',
+				pinkId: '',
+				couponId: '',
+				addressId: '',
+				invoiceData: {},
+				formvalidate: false
+			}
+		},
+		watch: {
+		},
+		onLoad(option) {
+			this.news = option.news;
+			this.cartId = option.cartId;
+			this.pinkId = option.pinkId;
+			this.couponId = option.couponId;
+			this.addressId = option.addressId;
+			if (option.special_invoice == 'false') {
+				this.$set(this, 'special_invoice', false);
+			}
+		},
+		onShow(){
+			this.getInvoiceDefault();
+			this.popupTitle = false;
+		},
+		methods: {
+			getInvoiceList() {	
+				let params = {
+					// receipt_title_type: this.receipt_title_type,
+					// receipt_type: this.receipt_type
+				}
+				invoice().then(res => {
+					for (let i = 0; i < res.data.length; i++) {
+						res.data[i].user_receipt_id = res.data[i].user_receipt_id.toString();
+						if (res.data[i].is_default) {
+							this.invoice_id = res.data[i].user_receipt_id;
+						}
+					}
+					this.$set(this, 'invoiceList', res.data);
+				}).catch(err => {
+					this.$util.Tips({
+						title: err
+					});
+				});
+			},
+			getInvoiceDefault() {
+				let params = {
+					is_default: 1,
+					// receipt_title_type: this.receipt_title_type,
+					// receipt_type: this.receipt_type
+				}
+				invoice(params).then(res => {
+					let data = res.data[0];
+					this.typeName = data.receipt_type == '1' ? '增值税电子普通发票' : '增值税专用发票'
+					this.receipt_title_type = data.receipt_title_type;
+					this.receipt_type = data.receipt_type ;
+					this.receipt_title = data.receipt_title;
+					// this.drawer_phone = res.data.drawer_phone;
+					this.email = data.email;
+					this.duty_paragraph = data.duty_paragraph;
+					this.bank_name = data.bank_name;
+					this.bank_code = data.bank_code;
+					this.address = data.address;
+					this.tel = data.tel;
+					this.invoice_id = data.user_receipt_id.toString();
+					this.popupTitle = false;
+				}).catch(err => {});
+			},
+			getInvoiceDetail(id){
+				invoiceDetail(id).then(res => {
+					uni.hideLoading();
+					this.receipt_title_type = res.data.receipt_title_type;
+					this.receipt_type = res.data.receipt_type;
+					this.typeName = this.receipt_type == '1' ? '增值税电子普通发票' : '增值税专用发票'
+					this.receipt_title = res.data.receipt_title;
+					this.email = res.data.email;
+					this.duty_paragraph = res.data.duty_paragraph;
+					this.bank_name = res.data.bank_name;
+					this.bank_code = res.data.bank_code;
+					this.address = res.data.address;
+					this.tel = res.data.tel;
+					this.is_default = res.data.is_default;
+				}).catch(err => {
+					uni.hideLoading();
+					this.$util.Tips({
+						title: err
+					});
+				});
+			},
+			close: function() {
+				if(this.formvalidate){
+					this.$emit('changeInvoiceClose',this.invoiceData)
+				}else{
+					this.$emit('changeInvoiceClose','');
+				}	
+			},
+			noInvoice: function(){
+				uni.setStorage({
+					key:'invoice_Data',
+					data:{},
+					success: function(){}
+				})
+				this.$emit('changeInvoiceClose','');
+			},
+			callType() {
+				this.popupType = true;
+			},
+			changeType(e) {
+				this.receipt_type = e.detail.value;
+				this.typeName = this.invoiceTypeList.find(value => {
+					return value.type == this.receipt_type;
+				}).name;			
+			},
+			closeType() {
+				this.popupType = false;
+			},
+			callTitle() {
+				this.popupTitle = true;
+			},
+			changeTitle(e) {
+				this.invoice_id = e.detail.value.toString();
+				this.getInvoiceDetail(e.detail.value)
+				this.popupTitle = false;
+			},
+			addTitle() {
+				this.popupType = false;	
+				this.popupTitle = false;
+				uni.navigateTo({
+					url: '/pages/users/user_invoice_form/index?mer_id='+this.invoice.mer_id
+				});				
+			},
+			closeTitle() {
+				this.popupTitle = false;
+			},
+			changeHeader(e) {
+				this.receipt_title_type = e.detail.value;
+				if(e.detail.value == 1){
+					this.receipt_type = 1;
+					this.typeName = '增值税电子普通发票'
+				}
+				this.receipt_type
+			},
+			changeDefault(e) {
+				this.is_default = e.detail.value.length ? 1 : 0;
+			},
+			// 提交发票数据
+			formSubmit(e) {
+				let that = this,
+					value = e.detail.value;
+				if (!value.receipt_title_type) return that.$util.Tips({
+					title: '请填写发票抬头'
+				});
+				if (!value.email) return that.$util.Tips({
+					title: '请填写邮箱'
+				});	
+				if (!/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/.test(value.email)) return that.$util.Tips({
+					title: '请输入正确的邮箱'
+				});			
+				if(value.receipt_title_type == 2){
+					if (!value.duty_paragraph) return that.$util.Tips({
+						title: '请填写税号'
+					});
+					if(value.receipt_type == '增值税专用发票'){
+						if (!value.bank_name) return that.$util.Tips({
+							title: '请填写开户行'
+						});						
+						if (!value.bank_code) return that.$util.Tips({
+							title: '请填写银行账号'
+						});
+						if (!value.address) return that.$util.Tips({
+							title: '请填写企业地址'
+						});
+						if (!value.tel) return that.$util.Tips({
+							title: '请填写企业电话'
+						});						
+						if(!/^(\d{9}|\d{14}|\d{18})$/.test(value.bank_code)){
+							return this.$util.Tips({
+								title: '请输入正确的银行账号'
+							});
+						}						
+						if(!/(^(\d{3,4})?\d{7,8})$|(13[0-9]{9})/.test(value.tel)){
+							return this.$util.Tips({
+								title: '请输入正确的电话号码'
+							});
+						}
+					}
+				}
+				that.formvalidate = true;
+				value.mer_id = that.invoice.mer_id;
+				value.receipt_type = that.receipt_type;
+				that.invoiceData = value
+				uni.setStorage({
+					key:'invoice_Data',
+					data: [that.invoiceData],
+					success: function(){
+					}
+				})
+				that.$emit('changeInvoiceClose',that.invoiceData)	
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.add_invoicing{
+		position: fixed;
+		bottom: 0;
+		width: 100%;
+		left: 0;
+		background-color: #fff;
+		z-index: 77;
+		border-radius: 16rpx 16rpx 0 0;
+		padding-bottom: 20rpx;
+		transform: translate3d(0, 100%, 0);
+		transition: all .3s cubic-bezier(.25, .5, .5, .9);
+	}	
+	.add_invoicing.on {
+		transform: translate3d(0, 0, 0);
+	}
+	.add_invoicing .title {
+		font-size: 32rpx;
+		font-weight: bold;
+		text-align: center;
+		height: 123rpx;
+		line-height: 123rpx;
+		position: relative;
+	}
+	.add_invoicing .title .iconfont {
+		position: absolute;
+		right: 30rpx;
+		color: #8a8a8a;
+		font-size: 35rpx;
+	}
+	form {
+		font-size: 28rpx;
+		color: #282828;
+	}
+	form input, form radio-group {
+		flex: 1;
+		text-align: right;
+	}
+	form input {
+		font-size: 26rpx;
+	}
+	form label {
+		margin-right: 50rpx;
+	}
+	form radio {
+		margin-right: 8rpx;
+	}
+	form checkbox-group {
+		height: 90rpx;
+	}
+	form checkbox {
+		margin-right: 20rpx;
+	}
+	/deep/radio .uni-radio-input.uni-radio-input-checked,
+	/deep/radio .wx-radio-input.wx-radio-input-checked{
+	  border: 1px solid var(--view-theme) !important;
+	  background-color: var(--view-theme) !important
+	}
+	form button {
+		height: 76rpx;
+		border-radius: 38rpx;
+		margin: 16rpx 30rpx;
+		background-color: var(--view-theme);
+		font-size: 30rpx;
+		line-height: 76rpx;
+		color: #FFFFFF;
+	}
+	.panel {
+		padding-right: 30rpx;
+		padding-left: 30rpx;
+		background-color: #FFFFFF;
+	}
+	.panel~.panel {
+		margin-top: 14rpx;
+	}
+	.panel .acea-row {
+		height: 90rpx;
+	}
+	.panel .acea-row~.acea-row {
+		border-top: 1px solid #EEEEEE;
+	}
+	.input-placeholder {
+		font-size: 26rpx;
+		color: #BBBBBB;
+	}
+	.icon-xiangyou {
+		margin-left: 25rpx;
+		font-size: 18rpx;
+		color: #BFBFBF;
+		cursor: pointer;
+	}
+	.btn-wrap {
+		width: 100%;
+		border-top: 1px solid #F5F5F5;
+	}
+	.btn-wrap .button{
+		height: 86rpx;
+		line-height: 86rpx;
+		border-radius: 50rpx;
+	}
+	.btn-wrap .back {
+		border-radius: 50rpx;
+		height: 86rpx;
+		line-height: 86rpx;
+		border: 1rpx solid var(--view-theme);
+		background: none;
+		color: var(--view-theme);
+	}
+	.popup {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		z-index: 99;
+		width: 100%;
+		padding-bottom: 30rpx;
+		border-top-left-radius: 16rpx;
+		border-top-right-radius: 16rpx;
+		background-color: #F5F5F5;
+		overflow: hidden;
+		transform: translateY(100%);
+		transition: 0.3s;
+	}
+	.popup.on {
+		transform: translateY(0);
+	}
+	.popup .title {
+		position: relative;
+		height: 137rpx;
+		font-size: 32rpx;
+		line-height: 137rpx;
+		text-align: center;
+	}
+	.popup scroll-view {
+		height: 466rpx;
+		padding-right: 30rpx;
+		padding-left: 30rpx;
+		box-sizing: border-box;
+	}
+	.popup label {
+		padding: 35rpx 30rpx;
+		border-radius: 16rpx;
+		margin-bottom: 20rpx;
+		background-color: #FFFFFF;
+	}
+	.popup .text {
+		flex: 1;
+		min-width: 0;
+		font-size: 28rpx;
+		color: #282828;
+	}
+	.popup .info {
+		margin-top: 10rpx;
+		font-size: 22rpx;
+		color: #909090;
+	}
+	.popup .icon-guanbi {
+		position: absolute;
+		top: 50%;
+		right: 30rpx;
+		z-index: 2;
+		transform: translateY(-50%);
+		font-size: 30rpx;
+		color: #707070;
+		cursor: pointer;
+	}
+	.popup button {
+		height: 86rpx;
+		border-radius: 43rpx;
+		margin-right: 30rpx;
+		margin-left: 30rpx;
+		background-color: var(--view-theme);
+		font-size: 30rpx;
+		line-height: 86rpx;
+		color: #FFFFFF;
+	}
+	button{
+		margin-top: 30rpx;
+	}
+	button.btn-default{
+		background-color: transparent;
+		color: var(--view-theme);
+		border: 1px solid var(--view-theme);
+	}
+	.popup .text .acea-row {
+		display: inline-flex;
+		max-width: 100%;
+	}
+	.popup .name {
+		flex: 1;
+		overflow: hidden;
+		white-space: nowrap;
+		text-overflow: ellipsis;
+		font-size: 30rpx;
+	}
+	.popup .label {
+		width: 70rpx;
+		height: 28rpx;
+		border: 1px solid #E93323;
+		margin-left: 18rpx;
+		font-size: 16rpx;
+		line-height: 26rpx;
+		text-align: center;
+		color: #E93323;
+	}
+	.popup .type {
+		width: 124rpx;
+		height: 42rpx;
+		margin-top: 14rpx;
+		background-color: #FCF0E0;
+		font-size: 24rpx;
+		line-height: 42rpx;
+		text-align: center;
+		color: #D67300;
+	}
+	.popup .type.special {
+		background-color: #FDE9E7;
+		color: #E93323;
+	}
+	.nothing {
+		margin: 50rpx 0;
+		text-align: center;	
+		image, uni-image{
+			width: 400rpx;
+			height: 260rpx;
+		}
+	}
+	.nothing_text{
+		margin-top: 20rpx;
+		color: #999999;
+	}
+</style>

+ 346 - 0
components/addcartWindow/index.vue

@@ -0,0 +1,346 @@
+<template>
+	<view :style="viewColor">
+		<view class="product-window" :class="(attr.cartAttr === true ? 'on' : '') + ' ' + (iSbnt || destri?'join':'') + ' ' + (isPresell ?'presell-window':'product-window')">
+			<view class="textpic acea-row row-between-wrapper">
+				<view class="pictrue">
+					<image :src="(attr.productSelect&&attr.productSelect.image) ? attr.productSelect.image : ''" @click="loookImg"></image>
+				</view>
+				<view class="text">
+					<view class="line1">
+						{{ attr.productSelect.store_name }}
+					</view>
+					<view v-if="isPresell" class="money presell_price">
+						<view>
+							预售价
+							¥<text class="num">{{ attr.productSelect.price }}</text>
+							<text v-if="presell_type === 2">定金¥<text class="num">{{ attr.productSelect.down_price }}</text></text>
+						</view>
+						<text class="stock" v-if='isShow'>库存: {{ attr.productSelect.stock }}</text>
+						<text class='stock' v-if="limitNum">限量: {{attr.productSelect.quota_show}}</text>
+					</view>
+					<view v-else class="money">
+						<view class="acea-row row-middle">
+							<view class="acea-row row-middle">
+								<text>¥</text><text class="num">{{ attr.productSelect.price }}</text>
+								<view v-if="attr.productSelect && attr.productSelect.svip_price" class="acea-row row-middle">
+									<text class='vip-money'>¥{{attr.productSelect.svip_price}}</text>
+									<view class="vipImg">
+										<image :src="`${domain}/static/images/svip.png`"></image>
+									</view>
+								</view>
+							</view>
+						</view>
+						<view class="stock_count">
+							<text class="stock" v-if='isShow'>库存: {{ attr.productSelect.stock }}</text>
+							<text class='stock' v-if="limitNum">限量: {{attr.productSelect.quota_show}}</text>
+						</view>
+					</view>
+				</view>
+				<view class="iconfont icon-guanbi" @click="closeAttr"></view>
+			</view>
+			<view class="productWinList">
+				<view class="item" v-for="(item, indexw) in attr.productAttr" :key="indexw">
+					<view class="title">{{ item.attr_name }}</view>
+					<view class="listn acea-row row-middle">	
+						<view class="itemn" :class="item.index === itemn.attr ? 'on' : ''" v-for="(itemn, indexn) in item.attr_value"
+						 @click="tapAttr(indexw, indexn)" :key="indexn">
+							{{ itemn.attr }}
+						</view>
+					</view>
+				</view>
+			</view>
+			<view class="joinBnt b-color" v-if="destri && attr.productSelect.stock>0" @click="goCat">确定</view>
+			<view class="joinBnt on" v-else-if="destri && attr.productSelect.stock<=0">已售罄</view>
+			<view class="joinBnt on" v-else-if="(iSbnt && attr.productSelect.stock<=0)">已售罄</view>
+		</view>
+		<view class="mask" @touchmove.prevent :hidden="attr.cartAttr === false" @click="closeAttr"></view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { mapGetters } from "vuex";
+	import { HTTP_REQUEST_URL } from '@/config/app';
+	export default {
+		computed: mapGetters(['viewColor']),
+		props: {
+			attr: {
+				type: Object,
+				default: () => {}
+			},
+			limitNum: {
+				type: Number,
+				value: 0
+			},
+			isShow: {
+				type: Number,
+				value: 0
+			},
+			iSbnt: {
+				type: Number,
+				value: 0
+			},
+			iSplus: {
+				type: Number,
+				value: 0
+			},
+			destri: {
+				type: Number,
+				value: 0
+			},
+			isPresell: {
+				type: Number,
+				value: 0
+			},
+			presell_type: {
+				type: Number,
+				value: 1
+			},
+			image: {
+				type: String,
+				value: ''
+			}
+		},
+		data() {
+			return {
+				domain: HTTP_REQUEST_URL,
+			}
+		},
+		mounted(){
+		},
+		methods: {
+			//  查看大图
+			loookImg(){
+				let self = this
+				let arr = [self.attr.productSelect.image ? self.attr.productSelect.image : self.image]
+				uni.previewImage({
+					urls: arr,
+				});
+			},
+			goCat: function() {
+				this.$emit('goCat');
+			},
+			closeAttr: function() {
+				this.$emit('myevent');
+			},
+			tapAttr: function(indexw, indexn) {
+				let that = this;
+				that.$emit("attrVal", {
+					indexw: indexw,
+					indexn: indexn
+				});
+				this.$set(this.attr.productAttr[indexw], 'index', this.attr.productAttr[indexw].attr_values[indexn]);
+				let value = that.getCheckedValue().join(",");
+				that.$emit("ChangeAttr", value);
+			},
+			//获取被选中属性;
+			getCheckedValue: function() {
+				let productAttr = this.attr.productAttr;
+				let value = [];
+				for (let i = 0; i < productAttr.length; i++) {
+					for (let j = 0; j < productAttr[i].attr_values.length; j++) {
+						if (productAttr[i].index === productAttr[i].attr_values[j]) {
+							value.push(productAttr[i].attr_values[j]);
+						}
+					}
+				}
+				return value;
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.product-window {
+		position: fixed;
+		bottom: 0;
+		width: 100%;
+		left: 0;
+		background-color: #fff;
+		z-index: 10000;
+		border-radius: 16rpx 16rpx 0 0;
+		padding-bottom: 140rpx;
+		padding-bottom: calc(140rpx+ constant(safe-area-inset-bottom)); ///兼容 IOS<11.2/
+		padding-bottom: calc(140rpx + env(safe-area-inset-bottom)); ///兼容 IOS>11.2/
+		transform: translate3d(0, 100%, 0);
+		transition: all .3s cubic-bezier(.25, .5, .5, .9);
+		&.presell-window {
+			padding-bottom: 200rpx;
+		}
+	}
+	.product-window.on {
+		transform: translate3d(0, 0, 0);
+	}
+	.product-window.join {
+		padding-bottom: 30rpx;
+	}
+	.product-window .textpic {
+		padding: 0 80rpx 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: 460rpx;
+		font-size: 32rpx;
+		color: #202020;
+	}
+	.product-window .textpic .text .money {
+		font-size: 24rpx;
+		margin-top: 40rpx;
+		color: var(--view-priceColor);
+	}
+	.product-window .textpic .text .money .num {
+		font-size: 36rpx;
+	}
+	.product-window .textpic .text .money .stock {
+		color: #999;
+		margin-left: 18rpx;
+	}
+	.vip-money {
+		color: #282828;
+		font-size: 22rpx;
+		margin-left: 6rpx;
+	}
+	.vipImg {
+		width: 65rpx;
+		height: 28rpx;
+		margin-left: 4rpx;
+		image {
+			width: 100%;
+			height: 100%;
+			display: block;
+		}
+	}
+	.product-window .textpic .text .presell_price {
+		color: #FF7F00;
+		.num {
+			display: inline-block;
+			margin-right: 20rpx;
+		}
+		.stock {
+			margin-left: 0;
+		}
+	}
+	.product-window .textpic .iconfont {
+		position: absolute;
+		right: 30rpx;
+		top: -5rpx;
+		font-size: 35rpx;
+		color: #8a8a8a;
+	}
+	.product-window .productWinList {
+		max-height: 395rpx;
+		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 #bbb;
+		font-size: 26rpx;
+		color: #282828;
+		padding: 7rpx 33rpx;
+		border-radius: 6rpx;
+		margin: 14rpx 0 0 14rpx;
+	}
+	.product-window .productWinList .item .listn .itemn.on {
+		color: #fff;
+		background-color: var(--view-theme);
+		border-color: var(--view-theme);
+	}
+	.product-window .cart {
+		margin-top: 36rpx;
+		padding: 0 30rpx;
+		display: -webkit-box;
+		display: -webkit-flex;
+		display: -ms-flexbox;
+		display: flex;
+		-webkit-box-pack: justify;
+		-webkit-justify-content: space-between;
+		-ms-flex-pack: justify;
+		justify-content: space-between;
+	}
+	.product-window .cart .title {
+		font-size: 30rpx;
+		color: #999;
+		line-height: 54rpx;
+	}
+	.product-window .cart .carnum {
+		height: 54rpx;
+	}
+	.product-window .cart .carnum view {
+		border: 1px solid #a4a4a4;
+		width: 84rpx;
+		text-align: center;
+		height: 100%;
+		line-height: 54rpx;
+		color: #a4a4a4;
+		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;
+	}
+	.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 {
+		color: #282828;
+		font-size: 28rpx;
+	}
+	.product-window .joinBnt {
+		font-size: 30rpx;
+		width: 620rpx;
+		height: 86rpx;
+		border-radius: 50rpx;
+		text-align: center;
+		line-height: 86rpx;
+		color: #fff;
+		margin: 100rpx auto 0 auto;
+		&.b-color{
+			background-color: var(--view-theme);
+		}
+	}
+	.product-window .joinBnt.on {
+		background-color: #bbb;
+		color: #fff;
+		background-image: none;
+	}
+</style>

+ 190 - 0
components/addressWindow/index.vue

@@ -0,0 +1,190 @@
+<template>
+	<view :style="viewColor">
+		<view class="address-window" :class="address.address==true?'on':''">
+			<view class='title'>选择地址<text class='iconfont icon-guanbi' @tap='close'></text></view>
+			<scroll-view scroll-y="true" class='list'>
+				<view class='item acea-row row-between-wrapper' :class='active==index?"t-color":""' v-for="(item,index) in addressList"
+				 @tap='tapAddress(index,item.address_id)' :key='index'>
+					<text class='iconfont icon-ditu' :class='active==index?"t-color":""'></text>
+					<view class='address'>
+						<view class='name' :class='active==index?"t-color":""'>{{item.real_name}}<text class='phone'>{{item.phone}}</text></view>
+						<view class='line1'>{{item.province}}{{item.city}}{{item.district}}{{item.street || ''}}{{item.detail}}</view>
+					</view>
+					<text class='iconfont icon-complete' :class='active==index?"t-color":""'></text>
+				</view>
+			</scroll-view>
+			<!-- 无地址 -->
+			<view class='pictrue' v-if="!is_loading && !addressList.length">
+				<image :src="`${domain}/static/images/noAddress.png`"></image>
+				<view>暂无地址</view>
+			</view>
+			<view class='addressBnt' @tap='goAddressPages'>添加新地址</view>
+		</view>
+		<view class='mask' catchtouchmove="true" :hidden='address.address==false' @tap='close'></view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { getAddressList } from '@/api/user.js';
+	import { mapGetters } from "vuex";
+	import { HTTP_REQUEST_URL } from '@/config/app';
+	export default {
+		props: {
+			pagesUrl: {
+				type: String,
+				default: '',
+			},
+			address: {
+				type: Object,
+				default: function() {
+					return {
+						address: true,
+						addressId: 0,
+					};
+				}
+			},
+			isLog: {
+				type: Boolean,
+				default: false,
+			},
+		},
+		computed: mapGetters(['viewColor']),
+		data() {
+			return {
+				domain: HTTP_REQUEST_URL,
+				active: 0,
+				//地址列表
+				addressList: [],
+				is_loading: true
+			};
+		},
+		methods: {
+			tapAddress: function(e, addressid) {
+				this.active = e;
+				this.$emit('OnChangeAddress', addressid);
+			},
+			close: function() {
+				this.$emit('changeClose');
+				this.$emit('changeTextareaStatus');
+			},
+			goAddressPages: function() {
+				this.$emit('changeClose');
+				this.$emit('changeTextareaStatus');
+				uni.navigateTo({
+					url: this.pagesUrl
+				});
+			},
+			getAddressList: function() {
+				let that = this;
+				getAddressList({
+					page: 1,
+					limit: 5
+				}).then(res => {
+					let addressList = res.data.list;
+					//处理默认选中项
+					for (let i = 0; i < res.data.list.length; i++) {
+						if (addressList[i].address_id == that.address.addressId) {
+							that.active = i;
+						}
+					}
+					that.$set(that, 'addressList', addressList);
+					that.is_loading = false;
+				})
+			}
+		}
+	}
+</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{
+		max-height: 650rpx;
+	}
+	.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;
+		background-color: var(--view-theme);
+	}
+	.address-window .pictrue {
+		text-align: center;
+	}
+	.address-window .pictrue image,.address-window .pictrue uni-image {
+		width: 414rpx;
+		height: 305rpx;
+	}
+	.address-window .pictrue view{	
+		color: #999;
+	}
+	.t-color {
+		color: var(--view-theme)!important;
+	}
+</style>

+ 85 - 0
components/alert/index.vue

@@ -0,0 +1,85 @@
+<template>
+	<view class="alert-wrapper" :style="viewColor">
+		<view class="alert-box">
+			<image :src="domain+'/static/diy/success'+keyColor+'.png'" mode=""></image>
+			<view class="txt">{{msg}}</view>
+			<view class="btn" @click="close">鎴戠煡閬撲簡</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { mapGetters } from "vuex";
+	import { HTTP_REQUEST_URL } from '@/config/app';
+	export default{
+		data() {
+			return {
+				domain: HTTP_REQUEST_URL,
+			}
+		},
+		props:{
+			msg:{
+				type:String,
+				default:''
+			},
+		},
+		computed: mapGetters(['viewColor','keyColor']),
+		methods:{
+			close(){
+				this.$emit('bindClose');
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+.alert-wrapper{
+	position: fixed;
+	left: 0;
+	top: 0;
+	width: 100%;
+	height: 100%;
+	background-color: rgba(0,0,0,.5);
+	z-index: 10;
+	.alert-box{
+		position: absolute;
+		left: 50%;
+		top: 50%;
+		transform: translate(-50%,-50%);
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		width: 500rpx;
+		height: 540rpx;
+		background-color: #fff;
+		border-radius: 10rpx;
+		font-size: 34rpx;
+		image{
+			width: 149rpx;
+			height: 230rpx;
+		}
+		.txt{
+			margin-bottom: 20rpx;
+		}
+		.btn{
+			width:340rpx;
+			height:90rpx;
+			line-height: 90rpx;
+			text-align: center;
+			background-image:linear-gradient(-90deg,var(--view-bntColor21) 0%,var(--view-bntColor22) 100%);
+			border-radius:45rpx;
+			color: #fff;
+		}
+	}
+}
+</style>

+ 266 - 0
components/areaWindow/index.vue

@@ -0,0 +1,266 @@
+<template>
+	<view :style="viewColor">
+		<view class="address-window" :class="display==true?'on':''">
+			<view class='title'>请选择所在地区<text class='iconfont icon-guanbi' @tap='close'></text></view>
+			<view class="address-count">
+				<view class="address-selected">
+					<view v-for="(item,index) in selectedArr" :key="index" class="selected-list" :class="{active:index === selectedIndex}" @click="change(item.parent_id, index)">
+						{{item.name}}
+						<text class="iconfont icon-xiangyou"></text>
+					</view>
+					<view class="selected-list" :class="{active:-1 === selectedIndex}"  v-if="showMore" @click="change(-1, -1)">
+						<text class="iconfont icon-xiangyou"></text>
+						请选择
+					</view>
+				</view>
+				<scroll-view scroll-y="true" :scroll-top="scrollTop" class="address-list" @scroll="scroll">
+					<view v-for="(item,index) in addressList" :key="index" class="list" :class="{active:item.id === activeId}" @click="selected(item)">
+						<text class="item-name">{{item.name}}</text>
+						<text v-if="item.id === activeId" class="iconfont icon-duihao2"></text>
+					</view>
+				</scroll-view>
+			</view>
+		</view>
+		<view class='mask' catchtouchmove="true" :hidden='display==false' @tap='close'></view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import {getAddressList} from '@/api/user.js';
+	import { getCityV2 } from '@/api/api.js';
+	import { mapGetters } from "vuex";
+	const CACHE_ADDRESS = {};	
+	export default {
+		props: {
+			display: {
+				type: Boolean,
+				default: true
+			},
+			cityShow: {
+				type: Number,
+				default: 3
+			},
+			address: {
+				type:Array | Object,
+				default:[]
+			},
+		},
+		data() {
+			return {
+				active: 0,
+				//地址列表
+				addressList: [],
+				selectedArr: [],
+				selectedIndex: -1,
+				is_loading: false,
+				old: { scrollTop: 0 },
+				scrollTop: 0
+			};
+		},
+		computed:{
+			...mapGetters(['viewColor']),
+			activeId(){
+				return this.selectedIndex == -1 ? 0 : this.selectedArr[this.selectedIndex].id
+			},
+			showMore(){
+				return this.selectedArr.length ? (this.selectedArr[this.selectedArr.length - 1].hasOwnProperty('children') && ((this.cityShow==1 && this.addressList.level<2) || (this.cityShow==2 && this.addressList.level<3) || (this.cityShow==3 && this.addressList.level<4))) : true
+			}
+		},
+		watch:{
+			address(n){
+				this.selectedArr = n ? [...n] : []
+			},
+			display(n){
+				if(!n) {
+					this.addressList = [];
+					this.selectedArr =  this.address ? [...this.address] : [];
+					this.selectedIndex = -1;
+					this.is_loading = false;
+				}else{
+					this.loadAddress(0)
+				}
+			}
+		},
+		mounted() {
+			this.loadAddress(0)
+		},
+		methods: {
+			change(pid,index){
+				if(this.selectedIndex == index) return;
+				if(pid === -1){
+					pid = this.selectedArr.length ? this.selectedArr[this.selectedArr.length -1].id : 0;
+				}
+				this.selectedIndex = index;
+				this.loadAddress(pid);
+			},
+			loadAddress(pid){
+				if(CACHE_ADDRESS[pid]){
+					this.addressList = CACHE_ADDRESS[pid];
+					return ;
+				}
+				this.is_loading = true;
+				getCityV2(pid).then(res=>{
+					this.is_loading = false;
+					CACHE_ADDRESS[pid] = res.data;
+					this.addressList = res.data;
+				})
+				this.goTop()
+			},
+			selected(item){
+				if(this.is_loading) return;
+				if(this.selectedIndex > -1){
+					this.selectedArr.splice(this.selectedIndex + 1,999)
+					this.selectedArr[this.selectedIndex] = item;
+					this.selectedIndex = -1;
+				}else if(!item.parent_id){
+					this.selectedArr = [item];
+				}else{
+					this.selectedArr.push(item);
+				}
+				if(item.snum && ((this.cityShow==1 && this.addressList[0].level<2) || (this.cityShow==2 && this.addressList[0].level<3) || (this.cityShow==3 && this.addressList[0].level<4))){
+					this.loadAddress(item.id);
+				} else {
+					this.$emit('submit', [...this.selectedArr]);
+					this.$emit('changeClose');
+				}
+				this.goTop()
+			},
+			close: function() {
+				this.$emit('changeClose');
+			},
+			scroll : function(e) {
+				this.old.scrollTop = e.detail.scrollTop
+			},
+			goTop: function(e) {
+				this.scrollTop = this.old.scrollTop
+				this.$nextTick(() => {
+					this.scrollTop = 0
+				});
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.address-window {
+		background-color: #fff;
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		width: 100%;
+		z-index: 101;
+		border-radius: 30rpx 30rpx 0 0;
+		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-count{
+		.address-selected{
+			padding: 0 30rpx;
+			margin-top: 10rpx;
+			position: relative;
+			padding-bottom: 20rpx;
+			border-bottom: 2rpx solid #f7f7f7;
+		}
+		.selected-list{
+			font-size: 26rpx;
+			color: #282828;
+			line-height: 50rpx;
+			padding-bottom: 10rpx;
+			padding-left: 60rpx;
+			position: relative;
+			&.active{
+				color: #e28d54;
+			}
+			&:before,&:after{
+				content: '';
+				display: block;
+				position: absolute;			
+			}
+			&:before{
+				width: 4rpx;
+				height: 100%;
+				background-color: var(--view-theme);
+				top: 0;
+				left: 10rpx;
+			}
+			&:after{
+				width: 12rpx;
+				height: 12rpx;
+				background: var(--view-theme);
+				border-radius: 100%;
+				left: 6rpx;
+				top: 50%;
+				margin-top: -8rpx;
+			}
+			&:first-child,&:last-child{
+				&:before{
+					height: 50%;
+				}
+			}
+			&:first-child{
+				&:before{
+					top: auto;
+					bottom: 0;
+				}
+			}
+			.iconfont{
+				font-size: 20rpx;
+				float: right;
+				color: #dddddd;
+			}
+		}
+		scroll-view{
+			height: 550rpx;
+		}
+		.address-list{
+			padding: 0 30rpx;
+			margin-top: 20rpx;
+			box-sizing: border-box;
+			.list{
+				.iconfont{
+					float: right;
+					color: #ddd;
+					font-size: 22rpx;
+				}
+				.item-name{
+					display: inline-block;
+					line-height: 50rpx;
+					margin-bottom: 20rpx;
+					font-size: 26rpx;
+				}
+				&.active{
+					color: #e28d54;
+					.iconfont{
+						color: #e28d54;
+					}
+				}
+			}
+		}
+	}
+</style>

+ 319 - 0
components/bindmobile.vue

@@ -0,0 +1,319 @@
+<template>
+	<view :style="viewColor">
+		<view class="container">
+			<view class="header">
+				<text class="title">绑定手机号</text>
+				<text class="iconfont icon-guanbi5" @tap="closePopup"></text>
+			</view>
+			<view class="main_counts">
+				<form report-submit='true'>
+					<view class="ChangePassword">
+						<view class="list">
+							<view class="item">
+								<input type='number' placeholder='输入手机号码' placeholder-class='placeholder' v-model="phone"></input>
+							</view>
+							<view class="item acea-row row-between-wrapper">
+								<input type='number' placeholder='输入验证码' placeholder-class='placeholder' class="codeIput" v-model="captcha"></input>
+								<button class="code" :class="disabled === true ? 'on' : ''" :disabled='disabled' @click="handleVerify">
+									{{ text }}
+								</button>
+							</view>
+						</view>
+						<button form-type="submit" @click="editPwd" class="confirmBnt">确认绑定</button>
+						<!-- #ifdef MP -->
+						<button v-if="!isCommuity && wechat_phone_switch == 1" form-type="submit" class="getPhoneBtn" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber"><text class="iconfont icon-weixin2"></text>使用微信绑定号码</button>
+						<!-- #endif -->
+					</view>
+				</form>		
+			</view>	
+		</view>
+		<Verify @success="success" :captchaType="'blockPuzzle'" :imgSize="{ width: '330px', height: '155px' }" ref="verify"></Verify>
+		<view class='mask' catchtouchmove="true" :hidden='popup.show==false' @tap="closePopup"></view>
+	</view>
+	
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import sendVerifyCode from "@/mixins/SendVerifyCode";
+	import { bindingPhone, verifyCode, appletsDecrypt } from '@/api/api.js';
+	import { registerVerify } from '@/api/user.js'
+	import { mapGetters } from "vuex";
+	import { configMap } from '@/utils/index';
+  import Verify from '@/components/verify/verify.vue';
+	export default {
+		props:{
+			isCommuity: {
+				type: Boolean,
+				default: false
+			}
+		},
+		mixins: [sendVerifyCode],
+		components: {
+      Verify
+		},
+		data() {
+			return {
+				phone:'',
+				captcha:'',
+				key: '',
+				codeVal: '',
+				popup: {
+					show: false
+				},
+			};
+		},
+		computed: {
+			...mapGetters(['isLogin', 'viewColor']),
+			...configMap(['wechat_phone_switch'])
+		},
+		onLoad() {},
+		methods: {
+			// 点击关闭按钮
+			closePopup() {
+				this.$emit('close');
+			},
+			// #ifdef MP
+			getPhoneNumber(e) {
+				let that = this;
+				appletsDecrypt({
+					iv:e.detail.iv,
+					encryptedData:e.detail.encryptedData,
+					code:that.codeVal
+					}).then(res => {
+						that.$util.Tips({
+							title: '绑定成功!',
+							icon: 'success'
+						})
+						setTimeout(()=>{
+							uni.switchTab({
+								url: '/pages/user/index',
+							});
+						},2000)
+					})
+			},
+			// #endif
+			editPwd: function() {
+				let that = this;
+				if (!that.phone) return that.$util.Tips({
+					title: '请输入手机号码!'
+				});
+				if (!(/^1(3|4|5|7|8|9|6)\d{9}$/i.test(that.phone))) return that.$util.Tips({
+					title: '请输入正确的手机号码!'
+				});
+				if (!that.captcha) return that.$util.Tips({
+					title: '请填写验证码'
+				});
+				bindingPhone({
+					phone: that.phone,
+					sms_code: that.captcha
+				}).then(res => {
+					if (res.data !== undefined && res.data.is_bind) {
+						uni.showModal({
+							title: '是否绑定手机号',
+							content: res.message,
+							confirmText: '绑定',
+							success(res) {
+								if (res.confirm) {
+									bindingPhone({
+										phone: that.phone,
+										captcha: that.captcha,
+										step: 1
+									}).then(res => {
+										that.$emit('close');
+										return that.$util.Tips({
+											title: res.message,
+											icon: 'success'
+										});
+									}).catch(err => {
+										return that.$util.Tips({
+											title: err
+										});
+									})
+								} else if (res.cancel) {
+									return that.$util.Tips({
+										title: '您已取消绑定!'
+									}, {
+										tab: 5,
+										url: '/pages/users/user_info/index'
+									});
+								}
+							}
+						});
+					} else
+						that.$emit('close');
+						return that.$util.Tips({
+							title: '绑定成功!',
+							icon: 'success'
+						});
+				}).catch(err => {
+					return that.$util.Tips({
+						title: err
+					});
+				})
+			},
+			/**
+			 * 发送验证码
+			 *
+			 */
+			async code(data) {
+				let that = this;
+				if (!that.phone) return that.$util.Tips({
+					title: '请填写手机号码!'
+				});
+				if (!(/^1(3|4|5|7|8|9|6)\d{9}$/i.test(that.phone))) return that.$util.Tips({
+					title: '请输入正确的手机号码!'
+				});
+				this.disabled = true
+				await registerVerify({
+					phone:that.phone,
+					key:that.key,
+					code:that.captcha,
+					type: 'binding',
+          captchaType: 'blockPuzzle',
+					captchaVerification: data.captchaVerification
+				}).then(res => {
+					this.disabled = false
+					that.$util.Tips({
+						title: res.message
+					});
+					that.sendCode();
+				}).catch(err => {
+					this.disabled = false
+					return that.$util.Tips({
+						title: err
+					});
+				});
+			},
+			success(data) {
+				this.$refs.verify.hide();
+				this.code(data);
+			},
+			handleVerify() {
+				let that = this;
+				if (!that.phone) return that.$util.Tips({
+					title: '请填写手机号码!'
+				});
+				if (!(/^1(3|4|5|7|8|9|6)\d{9}$/i.test(that.phone))) return that.$util.Tips({
+					title: '请输入正确的手机号码!'
+				});
+				this.$refs.verify.show();
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	
+.container{
+	background-color: #ffffff;
+	border-radius: 16rpx 16rpx 0 0;
+	position: relative;
+	padding-bottom: 30rpx;
+	.header{
+		position: relative;
+		padding: 40rpx 30rpx;
+		text-align: center;
+		.title{
+			color: #282828;
+			font-size: 32rpx;
+		}
+		.iconfont{
+			color: #8A8A8A;
+			font-size: 28rpx;
+			position: absolute;
+			top: 0;
+			right: 0;
+		}
+		.icon-guanbi5 {
+			right: 20rpx;
+			color: #8a8a8a;
+			font-size: 30rpx;
+			line-height: 30rpx;
+			top: 20rpx;
+			background-color: transparent;
+			font-weight: normal;
+		}		
+	}
+}
+.ChangePassword{
+	padding: 0 30rpx 100rpx;
+}
+.ChangePassword .phone {
+		font-size: 32rpx;
+		font-weight: bold;
+		text-align: center;
+		margin-top: 55rpx;
+	}
+	.ChangePassword .list .item {
+
+		height: 86rpx;
+		margin-bottom: 30rpx;
+		flex-direction: row;
+	}
+	.ChangePassword .list .item input {
+
+		height: 100%;
+		font-size: 32rpx;	
+		border: 1px solid #DCDCDC;
+		border-radius: 43rpx;
+		padding: 0 40rpx;
+	}
+	.ChangePassword .list .item .placeholder {
+		color: #b9b9bc;
+	}
+	.ChangePassword .list .item input.codeIput {
+		max-width: 400rpx;
+	}
+	.ChangePassword .list .item .code {
+		font-size: 28rpx;
+		background-color: var(--view-minorColor);
+		color: var(--view-theme);
+		width: 230rpx;
+		height: 86rpx;
+		border-radius: 43rpx;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+	}
+	.ChangePassword .list .item .code.on {
+		color: #b9b9bc;
+	}
+	.ChangePassword .confirmBnt {
+		font-size: 32rpx;
+		width: 580rpx;
+		height: 90rpx;
+		border-radius: 45rpx;
+		color: #fff;
+		margin: 60rpx auto 0 auto;
+		text-align: center;
+		line-height: 90rpx;
+		background-color: var(--view-theme);
+	}
+	.getPhoneBtn{
+		font-size: 32rpx;
+		width: 580rpx;
+		height: 90rpx;
+		border-radius: 45rpx;
+		border: 1px solid #3CB625;
+		color: #3CB625;
+		margin: 40rpx auto 0 auto;
+		text-align: center;
+		line-height: 90rpx;
+		.iconfont{
+			font-size: 32rpx;
+			margin-right: 12rpx;
+		}
+	}
+	/deep/.verifybox{
+		top: auto!important;
+	}
+</style>

+ 140 - 0
components/cash/index.vue

@@ -0,0 +1,140 @@
+<template>
+	<view :style="viewColor">
+		<view class="payment" :class="pay_close ? 'on' : ''">
+			<view class="title acea-row row-center-wrapper">
+				选择提现方式<text class="iconfont icon-guanbi" @click='close'></text>
+			</view>
+			<radio-group @change="radioChange">
+				<label class="item acea-row row-between-wrapper" v-for="(item,index) in payMode" :key="index">
+					<view class="acea-row-left">
+						<view class="icon-left" :style="[{backgroundColor: item.bg_color}]">
+							<view class="iconfont" :class="item.icon"></view>
+						</view>
+						<view class="name">{{item.name}}</view>
+					</view>
+					<view>
+						<radio :value="item.id.toString()" :checked="item.id == order_id" />
+					</view>
+				</label>
+			</radio-group>
+		</view>
+		<view class="mask" ref="close" @click='close' v-if="pay_close"></view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { orderPay, presellOrderPay } 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: '0'
+			}
+		},
+		computed: mapGetters(['viewColor']),
+		data() {
+			return {
+				type: '0'
+			};
+		},
+		methods: {
+			close: function() {
+				this.$emit('payClose')
+			},
+			radioChange(e) {
+				this.type = e.detail.value
+				this.$emit('onChangeFun', {
+					action: 'payClose',
+					type: this.type
+				});
+			}
+		}
+	}
+</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);
+	}
+	/deep/radio .uni-radio-input.uni-radio-input-checked {
+	  border: 1px solid var(--view-theme) !important;
+	  background-color: var(--view-theme) !important
+	}
+	.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: 1px 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: 1px solid #eee;
+		height: 130rpx;
+		margin-left: 30rpx;
+		padding-right: 30rpx;
+	}	
+	.payment .item .acea-row-left{
+		 .icon-left{
+			display: inline-block;
+			width: 56rpx;
+			height: 56rpx;
+			text-align: center;
+			line-height: 56rpx;
+			background-color: #FE960F;
+			margin-right: 20rpx;
+			border-radius: 50%;
+			.iconfont{
+				color: #FFF;
+				font-size: 36rpx;
+			}
+		}
+		.name{
+			display: inline-block;
+		}
+	}
+</style>

+ 388 - 0
components/checkCoupon/index.vue

@@ -0,0 +1,388 @@
+<template>
+	<view>
+		<view class='coupon-list-window animated' :class='coupon.status==true?"slideInUp":""'>
+			<view class='title'>
+				<view class="item">优惠券</view>
+			</view>
+			<block v-if="couponArr.length">
+				<view class='coupon-list'>
+					<view class='item acea-row row-center-wrapper' v-for="(item,index) in couponArr" @click="getCouponUser(index,item)"
+					 :key='index'>
+						<view class='money acea-row row-column row-center-wrapper'>
+							<view>¥<text class='num'>{{item.coupon_price}}</text></view>
+							<view class="pic-num">满{{item.use_min_price}}元可用</view>
+						</view>
+						<view class='text'>
+							<view class='condition line1'>
+								<span class='line-title' v-if='item.coupon.type===0'>店铺券</span>
+								<span class='line-title' v-else-if='item.coupon.type===1'>商品券</span>
+								<span>{{item.coupon_title}}</span>
+							</view>
+							<view class='data acea-row row-between-wrapper'>
+								<view>{{ item.start_time |timeYMD }} ~ {{ item.end_time |timeYMD}}</view>
+								<view class="iconfont icon-weixuanzhong" v-if="!item.checked"></view>
+								<view class='iconfont icon-xuanzhong1' v-else></view>
+							</view>
+						</view>
+					</view>
+				</view>
+				<view class="foot-box">
+					<view class="left">
+						已选择{{allNum}}张,可优惠<text>¥{{allCouponNum}}</text>
+					</view>
+					<view class="btn" @click="confirm">确定</view>
+				</view>
+			</block>
+			<!-- 无优惠券 -->
+			<view class='pictrue' v-else>
+				<image :src="`${domain}/static/images/noCoupon.png`"></image>
+			</view>
+		</view>
+		<view class='mask' catchtouchmove="true" :hidden='coupon.status==false' @click='close'></view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { setCouponReceive } from '@/api/api.js';
+	import { HTTP_REQUEST_URL } from '@/config/app'
+	export default {
+		props: {
+			//打开状态 0=领取优惠券,1=使用优惠券
+			openType: {
+				type: Number,
+				default: 0,
+			},
+			coupon: {
+				type: Object,
+				default: function() {
+					return {};
+				}
+			},
+			subCoupon: {
+				type: Object
+			}
+		},
+		filters: {
+			timeYMD: function(value) {
+				if(value){
+					var newDate=/\d{4}-(\d{1,2}\d{1,2}-\d{1,2}\d{1,2})/g.exec(value)
+					return newDate?.[1]||''
+				}
+			}
+		},
+		data() {
+			return {
+				domain: HTTP_REQUEST_URL,
+				couponArr: [],
+				couponData: {},
+				// 选中的数据存放
+				active: {},
+				allNum: 0,
+				allCouponNum: 0,
+				// 选中店铺优惠券id
+				use_store_coupon: 0,
+				// 单个店铺总价
+				pay_price: 0,
+				// 商品有优惠订单
+				goodsOrder: ''
+			};
+		},
+		mounted() {
+			this.couponData = this.coupon
+			// 深拷贝数据 不影响原来数据使用
+			this.couponArr = JSON.parse(JSON.stringify(this.coupon.coupon))
+			// 深拷贝数据 不影响原来数据使用
+			this.goodsOrder = JSON.parse(JSON.stringify(this.coupon.order))
+			let tempObj = this.active[this.couponData.mer_id] = {}
+			tempObj.product = []
+			tempObj.store = ''
+			this.allActive()
+		},
+		methods: {
+			close: function() {
+				this.$emit('ChangCouponsClose');
+			},
+			// 使用优惠券
+			getCouponUser: function(index, item) {
+				let self = this
+				// 先判断是哪个券 1商品 0店铺
+				if (item.coupon.type == 1) {
+					let order = this.goodsOrder
+					let orderToalPrice = 0
+					if (item.checked) {
+						/**
+						 * 取消选中 并且删除 use_coupon_product里的值
+						 * use_coupon_product 哪些商品可以用的券的id
+						 * */
+						for (let key in order.use_coupon_product) {
+							if (order.use_coupon_product[key] == item.coupon_user_id) {
+								delete order.use_coupon_product[key]
+							}
+						}
+						item.checked = false
+					} else {
+						/**
+						 * 选中
+						 * @item.product 该优惠券可以使用的商品
+						 * order.product_price 产品的价格 key是id
+						 * */
+						for (let i = 0; i < item.product.length; i++) {
+							if (order.product_price[item.product[i].product_id]) {
+								orderToalPrice = order.product_price[item.product[i].product_id]
+								//价格的值大于等于最小值就可以使用
+								if (orderToalPrice >= parseFloat(item.use_min_price)) {
+									// 可以用
+									if (!order.use_coupon_product[item.product[i].product_id]) {
+										item.checked = true
+										order.use_coupon_product[item.product[i].product_id] = item.coupon_user_id
+									} else {
+										// 上个商品用了就取消选中,点击的这个添加选中
+										this.couponArr.forEach(el => {
+											if (el.coupon_user_id == order.use_coupon_product[item.product[i].product_id]) {
+												el.checked = false
+											}
+										})
+										item.checked = true
+										order.use_coupon_product[item.product[i].product_id] = item.coupon_user_id
+									}
+									break
+								}
+							}
+						}
+					}
+				} else {
+					let order = this.couponData.order
+					// 店铺券
+					if (item.checked) {
+						item.checked = false
+						// this.pay_price = order.total_price
+					} else {
+						this.couponArr.forEach(el => {
+							if (el.coupon.type == 0 && el.checked) {
+								el.checked = false
+							}
+						})
+						item.checked = true
+					}
+					this.pay_price = this.$util.$h.Sub(order.total_price, item.coupon_price)
+				}
+				this.allActive()
+			},
+			// 选中计算
+			allActive() {
+				let tempObj = this.active[this.couponData.mer_id]
+				let sotreTotal = 0 //商铺券优惠
+				let goodsTotal = 0 //商品券优惠
+				tempObj.product = []
+				tempObj.store = ''
+				this.couponArr.forEach(el => {
+					/**
+					 * @el.coupon.type 0店铺 1商品
+					 */
+					if (el.coupon.type == 0 && el.checked) {
+						tempObj.store = el.coupon_user_id
+						sotreTotal = el.coupon_price
+						this.use_store_coupon = el.coupon_user_id
+					}
+					if (el.coupon.type == 1 && el.checked) {
+						tempObj.product.push(el.coupon_user_id)
+						goodsTotal = this.$util.$h.Add(goodsTotal, el.coupon_price)
+					}
+				})
+				if (tempObj.store) {
+					this.allNum = this.$util.$h.Add(tempObj.product.length, 1)
+				} else {
+					this.allNum = tempObj.product.length
+				}
+				let tempAllCouponNum = this.$util.$h.Add(sotreTotal, goodsTotal)
+				if (parseFloat(tempAllCouponNum) >= parseFloat(this.couponData.order.total_price)) {
+					this.allCouponNum = this.couponData.order.total_price
+				} else {
+					if(this.allNum !== 0 ){
+						if(sotreTotal || goodsTotal){
+							this.allCouponNum = tempAllCouponNum
+						}else{
+							this.allCouponNum = this.couponData.order.total_price							
+						}						
+					}else{
+						this.allCouponNum = tempAllCouponNum
+					}
+				}
+			},
+			// 确认
+			confirm() {
+				// 商品类
+				this.couponData.order = this.goodsOrder
+				// 店铺类
+				// 支付价格
+				let tempTotal = 0
+				tempTotal = this.$util.$h.Sub(this.couponData.order.total_price, this.allCouponNum)
+				if (tempTotal > 0) {
+					this.couponData.order.pay_price = this.$util.$h.Add(tempTotal, this.couponData.order.postage_price)
+				} else {
+					// 如果没有用优惠券
+					if(this.allNum == 0){
+						this.couponData.order.pay_price = this.$util.$h.Add(this.couponData.order.total_price, this.couponData.order.postage_price)
+					}else{
+						this.couponData.order.pay_price = this.couponData.order.postage_price
+					}					
+				}
+				// 列表的优惠总金额
+				this.couponData.order.coupon_price = this.allCouponNum
+				this.couponData.order.use_store_coupon = this.use_store_coupon
+				this.couponData.coupon = this.couponArr
+				this.active[this.coupon.mer_id].product = this.goodsOrder.use_coupon_product
+				this.subCoupon[this.coupon.mer_id] = this.active[this.coupon.mer_id]
+				this.$emit('ChangCoupons',this.couponData);
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.animated {
+		animation-duration: .3s
+	}
+	.title {
+		display: flex;
+		.item {
+			position: relative;
+			flex: 1;
+			font-size: 28rpx;
+			color: #999999;
+			&::after {
+				content: ' ';
+				position: absolute;
+				left: 50%;
+				bottom: 18rpx;
+				width: 50rpx;
+				height: 5rpx;
+				background: transparent;
+				border-radius: 3px;
+				transform: translateX(-50%);
+			}
+			&.on {
+				color: #282828;
+
+				&::after {
+					background: $theme-color;
+				}
+			}
+		}
+	}
+	.coupon-list {
+		padding: 30rpx;
+		.item {
+			box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.06);
+		}
+	}
+	.coupon-list-window {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		width: 100%;
+		background-color: #fff;
+		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 {
+		animation: aminup;
+	}
+	.coupon-list-window .title {
+		height: 106rpx;
+		width: 100%;
+		text-align: center;
+		line-height: 106rpx;
+		font-size: 32rpx;
+		font-weight: bold;
+		position: relative;
+		border: 1px solid #f5f5f5;
+	}
+	.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 0rpx 0;
+		height: 550rpx;
+		overflow: auto;
+	}
+	.coupon-list-window .pictrue {
+		width: 414rpx;
+		height: 336rpx;
+		margin: 0 auto 50rpx 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;
+	}
+	.foot-box {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		height: 100rpx;
+		padding: 0 30rpx;
+		border-top: 1px solid #F5F5F5;
+		.btn {
+			width: 240rpx;
+			height: 70rpx;
+			line-height: 70rpx;
+			text-align: center;
+			background: $theme-color;
+			border-radius: 35rpx;
+			color: #fff;
+			font-size: 30rpx;
+		}
+		.left {
+			text {
+				color: $theme-color;
+			}
+		}
+	}
+	.coupon-list .item .text .data .iconfont {
+		font-size: 36rpx;
+		&.icon-weixuanzhong {
+			color: #BFBFBF;
+		}
+		&.icon-xuanzhong1 {
+			color: $theme-color;
+		}
+	}
+</style>

+ 178 - 0
components/checkDelivery/index.vue

@@ -0,0 +1,178 @@
+<template>
+	<view :style="viewColor">
+		<!-- 选择送货方式 -->
+		<view class="mask-box">
+			<view class="bg" v-if="isShowBox"></view>
+			<view class="mask-content animated" :class="{slideInUp:isShowBox}">
+				<view class="title-bar">
+					配送方式
+					<view class="close" @click="closeShowBox"><text class="iconfont icon-guanbi"></text></view>
+				</view>
+				<view class="box">
+					<view class="check-item" v-for="(item,index) in radioList" :key="index" :class="{on:index == radioIndex}">
+						<view>{{item.title}}</view>
+						<view class="radio" @click="bindCheck(item,index)"> 
+							<block v-if="index == newData.order.isTake">
+								<view class="iconfont icon-xuanzhong1"></view>
+							</block>
+							<block v-else>
+								<view class="iconfont icon-weixuanzhong"></view>
+							</block>
+						</view>
+					</view>
+				</view>
+				<view class="foot">
+					<view class="btn" @click="confirmBtn">确定</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { mapGetters } from "vuex";
+	export default{
+		name:'checkDelivery',
+		props:{
+			isShowBox:{
+				type:Boolean,
+				default:false
+			},
+			activeObj:{
+				type:Object,
+				default:function(){
+					return {}
+				}
+			},
+			deliveryName:{
+				type:String,
+				default:'快递配送'
+			},
+			radioList:{
+				type:Array,
+				default: [
+					{
+						title:'快递配送',
+						check:true
+					},
+					{
+						title:'到店核销',
+						check:false
+					}
+				],
+			},
+		},
+		computed: mapGetters(['viewColor']),
+		data(){
+			return {
+				radioIndex:0,
+				oldRadioIndex:'', //旧的索引
+				newData:{}
+			}
+		},
+		created() {
+			this.newData = JSON.parse(JSON.stringify(this.activeObj))
+		},
+		methods:{
+			// 关闭配送方式弹窗
+			closeShowBox(){
+				this.$emit('close')
+			},
+			// 选择配送方式
+			bindCheck(item,index){
+				this.newData.order.isTake = index
+			},
+			confirmBtn(){
+				this.$emit('confirmBtn',this.newData)
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.mask-box{
+		.bg{
+			z-index: 30;
+			position: fixed;
+			left: 0;
+			bottom: 0;
+			width: 100%;
+			height: 100%;
+			background: rgba(0,0,0,0.5);
+		}
+		.mask-content{
+			z-index: 40;
+			position: fixed;
+			left: 0;
+			bottom: 0;
+			width: 100%;
+			background-color: #fff;
+			border-radius: 16rpx 16rpx 0 0;
+			transform: translate3d(0, 100%, 0);
+			transition: all .3s cubic-bezier(.25, .5, .5, .9);
+			.title-bar{
+				position: relative;
+				text-align: center;
+				padding: 30rpx 0;
+				margin-bottom: 20rpx;
+				font-size: 32rpx;
+				color: #282828;
+				.close{
+					position: absolute;
+					right: 30rpx;
+					top: 50%;
+					transform: translateY(-50%);
+					.iconfont{
+						color: #8A8A8A;
+					}
+				}
+			}
+			.box{
+				padding: 0 30rpx;
+				.check-item{
+					display: flex;
+					align-items: center;
+					justify-content: space-between;
+					height: 40rpx;
+					margin-bottom: 50rpx;
+					font-size: 28rpx;
+					.iconfont{
+						font-size: 38rpx;
+						color: #CCCCCC;
+						&.icon-xuanzhong1{
+							color: var(--view-theme);
+						}
+					}
+				}
+			}
+			.foot{
+				padding: 20rpx 30rpx;
+				padding-bottom: calc(20rpx+ constant(safe-area-inset-bottom)); ///兼容 IOS<11.2/
+				padding-bottom: calc(20rpx + env(safe-area-inset-bottom)); ///兼容 IOS>11.2/
+				border-top: 1px solid #F5F5F5;
+				.btn{
+					width: 100%;
+					height: 70rpx;
+					line-height: 70rpx;
+					text-align: center;
+					border-radius: 35rpx;
+					color: #fff;
+					font-size: 30rpx;
+					background: var(--view-theme);
+				}
+			}
+		}	
+	}
+	.animated {
+		animation-duration: .3s
+	}
+</style>

+ 318 - 0
components/combinNav/index.vue

@@ -0,0 +1,318 @@
+<template>
+	<view :style="viewColor">
+		<view class="navTabBox">
+			<view class="longTab">
+				<scroll-view scroll-x="true" style="white-space: nowrap; display: flex;height: 60rpx;" scroll-with-animation :scroll-left="tabLeft"
+				 show-scrollbar="true">
+					<view class="longItem" :style='"width:"+isWidth+"px"' :data-index="index" :class="index===tabClick?'click':''"
+					 v-for="(item,index) in tabTitle" :key="index" :id="'id'+index" @click="longClick(index,item.store_category_id)">{{item.cate_name}}
+					</view>
+					<view class="underlineBox" :style='"transform:translateX("+isLeft+"px);width:"+isWidth+"px"'>
+						<view class="underline bg-color-white"></view>
+					</view>
+				</scroll-view>
+				<view class='iconfont icon-xiangxia' style="padding: 8rpx 0 0 8rpx;" @click="tabsOpen = true"></view>
+			</view>
+			<view class="tabs-box" v-if="tabsOpen">
+				<view class="box-top">
+					<view class="">
+						切换分类
+					</view>
+					<view class="">
+						<view class='iconfont icon-xiangshang' @click="tabsOpen = false"></view>
+					</view>
+				</view>
+				<view class="all-tabs">
+					<view class="tabs-style" :class="index===tabClick?'active':''" v-for="(tab,index) in tabTitle" :key='index' @click="longClick(index,tab.store_category_id)">
+						<text class="line1">{{tab.cate_name}}</text>
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class='mask' v-if="tabsOpen" @click="tabsOpen = false"></view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { mapGetters } from "vuex";
+	let app = getApp();
+	export default {
+		name: 'navTab',
+		props: {
+			tabTitle: {
+				type: Array,
+				default: () => {
+					[]
+				}
+			}
+		},
+		computed: mapGetters(['viewColor']),
+		data() {
+			return {
+				tabClick: 0, //导航栏被点击
+				isLeft: 0, //导航栏下划线位置
+				isWidth: 0, //每个导航栏占位
+				tabLeft: 0,
+				swiperIndex: 0,
+				childIndex: 0,
+				childID: 0,
+				window: false,
+				tabsOpen: false
+			};
+		},
+		created() {
+			var that = this
+			// 获取设备宽度
+			uni.getSystemInfo({
+				success(e) {
+					that.isWidth = e.windowWidth / 5
+				}
+			})
+		},
+		methods: {
+			// 导航栏点击
+			longClick(index, id) {
+				this.childIndex = 0;
+				if (this.tabTitle.length > 5) {
+					var tempIndex = index - 2;
+					tempIndex = tempIndex <= 0 ? 0 : tempIndex;
+					this.tabLeft = (index - 2) * this.isWidth //设置下划线位置
+				}
+				this.tabClick = index //设置导航点击了哪一个
+				this.isLeft = index * this.isWidth //设置下划线位置
+				let obj = {
+					index: index,
+					pid: id
+				}
+				this.parentEmit(obj);
+				this.tabsOpen = false
+			},
+			parentEmit(obj) {
+				this.$emit('changeTab', obj);
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	@keyframes bounce-in {
+		0% {
+			opacity: 0;
+		}
+		100% {
+			opacity: 1;
+		}
+	}
+	.navTabBox {
+		width: 750rpx;
+		box-sizing: border-box;
+		padding: 0 40rpx;
+		color: rgba(255, 255, 255, 1);
+		position: relative;
+		.tabs-box {
+			position: absolute;
+			top: 0;
+			display: flex;
+			flex-direction: column;
+			width: 90%;
+			background-color: #FFFFFF;
+			border-radius: 24rpx;
+			z-index: 999;
+			animation: bounce-in .3s;
+			.box-top {
+				padding: 30rpx 20rpx 10rpx 20rpx;
+				display: flex;
+				justify-content: space-between;
+				color: #666;
+				font-size: 28rpx;
+			}
+			.all-tabs {
+				display: flex;
+				flex-wrap: wrap;
+				padding: 20rpx 0;
+				font-size: 24rpx;
+				.tabs-style {
+					display: flex;
+					justify-content: center;
+					background: #F2F2F2;
+					border-radius: 29px;
+					color: #282828;
+					width: 22%;
+					padding: 10rpx 20rpx;
+					white-space: nowrap;
+					border-radius: 30px;
+					margin: 10rpx;
+				}
+				.active {
+					background: var(--view-minorColor);
+					color: var(--view-theme);
+					border: 1px solid var(--view-theme);
+				}
+			}
+		}
+		.nav_toggle {
+			position: absolute;
+			top: 8rpx;
+			right: 20rpx;
+		}
+		.click {
+			color: white;
+		}
+		.longTab {
+			display: flex;
+			width: 95%;
+			/* #ifdef H5 */
+			padding-bottom: 20rpx;
+			/* #endif */
+			/* #ifdef MP */
+			padding-top: 12rpx;
+			padding-bottom: 12rpx;
+			/* #endif */
+			.longItem {
+				height: 50upx;
+				display: inline-block;
+				line-height: 50upx;
+				text-align: center;
+				font-size: 30rpx;
+				color: rgba(255, 255, 255, .8);
+				max-width: 160rpx;
+				white-space: nowrap;
+				overflow: hidden;
+				text-overflow: ellipsis;
+				overflow-x: scroll;
+				overflow-y: hidden;
+				/*解决ios上滑动不流畅*/
+				-webkit-overflow-scrolling: touch;
+				&.click {
+					font-weight: bold;
+					font-size: 30rpx;
+					color: #FFFFFF;
+				}
+			}
+			.underlineBox {
+				height: 3px;
+				width: 20%;
+				display: flex;
+				align-content: center;
+				justify-content: center;
+				transition: .5s;
+				.underline {
+					width: 60rpx;
+					height: 4rpx;
+				}
+			}
+		}
+	}
+	.bg-color-white {
+		background-color: #fff;
+	}
+	.child-box {
+		width: 100%;
+		position: relative;
+		background-color: #fff;
+		/* #ifdef H5 */
+		box-shadow: 0 2px 5px 1px rgba(0, 0, 0, 0.02);
+		/* #endif */
+		/* #ifdef MP */
+		box-shadow: 0 2rpx 3rpx 1rpx #f9f9f9;
+		/* #endif */
+		.wrapper {
+			display: flex;
+			align-items: center;
+			padding: 20rpx 0;
+			background: #fff;
+		}
+		.child-item {
+			flex-shrink: 0;
+			width: 140rpx;
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+			margin-left: 10rpx;
+			image {
+				width: 90rpx;
+				height: 90rpx;
+				border-radius: 50%;
+			}
+			.txt {
+				font-size: 24rpx;
+				color: #282828;
+				text-align: center;
+				margin-top: 10rpx;
+			}
+			&.on {
+				image {
+					border: 1px solid $theme-color-opacity;
+				}
+				.txt {
+					color: $theme-color;
+				}
+			}
+		}
+	}
+	.brand-wrapper {
+		flex: 1;
+		overflow: hidden;
+		width: 690rpx;
+		margin: 0 auto;
+		background: #ffffff;
+		border-radius: 12rpx;
+		padding: 25rpx;
+		.wrapper {
+			display: flex;
+			flex-wrap: wrap;
+			padding-bottom: 20rpx;
+		}
+		.item {
+			display: block;
+			width: 146rpx;
+			height: 58rpx;
+			line-height: 58rpx;
+			text-align: center;
+			background: rgba(242, 242, 242, 1);
+			border-radius: 28rpx;
+			margin-top: 25rpx;
+			padding: 0 10rpx;
+			margin-right: 19rpx;
+			color: #282828;
+			font-size: 24rpx;
+			&:nth-child(4n) {
+				margin-right: 0;
+			}
+			&.on {
+				background: rgba(255, 244, 243, 1);
+				border: 1px solid rgba(233, 51, 35, 1);
+				color: rgba(233, 51, 35, 1);
+			}
+		}
+		.btns {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			padding-top: 10rpx;
+			font-size: 22rpx;
+			color: #999;
+			.iconfont {
+				margin-left: 10rpx;
+				margin-top: 5rpx;
+				font-size: 20rpx;
+			}
+		}
+		.mask {
+			z-index: 300 !important;
+		}
+		.icon-xiangxia {
+			font-size: 20px;
+		}
+	}
+</style>

+ 481 - 0
components/comment.vue

@@ -0,0 +1,481 @@
+<template>
+	<view :style="viewColor">
+		<view class="container" :class="isShow==true?'on':''" @click.stop="loseFocus">
+			<view class="main_content">
+				<view class="header">
+					<text class="title">评论 {{all}}</text>
+					<text class="iconfont icon-guanbi5" @click="close"></text>
+				</view>
+				<view class="main">
+					<scroll-view scroll-y="true">
+						<view v-if="list.length > 0" @touchmove="onTouchmove" id="reply"> 
+							<view class="common_list" v-for="(item, index) in list" :key="index">
+								<view class="commen_one">
+									<image :src="(item.author&&item.author.avatar) || '/static/images/f.png'" class="image"></image>
+								</view>
+								<view class="info_count">
+									<view class="info">
+										<view class="message" @click.stop="toReply(item,index)">
+											<view v-if="item.author" class="name">{{item.author.nickname}}</view>
+											<view class="desc">{{item.content}}</view>
+											<view class="time">{{item.create_time}}</view>
+										</view>
+										<view class="like" @click.stop="starComment(item)">
+											<view class="iconfont":class="item.relevance_id ? 'icon-yidianzan' : 'icon-dianzan1'"></view>
+											{{item.count_start}}
+										</view>
+									</view>
+									<view v-if="item.children && item.children.length > 0" class="reply_count">
+										<view v-for="(itemn,indexn) in item.children" :key="indexn" class="reply_list">
+											<view class="item">
+												<view class="item_count" @click.stop="toReply(itemn,index)">
+													<image class="image" :src="itemn.author && itemn.author.avatar || '/static/images/f.png'"></image>
+													<view v-if="itemn.author" class="name_two">{{itemn.author.nickname}}</view>
+													<view class="desc_two">
+														<text class="reply_user" v-if="itemn.reply">回复 @{{itemn.reply.nickname}} </text> {{itemn.content}}
+													</view>
+													<view class="time_two">{{itemn.create_time}}</view>
+												</view>
+												<view class="like_two" @click.stop="starComment(itemn)">
+													<view class="iconfont":class="itemn.relevance_id ? 'icon-yidianzan' : 'icon-dianzan1'"></view>
+													{{itemn.count_start}}
+												</view>
+											</view>
+										</view>								
+									</view>	
+								</view>	
+							</view>
+							<view class="end"><text>到底了</text></view>
+						</view>
+						<Loading :loaded="loaded" :loading="loading"></Loading>
+						<view v-if="list.length == 0 && !loading" class="empty">
+							<image :src="`${domain}/static/images/no_commen.png`"></image>
+							<text>暂无评论,快去抢沙发吧~</text>
+						</view>	
+					</scroll-view>	
+				</view>	
+				
+			</view>
+			<view class="release_bar" :style="'bottom:'+bottom+'rpx;'">
+				<image class="image" :src="userInfo.avatar || '/static/images/f.png'"></image>
+				<view class="input_count" @click.stop="">
+					<input ref="myInput" type="text" :placeholder="placeholder" placeholder-style="color: #999999; font-size: 26rpx;" v-model="content" confirm-type="search">	
+				</view>
+				<button class="send" @click.stop="submitComment">发送</button>
+			</view>
+		</view>
+		<!-- 绑定手机号 -->
+		<uni-popup ref="bindmobile" type="bottom">
+			<bindmobile @close="closepoup" :isCommuity="true"></bindmobile>
+		</uni-popup>
+		<view class='mask' catchtouchmove="true" :hidden='isShow==false' @tap="close"></view>
+	</view>	
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { replyLstApi, starCommentApi, replyCreateApi } from '@/api/community.js';
+	import Loading from '@/components/Loading/index.vue';
+	import bindmobile from '@/components/bindmobile.vue';
+	import { getUserInfo } from '@/api/user.js';
+	import { mapGetters } from "vuex";
+	import { configMap } from '@/utils';
+	import { HTTP_REQUEST_URL } from '@/config/app';
+	import { toLogin } from '@/libs/login.js';
+	export default {
+		props:{
+			isShow: {
+				type: Boolean,
+				default: false
+			},
+			bottom: {
+				type: Number,
+				default: 0
+			},
+			userInfo: {
+				type: Object,
+				default: () => {uid:0;avatar:""}
+			}
+		},
+		components: {
+		  Loading,bindmobile
+		},
+		computed: configMap({community_reply_auth:0},mapGetters(['isLogin', 'viewColor'])),
+		data() {
+			return {
+				domain: HTTP_REQUEST_URL,
+				content: '',
+				id: "",
+				list: [],
+				loaded: false,
+				loading: false,
+				where: {
+					page: 1,
+					limit: 10
+				},
+				reply_id: "",
+				placeholder: "快来说点儿什么吧...",
+				isChild: false,
+				index: 0,
+				listIndex: 0,
+				focus: false,
+				all: 0,
+			};
+		},
+		
+		methods: {
+			// 点击关闭按钮
+			close() {
+				this.$emit('close');
+			},
+			onTouchmove(e){
+				if (this.loadend || this.loading) return;
+				const query = uni.createSelectorQuery().in(this);
+				query.select('#reply').boundingClientRect(data => {
+					console.log(data)
+					if(data.bottom < 1500 && data.top < 0) {
+						this.getList();	
+					}
+				}).exec();
+				// 模拟触底刷新
+			},
+			getData(item,index){
+				this.id = item.community_id
+				this.loading = this.loaded = false
+				this.where.page = 1
+				this.list = []
+				this.getList()
+				this.listIndex = index
+			},
+			getList(){
+				let that = this;
+				if(that.loading || that.loaded) return;
+				that.loading = true;
+				replyLstApi(that.id,that.where).then(res => {
+				    that.loading = false;
+					that.all = res.data.all;
+				    that.loaded = res.data.list.length < that.where.limit;
+				    that.list.push.apply(that.list, res.data.list);
+				    that.where.page = that.where.page + 1;
+				  },
+				  error => {
+				    that.$util.Tips({
+				      title: error.msg
+				    })
+				  }
+				);
+			},
+			/*发表评论*/
+			submitComment(){
+				let that = this;
+				if (that.isLogin === false) {
+					toLogin()
+				}else{
+					that.getUserInfo();
+				}
+			},
+			/**
+			 * 获取个人用户信息
+			 */
+			getUserInfo: function() {
+				let that = this;
+				getUserInfo().then(res => {
+					/*判断是否绑定手机号*/
+					if(res.data.phone || that.community_reply_auth == 0){
+						that.createReply()
+					}else{
+						that.$refs.bindmobile.open()
+					}
+				});
+			},
+			createReply() {
+				let that = this;
+				let reply_id = that.reply_id ? that.reply_id : 0
+				replyCreateApi(that.id,{content: that.content,reply_id: reply_id}).then(res => {
+					that.$util.Tips({
+						title: res.message
+					});
+					if(res.data.status == 1){
+						if(that.isChild){
+							if(that.list[that.index]['children']){
+								that.list[that.index]['children'].push(res.data)
+							}else{
+								that.list[that.index]['children'] = [res.data]
+							}
+						}else{
+							that.list.unshift(res.data)
+						}
+						that.all++
+					}
+					that.content = ""	
+					that.$util.Tips({
+						title: res.message
+					});
+					that.$emit('successFul',that.listIndex);
+					that.loseFocus()
+				}).catch(err => {
+					uni.showToast({
+						title: err,
+						icon: 'none'
+					})
+				});
+			},
+			toReply(item,index){
+				this.focus = true;
+				// this.$refs.myInput.focus();
+				this.content = ""
+				this.placeholder = '回复:'+item.author.nickname
+				this.reply_id = item.reply_id
+				this.isChild = true
+				this.index = index;
+				// this.focus = true;
+			},
+			loseFocus(){
+				this.focus = false;
+				this.reply_id = 0;
+				this.placeholder = "快来说点儿什么吧..."
+				this.isChild = false			
+			},
+			/*点赞评论*/
+			starComment(item){
+				let that = this;
+				let status = item.relevance_id ? 0 : 1
+				starCommentApi(item.reply_id,{status: status}).then(res => {
+					if (res.status === 200) {
+						if(item.relevance_id){
+							item.count_start--;
+							item.count_start = item.count_start == 0 ? 0 : item.count_start
+							item.relevance_id = false
+						}else{
+							item.count_start++;
+							item.relevance_id = true
+						}
+					}
+					that.$util.Tips({
+						title: res.message
+					});
+				}).catch(err => {
+					uni.showToast({
+						title: err,
+						icon: 'none'
+					})
+				});
+			},
+			closepoup(){
+				this.$refs.bindmobile.close()
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	
+.container{
+	display: block;
+	background: #ffffff;
+	border-radius: 16rpx 16rpx 0 0;
+	position: relative;
+	position: fixed;
+	bottom: 0;
+	padding-bottom: calc(0rpx+ constant(safe-area-inset-bottom)); ///兼容 IOS<11.2/
+	padding-bottom: calc(0rpx + env(safe-area-inset-bottom)); ///兼容 IOS>11.2/
+	width: 100%;
+	left: 0;
+	background-color: #f5f5f5;
+	z-index: 40;
+	border-radius: 16rpx 16rpx 0 0;
+	transform: translate3d(0, 100%, 0);
+	transition: all .3s cubic-bezier(.25, .5, .5, .9);	
+	&.on {
+		transform: translate3d(0, 0, 0);
+	}
+	.main_content{
+		padding: 24rpx 30rpx;
+		border-bottom: 1px solid #F5F5F5;
+		position: relative;
+	}
+	.header{
+		position: relative;
+		text-align: center;
+		.title{
+			color: #282828;
+			font-size: 36rpx;
+			font-weight: bold;
+		}
+		.iconfont{
+			color: #8A8A8A;
+			font-size: 36rpx;
+			position: absolute;
+			top: 4rpx;
+			right: 0;
+		}		
+	}
+}
+scroll-view{
+	max-height: 60vh;
+}
+.main{
+	margin-top: 60rpx;
+	padding-bottom: 20rpx;
+	position: static;
+	.common_list{
+		position: relative;
+		padding-left: 94rpx;
+		margin-bottom: 20rpx;
+		.commen_one{
+			position: absolute;
+			top: 0;
+			left: 0;
+			.image,uni-image{
+				width: 74rpx;
+				height: 74rpx;
+				border-radius: 100%;
+			}
+				
+		}
+		.info{
+			position: relative;
+			padding-right: 90rpx;
+			
+		}
+		.name,.name_two{
+			color: #999999;
+			font-size: 26rpx;
+		}
+		.desc,.desc_two{
+			color: #282828;
+			font-size: 28rpx;
+			margin-top: 10rpx;
+		}
+		.desc_two{
+			font-size: 26rpx;
+			.reply_user{
+				font-size: 24rpx;
+				color: #4A8AC9;
+				margin: 0 6rpx;
+			}
+		}
+		.time,.time_two{
+			color: #BBBBBB;
+			font-size: 22rpx;
+			margin-top: 15rpx;
+		}
+		.like,.like_two{
+			color: #999999;
+			font-size: 26rpx;
+			text-align: center;
+			position: absolute;
+			top: 0;
+			right: 0;
+			width: 75rpx;
+			.icon-yidianzan{
+				color: var(--view-theme);
+			}
+		}
+		.reply_list{
+			margin-top: 20rpx;
+			.item{
+				padding-right: 140rpx;
+				position: relative;
+			}
+			.item_count{
+				position: relative;
+				padding-left: 56rpx;
+				.image,un-image{
+					width: 36rpx;
+					height: 36rpx;
+					border-radius: 100%;
+					position: absolute;
+					top: 0;
+					left: 0;
+				}
+			}
+		}
+	}
+	.end{
+		margin-top: 50rpx;
+		text-align: center;
+		text{
+			color: #999999;
+			font-size: 22rpx;
+			position: relative;
+			&::before,&::after{
+				content: "";
+				display: inline-block;
+				width: 22rpx;
+				height: 1rpx;
+				background: #999999;
+				position: absolute;
+				top: 18rpx;
+				opacity: .5;
+			}
+			&::before{
+				left: -30rpx;
+			}
+			&::after{
+				right: -30rpx;
+			}
+		}
+	}
+}
+.release_bar{
+	// position: absolute;
+	width: 100%;
+	left: 0;
+	/*#ifndef MP*/
+	bottom: 0;
+	/*#endif*/
+	/*#ifdef MP*/
+	bottom: 10rpx;
+	/*#endif*/
+	background: #ffffff;
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	padding: 15rpx 30rpx;
+	border-top: 1px solid #F5F5F5;
+	flex-direction: row;
+	.avatar,image,uni-image{
+		width: 54rpx;
+		height: 54rpx;
+		border-radius: 100%;
+	}
+	.input_count{
+		width: 480rpx;
+		background: #F7F7F7;
+		border-radius: 31rpx;
+		padding: 12rpx 30rpx;
+	}
+	.send{
+		font-size: 26rpx;
+		color: #ffffff;
+		padding: 12rpx 30rpx;
+		background-image: linear-gradient(126deg, var(--view-bntColor21) 0%, var(--view-bntColor22) 100%);
+		border-radius: 30rpx;
+		text-align: center;
+	}
+}
+.empty{
+	display: block;
+	margin: 130rpx 0 150rpx;
+	text-align: center;
+	image,uni-image{
+		display: inline-block;
+		width: 414rpx;
+		height: 305rpx;
+	}
+	text{
+		display: block;
+		color: #999999;
+		font-size: 26rpx;
+	}
+}
+</style>

+ 147 - 0
components/copyPassword/index.vue

@@ -0,0 +1,147 @@
+<template>
+	<view :style="viewColor">
+		<view class='copy-list-window' :class='isCopy==true?"on":""'>
+			<button class="iconfont icon-guanbi" @click='close'></button>
+			<view class="title">
+				<text class="iconfont icon-xuanzhong11"></text>
+				{{title}}
+			</view>
+			<view class="copy-url">
+				<!-- #ifdef H5 -->
+				<text class="copy copy-data" :data-clipboard-text="copyUrl">{{copyUrl}}</text>
+				<!-- #endif -->
+				<!-- #ifndef H5 -->
+				<text>{{copyUrl}}</text>
+				<!-- #endif -->
+			</view>
+			<!-- #ifdef H5 -->
+			<button class="button copy-data" :data-clipboard-text="copyUrl">点击复制口令</button>
+			<!-- #endif -->
+			<!-- #ifndef H5 -->
+			<button class="button" @click="copyText">点击复制口令</button>
+			<!-- #endif -->
+		</view>
+		<view class='mask' catchtouchmove="true" :hidden='isCopy==false' @click='close'></view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	// #ifdef H5
+	import ClipboardJS from "@/plugin/clipboard/clipboard.js";
+	// #endif
+	import { mapGetters } from "vuex";
+	export default {
+		props: {
+			isCopy: {
+				type: Boolean,
+				default: false,
+			},
+			copyUrl: {
+				type: String,
+				default: '',
+			}
+		},
+		computed: mapGetters(['viewColor']),
+		data() {
+			return {
+				title: '复制以下口令'
+			};
+		},
+		mounted(){
+			let that = this
+			// #ifdef H5
+			that.$nextTick(function() {
+				var clipboard = new ClipboardJS('.copy-data');
+				clipboard.on('success', function(e) {
+					that.$util.Tips({
+						title:'复制成功'
+					})
+					setTimeout(()=>{
+						that.$emit('close');
+					},500)
+				});
+				clipboard.on('error', function(e) {
+					that.$util.Tips({
+						title:'复制失败'
+					})
+				});
+			});
+			// #endif
+		},
+		methods: {
+			//#ifndef H5
+			copyText:function(){
+				let that = this;
+				uni.setClipboardData({ data: this.copyUrl });
+				setTimeout(()=>{
+					that.$emit('close');
+				},500)			 
+			},
+			// #endif
+			close: function(){
+			  this.$emit('close');
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.copy-list-window {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		width: 100%;
+		background-color: #fff;
+		border-radius: 16rpx 16rpx 0 0;
+		z-index: 555;
+		transform: translate3d(0, 100%, 0);
+		transition: all .1s cubic-bezier(.25, .5, .5, .9);
+		padding: 50rpx 30rpx 30rpx;
+		.icon-guanbi{
+			position: absolute;
+			top: 20rpx;
+			right: 20rpx;
+			color: #8A8A8A;
+		}
+		.button{
+			height: 76rpx;
+			border-radius: 38rpx;
+			margin: 30rpx 30rpx 0;
+			background-color: var(--view-theme);
+			font-size: 30rpx;
+			line-height: 76rpx;
+			color: #FFFFFF;
+		}	
+	}
+	.copy-list-window.on {
+		transform: translate3d(0, 0, 0);
+	}
+	.copy-list-window .title {
+		width: 100%;
+		text-align: center;
+		font-size: 32rpx;
+		font-weight: bold;
+		font-family: 'PingFang SC';
+		.iconfont {
+			font-size: 30rpx;
+			color: #FBB324;
+			margin-right: 12rpx;
+		}
+	}
+	.copy-url{
+		margin-top: 30rpx;
+		background: #F5F5F5;
+		padding: 20rpx 25rpx;
+		color: #BBBBBB;
+		font-size: 28rpx;
+	}
+</style>

+ 174 - 0
components/countDown/index.vue

@@ -0,0 +1,174 @@
+<template>
+	<view class="time" :class="{themeColor : isView, bgTheme: isTheme}" :style="justifyLeft+viewColor+'background-color:'+ bgColor +';color:'+ colors +';background-image:url('+bgImage+');'">
+		<text class="red" v-if="tipText">{{ tipText }}</text>
+		<text class="styleAll" v-if="isDay === true">{{ day }}</text>
+		<text class="timeTxt red" v-if="dayText">{{ dayText }}</text>
+		<text class="styleAll">{{ hour }}</text>
+		<text class="timeTxt red" v-if="hourText">{{ hourText }}</text>
+		<text class="styleAll">{{ minute }}</text>
+		<text class="timeTxt red" v-if="minuteText">{{ minuteText }}</text>
+		<text class="styleAll">{{ second }}</text>
+		<text class="timeTxt red" v-if="secondText">{{ secondText }}</text>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { mapGetters } from "vuex";
+	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
+			},
+			id:{
+				type: String | Number,
+				default: ""
+			},
+			bgColor:{
+				type: String,
+				default: ""
+			},
+			colors:{
+				type: String,
+				default: ""
+			},
+			bgImage: {
+				type: String,
+				default: ""
+			},
+			isView: {
+				type: Boolean,
+				default: false
+			},
+			isTheme: {
+				type: Boolean,
+				default: false
+			},
+		},
+		data: function() {
+			return {
+				day: "00",
+				hour: "00",
+				minute: "00",
+				second: "00"
+			};
+		},
+		computed:{
+			...mapGetters(['viewColor']),
+		},
+		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";
+						that.$emit('getProduct');
+						uni.$emit('endTime',that.id)
+						clearInterval(runTime)
+					}
+				}
+				runTime();
+				setInterval(runTime, 1000);
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.styleAll {
+		padding: 0 6rpx;
+	}
+	.red{
+		margin: 0 4rpx;
+	}
+	.themeColor .red{
+		color: var(--view-theme);
+	}
+	.bgTheme .styleAll{
+		background-color: var(--view-theme);
+		color: #ffffff;
+		font-size: 22rpx;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+	}
+	.time{
+		display: flex;
+		justify-content: center;
+		font-size: 20rpx;
+	} 
+
+</style>

+ 238 - 0
components/couponListWindow/index.vue

@@ -0,0 +1,238 @@
+<template>
+	<view :style="viewColor">
+		<view class='coupon-list-window animated' :class='coupon.coupon==true?"slideInUp":""'>
+			<view class='title'>
+				<view class="item">优惠券<text class='iconfont icon-guanbi' @tap='close'></text></view>
+			</view>
+			<view class='coupon-list' v-if="coupon.list.length">
+				<view class='item acea-row row-center-wrapper' v-for="(item,index) in coupon.list" @click="getCouponUser(index,item)"
+				 :key='index'>
+					<view class='money acea-row row-column row-center-wrapper' :style="{ 'background-image': `url(${domain}/static/diy/couponBg${keyColor}.png)` }">
+						<view>¥<text class='num'>{{item.coupon_price}}</text></view>
+						<view class="pic-num">满{{item.use_min_price}}元可用</view>
+					</view>
+					<view class='text'>
+						<view class='condition line1'>
+							<span class='line-title' v-if='item.type===0'>店铺券</span>
+							<span class='line-title' v-else-if='item.type===1'>商品券</span>
+							<span>{{item.title}}</span>
+						</view>
+						<view class='data acea-row row-between-wrapper'>
+							<block v-if="item.coupon_type == 1">
+								<view>{{ item.use_start_time |timeYMD }}-{{ item.use_end_time |timeYMD}}</view>
+							</block>
+							<block v-if="item.coupon_type == 0">
+								<view>领取后{{ item.coupon_time}}天内可用</view>
+							</block>
+							<view class='gray iconfont icon-yilingqu2' v-if="item.issue"></view>
+							<view class='bnt b-color' v-else>{{coupon.statusTile || '立即领取'}}</view>
+						</view>
+					</view>
+				</view>
+			</view>
+			<!-- 无优惠券 -->
+			<view class='pictrue' v-else>
+				<image :src="`${domain}/static/images/noCoupon.png`"></image>
+			</view>
+		</view>
+		<view class='mask' catchtouchmove="true" :hidden='coupon.coupon==false' @click='close'></view>
+	</view>
+</template>
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { setCouponReceive } from '@/api/api.js';
+	import { mapGetters } from "vuex";
+	import { HTTP_REQUEST_URL } from '@/config/app';
+	export default {
+		props: {
+			//打开状态 0=领取优惠券,1=使用优惠券
+			openType: {
+				type: Number,
+				default: 0,
+			},
+			coupon: {
+				type: Object,
+				default: function() {
+					return {};
+				}
+			},
+			showTitle: {
+				type: Number,
+				default: 1,
+			},
+			isShop: {
+				type: Number,
+				default: 0,
+			},
+		},
+		filters: {
+		  timeYMD: function (value) {
+				if(value){
+					var newDate=/\d{4}-\d{1,2}-\d{1,2}/g.exec(value)
+					return newDate[0]
+				}		
+		  }
+		},
+		computed: mapGetters(['viewColor','keyColor']),
+		data() {
+			return {
+				domain: HTTP_REQUEST_URL,
+				tabList:['商品券','店铺券'],
+				tabIndex:this.isShop,
+				couponArr:[],
+			};
+		},
+		mounted() {
+			this.$nextTick(function(){
+				this.couponArr = this.coupon.list
+			})
+		},
+		methods: {
+			close: function() {
+				this.$emit('ChangCouponsClose');
+			},
+			getCouponUser: function(index, item) {
+				let that = this;		
+				if (item.issue) return true;
+				switch (this.openType) {
+					case 0:
+						//领取优惠券
+						setCouponReceive(item.coupon_id).then(res => {
+							item.issue = true
+							that.$emit('ChangCouponsUseState', index);
+							that.$util.Tips({
+								title: "领取成功"
+							});
+							that.$emit('ChangCoupons', item);
+						})
+						break;
+					case 1:
+						that.$emit('ChangCoupons', index);
+						break;
+				}
+			},
+			bindTab(item,index){
+				this.tabIndex = index
+				this.filterArray()
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.animated{
+		animation-duration:.3s
+	}
+	.title{
+		display: flex;
+		border-radius: 16rpx 16rpx 0 0;
+		.item{
+			position: relative;
+			flex: 1;
+			font-size: 28rpx;
+			color: #999999;
+			&::after{
+				content: ' ';
+				position: absolute;
+				left: 50%;
+				bottom: 18rpx;
+				width:50rpx;
+				height:5rpx;
+				background:transparent;
+				border-radius:3px;
+				transform: translateX(-50%);
+			}
+			&.on{
+				color: #282828;
+				&::after{
+					background: var(--view-theme);
+				}
+			}
+		}
+	}
+	.b-color {
+		background-color: var(--view-theme);
+	}
+	.coupon-list{
+		padding: 30rpx;
+		.item{
+			box-shadow:0px 2px 10px 0px rgba(0, 0, 0, 0.06);
+		}
+	}
+	.coupon-list-window {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		width: 100%;
+		background-color: #fff;
+		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 {
+		animation: aminup ;
+	}
+	.coupon-list-window .title {
+		height: 106rpx;
+		width: 100%;
+		text-align: center;
+		line-height: 106rpx;
+		font-size: 32rpx;
+		font-weight: bold;
+		position: relative;
+		border: 1px solid #f5f5f5;
+	}
+	.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 50rpx 0;
+		height: 550rpx;
+		overflow: auto;
+	}
+	.coupon-list-window .pictrue {
+		width: 414rpx;
+		height: 336rpx;
+		margin: 0 auto 50rpx 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: var(--view-minorColor);
+		border: 1px solid var(--view-theme);
+		opacity: 1;
+		border-radius: 20rpx;
+		font-size: 20rpx;
+		color: var(--view-theme);
+		margin-right: 12rpx;
+	}
+	.line-title.gray {
+		border-color: #BBB;
+		color: #bbb;
+		background-color: #F5F5F5;
+	}
+</style>

+ 124 - 0
components/customTab.vue

@@ -0,0 +1,124 @@
+<template>
+	<view>
+		<view v-if="newData.status && newData.status.status" style="height: 100rpx;"></view>
+		<view class="foot" v-if="newData.status && newData.status.status">
+			<view class="page-footer" id="target" :style="{'background-color':newData.bgColor.color[0].item}">
+				<view class="foot-item" v-for="(item,index) in newData.menuList" :key="index"
+					@click="goRouter(item)">
+					<block v-if="item.link == activeRouter">
+						<image :src="item.imgList[0]"></image>
+						<view class="txt" :style="{color:newData.activeTxtColor.color[0].item}">{{item.name}}
+						</view>
+					</block>
+					<block v-else>
+						<image :src="item.imgList[1]"></image>
+						<view class="txt" :style="{color:newData.txtColor.color[0].item}">{{item.name}}</view>
+					</block>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	export default{
+		props: {
+			newData: {
+				type: Object,
+				default: {},
+			},
+			activeRouter: {
+				type: String,
+				default: '',
+			}
+		},
+		data(){
+			return {
+				
+			}
+		},
+		mounted() {
+			
+		},
+		methods:{	
+			goRouter(item) {
+				var pages = getCurrentPages();
+				var page = (pages[pages.length - 1]).$page.fullPath;
+				if (item.link == page) return
+				uni.switchTab({
+					url: item.link,
+					fail(err) {
+						uni.redirectTo({
+							url: item.link
+						})
+					}
+				})
+			},
+		}
+	}
+	
+</script>
+
+<style lang="scss" scoped>
+	.page-footer {
+		position: fixed;
+		bottom: 0;
+		z-index: 30;
+		display: flex;
+		align-items: center;
+		justify-content: space-around;
+		width: 100%;
+		height: calc(98rpx+ constant(safe-area-inset-bottom)); ///兼容 IOS<11.2/
+		height: calc(98rpx + env(safe-area-inset-bottom)); ///兼容 IOS>11.2/
+		box-sizing: border-box;
+		border-top: 1px solid #F3F3F3;
+		background-color: #fff;
+		box-shadow: 0px 0px 17rpx 1rpx rgba(206, 206, 206, 0.32);
+		padding-bottom: constant(safe-area-inset-bottom); ///兼容 IOS<11.2/
+		padding-bottom: env(safe-area-inset-bottom); ///兼容 IOS>11.2/
+		&.filter{
+			filter: blur(2rpx);
+		}
+		.foot-item {
+			display: flex;
+			width: max-content;
+			align-items: center;
+			justify-content: center;
+			flex-direction: column;
+			position: relative;
+			.count-num {
+				position: absolute;
+				display: flex;
+				justify-content: center;
+				align-items: center;
+				width: 40rpx;
+				height: 40rpx;
+				top: 0rpx;
+				right: -15rpx;
+				color: #fff;
+				font-size: 20rpx;
+				background-color: #FD502F;
+				border-radius: 50%;
+				padding: 4rpx;
+			}
+		}
+		.foot-item image {
+			height: 50rpx;
+			width: 50rpx;
+			text-align: center;
+			margin: 0 auto;
+		}
+		.foot-item .txt {
+			font-size: 24rpx;
+		}
+	}
+</style>

+ 153 - 0
components/discountDetails/index.vue

@@ -0,0 +1,153 @@
+<template>
+	<view :style="viewColor">
+		<!-- 选择送货方式 -->
+		<view class="mask-box">
+			<view class="bg" v-if="isShowDiscount"></view>
+			<view class="mask-content animated" :class="{slideInUp:isShowDiscount}">
+				<view class="title-bar">
+					优惠明细
+					<view class="close" @click="closeShowBox"><text class="iconfont icon-guanbi"></text></view>
+				</view>
+				<view class="box">
+					<view class="check-item">
+						<view>商品售价:</view>
+						<view class="radio"> 
+							¥{{couponData.total_price}}
+						</view>
+					</view>
+					<view v-if="couponData.order_total_integral_price>0" class="check-item">
+						<view>积分抵扣:</view>
+						<view class="radio"> 
+							-¥{{couponData.order_total_integral_price}}
+						</view>
+					</view>
+					<view v-if="couponData.total_platform_coupon_price>0" class="check-item">
+						<view>平台优惠金额:</view>
+						<view class="radio"> 
+							-¥{{ couponData.total_platform_coupon_price }}
+						</view>
+					</view>
+					<view v-if="couponData.order_coupon_price>0" class="check-item">
+						<view>店铺优惠金额:</view>
+						<view class="radio"> 
+							-¥{{ couponData.order_coupon_price }}
+						</view>
+					</view>
+					<view v-if="couponData.order_svip_discount>0" class="check-item">
+						<view>SVIP优惠金额:</view>
+						<view class="radio"> 
+							-¥{{ couponData.order_svip_discount }}
+						</view>
+					</view>
+					<view v-if="couponData.total_coupon>0" class="check-item total">
+						<view>共优惠:</view>
+						<view class="radio"> 
+							-¥{{ couponData.total_coupon}}
+						</view>
+					</view>
+				</view>	
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { mapGetters } from "vuex";
+	export default{
+		name:'Discount',
+		props:{
+			isShowDiscount:{
+				type:Boolean,
+				default:false
+			},
+			couponData:{
+				type:Object,
+				default:function(){
+					return {}
+				}
+			}
+		},
+		computed: mapGetters(['viewColor']),
+		data(){
+			return {}
+		},
+		created() {},
+		methods:{
+			// 关闭配送方式弹窗
+			closeShowBox(){
+				this.$emit('close')
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.mask-box{
+		.bg{
+			z-index: 9;
+			position: fixed;
+			left: 0;
+			bottom: 100rpx;
+			width: 100%;
+			height: 100%;
+			background: rgba(0,0,0,0.5);
+		}
+		.mask-content{
+			z-index: 10;
+			position: fixed;
+			left: 0;
+			bottom: 100rpx;
+			width: 100%;
+			background-color: #f5f5f5;
+			border-radius: 16rpx 16rpx 0 0;
+			transform: translate3d(0, 200%, 0);	
+			transition: all .3s cubic-bezier(.25, .5, .5, .9);
+			.title-bar{
+				position: relative;
+				text-align: center;
+				padding: 30rpx 0;
+				margin-bottom: 20rpx;
+				font-size: 32rpx;
+				color: #282828;
+				.close{
+					position: absolute;
+					right: 30rpx;
+					top: 50%;
+					transform: translateY(-50%);
+					.iconfont{
+						color: #8A8A8A;
+					}
+				}
+			}
+			.box{
+				padding: 0 30rpx 60rpx;
+				.check-item{
+					display: flex;
+					align-items: center;
+					justify-content: space-between;
+					height: 40rpx;
+					margin-bottom: 50rpx;
+					font-size: 28rpx;
+					&.total{
+						font-weight: bold;
+						font-size: 32rpx;
+						.radio{
+							color: var(--view-priceColor);
+						}
+					}
+				}
+			}
+		}	
+	}
+	.animated {
+		animation-duration: .3s
+	}
+</style>

+ 185 - 0
components/easy-loadimage/easy-loadimage.vue

@@ -0,0 +1,185 @@
+<template>
+    <view class="easy-loadimage" :id="uid">
+        <image class="origin-img" :src="imageSrc" mode="aspectFill" v-if="loadImg&&!isLoadError" v-show="showImg"
+        	:class="{'no-transition':!openTransition,'show-transition':showTransition&&openTransition}"
+        	@load="handleImgLoad" @error="handleImgError">
+        </image>
+       <view class="loadfail-img" v-else-if="isLoadError" :style="{ 'background-image': `url(${domain}/static/images/loadfail.png)`}"></view>
+       <view :class="['loading-img','spin-circle',loadingMode]" v-show="!showImg&&!isLoadError" :style="{ 'background-image': `url(${domain}/static/images/loading.gif)`}"></view>
+    </view>
+</template>
+<script>
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+import { throttle } from '@/libs/uniApi';
+import { HTTP_REQUEST_URL } from '@/config/app';
+// 生成全局唯一id
+function generateUUID() {
+    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+        let r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
+        return v.toString(16);
+    })
+}
+export default{
+    props:{
+			imageSrc:{
+				type: String,
+			},
+			mode:{
+				type: String,
+			},
+			loadingMode:{
+				type: String,
+				default:'looming-gray'
+			},
+			openTransition:{
+				type: Boolean,
+				default:true,
+			},
+			viewHeight:{
+				type:Number,
+				default() {
+						return uni.getSystemInfoSync().windowHeight;
+				}
+			}
+    },
+    data(){
+			const that = this;
+			return {
+				domain: HTTP_REQUEST_URL,
+				uid: 'uid-' + generateUUID(),
+				loadImg: false,
+				showImg: false,
+				isLoadError: false,
+				borderLoaded: 0,
+				showTransition: false,
+				scrollFn: throttle(function() {
+					// 加载img时才执行滚动监听判断是否可加载
+					if (that.loadImg || that.isLoadError) return;
+					const id = that.uid
+					const query = uni.createSelectorQuery().in(that);
+					query.select('#' + id).boundingClientRect(data => {
+						if (!data) return;
+						
+						if (data.top - that.viewHeight < 0) {
+							that.loadImg = !!that.imageSrc;
+							that.isLoadError = !that.loadImg;
+						}
+					}).exec()
+				}, 200)
+			}
+    },
+    methods:{
+			init() {
+				this.$nextTick(this.onScroll)
+			},
+			handleBorderLoad(){
+				this.borderLoaded = 1;
+			},
+			handleBorderError(){
+				this.borderLoaded = 2;
+			},
+			handleImgLoad(e) {
+				this.showImg = true;
+				setTimeout(() => {
+					this.showTransition = true
+				}, 50)
+			},
+			handleImgError(e) {
+				this.isLoadError = true;
+			},
+			onScroll() {
+				this.scrollFn();
+			},
+    },
+    mounted() {
+      this.init()
+      uni.$on('scroll', this.scrollFn);
+      this.onScroll()
+    },
+		beforeDestroy() {
+			uni.$off('scroll', this.scrollFn);
+		}
+}
+</script>
+
+<style scoped>
+    /* 官方优化图片tips */
+    image{
+        will-change: transform
+    } 
+    /* 渐变过渡效果处理 */
+    image.origin-img{
+			width: 100%;
+			height: 100%;
+			opacity: 0.3;
+			max-height: 360rpx;
+    }
+    image.origin-img.show-transition{
+			transition: opacity .5s;
+			opacity: 1;
+    }
+    image.origin-img.no-transition{
+      opacity: 1;
+    }
+    /* 加载失败、加载中的占位图样式控制 */
+    .loadfail-img{
+			 height: 100%;
+			 background-repeat: no-repeat;
+			 background-size: 50%;
+			 background-position: center;
+    }
+    .loading-img{
+      height: 100%;
+			background-position: center;
+    }
+    /* 转圈 */
+    .spin-circle{
+			background-repeat: no-repeat;
+			background-size: 60%;
+    }
+    /* 动态灰色若隐若现 */
+    .looming-gray{
+			animation: looming-gray 1s infinite linear;
+			background-color: #e3e3e3;
+    }
+    @keyframes looming-gray{
+			0%   {background-color:#e3e3e3aa;}
+			50%  {background-color:#e3e3e3;}
+			100% {background-color:#e3e3e3aa;}
+    } 
+    /* 骨架屏1 */
+    .skeleton-1{
+			background-color: #e3e3e3;
+			background-image: linear-gradient(100deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0) 80%);
+			background-size: 100rpx 100%;
+			background-repeat: repeat-y;
+			background-position:0 0;
+			animation: skeleton-1 .6s infinite;
+    }
+    @keyframes skeleton-1 {
+			to {
+					background-position: 200% 0;
+			}
+    }
+    /* 骨架屏2 */
+    .skeleton-2{
+			background-image: linear-gradient(-90deg, #fefefe 0%, #e6e6e6 50%,#fefefe 100%);
+			background-size: 400% 400%;
+			background-position:0 0;
+			animation: skeleton-2 1.2s ease-in-out infinite;
+    }
+    @keyframes skeleton-2{
+			to {
+				background-position: -135% 0;
+			}
+    }
+</style>
+

+ 290 - 0
components/eidtUserModal/index.vue

@@ -0,0 +1,290 @@
+<template>
+	<view :style="viewColor">
+		<view class="product-window" :class="{'on':isShow}">
+			<view class="iconfont icon-guanbi" @click="closeAttr"></view>
+			<view class="mp-data">
+				<image :src="routine_logo" mode=""></image>
+				<text class="mp-name">{{site_name}} 申请</text>
+			</view>
+			<view class="trip-msg">
+				<view class="title">
+					获取您的昵称、头像
+				</view>
+				<view class="trip">
+					提供具有辨识度的用户中心界面
+				</view>
+			</view>
+			<form @submit="formSubmit">
+				<view class="edit">
+					<view class="avatar edit-box">
+						<view class="left">
+							<view class="head">头像</view>
+							<!-- <image :src="userInfo.avatar || defaultAvatar" mode=""></image> -->
+							<view class="avatar-box" v-if="!mp_is_new" @click.stop='uploadpic'>
+								<image :src="userInfo.avatar || domain+'/static/images/def_avatar.png'"></image>
+							</view>
+							<button v-else class="avatar-box" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
+								<image :src="userInfo.avatar || domain+'/static/images/def_avatar.png'"></image>
+							</button>
+						</view>
+					</view>
+					<view class="nickname edit-box">
+						<view class="left">
+							<view class="head">昵称</view>
+							<view class='input'><input type='nickname' placeholder-class="pl-sty"
+								placeholder="请输入昵称" name='nickname' :maxlength="16"
+								:value='userInfo.nickname'></input>
+							</view>
+						</view>
+					</view>
+				</view>
+				<view class="bottom">
+					<button class="save" formType="submit" :class="{'open': userInfo.avatar}">
+						保存
+					</button>
+				</view>
+			</form>
+		</view>
+		<canvas canvas-id="canvas" v-if="canvasStatus"
+			:style="{width: canvasWidth + 'px', height: canvasHeight + 'px',position: 'absolute',left:'-100000px',top:'-100000px'}"></canvas>
+		<view class="mask" @touchmove.prevent v-if="isShow" @click="closeAttr"></view>
+	</view>
+	</uni-popup>
+</template>
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { mapGetters } from "vuex";
+	import { configMap } from '@/utils';
+	import Cache from '@/utils/cache';
+	import { editAvatar } from '@/api/user.js';
+	import { HTTP_REQUEST_URL } from '@/config/app';
+	export default {
+		props: {
+			isShow: {
+				type: Boolean,
+				value: false
+			}
+		},
+		data() {	
+			return {
+				domain: HTTP_REQUEST_URL,
+				mp_is_new: this.$Cache.get('MP_VERSION_ISNEW') || false,
+				userInfo: {
+					avatar: '',
+					nickname: '',
+				},
+				canvasStatus: false,
+			};
+		},
+		computed: configMap({routine_logo: '',site_name: ''} ,mapGetters(['viewColor','keyColor'])),
+		mounted() {
+			
+		},
+		methods: {	
+			/**
+			 * 上传文件
+			 * 
+			 */
+			uploadpic: function() {
+				let that = this;
+				this.canvasStatus = true
+				that.$util.uploadImageChange('upload/image', (res) => {
+					let userInfo = that.userInfo;
+					if (userInfo !== undefined) {
+						that.userInfo.avatar = res.data.url;
+					}
+					this.canvasStatus = false
+				}, (res) => {
+					this.canvasStatus = false
+				}, (res) => {
+					this.canvasWidth = res.w
+					this.canvasHeight = res.h
+				});
+			},
+			// 微信头像获取
+			onChooseAvatar(e) {
+				const {avatarUrl} = e.detail
+				this.$util.uploadImgs('upload/image', avatarUrl, (res) => {
+					this.userInfo.avatar = res.data.path
+				}, (err) => {
+					console.log(err)
+				})
+			},
+			closeAttr: function() {
+				this.$emit('closeEdit');
+			},
+			/**
+			 * 提交修改
+			 */
+			formSubmit(e) {
+				let that = this
+				if (!this.userInfo.avatar) return that.$util.Tips({
+					title: '请上传头像'
+				});
+				if (!e.detail.value.nickname) return that.$util.Tips({
+					title: '请输入昵称'
+				});
+				this.userInfo.nickname = e.detail.value.nickname
+				editAvatar(this.userInfo).then(res => {
+					this.$emit('editSuccess')
+					return that.$util.Tips({
+						title: res.message,
+						icon: 'success'
+					}, {
+						tab: 3
+					});
+				}).catch(msg => {
+					return that.$util.Tips({
+						title: msg || '保存失败'
+					}, {
+						tab: 3,
+						url: 1
+					});
+				});
+			}
+		}
+	}
+</script>
+<style>
+	.pl-sty {
+		color: #999999;
+		font-size: 30rpx;
+	}
+</style>
+<style scoped lang="scss">
+	.product-window.on {
+		transform: translate3d(0, 0, 0);
+		display: block;
+	}
+	.mask {
+		z-index: 99;
+	}
+	.product-window {
+		position: fixed;
+		bottom: 0;
+		width: 100%;
+		left: 0;
+		background-color: #fff;
+		z-index: 1000;
+		border-radius: 20rpx 20rpx 0 0;
+		transform: translate3d(0, 100%, 0);
+		transition: all .3s cubic-bezier(.25, .5, .5, .9);
+		padding: 38rpx 40rpx;
+		display: none;
+		.icon-guanbi {
+			position: absolute;
+			top: 40rpx;
+			right: 40rpx;
+			font-size: 24rpx;
+			font-weight: bold;
+			color: #999;
+		
+		}
+		.mp-data {
+			display: flex;
+			align-items: center;
+			margin-bottom: 30rpx;
+			.mp-name {
+				font-size: 28rpx;
+				font-weight: bold;
+				color: #000000;
+			}
+			image {
+				width: 48rpx;
+				height: 48rpx;
+				border-radius: 50%;
+				margin-right: 16rpx;
+			}
+		}
+		.trip-msg {
+			padding-bottom: 32rpx;
+			border-bottom: 1px solid #F5F5F5;
+			.title {
+				font-size: 30rpx;
+				font-weight: bold;
+				color: #000;
+				margin-bottom: 6rpx;
+			}
+			.trip {
+				font-size: 26rpx;
+				color: #777777;
+			}
+		}
+		.edit {
+			border-bottom: 1px solid #F5F5F5;
+			.avatar {
+				border-bottom: 1px solid #F5F5F5;
+			}
+			.nickname {
+				.input {
+					width: 100%;
+				}
+				input {
+					height: 80rpx;
+				}
+			}
+			.edit-box {
+				display: flex;
+				justify-content: space-between;
+				align-items: center;
+				font-size: 30rpx;
+				padding: 22rpx 0;
+				.left {
+					display: flex;
+					align-items: center;
+					flex: 1;
+					.head {
+						color: rgba(0, 0, 0, 0.9);
+						white-space: nowrap;
+						margin-right: 60rpx;
+					}
+					button {
+						flex: 1;
+						display: flex;
+						align-items: center;
+					}
+				}
+				image {
+					width: 80rpx;
+					height: 80rpx;
+					border-radius: 6rpx;
+				}
+			}
+			.icon-xiangyou {
+				color: #cfcfcf;
+			}
+		}
+		.bottom {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			.save {
+				border: 1px solid #F5F5F5;
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				width: 368rpx;
+				height: 80rpx;
+				border-radius: 12rpx;
+				margin-top: 52rpx;
+				background-color: #F5F5F5;
+				color: #ccc;
+				font-size: 30rpx;
+				font-weight: bold;
+			}
+			.save.open {
+				border: 1px solid #fff;
+				background-color: #07C160;
+				color: #fff;
+			}
+		}
+	}
+</style>

+ 45 - 0
components/emptyPage.vue

@@ -0,0 +1,45 @@
+<template>
+	<view class="empty-box">
+		<image src="/static/images/empty-box.png"></image>
+		<view class="txt">{{title}}</view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	export default{
+		props: {
+			title: {
+				type: String,
+				default: '暂无记录',
+			},
+		},
+	}
+	
+</script>
+
+<style lang="scss">
+	.empty-box{
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		margin-top: 200rpx;
+		image{
+			width: 414rpx;
+			height: 240rpx;
+		}
+		.txt{
+			font-size: 26rpx;
+			color: #999;
+		}
+	}
+</style>

+ 216 - 0
components/freightGuarantee/index.vue

@@ -0,0 +1,216 @@
+<template>
+	<view :style="viewColor">
+		<view class="guaranee_tel">
+			<view class="popup" :class="{ on: isGuarantee }">
+				<view class="title">保障说明<text class="iconfont icon-guanbi" @click="close"></text></view>
+					<view v-if="guarantee.length" class="content">
+						<view v-for="(item,index) in guarantee" class="item">							
+							<view class="name"><image :src='item.image' class="image"></image>{{item.guarantee_name}}</view>
+							<view class="info" style="white-space: pre-line;">{{item.guarantee_info}}</view>
+						</view>
+					</view>
+				<button @tap="close">确定</button>
+			</view>		
+			<view class="popup" :class="{ on: isShipping }">
+				<view class="title">运费说明<text class="iconfont icon-guanbi" @click="close"></text></view>
+					<view class="content" style="white-space: pre-line;">
+						{{shipping}}
+					</view>
+				<button @tap="close">确定</button>
+			</view>			
+		</view>		
+		<view class='mask' catchtouchmove="true" :hidden='showMask==false' @tap='close'></view>
+	</view>	
+</template>
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { mapGetters } from "vuex";
+	export default {
+		props: {			
+			shipping: {
+				type: String,
+			},
+			guarantee: {
+				type: Array,
+			}		
+		},
+		computed: mapGetters(['viewColor']),
+		data() {
+			return {
+				isShipping: false,
+				isGuarantee: false,
+				showMask: false,
+			}
+		},
+		watch: {
+			
+		},
+		onLoad(option) {
+			
+		},
+		onShow(){
+			
+		},
+		methods: {
+			showShippingTel() {
+				this.isShipping = true;
+				this.showMask = true;
+			},
+			showGuaranteeTel() {
+				this.isGuarantee = true;
+				this.showMask = true;
+			},
+			close: function() {
+				this.isShipping = false;
+				this.isGuarantee = false;
+				this.showMask = false;				
+			}	
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.guaranee_tel{
+		position: fixed;
+		bottom: 0;
+		width: 100%;
+		left: 0;
+		background-color: #fff;
+		z-index: 300;
+		border-radius: 16rpx 16rpx 0 0;
+		padding-bottom: 20rpx;
+		transform: translate3d(0, 100%, 0);
+		transition: all .3s cubic-bezier(.25, .5, .5, .9);
+	}	
+	.guaranee_tel.on {
+		transform: translate3d(0, 0, 0);
+	}
+	.guaranee_tel .title {
+		font-size: 32rpx;
+		font-weight: bold;
+		text-align: center;
+		height: 123rpx;
+		line-height: 123rpx;
+		position: relative;
+	}
+	.guaranee_tel .title .iconfont {
+		position: absolute;
+		right: 30rpx;
+		color: #8a8a8a;
+		font-size: 35rpx;
+	}
+
+	.popup {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		z-index: 99;
+		width: 100%;
+		padding-bottom: 40rpx;
+		border-top-left-radius: 16rpx;
+		border-top-right-radius: 16rpx;
+		background-color: #F5F5F5;
+		overflow: hidden;
+		transform: translateY(100%);
+		transition: 0.3s;
+	}
+	.popup.on {
+		transform: translateY(0);
+	}
+	.popup .title {
+		position: relative;
+		height: 137rpx;
+		font-size: 32rpx;
+		line-height: 137rpx;
+		text-align: center;
+	}
+	.popup scroll-view {
+		height: 466rpx;
+		padding-right: 30rpx;
+		padding-left: 30rpx;
+		box-sizing: border-box;
+	}
+	.popup .icon-guanbi {
+		position: absolute;
+		top: 50%;
+		right: 30rpx;
+		z-index: 2;
+		transform: translateY(-50%);
+		font-size: 30rpx;
+		color: #707070;
+		cursor: pointer;
+	}
+	.popup .content{
+		padding: 0 50rpx;
+		color: #999999;
+		max-height: 800rpx;
+		overflow-y: scroll;
+		.item{
+			margin-bottom: 70rpx;
+		}
+		.image{
+			width: 30rpx;
+			height: 30rpx;
+			margin-right: 20rpx;
+			position: relative;
+			top: 4rpx;
+		}
+		.name{
+			color: #282828;
+			flex: 1;
+			overflow: hidden;
+			white-space: nowrap;
+			text-overflow: ellipsis;
+		}
+		.info{
+			margin-top: 15rpx;
+		}
+	}
+	.popup button {
+		height: 86rpx;
+		border-radius: 43rpx;
+		margin-right: 30rpx;
+		margin-left: 30rpx;
+		background-image: linear-gradient(to right, var(--view-bntColor21) 0%, var(--view-bntColor22) 100%);;
+		font-size: 30rpx;
+		line-height: 86rpx;
+		color: #FFFFFF;
+		margin-top: 80rpx;
+	}
+	.popup .text .acea-row {
+		display: inline-flex;
+		max-width: 100%;
+	}
+	.popup .label {
+		width: 56rpx;
+		height: 28rpx;
+		border: 1px solid #E93323;
+		margin-left: 18rpx;
+		font-size: 20rpx;
+		line-height: 26rpx;
+		text-align: center;
+		color: #E93323;
+	}
+	.popup .type {
+		width: 124rpx;
+		height: 42rpx;
+		margin-top: 14rpx;
+		background-color: #FCF0E0;
+		font-size: 24rpx;
+		line-height: 42rpx;
+		text-align: center;
+		color: #D67300;
+	}
+	.popup .type.special {
+		background-color: #FDE9E7;
+		color: #E93323;
+	}
+</style>

+ 179 - 0
components/goodList/index.vue

@@ -0,0 +1,179 @@
+<template>
+	<view class='goodList' :style="viewColor">
+		<block v-for="(item,index) in bastList" :key="index">
+			<view @click="goDetail(item)" class='item acea-row' hover-class="none">
+				<view class='pictrue'>
+					<image :src='item.image'></image>
+					<text class="pictrue_log pictrue_log_class" v-if="item.activity && item.activity.type === '1'">秒杀</text>
+					<text class="pictrue_log pictrue_log_class" v-if="item.activity && item.activity.type === '2'">砍价</text>
+					<text class="pictrue_log pictrue_log_class" v-if="item.activity && item.activity.type === '3'">拼团</text>
+				</view>
+				<view class='underline'>				
+						<view class='line1'>						
+						{{item.store_name}}
+						</view>
+						<view class="item_line">
+							<text v-if="item.merchant.type_name && item.product_type == 0" class="font-bg-red bt-color">{{item.merchant.type_name}}</text>
+							<text v-else-if="item.merchant.is_trader && item.product_type == 0" class="font-bg-red bt-color">自营</text>
+							<text v-if="item.product_type != 0" :class="'font_bg-red bt-color type'+item.product_type">{{item.product_type == 1 ? "秒杀" : item.product_type == 2 ? "预售" : item.product_type == 3 ? "助力" : item.product_type == 4 ? "拼团" : ""}}</text>
+							<text v-if="item.issetCoupon" class="coupon">领券购买更优惠</text>
+						</view>
+						<view class='money'>¥<text class='num'>{{item.price}}</text></view>				
+				</view>
+				<view class='gobuy acea-row row-center-wrapper'>去购买</view>
+			</view>
+		</block>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import {mapGetters} from "vuex";
+	import { goShopDetail } from '@/libs/order.js'
+	import {initiateAssistApi} from '@/api/activity.js';
+	import { toLogin } from '@/libs/login.js';
+	export default {
+		computed: mapGetters(['uid','viewColor']),
+		props: {
+			status: {
+				type: Number,
+				default: 0,
+			},
+			bastList: {
+				type: Array,
+				default: function() {
+					return [];
+				}
+			},
+			isLogin:{
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+
+			};
+		},
+		methods: {
+			goDetail(item){
+				goShopDetail(item, this.uid).then(res => {
+				if (this.isLogin) {
+						initiateAssistApi(item.activity_id).then(res => {
+							let id = res.data.product_assist_set_id;
+							uni.hideLoading();	
+							uni.navigateTo({
+								url: '/pages/activity/assist_detail/index?id=' + id
+							});
+						}).catch((err) => {
+							uni.showToast({
+								title: err,
+								icon: 'none'
+							})
+						});
+					} else {
+						toLogin();
+					}
+				})		
+			}
+			
+		}
+	}
+</script>
+
+<style scoped lang='scss'>
+	.bt-color {
+		background-color: var(--view-theme);
+		border: 1rpx solid var(--view-theme);
+		&.type2{
+			background-color: #FD6523;
+			border: 1rpx solid #FD6523;
+		}
+	}
+	.goodList .item {
+		position: relative;
+		padding: 20rpx;
+		margin-bottom: 20rpx;
+		background: #fff;
+		justify-content: space-between;
+		border-radius: 16rpx;
+	}
+	.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{
+		width: 450rpx;
+		font-size: 30rpx;
+		color: #222;
+		position: relative;
+	}
+	.goodList .item .item_line{
+		margin-top: 10rpx;
+	}
+	.goodList .item .item_line .coupon{
+		font-size: 22rpx;
+		margin-left: 4rpx;
+	}
+	.goodList .item .money {
+		font-size: 26rpx;
+		font-weight: bold;
+		position: absolute;
+		bottom: 10rpx;
+		left: 0;
+		color: var(--view-priceColor);
+	}
+	.goodList .item .money .num {
+		font-size: 34rpx;
+	}
+	.goodList .item .vip-money {
+		font-size: 24rpx;
+		color: #282828;
+		font-weight: bold;
+		margin-top: 15rpx;
+	}
+	.goodList .item .vip-money image {
+		width: 46rpx;
+		height: 21rpx;
+		margin-left: 5rpx;
+	}
+	.goodList .item .vip-money .num {
+		font-size: 22rpx;
+		color: #aaa;
+		font-weight: normal;
+		margin: -2rpx 0 0 22rpx;
+	}
+	.goodList .item .gobuy {
+		position: absolute;
+		right: 30rpx;
+		padding: 6rpx 20rpx;
+		border-radius: 50%;
+		font-size: 22rpx;
+		bottom: 30rpx;
+		color: #fff;
+		background: var(--view-theme);
+		border-radius: 27rpx;
+	}
+</style>

+ 187 - 0
components/guide/index.vue

@@ -0,0 +1,187 @@
+<template>
+	<!-- 开屏广告 -->
+	<view class="content">
+		<swiper class="swiper" :class="advData.list.length==1?'on':''" :autoplay="autoplay" :interval="interval" :duration="duration" :circular="circular" @change="stopChange"
+			v-if="advData.list.length">
+			<swiper-item v-for="(item,index) in advData.list" :key="index" @click="jump(item.url)">
+				<view class="swiper-item">
+					<view class="swiper-item-img">
+						<image :src="item.pic" mode="aspectFill"></image>
+					</view>
+				</view>
+			</swiper-item>
+		</swiper>
+		<view class="video-box" v-if="advData.type == 'video' && advData.video_link">
+			<video class="vid" :src="advData.video_link" :autoplay="true" :loop="true" :muted="true"
+				:controls="false"></video>
+		</view>
+		<view class="jump-over" :style="{ top: navH + 'rpx' }" @tap="launchFlag()">跳过<text v-if="closeType == 1">{{time}}</text><slot name="bottom"></slot></view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	let app = getApp();
+	export default {
+		data() {
+			return {
+				autoplay: true,
+				circular: true,
+				duration: 500,
+				interval: 2500,
+				jumpover: '跳过',
+				experience: '立即体验',
+				time: this.advData.config.open_screen_time,
+				timecount: undefined,
+				navH: 0
+			}
+		},
+		props: {
+			advData: {
+				type: Object,
+				default: () => {}
+			},
+			// 1 倒计时 2 手动关闭(预留)
+			closeType: {
+				type: Number,
+				default: 1
+			}
+		},
+		mounted() {
+			this.timer()
+			// #ifdef MP
+			this.navH = app.globalData.navHeight;
+			// #endif
+			// #ifndef MP
+			this.navH = 80;
+			// #endif
+		},
+		methods: {
+			stopChange(){
+				if(this.advData.list.length == 1){
+					return false
+				}
+			},
+			timer() {
+				var t = this.advData.config.open_screen_time || 5
+				this.timecount = setInterval(() => {
+					t--
+					this.time = t
+					if (t <= 0) {
+						clearInterval(this.timecount)
+						this.launchFlag()
+					}
+				}, 1000)
+			},
+			launchFlag() {
+				clearInterval(this.timecount)
+				uni.switchTab({
+					url: '/pages/index/index'
+				});
+			},
+			jump(url) {
+				if(url){
+					clearInterval(this.timecount)
+					if (url.indexOf("http") != -1) {
+						uni.navigateTo({
+							url: `/pages/annex/web_view/index?url=${url}`
+						});
+					} else {
+						if (['/pages/goods_cate/goods_cate', '/pages/order_addcart/order_addcart', '/pages/user/index', '/pages/index/index', '/pages/plant_grass/index']
+							.indexOf(url) == -1) {
+							uni.navigateTo({
+								url: url
+							})
+						} else {
+							uni.reLaunch({
+								url: url
+							})
+						}
+					}
+				}
+			},
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	page,
+	.content {
+		width: 100%;
+		height: 100%;
+		background-size: 100% auto;
+		padding: 0;
+	}
+
+	.swiper {
+		width: 100%;
+		height: 100vh;
+		background: #FFFFFF;
+		&.on{
+			position: relative;
+			&:after {
+			 content: '';
+			 position: absolute;
+			 top: 0;
+			 left: 0;
+			 right: 0;
+			 bottom: 0;
+			 z-index: 2;
+			}
+		}
+	}
+
+	.swiper-item {
+		width: 100%;
+		height: 100%;
+		text-align: center;
+		position: relative;
+		display: flex;
+		/* justify-content: center; */
+		align-items: flex-end;
+		flex-direction: column-reverse
+	}
+
+	.swiper-item-img {
+		width: 100vw;
+		height: 100vh;
+		margin: 0 auto;
+		display: flex;
+		// align-items: center;
+	}
+
+	.swiper-item-img image {
+		width: 100%;
+		height: 100%;
+	}
+
+	.jump-over {
+		position: absolute;
+		height: 45rpx;
+		line-height: 45rpx;
+		padding: 0 15rpx;
+		border-radius: 30rpx;
+		font-size: 24rpx;
+		color: #b09e9a;
+		border: 1px solid #b09e9a;
+		z-index: 999;
+		right: 30rpx;
+	}
+
+	.video-box {
+		width: 100vw;
+		height: 100vh;
+
+		.vid {
+			width: 100%;
+			height: 100%;
+		}
+	}
+</style>

+ 116 - 0
components/home/index.vue

@@ -0,0 +1,116 @@
+<template>
+	<view style="touch-action: none;" :style="viewColor">
+		<view class="home" style="position:fixed;" :style="{ top: top + 'px', bottom: bottom }" id="right-nav" @touchmove.stop.prevent="setTouchMove">
+			<view class="homeCon" :class="homeActive === true ? 'on' : ''" v-if="homeActive">
+				<navigator hover-class='none' url='/pages/index/index' open-type='switchTab' class='iconfont icon-shouye-xianxing'></navigator>
+				<navigator hover-class='none' url='/pages/order_addcart/order_addcart' open-type='switchTab' class='iconfont icon-caigou-xianxing'></navigator>
+				<navigator hover-class='none' url='/pages/user/index' open-type='switchTab' class='iconfont icon-yonghu1'></navigator>
+			</view>
+			<view @click="open" class="pictrueBox">
+				<view class="pictrue">
+					<image :src="homeActive === true ? domain+'/static/images/navbtn_open.gif' : domain+'/static/images/navbtn_close.gif'"
+					 class="image pictruea" />
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { mapGetters } from "vuex";
+	import { HTTP_REQUEST_URL } from '@/config/app';
+	export default {
+		name: "Home",
+		props: {},
+		data: function() {
+			return {
+				domain: HTTP_REQUEST_URL,
+				top: "",
+				bottom: ""
+			};
+		},
+		computed: mapGetters(["homeActive","viewColor","keyColor"]),
+		methods: {
+			setTouchMove(e) {
+				var that = this;
+				if (e.touches[0].clientY < 545 && e.touches[0].clientY > 66) {
+					that.top = e.touches[0].clientY
+					that.bottom = "auto";
+				}
+			},
+			open: function() {
+				this.homeActive ? this.$store.commit("CLOSE_HOME") : this.$store.commit("OPEN_HOME");
+			}
+		},
+		created() {
+			this.bottom = "50px";
+		}
+	};
+</script>
+
+<style scoped>
+	.pictrueBox {
+		width: 130rpx;
+		height: 120rpx;
+	}
+	/*返回主页按钮*/
+	.home {
+		position: fixed;
+		color: white;
+		text-align: center;
+		z-index: 9999;
+		right: 15rpx;
+		display: flex;
+	}
+	.home .homeCon {
+		border-radius: 50rpx;
+		opacity: 0;
+		height: 0;
+		color: #e93323;
+		width: 0;
+	}
+	.home .homeCon.on {
+		opacity: 1;
+		animation: bounceInRight 0.5s cubic-bezier(0.215, 0.610, 0.355, 1.000);
+		width: 300rpx;
+		height: 86rpx;
+		margin-bottom: 20rpx;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		background: var(--view-theme);
+	}
+	.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;
+		background-color: var(--view-theme);
+		box-shadow: 0 5rpx 12rpx rgba(0, 0, 0, 0.5);
+	}
+	.home .pictrue .image {
+		width: 100%;
+		height: 100%;
+	}	
+	.pictruea{
+		width: 100%;
+		height: 100%;
+		display: block;
+		object-fit: cover;
+		vertical-align: middle;	
+	}
+</style>

Datei-Diff unterdrückt, da er zu groß ist
+ 59 - 0
components/index.vue


+ 291 - 0
components/invoiceGoods/index.vue

@@ -0,0 +1,291 @@
+<template>
+	<view class="orderGoods" :style="viewColor">
+		<view class='goodWrapper'>
+			<view class="title">共{{cartInfo.length}}件商品</view>
+			<view v-for="(item,index) in cartInfo" :key="index">	
+				<view class='item acea-row row-between-wrapper'>
+					<view class='pictrue' @click="jumpCon(item.product_id)">
+						<image :src='(item.cart_info.productAttr && item.cart_info.productAttr.image) || item.cart_info.product.image' ></image>
+					</view>
+					<view class='text'>
+						<view class='acea-row row-between-wrapper'>
+							<view class='name line1'>{{item.cart_info.product.store_name}}</view>
+							<view class='num'>x {{item.product_num}}</view>
+						</view>
+						<view class='attr line1' v-if="item.cart_info.productAttr.sku">{{item.cart_info.productAttr.sku}}</view>
+						<view class="acea-row row-middle item-money">
+							<view class='moneys'>¥{{item.cart_info.productAttr.price}}</view>
+							<view class="btn-item err" v-if="item.is_refund ==1">退款中 x {{item.product_num - item.refund_num}}</view>
+							<view class='btn-item err' v-if="item.is_refund >1">已退款 x {{item.product_num - item.refund_num}}</view>
+						</view>						
+					</view>
+				</view>				
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { openOrderRefundSubscribe } from '@/utils/SubscribeMessage.js';
+	import { mapGetters } from "vuex";
+	export default {
+		props: {
+			evaluate: {
+				type: Number,
+				default: 0,
+			},
+			activityType: {
+				type: Number,
+				default: 0,
+			},
+			cartInfo: {
+				type: Array,
+				default: function() {
+					return [];
+				}
+			},
+			orderId: {
+				type: String,
+				default: '',
+			},
+			jump: {
+				type: Boolean,
+				default: false,
+			},
+			orderData: {
+				type: Object,
+				default: function() {
+					return {};
+				}
+			}
+		},
+		computed: mapGetters(['viewColor']),
+		data() {
+			return {
+				totalNmu:'',
+				isTimePay: false, //是否到支付时间
+			};
+		},
+		watch:{
+			cartInfo:function(nVal,oVal){
+				let num = 0
+				nVal.forEach((item,index)=>{
+					num += item.cart_num
+				})
+				this.totalNmu = num
+			}
+		},
+		onShow() {
+			this.isPayBalance()
+		},
+		mounted() {},
+		methods: {
+			evaluateTap:function(unique,orderId){
+				uni.navigateTo({
+					url:`/pages/users/goods_comment_con/index?uni=${unique}&order_id=${orderId}`
+				})
+			},
+			// 判断是否到支付尾款时间
+			isPayBalance(){
+				let that = this;
+				if(that.orderData.status === 10){
+					if(new Date() < new Date(that.orderData.presellOrder.final_start_time)){
+						that.isTimePay = false; //未开始
+					}else if((new Date() >= new Date(that.orderData.presellOrder.final_start_time)) && (new Date() <= new Date(that.orderData.presellOrder.final_start_time)) ){
+						that.isTimePay = true; //立即支付
+					}
+				}
+			},
+			jumpCon:function(id){
+				if(this.jump){
+					if(this.activityType == 2){
+						uni.navigateTo({
+							url: `/pages/activity/presell_details/index?id=${id}`
+						})
+					}else{
+						uni.navigateTo({
+							url: `/pages/goods_details/index?id=${id}`
+						})
+					}	
+				}
+			},
+			// 退款
+			refund(item){
+				// #ifdef MP
+				openOrderRefundSubscribe().then(() => {
+					uni.hideLoading();
+					if(this.evaluate == 0){						
+						uni.navigateTo({
+							url:'/pages/users/refund/confirm?order_id='+this.orderId+'&type=1'+'&ids='+item.order_product_id+'&refund_type=1'
+						})
+					}else{
+						uni.navigateTo({
+							url:'/pages/users/refund/select?order_id='+this.orderId+'&type=1'+'&ids='+item.order_product_id
+						})
+					}
+				}).catch(() => {
+					uni.hideLoading();
+				})
+				// #endif
+				// #ifdef H5
+				if(this.evaluate == 0){
+					
+					uni.navigateTo({
+						url:'/pages/users/refund/confirm?order_id='+this.orderId+'&type=1'+'&ids='+item.order_product_id+'&refund_type=1'
+					})
+				}else{
+					uni.navigateTo({
+						url:'/pages/users/refund/select?order_id='+this.orderId+'&type=1'+'&ids='+item.order_product_id
+					})
+				}
+				// #endif
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.orderGoods {
+		background-color: #fff;
+	}
+	.goodWrapper{
+		margin-top: 12rpx;
+		.title{
+			padding: 0 32rpx;
+			border-bottom: 1px solid #f0f0f0;	
+			height: 86rpx;
+			line-height: 86rpx;
+		}
+	}
+	.right-btn-box{
+		position: absolute;
+		right: 0;
+		bottom: 0;
+		display: flex;
+		align-items: center;
+		justify-content: flex-end;
+		&.event_box{
+			position: static;
+		}
+		.btn-item{
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			width:140rpx;
+			height:46rpx;
+			margin-left: 20rpx;
+			border:1px solid rgba(187,187,187,1);
+			border-radius:23rpx;
+			font-size: 24rpx;
+			color: #282828;
+			&.on{
+				background:rgba(220,220,220,1);
+				border-color: rgba(220,220,220,1);
+			}
+			&.err{
+				background:rgba(247,247,247,1);
+				border-color: rgba(247,247,247,1);
+				color: #AAAAAA;
+			}
+		}
+	}
+	.event_bg{
+		background: #FF7F00;
+	}
+	.event_color{
+		color: #FF7F00;
+	}
+	.item-money{
+		margin-top: 20rpx;
+	}
+	.moneys {
+		margin-top: 0;
+		color: var(--view-priceColor);
+	}
+	.btn-item{
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		width:140rpx;
+		height:46rpx;
+		margin-left: 10rpx;
+		border:1px solid rgba(187,187,187,1);
+		border-radius:23rpx;
+		font-size: 24rpx;
+		color: #282828;
+		&.on{
+			background:rgba(220,220,220,1);
+			border-color: rgba(220,220,220,1);
+		}
+		&.err{
+			background:rgba(247,247,247,1);
+			border-color: rgba(247,247,247,1);
+			color: #AAAAAA;
+		}
+	}
+	.presell_item{
+		height: auto;
+		padding-bottom: 15rpx;
+	}
+	.event_progress{
+		margin-top: 20rpx;
+		background: #fff;
+		.progress_name {
+			padding-left: 30rpx;
+			height: 60rpx;
+			line-height: 60rpx;
+			font-size: 24rpx;
+			font-weight: bold;
+			position: relative;
+			&::before{
+				content: "";
+				display: inline-block;
+				width: 5rpx;
+				height: 34rpx;
+				background: #E93323;
+				position: absolute;
+				top: 15rpx;
+				left: 0;
+			}
+		}
+		.align_right{
+			float: right;
+			font-weight: bold;
+		}
+		.progress_price{
+			padding: 20rpx 30rpx;
+			color: #999999;
+			font-size: 22rpx;
+	
+		}
+		.progress_pay{
+			padding: 25rpx 30rpx;
+			background: var(--view-minorColor);
+			font-size: 26rpx;
+			color: #282828;
+			
+		}
+	}
+	.event_name{
+		display: inline-block;
+		margin-right: 9rpx;
+		color: #fff;
+		font-size: 20rpx;
+		padding: 0 8rpx;
+		line-height: 30rpx;	
+		text-align: center;
+		border-radius: 6rpx;						
+	}
+	.event_ship{
+		font-size: 20rpx;
+		margin-top: 10rpx;						
+	}
+</style>

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

@@ -0,0 +1,825 @@
+<!--
+  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>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	// #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.style.width = '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>

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

@@ -0,0 +1,111 @@
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+/*
+  解析和匹配 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;
+	}
+}

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

@@ -0,0 +1,586 @@
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+/*
+  将 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;

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

@@ -0,0 +1,89 @@
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+/* 配置文件 */
+// #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;
+}

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

@@ -0,0 +1,44 @@
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+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;
+	}
+}

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

@@ -0,0 +1,53 @@
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+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;
+	}
+}

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

@@ -0,0 +1,488 @@
+<!--
+  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>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	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%;
+	}
+	video{
+		width: 100%;
+	}
+</style>

+ 98 - 0
components/kefuIcon/index.vue

@@ -0,0 +1,98 @@
+<template>
+	<!-- 客服跳转 -->
+	<!-- #ifdef APP-PLUS || H5 -->
+	<view class="acea-row row-center-wrapper cartf iconfont icon-kefu3" :style="{ top: top + 'px'}" @touchmove.stop.prevent="setTouchMove" @click="licks"></view>
+	<!-- #endif -->
+	<!-- #ifdef MP -->
+	<view v-if="routineContact == 0">
+		<view class="acea-row row-center-wrapper cartf iconfont icon-kefu3" :style="{ top: top + 'px'}" @touchmove.stop.prevent="setTouchMove" @click="licks"></view>
+	</view>
+	<button class="acea-row row-center-wrapper cartf iconfont icon-kefu3" open-type='contact' :style="{ top: top + 'px'}" @touchmove.stop.prevent="setTouchMove" v-else-if="routineContact==1 && !goodsCon"></button>
+	<button class="acea-row row-center-wrapper cartf iconfont icon-kefu3" open-type='contact' :send-message-title="storeInfo.store_name" :send-message-img="storeInfo.image" :send-message-path="`/pages/goods_details/index?id=${storeInfo.id}`" show-message-card :style="{ top: top + 'px'}" @touchmove.stop.prevent="setTouchMove" v-else-if="routineContact==1 && goodsCon"></button>
+	<!-- #endif -->
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	let app = getApp();
+	import {
+		mapGetters
+	} from "vuex";
+	export default {
+		name: "kefuIcon", 
+		props: {
+			ids: {
+				type: Number,
+				default: 0
+			},
+			routineContact: {
+				type: Number,
+				default: 0
+			},
+			storeInfo: {
+				type: Object,
+				default () {
+					return {};
+				}
+			},
+			goodsCon: {
+				type: Number,
+				default: 0
+			}
+		},
+		computed: mapGetters(['userInfo']),
+		data: function() {
+			return {
+				top: "480"
+			};
+		},
+		mounted() {
+			// #ifdef H5
+			this.top =  parseFloat(window.innerHeight) -200
+			// #endif
+		},
+		methods: {
+			setTouchMove(e) {
+				let that = this;
+				if (e.touches[0].clientY < 480 && e.touches[0].clientY > 66) {
+					that.top = e.touches[0].clientY
+				}
+			},
+			licks(){
+				let userInfo = {}
+				if(typeof this.userInfo === 'string'){
+					userInfo = JSON.parse(this.userInfo)
+				}else{
+					userInfo = this.userInfo
+				}
+				let url = `/pages/extension/customer_list/chat?productId=${this.ids}`
+				this.$util.getCustomer(userInfo,url)
+			}
+		},
+		created() {
+		}
+	};
+</script>
+
+<style lang="scss">
+	.cartf{
+		width: 96rpx;
+		height: 96rpx;
+		background: #FFFFFF;
+		box-shadow: 0 3rpx 16rpx rgba(0, 0, 0, 0.08);
+		border-radius: 50%;
+		font-size: 47rpx;
+		color: #666;
+		position: fixed;
+		right: 15rpx;
+		z-index: 9;
+	}
+</style>

+ 214 - 0
components/mentioned.vue

@@ -0,0 +1,214 @@
+<template>
+	<view :style="viewColor">
+		<view class="container" :class="popup.show==true?'on':''">
+			<view class="header">
+				<text class="title">TA提到的宝贝</text>
+				<text class="iconfont icon-guanbi5" @click="closePopup"></text>
+			</view>
+			<view class="main_count" :class="isHome ? 'mb90' : ''">
+				<scroll-view scroll-y="true">
+					<view v-for="(item, index) in list" :key="index" @click="goDetail(item.spu)" class="list">
+						<view class="pro_list">
+							<view class="picture">
+								<image :src="item.spu && item.spu.image" class="image"></image>
+							</view>
+							<view class="info">
+								<view class="name line2">{{item.spu && item.spu.store_name}}</view>
+								<view class="bottom">
+									<view class="price">¥<text>{{item.spu && item.spu.price}}</text></view>
+									<view class="buy-btn">立即购买</view>
+								</view>
+							</view>
+						</view>
+					</view>
+				</scroll-view>
+			</view>	
+		</view>
+		<view class='mask' catchtouchmove="true" :hidden='popup.show==false' @tap="closePopup"></view>
+	</view>
+	
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { goShopDetail } from '@/libs/order.js'
+	import { mapGetters } from "vuex";
+	export default {
+		computed:{
+			...mapGetters(['viewColor']),
+		},
+		props:{
+			list: {
+				type: Array,
+				default: []
+			},
+			uid: {
+				type: Number,
+			},
+			isHome: {
+				type: Boolean,
+				default: false
+			}	
+		},
+		data() {			
+			return {
+				popup: {
+					show: false
+				},
+			};
+		},
+		methods: {
+			// 点击关闭按钮
+			closePopup() {
+				this.$set(this.popup, 'show', false);
+			},
+			showPopup() {
+				this.$set(this.popup, 'show', true);
+			},
+			goDetail(item){
+				if (item.product_type === 1) {
+					uni.navigateTo({
+						url: `/pages/activity/goods_seckill_details/index?id=${item.product_id}&time=${item.stop_time}&spid=${this.uid}`
+					})
+				} else if (item.product_type === 2) {
+					uni.navigateTo({
+						url: `/pages/activity/presell_details/index?id=${item.activity_id}&spid=${this.uid}`
+					})
+				} else if (item.product_type === 0) {
+					uni.navigateTo({
+						url: `/pages/goods_details/index?id=${item.product_id}&spid=${this.uid}`
+					})	
+				}else if (item.product_type === 4) {
+					uni.navigateTo({
+						url: `/pages/activity/combination_details/index?id=${item.activity_id}&spid=${this.uid}`
+					})
+				}else if (item.product_type === 40) {
+					uni.navigateTo({
+						url: `/pages/activity/combination_status/index?id=${item.activity_id}&spid=${this.uid}`
+					})
+				}	
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+.container{
+	position: fixed;
+	bottom: 0;
+	width: 100%;
+	left: 0;
+	background-color: #ffffff;
+	z-index: 77;
+	border-radius: 16rpx 16rpx 0 0;
+	transform: translate3d(0, 100%, 0);
+	transition: all .3s cubic-bezier(.25, .5, .5, .9);
+	max-height: 1000rpx;
+	&.on {
+		transform: translate3d(0, 0, 0);
+	}
+	.header{
+		position: relative;
+		padding: 40rpx 30rpx;
+		.title{
+			color: #282828;
+			font-size: 30rpx;
+		}
+		.iconfont{
+			color: #8A8A8A;
+			font-size: 28rpx;
+			position: absolute;
+			top: 0;
+			right: 0;
+		}
+		.icon-guanbi5 {
+			right: 20rpx;
+			color: #8a8a8a;
+			font-size: 30rpx;
+			line-height: 30rpx;
+			top: 20rpx;
+			background-color: transparent;
+			font-weight: normal;
+		}
+	}
+	scroll-view{
+		max-height: 800rpx;
+	}
+	.main_count{
+		padding: 0 30rpx 30rpx;
+		max-height: 800rpx;
+		overflow-y: scroll;
+		/* #ifndef MP */
+		&.mb90{
+			margin-bottom: 90rpx;
+		}	
+		/* #endif */
+		.list{
+			margin-bottom: 40rpx;
+			height: auto;
+			flex-direction: row;
+			&:last-child{
+				margin-bottom: 0;
+			}
+		}
+		.pro_list{
+			display: flex;
+			flex-direction: row;
+			.picture,.image,uni-image{
+				width: 200rpx;
+				height: 200rpx;
+				border-radius: 16rpx;
+			}
+			.info{
+				margin-left: 30rpx;
+				position: relative;	
+				.bottom{
+					display: flex;
+					justify-content: space-between;
+					align-items: center;
+					width: 460rpx;
+					position: absolute;
+					left: 0;
+					bottom: 10rpx;
+					flex-direction: row;
+				}
+				.name{
+					color: #282828;
+					font-size: 30rpx;
+					line-height: 45rpx;		
+					display: -webkit-box;
+					width: 460rpx;
+				}
+				.price{
+					color: var(--view-priceColor);
+					font-size: 26rpx;
+					font-weight: bold;
+					flex-direction: row;
+					align-items: flex-end;
+					text{
+						font-size: 34rpx;	
+					}
+				}
+				.buy-btn{
+					color: #fff;
+					background: var(--view-theme);
+					border-radius: 26rpx;
+					width: 140rpx;
+					height: 48rpx;
+					display: flex;
+					align-items: center;
+					justify-content: center;
+					font-size: 22rpx;
+				}
+			}
+		}
+	}
+}
+</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;
+}

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

@@ -0,0 +1,533 @@
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+/**
+ * @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"; }

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

@@ -0,0 +1,1162 @@
+<template>
+  <div class="mpvue-calendar" ref="calendar">
+    <div class="calendar-tools" v-if="!isMonthRange">
+      <div class="calendar-prev" @click="prev">
+        <image :src="arrowLeft" v-if="!!arrowLeft">
+        <i class="iconfont icon-arrow-left" v-else></i>
+      </div>
+      <div class="calendar-next"  @click="next">
+        <image :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>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+  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;
+}

+ 290 - 0
components/orderCoupon/index.vue

@@ -0,0 +1,290 @@
+<template>
+	<view :style="viewColor">
+		<view class='coupon-list-window animated' :class='coupon.status==true?"slideInUp":""'>
+			<view class='title'>
+				<view class="item">{{couponTitle}}<text class='iconfont icon-guanbi' @tap='close'></text></view>
+			</view>
+			<block v-if="couponArr.length">
+				<view class='coupon-list'>
+					<view class='item acea-row row-center-wrapper' :class="item.disabled ? 'disabled' : ''" 
+					 v-for="(item,index) in couponArr" @click="getCouponUser(index,item)" :key='index'>
+						<view v-if="item.coupon.send_type == 5" class='money acea-row row-column row-center-wrapper vip-coupon'>
+							<view>¥<text class='num'>{{item.coupon_price}}</text></view>
+							<view class="pic-num">满{{item.use_min_price}}元可用</view>
+						</view>
+						<view v-else class='money acea-row row-column row-center-wrapper' :style="{ 'background-image': `url(${domain}/static/diy/couponBg${keyColor}.png)` }">
+							<view>¥<text class='num'>{{item.coupon_price}}</text></view>
+							<view class="pic-num">满{{item.use_min_price}}元可用</view>
+						</view>
+						<view class='text'>
+							<view class='condition line1'>
+								<span class='line-title' v-if='item.coupon.type===0'>店铺券</span>
+								<span class='line-title' v-else-if='item.coupon.type===1'>商品券</span>
+								<span>{{item.coupon_title}}</span>
+							</view>
+							<view class='data acea-row row-between-wrapper'>
+								<view>{{ item.start_time | timeYMD }} ~ {{ item.end_time | timeYMD}}</view>
+								<view class="iconfont icon-weixuanzhong" v-if="!item.checked"></view>
+								<view class='iconfont icon-xuanzhong1' v-else></view>
+							</view>
+						</view>
+					</view>
+				</view>
+				<view class="foot-box">
+					<view class="left">
+						已选择{{coupon_number}}张,可优惠<text>¥{{coupon_amount}}</text>
+					</view>
+				</view>
+			</block>
+			<!-- 无优惠券 -->
+			<view class='pictrue' v-else>
+				<image :src="`${domain}/static/images/noCoupon.png`"></image>
+			</view>
+		</view>
+		<view class='mask' catchtouchmove="true" :hidden='coupon.status==false' @click='close'></view>
+	</view>
+</template>
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { setCouponReceive } from '@/api/api.js';
+	import { mapGetters } from "vuex";
+	import { HTTP_REQUEST_URL } from '@/config/app';
+	export default {
+		props: {
+			//打开状态 0=领取优惠券,1=使用优惠券
+			openType: {
+				type: Number,
+				default: 0,
+			},
+			coupon: {
+				type: Object,
+				default: function() {
+					return {};
+				}
+			},
+			coupon_amount: {
+				type: Number
+			},
+			coupon_number: {
+				type: Number
+			},
+			couponTitle: {
+				type: String,
+				default: '优惠券',
+			}
+		},
+		filters: {
+			timeYMD: function(value) {
+				if(value){
+					var newDate=/\d{4}-(\d{1,2}\d{1,2}-\d{1,2}\d{1,2})/g.exec(value)
+					return newDate?.[0]||''
+				}
+			}
+		},
+		computed: mapGetters(['viewColor','keyColor']),
+		data() {
+			return {
+				domain: HTTP_REQUEST_URL,
+				couponArr: [],
+				couponData: {},
+				// 选中的数据存放
+				active: {},
+				allNum: 0,
+				allCouponNum: 0,
+				// 选中店铺优惠券id
+				use_store_coupon: 0,
+				// 单个店铺总价
+				pay_price: 0,
+				// 商品有优惠订单
+				goodsOrder: '',
+				moneyBg: '/static/images/couponBg',
+			};
+		},
+		watch: {
+			coupon:{
+				handler(nVal,oVal){
+					this.couponArr = JSON.parse(JSON.stringify(nVal.coupon))
+				},
+				immediate: true,
+				deep:true
+			}
+		},
+		mounted() {
+			this.couponData = this.coupon
+			// 深拷贝数据 不影响原来数据使用
+			this.couponArr = JSON.parse(JSON.stringify(this.coupon.coupon))
+			// 深拷贝数据 不影响原来数据使用
+			// this.goodsOrder = JSON.parse(JSON.stringify(this.coupon.order))
+			let tempObj = this.active[this.couponData.mer_id] = {}
+			tempObj.product = []
+			tempObj.store = ''
+		},
+		methods: {
+			close: function() {
+				this.$emit('ChangCouponsClose');
+			},
+			// 使用优惠券
+			getCouponUser: function(index, item) {
+				this.$emit('getCoupon',item);
+			}					
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.animated {
+		animation-duration: .3s
+	}
+	.title {
+		display: flex;
+		.item {
+			position: relative;
+			flex: 1;
+			font-size: 28rpx;
+			color: #999999;
+			&::after {
+				content: ' ';
+				position: absolute;
+				left: 50%;
+				bottom: 18rpx;
+				width: 50rpx;
+				height: 5rpx;
+				background: transparent;
+				border-radius: 3px;
+				transform: translateX(-50%);
+			}
+			&.on {
+				color: #282828;
+				&::after {
+					background: var(--view-theme);
+				}
+			}
+			
+		}
+	}
+	.coupon-list {
+		padding: 30rpx;
+		.item {
+			box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.06);
+			&.disabled{
+				pointer-events:none;
+				opacity: .6;
+				.iconfont{
+					background-color: #eee;
+					border-radius: 100%;
+				}
+			}
+		}
+	}
+	.coupon-list-window {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		width: 100%;
+		background-color: #fff;
+		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 {
+		animation: aminup;
+	}
+	.coupon-list-window .title {
+		height: 106rpx;
+		width: 100%;
+		text-align: center;
+		line-height: 106rpx;
+		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 0rpx 0;
+		height: 550rpx;
+		overflow: auto;
+	}
+	.coupon-list-window .pictrue {
+		width: 414rpx;
+		height: 336rpx;
+		margin: 0 auto 50rpx 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 var(--view-theme);
+		opacity: 1;
+		border-radius: 20rpx;
+		font-size: 20rpx;
+		color: var(--view-theme);
+		margin-right: 12rpx;
+	}
+
+	.line-title.gray {
+		border-color: #BBB;
+		color: #bbb;
+		background-color: #F5F5F5;
+	}
+	.foot-box {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		height: 100rpx;
+		padding: 0 30rpx;
+		border-top: 1px solid #F5F5F5;
+		.btn {
+			width: 240rpx;
+			height: 70rpx;
+			line-height: 70rpx;
+			text-align: center;
+			background: var(--view-theme);;
+			border-radius: 35rpx;
+			color: #fff;
+			font-size: 30rpx;
+		}
+		.left {
+			text {
+				color: var(--view-priceColor);
+			}
+		}
+	}
+
+	.coupon-list .item .text .data .iconfont {
+		font-size: 36rpx;
+
+		&.icon-weixuanzhong {
+			color: #BFBFBF;
+		}
+		&.icon-xuanzhong1 {
+			color: var(--view-theme);;
+		}
+	}
+</style>

+ 361 - 0
components/orderGoods/index.vue

@@ -0,0 +1,361 @@
+<template>
+	<view class="orderGoods" :style="viewColor">
+		<view class='goodWrapper' :class="'item'+orderData.order_type">
+			<view v-if="orderData.order_type == 1" class="title acea-row row-between-wrapper">
+				<view class="item-status" :class="'status'+evaluate">{{evaluate == 0 ? '待核销' : '已核销'}}</view>
+				<view v-if="evaluate != 0 && orderData.verify_time" class="item-date">{{orderData.verify_time}}</view>
+			</view>
+			<view v-for="(item,index) in cartInfo" :key="index">
+				<view v-if="activityType === 2">
+					<view class='item presell_item'>
+						<view class="acea-row row-between-wrapper">
+							<view class='pictrue' @click="jumpCon(item)">
+								<image :src='(item.cart_info.productAttr && item.cart_info.productAttr.image) || item.cart_info.product.image' ></image>
+							</view>
+							<view class='text'>
+								<view class='acea-row row-between-wrapper'>
+									<view class='name line1' style="width: 360rpx;"><text class="event_name event_bg">预售</text>{{item.cart_info.product.store_name}}</view>
+									<view class='num'><text class="p-color">¥{{item.cart_info.productPresellAttr.presell_price}}</text><br/>x {{item.product_num}}</view>
+								</view>
+								<view class='attr line1' v-if="item.cart_info.productAttr.sku" style="margin-top: 0;">{{item.cart_info.productAttr.sku}}</view>
+								<view v-if="evaluate === 0 || evaluate === 10 || evaluate === 11" class="event_ship event_color">发货时间:
+									<!--全款预售-->
+									<text v-if="item.cart_info.productPresell.presell_type === 1">{{ item.cart_info.productPresell.delivery_type === 1 ? '支付成功后' : '预售结束后' }}{{ item.cart_info.productPresell.delivery_day }}天内</text>
+									<!--定金预售-->
+									<text v-if="item.cart_info.productPresell.presell_type === 2">{{ item.cart_info.productPresell.delivery_type === 1 ? '支付尾款后' : '预售结束后' }}{{ item.cart_info.productPresell.delivery_day }}天内</text>
+								</view>	
+								<view class="right-btn-box event_box">
+									<view class="btn-item" v-if="item.refund_switch==1 && (item.is_refund ==0 && (evaluate != 10 && evaluate != 11) && orderData.refund_status || item.refund_num > 0)" @click.stop="refund(item)">申请退款</view>
+									<view class="btn-item err" v-if="item.is_refund ==1">退款中 x {{item.product_num - item.refund_num}}</view>
+									<view class='btn-item err' v-if="item.is_refund >1">已退款 x {{item.product_num - item.refund_num}}</view>
+									<view class='btn-item' v-if='item.is_reply==0 && evaluate==2 && item.is_refund==0' @click.stop="evaluateTap(item.order_product_id,orderId)">去评价</view>
+									<view class='btn-item on' v-else-if="item.is_reply==1 && evaluate==2">已评价</view>
+								</view>
+							</view>
+						</view>
+					</view>
+					<view v-if="orderData.status >= 10" class="event_progress">
+						<view class="progress_list">									
+							<view class="progress_name">阶段一: 买家已付款</view>
+							<view class="progress_price">商品定金  <text class="align_right">¥{{ orderData.pay_price }}</text></view>
+							<view class="progress_pay">定金实付款<text class="align_right t-color">¥{{ orderData.pay_price }}</text></view>
+						</view>
+						<view class="progress_list">
+							<view class="progress_name">阶段二: 
+								<text v-if="orderData.status == 10 && orderData.presellOrder.activeStatus == 0">未开始</text>
+								<text v-if="orderData.status == 10 && orderData.presellOrder.activeStatus == 1">等待买家付尾款</text>
+								<text v-if="orderData.status == 11 || orderData.presellOrder.activeStatus == 2">交易已关闭</text>
+							</view>
+							<view class="progress_price">商品尾款  <text class="align_right">¥{{ orderData.presellOrder.pay_price }}</text></view>
+							<view class="progress_pay">尾款需付款<text class="align_right t-color">¥{{ orderData.presellOrder.pay_price }}</text></view>
+						</view>
+					</view>
+				</view>
+				<view v-else class='item'>
+					<view class="acea-row row-between-wrapper">
+						<view class='pictrue' @click="jumpCon(item)">
+							<image :src='(item.cart_info.productAttr && item.cart_info.productAttr.image) || item.cart_info.product.image' ></image>
+						</view>
+						<view class='text'>
+								<view class='acea-row row-between-wrapper'>
+									<view class='name line1'><text v-if="item.product_type != 0 && item.product_type != 10" :class="'font_bg-red type'+item.product_type">{{item.product_type == 1 ? "秒杀" : item.product_type == 2 ? "预售" : item.product_type == 3 ? "助力" : item.product_type == 4 ? "拼团" : ""}}</text>{{item.cart_info.product.store_name}}</view>
+									<view class='num'>x {{item.product_num}}</view>
+								</view>
+								<view class='attr line1' v-if="item.cart_info.productAttr.sku">{{item.cart_info.productAttr.sku}}</view>
+								<view class='money p-color' v-if="item.cart_info.product_type ==3">¥{{item.cart_info.productAssistAttr.assist_price}}</view>
+								<view class='money p-color' v-else-if="item.cart_info.product_type ==4">¥{{item.cart_info.activeSku.active_price}}</view>
+								<view class='money acea-row row-middle' v-else>
+									<text>¥{{item.cart_info.productAttr.price}}</text>
+									<image v-if="item.cart_info.productAttr.show_svip_price" class="svip-img" :src="`${domain}/static/images/svip.png`"></image>
+								</view>
+						</view>
+					</view>
+					<view class="right-btn-box">
+						<view class="btn-item err" v-if="item.is_refund ==1">退款中 x {{item.product_num - item.refund_num}}</view>
+						<view class='btn-item err' v-if="item.is_refund >1">已退款 x {{item.product_num - item.refund_num}}</view>
+						<view class="btn-item" v-if="item.refund_switch==1&& (item.is_refund == 0 && evaluate != 9 && orderData.refund_status || item.refund_num > 0)" @click.stop="refund(item)">申请退款</view>
+						<view class='btn-item' v-if='item.is_reply==0 && evaluate==2 && item.refund_num > 0' @click.stop="evaluateTap(item.order_product_id,orderId)">去评价</view>
+						<view class='btn-item on' v-else-if="item.is_reply==1 && evaluate==2">已评价</view>
+					</view>
+				</view>	
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { openOrderRefundSubscribe } from '@/utils/SubscribeMessage.js';
+	import { goShopDetail } from '@/libs/order.js'
+	import { initiateAssistApi } from '@/api/activity.js';
+	import { mapGetters } from "vuex";
+	import { HTTP_REQUEST_URL } from '@/config/app';	
+	export default {
+		props: {
+			evaluate: {
+				type: Number,
+				default: 0,
+			},
+			activityType: {
+				type: Number,
+				default: 0,
+			},
+			cartInfo: {
+				type: Array,
+				default: function() {
+					return [];
+				}
+			},
+			orderId: {
+				type: String,
+				default: '',
+			},
+			jump: {
+				type: Boolean,
+				default: false,
+			},
+			orderData: {
+				type: Object,
+				default: function() {
+					return {};
+				}
+			}
+		},
+		computed: mapGetters(['viewColor']),
+		data() {
+			return {
+				domain: HTTP_REQUEST_URL,
+				totalNmu:'',
+				isTimePay: false, //是否到支付时间
+			};
+		},
+		watch:{
+			cartInfo:function(nVal,oVal){
+				let num = 0
+				nVal.forEach((item,index)=>{
+					num += item.cart_num
+				})
+				this.totalNmu = num
+			}
+		},
+		onShow() {
+			this.isPayBalance()
+		},
+		mounted() {},
+		methods: {
+			evaluateTap:function(unique,orderId){
+				uni.navigateTo({
+					url:`/pages/users/goods_comment_con/index?uni=${unique}&order_id=${orderId}`
+				})
+			},
+			// 判断是否到支付尾款时间
+			isPayBalance(){
+				let that = this;
+				if(that.orderData.status === 10){
+					if(new Date() < new Date(that.orderData.presellOrder.final_start_time)){
+						that.isTimePay = false; //未开始
+					}else if((new Date() >= new Date(that.orderData.presellOrder.final_start_time)) && (new Date() <= new Date(that.orderData.presellOrder.final_start_time)) ){
+						that.isTimePay = true; //立即支付
+					}
+				}
+			},
+			jumpCon:function(item){
+				if(item.product_type == 4)item.activity_id = item.cart_info && item.cart_info.activeSku.product_group_id
+				if(item.product_type == 3)item.activity_id = item.cart_info && item.cart_info.productAssistAttr.product_assist_id
+				goShopDetail(item).then(res => {
+					initiateAssistApi(item.activity_id).then(res => {
+						let id = res.data.product_assist_set_id;
+							uni.hideLoading();
+							uni.navigateTo({
+								url: '/pages/activity/assist_detail/index?id=' + id
+							});			
+						}).catch((err) => {
+							uni.showToast({
+								title: err,
+								icon: 'none'
+							})	
+					});
+				})	
+			},
+			// 退款
+			refund(item){  
+				// #ifdef MP
+				openOrderRefundSubscribe().then(() => {
+					uni.hideLoading();
+					if(this.evaluate == 0 || this.evaluate == 9 || this.orderData.is_virtual != 0){	
+						uni.navigateTo({
+							url:'/pages/users/refund/confirm?order_id='+this.orderId+'&type=1'+'&ids='+item.order_product_id+'&refund_type=1&order_type='+this.orderData.order_type
+						})
+					}else{
+						uni.navigateTo({
+							url:'/pages/users/refund/select?order_id='+this.orderId+'&type=1&order_type='+this.orderData.order_type+'&ids='+item.order_product_id
+						})
+					}
+				}).catch(() => {
+					uni.hideLoading();
+				})
+				// #endif
+				// #ifdef H5 || APP-PLUS
+				if(this.evaluate == 0 || this.evaluate == 9 || this.orderData.is_virtual != 0){					
+					uni.navigateTo({
+						url:'/pages/users/refund/confirm?order_id='+this.orderId+'&type=1'+'&ids='+item.order_product_id+'&refund_type=1&order_type='+this.orderData.order_type
+					})
+				}else{
+					uni.navigateTo({
+						url:'/pages/users/refund/select?order_id='+this.orderId+'&type=1&order_type='+this.orderData.order_type+'&ids='+item.order_product_id
+					})
+				}
+				// #endif
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.orderGoods {
+		background-color: #fff;
+	}
+	.p-color {
+		color: var(--view-priceColor);
+	}
+	.t-color{
+		color: var(--view-theme);
+	}
+	.svip-img{
+		width: 65rpx;
+		height: 28rpx;
+		margin: 4rpx 0 0 4rpx;
+	}
+	.title{
+		height: 86rpx;
+		position: relative;
+		padding: 0 30rpx;
+		&::after{
+			content: "";
+			width: 750rpx;
+			border-bottom: 2rpx dotted #D8D8D8; 
+			position: absolute;
+			bottom: 0;
+			left: 0;
+		}
+		.item-status{
+			color: #999999;
+			font-size: 30rpx;
+			&.status0{
+				color: #2291F8;
+			}
+		}
+		.item-date{
+			color: #666666;
+			font-size: 28rpx;
+		}
+		}
+	.right-btn-box{
+		display: flex;
+		align-items: center;
+		justify-content: flex-end;
+		&.event_box{
+			position: static;
+		}
+		.btn-item{
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			width:140rpx;
+			height:46rpx;
+			margin-left: 10rpx;
+			border:1px solid rgba(187,187,187,1);
+			border-radius:23rpx;
+			font-size: 24rpx;
+			color: #282828;
+			&.on{
+				background:rgba(220,220,220,1);
+				border-color: rgba(220,220,220,1);
+			}
+			&.err{
+				background:rgba(247,247,247,1);
+				border-color: rgba(247,247,247,1);
+				color: #AAAAAA;
+			}
+		}
+	}
+	.event_bg{
+		background: #FF7F00;
+	}
+	.event_color{
+		color: #FF7F00;
+	}
+	.presell_item{
+		height: auto;
+		padding-bottom: 15rpx;
+	}
+	.event_progress{
+		margin-top: 20rpx;
+		background: #fff;
+		.progress_name {
+			padding-left: 30rpx;
+			height: 60rpx;
+			line-height: 60rpx;
+			font-size: 24rpx;
+			font-weight: bold;
+			position: relative;
+			color: var(--view-theme);
+			&::before{
+				content: "";
+				display: inline-block;
+				width: 5rpx;
+				height: 34rpx;
+				background: var(--view-theme);
+				position: absolute;
+				top: 15rpx;
+				left: 0;
+			}
+		}
+		.align_right{
+			float: right;
+			font-weight: bold;
+		}
+		.progress_price{
+			padding: 20rpx 30rpx;
+			color: #999999;
+			font-size: 22rpx;
+		}
+		.progress_pay{
+			padding: 25rpx 30rpx;
+			background: var(--view-minorColor);
+			font-size: 26rpx;
+			color: #282828;
+		}
+	}
+	.event_name{
+		display: inline-block;
+		margin-right: 9rpx;
+		color: #fff;
+		font-size: 20rpx;
+		padding: 0 8rpx;
+		line-height: 30rpx;	
+		text-align: center;
+		border-radius: 6rpx;						
+	}
+	.event_ship{
+		font-size: 20rpx;
+		margin-top: 10rpx;						
+	}
+	.goodWrapper.item1{
+		&::after{
+			content: "";
+			display: block;
+			width: 750rpx;
+			height: 14rpx;
+			background: #F0F0F0;
+		}			
+	}
+</style>

+ 258 - 0
components/passwordPopup/index.vue

@@ -0,0 +1,258 @@
+<template>
+	<view>
+		<view class='password-window' :class='window==true?"on":""' :style="{ 'background-image': `url(${domain}/static/images/passwordPop.png)`}">
+			<view class='passwordCount'>
+				<view class="title1 acea-row row-between-wrapper">
+					<image v-if="userInfo.avatar" :src="userInfo.avatar" class="picture"></image>
+					<text class="name line1">{{userInfo.nickname}}</text>
+					给你分享了宝贝
+				</view>
+				<view class="banner">
+					<image :src="storeInfo.image"></image>
+				</view>
+				<view class="pro-info">
+					<view class="price acea-row">¥<text class="money">{{storeInfo.price}}</text>
+						<text v-if="storeInfo.product_type == 0" class="pro_type">普通商品</text>
+					</view>
+					<view class="name line1">{{storeInfo.store_name}}</view>
+				</view>
+				<button v-if="storeInfo.product_type != 3" class="go_btn" @click="goDetail(storeInfo)">查看详情</button>
+				<button v-if="storeInfo.product_type == 3" class="go_btn btn1" @click="goDetail(storeInfo)">帮他助力</button>
+			</view>
+			<view class='lid'>
+				<view class='iconfont icon-guanbi3' @click="close"></view>
+			</view>
+		</view>
+		
+		<view class='mask' catchtouchmove="true" :hidden="window==false" style="z-index: 999;"></view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { pwdResolution } from '@/api/user.js'
+	import { mapGetters } from "vuex";
+	import {toLogin} from '@/libs/login.js';
+	import { goShopDetail } from '@/libs/order.js'
+	import {initiateAssistApi} from '@/api/activity.js';
+	import {HTTP_REQUEST_URL} from '@/config/app.js';
+	export default {
+		props: {
+			isLogin:{
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+				window: false,
+				couponList: [],
+				userInfo: {},
+				storeInfo: {},
+				pwdInfo: {},
+				domain: HTTP_REQUEST_URL,
+			};
+		},
+		computed: mapGetters(["copyPwd", "uid"]),
+		watch: {
+			copyPwd:{
+				handler(nVal,oVal){
+					if(nVal)this.resolution(nVal)
+				},
+				immediate: true,
+				deep:true
+			}
+		},
+		mounted(){		
+		},
+		beforeDestroy(){
+			
+		},
+		methods: {
+			close:function(){
+			  this.window = false
+			},
+			resolution(data){
+				let com = uni.getStorageSync('pwdKey')
+				pwdResolution(data).then(res => {
+					if(res.data.user && res.data.user.uid != this.uid && com !=res.data.com){
+						this.window = true;
+						this.pwdInfo = res.data;
+						this.userInfo = res.data.user;
+						this.storeInfo = res.data.data;
+						if(res.data.activity_id )this.storeInfo.activity_id = res.data.activity_id 
+						if(res.data.product_type )this.storeInfo.product_type = res.data.product_type 
+						this.$store.commit("PARSE_PWD", null)
+						uni.setStorageSync('pwdKey', res.data.com);
+					}
+				})
+			},
+			goDetail(item){
+				goShopDetail(item, this.uid).then(res => {
+					this.$store.commit("PARSE_PWD", null)
+					if (this.isLogin) {
+							initiateAssistApi(item.activity_id).then(res => {
+								let id = res.data.product_assist_set_id;
+								uni.hideLoading();
+								uni.navigateTo({
+									url: '/pages/activity/assist_detail/index?id=' + id
+								});					
+							}).catch((err) => {
+								uni.showToast({
+									title: err,
+									icon: 'none'
+								})
+							});
+						} else {
+							toLogin();	
+						}
+					})		
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.password-window {
+		background-repeat: no-repeat;
+		background-size: 100% 100%;
+		background-color: #fff;
+		width: 580rpx;
+		height: 910rpx;
+		position: fixed;
+		top: 50%;
+		z-index: 1000;
+		left: 50%;
+		margin-left: -290rpx;
+		margin-top: -455rpx;
+		transform: translate3d(0, -200%, 0);
+		transition: all .3s cubic-bezier(.25, .5, .5, .9);
+		border-radius: 24rpx;
+		padding: 25rpx 30rpx 40rpx;
+	}
+	
+	.password-window.on {
+		transform: translate3d(0, 0, 0);
+	}
+	.passwordCount .title1{
+		width: 380rpx;
+		margin: 0 auto;
+		height: 42rpx;
+		background: #F1F1F1;
+		border-radius: 23rpx;
+		padding: 0 15rpx;
+		color: #666666;
+		font-size: 24rpx;
+		.picture{
+			width: 36rpx;
+			height: 36rpx;
+			border-radius: 100%;
+			image{
+				width: 36rpx;
+				height: 36rpx;
+			}
+		}
+		.name{
+			color: #282828;
+			font-weight: bold;
+			max-width: 100rpx;
+			margin: 0 10rpx;
+		}
+	}
+	.password-window .banner{
+		width: 520rpx;
+		height: 520rpx;
+		margin-top: 25rpx;
+		image{
+			width: 520rpx;
+			height: 520rpx;
+			border-radius: 16rpx;
+		}
+	}
+	.password-window .pro-info{
+		margin-top: 15rpx;
+		.price{
+			color: #E93323;
+			font-size: 26rpx;
+			justify-content: left;
+			align-items: center;
+			.money{
+				font-size: 42rpx;
+			}
+		}
+		.pro_type{
+			display: inline-block;
+			width: 100rpx;
+			height: 28rpx;
+			text-align: center;
+			line-height: 28rpx;
+			background: #FDEAE8;
+			margin-left: 30rpx;
+			font-size: 18rpx;
+			position: relative;
+			&::before{
+				content: "";
+				display: inline-block;
+				width: 0;
+				height: 0;
+				border-width: 15rpx 15rpx 15rpx 0;
+				border-style: solid;
+				border-color: transparent #FDEAE8 transparent transparent;
+				position: absolute;
+				left: -16rpx;
+			}
+			&::after{
+				content: "";
+				display: inline-block;
+				width: 6rpx;
+				height: 6rpx;
+				background: #fff;
+				border-radius: 100%;
+				position: absolute;
+				top: 12.5rpx;
+				left: -4rpx;
+				z-index: 10;
+			}
+		}
+		.name{
+			margin-top: 15rpx;
+			color: #282828;
+			font-size: 30rpx;
+		}
+	}
+	.password-window .lid {
+		position: absolute;
+		bottom: -100rpx;
+		left: 0;
+		width: 100%;
+		text-align: center;
+	}
+	.go_btn{
+		margin-top: 50rpx;
+		width: 520rpx;
+		height: 80rpx;
+		line-height: 80rpx;
+		text-align: center;
+		background: #E93323;
+		color: #ffffff;
+		border-radius: 40rpx;
+		font-size: 28rpx;
+		&.btn1{
+			background: #FC8327
+		}
+	}
+	.password-window .lid .iconfont {
+		color: #fff;
+		font-size: 60rpx;
+		text-align: center;
+		margin-left: -30rpx;
+	}
+</style>

+ 466 - 0
components/payment/index.vue

@@ -0,0 +1,466 @@
+<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" v-if="item.payStatus==1"
+			 :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" ref="close" @click='close' v-if="pay_close"></view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import {
+		orderPay,
+		integralOrderPay,
+		presellOrderPay
+	} from '@/api/order.js';
+	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'
+			},
+			order_type: {
+				type: Number,
+				default: 0,
+			},
+			isCall: {
+				type: Boolean,
+				default: false
+			},
+			returnUrl: {
+				type: String,
+				default: ''
+			}
+		},
+		data() {
+			return {
+
+			};
+		},
+		mounted:function(){
+			
+		},
+		methods: {
+			close: function() {
+				this.$emit('onChangeFun', {
+					action: 'payClose'
+				});
+			},
+			goPay: function(number, paytype) {
+				if (this.isCall) {
+					return this.$emit('onChangeFun', {
+						action: 'payCheck',
+						value: paytype
+					});
+				}
+				let that = this;
+				let type = ''
+				if (paytype == 'wechat') {
+					// #ifdef H5
+					type = this.$wechat.isWeixin() ? 'weixin' : 'h5';
+					// #endif
+					// #ifdef APP-PLUS
+					type = 'weixin';
+					// #endif
+					// #ifdef MP
+					type = 'routine';
+					// #endif
+				} else if (paytype == 'balance') {
+					type = 'balance';
+				}else if(paytype == 'alipay'){
+					// #ifndef MP
+					type = 'alipay';
+					// #endif					
+					// #ifdef MP
+					type = 'alipayQr';
+					// #endif
+				}
+				if (!that.order_id) return that.$util.Tips({
+					title: '请选择要支付的订单'
+				});
+				if (paytype == 'balance' && parseFloat(number) < parseFloat(that.totalPrice)) return that.$util.Tips({
+					title: '余额不足!'
+				});
+				uni.showLoading({
+					title: '支付中'
+				});
+				let orderApi = that.order_type === 1 ? presellOrderPay : that.order_type === 2 ? integralOrderPay : orderPay		
+				orderApi(that.order_id, {
+					type: type,
+					// #ifdef H5
+					return_url: that.returnUrl!=='' ? 'http://'+window.location.host+that.returnUrl : 'http://'+window.location.host+'/pages/users/order_list/index',
+					// #endif
+				}).then(res => {
+					let status = res.data.status,
+						orderId = res.data.result.order_id,
+						jsConfig = res.data.result.config,
+						callback_key = res.data.result.pay_key,
+						goPages = that.returnUrl ? that.returnUrl : '/pages/users/order_list/index';
+					switch (status) {			
+						case 'ORDER_EXIST':
+						case 'EXTEND_ORDER':
+						case 'PAY_ERROR':
+						case 'error':
+							uni.hideLoading();
+							this.$emit('onChangeFun', {
+								action: 'payClose'
+							});
+							return that.$util.Tips({
+								title: res.message
+							});							
+							break;
+						case 'success':
+							uni.hideLoading();
+							this.$emit('onChangeFun', {
+								action: 'payClose'
+							});							
+							return that.$util.Tips({
+								title: '支付成功',
+								icon: 'success'
+							}, {
+								tab: 5,
+								url: goPages + '?status=1'
+							});
+							break;
+						case 'alipay':
+						case 'alipayQr':
+							uni.hideLoading();
+							this.$emit('onChangeFun', {
+								action: 'payClose'
+							});				
+							uni.navigateTo({
+								url: '/pages/order_pay_back/index?keyCode='+callback_key+'&url='+jsConfig,
+							})	
+							return 								
+							break;	
+						// #ifndef MP
+						case "wechat":
+						case "weixin":
+						case "weixinApp":
+							jsConfig.timeStamp = jsConfig.timestamp;
+							// #ifndef APP-PLUS
+							that.$wechat.pay(jsConfig).then(res => {	
+								console.log('测试支付数据无效的success:'+res.data)
+								this.$emit('onChangeFun', {
+									action: 'payClose'
+								});
+								uni.hideLoading();
+								return that.$util.Tips({
+									title: '支付成功',
+									icon: 'success'
+								}, {	
+									tab: 5,
+									url: goPages + 'status=1'
+								});
+							}).catch(res => {
+								console.log('测试支付数据无效的catch:'+res.data)
+								if (res.errMsg == 'chooseWXPay:cancel'){
+									if(that.isCall){
+										return that.$util.Tips({
+											title: '取消支付'
+										});
+									}else{
+										return that.$util.Tips({
+											title: '取消支付'
+										}, {	
+											tab: 5,
+											url: goPages + '?status=0'
+										});
+									}
+								} 
+							})
+							// #endif
+							// #ifdef APP-PLUS
+							let mp_pay_name=''
+							if(uni.requestOrderPayment){
+								mp_pay_name='requestOrderPayment'
+							}else{
+								mp_pay_name='requestPayment'
+							}
+							uni[mp_pay_name]({
+								provider: 'wxpay',
+								orderInfo: jsConfig,
+								success: (e) => {
+									this.$emit('onChangeFun', {
+										action: 'payClose'
+									});
+									return that.$util.Tips({
+										title: '支付成功',
+										icon: 'success'
+									}, {	
+										tab: 5,
+										url: goPages + 'status=1'
+									});
+								},
+								fail: (e) => {
+									if(that.isCall){
+										return that.$util.Tips({
+											title: '取消支付'
+										});
+									}else{
+										return that.$util.Tips({
+											title: '取消支付'
+										}, {	
+											tab: 5,
+											url: goPages + '?status=0'
+										});
+									}
+								},
+								complete: () => {
+									uni.hideLoading();
+								},
+							});
+							// #endif
+							break;
+						// #endif
+						// #ifdef MP
+						case "routine":
+							jsConfig.timeStamp = jsConfig.timestamp;
+							that.toPay = true;
+							let mp_pay_name=''
+							if(uni.requestOrderPayment){
+								mp_pay_name='requestOrderPayment'
+							}else{
+								mp_pay_name='requestPayment'
+							}
+							uni[mp_pay_name]({
+								...jsConfig,
+								success: function(res) {
+									uni.hideLoading();
+									that.$emit('onChangeFun', {
+										action: 'payClose'
+									});
+									if (that.BargainId || that.combinationId || that.pinkId || that.seckillId)
+										return that.$util.Tips({
+											title: '支付成功',
+											icon: 'success'
+										}, {	
+											tab: 5,
+											url: goPages + '?status=1'
+										});
+									
+									return that.$util.Tips({
+										title: '支付成功',
+										icon: 'success'
+									}, {		
+										tab: 5,
+										url: goPages + '?status=1'
+									});
+								},
+								fail: function(e) {
+									uni.hideLoading();
+									that.$emit('onChangeFun', {
+										action: 'payClose'
+									});
+									return that.$util.Tips({
+										title: '取消支付'
+									});
+								},
+								complete: function(e) {
+									uni.hideLoading();									
+									//关闭当前页面跳转至订单状态
+									if (res.errMsg == 'requestPayment:cancel') return that.$util.Tips({
+										title: '取消支付'
+									});
+									that.$emit('onChangeFun', {
+										action: 'payClose'
+									});
+								},
+							})
+							break;
+						// #endif
+						case "balance":
+							uni.hideLoading();
+							that.$emit('onChangeFun', {
+								action: 'payClose'
+							});
+							//余额不足
+							return that.$util.Tips({
+								title: res.message
+							});
+							break;
+						// #ifdef H5
+						case 'h5':
+							let host = window.location.protocol+"//"+window.location.host;
+							let url = `${host}/pages/order_pay_status/index?order_id=${orderId}`
+							let eUrl = encodeURIComponent(url)
+							let jsurl = jsConfig.mweb_url || jsConfig.h5_url
+							let locations = `${jsurl}&redirect_url=${eUrl}`
+							setTimeout(() => {
+								location.href = locations;
+							}, 100);
+							break;
+						// #endif	
+							
+						// #ifdef APP-PLUS
+						case 'alipayApp':
+							uni.requestPayment({
+								provider: 'alipay',
+								orderInfo: jsConfig,
+								success: (e) => {
+									that.$emit('onChangeFun', {
+										action: 'payClose'
+									});
+									return that.$util.Tips({
+										title: '支付成功',
+										icon: 'success'
+									}, {	
+										tab: 5,
+										url: goPages + 'status=1'
+									});
+							
+								},
+								fail: (e) => {
+									return that.$util.Tips({
+										title: '取消支付'
+									});
+								},
+								complete: () => {
+									uni.hideLoading();
+								},
+							});
+						break;
+						// #endif
+					}
+				}).catch(err => {
+					uni.hideLoading();
+					return that.$util.Tips({
+						title: err
+					});
+				})
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.payment {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		width: 100%;
+		max-height: 600rpx;
+		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: 1px 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: 1px 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>

+ 152 - 0
components/plantConSwiper/index.vue

@@ -0,0 +1,152 @@
+<template>
+	<view class='product-bg'>
+		<swiper :indicator-dots="indicatorDots" indicator-active-color="#e93323" :circular="circular"
+		 :interval="interval" :duration="duration">	 
+			<block v-for="(item,index) in imgUrls" :key='index'>
+				<swiper-item>
+					<image :src="item" class="slide-image" @click='getpreviewImage(item)' mode="aspectFit" />
+					<!-- <view class="stop" v-if="isType == 2">
+						<image class="image" src="/static/images/stop.png"></image>
+					</view> -->
+				</swiper-item>
+			</block>
+		</swiper>
+	</view>
+</template>
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	export default {
+		props: {
+			imgUrls: {
+				type: Array,
+				default: function() {
+					return [];
+				}
+			},
+			videoline: {
+				type: String,
+				value: ""
+			},
+			isType: {
+				type: Number,
+				default: 1
+			}
+		},
+		data() {
+			return {
+				indicatorDots: true,
+				circular: true,
+				autoplay: true,
+				interval: 5000,
+				duration: 500,
+				currents: "1",
+				controls: true,
+				isPlay: true,
+				videoContext: ''
+			};
+		},
+		created() {
+		},
+		watch: {
+
+		},
+		mounted() {
+			// #ifndef APP-PLUS
+			this.videoContext = uni.createVideoContext('myVideo', this);
+			// #endif
+		},
+		methods: {
+			getpreviewImage: function(item) {
+				uni.previewImage({
+					urls: this.imgUrls,
+					current: item
+				});
+			},
+			
+			change: function(e) {
+				this.$set(this, 'currents', e.detail.current + 1);
+			}
+
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.product-bg {
+		width: 710rpx;
+		height: 710rpx;
+		position: relative;
+	}
+	.product-bg swiper {
+		width: 100%;
+		height: 100%;
+		position: relative;
+	}
+	.product-bg .slide-image {
+		width: 100%;
+		height: 100%;
+		border-radius: 16rpx;
+	}
+
+	.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>

+ 238 - 0
components/plantWaterfallsFlow/WaterfallsFlow.vue

@@ -0,0 +1,238 @@
+<template>
+    <view class="wf-page">
+			<!--left-->
+			<view>
+				<view id="left" v-if="leftList.length">
+						<view v-for="(item,index) in leftList" :key="index"
+									class="wf-item" @tap="itemTap(item)">
+								<WaterfallsFlowItem :item="item" :isAuth="isAuth" :uid="uid" :isShow="isShow" :tab="tab" @likeToggle="(item)=>likeToggle(false, index, item)"/>
+						</view>
+				</view>
+			</view>
+			<!--right-->
+			<view>
+				<view id="right" v-if="rightList.length">
+						<view v-for="(item,index) in rightList" :key="index"
+									class="wf-item" @tap="itemTap(item)">
+								<WaterfallsFlowItem :item="item" :isAuth="isAuth" :uid="uid" :isShow="isShow" :tab="tab" @likeToggle="(item)=>likeToggle(true, index, item)"/>
+						</view>
+				</view>
+			</view>
+		
+    </view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+import WaterfallsFlowItem from '../plantWaterfallsFlowItem/WaterfallsFlowItem.vue'
+import { graphicStartApi } from '@/api/community.js';
+import { mapGetters } from "vuex";
+import { toLogin } from '@/libs/login.js';
+export default {
+    components: {
+      WaterfallsFlowItem,
+    },
+    props: {
+        // 瀑布流列表
+        wfList: {
+					type: Array,
+					require: true
+        },
+        updateNum: {
+					type: Number,
+					default: 10
+        },
+				isAuth: {
+					type: Number || String,
+					default: 0
+				},
+				tab: {
+					type: Number,
+					default: 1
+				},
+				uid: {
+					type: Number,
+					default: 0
+				},
+				isShow: {
+					type: Boolean,
+					default: false
+				},
+    },
+    data() {
+			return {
+				allList: [],       // 全部列表
+				leftList: [],      // 左边列表
+				rightList: [],     // 右边列表
+				mark: 0,           // 列表标记
+				boxHeight: [],     // 下标0和1分别为左列和右列高度
+			};
+    },
+    watch: {
+		// 监听列表数据变化
+		'wfList.length':  {
+			handler(nVal,oVal){
+				// 如果数据为空或新的列表数据少于旧的列表数据(通常为下拉刷新或切换排序或使用筛选器),初始化变量
+				if (!this.wfList.length ||
+					(this.wfList.length === this.updateNum && this.wfList.length <= this.allList.length)) {
+					this.allList = [];
+					this.leftList = [];
+					this.rightList = [];
+					this.boxHeight = [];
+					this.mark = 0;
+				}
+				// 如果列表有值,调用waterfall方法
+				if (this.wfList.length) {
+				    this.allList = this.wfList;
+					this.leftList = [];
+					this.rightList = [];
+					this.boxHeight = [];
+					this.allList.forEach((v, i) => {
+						if(this.allList.length < 3 || (this.allList.length <= 7  && this.allList.length - i > 1) || (this.allList.length > 7 && this.allList.length - i > 2)) {
+							if(i % 2){
+								this.rightList.push(v);
+							}else{
+								this.leftList.push(v);
+							}
+						}
+					});
+					if(this.allList.length < 3){
+						this.mark = this.allList.length+1;
+					}else if(this.allList.length <= 7){
+						this.mark = this.allList.length - 1;
+					}else{
+						this.mark = this.allList.length - 2;
+					}
+					if(this.mark < this.allList.length){
+						this.waterFall()
+					}
+				}
+			},
+			immediate: true,
+			deep:true
+    },
+		mounted(){
+		},
+		// 监听标记,当标记发生变化,则执行下一个item排序
+		mark() {
+			const len = this.allList.length;
+			if (this.mark < len && this.mark !== 0 && this.boxHeight.length) {
+					this.waterFall();
+			}
+		}
+  },
+	computed: {
+		...mapGetters(['isLogin']),
+	},
+	methods: {
+		// 瀑布流排序
+		waterFall() {
+			const i = this.mark;
+			if(!this.allList[i]) return ;
+				if (i == 0) {
+					// 初始化,从左边开始插入
+					this.leftList.push(this.allList[i]);
+					// 更新左边列表高度
+					this.getViewHeight(0);
+				} else if (i == 1) {
+					// 第二个item插入,默认为右边插入
+					this.rightList.push(this.allList[i]);
+					// 更新右边列表高度
+					this.getViewHeight(1);
+				} else {
+					// 根据左右列表高度判断下一个item应该插入哪边
+					if(!this.boxHeight.length){
+						this.rightList.length < this.leftList.length
+						? this.rightList.push(this.allList[i])
+						: this.leftList.push(this.allList[i]);
+					} else {
+						const leftOrRight = this.boxHeight[0] > this.boxHeight[1] ? 1 : 0;
+						if (leftOrRight) {
+							this.rightList.push(this.allList[i])
+						} else {
+							this.leftList.push(this.allList[i])
+						}
+					}
+					// 更新插入列表高度
+					this.getViewHeight();
+				}
+			},
+		// 获取列表高度
+		getViewHeight() {
+			// 使用nextTick,确保页面更新结束后,再请求高度
+			this.$nextTick(() => {
+				setTimeout(()=>{
+					uni.createSelectorQuery().in(this).select('#right').boundingClientRect(res => {
+							res ? this.boxHeight[1] = res.height : '';
+						uni.createSelectorQuery().in(this).select('#left').boundingClientRect(res => {
+							res ? this.boxHeight[0] = res.height : '';
+							this.mark = this.mark + 1;
+						}).exec();
+					}).exec();
+				},100)
+			})
+		},
+		// item点击
+		itemTap(item) {
+			this.$emit('itemTap', item)
+		},
+		giveStart(item){
+			let status = item.relevance_id ? 0 : 1
+			graphicStartApi(item.community_id,{status: status}).then(res => {
+				if(item.relevance_id){
+					item.relevance_id = !item.relevance_id;
+					item.count_start--;
+					item.count_start = item.count_start == 0 ? 0 : item.count_start
+				}else{
+					item.relevance_id = !item.relevance_id;
+					item.count_start++;
+				}
+			}).catch(err => {
+				uni.showToast({
+					title: err,
+					icon: 'none'
+				})
+			});
+		},
+		likeToggle(type, index){
+			if(this.isLogin == true){
+				if(type){
+					this.giveStart(this.rightList[index])
+				}else{
+					this.giveStart(this.leftList[index])
+				}
+			}else{
+				toLogin()
+			}
+		},
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+$page-padding: 20rpx;
+$grid-gap: 20rpx;
+
+.wf-page {
+    display: grid;
+    grid-template-columns: 1fr 1fr;
+    grid-gap: $grid-gap;
+}
+.wf-item {
+    width: calc((100vw - 2 * #{$page-padding} - #{$grid-gap}) / 2);
+    margin-bottom: $grid-gap;
+	background: #fff;
+	border-radius: 16rpx;
+}
+.wf-item-page{
+	padding-bottom: 20rpx;
+}
+</style>

+ 249 - 0
components/plantWaterfallsFlowItem/WaterfallsFlowItem.vue

@@ -0,0 +1,249 @@
+<template>  
+	<view v-if="item" @click="goDetail(item)" class="wf-page2" :style="viewColor">
+		<view class='pictrue skeleton-rect'>
+			<!-- <image :src='item.image[0]' mode="widthFix"></image> -->
+			<easy-loadimage class="image" mode="widthFix" :image-src="item.image[0]"></easy-loadimage>
+			<view v-if="isShow" class="plant-show">
+				<text v-if="item.is_type == 1" class="iconfont icon-tuwen1"></text>
+				<text v-else-if="item.is_type == 2" class="iconfont icon-shipin1"></text>
+			</view>
+			<view v-else-if="item.is_type == 2" class="video_img">
+				<image class="image" src="../../static/images/stop.png" mode=""></image>
+			</view>
+			<view v-if="isAuth && tab == 0 && item.status != 1" class="approval_status">
+				<view v-if="item.status == -1" class="approval_title">审核未通过</view>
+				<view v-else-if="item.status == -2" class="approval_title">已下架</view>
+				<view v-else-if="item.status == 0" class="approval_title">正在审核</view>
+				<text v-if="item.status == -1 || item.status == -2" class="approval_info">查看未通过原因</text>
+				<text v-if="item.status == 0" class="approval_info">通过后将展示在列表</text>
+			</view>
+		</view>
+		<view class='text'>
+			<view class='name skeleton-rect'>
+				<text class="text_name line2">{{item.title}}
+				</text>				
+			</view>
+			<view class="count acea-row skeleton-rect">
+				<view v-if="!isAuth" class="author acea-row">
+					<image class="image" :src="(item.author && item.author.avatar) || '/static/images/f.png'"></image>
+					<text class="author_name line1">{{item.author && item.author.nickname}}</text>
+				</view>
+				<view v-else class="author acea-row">
+					<text class="author_time">
+					{{item.time}}
+					</text>
+				</view>
+				<view class="like">
+					<button class="like_count" hover-class="none" @click.stop="likeToggle(item)">
+						<text class="iconfont" :class="item.relevance_id ? 'icon-shoucang1' : 'icon-dianzan'"></text>
+						<text class="collect">{{item.count_start}}</text>
+					</button>
+				</view>
+			</view>
+		</view>	
+	</view>
+</template>
+<script>
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------	
+import easyLoadimage from '@/components/easy-loadimage/easy-loadimage.vue'
+import { mapGetters } from "vuex";
+export default {
+	components:{easyLoadimage},
+	computed: {...mapGetters(['viewColor'])},
+    props: {
+			item: {
+					type: Object,
+					require: true
+			},
+			type: {
+				type: Number,
+				default: 0
+			},
+			isAuth: {
+				type: Number || String,
+				default: 0
+			},
+			uid: {
+				type: Number || String,
+				default: 0
+			},
+			tab: {
+				type: Number,
+				default: 1
+			},
+			isFind: {
+				type: Boolean,
+				default: false
+			},
+			isShow: {
+				type: Boolean,
+				default: false
+			},
+    },
+	data() {
+		return {
+		};
+	},
+	
+	methods: {	
+		likeToggle(item){
+			this.$emit('likeToggle',item)
+		},
+		goDetail(item){
+			if(this.isFind){
+				uni.navigateTo({
+					url: '/pages/plantGrass/plant_featured/index?id='+item.topic_id
+				});	
+			}else{
+				this.plantDeail(item)
+			}
+		},
+		plantDeail(item){
+			if(item.is_type == 1){
+				uni.navigateTo({
+					url: '/pages/plantGrass/plant_detail/index?id='+item.community_id
+				});
+			}else{
+				let user = this.isAuth ? 1 : 0;
+				console.log(this.uid)
+				uni.navigateTo({
+					//#ifdef APP
+					url: '/pages/short_video/appSwiper/index?id='+item.community_id+'&user='+user+'&uid='+this.uid+'&tab='+this.tab
+					//#endif
+					//#ifndef APP
+					url: '/pages/short_video/nvueSwiper/index?id='+item.community_id+'&user='+user+'&uid='+this.uid+'&tab='+this.tab
+					//#endif
+				});
+			}
+		}
+	}
+		
+}
+
+</script>
+<style lang="scss" scoped>
+.wf-page2 .pictrue{
+	width: 345rpx;
+	height: 345rpx;
+	border-radius: 16rpx;
+	position: relative;
+	/deep/image,/deep/.easy-loadimage,/deep/.image{
+		width: 100%;
+		max-width: 345rpx;
+		height: 345rpx;
+		border-radius: 16rpx;
+	}
+	.video_img{
+		width: 40rpx;
+		height: 40rpx;
+		position: absolute;
+		top: 10rpx;
+		right: 10rpx;
+		z-index: 100;
+		.image,image,uni-image{
+			width: 40rpx;
+			height: 40rpx;
+		}
+	}
+	.plant-show{
+		width: 42rpx;
+		height: 42rpx;
+		border-radius: 100%;
+		background: rgba(0,0,0,.5);
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		position: absolute;
+		top: 10rpx;
+		left: 10rpx;
+		.iconfont{
+			font-size: 20rpx;
+			color: #fff;
+			
+		}
+	}
+}
+ .loadfail-img{
+	width: 100%;
+  height: 345rpx;
+}
+.wf-page2 .text{
+	padding: 20rpx;
+	// width: 345rpx;
+	.name{
+		color: #333333;
+		font-size: 28rpx;
+		font-weight: bold;
+	}
+	.count{
+		margin-top: 20rpx;
+		justify-content: space-between;
+		.author{
+			align-items: center;
+		}
+		.author_name{
+			margin-left: 10rpx;
+			max-width: 120rpx;
+			font-size: 24rpx;
+			color: #333333;
+			max-width: 180rpx;
+		}
+		.author_time{
+			color: #666666;
+		}
+		.like{	
+			font-size: 24rpx;
+			color: #999999;
+			display: flex;
+			.like_count{
+				display: flex;
+				align-items: center;
+			}
+			.iconfont{
+				font-size: 30rpx;
+			}
+			.icon-shoucang1{
+				color: var(--view-priceColor);
+			}
+			.collect{
+				font-size: 24rpx;
+				margin-left: 5rpx;
+			}
+		}
+	}
+}
+.wf-page2 .author .image,.wf-page2 .author uni-image{
+	width: 46rpx;
+	height: 46rpx;
+	border-radius: 100%;
+}
+.approval_status{
+	width: 100%;
+	height: 100%;
+	background: rgba(0,0,0,.4);
+	position: absolute;
+	top: 0;
+	left: 0;
+	border-radius: 16rpx;
+	color: #fff;
+	text-align: center;
+	z-index: 5;
+	.approval_title{
+		font-size: 28rpx;
+		margin-top: 135rpx;
+		font-weight: bold;
+	}
+	.approval_info{
+		font-size: 24rpx;
+		margin-top: 24rpx;
+	}
+}
+</style>

+ 203 - 0
components/privacyAgreementPopup/index.vue

@@ -0,0 +1,203 @@
+<template>
+	<view :style="viewColor">
+		<view class="mask" @touchmove.prevent :hidden="isShow == false" @click="rejectAgreement"></view>
+		<view class="product-window" :class="{'on':isShow}">
+			<view class="mp-data">
+				<text class="mp-name">{{site_name}}服务与隐私协议</text>
+			</view>
+			<view class="trip-msg">
+				<view class="trip">
+					欢迎您使用{{site_name}}!请仔细阅读以下内容,并作出适当的选择:
+				</view>
+			</view>
+			<view class="trip-title">
+				隐私政策概要
+			</view>
+			<view class="trip-msg">
+				<view class="trip">
+					当您点击同意并开始时用产品服务时,即表示您已理解并同息该条款内容,该条款将对您产生法律约束力。如您拒绝,将无法继续下一步操作。
+				</view>
+			</view>
+			<view class="main-color" @click.stop="privacy(3)">点击阅读{{agreementName}}</view>
+			<view class="bottom">
+				<button class="save open" type="default" id="agree-btn" open-type="agreePrivacyAuthorization"
+					@agreeprivacyauthorization="handleAgree">同意并继续</button>
+				<button class="reject" @click="rejectAgreement">
+					取消
+				</button>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------	
+	const app = getApp();
+	import { mapGetters } from 'vuex';
+	import { configMap } from "@/utils";
+	import Routine from '../../libs/routine';
+	export default {
+		data() {
+			return {
+				isShow: false,
+				agreementName: '',
+				mpData: uni.getStorageSync('copyRight'),
+			};
+		},
+		computed: {
+			...configMap({site_name: ''}, mapGetters(['viewColor'])),
+		},
+		mounted() {
+			wx.getPrivacySetting({
+				success: res => {
+					console.log(res.needAuthorization)
+					if (res.needAuthorization) {
+						// 需要弹出隐私协议
+						this.isShow = true
+						this.agreementName = res.privacyContractName
+					} else {
+						this.$emit('onAgree');
+						// 用户已经同意过隐私协议,所以不需要再弹出隐私协议,也能调用已声明过的隐私接口
+					}
+				},
+				fail: () => {},
+				complete: () => {}
+			})
+		},
+		methods: {
+			// 同意
+			handleAgree() {
+				this.isShow = false
+				this.$emit('onclose');			
+			},
+			// 拒绝
+			rejectAgreement() {
+				this.isShow = false
+				this.$emit('onReject');
+			},
+			// 跳转协议
+			privacy(type) {
+				uni.navigateTo({
+					url: "/pages/users/privacy/index?type=" + type
+				})
+			},
+		}
+	}
+</script>
+<style>
+	.pl-sty {
+		color: #999999;
+		font-size: 30rpx;
+	}
+</style>
+<style scoped lang="scss">
+	.product-window.on {
+		transform: translate3d(0, 0, 0);
+	}
+	.product-window {
+		position: fixed;
+		bottom: 0;
+		width: 100%;
+		left: 0;
+		background-color: #fff;
+		z-index: 1000;
+		border-radius: 40rpx 40rpx 0 0;
+		transform: translate3d(0, 100%, 0);
+		transition: all .3s cubic-bezier(.25, .5, .5, .9);
+		padding: 64rpx 40rpx;
+		padding-bottom: 38rpx;
+		padding-bottom: calc(38rpx+ constant(safe-area-inset-bottom)); ///兼容 IOS<11.2/
+		padding-bottom: calc(38rpx + env(safe-area-inset-bottom)); ///兼容 IOS>11.2/
+		box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.06);
+
+		.icon-guanbi {
+			position: absolute;
+			top: 40rpx;
+			right: 40rpx;
+			font-size: 24rpx;
+			font-weight: bold;
+			color: #999;
+		}
+
+		.mp-data {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			margin-bottom: 40rpx;
+
+			.mp-name {
+				font-size: 34rpx;
+				font-weight: 500;
+				color: #333333;
+				line-height: 48rpx;
+			}
+		}
+
+		.trip-msg {
+			padding-bottom: 32rpx;
+
+			.title {
+				font-size: 30rpx;
+				font-weight: bold;
+				color: #000;
+				margin-bottom: 6rpx;
+			}
+
+			.trip {
+				color: #333333;
+				font-size: 28rpx;
+				font-family: PingFang SC-Regular, PingFang SC;
+				font-weight: 400;
+			}
+		}
+
+		.trip-title {
+			font-size: 28rpx;
+			font-weight: 500;
+			color: #333333;
+			margin-bottom: 8rpx;
+		}
+
+		.main-color {
+			font-size: 28rpx;
+			font-weight: 400;
+			color: var(--view-theme);
+			margin-bottom: 40rpx;
+		}
+
+		.bottom {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			flex-direction: column;
+
+			.save,
+			.reject {
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				width: 670rpx;
+				height: 80rpx;
+				border-radius: 80rpx;
+				background-color: #F5F5F5;
+				color: #333;
+				font-size: 30rpx;
+				font-weight: 500;
+			}
+
+			.save {
+				background-color: var(--view-theme);
+				color: #fff;
+				margin-bottom: 24rpx;
+			}
+		}
+	}
+</style>

+ 199 - 0
components/productConSwiper/index.vue

@@ -0,0 +1,199 @@
+<template>
+	<view class='product-bg'>
+		<swiper :indicator-dots="indicatorDots" indicator-active-color="#e93323" :autoplay="autoplay" :circular="circular"
+		 :interval="interval" :duration="duration" @change="change" v-if="isPlay">
+			<!-- #ifndef APP-PLUS -->
+			<swiper-item v-if="videoline && videoline != '' && videoline != 'https:'">
+				<view class="item">
+					<video id="myVideo" :src='videoline && (videoline.substring(0,4) == "http" || videoline.substring(0,5) == "https") ? videoline : ("http:" + videoline)'
+					 objectFit="cover" controls style="width:100%;height:100% " show-mute-btn="true"
+					 auto-pause-if-navigate @ended="endedFun()"></video>
+					<view class="poster" v-if="controls">
+						<image class="image" :src="videoCoverImg"></image>
+					</view>
+					<view class="stop" v-if="controls" @tap="bindPause">
+						<image class="image" src="../../static/images/stop.png"></image>
+					</view>
+				</view>
+			</swiper-item>
+			<!-- #endif -->
+			<!-- #ifdef APP-PLUS -->
+			<swiper-item v-if="videoline">
+				<view class="item">
+					<view class="poster" v-show="controls">
+						<image class="image" :src="videoCoverImg"></image>
+					</view>
+					<view class="stop" v-show="controls" @tap="bindPause">
+						<image class="image" src="../../static/images/stop.png"></image>
+					</view>
+				</view>
+			</swiper-item>
+			<!-- #endif -->
+			<block v-for="(item,index) in imgUrls" :key='index'>
+				<swiper-item>
+					<image :src="item" class="slide-image" @click='getpreviewImage(item)' mode="widthFix" />
+				</swiper-item>
+			</block>
+		</swiper>
+		<!-- #ifdef APP-PLUS -->
+		<view v-if="!isPlay" style="width: 100%; height: 750rpx;">
+			<video id="myVideo" :src='videoline && (videoline.substring(0,4) == "http" || videoline.substring(0,5) == "https") ? videoline : "http:" + videoline'
+			 objectFit="cover" controls style="width:100%;height:100% " show-center-play-btn show-mute-btn="true"
+			 auto-pause-if-navigate :enable-progress-gesture="false" :poster="videoCoverImg" @pause="videoPause" @ended="endedFun()"></video>
+
+		</view>
+		<!-- #endif -->
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	export default {
+		props: {
+			imgUrls: {
+				type: Array,
+				default: function() {
+					return [];
+				}
+			},
+			videoline: {
+				type: String,
+				value: ""
+			},
+			videoCoverImg: {
+				type: String,
+			}
+		},
+		data() {
+			return {
+				indicatorDots: true,
+				circular: true,
+				autoplay: true,
+				interval: 5000,
+				duration: 500,
+				currents: "1",
+				controls: true,
+				isPlay: true,
+				videoContext: ''
+			};
+		},
+		created() {
+
+		},
+		watch: {
+
+		},
+		mounted() {
+			// #ifndef APP-PLUS
+			this.videoContext = uni.createVideoContext('myVideo', this);
+			// #endif
+		},
+		methods: {
+			getpreviewImage: function(item) {
+				uni.previewImage({
+					urls: this.imgUrls,
+					current: item
+				});
+			},
+			videoPause(e) {
+				// #ifdef APP-PLUS
+				this.isPlay = true
+				this.autoplay = true
+				// #endif
+			},
+			bindPause: function() {
+				// #ifndef APP-PLUS
+				this.videoContext.play();
+				this.$set(this, 'controls', false)
+				this.autoplay = false
+				// #endif
+
+				// #ifdef APP-PLUS
+				this.isPlay = false
+				this.videoContext = uni.createVideoContext('myVideo', this);
+				this.$nextTick(()=>{
+					this.videoContext.play();
+				})
+				// #endif
+			},
+			change: function(e) {
+				this.$set(this, 'currents', e.detail.current + 1);
+			},
+			endedFun() {
+				this.controls = true
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.product-bg {
+		width: 100%;
+		height: 750rpx;
+		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>

+ 463 - 0
components/productWindow/index.vue

@@ -0,0 +1,463 @@
+<template>
+	<view :style="viewColor">
+		<view class="product-window" :class="(attr.cartAttr === true ? 'on' : '') + ' ' + (iSbnt || destri?'join':'') + ' ' + (isPresell ?'presell-window':'')">
+			<view class="textpic acea-row row-between-wrapper">
+				<view class="pictrue">
+					<image :src="attr.productSelect.image ? attr.productSelect.image : image" @click="loookImg"></image>
+				</view>
+				<view class="text">
+					<view class="line1">
+						{{ attr.productSelect.store_name }}
+					</view>
+					<view v-if="isPresell" class="money presell_price">
+						<view>
+							预售价
+							¥<text class="num">{{ attr.productSelect.price }}</text>
+							<text v-if="presell_type === 2">定金¥<text class="num">{{ attr.productSelect.down_price }}</text></text>
+						</view>
+						<text class="stock" v-if='isShow'>库存: {{ attr.productSelect.stock }}</text>
+						<text class='stock' v-if="limitNum">限量: {{attr.productSelect.quota_show}}</text>
+					</view>
+					<view v-else class="money">
+						<view class="acea-row row-middle">
+							<view class="acea-row row-middle">
+								<view v-if="type=='points'" class="points_money">
+									<image :src="`${domain}/static/images/jf-point.png`" mode=""></image>
+									<text class="points-num">{{attr.productSelect.ot_price}}</text>积分 <text v-if="attr.productSelect.price > 0">+{{attr.productSelect.price}}元</text>
+									<text v-if="limitCount > 0" class="points-limit">限购{{limitCount}}件</text>
+								</view>
+								<view v-else>
+									<text>¥</text><text class="num">{{ attr.productSelect.price }}</text>
+								</view>
+								<view v-if="svipPrice" class="acea-row row-middle">
+									<text class='vip-money'>¥{{attr.productSelect.svip_price}}</text>
+									<view class="vipImg">
+										<image :src="`${domain}/static/images/svip.png`"></image>
+									</view>
+								</view>
+							</view>
+						</view>
+						<view class="stock_count">
+							<text class="stock" v-if='isShow'>库存: {{ attr.productSelect.stock }}</text>
+							<text class='stock' v-if="limitNum">限量: {{attr.productSelect.quota_show}}</text>
+						</view>
+					</view>
+				</view>
+				<view class="iconfont icon-guanbi" @click="closeAttr"></view>
+			</view>
+			<view class="productWinList">
+				<view class="item" v-for="(item, indexw) in attr.productAttr" :key="indexw">
+					<view class="title">{{ item.attr_name }}</view>
+					<view class="listn acea-row row-middle">
+						<view class="itemn" :class="item.index === itemn.attr ? 'on' : ''" v-for="(itemn, indexn) in item.attr_value"
+						 @click="tapAttr(indexw, indexn)" :key="indexn">
+							{{ itemn.attr }}
+						</view>
+					</view>
+				</view>
+			</view>
+			<view v-if="type !='points'" class="cart acea-row">
+				<view class="title">数量</view>
+				<view class="carnum acea-row row-left">
+					<view class="buy_limit" v-if="minCount>0 || maxCount>0">
+						(<text v-if="minCount>0">{{minCount}}件起购<text v-if="minCount>0 && maxCount>0">,</text></text><text v-if="maxCount>0">最多{{maxCount}}件</text>)
+					</view>
+					<view class="item reduce" :class="(attr.productSelect.cart_num <= 1 || (minCount>0 && attr.productSelect.cart_num<=minCount)) ? '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)" @keydown="btKeyDown" @keyup="btKeyUp"></input>
+					</view>
+					<view v-if="iSplus" class="item plus" :class="(attr.productSelect.cart_num >= attr.productSelect.stock || destri || (maxCount>0&&attr.productSelect.cart_num>=maxCount)) ? 'on' : ''"
+					 @click="CartNumAdd">+</view>
+					<view v-else class='item plus' :class='((attr.productSelect.cart_num >= attr.productSelect.quota_show) 
+						|| (attr.productSelect.cart_num >= attr.productSelect.product_stock) || (maxCount>0&&attr.productSelect.cart_num>=maxCount)) ? "on":""'
+					 @click='CartNumAdd'>+</view>
+				</view>
+			</view>
+			<view class="joinBnt b-color" v-if="destri && attr.productSelect.stock>0" @click="goCat">立即购买</view>
+			<view class="joinBnt on" v-else-if="destri && attr.productSelect.stock<=0">已售罄</view>
+			<view class="joinBnt b-color" v-if="iSbnt && attr.productSelect.stock>0"
+			 @click="goCat">我要参团</view>
+			<view class="joinBnt on" v-else-if="(iSbnt && attr.productSelect.stock<=0)">已售罄</view>
+		</view>
+		<view class="mask" @touchmove.prevent :hidden="attr.cartAttr === false" @click="closeAttr"></view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { mapGetters } from "vuex";
+	export default {
+		props: {
+			attr: {
+				type: Object,
+				default: () => {}
+			},
+			limitNum: {
+				type: Number,
+				value: 0
+			},
+			limitCount: {
+				type: Number || String,
+				value: 0
+			},
+			isShow: {
+				type: Number,
+				value: 0
+			},
+			iSbnt: {
+				type: Number,
+				value: 0
+			},
+			iSplus: {
+				type: Number,
+				value: 0
+			},
+			destri: {
+				type: Number,
+				value: 0
+			},
+			isPresell: {
+				type: Number,
+				value: 0
+			},
+			presell_type: {
+				type: Number,
+				value: 1
+			},
+			image: {
+				type: String,
+				value: ''
+			},
+			maxCount: {
+				type: Number,
+				value: 0
+			},
+			minCount: {
+				type: Number,
+				value: 0
+			},
+			payLimit: {
+				type: Number || String,
+				value: 1
+			},
+			svipPrice: {
+				type: Boolean,
+				value: false
+			},
+			type: {
+				type: String,
+				value: ''
+			},
+			domain: {
+				type: String,
+				value: ''
+			}
+		},
+		computed: mapGetters(['viewColor']),
+		data() {
+			return {
+			};
+		},
+		mounted(){
+			console.log(this.maxCount)
+		},
+		methods: {
+			//  查看大图
+			loookImg(){
+				let self = this
+				let arr = [self.attr.productSelect.image ? self.attr.productSelect.image : self.image]
+				uni.previewImage({
+					urls: arr,
+				});
+			},
+			goCat: function() {
+				this.$emit('goCat');
+			},
+			/**
+			 * 购物车手动输入数量
+			 * 
+			 */
+			bindCode: function(e) {
+				let num = JSON.parse(JSON.stringify(e))
+				this.$emit('iptCartNum', num);
+			},
+			btKeyDown(e) {
+				e.target.value = e.target.value.replace(/[^\a-\z\A-\Z0-9\u4E00-\u9FA5]/g,"");
+			},
+			btKeyUp(e) {
+				e.target.value = e.target.value.replace(/[`~!@#$%^&*()_\-+=<>?:"{}|,.\/;'\\[\]·~!@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、]/g,"")
+			},
+			closeAttr: function() {
+				this.$emit('myevent');
+			},
+			CartNumDes: function() {
+				if (!this.destri) {
+					this.$emit('ChangeCartNum', false);
+				}
+			},
+			CartNumAdd: function() {
+				if (!this.destri) {
+					this.$emit('ChangeCartNum', true);
+				}
+			},
+			tapAttr: function(indexw, indexn) {
+				let that = this;
+				that.$emit("attrVal", {
+					indexw: indexw,
+					indexn: indexn
+				});
+				this.$set(this.attr.productAttr[indexw], 'index', this.attr.productAttr[indexw].attr_values[indexn]);
+				let value = that.getCheckedValue().join(",");
+				that.$emit("ChangeAttr", value);
+			},
+			//获取被选中属性;
+			getCheckedValue: function() {
+				let productAttr = this.attr.productAttr;
+				let value = [];
+				for (let i = 0; i < productAttr.length; i++) {
+					for (let j = 0; j < productAttr[i].attr_values.length; j++) {
+						if (productAttr[i].index === productAttr[i].attr_values[j]) {
+							value.push(productAttr[i].attr_values[j]);
+						}
+					}
+				}
+				return value;
+			}
+		}
+	}
+</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;
+		padding-bottom: calc(140rpx+ constant(safe-area-inset-bottom)); ///兼容 IOS<11.2/
+		padding-bottom: calc(140rpx + env(safe-area-inset-bottom)); ///兼容 IOS>11.2/
+		transform: translate3d(0, 100%, 0);
+		transition: all .3s cubic-bezier(.25, .5, .5, .9);
+		&.presell-window {
+			padding-bottom: 200rpx;
+			padding-bottom: calc(200rpx+ constant(safe-area-inset-bottom)); ///兼容 IOS<11.2/
+			padding-bottom: calc(200rpx + env(safe-area-inset-bottom)); ///兼容 IOS>11.2/
+		}
+	}
+	.product-window.on {
+		transform: translate3d(0, 0, 0);
+	}
+	.product-window.join {
+		padding-bottom: 30rpx;
+	}
+	.product-window .textpic {
+		padding: 0 80rpx 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: 460rpx;
+		font-size: 32rpx;
+		color: #202020;
+	}
+	.product-window .textpic .text .money {
+		font-size: 24rpx;
+		margin-top: 26rpx;
+		font-weight: 700;
+		color: var(--view-priceColor);
+	}
+	.product-window .textpic .text .money .points_money {
+		font-weight: normal;
+		display: flex;
+		align-items: baseline;
+	}
+	.product-window .textpic .text .money .points_money image{
+		width: 26rpx;
+		height: 26rpx;
+	}
+	.product-window .textpic .text .money .points_money .points-num{
+		font-size: 34rpx;
+	}
+	.product-window .textpic .text .money .points_money .points-limit{
+		display: flex;
+		padding: 0 10rpx;
+		align-items: center;
+		justify-content: center;
+		color: var(--view-theme);
+		border: 1px solid var(--view-theme);
+		height: 30rpx;
+		border-radius: 13rpx;
+		background: var(--view-bgColor);
+		margin-left: 10rpx;
+		font-size: 18rpx;
+	}
+	.product-window .stock_count{
+		margin-top: 5rpx;
+	}
+	.product-window .textpic .text .money .num {
+		font-size: 36rpx;
+	}
+	.product-window .textpic .text .money .stock {
+		color: #999;
+		margin-left: 18rpx;
+		font-weight: normal;
+	}
+	.product-window .textpic .text .presell_price {
+		color: #FF7F00;
+		.num {
+			display: inline-block;
+			margin-right: 20rpx;
+		}
+		.stock {
+			margin-left: 0;
+		}
+	}
+	.vip-money {
+		color: #282828;
+		font-size: 22rpx;
+		margin-left: 6rpx;
+	}
+	.vipImg {
+		width: 65rpx;
+		height: 28rpx;
+		margin-left: 4rpx;
+		image {
+			width: 100%;
+			height: 100%;
+			display: block;
+		}
+	}
+	.product-window .textpic .iconfont {
+		position: absolute;
+		right: 30rpx;
+		top: -5rpx;
+		font-size: 35rpx;
+		color: #8a8a8a;
+	}
+	.product-window .productWinList {
+		max-height: 395rpx;
+		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 #bbb;
+		font-size: 26rpx;
+		color: #282828;
+		padding: 7rpx 33rpx;
+		border-radius: 6rpx;
+		margin: 14rpx 0 0 14rpx;
+	}
+	.product-window .productWinList .item .listn .itemn.on {
+		color: #fff;
+		background-color: var(--view-theme);
+		border-color: var(--view-theme);
+	}
+	.product-window .cart {
+		margin-top: 36rpx;
+		padding: 0 30rpx;
+		display: -webkit-box;
+		display: -webkit-flex;
+		display: -ms-flexbox;
+		display: flex;
+		-webkit-box-pack: justify;
+		-webkit-justify-content: space-between;
+		-ms-flex-pack: justify;
+		justify-content: space-between;
+	}
+	.product-window .cart .title {
+		font-size: 30rpx;
+		color: #999;
+		line-height: 54rpx;
+	}
+	.product-window .cart .carnum {
+		height: 54rpx;
+		align-items: center;
+	}
+	.product-window .cart .carnum .buy_limit {
+		font-size: 22rpx;
+		color: var(--view-theme);
+		margin-right: 30rpx;
+	}
+	.product-window .cart .carnum .item {
+		border: 1px solid #a4a4a4;
+		width: 84rpx;
+		text-align: center;
+		height: 100%;
+		line-height: 54rpx;
+		color: #a4a4a4;
+		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;
+	}
+	.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 {
+		color: #282828;
+		font-size: 28rpx;
+	}
+	.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;
+		&.b-color{
+			background-color: var(--view-theme);
+		}
+	}
+	.product-window .joinBnt.on {
+		background-color: #bbb;
+		color: #fff;
+		background-image: none;
+	}
+</style>

+ 114 - 0
components/recommend/index.vue

@@ -0,0 +1,114 @@
+<template>
+	<view class='recommend'>
+		<view class="common-hd">
+			<view class="title" :style="{ 'background-image': `url(${domain}/static/images/index-title.png)`}">为你推荐</view>
+		</view>
+		<view class='recommendList' :class="indexP?'on':''">
+			<WaterfallsFlow :wfList='hostProduct' @itemTap="goDetail" :type="0" />
+		</view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import {mapGetters} from "vuex";
+	import { goShopDetail } from '@/libs/order.js'
+	import {initiateAssistApi} from '@/api/activity.js';
+	import {toLogin} from '@/libs/login.js';
+	import WaterfallsFlow from '@/components/WaterfallsFlow/WaterfallsFlow.vue'
+	import { HTTP_REQUEST_URL } from '@/config/app';
+	export default {
+		components:{WaterfallsFlow},
+		computed: mapGetters(['uid']),
+		props: {
+			hostProduct: {
+				type: Array,
+				default: function() {
+					return [];
+				}
+			},
+			indexP:{
+				type: Boolean,
+				default: false
+			},
+			isLogin:{
+				type: Boolean,
+				default: false
+			}
+		},
+		data() {
+			return {
+				domain: HTTP_REQUEST_URL,
+			};
+		},
+		mounted() {
+
+		},
+		onPullDownRefresh(){
+			// 模拟上拉刷新
+			setTimeout(()=>{
+				const newList = this.hostProduct.reverse();
+				this.hostProduct = newList;
+				uni.stopPullDownRefresh();
+			},500)
+		},
+		methods: {
+			goDetail(item){
+				goShopDetail(item, this.uid).then(res => {
+				if (this.isLogin) {
+						initiateAssistApi(item.activity_id).then(res => {
+							let id = res.data.product_assist_set_id;
+							uni.hideLoading();
+							uni.navigateTo({
+								url: '/pages/activity/assist_detail/index?id=' + id
+							});		
+						}).catch((err) => {
+							uni.showToast({
+								title: err,
+								icon: 'none'
+							})
+						});
+					} else {
+						toLogin();	
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.common-hd {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		height: 118rpx;
+		.title {
+			padding: 0 80rpx;
+			font-size: 34rpx;
+			color: $theme-color;
+			font-weight: bold;
+			background-repeat: no-repeat;
+			background-size: 100% auto;
+			background-position: left center;
+		}
+	}
+	.recommend .recommendList {
+		padding: 0 20rpx;
+		min-height: 100rpx;
+	}
+	.recommend .recommendList.on{
+		padding: 0;
+	}
+	/deep/.looming-gray{
+		border-radius: 16rpx 16rpx 0 0;
+	}
+</style>

+ 446 - 0
components/rightSlider.vue

@@ -0,0 +1,446 @@
+<template>
+	<view class="right-wrapper" @touchmove.stop.prevent="moveStop" :style="viewColor">
+		<view class="control-wrapper animated" :class="showBox?'slideInRight':''">
+			<view class="wrapper-count">
+				<view class="header">
+					<view class="title">价格区间</view>
+					<view class="input-wrapper">
+						<input placeholder="最低价" v-model="min" type="number"/>
+						<view class="line"></view>
+						<input placeholder="最高价" v-model="max" type="number"/>
+					</view>
+				</view>
+				<view class="store_type content-border">
+					<view class="title">商户类别</view>
+					<view class="brand-wrapper">
+						<view class="wrapper">			
+							<view class="item line1" v-for="(item,index) in storeCateList" :key="index" :class="item.check?'on':''" @tap="bindChenckType(item,index)">
+								{{item.name}}
+							</view>				
+						</view>
+					</view>
+				</view>
+				<view class="content-box">
+					<view class="content-title">
+						<view class="title">品牌</view>
+						<view v-if="list.length>3">
+							<view class="btns" @click="isShowBrand = !isShowBrand">{{isShowBrand?'收起':'展开'}}<text class="iconfont" :class="isShowBrand ? 'icon-xiangshang' : 'icon-xiangxia'"></text></view>
+						</view>
+					</view>
+					<view class="brand-wrapper">
+						<scroll-view :style="{'max-height':isShowBrand?'90%':'100rpx'}" :scroll-y="isShowBrand">
+							<view class="wrapper">			
+								<view class="item line1" v-for="(item,index) in list" :key="index" :class="item.check?'on':''" @tap="bindChenck(item,'brand')">
+									{{item.brand_name}}
+								</view>				
+							</view>
+						</scroll-view>
+					</view>
+				</view>
+				<!--店铺类型-->
+				<view class="content-box">	
+					<view class="content-title">
+						<view class="title">店铺类型</view>
+						<view v-if="storeTypeList.length>3">
+							<view class="btns" @click="isShowType = !isShowType">{{isShowType?'收起':'展开'}}<text class="iconfont" :class="isShowType ? 'icon-xiangshang' : 'icon-xiang'"></text></view>
+						</view>
+					</view>
+					<view class="brand-wrapper">
+						<scroll-view :scroll-y="isShowType">
+							<view class="wrapper">			
+								<view class="item line1" v-for="(item,index) in storeTypeList" :key="index" :class="item.check?'on':''" @tap="bindChenck(item)">
+									{{item.type_name}}
+								</view>				
+							</view>
+						</scroll-view>	
+					</view>
+				</view>	
+				<!--商品参数-->
+				<view class="content-border">
+					<scroll-view style="max-height:400rpx" scroll-y="true">
+						<view class="content-box" v-for="(item,index) in productParmasList">
+							<view class="content-title">
+								<view class="title">{{item.name}}</view>
+								<view>
+									<view class="btns" @click="getParmasValue(item)">{{item.showValue?'收起':'展开'}}<text class="iconfont" :class="item.showValue ? 'icon-xiangshang' : 'icon-xiangxia'"></text></view>
+								</view>
+							</view>
+							<view v-if="item.parmasValue.length>0 && item.showValue" class="brand-wrapper">
+								<scroll-view :scroll-y="isShowParmas">
+									<view class="wrapper">		
+										<view class="item line1" v-for="(itm,idx) in item.parmasValue" :key="idx" :class="itm.check?'on':''" @tap="bindChenck(itm)">
+											{{itm.value}}
+										</view>				
+									</view>
+								</scroll-view>
+							</view>
+						</view>
+					</scroll-view>	
+				</view>
+			</view>
+			<view class="foot-btn">
+				<view class="btn-item" @click="reset">重置</view>
+				<view class="btn-item confirm" @click="confirm">确定</view>
+			</view>
+		</view>
+		<view class="right-bg" @click="close"></view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { mapGetters } from "vuex";
+	import { getParmasValue } from '@/api/store.js';
+	export default{
+		props: {
+			brandList: {
+				type: Array,
+			},
+			storeTypeArr: { //店铺类型
+				type: Array,
+			},
+			parmasList: { //商品参数
+				type: Array,
+			},
+			status:{
+				type:Boolean,
+				default:false
+			},
+			isCate:{
+				type:Boolean,
+				default:false
+			},
+			price_on:{
+				type:String,
+				default:''
+			},
+			price_off:{
+				type:String,
+				default:''
+			},
+			activeIndex:{
+				type:Number,
+				default:0
+			}
+		},
+		computed: mapGetters(['viewColor']),
+		data(){
+			return {
+				min: '',
+				max:'',
+				is_trader: '',
+				isShow:false,
+				isShowBrand: false,
+				isShowType: false,
+				isShowParmas: false,
+				list:[],
+				storeTypeList: [],
+				productParmasList: [],
+				storeCateList: [
+					{name: '全部', value: '',check: true},
+					{name: '自营', value: 'trader',check: false},
+					{name: '非自营', value: 'trader',check: false},
+				],
+				activeList:[],
+				showBox:false,
+				index: this.activeIndex
+			}
+		},
+		mounted() {
+			// 重要组件挂载后
+			this.list =  this.brandList
+			this.storeTypeList = this.storeTypeArr
+			this.productParmasList = this.parmasList
+			this.showBox = this.status
+			this.min = this.price_on
+			this.max = this.price_off
+			this.bindChenckType({},this.activeIndex)
+		},
+		methods:{	
+			bindChenck(item,key){
+				item.check = !item.check
+				// this.arrFilter()
+			},	
+			bindChenckType(item,index){	
+				this.storeCateList = [
+					{name: '全部', value: '',check: false},
+					{name: '自营', value: 'trader',check: false},
+					{name: '非自营', value: 'trader',check: false},
+				]
+				this.storeCateList[index]['check'] = true
+				this.is_trader = this.storeCateList[0]['check'] ? '' : this.storeCateList[1]['check'] ? 1 : 0
+				this.index = index
+			}, 
+			/*获取参数值*/
+			getParmasValue(item){
+				item.showValue = !item.showValue
+				getParmasValue(item.parameter_id).then(res => {
+					res.data.forEach((data, index) => {
+						data.check = false
+					})
+					if(item.parmasValue.length>0)return
+					item.parmasValue =  res.data
+				});
+			},
+			arrFilter(list){
+				let arr = []
+				arr = list.filter(item=>{
+					return item.check == true
+				})
+				return arr
+			},
+			reset(){
+				this.list.forEach((el,index)=>{
+					el.check = false
+				})
+				this.storeTypeList.forEach((el,index)=>{
+					el.check = false
+				})
+				this.productParmasList.forEach((item,index)=>{
+					item.parmasValue.forEach((el,idx)=>{
+						el.check = false
+					})
+				})
+				this.storeCateList = [
+					{name: '全部', value: '',check: true},
+					{name: '自营', value: 'trader',check: false},
+					{name: '非自营', value: 'trader',check: false}
+				]
+				this.min = this.max = ''
+				// this.arrFilter()
+			},
+			confirm(){
+				let parmas = {}
+				this.productParmasList.forEach((item,index)=>{
+					parmas[item.parameter_id]=[]
+					item.parmasValue.forEach((el,idx)=>{
+						if(el.check){
+							parmas[item.parameter_id].push(el.value)
+						}
+					})
+				})
+				let obj = {
+					brandList: this.arrFilter(this.list),
+					typeList: this.arrFilter(this.storeTypeList),
+					parmasList: parmas,
+					price_on: this.min,
+					price_off: this.max,
+					status: false,
+					is_trader: this.is_trader
+				}
+				this.showBox = false
+				this.$emit('confirm',obj,this.index)				
+			},
+			close(){
+				this.showBox = false
+				this.$emit('close')
+			},
+			moveStop(){}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.slideInRight{
+		animation-duration:.5s
+	}
+	.right-wrapper{
+		z-index: 99;
+		position: fixed;
+		left: 0;
+		top: 0;
+		width: 100%;
+		height: 100%;
+		.control-wrapper{
+			z-index: 90;
+			position: absolute;
+			right: 0;
+			top: 0;
+			display: flex;
+			flex-direction: column;
+			width: 635rpx;
+			height: 100%;
+			background-color: #FFFFFF;
+			.wrapper-count{
+				height: calc(100% - 120rpx);
+				overflow-y: auto;
+			}
+			.header{
+				padding: 50rpx 26rpx 40rpx;
+				background-color: #fff;
+				.content-title{
+					display: flex;
+					align-items: center;
+					justify-content: space-between;
+				}
+				.title{
+					font-size: 26rpx;
+					font-weight: bold;
+					color: #282828;
+				}	
+				.input-wrapper{
+					display: flex;
+					align-items: center;
+					justify-content: space-between;
+					margin-top: 28rpx;
+					input{
+						width:260rpx;
+						height:56rpx;
+						padding: 0 10rpx;
+						background:rgba(242,242,242,1);
+						border-radius:28rpx;
+						font-size: 22rpx;
+						text-align: center;
+					}
+					.line{
+						width:15rpx;
+						height:2rpx;
+						background:#7D7D7D;
+					}
+				}
+			}
+			.content-border{
+				border-top: 20rpx solid #f5f5f5;
+			}
+			.content-box{
+				position: relative;
+				display: flex;
+				flex-direction: column;
+				padding: 0 26rpx;		
+				.content-title{
+					display: flex;
+					align-items: center;
+					justify-content: space-between;
+					padding: 40rpx 0 20rpx;
+					.title{
+						font-size: 26rpx;
+						font-weight: bold;
+						color: #282828;
+					}
+					.btns{
+						display: flex;
+						align-items: center;
+						justify-content: center;
+						font-size: 22rpx;
+						color: #999;
+						.iconfont{
+							margin-left: 10rpx;
+							margin-top: 5rpx;
+							font-size: 20rpx;
+						}
+					}
+				}
+				.brand-wrapper{
+					flex: 1;
+					overflow: hidden;
+					.wrapper{
+						display: flex;
+						flex-wrap: wrap;
+						padding-bottom: 20rpx;
+					}
+					.item{
+						display: block;
+						width:186rpx;
+						height:56rpx;
+						line-height: 56rpx;
+						text-align: center;
+						background:rgba(242,242,242,1);
+						border-radius:28rpx;
+						margin-top: 25rpx;
+						padding: 0 10rpx;
+						margin-right: 12rpx;
+						&:nth-child(3n){
+							margin-right: 0;
+						}
+						&.on{
+							background: var(--view-minorColor);
+							border:1px solid var(--view-theme);
+							color: var(--view-theme);
+						}
+					}		
+				}			
+			}
+			.foot-btn{
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				padding-bottom: 30rpx;
+				position: absolute;
+				bottom: 0;
+				left: 20rpx;		
+				.btn-item{
+					display: flex;
+					align-items: center;
+					justify-content: center;
+					width:286rpx;
+					height:68rpx;
+					background:rgba(255,255,255,1);
+					border:1px solid rgba(170,170,170,1);
+					border-radius:34rpx;
+					font-size: 26rpx;
+					color: #282828;
+					&.confirm{
+						background: var(--view-theme);
+						border-color: var(--view-theme);
+						color: #fff;
+						margin-left: 20rpx;
+					}
+				}
+			}
+			.store_type{
+				position: relative;
+				border-top: 20rpx solid #f5f5f5;
+				padding: 0 26rpx;		
+				.title{
+					padding: 40rpx 0 20rpx;
+					font-size: 26rpx;
+					font-weight: bold;
+					color: #282828;
+				}
+				.brand-wrapper{
+					overflow: hidden;
+					.wrapper{
+						display: flex;
+						flex-wrap: wrap;
+					}
+					.item{
+						display: block;
+						width:186rpx;
+						height:56rpx;
+						line-height: 56rpx;
+						text-align: center;
+						background:rgba(242,242,242,1);
+						border-radius:28rpx;
+						margin-top: 25rpx;
+						padding: 0 10rpx;
+						margin-right: 12rpx;
+						&:nth-child(3n){
+							margin-right: 0;
+						}
+						&.on{
+							background: var(--view-minorColor);
+							border:1px solid var(--view-theme);
+							color: var(--view-theme);
+						}
+					}		
+				}			
+			}
+		}
+		.right-bg{
+			position: absolute;
+			left: 0;
+			top: 0;
+			width: 100%;
+			height: 100%;
+			background-color: rgba(0,0,0,.5);
+		}
+	}
+</style>

+ 283 - 0
components/rightSlider/index.vue

@@ -0,0 +1,283 @@
+
+<template>
+	<view class="right-wrapper" @touchmove.stop.prevent="moveStop" :style="viewColor">
+		<view class="control-wrapper animated" :class="showBox?'slideInRight':''">		
+			<view class="wrapper-count">
+				<view class="content-box">
+						<view class="content-title">
+							<view class="title">店铺类型</view>
+							<view v-if="list.length>20">
+								<view class="btns" @click="isShowType = !isShowType">{{isShowType?'收起':'展开'}}<text class="iconfont" :class="isShowType ? 'icon-xiangshang' : 'icon-xiangxia'"></text></view>
+							</view>
+						</view>
+						<view class="brand-wrapper">
+							<scroll-view style="max-height: 400rpx;" :scroll-y="isShow">
+								<view class="wrapper">	
+									<view class="item line1" v-for="(item,index) in list" :key="index" :class="item.check?'on':''" @tap="bindChenck1(item)">
+										{{item.type_name}}
+									</view>				
+								</view>
+							</scroll-view>
+						</view>
+					</view>
+					<view class="content-box">
+						<view class="content-title">
+							<view class="title">商户分类</view>
+							<view v-if="merCate.length>20">
+								<view class="btns" @click="isShowCate = !isShowCate">{{isShowCate?'收起':'展开'}}<text class="iconfont" :class="isShowCate ? 'icon-xiangshang' : 'icon-xiangxia'"></text></view>
+							</view>
+						</view>
+						<view class="brand-wrapper">
+							<scroll-view style="max-height: 400rpx;" :scroll-y="isShow">
+								<view class="wrapper">			
+									<view class="item line1" v-for="(item,index) in merCate" :key="index" :class="item.check?'on':''" @tap="bindChenck2(item)">
+										{{item.category_name}}
+									</view>				
+								</view>
+							</scroll-view>
+						</view>
+					</view>	
+				</view>
+				<view class="foot-btn">
+					<view class="btn-item" @click="reset">重置</view>
+					<view class="btn-item confirm" @click="confirm">确定</view>
+				</view>
+			</view>
+		<view class="right-bg" @click="close"></view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { mapGetters } from "vuex";
+	export default{
+		props: {
+			storeTypeArr: { //店铺类型
+				type: Array,
+			},
+			merList: { //商户分类
+				type: Array,
+			},
+			status:{
+				type:Boolean,
+				default:false
+			},
+		},
+		computed: mapGetters(['viewColor']),
+		data(){
+			return {
+				min: '',
+				max:'',
+				isShow: false,
+				isShowType: false,
+				isShowCate: false,
+				list:[],
+				merCate: [],
+				activeList:[],
+				selectList: [],
+				showBox:false
+			}
+		},
+		mounted() {
+			// 重要组件挂载后
+			this.list = this.storeTypeArr //店铺类型
+			this.merCate = this.merList //商户分类
+			this.showBox = this.status
+		},
+		methods:{	
+			bindChenck1(item){
+				item.check = !item.check
+				this.arrFilter1()
+			},
+			bindChenck2(item){
+				item.check = !item.check
+				this.arrFilter2()
+			},
+			arrFilter1(){
+				this.selectList = this.list.filter(item=>{
+					return item.check == true
+				})
+			},
+			arrFilter2(){
+				this.activeList = this.merCate.filter(item=>{
+					return item.check == true
+				})
+			},
+			reset(){
+				this.list.forEach((el,index)=>{
+					el.check = false
+				})
+				this.merCate.forEach((el,index)=>{
+					el.check = false
+				})
+				this.arrFilter1()
+				this.arrFilter2()
+			},
+			confirm(){
+				this.arrFilter1()
+				this.arrFilter2()
+				console.log(this.activeList)
+				let obj = {
+					storeTypeArr:this.selectList,
+					merList: this.activeList,
+					status:false
+				}
+				this.showBox = false
+				this.$emit('confirm',obj)				
+			},
+			close(){
+				this.showBox = false
+				this.$emit('close')
+			},
+			moveStop(){}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.slideInRight{
+		animation-duration:.5s
+	}
+	.content-border{
+		border-top: 20rpx solid #f5f5f5;
+	}
+	.right-wrapper{
+		z-index: 99;
+		position: fixed;
+		left: 0;
+		top: 0;
+		width: 100%;
+		height: 100%;
+		.control-wrapper{
+			z-index: 90;
+			position: absolute;
+			right: 0;
+			top: 0;
+			display: flex;
+			flex-direction: column;
+			width: 635rpx;
+			height: 100%;
+			background-color: #FFFFFF;
+			.wrapper-count{
+				height: calc(100% - 120rpx);
+				overflow-y: auto;
+			}
+			.content-box{
+				position: relative;
+				display: flex;
+				flex-direction: column;
+				padding: 0 26rpx;		
+				.content-title{
+					display: flex;
+					align-items: center;
+					justify-content: space-between;
+					padding: 40rpx 0 20rpx;
+					.title{
+						font-size: 26rpx;
+						font-weight: bold;
+						color: #282828;
+					}
+					.btns{
+						display: flex;
+						align-items: center;
+						justify-content: center;
+						font-size: 22rpx;
+						color: #999;
+						.iconfont{
+							margin-left: 10rpx;
+							margin-top: 5rpx;
+							font-size: 20rpx;
+						}
+					}
+				}
+				.brand-wrapper{
+					flex: 1;
+					overflow: hidden;
+					.wrapper{
+						display: flex;
+						flex-wrap: wrap;
+						padding-bottom: 20rpx;
+					}
+					.item{
+						display: block;
+						width:186rpx;
+						height:56rpx;
+						line-height: 56rpx;
+						text-align: center;
+						background:rgba(242,242,242,1);
+						border-radius:28rpx;
+						margin-top: 25rpx;
+						padding: 0 10rpx;
+						margin-right: 12rpx;
+						&:nth-child(3n){
+							margin-right: 0;
+						}
+						&.on{
+							background: var(--view-minorColor);
+							border:1px solid var(--view-theme);
+							color: var(--view-theme);
+						}
+					}
+					.btns{
+						display: flex;
+						align-items: center;
+						justify-content: center;
+						padding-top: 10rpx;
+						font-size: 22rpx;
+						color: #999;
+						.iconfont{
+							margin-left: 10rpx;
+							margin-top: 5rpx;
+							font-size: 20rpx;
+						}
+					}
+				}
+				
+			}
+		}
+		.foot-btn{
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			padding-bottom: 30rpx;
+			position: absolute;
+			bottom: 0;
+			left: 20rpx;
+			
+			.btn-item{
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				width:286rpx;
+				height:68rpx;
+				background:rgba(255,255,255,1);
+				border:1px solid rgba(170,170,170,1);
+				border-radius:34rpx;
+				font-size: 26rpx;
+				color: #282828;
+				&.confirm{
+					background: var(--view-theme);
+					border-color: var(--view-theme);
+					color: #fff;
+					margin-left: 20rpx;
+				}
+			}
+		}
+		.right-bg{
+			position: absolute;
+			left: 0;
+			top: 0;
+			width: 100%;
+			height: 100%;
+			background-color: rgba(0,0,0,.5);
+		}
+	}
+</style>

+ 328 - 0
components/rightSlidera.vue

@@ -0,0 +1,328 @@
+<template>
+	<view class="right-wrapper" @touchmove.stop.prevent="moveStop" :style="viewColor">
+		<view class="control-wrapper animated" :class="showBox?'slideInRight':''">
+			<view class="header">
+				<view class="title">价格区间</view>
+				<view class="input-wrapper">
+					<input placeholder="最低价" v-model="min" type="number"/>
+					<view class="line"></view>
+					<input placeholder="最高价" v-model="max" type="number"/>
+				</view>
+			</view>
+			<view class="content-box">
+				<view class="title">品牌</view>
+				<view class="brand-wrapper">
+					<scroll-view :style="{'height':isShow?'90%':'250rpx'}" :scroll-y="isShow">
+						<view class="wrapper">			
+							<view class="item line1" v-for="(item,index) in list" :key="index" :class="item.check?'on':''" @tap="bindChenck(item)">
+								{{item.brand_name}}
+							</view>				
+						</view>
+					</scroll-view>
+					<view class="btns" v-if="!isShow && list.length>9" @click="isShow = true">展开全部<text class="iconfont icon-xiangxia"></text></view>
+					<view class="btns" v-if="isShow && list.length>9"  @click="isShow = false">收起<text class="iconfont icon-xiangshang"></text></view>
+				</view>
+				<view class="foot-btn">
+					<view class="btn-item" @click="reset">重置</view>
+					<view class="btn-item confirm" @click="confirm">确定</view>
+				</view>
+			</view>
+		</view>
+		<view class="right-bg" @click="close"></view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { mapGetters } from "vuex";
+	export default{
+		props: {
+			brandList: {
+				type: Array,
+			},
+			status:{
+				type:Boolean,
+				default:false
+			},
+			price_on:{
+				type:String,
+				default:''
+			},
+			price_off:{
+				type:String,
+				default:''
+			}
+		},
+		computed: mapGetters(['viewColor']),
+		data(){
+			return {
+				min: '',
+				max:'',
+				is_trader: '',
+				isShow:false,
+				list:[],
+				storeTypeList: [
+					{name: '全部', value: '',check: true},
+					{name: '自营', value: 'trader',check: false},
+				],
+				activeList:[],
+				showBox:false
+			}
+		},
+		mounted() {
+			// 重要组件挂载后
+			this.list = this.brandList
+			this.showBox = this.status
+			this.min = this.price_on
+			this.max = this.price_off
+		},
+		methods:{	
+			bindChenck(item){
+				item.check = !item.check
+				this.arrFilter()
+			},	
+			bindChenckType(item,index){
+				
+				this.storeTypeList = [
+					{name: '全部', value: '',check: false},
+					{name: '自营', value: 'trader',check: false},
+				]
+				this.storeTypeList[index]['check'] = true
+				this.is_trader = this.storeTypeList[0]['check'] ? '' : 1
+			}, 
+			arrFilter(){
+				this.activeList = this.list.filter(item=>{
+					return item.check == true
+				})
+			},
+			reset(){
+				this.list.forEach((el,index)=>{
+					el.check = false
+				})
+				this.storeTypeList = [
+					{name: '全部', value: '',check: true},
+					{name: '自营', value: 'trader',check: false}
+				]
+				this.min = this.max = ''
+				this.arrFilter()
+			},
+			confirm(){
+				this.arrFilter()
+				console.log(this.activeList)
+				let obj = {
+					brandList:this.activeList,
+					price_on:this.min,
+					price_off:this.max,
+					status:false,
+					is_trader: this.is_trader
+				}
+				this.showBox = false
+				this.$emit('confirm',obj)				
+			},
+			close(){
+				// this.list.forEach((el,index)=>{
+				// 	el.check = false
+				// })
+				// this.arrFilter()
+				this.showBox = false
+				this.$emit('close')
+			},
+			moveStop(){}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.slideInRight{
+		animation-duration:.5s
+	}
+	.right-wrapper{
+		z-index: 99;
+		position: fixed;
+		left: 0;
+		top: 0;
+		width: 100%;
+		height: 100%;
+		.control-wrapper{
+			z-index: 90;
+			position: absolute;
+			right: 0;
+			top: 0;
+			display: flex;
+			flex-direction: column;
+			width: 635rpx;
+			height: 100%;
+			background-color: #F5F5F5;
+			.header{
+				padding: 50rpx 26rpx 40rpx;
+				background-color: #fff;
+				.title{
+					font-size: 26rpx;
+					font-weight: bold;
+					color: #282828;
+				}
+				.input-wrapper{
+					display: flex;
+					align-items: center;
+					justify-content: space-between;
+					margin-top: 28rpx;
+					input{
+						width:260rpx;
+						height:56rpx;
+						padding: 0 10rpx;
+						background:rgba(242,242,242,1);
+						border-radius:28rpx;
+						font-size: 22rpx;
+						text-align: center;
+					}
+					.line{
+						width:15rpx;
+						height:2rpx;
+						background:#7D7D7D;
+					}
+				}
+			}
+			.content-box{
+				position: relative;
+				flex: 1;
+				display: flex;
+				flex-direction: column;
+				margin-top: 20rpx;
+				padding: 0 26rpx;
+				background-color: #fff;
+				overflow: hidden;
+				.title{
+					padding: 40rpx 0 20rpx;
+					font-size: 26rpx;
+					font-weight: bold;
+					color: #282828;
+				}
+				.brand-wrapper{
+					flex: 1;
+					overflow: hidden;
+					.wrapper{
+						display: flex;
+						flex-wrap: wrap;
+						padding-bottom: 20rpx;
+					}
+					.item{
+						display: block;
+						width:186rpx;
+						height:56rpx;
+						line-height: 56rpx;
+						text-align: center;
+						background:rgba(242,242,242,1);
+						border-radius:28rpx;
+						margin-top: 25rpx;
+						padding: 0 10rpx;
+						margin-right: 12rpx;
+						&:nth-child(3n){
+							margin-right: 0;
+						}
+						&.on{
+							background: var(--view-minorColor);
+							border:1px solid var(--view-theme);
+							color: var(--view-theme);
+						}
+					}
+					.btns{
+						display: flex;
+						align-items: center;
+						justify-content: center;
+						padding-top: 10rpx;
+						font-size: 22rpx;
+						color: #999;
+						.iconfont{
+							margin-left: 10rpx;
+							margin-top: 5rpx;
+							font-size: 20rpx;
+						}
+					}
+				}
+				.foot-btn{
+					display: flex;
+					align-items: center;
+					justify-content: space-between;
+					padding-bottom: 30rpx;
+					.btn-item{
+						display: flex;
+						align-items: center;
+						justify-content: center;
+						width:286rpx;
+						height:68rpx;
+						background:rgba(255,255,255,1);
+						border:1px solid rgba(170,170,170,1);
+						border-radius:34rpx;
+						font-size: 26rpx;
+						color: #282828;
+						&.confirm{
+							background: var(--view-theme);
+							border-color: var(--view-theme);
+							color: #fff;
+						}
+					}
+				}
+			}
+			.store_type{
+				position: relative;
+				margin-top: 20rpx;
+				padding: 0 26rpx;
+				background-color: #fff;
+				overflow: hidden;
+			
+				.title{
+					padding: 40rpx 0 20rpx;
+					font-size: 26rpx;
+					font-weight: bold;
+					color: #282828;
+				}
+				.brand-wrapper{
+					overflow: hidden;
+					.wrapper{
+						display: flex;
+						flex-wrap: wrap;
+						padding-bottom: 20rpx;
+					}
+					.item{
+						display: block;
+						width:186rpx;
+						height:56rpx;
+						line-height: 56rpx;
+						text-align: center;
+						background:rgba(242,242,242,1);
+						border-radius:28rpx;
+						margin-top: 25rpx;
+						padding: 0 10rpx;
+						margin-right: 12rpx;
+						&:nth-child(3n){
+							margin-right: 0;
+						}
+						&.on{
+							background: var(--view-minorColor);
+							border:1px solid var(--view-theme);
+							color: var(--view-theme);
+						}
+					}
+					
+				}
+				
+			}
+		}
+		.right-bg{
+			position: absolute;
+			left: 0;
+			top: 0;
+			width: 100%;
+			height: 100%;
+			background-color: rgba(0,0,0,.5);
+		}
+	}
+</style>

+ 58 - 0
components/shareInfo/index.vue

@@ -0,0 +1,58 @@
+<template>
+	<view v-if="shareInfoStatus" class="poster-first">
+	    <view class="mask-share">
+			<image :src="`${domain}/static/images/share-info.png`" @click="shareInfoClose" @touchmove.stop.prevent="false"></image>
+	    </view>
+	  </view>
+</template>
+
+<script>
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------	
+import { HTTP_REQUEST_URL } from '@/config/app';
+export default {
+	props: {
+	 shareInfoStatus: {
+			type: Boolean,
+			default:false,
+		}
+	},
+  data: function() {
+    return {
+			domain: HTTP_REQUEST_URL,
+		};
+  },
+  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>

+ 159 - 0
components/shareRedPackets/index.vue

@@ -0,0 +1,159 @@
+<template>
+	<view class='sharing-packets' :class='isAnimate==true?"":"right"'>
+		<view class='sharing-con' @click='goShare'>
+			<image :src="domain+'/static/images/red-packets.png'"></image>
+			<view class='text font-color'>
+				<!-- <view class="title">分享赚佣金</view> -->
+				<!-- <view class='money'><text class='label'>¥</text>{{sharePacket.priceName}}</view> -->
+				<view class='money'><text class='label'>¥</text>{{parseFloat(sharePacket.max)}}</view>
+				<!-- <view class='tip'>下单即返佣金</view>
+				<view class='shareBut'>立即分享</view> -->
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { HTTP_REQUEST_URL } from '@/config/app';
+	export default {	
+		props: { 
+			sharePacket: {
+				type: Object,
+				default: function() {
+					return {
+						isState: true,
+						priceName: ''
+					}
+				}
+			},
+			showAnimate:{
+				type: Boolean,
+			},		
+		},
+		watch:{
+			showAnimate(nVal,oVal){
+				setTimeout(res=>{
+					this.isAnimate = nVal
+				},1000)
+			}
+		},
+		data() {
+			return {
+				domain: HTTP_REQUEST_URL,
+				scrollNum:0,
+				isAnimate:true
+			};
+		},
+
+		methods: {
+			closeShare: function() {
+				this.$emit('closeChange');
+			},
+			goShare: function() {
+				if(this.isAnimate){
+					this.$emit('listenerActionSheet');
+				}else{
+					this.isAnimate = true
+					this.$emit('boxStatus',true);
+				}
+			}
+		},
+	}
+</script>
+
+<style scoped lang="scss">
+	.sharing-packets {
+		position: fixed;
+		right: 30rpx;
+		bottom: 200rpx;
+		z-index: 5;
+		transition: all 0.3s ease-in-out 0s;
+		opacity: 1;
+		transform: scale(1);
+		&.right{
+			right: -140rpx;
+		}
+	}
+
+	.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: 197rpx;
+		height: 195rpx;
+		position: relative;
+	}
+
+	.sharing-packets .sharing-con image {
+		width: 100%;
+		height: 100%;
+	}
+
+	.sharing-packets .sharing-con .text {
+		position: absolute;
+		top: 20rpx;
+		font-size: 20rpx;
+		width: 100%;
+		text-align: center;
+	}
+
+	.sharing-packets .sharing-con .text .money {
+		font-size: 32rpx;
+		font-weight: bold;
+		margin-top: 24rpx;
+	}
+
+	.sharing-packets .sharing-con .text .money .label {
+		font-size: 16rpx;
+	}
+
+	.sharing-packets .sharing-con .text .tip {
+		font-size: 18rpx;
+		color: #AA6E56;
+		margin-top: 5rpx;
+	}
+
+	.sharing-packets .sharing-con .text .shareBut {
+		width: 60%;
+		font-size: 20rpx;
+		color: #F13926;
+		margin-top: 18rpx;
+		height: 30rpx;
+		line-height: 30rpx;
+		background: #FFE8BB;
+		border-radius: 30rpx;
+		margin: 26rpx auto 0;
+	}
+</style>

+ 298 - 0
components/shopList/index.vue

@@ -0,0 +1,298 @@
+<template>
+	<view>
+		<view class="store_content" :class="isShow?'on':''">			
+			<view class="popup" :class="{ on: isShow }">
+				<scroll-view scroll-y="true">
+					<radio-group name="store_name" @change="changeStore">
+						<template v-for="item in storeList">
+							<div v-if="item.merchant" class="store-list">
+								<!-- <div class="invoice-list"> -->
+									<label :key="item.merchant.mer_id" class="acea-row row-middle">
+										<view class="text">
+											<view class="acea-row row-middle">
+												<image class="mer_logo" v-if="item.merchant.mer_avatar" 
+												:src="item.merchant.mer_avatar" mode=""></image>
+												<view class="name line1">{{ item.merchant.mer_name }}</view>										
+											</view>									
+										</view>
+										<radio class="radio" :value="item.merchant.mer_id.toString()" :checked="item.merchant.mer_id == id ? true : false" />
+									</label>
+								<!-- </div> -->
+							</div>							
+						</template>
+					</radio-group>
+				</scroll-view>
+			</view>			
+		</view>		
+		<view class='mask' catchtouchmove="true" :hidden='!isShow' @tap='close'></view>
+	</view>	
+</template>
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import {
+		getStoreList
+	} from '@/api/user.js';
+	export default {
+		props:['is_sys'],
+		data() {
+			return {
+				isShow: false,
+				id: '',
+				storeList: [],
+			}
+		},
+		watch: {			
+		},
+		mounted(){
+			
+		},
+		methods: {
+			isShowStore(){
+				this.isShow = !this.isShow;
+			},
+			close: function() {				
+				this.$emit('changeStoreClose');	
+				this.isShow = false;
+			},		
+			changeStore(e) {
+				this.close()
+				this.getStoreName(e.detail.value)
+				uni.setStorageSync('serMerId',e.detail.value)	
+			},
+			//获取店铺列表
+			getStoreList(parmas){
+				getStoreList(parmas).then(res => {
+					this.storeList = res.data;
+					let serMerId = this.is_sys !== '' ? null : uni.getStorageSync('serMerId')
+					let storeInfo = null;
+					let flag = false
+					this.storeList.forEach(item=>{
+						if(flag) return;
+						if(!serMerId){
+							if(this.is_sys && !item['merchant']['mer_id']){
+								storeInfo = item;
+								flag = true;
+							}else if(this.is_sys == '0' && item['merchant']['mer_id']){
+								storeInfo = item;
+								flag = true;
+							}
+						}else if(serMerId == item['merchant']['mer_id']){
+							storeInfo = item;
+							flag = true;
+						}
+					})
+					if(!storeInfo){
+						storeInfo = this.storeList[0]
+					}
+					this.id = storeInfo ? storeInfo['mer_id'] : ''
+					this.$emit('getStoreInfo', storeInfo['merchant'])
+					this.$emit('getService', storeInfo);
+				})
+			},
+			//根据店铺ID匹配店铺名
+			getStoreName(id){
+				for (let i = 0; i < this.storeList.length; i++) {
+					if (this.storeList[i]['merchant']['mer_id'] == id) {
+						this.$emit('getStoreInfo',this.storeList[i]['merchant']);
+						uni.setStorageSync('storeInfo', this.storeList[i]['merchant']);
+						this.$emit('getService',this.storeList[i]);
+					}
+				}
+			},		
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.store_content{
+		position: fixed;
+		bottom: 0;
+		width: 100%;
+		left: 0;
+		background-color: #fff;
+		z-index: 77;
+		border-radius: 16rpx 16rpx 0 0;
+		padding-bottom: 60rpx;
+		transform: translate3d(0, 100%, 0);
+		transition: all .3s cubic-bezier(.25, .5, .5, .9);
+	}	
+	.store_content.on {
+		transform: translate3d(0, 0, 0);
+	}
+	.store_content .title {
+		font-size: 32rpx;
+		font-weight: bold;
+		text-align: center;
+		height: 123rpx;
+		line-height: 123rpx;
+		position: relative;
+	}
+	.store_content .title .iconfont {
+		position: absolute;
+		right: 30rpx;
+		color: #8a8a8a;
+		font-size: 35rpx;
+	}
+	.store_content .store-list{
+		height: 120rpx;
+		line-height: 120rpx;
+	}
+	.store_content .store-list .mer_logo{
+		width: 60rpx;
+		height: 60rpx;	
+		margin-right: 20rpx;
+	}
+	form {
+		font-size: 28rpx;
+		color: #282828;
+	}
+	form input,
+	form radio-group {
+		flex: 1;
+		text-align: right;
+	}
+	form input {
+		font-size: 26rpx;
+	}
+	form label {
+		margin-right: 50rpx;
+	}
+	form radio {
+		margin-right: 8rpx;
+	}
+	form checkbox-group {
+		height: 90rpx;
+	}
+	form checkbox {
+		margin-right: 20rpx;
+	}
+	form button {
+		height: 76rpx;
+		border-radius: 38rpx;
+		margin: 16rpx 30rpx;
+		background-color: #E93323;
+		font-size: 30rpx;
+		line-height: 76rpx;
+		color: #FFFFFF;
+	}
+	.panel {
+		padding-right: 30rpx;
+		padding-left: 30rpx;
+		background-color: #FFFFFF;
+	}
+	.panel~.panel {
+		margin-top: 14rpx;
+	}
+	.panel .acea-row {
+		height: 90rpx;
+	}
+	.panel .acea-row~.acea-row {
+		border-top: 1px solid #EEEEEE;
+	}
+	.input-placeholder {
+		font-size: 26rpx;
+		color: #BBBBBB;
+	}
+	.icon-xiangyou {
+		margin-left: 25rpx;
+		font-size: 18rpx;
+		color: #BFBFBF;
+	}
+	.btn-wrap {
+		width: 100%;
+		padding: 8px 16px;
+		border-top: 1px solid #F5F5F5;
+	}
+	.btn-wrap .back {
+		border: 1px solid #E93323;
+		background: none;
+		color: #E93323;
+	}
+	.popup {
+		width: 100%;
+		border-top-left-radius: 16rpx;
+		border-top-right-radius: 16rpx;
+		background-color: #FFFFFF;
+		overflow: hidden;
+	}
+	.popup scroll-view {
+		height: 466rpx;
+		padding-right: 30rpx;
+		padding-left: 30rpx;
+		box-sizing: border-box;
+	}
+	.popup .text {
+		flex: 1;
+		min-width: 0;
+		font-size: 28rpx;
+		color: #282828;
+	}
+	.popup .info {
+		margin-top: 10rpx;
+		font-size: 22rpx;
+		color: #909090;
+	}
+	.popup .icon-guanbi {
+		position: absolute;
+		top: 50%;
+		right: 30rpx;
+		z-index: 2;
+		transform: translateY(-50%);
+		font-size: 30rpx;
+		color: #707070;
+		cursor: pointer;
+	}
+	.popup button {
+		height: 86rpx;
+		border-radius: 43rpx;
+		margin-right: 30rpx;
+		margin-left: 30rpx;
+		background-color: #E93323;
+		font-size: 30rpx;
+		line-height: 86rpx;
+		color: #FFFFFF;
+	}
+	.popup .text .acea-row {
+		display: inline-flex;
+		max-width: 100%;
+	}
+	.popup .name {
+		flex: 1;
+		overflow: hidden;
+		white-space: nowrap;
+		text-overflow: ellipsis;
+		font-size: 30rpx;
+	}
+	.popup .label {
+		width: 56rpx;
+		height: 28rpx;
+		border: 1px solid #E93323;
+		margin-left: 18rpx;
+		font-size: 20rpx;
+		line-height: 26rpx;
+		text-align: center;
+		color: #E93323;
+	}
+	.popup .type {
+		width: 124rpx;
+		height: 42rpx;
+		margin-top: 14rpx;
+		background-color: #FCF0E0;
+		font-size: 24rpx;
+		line-height: 42rpx;
+		text-align: center;
+		color: #D67300;
+	}
+	.popup .type.special {
+		background-color: #FDE9E7;
+		color: #E93323;
+	}
+
+</style>

+ 199 - 0
components/skeleton/index.vue

@@ -0,0 +1,199 @@
+<template>
+	<view v-if="show"
+		:style="{width: systemInfo.width + 'px', height: systemInfo.height + 'px', backgroundColor: bgcolor, position: 'absolute', left: 0, top: 0, zIndex: 9998}">
+		<view v-for="(item,rect_idx) in skeletonRectLists" :key="rect_idx + 'rect'"
+			:class="[loading == 'chiaroscuro' ? 'chiaroscuro' : '']"
+			:style="{width: item.width + 'px', height: item.height + 'px', backgroundColor: 'rgb(194, 207, 214,.3)', position: 'absolute', left: item.left + 'px', top: item.top + 'px'}">
+		</view>
+		<view v-for="(item,circle_idx) in skeletonCircleLists" :key="circle_idx + 'circle'"
+			:class="loading == 'chiaroscuro' ? 'chiaroscuro' : ''"
+			:style="{width: item.width + 'px', height: item.height + 'px', backgroundColor: 'rgb(194, 207, 214,.3)', borderRadius: item.width + 'px', position: 'absolute', left: item.left + 'px', top: item.top + 'px'}">
+		</view>
+		<view class="spinbox" v-if="loading == 'spin'">
+			<view class="spin"></view>
+		</view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	export default {
+		name: "skeleton",
+		props: {
+			bgcolor: {
+				type: String,
+				value: '#FFF'
+			},
+			selector: {
+				type: String,
+				value: 'skeleton'
+			},
+			loading: {
+				type: String,
+				value: 'spin'
+			},
+			show: {
+				type: Boolean,
+				value: false
+			},
+			isNodes: {
+				type: Number,
+				value: false
+			} //控制什么时候开始抓取元素节点,只要数值改变就重新抓取
+		},
+		data() {
+			return {
+				loadingAni: ['spin', 'chiaroscuro'],
+				systemInfo: {},
+				skeletonRectLists: [],
+				skeletonCircleLists: []
+			}
+		},
+		watch: {
+			isNodes(val) {
+				this.readyAction();
+			}
+		},
+		mounted() {
+			this.attachedAction();
+		},
+		methods: {
+			attachedAction: function() {
+				//默认的首屏宽高,防止内容闪现
+				const systemInfo = uni.getSystemInfoSync();
+				this.systemInfo = {
+					width: systemInfo.windowWidth,
+					height: systemInfo.windowHeight
+				};
+				this.loading = this.loadingAni.includes(this.loading) ? this.loading : 'spin';
+			},
+			readyAction: function() {
+				const that = this;
+				//绘制背景
+				uni.createSelectorQuery().selectAll(`.${this.selector}`).boundingClientRect().exec(function(res) {
+					if(res[0].length>0)
+					that.systemInfo.height = res[0][0].height + res[0][0].top;
+				});
+
+				//绘制矩形
+				this.rectHandle();
+
+				//绘制圆形
+				this.radiusHandle();
+			},
+			rectHandle: function() {
+				const that = this;
+
+				//绘制不带样式的节点
+				uni.createSelectorQuery().selectAll(`.${this.selector}-rect`).boundingClientRect().exec(function(res) {
+					that.skeletonRectLists = res[0];
+				});
+
+			},
+			radiusHandle() {
+				const that = this;
+
+				uni.createSelectorQuery().selectAll(`.${this.selector}-radius`).boundingClientRect().exec(function(res) {
+					that.skeletonCircleLists = res[0];
+				});
+			}
+		}
+	}
+</script>
+
+<style>
+	.spinbox {
+		position: fixed;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		height: 100%;
+		width: 100%;
+		z-index: 9999
+	}
+
+	.spin {
+		display: inline-block;
+		width: 64rpx;
+		height: 64rpx;
+	}
+
+	.spin:after {
+		content: " ";
+		display: block;
+		width: 46rpx;
+		height: 46rpx;
+		margin: 1rpx;
+		border-radius: 50%;
+		border: 5rpx solid #409eff;
+		border-color: #409eff transparent #409eff transparent;
+		animation: spin 1.2s linear infinite;
+	}
+
+	@keyframes spin {
+		0% {
+			transform: rotate(0deg);
+		}
+
+		100% {
+			transform: rotate(360deg);
+		}
+	}
+
+	.chiaroscuro {
+		width: 100%;
+		height: 100%;
+		background: rgb(194, 207, 214);
+		animation-duration: 2s;
+		animation-name: blink;
+		animation-iteration-count: infinite;
+	}
+
+	@keyframes blink {
+		0% {
+			opacity: .4;
+		}
+
+		50% {
+			opacity: 1;
+		}
+
+		100% {
+			opacity: .4;
+		}
+	}
+
+	@keyframes flush {
+		0% {
+			left: -100%;
+		}
+
+		50% {
+			left: 0;
+		}
+
+		100% {
+			left: 100%;
+		}
+	}
+
+	.shine {
+		animation: flush 2s linear infinite;
+		position: absolute;
+		top: 0;
+		bottom: 0;
+		width: 100%;
+		background: linear-gradient(to left,
+				rgba(255, 255, 255, 0) 0%,
+				rgba(255, 255, 255, .85) 50%,
+				rgba(255, 255, 255, 0) 100%)
+	}
+</style>

+ 114 - 0
components/specs/index.vue

@@ -0,0 +1,114 @@
+<template>
+	<!-- 产品参数 -->
+	<view>
+		<view class="specs" :class="specsInfo.show === true ? 'on' : ''">
+			<view class="title">商品参数<text class="iconfont icon-guanbi5" @click="closeSpecs"></text></view>
+			<view class="list">
+				<view class="item acea-row" v-for="(item,index) in specsInfo.params" :key="index">
+					<view class="name">{{item.name}}</view>
+					<view class="val">{{item.value}}</view>
+				</view>
+			</view>
+			<view class="bnt" @click="closeSpecs">完成</view>
+			<slot name="bottom"></slot>
+		</view>
+		<view class="mask" @touchmove.prevent :hidden="specsInfo.show === false" @click="closeSpecs"></view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	export default {
+		props: {
+			specsInfo: {
+				type: Object,
+				default: () => {}
+			},
+		},
+		data() {
+			return {};
+		},
+		mounted() {},
+		methods: {
+			closeSpecs(){
+				this.$emit('myevent');
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.specs{
+		position: fixed;
+		bottom: 0;
+		width: 100%;
+		left: 0;
+		background-color: #fff;
+		z-index: 280;
+		border-radius: 16rpx 16rpx 0 0;
+		transform: translate3d(0, 100%, 0);
+		transition: all .3s cubic-bezier(.25, .5, .5, .9);
+		padding-bottom: 22rpx;
+		padding-bottom: calc(22rpx+ constant(safe-area-inset-bottom)); ///兼容 IOS<11.2/
+		padding-bottom: calc(22rpx + env(safe-area-inset-bottom)); ///兼容 IOS>11.2/
+		.title{
+			font-size: 32rpx;
+			color: #282828;
+			text-align: center;
+			margin: 38rpx 0 36rpx 0;
+			position: relative;
+			font-weight: bold;
+			.iconfont{
+				position: absolute;
+				right: 30rpx;
+				top:0;
+				font-size: 36rpx;
+				font-weight: normal;
+				color: #999999;
+			}
+		}
+		.list{
+			height: 750rpx;
+			margin: 0 30rpx;
+			color: #999999;
+			overflow-x: hidden;
+			overflow-y: auto;
+			.item{
+				padding: 30rpx 0;
+				border-bottom: 1rpx solid #eee;
+				.name{
+					width: 160rpx;
+					margin-right: 10rpx;
+					word-break: break-all;
+				}
+				.val{
+					width: 510rpx;
+					word-break: break-all;
+					color: #282828;
+				}
+			}
+		}
+		.bnt{
+			width: 690rpx;
+			height: 86rpx;
+			text-align: center;
+			line-height: 86rpx;
+			border-radius: 43rpx;
+			background-color: var(--view-theme);
+			font-size: 30rpx;
+			color: #fff;
+			margin: 0 auto;
+		}
+	}
+	.specs.on{
+		transform: translate3d(0, 0, 0);
+	}
+</style>

+ 60 - 0
components/swipers/index.vue

@@ -0,0 +1,60 @@
+<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>
+		// +----------------------------------------------------------------------
+		// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+		// +----------------------------------------------------------------------
+		// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+		// +----------------------------------------------------------------------
+		// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+		// +----------------------------------------------------------------------
+		// | Author: CRMEB Team <admin@crmeb.com>
+		// +----------------------------------------------------------------------
+		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:#e93323;background-color:#e93323;}
+</style>

+ 207 - 0
components/timeSlot/index.vue

@@ -0,0 +1,207 @@
+<template>
+	<!-- 日期组件 -->
+	<view>
+		<view class="list">
+			<view class="times">
+				<view class="item" :class="time == 'all' ? 'on' : ''" @click="setTime('all')">全部</view>
+				<view class="item" :class="time == 'today' ? 'on' : ''" @click="setTime('today')">今日</view>
+				<view class="item" :class="time == 'yesterday' ? 'on' : ''" @click="setTime('yesterday')">昨日</view>
+				<view class="item" :class="time == 'seven' ? 'on' : ''" @click="setTime('seven')">近7日</view>
+			</view>
+			<view class="item" :class="time == 'date' ? 'on' : ''" @click="dateTitle">自定义时间 <text
+					class="iconfont icon-xiangxia aaa"></text></view>
+		</view>
+		<uni-calendar ref="calendar" :date="info.date" :insert="info.insert" :lunar="info.lunar"
+			:startDate="info.startDate" :endDate="info.endDate" :range="info.range" @confirm="confirm"
+			:showMonth="info.showMonth" />
+		<view class="mask" @touchmove.prevent v-show="current === true" @click="close"></view>
+		<Loading :loaded="loaded" :loading="loading"></Loading>
+	</view>
+
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import Loading from '@/components/Loading/index.vue'
+	import uniCalendar from '@/components/uni-calendar/uni-calendar.vue'
+	const year = new Date().getFullYear();
+	const month = new Date().getMonth() + 1;
+	const day = new Date().getDate();
+	export default {
+		components: {
+			uniCalendar,
+			Loading
+		},
+		data() {
+			return {
+				time:'all',
+				current: false,
+				loaded: false,
+				loading: false,
+				info: {
+					startDate: '',
+					endDate: '',
+					lunar: false,
+					range: true,
+					insert: false,
+					selected: [],
+					showMonth: false
+				},
+				where: {
+					start: "",
+					stop: "",
+				},
+			}
+		},
+		methods: {
+			close() {
+				this.current = false;
+			},
+			// 日历确定
+			confirm(e) {
+				let self = this
+				let star, stop;
+				if ((e.range.after && e.range.before) || e.fulldate) {
+					if(e.range.after && e.range.before){
+						if (e.range.before > e.range.after) {
+							star = new Date(e.range.after + ' 00:00:00').getTime() / 1000
+							stop = new Date(e.range.before + ' 23:59:59').getTime() / 1000
+						} else {
+							star = new Date(e.range.before + ' 00:00:00').getTime() / 1000
+							stop = new Date(e.range.after + ' 23:59:59').getTime() / 1000
+						}
+					}else{
+						var year = new Date(e.fulldate).getFullYear(),
+							month = new Date(e.fulldate).getMonth() + 1,
+							day = new Date(e.fulldate).getDate();
+						star = new Date(e.fulldate + ' 00:00:00').getTime() / 1000
+						stop =new Date(Date.parse(year + "/" + month + "/" + day)).getTime() / 1000 +
+							24 * 60 * 60 -
+							1;
+					}
+					self.where.start = star
+					self.where.stop = stop
+					self.loaded = false;
+					self.loading = false;
+					this.$emit('changeTime', this.where)
+				}
+			},
+			dateTitle() {
+				this.$refs.calendar.open()
+				this.time = 'date'
+			},
+			setTime(time) {
+				let self = this
+				this.time = time;
+				var year = new Date().getFullYear(),
+					month = new Date().getMonth() + 1,
+					day = new Date().getDate();
+				this.tip = 1
+				this.loaded = false;
+				this.loading = false;
+				switch (time) {
+					case "all":
+						this.where.start = 0
+						this.where.stop = 0
+						this.title = "全部";
+						this.$emit('changeTime', this.where)
+						break;
+					case "today":
+						this.where.start =
+							new Date(Date.parse(year + "/" + month + "/" + day)).getTime() /
+							1000;
+						this.where.stop =
+							new Date(Date.parse(year + "/" + month + "/" + day)).getTime() /
+							1000 +
+							24 * 60 * 60 -
+							1;
+						this.title = "今日";
+						this.$emit('changeTime', this.where)
+						break;
+					case "yesterday":
+						this.where.start =
+							new Date(Date.parse(year + "/" + month + "/" + day)).getTime() /
+							1000 -
+							24 * 60 * 60;
+						this.where.stop =
+							new Date(Date.parse(year + "/" + month + "/" + day)).getTime() /
+							1000 -
+							1;
+						this.title = "昨日";
+						this.$emit('changeTime', this.where)
+						break;
+					case "month":
+						this.where.start =
+							new Date(year, new Date().getMonth(), 1).getTime() / 1000;
+						this.where.stop = new Date(year, month, 1).getTime() / 1000 - 1;
+						this.title = "本月";
+						this.$emit('changeTime', this.where)
+						break;
+					case "seven":
+						this.where.start =
+							new Date(Date.parse(year + "/" + month + "/" + day)).getTime() /
+							1000 +
+							24 * 60 * 60 -
+							7 * 3600 * 24;
+						this.where.stop =
+							new Date(Date.parse(year + "/" + month + "/" + day)).getTime() /
+							1000 +
+							24 * 60 * 60 -
+							1;
+						this.title = "七日";
+						this.$emit('changeTime', this.where)
+						break;
+						// #ifdef MP
+					case "date":
+						let star = new Date(self.before).getTime() / 1000
+						let stop = new Date(self.after).getTime() / 1000
+						self.where.start = star
+						self.where.stop = stop
+						// Promise.all([self.getList()]);
+						this.$emit('changeTime', this.where)
+						break;
+						// #endif
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.list {
+		display: flex;
+		justify-content: space-between;
+		padding: 24rpx 30rpx;
+		background-color: #fff;
+		color: #666666;
+		font-size: 26rpx;
+		.times {
+			display: flex;
+			.item {
+				margin-right: 20rpx;
+				background: #F5F5F5;
+				padding: 10rpx 20rpx;
+				border-radius: 30rpx;
+			}
+			.item.on {
+				color: var(--view-theme);
+				background-color: var(--view-minorColor);
+			}
+		}
+		.item{
+			padding: 10rpx 0rpx;
+		}
+	}
+	.aaa {
+		padding-left: 10rpx;
+		font-size: 20rpx !important;
+	}
+</style>

+ 168 - 0
components/timeranges/index.vue

@@ -0,0 +1,168 @@
+<template>
+	<view>
+	  <view class="time1" :class='isShow==true?"on":""'>
+	    <view class="top acea-row row-between-wrapper">
+	    	<text @tap="cancel">取消</text>
+	    	<text @tap="confirm">确定</text>
+	    </view>
+	    <picker-view class="picker" :value="value" @change="getime" indicator-style="height:34px;">
+	    	<picker-view-column>
+	    		<view class="hours" v-for="(item,index) in hoursList" :key="index">{{item}}</view>
+	    	</picker-view-column>
+	    	<picker-view-column>
+	    		<view class="minutes" v-for="(item,index) in minutes" :key="index">{{item}}</view>
+	    	</picker-view-column>
+	    	<picker-view-column>
+	    		<view class="center">-</view>
+	    	</picker-view-column>
+	    	<picker-view-column>
+	    		<view class="hours" v-for="(item,index) in hoursList" :key="index">{{item}}</view>
+	    	</picker-view-column>
+	    	<picker-view-column>
+	    		<view class="minutes" v-for="(item,index) in minutes" :key="index">{{item}}</view>
+	    	</picker-view-column>
+	    </picker-view>
+	  </view>
+	  <view class="mask" @tap="cancel" catchtouchmove="true" :hidden="isShow==false"></view>
+	</view>
+</template>
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	let minutes=[]
+	for (let i = 0; i <= 59; i++) {
+		if(i<10){
+			i="0"+i
+		}
+	  minutes.push(i)
+	}
+	let hoursList = []
+	for (let i = 0; i <= 23; i++) {
+		if(i<10){
+			i="0"+i
+		}
+	  hoursList.push(i)
+	}
+	export default{
+		props:{
+		  isShow:{
+			type: Boolean,
+			default: false
+		  },
+		  time:{
+		  	type: Array,
+		  	default() {
+		  	  return [];
+		  	}
+		  }
+		},
+		watch:{
+			time:function(){
+				this.value=this.time
+			}
+		},
+		created(){
+			
+		},
+		data(){
+			return{
+				value:this.time,//默认结束开始时间
+				hoursList,
+				minutes,
+			}
+		},
+		methods:{
+			confirm(){
+				let time = this.value[0]+":"+this.value[1]+" - "+this.value[3]+":"+this.value[4]
+				if(this.value[3]>this.value[0] || (this.value[3]==this.value[0] && this.value[4]>=this.value[1])){
+				  this.$emit("confrim",{time:time,val:this.value})
+				}else{
+				  return this.$util.Tips({
+				    title: '开始时间必须小于结束时间'
+				  });
+				}
+			},
+			cancel(){
+				let time = this.value[0]+":"+this.value[1]+" - "+this.value[3]+":"+this.value[4]
+				this.$emit("cancel",{time:time})
+			},
+			getime(e){
+				let val = e.detail.value
+				this.value[0] = this.hoursList[val[0]] 
+				this.value[1] = this.minutes[val[1]] 
+				this.value[2] = val[2]
+				this.value[3] = this.hoursList[val[3]] 
+				this.value[4] = this.minutes[val[4]]
+			},
+		}
+	}
+</script>
+<style lang="scss">
+.time1{
+	width:100%;
+	margin: 0 auto;
+	background-color:#FFFFFF;
+	color: #000;
+	height: 568rpx;
+	position: fixed;
+	bottom: 0;
+	z-index: 99;
+	transform: translate3d(0, 200%, 0);
+	transition: all .3s cubic-bezier(.25, .5, .5, .9);
+	&.on{
+	 transform: translate3d(0, 0, 0);
+	}
+	.top{
+		height: 90rpx;
+		border-bottom: 1px solid #eee;
+		padding: 0 30rpx;
+		text{
+			font-size: 32rpx;
+			&:nth-child(1){
+				color: #888;
+			}
+			&:nth-child(2){
+				color: #007aff;
+			}
+		}
+	}
+	.tip12{
+		width: 100%;
+		height: 100rpx;
+		view{
+			width: 50%;
+			text-align: center;
+			line-height: 100rpx;
+			font-size: 40rpx;
+			color: #000000;
+		}
+	}
+	.hours{
+		font-size: 32rpx;
+		color: #000;
+		line-height:34px; 
+		text-align: center;
+	}
+	.minutes{
+		font-size: 32rpx;
+		color: #000;
+		line-height:34px; 
+		text-align: center;
+	}
+	.center{
+		line-height:34px;
+		text-align: center;
+	}
+}
+.picker{
+	width: 100%;
+	height: 476rpx;
+}
+</style>

+ 248 - 0
components/tui-skeleton.vue

@@ -0,0 +1,248 @@
+<template>
+	<view class="tui-skeleton-cmomon tui-skeleton-box" :style="{width: winWidth+'px', height:winHeight+'px', backgroundColor:backgroundColor}">
+		<view class="tui-skeleton-cmomon" v-for="(item,index) in skeletonElements" :key="index" :style="{width: item.width+'px', height:item.height+'px', left: item.left+'px', top: item.top+'px',backgroundColor: skeletonBgColor,borderRadius:getRadius(item.skeletonType,borderRadius)}"></view>
+		<view class="tui-loading" :class="[getLoadingType(loadingType)]" v-if="isLoading"></view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	export default {
+		name: "tuiSkeleton",
+		props: {
+			//选择器(外层容器)
+			selector: {
+				type: String,
+				default: "tui-skeleton"
+			},
+			//外层容器背景颜色
+			backgroundColor: {
+				type: String,
+				default: "#fff"
+			},
+			//骨架元素背景颜色
+			skeletonBgColor: {
+				type: String,
+				default: "#e9e9e9"
+			},
+			//骨架元素类型:矩形,圆形,带圆角矩形["rect","circular","fillet"]
+			//默认所有,根据页面情况进行传值
+			//页面对应元素class为:tui-skeleton-rect,tui-skeleton-circular,tui-skeleton-fillet
+			//如果传入的值不在下列数组中,则为自定义class值,默认按矩形渲染
+			skeletonType: {
+				type: Array,
+				default () {
+					return ["rect", "circular", "fillet"]
+				}
+			},
+			//圆角值,skeletonType=fillet时生效
+			borderRadius: {
+				type: String,
+				default: "16rpx"
+			},
+			//骨架屏预生成数据:提前生成好的数据,当传入该属性值时,则不会再次查找子节点信息
+			preloadData: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			//是否需要loading
+			isLoading: {
+				type: Boolean,
+				default: false
+			},
+			//loading类型[1-10]
+			loadingType: {
+				type: Number,
+				default: 1
+			}
+		},
+		created() {
+			const res = uni.getSystemInfoSync();
+			this.winWidth = res.windowWidth;
+			this.winHeight = res.windowHeight;
+			//如果有预生成数据,则直接使用
+			this.isPreload(true)
+		},
+		mounted() {
+			this.$nextTick(() => {
+				this.nodesRef(`.${this.selector}`).then((res) => {
+					if(res && res[0]){
+						this.winHeight = res[0].height + Math.abs(res[0].top)
+					}
+				});
+				!this.isPreload() && this.selectorQuery()
+			})
+
+		},
+		data() {
+			return {
+				winWidth: 375,
+				winHeight: 800,
+				skeletonElements: []
+			};
+		},
+		methods: {
+			getLoadingType: function(type) {
+				let value = 1
+				if (type && type > 0 && type < 11) {
+					value = type
+				}
+				return 'tui-loading-' + value
+			},
+			getRadius: function(type, val) {
+				let radius = "0"
+				if (type == "circular") {
+					radius = "50%"
+				} else if (type == "fillet") {
+					radius = val
+				}
+				return radius;
+			},
+			isPreload(init) {
+				let preloadData = this.preloadData || []
+				if (preloadData.length) {
+					init && (this.skeletonElements = preloadData)
+					return true
+				}
+				return false
+			},
+			async selectorQuery() {
+				let skeletonType = this.skeletonType || []
+				let nodes = []
+				for (let item of skeletonType) {
+					let className = '';
+					// #ifndef MP-WEIXIN
+					className = `.${item}`;
+					if (~'rect_circular_fillet'.indexOf(item)) {
+						className = `.${this.selector}-${item}`;
+					}
+					// #endif
+
+					// #ifdef MP-WEIXIN
+					className = `.${this.selector} >>> .${item}`;
+					if (~'rect_circular_fillet'.indexOf(item)) {
+						className = `.${this.selector} >>> .${this.selector}-${item}`;
+					}
+					// #endif
+					await this.nodesRef(className).then((res) => {
+						res.map(d => {
+							d.skeletonType = item
+						})
+						nodes = nodes.concat(res)
+					})
+				}
+				this.skeletonElements = nodes
+			},
+			async nodesRef(className) {
+				return await new Promise((resolve, reject) => {
+					uni.createSelectorQuery().selectAll(className).boundingClientRect((res) => {
+						if (res) {
+							resolve(res);
+						} else {
+							reject(res)
+						}
+					}).exec();
+				})
+			}
+		}
+	}
+</script>
+
+<style scoped>
+	.tui-skeleton-cmomon {
+		position: absolute;
+		z-index: 99999;
+	}
+
+	.tui-skeleton-box {
+		left: 0;
+		top: 0;
+	}
+
+	.tui-loading {
+		display: inline-block;
+		vertical-align: middle;
+		width: 40rpx;
+		height: 40rpx;
+		background: 0 0;
+		border-radius: 50%;
+		border: 2px solid;
+		animation: tui-rotate 0.7s linear infinite;
+		position: fixed;
+		z-index: 999999;
+		left: 50%;
+		top: 50%;
+		margin-left: -20rpx;
+		margin-top: -20rpx;
+	}
+
+	.tui-loading-1 {
+		border-color: #e5e5e5 #e5e5e5 #e5e5e5 #5677fc;
+	}
+
+	.tui-loading-2 {
+		border-color: #e5e5e5 #e5e5e5 #e5e5e5 #8f8d8e;
+	}
+
+	.tui-loading-3 {
+		border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) #fff;
+	}
+
+	.tui-loading-4 {
+		border-color: #e5e5e5 #e5e5e5 #e5e5e5 #35b06a;
+	}
+
+	.tui-loading-5 {
+		border-color: #e5e5e5 #e5e5e5 #e5e5e5 #fc872d;
+	}
+
+	.tui-loading-6 {
+		border-color: #e5e5e5 #e5e5e5 #e5e5e5 #eb0909;
+	}
+
+	.tui-loading-7 {
+		border-color: #5677fc transparent #5677fc transparent;
+	}
+
+	.tui-loading-8 {
+		border-color: #35b06a transparent #35b06a transparent;
+	}
+
+	.tui-loading-9 {
+		border-color: #fc872d transparent #fc872d transparent;
+	}
+
+	.tui-loading-10 {
+		border-color: #eb0909 transparent #eb0909 transparent;
+	}
+
+	@-webkit-keyframes tui-rotate {
+		0% {
+			transform: rotate(0);
+		}
+
+		100% {
+			transform: rotate(360deg);
+		}
+	}
+
+	@keyframes tui-rotate {
+		0% {
+			transform: rotate(0);
+		}
+
+		100% {
+			transform: rotate(360deg);
+		}
+	}
+</style>

+ 171 - 0
components/ucharts/component.vue

@@ -0,0 +1,171 @@
+<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>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	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) {}
+		},
+	};
+</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方式导出
+}

Datei-Diff unterdrückt, da er zu groß ist
+ 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

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

@@ -0,0 +1,161 @@
+<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>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	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>

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

@@ -0,0 +1,444 @@
+<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>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	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
+						this.$emit('close')
+					}, 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.setMultiple('')
+				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>

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

@@ -0,0 +1,337 @@
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+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

+ 168 - 0
components/userEvaluation/index.vue

@@ -0,0 +1,168 @@
+<template>
+	<view class="evaluateWtapper">
+		<view class="evaluateItem" v-for="(item, indexw) in reply" :key="indexw">
+			<view class="pic-text acea-row row-middle">
+				<view class="pictrue">
+					<image :src="item.avatar ? item.avatar : '/static/images/f.png'"></image>
+				</view>
+				<view class="acea-row row-middle">
+					<view class="name line1">{{ item.nickname }}</view>
+					<view class="start" :style="'width:'+(item.rate/5)*124+'rpx;background-image: url('+domain+'/static/diy/score2'+keyColor+'.png)'"></view>
+				</view>
+			</view>
+			<view class="time">{{ item.create_time }} {{ item.sku ? item.sku : '' }}</view>
+			<view class="evaluate-infor">{{ item.comment }}</view>
+			<view class="imgList acea-row">
+				<view class="pictrue" v-for="(itemn, indexn) in item.pics" :key="indexn" @click='getpreviewImage(indexw, indexn)'>
+					<easy-loadimage :image-src="itemn"></easy-loadimage>
+					<!-- <image :src="itemn" class="image" @click='getpreviewImage(indexw, indexn)'></image> -->
+					<!-- <view v-else class="image loadfail-img"></view> -->
+					<!-- <text v-if="item.pics.length>4 && indexn == 3" class="morePic">+{{item.pics.length-4}}</text> -->
+				</view>
+			</view>
+			<view class="reply" v-if="item.merchant_reply_content">
+				<text>店小二</text>:{{item.merchant_reply_content}}
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	import { mapGetters } from "vuex";
+	import { HTTP_REQUEST_URL } from '@/config/app';
+	import easyLoadimage from '@/components/easy-loadimage/easy-loadimage.vue';
+	export default {
+		props: {
+			reply: {
+				type: Array,
+				default: () => []
+			}
+		},
+		computed: mapGetters(['keyColor']),
+		components: {
+			easyLoadimage
+		},
+		data: function() {
+			return {
+				domain: HTTP_REQUEST_URL
+			};
+		},
+		methods: {
+			getpreviewImage: function(indexw, indexn) {
+				console.log(this.reply)
+				uni.previewImage({
+					urls: this.reply[indexw].pics,
+					current: this.reply[indexw].pics[indexn]
+				});
+			}
+		}
+	}
+</script>
+<style lang="scss">
+	.evaluateWtapper .evaluateItem {
+		background-color: #fff;
+		padding-bottom: 25rpx;
+	}
+	.evaluateWtapper .evaluateItem~.evaluateItem {
+		border-top: 1px solid #f5f5f5;
+	}
+	.evaluateWtapper .evaluateItem .pic-text {
+		font-size: 26rpx;
+		color: #282828;
+		height: 95rpx;
+		padding: 0 30rpx;
+	}
+	.evaluateWtapper .evaluateItem .pic-text .pictrue {
+		width: 56rpx;
+		height: 56rpx;
+		margin-right: 20rpx;
+	}
+	.evaluateWtapper .evaluateItem .pic-text .pictrue image {
+		width: 100%;
+		height: 100%;
+		border-radius: 50%;
+	}
+	.evaluateWtapper .evaluateItem .pic-text .name {
+		max-width: 450rpx;
+		margin-right: 15rpx;
+	}
+	.evaluateWtapper .evaluateItem .time {
+		font-size: 24rpx;
+		color: #82848f;
+		padding: 0 30rpx;
+	}
+	.evaluateWtapper .evaluateItem .evaluate-infor {
+		font-size: 28rpx;
+		color: #282828;
+		margin-top: 19rpx;
+		padding: 0 30rpx;
+	}
+	.evaluateWtapper .evaluateItem .imgList {
+		padding: 0 30rpx 0 15rpx;
+		margin-top: 25rpx;
+	}
+	.evaluateWtapper .evaluateItem .imgList .pictrue {
+		width: 160rpx;
+		height: 160rpx;
+		margin: 0 0 15rpx 15rpx;
+		position: relative;
+		/deep/.easy-loadimage,/deep/uni-image,/deep/image{
+			width: 160rpx;
+			height: 160rpx;
+		}
+	}
+
+	.evaluateWtapper .evaluateItem .imgList .morePic{
+		position: absolute;
+		width: 160rpx;
+		height: 160rpx;
+		color: #ffffff;
+		font-size: 40rpx;
+		text-align: center;
+		top: 0;
+		left: 0;
+		background: rgba(0,0,0,.5);
+		padding-top: 50rpx;
+		box-sizing: border-box;
+	}
+
+	.evaluateWtapper .evaluateItem .reply {
+		font-size: 26rpx;
+		color: #454545;
+		background-color: #f7f7f7;
+		border-radius: 5rpx;
+		margin: 20rpx 30rpx 0 30rpx;
+		padding: 30rpx;
+		position: relative;
+		text{
+			color: var(--view-theme);
+		}
+	}
+	.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: -30rpx;
+		left: 40rpx;
+	}
+	.start{
+		position: relative;
+		top: 4rpx;
+	}
+	.star_purple{
+		background: url(../../pages/columnGoods/images/star_active_purple.png) left top/111rpx 19rpx no-repeat;
+	}
+	
+</style>

+ 117 - 0
components/userNameSwiper/userNameSwiper.vue

@@ -0,0 +1,117 @@
+<template>
+	<view class="user-swiper">
+		<view class="box" id="box" :style="{marginLeft: `${distance}px`}">
+			<view class="user-list" v-for="(item,index) in combinationUserList" :key="index">
+				<image v-show="init" class="header-img" :src="item.avatar ? item.avatar : '/static/images/f.png'" mode=""></image>
+				<text v-show="init" class="user-name">***** 拼团成功</text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	export default {
+		data() {
+			return {
+				init: true,
+				distance: 0,
+				boxWidth: 0,
+				timer: null // 定时器名称 
+			}
+		},
+		props: {
+			combinationUserList: {
+				type: Array,
+				default: () => {
+					[]
+				}
+			}
+		},
+		watch: {
+			combinationUserList() {
+				setTimeout(e => {
+					const query = uni.createSelectorQuery().in(this);
+					query.select('#box').boundingClientRect(data => {
+						this.width = data.width
+						this.move()
+					}).exec();
+				}, 1000)
+			}
+		},
+		activated() {
+			this.move()
+		},
+		methods: {
+			move() {
+				// 设置位移
+				this.timer = setInterval(() => {
+					this.$set(this, 'distance', this.distance - this.width / this.combinationUserList.length)
+					// 如果位移超过宽度,则回到起点
+					if (this.distance === -this.width) {
+						this.init = false
+						this.distance = 0
+						setTimeout(e => {
+							this.init = true
+						}, 800)
+					}
+				}, 2500)
+			}
+		},
+		destroyed() {
+			clearInterval(this.timer);
+			this.timer = null;
+		},
+		deactivated() {
+			clearInterval(this.timer);
+			this.timer = null;
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.user-swiper {
+		width: 260rpx;
+		overflow: hidden;
+		border-radius: 25rpx;
+		background: rgba(#000000, 0.3);
+		height: 50rpx;
+		display: flex;
+		align-items: center;
+
+		.box {
+			width: max-content;
+			display: flex;
+			align-items: center;
+			flex-wrap: nowrap;
+			transition: all .8s;
+
+			.user-list {
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				width: 260rpx;
+				border-radius: 25rpx;
+				padding: 8rpx 18rpx;
+				color: #fff;
+				font-size: 22rpx;
+
+				.header-img {
+					width: 34rpx;
+					height: 34rpx;
+					border-radius: 50%;
+					border: 1px solid #FFFFFF;
+					margin-right: 6rpx;
+				}
+			}
+		}
+	}
+</style>

Datei-Diff unterdrückt, da er zu groß ist
+ 9 - 0
components/vconsole.min.js


+ 12 - 0
components/verify/utils/ase.js

@@ -0,0 +1,12 @@
+// import CryptoJS from './crypto-js.js'
+/**
+ * @word 要加密的内容
+ * @keyWord String  服务器随机返回的关键字
+ *  */
+export function aesEncrypt(word,keyWord="XwKsGlMcdPMEhR1B"){
+  // var key = CryptoJS.enc.Utf8.parse(keyWord);
+  // var srcs = CryptoJS.enc.Utf8.parse(word);
+  // var encrypted = CryptoJS.AES.encrypt(srcs, key, {mode:CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});
+  // return encrypted.toString();
+  return word
+}

Datei-Diff unterdrückt, da er zu groß ist
+ 446 - 0
components/verify/verify.vue


Datei-Diff unterdrückt, da er zu groß ist
+ 475 - 0
components/verify/verifyPoint/verifyPoint.vue


Datei-Diff unterdrückt, da er zu groß ist
+ 579 - 0
components/verify/verifySlider/verifySlider.vue


+ 1376 - 0
components/yq-avatar/yq-avatar.vue

@@ -0,0 +1,1376 @@
+<template name="yq-avatar">
+	<view>
+		<image :src="imgSrc.imgSrc" @click="fSelect" :style="[ iS ]" class="my-avatar"></image>
+		<canvas canvas-id="avatar-canvas" id="avatar-canvas" class="my-canvas" :style="{top: sT, height: csH}"
+		 disable-scroll="false"></canvas>
+		<canvas canvas-id="oper-canvas" id="oper-canvas" class="oper-canvas" :style="{top: sT, height: csH}"
+		 disable-scroll="false" @touchstart="fStart" @touchmove="fMove" @touchend="fEnd"></canvas>
+		<canvas canvas-id="prv-canvas" id="prv-canvas" class="prv-canvas" disable-scroll="false" 
+		@touchstart="fHideImg" :style="{ height: csH, top: pT }"></canvas>
+		<view class="oper-wrapper" :style="{display: sD, top:tp}">
+			<view class="oper">
+				<view class="btn-wrapper" v-if="sO">
+					<view @click="fSelect" hover-class="hover" :style="{width: bW}"><text>重选</text></view>
+					<view @click="fClose" hover-class="hover" :style="{width: bW}"><text>关闭</text></view>
+					<view @click="fRotate" hover-class="hover" :style="{width: bW, display: bD}"><text>旋转</text></view>
+					<view @click="fPreview" hover-class="hover" :style="{width: bW}"><text>预览</text></view>
+					<view @click="fUpload" hover-class="hover" :style="{width: bW}"><text>上传</text></view>
+				</view>
+				<view class="clr-wrapper" v-else>
+					<slider class="my-slider" @change="fColorChange" block-size="25" value="0" min="-100" max="100" activeColor="red"
+					 backgroundColor="green" block-color="grey" show-value></slider>
+					<view @click="fPrvUpload" hover-class="hover" :style="{width: bW}"><text>上传</text></view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	// +----------------------------------------------------------------------
+	// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+	// +----------------------------------------------------------------------
+	// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+	// +----------------------------------------------------------------------
+	// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+	// +----------------------------------------------------------------------
+	// | Author: CRMEB Team <admin@crmeb.com>
+	// +----------------------------------------------------------------------
+	"use strict";
+	const tH = 50;
+	export default {
+		name: "yq-avatar",
+		data() {
+			return {
+				csH: '0px',
+				sD: 'none',
+				sT: '-10000px',
+				pT: '-10000px',
+				iS: {},
+				sS: {},
+				sO: true,
+				bW: '19%',
+				bD: 'flex',
+				tp: 0,
+				imgSrc: {
+					imgSrc: ''
+				}
+			};
+		},
+		watch: {
+			avatarSrc() {
+				this.imgSrc.imgSrc = this.avatarSrc;
+			}
+		},
+		props: {
+			avatarSrc: '',
+			avatarStyle: '',
+			selWidth: '',
+			selHeight: '',
+			expWidth: '',
+			expHeight: '',
+			minScale: '',
+			maxScale: '',
+			canScale: '',
+			canRotate: '',
+			lockWidth: '',
+			lockHeight: '',
+			stretch: '',
+			lock: '',
+			fileType: '',
+			noTab: '',
+			inner: '',
+			quality: '',
+			index: '',
+			bgImage: '',
+		},
+		created() {
+			this.cc = uni.createCanvasContext('avatar-canvas', this);
+			this.cco = uni.createCanvasContext('oper-canvas', this);
+			this.ccp = uni.createCanvasContext('prv-canvas', this);
+			this.qlty = parseFloat(this.quality) || 1;
+			this.imgSrc.imgSrc = this.avatarSrc;
+			this.letRotate = (this.canRotate === false || this.inner === true || this.inner === 'true' || this.canRotate === 'false') ? 0 : 1;
+			this.letScale = (this.canScale === false || this.canScale === 'false') ? 0 : 1;
+			this.isin = (this.inner === true || this.inner === 'true') ? 1 : 0;
+			this.indx = this.index || undefined;
+			this.mnScale = parseFloat(this.minScale) || 0.3;
+			this.mxScale = parseFloat(this.maxScale) || 4;
+			this.noBar = (this.noTab === true || this.noTab === 'true') ? 1 : 0;
+			this.stc = this.stretch;
+			this.lck = this.lock;
+			this.fType = this.fileType === 'jpg' ? 'jpg' : 'png';
+			if (this.isin||!this.letRotate) {
+				this.bW = '24%';
+				this.bD = 'none';
+			} else {
+				this.bW = '19%';
+				this.bD = 'flex';
+			}
+			
+			if (this.noBar) {
+				this.fWindowResize();
+			} else {
+				uni.showTabBar({
+					fail: ()=>{
+						this.noBar = 1;
+					},
+					success: ()=>{
+						this.noBar = 0;
+					},
+					complete: (res) => {
+						this.fWindowResize();
+					}
+				});
+			}
+		},
+		methods: {
+			fWindowResize() {
+				let sysInfo = uni.getSystemInfoSync();
+				this.platform = sysInfo.platform;
+				this.wW = sysInfo.windowWidth;
+
+				// #ifdef H5
+				this.drawTop = sysInfo.windowTop;
+				// #endif
+				// #ifndef H5
+				this.drawTop = 0;
+				// #endif
+				
+				// #ifdef MP-ALIPAY
+				this.wH = sysInfo.screenHeight - sysInfo.statusBarHeight - sysInfo.titleBarHeight;
+				this.csH = this.wH - tH  + 'px';
+				// #endif
+				
+				// #ifndef MP-ALIPAY
+				this.wH = sysInfo.windowHeight;
+				if(!this.noBar) this.wH += tH;
+				this.csH = this.wH - tH  + 'px';
+				// #endif
+				
+				this.tp = this.csH;
+				// #ifdef H5
+				this.tp =  sysInfo.windowTop + parseInt(this.csH)+ 'px';
+				// #endif
+				
+				this.pxRatio = this.wW / 750;
+
+				let style = this.avatarStyle;
+				if (style && style !== true && (style = style.trim())) {
+					style = style.split(';');
+					let obj = {};
+					for (let v of style) {
+						if (!v) continue;
+						v = v.trim().split(':');
+						if (v[1].toString().indexOf('upx') >= 0) {
+							let arr = v[1].trim().split(' ');
+							for (let k in arr) {
+								if (!arr[k]) continue;
+								if (arr[k].toString().indexOf('upx') >= 0) {
+									arr[k] = parseFloat(arr[k]) * this.pxRatio + 'px';
+								}
+							}
+							v[1] = arr.join(' ');
+						}
+						obj[v[0].trim()] = v[1].trim();
+					}
+					this.iS = obj;
+				}
+
+				this.expWidth && (this.eW = this.expWidth.toString().indexOf('upx') >= 0 ? parseInt(this.expWidth) * this.pxRatio :
+					parseInt(this.expWidth));
+				this.expHeight && (this.eH = this.expHeight.toString().indexOf('upx') >= 0 ? parseInt(this.expHeight) * this.pxRatio :
+					parseInt(this.expHeight));
+				
+				if (this.sD === 'flex') {
+					this.fDrawInit(true);
+				}
+				this.fHideImg();
+			},
+			fSelect() {
+				if (this.fSelecting) return;
+				this.fSelecting = true;
+				setTimeout(() => {
+					this.fSelecting = false;
+				}, 500);
+
+				uni.chooseImage({
+					count: 1,
+					sizeType: ['original', 'compressed'],
+					sourceType: ['album', 'camera'],
+					success: (r) => {
+						// #ifdef MP-ALIPAY
+						uni.showLoading();
+						// #endif
+						// #ifndef MP-ALIPAY
+						uni.showLoading({
+							title: '加载中...',
+							mask: true
+						});
+						// #endif
+						let path = this.imgPath = r.tempFilePaths[0];
+						let name = r.tempFiles[0].name;
+						if(!name && path) {
+							name = path.split('/');
+							name = name[name.length - 1];
+						}
+						uni.getImageInfo({
+							src: path,
+							success: r => {
+								this.imgWidth = r.width;
+								this.imgHeight = r.height;
+								this.path = path;
+								if (!this.hasSel) {
+									let style = this.sS || {};
+									if (this.selWidth && this.selHeight) {
+										let sW = this.selWidth.toString().indexOf('upx') >= 0 ? parseInt(this.selWidth) * this.pxRatio : parseInt(
+												this.selWidth),
+											sH = this.selHeight.toString().indexOf('upx') >= 0 ? parseInt(this.selHeight) * this.pxRatio : parseInt(
+												this.selHeight);
+										style.width = sW + 'px';
+										style.height = sH + 'px';
+										style.top = ((this.wH - sH - tH)|0) / 2 + 'px';
+										style.left = ((this.wW - sW)|0) / 2 + 'px';
+										
+									} else {
+										uni.showModal({
+											title: '裁剪框的宽或高没有设置',
+											showCancel: false
+										})
+										return;
+									}
+									this.sS = style;
+								}
+
+								if (this.noBar) {
+									this.fDrawInit(true);
+								} else {
+									uni.hideTabBar({
+										complete: () => {
+											this.fDrawInit(true);
+										}
+									});
+								}
+								this.$emit('getName',name);
+							},
+							fail: () => {
+								uni.showToast({
+									title: "请选择正确图片",
+									duration: 2000,
+								})
+							},
+							complete() {
+								uni.hideLoading();
+							}
+						});
+					}
+				})
+			},
+			fUpload() {
+				if (this.fUploading) return;
+				this.fUploading = true;
+				setTimeout(() => {
+					this.fUploading = false;
+				}, 1000)
+
+				let style = this.sS,
+					x = parseInt(style.left),
+					y = parseInt(style.top),
+					width = parseInt(style.width),
+					height = parseInt(style.height),
+					expWidth = this.eW || (width* this.pixelRatio),
+					expHeight = this.eH || (height* this.pixelRatio);
+
+				// #ifdef MP-ALIPAY
+				uni.showLoading();
+				// #endif
+				// #ifndef MP-ALIPAY
+				uni.showLoading({
+					title: '加载中...',
+					mask: true
+				});
+				// #endif
+
+				this.sD = 'none';
+				this.sT = '-10000px';
+				this.hasSel = false;
+				this.fHideImg();
+				// #ifdef MP-ALIPAY
+				this.cc.toTempFilePath({
+					x: x,
+					y: y,
+					width: width,
+					height: height,
+					destWidth: expWidth,
+					destHeight: expHeight,
+					fileType: this.fType,
+					quality: this.qlty,
+					success: (r) => {
+						r = r.apFilePath;
+						this.$emit("upload", {
+							avatar: this.imgSrc,
+							path: r,
+							index: this.indx,
+							data: this.rtn,
+							base64: this.base64 || null
+						});
+					},
+					fail: (res) => {
+						uni.showToast({
+							title: "error1",
+							duration: 2000,
+						})
+					},
+					complete: () => {
+						uni.hideLoading();
+						this.noBar || uni.showTabBar();
+						this.$emit("end");
+					}
+				});
+				// #endif
+				// #ifndef MP-ALIPAY
+				uni.canvasToTempFilePath({
+					x: x,
+					y: y,
+					width: width,
+					height: height,
+					destWidth: expWidth,
+					destHeight: expHeight,
+					canvasId: 'avatar-canvas',
+					fileType: this.fType,
+					quality: this.qlty,
+					success: (r) => {
+						r = r.tempFilePath;
+						// #ifdef H5
+						this.btop(r).then((r) => {
+							this.$emit("upload", {
+								avatar: this.imgSrc,
+								path: r,
+								index: this.indx,
+								data: this.rtn,
+								base64: this.base64 || null
+							});
+							return;
+						})
+						// #endif
+						// #ifndef H5
+						this.$emit("upload", {
+							avatar: this.imgSrc,
+							path: r,
+							index: this.indx,
+							data: this.rtn,
+							base64: this.base64 || null
+						});
+						// #endif
+					},
+					fail: (res) => {
+						uni.showToast({
+							title: "error1",
+							duration: 2000,
+						})
+					},
+					complete: () => {
+						uni.hideLoading();
+						this.noBar || uni.showTabBar();
+						this.$emit("end");
+					}
+				}, this);
+				// #endif
+			},
+			fPrvUpload() {
+				if (this.fPrvUploading) return;
+				this.fPrvUploading = true;
+				setTimeout(() => {
+					this.fPrvUploading = false;
+				}, 1000)
+
+				let style = this.sS,
+					destWidth = parseInt(style.width),
+					destHeight = parseInt(style.height),
+					prvX = this.prvX,
+					prvY = this.prvY,
+					prvWidth = this.prvWidth,
+					prvHeight = this.prvHeight,
+					expWidth = this.eW || (parseInt(style.width) * this.pixelRatio),
+					expHeight = this.eH || (parseInt(style.height) * this.pixelRatio);
+
+				// #ifdef MP-ALIPAY
+				uni.showLoading();
+				// #endif
+				// #ifndef MP-ALIPAY
+				uni.showLoading({
+					title: '加载中...',
+					mask: true
+				});
+				// #endif
+
+				this.sD = 'none';
+				this.sT = '-10000px';
+				this.hasSel = false;
+				this.fHideImg();
+				// #ifdef MP-ALIPAY
+				this.ccp.toTempFilePath({
+					x: prvX,
+					y: prvY,
+					width: prvWidth,
+					height: prvHeight,
+					destWidth: expWidth,
+					destHeight: expHeight,
+					fileType: this.fType,
+					quality: this.qlty,
+					success: (r) => {
+						r = r.apFilePath;
+						this.$emit("upload", {
+							avatar: this.imgSrc,
+							path: r,
+							index: this.indx,
+							data: this.rtn,
+							base64: this.base64 || null
+						});
+					},
+					fail: () => {
+						uni.showToast({
+							title: "error_prv",
+							duration: 2000,
+						})
+					},
+					complete: () => {
+						uni.hideLoading();
+						this.noBar || uni.showTabBar();
+						this.$emit("end");
+					}
+				});
+				// #endif
+				// #ifndef MP-ALIPAY
+				uni.canvasToTempFilePath({
+					x: prvX,
+					y: prvY,
+					width: prvWidth,
+					height: prvHeight,
+					destWidth: expWidth,
+					destHeight: expHeight,
+					canvasId: 'prv-canvas',
+					fileType: this.fType,
+					quality: this.qlty,
+					success: (r) => {
+						r = r.tempFilePath;
+						// #ifdef H5
+						this.btop(r).then((r) => {
+							this.$emit("upload", {
+								avatar: this.imgSrc,
+								path: r,
+								index: this.indx,
+								data: this.rtn,
+								base64: this.base64 || null
+							});
+						})
+						// #endif
+						// #ifndef H5
+						this.$emit("upload", {
+							avatar: this.imgSrc,
+							path: r,
+							index: this.indx,
+							data: this.rtn,
+							base64: this.base64 || null
+						});
+						// #endif
+					},
+					fail: () => {
+						uni.showToast({
+							title: "error_prv",
+							duration: 2000,
+						})
+					},
+					complete: () => {
+						uni.hideLoading();
+						this.noBar || uni.showTabBar();
+						this.$emit("end");
+					}
+				}, this);
+				// #endif
+			},
+			fDrawInit(ini = false) {
+				let allWidth = this.wW,
+					allHeight = this.wH,
+					imgWidth = this.imgWidth,
+					imgHeight = this.imgHeight,
+					imgRadio = imgWidth / imgHeight,
+					useWidth = allWidth - 40,
+					useHeight = allHeight - tH - 80,
+					useRadio = useWidth / useHeight,
+					sW = parseInt(this.sS.width),
+					sH = parseInt(this.sS.height);
+
+				this.fixWidth = 0;
+				this.fixHeight = 0;
+				this.lckWidth = 0;
+				this.lckHeight = 0;
+				switch (this.stc) {
+					case 'x':
+						this.fixWidth = 1;
+						break;
+					case 'y':
+						this.fixHeight = 1;
+						break;
+					case 'long':
+						if (imgRadio > 1) this.fixWidth = 1;
+						else this.fixHeight = 1;
+						break;
+					case 'short':
+						if (imgRadio > 1) this.fixHeight = 1;
+						else this.fixWidth = 1;
+						break;
+					case 'longSel':
+						if (sW > sH) this.fixWidth = 1;
+						else this.fixHeight = 1;
+						break;
+					case 'shortSel':
+						if (sW > sH) this.fixHeight = 1;
+						else this.fixWidth = 1;
+						break;
+				}
+				switch (this.lck) {
+					case 'x':
+						this.lckWidth = 1;
+						break;
+					case 'y':
+						this.lckHeight = 1;
+						break;
+					case 'long':
+						if (imgRadio > 1) this.lckWidth = 1;
+						else this.lckHeight = 1;
+						break;
+					case 'short':
+						if (imgRadio > 1) this.lckHeight = 1;
+						else this.lckWidth = 1;
+						break;
+					case 'longSel':
+						if (sW > sH) this.lckWidth = 1;
+						else this.lckHeight = 1;
+						break;
+					case 'shortSel':
+						if (sW > sH) this.lckHeight = 1;
+						else this.lckWidth = 1;
+						break;
+				}
+				if (this.fixWidth) {
+					useWidth = sW;
+					useHeight = useWidth / imgRadio;
+				} else if (this.fixHeight) {
+					useHeight = sH;
+					useWidth = useHeight * imgRadio;
+				} else if (imgRadio < useRadio) {
+					if (imgHeight < useHeight) {
+						useWidth = imgWidth;
+						useHeight = imgHeight;
+					} else {
+						useWidth = useHeight * imgRadio;
+					}
+				} else {
+					if (imgWidth < useWidth) {
+						useWidth = imgWidth;
+						useHeight = imgHeight;
+					} else {
+						useHeight = useWidth / imgRadio;
+					}
+				}
+				if (this.isin) {
+					if (useWidth < sW) {
+						useWidth = sW;
+						useHeight = useWidth / imgRadio;
+						this.lckHeight = 0;
+					}
+					if (useHeight < sH) {
+						useHeight = sH;
+						useWidth = useHeight * imgRadio;
+						this.lckWidth = 0;
+					}
+				}
+
+				this.scaleSize = 1;
+				this.rotateDeg = 0;
+				this.posWidth = (allWidth - useWidth) / 2 | 0;
+				this.posHeight = (allHeight - useHeight - tH) / 2 | 0;
+				this.useWidth = useWidth | 0;
+				this.useHeight = useHeight | 0;
+				this.centerX = this.posWidth + useWidth / 2;
+				this.centerY = this.posHeight + useHeight / 2;
+				this.focusX = 0;
+				this.focusY = 0;
+
+				let style = this.sS,
+					left = parseInt(style.left),
+					top = parseInt(style.top),
+					width = parseInt(style.width),
+					height = parseInt(style.height),
+					canvas = this.canvas,
+					canvasOper = this.canvasOper,
+					cc = this.cc,
+					cco = this.cco;
+					
+				cco.beginPath();
+				cco.setLineWidth(3);
+				cco.setGlobalAlpha(1);
+				cco.setStrokeStyle('white');
+				cco.strokeRect(left, top, width, height);
+				
+				cco.setFillStyle('black');
+				cco.setGlobalAlpha(0.5);
+				cco.fillRect(0, 0, this.wW, top);
+				cco.fillRect(0, top, left, height);
+				cco.fillRect(0, top + height, this.wW, this.wH - height - top - tH);
+				cco.fillRect(left + width, top, this.wW - width - left, height);
+				
+				cco.setGlobalAlpha(1);
+				cco.setStrokeStyle('red');
+				cco.moveTo(left+15, top);
+				cco.lineTo(left, top);
+				cco.lineTo(left, top+15);
+				cco.moveTo(left+width-15, top);
+				cco.lineTo(left+width, top);
+				cco.lineTo(left+width, top+15);
+				cco.moveTo(left+15, top+height);
+				cco.lineTo(left, top+height);
+				cco.lineTo(left, top+height-15);
+				cco.moveTo(left+width-15, top+height);
+				cco.lineTo(left+width, top+height);
+				cco.lineTo(left+width, top+height-15);
+				cco.stroke();
+				
+				cco.draw(false, () => {
+					if (ini) {
+						this.sD = 'flex';
+						this.sT = this.drawTop + 'px';
+						this.fDrawImage(true);
+					}
+				});
+				this.$emit("init");
+			},
+			fDrawImage(ini = false) {
+				let tm_now = Date.now();
+				if (tm_now - this.drawTm < 20) return;
+				this.drawTm = tm_now;
+
+				let cc = this.cc,
+					imgWidth = this.useWidth * this.scaleSize,
+					imgHeight = this.useHeight * this.scaleSize;
+
+				// #ifdef MP-ALIPAY	
+				cc.save();
+				// #endif
+				
+				if (this.bgImage) {
+					// #ifdef MP-ALIPAY
+					cc.clearRect(0, 0, this.wW, this.wH - tH);
+					// #endif
+					// #ifndef MP-ALIPAY
+					cc.drawImage(this.bgImage, 0, 0, this.wW, this.wH - tH);
+					// #endif
+				} else {
+					cc.fillRect(0, 0, this.wW, this.wH - tH);
+				}
+
+				if (this.isin) {
+					let cx = this.focusX * (this.scaleSize - 1),
+						cy = this.focusY * (this.scaleSize - 1);
+
+					cc.translate(this.centerX, this.centerY);
+					cc.rotate(this.rotateDeg * Math.PI / 180);
+					cc.drawImage(this.imgPath, this.posWidth-this.centerX-cx, this.posHeight-this.centerY-cy, imgWidth, imgHeight);
+				
+				} else {
+					cc.translate(this.posWidth + imgWidth / 2, this.posHeight + imgHeight / 2);
+					cc.rotate(this.rotateDeg * Math.PI / 180);
+					cc.drawImage(this.imgPath, -imgWidth / 2, -imgHeight / 2, imgWidth, imgHeight);
+				}
+
+				cc.draw(false);
+
+				// #ifdef MP-ALIPAY
+				cc.restore();
+				// #endif
+			},
+			fPreview() {
+				if (this.fPreviewing) return;
+				this.fPreviewing = true;
+				setTimeout(() => {
+					this.fPreviewing = false;
+				}, 1000);
+
+				let style = this.sS,
+					x = parseInt(style.left),
+					y = parseInt(style.top),
+					width = parseInt(style.width),
+					height = parseInt(style.height);
+
+				// #ifdef MP-ALIPAY
+				uni.showLoading();
+				// #endif
+				// #ifndef MP-ALIPAY
+				uni.showLoading({
+					title: '加载中...',
+					mask: true
+				});
+				// #endif
+
+				// #ifdef MP-ALIPAY
+				this.cc.toTempFilePath({
+					x: x,
+					y: y,
+					width: width,
+					height: height,
+					expWidth: width * this.pixelRatio,
+					expHeight: height * this.pixelRatio,
+					fileType: this.fType,
+					quality: this.qlty,
+					success: (r) => {
+						this.prvImgTmp = r = r.apFilePath;
+						let ccp = this.ccp,
+							prvX = this.wW,
+							prvY = parseInt(this.csH),
+							prvWidth = parseInt(this.sS.width),
+							prvHeight = parseInt(this.sS.height),
+							useWidth = prvX - 40,
+							useHeight = prvY - 80,
+							radio = useWidth / prvWidth,
+							rHeight = prvHeight * radio;
+						if (rHeight < useHeight) {
+							prvWidth = useWidth;
+							prvHeight = rHeight;
+						} else {
+							radio = useHeight / prvHeight;
+							prvWidth *= radio;
+							prvHeight = useHeight;
+						}
+						ccp.fillRect(0, 0, prvX, prvY);
+						this.prvX = prvX = ((prvX - prvWidth) / 2) | 0;
+						this.prvY = prvY = ((prvY - prvHeight) / 2) | 0;
+						this.prvWidth = prvWidth = prvWidth | 0;
+						this.prvHeight = prvHeight = prvHeight | 0;
+						ccp.drawImage(r, prvX, prvY, prvWidth, prvHeight);
+						ccp.draw(false);
+
+						this.sO = false;
+						this.pT = '0';
+					},
+					fail: () => {
+						uni.showToast({
+							title: "error2",
+							duration: 2000,
+						})
+					},
+					complete: () => {
+						uni.hideLoading();
+					}
+				});
+				// #endif
+
+				// #ifndef MP-ALIPAY
+				uni.canvasToTempFilePath({
+					x: x,
+					y: y,
+					width: width,
+					height: height,
+					expWidth: width * this.pixelRatio,
+					expHeight: height * this.pixelRatio,
+					canvasId: 'avatar-canvas',
+					fileType: this.fType,
+					quality: this.qlty,
+					success: (r) => {
+						this.prvImgTmp = r = r.tempFilePath;
+
+						let ccp = this.ccp,
+							prvX = this.wW,
+							prvY = parseInt(this.csH);
+
+						// #ifndef H5||MP-WEIXIN||APP-PLUS
+						prvY += tH;
+						// #endif
+						// #ifdef APP-PLUS
+						if (this.platform === 'android') {
+							prvY += tH;
+						}
+						// #endif
+
+						let prvWidth = parseInt(this.sS.width),
+							prvHeight = parseInt(this.sS.height),
+							useWidth = prvX - 40,
+							useHeight = prvY - 80,
+							radio = useWidth / prvWidth,
+							rHeight = prvHeight * radio;
+						if (rHeight < useHeight) {
+							prvWidth = useWidth;
+							prvHeight = rHeight;
+						} else {
+							radio = useHeight / prvHeight;
+							prvWidth *= radio;
+							prvHeight = useHeight;
+						}
+
+						ccp.fillRect(0, 0, prvX, prvY);
+						this.prvX = prvX = ((prvX - prvWidth) / 2) | 0;
+						this.prvY = prvY = ((prvY - prvHeight) / 2) | 0;
+						this.prvWidth = prvWidth = prvWidth | 0;
+						this.prvHeight = prvHeight = prvHeight | 0;
+						ccp.drawImage(r, prvX, prvY, prvWidth, prvHeight);
+						ccp.draw(false);
+
+						// #ifdef H5
+						this.btop(r).then((r) => {
+							this.sO = false;
+							this.pT = this.drawTop + 'px';
+						})
+						// #endif
+						
+						this.sO = false; 
+						// if (this.platform === 'android') this.sO = false;
+						this.pT = this.drawTop + 'px';
+					},
+					fail: () => {
+						uni.showToast({
+							title: "error2",
+							duration: 2000,
+						})
+					},
+					complete: () => {
+						uni.hideLoading();
+					}
+				}, this);
+				// #endif
+			},
+			fChooseImg(index = undefined, params = undefined, data = undefined) {
+				if (params) {
+					let sW = params.selWidth,
+						sH = params.selHeight,
+						expWidth = params.expWidth,
+						expHeight = params.expHeight,
+						quality = params.quality,
+						canRotate = params.canRotate,
+						canScale = params.canScale,
+						minScale = params.minScale,
+						maxScale = params.maxScale,
+						stretch = params.stretch,
+						fileType = params.fileType,
+						inner = params.inner,
+						lock = params.lock;
+
+					expWidth && (this.eW = expWidth.toString().indexOf('upx') >= 0 ? parseInt(expWidth) * this.pxRatio : parseInt(
+						expWidth));
+					expHeight && (this.eH = expHeight.toString().indexOf('upx') >= 0 ? parseInt(expHeight) * this.pxRatio : parseInt(
+						expHeight));
+					this.letRotate = (canRotate === false || inner === true || inner === 'true' || canRotate === 'false') ? 0 : 1;
+					this.letScale = (canScale === false || canScale === 'false') ? 0 : 1;
+					this.qlty = parseFloat(quality) || 1;
+					this.mnScale = parseFloat(minScale) || 0.3;
+					this.mxScale = parseFloat(maxScale) || 4;
+					this.stc = stretch;
+					this.isin = (inner === true || inner === 'true') ? 1 : 0;
+					this.fType = fileType === 'jpg' ? 'jpg' : 'png';
+					this.lck = lock;
+					if (this.isin||!this.letRotate) {
+						this.bW = '24%';
+						this.bD = 'none';
+					} else {
+						this.bW = '19%';
+						this.bD = 'flex';
+					}
+
+					if (sW && sH) {
+						sW = sW.toString().indexOf('upx') >= 0 ? parseInt(sW) * this.pxRatio : parseInt(sW);
+						sH = sH.toString().indexOf('upx') >= 0 ? parseInt(sH) * this.pxRatio : parseInt(sH);
+						this.sS.width = sW + 'px';
+						this.sS.height = sH + 'px';
+						this.sS.top = ((this.wH - sH - tH)|0) / 2 + 'px';
+						this.sS.left = ((this.wW - sW)|0) / 2 + 'px';
+						this.hasSel = true;
+					}
+				}
+				this.rtn = data;
+				this.indx = index;
+				this.fSelect();
+			},
+			fRotate() {
+				this.rotateDeg += 90 - this.rotateDeg % 90;
+				this.fDrawImage();
+			},
+			fStart(e) {
+				let touches = e.touches,
+					touch0 = touches[0],
+					touch1 = touches[1];
+
+				this.touch0 = touch0;
+				this.touch1 = touch1;
+
+				if (touch1) {
+					let x = touch1.x - touch0.x,
+						y = touch1.y - touch0.y;
+					this.fgDistance = Math.sqrt(x * x + y * y);
+				}
+			},
+			fMove(e) {
+				let touches = e.touches,
+					touch0 = touches[0],
+					touch1 = touches[1];
+
+				if (touch1) {
+					let x = touch1.x - touch0.x,
+						y = touch1.y - touch0.y,
+						fgDistance = Math.sqrt(x * x + y * y),
+						scaleSize = 0.005 * (fgDistance - this.fgDistance),
+						beScaleSize = this.scaleSize + scaleSize;
+
+					do {
+						if (!this.letScale) break;
+						if (beScaleSize < this.mnScale) break;
+						if (beScaleSize > this.mxScale) break;
+
+						let growX = this.useWidth * scaleSize / 2,
+							growY = this.useHeight * scaleSize / 2;
+						if (this.isin) {
+							let imgWidth = this.useWidth * beScaleSize,
+								imgHeight = this.useHeight * beScaleSize,
+								l = this.posWidth - growX,
+								t = this.posHeight - growY,
+								r = l + imgWidth,
+								b = t + imgHeight,
+								left = parseInt(this.sS.left),
+								top = parseInt(this.sS.top),
+								width = parseInt(this.sS.width),
+								height = parseInt(this.sS.height),
+								right = left + width,
+								bottom = top + height,
+								cx, cy;
+
+							if (imgWidth <= width || imgHeight <= height) break;
+							this.cx = cx = this.focusX * beScaleSize - this.focusX,
+								this.cy = cy = this.focusY * beScaleSize - this.focusY;
+							this.posWidth -= growX;
+							this.posHeight -= growY;
+							if (this.posWidth - cx > left) {
+								this.posWidth = left + cx;
+							}
+							if (this.posWidth + imgWidth - cx < right) {
+								this.posWidth = right - imgWidth + cx;
+							}
+							if (this.posHeight - cy > top) {
+								this.posHeight = top + cy;
+							}
+							if (this.posHeight + imgHeight - cy < bottom) {
+								this.posHeight = bottom - imgHeight + cy;
+							}
+						} else {
+							this.posWidth -= growX;
+							this.posHeight -= growY;
+						}
+
+						this.scaleSize = beScaleSize;
+					} while (0);
+					this.fgDistance = fgDistance;
+					if (touch1.x !== touch0.x && this.letRotate) {
+						x = (this.touch1.y - this.touch0.y) / (this.touch1.x - this.touch0.x);
+						y = (touch1.y - touch0.y) / (touch1.x - touch0.x);
+						this.rotateDeg += Math.atan((y - x) / (1 + x * y)) * 180 / Math.PI;
+						this.touch0 = touch0;
+						this.touch1 = touch1;
+					}
+
+					this.fDrawImage();
+				} else if (this.touch0) {
+					let x = touch0.x - this.touch0.x,
+						y = touch0.y - this.touch0.y,
+						beX = this.posWidth + x,
+						beY = this.posHeight + y;
+					if (this.isin) {
+						let imgWidth = this.useWidth * this.scaleSize,
+							imgHeight = this.useHeight * this.scaleSize,
+							l = beX,
+							t = beY,
+							r = l + imgWidth,
+							b = t + imgHeight,
+							left = parseInt(this.sS.left),
+							top = parseInt(this.sS.top),
+							right = left + parseInt(this.sS.width),
+							bottom = top + parseInt(this.sS.height),
+							cx, cy;
+
+						this.cx = cx = this.focusX * this.scaleSize - this.focusX;
+						this.cy = cy = this.focusY * this.scaleSize - this.focusY;
+
+						if (!this.lckWidth && Math.abs(x) < 100) {
+							if (left < l - cx) {
+								this.posWidth = left + cx;
+							} else if (right > r - cx) {
+								this.posWidth = right - imgWidth + cx;
+							} else {
+								this.posWidth = beX;
+								this.focusX -= x;
+							}
+						}
+						if (!this.lckHeight && Math.abs(y) < 100) {
+							if (top < t - cy) {
+								this.focusY -= (top + cy - this.posHeight);
+								this.posHeight = top + cy;
+							} else if (bottom > b - cy) {
+								this.focusY -= (bottom + cy - (this.posHeight + imgHeight));
+								this.posHeight = bottom - imgHeight + cy;
+							} else {
+								this.posHeight = beY;
+								this.focusY -= y;
+							}
+						}
+					} else {
+						if (Math.abs(x) < 100 && !this.lckWidth) this.posWidth = beX;
+						if (Math.abs(y) < 100 && !this.lckHeight) this.posHeight = beY;
+						this.focusX -= x;
+						this.focusY -= y;
+					}
+
+					this.touch0 = touch0;
+					this.fDrawImage();
+				}
+			},
+			fEnd(e) {
+				let touches = e.touches,
+					touch0 = touches && touches[0],
+					touch1 = touches && touches[1];
+				if (touch0) {
+					this.touch0 = touch0;
+				} else {
+					this.touch0 = null;
+					this.touch1 = null;
+				}
+			},
+			fHideImg() {
+				this.prvImg = '';
+				this.pT = '-10000px';
+				this.sO = true;
+				this.prvImgData = null;
+				this.target = null;
+			},
+			fClose() {
+				this.sD = 'none';
+				this.sT = '-10000px';
+				this.hasSel = false;
+				this.fHideImg();
+				this.noBar || uni.showTabBar();
+				this.$emit("end");
+			},
+			fGetImgData() {
+				return new Promise((resolve, reject) => {
+					let prvX = this.prvX,
+						prvY = this.prvY,
+						prvWidth = this.prvWidth,
+						prvHeight = this.prvHeight;
+					// #ifdef MP-ALIPAY
+					this.ccp.getImageData({
+						x: prvX,
+						y: prvY,
+						width: prvWidth,
+						height: prvHeight,
+						success(res) {
+							resolve(res.data);
+						},
+						fail(err) {
+							reject(err);
+						}
+					}, this);
+					// #endif
+					// #ifndef MP-ALIPAY
+					uni.canvasGetImageData({
+						canvasId: 'prv-canvas',
+						x: prvX,
+						y: prvY,
+						width: prvWidth,
+						height: prvHeight,
+						success(res) {
+							resolve(res.data);
+						},
+						fail(err) {
+							reject(err);
+						}
+					}, this);
+					// #endif
+				});
+			},
+			async fColorChange(e) {
+				let tm_now = Date.now();
+				if (tm_now - this.prvTm < 100) return;
+				this.prvTm = tm_now;
+
+				// #ifdef MP-ALIPAY
+				uni.showLoading();
+				// #endif
+				// #ifndef MP-ALIPAY
+				uni.showLoading({
+					title: '加载中...',
+					mask: true
+				});
+				// #endif
+
+				if (!this.prvImgData) {
+					if (!(this.prvImgData = await this.fGetImgData().catch(() => {
+							uni.showToast({
+								title: "error_read",
+								duration: 2000,
+							})
+						}))) return;
+
+					this.target = new Uint8ClampedArray(this.prvImgData.length);
+				}
+
+				let data = this.prvImgData,
+					target = this.target,
+					i = e.detail.value,
+					r, g, b, a, h, s, l, d, p, q, t, min, max, hK, tR, tG, tB;
+
+				if (i === 0) {
+					target = data;
+				} else {
+					i = (i + 100) / 200;
+					if (i < 0.005) i = 0;
+					if (i > 0.995) i = 1;
+					for (let n = data.length - 1; n >= 0; n -= 4) {
+						r = data[n - 3] / 255;
+						g = data[n - 2] / 255;
+						b = data[n - 1] / 255;
+						max = Math.max(r, g, b);
+						min = Math.min(r, g, b);
+						d = max - min;
+						if (max === min) {
+							h = 0;
+						} else if (max === r && g >= b) {
+							h = 60 * ((g - b) / d);
+						} else if (max === r && g < b) {
+							h = 60 * ((g - b) / d) + 360;
+						} else if (max === g) {
+							h = 60 * ((b - r) / d) + 120;
+						} else if (max === b) {
+							h = 60 * ((r - g) / d) + 240;
+						}
+						l = (max + min) / 2;
+						if (l === 0 || max === min) {
+							s = 0;
+						} else if (0 < l && l <= 0.5) {
+							s = d / (2 * l);
+						} else if (l > 0.5) {
+							s = d / (2 - 2 * l);
+						}
+						data[n] && (a = data[n]);
+
+						if (i < 0.5) {
+							s = s * i / 0.5;
+						} else if (i > 0.5) {
+							s = 2 * s + 2 * i - (s * i / 0.5) - 1;
+						}
+
+						if (s === 0) {
+							r = g = b = Math.round(l * 255);
+						} else {
+							if (l < 0.5) {
+								q = l * (1 + s);
+							} else if (l >= 0.5) {
+								q = l + s - (l * s);
+							}
+							p = 2 * l - q;
+							hK = h / 360;
+							tR = hK + 1 / 3;
+							tG = hK;
+							tB = hK - 1 / 3;
+							let correctRGB = (t) => {
+								if (t < 0) {
+									return t + 1.0;
+								}
+								if (t > 1) {
+									return t - 1.0;
+								}
+								return t;
+							};
+							let createRGB = (t) => {
+								if (t < (1 / 6)) {
+									return p + ((q - p) * 6 * t);
+								} else if (t >= (1 / 6) && t < (1 / 2)) {
+									return q;
+								} else if (t >= (1 / 2) && t < (2 / 3)) {
+									return p + ((q - p) * 6 * ((2 / 3) - t));
+								}
+								return p;
+							};
+							r = tR = Math.round(createRGB(correctRGB(tR)) * 255);
+							g = tG = Math.round(createRGB(correctRGB(tG)) * 255);
+							b = tB = Math.round(createRGB(correctRGB(tB)) * 255);
+						}
+						a && (target[n] = a);
+						target[n - 3] = r;
+						target[n - 2] = g;
+						target[n - 1] = b;
+					}
+				}
+
+				let prvX = this.prvX,
+					prvY = this.prvY,
+					prvWidth = this.prvWidth,
+					prvHeight = this.prvHeight;
+
+				// #ifdef MP-ALIPAY
+				this.ccp.putImageData({
+					x: prvX,
+					y: prvY,
+					width: prvWidth,
+					height: prvHeight,
+					data: target,
+					fail() {
+						uni.showToast({
+							title: 'error_put',
+							duration: 2000
+						})
+					},
+					complete() {
+						uni.hideLoading();
+					}
+				}, this);
+				// #endif
+
+				// #ifndef MP-ALIPAY
+				uni.canvasPutImageData({
+					canvasId: 'prv-canvas',
+					x: prvX,
+					y: prvY,
+					width: prvWidth,
+					height: prvHeight,
+					data: target,
+					fail() {
+						uni.showToast({
+							title: 'error_put',
+							duration: 2000
+						})
+					},
+					complete() {
+						uni.hideLoading();
+					}
+				}, this);
+				// #endif
+			},
+			btop(base64) {
+				this.base64 = base64;
+				return new Promise(function(resolve, reject) {
+					var arr = base64.split(','),
+						mime = arr[0].match(/:(.*?);/)[1],
+						bstr = atob(arr[1]),
+						n = bstr.length,
+						u8arr = new Uint8Array(n);
+					while (n--) {
+						u8arr[n] = bstr.charCodeAt(n);
+					}
+					return resolve((window.URL || window.webkitURL).createObjectURL(new Blob([u8arr], {
+						type: mime
+					})));
+				});
+			},
+		}
+	}
+</script>
+
+<style scoped>
+	.my-canvas {
+		display: flex;
+		position: fixed !important;
+		background: #000000;
+		left: 0;
+		z-index: 100000;
+		width: 100%;
+	}
+
+	.my-avatar {
+		width: 150upx;
+		height: 150upx;
+		border-radius: 100%;
+	}
+
+	.oper-canvas {
+		display: flex;
+		position: fixed !important;
+		left: 0;
+		z-index: 100001;
+		width: 100%;
+	}
+
+	.prv-canvas {
+		display: flex;
+		position: fixed !important;
+		background: #000000;
+		left: 0;
+		z-index: 200000;
+		width: 100%;
+	}
+
+	.oper-wrapper {
+		height: 50px;
+		position: fixed !important;
+		box-sizing: border-box;
+		border: 1px solid #F1F1F1;
+		background: #ffffff;
+		width: 100%;
+		left: 0;
+		bottom: 0;
+		z-index: 100009;
+		flex-direction: row;
+	}
+
+	.oper {
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		padding: 10upx 20upx;
+		width: 100%;
+		height: 100%;
+		box-sizing: border-box;
+		align-self: center;
+	}
+
+	.btn-wrapper {
+		display: flex;
+		flex-direction: row;
+		/* #ifndef H5 */
+		flex-grow: 1;
+		/* #endif */
+		/* #ifdef H5 */
+		height: 50px;
+		/* #endif */
+		justify-content: space-between;
+	}
+
+	.btn-wrapper view {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		font-size: 16px;
+		color: #333;
+		border: 1px solid #f1f1f1;
+		border-radius: 6%;
+	}
+
+	.hover {
+		background: #f1f1f1;
+		border-radius: 6%;
+	}
+
+	.clr-wrapper {
+		display: flex;
+		flex-direction: row;
+		flex-grow: 1;
+	}
+
+	.clr-wrapper view {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		font-size: 16px;
+		color: #333;
+		border: 1px solid #f1f1f1;
+		border-radius: 6%;
+	}
+
+	.my-slider {
+		flex-grow: 1;
+	}
+</style>

+ 1210 - 0
components/zb-code/qrcode.js

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

+ 219 - 0
components/zb-code/zb-code.vue

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

+ 56 - 0
config/app.js

@@ -0,0 +1,56 @@
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+// #ifdef H5
+let VUE_APP_WS_URL = `ws://${location.hostname}?type=user`
+// #endif
+
+let openPlantGrass = '-openPlantGrass-'
+
+// 网络接口修改此字符 小程序域名要求https
+// let httpApi = 'http://192.168.31.106:8324' //测试
+// let httpApi = 'https://merchant.wssms.com' //生产
+let httpApi = 'https://shushijia.qiniu1314.com' //生产
+
+// 聊天接口修改此字符 小程序聊天要求wss 例如:wss://mer.crmeb.net
+// let wsApi = 'ws://192.168.3.20:8324'
+let wsApi = 'wss://shushijia.qiniu1314.com'
+
+module.exports = {
+	// 请求域名 格式: https://您的域名
+	// #ifdef MP || APP-PLUS
+	// HTTP_REQUEST_URL: httpApi,
+	HTTP_REQUEST_URL: httpApi,
+	VUE_APP_WS_URL: `${wsApi}?type=user`,
+	// #endif
+
+	// #ifdef H5
+	//H5接口是浏览器地址
+	HTTP_REQUEST_URL: httpApi || window.location.protocol + "//" + window.location.host,
+	// 聊天长连接地址
+	VUE_APP_WS_URL: wsApi ? `${wsApi}?type=user` : VUE_APP_WS_URL,
+	// #endif
+	openPlantGrass: openPlantGrass,
+	HEADER: {
+		'content-type': 'application/json',
+		//#ifdef H5
+		'Form-type': navigator.userAgent.toLowerCase().indexOf("micromessenger") !== -1 ? 'wechat' : 'h5',
+		//#endif
+		//#ifdef MP
+		'Form-type': 'routine',
+		//#endif
+		//#ifdef APP-PLUS
+		'Form-type': 'app',
+		//#endif
+	},
+	// 回话密钥名称 请勿修改此配置
+	TOKENNAME: 'X-Token',
+	// 缓存时间 0 永久
+	EXPIRE: 0,
+};

+ 42 - 0
config/cache.js

@@ -0,0 +1,42 @@
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+module.exports = {
+	//token
+	LOGIN_STATUS: 'LOGIN_STATUS_TOKEN',
+	UUID:'UUID',
+	// uid
+	UID:'UID',
+	//�û�
+	USER_INFO: 'USER_INFO',
+	//token�����¼�
+	EXPIRES_TIME: 'EXPIRES_TIME',
+	//�Ƿ���Ȩ
+	WX_AUTH: 'WX_AUTH',
+	//���ں���Ȩcode
+	STATE_KEY: 'wx_authorize_state',
+	//�û�����
+	LOGINTYPE: 'loginType',
+	//���ں���ת����
+	BACK_URL: 'login_back_url',
+	// ����code
+	STATE_R_KEY: 'roution_authorize_state',
+	//��ȨlogoС����
+	LOGO_URL: 'LOGO_URL',
+	//模板缓存
+	SUBSCRIBE_MESSAGE: 'SUBSCRIBE_MESSAGE',
+	TIPS_KEY: 'TIPS_KEY',
+	SPREAD: 'spread',
+	//缓存经度
+	CACHE_LONGITUDE: 'LONGITUDE',
+	//缓存纬度
+	CACHE_LATITUDE: 'LATITUDE',
+	CART_ID: 'CART_ID',
+	CART_TIME: 'CART_TIME'
+}

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.