Browse Source

first commit

gcz 4 years ago
commit
79a5632913
100 changed files with 12876 additions and 0 deletions
  1. 22 0
      App.vue
  2. 121 0
      common/css/common.css
  3. 55 0
      components/mescroll-uni/components/mescroll-down.css
  4. 47 0
      components/mescroll-uni/components/mescroll-down.vue
  5. 90 0
      components/mescroll-uni/components/mescroll-empty.vue
  6. 83 0
      components/mescroll-uni/components/mescroll-top.vue
  7. 47 0
      components/mescroll-uni/components/mescroll-up.css
  8. 39 0
      components/mescroll-uni/components/mescroll-up.vue
  9. 19 0
      components/mescroll-uni/mescroll-body.css
  10. 344 0
      components/mescroll-uni/mescroll-body.vue
  11. 65 0
      components/mescroll-uni/mescroll-mixins.js
  12. 33 0
      components/mescroll-uni/mescroll-uni-option.js
  13. 36 0
      components/mescroll-uni/mescroll-uni.css
  14. 788 0
      components/mescroll-uni/mescroll-uni.js
  15. 420 0
      components/mescroll-uni/mescroll-uni.vue
  16. 50 0
      components/mescroll-uni/mixins/mescroll-comp.js
  17. 51 0
      components/mescroll-uni/mixins/mescroll-more-item.js
  18. 56 0
      components/mescroll-uni/mixins/mescroll-more.js
  19. 102 0
      components/mescroll-uni/wxs/mixins.js
  20. 92 0
      components/mescroll-uni/wxs/renderjs.js
  21. 268 0
      components/mescroll-uni/wxs/wxs.wxs
  22. 96 0
      components/uni-icons/icons.js
  23. 57 0
      components/uni-icons/uni-icons.vue
  24. 22 0
      components/uni-popup/message.js
  25. 25 0
      components/uni-popup/popup.js
  26. 243 0
      components/uni-popup/uni-popup-dialog.vue
  27. 116 0
      components/uni-popup/uni-popup-message.vue
  28. 165 0
      components/uni-popup/uni-popup-share.vue
  29. 294 0
      components/uni-popup/uni-popup.vue
  30. 183 0
      components/uni-search-bar/uni-search-bar.vue
  31. 136 0
      components/uni-section/uni-section.vue
  32. 292 0
      components/uni-swipe-action-item/bindingx.js
  33. 266 0
      components/uni-swipe-action-item/index.wxs
  34. 207 0
      components/uni-swipe-action-item/mpalipay.js
  35. 252 0
      components/uni-swipe-action-item/mpother.js
  36. 116 0
      components/uni-swipe-action-item/mpwxs.js
  37. 365 0
      components/uni-swipe-action-item/uni-swipe-action-item.vue
  38. 42 0
      components/uni-swipe-action/uni-swipe-action.vue
  39. 279 0
      components/uni-transition/uni-transition.vue
  40. 5 0
      config/CONST.js
  41. 34 0
      config/config.js
  42. 1 0
      js_sdk/jweixin-1.4.0.js
  43. 88 0
      js_sdk/luch-request/luch-request/adapters/index.js
  44. 51 0
      js_sdk/luch-request/luch-request/core/InterceptorManager.js
  45. 199 0
      js_sdk/luch-request/luch-request/core/Request.js
  46. 20 0
      js_sdk/luch-request/luch-request/core/buildFullPath.js
  47. 30 0
      js_sdk/luch-request/luch-request/core/defaults.js
  48. 6 0
      js_sdk/luch-request/luch-request/core/dispatchRequest.js
  49. 89 0
      js_sdk/luch-request/luch-request/core/mergeConfig.js
  50. 16 0
      js_sdk/luch-request/luch-request/core/settle.js
  51. 69 0
      js_sdk/luch-request/luch-request/helpers/buildURL.js
  52. 14 0
      js_sdk/luch-request/luch-request/helpers/combineURLs.js
  53. 14 0
      js_sdk/luch-request/luch-request/helpers/isAbsoluteURL.js
  54. 2 0
      js_sdk/luch-request/luch-request/index.js
  55. 131 0
      js_sdk/luch-request/luch-request/utils.js
  56. 44 0
      main.js
  57. 95 0
      manifest.json
  58. 150 0
      pages.json
  59. 41 0
      pages/inbuild/inbuild.vue
  60. 63 0
      pages/index/index.css
  61. 356 0
      pages/index/index.vue
  62. 191 0
      pages/login/login - 副本.vue
  63. 43 0
      pages/login/login.vue
  64. 39 0
      pages/product/product.css
  65. 237 0
      pages/product/product.vue
  66. 24 0
      pages/productcategory/productcategory.css
  67. 118 0
      pages/productcategory/productcategory.vue
  68. 22 0
      pages/producttype/producttype.css
  69. 230 0
      pages/producttype/producttype.vue
  70. 116 0
      pages/publish/chosetype/chosetype - 副本.vue
  71. 107 0
      pages/publish/chosetype/chosetype.vue
  72. 48 0
      pages/publish/publish.css
  73. 629 0
      pages/publish/publish.vue
  74. 46 0
      pages/publish/publishsuccess/publishsuccess.vue
  75. 33 0
      pages/publish/publishtype/publishtype.css
  76. 184 0
      pages/publish/publishtype/publishtype.vue
  77. 15 0
      pages/searchresults/searchresults.css
  78. 178 0
      pages/searchresults/searchresults.vue
  79. 161 0
      pages/supplier/supplier.vue
  80. 49 0
      pages/supplier/supplierdetail/supplierdetail.css
  81. 199 0
      pages/supplier/supplierdetail/supplierdetail.vue
  82. 256 0
      pages/usercenter/authentication/authentication.vue
  83. 160 0
      pages/usercenter/feedback/feedback.vue
  84. 185 0
      pages/usercenter/focusme/focusme.vue
  85. 201 0
      pages/usercenter/myfocus/myfocus.vue
  86. 172 0
      pages/usercenter/mypublish/mypublish.vue
  87. 155 0
      pages/usercenter/usercenter - 副本.vue
  88. 24 0
      pages/usercenter/usercenter.css
  89. 161 0
      pages/usercenter/usercenter.vue
  90. 539 0
      static/iconfont/demo.css
  91. 561 0
      static/iconfont/demo_index.html
  92. 85 0
      static/iconfont/iconfont.css
  93. BIN
      static/iconfont/iconfont.eot
  94. 1 0
      static/iconfont/iconfont.js
  95. 128 0
      static/iconfont/iconfont.json
  96. 77 0
      static/iconfont/iconfont.svg
  97. BIN
      static/iconfont/iconfont.ttf
  98. BIN
      static/iconfont/iconfont.woff
  99. BIN
      static/iconfont/iconfont.woff2
  100. 0 0
      static/img/checkbox-checked.png

+ 22 - 0
App.vue

@@ -0,0 +1,22 @@
+<script>
+	import iconfont from "@/static/iconfont/iconfont.js";
+	export default {
+		onLaunch: function() {
+			// this.$wxApi.config()
+			// console.log('App Launch')
+		},
+		onShow: function() {
+			// console.log('App Show')
+		},
+		onHide: function() {
+			// console.log('App Hide')
+		}
+	}
+</script>
+
+<style>
+	/*每个页面公共css */	
+	@import url("static/iconfont/iconfont.css");
+	@import url("common/css/common.css");
+	
+</style>

+ 121 - 0
common/css/common.css

@@ -0,0 +1,121 @@
+body{font-size: 28rpx;font-family: PingFangSC-Medium, PingFang SC;color: #333;}
+page{background-color: #FBFBFC;}
+uni-input{font-size: 28rpx;}
+input{outline: 0!important;}
+uni-button[type=primary]{background-color: #6BBC6D;}
+
+.wrap{margin: 0 40rpx;}
+
+/* 轮播 */
+.appAdv{height:400rpx;}
+.appAdv .adv-item{height:100%;}
+.appAdv .adv-item .pic{width:750rpx;height:100%;}
+.appAdv .swiper{height:100%;}
+.appAdv .swiper >>> .uni-swiper-dots-horizontal{bottom:25rpx;}
+.appAdv  >>> .uni-swiper-dot-active{background-color: #90c0ef;}
+
+
+/* 图片 */
+.full-img{width: 100%;height: auto;}
+
+/* 对齐 */
+.f-tac{text-align: center;}
+
+/* 内容块 */
+.write-radius{background-color: #fff;border-radius: 24rpx;padding: 24rpx;margin-bottom: 24rpx;}
+
+
+/* 浮底按钮 */
+.bottom-btn-wrap{height: 110rpx;margin-top: 80rpx;}
+.bottom-btn{height: 110rpx;line-height: 110rpx;position: fixed;left: 24rpx;bottom: 24rpx;right: 24rpx;}
+
+/* 浮底多个按钮 */
+.bottom-flex-btn-wrap{box-sizing: border-box;height: 148rpx;margin-top: 80rpx;;}
+.bottom-flex-btn-content{display: flex;align-items: center;padding: 0 24rpx;border-top: 1px solid #eee;height: 148rpx;position: fixed;left: 0;bottom: 0;right: 0;background-color: #fff;}
+.bottom-flex-btn{flex: 1;height: 99rpx;line-height: 99rpx;}
+.bottom-flex-btn + .bottom-flex-btn{margin-left: 24rpx;}
+.bottom-flex-btn.primary{background-color: #6BBC6D;border: 0;color: #fff;}
+
+/* 页面大按钮 */
+.big-btn{display: block;margin: 24rpx;padding: 24rpx 0;border-radius: 8rpx;text-align: center;font-size: 36rpx;line-height: 50rpx;font-weight: 400;}
+.big-btn.primary{background-color: #6BBC6D;color: #fff;}
+.big-btn-hollow{border: 1px solid #E5E5E5;color: #333;}
+
+/* 常规间距 */
+.nomal-top{margin-top: 40rpx;}
+.nomal-bottom{margin-bottom: 40rpx;}
+
+/* 搜索 */
+.full-search{display: flex;overflow: hidden;background-color: #fff;border: 1px solid #cecece;border-radius: 100rpx;}
+.full-search-btn{margin: 0;outline: none;border: 0;}
+.full-search-btn:after{border: 0;border-radius: 0;}
+.full-search-btn,
+.full-search-input{height: 80rpx;line-height: 80rpx;}
+.full-search-input{flex: 1;padding-left: 10rpx;}
+
+
+/* 表单样式 */
+.form-item-type{margin: 24rpx 0;}
+.form-item{position: relative;display: flex;min-height: 96rpx;justify-content: space-between;align-items: center;margin: 0 0 0 24rpx;border-bottom: 1px solid #eee;}
+.form-item.arrow::after{content: '';width: 20rpx;height: 20rpx;margin-top: -10rpx;right: 24rpx;position: absolute;top: 50%;border-top: 1px solid #ccc;border-right: 1px solid #ccc;transform: rotate(45deg);}
+.form-item-til,
+.form-item-con{padding: 20rpx 0;}
+.form-item-til{padding-right: 15rpx;font-size: 34rpx;}
+.form-item-con{display: flex;text-align: right;padding-right: 24rpx;color: #999;font-size: 30rpx;min-width: 70%;justify-content: flex-end;flex: 1;}
+.form-item.arrow .form-item-con{padding-right: 65rpx;}
+.form-item-con input{width: 100%;box-sizing: border-box;}
+.form-item-chooseIMGs-wrap .choosedIMG{width: 128rpx;height: 128rpx;margin-left: 20rpx;}
+.chooseIMG-btn{width: 128rpx;height: 128rpx;margin-left: 40rpx;box-sizing: border-box;padding: 10rpx;background-color: #F4F4F5;}
+.chooseIMG-btn .chooseIMG-btn-img{width: 100%;height: 100%;}
+.form-item.upfile{display: block;text-align: left;}
+.form-item.upfile .form-item-con{justify-content: flex-start;}
+.chooseIMG-btn-havetxt{width: 180rpx;height: 180rpx;margin-left: 40rpx;box-sizing: border-box;padding: 10rpx;background-color: #F4F4F5;display: flex;flex-wrap: wrap;align-items: center;justify-content: center;}
+.chooseIMG-btn-havetxt .chooseIMG-btn-img{width: 80rpx;height: 80rpx;}
+.chooseIMG-txt{font-size: 12rpx;font-weight: 400;color: #D8D8D8;line-height: 17rpx;}
+.form-item-til-tip{font-size: 12rpx;color: #999;margin-left: 10rpx;}
+
+/* 截取隐藏 */
+.f-ellipsis{overflow: hidden;white-space: nowrap;text-overflow: ellipsis;}
+
+/* 页面大标题 */
+.big-til{font-size: 40rpx;margin: 24rpx 0;font-weight: 500;color: #333;line-height: 56rpx;}
+
+/* 图标 标题 右箭头 */
+.select-bar{display: flex;margin-bottom: 8rpx;align-items: center;box-sizing: border-box;height: 96rpx;border-radius: 16rpx;background-color: #fff;padding: 0 24rpx 0 30rpx;}
+.select-bar-icon{width: 56rpx;height: 56rpx;}
+.select-bar-text{flex: 1;margin: 0 24rpx;font-size: 34rpx;font-family: PingFangSC-Regular, PingFang SC;font-weight: 400;color: #333;line-height: 48rpx;}
+.select-bar-arrow{width: 30rpx;height: 30rpx;margin-left: auto;border-top: 2rpx solid #ccc;border-right: 2rpx solid #ccc;transform: rotate(45deg);transition: all .3s;}
+.open .select-bar-arrow{transform: rotate(135deg);}
+.select-bar-img-wrap{width: 56rpx;height: 56rpx;border-radius: 50%;overflow: hidden;}
+.select-bar-img{width: 100%;height: 100%;}
+
+/* 内容全屏 */
+/* top:calc(44px + env(safe-area-inset-top)); 头部导航时 */
+.fix-content{position: fixed;left: 0;right: 0;top:calc(44px + env(safe-area-inset-top));bottom: 0;}
+/* 隐藏弹出 */
+.fix-content.translate{transition: all .3s;background-color: #fff;z-index: 100;transform: translateX(100%);}
+.fix-content.translate.open{transform: translateX(0);}
+
+/* 圆角带边框左对齐搜索 */
+.search-left-color >>> .uni-searchbar__box{justify-content: left!important;border-color: #6BBC6D!important;border-radius: 50rpx!important;background-color: #fff!important;}
+
+/* 产品列表 */
+.product-wrap{background-color: #fff;border-radius: 16rpx;margin: 24rpx;padding: 24rpx;}
+.product-wrap .product-item:last-of-type{border-bottom: 0;}
+.product-item{display: flex;border-bottom: 1px solid #eee;padding-bottom: 24rpx;margin-bottom: 24rpx;}
+.product-item-info{flex: 1;}
+.product-item-img-wrap{width: 160rpx;max-height: 160rpx;overflow: hidden;margin-right: 24rpx;}
+.product-item-img{width: 100%;height: 100%;}
+.product-item-til{font-size: 30rpx;font-weight: 500;color: #333;line-height: 42rpx;margin-bottom: 13rpx;display: -webkit-box;-webkit-line-clamp: 2;-webkit-box-orient: vertical;overflow: hidden;}
+.product-item-brand{margin-bottom: 14rpx;font-size: 26rpx;font-weight: 400;color: #999;line-height: 37rpx;}
+.product-item-brand .postage{margin-left: 24rpx;padding: 2rpx 12rpx;font-size: 20rpx;font-weight: 400;color: #6BBC6D;line-height: 1;background-color: #E1F2E2;border-radius: 24rpx;}
+.product-item-addr{font-size: 26rpx;font-weight: 400;color: #999;line-height: 37rpx;margin-bottom: 8rpx;}
+.product-item-price {font-size: 24rpx;font-weight: 400;color: #999;line-height: 33rpx;}
+.product-item-price .price{font-size: 36rpx;font-weight: 500;color: #FF5030;line-height: 50rpx;margin-right: 5rpx;}
+.product-item-price .rmb{font-size: 28rpx;font-weight: 500;color: #FF5030;line-height: 40rpx;margin-right: 8rpx;}
+.product-item-price {font-size: 24rpx;font-weight: 400;color: #999;line-height: 33rpx;}
+
+/* 页面提示信息 */
+.page-tip{margin: 24rpx 40rpx;font-size: 32rpx;color: #999;line-height: 45rpx;}
+.page-tip-til{color: #6BBC6D;}
+

+ 55 - 0
components/mescroll-uni/components/mescroll-down.css

@@ -0,0 +1,55 @@
+/* 下拉刷新区域 */
+.mescroll-downwarp {
+	position: absolute;
+	top: -100%;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	text-align: center;
+}
+
+/* 下拉刷新--内容区,定位于区域底部 */
+.mescroll-downwarp .downwarp-content {
+	position: absolute;
+	left: 0;
+	bottom: 0;
+	width: 100%;
+	min-height: 60rpx;
+	padding: 20rpx 0;
+	text-align: center;
+}
+
+/* 下拉刷新--提示文本 */
+.mescroll-downwarp .downwarp-tip {
+	display: inline-block;
+	font-size: 28rpx;
+	vertical-align: middle;
+	margin-left: 16rpx;
+	/* color: gray; 已在style设置color,此处删去*/
+}
+
+/* 下拉刷新--旋转进度条 */
+.mescroll-downwarp .downwarp-progress {
+	display: inline-block;
+	width: 32rpx;
+	height: 32rpx;
+	border-radius: 50%;
+	border: 2rpx solid gray;
+	border-bottom-color: transparent !important; /*已在style设置border-color,此处需加 !important*/
+	vertical-align: middle;
+}
+
+/* 旋转动画 */
+.mescroll-downwarp .mescroll-rotate {
+	animation: mescrollDownRotate 0.6s linear infinite;
+}
+
+@keyframes mescrollDownRotate {
+	0% {
+		transform: rotate(0deg);
+	}
+
+	100% {
+		transform: rotate(360deg);
+	}
+}

+ 47 - 0
components/mescroll-uni/components/mescroll-down.vue

@@ -0,0 +1,47 @@
+<!-- 下拉刷新区域 -->
+<template>
+	<view v-if="mOption.use" class="mescroll-downwarp" :style="{'background-color':mOption.bgColor,'color':mOption.textColor}">
+		<view class="downwarp-content">
+			<view class="downwarp-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mOption.textColor, 'transform':downRotate}"></view>
+			<view class="downwarp-tip">{{downText}}</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		option: Object , // down的配置项
+		type: Number, // 下拉状态(inOffset:1, outOffset:2, showLoading:3, endDownScroll:4)
+		rate: Number // 下拉比率 (inOffset: rate<1; outOffset: rate>=1)
+	},
+	computed: {
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
+		mOption(){
+			return this.option || {}
+		},
+		// 是否在加载中
+		isDownLoading(){
+			return this.type === 3
+		},
+		// 旋转的角度
+		downRotate(){
+			return 'rotate(' + 360 * this.rate + 'deg)'
+		},
+		// 文本提示
+		downText(){
+			switch (this.type){
+				case 1: return this.mOption.textInOffset;
+				case 2: return this.mOption.textOutOffset;
+				case 3: return this.mOption.textLoading;
+				case 4: return this.mOption.textLoading;
+				default: return this.mOption.textInOffset;
+			}
+		}
+	}
+};
+</script>
+
+<style>
+@import "./mescroll-down.css";
+</style>

+ 90 - 0
components/mescroll-uni/components/mescroll-empty.vue

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

+ 83 - 0
components/mescroll-uni/components/mescroll-top.vue

@@ -0,0 +1,83 @@
+<!-- 回到顶部的按钮 -->
+<template>
+	<image
+		v-if="mOption.src"
+		class="mescroll-totop"
+		:class="[value ? 'mescroll-totop-in' : 'mescroll-totop-out', {'mescroll-totop-safearea': mOption.safearea}]"
+		:style="{'z-index':mOption.zIndex, 'left': left, 'right': right, 'bottom':addUnit(mOption.bottom), 'width':addUnit(mOption.width), 'border-radius':addUnit(mOption.radius)}"
+		:src="mOption.src"
+		mode="widthFix"
+		@click="toTopClick"
+	/>
+</template>
+
+<script>
+export default {
+	props: {
+		// up.toTop的配置项
+		option: Object,
+		// 是否显示
+		value: false
+	},
+	computed: {
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
+		mOption(){
+			return this.option || {}
+		},
+		// 优先显示左边
+		left(){
+			return this.mOption.left ? this.addUnit(this.mOption.left) : 'auto';
+		},
+		// 右边距离 (优先显示左边)
+		right() {
+			return this.mOption.left ? 'auto' : this.addUnit(this.mOption.right);
+		}
+	},
+	methods: {
+		addUnit(num){
+			if(!num) return 0;
+			if(typeof num === 'number') return num + 'rpx';
+			return num
+		},
+		toTopClick() {
+			this.$emit('input', false); // 使v-model生效
+			this.$emit('click'); // 派发点击事件
+		}
+	}
+};
+</script>
+
+<style>
+/* 回到顶部的按钮 */
+.mescroll-totop {
+	z-index: 9990;
+	position: fixed !important; /* 加上important避免编译到H5,在多mescroll中定位失效 */
+	right: 20rpx;
+	bottom: 120rpx;
+	width: 72rpx;
+	height: auto;
+	border-radius: 50%;
+	opacity: 0;
+	transition: opacity 0.5s; /* 过渡 */
+	margin-bottom: var(--window-bottom); /* css变量 */
+}
+
+/* 适配 iPhoneX */
+@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
+	.mescroll-totop-safearea {
+		margin-bottom: calc(var(--window-bottom) + constant(safe-area-inset-bottom)); /* window-bottom + 适配 iPhoneX */
+		margin-bottom: calc(var(--window-bottom) + env(safe-area-inset-bottom));
+	}
+}
+
+/* 显示 -- 淡入 */
+.mescroll-totop-in {
+	opacity: 1;
+}
+
+/* 隐藏 -- 淡出且不接收事件*/
+.mescroll-totop-out {
+	opacity: 0;
+	pointer-events: none;
+}
+</style>

+ 47 - 0
components/mescroll-uni/components/mescroll-up.css

@@ -0,0 +1,47 @@
+/* 上拉加载区域 */
+.mescroll-upwarp {
+	box-sizing: border-box;
+	min-height: 110rpx;
+	padding: 30rpx 0;
+	text-align: center;
+	clear: both;
+}
+
+/*提示文本 */
+.mescroll-upwarp .upwarp-tip,
+.mescroll-upwarp .upwarp-nodata {
+	display: inline-block;
+	font-size: 28rpx;
+	vertical-align: middle;
+	/* color: gray; 已在style设置color,此处删去*/
+}
+
+.mescroll-upwarp .upwarp-tip {
+	margin-left: 16rpx;
+}
+
+/*旋转进度条 */
+.mescroll-upwarp .upwarp-progress {
+	display: inline-block;
+	width: 32rpx;
+	height: 32rpx;
+	border-radius: 50%;
+	border: 2rpx solid gray;
+	border-bottom-color: transparent !important; /*已在style设置border-color,此处需加 !important*/
+	vertical-align: middle;
+}
+
+/* 旋转动画 */
+.mescroll-upwarp .mescroll-rotate {
+	animation: mescrollUpRotate 0.6s linear infinite;
+}
+
+@keyframes mescrollUpRotate {
+	0% {
+		transform: rotate(0deg);
+	}
+
+	100% {
+		transform: rotate(360deg);
+	}
+}

+ 39 - 0
components/mescroll-uni/components/mescroll-up.vue

@@ -0,0 +1,39 @@
+<!-- 上拉加载区域 -->
+<template>
+	<view class="mescroll-upwarp" :style="{'background-color':mOption.bgColor,'color':mOption.textColor}">
+		<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
+		<view v-show="isUpLoading">
+			<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mOption.textColor}"></view>
+			<view class="upwarp-tip">{{ mOption.textLoading }}</view>
+		</view>
+		<!-- 无数据 -->
+		<view v-if="isUpNoMore" class="upwarp-nodata">{{ mOption.textNoMore }}</view>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		option: Object, // up的配置项
+		type: Number // 上拉加载的状态:0(loading前),1(loading中),2(没有更多了)
+	},
+	computed: {
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
+		mOption() {
+			return this.option || {};
+		},
+		// 加载中
+		isUpLoading() {
+			return this.type === 1;
+		},
+		// 没有更多了
+		isUpNoMore() {
+			return this.type === 2;
+		}
+	}
+};
+</script>
+
+<style>
+@import './mescroll-up.css';
+</style>

+ 19 - 0
components/mescroll-uni/mescroll-body.css

@@ -0,0 +1,19 @@
+.mescroll-body {
+	position: relative; /* 下拉刷新区域相对自身定位 */
+	height: auto; /* 不可固定高度,否则overflow:hidden导致无法滑动; 同时使设置的最小高生效,实现列表不满屏仍可下拉*/
+	overflow: hidden; /* 当有元素写在mescroll-body标签前面时,可遮住下拉刷新区域 */
+	box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
+}
+
+/* 使sticky生效: 父元素不能overflow:hidden或者overflow:auto属性 */
+.mescroll-body.mescorll-sticky{
+	overflow: unset !important
+}
+
+/* 适配 iPhoneX */
+@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
+	.mescroll-safearea {
+		padding-bottom: constant(safe-area-inset-bottom);
+		padding-bottom: env(safe-area-inset-bottom);
+	}
+}

+ 344 - 0
components/mescroll-uni/mescroll-body.vue

@@ -0,0 +1,344 @@
+<template>
+	<view 
+	class="mescroll-body mescroll-render-touch" 
+	:class="{'mescorll-sticky': sticky}"
+	:style="{'minHeight':minHeight, 'padding-top': padTop, 'padding-bottom': padBottom}" 
+	@touchstart="wxsBiz.touchstartEvent" 
+	@touchmove="wxsBiz.touchmoveEvent" 
+	@touchend="wxsBiz.touchendEvent" 
+	@touchcancel="wxsBiz.touchendEvent"
+	:change:prop="wxsBiz.propObserver"
+	:prop="wxsProp"
+	>
+		<!-- 状态栏 -->
+		<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
+		
+		<view class="mescroll-body-content mescroll-wxs-content" :style="{ transform: translateY, transition: transition }" :change:prop="wxsBiz.callObserver" :prop="callProp">
+			<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
+			<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
+			<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
+				<view class="downwarp-content">
+					<view class="downwarp-progress mescroll-wxs-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mescroll.optDown.textColor, 'transform': downRotate}"></view>
+					<view class="downwarp-tip">{{downText}}</view>
+				</view>
+			</view>
+	
+			<!-- 列表内容 -->
+			<slot></slot>
+
+			<!-- 空布局 -->
+			<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
+
+			<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
+			<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
+			<view v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
+				<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
+				<view v-show="upLoadType===1">
+					<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
+					<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
+				</view>
+				<!-- 无数据 -->
+				<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
+			</view>
+		</view>
+		
+		<!-- 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效) -->
+		<!-- #ifdef H5 -->
+		<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
+		<!-- #endif -->
+		
+		<!-- 适配iPhoneX -->
+		<view v-if="safearea" class="mescroll-safearea"></view>
+		
+		<!-- 回到顶部按钮 (fixed元素需写在transform外面,防止降级为absolute)-->
+		<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
+		
+		<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
+		<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
+		<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
+		<!-- #endif -->
+	</view>
+</template>
+
+<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
+<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
+<script src="./wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
+<!-- #endif -->
+
+<!-- app, h5使用renderjs -->
+<!-- #ifdef APP-PLUS || H5 -->
+<script module="renderBiz" lang="renderjs">
+	import renderBiz from './wxs/renderjs.js';
+	export default {
+		mixins: [renderBiz]
+	}
+</script>
+<!-- #endif -->
+
+<script>
+	// 引入mescroll-uni.js,处理核心逻辑
+	import MeScroll from './mescroll-uni.js';
+	// 引入全局配置
+	import GlobalOption from './mescroll-uni-option.js';
+	// 引入空布局组件
+	import MescrollEmpty from './components/mescroll-empty.vue';
+	// 引入回到顶部组件
+	import MescrollTop from './components/mescroll-top.vue';
+	// 引入兼容wxs(含renderjs)写法的mixins
+	import WxsMixin from './wxs/mixins.js';
+	
+	export default {
+		mixins: [WxsMixin],
+		components: {
+			MescrollEmpty,
+			MescrollTop
+		},
+		data() {
+			return {
+				mescroll: {optDown:{},optUp:{}}, // mescroll实例
+				downHight: 0, //下拉刷新: 容器高度
+				downRate: 0, // 下拉比率(inOffset: rate<1; outOffset: rate>=1)
+				downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
+				upLoadType: 0, // 上拉加载状态:0(loading前),1(loading中),2(没有更多了,显示END文本提示),3(没有更多了,不显示END文本提示)
+				isShowEmpty: false, // 是否显示空布局
+				isShowToTop: false, // 是否显示回到顶部按钮
+				windowHeight: 0, // 可使用窗口的高度
+				windowBottom: 0, // 可使用窗口的底部位置
+				statusBarHeight: 0 // 状态栏高度
+			};
+		},
+		props: {
+			down: Object, // 下拉刷新的参数配置
+			up: Object, // 上拉加载的参数配置
+			top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+			topbar: [Boolean, String], // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
+			bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+			safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
+			height: [String, Number], // 指定mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
+			bottombar:{ // 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效)
+				type: Boolean,
+				default: true
+			},
+			sticky: Boolean // 是否支持sticky,默认false; 当值配置true时,需避免在mescroll-body标签前面加非定位的元素,否则下拉区域无法会隐藏
+		},
+		computed: {
+			// mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
+			minHeight(){
+				return this.toPx(this.height || '100%') + 'px'
+			},
+			// 下拉布局往下偏移的距离 (px)
+			numTop() {
+				return this.toPx(this.top)
+			},
+			padTop() {
+				return this.numTop + 'px';
+			},
+			// 上拉布局往上偏移 (px)
+			numBottom() {
+				return this.toPx(this.bottom);
+			},
+			padBottom() {
+				return this.numBottom + 'px';
+			},
+			// 是否为重置下拉的状态
+			isDownReset() {
+				return this.downLoadType === 3 || this.downLoadType === 4;
+			},
+			// 过渡
+			transition() {
+				return this.isDownReset ? 'transform 300ms' : '';
+			},
+			translateY() {
+				return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : ''; // transform会使fixed失效,需注意把fixed元素写在mescroll之外
+			},
+			// 是否在加载中
+			isDownLoading(){
+				return this.downLoadType === 3
+			},
+			// 旋转的角度
+			downRotate(){
+				return 'rotate(' + 360 * this.downRate + 'deg)'
+			},
+			// 文本提示
+			downText(){
+				if(!this.mescroll) return ""; // 避免头条小程序初始化时报错
+				switch (this.downLoadType){
+					case 1: return this.mescroll.optDown.textInOffset;
+					case 2: return this.mescroll.optDown.textOutOffset;
+					case 3: return this.mescroll.optDown.textLoading;
+					case 4: return this.mescroll.optDown.textLoading;
+					default: return this.mescroll.optDown.textInOffset;
+				}
+			}
+		},
+		methods: {
+			//number,rpx,upx,px,% --> px的数值
+			toPx(num) {
+				if (typeof num === 'string') {
+					if (num.indexOf('px') !== -1) {
+						if (num.indexOf('rpx') !== -1) {
+							// "10rpx"
+							num = num.replace('rpx', '');
+						} else if (num.indexOf('upx') !== -1) {
+							// "10upx"
+							num = num.replace('upx', '');
+						} else {
+							// "10px"
+							return Number(num.replace('px', ''));
+						}
+					} else if (num.indexOf('%') !== -1) {
+						// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
+						let rate = Number(num.replace('%', '')) / 100;
+						return this.windowHeight * rate;
+					}
+				}
+				return num ? uni.upx2px(Number(num)) : 0;
+			},
+			// 点击空布局的按钮回调
+			emptyClick() {
+				this.$emit('emptyclick', this.mescroll);
+			},
+			// 点击回到顶部的按钮回调
+			toTopClick() {
+				this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
+				this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
+			}
+		},
+		// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
+		created() {
+			let vm = this;
+
+			let diyOption = {
+				// 下拉刷新的配置
+				down: {
+					inOffset() {
+						vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
+					},
+					outOffset() {
+						vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
+					},
+					onMoving(mescroll, rate, downHight) {
+						// 下拉过程中的回调,滑动过程一直在执行;
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						vm.downRate = rate; //下拉比率 (inOffset: rate<1; outOffset: rate>=1)
+					},
+					showLoading(mescroll, downHight) {
+						vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+					},
+					endDownScroll() {
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						if(vm.downResetTimer) {clearTimeout(vm.downResetTimer); vm.downResetTimer = null} // 移除重置倒计时
+						vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,避免下次inOffset不及时显示textInOffset
+							if(vm.downLoadType === 4) vm.downLoadType = 0
+						},300)
+					},
+					// 派发下拉刷新的回调
+					callback: function(mescroll) {
+						vm.$emit('down', mescroll);
+					}
+				},
+				// 上拉加载的配置
+				up: {
+					// 显示加载中的回调
+					showLoading() {
+						vm.upLoadType = 1;
+					},
+					// 显示无更多数据的回调
+					showNoMore() {
+						vm.upLoadType = 2;
+					},
+					// 隐藏上拉加载的回调
+					hideUpScroll(mescroll) {
+						vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
+					},
+					// 空布局
+					empty: {
+						onShow(isShow) {
+							// 显示隐藏的回调
+							vm.isShowEmpty = isShow;
+						}
+					},
+					// 回到顶部
+					toTop: {
+						onShow(isShow) {
+							// 显示隐藏的回调
+							vm.isShowToTop = isShow;
+						}
+					},
+					// 派发上拉加载的回调
+					callback: function(mescroll) {
+						vm.$emit('up', mescroll);
+					}
+				}
+			};
+
+			MeScroll.extend(diyOption, GlobalOption); // 混入全局的配置
+			let myOption = JSON.parse(JSON.stringify({down: vm.down,up: vm.up})); // 深拷贝,避免对props的影响
+			MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
+
+			// 初始化MeScroll对象
+			vm.mescroll = new MeScroll(myOption, true); // 传入true,标记body为滚动区域
+			// init回调mescroll对象
+			vm.$emit('init', vm.mescroll);
+
+			// 设置高度
+			const sys = uni.getSystemInfoSync();
+			if (sys.windowHeight) vm.windowHeight = sys.windowHeight;
+			if (sys.windowBottom) vm.windowBottom = sys.windowBottom;
+			if (sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
+			// 使down的bottomOffset生效
+			vm.mescroll.setBodyHeight(sys.windowHeight);
+
+			// 因为使用的是page的scroll,这里需自定义scrollTo
+			vm.mescroll.resetScrollTo((y, t) => {
+				if(typeof y === 'string'){
+					// 滚动到指定view (y为css选择器)
+					setTimeout(()=>{ // 延时确保view已渲染; 不使用$nextTick
+						let selector;
+						if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
+							selector = '#'+y // 不带#和. 则默认为id选择器
+						}else{
+							selector = y
+							// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
+							if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
+								selector = y.split('>>>')[1].trim()
+							}
+							// #endif
+						}
+						uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
+							if (rect) {
+								let top = rect.top
+								top += vm.mescroll.getScrollTop()
+								uni.pageScrollTo({
+									scrollTop: top,
+									duration: t
+								})
+							} else{
+								console.error(selector + ' does not exist');
+							}
+						}).exec()
+					},30)
+				} else{
+					// 滚动到指定位置 (y必须为数字)
+					uni.pageScrollTo({
+						scrollTop: y,
+						duration: t
+					})
+				}
+			});
+
+			// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
+			if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
+				vm.mescroll.optUp.toTop.safearea = vm.safearea;
+			}
+		}
+	};
+</script>
+
+<style>
+	@import "./mescroll-body.css";
+	@import "./components/mescroll-down.css";
+	@import './components/mescroll-up.css';
+</style>

+ 65 - 0
components/mescroll-uni/mescroll-mixins.js

@@ -0,0 +1,65 @@
+// mescroll-body 和 mescroll-uni 通用
+
+// import MescrollUni from "./mescroll-uni.vue";
+// import MescrollBody from "./mescroll-body.vue";
+
+const MescrollMixin = {
+	// components: { // 非H5端无法通过mixin注册组件, 只能在main.js中注册全局组件或具体界面中注册
+	// 	MescrollUni,
+	// 	MescrollBody
+	// },
+	data() {
+		return {
+			mescroll: null //mescroll实例对象
+		}
+	},
+	// 注册系统自带的下拉刷新 (配置down.native为true时生效, 还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
+	onPullDownRefresh(){
+		this.mescroll && this.mescroll.onPullDownRefresh();
+	},
+	// 注册列表滚动事件,用于判定在顶部可下拉刷新,在指定位置可显示隐藏回到顶部按钮 (此方法为页面生命周期,无法在子组件中触发, 仅在mescroll-body生效)
+	onPageScroll(e) {
+		this.mescroll && this.mescroll.onPageScroll(e);
+	},
+	// 注册滚动到底部的事件,用于上拉加载 (此方法为页面生命周期,无法在子组件中触发, 仅在mescroll-body生效)
+	onReachBottom() {
+		this.mescroll && this.mescroll.onReachBottom();
+	},
+	methods: {
+		// mescroll组件初始化的回调,可获取到mescroll对象
+		mescrollInit(mescroll) {
+			this.mescroll = mescroll;
+			this.mescrollInitByRef(); // 兼容字节跳动小程序
+		},
+		// 以ref的方式初始化mescroll对象 (兼容字节跳动小程序: http://www.mescroll.com/qa.html?v=20200107#q26)
+		mescrollInitByRef() {
+			if(!this.mescroll || !this.mescroll.resetUpScroll){
+				let mescrollRef = this.$refs.mescrollRef;
+				if(mescrollRef) this.mescroll = mescrollRef.mescroll
+			}
+		},
+		// 下拉刷新的回调 (mixin默认resetUpScroll)
+		downCallback() {
+			if(this.mescroll.optUp.use){
+				this.mescroll.resetUpScroll()
+			}else{
+				setTimeout(()=>{
+					this.mescroll.endSuccess();
+				}, 500)
+			}
+		},
+		// 上拉加载的回调
+		upCallback() {
+			// mixin默认延时500自动结束加载
+			setTimeout(()=>{
+				this.mescroll.endErr();
+			}, 500)
+		}
+	},
+	mounted() {
+		this.mescrollInitByRef(); // 兼容字节跳动小程序, 避免未设置@init或@init此时未能取到ref的情况
+	}
+	
+}
+
+export default MescrollMixin;

+ 33 - 0
components/mescroll-uni/mescroll-uni-option.js

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

+ 36 - 0
components/mescroll-uni/mescroll-uni.css

@@ -0,0 +1,36 @@
+.mescroll-uni-warp{
+	height: 100%;
+}
+
+.mescroll-uni-content{
+	height: 100%;
+}
+
+.mescroll-uni {
+	position: relative;
+	width: 100%;
+	height: 100%;
+	min-height: 200rpx;
+	overflow-y: auto;
+	box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
+}
+
+/* 定位的方式固定高度 */
+.mescroll-uni-fixed{
+	z-index: 1;
+	position: fixed;
+	top: 0;
+	left: 0;
+	right: 0;
+	bottom: 0;
+	width: auto; /* 使right生效 */
+	height: auto; /* 使bottom生效 */
+}
+
+/* 适配 iPhoneX */
+@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
+	.mescroll-safearea {
+		padding-bottom: constant(safe-area-inset-bottom);
+		padding-bottom: env(safe-area-inset-bottom);
+	}
+}

+ 788 - 0
components/mescroll-uni/mescroll-uni.js

@@ -0,0 +1,788 @@
+/* mescroll
+ * version 1.3.2
+ * 2020-08-05 wenju
+ * http://www.mescroll.com
+ */
+
+export default function MeScroll(options, isScrollBody) {
+	let me = this;
+	me.version = '1.3.2'; // mescroll版本号
+	me.options = options || {}; // 配置
+	me.isScrollBody = isScrollBody || false; // 滚动区域是否为原生页面滚动; 默认为scroll-view
+
+	me.isDownScrolling = false; // 是否在执行下拉刷新的回调
+	me.isUpScrolling = false; // 是否在执行上拉加载的回调
+	let hasDownCallback = me.options.down && me.options.down.callback; // 是否配置了down的callback
+
+	// 初始化下拉刷新
+	me.initDownScroll();
+	// 初始化上拉加载,则初始化
+	me.initUpScroll();
+
+	// 自动加载
+	setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
+		// 自动触发下拉刷新 (只有配置了down的callback才自动触发下拉刷新)
+		if ((me.optDown.use || me.optDown.native) && me.optDown.auto && hasDownCallback) {
+			if (me.optDown.autoShowLoading) {
+				me.triggerDownScroll(); // 显示下拉进度,执行下拉回调
+			} else {
+				me.optDown.callback && me.optDown.callback(me); // 不显示下拉进度,直接执行下拉回调
+			}
+		}
+		// 自动触发上拉加载
+		if(!me.isUpAutoLoad){ // 部分小程序(头条小程序)emit是异步, 会导致isUpAutoLoad判断有误, 先延时确保先执行down的callback,再执行up的callback
+			setTimeout(function(){
+				me.optUp.use && me.optUp.auto && !me.isUpAutoLoad && me.triggerUpScroll();
+			},100)
+		}
+	}, 30); // 需让me.optDown.inited和me.optUp.inited先执行
+}
+
+/* 配置参数:下拉刷新 */
+MeScroll.prototype.extendDownScroll = function(optDown) {
+	// 下拉刷新的配置
+	MeScroll.extend(optDown, {
+		use: true, // 是否启用下拉刷新; 默认true
+		auto: true, // 是否在初始化完毕之后自动执行下拉刷新的回调; 默认true
+		native: false, // 是否使用系统自带的下拉刷新; 默认false; 仅mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
+		autoShowLoading: false, // 如果设置auto=true(在初始化完毕之后自动执行下拉刷新的回调),那么是否显示下拉刷新的进度; 默认false
+		isLock: false, // 是否锁定下拉刷新,默认false;
+		offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
+		startTop: 100, // scroll-view快速滚动到顶部时,此时的scroll-top可能大于0, 此值用于控制最大的误差
+		inOffsetRate: 1, // 在列表顶部,下拉的距离小于offset时,改变下拉区域高度比例;值小于1且越接近0,高度变化越小,表现为越往下越难拉
+		outOffsetRate: 0.2, // 在列表顶部,下拉的距离大于offset时,改变下拉区域高度比例;值小于1且越接近0,高度变化越小,表现为越往下越难拉
+		bottomOffset: 20, // 当手指touchmove位置在距离body底部20px范围内的时候结束上拉刷新,避免Webview嵌套导致touchend事件不执行
+		minAngle: 45, // 向下滑动最少偏移的角度,取值区间  [0,90];默认45度,即向下滑动的角度大于45度则触发下拉;而小于45度,将不触发下拉,避免与左右滑动的轮播等组件冲突;
+		textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
+		textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
+		textLoading: '加载中 ...', // 加载中的提示文本
+		bgColor: "transparent", // 背景颜色 (建议在pages.json中再设置一下backgroundColorTop)
+		textColor: "gray", // 文本颜色 (当bgColor配置了颜色,而textColor未配置时,则textColor会默认为白色)
+		inited: null, // 下拉刷新初始化完毕的回调
+		inOffset: null, // 下拉的距离进入offset范围内那一刻的回调
+		outOffset: null, // 下拉的距离大于offset那一刻的回调
+		onMoving: null, // 下拉过程中的回调,滑动过程一直在执行; rate下拉区域当前高度与指定距离的比值(inOffset: rate<1; outOffset: rate>=1); downHight当前下拉区域的高度
+		beforeLoading: null, // 准备触发下拉刷新的回调: 如果return true,将不触发showLoading和callback回调; 常用来完全自定义下拉刷新, 参考案例【淘宝 v6.8.0】
+		showLoading: null, // 显示下拉刷新进度的回调
+		afterLoading: null, // 显示下拉刷新进度的回调之后,马上要执行的代码 (如: 在wxs中使用)
+		beforeEndDownScroll: null, // 准备结束下拉的回调. 返回结束下拉的延时执行时间,默认0ms; 常用于结束下拉之前再显示另外一小段动画,才去隐藏下拉刷新的场景, 参考案例【dotJump】
+		endDownScroll: null, // 结束下拉刷新的回调
+		afterEndDownScroll: null, // 结束下拉刷新的回调,马上要执行的代码 (如: 在wxs中使用)
+		callback: function(mescroll) {
+			// 下拉刷新的回调;默认重置上拉加载列表为第一页
+			mescroll.resetUpScroll();
+		}
+	})
+}
+
+/* 配置参数:上拉加载 */
+MeScroll.prototype.extendUpScroll = function(optUp) {
+	// 上拉加载的配置
+	MeScroll.extend(optUp, {
+		use: true, // 是否启用上拉加载; 默认true
+		auto: true, // 是否在初始化完毕之后自动执行上拉加载的回调; 默认true
+		isLock: false, // 是否锁定上拉加载,默认false;
+		isBoth: true, // 上拉加载时,如果滑动到列表顶部是否可以同时触发下拉刷新;默认true,两者可同时触发;
+		callback: null, // 上拉加载的回调;function(page,mescroll){ }
+		page: {
+			num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
+			size: 10, // 每页数据的数量
+			time: null // 加载第一页数据服务器返回的时间; 防止用户翻页时,后台新增了数据从而导致下一页数据重复;
+		},
+		noMoreSize: 5, // 如果列表已无数据,可设置列表的总数量要大于等于5条才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看
+		offset: 80, // 距底部多远时,触发upCallback
+		textLoading: '加载中 ...', // 加载中的提示文本
+		textNoMore: '-- END --', // 没有更多数据的提示文本
+		bgColor: "transparent", // 背景颜色 (建议在pages.json中再设置一下backgroundColorBottom)
+		textColor: "gray", // 文本颜色 (当bgColor配置了颜色,而textColor未配置时,则textColor会默认为白色)
+		inited: null, // 初始化完毕的回调
+		showLoading: null, // 显示加载中的回调
+		showNoMore: null, // 显示无更多数据的回调
+		hideUpScroll: null, // 隐藏上拉加载的回调
+		errDistance: 60, // endErr的时候需往上滑动一段距离,使其往下滑动时再次触发onReachBottom,仅mescroll-body生效
+		toTop: {
+			// 回到顶部按钮,需配置src才显示
+			src: null, // 图片路径,默认null (绝对路径或网络图)
+			offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000
+			duration: 300, // 回到顶部的动画时长,默认300ms (当值为0或300则使用系统自带回到顶部,更流畅; 其他值则通过step模拟,部分机型可能不够流畅,所以非特殊情况不建议修改此项)
+			btnClick: null, // 点击按钮的回调
+			onShow: null, // 是否显示的回调
+			zIndex: 9990, // fixed定位z-index值
+			left: null, // 到左边的距离, 默认null. 此项有值时,right不生效. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+			right: 20, // 到右边的距离, 默认20 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+			bottom: 120, // 到底部的距离, 默认120 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+			safearea: false, // bottom的偏移量是否加上底部安全区的距离, 默认false, 需要适配iPhoneX时使用 (具体的界面如果不配置此项,则取本vue的safearea值)
+			width: 72, // 回到顶部图标的宽度, 默认72 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+			radius: "50%" // 圆角, 默认"50%" (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+		},
+		empty: {
+			use: true, // 是否显示空布局
+			icon: null, // 图标路径
+			tip: '~ 暂无相关数据 ~', // 提示
+			btnText: '', // 按钮
+			btnClick: null, // 点击按钮的回调
+			onShow: null, // 是否显示的回调
+			fixed: false, // 是否使用fixed定位,默认false; 配置fixed为true,以下的top和zIndex才生效 (transform会使fixed失效,最终会降级为absolute)
+			top: "100rpx", // fixed定位的top值 (完整的单位值,如 "10%"; "100rpx")
+			zIndex: 99 // fixed定位z-index值
+		},
+		onScroll: false // 是否监听滚动事件
+	})
+}
+
+/* 配置参数 */
+MeScroll.extend = function(userOption, defaultOption) {
+	if (!userOption) return defaultOption;
+	for (let key in defaultOption) {
+		if (userOption[key] == null) {
+			let def = defaultOption[key];
+			if (def != null && typeof def === 'object') {
+				userOption[key] = MeScroll.extend({}, def); // 深度匹配
+			} else {
+				userOption[key] = def;
+			}
+		} else if (typeof userOption[key] === 'object') {
+			MeScroll.extend(userOption[key], defaultOption[key]); // 深度匹配
+		}
+	}
+	return userOption;
+}
+
+/* 简单判断是否配置了颜色 (非透明,非白色) */
+MeScroll.prototype.hasColor = function(color) {
+	if(!color) return false;
+	let c = color.toLowerCase();
+	return c != "#fff" && c != "#ffffff" && c != "transparent" && c != "white"
+}
+
+/* -------初始化下拉刷新------- */
+MeScroll.prototype.initDownScroll = function() {
+	let me = this;
+	// 配置参数
+	me.optDown = me.options.down || {};
+	if(!me.optDown.textColor && me.hasColor(me.optDown.bgColor)) me.optDown.textColor = "#fff"; // 当bgColor有值且textColor未设置,则textColor默认白色
+	me.extendDownScroll(me.optDown);
+	
+	// 如果是mescroll-body且配置了native,则禁止自定义的下拉刷新
+	if(me.isScrollBody && me.optDown.native){
+		me.optDown.use = false
+	}else{
+		me.optDown.native = false // 仅mescroll-body支持,mescroll-uni不支持
+	}
+	
+	me.downHight = 0; // 下拉区域的高度
+
+	// 在页面中加入下拉布局
+	if (me.optDown.use && me.optDown.inited) {
+		// 初始化完毕的回调
+		setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
+			me.optDown.inited(me);
+		}, 0)
+	}
+}
+
+/* 列表touchstart事件 */
+MeScroll.prototype.touchstartEvent = function(e) {
+	if (!this.optDown.use) return;
+
+	this.startPoint = this.getPoint(e); // 记录起点
+	this.startTop = this.getScrollTop(); // 记录此时的滚动条位置
+	this.startAngle = 0; // 初始角度
+	this.lastPoint = this.startPoint; // 重置上次move的点
+	this.maxTouchmoveY = this.getBodyHeight() - this.optDown.bottomOffset; // 手指触摸的最大范围(写在touchstart避免body获取高度为0的情况)
+	this.inTouchend = false; // 标记不是touchend
+}
+
+/* 列表touchmove事件 */
+MeScroll.prototype.touchmoveEvent = function(e) {
+	if (!this.optDown.use) return;
+	let me = this;
+
+	let scrollTop = me.getScrollTop(); // 当前滚动条的距离
+	let curPoint = me.getPoint(e); // 当前点
+
+	let moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+
+	// 向下拉 && 在顶部
+	// mescroll-body,直接判定在顶部即可
+	// scroll-view在滚动时不会触发touchmove,当触顶/底/左/右时,才会触发touchmove
+	// scroll-view滚动到顶部时,scrollTop不一定为0,也有可能大于0; 在iOS的APP中scrollTop可能为负数,不一定和startTop相等
+	if (moveY > 0 && (
+			(me.isScrollBody && scrollTop <= 0)
+			||
+			(!me.isScrollBody && (scrollTop <= 0 || (scrollTop <= me.optDown.startTop && scrollTop === me.startTop)) )
+		)) {
+		// 可下拉的条件
+		if (!me.inTouchend && !me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling &&
+				me.optUp.isBoth))) {
+
+			// 下拉的初始角度是否在配置的范围内
+			if(!me.startAngle) me.startAngle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90]
+			if (me.startAngle < me.optDown.minAngle) return; // 如果小于配置的角度,则不往下执行下拉刷新
+
+			// 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发
+			if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) {
+				me.inTouchend = true; // 标记执行touchend
+				me.touchendEvent(); // 提前触发touchend
+				return;
+			}
+			
+			me.preventDefault(e); // 阻止默认事件
+
+			let diff = curPoint.y - me.lastPoint.y; // 和上次比,移动的距离 (大于0向下,小于0向上)
+
+			// 下拉距离  < 指定距离
+			if (me.downHight < me.optDown.offset) {
+				if (me.movetype !== 1) {
+					me.movetype = 1; // 加入标记,保证只执行一次
+					me.optDown.inOffset && me.optDown.inOffset(me); // 进入指定距离范围内那一刻的回调,只执行一次
+					me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
+				}
+				me.downHight += diff * me.optDown.inOffsetRate; // 越往下,高度变化越小
+
+				// 指定距离  <= 下拉距离
+			} else {
+				if (me.movetype !== 2) {
+					me.movetype = 2; // 加入标记,保证只执行一次
+					me.optDown.outOffset && me.optDown.outOffset(me); // 下拉超过指定距离那一刻的回调,只执行一次
+					me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
+				}
+				if (diff > 0) { // 向下拉
+					me.downHight += diff * me.optDown.outOffsetRate; // 越往下,高度变化越小
+				} else { // 向上收
+					me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
+				}
+			}
+			
+			me.downHight = Math.round(me.downHight) // 取整
+			let rate = me.downHight / me.optDown.offset; // 下拉区域当前高度与指定距离的比值
+			me.optDown.onMoving && me.optDown.onMoving(me, rate, me.downHight); // 下拉过程中的回调,一直在执行
+		}
+	}
+
+	me.lastPoint = curPoint; // 记录本次移动的点
+}
+
+/* 列表touchend事件 */
+MeScroll.prototype.touchendEvent = function(e) {
+	if (!this.optDown.use) return;
+	// 如果下拉区域高度已改变,则需重置回来
+	if (this.isMoveDown) {
+		if (this.downHight >= this.optDown.offset) {
+			// 符合触发刷新的条件
+			this.triggerDownScroll();
+		} else {
+			// 不符合的话 则重置
+			this.downHight = 0;
+			this.endDownScrollCall(this);
+		}
+		this.movetype = 0;
+		this.isMoveDown = false;
+	} else if (!this.isScrollBody && this.getScrollTop() === this.startTop) { // scroll-view到顶/左/右/底的滑动事件
+		let isScrollUp = this.getPoint(e).y - this.startPoint.y < 0; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+		// 上滑
+		if (isScrollUp) {
+			// 需检查滑动的角度
+			let angle = this.getAngle(this.getPoint(e), this.startPoint); // 两点之间的角度,区间 [0,90]
+			if (angle > 80) {
+				// 检查并触发上拉
+				this.triggerUpScroll(true);
+			}
+		}
+	}
+}
+
+/* 根据点击滑动事件获取第一个手指的坐标 */
+MeScroll.prototype.getPoint = function(e) {
+	if (!e) {
+		return {
+			x: 0,
+			y: 0
+		}
+	}
+	if (e.touches && e.touches[0]) {
+		return {
+			x: e.touches[0].pageX,
+			y: e.touches[0].pageY
+		}
+	} else if (e.changedTouches && e.changedTouches[0]) {
+		return {
+			x: e.changedTouches[0].pageX,
+			y: e.changedTouches[0].pageY
+		}
+	} else {
+		return {
+			x: e.clientX,
+			y: e.clientY
+		}
+	}
+}
+
+/* 计算两点之间的角度: 区间 [0,90]*/
+MeScroll.prototype.getAngle = function(p1, p2) {
+	let x = Math.abs(p1.x - p2.x);
+	let y = Math.abs(p1.y - p2.y);
+	let z = Math.sqrt(x * x + y * y);
+	let angle = 0;
+	if (z !== 0) {
+		angle = Math.asin(y / z) / Math.PI * 180;
+	}
+	return angle
+}
+
+/* 触发下拉刷新 */
+MeScroll.prototype.triggerDownScroll = function() {
+	if (this.optDown.beforeLoading && this.optDown.beforeLoading(this)) {
+		//return true则处于完全自定义状态
+	} else {
+		this.showDownScroll(); // 下拉刷新中...
+		!this.optDown.native && this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据
+	}
+}
+
+/* 显示下拉进度布局 */
+MeScroll.prototype.showDownScroll = function() {
+	this.isDownScrolling = true; // 标记下拉中
+	if (this.optDown.native) {
+		uni.startPullDownRefresh(); // 系统自带的下拉刷新
+		this.showDownLoadingCall(0); // 仍触发showLoading,因为上拉加载用到
+	} else{
+		this.downHight = this.optDown.offset; // 更新下拉区域高度
+		this.showDownLoadingCall(this.downHight); // 下拉刷新中...
+	}
+}
+
+MeScroll.prototype.showDownLoadingCall = function(downHight) {
+	this.optDown.showLoading && this.optDown.showLoading(this, downHight); // 下拉刷新中...
+	this.optDown.afterLoading && this.optDown.afterLoading(this, downHight); // 下拉刷新中...触发之后马上要执行的代码
+}
+
+/* 显示系统自带的下拉刷新时需要处理的业务 */
+MeScroll.prototype.onPullDownRefresh = function() {
+	this.isDownScrolling = true; // 标记下拉中
+	this.showDownLoadingCall(0); // 仍触发showLoading,因为上拉加载用到
+	this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据
+}
+
+/* 结束下拉刷新 */
+MeScroll.prototype.endDownScroll = function() {
+	if (this.optDown.native) { // 结束原生下拉刷新
+		this.isDownScrolling = false;
+		this.endDownScrollCall(this);
+		uni.stopPullDownRefresh();
+		return
+	}
+	let me = this;
+	// 结束下拉刷新的方法
+	let endScroll = function() {
+		me.downHight = 0;
+		me.isDownScrolling = false;
+		me.endDownScrollCall(me);
+		if(!me.isScrollBody){
+			me.setScrollHeight(0) // scroll-view重置滚动区域,使数据不满屏时仍可检查触发翻页
+			me.scrollTo(0,0) // scroll-view需重置滚动条到顶部,避免startTop大于0时,对下拉刷新的影响
+		}
+	}
+	// 结束下拉刷新时的回调
+	let delay = 0;
+	if (me.optDown.beforeEndDownScroll) delay = me.optDown.beforeEndDownScroll(me); // 结束下拉刷新的延时,单位ms
+	if (typeof delay === 'number' && delay > 0) {
+		setTimeout(endScroll, delay);
+	} else {
+		endScroll();
+	}
+}
+
+MeScroll.prototype.endDownScrollCall = function() {
+	this.optDown.endDownScroll && this.optDown.endDownScroll(this);
+	this.optDown.afterEndDownScroll && this.optDown.afterEndDownScroll(this);
+}
+
+/* 锁定下拉刷新:isLock=ture,null锁定;isLock=false解锁 */
+MeScroll.prototype.lockDownScroll = function(isLock) {
+	if (isLock == null) isLock = true;
+	this.optDown.isLock = isLock;
+}
+
+/* 锁定上拉加载:isLock=ture,null锁定;isLock=false解锁 */
+MeScroll.prototype.lockUpScroll = function(isLock) {
+	if (isLock == null) isLock = true;
+	this.optUp.isLock = isLock;
+}
+
+/* -------初始化上拉加载------- */
+MeScroll.prototype.initUpScroll = function() {
+	let me = this;
+	// 配置参数
+	me.optUp = me.options.up || {use: false}
+	if(!me.optUp.textColor && me.hasColor(me.optUp.bgColor)) me.optUp.textColor = "#fff"; // 当bgColor有值且textColor未设置,则textColor默认白色
+	me.extendUpScroll(me.optUp);
+
+	if (me.optUp.use === false) return; // 配置不使用上拉加载时,则不初始化上拉布局
+	me.optUp.hasNext = true; // 如果使用上拉,则默认有下一页
+	me.startNum = me.optUp.page.num + 1; // 记录page开始的页码
+
+	// 初始化完毕的回调
+	if (me.optUp.inited) {
+		setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
+			me.optUp.inited(me);
+		}, 0)
+	}
+}
+
+/*滚动到底部的事件 (仅mescroll-body生效)*/
+MeScroll.prototype.onReachBottom = function() {
+	if (this.isScrollBody && !this.isUpScrolling) { // 只能支持下拉刷新的时候同时可以触发上拉加载,否则滚动到底部就需要上滑一点才能触发onReachBottom
+		if (!this.optUp.isLock && this.optUp.hasNext) {
+			this.triggerUpScroll();
+		}
+	}
+}
+
+/*列表滚动事件 (仅mescroll-body生效)*/
+MeScroll.prototype.onPageScroll = function(e) {
+	if (!this.isScrollBody) return;
+	
+	// 更新滚动条的位置 (主要用于判断下拉刷新时,滚动条是否在顶部)
+	this.setScrollTop(e.scrollTop);
+
+	// 顶部按钮的显示隐藏
+	if (e.scrollTop >= this.optUp.toTop.offset) {
+		this.showTopBtn();
+	} else {
+		this.hideTopBtn();
+	}
+}
+
+/*列表滚动事件*/
+MeScroll.prototype.scroll = function(e, onScroll) {
+	// 更新滚动条的位置
+	this.setScrollTop(e.scrollTop);
+	// 更新滚动内容高度
+	this.setScrollHeight(e.scrollHeight);
+
+	// 向上滑还是向下滑动
+	if (this.preScrollY == null) this.preScrollY = 0;
+	this.isScrollUp = e.scrollTop - this.preScrollY > 0;
+	this.preScrollY = e.scrollTop;
+
+	// 上滑 && 检查并触发上拉
+	this.isScrollUp && this.triggerUpScroll(true);
+
+	// 顶部按钮的显示隐藏
+	if (e.scrollTop >= this.optUp.toTop.offset) {
+		this.showTopBtn();
+	} else {
+		this.hideTopBtn();
+	}
+
+	// 滑动监听
+	this.optUp.onScroll && onScroll && onScroll()
+}
+
+/* 触发上拉加载 */
+MeScroll.prototype.triggerUpScroll = function(isCheck) {
+	if (!this.isUpScrolling && this.optUp.use && this.optUp.callback) {
+		// 是否校验在底部; 默认不校验
+		if (isCheck === true) {
+			let canUp = false;
+			// 还有下一页 && 没有锁定 && 不在下拉中
+			if (this.optUp.hasNext && !this.optUp.isLock && !this.isDownScrolling) {
+				if (this.getScrollBottom() <= this.optUp.offset) { // 到底部
+					canUp = true; // 标记可上拉
+				}
+			}
+			if (canUp === false) return;
+		}
+		this.showUpScroll(); // 上拉加载中...
+		this.optUp.page.num++; // 预先加一页,如果失败则减回
+		this.isUpAutoLoad = true; // 标记上拉已经自动执行过,避免初始化时多次触发上拉回调
+		this.num = this.optUp.page.num; // 把最新的页数赋值在mescroll上,避免对page的影响
+		this.size = this.optUp.page.size; // 把最新的页码赋值在mescroll上,避免对page的影响
+		this.time = this.optUp.page.time; // 把最新的页码赋值在mescroll上,避免对page的影响
+		this.optUp.callback(this); // 执行回调,联网加载数据
+	}
+}
+
+/* 显示上拉加载中 */
+MeScroll.prototype.showUpScroll = function() {
+	this.isUpScrolling = true; // 标记上拉加载中
+	this.optUp.showLoading && this.optUp.showLoading(this); // 回调
+}
+
+/* 显示上拉无更多数据 */
+MeScroll.prototype.showNoMore = function() {
+	this.optUp.hasNext = false; // 标记无更多数据
+	this.optUp.showNoMore && this.optUp.showNoMore(this); // 回调
+}
+
+/* 隐藏上拉区域**/
+MeScroll.prototype.hideUpScroll = function() {
+	this.optUp.hideUpScroll && this.optUp.hideUpScroll(this); // 回调
+}
+
+/* 结束上拉加载 */
+MeScroll.prototype.endUpScroll = function(isShowNoMore) {
+	if (isShowNoMore != null) { // isShowNoMore=null,不处理下拉状态,下拉刷新的时候调用
+		if (isShowNoMore) {
+			this.showNoMore(); // isShowNoMore=true,显示无更多数据
+		} else {
+			this.hideUpScroll(); // isShowNoMore=false,隐藏上拉加载
+		}
+	}
+	this.isUpScrolling = false; // 标记结束上拉加载
+}
+
+/* 重置上拉加载列表为第一页
+ *isShowLoading 是否显示进度布局;
+ * 1.默认null,不传参,则显示上拉加载的进度布局
+ * 2.传参true, 则显示下拉刷新的进度布局
+ * 3.传参false,则不显示上拉和下拉的进度 (常用于静默更新列表数据)
+ */
+MeScroll.prototype.resetUpScroll = function(isShowLoading) {
+	if (this.optUp && this.optUp.use) {
+		let page = this.optUp.page;
+		this.prePageNum = page.num; // 缓存重置前的页码,加载失败可退回
+		this.prePageTime = page.time; // 缓存重置前的时间,加载失败可退回
+		page.num = this.startNum; // 重置为第一页
+		page.time = null; // 重置时间为空
+		if (!this.isDownScrolling && isShowLoading !== false) { // 如果不是下拉刷新触发的resetUpScroll并且不配置列表静默更新,则显示进度;
+			if (isShowLoading == null) {
+				this.removeEmpty(); // 移除空布局
+				this.showUpScroll(); // 不传参,默认显示上拉加载的进度布局
+			} else {
+				this.showDownScroll(); // 传true,显示下拉刷新的进度布局,不清空列表
+			}
+		}
+		this.isUpAutoLoad = true; // 标记上拉已经自动执行过,避免初始化时多次触发上拉回调
+		this.num = page.num; // 把最新的页数赋值在mescroll上,避免对page的影响
+		this.size = page.size; // 把最新的页码赋值在mescroll上,避免对page的影响
+		this.time = page.time; // 把最新的页码赋值在mescroll上,避免对page的影响
+		this.optUp.callback && this.optUp.callback(this); // 执行上拉回调
+	}
+}
+
+/* 设置page.num的值 */
+MeScroll.prototype.setPageNum = function(num) {
+	this.optUp.page.num = num - 1;
+}
+
+/* 设置page.size的值 */
+MeScroll.prototype.setPageSize = function(size) {
+	this.optUp.page.size = size;
+}
+
+/* 联网回调成功,结束下拉刷新和上拉加载
+ * dataSize: 当前页的数据量(必传)
+ * totalPage: 总页数(必传)
+ * systime: 服务器时间 (可空)
+ */
+MeScroll.prototype.endByPage = function(dataSize, totalPage, systime) {
+	let hasNext;
+	if (this.optUp.use && totalPage != null) hasNext = this.optUp.page.num < totalPage; // 是否还有下一页
+	this.endSuccess(dataSize, hasNext, systime);
+}
+
+/* 联网回调成功,结束下拉刷新和上拉加载
+ * dataSize: 当前页的数据量(必传)
+ * totalSize: 列表所有数据总数量(必传)
+ * systime: 服务器时间 (可空)
+ */
+MeScroll.prototype.endBySize = function(dataSize, totalSize, systime) {
+	let hasNext;
+	if (this.optUp.use && totalSize != null) {
+		let loadSize = (this.optUp.page.num - 1) * this.optUp.page.size + dataSize; // 已加载的数据总数
+		hasNext = loadSize < totalSize; // 是否还有下一页
+	}
+	this.endSuccess(dataSize, hasNext, systime);
+}
+
+/* 联网回调成功,结束下拉刷新和上拉加载
+ * dataSize: 当前页的数据个数(不是所有页的数据总和),用于上拉加载判断是否还有下一页.如果不传,则会判断还有下一页
+ * hasNext: 是否还有下一页,布尔类型;用来解决这个小问题:比如列表共有20条数据,每页加载10条,共2页.如果只根据dataSize判断,则需翻到第三页才会知道无更多数据,如果传了hasNext,则翻到第二页即可显示无更多数据.
+ * systime: 服务器时间(可空);用来解决这个小问题:当准备翻下一页时,数据库新增了几条记录,此时翻下一页,前面的几条数据会和上一页的重复;这里传入了systime,那么upCallback的page.time就会有值,把page.time传给服务器,让后台过滤新加入的那几条记录
+ */
+MeScroll.prototype.endSuccess = function(dataSize, hasNext, systime) {
+	let me = this;
+	// 结束下拉刷新
+	if (me.isDownScrolling) me.endDownScroll();
+
+	// 结束上拉加载
+	if (me.optUp.use) {
+		let isShowNoMore; // 是否已无更多数据
+		if (dataSize != null) {
+			let pageNum = me.optUp.page.num; // 当前页码
+			let pageSize = me.optUp.page.size; // 每页长度
+			// 如果是第一页
+			if (pageNum === 1) {
+				if (systime) me.optUp.page.time = systime; // 设置加载列表数据第一页的时间
+			}
+			if (dataSize < pageSize || hasNext === false) {
+				// 返回的数据不满一页时,则说明已无更多数据
+				me.optUp.hasNext = false;
+				if (dataSize === 0 && pageNum === 1) {
+					// 如果第一页无任何数据且配置了空布局
+					isShowNoMore = false;
+					me.showEmpty();
+				} else {
+					// 总列表数少于配置的数量,则不显示无更多数据
+					let allDataSize = (pageNum - 1) * pageSize + dataSize;
+					if (allDataSize < me.optUp.noMoreSize) {
+						isShowNoMore = false;
+					} else {
+						isShowNoMore = true;
+					}
+					me.removeEmpty(); // 移除空布局
+				}
+			} else {
+				// 还有下一页
+				isShowNoMore = false;
+				me.optUp.hasNext = true;
+				me.removeEmpty(); // 移除空布局
+			}
+		}
+
+		// 隐藏上拉
+		me.endUpScroll(isShowNoMore);
+	}
+}
+
+/* 回调失败,结束下拉刷新和上拉加载 */
+MeScroll.prototype.endErr = function(errDistance) {
+	// 结束下拉,回调失败重置回原来的页码和时间
+	if (this.isDownScrolling) {
+		let page = this.optUp.page;
+		if (page && this.prePageNum) {
+			page.num = this.prePageNum;
+			page.time = this.prePageTime;
+		}
+		this.endDownScroll();
+	}
+	// 结束上拉,回调失败重置回原来的页码
+	if (this.isUpScrolling) {
+		this.optUp.page.num--;
+		this.endUpScroll(false);
+		// 如果是mescroll-body,则需往回滚一定距离
+		if(this.isScrollBody && errDistance !== 0){ // 不处理0
+			if(!errDistance) errDistance = this.optUp.errDistance; // 不传,则取默认
+			this.scrollTo(this.getScrollTop() - errDistance, 0) // 往上回滚的距离
+		}
+	}
+}
+
+/* 显示空布局 */
+MeScroll.prototype.showEmpty = function() {
+	this.optUp.empty.use && this.optUp.empty.onShow && this.optUp.empty.onShow(true)
+}
+
+/* 移除空布局 */
+MeScroll.prototype.removeEmpty = function() {
+	this.optUp.empty.use && this.optUp.empty.onShow && this.optUp.empty.onShow(false)
+}
+
+/* 显示回到顶部的按钮 */
+MeScroll.prototype.showTopBtn = function() {
+	if (!this.topBtnShow) {
+		this.topBtnShow = true;
+		this.optUp.toTop.onShow && this.optUp.toTop.onShow(true);
+	}
+}
+
+/* 隐藏回到顶部的按钮 */
+MeScroll.prototype.hideTopBtn = function() {
+	if (this.topBtnShow) {
+		this.topBtnShow = false;
+		this.optUp.toTop.onShow && this.optUp.toTop.onShow(false);
+	}
+}
+
+/* 获取滚动条的位置 */
+MeScroll.prototype.getScrollTop = function() {
+	return this.scrollTop || 0
+}
+
+/* 记录滚动条的位置 */
+MeScroll.prototype.setScrollTop = function(y) {
+	this.scrollTop = y;
+}
+
+/* 滚动到指定位置 */
+MeScroll.prototype.scrollTo = function(y, t) {
+	this.myScrollTo && this.myScrollTo(y, t) // scrollview需自定义回到顶部方法
+}
+
+/* 自定义scrollTo */
+MeScroll.prototype.resetScrollTo = function(myScrollTo) {
+	this.myScrollTo = myScrollTo
+}
+
+/* 滚动条到底部的距离 */
+MeScroll.prototype.getScrollBottom = function() {
+	return this.getScrollHeight() - this.getClientHeight() - this.getScrollTop()
+}
+
+/* 计步器
+ star: 开始值
+ end: 结束值
+ callback(step,timer): 回调step值,计步器timer,可自行通过window.clearInterval(timer)结束计步器;
+ t: 计步时长,传0则直接回调end值;不传则默认300ms
+ rate: 周期;不传则默认30ms计步一次
+ * */
+MeScroll.prototype.getStep = function(star, end, callback, t, rate) {
+	let diff = end - star; // 差值
+	if (t === 0 || diff === 0) {
+		callback && callback(end);
+		return;
+	}
+	t = t || 300; // 时长 300ms
+	rate = rate || 30; // 周期 30ms
+	let count = t / rate; // 次数
+	let step = diff / count; // 步长
+	let i = 0; // 计数
+	let timer = setInterval(function() {
+		if (i < count - 1) {
+			star += step;
+			callback && callback(star, timer);
+			i++;
+		} else {
+			callback && callback(end, timer); // 最后一次直接设置end,避免计算误差
+			clearInterval(timer);
+		}
+	}, rate);
+}
+
+/* 滚动容器的高度 */
+MeScroll.prototype.getClientHeight = function(isReal) {
+	let h = this.clientHeight || 0
+	if (h === 0 && isReal !== true) { // 未获取到容器的高度,可临时取body的高度 (可能会有误差)
+		h = this.getBodyHeight()
+	}
+	return h
+}
+MeScroll.prototype.setClientHeight = function(h) {
+	this.clientHeight = h;
+}
+
+/* 滚动内容的高度 */
+MeScroll.prototype.getScrollHeight = function() {
+	return this.scrollHeight || 0;
+}
+MeScroll.prototype.setScrollHeight = function(h) {
+	this.scrollHeight = h;
+}
+
+/* body的高度 */
+MeScroll.prototype.getBodyHeight = function() {
+	return this.bodyHeight || 0;
+}
+MeScroll.prototype.setBodyHeight = function(h) {
+	this.bodyHeight = h;
+}
+
+/* 阻止浏览器默认滚动事件 */
+MeScroll.prototype.preventDefault = function(e) {
+	// 小程序不支持e.preventDefault, 已在wxs中禁止
+	// app的bounce只能通过配置pages.json的style.app-plus.bounce为"none"来禁止, 或使用renderjs禁止
+	// cancelable:是否可以被禁用; defaultPrevented:是否已经被禁用
+	if (e && e.cancelable && !e.defaultPrevented) e.preventDefault()
+}

+ 420 - 0
components/mescroll-uni/mescroll-uni.vue

@@ -0,0 +1,420 @@
+<template>
+	<view class="mescroll-uni-warp">
+		<scroll-view :id="viewId" class="mescroll-uni" :class="{'mescroll-uni-fixed':isFixed}" :style="{'height':scrollHeight,'padding-top':padTop,'padding-bottom':padBottom,'top':fixedTop,'bottom':fixedBottom}" :scroll-top="scrollTop" :scroll-with-animation="scrollAnim" @scroll="scroll" :scroll-y='scrollable' :enable-back-to-top="true">
+			<view class="mescroll-uni-content mescroll-render-touch"
+			@touchstart="wxsBiz.touchstartEvent" 
+			@touchmove="wxsBiz.touchmoveEvent" 
+			@touchend="wxsBiz.touchendEvent" 
+			@touchcancel="wxsBiz.touchendEvent"
+			:change:prop="wxsBiz.propObserver"
+			:prop="wxsProp">
+				<!-- 状态栏 -->
+				<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
+		
+				<view class="mescroll-wxs-content" :style="{'transform': translateY, 'transition': transition}" :change:prop="wxsBiz.callObserver" :prop="callProp">
+					<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
+					<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
+					<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
+						<view class="downwarp-content">
+							<view class="downwarp-progress mescroll-wxs-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mescroll.optDown.textColor, 'transform': downRotate}"></view>
+							<view class="downwarp-tip">{{downText}}</view>
+						</view>
+					</view>
+
+					<!-- 列表内容 -->
+					<slot></slot>
+
+					<!-- 空布局 -->
+					<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
+
+					<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
+					<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
+					<view v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
+						<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
+						<view v-show="upLoadType===1">
+							<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
+							<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
+						</view>
+						<!-- 无数据 -->
+						<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
+					</view>
+				</view>
+			
+				<!-- 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效) -->
+				<!-- #ifdef H5 -->
+				<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
+				<!-- #endif -->
+				
+				<!-- 适配iPhoneX -->
+				<view v-if="safearea" class="mescroll-safearea"></view>
+			</view>
+		</scroll-view>
+
+		<!-- 回到顶部按钮 (fixed元素,需写在scroll-view外面,防止滚动的时候抖动)-->
+		<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
+		
+		<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
+		<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
+		<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
+		<!-- #endif -->
+	</view>
+</template>
+
+<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
+<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
+<script src="./wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
+<!-- #endif -->
+
+<!-- app, h5使用renderjs -->
+<!-- #ifdef APP-PLUS || H5 -->
+<script module="renderBiz" lang="renderjs">
+	import renderBiz from './wxs/renderjs.js';
+	export default {
+		mixins:[renderBiz]
+	}
+</script>
+<!-- #endif -->
+
+<script>
+	// 引入mescroll-uni.js,处理核心逻辑
+	import MeScroll from './mescroll-uni.js';
+	// 引入全局配置
+	import GlobalOption from './mescroll-uni-option.js';
+	// 引入空布局组件
+	import MescrollEmpty from './components/mescroll-empty.vue';
+	// 引入回到顶部组件
+	import MescrollTop from './components/mescroll-top.vue';
+	// 引入兼容wxs(含renderjs)写法的mixins
+	import WxsMixin from './wxs/mixins.js';
+	
+	export default {
+		mixins: [WxsMixin],
+		components: {
+			MescrollEmpty,
+			MescrollTop
+		},
+		data() {
+			return {
+				mescroll: {optDown:{},optUp:{}}, // mescroll实例
+				viewId: 'id_' + Math.random().toString(36).substr(2,16), // 随机生成mescroll的id(不能数字开头,否则找不到元素)
+				downHight: 0, //下拉刷新: 容器高度
+				downRate: 0, // 下拉比率(inOffset: rate<1; outOffset: rate>=1)
+				downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
+				upLoadType: 0, // 上拉加载状态: 0(loading前), 1loading中, 2没有更多了,显示END文本提示, 3(没有更多了,不显示END文本提示)
+				isShowEmpty: false, // 是否显示空布局
+				isShowToTop: false, // 是否显示回到顶部按钮
+				scrollTop: 0, // 滚动条的位置
+				scrollAnim: false, // 是否开启滚动动画
+				windowTop: 0, // 可使用窗口的顶部位置
+				windowBottom: 0, // 可使用窗口的底部位置
+				windowHeight: 0, // 可使用窗口的高度
+				statusBarHeight: 0 // 状态栏高度
+			}
+		},
+		props: {
+			down: Object, // 下拉刷新的参数配置
+			up: Object, // 上拉加载的参数配置
+			top: [String, Number], // 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+			topbar: [Boolean, String], // top的偏移量是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
+			bottom: [String, Number], // 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+			safearea: Boolean, // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
+			fixed: { // 是否通过fixed固定mescroll的高度, 默认true
+				type: Boolean,
+				default: true
+			},
+			height: [String, Number], // 指定mescroll的高度, 此项有值,则不使用fixed. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+			bottombar:{ // 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效)
+				type: Boolean,
+				default: true
+			}
+		},
+		computed: {
+			// 是否使用fixed定位 (当height有值,则不使用)
+			isFixed(){
+				return !this.height && this.fixed
+			},
+			// mescroll的高度
+			scrollHeight(){
+				if (this.isFixed) {
+					return "auto"
+				} else if(this.height){
+					return this.toPx(this.height) + 'px'
+				}else{
+					return "100%"
+				}
+			},
+			// 下拉布局往下偏移的距离 (px)
+			numTop() {
+				return this.toPx(this.top)
+			},
+			fixedTop() {
+				return this.isFixed ? (this.numTop + this.windowTop) + 'px' : 0
+			},
+			padTop() {
+				return !this.isFixed ? this.numTop + 'px' : 0
+			},
+			// 上拉布局往上偏移 (px)
+			numBottom() {
+				return this.toPx(this.bottom)
+			},
+			fixedBottom() {
+				return this.isFixed ? (this.numBottom + this.windowBottom) + 'px' : 0
+			},
+			padBottom() {
+				return !this.isFixed ? this.numBottom + 'px' : 0
+			},
+			// 是否为重置下拉的状态
+			isDownReset(){
+				return this.downLoadType===3 || this.downLoadType===4
+			},
+			// 过渡
+			transition() {
+				return this.isDownReset ? 'transform 300ms' : '';
+			},
+			translateY() {
+				return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : ''; // transform会使fixed失效,需注意把fixed元素写在mescroll之外
+			},
+			// 列表是否可滑动
+			scrollable(){
+				return this.downLoadType===0 || this.isDownReset
+			},
+			// 是否在加载中
+			isDownLoading(){
+				return this.downLoadType === 3
+			},
+			// 旋转的角度
+			downRotate(){
+				return 'rotate(' + 360 * this.downRate + 'deg)'
+			},
+			// 文本提示
+			downText(){
+				if(!this.mescroll) return ""; // 避免头条小程序初始化时报错
+				switch (this.downLoadType){
+					case 1: return this.mescroll.optDown.textInOffset;
+					case 2: return this.mescroll.optDown.textOutOffset;
+					case 3: return this.mescroll.optDown.textLoading;
+					case 4: return this.mescroll.optDown.textLoading;
+					default: return this.mescroll.optDown.textInOffset;
+				}
+			}
+		},
+		methods: {
+			//number,rpx,upx,px,% --> px的数值
+			toPx(num){
+				if(typeof num === "string"){
+					if (num.indexOf('px') !== -1) {
+						if(num.indexOf('rpx') !== -1) { // "10rpx"
+							num = num.replace('rpx', '');
+						} else if(num.indexOf('upx') !== -1) { // "10upx"
+							num = num.replace('upx', '');
+						} else { // "10px"
+							return Number(num.replace('px', ''))
+						}
+					}else if (num.indexOf('%') !== -1){
+						// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
+						let rate = Number(num.replace("%","")) / 100
+						return this.windowHeight * rate
+					}
+				}
+				return num ? uni.upx2px(Number(num)) : 0
+			},
+			//注册列表滚动事件,用于下拉刷新和上拉加载
+			scroll(e) {
+				this.mescroll.scroll(e.detail, () => {
+					this.$emit('scroll', this.mescroll) // 此时可直接通过 this.mescroll.scrollTop获取滚动条位置; this.mescroll.isScrollUp获取是否向上滑动
+				})
+			},
+			// 点击空布局的按钮回调
+			emptyClick() {
+				this.$emit('emptyclick', this.mescroll)
+			},
+			// 点击回到顶部的按钮回调
+			toTopClick() {
+				this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
+				this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
+			},
+			// 更新滚动区域的高度 (使内容不满屏和到底,都可继续翻页)
+			setClientHeight() {
+				if (this.mescroll.getClientHeight(true) === 0 && !this.isExec) {
+					this.isExec = true; // 避免多次获取
+					this.$nextTick(() => { // 确保dom已渲染
+						this.getClientInfo(data=>{
+							this.isExec = false;
+							if (data) {
+								this.mescroll.setClientHeight(data.height);
+							} else if (this.clientNum != 3) { // 极少部分情况,可能dom还未渲染完毕,递归获取,最多重试3次
+								this.clientNum = this.clientNum == null ? 1 : this.clientNum + 1;
+								setTimeout(() => {
+									this.setClientHeight()
+								}, this.clientNum * 100)
+							}
+						})
+					})
+				}
+			},
+			// 获取滚动区域的信息
+			getClientInfo(success){
+				let query = uni.createSelectorQuery();
+				// #ifndef MP-ALIPAY || MP-DINGTALK
+				query = query.in(this) // 支付宝小程序不支持in(this),而字节跳动小程序必须写in(this), 否则都取不到值
+				// #endif
+				let view = query.select('#' + this.viewId);
+				view.boundingClientRect(data => {
+					success(data)
+				}).exec();
+			}
+		},
+		// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
+		created() {
+			let vm = this;
+
+			let diyOption = {
+				// 下拉刷新的配置
+				down: {
+					inOffset() {
+						vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
+					},
+					outOffset() {
+						vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
+					},
+					onMoving(mescroll, rate, downHight) {
+						// 下拉过程中的回调,滑动过程一直在执行;
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						vm.downRate = rate; //下拉比率 (inOffset: rate<1; outOffset: rate>=1)
+					},
+					showLoading(mescroll, downHight) {
+						vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+					},
+					endDownScroll() {
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						vm.downResetTimer && clearTimeout(vm.downResetTimer)
+						vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,以便置空this.transition,避免iOS小程序列表渲染不完整
+							if(vm.downLoadType===4) vm.downLoadType = 0
+						},300)
+					},
+					// 派发下拉刷新的回调
+					callback: function(mescroll) {
+						vm.$emit('down', mescroll)
+					}
+				},
+				// 上拉加载的配置
+				up: {
+					// 显示加载中的回调
+					showLoading() {
+						vm.upLoadType = 1;
+					},
+					// 显示无更多数据的回调
+					showNoMore() {
+						vm.upLoadType = 2;
+					},
+					// 隐藏上拉加载的回调
+					hideUpScroll(mescroll) {
+						vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
+					},
+					// 空布局
+					empty: {
+						onShow(isShow) { // 显示隐藏的回调
+							vm.isShowEmpty = isShow;
+						}
+					},
+					// 回到顶部
+					toTop: {
+						onShow(isShow) { // 显示隐藏的回调
+							vm.isShowToTop = isShow;
+						}
+					},
+					// 派发上拉加载的回调
+					callback: function(mescroll) {
+						vm.$emit('up', mescroll);
+						// 更新容器的高度 (多mescroll的情况)
+						vm.setClientHeight()
+					}
+				}
+			}
+
+			MeScroll.extend(diyOption, GlobalOption); // 混入全局的配置
+			let myOption = JSON.parse(JSON.stringify({'down': vm.down,'up': vm.up})) // 深拷贝,避免对props的影响
+			MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
+
+			// 初始化MeScroll对象
+			vm.mescroll = new MeScroll(myOption);
+			vm.mescroll.viewId = vm.viewId; // 附带id
+			// init回调mescroll对象
+			vm.$emit('init', vm.mescroll);
+			
+			// 设置高度
+			const sys = uni.getSystemInfoSync();
+			if(sys.windowTop) vm.windowTop = sys.windowTop;
+			if(sys.windowBottom) vm.windowBottom = sys.windowBottom;
+			if(sys.windowHeight) vm.windowHeight = sys.windowHeight;
+			if(sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
+			// 使down的bottomOffset生效
+			vm.mescroll.setBodyHeight(sys.windowHeight);
+
+			// 因为使用的是scrollview,这里需自定义scrollTo
+			vm.mescroll.resetScrollTo((y, t) => {
+				vm.scrollAnim = (t !== 0); // t为0,则不使用动画过渡
+				if(typeof y === 'string'){
+					// 小程序不支持slot里面的scroll-into-view, 统一使用计算的方式实现
+					vm.getClientInfo(function(rect){
+						let mescrollTop = rect.top // mescroll到顶部的距离
+						let selector;
+						if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
+							selector = '#'+y // 不带#和. 则默认为id选择器
+						}else{
+							selector = y
+							// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
+							if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
+								selector = y.split('>>>')[1].trim()
+							}
+							// #endif
+						}
+						uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
+							if (rect) {
+								let curY = vm.mescroll.getScrollTop()
+								let top = rect.top - mescrollTop
+								top += curY
+								if(!vm.isFixed) top -= vm.numTop
+								vm.scrollTop = curY;
+								vm.$nextTick(function() {
+									vm.scrollTop = top
+								})
+							} else{
+								console.error(selector + ' does not exist');
+							}
+						}).exec()
+					})
+					return;
+				}
+				let curY = vm.mescroll.getScrollTop()
+				if (t === 0 || t === 300) { // 当t使用默认配置的300时,则使用系统自带的动画过渡
+					vm.scrollTop = curY;
+					vm.$nextTick(function() {
+						vm.scrollTop = y
+					})
+				} else {
+					vm.mescroll.getStep(curY, y, step => { // 此写法可支持配置t
+						vm.scrollTop = step
+					}, t)
+				}
+			})
+			
+			// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
+			if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
+				vm.mescroll.optUp.toTop.safearea = vm.safearea;
+			}
+		},
+		mounted() {
+			// 设置容器的高度
+			this.setClientHeight()
+		}
+	}
+</script>
+
+<style>
+	@import "./mescroll-uni.css";
+	@import "./components/mescroll-down.css";
+	@import './components/mescroll-up.css';
+</style>

+ 50 - 0
components/mescroll-uni/mixins/mescroll-comp.js

@@ -0,0 +1,50 @@
+/**
+ * mescroll-body写在子组件时,需通过mescroll的mixins补充子组件缺少的生命周期:
+ * 当一个页面只有一个mescroll-body写在子组件时, 则使用mescroll-comp.js (参考 mescroll-comp 案例)
+ * 当一个页面有多个mescroll-body写在子组件时, 则使用mescroll-more.js (参考 mescroll-more 案例)
+ */
+const MescrollCompMixin = {
+	// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件 (一级)
+	onPageScroll(e) {
+		this.handlePageScroll(e)
+	},
+	onReachBottom() {
+		this.handleReachBottom()
+	},
+	// 当down的native: true时, 还需传递此方法进到子组件
+	onPullDownRefresh(){
+		this.handlePullDownRefresh()
+	},
+	// mescroll-body写在子子子...组件的情况 (多级)
+	data() {
+		return {
+			mescroll: {
+				onPageScroll: e=>{
+					this.handlePageScroll(e)
+				},
+				onReachBottom: ()=>{
+					this.handleReachBottom()
+				},
+				onPullDownRefresh: ()=>{
+					this.handlePullDownRefresh()
+				}
+			}
+		}
+	},
+	methods:{
+		handlePageScroll(e){
+			let item = this.$refs["mescrollItem"];
+			if(item && item.mescroll) item.mescroll.onPageScroll(e);
+		},
+		handleReachBottom(){
+			let item = this.$refs["mescrollItem"];
+			if(item && item.mescroll) item.mescroll.onReachBottom();
+		},
+		handlePullDownRefresh(){
+			let item = this.$refs["mescrollItem"];
+			if(item && item.mescroll) item.mescroll.onPullDownRefresh();
+		}
+	}
+}
+
+export default MescrollCompMixin;

+ 51 - 0
components/mescroll-uni/mixins/mescroll-more-item.js

@@ -0,0 +1,51 @@
+/**
+ * mescroll-more-item的mixins, 仅在多个 mescroll-body 写在子组件时使用 (参考 mescroll-more 案例)
+ */
+const MescrollMoreItemMixin = {
+	// 支付宝小程序不支持props的mixin,需写在具体的页面中
+	// #ifndef MP-ALIPAY || MP-DINGTALK
+	props:{
+		i: Number, // 每个tab页的专属下标
+		index: { // 当前tab的下标
+			type: Number,
+			default(){
+				return 0
+			}
+		}
+	},
+	// #endif
+	data() {
+		return {
+			downOption:{
+				auto:false // 不自动加载
+			},
+			upOption:{
+				auto:false // 不自动加载
+			},
+			isInit: false // 当前tab是否已初始化
+		}
+	},
+	watch:{
+		// 监听下标的变化
+		index(val){
+			if (this.i === val && !this.isInit) {
+				this.isInit = true; // 标记为true
+				this.mescroll && this.mescroll.triggerDownScroll();
+			}
+		}
+	},
+	methods: {
+		// mescroll组件初始化的回调,可获取到mescroll对象 (覆盖mescroll-mixins.js的mescrollInit, 为了标记isInit)
+		mescrollInit(mescroll) {
+			this.mescroll = mescroll;
+			this.mescrollInitByRef && this.mescrollInitByRef(); // 兼容字节跳动小程序
+			// 自动加载当前tab的数据
+			if(this.i === this.index){
+				this.isInit = true; // 标记为true
+				this.mescroll.triggerDownScroll();
+			}
+		},
+	}
+}
+
+export default MescrollMoreItemMixin;

+ 56 - 0
components/mescroll-uni/mixins/mescroll-more.js

@@ -0,0 +1,56 @@
+/**
+ * mescroll-body写在子组件时,需通过mescroll的mixins补充子组件缺少的生命周期:
+ * 当一个页面只有一个mescroll-body写在子组件时, 则使用mescroll-comp.js (参考 mescroll-comp 案例)
+ * 当一个页面有多个mescroll-body写在子组件时, 则使用mescroll-more.js (参考 mescroll-more 案例)
+ */
+const MescrollMoreMixin = {
+	data() {
+		return {
+			tabIndex: 0 // 当前tab下标
+		}
+	},
+	// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件
+	onPageScroll(e) {
+		let mescroll = this.getMescroll(this.tabIndex);
+		mescroll && mescroll.onPageScroll(e);
+	},
+	onReachBottom() {
+		let mescroll = this.getMescroll(this.tabIndex);
+		mescroll && mescroll.onReachBottom();
+	},
+	// 当down的native: true时, 还需传递此方法进到子组件
+	onPullDownRefresh(){
+		let mescroll = this.getMescroll(this.tabIndex);
+		mescroll && mescroll.onPullDownRefresh();
+	},
+	methods:{
+		// 根据下标获取对应子组件的mescroll
+		getMescroll(i){
+			if(!this.mescrollItems) this.mescrollItems = [];
+			if(!this.mescrollItems[i]) {
+				// v-for中的refs
+				let vForItem = this.$refs["mescrollItem"];
+				if(vForItem){
+					this.mescrollItems[i] = vForItem[i]
+				}else{
+					// 普通的refs,不可重复
+					this.mescrollItems[i] = this.$refs["mescrollItem"+i];
+				}
+			}
+			let item = this.mescrollItems[i]
+			return item ? item.mescroll : null
+		},
+		// 切换tab,恢复滚动条位置
+		tabChange(i){
+			let mescroll = this.getMescroll(i);
+			if(mescroll){
+				// 延时(比$nextTick靠谱一些),确保元素已渲染
+				setTimeout(()=>{
+					mescroll.scrollTo(mescroll.getScrollTop(),0)
+				},30)
+			}
+		}
+	}
+}
+
+export default MescrollMoreMixin;

+ 102 - 0
components/mescroll-uni/wxs/mixins.js

@@ -0,0 +1,102 @@
+// 定义在wxs (含renderjs) 逻辑层的数据和方法, 与视图层相互通信
+const WxsMixin = {
+	data() {
+		return {
+			// 传入wxs视图层的数据 (响应式)
+			wxsProp: {
+				optDown:{}, // 下拉刷新的配置
+				scrollTop:0, // 滚动条的距离
+				bodyHeight:0, // body的高度
+				isDownScrolling:false, // 是否正在下拉刷新中
+				isUpScrolling:false, // 是否正在上拉加载中
+				isScrollBody:true, // 是否为mescroll-body滚动
+				isUpBoth:true, // 上拉加载时,是否同时可以下拉刷新
+				t: 0 // 数据更新的标记 (只有数据更新了,才会触发wxs的Observer)
+			},
+			
+			// 标记调用wxs视图层的方法
+			callProp: {
+				callType: '', // 方法名
+				t: 0 // 数据更新的标记 (只有数据更新了,才会触发wxs的Observer)
+			},
+			
+			// 不用wxs的平台使用此处的wxsBiz对象,抹平wxs的写法 (微信小程序和APP使用的wxsBiz对象是./wxs/wxs.wxs)
+			// #ifndef MP-WEIXIN || MP-QQ || APP-PLUS || H5
+			wxsBiz: {
+				//注册列表touchstart事件,用于下拉刷新
+				touchstartEvent: e=> {
+					this.mescroll.touchstartEvent(e);
+				},
+				//注册列表touchmove事件,用于下拉刷新
+				touchmoveEvent: e=> {
+					this.mescroll.touchmoveEvent(e);
+				},
+				//注册列表touchend事件,用于下拉刷新
+				touchendEvent: e=> {
+					this.mescroll.touchendEvent(e);
+				},
+				propObserver(){}, // 抹平wxs的写法
+				callObserver(){} // 抹平wxs的写法
+			},
+			// #endif
+			
+			// 不用renderjs的平台使用此处的renderBiz对象,抹平renderjs的写法 (app 和 h5 使用的renderBiz对象是./wxs/renderjs.js)
+			// #ifndef APP-PLUS || H5
+			renderBiz: {
+				propObserver(){} // 抹平renderjs的写法
+			}
+			// #endif
+		}
+	},
+	methods: {
+		// wxs视图层调用逻辑层的回调
+		wxsCall(msg){
+			if(msg.type === 'setWxsProp'){
+				// 更新wxsProp数据 (值改变才触发更新)
+				this.wxsProp = {
+					optDown: this.mescroll.optDown,
+					scrollTop: this.mescroll.getScrollTop(),
+					bodyHeight: this.mescroll.getBodyHeight(),
+					isDownScrolling: this.mescroll.isDownScrolling,
+					isUpScrolling: this.mescroll.isUpScrolling,
+					isUpBoth: this.mescroll.optUp.isBoth,
+					isScrollBody:this.mescroll.isScrollBody,
+					t: Date.now()
+				}
+			}else if(msg.type === 'setLoadType'){
+				// 设置inOffset,outOffset的状态
+				this.downLoadType = msg.downLoadType
+			}else if(msg.type === 'triggerDownScroll'){
+				// 主动触发下拉刷新
+				this.mescroll.triggerDownScroll();
+			}else if(msg.type === 'endDownScroll'){
+				// 结束下拉刷新
+				this.mescroll.endDownScroll();
+			}else if(msg.type === 'triggerUpScroll'){
+				// 主动触发上拉加载
+				this.mescroll.triggerUpScroll(true);
+			}
+		}
+	},
+	mounted() {
+		// #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5
+		// 配置主动触发wxs显示加载进度的回调
+		this.mescroll.optDown.afterLoading = ()=>{
+			this.callProp = {callType: "showLoading", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
+		}
+		// 配置主动触发wxs隐藏加载进度的回调
+		this.mescroll.optDown.afterEndDownScroll = ()=>{
+			this.callProp = {callType: "endDownScroll", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
+			setTimeout(()=>{
+				if(this.downLoadType === 4 || this.downLoadType === 0){
+					this.callProp = {callType: "clearTransform", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
+				}
+			},320)
+		}
+		// 初始化wxs的数据
+		this.wxsCall({type: 'setWxsProp'})
+		// #endif
+	}
+}
+
+export default WxsMixin;

+ 92 - 0
components/mescroll-uni/wxs/renderjs.js

@@ -0,0 +1,92 @@
+// 使用renderjs直接操作window对象,实现动态控制app和h5的bounce
+// bounce: iOS橡皮筋,Android半月弧,h5浏览器下拉背景等效果 (下拉刷新时禁止)
+// https://uniapp.dcloud.io/frame?id=renderjs
+
+// 与wxs的me实例一致
+var me = {}
+
+// 初始化window对象的touch事件 (仅初始化一次)
+if(window && !window.$mescrollRenderInit){
+	window.$mescrollRenderInit = true
+	
+	
+	window.addEventListener('touchstart', function(e){
+		if (me.disabled()) return;
+		me.startPoint = me.getPoint(e); // 记录起点
+	}, {passive: true})
+	
+	
+	window.addEventListener('touchmove', function(e){
+		if (me.disabled()) return;
+		if (me.getScrollTop() > 0) return; // 需在顶部下拉,才禁止bounce
+		
+		var curPoint = me.getPoint(e); // 当前点
+		var moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+		// 向下拉
+		if (moveY > 0) {
+			// 可下拉的条件
+			if (!me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling && me.isUpBoth))) {
+				
+				// 只有touch在mescroll的view上面,才禁止bounce
+				var el = e.target;
+				var isMescrollTouch = false;
+				while (el && el.tagName && el.tagName !== 'UNI-PAGE-BODY' && el.tagName != "BODY") {
+					var cls = el.classList;
+					if (cls && cls.contains('mescroll-render-touch')) {
+						isMescrollTouch = true
+						break;
+					}
+					el = el.parentNode; // 继续检查其父元素
+				}
+				// 禁止bounce (不会对swiper和iOS侧滑返回造成影响)
+				if (isMescrollTouch && e.cancelable && !e.defaultPrevented) e.preventDefault();
+			}
+		}
+	}, {passive: false})
+}
+
+/* 获取滚动条的位置 */
+me.getScrollTop = function() {
+	return me.scrollTop || 0
+}
+
+/* 是否禁用下拉刷新 */
+me.disabled = function(){
+	return !me.optDown || !me.optDown.use || me.optDown.native
+}
+
+/* 根据点击滑动事件获取第一个手指的坐标 */
+me.getPoint = function(e) {
+	if (!e) {
+		return {x: 0,y: 0}
+	}
+	if (e.touches && e.touches[0]) {
+		return {x: e.touches[0].pageX,y: e.touches[0].pageY}
+	} else if (e.changedTouches && e.changedTouches[0]) {
+		return {x: e.changedTouches[0].pageX,y: e.changedTouches[0].pageY}
+	} else {
+		return {x: e.clientX,y: e.clientY}
+	}
+}
+
+/**
+ * 监听逻辑层数据的变化 (实时更新数据)
+ */
+function propObserver(wxsProp) {
+	me.optDown = wxsProp.optDown
+	me.scrollTop = wxsProp.scrollTop
+	me.isDownScrolling = wxsProp.isDownScrolling
+	me.isUpScrolling = wxsProp.isUpScrolling
+	me.isUpBoth = wxsProp.isUpBoth
+}
+
+/* 导出模块 */
+const renderBiz = {
+	data() {
+		return {
+			propObserver: propObserver,
+		}
+	}
+}
+
+export default renderBiz;

+ 268 - 0
components/mescroll-uni/wxs/wxs.wxs

@@ -0,0 +1,268 @@
+// 使用wxs处理交互动画, 提高性能, 同时避免小程序bounce对下拉刷新的影响
+// https://uniapp.dcloud.io/frame?id=wxs
+// https://developers.weixin.qq.com/miniprogram/dev/framework/view/interactive-animation.html 
+
+// 模拟mescroll实例, 与mescroll.js的写法尽量保持一致
+var me = {}
+
+// ------ 自定义下拉刷新动画 start ------
+
+/* 下拉过程中的回调,滑动过程一直在执行 (rate<1为inOffset; rate>1为outOffset) */
+me.onMoving = function (ins, rate, downHight){
+	ins.requestAnimationFrame(function () {
+		ins.selectComponent('.mescroll-wxs-content').setStyle({
+			'will-change': 'transform', // 可解决下拉过程中, image和swiper脱离文档流的问题
+			'transform': 'translateY(' + downHight + 'px)',
+			'transition': ''
+		})
+		// 环形进度条
+		var progress = ins.selectComponent('.mescroll-wxs-progress')
+		progress && progress.setStyle({transform: 'rotate(' + 360 * rate + 'deg)'})
+	})
+}
+
+/* 显示下拉刷新进度 */
+me.showLoading = function (ins){
+	me.downHight = me.optDown.offset
+	ins.requestAnimationFrame(function () {
+		ins.selectComponent('.mescroll-wxs-content').setStyle({
+			'will-change': 'auto',
+			'transform': 'translateY(' + me.downHight + 'px)',
+			'transition': 'transform 300ms'
+		})
+	})
+}
+
+/* 结束下拉 */
+me.endDownScroll = function (ins){
+	me.downHight = 0;
+	me.isDownScrolling = false;
+	ins.requestAnimationFrame(function () {
+		ins.selectComponent('.mescroll-wxs-content').setStyle({
+			'will-change': 'auto',
+			'transform': 'translateY(0)', // 不可以写空串,否则scroll-view渲染不完整 (延时350ms会调clearTransform置空)
+			'transition': 'transform 300ms'
+		})
+	})
+}
+
+/* 结束下拉动画执行完毕后, 清除transform和transition, 避免对列表内容样式造成影响, 如: h5的list-msg示例下拉进度条漏出来等 */
+me.clearTransform = function (ins){
+	ins.requestAnimationFrame(function () {
+		ins.selectComponent('.mescroll-wxs-content').setStyle({
+			'will-change': '',
+			'transform': '',
+			'transition': ''
+		})
+	})
+}
+
+// ------ 自定义下拉刷新动画 end ------
+
+/**
+ * 监听逻辑层数据的变化 (实时更新数据)
+ */
+function propObserver(wxsProp) {
+	me.optDown = wxsProp.optDown
+	me.scrollTop = wxsProp.scrollTop
+	me.bodyHeight = wxsProp.bodyHeight
+	me.isDownScrolling = wxsProp.isDownScrolling
+	me.isUpScrolling = wxsProp.isUpScrolling
+	me.isUpBoth = wxsProp.isUpBoth
+	me.isScrollBody = wxsProp.isScrollBody
+	me.startTop = wxsProp.scrollTop // 及时更新touchstart触发的startTop, 避免scroll-view快速惯性滚动到顶部取值不准确
+}
+
+/**
+ * 监听逻辑层数据的变化 (调用wxs的方法)
+ */
+function callObserver(callProp, oldValue, ins) {
+	if (me.disabled()) return;
+	if(callProp.callType){
+		// 逻辑层(App Service)的style已失效,需在视图层(Webview)设置style
+		if(callProp.callType === 'showLoading'){
+			me.showLoading(ins)
+		}else if(callProp.callType === 'endDownScroll'){
+			me.endDownScroll(ins)
+		}else if(callProp.callType === 'clearTransform'){
+			me.clearTransform(ins)
+		}
+	}
+}
+
+/**
+ * touch事件
+ */
+function touchstartEvent(e, ins) {
+	me.downHight = 0; // 下拉的距离
+	me.startPoint = me.getPoint(e); // 记录起点
+	me.startTop = me.getScrollTop(); // 记录此时的滚动条位置
+	me.startAngle = 0; // 初始角度
+	me.lastPoint = me.startPoint; // 重置上次move的点
+	me.maxTouchmoveY = me.getBodyHeight() - me.optDown.bottomOffset; // 手指触摸的最大范围(写在touchstart避免body获取高度为0的情况)
+	me.inTouchend = false; // 标记不是touchend
+	
+	me.callMethod(ins, {type: 'setWxsProp'}) // 同步更新wxsProp的数据 (小程序是异步的,可能touchmove先执行,才到propObserver; h5和app是同步)
+}
+
+function touchmoveEvent(e, ins) {
+	var isPrevent = true // false表示不往上冒泡,相当于调用了同时调用了stopPropagation和preventDefault (对小程序生效, h5和app无效)
+	
+	if (me.disabled()) return isPrevent;
+	
+	var scrollTop = me.getScrollTop(); // 当前滚动条的距离
+	var curPoint = me.getPoint(e); // 当前点
+	
+	var moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+	
+	// 向下拉 && 在顶部
+	// mescroll-body,直接判定在顶部即可
+	// scroll-view在滚动时不会触发touchmove,当触顶/底/左/右时,才会触发touchmove
+	// scroll-view滚动到顶部时,scrollTop不一定为0,也有可能大于0; 在iOS的APP中scrollTop可能为负数,不一定和startTop相等
+	if (moveY > 0 && (
+			(me.isScrollBody && scrollTop <= 0)
+			||
+			(!me.isScrollBody && (scrollTop <= 0 || (scrollTop <= me.optDown.startTop && scrollTop === me.startTop)) )
+		)) {
+		// 可下拉的条件
+		if (!me.inTouchend && !me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling &&
+				me.isUpBoth))) {
+	
+			// 下拉的角度是否在配置的范围内
+			if(!me.startAngle) me.startAngle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90]
+			if (me.startAngle < me.optDown.minAngle) return isPrevent; // 如果小于配置的角度,则不往下执行下拉刷新
+	
+			// 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发
+			if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) {
+				me.inTouchend = true; // 标记执行touchend
+				touchendEvent(e, ins); // 提前触发touchend
+				return isPrevent;
+			}
+			
+			isPrevent = false // 小程序是return false
+	
+			var diff = curPoint.y - me.lastPoint.y; // 和上次比,移动的距离 (大于0向下,小于0向上)
+	
+			// 下拉距离  < 指定距离
+			if (me.downHight < me.optDown.offset) {
+				if (me.movetype !== 1) {
+					me.movetype = 1; // 加入标记,保证只执行一次
+					// me.optDown.inOffset && me.optDown.inOffset(me); // 进入指定距离范围内那一刻的回调,只执行一次
+					me.callMethod(ins, {type: 'setLoadType', downLoadType: 1})
+					me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
+				}
+				me.downHight += diff * me.optDown.inOffsetRate; // 越往下,高度变化越小
+	
+				// 指定距离  <= 下拉距离
+			} else {
+				if (me.movetype !== 2) {
+					me.movetype = 2; // 加入标记,保证只执行一次
+					// me.optDown.outOffset && me.optDown.outOffset(me); // 下拉超过指定距离那一刻的回调,只执行一次
+					me.callMethod(ins, {type: 'setLoadType', downLoadType: 2})
+					me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
+				}
+				if (diff > 0) { // 向下拉
+					me.downHight += diff * me.optDown.outOffsetRate; // 越往下,高度变化越小
+				} else { // 向上收
+					me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
+				}
+			}
+			
+			me.downHight = Math.round(me.downHight) // 取整
+			var rate = me.downHight / me.optDown.offset; // 下拉区域当前高度与指定距离的比值
+			// me.optDown.onMoving && me.optDown.onMoving(me, rate, me.downHight); // 下拉过程中的回调,一直在执行
+			me.onMoving(ins, rate, me.downHight)
+		}
+	}
+	
+	me.lastPoint = curPoint; // 记录本次移动的点
+	
+	return isPrevent // false表示不往上冒泡,相当于调用了同时调用了stopPropagation和preventDefault (对小程序生效, h5和app无效)
+}
+
+function touchendEvent(e, ins) {
+	// 如果下拉区域高度已改变,则需重置回来
+	if (me.isMoveDown) {
+		if (me.downHight >= me.optDown.offset) {
+			// 符合触发刷新的条件
+			me.downHight = me.optDown.offset; // 更新下拉区域高度
+			// me.triggerDownScroll();
+			me.callMethod(ins, {type: 'triggerDownScroll'})
+		} else {
+			// 不符合的话 则重置
+			me.downHight = 0;
+			// me.optDown.endDownScroll && me.optDown.endDownScroll(me);
+			me.callMethod(ins, {type: 'endDownScroll'})
+		}
+		me.movetype = 0;
+		me.isMoveDown = false;
+	} else if (!me.isScrollBody && me.getScrollTop() === me.startTop) { // scroll-view到顶/左/右/底的滑动事件
+		var isScrollUp = me.getPoint(e).y - me.startPoint.y < 0; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+		// 上滑
+		if (isScrollUp) {
+			// 需检查滑动的角度
+			var angle = me.getAngle(me.getPoint(e), me.startPoint); // 两点之间的角度,区间 [0,90]
+			if (angle > 80) {
+				// 检查并触发上拉
+				// me.triggerUpScroll(true);
+				me.callMethod(ins, {type: 'triggerUpScroll'})
+			}
+		}
+	}
+	me.callMethod(ins, {type: 'setWxsProp'}) // 同步更新wxsProp的数据 (小程序是异步的,可能touchmove先执行,才到propObserver; h5和app是同步)
+}
+
+/* 是否禁用下拉刷新 */
+me.disabled = function(){
+	return !me.optDown || !me.optDown.use || me.optDown.native
+}
+
+/* 根据点击滑动事件获取第一个手指的坐标 */
+me.getPoint = function(e) {
+	if (!e) {
+		return {x: 0,y: 0}
+	}
+	if (e.touches && e.touches[0]) {
+		return {x: e.touches[0].pageX,y: e.touches[0].pageY}
+	} else if (e.changedTouches && e.changedTouches[0]) {
+		return {x: e.changedTouches[0].pageX,y: e.changedTouches[0].pageY}
+	} else {
+		return {x: e.clientX,y: e.clientY}
+	}
+}
+
+/* 计算两点之间的角度: 区间 [0,90]*/
+me.getAngle = function (p1, p2) {
+	var x = Math.abs(p1.x - p2.x);
+	var y = Math.abs(p1.y - p2.y);
+	var z = Math.sqrt(x * x + y * y);
+	var angle = 0;
+	if (z !== 0) {
+		angle = Math.asin(y / z) / Math.PI * 180;
+	}
+	return angle
+}
+
+/* 获取滚动条的位置 */
+me.getScrollTop = function() {
+	return me.scrollTop || 0
+}
+
+/* 获取body的高度 */
+me.getBodyHeight = function() {
+	return me.bodyHeight || 0;
+}
+
+/* 调用逻辑层的方法 */
+me.callMethod = function(ins, param) {
+	if(ins) ins.callMethod('wxsCall', param)
+}
+
+/* 导出模块 */
+module.exports = {
+	propObserver: propObserver,
+	callObserver: callObserver,
+	touchstartEvent: touchstartEvent,
+	touchmoveEvent: touchmoveEvent,
+	touchendEvent: touchendEvent
+}

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

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

File diff suppressed because it is too large
+ 57 - 0
components/uni-icons/uni-icons.vue


+ 22 - 0
components/uni-popup/message.js

@@ -0,0 +1,22 @@
+export default {
+	created() {
+		if (this.type === 'message') {
+			// 不显示遮罩
+			this.maskShow = false 
+			// 获取子组件对象
+			this.childrenMsg = null
+		}
+	},
+	methods: {
+		customOpen() {
+			if (this.childrenMsg) {
+				this.childrenMsg.open()
+			}
+		},
+		customClose() {
+			if (this.childrenMsg) {
+				this.childrenMsg.close()
+			}
+		}
+	}
+}

+ 25 - 0
components/uni-popup/popup.js

@@ -0,0 +1,25 @@
+import message from './message.js';
+// 定义 type 类型:弹出类型:top/bottom/center
+const config = {
+	// 顶部弹出
+	top:'top',
+	// 底部弹出
+	bottom:'bottom',
+	// 居中弹出
+	center:'center',
+	// 消息提示
+	message:'top',
+	// 对话框
+	dialog:'center',
+	// 分享
+	share:'bottom',
+}
+
+export default {
+	data(){
+		return {
+			config:config
+		}
+	},
+	mixins: [message],
+}

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

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

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

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

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

@@ -0,0 +1,165 @@
+<template>
+	<view class="uni-popup-share">
+		<view class="uni-share-title"><text class="uni-share-title-text">{{title}}</text></view>
+		<view class="uni-share-content">
+			<view class="uni-share-content-box">
+				<view class="uni-share-content-item" v-for="(item,index) in bottomData" :key="index" @click.stop="select(item,index)">
+					<image class="uni-share-image" :src="item.icon" mode="aspectFill"></image>
+					<text class="uni-share-text">{{item.text}}</text>
+				</view>
+
+			</view>
+		</view>
+		<view class="uni-share-button-box">
+			<button class="uni-share-button" @click="close">取消</button>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: 'UniPopupShare',
+		props: {
+			title: {
+				type: String,
+				default: '分享到'
+			}
+		},
+		inject: ['popup'],
+		data() {
+			return {
+				bottomData: [{
+						text: '微信',
+						icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-2.png',
+						name: 'wx'
+					},
+					{
+						text: '支付宝',
+						icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-8.png',
+						name: 'wx'
+					},
+					{
+						text: 'QQ',
+						icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/gird-3.png',
+						name: 'qq'
+					},
+					{
+						text: '新浪',
+						icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-1.png',
+						name: 'sina'
+					},
+					{
+						text: '百度',
+						icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-7.png',
+						name: 'copy'
+					},
+					{
+						text: '其他',
+						icon: 'https://img-cdn-qiniu.dcloud.net.cn/uni-ui/grid-5.png',
+						name: 'more'
+					}
+				]
+			}
+		},
+		created() {},
+		methods: {
+			/**
+			 * 选择内容
+			 */
+			select(item, index) {
+				this.$emit('select', {
+					item,
+					index
+				}, () => {
+					this.popup.close()
+				})
+			},
+			/**
+			 * 关闭窗口
+			 */
+			close() {
+				this.popup.close()
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.uni-popup-share {
+		background-color: #fff;
+	}
+	.uni-share-title {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		justify-content: center;
+		height: 40px;
+	}
+	.uni-share-title-text {
+		font-size: 14px;
+		color: #666;
+	}
+	.uni-share-content {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		padding-top: 10px;
+	}
+	
+	.uni-share-content-box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		flex-wrap: wrap;
+		width: 360px;
+	}
+	
+	.uni-share-content-item {
+		width: 90px;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		padding: 10px 0;
+		align-items: center;
+	}
+	
+	.uni-share-content-item:active {
+		background-color: #f5f5f5;
+	}
+	
+	.uni-share-image {
+		width: 30px;
+		height: 30px;
+	}
+	
+	.uni-share-text {
+		margin-top: 10px;
+		font-size: 14px;
+		color: #3B4144;
+	}
+	
+	.uni-share-button-box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		padding: 10px 15px;
+	}
+	
+	.uni-share-button {
+		flex: 1;
+		border-radius: 50px;
+		color: #666;
+		font-size: 16px;
+	}
+	
+	.uni-share-button::after {
+		border-radius: 50px;
+	}
+</style>

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

@@ -0,0 +1,294 @@
+<template>
+	<view v-if="showPopup" class="uni-popup" :class="[popupstyle]" @touchmove.stop.prevent="clear">
+		<uni-transition v-if="maskShow" :mode-class="['fade']" :styles="maskClass" :duration="duration" :show="showTrans"
+		 @click="onTap" />
+		<uni-transition :mode-class="ani" :styles="transClass" :duration="duration" :show="showTrans" @click="onTap">
+			<view class="uni-popup__wrapper-box" @click.stop="clear">
+				<slot />
+			</view>
+		</uni-transition>
+	</view>
+</template>
+
+<script>
+	import uniTransition from '../uni-transition/uni-transition.vue'
+	import popup from './popup.js'
+	/**
+	 * PopUp 弹出层
+	 * @description 弹出层组件,为了解决遮罩弹层的问题
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=329
+	 * @property {String} type = [top|center|bottom] 弹出方式
+	 * 	@value top 顶部弹出
+	 * 	@value center 中间弹出
+	 * 	@value bottom 底部弹出
+	 * 	@value message 消息提示
+	 * 	@value dialog 对话框
+	 * 	@value share 底部分享示例
+	 * @property {Boolean} animation = [ture|false] 是否开启动画
+	 * @property {Boolean} maskClick = [ture|false] 蒙版点击是否关闭弹窗
+	 * @event {Function} change 打开关闭弹窗触发,e={show: false}
+	 */
+
+	export default {
+		name: 'UniPopup',
+		components: {
+			uniTransition
+		},
+		props: {
+			// 开启动画
+			animation: {
+				type: Boolean,
+				default: true
+			},
+			// 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
+			// message: 消息提示 ; dialog : 对话框
+			type: {
+				type: String,
+				default: 'center'
+			},
+			// maskClick
+			maskClick: {
+				type: Boolean,
+				default: true
+			}
+		},
+		provide() {
+			return {
+				popup: this
+			}
+		},
+		mixins: [popup],
+		watch: {
+			/**
+			 * 监听type类型
+			 */
+			type: {
+				handler: function(newVal) {
+					this[this.config[newVal]]()
+				},
+				immediate: true
+			},
+			/**
+			 * 监听遮罩是否可点击
+			 * @param {Object} val
+			 */
+			maskClick(val) {
+				this.mkclick = val
+			}
+		},
+		data() {
+			return {
+				duration: 300,
+				ani: [],
+				showPopup: false,
+				showTrans: false,
+				maskClass: {
+					'position': 'fixed',
+					'bottom': 0,
+					'top': 0,
+					'left': 0,
+					'right': 0,
+					'backgroundColor': 'rgba(0, 0, 0, 0.4)'
+				},
+				transClass: {
+					'position': 'fixed',
+					'left': 0,
+					'right': 0,
+				},
+				maskShow: true,
+				mkclick: true,
+				popupstyle: 'top'
+			}
+		},
+		created() {
+			this.mkclick = this.maskClick
+			if (this.animation) {
+				this.duration = 300
+			} else {
+				this.duration = 0
+			}
+		},
+		methods: {
+			clear(e) {
+				// TODO nvue 取消冒泡
+				e.stopPropagation()
+			},
+			open() {
+				this.showPopup = true
+				this.$nextTick(() => {
+					new Promise(resolve => {
+						clearTimeout(this.timer)
+						this.timer = setTimeout(() => {
+							this.showTrans = true
+							// fixed by mehaotian 兼容 app 端
+							this.$nextTick(() => {
+								resolve();
+							})
+						}, 50);
+					}).then(res => {
+						// 自定义打开事件
+						clearTimeout(this.msgtimer)
+						this.msgtimer = setTimeout(() => {
+							this.customOpen && this.customOpen()
+						}, 100)
+						this.$emit('change', {
+							show: true,
+							type: this.type
+						})
+					})
+				})
+			},
+			close(type) {
+				this.showTrans = false
+				this.$nextTick(() => {
+					this.$emit('change', {
+						show: false,
+						type: this.type
+					})
+					clearTimeout(this.timer)
+					// 自定义关闭事件
+					this.customOpen && this.customClose()
+					this.timer = setTimeout(() => {
+						this.showPopup = false
+					}, 300)
+				})
+			},
+			onTap() {
+				if (!this.mkclick) return
+				this.close()
+			},
+			/**
+			 * 顶部弹出样式处理
+			 */
+			top() {
+				this.popupstyle = 'top'
+				this.ani = ['slide-top']
+				this.transClass = {
+					'position': 'fixed',
+					'left': 0,
+					'right': 0,
+				}
+			},
+			/**
+			 * 底部弹出样式处理
+			 */
+			bottom() {
+				this.popupstyle = 'bottom'
+				this.ani = ['slide-bottom']
+				this.transClass = {
+					'position': 'fixed',
+					'left': 0,
+					'right': 0,
+					'bottom': 0
+				}
+			},
+			/**
+			 * 中间弹出样式处理
+			 */
+			center() {
+				this.popupstyle = 'center'
+				this.ani = ['zoom-out', 'fade']
+				this.transClass = {
+					'position': 'fixed',
+					/* #ifndef APP-NVUE */
+					'display': 'flex',
+					'flexDirection': 'column',
+					/* #endif */
+					'bottom': 0,
+					'left': 0,
+					'right': 0,
+					'top': 0,
+					'justifyContent': 'center',
+					'alignItems': 'center'
+				}
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.uni-popup {
+		position: fixed;
+		/* #ifndef APP-NVUE */
+		z-index: 99;
+		/* #endif */
+	}
+
+	.uni-popup__mask {
+		position: absolute;
+		top: 0;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		background-color: $uni-bg-color-mask;
+		opacity: 0;
+	}
+
+	.mask-ani {
+		transition-property: opacity;
+		transition-duration: 0.2s;
+	}
+
+	.uni-top-mask {
+		opacity: 1;
+	}
+
+	.uni-bottom-mask {
+		opacity: 1;
+	}
+
+	.uni-center-mask {
+		opacity: 1;
+	}
+
+	.uni-popup__wrapper {
+		/* #ifndef APP-NVUE */
+		display: block;
+		/* #endif */
+		position: absolute;
+	}
+
+	.top {
+		/* #ifdef H5 */
+		top: var(--window-top);
+		/* #endif */
+		/* #ifndef H5 */
+		top: 0;
+		/* #endif */
+	}
+
+	.bottom {
+		bottom: 0;
+	}
+
+	.uni-popup__wrapper-box {
+		/* #ifndef APP-NVUE */
+		display: block;
+		/* #endif */
+		position: relative;
+		/* iphonex 等安全区设置,底部安全区适配 */
+		/* #ifndef APP-NVUE */
+		padding-bottom: constant(safe-area-inset-bottom);
+		padding-bottom: env(safe-area-inset-bottom);
+		/* #endif */
+	}
+
+	.content-ani {
+		// transition: transform 0.3s;
+		transition-property: transform, opacity;
+		transition-duration: 0.2s;
+	}
+
+
+	.uni-top-content {
+		transform: translateY(0);
+	}
+
+	.uni-bottom-content {
+		transform: translateY(0);
+	}
+
+	.uni-center-content {
+		transform: scale(1);
+		opacity: 1;
+	}
+</style>

+ 183 - 0
components/uni-search-bar/uni-search-bar.vue

@@ -0,0 +1,183 @@
+<template>
+	<view class="uni-searchbar">
+		<view :style="{borderRadius:radius+'px',backgroundColor: bgColor}" class="uni-searchbar__box" @click="searchClick">
+			<!-- #ifdef MP-ALIPAY -->
+			<view class="uni-searchbar__box-icon-search">
+				<uni-icons color="#999999" size="18" type="search" />
+			</view>
+			<!-- #endif -->
+			<!-- #ifndef MP-ALIPAY -->
+			<uni-icons color="#999999" class="uni-searchbar__box-icon-search" size="18" type="search" />
+			<!-- #endif -->
+			<input v-if="show" :focus="showSync" :placeholder="placeholder" :maxlength="maxlength" @confirm="confirm" class="uni-searchbar__box-search-input"
+			 confirm-type="search" type="text" v-model="searchVal" />
+			<text v-else class="uni-searchbar__text-placeholder">{{ placeholder }}</text>
+			<view v-if="show && (clearButton==='always'||clearButton==='auto'&&searchVal!=='')" class="uni-searchbar__box-icon-clear" @click="clear">
+				<uni-icons color="#999999" class="" size="24" type="clear" />
+			</view>
+		</view>
+		<text @click="cancel" class="uni-searchbar__cancel" v-if="cancelButton ==='always' || show && cancelButton ==='auto'">{{cancelText}}</text>
+	</view>
+</template>
+
+<script>
+	import uniIcons from "../uni-icons/uni-icons.vue";
+	export default {
+		name: "UniSearchBar",
+		components: {
+			uniIcons
+		},
+		props: {
+			placeholder: {
+				type: String,
+				default: "请输入搜索内容"
+			},
+			radius: {
+				type: [Number, String],
+				default: 5
+			},
+			clearButton: {
+				type: String,
+				default: "auto"
+			},
+			cancelButton: {
+				type: String,
+				default: "auto"
+			},
+			cancelText: {
+				type: String,
+				default: '取消'
+			},
+			bgColor: {
+				type: String,
+				default: "#F8F8F8"
+			},
+			maxlength: {
+				type: [Number, String],
+				default: 100
+			}
+		},
+		data() {
+			return {
+				show: false,
+				showSync: false,
+				searchVal: ""
+			}
+		},
+		watch: {
+			searchVal() {
+				this.$emit("input", {
+					value: this.searchVal
+				})
+			}
+		},
+		methods: {
+			searchClick() {
+				if (this.show) {
+					return
+				}
+				this.searchVal = ""
+				this.show = true;
+				this.$nextTick(() => {
+					this.showSync = true;
+				})
+			},
+			clear() {
+				this.searchVal = ""
+			},
+			cancel() {
+				this.$emit("cancel", {
+					value: this.searchVal
+				});
+				this.searchVal = ""
+				this.show = false
+				this.showSync = false
+				// #ifndef APP-PLUS
+				uni.hideKeyboard()
+				// #endif
+				// #ifdef APP-PLUS
+				plus.key.hideSoftKeybord()
+				// #endif
+			},
+			confirm() {
+				// #ifndef APP-PLUS
+				uni.hideKeyboard();
+				// #endif
+				// #ifdef APP-PLUS
+				plus.key.hideSoftKeybord()
+				// #endif
+				this.$emit("confirm", {
+					value: this.searchVal
+				})
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	$uni-searchbar-height: 36px;
+
+	.uni-searchbar {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		position: relative;
+		padding: $uni-spacing-col-base;
+		background-color: $uni-bg-color;
+	}
+
+	.uni-searchbar__box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		box-sizing: border-box;
+		/* #endif */
+		overflow: hidden;
+		position: relative;
+		flex: 1;
+		justify-content: center;
+		flex-direction: row;
+		align-items: center;
+		height: $uni-searchbar-height;
+		padding: 5px 8px 5px 0px;
+		border-width: 0.5px;
+		border-style: solid;
+		border-color: $uni-border-color;
+	}
+
+	.uni-searchbar__box-icon-search {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		width: 32px;
+		justify-content: center;
+		align-items: center;
+		color: $uni-text-color-placeholder;
+	}
+
+	.uni-searchbar__box-search-input {
+		flex: 1;
+		font-size: $uni-font-size-base;
+		color: $uni-text-color;
+	}
+
+	.uni-searchbar__box-icon-clear {
+		align-items: center;
+		line-height: 24px;
+		padding-left: 5px;
+	}
+
+	.uni-searchbar__text-placeholder {
+		font-size: $uni-font-size-base;
+		color: $uni-text-color-placeholder;
+		margin-left: 5px;
+	}
+
+	.uni-searchbar__cancel {
+		padding-left: 10px;
+		line-height: $uni-searchbar-height;
+		font-size: 14px;
+		color: $uni-text-color;
+	}
+</style>

+ 136 - 0
components/uni-section/uni-section.vue

@@ -0,0 +1,136 @@
+<template>
+	<view class="uni-section" nvue>
+		<view v-if="type" class="uni-section__head">
+			<view :class="type" class="uni-section__head-tag" />
+		</view>
+		<view class="uni-section__content">
+			<text :class="{'distraction':!subTitle}" class="uni-section__content-title">{{ title }}</text>
+			<text v-if="subTitle" class="uni-section__content-sub">{{ subTitle }}</text>
+		</view>
+		<slot />
+	</view>
+</template>
+
+<script>
+
+	/**
+	 * Section 标题栏
+	 * @description 标题栏
+	 * @property {String} type = [line|circle] 标题装饰类型
+	 * 	@value line 竖线
+	 * 	@value circle 圆形
+	 * @property {String} title 主标题
+	 * @property {String} subTitle 副标题
+	 */
+
+	export default {
+		name: 'UniSection',
+		props: {
+			type: {
+				type: String,
+				default: ''
+			},
+			title: {
+				type: String,
+				default: ''
+			},
+			subTitle: {
+				type: String,
+				default: ''
+			}
+		},
+		data() {
+			return {}
+		},
+		watch: {
+			title(newVal) {
+				if (uni.report && newVal !== '') {
+					uni.report('title', newVal)
+				}
+			}
+		},
+		methods: {
+			onClick() {
+				this.$emit('click')
+			}
+		}
+	}
+</script>
+<style lang="scss" scoped>
+	.uni-section {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		margin-top: 10px;
+		flex-direction: row;
+		align-items: center;
+		padding: 0 10px;
+		height: 50px;
+		background-color: $uni-bg-color-grey;
+		/* #ifdef APP-NVUE */
+		// border-bottom-color: $uni-border-color;
+		// border-bottom-style: solid;
+		// border-bottom-width: 0.5px;
+		/* #endif */
+		font-weight: normal;
+	}
+	/* #ifndef APP-NVUE */
+	// .uni-section:after {
+	// 	position: absolute;
+	// 	bottom: 0;
+	// 	right: 0;
+	// 	left: 0;
+	// 	height: 1px;
+	// 	content: '';
+	// 	-webkit-transform: scaleY(.5);
+	// 	transform: scaleY(.5);
+	// 	background-color: $uni-border-color;
+	// }
+	/* #endif */
+
+	.uni-section__head {
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		margin-right: 10px;
+	}
+
+	.line {
+		height: 15px;
+		background-color: $uni-text-color-disable;
+		border-radius: 5px;
+		width: 3px;
+	}
+
+	.circle {
+		width: 8px;
+		height: 8px;
+		border-top-right-radius: 50px;
+		border-top-left-radius: 50px;
+		border-bottom-left-radius: 50px;
+		border-bottom-right-radius: 50px;
+		background-color: $uni-text-color-disable;
+	}
+
+	.uni-section__content {
+		flex-direction: column;
+		flex: 1;
+		color: $uni-text-color;
+	}
+
+	.uni-section__content-title {
+		font-size: $uni-font-size-base;
+		color: $uni-text-color;
+	}
+
+	.distraction {
+		flex-direction: row;
+		align-items: center;
+	}
+
+	.uni-section__content-sub {
+		font-size: $uni-font-size-sm;
+		color: $uni-text-color-grey;
+	}
+</style>

+ 292 - 0
components/uni-swipe-action-item/bindingx.js

@@ -0,0 +1,292 @@
+const BindingX = uni.requireNativePlugin('bindingx');
+const dom = uni.requireNativePlugin('dom');
+const animation = uni.requireNativePlugin('animation');
+
+export default {
+	data() {
+		return {}
+	},
+
+	watch: {
+		show(newVal) {
+			if (this.autoClose) return
+			if (this.stop) return
+			this.stop = true
+			if (newVal) {
+				this.open(newVal)
+			} else {
+				this.close()
+			}
+		},
+		leftOptions() {
+			this.getSelectorQuery()
+			this.init()
+		},
+		rightOptions(newVal) {
+			this.init()
+		}
+	},
+	created() {
+		if (this.swipeaction.children !== undefined) {
+			this.swipeaction.children.push(this)
+		}
+	},
+	mounted() {
+		this.box = this.getEl(this.$refs['selector-box--hock'])
+		this.selector = this.getEl(this.$refs['selector-content--hock']);
+		this.leftButton = this.getEl(this.$refs['selector-left-button--hock']);
+		this.rightButton = this.getEl(this.$refs['selector-right-button--hock']);
+		this.init()
+	},
+	beforeDestroy() {
+		this.swipeaction.children.forEach((item, index) => {
+			if (item === this) {
+				this.swipeaction.children.splice(index, 1)
+			}
+		})
+	},
+	methods: {
+		init() {
+			this.$nextTick(() => {
+				this.x = 0
+				this.button = {
+					show: false
+				}
+				setTimeout(() => {
+					this.getSelectorQuery()
+				}, 200)
+			})
+		},
+		onClick(index, item, position) {
+			this.$emit('click', {
+				content: item,
+				index,
+				position
+			})
+		},
+		touchstart(e) {
+			// 每次只触发一次,避免多次监听造成闪烁
+			if (this.stop) return
+			this.stop = true
+			if (this.autoClose) {
+				this.swipeaction.closeOther(this)
+			}
+				
+			const leftWidth = this.button.left.width
+			const rightWidth = this.button.right.width
+			let expression = this.range(this.x, -rightWidth, leftWidth)
+			let leftExpression = this.range(this.x - leftWidth, -leftWidth, 0)
+			let rightExpression = this.range(this.x + rightWidth, 0, rightWidth)
+
+			this.eventpan = BindingX.bind({
+				anchor: this.box,
+				eventType: 'pan',
+				props: [{
+					element: this.selector,
+					property: 'transform.translateX',
+					expression
+				}, {
+					element: this.leftButton,
+					property: 'transform.translateX',
+					expression: leftExpression
+				}, {
+					element: this.rightButton,
+					property: 'transform.translateX',
+					expression: rightExpression
+				}, ]
+			}, (e) => {
+				// nope
+				if (e.state === 'end') {
+					this.x = e.deltaX + this.x;
+					this.isclick = true
+					this.bindTiming(e.deltaX)
+				}
+			});
+		},
+		touchend(e) {
+			if (this.isopen !== 'none' && !this.isclick) {
+				this.open('none')
+			}
+		},
+		bindTiming(x) {
+			const left = this.x
+			const leftWidth = this.button.left.width
+			const rightWidth = this.button.right.width
+			const threshold = this.threshold
+			if (!this.isopen || this.isopen === 'none') {
+				if (left > threshold) {
+					this.open('left')
+				} else if (left < -threshold) {
+					this.open('right')
+				} else {
+					this.open('none')
+				}
+			} else {
+				if ((x > -leftWidth && x < 0) || x > rightWidth) {
+					if ((x > -threshold && x < 0) || (x - rightWidth > threshold)) {
+						this.open('left')
+					} else {
+						this.open('none')
+					}
+				} else {
+					if ((x < threshold && x > 0) || (x + leftWidth < -threshold)) {
+						this.open('right')
+					} else {
+						this.open('none')
+					}
+				}
+			}
+		},
+
+		/**
+		 * 移动范围
+		 * @param {Object} num
+		 * @param {Object} mix
+		 * @param {Object} max
+		 */
+		range(num, mix, max) {
+			return `min(max(x+${num}, ${mix}), ${max})`
+		},
+
+		/**
+		 * 开启swipe
+		 */
+		open(type) {
+			this.animation(type)
+		},
+
+		/**
+		 * 关闭swipe
+		 */
+		close() {
+			this.animation('none')
+		},
+
+		/**
+		 * 开启关闭动画
+		 * @param {Object} type
+		 */
+		animation(type) {
+			const time = 300
+			const leftWidth = this.button.left.width
+			const rightWidth = this.button.right.width
+			if (this.eventpan && this.eventpan.token) {
+				BindingX.unbind({
+					token: this.eventpan.token,
+					eventType: 'pan'
+				})
+			}
+
+			switch (type) {
+				case 'left':
+					Promise.all([
+						this.move(this.selector, leftWidth),
+						this.move(this.leftButton, 0),
+						this.move(this.rightButton, rightWidth * 2)
+					]).then(() => {
+						this.setEmit(leftWidth, type)
+					})
+					break
+				case 'right':
+					Promise.all([
+						this.move(this.selector, -rightWidth),
+						this.move(this.leftButton, -leftWidth * 2),
+						this.move(this.rightButton, 0)
+					]).then(() => {
+						this.setEmit(-rightWidth, type)
+					})
+					break
+				default:
+					Promise.all([
+						this.move(this.selector, 0),
+						this.move(this.leftButton, -leftWidth),
+						this.move(this.rightButton, rightWidth)
+					]).then(() => {
+						this.setEmit(0, type)
+					})
+
+			}
+		},
+		setEmit(x, type) {
+			const leftWidth = this.button.left.width
+			const rightWidth = this.button.right.width
+			this.isopen = this.isopen || 'none'
+			this.stop = false
+			this.isclick = false
+			// 只有状态不一致才会返回结果
+			if (this.isopen !== type && this.x !== x) {
+				if (type === 'left' && leftWidth > 0) {
+					this.$emit('change', 'left')
+				}
+				if (type === 'right' && rightWidth > 0) {
+					this.$emit('change', 'right')
+				}
+				if (type === 'none') {
+					this.$emit('change', 'none')
+				}
+			}
+			this.x = x
+			this.isopen = type
+		},
+		move(ref, value) {
+			return new Promise((resolve, reject) => {
+				animation.transition(ref, {
+					styles: {
+						transform: `translateX(${value})`,
+					},
+					duration: 150, //ms
+					timingFunction: 'linear',
+					needLayout: false,
+					delay: 0 //ms
+				}, function(res) {
+					resolve(res)
+				})
+			})
+
+		},
+
+		/**
+		 * 获取ref
+		 * @param {Object} el
+		 */
+		getEl(el) {
+			return el.ref
+		},
+		/**
+		 * 获取节点信息
+		 */
+		getSelectorQuery() {
+			Promise.all([
+				this.getDom('left'),
+				this.getDom('right'),
+			]).then((data) => {
+				let show = 'none'
+				if (this.autoClose) {
+					show = 'none'
+				} else {
+					show = this.show
+				}
+
+				if (show === 'none') {
+					// this.close()
+				} else {
+					this.open(show)
+				}
+
+			})
+
+		},
+		getDom(str) {
+			return new Promise((resolve, reject) => {
+				dom.getComponentRect(this.$refs[`selector-${str}-button--hock`], (data) => {
+					if (data) {
+						this.button[str] = data.size
+						resolve(data)
+					} else {
+						reject()
+					}
+				})
+			})
+		}
+	}
+}

+ 266 - 0
components/uni-swipe-action-item/index.wxs

@@ -0,0 +1,266 @@
+var MIN_DISTANCE = 10;
+
+/**
+ * 监听页面内值的变化,主要用于动态开关swipe-action
+ * @param {Object} newValue
+ * @param {Object} oldValue
+ * @param {Object} ownerInstance
+ * @param {Object} instance
+ */
+function sizeReady(newValue, oldValue, ownerInstance, instance) {
+	var state = instance.getState()
+	var buttonPositions = JSON.parse(newValue)
+	if (!buttonPositions || !buttonPositions.data || buttonPositions.data.length === 0) return
+	state.leftWidth = buttonPositions.data[0].width
+	state.rightWidth = buttonPositions.data[1].width
+	state.threshold = instance.getDataset().threshold
+
+	if (buttonPositions.show && buttonPositions.show !== 'none') {
+		openState(buttonPositions.show, instance, ownerInstance)
+		return
+	}
+
+	if (state.left) {
+		openState('none', instance, ownerInstance)
+	}
+	resetTouchStatus(instance)
+}
+
+/**
+ * 开始触摸操作
+ * @param {Object} e
+ * @param {Object} ins
+ */
+function touchstart(e, ins) {
+	var instance = e.instance;
+	var disabled = instance.getDataset().disabled
+	var state = instance.getState();
+	// fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复
+	disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false;
+	if (disabled) return
+	// 开始触摸时移除动画类
+	instance.requestAnimationFrame(function(){
+		instance.removeClass('ani');
+		ins.callMethod('closeSwipe');
+	})
+
+	// 记录上次的位置
+	state.x = state.left || 0
+	// 计算滑动开始位置
+	stopTouchStart(e, ins)
+}
+
+/**
+ * 开始滑动操作
+ * @param {Object} e
+ * @param {Object} ownerInstance
+ */
+function touchmove(e, ownerInstance) {
+
+	var instance = e.instance;
+	var disabled = instance.getDataset().disabled
+	var state = instance.getState()
+	// fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复
+	disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false;
+	if (disabled) return
+	// 是否可以滑动页面
+	stopTouchMove(e);
+	if (state.direction !== 'horizontal') {
+		return;
+	}
+
+	if (e.preventDefault) {
+		// 阻止页面滚动
+		e.preventDefault()
+	}
+
+	move(state.x + state.deltaX, instance, ownerInstance)
+}
+
+/**
+ * 结束触摸操作
+ * @param {Object} e
+ * @param {Object} ownerInstance
+ */
+function touchend(e, ownerInstance) {
+	var instance = e.instance;
+	var disabled = instance.getDataset().disabled
+	var state = instance.getState()
+	// fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复
+	disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false;
+
+	if (disabled) return
+	// 滑动过程中触摸结束,通过阙值判断是开启还是关闭
+	// fixed by mehaotian 定时器解决点击按钮,touchend 触发比 click 事件时机早的问题 ,主要是 ios13
+	moveDirection(state.left, instance, ownerInstance)
+
+}
+
+/**
+ * 设置移动距离
+ * @param {Object} value
+ * @param {Object} instance
+ * @param {Object} ownerInstance
+ */
+function move(value, instance, ownerInstance) {
+	value = value || 0
+	var state = instance.getState()
+	var leftWidth = state.leftWidth
+	var rightWidth = state.rightWidth
+	// 获取可滑动范围
+	state.left = range(value, -rightWidth, leftWidth);
+	instance.requestAnimationFrame(function(){
+		instance.setStyle({
+			transform: 'translateX(' + state.left + 'px)',
+			'-webkit-transform': 'translateX(' + state.left + 'px)'
+		})
+	})
+
+}
+
+/**
+ * 获取范围
+ * @param {Object} num
+ * @param {Object} min
+ * @param {Object} max
+ */
+function range(num, min, max) {
+	return Math.min(Math.max(num, min), max);
+}
+
+
+/**
+ * 移动方向判断
+ * @param {Object} left
+ * @param {Object} value
+ * @param {Object} ownerInstance
+ * @param {Object} ins
+ */
+function moveDirection(left, ins, ownerInstance) {
+	var state = ins.getState()
+	var threshold = state.threshold
+	var position = state.position
+	var isopen = state.isopen || 'none'
+	var leftWidth = state.leftWidth
+	var rightWidth = state.rightWidth
+	if (state.deltaX === 0) {
+		openState('none', ins, ownerInstance)
+		return
+	}
+	if ((isopen === 'none' && rightWidth > 0 && -left > threshold) || (isopen !== 'none' && rightWidth > 0 && rightWidth +
+			left < threshold)) {
+		// right
+		openState('right', ins, ownerInstance)
+	} else if ((isopen === 'none' && leftWidth > 0 && left > threshold) || (isopen !== 'none' && leftWidth > 0 &&
+			leftWidth - left < threshold)) {
+		// left
+		openState('left', ins, ownerInstance)
+	} else {
+		// default
+		openState('none', ins, ownerInstance)
+	}
+}
+
+
+/**
+ * 开启状态
+ * @param {Boolean} type
+ * @param {Object} ins
+ * @param {Object} ownerInstance
+ */
+function openState(type, ins, ownerInstance) {
+	var state = ins.getState()
+	var position = state.position
+	var leftWidth = state.leftWidth
+	var rightWidth = state.rightWidth
+	var left = ''
+	state.isopen = state.isopen ? state.isopen : 'none'
+	switch (type) {
+		case "left":
+			left = leftWidth
+			break
+		case "right":
+			left = -rightWidth
+			break
+		default:
+			left = 0
+	}
+
+	// && !state.throttle
+
+	if (state.isopen !== type ) {
+		state.throttle = true
+		ownerInstance.callMethod('change', {
+			open: type
+		})
+
+	}
+
+	state.isopen = type
+	// 添加动画类
+	ins.requestAnimationFrame(function(){
+		ins.addClass('ani');
+		move(left, ins, ownerInstance)
+	})
+	// 设置最终移动位置,理论上只要进入到这个函数,肯定是要打开的
+}
+
+
+function getDirection(x, y) {
+	if (x > y && x > MIN_DISTANCE) {
+		return 'horizontal';
+	}
+	if (y > x && y > MIN_DISTANCE) {
+		return 'vertical';
+	}
+	return '';
+}
+
+/**
+ * 重置滑动状态
+ * @param {Object} event
+ */
+function resetTouchStatus(instance) {
+	var state = instance.getState();
+	state.direction = '';
+	state.deltaX = 0;
+	state.deltaY = 0;
+	state.offsetX = 0;
+	state.offsetY = 0;
+}
+
+/**
+ * 设置滑动开始位置
+ * @param {Object} event
+ */
+function stopTouchStart(event) {
+	var instance = event.instance;
+	var state = instance.getState();
+	resetTouchStatus(instance);
+	var touch = event.touches[0];
+	state.startX = touch.clientX;
+	state.startY = touch.clientY;
+}
+
+/**
+ * 滑动中,是否禁止打开
+ * @param {Object} event
+ */
+function stopTouchMove(event) {
+	var instance = event.instance;
+	var state = instance.getState();
+	var touch = event.touches[0];
+	state.deltaX = touch.clientX - state.startX;
+	state.deltaY = touch.clientY - state.startY;
+	state.offsetX = Math.abs(state.deltaX);
+	state.offsetY = Math.abs(state.deltaY);
+	state.direction = state.direction || getDirection(state.offsetX, state.offsetY);
+}
+
+
+module.exports = {
+	sizeReady: sizeReady,
+	touchstart: touchstart,
+	touchmove: touchmove,
+	touchend: touchend
+}

+ 207 - 0
components/uni-swipe-action-item/mpalipay.js

@@ -0,0 +1,207 @@
+export default {
+	data() {
+		return {
+			x: 0,
+			transition: false,
+			width: 0,
+			viewWidth: 0,
+			swipeShow: 0
+		}
+	},
+	watch: {
+		show(newVal) {
+			if (this.autoClose) return
+			if (newVal && newVal !== 'none' ) {
+				this.transition = true
+				this.open(newVal)
+			} else {
+				this.close()
+			}
+		}
+	},
+	created() {
+		if (this.swipeaction.children !== undefined) {
+			this.swipeaction.children.push(this)
+		}
+	},
+
+	beforeDestroy() {
+		this.swipeaction.children.forEach((item, index) => {
+			if (item === this) {
+				this.swipeaction.children.splice(index, 1)
+			}
+		})
+	},
+	mounted() {
+		this.isopen = false
+		setTimeout(() => {
+			this.getQuerySelect()
+		}, 50)
+	},
+	methods: {
+		appTouchStart(e) {
+			const {
+				clientX
+			} = e.changedTouches[0]
+			this.clientX = clientX
+			this.timestamp = new Date().getTime()
+		},
+		appTouchEnd(e, index, item, position) {
+			const {
+				clientX
+			} = e.changedTouches[0]
+			// fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题
+			let diff = Math.abs(this.clientX - clientX)
+			let time = (new Date().getTime()) - this.timestamp
+			if (diff < 40 && time < 300) {
+				this.$emit('click', {
+					content: item,
+					index,
+					position
+				})
+			}
+		},
+		// onClick(index, item, position) {
+		// 	this.$emit('click', {
+		// 		content: item,
+		// 		index,
+		// 		position
+		// 	})
+		// },
+		/**
+		 * 移动触发
+		 * @param {Object} e
+		 */
+		onChange(e) {
+			this.moveX = e.detail.x
+			this.isclose = false
+		},
+		touchstart(e) {
+			this.transition = false
+			this.isclose = true
+			this.autoClose && this.swipeaction.closeOther(this)
+		},
+		touchmove(e) {},
+		touchend(e) {
+			// 0的位置什么都不执行
+			if (this.isclose && this.isopen === 'none') return
+			if (this.isclose && this.isopen !== 'none') {
+				this.transition = true
+				this.close()
+			} else {
+				this.move(this.moveX + this.leftWidth)
+			}
+		},
+
+		/**
+		 * 移动
+		 * @param {Object} moveX
+		 */
+		move(moveX) {
+			// 打开关闭的处理逻辑不太一样
+			this.transition = true
+			// 未打开状态
+			if (!this.isopen || this.isopen === 'none') {
+				if (moveX > this.threshold) {
+					this.open('left')
+				} else if (moveX < -this.threshold) {
+					this.open('right')
+				} else {
+					this.close()
+				}
+			} else {
+				if (moveX < 0 && moveX < this.rightWidth) {
+					const rightX = this.rightWidth + moveX
+					if (rightX < this.threshold) {
+						this.open('right')
+					} else {
+						this.close()
+					}
+				} else if (moveX > 0 && moveX < this.leftWidth) {
+					const leftX = this.leftWidth - moveX
+					if (leftX < this.threshold) {
+						this.open('left')
+					} else {
+						this.close()
+					}
+				}
+
+			}
+
+		},
+
+		/**
+		 * 打开
+		 */
+		open(type) {
+			this.x = this.moveX
+			this.animation(type)
+		},
+
+		/**
+		 * 关闭
+		 */
+		close() {
+			this.x = this.moveX
+			// TODO 解决 x 值不更新的问题,所以会多触发一次 nextTick ,待优化
+			this.$nextTick(() => {
+				this.x = -this.leftWidth
+				if(this.isopen!=='none'){
+					this.$emit('change', 'none')
+				}
+				this.isopen = 'none'
+			})
+		},
+
+		/**
+		 * 执行结束动画
+		 * @param {Object} type
+		 */
+		animation(type) {
+			this.$nextTick(() => {
+				if (type === 'left') {
+					this.x = 0
+				} else {
+					this.x = -this.rightWidth - this.leftWidth
+				}
+				
+				if(this.isopen!==type){
+					this.$emit('change', type)
+				}
+				this.isopen = type
+			})
+
+		},
+		getSlide(x) {},
+		getQuerySelect() {
+			const query = uni.createSelectorQuery().in(this);
+			query.selectAll('.movable-view--hock').boundingClientRect(data => {
+				this.leftWidth = data[1].width
+				this.rightWidth = data[2].width
+				this.width = data[0].width
+				this.viewWidth = this.width + this.rightWidth + this.leftWidth
+				if (this.leftWidth === 0) {
+					// TODO 疑似bug ,初始化的时候如果x 是0,会导致移动位置错误,所以让元素超出一点
+					this.x = -0.1
+				} else {
+					this.x = -this.leftWidth
+				}
+				this.moveX = this.x
+				this.$nextTick(() => {
+					this.swipeShow = 1
+				})
+
+				if (!this.buttonWidth) {
+					this.disabledView = true
+				}
+
+				if (this.autoClose) return
+				if (this.show !== 'none') {
+					this.transition = true
+					this.open(this.shows)
+				}
+			}).exec();
+
+		}
+	}
+}

+ 252 - 0
components/uni-swipe-action-item/mpother.js

@@ -0,0 +1,252 @@
+const MIN_DISTANCE = 10;
+export default {
+	data() {
+		return {
+			uniShow: false,
+			left: 0,
+			buttonShow: 'none',
+			ani: false,
+			moveLeft:''
+		}
+	},
+	watch: {
+		show(newVal) {
+			if (this.autoClose) return
+			this.openState(newVal)
+		},
+		left(){
+			this.moveLeft = `translateX(${this.left}px)`
+		},
+		buttonShow(newVal){
+			if (this.autoClose) return
+			this.openState(newVal)
+		},
+		leftOptions() {
+			this.init()
+		},
+		rightOptions() {
+			this.init()
+		}
+	},
+	mounted() {
+		// this.position = {}
+		if (this.swipeaction.children !== undefined) {
+			this.swipeaction.children.push(this)
+		}
+		this.init()
+	},
+	beforeDestoy() {
+		this.swipeaction.children.forEach((item, index) => {
+			if (item === this) {
+				this.swipeaction.children.splice(index, 1)
+			}
+		})
+	},
+	methods: {
+		init(){
+			clearTimeout(this.timer)
+			this.timer = setTimeout(() => {
+				this.getSelectorQuery()
+			}, 100)
+			// 移动距离
+			this.left = 0
+			this.x = 0
+		},
+		closeSwipe(e) {
+			if (!this.autoClose) return
+			this.swipeaction.closeOther(this)
+		},
+		appTouchStart(e) {
+			const {
+				clientX
+			} = e.changedTouches[0]
+			this.clientX = clientX
+			this.timestamp = new Date().getTime()
+		},
+		appTouchEnd(e, index, item, position) {
+			const {
+				clientX
+			} = e.changedTouches[0]
+			// fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题
+			let diff = Math.abs(this.clientX - clientX)
+			let time = (new Date().getTime()) - this.timestamp
+			if (diff < 40 && time < 300) {
+				this.$emit('click', {
+					content: item,
+					index,
+					position
+				})
+			}
+		},
+		touchstart(e) {
+			if (this.disabled) return
+			this.ani = false
+			this.x = this.left || 0
+			this.stopTouchStart(e)
+			this.autoClose && this.closeSwipe()
+		},
+		touchmove(e) {
+			if (this.disabled) return
+			// 是否可以滑动页面
+			this.stopTouchMove(e);
+			if (this.direction !== 'horizontal') {
+				return;
+			}
+
+			this.move(this.x + this.deltaX)
+		},
+		touchend() {
+			if (this.disabled) return
+			this.moveDirection(this.left)
+		},
+		/**
+		 * 设置移动距离
+		 * @param {Object} value
+		 */
+		move(value) {
+			value = value || 0
+			const leftWidth = this.leftWidth
+			const rightWidth = this.rightWidth
+			// 获取可滑动范围
+			this.left = this.range(value, -rightWidth, leftWidth);
+		},
+
+		/**
+		 * 获取范围
+		 * @param {Object} num
+		 * @param {Object} min
+		 * @param {Object} max
+		 */
+		range(num, min, max) {
+			return Math.min(Math.max(num, min), max);
+		},
+		/**
+		 * 移动方向判断
+		 * @param {Object} left
+		 * @param {Object} value
+		 */
+		moveDirection(left) {
+			const threshold = this.threshold
+			const isopen = this.isopen || 'none'
+			const leftWidth = this.leftWidth
+			const rightWidth = this.rightWidth
+			if (this.deltaX === 0) {
+				this.openState('none')
+				return
+			}
+			if ((isopen === 'none' && rightWidth > 0 && -left > threshold) || (isopen !== 'none' && rightWidth > 0 && rightWidth +
+					left < threshold)) {
+				// right
+				this.openState('right')
+			} else if ((isopen === 'none' && leftWidth > 0 && left > threshold) || (isopen !== 'none' && leftWidth > 0 &&
+					leftWidth - left < threshold)) {
+				// left
+				this.openState('left')
+			} else {
+				// default
+				this.openState('none')
+			}
+		},
+
+		/**
+		 * 开启状态
+		 * @param {Boolean} type
+		 */
+		openState(type) {
+			const leftWidth = this.leftWidth
+			const rightWidth = this.rightWidth
+			let left = ''
+			this.isopen = this.isopen ? this.isopen : 'none'
+			switch (type) {
+				case "left":
+					left = leftWidth
+					break
+				case "right":
+					left = -rightWidth
+					break
+				default:
+					left = 0
+			}
+
+
+			if (this.isopen !== type) {
+				this.throttle = true
+				this.$emit('change', type)
+			}
+
+			this.isopen = type
+			// 添加动画类
+			this.ani = true
+			this.$nextTick(() => {
+				this.move(left)
+			})
+			// 设置最终移动位置,理论上只要进入到这个函数,肯定是要打开的
+		},
+		close() {
+			this.openState('none')
+		},
+		getDirection(x, y) {
+			if (x > y && x > MIN_DISTANCE) {
+				return 'horizontal';
+			}
+			if (y > x && y > MIN_DISTANCE) {
+				return 'vertical';
+			}
+			return '';
+		},
+
+		/**
+		 * 重置滑动状态
+		 * @param {Object} event
+		 */
+		resetTouchStatus() {
+			this.direction = '';
+			this.deltaX = 0;
+			this.deltaY = 0;
+			this.offsetX = 0;
+			this.offsetY = 0;
+		},
+
+		/**
+		 * 设置滑动开始位置
+		 * @param {Object} event
+		 */
+		stopTouchStart(event) {
+			this.resetTouchStatus();
+			const touch = event.touches[0];
+			this.startX = touch.clientX;
+			this.startY = touch.clientY;
+		},
+
+		/**
+		 * 滑动中,是否禁止打开
+		 * @param {Object} event
+		 */
+		stopTouchMove(event) {
+			const touch = event.touches[0];
+			this.deltaX = touch.clientX - this.startX;
+			this.deltaY = touch.clientY - this.startY;
+			this.offsetX = Math.abs(this.deltaX);
+			this.offsetY = Math.abs(this.deltaY);
+			this.direction = this.direction || this.getDirection(this.offsetX, this.offsetY);
+		},
+
+		getSelectorQuery() {
+			const views = uni.createSelectorQuery().in(this)
+			views
+				.selectAll('.uni-swipe_button-group')
+				.boundingClientRect(data => {
+					let show = 'none'
+					if (this.autoClose) {
+						show = 'none'
+					} else {
+						show = this.show
+					}
+					this.leftWidth = data[0].width || 0
+					this.rightWidth = data[1].width || 0
+					this.buttonShow = show
+				})
+				.exec()
+		}
+	}
+}

+ 116 - 0
components/uni-swipe-action-item/mpwxs.js

@@ -0,0 +1,116 @@
+export default {
+	data() {
+		return {
+			position: [],
+			button: {},
+			btn: "[]"
+		}
+	},
+	// computed: {
+	// 	pos() {
+	// 		return JSON.stringify(this.position)
+	// 	},
+	// 	btn() {
+	// 		return JSON.stringify(this.button)
+	// 	}
+	// },
+	watch: {
+		button: {
+			handler(newVal) {
+				this.btn = JSON.stringify(newVal)
+			},
+			deep: true
+		},
+		show(newVal) {
+			if (this.autoClose) return
+			if (!this.button) {
+				this.init()
+				return
+			}
+			this.button.show = newVal
+		},
+		leftOptions() {
+			this.init()
+		},
+		rightOptions() {
+			this.init()
+		}
+	},
+	created() {
+		if (this.swipeaction.children !== undefined) {
+			this.swipeaction.children.push(this)
+		}
+	},
+	mounted() {
+		this.init()
+	},
+	beforeDestroy() {
+		this.swipeaction.children.forEach((item, index) => {
+			if (item === this) {
+				this.swipeaction.children.splice(index, 1)
+			}
+		})
+	},
+	methods: {
+		init() {
+			clearTimeout(this.swipetimer)
+			this.swipetimer = setTimeout(() => {
+				this.getButtonSize()
+			}, 50)
+		},
+		closeSwipe(e) {
+			if (!this.autoClose) return
+			this.swipeaction.closeOther(this)
+		},
+
+		change(e) {
+			this.$emit('change', e.open)
+			let show = this.button.show
+			if (show !== e.open) {
+				this.button.show = e.open
+			}
+
+		},
+
+		appTouchStart(e) {
+			const {
+				clientX
+			} = e.changedTouches[0]
+			this.clientX = clientX
+			this.timestamp = new Date().getTime()
+		},
+		appTouchEnd(e, index, item, position) {
+			const {
+				clientX
+			} = e.changedTouches[0]
+			// fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题
+			let diff = Math.abs(this.clientX - clientX)
+			let time = (new Date().getTime()) - this.timestamp
+			if (diff < 40 && time < 300) {
+				this.$emit('click', {
+					content: item,
+					index,
+					position
+				})
+			}
+		},
+		getButtonSize() {
+			const views = uni.createSelectorQuery().in(this)
+			views
+				.selectAll('.uni-swipe_button-group')
+				.boundingClientRect(data => {
+					let show = 'none'
+					if (this.autoClose) {
+						show = 'none'
+					} else {
+						show = this.show
+					}
+					this.button = {
+						data,
+						show
+					}
+				})
+				.exec()
+		}
+	}
+}

+ 365 - 0
components/uni-swipe-action-item/uni-swipe-action-item.vue

@@ -0,0 +1,365 @@
+<template>
+	<!-- 在微信小程序 app vue端 h5 使用wxs 实现-->
+	<!-- #ifdef APP-VUE || MP-WEIXIN || H5 -->
+	<view class="uni-swipe">
+		<view
+		    class="uni-swipe_box"
+		    :data-threshold="threshold"
+		    :data-disabled="disabled"
+		    :change:prop="swipe.sizeReady"
+		    :prop="btn"
+		    @touchstart="swipe.touchstart"
+		    @touchmove="swipe.touchmove"
+		    @touchend="swipe.touchend"
+		>
+			<!-- 在微信小程序 app vue端 h5 使用wxs 实现-->
+			<view class="uni-swipe_button-group button-group--left">
+				<slot name="left">
+					<view
+					    v-for="(item,index) in leftOptions"
+					    :data-button="btn"
+					    :key="index"
+					    :style="{
+					  backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD',
+					  fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'
+					}"
+					    class="uni-swipe_button button-hock"
+					    @touchstart="appTouchStart"
+					    @touchend="appTouchEnd($event,index,item,'left')"
+					><text
+						    class="uni-swipe_button-text"
+						    :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',}"
+						>{{ item.text }}</text></view>
+				</slot>
+			</view>
+			<slot></slot>
+			<view class="uni-swipe_button-group button-group--right">
+				<slot name="right">
+					<view
+					    v-for="(item,index) in rightOptions"
+					    :data-button="btn"
+					    :key="index"
+					    :style="{
+					  backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD',
+					  fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'
+					}"
+					    class="uni-swipe_button button-hock"
+					    @touchstart="appTouchStart"
+					    @touchend="appTouchEnd($event,index,item,'right')"
+					><text
+						    class="uni-swipe_button-text"
+						    :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',}"
+						>{{ item.text }}</text></view>
+				</slot>
+			</view>
+		</view>
+	</view>
+	<!-- #endif -->
+	<!-- app nvue端 使用 bindingx -->
+	<!-- #ifdef APP-NVUE -->
+	<view
+	    ref="selector-box--hock"
+	    class="uni-swipe"
+	    @horizontalpan="touchstart"
+	    @touchend="touchend"
+	>
+		<view
+		    ref='selector-left-button--hock'
+		    class="uni-swipe_button-group button-group--left"
+		>
+			<slot name="left">
+				<view
+				    v-for="(item,index) in leftOptions"
+				    :data-button="btn"
+				    :key="index"
+				    :style="{
+				  backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD',
+				  fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'
+				}"
+				    class="uni-swipe_button button-hock"
+				    @click.stop="onClick(index,item,'left')"
+				><text
+					    class="uni-swipe_button-text"
+					    :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',}"
+					>{{ item.text }}</text></view>
+			</slot>
+		</view>
+		<view
+		    ref='selector-right-button--hock'
+		    class="uni-swipe_button-group button-group--right"
+		>
+			<slot name="right">
+				<view
+				    v-for="(item,index) in rightOptions"
+				    :data-button="btn"
+				    :key="index"
+				    :style="{
+				  backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD',
+				  fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'
+				}"
+				    class="uni-swipe_button button-hock"
+				    @click.stop="onClick(index,item,'right')"
+				><text
+					    class="uni-swipe_button-text"
+					    :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',}"
+					>{{ item.text }}</text></view>
+			</slot>
+		</view>
+		<view
+		    ref='selector-content--hock'
+		    class="uni-swipe_box"
+		>
+			<slot></slot>
+		</view>
+	</view>
+	<!-- #endif -->
+	<!-- 其他平台使用 js ,长列表性能可能会有影响-->
+	<!-- #ifdef MP-ALIPAY || MP-BAIDU || MP-TOUTIAO || MP-QQ -->
+	<view class="uni-swipe">
+		<view
+		    class="uni-swipe_box"
+		    @touchstart="touchstart"
+		    @touchmove="touchmove"
+		    @touchend="touchend"
+		    :style="{transform:moveLeft}"
+		    :class="{ani:ani}"
+		>
+			<view class="uni-swipe_button-group button-group--left">
+				<slot name="left">
+					<view
+					    v-for="(item,index) in leftOptions"
+					    :data-button="btn"
+					    :key="index"
+					    :style="{
+					  backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD',
+					  fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'
+					}"
+					    class="uni-swipe_button button-hock"
+					    @touchstart="appTouchStart"
+					    @touchend="appTouchEnd($event,index,item,'left')"
+					><text
+						    class="uni-swipe_button-text"
+						    :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',}"
+						>{{ item.text }}</text></view>
+				</slot>
+			</view>
+			<slot></slot>
+			<view class="uni-swipe_button-group button-group--right">
+				<slot name="right">
+					<view
+					    v-for="(item,index) in rightOptions"
+					    :data-button="btn"
+					    :key="index"
+					    :style="{
+					  backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD',
+					  fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'
+					}"
+					    @touchstart="appTouchStart"
+					    @touchend="appTouchEnd($event,index,item,'right')"
+					    class="uni-swipe_button button-hock"
+					><text
+						    class="uni-swipe_button-text"
+						    :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',}"
+						>{{ item.text }}</text></view>
+				</slot>
+			</view>
+		</view>
+	</view>
+	<!-- #endif -->
+
+</template>
+<script
+    src="./index.wxs"
+    module="swipe"
+    lang="wxs"
+></script>
+<script>
+	// #ifdef APP-VUE|| MP-WEIXIN || H5
+	import mpwxs from './mpwxs'
+	// #endif
+
+	// #ifdef APP-NVUE
+	import bindingx from './bindingx.js'
+	// #endif
+
+	// #ifndef APP-PLUS|| MP-WEIXIN  ||  H5
+	import mixins from './mpother'
+	// #endif
+
+	/**
+	 * SwipeActionItem 滑动操作子组件
+	 * @description 通过滑动触发选项的容器
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=181
+	 * @property {Boolean} show = [left|right|none] 	开启关闭组件,auto-close = false 时生效
+	 * @property {Boolean} disabled = [true|false] 		是否禁止滑动
+	 * @property {Boolean} autoClose = [true|false] 	滑动打开当前组件,是否关闭其他组件
+	 * @property {Number}  threshold 					滑动缺省值
+	 * @property {Array} leftOptions 					左侧选项内容及样式
+	 * @property {Array} rgihtOptions 					右侧选项内容及样式
+	 * @event {Function} click 							点击选项按钮时触发事件,e = {content,index} ,content(点击内容)、index(下标)
+	 * @event {Function} change 						组件打开或关闭时触发,left\right\none
+	 */
+
+	export default {
+		// #ifdef APP-VUE|| MP-WEIXIN||H5
+		mixins: [mpwxs],
+		// #endif
+
+		// #ifdef APP-NVUE
+		mixins: [bindingx],
+		// #endif
+
+		// #ifndef APP-PLUS|| MP-WEIXIN ||  H5
+		mixins: [mixins],
+		// #endif
+
+		props: {
+			// 控制开关
+			show: {
+				type: String,
+				default: 'none'
+			},
+
+			// 禁用
+			disabled: {
+				type: Boolean,
+				default: false
+			},
+
+			// 是否自动关闭
+			autoClose: {
+				type: Boolean,
+				default: true
+			},
+
+			// 滑动缺省距离
+			threshold: {
+				type: Number,
+				default: 20
+			},
+
+			// 左侧按钮内容
+			leftOptions: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+
+			// 右侧按钮内容
+			rightOptions: {
+				type: Array,
+				default () {
+					return []
+				}
+			}
+
+		},
+		inject: ['swipeaction']
+	}
+</script>
+<style
+    lang="scss"
+    scoped
+>
+	.uni-swipe {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		overflow: hidden;
+		/* #endif */
+	}
+
+	.uni-swipe_box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		flex-shrink: 0;
+		/* #endif */
+		position: relative;
+	}
+
+	.uni-swipe_content {
+		// border: 1px red solid;
+	}
+
+	.uni-swipe_button-group {
+		/* #ifndef APP-NVUE */
+		box-sizing: border-box;
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		position: absolute;
+		top: 0;
+		bottom: 0;
+	}
+
+	.button-group--left {
+		left: 0;
+		transform: translateX(-100%)
+	}
+
+	.button-group--right {
+		right: 0;
+		transform: translateX(100%)
+	}
+
+	.uni-swipe_button {
+		/* #ifdef APP-NVUE */
+		flex: 1;
+		/* #endif */
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		padding: 0 20px;
+	}
+
+	.uni-swipe_button-text {
+		/* #ifndef APP-NVUE */
+		flex-shrink: 0;
+		/* #endif */
+		font-size: 14px;
+	}
+
+	.ani {
+		transition-property: transform;
+		transition-duration: 0.3s;
+		transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1);
+	}
+
+	/* #ifdef MP-ALIPAY */
+	.movable-area {
+		/* width: 100%; */
+		height: 45px;
+	}
+
+	.movable-view {
+		display: flex;
+		/* justify-content: center; */
+		position: relative;
+		flex: 1;
+		height: 45px;
+		z-index: 2;
+	}
+
+	.movable-view-button {
+		display: flex;
+		flex-shrink: 0;
+		flex-direction: row;
+		height: 100%;
+		background: #C0C0C0;
+	}
+
+	/* .transition {
+		transition: all 0.3s;
+	} */
+
+	.movable-view-box {
+		flex-shrink: 0;
+		height: 100%;
+		background-color: #fff;
+	}
+
+	/* #endif */
+</style>

+ 42 - 0
components/uni-swipe-action/uni-swipe-action.vue

@@ -0,0 +1,42 @@
+<template>
+	<view>
+		<slot></slot>
+	</view>
+</template>
+
+<script>
+	/**
+	 * SwipeAction 滑动操作
+	 * @description 通过滑动触发选项的容器
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=181
+	 */
+	export default {
+		data() {
+			return {};
+		},
+		provide() {
+			return {
+				swipeaction: this
+			};
+		},
+		created() {
+			this.children = [];
+		},
+		methods: {
+			closeOther(vm) {
+				if (this.openItem && this.openItem !== vm) {
+					// #ifdef APP-VUE || H5 || MP-WEIXIN
+					this.openItem.button.show = 'none'
+					// #endif
+
+					// #ifndef APP-VUE || H5 || MP-WEIXIN
+					this.openItem.close()
+					// #endif
+				}
+				this.openItem = vm
+			}
+		}
+	};
+</script>
+
+<style></style>

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

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

+ 5 - 0
config/CONST.js

@@ -0,0 +1,5 @@
+//分页定义
+export const paginationConfig = {
+	  pageNo: 1,
+	  pageSize: 10
+}

+ 34 - 0
config/config.js

@@ -0,0 +1,34 @@
+const config = {
+	//dev 
+	apiBaseurl:'http://forest.hw.hongweisoft.com/data/forest-portal',//测试
+	// apiBaseurl:'http://172.16.90.64:9098/forest-portal',//测试
+	// apiBaseurl:'http://172.16.90.50:9098/forest-portal',//邱波
+	//本地图片
+	// imgUrl:'http://'+window.location.host,
+	// 图片占位
+	placeImg:'http://placekitten.com',
+	
+	// loginUrl:'http://tanhui.hongweisoft.com/forest-portal/wechat/h5/authorize?returnUrl=/',//登录后跳转到测试链接
+	loginUrl:'http://forest.hw.hongweisoft.com/data/forest-portal/wechat/h5/authorize?returnUrl=/',//登录后跳转到测试链接
+	
+	// 密码授权令牌
+	// client_id:4,
+	// client_secret:'jO1XHQu0GytEdxJzNRFwcIeWmS57yCMBuA5P9yDo',
+
+	
+	
+	//online 线上地址
+	// apiBaseurl:'http://hd.phpim.cn/',//线上
+	
+	
+	//gloab 全局
+	imgUrl:"",
+}
+export {
+	config
+}
+
+
+
+
+

File diff suppressed because it is too large
+ 1 - 0
js_sdk/jweixin-1.4.0.js


+ 88 - 0
js_sdk/luch-request/luch-request/adapters/index.js

@@ -0,0 +1,88 @@
+import buildURL from '../helpers/buildURL'
+import buildFullPath from '../core/buildFullPath'
+import settle from '../core/settle'
+
+/**
+ * 返回可选值存在的配置
+ * @param {Array} keys - 可选值数组
+ * @param {Object} config2 - 配置
+ * @return {{}} - 存在的配置项
+ */
+const mergeKeys = (keys, config2) => {
+  let config = {}
+  keys.forEach(prop => {
+    if (typeof config2[prop] !== 'undefined') {
+      config[prop] = config2[prop]
+    }
+  })
+  return config
+}
+export default (config) => {
+  return new Promise((resolve, reject) => {
+    const _config = {
+      url: buildURL(buildFullPath(config.baseURL, config.url), config.params),
+      header: config.header,
+      complete: (response) => {
+        response.config = config
+        try {
+          // 对可能字符串不是json 的情况容错
+          if (typeof response.data === 'string') {
+            response.data = JSON.parse(response.data)
+          }
+          // eslint-disable-next-line no-empty
+        } catch (e) {
+        }
+        settle(resolve, reject, response)
+      }
+    }
+    let requestTask
+    if (config.method === 'UPLOAD') {
+      delete _config.header['content-type']
+      delete _config.header['Content-Type']
+      let otherConfig = {
+        // #ifdef MP-ALIPAY
+        fileType: config.fileType,
+        // #endif
+        filePath: config.filePath,
+        name: config.name
+      }
+      const optionalKeys = [
+        // #ifdef APP-PLUS || H5
+        'files',
+        // #endif
+        // #ifdef H5
+        'file',
+        // #endif
+        'formData'
+      ]
+      requestTask = uni.uploadFile({..._config, ...otherConfig, ...mergeKeys(optionalKeys, config)})
+    } else if (config.method === 'DOWNLOAD') {
+      requestTask = uni.downloadFile(_config)
+    } else {
+      const optionalKeys = [
+        'data',
+        'method',
+        // #ifdef MP-ALIPAY || MP-WEIXIN
+        'timeout',
+        // #endif
+        'dataType',
+        // #ifndef MP-ALIPAY || APP-PLUS
+        'responseType',
+        // #endif
+        // #ifdef APP-PLUS
+        'sslVerify',
+        // #endif
+        // #ifdef H5
+        'withCredentials',
+        // #endif
+        // #ifdef APP-PLUS
+        'firstIpv4',
+        // #endif
+      ]
+      requestTask = uni.request({..._config,...mergeKeys(optionalKeys, config)})
+    }
+    if (config.getTask) {
+      config.getTask(requestTask, config)
+    }
+  })
+}

+ 51 - 0
js_sdk/luch-request/luch-request/core/InterceptorManager.js

@@ -0,0 +1,51 @@
+'use strict'
+
+
+function InterceptorManager() {
+  this.handlers = []
+}
+
+/**
+ * Add a new interceptor to the stack
+ *
+ * @param {Function} fulfilled The function to handle `then` for a `Promise`
+ * @param {Function} rejected The function to handle `reject` for a `Promise`
+ *
+ * @return {Number} An ID used to remove interceptor later
+ */
+InterceptorManager.prototype.use = function use(fulfilled, rejected) {
+  this.handlers.push({
+    fulfilled: fulfilled,
+    rejected: rejected
+  })
+  return this.handlers.length - 1
+}
+
+/**
+ * Remove an interceptor from the stack
+ *
+ * @param {Number} id The ID that was returned by `use`
+ */
+InterceptorManager.prototype.eject = function eject(id) {
+  if (this.handlers[id]) {
+    this.handlers[id] = null
+  }
+}
+
+/**
+ * Iterate over all the registered interceptors
+ *
+ * This method is particularly useful for skipping over any
+ * interceptors that may have become `null` calling `eject`.
+ *
+ * @param {Function} fn The function to call for each interceptor
+ */
+InterceptorManager.prototype.forEach = function forEach(fn) {
+  this.handlers.forEach(h => {
+    if (h !== null) {
+      fn(h)
+    }
+  })
+}
+
+export default InterceptorManager

+ 199 - 0
js_sdk/luch-request/luch-request/core/Request.js

@@ -0,0 +1,199 @@
+/**
+ * @Class Request
+ * @description luch-request http请求插件
+ * @version 3.0.4
+ * @Author lu-ch
+ * @Date 2020-07-05
+ * @Email webwork.s@qq.com
+ * 文档: https://www.quanzhan.co/luch-request/
+ * github: https://github.com/lei-mu/luch-request
+ * DCloud: http://ext.dcloud.net.cn/plugin?id=392
+ * HBuilderX: beat-2.7.14 alpha-2.8.0
+ */
+
+
+import dispatchRequest from './dispatchRequest'
+import InterceptorManager from './InterceptorManager'
+import mergeConfig from './mergeConfig'
+import defaults from './defaults'
+import { isPlainObject } from '../utils'
+
+export default class Request {
+  /**
+   * @param {Object} arg - 全局配置
+   * @param {String} arg.baseURL - 全局根路径
+   * @param {Object} arg.header - 全局header
+   * @param {String} arg.method = [GET|POST|PUT|DELETE|CONNECT|HEAD|OPTIONS|TRACE] - 全局默认请求方式
+   * @param {String} arg.dataType = [json] - 全局默认的dataType
+   * @param {String} arg.responseType = [text|arraybuffer] - 全局默认的responseType。App和支付宝小程序不支持
+   * @param {Object} arg.custom - 全局默认的自定义参数
+   * @param {Number} arg.timeout - 全局默认的超时时间,单位 ms。默认30000。仅微信小程序(2.10.0)、支付宝小程序支持
+   * @param {Boolean} arg.sslVerify - 全局默认的是否验证 ssl 证书。默认true.仅App安卓端支持(HBuilderX 2.3.3+)
+   * @param {Boolean} arg.withCredentials - 全局默认的跨域请求时是否携带凭证(cookies)。默认false。仅H5支持(HBuilderX 2.6.15+)
+   * @param {Boolean} arg.firstIpv4 - 全DNS解析时优先使用ipv4。默认false。仅 App-Android 支持 (HBuilderX 2.8.0+)
+   * @param {Function(statusCode):Boolean} arg.validateStatus - 全局默认的自定义验证器。默认statusCode >= 200 && statusCode < 300
+   */
+  constructor(arg = {}) {
+    if (!isPlainObject(arg)) {
+      arg = {}
+      console.warn('设置全局参数必须接收一个Object')
+    }
+    this.config = {...defaults, ...arg}
+    this.interceptors = {
+      request: new InterceptorManager(),
+      response: new InterceptorManager()
+    }
+  }
+
+  /**
+   * @Function
+   * @param {Request~setConfigCallback} f - 设置全局默认配置
+   */
+  setConfig(f) {
+    this.config = f(this.config)
+  }
+
+  middleware(config) {
+    config = mergeConfig(this.config, config)
+    let chain = [dispatchRequest, undefined]
+    let promise = Promise.resolve(config)
+
+    this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
+      chain.unshift(interceptor.fulfilled, interceptor.rejected)
+    })
+
+    this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
+      chain.push(interceptor.fulfilled, interceptor.rejected)
+    })
+
+    while (chain.length) {
+      promise = promise.then(chain.shift(), chain.shift())
+    }
+
+    return promise
+  }
+
+  /**
+   * @Function
+   * @param {Object} config - 请求配置项
+   * @prop {String} options.url - 请求路径
+   * @prop {Object} options.data - 请求参数
+   * @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型
+   * @prop {Object} [options.dataType = config.dataType] - 如果设为 json,会尝试对返回的数据做一次 JSON.parse
+   * @prop {Object} [options.header = config.header] - 请求header
+   * @prop {Object} [options.method = config.method] - 请求方法
+   * @returns {Promise<unknown>}
+   */
+  request(config = {}) {
+    return this.middleware(config)
+  }
+
+  get(url, options = {}) {
+    return this.middleware({
+      url,
+      method: 'GET',
+      ...options
+    })
+  }
+
+  post(url, data, options = {}) {
+    return this.middleware({
+      url,
+      data,
+      method: 'POST',
+      ...options
+    })
+  }
+
+  // #ifndef MP-ALIPAY
+  put(url, data, options = {}) {
+    return this.middleware({
+      url,
+      data,
+      method: 'PUT',
+      ...options
+    })
+  }
+
+  // #endif
+
+  // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
+  delete(url, data, options = {}) {
+    return this.middleware({
+      url,
+      data,
+      method: 'DELETE',
+      ...options
+    })
+  }
+
+  // #endif
+
+  // #ifdef APP-PLUS || H5 || MP-WEIXIN
+  connect(url, data, options = {}) {
+    return this.middleware({
+      url,
+      data,
+      method: 'CONNECT',
+      ...options
+    })
+  }
+
+  // #endif
+
+  // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
+  head(url, data, options = {}) {
+    return this.middleware({
+      url,
+      data,
+      method: 'HEAD',
+      ...options
+    })
+  }
+
+  // #endif
+
+  // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
+  options(url, data, options = {}) {
+    return this.middleware({
+      url,
+      data,
+      method: 'OPTIONS',
+      ...options
+    })
+  }
+
+  // #endif
+
+  // #ifdef APP-PLUS || H5 || MP-WEIXIN
+  trace(url, data, options = {}) {
+    return this.middleware({
+      url,
+      data,
+      method: 'TRACE',
+      ...options
+    })
+  }
+
+  // #endif
+
+  upload(url, config = {}) {
+    config.url = url
+    config.method = 'UPLOAD'
+    return this.middleware(config)
+  }
+
+  download(url, config = {}) {
+    config.url = url
+    config.method = 'DOWNLOAD'
+    return this.middleware(config)
+  }
+}
+
+
+/**
+ * setConfig回调
+ * @return {Object} - 返回操作后的config
+ * @callback Request~setConfigCallback
+ * @param {Object} config - 全局默认config
+ */

+ 20 - 0
js_sdk/luch-request/luch-request/core/buildFullPath.js

@@ -0,0 +1,20 @@
+'use strict'
+
+import isAbsoluteURL from '../helpers/isAbsoluteURL'
+import combineURLs from '../helpers/combineURLs'
+
+/**
+ * Creates a new URL by combining the baseURL with the requestedURL,
+ * only when the requestedURL is not already an absolute URL.
+ * If the requestURL is absolute, this function returns the requestedURL untouched.
+ *
+ * @param {string} baseURL The base URL
+ * @param {string} requestedURL Absolute or relative URL to combine
+ * @returns {string} The combined full path
+ */
+export default function buildFullPath(baseURL, requestedURL) {
+  if (baseURL && !isAbsoluteURL(requestedURL)) {
+    return combineURLs(baseURL, requestedURL)
+  }
+  return requestedURL
+}

+ 30 - 0
js_sdk/luch-request/luch-request/core/defaults.js

@@ -0,0 +1,30 @@
+/**
+ * 默认的全局配置
+ */
+
+
+export default {
+  baseURL: '',
+  header: {},
+  method: 'GET',
+  dataType: 'json',
+  // #ifndef MP-ALIPAY || APP-PLUS
+  responseType: 'text',
+  // #endif
+  custom: {},
+  // #ifdef MP-ALIPAY || MP-WEIXIN
+  timeout: 30000,
+  // #endif
+  // #ifdef APP-PLUS
+  sslVerify: true,
+  // #endif
+  // #ifdef H5
+  withCredentials: false,
+  // #endif
+  // #ifdef APP-PLUS
+  firstIpv4: false,
+  // #endif
+  validateStatus: function validateStatus(status) {
+    return status >= 200 && status < 300
+  }
+}

+ 6 - 0
js_sdk/luch-request/luch-request/core/dispatchRequest.js

@@ -0,0 +1,6 @@
+import adapter from '../adapters/index'
+
+
+export default (config) => {
+  return adapter(config)
+}

+ 89 - 0
js_sdk/luch-request/luch-request/core/mergeConfig.js

@@ -0,0 +1,89 @@
+import {deepMerge} from '../utils'
+
+/**
+ * 合并局部配置优先的配置,如果局部有该配置项则用局部,如果全局有该配置项则用全局
+ * @param {Array} keys - 配置项
+ * @param {Object} globalsConfig - 当前的全局配置
+ * @param {Object} config2 - 局部配置
+ * @return {{}}
+ */
+const mergeKeys = (keys, globalsConfig, config2) => {
+  let config = {}
+  keys.forEach(prop => {
+    if (typeof config2[prop] !== 'undefined') {
+      config[prop] = config2[prop]
+    } else if (typeof globalsConfig[prop] !== 'undefined') {
+      config[prop] = globalsConfig[prop]
+    }
+  })
+  return config
+}
+/**
+ *
+ * @param globalsConfig - 当前实例的全局配置
+ * @param config2 - 当前的局部配置
+ * @return - 合并后的配置
+ */
+export default (globalsConfig, config2 = {}) => {
+  const method = config2.method || globalsConfig.method || 'GET'
+  let config = {
+    baseURL: globalsConfig.baseURL || '',
+    method: method,
+    url: config2.url || '',
+    params: config2.params || {},
+    custom: {...(globalsConfig.custom || {}), ...(config2.custom || {})},
+    header: deepMerge(globalsConfig.header || {}, config2.header || {})
+  }
+  const defaultToConfig2Keys = ['getTask', 'validateStatus']
+  config = {...config, ...mergeKeys(defaultToConfig2Keys, globalsConfig, config2)}
+
+  // eslint-disable-next-line no-empty
+  if (method === 'DOWNLOAD') {
+
+  } else if (method === 'UPLOAD') {
+    delete config.header['content-type']
+    delete config.header['Content-Type']
+    const uploadKeys = [
+      // #ifdef APP-PLUS || H5
+      'files',
+      // #endif
+      // #ifdef MP-ALIPAY
+      'fileType',
+      // #endif
+      // #ifdef H5
+      'file',
+      // #endif
+      'filePath',
+      'name',
+      'formData',
+    ]
+    uploadKeys.forEach(prop => {
+      if (typeof config2[prop] !== 'undefined') {
+        config[prop] = config2[prop]
+      }
+    })
+  } else {
+    const defaultsKeys = [
+      'data',
+      // #ifdef MP-ALIPAY || MP-WEIXIN
+      'timeout',
+      // #endif
+      'dataType',
+      // #ifndef MP-ALIPAY || APP-PLUS
+      'responseType',
+      // #endif
+      // #ifdef APP-PLUS
+      'sslVerify',
+      // #endif
+      // #ifdef H5
+      'withCredentials',
+      // #endif
+      // #ifdef APP-PLUS
+      'firstIpv4',
+      // #endif
+    ]
+    config = {...config, ...mergeKeys(defaultsKeys, globalsConfig, config2)}
+  }
+
+  return config
+}

+ 16 - 0
js_sdk/luch-request/luch-request/core/settle.js

@@ -0,0 +1,16 @@
+/**
+ * Resolve or reject a Promise based on response status.
+ *
+ * @param {Function} resolve A function that resolves the promise.
+ * @param {Function} reject A function that rejects the promise.
+ * @param {object} response The response.
+ */
+export default function settle(resolve, reject, response) {
+  const validateStatus = response.config.validateStatus
+  const status = response.statusCode
+  if (status && (!validateStatus || validateStatus(status))) {
+    resolve(response)
+  } else {
+    reject(response)
+  }
+}

+ 69 - 0
js_sdk/luch-request/luch-request/helpers/buildURL.js

@@ -0,0 +1,69 @@
+'use strict'
+
+import * as utils from './../utils'
+
+function encode(val) {
+  return encodeURIComponent(val).
+    replace(/%40/gi, '@').
+    replace(/%3A/gi, ':').
+    replace(/%24/g, '$').
+    replace(/%2C/gi, ',').
+    replace(/%20/g, '+').
+    replace(/%5B/gi, '[').
+    replace(/%5D/gi, ']')
+}
+
+/**
+ * Build a URL by appending params to the end
+ *
+ * @param {string} url The base of the url (e.g., http://www.google.com)
+ * @param {object} [params] The params to be appended
+ * @returns {string} The formatted url
+ */
+export default function buildURL(url, params) {
+  /*eslint no-param-reassign:0*/
+  if (!params) {
+    return url
+  }
+
+  var serializedParams
+  if (utils.isURLSearchParams(params)) {
+    serializedParams = params.toString()
+  } else {
+    var parts = []
+
+    utils.forEach(params, function serialize(val, key) {
+      if (val === null || typeof val === 'undefined') {
+        return
+      }
+
+      if (utils.isArray(val)) {
+        key = key + '[]'
+      } else {
+        val = [val]
+      }
+
+      utils.forEach(val, function parseValue(v) {
+        if (utils.isDate(v)) {
+          v = v.toISOString()
+        } else if (utils.isObject(v)) {
+          v = JSON.stringify(v)
+        }
+        parts.push(encode(key) + '=' + encode(v))
+      })
+    })
+
+    serializedParams = parts.join('&')
+  }
+
+  if (serializedParams) {
+    var hashmarkIndex = url.indexOf('#')
+    if (hashmarkIndex !== -1) {
+      url = url.slice(0, hashmarkIndex)
+    }
+
+    url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams
+  }
+
+  return url
+}

+ 14 - 0
js_sdk/luch-request/luch-request/helpers/combineURLs.js

@@ -0,0 +1,14 @@
+'use strict'
+
+/**
+ * Creates a new URL by combining the specified URLs
+ *
+ * @param {string} baseURL The base URL
+ * @param {string} relativeURL The relative URL
+ * @returns {string} The combined URL
+ */
+export default function combineURLs(baseURL, relativeURL) {
+  return relativeURL
+    ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '')
+    : baseURL
+}

+ 14 - 0
js_sdk/luch-request/luch-request/helpers/isAbsoluteURL.js

@@ -0,0 +1,14 @@
+'use strict'
+
+/**
+ * Determines whether the specified URL is absolute
+ *
+ * @param {string} url The URL to test
+ * @returns {boolean} True if the specified URL is absolute, otherwise false
+ */
+export default function isAbsoluteURL(url) {
+  // A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL).
+  // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
+  // by any combination of letters, digits, plus, period, or hyphen.
+  return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url)
+}

+ 2 - 0
js_sdk/luch-request/luch-request/index.js

@@ -0,0 +1,2 @@
+import Request from './core/Request'
+export default Request

+ 131 - 0
js_sdk/luch-request/luch-request/utils.js

@@ -0,0 +1,131 @@
+'use strict'
+
+// utils is a library of generic helper functions non-specific to axios
+
+var toString = Object.prototype.toString
+
+/**
+ * Determine if a value is an Array
+ *
+ * @param {Object} val The value to test
+ * @returns {boolean} True if value is an Array, otherwise false
+ */
+export function isArray (val) {
+  return toString.call(val) === '[object Array]'
+}
+
+
+/**
+ * Determine if a value is an Object
+ *
+ * @param {Object} val The value to test
+ * @returns {boolean} True if value is an Object, otherwise false
+ */
+export function isObject (val) {
+  return val !== null && typeof val === 'object'
+}
+
+/**
+ * Determine if a value is a Date
+ *
+ * @param {Object} val The value to test
+ * @returns {boolean} True if value is a Date, otherwise false
+ */
+export function isDate (val) {
+  return toString.call(val) === '[object Date]'
+}
+
+/**
+ * Determine if a value is a URLSearchParams object
+ *
+ * @param {Object} val The value to test
+ * @returns {boolean} True if value is a URLSearchParams object, otherwise false
+ */
+export function isURLSearchParams (val) {
+  return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams
+}
+
+
+/**
+ * Iterate over an Array or an Object invoking a function for each item.
+ *
+ * If `obj` is an Array callback will be called passing
+ * the value, index, and complete array for each item.
+ *
+ * If 'obj' is an Object callback will be called passing
+ * the value, key, and complete object for each property.
+ *
+ * @param {Object|Array} obj The object to iterate
+ * @param {Function} fn The callback to invoke for each item
+ */
+export function forEach (obj, fn) {
+  // Don't bother if no value provided
+  if (obj === null || typeof obj === 'undefined') {
+    return
+  }
+
+  // Force an array if not already something iterable
+  if (typeof obj !== 'object') {
+    /*eslint no-param-reassign:0*/
+    obj = [obj]
+  }
+
+  if (isArray(obj)) {
+    // Iterate over array values
+    for (var i = 0, l = obj.length; i < l; i++) {
+      fn.call(null, obj[i], i, obj)
+    }
+  } else {
+    // Iterate over object keys
+    for (var key in obj) {
+      if (Object.prototype.hasOwnProperty.call(obj, key)) {
+        fn.call(null, obj[key], key, obj)
+      }
+    }
+  }
+}
+
+/**
+ * 是否为boolean 值
+ * @param val
+ * @returns {boolean}
+ */
+export function isBoolean(val) {
+  return typeof val === 'boolean'
+}
+
+/**
+ * 是否为真正的对象{} new Object
+ * @param {any} obj - 检测的对象
+ * @returns {boolean}
+ */
+export function isPlainObject(obj) {
+  return Object.prototype.toString.call(obj) === '[object Object]'
+}
+
+
+
+/**
+ * Function equal to merge with the difference being that no reference
+ * to original objects is kept.
+ *
+ * @see merge
+ * @param {Object} obj1 Object to merge
+ * @returns {Object} Result of all merge properties
+ */
+export function deepMerge(/* obj1, obj2, obj3, ... */) {
+  let result = {}
+  function assignValue(val, key) {
+    if (typeof result[key] === 'object' && typeof val === 'object') {
+      result[key] = deepMerge(result[key], val)
+    } else if (typeof val === 'object') {
+      result[key] = deepMerge({}, val)
+    } else {
+      result[key] = val
+    }
+  }
+  for (let i = 0, l = arguments.length; i < l; i++) {
+    forEach(arguments[i], assignValue)
+  }
+  return result
+}

+ 44 - 0
main.js

@@ -0,0 +1,44 @@
+import Vue from 'vue'
+import App from './App'
+import { config } from './config/config'
+import store from './store' 
+import Request from '@/js_sdk/luch-request/luch-request/index.js';
+const http = new Request();
+import './utils/filter' 
+
+Vue.config.productionTip = false
+
+//栏目标题设置
+const setNavbarTitle = (title,defaultTitle) => {
+	uni.setNavigationBarTitle({
+		title:title || defaultTitle
+	})
+}
+
+//封装全局登录检查函数:backpage为登录后返回的页面;backtype为打开页面的类型[1 : redirectTo 2 : switchTab]
+//3种页面跳转方式:NavigationTo(直接打开新页面),RedirectTo(覆盖原页面后打开新页面),SwitchTo(切换顶部导航的方式来切换页面)
+Vue.prototype.checkLogin = function(backpage, backtype){
+    var TOKEN  = uni.getStorageSync('token');//本地持久化存储
+	var TOKENHEAD = uni.getStorageSync('tokenhead');
+	var USERINFO = uni.getStorageSync('userInfo');
+    if(TOKEN == '' || TOKENHEAD == '' || USERINFO == ''){
+        uni.redirectTo({url:'/pages/login/login?backpage='+backpage+'&backtype='+backtype});
+        return false;
+    }
+    return [TOKEN,TOKENHEAD,USERINFO];//已经登录返回数组TOKEN等用户信息
+}
+
+import $wxApi from "./wxapi.js";
+Vue.prototype.$wxApi = $wxApi;
+
+Vue.prototype.$api = {http,setNavbarTitle}
+Vue.prototype.$getimg = config.imgUrl
+Vue.prototype.$placeImg = config.placeImg
+Vue.prototype.$store = store
+Vue.prototype.config = config
+App.mpType = 'app'
+
+const app = new Vue({
+    ...App
+})
+app.$mount()

+ 95 - 0
manifest.json

@@ -0,0 +1,95 @@
+{
+    "name" : "forest",
+    "appid" : "__UNI__8AE3764",
+    "description" : "",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        /* 模块配置 */
+        "modules" : {},
+        /* 应用发布信息 */
+        "distribute" : {
+            /* android打包配置 */
+            "android" : {
+                "permissions" : [
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ]
+            },
+            /* ios打包配置 */
+            "ios" : {},
+            /* SDK配置 */
+            "sdkConfigs" : {}
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "",
+        "setting" : {
+            "urlCheck" : false
+        },
+        "usingComponents" : true
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    },
+    "h5" : {
+        "router" : {
+            "mode" : "hash",
+            "base" : "./"
+        },
+        "devServer" : {
+            "https" : false
+        },
+        "title" : "贵州林产品信息发布平台",
+        "sdkConfigs" : {
+            "maps" : {
+                "qqmap" : {
+                    "key" : ""
+                }
+            }
+        }
+    }
+}

+ 150 - 0
pages.json

@@ -0,0 +1,150 @@
+{
+	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+		{
+			"path": "pages/index/index",
+			"style": {
+				"navigationBarTitleText": "首页",
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path": "pages/login/login",
+			"style": {
+				"navigationBarTitleText": "贵州林产品-登录"
+			}
+		},
+		{
+			"path": "pages/publish/publish",
+			"style": {
+				"navigationBarTitleText": "贵州林产品-发布商品"
+			}
+		},
+		{
+			"path": "pages/publish/chosetype/chosetype",
+			"style": {
+				"navigationBarTitleText": "贵州林产品-我要发布",
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path": "pages/publish/publishtype/publishtype",
+			"style": {
+				"navigationBarTitleText": "贵州林产品-我要发布"
+			}
+		},
+		{
+			"path": "pages/publish/publishsuccess/publishsuccess",
+			"style": {
+				"navigationBarTitleText": "等待处理"
+			}
+		},
+		{
+			"path": "pages/producttype/producttype",
+			"style": {
+				"navigationBarTitleText": "贵州林产品-产品分类"
+			}
+		},
+		{
+			"path": "pages/productcategory/productcategory",
+			"style": {
+				"navigationBarTitleText": "贵州林产品-产品类别"
+			}
+		},
+		{
+			"path": "pages/searchresults/searchresults",
+			"style": {
+				"navigationBarTitleText": "贵州林产品-搜索结果"
+			}
+		},
+		{
+			"path": "pages/product/product",
+			"style": {
+				"navigationBarTitleText": "贵州林产品-产品详情"
+			}
+		},
+		{
+			"path": "pages/usercenter/usercenter",
+			"style": {
+				"navigationBarTitleText": "我的",
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path": "pages/usercenter/myfocus/myfocus",
+			"style": {
+				"navigationBarTitleText": "我有意向的"
+			}
+		},
+		{
+			"path": "pages/usercenter/focusme/focusme",
+			"style": {
+				"navigationBarTitleText": "有意向我的"
+			}
+		},
+		{
+			"path": "pages/usercenter/mypublish/mypublish",
+			"style": {
+				"navigationBarTitleText": "我的信息发布"
+			}
+		},
+		{
+			"path": "pages/usercenter/authentication/authentication",
+			"style": {
+				"navigationBarTitleText": "贵州林产品-企业认证"
+			}
+		},
+		{
+			"path": "pages/usercenter/feedback/feedback",
+			"style": {
+				"navigationBarTitleText": "贵州林产品-意见反馈"
+			}
+		},
+		{
+			"path": "pages/inbuild/inbuild",
+			"style": {
+				"navigationBarTitleText": "正在建设"
+			}
+		},
+		{
+			"path": "pages/supplier/supplier",
+			"style": {
+				"navigationBarTitleText": "供应商"
+			}
+		},
+		{
+			"path": "pages/supplier/supplierdetail/supplierdetail",
+			"style": {
+				"navigationBarTitleText": "供应商详情"
+			}
+		}
+	],
+	"tabBar": {
+	    "color": "#7A7E83",
+	    "selectedColor": "#3cc51f",
+	    "borderStyle": "black",
+	    "backgroundColor": "#ffffff",
+	    "list": [{
+	        "pagePath": "pages/index/index",
+	        "iconPath": "static/img/icon_home.png",
+			"selectedIconPath":"static/img/icon_home-active.png",
+	        "text": "首页"
+	    },{
+	        "pagePath": "pages/publish/chosetype/chosetype",
+	        "iconPath": "static/img/icon_publish.png",
+			"selectedIconPath":"static/img/icon_publish_active.png",
+	        "text": "我要发布"
+	    },{
+	        "pagePath": "pages/usercenter/usercenter",
+	        "iconPath": "static/img/icon_user.png",
+			"selectedIconPath":"static/img/icon_user-active.png",
+	        "text": "我的"
+	    }]
+	},
+	"globalStyle": {
+		// "navigationStyle":"custom",
+		"navigationBarTextStyle": "black",
+		"navigationBarTitleText": "贵州林产品",
+		"navigationBarBackgroundColor": "#F8F8F8",
+		"backgroundColor": "#F8F8F8"
+	}
+}

+ 41 - 0
pages/inbuild/inbuild.vue

@@ -0,0 +1,41 @@
+<template>
+	<view class="pages">
+		<view class="inbuild-img-wrap">
+			<image class="inbuild-img" src="/static/img/inbuild.png" mode=""></image>
+		</view>
+		<view class="inbuild-text">功能还在建设中</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				params:{
+					token:'',
+				}
+				
+			}
+		},
+		onShow() {
+			let serf = this;
+			//检查登录,获取token
+			let loginRes = this.checkLogin('/pages/index/index', '2');
+			if(!loginRes){return false;}
+			serf.params.token=loginRes[0];			
+		},
+		onLoad() {
+
+		},
+		methods: {
+
+		}
+	}
+</script>
+
+<style scoped>
+/* @import url("./inbuild.css"); */
+.inbuild-img-wrap{margin: 184rpx 0 77rpx;text-align: center;}
+.inbuild-img{width: 562rpx;height: 592rpx;}
+.inbuild-text{font-size: 32rpx;font-weight: 600;line-height: 45rpx;text-align: center;}
+</style>

+ 63 - 0
pages/index/index.css

@@ -0,0 +1,63 @@
+/* 搜索修改 */
+.search-wrap{padding: 10rpx 8rpx;background-color: #fff;}
+.index >>> .uni-searchbar__box{justify-content: left;border: 0;background-color: #F1F1F2!important;}
+
+.appAdv{height:300rpx;}
+.appAdv .adv-item .pic{width: 100%;}
+.appAdv .adv-item{border-radius: 24rpx;overflow: hidden;transform: scale(0.9);transition: all .3s;}
+.appAdv .adv-item.active{transform: scale(1);}
+
+/* 快速导航 */
+.quick-links{display: flex;flex-wrap: wrap;padding: 0;justify-content: space-between;}
+.quick-links-item{width: 48%;margin-bottom: 24rpx;position: relative;}
+.quick-links .quick-links-item:nth-child(1){color: #4A90E2;}
+.quick-links .quick-links-item:nth-child(2){color: #397B53;}
+.quick-links .quick-links-item:nth-child(3){color: #9013FE;}
+.quick-links .quick-links-item:nth-child(4){color: #F5A623;}
+.quick-links-item-til{position: absolute;left: 16rpx;top: 16rpx;z-index: 10;font-size: 24rpx;font-weight: bold;}
+.quick-links-item-subtil{position: absolute;left: 16rpx;top: 53rpx;z-index: 10;font-size: 16rpx;}
+.quick-links-item-img{width: 100%;height: 160rpx;overflow: hidden;border-radius: 10rpx;}
+.quick-links-item-img image{width: 100%;height: 100%;}
+
+/* 分类链接 */
+.category{display: flex;align-items: end;height: 88rpx;line-height: 88rpx;background-color: #fff;}
+.category .category-item{position: relative;height: 100%;text-align: center;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;font-size: 32rpx;color: #666;}
+.category .category-item.active{font-size: 36rpx;color: #6BBC6D;}
+.category .category-item.active::after{content: '';width: 100%;height: 6rpx;background-color: #6BBC6D;border-radius: 3rpx;position: absolute;left: 0;bottom: 0;}
+.category .category-item-more{color: #6BBC6D;width: 100rpx;padding-right: 10rpx;margin-right: 10rpx;}
+.category .category-item-more::after{content: '';width: 20rpx;height: 20rpx;position: absolute;right: 10rpx;top: 50%;margin-top: -10rpx;border-top: 1px solid #6BBC6D;border-right: 1px solid #6BBC6D;transform: rotate(45deg);}
+.category .category-item-all{margin-left: 40rpx;}
+.category-scroll-view{flex: 1;overflow: hidden;white-space: nowrap;padding-left: 23rpx;}
+.category-scroll-view .category-item{display: inline-block;margin: 0 23rpx;}
+
+/* 瀑布流 */
+/* .waterfall{column-count: 2;column-gap: 1vw;padding-top: 1vw;}
+.waterfall .item-wrap{width: 100%;overflow: hidden;border-radius: 24rpx;break-inside: avoid;margin-bottom: 24rpx;}
+.waterfall .item-img{width: 100%;height: auto;}
+.waterfall .item-til{margin-bottom: 10rpx;font-size: 28rpx;padding: 0 20rpx;}
+.waterfall .item-info{font-size: 24rpx;padding: 0 20rpx;}
+.waterfall .item-info .price{color: #f60;margin-right: 5rpx;}
+.waterfall .item-info .addr{color: #666;} */
+
+.indexproduct{display: flex;flex-wrap: wrap;}
+.indexproduct .item-wrap{width: 50%;margin-bottom: 22rpx;}
+.indexproduct .item-wrap .item{box-shadow: 0px 0px 14px -4px rgba(0, 0, 0, 0.3);border-radius: 18rpx;}
+.indexproduct .item-wrap:nth-child(odd) .item{margin-right: 11rpx;}
+.indexproduct .item-wrap:nth-child(even) .item{margin-left: 11rpx;}
+.indexproduct .item-img-wrap{overflow: hidden;width: 100%;height: 322rpx;margin-bottom: 15rpx;border-top-left-radius: 18rpx;border-top-right-radius: 18rpx;}
+.indexproduct .item-img{width: 100%;height: 100%;}
+.indexproduct .item-text-wrap{padding:  0 24rpx 25rpx;padding-right: 0;box-sizing: border-box;height: 192rpx;}
+.indexproduct .merchant{line-height: 33rpx;margin-bottom: 5rpx;color: #6BBC6D;}
+.indexproduct .merchant .postage{height: 24rpx;padding: 0 14rpx;margin-left: 16rpx;line-height: 24rpx;display: inline-block;font-size: 20rpx;background-color: #E1F2E2;border-radius: 12rpx;}
+.indexproduct .item-til{font-size: 32rpx;font-weight: 500;color: #333;line-height: 45rpx;margin-bottom: 5rpx;}
+.indexproduct .item-addr{font-size: 24rpx;font-weight: 400;color: #666;line-height: 33rpx;margin-bottom: 5rpx;}
+.indexproduct .item-price-info .rmb{font-size: 28rpx;font-weight: 500;color: #FF5030;line-height: 40rpx;}
+.indexproduct .item-price-info .price{font-size: 36rpx;font-weight: 500;color: #FF5030;line-height: 50rpx;}
+.indexproduct .item-price-info .unit{font-size: 24rpx;font-weight: 400;color: #999999;line-height: 33rpx;}
+
+
+
+
+
+
+

+ 356 - 0
pages/index/index.vue

@@ -0,0 +1,356 @@
+<template>
+	<view class="pages index">
+		<view class="search-wrap">
+			<view class="search">
+				<view class="searchTxt">
+					<!-- <uni-search-bar @confirm="search" @input="search" ></uni-search-bar> -->
+					<uni-search-bar @confirm="search" ></uni-search-bar>
+				</view>
+			</view>	
+		</view>
+		<!-- 搜索 结束 -->
+		<view class="appAdv nomal-bottom">
+			<swiper class="swiper" @change="swiperchange" :indicator-dots="swiper.indicatorDots" :previous-margin="swiper.previousmargin" :next-margin="swiper.nextmargin" :autoplay="swiper.autoplay" :interval="swiper.interval" :duration="swiper.duration">
+				<swiper-item v-for="(item,index) in advertiseList" :key="index">
+					<view class="adv-item" :class="{active:index==currentswiper}" @click="jumLink(item.url)">
+						<image :src="$getimg+item.pic|miniImg(75)+'/thumbnail/600x300'" class="pic" mode="scaleToFill"></image>
+					</view>
+				</swiper-item>
+			</swiper>
+		</view>
+		<!-- 轮播结束 -->
+		<view class="quick-links wrap write-radius nomal-bottom">
+			<navigator class="quick-links-item" url="/pages/supplier/supplier">
+				<view class="quick-links-item-til">供应商</view>
+				<view class="quick-links-item-subtil">保供稳价</view>
+				<view class="quick-links-item-img"><image src="/static/img/quick-links-04.png" mode="scaleToFill"></image></view>				
+			</navigator>
+			
+			<navigator class="quick-links-item" url="/pages/inbuild/inbuild">
+				<view class="quick-links-item-til">产品溯源</view>
+				<view class="quick-links-item-subtil">低价货源</view>
+				<view class="quick-links-item-img"><image src="/static/img/quick-links-02.png" mode="scaleToFill"></image></view>				
+			</navigator>
+			
+			<navigator class="quick-links-item" url="/pages/inbuild/inbuild">
+				<view class="quick-links-item-til">基地展示</view>
+				<view class="quick-links-item-subtil">轻松卖货</view>
+				<view class="quick-links-item-img"><image src="/static/img/quick-links-03.png" mode="scaleToFill"></image></view>				
+			</navigator>
+			
+			<navigator class="quick-links-item" url="/pages/inbuild/inbuild">
+				<view class="quick-links-item-til">价格行情</view>
+				<view class="quick-links-item-subtil">全国走货</view>
+				<view class="quick-links-item-img"><image src="/static/img/quick-links-01.png" mode="scaleToFill"></image></view>				
+			</navigator>
+		</view>
+		<!-- 快速导航 结束 -->
+		<view class="category nomal-bottom">
+			<view class="category-item category-item-all" @click="category()" :class="{active:categoryall==true}">全部</view>
+			<scroll-view scroll-x="true" class="scroll-X category-scroll-view">
+				<view v-for="(item,index) in categorylink" :key="index" @click="categoryclick(index)" :class="{active:index==ins && notall==true}" class="category-item">{{item.name}}</view>
+			</scroll-view>			
+			<navigator class="category-item category-item-more" url="/pages/productcategory/productcategory">更多</navigator>
+			<!-- <navigator v-for="(item,index) in categorylink" :key="index" class="category-item" :url="'pages/searchresults/searchresults?type=' + item">{{item}}</navigator> -->
+		</view>
+		<!-- 分类链接结束 -->	
+		<mescroll-body class="wrap" ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption" :up="upOption">
+			<view class="indexproduct">
+				 <view class="item-wrap" v-for="(item,index) in mescrollList" :key="index">
+					<view class="item" :data-id="item.id" @click="jumpDetails(item.id)">
+						<view class="item-img-wrap">
+							<image :src="$getimg+item.pic|miniImg(60)+'/thumbnail/358x354'||'/static/img/inbuild.png'" class="item-img" mode="aspectFit"></image>
+						</view>
+						<view class="item-text-wrap">
+							<view class="merchant f-ellipsis">
+								{{item.brandName}}
+								<!-- <text v-if="item.postage" class="postage">包邮</text> -->
+							</view>
+							<view class="item-til f-ellipsis">{{item.name}}</view>							
+							<view class="item-addr f-ellipsis">{{item.umsCompanyInfo}}</view>
+							<view class="item-price-info"><text class="rmb">¥</text><text class="price">{{item.price}}</text><text class="unit" v-if="item.unit">/{{item.unit}}</text></view>
+						</view>						
+					</view> 
+				 </view>
+			 	
+			</view>			 
+		</mescroll-body>
+		<!-- 商品展示结束 -->
+		
+		
+		
+	</view>
+</template>
+
+<script>
+	// #ifdef H5
+	import { mapMutations } from 'vuex';
+	// #endif
+	import uniSearchBar from '@/components/uni-search-bar/uni-search-bar.vue'
+	// 引入mescroll-mixins.js
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";	
+	// 引入mescroll-body组件 (如已在main.js注册全局组件,则省略此步骤)
+	import MescrollBody from "@/components/mescroll-uni/mescroll-body.vue"; // 注意.vue后缀不能省
+	export default {
+		mixins: [MescrollMixin], // 使用mixin
+		components: {
+			MescrollBody,
+			uniSearchBar,
+		},
+		data() {
+			return {
+				params:{
+					token:'',
+					tokenhead:'',
+					keyword:'',
+					productCategoryId:'',
+					// productCategoryName:'',
+				},
+				//轮播数据
+				advertiseList:[],
+				swiper: {
+					indicatorDots: true,
+					autoplay: false,
+					interval: 9000,
+					duration: 500,
+					previousmargin:'55rpx',
+					nextmargin:'55rpx'
+				},
+				currentswiper:0,
+				categorylink:[],
+				mescroll: null, // mescroll实例对象 (此行可删,mixins已默认)
+				// 下拉刷新的配置(可选, 绝大部分情况无需配置)
+				downOption: { 
+					// ...
+				},
+				// 上拉加载的配置(可选, 绝大部分情况无需配置)
+				upOption: {
+					page: {
+						size: 10 // 每页数据的数量,默认10
+					},
+					noMoreSize: 5, // 配置列表的总数量要大于等于5条才显示'-- END --'的提示
+					empty: {
+						tip: '暂无相关数据'
+					}
+				},
+				// 列表数据
+				mescrollList:[],
+				//category active
+				ins:null,
+				//category all
+				categoryall:true,
+				notall:false,
+				
+			}
+		},
+		onLoad() {
+			let serf = this;
+			//检查登录,获取token
+			// let loginRes = this.checkLogin('/pages/index/index', '2');
+			// if(!loginRes){return false;}
+			// serf.params.token=loginRes[0];
+			let locationLocaturl = window.location.search;
+			function getUrlParams(url) {
+				if (decodeURIComponent(url).indexOf('?') > -1) {
+					var result = [];
+					var urlParamsArr = decodeURIComponent(url).split('?');
+					urlParamsArr.shift();
+					let newUrlParamsArr = urlParamsArr.join('&').split('&');
+					for (var i = 0; i < newUrlParamsArr.length; i++) {
+						var paramKey = newUrlParamsArr[i].split('=')[0];
+						var paramValue = newUrlParamsArr[i].split('=')[1];
+						result.push({
+							key: paramKey,
+							value: paramValue
+						});
+					}
+					return result;
+				} else {
+					console.log('该URL中不含参数');
+				}
+			};
+			
+			let locationArr = getUrlParams(locationLocaturl);
+			if(!locationArr){
+				window.location.replace(this.config.loginUrl);
+				return
+			};
+			let itemtoken = locationArr.find(function(locationArr) {
+				return locationArr.key === 'token';
+			});
+			let itemtokenHead = locationArr.find(function(locationArr) {
+				return locationArr.key === 'tokenHead';
+			});
+			if (itemtoken) {
+				this.params.token = itemtoken.value;				
+			}
+			if (itemtokenHead) {
+				this.params.tokenhead = itemtokenHead.value;				
+			}
+			if(this.params.tokenhead&&this.params.token){
+				let thetoken = this.params.tokenhead+this.params.token;
+				this.$api.http.get(this.config.apiBaseurl+'/wechat/h5/info', {header: {Authorization:thetoken}}).then(res => {					
+					let userInfo = res.data;
+					userInfo.accessToken = this.params.token;
+					userInfo.tokenhead = this.params.tokenhead;
+					this.setLogin(userInfo);
+				}).catch(err => {
+					console.log(err)
+				});
+			};
+			//首页内容页信息展示
+			this.$api.http.get(this.config.apiBaseurl+'/home/content', {header: {Authorization:this.params.tokenhead+this.params.token}}).then(res => {
+				console.log('content',res)
+				this.advertiseList = res.data.data.advertiseList;
+			}).catch(err => {
+				console.log(err)
+			});
+			//获取产品分类
+			this.$api.http.get(this.config.apiBaseurl+'/product/categorySearch', {header: {Authorization:this.params.tokenhead+this.params.token}}).then(res => {
+				console.log('产品分类',res)
+				this.categorylink = res.data.data;
+			}).catch(err => {
+				console.log(err)
+			});
+		
+		},
+		onShow() {
+			
+			
+			
+		},		
+		methods: {
+			// #ifdef H5
+			...mapMutations(['setLogin']),
+			// #endif
+			
+			//搜索
+			search(e) {
+				console.log(e);
+				// url:`/pages/searchresults/searchresults?id=${e}`,
+				uni.navigateTo({					
+					url:`/pages/searchresults/searchresults?keyword=${e.value}`
+				})
+			},
+			
+			/*mescroll组件初始化的回调,可获取到mescroll对象 (此处可删,mixins已默认)*/
+			mescrollInit(mescroll) {
+				this.mescroll = mescroll;
+			},
+			/*下拉刷新的回调, 有三种处理方式:*/
+			downCallback(){
+			
+				// 第2种: 下拉刷新和上拉加载调同样的接口, 那么不用第1种方式, 直接mescroll.resetUpScroll()即可
+				this.mescroll.resetUpScroll(); // 重置列表为第一页 (自动执行 page.num=1, 再触发upCallback方法 )
+			},
+			/*上拉加载的回调*/
+			upCallback(page) {
+				let pageNum = page.num; // 页码, 默认从1开始
+				let pageSize = page.size; // 页长, 默认每页10条
+				this.params = Object.assign(this.params,{pageNum:pageNum,pageSize:pageSize});
+				this.$api.http.get(this.config.apiBaseurl+'/product/search',{params: this.params}).then(data => {
+					if(data.data.code=='1001'){
+						// uni.redirectTo({
+						// 	url:'/pages/login/login?backpage=/pages/index/index'+'&backtype='+2,
+						// });							
+					};
+					console.log(data);
+					// 接口返回的当前页数据列表 (数组)
+					let curPageData = data.data.data.list; 
+					// console.log('curPageData',curPageData);
+					// 接口返回的当前页数据长度 (如列表有26个数据,当前页返回8个,则curPageLen=8)
+					let curPageLen = curPageData.length; 
+					// 接口返回的总页数 (如列表有26个数据,每页10条,共3页; 则totalPage=3)
+					let totalPage =  data.data.data.totalPage; 
+					// 接口返回的总数据量(如列表有26个数据,每页10条,共3页; 则totalSize=26)
+					let totalSize = data.data.data.total; 
+					// this.mescrollList = curPageData;
+					// 接口返回的是否有下一页 (true/false)
+					// let hasNext = data.xxx; 
+					
+					//设置列表数据
+					if(page.num == 1) this.mescrollList = []; //如果是第一页需手动置空列表
+					this.mescrollList = this.mescrollList.concat(curPageData); //追加新数据
+					// console.log('page.num',page.num);
+					console.log('this.mescrollList',JSON.parse(JSON.stringify(this.mescrollList)));
+					// 请求成功,隐藏加载状态
+					//方法一(推荐): 后台接口有返回列表的总页数 totalPage
+					this.mescroll.endByPage(curPageLen, totalPage); 
+					//方法二(推荐): 后台接口有返回列表的总数据量 totalSize
+					// this.mescroll.endBySize(curPageLen, totalSize); 
+			
+					// setTimeout(()=>{
+					// 	this.mescroll.endSuccess(curPageLen)
+					// },20)
+				
+				}).catch(err => {
+					this.mescroll.endErr()
+					console.log(err)
+				
+				});
+				
+			},
+			//分类点击
+			categoryclick(num){
+				this.categoryall = false;
+				this.notall = true;
+				this.ins=num;
+				this.nowtype = this.categorylink[num].name;
+				// console.log(this.categorylink[this.ins]);
+				this.params.productCategoryId = this.categorylink[this.ins].id;
+				// this.params.productCategoryName = this.categorylink[this.ins].productCategoryName;
+				console.log('this.params',this.params);
+				this.downCallback();
+			},
+			//全部分类点击
+			category(){
+				this.categoryall = true;
+				this.notall = false;
+				this.params.productCategoryId = '';
+				this.downCallback();
+			},
+			
+			//轮播链接跳转
+			jumLink(url){
+				function handleBrowsingItemLink(link){
+				  const browsingItemLinkHttp = link.substr(0, 7).toLowerCase();
+				  const browsingItemLinkHttps = link.substr(0, 8).toLowerCase();				
+				  if (browsingItemLinkHttp === "http://" || browsingItemLinkHttps === "https://") {
+				    return link
+				  }				
+				  return `http://${link}`
+				}
+				url = handleBrowsingItemLink(url);
+				if(!url){uni.showToast({
+					title:'地址无效'
+				})}
+				window.location.assign(url);
+				// uni.navigateTo({
+				// 	url:`/pages/product/product?id=${id}`,
+				// 	fail(err) {
+				// 		console.log(err)
+				// 	}
+				// })
+			},
+			//产品跳转链接
+			jumpDetails(id){
+				uni.navigateTo({
+					url:`/pages/product/product?id=${id}`,
+					fail(err) {
+						console.log(err)
+					}
+				})				
+			},
+			//swiperchange
+			swiperchange(e){
+				this.currentswiper = e.detail.current;
+			}
+
+		}
+	}
+</script>
+
+<style scoped>
+@import url("./index.css");
+
+</style>

+ 191 - 0
pages/login/login - 副本.vue

@@ -0,0 +1,191 @@
+<template>
+	<view class="pages">
+		
+		<view class="wrap login">
+			<form @submit="" @reset="">
+				<view class="til">
+					登录
+				</view>
+				<view class="uni-form-item-wrap">
+					<view class="uni-form-item">
+						<input class="uni-input" v-model="userName" maxlength="11" focus placeholder="手机号" />
+					</view>
+					<view class="uni-form-item">
+						<input v-model="userPwd" :type="viewpassword?'text':'password'" class="uni-input" maxlength="16" focus placeholder="密码" />
+						<button type="default" @click="eyeclick" class="eyebtn" :class="viewpassword?'text':'password'"></button>
+					</view>
+				</view>		
+				<button form-type="submit" type="primary" @click="login" class="btn">登录</button>						
+			</form>			
+		</view>	
+		<view class="wxlogin">
+			<view open-type="getUserInfo" @getuserinfo="wxLogin" class="wxlogin-btn">
+				<image class="wxlogin-icon" src="/static/img/wx-icon.png" mode=""></image>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { mapState, mapMutations } from 'vuex';
+	export default{
+		components:{
+			
+		},
+		props:{
+			
+		},
+		data(){
+			return{
+				backpage:'',
+				//用户输入的内容
+				userName:"",
+				userPwd:"",
+				params:{
+					username: this.userName, 
+					password: this.userPwd,
+					grant_type:'password',
+					client_id:this.config.client_id,
+					client_secret:this.config.client_secret,
+				},
+				viewpassword:false,
+				//验证的规则
+				rules:{
+					userName:{
+						rule:/\S/,
+						msg:"账号不能为空"
+					},
+					userPwd:{
+						rule:/^[0-9a-zA-Z]{6,16}$/,
+						msg:"密码应该为6-16位"
+					}
+				}
+				
+			}
+		},
+		onLoad(options){
+			this.backpage = options.backpage
+			
+		},
+		methods:{
+			...mapMutations(['setLogin']),
+			login(){
+				let serf = this;
+				if(!this.validate('userName')) return;
+				if(!this.validate("userPwd"))  return;
+				uni.showLoading({
+					title:"登录中..."
+				});
+				this.$api.http.post(this.config.apiBaseurl+'oauth/token',{
+					username: this.userName, 
+					password: this.userPwd,
+					grant_type:'password',
+					client_id:this.config.client_id,
+					client_secret:this.config.client_secret,
+				}).then(res => {
+					console.log(res);
+					uni.hideLoading();
+					uni.showToast({
+						icon:'none',
+						title:res.data.message,
+						duration: 2000
+					});
+					serf.setLogin(res.data);
+					if(serf.backpage == '' || !serf.backpage){
+						uni.redirectTo({
+							url: '/pages/index/index',
+							fail:function(e){
+								console.log(e);
+							}
+						})
+					}else{
+						uni.navigateTo({
+							url:serf.backpage
+						})
+					}
+					// setTimeout(()=>{
+					// 	uni.redirectTo({
+					// 	    url: '../index/index'
+					// 	});
+					// },1000);
+				}).catch(err => {
+					console.log(err)
+				
+				});
+			},
+			//微信登录
+			wxLogin(e){
+				console.log(e);
+				let serf = this
+				let userinfo = e.detail.userInfo
+				uni.login({
+					provider:'weixin',
+					success(res){
+						let code = res.code
+						let wxparams = {
+							code:code,
+							sex:userinfo.gender,
+							headImgUrl:userinfo.avatarUrl,
+							nickname:userinfo.nickName
+						}
+						serf.$api.http.post("/wechat/app/login",wxparams).then(res=>{
+							serf.setLogin(res.retBody)
+							if(serf.backpage == '' || !serf.backpage){
+								uni.switchTab({
+									url: '/pages/userCenter/userCenter'
+								})
+							}else{
+								uni.navigateTo({
+									url:serf.backpage
+								})
+							}
+						})
+					}
+				})
+			},
+			//判断验证是否符合要求
+			validate(key){
+				let bool=true;
+				if(!this.rules[key].rule.test(this[key])){
+					//提示信息
+					uni.showToast({
+						title:this.rules[key].msg,
+					})
+					//取反
+					bool=false;
+					return false;
+				}
+				return bool;
+			},
+			//显示隐藏密码
+			eyeclick(){
+				this.viewpassword = !this.viewpassword;
+			}
+			
+		}
+	}
+</script>
+
+<style scoped>
+
+page{background-color: #f8f8f8;}
+.til{margin: 24rpx 0;}
+.login{padding-top: 24rpx;}
+.uni-input{width: 100%;box-sizing: border-box;border-radius:100rpx;background-color: #fff;height: 50px;padding-left: 80rpx;}
+.uni-form-item{position: relative;margin-bottom: 40rpx;}
+.btn{border-radius: 100rpx;}
+.uni-form-item-wrap{margin-bottom:  80rpx;}
+
+.eyebtn {position: absolute;height: 100%; width: 50px;top: 0; right: 0;background: transparent;cursor: pointer;border: 0;padding: 0;margin: 0;outline: 0;}
+.eyebtn::before,
+.eyebtn::after {content:'';position: absolute;top: 0; left: 0; bottom: 0; right: 0;margin: auto;}
+.eyebtn::after{transform-origin:unset;transform: scale(0.8);}
+.eyebtn.password::before {width: 20px;height: 20px;background: #e0e0e0;border-radius: 15px 0 15px 0;-webkit-transform: rotate(45deg);-ms-transform: rotate(45deg);transform: rotate(45deg);-webkit-transition: height .168s;transition: height .168s;}
+.eyebtn.password::after {width: 10px;height: 10px;border-radius: 50%;background: #424242;}
+.eyebtn.text::before {width: 15px;height: 15px;background: transparent;border: 3px solid #e0e0e0;border-radius: 15px 0 15px 0;-webkit-transform: rotate(45deg);-ms-transform: rotate(45deg);transform: rotate(45deg);}
+.eyebtn.text::after {width: 3px;height: 30px;border-radius: 0;-webkit-transform: rotate(30deg);-ms-transform: rotate(30deg);transform: rotate(30deg);background: #e0e0e0;-webkit-transition: height .132s;transition: height .132s;}
+
+.wxlogin{height: 100rpx;}
+.wxlogin-btn{position: fixed;left: 50%;bottom: 50rpx;width: 100rpx;height: 100rpx;margin-left: -50rpx;background-color: #007AFF;border-radius: 50%;}
+.wxlogin-btn .wxlogin-icon{width: 100%;height: 100%;transform: scale(0.5);}
+</style>

+ 43 - 0
pages/login/login.vue

@@ -0,0 +1,43 @@
+<template>
+	<view class="pages">
+		
+		<view class="login">
+			<image class="loginbg" src="/static/img/loginbg.png" mode="widthFix"></image>
+			<button type="default" class="big-btn" @click="wxclick">微信登录</button>
+		</view>
+		
+	</view>
+</template>
+
+<script>
+	import { mapState, mapMutations } from 'vuex';
+	export default{
+		components:{
+			
+		},
+		props:{
+			
+		},
+		data(){
+			return{
+				
+			}
+		},
+		onLoad(options){
+			this.backpage = options.backpage
+			
+		},
+		methods:{
+			wxclick(){
+				window.location.replace(this.config.loginUrl);
+			}
+			
+		}
+	}
+</script>
+
+<style scoped>
+page{background: #fff;}
+.loginbg{width: 100%;height: auto;margin: 80rpx 0 40rpx;}
+.big-btn{margin: 0 95rpx;background-color: #65B74E;border-radius: 100rpx;color: #fff;font-size: 32rpx;font-weight: 400;line-height: 45rpx;letter-spacing: 5rpx;}
+</style>

+ 39 - 0
pages/product/product.css

@@ -0,0 +1,39 @@
+.product-info-wrap{background-color: #fff;padding: 0 40rpx 46rpx;}
+.product-info-til{margin-bottom: 13rpx;font-size: 32rpx;font-weight: 500;color: #333333;line-height: 45rpx;font-weight: bold;}
+.product-info-brand{margin-bottom: 8rpx;font-size: 24rpx;font-weight: 400;color: #6BBC6D;line-height: 33rpx;}
+.product-info-brand .postage{margin-left: 16rpx;padding: 2rpx 14rpx;border-radius: 24rpx;font-size: 20rpx;font-weight: 400;color: #6BBC6D;line-height: 28rpx;background-color: #E1F2E2;}
+.product-info-item{display: flex;align-items: center;justify-content: space-between;margin-bottom: 8rpx;font-size: 26rpx;font-weight: 400;color: #999;line-height: 37rpx;}
+.product-info-price{font-size: 24rpx;font-weight: 400;color: #999;line-height: 33rpx;}
+.product-info-price .rmb{margin-right: 10rpx;font-size: 28rpx;font-weight: 500;color: #FF5030;line-height: 40rpx;}
+.product-info-price .price{font-size: 36rpx;font-weight: 500;color: #FF5030;line-height: 50rpx;}
+
+.block-til{display: flex;margin-bottom: 24rpx;justify-content: space-between;align-items: center;height: 90rpx;border-bottom: 1px solid #eee;}
+.block-til-left{font-size: 30rpx;font-weight: bold;line-height: 42rpx;color: #333;}
+.block-til-right{font-weight: 400;color: #6BBC6D;line-height: 33px;}
+
+.placeoforigin-info{display: flex;align-items: center;}
+.placeoforigin-info-img-wrap{width: 240rpx;height: 240rpx;overflow: hidden;margin-right: 24rpx;text-align: center;}
+.placeoforigin-info-img{width: 100%;height: 100%;}
+.placeoforigin-info-text{flex: 1;font-weight: 400;}
+.placeoforigin-info-text-til{font-size: 34rpx;line-height: 48rpx;color: #333;}
+.placeoforigin-info-text-con{font-size: 28rpx;color: #999;line-height: 40rpx;}
+
+.placeoforigin-addr{font-size: 34rpx;font-weight: 400;color: #6BBC6D;line-height: 48rpx;}
+
+.introduce-table{display: flex;margin-bottom: 24rpx;}
+.introduce-table-til{font-size: 30rpx;font-weight: 400;color: #333;line-height: 42px;margin-right: 25rpx;}
+.introduce-table-con{flex:1;background-color: #FAFAFA;border: 1px solid #E8E8E8;border-bottom: 0;font-size: 20rpx;color: #666;}
+.introduce-table-row{display: flex;border-bottom: 1px solid #E8E8E8;}
+.introduce-table-col{flex: 1;padding: 19rpx 0;text-align: center;}
+.introduce-table-col + .introduce-table-col{border-left: 1px solid #E8E8E8;}
+
+.page-article-text{font-size: 24rpx;font-weight: 400;color: #999;line-height: 33rpx;}
+.page-article-text p{text-indent: 2em;margin-bottom: 5rpx;}
+
+.safe-icon{width: 36rpx;height: 36rpx;margin-right: 10rpx;display: inline-block;vertical-align: -10rpx;}
+.safe-icon-img{width: 100%;height: 100%;}
+
+
+
+
+

+ 237 - 0
pages/product/product.vue

@@ -0,0 +1,237 @@
+<template>
+	<view class="pages">
+		<view class="appAdv nomal-bottom">
+			<swiper class="swiper" :indicator-dots="swiper.indicatorDots" :autoplay="swiper.autoplay" :interval="swiper.interval" :duration="swiper.duration">
+				<!-- <swiper-item v-for="(item,index) in albumPics" :key="index"> -->
+				<swiper-item>
+					<view class="adv-item"><image :src="$getimg+product.pic|miniImg(75)" class="pic" mode="aspectFit"></image></view>
+				</swiper-item>
+			</swiper>
+		</view>
+		<!-- 轮播结束 -->
+		<view class="product-info-wrap">
+			<view class="product-info-til">{{product.name}}</view>
+			<view class="product-info-brand">{{product.brandName}}<!-- <text class="postage">包邮</text> --></view>
+			<view class="product-info-item">
+				<text class="item">保质期:{{product.qualityGuaranteePeriod}}</text>
+				<text class="item">生产日期:{{createTime}}</text>
+				<text class="item">包装:{{product.unit}}</text>
+			</view>
+			<view class="product-info-price">
+				<text class="rmb">¥</text>
+				<text class="price">{{product.price}}</text>
+				<text class="unit" v-if="product.unit">/{{product.unit}}</text>
+			</view>
+		</view>
+		<!-- 产品基本信息结束 -->
+		<view class="write-radius supplier wrap">
+			<view class="block-til">
+				<view class="block-til-left">供应商</view>
+				<view class="block-til-right" v-if="companyinfo.isCert==1">
+					<view class="safe-icon">
+						<image class="safe-icon-img" src="/static/img/icon-safe.png" mode=""></image>
+					</view>
+					商家已缴纳诚信保证金
+				</view>
+			</view>
+			<view class="placeoforigin-info" @click="gocompany(product.umsCompanyInfoId)">
+				<view class="placeoforigin-info-img-wrap" >
+					<image class="placeoforigin-info-img" :src="firstimg||'/static/img/inbuild.png'|miniImg(75)" mode="widthFix"></image>
+				</view>
+				<view class="placeoforigin-info-text">
+					<view class="placeoforigin-info-text-til">{{companyinfo.compName}}</view>
+					<view class="placeoforigin-info-text-con">
+						<view class="placeoforigin-info-text-level">等级:{{companyinfo.compLevel}}级</view>
+						<view class="placeoforigin-info-text-product">经营产品:{{companyinfo.compManageProduct}}</view>
+						<view class="placeoforigin-info-text-addr">地址:{{ companyinfo.provinceId + companyinfo.cityId||''}}</view>						
+					</view>					
+				</view>
+			</view>
+		</view>
+		<!-- 供应商 end -->
+		<view class="write-radius placeoforigin wrap">
+			<view class="block-til">
+				<view class="block-til-left">生产地</view>
+			</view>
+			<view class="placeoforigin-addr">{{product.placeOfProduction}}</view>
+		</view>
+		<!-- 生产地 end -->
+		<view class="write-radius introduce wrap">
+			<view class="block-til">
+				<view class="block-til-left">产品介绍</view>
+			</view>
+			<!-- <view class="introduce-table">
+				<view class="introduce-table-til">规格</view>
+				<view class="introduce-table-con">
+					<view class="introduce-table-row">
+						<view class="introduce-table-col">5斤装</view>
+						<view class="introduce-table-col">30.00元/件</view>
+					</view>
+					<view class="introduce-table-row">
+						<view class="introduce-table-col">10斤装</view>
+						<view class="introduce-table-col">50.00元/件</view>
+					</view>
+				</view>
+			</view> -->
+			<!-- introduce-table end -->
+			<view class="page-article">
+				<view class="page-article-text">
+					{{origin.summary}}
+				</view>
+				<image v-for="(item,index) in albumPics" :key="index" :src="$getimg+item|miniImg(75)" class="full-img" mode="widthFix"></image>
+			</view>			
+		</view>
+		<!-- 产品介绍 end -->
+		<view class="bottom-flex-btn-wrap">
+			<view class="bottom-flex-btn-content">
+				<button type="default" :disabled="isPred==0?false:true"  class="bottom-flex-btn" @click="focuse(params.productid)">有意向</button>
+				<button type="default" class="bottom-flex-btn primary" @click="callnumber(phoneNumber)">打电话</button>
+			</view>			
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				
+				params:{
+					token:'',
+					tokenhead:'',
+					productid:'',					
+				},
+				phoneNumber:'',
+				swiper: {
+					indicatorDots: false,
+					autoplay: true,
+					interval: 9000,
+					duration: 500
+				},
+				productresult:[],
+				brand:[],
+				companyinfo:[],
+				origin:[],
+				product:[],
+				createTime:'',
+				isPred:0,
+				albumPics:null,
+				firstimg:'',
+				files:[],
+				thispage:'',
+				
+				
+			}
+		},
+		onShow() {
+			let serf = this;
+			//检查登录,获取token
+			// let loginRes = this.checkLogin('/pages/index/index', '2');
+			// if(!loginRes){return false;}
+			// serf.params.token=loginRes[0];			
+		},
+		onLoad(option) {
+			let self = this;
+			uni.getStorage({
+				key:'token',
+				success: function (res) {
+					self.params.token = res.data;
+				   // console.log(res.data);
+				}
+			});
+			uni.getStorage({
+				key:'tokenhead',
+				success: function (res) {
+					self.params.tokenhead = res.data;
+				   // console.log(res.data);
+				}
+			});
+			console.log('option',option);
+			this.params.productid = option.id;
+			this.thispage = `/pages/product/product?id=${this.params.productid}`;
+			//获取产品
+			//,{params:this.params}
+			this.$api.http.get(this.config.apiBaseurl+'/product/detail/'+this.params.productid).then(res => {
+				console.log('res',JSON.parse(JSON.stringify(res)));
+				this.isPred = res.data.data.isPred;
+				// console.log('this.isPred',this.isPred)
+				this.productresult = res.data.data || '';
+				this.brand = res.data.data.brand || '';
+				this.companyinfo = res.data.data.companyInfo || '';				
+				this.origin = res.data.data.origin || '';
+				this.product = res.data.data.product || '';
+				this.albumPics = this.product.albumPics||'';
+				this.albumPics = this.albumPics.split(',');
+				this.files = res.data.data.companyInfo.files||'';
+				this.firstimg = this.files[0].fileUrl||'';
+				// console.log('this.albumPics',this.albumPics,'type',typeof this.albumPics,'this.firstimg',this.firstimg);
+				this.phoneNumber = this.companyinfo.compConNum;
+				// console.log('this.brand',JSON.parse(JSON.stringify(this.brand)));
+				// console.log('this.companyinfo',JSON.parse(JSON.stringify(this.companyinfo)));
+				// console.log('this.origin',JSON.parse(JSON.stringify(this.origin)));
+				// console.log('this.product',JSON.parse(JSON.stringify(this.product)));
+				this.createTime = this.product.createTime.substring(0, 10);
+			}).catch(err => {
+				console.log(err)
+			});
+
+		},
+		methods: {
+			//有意向
+			focuse(productid){
+				
+				let thetoken = this.params.tokenhead + this.params.token;
+				this.$api.http.put(this.config.apiBaseurl+'/pre/add',{productId:productid},{header: {Authorization:thetoken}}).then(res => {
+					console.log(res);
+					uni.showToast({
+						icon:'success',
+						mask:true,
+						title:`已添加到有意向`,
+						duration: 2000
+					});
+					
+				}).catch(err => {
+					if(err.data.message =='暂未登录或token已经过期'){
+						let loginurl = `${this.config.apiBaseurl}/wechat/h5/authorize?returnUrl=${this.thispage}`
+						window.location.replace(loginurl);
+						return
+					}
+					uni.showToast({
+						icon:'none',
+						mask:true,
+						// title:err.data.message,
+						title:'已经添加过',
+						duration: 2000
+					});
+					console.log(err.data.message);
+				});				
+			},
+			//打电话
+			callnumber(number){
+				if(!number){
+					uni.showToast({
+						icon:'none',
+						mask:true,
+						title:`手机号码为空`,
+						duration: 2000
+					});
+					return;
+				};
+				uni.makePhoneCall({
+				    phoneNumber: number 
+				});				
+			},
+			// 跳转到供应商
+			gocompany(id){
+				uni.navigateTo({
+					url:`/pages/supplier/supplierdetail/supplierdetail?id=${id}`
+				})
+			}
+
+		}
+	}
+</script>
+
+<style scoped>
+	@import url("./product.css");
+</style>

+ 24 - 0
pages/productcategory/productcategory.css

@@ -0,0 +1,24 @@
+.producttype-wrap >>> .uni-searchbar__box{justify-content: left;border-color: #6BBC6D;border-radius: 50rpx!important;background-color: #fff!important;}
+.producttype-wrap >>> .uni-searchbar{padding-left: 0;}
+
+.producttype{display: flex;background: #fff;}
+.producttype-nav{width: 200rpx;height: 100%;box-sizing: border-box;background-color: #F6F6F9;overflow-y: auto;position: fixed;left: 0;bottom: 0;top: calc(44px + env(safe-area-inset-top));}
+.producttype-nav-item{position: relative;padding: 29rpx 0;box-sizing: border-box;text-align: center;font-size: 30rpx;font-weight: 500;color: #333;line-height: 42rpx;}
+.producttype-nav-item.active{background-color: #fff;color: #6BBC6D;}
+.producttype-nav-item.active::before{content: '';position: absolute;left: 0;top: 50%;margin-top: -15rpx;width: 6rpx;height: 30rpx;background: #6BBC6D;border-radius: 3px;}
+
+.producttype-con{position: relative;flex: 1;margin: 0 0 0 237rpx;padding-top: 20rpx;}
+
+.search-typelist-item{display: flex;margin-left: 22rpx;height: 84rpx;align-items: center;border-bottom: 1px solid #eee;}
+.search-typelist-item-serial{margin-right: 24rpx;padding: 5rpx 13rpx;min-width: 35rpx;text-align: center;background-color: #6BBC6D;color: #fff;}
+.search-typelist-item-name{font-size: 24rpx;}
+
+
+
+
+
+
+
+
+
+

+ 118 - 0
pages/productcategory/productcategory.vue

@@ -0,0 +1,118 @@
+<template>
+	<view class="pages producttype-wrap">
+		<view class="producttype">
+			<view class="producttype-nav">
+				<view v-for="(item,index) in navitem" :key="index" @click="typeclick(index)" :class="{active:index==ins}" class="producttype-nav-item">{{ item.name }}</view>
+			</view>
+			<view class="producttype-con">
+				<view class="search">
+					<view class="searchTxt">
+						<uni-search-bar :placeholder="searchtxt" @confirm="search" ></uni-search-bar>
+					</view>
+				</view>
+				<view class="search-typelist">
+					<view v-for="(item,index) in typelist" :key="index" @click="publishtype(item.id,item.name)" class="search-typelist-item">
+						<view class="search-typelist-item-serial">{{ index +1 }}</view>
+						<view class="search-typelist-item-name">{{item.name}}</view>
+					</view>					
+				</view>
+				<!-- <mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption" :up="upOption">
+					<view class="search-typelist">
+						<view v-for="(item,index) in typelist" :key="index" @click="publishtype(item.name)" class="search-typelist-item">
+							<view class="search-typelist-item-serial">{{ index +1 }}</view>
+							<view class="search-typelist-item-name">{{item.name}}</view>
+						</view>					
+					</view>	 
+				</mescroll-body> -->				
+			</view>
+			<!-- producttype-con end -->
+		</view>
+		<!-- fix-content end -->
+		
+	</view>
+</template>
+
+<script>	
+	import uniSearchBar from '@/components/uni-search-bar/uni-search-bar.vue'
+	export default {
+		components: {
+			uniSearchBar,	
+		
+		},
+		data() {
+			return {
+				params:{
+					token:'',
+				},
+				searchtxt:'产品名称',
+				//nav active
+				ins:0,
+				//菜单类别
+				navitem:[],
+				//热门列表
+				// hotlist:[
+				// ],
+				//typelist
+				typelist:[],
+				//显示的分类标题
+				nowtype:'',
+				
+				
+				
+			}
+		},
+		onShow() {
+			let serf = this;
+			//检查登录,获取token
+			// let loginRes = this.checkLogin('/pages/index/index', '2');
+			// if(!loginRes){return false;}
+			// serf.params.token=loginRes[0];			
+		},
+		onLoad() {
+			this.nowtype = this.navitem[this.ins];
+			this.params.product = this.nowtype;
+			//获取产品分类
+			// ,{header: {Authorization:this.params.tokenhead+this.params.token}}
+			this.$api.http.get(this.config.apiBaseurl+'/product/categoryTreeList').then(res => {
+				console.log('产品分类',res);
+				this.navitem = res.data.data;
+				this.nowtype = this.navitem[this.ins].name;
+				// this.params.productCategoryId = this.navitem[this.ins].id;
+				this.searchtxt = this.navitem[this.ins].name;
+				this.typelist = this.navitem[this.ins].children;
+				// this.downCallback();
+			}).catch(err => {
+				console.log(err)
+			});
+		},
+		methods: {
+			//搜索
+			search(e) {
+				// console.log(e);
+				uni.navigateTo({
+					url:`/pages/searchresults/searchresults?keyword=${e.value}`,					
+				})	
+			},
+			
+			//左侧导航点击
+			typeclick(num){
+				this.ins=num;
+				this.nowtype = this.navitem[num];
+				this.searchtxt = this.navitem[this.ins].name;
+				this.typelist = this.navitem[this.ins].children;
+			},
+			//产品点击
+			publishtype(id,name){
+				// console.log(id,name);
+				uni.navigateTo({
+					url:`/pages/searchresults/searchresults?productCategoryId=${id}&name=${name}`,					
+				})				
+			},
+
+		}
+	}
+</script>
+
+<style scoped>
+	@import url("./productcategory.css");
+</style>

+ 22 - 0
pages/producttype/producttype.css

@@ -0,0 +1,22 @@
+.producttype-wrap >>> .uni-searchbar__box{justify-content: left;border-color: #6BBC6D;border-radius: 50rpx!important;background-color: #fff!important;}
+.producttype-wrap >>> .uni-searchbar{padding-left: 0;}
+
+.producttype{display: flex;background: #fff;}
+.producttype-nav{width: 200rpx;height: 100%;box-sizing: border-box;background-color: #F6F6F9;overflow-y: auto;position: fixed;left: 0;bottom: 0;top: calc(44px + env(safe-area-inset-top));}
+.producttype-nav-item{position: relative;padding: 29rpx 0;box-sizing: border-box;text-align: center;font-size: 30rpx;font-weight: 500;color: #333;line-height: 42rpx;}
+.producttype-nav-item.active{background-color: #fff;color: #6BBC6D;}
+.producttype-nav-item.active::before{content: '';position: absolute;left: 0;top: 50%;margin-top: -15rpx;width: 6rpx;height: 30rpx;background: #6BBC6D;border-radius: 3px;}
+
+.producttype-con{position: relative;flex: 1;margin: 0 0 0 237rpx;box-sizing: border-box;height: 100%;overflow-y: auto;}
+
+.producttype-wrap .product-wrap{margin: 0;}
+
+
+
+
+
+
+
+
+
+

+ 230 - 0
pages/producttype/producttype.vue

@@ -0,0 +1,230 @@
+<template>
+	<view class="pages producttype-wrap">
+		<view class="producttype">
+			<view class="producttype-nav">
+				<view v-for="(item,index) in navitem" :key="index" @click="typeclick(index)" :class="{active:index==ins}" class="producttype-nav-item">{{ item.name }}</view>
+			</view>
+			<view class="producttype-con">
+				<view class="search">
+					<view class="searchTxt">
+						<uni-search-bar :placeholder="searchtxt" @confirm="search" v-model="searchtxt" @input="search" ></uni-search-bar>
+					</view>
+				</view>		
+				<!-- 搜索 结束 -->
+				<!-- <view class="producttype-con-block producttype-con-block-haveimg">
+					<view class="producttype-con-block-til">当季热门</view>
+					<view class="producttype-con-block-con">
+						<view v-for="(item,index) in hotlist" :key="index" @click="productclick(item.name)" class="producttype-con-block-item">
+							<image class="producttype-con-block-con-img" :src="item.img" mode="aspectFit"></image>
+							<view class="producttype-con-block-con-name">{{ item.name }}</view>
+						</view>						
+					</view>					
+				</view> -->
+				<!-- 当季热门 end -->
+				
+				<mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :bottom="0" :down="downOption" :up="upOption">
+					<!-- <view class="product-wrap"> -->
+						<view v-for="(item,index) in productlist" :key="index" @click="productclick(item.id)" class="product-item">
+							<view class="product-item-img-wrap">
+								<image class="product-item-img" :src="item.pic" mode="widthFix"></image>
+							</view>
+							<view class="product-item-info">
+								<view class="product-item-til">{{item.name}}</view>
+								<view class="product-item-brand">{{item.brandName}} <text class="postage" v-if="item.postage">包邮</text></view>
+								<view class="product-item-addr">{{item.addr}}</view>
+								<view class="product-item-price"><text class="rmb">¥</text> <text class="price">{{item.price}}</text> / {{item.unit}}</view>
+							</view>
+						</view>
+					<!-- </view> -->
+				</mescroll-body>				
+			</view>
+			<!-- producttype-con end -->
+		</view>
+		<!-- fix-content end -->
+		
+	</view>
+</template>
+
+<script>
+	// 引入mescroll-mixins.js
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";	
+	// 引入mescroll-body组件 (如已在main.js注册全局组件,则省略此步骤)
+	import MescrollBody from "@/components/mescroll-uni/mescroll-body.vue"; // 注意.vue后缀不能省
+	
+	import uniSearchBar from '@/components/uni-search-bar/uni-search-bar.vue'
+	export default {
+		mixins: [MescrollMixin], // 使用mixin
+		components: {
+			MescrollBody,
+			uniSearchBar,
+			
+		
+		},
+		data() {
+			return {
+				useoption:true,
+				params:{
+					// token:'',
+					keyword:'',
+					productCategoryId:'',
+				},
+				searchtxt:'产品名称',
+				mescroll: null, // mescroll实例对象 (此行可删,mixins已默认)
+				// 下拉刷新的配置(可选, 绝大部分情况无需配置)
+				downOption: { 
+					// ...
+				},
+				// 上拉加载的配置(可选, 绝大部分情况无需配置)
+				upOption: {
+					page: {
+						size: 10 // 每页数据的数量,默认10
+					},
+					noMoreSize: 5, // 配置列表的总数量要大于等于5条才显示'-- END --'的提示
+					empty: {
+						tip: '暂无相关数据'
+					}
+				},
+				//nav active
+				ins:0,
+				//菜单类别
+				navitem:[],
+				//热门列表
+				// hotlist:[
+				// ],
+				//productlist
+				productlist:[],
+				//显示的分类标题
+				nowtype:'',
+				
+				
+				
+			}
+		},
+		onShow() {
+			let serf = this;
+			//检查登录,获取token
+			// let loginRes = this.checkLogin('/pages/index/index', '2');
+			// if(!loginRes){return false;}
+			// serf.params.token=loginRes[0];			
+		},
+		onLoad(option) {
+			console.log(option);
+			this.nowtype = this.navitem[this.ins];
+			this.params.keyword = this.nowtype;
+			if(option.search){
+				this.nowtype = option.search;
+				this.searchtxt = option.search;				
+			}
+			//获取产品分类
+			// ,{header: {Authorization:this.params.tokenhead+this.params.token}}
+			this.$api.http.get(this.config.apiBaseurl+'/product/categorySearch').then(res => {
+				// console.log('产品分类',res);
+				this.navitem = res.data.data;
+				this.nowtype = this.navitem[this.ins].name;
+				// this.params.keyword = this.navitem[this.ins].name;
+				this.params.productCategoryId = this.navitem[this.ins].id;
+				this.searchtxt = this.navitem[this.ins].name;
+				if(this.useoption&&option.search){
+					this.params.productCategoryId = '';
+					this.params.keyword = option.search;
+					this.searchtxt = option.search;
+					this.ins = null;					
+				}
+				this.downCallback();
+			}).then(()=>{
+				this.useoption = false;
+				this.params.keyword = '';
+			}).catch(err => {
+				console.log(err)
+			});
+			
+		},
+		methods: {
+			//搜索
+			search(e) {	
+				this.params.keyword = e.value;
+				this.params.productCategoryId = '';
+				this.downCallback();				
+				this.searchtxt = this.params.keyword||'产品名称';
+				this.ins = null;
+				// this.poorList = [];
+				// this.params.farmerName = e.value;
+				// this.pagination = Object.assign(this.pagination,{"farmerName":e.value});
+				// this.pagination.pageNo = 1;
+				// this.getPoorList(this.pagination)
+			},
+			
+			/*mescroll组件初始化的回调,可获取到mescroll对象 (此处可删,mixins已默认)*/
+			mescrollInit(mescroll) {
+				this.mescroll = mescroll;
+			},
+			downCallback(){
+				this.mescroll.resetUpScroll(); // 重置列表为第一页 (自动执行 page.num=1, 再触发upCallback方法 )
+			},
+			/*上拉加载的回调*/
+			upCallback(page) {
+				let pageNum = page.num; // 页码, 默认从1开始
+				let pageSize = page.size; // 页长, 默认每页10条
+				this.params = Object.assign(this.params,{pageNum:pageNum,pageSize:pageSize});
+				// console.log('this.params',this.params);
+				this.$api.http.get(this.config.apiBaseurl+'/product/search',{params: this.params}).then(data => {
+					if(data.data.code=='1001'){
+						// uni.redirectTo({
+						// 	url:'/pages/login/login?backpage=/pages/index/index'+'&backtype='+2,
+						// });							
+					};
+					console.log(data);
+					// 接口返回的当前页数据列表 (数组)
+					let curPageData = data.data.data.list;
+					// 接口返回的当前页数据长度 (如列表有26个数据,当前页返回8个,则curPageLen=8)
+					let curPageLen = curPageData.length; 
+					this.productlist = curPageData;
+					console.log('this.productlist',this.productlist);
+					// 接口返回的总页数 (如列表有26个数据,每页10条,共3页; 则totalPage=3)
+					// let totalPage = data.xxx; 
+					// 接口返回的总数据量(如列表有26个数据,每页10条,共3页; 则totalSize=26)
+					let totalSize = data.data.data.total; 
+					// 接口返回的是否有下一页 (true/false)
+					// let hasNext = data.xxx; 
+					
+					//设置列表数据
+					if(page.num == 1) this.productlist = []; //如果是第一页需手动置空列表
+					this.productlist = this.productlist.concat(curPageData); //追加新数据
+					
+					//方法二(推荐): 后台接口有返回列表的总数据量 totalSize
+					this.mescroll.endBySize(curPageLen, totalSize); 
+			
+					// setTimeout(()=>{
+					// 	this.mescroll.endSuccess(curPageLen)
+					// },20)
+				
+				}).catch(err => {
+					this.mescroll.endErr()
+					console.log(err)
+				
+				});								
+			},
+			
+			//左侧导航点击
+			typeclick(num){
+				this.ins=num;
+				this.nowtype = this.navitem[num].name;
+				// this.params.keyword = this.nowtype;
+				this.params.productCategoryId = this.navitem[this.ins].id;
+				this.downCallback();
+				this.searchtxt = this.navitem[num].name;
+			},
+			//产品点击
+			productclick(id){
+				uni.navigateTo({
+					url:`/pages/product/product?id=${id}`,					
+				})				
+			},
+
+		}
+	}
+</script>
+
+<style scoped>
+	@import url("./producttype.css");
+</style>

+ 116 - 0
pages/publish/chosetype/chosetype - 副本.vue

@@ -0,0 +1,116 @@
+<template>
+	<view class="pages wrap">
+		<view class="type-block">
+			<view class="big-til">商品买卖类</view>
+			
+			<navigator url="/pages/publish/publishtype/publishtype">
+			<view class="select-bar">
+				<svg class="icon select-bar-icon" aria-hidden="true">
+					<use xlink:href="#iconshangpin"></use>
+				</svg>
+				<view class="select-bar-text">发布商品</view>
+				<view class="select-bar-arrow"></view>
+			</view>
+			</navigator>
+			<navigator url="/pages/publish/publishtype/publishtype">
+			<view class="select-bar">
+				<svg class="icon select-bar-icon" aria-hidden="true">
+					<use xlink:href="#iconcaigou"></use>
+				</svg>
+				<view class="select-bar-text">发布采购</view>
+				<view class="select-bar-arrow"></view>
+			</view>
+			</navigator>
+			<navigator url="/pages/publish/publishtype/publishtype">
+			<view class="select-bar">
+				<svg class="icon select-bar-icon" aria-hidden="true">
+					<use xlink:href="#icondizhi"></use>
+				</svg>
+				<view class="select-bar-text">飞鸽代采</view>
+				<view class="select-bar-arrow"></view>
+			</view>
+			</navigator>
+			<navigator url="/pages/publish/publishtype/publishtype">
+			<view class="select-bar">
+				<svg class="icon select-bar-icon" aria-hidden="true">
+					<use xlink:href="#iconzhaodaimai"></use>
+				</svg>
+				<view class="select-bar-text">找代卖</view>
+				<view class="select-bar-arrow"></view>
+			</view>
+			</navigator>
+		</view>
+		<!-- type-block end -->
+		<view class="type-block">
+			<view class="big-til">技术服务类</view>
+			<navigator url="/pages/publish/publishtype/publishtype">
+			<view class="select-bar">
+				<svg class="icon select-bar-icon" aria-hidden="true">
+					<use xlink:href="#iconshangpin"></use>
+				</svg>
+				<view class="select-bar-text">发布商品</view>
+				<view class="select-bar-arrow"></view>
+			</view>
+			</navigator>
+			<navigator url="/pages/publish/publishtype/publishtype">
+			<view class="select-bar">
+				<svg class="icon select-bar-icon" aria-hidden="true">
+					<use xlink:href="#iconcaigou"></use>
+				</svg>
+				<view class="select-bar-text">发布采购</view>
+				<view class="select-bar-arrow"></view>
+			</view>
+			</navigator>
+			<navigator url="/pages/publish/publishtype/publishtype">
+			<view class="select-bar">
+				<svg class="icon select-bar-icon" aria-hidden="true">
+					<use xlink:href="#icondizhi"></use>
+				</svg>
+				<view class="select-bar-text">飞鸽代采</view>
+				<view class="select-bar-arrow"></view>
+			</view>
+			</navigator>
+			<navigator url="/pages/publish/publishtype/publishtype">
+			<view class="select-bar">
+				<svg class="icon select-bar-icon" aria-hidden="true">
+					<use xlink:href="#iconzhaodaimai"></use>
+				</svg>
+				<view class="select-bar-text">找代卖</view>
+				<view class="select-bar-arrow"></view>
+			</view>
+			</navigator>		
+		</view>
+		<!-- type-block end -->
+		
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				params:{
+					token:'',
+				}
+				
+			}
+		},
+		onShow() {
+			let serf = this;
+			//检查登录,获取token
+			let loginRes = this.checkLogin('/pages/index/index', '2');
+			if(!loginRes){return false;}
+			serf.params.token=loginRes[0];			
+		},
+		onLoad() {
+
+		},
+		methods: {
+
+		}
+	}
+</script>
+
+<style scoped>
+	/* @import url("./chosetype.css"); */
+</style>

+ 107 - 0
pages/publish/chosetype/chosetype.vue

@@ -0,0 +1,107 @@
+<template>
+	<view class="pages">
+		<view class="search-wrap">
+			<view class="search">
+				<view class="searchTxt">
+					<uni-search-bar placeholder="搜索你想发布的品类" @confirm="searchtype" @input="searchtype" ></uni-search-bar>
+				</view>
+			</view>	
+		</view>
+		<!-- 搜索 结束 -->
+		<view class="typelist-wrap wrap">
+			<view v-for="(item,index) in typelist" :key="index" class="typelist-block">
+				<view class="select-bar" @click="typeclick(index)" :class="{open:index==ins}">
+					<view class="select-bar-img-wrap">
+						<image class="select-bar-img" :src="item.icon" mode=""></image>
+					</view>
+					<view class="select-bar-text">{{item.name}}</view>
+					<view class="select-bar-arrow"></view>
+				</view>
+				<view class="typelist-item-wrap">
+					<view v-for="(product,productindex) in item.children" :key="productindex" @click="publishitem(product)" class="typelist-item">
+						<view class="productindex">{{productindex + 1}}</view>
+						<view class="product">{{product.name}}</view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { bus } from '../../../utils/bus.js';
+	export default {
+		data() {
+			return {
+				params:{
+					token:'',
+				},
+				typelist:[],
+				ins:null,
+				
+			}
+		},
+		onShow() {
+			let serf = this;
+			//检查登录,获取token
+			// let loginRes = this.checkLogin('/pages/index/index', '2');
+			// if(!loginRes){return false;}
+			// serf.params.token=loginRes[0];			
+		},
+		onLoad() {
+			
+			//获取产品分类
+			// ,{header: {Authorization:this.params.tokenhead+this.params.token}}
+			this.$api.http.get(this.config.apiBaseurl+'/product/categoryTreeList').then(res => {
+				console.log('产品分类',res);
+				this.typelist = res.data.data;
+			}).catch(err => {
+				console.log(err)
+			});
+
+		},
+		methods: {
+			// 搜索
+			searchtype(e){
+				console.log(e)
+				this.$api.http.get(this.config.apiBaseurl+'/product/categorySearch',{params:{key:e.value}}).then(res => {
+					console.log('搜索结果',res);
+					this.typelist = res.data.data;
+				}).catch(err => {
+					console.log(err)
+				});
+			},
+			//分类点击
+			typeclick(num){
+				if(this.ins==num){
+					this.ins = null;
+					return
+				}
+				this.ins=num;
+			},
+			//选择发布
+			publishitem(product){
+				// console.log(product);
+				bus.$emit('toDetailPage', {
+					data: product
+				});
+				uni.navigateTo({
+					url:`/pages/publish/publish`
+				})
+			},
+
+		}
+	}
+</script>
+
+<style scoped>
+	/* @import url("./chosetype.css"); */
+	.typelist-wrap{margin-top: 24rpx;}
+	.typelist-item-wrap{height: 0;overflow: hidden;}
+	.select-bar.open + .typelist-item-wrap{height: auto;}
+	.typelist-item{display: flex;align-items: center;padding-left: 50rpx;height: 96rpx;border-bottom: 1px solid #eee;}
+	.typelist-block .typelist-item:last-child{border-bottom: 0;}
+	.typelist-item .productindex{width: 36rpx;height: 36rpx;line-height: 36rpx;margin-right: 25rpx;text-align: center;;background-color: #6BBC6D;color: #fff;}
+	.select-bar-img-wrap{background-color: #6bbc6d;width: 0;}
+	.search-wrap >>> .uni-searchbar__box{justify-content: left;border: 0;background-color: #F1F1F2!important;}
+</style>

+ 48 - 0
pages/publish/publish.css

@@ -0,0 +1,48 @@
+/* 搜索列表也 */
+.simplepage-wrap{padding-left: 24rpx;height: calc( 100% - 9rpx );}
+.simplepage-item{padding: 24rpx 0;border-bottom: 1px solid #eee;}
+
+.specifications-wrap uni-checkbox-group{display: flex;flex-wrap: wrap;margin-top: 24rpx;}
+.checkbox-style{position: relative;font-size: 24rpx;width: 30%;height: 72rpx;line-height: 72rpx;text-align: center;background-color: #F5F5F5;margin-bottom: 24rpx;overflow: hidden;border-radius: 8rpx;}
+.checkbox-style >>> uni-checkbox .uni-checkbox-input{color: #fff!important;margin-right: 0;position: absolute;right: 0;bottom: 0;background: transparent;border: 0;}
+.checkbox-style >>> uni-checkbox .uni-checkbox-input.uni-checkbox-input-checked:before{width: 39rpx;height: 37rpx;background: url(../../static/img/checkbox-checked.png) no-repeat;background-size: 100% 100%;font-size: 0;}
+/* .checkbox-style >>> uni-checkbox .uni-checkbox-input.uni-checkbox-input-checked::after{content: '';position: absolute;right: -40rpx;bottom: -40rpx;width: 80rpx;height: 80rpx;background-color: #6BBC6D;z-index: 5;transform: rotate(45deg);} */
+.checkbox-style:nth-child(3n-1){margin: 0 24rpx;}
+.pecifications-btn-wrap{display: flex;margin-top: 40rpx;}
+.pecifications-btn-wrap .big-btn{flex: 1;margin: 0;}
+.pecifications-btn-wrap .big-btn.primary{margin-left: 20rpx;}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 629 - 0
pages/publish/publish.vue

@@ -0,0 +1,629 @@
+<template>
+	<view class="pages">				
+		<form @submit="formSubmit" @reset="formReset">
+			<view class="form-item-type nomal-top">				
+				<view class="form-item">
+					<view class="form-item-til">类目</view>
+					<view class="form-item-con">
+						<input type="text" disabled placeholder="商品类目" v-model="params.type" value="" />
+					</view>
+				</view>
+			<!-- 	<view class="form-item arrow">
+					<view class="form-item-til">地理位置</view>
+					<view class="form-item-con" @click="chooseaddr">
+						<input type="text" placeholder="请选择地理位置" v-model="params.myposition" value="" />
+					</view>
+				</view>	 -->			
+			</view>
+			<view class="form-item-type">
+				<view class="form-item">
+					<view class="form-item-til">标题</view>
+					<view class="form-item-con">
+						<input type="text" placeholder="请输入发布产品标题" v-model="params.title" value="" />
+					</view>
+				</view>
+				<view class="form-item">
+					<view class="form-item-til">品牌</view>
+					<view class="form-item-con">
+						<input type="text" placeholder="请输入品牌" v-model="params.brand" value="" />
+					</view>
+				</view>
+				<view class="form-item">
+					<view class="form-item-til">零售价</view>
+					<view class="form-item-con">
+						<input type="text" placeholder="请输入零售价" v-model="params.price" value="" />
+					</view>
+				</view>
+				<view class="form-item">
+					<view class="form-item-til">供应商</view>
+					<view class="form-item-con">
+						<input type="text" disabled placeholder="" v-model="params.supplier" value="" />
+					</view>
+				</view>
+				<view class="form-item arrow" @click="openplaceoforigin">
+					<view class="form-item-til">生产地</view>
+					<view class="form-item-con">
+						<input type="text" placeholder="" v-model="params.placeoforigin" value="" />
+					</view>
+				</view>
+				<view class="form-item arrow" @click="openspecifications">
+					<view class="form-item-til">规格</view>
+					<view class="form-item-con">
+						<text v-for="(item,index) in params.specifications">{{item}},</text>
+						<!-- <input type="text" placeholder="" v-model="params.specifications" value="" /> -->
+					</view>
+				</view>
+				<view class="form-item">
+					<view class="form-item-til">保质期</view>
+					<view class="form-item-con">
+						<input type="text" placeholder="请输入保质期" v-model="params.shelflife" value="" />
+					</view>
+				</view>
+				<view class="form-item">
+					<view class="form-item-til">生产日期</view>
+					<view class="form-item-con">
+						<picker mode="date" :value="params.date" :start="startDate" :end="endDate" @change="bindDateChange">
+							<view class="uni-input">{{params.date}}</view>
+						</picker>
+					<!-- <input type="text" placeholder="请输入生产日期" v-model="params.productiondate" value="" /> -->
+					</view>
+				</view>
+				<view class="form-item">
+					<view class="form-item-til">包装</view>
+					<view class="form-item-con">
+						<input type="text" placeholder="请输入包装" v-model="params.packaging" value="" />
+					</view>
+				</view>
+				<view class="form-item">
+					<view class="form-item-til">标签</view>
+					<view class="form-item-con">
+						<input type="text" placeholder="请输入标签,用逗号隔开" v-model="params.productlabel" value="" />
+					</view>
+				</view>
+			</view>
+			<view class="form-item upfile">
+				<view class="form-item-til">图片视频</view>
+				<view class="form-item-con">					
+					<view class="form-item-chooseIMGs-wrap">
+						<image v-for="(item,index) in params.media" :key="index" :src="item" class="choosedIMG" ></image>
+					</view>
+					<view class="chooseIMG-btn">
+						<image class="chooseIMG-btn-img" @click="chooseimg" src="/static/img/icon-upload.png"></image>
+					</view>					
+				</view>				
+			</view>
+		</form>
+		<view class="bottom-btn-wrap">
+			<button class="bottom-btn" @click="publish" type="primary">发布</button>
+		</view>
+		<!-- 页面显示结束 下面是隐藏内容 -->
+		<!-- 供应商开始 -->
+		<!-- <view class="supplier-page fix-content translate" :class="{ 'open': supplieropen }" >
+			<view class="search-wrap search-left-color">
+				<view class="search">
+					<view class="searchTxt">
+						<uni-search-bar placeholder="搜索供应商关键词" @confirm="searchsupplier" @input="searchsupplier" ></uni-search-bar>
+					</view>
+				</view>	
+			</view>
+			<view class="simplepage-wrap">
+				<mescroll-body ref="mescrollRef0" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption" :up="upOption">
+					<view>
+						<view v-for="(item,index) in supplierlist" :key="index" @click="supplierclick(item)" class="simplepage-item">{{item}}</view>					
+					</view>	 					
+				</mescroll-body>
+			</view>
+		</view> -->
+		<!-- 供应商结束 -->
+		<!-- 产地开始 -->
+		<view class="placeoforigin-page fix-content translate" :class="{ 'open': placeoforiginopen }" >
+			<view class="search-wrap search-left-color">
+				<view class="search">
+					<view class="searchTxt">
+						<uni-search-bar placeholder="搜索产地关键词" @confirm="searchplace" @input="searchplace" ></uni-search-bar>
+					</view>
+				</view>	
+			</view>
+			<!-- 搜索 结束 -->
+			<scroll-view class="simplepage-wrap" scroll-y="true">
+				<view v-for="(item,index) in placeoforiginlist" :key="index" @click="placeoforiginclick(item.detailAddress)" class="simplepage-item">{{item.detailAddress}}</view>
+			</scroll-view>
+		</view>
+		<!-- 产地结束 -->
+		<!-- 规格开始 -->
+		<view class="specifications-page fix-content translate" :class="{ 'open': specificationsopen }" >
+			<view class="specifications-wrap wrap">
+				 <checkbox-group @change="checkboxChange">
+					<label  v-for="(item,index) in specificationslist" :key="index" class="checkbox-style" >
+						<checkbox :value="item.value" :checked="item.checked"   />{{item.name}}
+					</label>
+				</checkbox-group>
+				<view class="pecifications-btn-wrap">
+					<button type="default" class="big-btn big-btn-hollow" @click="resetcheckbox">重置</button>
+					<button type="default" class="big-btn primary" @click="specificationsclick">确定</button>
+				</view>
+			</view>
+		</view>
+		<!-- 规格结束 -->
+	</view>
+</template>
+
+<script>
+	// 引入mescroll-mixins.js
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";	
+	// 引入mescroll-body组件 (如已在main.js注册全局组件,则省略此步骤)
+	import MescrollBody from "@/components/mescroll-uni/mescroll-body.vue"; // 注意.vue后缀不能省
+	
+	import uniSearchBar from '@/components/uni-search-bar/uni-search-bar.vue';
+	import { bus } from '../../utils/bus.js';
+	export default {
+		mixins: [MescrollMixin], // 使用mixin
+		components: {
+			MescrollBody,
+			uniSearchBar,
+			
+		
+		},
+		data() {
+			const currentDate = this.getDate({
+				format: true
+			})
+			return {				
+				mescroll: null, // mescroll实例对象 (此行可删,mixins已默认)
+				// 下拉刷新的配置(可选, 绝大部分情况无需配置)
+				downOption: { 
+					// ...
+				},
+				// 上拉加载的配置(可选, 绝大部分情况无需配置)
+				upOption: {
+					page: {
+						size: 10 // 每页数据的数量,默认10
+					},
+					noMoreSize: 5, // 配置列表的总数量要大于等于5条才显示'-- END --'的提示
+					empty: {
+						tip: '暂无相关数据'
+					}
+				},
+				params: {
+					token: '',
+					type:'',
+					myposition:'',					
+					title:'',
+					brand:'',
+					price:'',
+					supplier:'默认的供应商',//供应商
+					placeoforigin:'',//产地
+					specifications:[],//规格
+					shelflife:'',//保质期					
+					date: currentDate,//生产日期 // productiondate:'',//生产日期
+					packaging:'',//包装
+					productlabel:'',//标签
+					media:[],//图片视频
+				},
+				//表单检查结果
+				checkStatus:false,
+				//供应商
+				supplieropen:false,
+				supplierlist:[],
+				// 产地
+				placeoforiginopen:false,
+				placeoforiginlist:[],
+				places:[],//临时暂存
+				// 规格
+				specificationsopen:false,
+				specificationslist:[
+					{value: '500g/提(250g/盒*2)',name: '500g/提(250g/盒*2)',checked: false},
+					{value: '10g/罐',name: '10g/罐',checked: false},
+					{value: '30g/盒',name: '30g/盒',checked: false},
+					{value: '150g/盒',name: '150g/盒',checked: false},
+					{value: '30g/盒(1.5g*20瓶)',name: '30g/盒(1.5g*20瓶)',checked: false},
+				],
+				backtyep:1,
+
+			}
+		},
+		onShow() {
+			uni.hideTabBar();
+			let serf = this;
+			//检查登录,获取token
+			// let loginRes = this.checkLogin('/pages/publish/publish', '2');
+			// if (!loginRes) {
+			// 	return false;
+			// }
+			// serf.params.token = loginRes[0];
+		},
+		onHide() {
+			uni.showTabBar()
+		},
+		onLoad(option) {
+			let self = this;
+			if(Object.keys(bus.data).length>0){
+				console.log('bus.data',bus.data)
+				this.params.type = bus.data.name;
+				// console.log(this.params.type);
+			};
+			// console.log(option);
+			// this.params.type = option.type;
+			
+			uni.getStorage({
+				key:'token',
+				success: function (res) {
+					self.params.token = res.data;
+				   // console.log(res.data);
+				}
+			});
+			uni.getStorage({
+				key:'tokenhead',
+				success: function (res) {
+					self.params.tokenhead = res.data;
+				   // console.log(res.data);
+				}
+			});
+			
+			//供应商详情
+			let thetoken = this.params.tokenhead+this.params.token;
+			this.$api.http.get(this.config.apiBaseurl+'/company/cp/detail',{header: {Authorization:thetoken},params:{compId:72}}).then(res =>{
+				console.log('res',JSON.parse(JSON.stringify(res)));
+				this.placeoforiginlist = res.data.data.origines;
+				this.places = res.data.data.origines;
+				this.params.supplier= res.data.data.compName;
+			}).catch( err => {
+				console.log(err)				
+			});
+			
+		},
+		computed: {
+			startDate() {
+				return this.getDate('start');
+			},
+			endDate() {
+				return this.getDate('end');
+			}
+		},
+		methods: {
+			getDate(type) {
+				const date = new Date();
+				let year = date.getFullYear();
+				let month = date.getMonth() + 1;
+				let day = date.getDate();
+	
+				if (type === 'start') {
+					year = year - 60;
+				} else if (type === 'end') {
+					year = year + 2;
+				}
+				month = month > 9 ? month : '0' + month;;
+				day = day > 9 ? day : '0' + day;
+				return `${year}-${month}-${day}`;
+			},
+			bindDateChange: function(e) {
+				this.params.date = e.target.value
+				// console.log(this.params.date);
+			},
+			/*mescroll组件初始化的回调,可获取到mescroll对象 (此处可删,mixins已默认)*/
+			mescrollInit(mescroll) {
+				this.mescroll = mescroll;
+			},
+			downCallback(){
+				this.mescroll.resetUpScroll(); // 重置列表为第一页 (自动执行 page.num=1, 再触发upCallback方法 )
+			},
+			/*上拉加载的回调*/
+			upCallback(page) {
+				let pageNum = page.num; // 页码, 默认从1开始
+				let pageSize = page.size; // 页长, 默认每页10条
+				this.params = Object.assign(this.params,{page:pageNum,rows:pageSize});
+				this.$api.http.post(this.config.apiBaseurl+'hotel/reserve/data',this.params,{
+					header: {
+						Accept:'application/json',
+						Authorization: 'Bearer '+ this.params.token, //注意Bearer后面有一空格
+					}
+				}).then(data => {
+					if(data.data.code=='1001'){
+						// uni.redirectTo({
+						// 	url:'/pages/login/login?backpage=/pages/index/index'+'&backtype='+2,
+						// });							
+					};
+					console.log(data);
+					// 接口返回的当前页数据列表 (数组)
+					let curPageData = data.data.rows; 
+					console.log('curPageData',curPageData);
+					// 接口返回的当前页数据长度 (如列表有26个数据,当前页返回8个,则curPageLen=8)
+					let curPageLen = curPageData.length; 
+					// 接口返回的总页数 (如列表有26个数据,每页10条,共3页; 则totalPage=3)
+					// let totalPage = data.xxx; 
+					// 接口返回的总数据量(如列表有26个数据,每页10条,共3页; 则totalSize=26)
+					let totalSize = data.data.total; 
+					// 接口返回的是否有下一页 (true/false)
+					// let hasNext = data.xxx; 
+					
+					//设置列表数据
+					if(page.num == 1) this.dataList = []; //如果是第一页需手动置空列表
+					this.dataList = this.dataList.concat(curPageData); //追加新数据
+					
+					//方法二(推荐): 后台接口有返回列表的总数据量 totalSize
+					this.mescroll.endBySize(curPageLen, totalSize); 
+			
+					// setTimeout(()=>{
+					// 	this.mescroll.endSuccess(curPageLen)
+					// },20)
+				
+				}).catch(err => {
+					this.mescroll.endErr()
+					console.log(err)
+				
+				});								
+			},
+
+			//选择图片
+			chooseimg() {
+				let _self = this;
+				uni.chooseImage({
+					count: 3,
+					sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
+					sourceType: ['album', 'camera'], //从相册选择
+					success: function(res) {
+						const tempFilePaths = res.tempFilePaths;
+						_self.image = tempFilePaths[0];
+						console.log("tempFilePaths[0]", tempFilePaths[0]) //能够打印出选中的图片
+						_self.iconcheck = 1; //点击后图片更改状态由0变成1
+						console.log('111', JSON.stringify(res.tempFilePaths));
+						_self.params.media = res.tempFilePaths;
+						console.log(_self.params.media);
+						
+						// 预览图片
+						// uni.previewImage({
+						// 	urls: res.tempFilePaths,
+						// 	longPressActions: {
+						// 		// itemList: [],
+						// 		success: function(data) {
+						// 			console.log('选中了第' + (data.tapIndex + 1) + '个按钮,第' + (data.index + 1) + '张图片');
+						// 		},
+						// 		fail: function(err) {
+						// 			console.log(err.errMsg);
+						// 		}
+						// 	}
+						// });
+								
+					},
+					error: function(e) {
+						console.log(e);
+					}
+				});
+			},
+			//发布
+			publish() {
+				this.checkStatus = this.check_form();
+				if(this.checkStatus){					
+					let _self = this;
+					//上传图片
+					// uni.uploadFile({
+					// 	url: _self.httpUrl + '', // 后端api接口
+					// 	filePath: _self.image, // uni.chooseImage函数调用后获取的本地文件路劲
+					// 	name: 'EmployeeImage', //后端通过'file'获取上传的文件对象(字段)
+					// 	formData: {
+					// 		// openid:_self.openid,  //剩下的字段
+					// 	},
+					// 	header: {
+					// 		"Content-Type": "multipart/form-data"
+					// 	},
+					// 	success: (res) => {
+					// 		console.log(res)
+					// 		if (res.statusCode == 200) {
+					// 			uni.showToast({
+					// 				icon: 'success',
+					// 				title: "添加成功...",
+					// 			})
+					// 		}
+					// 		setTimeout(() => {
+					// 			uni.navigateTo({
+					// 				url: ''
+					// 			})
+					// 		}, 2000)
+
+					// 	},
+					// 	error(error) {
+					// 		uni.showToast({
+					// 			title: "添加失败...",
+					// 		})
+					// 	}
+					// });
+					uni.showLoading({
+					    title: '请稍等'
+					});					
+					setTimeout(function () {
+					    uni.hideLoading();
+					}, 2000);
+					// setTimeout(()=>{
+					// 	uni.showToast({
+					// 		icon:'success',
+					// 		title:`发布成功,请等待审核!`,
+					// 		duration: 2000
+					// 	});						
+					// },2000);
+					
+					setTimeout(() => {
+						uni.redirectTo({
+							url:'/pages/publish/publishsuccess/publishsuccess'
+						})
+					}, 2000);
+				}
+
+			},
+			//表单验证
+			check_form() {
+				console.log('this.params',this.params);
+				if(this.params.type.length <= 0) {					
+					uni.showToast({icon:'none',title:`请填写类别!`,duration: 2000});					
+					return false;
+				}
+				// if(this.params.myposition.length <= 0) {
+				// 	console.log(this.params.myposition);
+				// 	uni.showToast({icon:'none',title:`请填写地理位置!`,duration: 2000});					
+				// 	return false;
+				// }
+				if(this.params.title.length <= 0) {
+					uni.showToast({icon:'none',title:`请填写标题!`,duration: 2000});					
+					return false;
+				}
+				if(this.params.brand.length <= 0) {
+					uni.showToast({icon:'none',title:`请填写品牌!`,duration: 2000});					
+					return false;
+				}
+				if(this.params.price.length <= 0) {
+					uni.showToast({icon:'none',title:`请填写零售价!`,duration: 2000});					
+					return false;
+				}
+				if(this.params.supplier.length <= 0) {
+					uni.showToast({icon:'none',title:`请填写供应商!`,duration: 2000});					
+					return false;
+				}
+				if(this.params.placeoforigin.length <= 0) {
+					uni.showToast({icon:'none',title:`请填写生产地!`,duration: 2000});					
+					return false;
+				}
+				if(this.params.specifications.length <= 0) {
+					uni.showToast({icon:'none',title:`请填写规格!`,duration: 2000});					
+					return false;
+				}
+				if(this.params.shelflife.length <= 0) {
+					uni.showToast({icon:'none',title:`请填写保质期!`,duration: 2000});					
+					return false;
+				}
+				if(this.params.date.length <= 0) {
+					uni.showToast({icon:'none',title:`请填写生产日期!`,duration: 2000});					
+					return false;
+				}
+				if(this.params.packaging.length <= 0) {
+					uni.showToast({icon:'none',title:`请填写包装!`,duration: 2000});					
+					return false;
+				}
+				if(this.params.productlabel.length <= 0) {
+					uni.showToast({icon:'none',title:`请填写标签!`,duration: 2000});					
+					return false;
+				}
+				if(this.params.media.length <= 0) {
+					uni.showToast({icon:'none',title:`请上传图片!`,duration: 2000});					
+					return false;
+				}				
+				return true;
+			},
+			//选择供应商
+			// supplierclick(name){
+			// 	this.params.supplier = name;
+			// 	this.supplieropen = false;
+			// 	this.showback();
+			// },
+			//打开供应商弹出框
+			// opensupplier(){
+			// 	this.supplieropen = true;
+			// 	this.hideback();
+			// },
+			// 搜索供应商
+			// searchsupplier(e){
+			// 	console.log(e);
+			// },
+			//选择生产地
+			placeoforiginclick(name){
+				this.params.placeoforigin = name;
+				this.placeoforiginopen = false;
+				this.showback();
+			},
+			//打开生产地弹出框
+			openplaceoforigin(){
+				this.placeoforiginopen = true;
+				this.hideback();
+			},
+			//选择规格
+			specificationsclick(e){
+				// this.params.specifications = name;
+				this.specificationsopen = false;
+				this.showback();
+			},
+			//打开规格弹出框
+			openspecifications(){
+				this.specificationsopen = true;
+				this.hideback();
+			},
+			// 规格checkbox
+			checkboxChange: function (e) {
+				var items = this.specificationslist,
+					values = e.detail.value;
+				for (var i = 0, lenI = items.length; i < lenI; ++i) {
+					const item = items[i]
+					if(values.includes(item.value)){
+						this.$set(item,'checked',true)
+					}else{
+						this.$set(item,'checked',false)
+					}
+				}
+				this.params.specifications = e.detail.value;
+				console.log(this.params.specifications);
+			},
+			resetcheckbox(){
+				var items = this.specificationslist;
+				for (var i = 0, lenI = items.length; i < lenI; ++i) {
+					const item = items[i];
+					this.$set(item,'checked',false)				
+				}
+				this.params.specifications = [];
+				// console.log(this.params.specifications);
+			},
+			//隐藏返回按钮
+			hideback(){
+				// let backbtn = document.getElementsByClassName('uni-page-head-hd')[0];
+				// backbtn.style.display = 'none';
+				this.backtyep = 2;
+				console.log(this.backtyep)
+			},
+			// 显示返回按钮
+			showback(){
+				// let backbtn = document.getElementsByClassName('uni-page-head-hd')[0];
+				// backbtn.style.display = 'block';
+				this.backtyep = 1;
+				console.log(this.backtyep)
+			},
+			// 导航返回按钮
+			onBackPress(e){
+				console.log(this.backtyep)
+				if(this.backtyep == 2){
+					this.placeoforiginopen = false;
+					this.backtyep = 1;
+					return true;
+				};				
+			},
+			// 选择位置
+			chooseaddr(){
+				let self = this;
+				uni.chooseLocation({
+					success: function (res) {
+						self.params.myposition = res.address;
+						console.log('位置名称:' + res.name);
+						console.log('详细地址:' + res.address);
+						console.log('纬度:' + res.latitude);
+						console.log('经度:' + res.longitude);
+					}
+				});
+			},
+			// 搜索产地
+			searchplace(e){							
+				if(e.value){
+				let place = this.placeoforiginlist.filter(function (value) {
+						console.log(value.detailAddress);	
+					  return value.detailAddress.includes(e.value);
+				});
+				console.log('place',place);
+				this.placeoforiginlist = place;
+				}else{
+					this.placeoforiginlist = this.places;
+				}
+			}
+			
+			
+
+		}
+	}
+</script>
+
+<style >
+	@import url("./publish.css");
+</style>

+ 46 - 0
pages/publish/publishsuccess/publishsuccess.vue

@@ -0,0 +1,46 @@
+<template>
+	<view class="pages">
+		<view class="tip-icon-wrap">
+			<image class="tip-icon" src="/static/img/icon-success.png" mode=""></image>
+		</view>
+		<view class="tip-text">操作成功</view>
+		<view class="tip-sub">商品已发布成功,请等待后台审核</view>
+		<navigator url="/pages/index/index" class="big-btn primary" open-type="switchTab">返回主页</navigator>
+		<!-- <navigator url="/" class="big-btn big-btn-hollow">查看商品</navigator> -->
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				params:{
+					token:'',
+				}
+				
+			}
+		},
+		onShow() {
+			let serf = this;
+			//检查登录,获取token
+			// let loginRes = this.checkLogin('/pages/index/index', '2');
+			// if(!loginRes){return false;}
+			// serf.params.token=loginRes[0];			
+		},
+		onLoad() {
+
+		},
+		methods: {
+
+		}
+	}
+</script>
+
+<style scoped>
+	/* @import url("./publishsuccess.css"); */
+	page{background-color: #fff;}
+	.tip-icon-wrap{text-align: center;}
+	.tip-icon{width: 128rpx;height: 128rpx;margin-top: 64rpx;margin-bottom: 40rpx;}
+	.tip-text{margin-bottom: 16rpx;font-size: 36rpx;font-weight: 400;color: #333;line-height: 50rpx;text-align: center;}
+	.tip-sub{margin-bottom: 104rpx;min-height: 74rpx;font-size: 26rpx;font-weight: 400;color: #999;line-height: 37rpx;text-align: center;}
+</style>

+ 33 - 0
pages/publish/publishtype/publishtype.css

@@ -0,0 +1,33 @@
+.producttype-wrap >>> .uni-searchbar__box{justify-content: left;border-color: #6BBC6D;border-radius: 50rpx!important;background-color: #fff!important;}
+.producttype-wrap >>> .uni-searchbar{padding-left: 0;}
+
+.producttype{display: flex;background: #fff;}
+.producttype-nav{width: 200rpx;height: 100%;box-sizing: border-box;background-color: #F6F6F9;overflow-y: auto;}
+.producttype-nav-item{position: relative;padding: 29rpx 0;box-sizing: border-box;text-align: center;font-size: 30rpx;font-weight: 500;color: #333;line-height: 42rpx;}
+.producttype-nav-item.active{background-color: #fff;color: #6BBC6D;}
+.producttype-nav-item.active::before{content: '';position: absolute;left: 0;top: 50%;margin-top: -15rpx;width: 6rpx;height: 30rpx;background: #6BBC6D;border-radius: 3px;}
+
+.producttype-con{position: relative;flex: 1;margin: 0 0 0 22rpx;padding-top: 20rpx;}
+
+.search-typelist-item{display: flex;margin-left: 22rpx;height: 84rpx;align-items: center;border-bottom: 1px solid #eee;}
+.search-typelist-item-serial{margin-right: 24rpx;padding: 5rpx 13rpx;min-width: 35rpx;text-align: center;background-color: #6BBC6D;color: #fff;}
+.search-typelist-item-name{font-size: 24rpx;}
+
+
+/* 
+.producttype-con-block{padding: 24rpx;margin-bottom: 20rpx;border: 1px solid #ddd;border-radius: 10rpx;}
+.producttype-con-block-til{margin-bottom: 10rpx;}
+.producttype-con-block-con{display: flex;flex-direction: row;flex-wrap: wrap;}
+.producttype-con-block-item{width: 31%;margin: 0 1% 10rpx;box-sizing: border-box;padding: 0 10rpx;text-align: center;}
+.producttype-con-block-noimg .producttype-con-block-item{border: 1px solid #ddd;padding: 10rpx;}
+.producttype-con-block-con-img{height: 200rpx;width: 100%;}
+ */
+
+
+
+
+
+
+
+
+

+ 184 - 0
pages/publish/publishtype/publishtype.vue

@@ -0,0 +1,184 @@
+<template>
+	<view class="pages producttype-wrap">
+		<view class="producttype fix-content">
+			<view class="producttype-nav">
+				<view v-for="(item,index) in navitem" :key="index" @click="typeclick(index)" :class="{active:index==ins}" class="producttype-nav-item">{{ item }}</view>
+			</view>
+			<view class="producttype-con">
+				<view class="search">
+					<view class="searchTxt">
+						<uni-search-bar placeholder="产品名称" @confirm="search" @input="search" ></uni-search-bar>
+					</view>
+				</view>
+				<mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption" :up="upOption">
+					<view class="search-typelist">
+						<view v-for="(item,index) in typelist" :key="index" @click="publishtype(item.name)" class="search-typelist-item">
+							<view class="search-typelist-item-serial">{{ index +1 }}</view>
+							<view class="search-typelist-item-name">{{item.name}}</view>
+						</view>					
+					</view>	 
+				</mescroll-body>				
+			</view>
+			<!-- producttype-con end -->
+		</view>
+		<!-- fix-content end -->
+		
+	</view>
+</template>
+
+<script>
+	// 引入mescroll-mixins.js
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";	
+	// 引入mescroll-body组件 (如已在main.js注册全局组件,则省略此步骤)
+	import MescrollBody from "@/components/mescroll-uni/mescroll-body.vue"; // 注意.vue后缀不能省
+	
+	import uniSearchBar from '@/components/uni-search-bar/uni-search-bar.vue'
+	export default {
+		mixins: [MescrollMixin], // 使用mixin
+		components: {
+			MescrollBody,
+			uniSearchBar,
+			
+		
+		},
+		data() {
+			return {
+				params:{
+					token:'',
+					product:'',
+				},
+				mescroll: null, // mescroll实例对象 (此行可删,mixins已默认)
+				// 下拉刷新的配置(可选, 绝大部分情况无需配置)
+				downOption: { 
+					// ...
+				},
+				// 上拉加载的配置(可选, 绝大部分情况无需配置)
+				upOption: {
+					page: {
+						size: 10 // 每页数据的数量,默认10
+					},
+					noMoreSize: 5, // 配置列表的总数量要大于等于5条才显示'-- END --'的提示
+					empty: {
+						tip: '暂无相关数据'
+					}
+				},
+				//nav active
+				ins:0,
+				//菜单类别
+				navitem:['竹','油茶','花椒','皂角','刺梨','无患子','核桃','石斛','茶','林下食用菌','鲜果','林下肉蛋禽','板栗','林下蜂蜜','林下中药材'],
+				//热门列表
+				// hotlist:[
+				// ],
+				//typelist
+				typelist:[
+					{name:'松树'},
+					{name:'枫树'},
+					{name:'柏树'},
+					{name:'紫薇'},
+					{name:'槐树'},
+					{name:'女贞'},
+				],
+				//显示的分类标题
+				nowtype:'',
+				
+				
+				
+			}
+		},
+		onShow() {
+			let serf = this;
+			//检查登录,获取token
+			// let loginRes = this.checkLogin('/pages/index/index', '2');
+			// if(!loginRes){return false;}
+			// serf.params.token=loginRes[0];			
+		},
+		onLoad() {
+			this.nowtype = this.navitem[this.ins];
+			this.params.product = this.nowtype;
+		},
+		methods: {
+			//搜索
+			search(e) {
+				console.log(e);
+				// this.poorList = [];
+				// this.params.farmerName = e.value;
+				// this.pagination = Object.assign(this.pagination,{"farmerName":e.value});
+				// this.pagination.pageNo = 1;
+				// this.getPoorList(this.pagination)
+			},
+			
+			/*mescroll组件初始化的回调,可获取到mescroll对象 (此处可删,mixins已默认)*/
+			mescrollInit(mescroll) {
+				this.mescroll = mescroll;
+			},
+			downCallback(){
+				this.mescroll.resetUpScroll(); // 重置列表为第一页 (自动执行 page.num=1, 再触发upCallback方法 )
+			},
+			/*上拉加载的回调*/
+			upCallback(page) {
+				let pageNum = page.num; // 页码, 默认从1开始
+				let pageSize = page.size; // 页长, 默认每页10条
+				this.params = Object.assign(this.params,{page:pageNum,rows:pageSize});
+				this.$api.http.post(this.config.apiBaseurl+'hotel/reserve/data',this.params,{
+					header: {
+						Accept:'application/json',
+						Authorization: 'Bearer '+ this.params.token, //注意Bearer后面有一空格
+					}
+				}).then(data => {
+					if(data.data.code=='1001'){
+						// uni.redirectTo({
+						// 	url:'/pages/login/login?backpage=/pages/index/index'+'&backtype='+2,
+						// });							
+					};
+					console.log(data);
+					// 接口返回的当前页数据列表 (数组)
+					let curPageData = data.data.rows; 
+					console.log('curPageData',curPageData);
+					// 接口返回的当前页数据长度 (如列表有26个数据,当前页返回8个,则curPageLen=8)
+					let curPageLen = curPageData.length; 
+					// 接口返回的总页数 (如列表有26个数据,每页10条,共3页; 则totalPage=3)
+					// let totalPage = data.xxx; 
+					// 接口返回的总数据量(如列表有26个数据,每页10条,共3页; 则totalSize=26)
+					let totalSize = data.data.total; 
+					// 接口返回的是否有下一页 (true/false)
+					// let hasNext = data.xxx; 
+					
+					//设置列表数据
+					if(page.num == 1) this.dataList = []; //如果是第一页需手动置空列表
+					this.dataList = this.dataList.concat(curPageData); //追加新数据
+					
+					//方法二(推荐): 后台接口有返回列表的总数据量 totalSize
+					this.mescroll.endBySize(curPageLen, totalSize); 
+			
+					// setTimeout(()=>{
+					// 	this.mescroll.endSuccess(curPageLen)
+					// },20)
+				
+				}).catch(err => {
+					this.mescroll.endErr()
+					console.log(err)
+				
+				});								
+			},
+			
+			//左侧导航点击
+			typeclick(num){
+				this.ins=num;
+				this.nowtype = this.navitem[num];
+				this.params.product = this.nowtype;
+				this.downCallback();
+			},
+			//产品点击
+			publishtype(product){
+				uni.navigateTo({
+					url:`/pages/publish/publish?type=${product}`,					
+				})				
+			},
+
+		}
+	}
+</script>
+
+<style scoped>
+	@import url("./publishtype.css");
+</style>

+ 15 - 0
pages/searchresults/searchresults.css

@@ -0,0 +1,15 @@
+/* 搜索修改 */
+.search-wrap{padding: 10rpx 8rpx;background-color: #fff;}
+.search-wrap >>> .uni-searchbar__box{justify-content: left;border: 0;background-color: #F1F1F2!important;}
+
+.results-item{display: flex;margin-bottom: 24rpx;border-bottom: 1px solid #eee;}
+.results-item-img-wrap{width: 220rpx;height: 220rpx;margin-right: 24rpx;overflow: hidden;border-radius: 12rpx;}
+.results-item-img-wrap .results-item-img{width: 100%;height: 100%;}
+.results-item-info{flex: 1;}
+.results-item-info-til{margin-bottom: 10rpx;}
+.results-item-info-brand{color: #999;margin-bottom: 10rpx;}
+.results-item-info-addr{color: #666;margin-bottom: 10rpx;font-size: 24rpx;}
+.results-item-info-label .postage{height: 24rpx;padding: 0 14rpx;margin-left: 16rpx;line-height: 24rpx;display: inline-block;font-size: 20rpx;background-color: #E1F2E2;border-radius: 12rpx;}
+.results-item-info-price .rmb{font-size: 28rpx;font-weight: 500;color: #FF5030;line-height: 40rpx;}
+.results-item-info-price .price{font-size: 36rpx;font-weight: 500;color: #FF5030;line-height: 50rpx;}
+.results-item-info-price .minsell{font-size: 24rpx;font-weight: 400;color: #999999;line-height: 33rpx;}

+ 178 - 0
pages/searchresults/searchresults.vue

@@ -0,0 +1,178 @@
+<template>
+	<view class="pages">
+		
+		<view class="search-wrap nomal-bottom">
+			<view class="search">
+				<view class="searchTxt">
+					<uni-search-bar :placeholder="searchtxt" @confirm="search" @input="search" ></uni-search-bar>
+				</view>
+			</view>	
+		</view>
+		<!-- 搜索 结束 -->
+		<mescroll-body class="wrap" ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption" :up="upOption">
+			<view class="results write-radius">
+				<view v-for="(item,index) in dataList" :key="index" @click="productdetails(item.id)" class="results-item">
+					<view class="results-item-img-wrap"><image :src="$getimg+item.pic|miniImg(60)+'/thumbnail/358x358'" mode="aspectFit" class="results-item-img"></image></view>				
+					<view class="results-item-info">
+						<view class="results-item-info-til">{{item.name}}</view>
+						<view class="results-item-info-brand">{{item.brandName}}</view>
+						<!-- <view class="results-item-info-addr">{{item.placeOfProduction}}</view> -->						
+						<view class="results-item-info-label">
+							<text v-if="item.postage" class="postage">包邮</text>
+						</view>
+						<view class="results-item-info-company">{{item.umsCompanyInfo}}</view>
+						<view class="results-item-info-price">
+							<text class="rmb">¥</text>
+							<text class="price">{{item.price}}</text>
+							<text class="unit">/{{item.unit}}</text>
+							<!-- <text class="minsell">4000斤起售</text> -->
+						</view>						
+					</view>
+				</view>			
+			</view>		 
+		</mescroll-body>	
+	</view>
+</template>
+
+<script>
+	// 引入mescroll-mixins.js
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";	
+	// 引入mescroll-body组件 (如已在main.js注册全局组件,则省略此步骤)
+	import MescrollBody from "@/components/mescroll-uni/mescroll-body.vue"; // 注意.vue后缀不能省
+	
+	import uniSearchBar from '@/components/uni-search-bar/uni-search-bar.vue'
+	export default {
+		mixins: [MescrollMixin], // 使用mixin
+		components: {
+			MescrollBody,
+			uniSearchBar,
+			
+		
+		},
+		data() {
+			return {
+				searchtxt:'产品名称',
+				params:{
+					token:'',
+					keyword:'',
+					productCategoryId:'',
+				},
+				mescroll: null, // mescroll实例对象 (此行可删,mixins已默认)
+				// 下拉刷新的配置(可选, 绝大部分情况无需配置)
+				downOption: { 
+					// ...
+				},
+				// 上拉加载的配置(可选, 绝大部分情况无需配置)
+				upOption: {
+					page: {
+						size: 10 // 每页数据的数量,默认10
+					},
+					noMoreSize: 5, // 配置列表的总数量要大于等于5条才显示'-- END --'的提示
+					empty: {
+						tip: '暂无相关数据'
+					}
+				},
+				// 列表数据
+				dataList:[],
+				
+			}
+		},
+		onShow() {
+			let serf = this;
+			//检查登录,获取token
+			// let loginRes = this.checkLogin('/pages/index/index', '2');
+			// if(!loginRes){return false;}
+			// serf.params.token=loginRes[0];			
+		},
+		onLoad(option) {
+			
+			if(option.productCategoryId){
+				this.params.productCategoryId = option.productCategoryId;
+				this.searchtxt = option.name;
+			}
+			if(option.keyword){
+				this.params.keyword = option.keyword;
+				this.searchtxt = option.keyword;
+			}
+			// console.log(this.params.keyword);
+
+		},
+		methods: {
+			//搜索
+			search(e) {
+				console.log(e);
+				this.params.keyword = e.value;
+				this.params.productCategoryId = '';
+				this.searchtxt = e.value;
+				this.downCallback();
+				// this.poorList = [];
+				// this.params.farmerName = e.value;
+				// this.pagination = Object.assign(this.pagination,{"farmerName":e.value});
+				// this.pagination.pageNo = 1;
+				// this.getPoorList(this.pagination)
+			},
+			
+			/*mescroll组件初始化的回调,可获取到mescroll对象 (此处可删,mixins已默认)*/
+			mescrollInit(mescroll) {
+				this.mescroll = mescroll;
+			},
+			downCallback(){
+				this.mescroll.resetUpScroll(); // 重置列表为第一页 (自动执行 page.num=1, 再触发upCallback方法 )
+			},
+			/*上拉加载的回调*/
+			upCallback(page) {
+				let pageNum = page.num; // 页码, 默认从1开始
+				let pageSize = page.size; // 页长, 默认每页10条
+				this.params = Object.assign(this.params,{pageNum:pageNum,pageSize:pageSize});
+				this.$api.http.get(this.config.apiBaseurl+'/product/search',{params: this.params}).then(data => {
+					if(data.data.code=='1001'){
+						// uni.redirectTo({
+						// 	url:'/pages/login/login?backpage=/pages/index/index'+'&backtype='+2,
+						// });							
+					};
+					console.log('data',JSON.parse(JSON.stringify(data)));
+					// 接口返回的当前页数据列表 (数组)
+					let curPageData = data.data.data.list;
+					console.log('curPageData',JSON.parse(JSON.stringify(curPageData)));
+					// 接口返回的当前页数据长度 (如列表有26个数据,当前页返回8个,则curPageLen=8)
+					let curPageLen = curPageData.length; 
+					// 接口返回的总页数 (如列表有26个数据,每页10条,共3页; 则totalPage=3)
+					let totalPage =  data.data.data.totalPage; 
+					// 接口返回的总数据量(如列表有26个数据,每页10条,共3页; 则totalSize=26)
+					let totalSize = data.data.data.total; 
+					// 接口返回的是否有下一页 (true/false)
+					// let hasNext = data.xxx; 
+					console.log('totalPage',totalPage,'curPageLen',curPageLen);
+					//设置列表数据
+					if(page.num == 1) this.dataList = []; //如果是第一页需手动置空列表
+					this.dataList = this.dataList.concat(curPageData); //追加新数据
+					// 请求成功,隐藏加载状态
+					//方法一(推荐): 后台接口有返回列表的总页数 totalPage
+					this.mescroll.endByPage(curPageLen, totalPage); 
+					//方法二(推荐): 后台接口有返回列表的总数据量 totalSize
+					// this.mescroll.endBySize(curPageLen, totalSize); 
+			
+					// setTimeout(()=>{
+					// 	this.mescroll.endSuccess(curPageLen)
+					// },20)
+				
+				}).catch(err => {
+					this.mescroll.endErr()
+					console.log(err)
+				
+				});								
+			},
+			//跳转详情
+			productdetails(id){
+				uni.navigateTo({
+					url:`/pages/product/product?id=${id}`
+				})				
+			},
+
+		}
+	}
+</script>
+
+<style scoped>
+	@import url("./searchresults.css");
+</style>

+ 161 - 0
pages/supplier/supplier.vue

@@ -0,0 +1,161 @@
+<template>
+	<view class="pages">
+		<view class="supplier-page translate open" >
+			<view class="search-wrap search-left-color">
+				<view class="search">
+					<view class="searchTxt">
+						<uni-search-bar placeholder="搜索供应商关键词" @confirm="searchsupplier" @input="searchsupplier" ></uni-search-bar>
+					</view>
+				</view>	
+			</view>
+			<!-- 搜索 结束 -->
+			<view class="simplepage-wrap">
+				<mescroll-body ref="mescrollRef0" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption" :up="upOption">
+					<view>
+						<view v-for="(item,index) in supplierlist" :key="index" @click="supplierclick(item.id)" class="simplepage-item">{{item.compName}}</view>					
+					</view>	 					
+				</mescroll-body>
+			</view>
+		</view>
+		<!-- 供应商结束 -->
+	</view>
+</template>
+
+<script>
+	// 引入mescroll-mixins.js
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";	
+	// 引入mescroll-body组件 (如已在main.js注册全局组件,则省略此步骤)
+	import MescrollBody from "@/components/mescroll-uni/mescroll-body.vue"; // 注意.vue后缀不能省
+	
+	import uniSearchBar from '@/components/uni-search-bar/uni-search-bar.vue'
+	export default {
+		mixins: [MescrollMixin], // 使用mixin
+		components: {
+			MescrollBody,
+			uniSearchBar,
+			
+		
+		},
+		data() {
+			return {
+				params:{
+					token:'',
+					tokenhead:'',
+					key:'',
+				},
+				supplierlist:[],
+				mescroll: null, // mescroll实例对象 (此行可删,mixins已默认)
+				// 下拉刷新的配置(可选, 绝大部分情况无需配置)
+				downOption: { 
+					// ...
+				},
+				// 上拉加载的配置(可选, 绝大部分情况无需配置)
+				upOption: {
+					page: {
+						size: 10 // 每页数据的数量,默认10
+					},
+					noMoreSize: 5, // 配置列表的总数量要大于等于5条才显示'-- END --'的提示
+					empty: {
+						tip: '暂无相关数据'
+					}
+				},
+				
+			}
+		},
+		onShow() {
+			let self = this;
+			//检查登录,获取token
+			// let loginRes = this.checkLogin('/pages/index/index', '2');
+			// if(!loginRes){return false;}
+			// serf.params.token=loginRes[0];
+			uni.getStorage({
+				key:'token',
+				success: function (res) {
+					self.params.token = res.data;
+				   // console.log(res.data);
+				}
+			});
+			uni.getStorage({
+				key:'tokenhead',
+				success: function (res) {
+					self.params.tokenhead = res.data;
+				   // console.log(res.data);
+				}
+			});
+		},
+		onLoad() {
+			
+
+		},
+		methods: {
+			searchsupplier(e){
+				console.log(e);
+				this.params.key = e.value;
+				this.mescroll.resetUpScroll();
+			},
+			/*mescroll组件初始化的回调,可获取到mescroll对象 (此处可删,mixins已默认)*/
+			mescrollInit(mescroll) {
+				this.mescroll = mescroll;
+			},
+			downCallback(){
+				this.mescroll.resetUpScroll(); // 重置列表为第一页 (自动执行 page.num=1, 再触发upCallback方法 )
+			},
+			/*上拉加载的回调*/
+			upCallback(page) {
+				let pageNum = page.num; // 页码, 默认从1开始
+				let pageSize = page.size; // 页长, 默认每页10条
+				this.params = Object.assign(this.params,{page:pageNum,rows:pageSize});
+				let thetoken = this.params.tokenhead+this.params.token;
+				this.$api.http.get(this.config.apiBaseurl+'/company/cp/search',{header: {Authorization:thetoken},params:{key:this.params.key}}).then(data => {
+					if(data.data.code=='1001'){
+						// uni.redirectTo({
+						// 	url:'/pages/login/login?backpage=/pages/index/index'+'&backtype='+2,
+						// });							
+					};
+					console.log(data);
+					// 接口返回的当前页数据列表 (数组)
+					let curPageData = data.data.data; 
+					console.log('curPageData',curPageData);
+					// 接口返回的当前页数据长度 (如列表有26个数据,当前页返回8个,则curPageLen=8)
+					let curPageLen = curPageData.length; 
+					// 接口返回的总页数 (如列表有26个数据,每页10条,共3页; 则totalPage=3)
+					// let totalPage = data.xxx; 
+					// 接口返回的总数据量(如列表有26个数据,每页10条,共3页; 则totalSize=26)
+					let totalSize = data.data.total; 
+					// 接口返回的是否有下一页 (true/false)
+					// let hasNext = data.xxx; 
+					
+					//设置列表数据
+					if(page.num == 1) this.supplierlist = []; //如果是第一页需手动置空列表
+					this.supplierlist = this.supplierlist.concat(curPageData); //追加新数据
+					
+					//方法二(推荐): 后台接口有返回列表的总数据量 totalSize
+					this.mescroll.endBySize(curPageLen, totalSize); 
+			
+					// setTimeout(()=>{
+					// 	this.mescroll.endSuccess(curPageLen)
+					// },20)
+				
+				}).catch(err => {
+					this.mescroll.endErr()
+					console.log(err)
+				
+				});								
+			},
+			//跳转详情
+			supplierclick(id){
+				console.log(id);
+				uni.navigateTo({
+					url:`/pages/supplier/supplierdetail/supplierdetail?id=${id}`
+				})
+			}
+
+		}
+	}
+</script>
+
+<style scoped>
+	/* @import url("./supplier.css"); */
+	.simplepage-wrap{padding-left: 24rpx;}
+	.simplepage-item{padding: 24rpx 0;border-bottom: 1px solid #eee;}
+</style>

+ 49 - 0
pages/supplier/supplierdetail/supplierdetail.css

@@ -0,0 +1,49 @@
+.block-til{display: flex;margin-bottom: 24rpx;justify-content: space-between;align-items: center;height: 90rpx;border-bottom: 1px solid #eee;}
+.block-til-left{font-size: 30rpx;font-weight: bold;line-height: 42rpx;color: #333;}
+.block-til-right{font-weight: 400;color: #6BBC6D;line-height: 33px;}
+.safe-icon{width: 36rpx;height: 36rpx;margin-right: 10rpx;display: inline-block;vertical-align: -10rpx;}
+.safe-icon-img{width: 100%;height: 100%;}
+
+.full-img-wrap{padding: 0 40rpx 24rpx;background-color: #fff;}
+.base-info{padding: 0 40rpx;background-color: #fff;margin-bottom: 24rpx;padding-bottom: 24rpx;}
+.base-info-til{margin-bottom: 8rpx;font-size: 32rpx;font-weight: 500;color: #333;line-height: 45rpx;}
+.base-info-item{font-size: 26rpx;font-weight: 400;color: #999;line-height: 37rpx;margin-bottom: 16rpx;}
+.base-info-item .item + .item{margin-left: 50rpx;}
+
+.shop-link{display: flex;justify-content: space-between;}
+.shop-link-item{flex: 1;text-align: center;}
+.shop-link-item .shop-img{width: 64rpx;height: 64rpx;display: inline-block;}
+.shop-link-item .shop-img image{width: 100%;height: 100%;}
+.shop-link-item + .shop-link-item{margin-left: 16rpx;}
+
+.supplier-info-til{font-size: 34rpx;font-weight: 500;color: #333;line-height: 48rpx;margin-bottom: 8rpx;}
+.supplier-info-item{margin-bottom: 8rpx;color: #999;}
+.supplier-info-item .text + .text{margin-left: 50rpx;}
+.supplier-info-certificate{display: flex;flex-wrap: wrap;margin-top: 24rpx;}
+.supplier-info-certificate .image{width: 50%;flex: 1;}
+.supplier-info-certificate .image + .image{margin-left: 20rpx;}
+
+.placeoforigin-wrap{width: 80%;}
+.placeoforigin-banner{overflow: hidden;border-top-left-radius: 24rpx;border-top-right-radius: 24rpx;}
+.placeoforigin-banner-img{width: 100%;height: 100%;}
+.placeoforigin-text-til{font-size: 34rpx;font-weight: 500;color: #333;line-height: 48rpx;}
+.placeoforigin-text{font-size: 26rpx;font-weight: 400;color: #999;line-height: 37rpx;}
+.placeoforigin-text-item .text + .text{margin-left: 40rpx;}
+
+.placeoforigin-scroll-view{flex: 1;overflow: hidden;white-space: nowrap;}
+.placeoforigin-scroll-view .placeoforigin-wrap{display: inline-block;margin: 0 23rpx;}
+
+.results-item{display: flex;margin-bottom: 24rpx;border-bottom: 1px solid #eee;}
+.results-item-img-wrap{width: 220rpx;height: 220rpx;margin-right: 24rpx;overflow: hidden;border-radius: 12rpx;}
+.results-item-img-wrap .results-item-img{width: 100%;height: 100%;}
+.results-item-info{flex: 1;}
+.results-item-info-til{margin-bottom: 10rpx;}
+.results-item-info-brand{color: #999;margin-bottom: 10rpx;}
+.results-item-info-addr{color: #666;margin-bottom: 10rpx;font-size: 24rpx;}
+.results-item-info-label .postage{height: 24rpx;padding: 0 14rpx;margin-left: 16rpx;line-height: 24rpx;display: inline-block;font-size: 20rpx;background-color: #E1F2E2;border-radius: 12rpx;}
+.results-item-info-price .rmb{font-size: 28rpx;font-weight: 500;color: #FF5030;line-height: 40rpx;}
+.results-item-info-price .price{font-size: 36rpx;font-weight: 500;color: #FF5030;line-height: 50rpx;}
+.results-item-info-price .minsell{font-size: 24rpx;font-weight: 400;color: #999999;line-height: 33rpx;}
+
+
+

+ 199 - 0
pages/supplier/supplierdetail/supplierdetail.vue

@@ -0,0 +1,199 @@
+<template>
+	<view class="pages">
+		<view class="supplierdetail">
+			
+			<view class="full-img-wrap">
+				<image class="full-img" v-if="firstcompanyimg" :src="firstcompanyimg|miniImg(75)" mode="widthFix"></image>
+			</view>	
+			<view class="base-info">
+				<view class="base-info-til">{{companyinfo.compName}}</view>
+				<view class="base-info-item">
+					<text class="item">供应商资质:{{companyinfo.compQual}}</text>
+					<text class="item">经营产品:{{companyinfo.compManageProduct}}</text>
+				</view>				
+			</view>
+			<!-- base-info end -->
+			<view class="shop write-radius wrap" v-if="shops.length>0">
+				<view class="block-til">
+					<view class="block-til-left">店铺</view>
+				</view>
+				<view class="shop-link">
+					<view v-for="(item,index) in shops" :key="index" class="shop-link-item" @click="shopclick(item.shopUrl)">
+						<view class="shop-img">
+							<image :src="item.shopImg" mode=""></image>
+						</view>						
+						<view class="shop-name" v-if="item.shopType == 'taobao'">天猫商城</view>
+						<view class="shop-name" v-else-if="item.shopType == 'jd'">京东商城</view>
+						<view class="shop-name" v-else>自营商城</view>
+					</view>
+				</view>				
+			</view>
+			<!-- shop end -->
+			<view class="write-radius supplier wrap">
+				<view class="block-til">
+					<view class="block-til-left">供应商</view>
+					<view class="block-til-right" v-if="companyinfo.isCert==1">
+						<view class="safe-icon">
+							<image class="safe-icon-img" src="/static/img/icon-safe.png" mode=""></image>
+						</view>
+						商家已缴纳诚信保证金
+					</view>
+				</view>
+				<view class="supplier-info">
+					<view class="supplier-info-til">{{companyinfo.compName}}</view>
+					<view class="supplier-info-item">
+						<text class="text">联系人:{{companyinfo.compConPerson}}</text>
+						<text class="text">联系电话:{{companyinfo.compConNum}}</text>
+					</view>
+					<view class="supplier-info-item">
+						地址:{{companyinfo.detailAddress}}
+					</view>
+					<view class="supplier-info-item">
+						社会信用代码:{{companyinfo.compSocialCode}}
+					</view>
+					<view class="supplier-info-item">
+						生产许可证号:{{companyinfo.compManageLicenseNum}}
+					</view>
+					<view class="supplier-info-item">
+						简介:{{companyinfo.compSum}}
+					</view>
+					<view class="supplier-info-certificate">
+						<image v-for="(img,index) in companyimg" :key="index" :src="img.fileUrl|miniImg(75)" mode="widthFix"></image>
+						<!-- <image class="image" src="../../../static/img/banner-supplierdetail.png" mode="widthFix"></image>
+						<image class="image" src="../../../static/img/banner-supplierdetail.png" mode="widthFix"></image> -->
+					</view>
+				</view>
+			</view>
+			<!-- 供应商 end -->
+			<view class="write-radius placeoforigin wrap" v-if="origines.length > 0">
+				<view class="block-til">
+					<view class="block-til-left">生产地</view>
+				</view>
+				<scroll-view scroll-x="true" class="scroll-X placeoforigin-scroll-view">
+					<view class="placeoforigin-wrap"  v-for="(item,index) in origines" :key="index">
+						<view class="placeoforigin-banner">
+							<image class="placeoforigin-banner-img" :src="item.imgUrl||'/static/img/placeoforigin-banner.png'|miniImg(75)" mode="widthFix"></image>
+						</view>
+						<view class="placeoforigin-text">
+							<view class="placeoforigin-text-til">{{item.originName}}</view>
+							<view class="placeoforigin-text-item">
+								<text class="text">面积:{{item.originArea}}</text>
+								<text class="text">人员数:{{item.peopleNum}}人</text>
+							</view>
+							<view class="placeoforigin-text-item">
+								地址:{{item.detailAddress}}
+							</view>
+						</view>
+					</view>
+				</scroll-view>
+			</view>
+			<!-- 生产地 end -->
+			<view class="results write-radius" v-if="products.length > 0">
+				<view v-for="(item,index) in products" :key="index" @click="productdetails(item.id)" class="results-item">
+					<view class="results-item-img-wrap"><image :src="item.pic|miniImg(75)" mode="aspectFit" class="results-item-img"></image></view>				
+					<view class="results-item-info">
+						<view class="results-item-info-til">{{item.name}}</view>
+						<view class="results-item-info-brand">{{item.brandName}}</view>
+						<!-- <view class="results-item-info-addr">{{item.placeOfProduction}}</view> -->						
+						<view class="results-item-info-label">
+							<text v-if="item.postage" class="postage">包邮</text>
+						</view>
+						<view class="results-item-info-company">{{item.umsCompanyInfo}}</view>
+						<view class="results-item-info-price">
+							<text class="rmb">¥</text>
+							<text class="price">{{item.price}}</text>
+							<text class="unit">/{{item.unit}}</text>
+							<!-- <text class="minsell">4000斤起售</text> -->
+						</view>						
+					</view>
+				</view>			
+			</view>	
+			
+			
+		</view>		
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				params:{
+					token:'',
+				},
+				companyinfo:[],
+				shops:[],
+				companyimg:[],
+				firstcompanyimg:'',
+				origines:[],
+				products:[],
+				
+			}
+		},
+		onShow() {
+			let serf = this;
+			//检查登录,获取token
+			// let loginRes = this.checkLogin('/pages/index/index', '2');
+			// if(!loginRes){return false;}
+			// serf.params.token=loginRes[0];			
+		},
+		onLoad(option) {
+			let self = this;
+			//检查登录,获取token
+			// let loginRes = this.checkLogin('/pages/index/index', '2');
+			// if(!loginRes){return false;}
+			// serf.params.token=loginRes[0];
+			uni.getStorage({
+				key:'token',
+				success: function (res) {
+					self.params.token = res.data;
+				   // console.log(res.data);
+				}
+			});
+			uni.getStorage({
+				key:'tokenhead',
+				success: function (res) {
+					self.params.tokenhead = res.data;
+				   // console.log(res.data);
+				}
+			});
+			let thetoken = this.params.tokenhead+this.params.token;
+			uni.showLoading({
+			    title: '请稍等'
+			});	
+			this.$api.http.get(this.config.apiBaseurl+'/company/cp/detail',{header: {Authorization:thetoken},params:{compId:option.id}}).then(res =>{	
+				uni.hideLoading();
+				this.companyinfo = res.data.data;
+				this.companyimg = res.data.data.files;			
+				this.shops = res.data.data.shops;
+				// console.log('this.companyimg',this.companyimg);
+				this.products = res.data.data.products;
+				this.origines = res.data.data.origines;
+				console.log('res',JSON.parse(JSON.stringify(res)));
+				this.firstcompanyimg = this.companyimg[0].fileUrl||'';
+				// console.log('this.companyinfo',JSON.parse(JSON.stringify(this.companyinfo)));
+			});
+			// console.log(option);
+
+		},
+		methods: {
+			//跳转详情
+			productdetails(id){
+				uni.navigateTo({
+					url:`/pages/product/product?id=${id}`
+				})				
+			},
+			// shopclick
+			shopclick(url){
+				window.location.assign(url);
+			}
+
+		}
+	}
+</script>
+
+<style scoped>
+	
+	@import url("./supplierdetail.css");
+	page{background-color: #FBFBFC;}
+</style>

+ 256 - 0
pages/usercenter/authentication/authentication.vue

@@ -0,0 +1,256 @@
+<template>
+	<view class="pages">	
+		<view class="form-item-type nomal-top">
+			<view class="form-item">
+				<view class="form-item-til">主体类型</view>
+				<view class="form-item-con">
+					<picker @change="bindPickerChange" :value="typeindex" :range="typearray">
+						<view class="uni-input">{{typearray[typeindex]}}</view>
+					</picker>
+					<!-- <input type="text" placeholder="请输入主体类型" v-model="params.type" value="" /> -->
+				</view>
+			</view>
+			<view class="form-item"  v-if="typeindex==0">
+				<view class="form-item-til">主体名称</view>
+				<view class="form-item-con">
+					<input type="text" placeholder="请输入单位/企业名称" v-model="params.companyName" value="" />
+				</view>
+			</view>
+			<view class="form-item"  v-if="typeindex==0">
+				<view class="form-item-til">邀请码</view>
+				<view class="form-item-con">
+					<input type="text" placeholder="请输入邀请码" v-model="params.code" value="" />
+				</view>
+			</view>		
+			<view class="form-item">
+				<view class="form-item-til">联系人</view>
+				<view class="form-item-con">
+					<input type="text" placeholder=" 请输入姓名" v-model="params.username" value="" />
+				</view>
+			</view>	
+			<view class="form-item">
+				<view class="form-item-til">手机号码</view>
+				<view class="form-item-con">
+					<input type="text" placeholder="请输入手机号码" maxlength="11" v-model="params.phone" value="" />
+				</view>
+			</view>	
+			<view class="form-item">
+				<view class="form-item-til">邮箱</view>
+				<view class="form-item-con">
+					<input type="text" placeholder="请输入邮箱" v-model="params.mail" value="" />
+				</view>
+			</view>
+			<view class="form-item">
+				<view class="form-item-til">地址</view>
+				<view class="form-item-con">
+					<input type="text" placeholder="请输入地址" v-model="params.address" value="" />
+				</view>
+			</view>
+		</view>
+		<!-- form-item-type end -->
+		<!-- <view class="form-item-type nomal-top">
+			<view class="form-item upfile">
+				<view class="form-item-til">资料上传<text class="form-item-til-tip">(营业执照及其他证明材料)</text></view>
+				<view class="form-item-con">					
+					<view class="form-item-chooseIMGs-wrap">
+						<image v-for="(item,index) in params.media" :key="index" :src="item" class="choosedIMG" ></image>
+					</view>
+					<view class="chooseIMG-btn">
+						<image class="chooseIMG-btn-img" @click="chooseimg" src="/static/img/icon-upload.png"></image>
+					</view>					
+				</view>				
+			</view>
+		</view> -->
+		<!-- form-item-type end -->
+		<view class="page-tip">
+			<text class="page-tip-til">特别提示:</text>提交材料后,管理员将审核您的信息,如核实无误将进行认证。
+		</view>
+		<view class="bottom-btn-wrap">
+			<button class="bottom-btn" @click="saveclick" type="primary">保存</button>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				params:{
+					token:'',
+					tokenhead:'',
+					companyName:'',
+					type:'',
+					username:'',
+					phone:'',
+					mail:'',
+					address:'',
+					// media:[],
+					typeindex:0,
+					code:'',
+				},
+				typeindex:0,
+				typearray:['供应商','农户'],
+				//验证的规则
+				rules:{
+					phone:{
+						rule:/^1[3456789]\d{9}$/,
+					},
+					mail:{
+						rule:/^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/,
+					},
+				},
+				//表单检查结果
+				checkStatus:false,
+				
+				
+			}
+		},
+		onShow() {
+			let serf = this;
+			//检查登录,获取token
+			// let loginRes = this.checkLogin('/pages/index/index', '2');
+			// if(!loginRes){return false;}
+			// serf.params.token=loginRes[0];			
+		},
+		onLoad() {
+			this.params.type = this.typearray[this.typeindex];
+			let self = this;
+			uni.getStorage({
+				key:'token',
+				success: function (res) {
+					self.params.token = res.data;
+				   // console.log(res.data);
+				}
+			});
+			uni.getStorage({
+				key:'tokenhead',
+				success: function (res) {
+					self.params.tokenhead = res.data;
+				   // console.log(res.data);
+				}
+			});
+		},
+		methods: {
+			bindPickerChange: function(e) {
+				console.log('picker发送选择改变,携带值为', e.target.value)
+				this.typeindex = e.target.value;				
+				this.params.typeindex = e.target.value;
+				this.params.type = this.typearray[this.params.typeindex];
+			},
+			//选择图片
+			chooseimg() {
+				let _self = this;
+				uni.chooseImage({
+					count: 2,
+					sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
+					sourceType: ['album', 'camera'], //从相册选择
+					success: function(res) {
+						const tempFilePaths = res.tempFilePaths;
+						_self.image = tempFilePaths[0];
+						console.log("tempFilePaths[0]", tempFilePaths[0]) //能够打印出选中的图片
+						_self.iconcheck = 1; //点击后图片更改状态由0变成1
+						console.log('111', JSON.stringify(res.tempFilePaths));
+						_self.params.media = res.tempFilePaths;
+						// _self.params.media = _self.params.media.concat(res.tempFilePaths)
+						console.log(_self.params.media);
+								
+					},
+					error: function(e) {
+						console.log(e);
+					}
+				});
+			},
+			//点击保存
+			saveclick(){
+				// console.log(this.params.type);
+				// console.log(this.params.typeindex);
+				// console.log(this.params.code);
+				
+				
+				this.checkStatus = this.check_form();
+				if(this.checkStatus){
+					uni.showLoading({
+					    title: '请稍等'
+					});	
+						
+					let thetoken = this.params.tokenhead+this.params.token;
+					this.$api.http.put(this.config.apiBaseurl+'/wechat/h5/cert',this.params,{header: {Authorization:thetoken}}).then( res =>{
+						uni.hideLoading();
+						uni.showToast({
+							icon:'success',
+							title:res.data.message,
+							duration: 3000
+						});
+						console.log(res)
+					}).catch(err => {
+						uni.hideLoading();
+						console.log(err)
+					});
+				
+					// setTimeout(function () {
+					//     uni.hideLoading();
+					// }, 1500);
+					
+					setTimeout(() => {
+						uni.showToast({
+							icon:'success',
+							title:`保存成功,请等待审核!`,
+							duration: 2000
+						});
+					}, 3000);
+					
+					
+					setTimeout(() => {
+						uni.switchTab({
+							url:'/pages/usercenter/usercenter'
+						})
+					}, 3000);
+					
+				}
+				
+			},
+			//表单验证
+			check_form() {
+				if(this.params.companyName.length <= 0 && this.params.typeindex == 0) {					
+					uni.showToast({icon:'none',title:`请输入单位/企业名称!`,duration: 2000});					
+					return false;
+				}
+				if(this.params.code.length <= 0 && this.params.typeindex == 0) {
+					uni.showToast({icon:'none',title:`请输入邀请码!`,duration: 2000});					
+					return false;
+				}
+				if(this.params.typeindex.length <= 0) {
+					uni.showToast({icon:'none',title:`请输入主体类型!`,duration: 2000});					
+					return false;
+				}
+				if(this.params.username.length <= 0) {
+					uni.showToast({icon:'none',title:`请输入姓名!`,duration: 2000});					
+					return false;
+				}
+				if(!this.rules['phone'].rule.test(this.params.phone)) {
+					uni.showToast({icon:'none',title:`请输入正确手机号码!`,duration: 2000});					
+					return false;
+				}
+				if(!this.rules['mail'].rule.test(this.params.mail)) {
+					uni.showToast({icon:'none',title:`请输入正确邮箱!`,duration: 2000});					
+					return false;
+				}
+				if(this.params.address.length <= 0) {
+					uni.showToast({icon:'none',title:`请输入地址!`,duration: 2000});					
+					return false;
+				}
+				// if(this.params.media.length < 2) {
+				// 	uni.showToast({icon:'none',title:`请填上传所需资料!`,duration: 2000});					
+				// 	return false;
+				// }				
+				return true;
+			},
+			
+
+		}
+	}
+</script>
+
+<style scoped>
+	/* @import url("./authentication.css"); */
+</style>

+ 160 - 0
pages/usercenter/feedback/feedback.vue

@@ -0,0 +1,160 @@
+<template>
+	<view class="feedback">
+		<view class="uni-form-item content">
+			<view class="title">反馈内容</view>
+			<textarea class="uni-textarea" placeholder="请输入留言内容" v-model="params.feedbackContext"/>
+		</view>
+		<view class="uni-form-item tel">
+			<view class="title">如有需要,留下您的联系方式,方便我们联系您</view>
+			<input class="uni-input" focus placeholder="邮箱/QQ/微信/手机号(可选)"  v-model="params.tel" />
+		</view>
+		<view class="uni-form-item">
+			<button type="primary" @click="postData">提交反馈</button>
+		</view>
+		<uni-popup ref="popup" type="message">
+		    <uni-popup-message :type="messageType" :message="message" :duration="3000"></uni-popup-message>
+		</uni-popup>
+	</view>
+</template>
+
+<script>
+	export default{
+		name:"feedback",		
+		components:{		
+		},
+		onLoad() {
+			let self = this;
+			uni.getStorage({
+				key:'userInfo',
+				success: function (res) {
+					self.params.userId = res.data.user.id;
+				   // console.log(res.data.user.id);
+				}
+			});			
+		},
+		data(){
+			return{
+				content:'',
+				tel:'',
+				message:'',
+				messageType:'',
+				params:{
+					"userId":'',
+					"feedbackContext":'',
+					"tel":''
+				}
+			}
+		},
+		methods:{
+			postData(data){
+				if(!this.params.feedbackContext){
+					this.messageType ="error";
+					this.message = "请输入留言内容";
+					uni.showToast({
+						icon:'none',
+						title:this.message,
+						duration: 2000
+					});
+					return false;
+				}else{
+					var wxReg = new RegExp("^[a-zA-Z]([-_a-zA-Z0-9]{5,19})+$"); //微信号正则校验
+					var qqReg = new RegExp("[1-9][0-9]{4,}"); //QQ号正则校验   
+					var phReg =  /^[1][3,4,5,6,7,8,9][0-9]{9}$/; //手机号正则校验 
+					if(this.params.tel.length > 0){
+						if(!wxReg.test(this.params.tel)&&!qqReg.test(this.params.tel)&&!phReg.test(this.params.tel)){
+							this.messageType ="error";
+							this.message = "邮箱/QQ/微信/手机号 输入不正确";
+							uni.showToast({
+								icon:'none',
+								title:this.message,
+								duration: 2000
+							});
+							return false;
+						}
+					};					
+					this.messageType ="success";
+					this.message = "留言成功"
+					
+					uni.showLoading({
+					    title: '请稍等'
+					});					
+					setTimeout(function () {
+					    uni.hideLoading();
+					}, 1500);
+					
+					setTimeout(() => {
+						uni.showToast({
+							icon:'success',
+							title:`保存成功,请等待审核!`,
+							duration: 2000
+						});
+					}, 1500);
+					
+					
+					setTimeout(() => {
+						uni.switchTab({
+							url:'/pages/index/index'
+						})
+					}, 3000);
+					
+					// this.$api.http.post(`/articleFeedback/add`, this.params).then(res => {
+					// 		uni.showToast({
+					// 			icon:'none',
+					// 			title:this.message,
+					// 			duration: 2000
+					// 		});
+					// 		this.params.feedbackContext = '';
+					// 		this.params.tel = '';
+					// 	// console.log(res); 
+					// })
+				}
+				
+			}
+		}
+	}
+</script>
+
+<style scoped>
+	.feedback{
+		padding:0 40rpx;
+	}
+	.feedback .uni-form-item{
+		margin-bottom:30rpx;
+	}
+	.feedback .content .title{
+		font-size:34rpx;
+		font-weight:500;
+		color:rgba(102,102,102,1);
+		padding:20rpx 0;
+	}
+	.feedback .content .uni-textarea{
+		width:100%;
+		height:500rpx;
+		background:rgba(248,248,248,1);
+		border-radius:28rpx;
+		padding:20rpx;
+		box-sizing: border-box;
+	}
+	.feedback .tel .title{
+		font-size:28rpx;
+		font-weight:400;
+		color:rgba(153,153,153,1);
+		line-height:40px;
+	}
+	.feedback .tel .uni-input{
+		box-sizing: border-box;
+		width:100%;
+		height:110rpx;
+		background:rgba(248,248,248,1);
+		border-radius:24rpx;
+		padding:3rpx 30rpx;
+	}
+	.feedback .tel .uni-input::-webkit-input-placeholder{color: #D4D4D4;}
+	button{
+		width: 400rpx;
+		height: 88rpx;
+		line-height: 88rpx;
+		background-color: #65B74E;
+		border-radius: 44rpx;
+	}
+</style>

+ 185 - 0
pages/usercenter/focusme/focusme.vue

@@ -0,0 +1,185 @@
+<template>
+	<view class="pages">
+		<mescroll-body class="wrap" ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption" :up="upOption">
+			<view class="focusme">
+				<view v-for="(item,index) in focusmelist" :key="index" class="focusme-item">
+					<view class="focusme-item-photo">
+						<image class="focusme-item-photo-img" :src="item.img" mode=""></image>
+					</view>
+					<view class="focusme-item-text">
+						<view class="focusme-item-name">{{item.name}} <text class="type">{{item.type}}</text></view>
+						<view class="focusme-item-addr">{{item.addr}}</view>
+						<view class="focusme-item-care">关心:<text v-for="(careitem,careindex) in item.care" :key="careindex">{{careitem}},</text></view>
+					</view>
+					<view class="focusme-item-call" @click="callnumber(item.phonenumber)">
+						<svg class="icon focusme-item-call-icon" aria-hidden="true">
+							<use xlink:href="#icondianhua"></use>
+						</svg>
+					</view>
+				</view>
+			</view>
+		</mescroll-body>		
+	</view>
+</template>
+
+<script>
+	// 引入mescroll-mixins.js
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";	
+	// 引入mescroll-body组件 (如已在main.js注册全局组件,则省略此步骤)
+	import MescrollBody from "@/components/mescroll-uni/mescroll-body.vue"; // 注意.vue后缀不能省
+	export default {
+		mixins: [MescrollMixin], // 使用mixin
+		components: {
+			MescrollBody,
+		},
+		data() {
+			return {
+				mescroll: null, // mescroll实例对象 (此行可删,mixins已默认)
+				// 下拉刷新的配置(可选, 绝大部分情况无需配置)
+				downOption: { 
+					// ...
+				},
+				// 上拉加载的配置(可选, 绝大部分情况无需配置)
+				upOption: {
+					page: {
+						size: 10 // 每页数据的数量,默认10
+					},
+					noMoreSize: 5, // 配置列表的总数量要大于等于5条才显示'-- END --'的提示
+					empty: {
+						tip: '暂无相关数据'
+					}
+				},
+				params:{
+					tokenhead:'',
+					token:'',
+				},
+				focusmelist:[
+					// {
+					// 	img:"http://placekitten.com/90/90",
+					// 	name:'周飞向',
+					// 	type:'养殖大户',
+					// 	addr:'贵州大亨油茶科技有限公司',
+					// 	care:["油茶","茶","芝麻"],
+					// 	phonenumber:'13655544126',
+					// },
+					// {
+					// 	img:"http://placekitten.com/90/90",
+					// 	name:'张永清',
+					// 	type:'农户',
+					// 	addr:'贵州毕节',
+					// 	care:["林下食用菌"],
+					// 	phonenumber:'13655544126',
+					// },
+				],
+				
+			}
+		},
+		onShow() {
+			let serf = this;
+			//检查登录,获取token
+			// let loginRes = this.checkLogin('/pages/index/index', '2');
+			// if(!loginRes){return false;}
+			// serf.params.token=loginRes[0];			
+		},
+		onLoad() {
+			let self = this;
+			uni.getStorage({
+				key:'token',
+				success: function (res) {
+					self.params.token = res.data;
+				   // console.log(res.data);
+				}
+			});
+			uni.getStorage({
+				key:'tokenhead',
+				success: function (res) {
+					self.params.tokenhead = res.data;
+				   // console.log(res.data);
+				}
+			});
+
+		},
+		methods: {
+			/*mescroll组件初始化的回调,可获取到mescroll对象 (此处可删,mixins已默认)*/
+			mescrollInit(mescroll) {
+				this.mescroll = mescroll;
+			},
+			/*下拉刷新的回调, 有三种处理方式:*/
+			downCallback(){
+			
+				// 第2种: 下拉刷新和上拉加载调同样的接口, 那么不用第1种方式, 直接mescroll.resetUpScroll()即可
+				this.mescroll.resetUpScroll(); // 重置列表为第一页 (自动执行 page.num=1, 再触发upCallback方法 )
+			},
+			/*上拉加载的回调*/
+			upCallback(page) {
+				let pageNum = page.num; // 页码, 默认从1开始
+				let pageSize = page.size; // 页长, 默认每页10条
+				this.params = Object.assign(this.params,{pageNum:pageNum,pageSize:pageSize});				
+				let thetoken = this.params.tokenhead + this.params.token;
+				console.log('thetoken',thetoken);
+				this.$api.http.get(this.config.apiBaseurl+'/pre/own',{header: {Authorization:thetoken}}).then(data => {
+					console.log('1111111111',data);
+					if(data.data.code=='1001'){
+						// uni.redirectTo({
+						// 	url:'/pages/login/login?backpage=/pages/index/index'+'&backtype='+2,
+						// });							
+					};
+					console.log(data);
+					// 接口返回的当前页数据列表 (数组)
+					let curPageData = data.data.data.list; 
+					// console.log('curPageData',curPageData);
+					// 接口返回的当前页数据长度 (如列表有26个数据,当前页返回8个,则curPageLen=8)
+					let curPageLen = curPageData.length; 
+					// 接口返回的总页数 (如列表有26个数据,每页10条,共3页; 则totalPage=3)
+					// let totalPage = data.xxx; 
+					// 接口返回的总数据量(如列表有26个数据,每页10条,共3页; 则totalSize=26)
+					let totalSize = data.data.data.total; 
+					// this.mescrollList = curPageData;
+					// 接口返回的是否有下一页 (true/false)
+					// let hasNext = data.xxx; 
+					
+					//设置列表数据
+					if(page.num == 1) this.mescrollList = []; //如果是第一页需手动置空列表
+					this.mescrollList = this.mescrollList.concat(curPageData); //追加新数据
+					// console.log('page.num',page.num);
+					console.log('this.mescrollList',JSON.parse(JSON.stringify(this.mescrollList)));
+					
+					//方法二(推荐): 后台接口有返回列表的总数据量 totalSize
+					this.mescroll.endBySize(curPageLen, totalSize); 
+			
+					// setTimeout(()=>{
+					// 	this.mescroll.endSuccess(curPageLen)
+					// },20)
+				
+				}).catch(err => {
+					this.mescroll.endErr()
+					console.log(err)
+				
+				});
+				
+			},
+			//打电话
+			callnumber(number){
+				uni.makePhoneCall({
+				    phoneNumber: number 
+				});				
+			},
+			
+
+		}
+	}
+</script>
+
+<style scoped>
+/* @import url("../usercenter.css"); */
+.focusme-item{display: flex;position: relative;align-items: center;background-color: #fff;padding: 24rpx 24rpx 17rpx 24rpx;margin-bottom: 8rpx;}
+.focusme-item::after{content: '';width: calc( 100% - 24rpx );height: 1px;background-color: #eee;position: absolute;right: 0;bottom: 0;}
+.focusme-item-photo{width: 90rpx;height: 90rpx;overflow: hidden;border-radius: 50%;margin-right: 30rpx;}
+.focusme-item-photo-img{width: 100%;height: 100%;}
+.focusme-item-text{flex: 1;}
+.focusme-item-name{margin-bottom: 4rpx;font-size: 34rpx;font-weight: 500;color: #333;line-height: 48rpx;}
+.focusme-item-name .type{margin-left: 24rpx;font-size: 24rpx;font-weight: 400;color: #6BBC6D;line-height: 33rpx;}
+.focusme-item-care,
+.focusme-item-addr{font-size: 28rpx;font-weight: 400;color: #666;line-height: 40rpx;}
+.focusme-item-call-icon{width: 58rpx;height: 58rpx;}
+</style>

+ 201 - 0
pages/usercenter/myfocus/myfocus.vue

@@ -0,0 +1,201 @@
+<template>
+	<view class="pages">
+		<mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption" :up="upOption">
+			<view class="product-wrap">
+				<uni-swipe-action>
+					<view v-for="(item,index) in productlist" :key="index" class="product-item">
+						<!-- <uni-swipe-action-item :right-options="swipeOptions" @click="delItem(item.id)" @change="changeSwipe"> -->
+							<view class="product-item-img-wrap" @click="productclick(item.pmsProduct.id)">
+								<image class="product-item-img" :src="$getimg+item.pmsProduct.pic" mode="widthFix"></image>
+							</view>
+							<view class="product-item-info" @click="productclick(item.pmsProduct.id)">
+								<view class="product-item-til">{{item.productName}}</view>
+								<view class="product-item-brand">{{item.productBrand}} <text class="postage" v-if="item.postage">包邮</text></view>
+								<view class="product-item-addr">{{item.addr}}</view>
+								<view class="product-item-company">{{item.companyName}}</view>
+								<view class="product-item-price"><text class="rmb">¥</text> <text class="price">{{item.pmsProduct.price}}</text> / {{item.pmsProduct.unit}}</view>
+							</view>
+						<!-- </uni-swipe-action-item> -->
+					</view>
+				</uni-swipe-action>
+			</view>
+		</mescroll-body>
+	</view>
+</template>
+
+<script>
+	// 引入mescroll-mixins.js
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";	
+	// 引入mescroll-body组件 (如已在main.js注册全局组件,则省略此步骤)
+	import MescrollBody from "@/components/mescroll-uni/mescroll-body.vue"; // 注意.vue后缀不能省
+	
+	import uniSwipeAction from '@/components/uni-swipe-action/uni-swipe-action.vue';
+	import uniSwipeActionItem from '@/components/uni-swipe-action-item/uni-swipe-action-item.vue';
+	export default {
+		mixins: [MescrollMixin], // 使用mixin
+		components: {
+			MescrollBody,
+			uniSwipeAction,
+			uniSwipeActionItem
+		},
+		data() {
+			return {
+				mescroll: null, // mescroll实例对象 (此行可删,mixins已默认)
+				// 下拉刷新的配置(可选, 绝大部分情况无需配置)
+				downOption: { 
+					// ...
+				},
+				// 上拉加载的配置(可选, 绝大部分情况无需配置)
+				upOption: {
+					page: {
+						size: 10 // 每页数据的数量,默认10
+					},
+					noMoreSize: 5, // 配置列表的总数量要大于等于5条才显示'-- END --'的提示
+					empty: {
+						tip: '暂无相关数据'
+					}
+				},
+				params:{
+					tokenhead:'',
+					token:'',
+				},
+				productlist:[
+					// {
+					// 	img:"http://placekitten.com/160/160",
+					// 	title:'红球源500ml*2瓶装传统精炼纯正山茶油',
+					// 	brand:'红球源',
+					// 	postage:true,
+					// 	addr:'贵州大亨油茶科技有限公司',
+					// 	price:'258',
+					// 	unit:'盒'
+					// },
+					// {
+					// 	img:"http://placekitten.com/160/160",
+					// 	title:'红球源500ml*2瓶装传统精炼纯正山茶油',
+					// 	brand:'红球源',
+					// 	postage:true,
+					// 	addr:'贵州大亨油茶科技有限公司',
+					// 	price:'258',
+					// 	unit:'盒'
+					// }
+				],
+				swipeOptions:[
+					{
+						text: '删除',
+						style: {
+							backgroundColor: '#dd524d'
+						}
+					}
+				],
+				
+			}
+		},
+		onShow() {
+			let serf = this;
+			// //检查登录,获取token
+			// let loginRes = this.checkLogin('/pages/index/index', '2');
+			// if(!loginRes){return false;}
+			// serf.params.token=loginRes[0];			
+		},
+		onLoad() {
+			let self = this;
+			uni.getStorage({
+				key:'token',
+				success: function (res) {
+					self.params.token = res.data;
+				   // console.log(res.data);
+				}
+			});
+			uni.getStorage({
+				key:'tokenhead',
+				success: function (res) {
+					self.params.tokenhead = res.data;
+				   // console.log(res.data);
+				}
+			});
+
+		},
+		methods: {
+			/*mescroll组件初始化的回调,可获取到mescroll对象 (此处可删,mixins已默认)*/
+			mescrollInit(mescroll) {
+				this.mescroll = mescroll;
+			},
+			/*下拉刷新的回调, 有三种处理方式:*/
+			downCallback(){
+			
+				// 第2种: 下拉刷新和上拉加载调同样的接口, 那么不用第1种方式, 直接mescroll.resetUpScroll()即可
+				this.mescroll.resetUpScroll(); // 重置列表为第一页 (自动执行 page.num=1, 再触发upCallback方法 )
+			},
+			/*上拉加载的回调*/
+			upCallback(page) {
+				let pageNum = page.num; // 页码, 默认从1开始
+				let pageSize = page.size; // 页长, 默认每页10条
+				this.params = Object.assign(this.params,{pageNO:pageNum,pageSize:pageSize});				
+				let thetoken = this.params.tokenhead + this.params.token;
+				console.log('thetoken',thetoken);
+				this.$api.http.get(this.config.apiBaseurl+'/pre/other',{params:this.params,header: {Authorization:thetoken}}).then(data => {
+					console.log('1111111111',data);
+					if(data.data.code=='1001'){
+						// uni.redirectTo({
+						// 	url:'/pages/login/login?backpage=/pages/index/index'+'&backtype='+2,
+						// });							
+					};
+					console.log('data',JSON.parse(JSON.stringify(data)));
+					// 接口返回的当前页数据列表 (数组)
+					let curPageData = data.data.data.list; 
+					// console.log('curPageData',curPageData);
+					// 接口返回的当前页数据长度 (如列表有26个数据,当前页返回8个,则curPageLen=8)
+					let curPageLen = curPageData.length; 
+					// 接口返回的总页数 (如列表有26个数据,每页10条,共3页; 则totalPage=3)
+					let totalPage =data.data.data.totalPage;
+					// 接口返回的总数据量(如列表有26个数据,每页10条,共3页; 则totalSize=26)
+					let totalSize = data.data.data.total; 
+					// this.productlist = curPageData;
+					// 接口返回的是否有下一页 (true/false)
+					// let hasNext = data.xxx; 
+					
+					//设置列表数据
+					if(page.num == 1) this.productlist = []; //如果是第一页需手动置空列表
+					this.productlist = this.productlist.concat(curPageData); //追加新数据
+					// console.log('page.num',page.num);
+					console.log('this.productlist',JSON.parse(JSON.stringify(this.productlist)));					
+					// 请求成功,隐藏加载状态
+					//方法一(推荐): 后台接口有返回列表的总页数 totalPage
+					this.mescroll.endByPage(curPageLen, totalPage); 
+					//方法二(推荐): 后台接口有返回列表的总数据量 totalSize
+					// this.mescroll.endBySize(curPageLen, totalSize); 
+			
+					// setTimeout(()=>{
+					// 	this.mescroll.endSuccess(curPageLen)
+					// },20)
+				
+				}).catch(err => {
+					this.mescroll.endErr()
+					console.log(err)
+				
+				});
+				
+			},
+			//产品点击
+			productclick(id){
+				uni.navigateTo({
+					url:`/pages/product/product?id=${id}`,					
+				})				
+			},
+			//滑动change事件
+			changeSwipe(e){
+				console.log(e)
+				
+			},
+			delItem(id){
+				console.log(id);
+			}
+
+		}
+	}
+</script>
+
+<style scoped>
+	/* @import url("./mypublish.css"); */
+	/* .product-item{display: block;} */
+</style>

+ 172 - 0
pages/usercenter/mypublish/mypublish.vue

@@ -0,0 +1,172 @@
+<template>
+	<view class="pages">
+		<mescroll-body class="wrap" ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption" :up="upOption">
+			<view class="product-wrap">
+				<view v-for="(item,index) in productlist" :key="index" @click="productclick" class="product-item">
+					<view class="product-item-img-wrap">
+						<image class="product-item-img" :src="item.img" mode="widthFix"></image>
+					</view>
+					<view class="product-item-info">
+						<view class="product-item-til">{{item.title}}</view>
+						<view class="product-item-brand">{{item.brand}} <text class="postage" v-if="item.postage">包邮</text></view>
+						<view class="product-item-addr">{{item.addr}}</view>
+						<view class="product-item-price"><text class="rmb">¥</text> <text class="price">{{item.price}}</text> / {{item.unit}}</view>
+					</view>
+				</view>
+			</view>
+		</mescroll-body>
+	</view>
+</template>
+
+<script>
+	// 引入mescroll-mixins.js
+	import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";	
+	// 引入mescroll-body组件 (如已在main.js注册全局组件,则省略此步骤)
+	import MescrollBody from "@/components/mescroll-uni/mescroll-body.vue"; // 注意.vue后缀不能省
+	export default {
+		mixins: [MescrollMixin], // 使用mixin
+		components: {
+			MescrollBody,
+		},
+		data() {
+			return {
+				mescroll: null, // mescroll实例对象 (此行可删,mixins已默认)
+				// 下拉刷新的配置(可选, 绝大部分情况无需配置)
+				downOption: { 
+					// ...
+				},
+				// 上拉加载的配置(可选, 绝大部分情况无需配置)
+				upOption: {
+					page: {
+						size: 10 // 每页数据的数量,默认10
+					},
+					noMoreSize: 5, // 配置列表的总数量要大于等于5条才显示'-- END --'的提示
+					empty: {
+						tip: '暂无相关数据'
+					}
+				},
+				params:{
+					tokenhead:'',
+					token:'',
+				},
+				productlist:[
+					// {
+					// 	img:"http://placekitten.com/160/160",
+					// 	title:'红球源500ml*2瓶装传统精炼纯正山茶油',
+					// 	brand:'红球源',
+					// 	postage:true,
+					// 	addr:'贵州大亨油茶科技有限公司',
+					// 	price:'258',
+					// 	unit:'盒'
+					// },
+					// {
+					// 	img:"http://placekitten.com/160/160",
+					// 	title:'红球源500ml*2瓶装传统精炼纯正山茶油',
+					// 	brand:'红球源',
+					// 	postage:true,
+					// 	addr:'贵州大亨油茶科技有限公司',
+					// 	price:'258',
+					// 	unit:'盒'
+					// }
+				],
+				
+			}
+		},
+		onShow() {
+			let serf = this;
+			// //检查登录,获取token
+			// let loginRes = this.checkLogin('/pages/index/index', '2');
+			// if(!loginRes){return false;}
+			// serf.params.token=loginRes[0];			
+		},
+		onLoad() {
+			let self = this;
+			uni.getStorage({
+				key:'token',
+				success: function (res) {
+					self.params.token = res.data;
+				   // console.log(res.data);
+				}
+			});
+			uni.getStorage({
+				key:'tokenhead',
+				success: function (res) {
+					self.params.tokenhead = res.data;
+				   // console.log(res.data);
+				}
+			});
+
+		},
+		methods: {
+			/*mescroll组件初始化的回调,可获取到mescroll对象 (此处可删,mixins已默认)*/
+			mescrollInit(mescroll) {
+				this.mescroll = mescroll;
+			},
+			/*下拉刷新的回调, 有三种处理方式:*/
+			downCallback(){
+			
+				// 第2种: 下拉刷新和上拉加载调同样的接口, 那么不用第1种方式, 直接mescroll.resetUpScroll()即可
+				this.mescroll.resetUpScroll(); // 重置列表为第一页 (自动执行 page.num=1, 再触发upCallback方法 )
+			},
+			/*上拉加载的回调*/
+			upCallback(page) {
+				let pageNum = page.num; // 页码, 默认从1开始
+				let pageSize = page.size; // 页长, 默认每页10条
+				this.params = Object.assign(this.params,{pageNum:pageNum,pageSize:pageSize});				
+				let thetoken = this.params.tokenhead + this.params.token;
+				console.log('thetoken',thetoken);
+				this.$api.http.get(this.config.apiBaseurl+'/pre/own',{header: {Authorization:thetoken}}).then(data => {
+					console.log('1111111111',data);
+					if(data.data.code=='1001'){
+						// uni.redirectTo({
+						// 	url:'/pages/login/login?backpage=/pages/index/index'+'&backtype='+2,
+						// });							
+					};
+					console.log(data);
+					// 接口返回的当前页数据列表 (数组)
+					let curPageData = data.data.data.list; 
+					// console.log('curPageData',curPageData);
+					// 接口返回的当前页数据长度 (如列表有26个数据,当前页返回8个,则curPageLen=8)
+					let curPageLen = curPageData.length; 
+					// 接口返回的总页数 (如列表有26个数据,每页10条,共3页; 则totalPage=3)
+					// let totalPage = data.xxx; 
+					// 接口返回的总数据量(如列表有26个数据,每页10条,共3页; 则totalSize=26)
+					let totalSize = data.data.data.total; 
+					// this.mescrollList = curPageData;
+					// 接口返回的是否有下一页 (true/false)
+					// let hasNext = data.xxx; 
+					
+					//设置列表数据
+					if(page.num == 1) this.mescrollList = []; //如果是第一页需手动置空列表
+					this.mescrollList = this.mescrollList.concat(curPageData); //追加新数据
+					// console.log('page.num',page.num);
+					console.log('this.mescrollList',JSON.parse(JSON.stringify(this.mescrollList)));
+					
+					//方法二(推荐): 后台接口有返回列表的总数据量 totalSize
+					this.mescroll.endBySize(curPageLen, totalSize); 
+			
+					// setTimeout(()=>{
+					// 	this.mescroll.endSuccess(curPageLen)
+					// },20)
+				
+				}).catch(err => {
+					this.mescroll.endErr()
+					console.log(err)
+				
+				});
+				
+			},
+			//产品点击
+			productclick(product){
+				uni.navigateTo({
+					url:`/pages/product/product?product=${product}`,					
+				})				
+			},
+
+		}
+	}
+</script>
+
+<style scoped>
+	/* @import url("./mypublish.css"); */
+</style>

+ 155 - 0
pages/usercenter/usercenter - 副本.vue

@@ -0,0 +1,155 @@
+<template>
+	<view class="pages">
+		<view class="myinfo-wrap">
+			<view class="myinfo">
+				<view class="myinfo-photo">
+					<image class="myinfo-photo-img" :src="user.icon" mode=""></image>
+				</view>
+				<view class="myinfo-text">
+					<view class="myinfo-text-name">{{user.nickname || '登录/注册'}}</view>
+					<view class="myinfo-text-identity">{{usertype}} | {{usercompany}}</view>
+				</view>
+			</view>		
+			<view class="myinfo-focus">
+				<view class="myinfo-focus-item myinfo-focus-myfocus">
+					<navigator url="/pages/usercenter/myfocus/myfocus">
+						<view class="number">{{pre.want||0}}</view>
+					</navigator>
+					<view class="text">我有意向的</view>
+				</view>
+				<view class="myinfo-focus-item myinfo-focus-focusme">
+					<navigator url="/pages/usercenter/focusme/focusme">
+						<view class="number">{{pre.own}}</view>
+					</navigator>
+					<view class="text">有意向我的</view>
+				</view>
+			</view>
+		</view>		
+		<!-- myinfo-wrap end -->
+		<view class="wrap">
+			<navigator url="/pages/usercenter/mypublish/mypublish">
+				<view class="select-bar">
+					<svg class="icon select-bar-icon" aria-hidden="true">
+						<use xlink:href="#iconwodedingdan"></use>
+					</svg>
+					<view class="select-bar-text">我的信息发布</view>
+					<view class="select-bar-arrow"></view>
+				</view>
+			</navigator>
+			<navigator url="/pages/usercenter/authentication/authentication">
+				<view class="select-bar">
+					<svg class="icon select-bar-icon" aria-hidden="true">
+						<use xlink:href="#iconwodetuandui"></use>
+					</svg>
+					<view class="select-bar-text">企业认证</view>
+					<view class="select-bar-arrow"></view>
+				</view>
+			</navigator>
+			<navigator url="/pages/usercenter/feedback/feedback">
+				<view class="select-bar">
+					<svg class="icon select-bar-icon" aria-hidden="true">
+						<use xlink:href="#iconbangzhu"></use>
+					</svg>
+					<view class="select-bar-text">帮助反馈</view>
+					<view class="select-bar-arrow"></view>
+				</view>
+			</navigator>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { mapState, mapMutations } from 'vuex';
+	export default {
+		data() {
+			return {
+				params:{
+					token:'',
+					tokenhead:'',
+				},
+				name:'',
+				usertype:'',
+				usercompany:'',
+				myfocus:0,
+				focusme:1,
+				pre:[],
+				user:[],
+				wxuser:[],
+				
+			}
+		},
+		onShow() {
+			let serf = this;
+			//检查登录,获取token
+			let loginRes = this.checkLogin('/pages/index/index', '2');
+			// console.log(loginRes);
+			if(!loginRes){return false;}
+			serf.params.token=loginRes[0];			
+		},
+		onLoad() {
+			// let type=localStorage.getItem("type");
+			// if(type){
+			// 	this.change(type)
+			// }
+			// else{
+			// 	localStorage.setItem("type",this.type);
+			// };
+			console.log('userInfo',this.userInfo,'hasLogin',this.hasLogin);
+			let self = this;
+			uni.getStorage({
+				key:'token',
+				success: function (res) {
+					self.params.token = res.data;
+				   // console.log(res.data);
+				}
+			});
+			uni.getStorage({
+				key:'tokenhead',
+				success: function (res) {
+					self.params.tokenhead = res.data;
+				   // console.log(res.data);
+				}
+			});
+			try {
+			    const value = uni.getStorageSync('userInfo');
+			    if (value) {
+					console.log('this.userinfo',JSON.parse(JSON.stringify(value)));
+					this.pre = value.pre;
+					this.user = value.user;
+					this.wxuser = value.wxUsers;
+			    }
+			} catch (e) {
+				window.location.replace(this.config.loginUrl);
+			};
+			uni.getStorage({
+				key:'userInfo',
+				success: function (res) {
+					// this.pre = res.data.pre;
+					// this.user = res.data.user;
+					// this.wxuser = res.data.wxUsers;
+					// console.log('res.data',res.data);
+					// console.log('this.pre',this.pre);
+					// console.log('this.user',this.user);
+					// console.log('this.wxuser',this.wxuser);
+				}
+			});
+			//获取会员信息
+			let thetoken = this.params.tokenhead + this.params.token;
+			// this.$api.http.get(this.config.apiBaseurl+'/sso/info',{header: {Authorization:thetoken}}).then(res => {				
+			// 	this.userinfo = res.data.data;
+			// 	console.log('this.userinfo',JSON.parse(JSON.stringify(this.userinfo)));
+			// }).catch(err => {			
+			// });
+		},
+		methods: {
+
+		},
+		// computed:{
+		// 	...mapState(['hasLogin','userInfo'])
+		// }
+	}
+</script>
+
+<style scoped>
+	@import url("./usercenter.css");
+</style>

+ 24 - 0
pages/usercenter/usercenter.css

@@ -0,0 +1,24 @@
+.myinfo-wrap{background-color: #fff;padding-bottom: 24rpx;margin-bottom: 24rpx;}
+.myinfo{display: flex;align-items: center;margin: 0 24rpx 24rpx;padding: 24rpx 0;border-bottom: 1px solid #eee;}
+.myinfo-photo{width: 120rpx;height: 120rpx;margin-right: 24rpx;border-radius: 50%;overflow: hidden;}
+.myinfo-photo-img{width: 100%;height: 100%;}
+.myinfo-text-name{font-size: 36rpx;font-weight: 400;color: #333;line-height: 50rpx;margin-bottom: 4rpx;}
+.myinfo-text-identity{font-size: 30rpx;font-weight: 400;color: #999;line-height: 42rpx;}
+.myinfo-focus{display: flex;align-items: center;}
+.myinfo-focus-item{flex: 1;text-align: center;}
+.myinfo-focus-item .number{font-size: 50rpx;font-weight: 400;line-height: 70rpx;}
+.myinfo-focus-item .text{font-size: 26rpx;font-weight: 400;color: #999;line-height: 37rpx;}
+.myinfo-focus-myfocus .number{color: #6BBC6D;}
+.myinfo-focus-focusme .number{color: #F5A623;}
+
+
+
+
+
+
+
+
+
+
+
+

+ 161 - 0
pages/usercenter/usercenter.vue

@@ -0,0 +1,161 @@
+<template>
+	<view class="pages">
+		<view class="myinfo-wrap">
+			<view class="myinfo">
+				<view class="myinfo-photo">
+					<image class="myinfo-photo-img" :src="user.icon" mode=""></image>
+				</view>
+				<view class="myinfo-text">
+					<view class="myinfo-text-name">{{user.nickname || '登录/注册'}}</view>
+					<view class="myinfo-text-identity">{{user.type}} <text v-if="user.companyInfo">| {{user.companyInfo}}</text></view>
+				</view>
+			</view>		
+			<view class="myinfo-focus">
+				<view class="myinfo-focus-item myinfo-focus-myfocus">
+					<navigator url="/pages/usercenter/myfocus/myfocus">
+						<view class="number">{{pre.want||0}}</view>
+					</navigator>
+					<view class="text">我有意向的</view>
+				</view>
+				<view class="myinfo-focus-item myinfo-focus-focusme">
+					<navigator url="/pages/usercenter/focusme/focusme">
+						<view class="number">{{pre.own||0}}</view>
+					</navigator>
+					<view class="text">有意向我的</view>
+				</view>
+			</view>
+		</view>		
+		<!-- myinfo-wrap end -->
+		<view class="wrap">
+			<navigator url="/pages/usercenter/mypublish/mypublish">
+				<view class="select-bar">
+					<svg class="icon select-bar-icon" aria-hidden="true">
+						<use xlink:href="#iconwodedingdan"></use>
+					</svg>
+					<view class="select-bar-text">我的信息发布</view>
+					<view class="select-bar-arrow"></view>
+				</view>
+			</navigator>
+			<navigator url="/pages/usercenter/authentication/authentication">
+				<view class="select-bar">
+					<svg class="icon select-bar-icon" aria-hidden="true">
+						<use xlink:href="#iconwodetuandui"></use>
+					</svg>
+					<view class="select-bar-text">企业认证</view>
+					<view class="select-bar-arrow"></view>
+				</view>
+			</navigator>
+			<navigator url="/pages/usercenter/feedback/feedback">
+				<view class="select-bar">
+					<svg class="icon select-bar-icon" aria-hidden="true">
+						<use xlink:href="#iconbangzhu"></use>
+					</svg>
+					<view class="select-bar-text">帮助反馈</view>
+					<view class="select-bar-arrow"></view>
+				</view>
+			</navigator>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { mapState, mapMutations } from 'vuex';
+	export default {
+		data() {
+			return {
+				params:{
+					token:'',
+					tokenhead:'',
+				},
+				info:'',
+				name:'',
+				usertype:'',
+				usercompany:'',
+				myfocus:0,
+				focusme:1,
+				pre:[],
+				user:[],
+				wxuser:[],
+				
+			}
+		},
+		onShow() {		
+		},
+		onLoad() {
+			let serf = this;
+			let locationLocaturl = window.location.search;
+			function getUrlParams(url) {
+				if (decodeURIComponent(url).indexOf('?') > -1) {
+					var result = [];
+					var urlParamsArr = decodeURIComponent(url).split('?');
+					urlParamsArr.shift();
+					let newUrlParamsArr = urlParamsArr.join('&').split('&');
+					for (var i = 0; i < newUrlParamsArr.length; i++) {
+						var paramKey = newUrlParamsArr[i].split('=')[0];
+						var paramValue = newUrlParamsArr[i].split('=')[1];
+						result.push({
+							key: paramKey,
+							value: paramValue
+						});
+					}
+					return result;
+				} else {
+					console.log('该URL中不含参数');
+				}
+			};
+			
+			let locationArr = getUrlParams(locationLocaturl);
+			if(!locationArr){
+				let loginurl = `${this.config.apiBaseurl}/wechat/h5/authorize?returnUrl=/pages/usercenter/usercenter`
+				window.location.replace(loginurl);
+				return
+			};
+			let itemtoken = locationArr.find(function(locationArr) {
+				return locationArr.key === 'token';
+			});
+			let itemtokenHead = locationArr.find(function(locationArr) {
+				return locationArr.key === 'tokenHead';
+			});
+			if (itemtoken) {
+				this.params.token = itemtoken.value;				
+			}
+			if (itemtokenHead) {
+				this.params.tokenhead = itemtokenHead.value;				
+			}
+			if(this.params.tokenhead&&this.params.token){
+				let thetoken = this.params.tokenhead+this.params.token;
+				this.$api.http.get(this.config.apiBaseurl+'/wechat/h5/info', {header: {Authorization:thetoken}}).then(res => {					
+					let userInfo = res.data;
+					this.info = userInfo;
+					this.pre = this.info.data.pre;
+					this.user = this.info.data.user;
+					this.wxuser = this.info.data.wxUsers;
+					console.log('this.info',JSON.parse(JSON.stringify(this.info)));
+					userInfo.accessToken = this.params.token;
+					userInfo.tokenhead = this.params.tokenhead;
+					this.setLogin(userInfo);
+				}).catch(err => {
+					console.log(err)
+				});
+			};
+			//获取会员信息
+			let thetoken = this.params.tokenhead + this.params.token;
+			// this.$api.http.get(this.config.apiBaseurl+'/sso/info',{header: {Authorization:thetoken}}).then(res => {				
+			// 	this.userinfo = res.data.data;
+			// 	console.log('this.userinfo',JSON.parse(JSON.stringify(this.userinfo)));
+			// }).catch(err => {			
+			// });
+		},
+		methods: {
+			...mapMutations(['setLogin']),
+
+		},
+		// computed:{
+		// 	...mapState(['hasLogin','userInfo'])
+		// }
+	}
+</script>
+
+<style scoped>
+	@import url("./usercenter.css");
+</style>

+ 539 - 0
static/iconfont/demo.css

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

+ 561 - 0
static/iconfont/demo_index.html

@@ -0,0 +1,561 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8"/>
+  <title>IconFont Demo</title>
+  <link rel="shortcut icon" href="https://img.alicdn.com/tps/i4/TB1_oz6GVXXXXaFXpXXJDFnIXXX-64-64.ico" type="image/x-icon"/>
+  <link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
+  <link rel="stylesheet" href="demo.css">
+  <link rel="stylesheet" href="iconfont.css">
+  <script src="iconfont.js"></script>
+  <!-- jQuery -->
+  <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
+  <!-- 代码高亮 -->
+  <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
+</head>
+<body>
+  <div class="main">
+    <h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank">&#xe86b;</a></h1>
+    <div class="nav-tabs">
+      <ul id="tabs" class="dib-box">
+        <li class="dib active"><span>Unicode</span></li>
+        <li class="dib"><span>Font class</span></li>
+        <li class="dib"><span>Symbol</span></li>
+      </ul>
+      
+      <a href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=2039516" target="_blank" class="nav-more">查看项目</a>
+      
+    </div>
+    <div class="tab-container">
+      <div class="content unicode" style="display: block;">
+          <ul class="icon_lists dib-box">
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe65d;</span>
+                <div class="name">企业认证</div>
+                <div class="code-name">&amp;#xe65d;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe65c;</span>
+                <div class="name">加号</div>
+                <div class="code-name">&amp;#xe65c;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe65b;</span>
+                <div class="name">我的订单</div>
+                <div class="code-name">&amp;#xe65b;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe65a;</span>
+                <div class="name">Group</div>
+                <div class="code-name">&amp;#xe65a;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe659;</span>
+                <div class="name">帮助</div>
+                <div class="code-name">&amp;#xe659;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe658;</span>
+                <div class="name">电话</div>
+                <div class="code-name">&amp;#xe658;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe657;</span>
+                <div class="name">商品</div>
+                <div class="code-name">&amp;#xe657;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe654;</span>
+                <div class="name">采购</div>
+                <div class="code-name">&amp;#xe654;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe655;</span>
+                <div class="name">地址</div>
+                <div class="code-name">&amp;#xe655;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe656;</span>
+                <div class="name">找代卖</div>
+                <div class="code-name">&amp;#xe656;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe653;</span>
+                <div class="name">Group 2</div>
+                <div class="code-name">&amp;#xe653;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe64e;</span>
+                <div class="name">发布1</div>
+                <div class="code-name">&amp;#xe64e;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe650;</span>
+                <div class="name">home</div>
+                <div class="code-name">&amp;#xe650;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe651;</span>
+                <div class="name">我的1</div>
+                <div class="code-name">&amp;#xe651;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe652;</span>
+                <div class="name">我的2</div>
+                <div class="code-name">&amp;#xe652;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe64c;</span>
+                <div class="name">发布2</div>
+                <div class="code-name">&amp;#xe64c;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe64d;</span>
+                <div class="name">home2</div>
+                <div class="code-name">&amp;#xe64d;</div>
+              </li>
+          
+          </ul>
+          <div class="article markdown">
+          <h2 id="unicode-">Unicode 引用</h2>
+          <hr>
+
+          <p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
+          <ul>
+            <li>兼容性最好,支持 IE6+,及所有现代浏览器。</li>
+            <li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
+            <li>但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。</li>
+          </ul>
+          <blockquote>
+            <p>注意:新版 iconfont 支持多色图标,这些多色图标在 Unicode 模式下将不能使用,如果有需求建议使用symbol 的引用方式</p>
+          </blockquote>
+          <p>Unicode 使用步骤如下:</p>
+          <h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
+<pre><code class="language-css"
+>@font-face {
+  font-family: 'iconfont';
+  src: url('iconfont.eot');
+  src: url('iconfont.eot?#iefix') format('embedded-opentype'),
+      url('iconfont.woff2') format('woff2'),
+      url('iconfont.woff') format('woff'),
+      url('iconfont.ttf') format('truetype'),
+      url('iconfont.svg#iconfont') format('svg');
+}
+</code></pre>
+          <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
+<pre><code class="language-css"
+>.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+</code></pre>
+          <h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
+<pre>
+<code class="language-html"
+>&lt;span class="iconfont"&gt;&amp;#x33;&lt;/span&gt;
+</code></pre>
+          <blockquote>
+            <p>"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
+          </blockquote>
+          </div>
+      </div>
+      <div class="content font-class">
+        <ul class="icon_lists dib-box">
+          
+          <li class="dib">
+            <span class="icon iconfont iconwodetuandui"></span>
+            <div class="name">
+              企业认证
+            </div>
+            <div class="code-name">.iconwodetuandui
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconjiahao"></span>
+            <div class="name">
+              加号
+            </div>
+            <div class="code-name">.iconjiahao
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconwodedingdan"></span>
+            <div class="name">
+              我的订单
+            </div>
+            <div class="code-name">.iconwodedingdan
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconGroup"></span>
+            <div class="name">
+              Group
+            </div>
+            <div class="code-name">.iconGroup
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconbangzhu"></span>
+            <div class="name">
+              帮助
+            </div>
+            <div class="code-name">.iconbangzhu
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icondianhua"></span>
+            <div class="name">
+              电话
+            </div>
+            <div class="code-name">.icondianhua
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconshangpin"></span>
+            <div class="name">
+              商品
+            </div>
+            <div class="code-name">.iconshangpin
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconcaigou"></span>
+            <div class="name">
+              采购
+            </div>
+            <div class="code-name">.iconcaigou
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icondizhi"></span>
+            <div class="name">
+              地址
+            </div>
+            <div class="code-name">.icondizhi
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconzhaodaimai"></span>
+            <div class="name">
+              找代卖
+            </div>
+            <div class="code-name">.iconzhaodaimai
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconGroup2"></span>
+            <div class="name">
+              Group 2
+            </div>
+            <div class="code-name">.iconGroup2
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconfabu1"></span>
+            <div class="name">
+              发布1
+            </div>
+            <div class="code-name">.iconfabu1
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconhome"></span>
+            <div class="name">
+              home
+            </div>
+            <div class="code-name">.iconhome
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconwode1"></span>
+            <div class="name">
+              我的1
+            </div>
+            <div class="code-name">.iconwode1
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconwode2"></span>
+            <div class="name">
+              我的2
+            </div>
+            <div class="code-name">.iconwode2
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconfabu2"></span>
+            <div class="name">
+              发布2
+            </div>
+            <div class="code-name">.iconfabu2
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont iconhome2"></span>
+            <div class="name">
+              home2
+            </div>
+            <div class="code-name">.iconhome2
+            </div>
+          </li>
+          
+        </ul>
+        <div class="article markdown">
+        <h2 id="font-class-">font-class 引用</h2>
+        <hr>
+
+        <p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p>
+        <p>与 Unicode 使用方式相比,具有如下特点:</p>
+        <ul>
+          <li>兼容性良好,支持 IE8+,及所有现代浏览器。</li>
+          <li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
+          <li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li>
+          <li>不过因为本质上还是使用的字体,所以多色图标还是不支持的。</li>
+        </ul>
+        <p>使用步骤如下:</p>
+        <h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
+<pre><code class="language-html">&lt;link rel="stylesheet" href="./iconfont.css"&gt;
+</code></pre>
+        <h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
+<pre><code class="language-html">&lt;span class="iconfont iconxxx"&gt;&lt;/span&gt;
+</code></pre>
+        <blockquote>
+          <p>"
+            iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
+        </blockquote>
+      </div>
+      </div>
+      <div class="content symbol">
+          <ul class="icon_lists dib-box">
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconwodetuandui"></use>
+                </svg>
+                <div class="name">企业认证</div>
+                <div class="code-name">#iconwodetuandui</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconjiahao"></use>
+                </svg>
+                <div class="name">加号</div>
+                <div class="code-name">#iconjiahao</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconwodedingdan"></use>
+                </svg>
+                <div class="name">我的订单</div>
+                <div class="code-name">#iconwodedingdan</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconGroup"></use>
+                </svg>
+                <div class="name">Group</div>
+                <div class="code-name">#iconGroup</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconbangzhu"></use>
+                </svg>
+                <div class="name">帮助</div>
+                <div class="code-name">#iconbangzhu</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icondianhua"></use>
+                </svg>
+                <div class="name">电话</div>
+                <div class="code-name">#icondianhua</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconshangpin"></use>
+                </svg>
+                <div class="name">商品</div>
+                <div class="code-name">#iconshangpin</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconcaigou"></use>
+                </svg>
+                <div class="name">采购</div>
+                <div class="code-name">#iconcaigou</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icondizhi"></use>
+                </svg>
+                <div class="name">地址</div>
+                <div class="code-name">#icondizhi</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconzhaodaimai"></use>
+                </svg>
+                <div class="name">找代卖</div>
+                <div class="code-name">#iconzhaodaimai</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconGroup2"></use>
+                </svg>
+                <div class="name">Group 2</div>
+                <div class="code-name">#iconGroup2</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconfabu1"></use>
+                </svg>
+                <div class="name">发布1</div>
+                <div class="code-name">#iconfabu1</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconhome"></use>
+                </svg>
+                <div class="name">home</div>
+                <div class="code-name">#iconhome</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconwode1"></use>
+                </svg>
+                <div class="name">我的1</div>
+                <div class="code-name">#iconwode1</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconwode2"></use>
+                </svg>
+                <div class="name">我的2</div>
+                <div class="code-name">#iconwode2</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconfabu2"></use>
+                </svg>
+                <div class="name">发布2</div>
+                <div class="code-name">#iconfabu2</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#iconhome2"></use>
+                </svg>
+                <div class="name">home2</div>
+                <div class="code-name">#iconhome2</div>
+            </li>
+          
+          </ul>
+          <div class="article markdown">
+          <h2 id="symbol-">Symbol 引用</h2>
+          <hr>
+
+          <p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
+            这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:</p>
+          <ul>
+            <li>支持多色图标了,不再受单色限制。</li>
+            <li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> 来调整样式。</li>
+            <li>兼容性较差,支持 IE9+,及现代浏览器。</li>
+            <li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
+          </ul>
+          <p>使用步骤如下:</p>
+          <h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
+<pre><code class="language-html">&lt;script src="./iconfont.js"&gt;&lt;/script&gt;
+</code></pre>
+          <h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
+<pre><code class="language-html">&lt;style&gt;
+.icon {
+  width: 1em;
+  height: 1em;
+  vertical-align: -0.15em;
+  fill: currentColor;
+  overflow: hidden;
+}
+&lt;/style&gt;
+</code></pre>
+          <h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
+<pre><code class="language-html">&lt;svg class="icon" aria-hidden="true"&gt;
+  &lt;use xlink:href="#icon-xxx"&gt;&lt;/use&gt;
+&lt;/svg&gt;
+</code></pre>
+          </div>
+      </div>
+
+    </div>
+  </div>
+  <script>
+  $(document).ready(function () {
+      $('.tab-container .content:first').show()
+
+      $('#tabs li').click(function (e) {
+        var tabContent = $('.tab-container .content')
+        var index = $(this).index()
+
+        if ($(this).hasClass('active')) {
+          return
+        } else {
+          $('#tabs li').removeClass('active')
+          $(this).addClass('active')
+
+          tabContent.hide().eq(index).fadeIn()
+        }
+      })
+    })
+  </script>
+</body>
+</html>

File diff suppressed because it is too large
+ 85 - 0
static/iconfont/iconfont.css


BIN
static/iconfont/iconfont.eot


File diff suppressed because it is too large
+ 1 - 0
static/iconfont/iconfont.js


+ 128 - 0
static/iconfont/iconfont.json

@@ -0,0 +1,128 @@
+{
+  "id": "2039516",
+  "name": "林产品",
+  "font_family": "iconfont",
+  "css_prefix_text": "icon",
+  "description": "",
+  "glyphs": [
+    {
+      "icon_id": "16956711",
+      "name": "企业认证",
+      "font_class": "wodetuandui",
+      "unicode": "e65d",
+      "unicode_decimal": 58973
+    },
+    {
+      "icon_id": "16954762",
+      "name": "加号",
+      "font_class": "jiahao",
+      "unicode": "e65c",
+      "unicode_decimal": 58972
+    },
+    {
+      "icon_id": "16944010",
+      "name": "我的订单",
+      "font_class": "wodedingdan",
+      "unicode": "e65b",
+      "unicode_decimal": 58971
+    },
+    {
+      "icon_id": "16943964",
+      "name": "Group",
+      "font_class": "Group",
+      "unicode": "e65a",
+      "unicode_decimal": 58970
+    },
+    {
+      "icon_id": "16943937",
+      "name": "帮助",
+      "font_class": "bangzhu",
+      "unicode": "e659",
+      "unicode_decimal": 58969
+    },
+    {
+      "icon_id": "16943906",
+      "name": "电话",
+      "font_class": "dianhua",
+      "unicode": "e658",
+      "unicode_decimal": 58968
+    },
+    {
+      "icon_id": "16915549",
+      "name": "商品",
+      "font_class": "shangpin",
+      "unicode": "e657",
+      "unicode_decimal": 58967
+    },
+    {
+      "icon_id": "16915518",
+      "name": "采购",
+      "font_class": "caigou",
+      "unicode": "e654",
+      "unicode_decimal": 58964
+    },
+    {
+      "icon_id": "16915519",
+      "name": "地址",
+      "font_class": "dizhi",
+      "unicode": "e655",
+      "unicode_decimal": 58965
+    },
+    {
+      "icon_id": "16915520",
+      "name": "找代卖",
+      "font_class": "zhaodaimai",
+      "unicode": "e656",
+      "unicode_decimal": 58966
+    },
+    {
+      "icon_id": "16908917",
+      "name": "Group 2",
+      "font_class": "Group2",
+      "unicode": "e653",
+      "unicode_decimal": 58963
+    },
+    {
+      "icon_id": "16904455",
+      "name": "发布1",
+      "font_class": "fabu1",
+      "unicode": "e64e",
+      "unicode_decimal": 58958
+    },
+    {
+      "icon_id": "16904457",
+      "name": "home",
+      "font_class": "home",
+      "unicode": "e650",
+      "unicode_decimal": 58960
+    },
+    {
+      "icon_id": "16904458",
+      "name": "我的1",
+      "font_class": "wode1",
+      "unicode": "e651",
+      "unicode_decimal": 58961
+    },
+    {
+      "icon_id": "16904459",
+      "name": "我的2",
+      "font_class": "wode2",
+      "unicode": "e652",
+      "unicode_decimal": 58962
+    },
+    {
+      "icon_id": "16904441",
+      "name": "发布2",
+      "font_class": "fabu2",
+      "unicode": "e64c",
+      "unicode_decimal": 58956
+    },
+    {
+      "icon_id": "16904442",
+      "name": "home2",
+      "font_class": "home2",
+      "unicode": "e64d",
+      "unicode_decimal": 58957
+    }
+  ]
+}

File diff suppressed because it is too large
+ 77 - 0
static/iconfont/iconfont.svg


BIN
static/iconfont/iconfont.ttf


BIN
static/iconfont/iconfont.woff


BIN
static/iconfont/iconfont.woff2


+ 0 - 0
static/img/checkbox-checked.png


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