hwq 2 years ago
parent
commit
260366774b
100 changed files with 7301 additions and 0 deletions
  1. BIN
      .DS_Store
  2. 20 0
      .hbuilderx/launch.json
  3. 104 0
      App.vue
  4. BIN
      components/.DS_Store
  5. 127 0
      components/goods-detail.vue
  6. 317 0
      components/lff-barrage/lff-barrage.vue
  7. 15 0
      components/lff-barrage/markdown.md
  8. 150 0
      components/lunbobox.vue
  9. 287 0
      components/payment.vue
  10. 137 0
      components/pick-regions/pick-regions.vue
  11. 0 0
      components/pick-regions/regions.json
  12. 278 0
      components/prize-flying.vue
  13. 134 0
      components/recycle.vue
  14. 425 0
      components/show-result.bak.vue
  15. 569 0
      components/show-result.vue
  16. 27 0
      components/u-parse/components/wxParseAudio.vue
  17. 86 0
      components/u-parse/components/wxParseImg.vue
  18. 107 0
      components/u-parse/components/wxParseTemplate0.vue
  19. 99 0
      components/u-parse/components/wxParseTemplate1.vue
  20. 97 0
      components/u-parse/components/wxParseTemplate10.vue
  21. 87 0
      components/u-parse/components/wxParseTemplate11.vue
  22. 98 0
      components/u-parse/components/wxParseTemplate2.vue
  23. 98 0
      components/u-parse/components/wxParseTemplate3.vue
  24. 98 0
      components/u-parse/components/wxParseTemplate4.vue
  25. 98 0
      components/u-parse/components/wxParseTemplate5.vue
  26. 98 0
      components/u-parse/components/wxParseTemplate6.vue
  27. 98 0
      components/u-parse/components/wxParseTemplate7.vue
  28. 98 0
      components/u-parse/components/wxParseTemplate8.vue
  29. 98 0
      components/u-parse/components/wxParseTemplate9.vue
  30. 15 0
      components/u-parse/components/wxParseVideo.vue
  31. 261 0
      components/u-parse/libs/html2json.js
  32. 156 0
      components/u-parse/libs/htmlparser.js
  33. 195 0
      components/u-parse/libs/wxDiscode.js
  34. 102 0
      components/u-parse/readme.md
  35. 232 0
      components/u-parse/u-parse.css
  36. 117 0
      components/u-parse/u-parse.vue
  37. BIN
      components/v-tabs/.DS_Store
  38. 182 0
      components/v-tabs/readme.md
  39. 339 0
      components/v-tabs/v-tabs.vue
  40. 214 0
      http/api.js
  41. 19 0
      http/debounce.js
  42. 142 0
      http/index.js
  43. 0 0
      js_sdk/index.js
  44. 15 0
      main.js
  45. 103 0
      manifest.json
  46. 47 0
      node_modules/lodash/LICENSE
  47. 39 0
      node_modules/lodash/README.md
  48. 7 0
      node_modules/lodash/_DataView.js
  49. 32 0
      node_modules/lodash/_Hash.js
  50. 28 0
      node_modules/lodash/_LazyWrapper.js
  51. 32 0
      node_modules/lodash/_ListCache.js
  52. 22 0
      node_modules/lodash/_LodashWrapper.js
  53. 7 0
      node_modules/lodash/_Map.js
  54. 32 0
      node_modules/lodash/_MapCache.js
  55. 7 0
      node_modules/lodash/_Promise.js
  56. 7 0
      node_modules/lodash/_Set.js
  57. 27 0
      node_modules/lodash/_SetCache.js
  58. 27 0
      node_modules/lodash/_Stack.js
  59. 6 0
      node_modules/lodash/_Symbol.js
  60. 6 0
      node_modules/lodash/_Uint8Array.js
  61. 7 0
      node_modules/lodash/_WeakMap.js
  62. 21 0
      node_modules/lodash/_apply.js
  63. 22 0
      node_modules/lodash/_arrayAggregator.js
  64. 22 0
      node_modules/lodash/_arrayEach.js
  65. 21 0
      node_modules/lodash/_arrayEachRight.js
  66. 23 0
      node_modules/lodash/_arrayEvery.js
  67. 25 0
      node_modules/lodash/_arrayFilter.js
  68. 17 0
      node_modules/lodash/_arrayIncludes.js
  69. 22 0
      node_modules/lodash/_arrayIncludesWith.js
  70. 49 0
      node_modules/lodash/_arrayLikeKeys.js
  71. 21 0
      node_modules/lodash/_arrayMap.js
  72. 20 0
      node_modules/lodash/_arrayPush.js
  73. 26 0
      node_modules/lodash/_arrayReduce.js
  74. 24 0
      node_modules/lodash/_arrayReduceRight.js
  75. 15 0
      node_modules/lodash/_arraySample.js
  76. 17 0
      node_modules/lodash/_arraySampleSize.js
  77. 15 0
      node_modules/lodash/_arrayShuffle.js
  78. 23 0
      node_modules/lodash/_arraySome.js
  79. 12 0
      node_modules/lodash/_asciiSize.js
  80. 12 0
      node_modules/lodash/_asciiToArray.js
  81. 15 0
      node_modules/lodash/_asciiWords.js
  82. 20 0
      node_modules/lodash/_assignMergeValue.js
  83. 28 0
      node_modules/lodash/_assignValue.js
  84. 21 0
      node_modules/lodash/_assocIndexOf.js
  85. 21 0
      node_modules/lodash/_baseAggregator.js
  86. 17 0
      node_modules/lodash/_baseAssign.js
  87. 17 0
      node_modules/lodash/_baseAssignIn.js
  88. 25 0
      node_modules/lodash/_baseAssignValue.js
  89. 23 0
      node_modules/lodash/_baseAt.js
  90. 22 0
      node_modules/lodash/_baseClamp.js
  91. 166 0
      node_modules/lodash/_baseClone.js
  92. 18 0
      node_modules/lodash/_baseConforms.js
  93. 27 0
      node_modules/lodash/_baseConformsTo.js
  94. 30 0
      node_modules/lodash/_baseCreate.js
  95. 21 0
      node_modules/lodash/_baseDelay.js
  96. 67 0
      node_modules/lodash/_baseDifference.js
  97. 14 0
      node_modules/lodash/_baseEach.js
  98. 14 0
      node_modules/lodash/_baseEachRight.js
  99. 21 0
      node_modules/lodash/_baseEvery.js
  100. 32 0
      node_modules/lodash/_baseExtremum.js

BIN
.DS_Store


+ 20 - 0
.hbuilderx/launch.json

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

+ 104 - 0
App.vue

@@ -0,0 +1,104 @@
+<script>
+// #ifdef H5
+var jweixin = require('@/js_sdk');
+// #endif
+export default {
+	onLaunch: function() {
+		console.log('App Launch');
+	},
+	onShow: function() {
+		console.log('App Show');
+	},
+	onHide: function() {
+		console.log('App Hide');
+	}
+};
+</script>
+
+<style>
+/*每个页面公共css */
+* {
+	margin: 0;
+	padding: 0;
+	box-sizing: border-box;
+}
+view,
+text {
+	font-size: 24rpx;
+	font-family: PingFang SC;
+	font-weight: 500;
+	color: rgba(51, 51, 51, 1);
+	box-sizing: border-box;
+}
+image {
+	display: block;
+	width: 100%;
+	height: 100%;
+}
+input {
+	display: block;
+	border: none;
+	outline: none;
+	box-sizing: border-box;
+}
+textarea {
+	display: block;
+	width: 100%;
+	height: 100%;
+	font-size: 26rpx;
+	color: #333333;
+}
+page {
+	background: #fafafa;
+}
+.flex {
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+}
+.flexs {
+	display: flex;
+	align-items: center;
+}
+.center {
+	display: flex;
+	align-items: center;
+	justify-content: center;
+}
+.footer_btn {
+	position: fixed;
+	left: 0;
+	display: flex;
+	align-items: center;
+	justify-content: space-around;
+	color: #ffffff;
+	font-size: 30rpx;
+	font-weight: bold;
+	width: 100%;
+	height: 98rpx;
+	bottom: 0;
+	background: #debb81;
+}
+button {
+	background: transparent;
+	border: none;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	padding: 0;
+	margin: 0;
+	overflow: unset;
+	color: #ffffff;
+	font-size: 30rpx;
+}
+button::after {
+	width: 0;
+	height: 0;
+}
+.hover-view {
+	opacity: 0.7;
+}
+scroll-view {
+	box-sizing: border-box;
+}
+</style>

BIN
components/.DS_Store


+ 127 - 0
components/goods-detail.vue

@@ -0,0 +1,127 @@
+<template>
+	<uni-popup ref="more-detail" type="bottom" :mask-click="false" height="30px">
+		<view class="more-detail">
+			<view class="more-detail-close" @click="$refs['more-detail'].close()">
+				<image src="/static/image/home/guanbi@2x.png" mode=""></image>
+			</view>
+			<view class="more-detail-head">商品详情</view>
+			<view class="content">
+				<view>{{goods.goods_name}}</view>
+				<view class="props">
+					<view>
+						<view class="label">参考价:</view>
+						<view class="value">¥{{goods.price}}</view>
+					</view>
+					<view>
+						<view class="label">参考概率:</view>
+						<view class="value">{{rate.value}}</view>
+					</view>
+				</view>
+			</view>
+			<view class="goods.xiangqing">
+				<!-- 产品多图 -->
+				<image v-for="xq in goods.xiangqing" :src="xq" mode="widthFix"></image>  
+				<!-- 产品单图 -->
+				<!-- <image :src="goods.image" @click="lookImg()" mode="widthFix"></image> -->
+			</view>
+		</view>
+	</uni-popup>
+</template>
+
+<script>
+	export default {
+		name:"payment",
+		props:{
+			//盒子id
+			boxId: Number,
+		},
+		data() {
+			return {
+				goods: {},
+				rate: {},
+				boxDetail:{}
+			};
+		},
+		methods: {
+			/**
+			 * 打开页面
+			 * 
+			 */
+			open (goods, rate){
+				this.goods = goods
+				this.rate = rate || {}
+				this.$refs['more-detail'].open()
+			},
+			lookImg () {
+				uni.previewImage({
+						urls: [this.goods.image],
+						longPressActions: {
+								itemList: ['发送给朋友', '保存图片', '收藏'],
+								success: function(data) {
+										console.log('选中了第' + (data.tapIndex + 1) + '个按钮,第' + (data.index + 1) + '张图片');
+								},
+								fail: function(err) {
+										console.log(err.errMsg);
+								}
+						}
+				});
+			},
+			//获取详情
+			getDetails () {
+				this.$api.boxDetail({box_id:this.boxId}).then(res=>{
+					if (res.code === 1) {
+						this.boxDetail = res.data
+						this.record3 = res.data.record.slice(0, 3)
+						console.log(this.boxDetail)
+					}
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+.more-detail {
+	max-height: 70vh;
+	overflow: auto;
+	background: #FFFFFF;
+	border-radius: 20rpx 20rpx 0rpx 0rpx;
+	position: relative;
+	.more-detail-close {
+		top: 30rpx;
+		right: 30rpx;
+		width: 44rpx;
+		height: 44rpx;
+		position: absolute;
+	}
+	.content{
+		padding: 0 50rpx 30rpx 50rpx;
+	}
+	.more-detail-head {
+		padding: 0 50rpx 150rpx 50rpx;
+		text-align: center;
+		font-size: 30rpx;
+		font-weight: bold;
+		padding: 40rpx 0 50rpx 0;
+	}
+	.props{
+		display: flex;
+		margin: 16rpx 0;
+		>view{
+			display: flex;
+			flex: 1;
+			align-items: center;
+		}
+		.label{
+			font-size: 28rpx;
+			color: #959595;
+		}
+		.value{
+			margin-left: 10rpx;
+			font-size: 32rpx;
+			font-weight: bold;
+		}
+	}
+}
+
+</style>

+ 317 - 0
components/lff-barrage/lff-barrage.vue

@@ -0,0 +1,317 @@
+<template>
+	<view style="overflow: hidden;position: absolute;width: 100%;height: 100%;pointer-events: none; top: 0;">
+		<view class="danmu-li" v-for="(item,index) in listData" :class="item.type" :style="item.style" :key="index">
+			<!-- <view class="danmu-inner">
+				<view class="user-box">
+					<view class="user-img">
+						<view class="img-box">
+							<image :src="item.avatar" mode="aspectFit"></image>
+						</view>
+					</view>
+					<view class="user-text cl1">
+						{{item.username}}
+					</view>
+					<view class="user-status cl1">
+						抽中了 {{item.goods_name}}
+					</view>
+				</view>
+			</view> -->
+      
+      <!-- 飘屏项start -->
+      <view class="pp-item">
+        <view class="avatar">
+          <image :src="item.avatar" mode=""></image>
+        </view>
+        <view class="text-view">
+          <text class="username">{{item.username}}</text>
+          <text class="goods-name">抽中了 {{item.goods_name}}</text>
+        </view>
+      </view>
+      <!-- 飘屏项end -->
+      
+    </view>
+	</view>
+</template>
+<script>
+	export default {
+		props: {
+			//rightToLeft leftToRight leftBottom
+			type: {
+				type: String,
+				default: 'rightToLeft'
+			},
+			list: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			minTime: {
+				type: Number,
+				default: 4
+			},
+			maxTime: {
+				type: Number,
+				default: 9
+			},
+			minTop: {
+				type: Number,
+				default: 0
+			},
+			maxTop: {
+				type: Number,
+				default: 240
+			},
+			hrackH: { //轨道高度
+				type: Number,
+				default: 40
+			}
+		},
+		data() {
+			return {
+				listData: []
+			}
+		},
+		mounted() {
+			//leftBottom 使用参数
+			if (this.type === 'leftBottom') {
+				this.hrackNum = Math.floor(this.maxTop / this.hrackH);
+			}
+		},
+		methods: {
+			add(obj) {
+				let data = {
+					item: obj.item,
+          goods_name: obj.goods_name,
+          username: obj.username,
+          avatar: obj.avatar,
+					id:Date.parse(new Date()),
+					time: Math.ceil(Math.floor(Math.random() * (this.maxTime - this.minTime + 1) + this.minTime)),
+					type: this.type
+				}
+				if (this.type === 'leftBottom') {
+					let objData = {
+						item: data.item,
+            goods_name: data.goods_name,
+            username: data.username,
+            avatar: data.avatar,
+						type: 'leftBottomEnter',
+						style: {
+							transition: `all 0.5s`,
+							animationDuration: `0.5s`,
+							transform: `translateX(0%)`,
+							bottom: `${this.minTop}px`
+						}
+					}
+					let listLen = this.listData.length;
+					let hrackNum = this.hrackNum;
+					for (let i in this.listData) {
+						if(this.listData[i].status === 'reuse'){ //重用
+							this.$set(this.listData,i,objData);
+						}else if(this.listData[i].status === 'reset'){ //重置
+							this.listData[i].style.transition = 'none';
+							this.listData[i].style.bottom = 0;
+							this.listData[i].status = 'reuse';
+						}else if(this.listData[i].status === 'recycle'){ //回收
+							this.listData[i].type = 'leftBottomExit';
+							this.listData[i].status = 'reset';
+						}else{
+							this.listData[i].style.bottom = parseInt(this.listData[i].style.bottom) + this.hrackH + 'px';
+						}
+						if(parseInt(this.listData[i].style.bottom) >= (this.maxTop - this.hrackH) && this.listData[i].status !== 'reset'){ //需要回收
+							this.listData[i].status = 'recycle';
+						}
+					}
+					if(listLen < hrackNum + 2){
+						this.listData.push(objData);
+					}
+				} else if (this.type === 'rightToLeft') {
+					let objData = {
+						item: data.item,
+            goods_name: data.goods_name,
+            username: data.username,
+            avatar: data.avatar,
+						type: 'rightToLeft',
+						style: {
+							animationDuration: `${data.time}s`,
+							top: `${Math.ceil(Math.random() * (this.maxTop - this.minTop + 1) + this.minTop)}px`
+						},
+						delTime: Date.parse(new Date()) + data.time * 1200
+					}
+					for (let i in this.listData) {
+						if (this.listData[i].delTime <= Date.parse(new Date())) {
+							this.repaint(i, objData.type);
+							objData.type = '';
+							this.$set(this.listData, i, objData);
+							return
+						}
+					}
+					this.listData.push(objData);
+				}
+			},
+			repaint(index, type) {
+				setTimeout(() => {
+					this.listData[index].type = type;
+				}, 100)
+			}
+		}
+
+	}
+</script>
+<style>
+
+</style>
+<style lang="scss">
+	@keyframes leftBottomEnter {
+		0% {
+			transform: translateY(100%);
+			opacity: 0;
+		}
+
+		100% {
+			transform: translateY(0%);
+			opacity: 1;
+		}
+	}
+
+	@keyframes leftBottomExit {
+		0% {
+			transform: translateY(0%);
+			opacity: 1;
+		}
+		
+		100% {
+			transform: translateY(-200%);
+			opacity: 0;
+		}
+	}
+
+	@keyframes leftToRight {
+		0% {
+			transform: translateX(-100%);
+		}
+
+		100% {
+			transform: translateX(100%);
+		}
+	}
+
+	@keyframes rightToLeft {
+		0% {
+			transform: translateX(100%);
+		}
+
+		100% {
+			transform: translateX(-100%);
+		}
+	}
+
+	.danmu-li {
+		position: absolute;
+		width: 100%;
+		transform: translateX(100%);
+		animation-timing-function: linear;
+
+		&.leftBottomEnter {
+			animation-name: leftBottomEnter;
+		}
+		&.leftBottomExit{
+			animation-name: leftBottomExit;
+			animation-fill-mode: forwards;
+		}
+
+		&.rightToLeft {
+			animation-name: rightToLeft;
+		}
+
+		&.leftToRight {
+			animation-name: rightToLeft;
+		}
+
+		.danmu-inner {
+			display: inline-block;
+
+			.user-box {
+				display: flex;
+				padding: 3rpx 40rpx 3rpx 10rpx;
+				background: rgba(0, 0, 0, 0.3);
+				border-radius: 32rpx;
+				align-items: center;
+
+				.user-img {
+					.img-box {
+						display: flex;
+
+						image {
+							width: 58rpx;
+							height: 58rpx;
+							background: rgba(55, 55, 55, 1);
+							border-radius: 50%;
+						}
+					}
+				}
+
+				.user-status {
+					margin-left: 10rpx;
+					white-space: nowrap;
+					font-size: 28rpx;
+					font-weight: 400;
+					color: rgba(255, 255, 255, 1);
+				}
+
+				.user-text {
+					margin-left: 10rpx;
+					// white-space: nowrap;
+					font-size: 28rpx;
+					font-weight: 400;
+					width: 80rpx;
+					color: rgba(255, 255, 255, 1);
+				}
+			}
+		}
+	}
+
+  .pp-item {
+    top: 40rpx;
+    left: 40rpx;
+    background-image: url(../../static/image/box/pp@2x.png);
+    background-position: center;
+    background-repeat: no-repeat;
+    background-size: 100% 100%;
+    width: 434rpx;
+    height: 40rpx;
+    line-height: 40rpx;
+    position: absolute;
+    padding-left: 52rpx;
+    .avatar {
+      position: absolute;
+      z-index: 2;
+      left: -10rpx;
+      top: 50%;
+      transform: translateY(-50%);
+      width: 44rpx;
+      height: 44rpx;
+      border-radius: 50%;
+      border: 2rpx solid #FFF;
+      background: #D8D8D8;
+      overflow: hidden;
+    }
+    .text-view {
+      .username {
+        font-size: 24rpx;
+        font-family: PingFangSC-Regular, PingFang SC;
+        font-weight: 400;
+        color: #FFC50F;
+        line-height: 34rpx;
+        margin-right: 22rpx;
+      }
+      .goods-name {
+        font-size: 24rpx;
+        font-family: PingFangSC-Regular, PingFang SC;
+        font-weight: 400;
+        color: #FFFFFF;
+        line-height: 34rpx;
+      }
+    }
+  }
+</style>

+ 15 - 0
components/lff-barrage/markdown.md

@@ -0,0 +1,15 @@
+#如何使用
+###js
+```javascript
+import lffBarrage from '@/components/lff-barrage/lff-barrage.vue'
+components:{lffBarrage},
+methods:{
+	colrdo(){ //插入一条弹幕
+		this.$refs.lffBarrage.add({item:'你好呀小伙子'});
+	}
+}
+```
+###HTML
+```html
+<lff-barrage ref="lffBarrage"></lff-barrage>
+```

+ 150 - 0
components/lunbobox.vue

@@ -0,0 +1,150 @@
+<template>
+	<view class="lunbobox-container">
+		<template v-for="(lunbobox, index) in lunboboxList">
+			<view :key="index" :class="['lunbobox', showIndex == index && 'show', nextIndex == index && 'next']"  @click="toDetail">
+				<view class="left-info">
+					<view class="avatar">
+						<image :src="'/static/def-avatar.png'" mode="heightFix"></image>
+					</view>
+					<view class="user">{{ lunbobox.nickname }}</view>
+					<view class="goods-name">获得{{ lunbobox.goods_name }}</view>
+				</view>
+				<view class="goods-img">
+					<image :src="lunbobox.goods_image" mode="widthFix"></image>
+				</view>
+			</view>
+		</template>
+	</view>
+</template>
+
+<script>
+	export default {
+		name:"lunbobox",
+		data() {
+			return {
+				lunboboxList: [],
+				showIndex: 0
+			};
+		},
+		computed:{
+			nextIndex(){
+				if(this.showIndex < this.lunboboxList.length - 1){
+					return this.showIndex + 1
+				} else {
+					return 0
+				}
+			},
+			cueeShowBox(){
+				return this.lunboboxList[this.showIndex]
+			}
+		},
+		created() {
+			this.getLunboBoxList()
+			setInterval(() => {
+				this.showIndex = this.nextIndex
+			}, 3000)
+		},
+		methods: {
+			getLunboBoxList(){
+				this.$api.lunbobox().then( ({code, data}) => {
+					if(code == 1){
+						this.lunboboxList = this.dataHandler(data)
+					}
+				})
+			},
+			dataHandler(data){
+				let dataList = data.map(item => {
+					item.nickname = this.nicknameHandler(item.nickname)
+					item.goods_name = this.goodsNameHandler(item.goods_name)
+					return item
+				})
+				return dataList
+			},
+			nicknameHandler(nickname){
+				let left = nickname.substring(0, 1)
+				let right = nickname.substring(nickname.length - 2, nickname.length - 1)
+				return `${left}***${right}`
+			},
+			goodsNameHandler(goodsName){
+				let len = 4
+				if(goodsName.length <= len){
+					return goodsName
+				}
+				return goodsName.substring(0, len)
+			},
+			toDetail(){
+				const boxId = this.cueeShowBox.box_id
+				console.log(this.cueeShowBox)
+				if(boxId){
+					uni.navigateTo({url:'/pages/index/details?id=' + boxId + '&type=0'})
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.user,.goods-name{
+		color: #FFFFFF;
+	}
+	.lunbobox-container{
+		padding: 10rpx 10rpx;
+		margin-top: 5rpx;
+		.lunbobox{
+			transition: .5s;
+			opacity: 0;
+			display: flex;
+			height: 0;
+			background-image: url('@/static/lunbo.png');   
+			background-repeat: no-repeat;
+			background-size: auto 100%;
+			width: 205px;
+			$height: 35px;
+			$avatar-size: 28px;
+			position: relative;
+			.avatar{
+				width: $avatar-size;
+				height: $avatar-size;
+				border: solid 1px #fff;
+				border-radius: 50px;
+				display: flex;
+				justify-content: center;
+				align-items: center;
+				overflow: hidden;
+				margin-left: 5px;
+				margin-right: 10px;
+			}
+			&.show{
+				opacity: 1;
+				height: $height;
+			}
+			&.next{
+				height: $height;
+				opacity: .5;
+				transform: scale(0.7) translateX(-20%);
+			}
+			.left-info{
+				height: 100%;
+				display: flex;
+				width: 162px;
+				align-items: center;
+				>view{
+					font-size: 13px;
+					font-weight: 500;
+				}
+			}
+			.goods-img{
+				height: 83%;
+				transform: translateY(4px);
+				flex: 1;
+				display: flex;
+				align-items: center;
+				overflow: hidden;
+				justify-content: center;
+				image{
+					width: 45%;
+				}
+			}
+		}
+	}
+</style>

+ 287 - 0
components/payment.vue

@@ -0,0 +1,287 @@
+<template>
+	<uni-popup ref="popup" type="bottom" :mask-click="false">
+		<view class="pay">
+		<view class="pay_close" @click="$refs.popup.close()">
+			<image src="/static/image/home/guanbi@2x.png" mode=""></image>
+		</view>
+		<view class="pay_head">支付</view>
+			<view class="pay_shop flexs">
+				<view class="pay_shop_img">
+					<view class="pay_shop_img_top">
+						<view class="pay_shop_img_left">
+							<image :src="boxMessage.image" mode="aspectFill"></image>
+						</view>
+						<view class="pay_shop_img_right">
+							<image :src="item" mode="aspectFill" v-for="(item,index) in boxMessage.right" :key="index"></image>
+						</view>
+					</view>
+					<view class="pay_shop_img_bot">
+						<image :src="item" mode="aspectFill" v-for="(item,index) in boxMessage.bot" :key="index"></image>
+					</view>
+				</view>
+				<view class="pay_shop_main">
+					<view class="pay_shop_name">{{ boxMessage.box_name }}</view>
+					<view class="pay_shop_price flex">
+						<view class="pay_shop_price_l">{{ boxMessage.coin_amount }}金币</view>
+						<view class="pay_shop_price_btn center" v-if="boxMessage.coin_not_enough">金额不足</view>
+					</view>
+
+				</view>
+			</view>
+			<view class="pay_attention">{{ boxMessage.notice }}</view>
+			<view class="pay_contant flexs">
+				<image @click="isConsent = !isConsent" :src="isConsent ? '/static/image/publice/xuanzhong1@2x.png' : '/static/image/publice/weixuanzhong1@2x.png' " mode=""></image>
+				<text @click="goBuyer">我已阅读并同意《喵喵开盒买家须知》</text>
+			</view>
+			<view class="pay_ul flex">
+				<button class="pay_gold" @click="goldPay" v-if="!boxMessage.coin_not_enough" hover-class="hover-view" :disabled="!payFlag">金币支付</button>
+				<button v-else class="pay_ul_btn" @click="recharge()" hover-class="hover-view">立即充值</button>
+				<!--button class="pay_ul_btn" @click="patternIndex(index)" hover-class="hover-view" v-for="(item,index) in 2" :key="index">{{ index == 0 ? '微信支付'+ boxMessage.rmb_amount +'金币' : '支付宝支付'+ boxMessage.rmb_amount +'金币' }}</button-->
+				<button class="pay_ul_btn" @click="balance()" hover-class="hover-view">余额支付</button>
+				<!-- <button class="pay_ul_btn" @click="cashPay()" hover-class="hover-view">现金支付</button> -->
+			</view>
+		</view>
+	</uni-popup>
+</template>
+
+<script>
+	export default {
+		name:"payment",
+		props:{
+			//盒子id
+			boxId: Number,
+			//支付页面
+			payPage: String
+		},
+		data() {
+			return {
+				isConsent: true,//是否同意
+				boxMessage: {} ,//盒子详情
+				payFlag: true, //支付锁 防止重复点击
+				payUrls: {}//支付链接
+			};
+		},
+		methods: {
+			/**
+			 * 打开页面
+			 * 
+			 * @param {Object} type 开盒类型 1:试玩 其他:正式开盒
+			 * @param {Object} num 数量 1 5 9
+			 */
+			open (type, num){
+				this.isConsent = true
+				this.$api[type == 1 ? 'haveATry' : 'createOrder']({
+					box_id: this.boxId,
+					num: num,
+					msg: type == 1 ? '试玩' : '创建订单中',
+				}).then( ({code, data}) => {
+					if (code === 1) {
+						//创建订单成功
+						if (type != 1) {
+							//缓存订单id
+							uni.setStorageSync('order_id', data.order_id)
+							//正式开盒
+							this.$emit("create-success")
+							data.right = []
+							data.bot = []
+							data.image = data.images[0]
+							data.images.forEach((item,index)=>{
+								if (index > 0 && index < 3) {
+									data.right.push(item)
+								}
+								if (index > 2) {
+									data.bot.push(item)
+								}
+							})
+							this.boxMessage = data
+							//支付链接
+							this.payUrls.alipay = data.alipay
+							this.payUrls.wechat = data.wechat
+							this.$refs.popup.open()
+						} else {
+							//返回结果
+							this.$emit("show-result", data)
+						}
+					}
+				})
+			},
+			//买家须知
+			goBuyer () {
+				uni.navigateTo({url:'/pagesA/pages/buyer'})
+			},
+			//金币支付
+			goldPay () {
+				if (!this.isConsent) return uni.showToast({title:'请阅读并勾选买家须知',icon:'none'})
+				if (!this.payFlag) return
+				this.payFlag = false
+				//访问后台支付
+				this.$api.coinPay({order_id: this.boxMessage.order_id}).then(({code, data}) => {
+					//解除支付锁
+					this.payFlag = true
+					if (code === 1) {
+						//关闭弹窗
+						this.$refs.popup.close()
+						//返回结果
+						this.$emit("show-result", data.prize)
+					}
+				})
+			},
+			//余额支付
+			balance () {
+				if (!this.isConsent) return uni.showToast({title:'请阅读并勾选买家须知',icon:'none'})
+				if (!this.payFlag) return
+				this.payFlag = false
+				//访问后台支付
+				this.$api.cmoneyPay({order_id: this.boxMessage.order_id}).then(({code, data}) => {
+					//解除支付锁
+					this.payFlag = true
+					if (code === 1) {
+						//关闭弹窗
+						this.$refs.popup.close()
+						//返回结果
+						this.$emit("show-result", data.prize)
+					}
+				})
+			},
+			//去充值
+			recharge () {
+				if (!this.isConsent) return uni.showToast({title:'请阅读并勾选买家须知',icon:'none'})
+				uni.navigateTo({url:'/pages/me/recharge'})
+			},
+			//现金支付
+			cashPay(){
+				if (!this.isConsent) return uni.showToast({title:'请阅读并勾选买家须知',icon:'none'})
+				if (!this.payFlag) return
+				this.payFlag = false
+				uni.navigateTo({
+					url:`/pages/tabbar/cashPay/cashPay?alipay=${this.payUrls.alipay}&wechat=${this.payUrls.wechat}&payPage=${this.payPage}`,
+					complete: () => {
+						this.payFlag = true
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+.pay {
+	background: #FFFFFF;
+	border-radius: 20rpx 20rpx 0rpx 0rpx;
+	padding: 0 50rpx 32rpx 50rpx;
+	position: relative;
+	.pay_close {
+		top: 30rpx;
+		right: 30rpx;
+		width: 44rpx;
+		height: 44rpx;
+		position: absolute;
+	}
+	.pay_head {
+		text-align: center;
+		font-size: 30rpx;
+		font-weight: bold;
+		padding: 40rpx 0 50rpx 0;
+	}
+	.pay_shop {
+		
+		margin-bottom: 40rpx;
+		.pay_shop_main {
+			display: flex;
+			flex: 1;
+			height: 200rpx;
+			flex-direction: column;
+			justify-content: space-around;
+		}
+		.pay_shop_img {
+			width: 200rpx;
+			height: 200rpx;
+			padding: 12rpx;
+			.pay_shop_img_top {
+				display: flex;
+				margin-bottom: 12rpx;
+				.pay_shop_img_left {
+					image {
+						width: 114rpx;
+						height: 114rpx;
+						border-radius: 6rpx;
+					}
+					margin-right: 12rpx;
+				}
+				.pay_shop_img_right {
+					image {
+						width: 51rpx;
+						height: 51rpx;
+						&:first-child {
+							margin-bottom: 12rpx;
+						}
+						border-radius: 6rpx;
+					}
+				}
+			}
+			.pay_shop_img_bot {
+				display: flex;
+				justify-content: flex-end;
+				image {
+					width: 51rpx;
+					height: 51rpx;
+					margin-left: 12rpx;
+					&:first-child {
+						margin-left: 0;
+					}
+					border-radius: 6rpx;
+				}
+			}
+			margin-right: 30rpx;
+		}
+		.pay_shop_name {
+			font-size: 28rpx;
+		}
+		.pay_shop_price_l {
+			color: #CF271B;
+			font-size: 30rpx;
+		}
+		.pay_shop_price_btn {
+			width: 183rpx;
+			height: 50rpx;
+			color: #FA7E48;
+			font-size: 30rpx;
+			font-weight: bold;
+			background: rgba(246, 175, 50, 0.5);
+			border-radius: 25rpx;
+		}
+	}
+	.pay_attention {
+		color: #666666;
+	}
+	.pay_contant {
+		margin: 24rpx 0 59rpx 0;
+		image {
+			width: 32rpx;
+			height: 32rpx;
+			margin-right: 10rpx;
+		}
+		text {
+			font-size: 26rpx;
+		}
+	}
+	.pay_gold {
+		width: 310rpx;
+		height: 78rpx;
+		font-size: 26rpx;
+		background: -webkit-linear-gradient(60deg, #ffc8de 0%, #ff67a4 100%);
+		border-radius: 39rpx;
+	}
+	.pay_ul_btn {
+		width: 310rpx;
+		height: 78rpx;
+		font-size: 26rpx;
+		background: -webkit-linear-gradient(60deg, #ffc8de 0%, #ff67a4 100%);
+		border-radius: 39rpx;
+		&:last-child {
+			background: -webkit-linear-gradient(0deg, #89f7fe 0%, #66a6ff 100%);
+		}
+	}
+}
+
+</style>

+ 137 - 0
components/pick-regions/pick-regions.vue

@@ -0,0 +1,137 @@
+<template>
+    <picker mode="multiSelector" 
+            :value="multiIndex" 
+            :range="multiArray" 
+            @change="handleValueChange"
+            @columnchange="handleColumnChange">
+        <slot></slot>
+    </picker>
+</template>
+
+<script>
+    const CHINA_REGIONS = require('./regions.json')
+	export default {
+        props:{
+            defaultRegions:{
+                type:Array,
+                default(){
+                    return []
+                }
+            },
+            defaultRegionCode:{
+                type:String
+            },
+            defaultRegion:[String,Array]
+        },
+		data() {
+			return {
+                cityArr:CHINA_REGIONS[0].childs,
+                districtArr:CHINA_REGIONS[0].childs[0].childs,
+                multiIndex: [0, 0, 0],
+                isInitMultiArray:true,
+			}
+		},
+        watch:{
+            defaultRegion:{
+                handler(region,oldRegion){
+                    if(Array.isArray(region)){
+                        // 避免传的是字面量的时候重复触发
+                        oldRegion = oldRegion || []
+                        if(region.join('')!==oldRegion.join('')){
+                            this.handleDefaultRegion(region)
+                        }
+                    }else if(region&&region.length == 6){
+                        this.handleDefaultRegion(region)
+                    }else{
+                        console.warn('defaultRegion非有效格式')
+                    }
+                },
+                immediate:true,
+            }
+        },
+        computed:{
+            multiArray(){
+                return this.pickedArr.map(arr=>arr.map(item=>item.name))
+            },
+            pickedArr(){
+                // 进行初始化
+                if(this.isInitMultiArray){
+                    return [
+                        CHINA_REGIONS,
+                        CHINA_REGIONS[0].childs,
+                        CHINA_REGIONS[0].childs[0].childs
+                    ]
+                }
+                return [CHINA_REGIONS,this.cityArr,this.districtArr];
+            }
+        },
+		methods: {
+            handleColumnChange(e){
+                // console.log(e);
+                this.isInitMultiArray = false;
+                const that = this;
+                let col = e.detail.column;
+                let row = e.detail.value;
+                that.multiIndex[col] = row;
+                try{
+                    switch(col){
+                        case 0:
+                            if(CHINA_REGIONS[that.multiIndex[0]].childs.length==0){
+                                that.cityArr = that.districtArr = [CHINA_REGIONS[that.multiIndex[0]]]
+                                break;
+                            }
+                            that.cityArr = CHINA_REGIONS[that.multiIndex[0]].childs
+                            that.districtArr = CHINA_REGIONS[that.multiIndex[0]].childs[that.multiIndex[1]].childs
+                            break;
+                        case 1:
+                            that.districtArr = CHINA_REGIONS[that.multiIndex[0]].childs[that.multiIndex[1]].childs
+                            break;
+                        case 2:
+                            break;
+                    }
+                }catch(e){
+                    // console.log(e);
+                    that.districtArr = CHINA_REGIONS[that.multiIndex[0]].childs[0].childs
+                }
+                
+            },
+            handleValueChange(e){
+                // 结构赋值
+                let [index0,index1,index2] = e.detail.value;
+                let [arr0,arr1,arr2] = this.pickedArr;
+                let address = [arr0[index0],arr1[index1],arr2[index2]];
+                // console.log(address);
+                this.$emit('getRegion',address)
+            },
+            handleDefaultRegion(region){
+                const isCode = !Array.isArray(region)
+                this.isInitMultiArray = false;
+                let children = CHINA_REGIONS
+                for(let i=0;i<3;i++){
+                    for(let j=0;j<children.length;j++){
+                       let condition = isCode?children[j].code==region.slice(0,(i+1)*2):children[j].name.includes(region[i]);
+                       if(condition){
+                           // 匹配成功进行赋值
+                           // console.log(i,j,children.length-1);
+                           children = children[j].childs;
+                           if(i==0){
+                               this.cityArr = children
+                           }else if(i==1){
+                               this.districtArr = children
+                           }
+                           this.$set(this.multiIndex,i,j)
+                           // console.log(this.multiIndex);
+                           break;
+                       }else{
+                           // 首次匹配失败就用默认的初始化
+                           // console.log(i,j,children.length-1);
+                           if(i==0 && j==(children.length-1)){
+                               this.isInitMultiArray = true;
+                           }
+                       }
+                    }
+                }
+            }
+		},
+	}
+</script>

File diff suppressed because it is too large
+ 0 - 0
components/pick-regions/regions.json


+ 278 - 0
components/prize-flying.vue

@@ -0,0 +1,278 @@
+<template>
+	<view class="prize-flying">
+		<template v-for="(flyLeft, index) in flyLeftList">
+			<!-- <view :key="'fly-left-' + index" :class="['fly-prize', 'fly-left']" :style="flyStyle(flyLeft)"></view> -->
+			<view
+				:key="'fly-left-' + index"
+				:class="['fly-prize', 'fly-left']"
+				:style="{ background: flyLeft.image, '-webkit-animation-duration': flySpeed + 's', 'animation-duration': flySpeed + 's' }"
+			></view>
+		</template>
+		<template v-for="(flyCenter, index) in flyCenterList">
+			<!-- <view :key="'fly-center-' + index" :class="['fly-prize', 'fly-center']" :style="flyStyle(flyCenter)"></view> -->
+			<view
+				:key="'fly-center-' + index"
+				:class="['fly-prize', 'fly-center']"
+				:style="{ background: flyCenter.image, '-webkit-animation-duration': flySpeed + 's', 'animation-duration': flySpeed + 's' }"
+			></view>
+		</template>
+		<template v-for="(flyRight, index) in flyRightList">
+			<!-- <view :key="'fly-right-' + index" :class="['fly-prize', 'fly-right']" :style="flyStyle(flyRight)"></view> -->
+			<view
+				:key="'fly-right-' + index"
+				:class="['fly-prize', 'fly-right']"
+				:style="{ background: flyRight.image, '-webkit-animation-duration': flySpeed + 's', 'animation-duration': flySpeed + 's' }"
+			></view>
+		</template>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'prize-flying',
+	props: {
+		boxId: Number
+	},
+	data() {
+		return {
+			prizeList: [],
+			flyLeftList: [],
+			flyCenterList: [],
+			flyRightList: [],
+			indexRecord: 0, //记录奖品下标
+			intervalTime: 2, //飞出频率 秒
+			interval: null,
+			flySpeed: 25 //飞行速度 秒 越小越快
+		};
+	},
+	created() {
+		this.loadPrizeList();
+	},
+	destroyed() {
+		//销毁
+		clearInterval(this.interval);
+	},
+	computed: {
+		prizeLength() {
+			return this.prizeList.length;
+		}
+	},
+	methods: {
+		//加载奖品列表
+		loadPrizeList() {
+			if (!this.boxId) return;
+			this.$api.boximages({ box_id: this.boxId }).then(({ data }) => {
+				this.prizeList = data.goodsimagelist;
+				this.flyInterval();
+			});
+		},
+		//循环
+		flyInterval() {
+			this.pushFlyList();
+			this.interval = setInterval(() => {
+				this.pushFlyList();
+			}, this.intervalTime * 1000);
+		},
+		pushFlyList() {
+			let count = 0;
+			while (count < 3) {
+				if (this.indexRecord == this.prizeLength - 1) {
+					this.indexRecord = 0;
+				} else {
+					this.indexRecord++;
+				}
+				let prize = this.prizeList[this.indexRecord];
+				switch (count) {
+					case 0:
+						this.flyLeftList.push(prize);
+						break;
+					case 1:
+						this.flyCenterList.push(prize);
+						break;
+					case 2:
+						this.flyRightList.push(prize);
+						break;
+				}
+				count++;
+			}
+		},
+		flyStyle(prize) {
+			return {
+				background: `url(${prize.image})`,
+				'-webkit-animation-duration': `${this.flySpeed}s`,
+				'animation-duration': `${this.flySpeed}s`
+			};
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.prize-flying {
+	z-index: 99;
+	width: 200rpx;
+	position: absolute;
+	bottom: 50%;
+	left: 50%;
+	transform: translateX(-50%);
+	.fly-prize {
+		text-align: center;
+		position: absolute;
+		top: 100%;
+		opacity: 0;
+		width: 78rpx;
+		height: 78rpx;
+		background-repeat: no-repeat !important;
+		background-size: 100% 100% !important;
+		border-radius: 50%;
+		background: #fff;
+		border: 1px solid #3277ff;
+		padding: 14rpx;
+	}
+	.fly-left {
+		left: 0px;
+		z-index: 5;
+		-webkit-animation: flyleft;
+		animation: flyleft;
+	}
+	.fly-center {
+		left: 60rpx;
+		z-index: 3;
+		-webkit-animation: flycenter;
+		animation: flycenter;
+	}
+	.fly-right {
+		right: 0px;
+		z-index: 5;
+		-webkit-animation: flyright;
+		animation: flyright;
+	}
+}
+// 动画
+@keyframes flyleft {
+	0% {
+		// top: 100%;
+		-webkit-transform: scale(0) translateX(0);
+		transform: scale(0) translateX(0);
+		opacity: 0.6;
+	}
+	3% {
+		// top: 95%;
+	}
+	5% {
+		-webkit-transform: scale(1) translateX(0) translateY(-80%);
+		transform: scale(1) translateX(0) translateY(-80%);
+		opacity: 0.8;
+	}
+	20% {
+		opacity: 1;
+	}
+	30% {
+		opacity: 0.2;
+	}
+	32% {
+		opacity: 0;
+		visibility: hidden;
+	}
+	50% {
+		-webkit-transform: scale(1.9) translateX(-40px) translateY(-340%);
+		transform: scale(1.9) translateX(-40px) translateY(-340%);
+	}
+	100% {
+		// top: 0%;
+		-webkit-transform: translateX(-100px);
+		transform: translateX(-100px);
+	}
+}
+@keyframes flycenter {
+	0% {
+		// top: 100%;
+		-webkit-transform: scale(0) translateX(0);
+		transform: scale(0) translateX(0);
+		opacity: 0.6;
+	}
+
+	3% {
+		// top: 95%;
+	}
+
+	5% {
+		-webkit-transform: scale(1) translateX(0) translateY(-80%);
+		transform: scale(1) translateX(0) translateY(-80%);
+		opacity: 0.8;
+	}
+
+	20% {
+		opacity: 1;
+	}
+
+	30% {
+		opacity: 0.2;
+	}
+
+	32% {
+		opacity: 0;
+		visibility: hidden;
+	}
+
+	34% {
+		opacity: 0;
+	}
+
+	50% {
+		-webkit-transform: scale(1.9) translateX(0) translateY(-340%);
+		transform: scale(1.9) translateX(0) translateY(-340%);
+	}
+	100% {
+		// top: 0%;
+		-webkit-transform: translateX(0);
+		transform: translateX(0);
+	}
+}
+@keyframes flyright {
+	0% {
+		// top: 100%;
+		-webkit-transform: scale(0) translateX(0);
+		transform: scale(0) translateX(0);
+		opacity: 0.6;
+	}
+
+	3% {
+		// top: 95%;
+	}
+
+	5% {
+		-webkit-transform: scale(1) translateX(0) translateY(-80%);
+		transform: scale(1) translateX(0) translateY(-80%);
+		opacity: 0.8;
+	}
+
+	20% {
+		opacity: 1;
+	}
+
+	30% {
+		opacity: 0.2;
+	}
+
+	32% {
+		opacity: 0;
+		visibility: hidden;
+	}
+
+	34% {
+		opacity: 0;
+	}
+
+	50% {
+		-webkit-transform: scale(1.9) translateX(40px) translateY(-340%);
+		transform: scale(1.9) translateX(40px) translateY(-340%);
+	}
+
+	100% {
+		// top: 0%;
+		-webkit-transform: translateX(100px);
+		transform: translateX(100px);
+	}
+}
+</style>

+ 134 - 0
components/recycle.vue

@@ -0,0 +1,134 @@
+<template>
+	<uni-popup ref="recycle" :mask-click="false">
+		<view class="recycle">
+			<view class="recycle_close" @click="$refs.recycle.close()">
+				<image src="/static/image/publice/guanbi@2x.png" mode=""></image>
+			</view>
+			<view class="recycle_head">提示</view>
+			<view class="recycle_price center">
+				<text>¥{{ recycleMessage.amount }}</text>
+				<text>回收金币</text>
+			</view>
+			<view class="recycle_ul">
+				<view class="recycle_ul_li flex" v-for="(item,index) in recycleMessage.goods_info" :key="index">
+					<view class="recycle_ul_li_txt">{{ item.name }}</view>
+					<view class="recycle_ul_li_r flexs">
+						<text>¥{{ item.price }}</text>
+						<text>x{{ item.num }}</text>
+					</view>
+				</view>
+			</view>
+			<view class="recycle_txt">回收规则:平台统一回收折扣10,回收金币直接放【我的】 - 【余额】</view>
+			<view class="sure" @click="$refs.recycle.close()">
+			  确定
+			</view>
+		</view>
+	</uni-popup>
+</template>
+
+<script>
+	export default {
+		name:"recycle",
+		data() {
+			return {
+				recycleMessage: {}
+			};
+		},
+		methods: {
+			//抽奖结束点击回收触发
+			recycle(prizeInfo){
+				let ids = []
+				prizeInfo.forEach(item=>{
+					ids.push(item.prize_id)
+				})
+				this.$api.exchange({record_ids:ids.join(',')}).then(res=>{
+					if (res.code === 1) {
+						this.recycleMessage = res.data
+						this.$refs.recycle.open()
+					}
+				})
+			},
+			//打开回收结果窗口
+			open(recycleMessage){
+				this.recycleMessage = recycleMessage
+				this.$refs.recycle.open()
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.sure{padding: 10rpx 20rpx;border-radius: 30rpx;font-size: 26rpx;background:-webkit-linear-gradient(0deg, #89f7fe 0%, #66a6ff 100%);color: #fff;width: 200rpx;text-align: center;margin: 0 auto;margin-top: 40rpx;}
+	
+.recycle {
+	.sure{padding: 10rpx 20rpx;border-radius: 30rpx;font-size: 26rpx;background: -webkit-linear-gradient(0deg, #89f7fe 0%, #66a6ff 100%);color: #fff;width: 200rpx;text-align: center;margin: 0 auto;margin-top: 40rpx;}
+	width: 640rpx;
+	padding-bottom: 95rpx;
+	position: relative;
+	background: #FFFFFF;
+	box-shadow: 0rpx -5rpx 20rpx 0rpx rgba(0, 0, 0, 0.1);
+	border-radius: 30rpx;
+	.recycle_close {
+		top: 20rpx;
+		right: 30rpx;
+		width: 44rpx;
+		height: 44rpx;
+		position: absolute;
+	}
+	.recycle_head {
+		height: 100rpx;
+		text-align: center;
+		line-height: 100rpx;
+		font-size: 32rpx;
+		font-weight: bold;
+		border-bottom: 2rpx solid #E9E9E9;
+	}
+	.recycle_price {
+		margin: 40rpx 0 0rpx 0;
+		flex-direction: column;
+		text {
+			color: #FA7E48;
+			font-size: 36rpx;
+			font-weight: bold;
+			&:last-child {
+				color: #333333;
+				margin-top: 10rpx;
+				font-size: 26rpx;
+			}
+		}
+	}
+	.recycle_ul {
+		padding: 0 30rpx;
+		margin: 50rpx 0;
+	}
+	.recycle_ul_li {
+		margin-bottom: 30rpx;
+		&:last-child {
+			margin-bottom: 0;
+		}
+		.recycle_ul_li_txt {
+			color: #999999;
+			font-size: 26rpx;
+		}
+		.recycle_ul_li_r {
+			text {
+				color: #999999;
+				font-size: 26rpx;
+				&:last-child {
+					width: 80rpx;
+					text-align: right;
+					color: #FA7E48;
+					font-size: 30rpx;
+				}
+			}
+		}
+	}
+	.recycle_txt {
+		padding: 0 30rpx;
+		color: #999999;
+		font-size: 26rpx;
+	}
+	
+}
+
+</style>

+ 425 - 0
components/show-result.bak.vue

@@ -0,0 +1,425 @@
+<template>
+	<uni-popup ref="prize" mode="top" :mask-click="false">
+		<view v-show="!openLoading" class="prize ">
+			<view class="prize_box">
+				<view class="bj"></view>
+				<view class="prize_close" @click="$refs.prize.close()"><image src="/static/image/home/guanbi@2x.png" mode=""></image></view>
+				<view class="prize-top" :class="'prize-top-' + mode"><image src="/static/image/result/gaizi@2x.png" mode="aspectFit"></image></view>
+				<!-- 1开 -->
+				<view :class="['prize_shop', 'center', 'onebox', 'tag-' + tag, scale && 'prize_shop_scale']" v-if="mode == 0">
+					<view class="left-top-tag">
+						<image class="tag-img" :src="tagImg" mode="aspectFit"></image>
+						<text class="tag-text">{{ tagText }}</text>
+					</view>
+					<image class="one" v-if="prizedata && prizedata.prizeInfo" :src="prizedata.prizeInfo[0].image" mode="widthFix"></image>
+					<text class="colorblack" v-if="prizedata && prizedata.prizeInfo">{{ prizedata.prizeInfo[0].goods_name }}</text>
+				</view>
+				<!-- 5开or10开 -->
+				<view class="prize_ul" :class="'prize_ul-' + mode" v-else>
+					<!-- <scroll-view scroll-x="true" class="scroll-view"> -->
+					<view :class="['prize_ul_li', 'twobox', 'flex', 'tag-' + tagFun(item)]" v-for="(item, index) in prizedata.prizeInfo" :key="index">
+						<view class="left-top-tag">
+							<image class="tag-img" :src="tagImgFun(item)" mode="aspectFit"></image>
+							<text class="tag-text">{{ tagTextFun(item) }}</text>
+						</view>
+						<image class="two" :src="item.image" mode="widthFix"></image>
+						<text class="a">{{ item.goods_name }}</text>
+					</view>
+					<!-- </scroll-view> -->
+				</view>
+				<view class="prize_footer" :class="'prize_footer-' + mode" v-if="type == 0">
+					<!-- 立即收下 -->
+					<view class="btn btn-1" @click="$emit('accept')"></view>
+					<!-- 一键回收 -->
+					<view class="btn btn-0" @click="$emit('recycle', prizedata)"></view>
+				</view>
+				<view class="shiwan center" v-else>试玩结果仅供展示哦~</view>
+			</view>
+		</view>
+		<view v-show="openLoading" class="loading-mask"><image class="loading-gif" src="/h5/static/image/result/kh.gif" mode="aspectFit"></image></view>
+	</uni-popup>
+</template>
+
+<script>
+export default {
+	name: 'show-result',
+	props: {
+		//数量
+		num: Number,
+		/**
+		 * 类型 0:正式开盒 1:试玩
+		 */
+		type: {
+			type: Number,
+			default: 0
+		}
+	},
+	data() {
+		return {
+			//奖品列表
+			prizedata: [],
+			//特效
+			scale: false,
+			//开盒动画
+			openLoading: true
+		};
+	},
+	computed: {
+		//开盒模式 0:1抽 1:5抽 2:9抽
+		mode() {
+			switch (this.num) {
+				case 5:
+					return 1;
+				case 9:
+					return 2;
+			}
+			return 0;
+		},
+		//当前单抽商品
+		currPrizedata() {
+			if (!this.prizedata) {
+				return null;
+			}
+			if (!this.prizedata.prizeInfo || this.prizedata.length < 1) {
+				return null;
+			}
+			return this.prizedata.prizeInfo[0];
+		},
+		//单抽商品品质
+		tag() {
+			return this.tagFun(this.currPrizedata);
+		},
+		//单抽品质文本
+		tagText() {
+			return this.tagTextFun(this.currPrizedata);
+		},
+		//单抽品质角标
+		tagImg() {
+			return this.tagImgFun(this.currPrizedata);
+		}
+	},
+	methods: {
+		open(prizedata) {
+			//打开结果
+			this.$refs.prize.open();
+			//加载动画
+			this.openLoading = true;
+			this.prizedata = prizedata;
+			setTimeout(() => {
+				//关闭加载
+				this.openLoading = false;
+				//品质不等于普通
+				if (this.tag != 'normal') {
+					//开启震动
+					try {
+						uni.vibrate({
+							success: function() {
+								console.log('震动');
+							}
+						});
+					} catch (e) {
+						console.log(e);
+					}
+					//开启效果
+					this.scale = true;
+					setTimeout(() => {
+						this.scale = false;
+					}, 1000);
+				}
+			}, 3000);
+		},
+		//关闭
+		close() {
+			this.$refs.prize.close();
+		},
+		//商品品质
+		tagFun(prizeInfo) {
+			if (!prizeInfo) return null;
+			return prizeInfo.tag;
+		},
+		//品质文本
+		tagTextFun(prizeInfo) {
+			if (!prizeInfo) return null;
+			if (prizeInfo.tag == 'normal') {
+				return '普通';
+			}
+			if (prizeInfo.tag == 'rare') {
+				return '稀有';
+			}
+			if (prizeInfo.tag == 'supreme') {
+				return '史诗';
+			}
+			if (prizeInfo.tag == 'legend') {
+				return '传说';
+			}
+		},
+		//品质角标
+		tagImgFun(prizeInfo) {
+			if (!prizeInfo) return null;
+			if (prizeInfo.tag == 'normal') {
+				return '/h5/pagesA/static/tag-1.png';
+			}
+			if (prizeInfo.tag == 'rare') {
+				return '/h5/pagesA/static/tag-2.png';
+			}
+			if (prizeInfo.tag == 'supreme') {
+				return '/h5/pagesA/static/tag-3.png';
+			}
+			if (prizeInfo.tag == 'legend') {
+				return '/h5/pagesA/static/tag-3.png';
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.bj {
+	// background: url(../static/image/result/guang@2x.png) no-repeat;
+	width: 100%;
+	margin-top: 0rpx;
+	height: 1000rpx;
+	position: relative;
+	flex-direction: column;
+	background-size: 100% 100%;
+	background-position: top;
+	background-repeat: no-repeat;
+	animation: donut-spin 2.2s linear infinite;
+}
+.onebox {
+	height: 372rpx;
+	width: 352rpx;
+	border-radius: 26rpx;
+	background-color: #fff;
+}
+.twobox {
+	height: 186rpx;
+	width: 176rpx;
+	border-radius: 13rpx;
+	background-color: #fff;
+}
+.prize {
+	width: 750rpx;
+	height: 100vh;
+	display: flex;
+	justify-content: center;
+	.prize_box {
+		width: 100%;
+		margin-top: 00rpx;
+		height: 1120rpx;
+		position: relative;
+		flex-direction: column;
+		background: url(https://chaomd.liuniu946.com/image/bj.jpg);
+		background-size: 100% 100%;
+		background-position: top;
+		background-repeat: no-repeat;
+		.prize_close {
+			width: 64rpx;
+			height: 64rpx;
+			top: 64rpx;
+			right: 30rpx;
+			position: absolute;
+		}
+		.prize-top {
+			width: 528rpx;
+			height: 170rpx;
+			position: absolute;
+			top: 120rpx;
+			left: 118rpx;
+			&-2 {
+				top: 40rpx;
+			}
+		}
+		.prize_footer {
+			width: 100%;
+			height: 472rpx;
+			background-image: url(https://chaomd.liuniu946.com/image/btns@2x.png);
+			background-position: center;
+			background-repeat: no-repeat;
+			background-size: 100% 100%;
+			position: absolute;
+			left: 0;
+			bottom: -34rpx;
+			&-2 {
+				bottom: -114rpx;
+			}
+			.btn {
+				width: 310rpx;
+				height: 84rpx;
+				border-radius: 16rpx;
+				position: absolute;
+				left: 220rpx;
+			}
+			.btn-1 {
+				top: 210rpx;
+			}
+			.btn-0 {
+				top: 316rpx;
+			}
+		}
+	}
+	.shiwan {
+		background: url(https://chaomd.liuniu946.com/image/tanchuangbeijing@2x.png) no-repeat;
+		background-size: cover;
+	}
+	.prize_ul {
+		width: 566rpx;
+		// padding: 0 92rpx;
+		display: flex;
+		flex-wrap: wrap;
+		justify-content: space-between;
+		position: absolute;
+		top: 336rpx;
+		left: 92rpx;
+		&-2 {
+			top: 226rpx;
+		}
+		&::after {
+			content: '';
+			width: 176rpx;
+		}
+	}
+	// .scroll-view {
+	// 	width: 522rpx;
+	// 	white-space:nowrap;
+	.prize_ul_li {
+		margin-bottom: 16rpx;
+		position: relative;
+		width: 176rpx;
+		height: 186rpx;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		display: flex;
+		padding: 8rpx 0;
+		// margin-right: 20rpx;
+		box-sizing: border-box;
+
+		image {
+			margin: 0 auto;
+		}
+		text {
+			font-size: 24rpx;
+			// font-family: PingFangSC-Medium, PingFang SC;
+			font-weight: 500;
+			color: #171a20;
+			width: 146rpx;
+			overflow: hidden;
+			white-space: nowrap;
+			text-overflow: ellipsis;
+		}
+		.left-top-tag {
+			width: 3rem;
+			height: 3rem;
+			.tag-text {
+				font-size: 0.6rem !important;
+			}
+		}
+	}
+	// }
+	.prize_shop {
+		transition: 1s;
+		position: absolute;
+		top: 336rpx;
+		left: 198rpx;
+		display: flex;
+		flex-direction: column;
+		image {
+			width: 174rpx;
+			height: 235rpx;
+			margin-bottom: 20rpx;
+		}
+		text {
+			width: 70%;
+			font-size: 16rpx;
+			font-family: PingFangSC-Medium, PingFang SC;
+			font-weight: 500;
+			color: #171a20;
+			overflow: hidden;
+			white-space: nowrap;
+			text-overflow: ellipsis;
+			text-align: center;
+		}
+		.colorblack {
+			font-size: 32rpx !important;
+			font-family: PingFangSC-Medium, PingFang SC;
+			font-weight: 500;
+			color: #171a20;
+		}
+		.left-top-tag {
+			width: 5rem;
+			height: 5rem;
+			.tag-text {
+				font-size: 1rem;
+			}
+		}
+	}
+	.tag-normal {
+		box-shadow: 0 0 40rpx 18rpx #9d85ff;
+	}
+	.tag-rare {
+		box-shadow: 0 0 40rpx 18rpx #47a8ff;
+	}
+	.tag-supreme {
+		box-shadow: 0 0 40rpx 32rpx #feb337;
+	}
+	.tag-legend {
+		box-shadow: 0 0 40rpx 32rpx #fe615e;
+	}
+	.shiwan {
+		width: 399rpx;
+		height: 48rpx;
+		color: #ffffff;
+		font-size: 30rpx;
+		margin-top: 40rpx;
+		background: #000000;
+		border-radius: 27rpx 27rpx 27rpx 27rpx;
+	}
+}
+.left-top-tag {
+	z-index: 999999;
+	position: absolute;
+	left: -2px;
+	top: -3px;
+	.tag-img {
+		height: 100% !important;
+		width: 100% !important;
+	}
+	.tag-text {
+		position: absolute;
+		top: 30%;
+		left: 30%;
+		color: #fff !important;
+		width: auto !important;
+		transform: translateX(-50%) translateY(-50%) rotate(-45deg);
+	}
+}
+.prize_shop_scale {
+	transform: scale(1.3);
+}
+.loading-mask {
+	position: fixed;
+	height: 100vh;
+	width: 100vw;
+	bottom: 0;
+	left: 0;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	background-color: #76767652;
+	.loading-gif {
+		width: 60%;
+	}
+}
+//音乐背景旋转
+@keyframes donut-spin {
+	0% {
+		transform: rotate(0deg);
+	}
+
+	100% {
+		transform: rotate(360deg);
+	}
+}
+</style>

+ 569 - 0
components/show-result.vue

@@ -0,0 +1,569 @@
+<template>
+	<uni-popup class="result-popup-main" ref="prize" mode="top" :mask="false">
+		<view class="result-popup">
+			<view :class="['prize', explode && 'show']">
+				<view class="prize_box">
+					<view class="prize_close" @click="close()"><image src="/static/image/home/guanbi@2x.png" mode=""></image></view>
+					<!-- 					<view class="prize-top" :class="`prize-top-${mode}`">
+						<image src="/static/image/result/gaizi@2x.png" mode="aspectFit"></image>
+					</view -->
+					>
+					<!-- 1开 -->
+					<view :class="['prize_shop', 'center', 'onebox', 'tag-' + tag, scale && 'prize_shop_scale']" v-if="mode == 0">
+						<view class="spining"></view>
+						<view class="left-top-tag">
+							<image class="tag-img" :src="tagImg" mode="aspectFit"></image>
+							<text class="tag-text">{{ tagText }}</text>
+						</view>
+						<image class="one" v-if="prizedata && prizedata.prizeInfo" :src="prizedata.prizeInfo[0].image" mode="widthFix"></image>
+						<text class="colorblack" v-if="prizedata && prizedata.prizeInfo">{{ prizedata.prizeInfo[0].goods_name }}</text>
+					</view>
+					<!-- 5开or10开 -->
+					<view class="prize_ul" :class="'prize_ul-' + mode" v-else>
+						<!-- <scroll-view scroll-x="true" class="scroll-view"> -->
+						<view :class="['prize_ul_li', 'twobox', 'flex', 'tag-' + tagFun(item)]" v-for="(item, index) in prizedata.prizeInfo" :key="index">
+							<view class="left-top-tag">
+								<image class="tag-img" :src="tagImgFun(item)" mode="aspectFit"></image>
+								<text class="tag-text">{{ tagTextFun(item) }}</text>
+							</view>
+							<image class="two" :src="item.image" mode="widthFix"></image>
+							<text class="a">{{ item.goods_name }}</text>
+						</view>
+						<!-- </scroll-view> -->
+					</view>
+					<view class="prize_footer" :class="'prize_footer-' + mode" v-if="type == 0">
+						<!-- 立即收下 -->
+						<view class="btn btn-1" @click="$emit('accept')">立即收下</view>
+						<!-- 一键回收 -->
+						<view class="btn btn-0" @click="recycle()">一键回收</view>
+					</view>
+					<view class="shiwan center" v-else>试玩结果仅供展示哦~</view>
+				</view>
+			</view>
+			<view v-show="!hide" class="loading-mask">
+				<view :animation="boxAnimation" :class="['animation-wrapper', explode && 'explode']">
+					<image :src="boxImg" mode="heightFix" class="box"></image>
+					<image src="@/static/image/result/halo.png" mode="heightFix" class="halo"></image>
+				</view>
+			</view>
+			<audio id="result-audio" src="@/static/image/result/1.mp3"></audio>
+		</view>
+	</uni-popup>
+</template>
+
+<script>
+export default {
+	name: 'show-result',
+	props: {
+		//数量
+		num: Number,
+		/**
+		 * 类型 0:正式开盒 1:试玩
+		 */
+		type: {
+			type: Number,
+			default: 0
+		}
+	},
+	data() {
+		return {
+			//奖品列表
+			prizedata: [],
+			//特效
+			scale: false,
+			//开盒动画
+			openLoading: true,
+			boxImg: require('@/static/image/result/box.png'),
+			//动画
+			boxAnimation: {},
+			animationTime: 100,
+			explode: false,
+			hide: false
+		};
+	},
+	computed: {
+		//开盒模式 0:1抽 1:5抽 2:9抽
+		mode() {
+			switch (this.num) {
+				case 5:
+					return 1;
+				case 9:
+					return 2;
+			}
+			return 0;
+		},
+		//当前单抽商品
+		currPrizedata() {
+			if (!this.prizedata) {
+				return null;
+			}
+			if (!this.prizedata.prizeInfo || this.prizedata.length < 1) {
+				return null;
+			}
+			return this.prizedata.prizeInfo[0];
+		},
+		//单抽商品品质
+		tag() {
+			return this.tagFun(this.currPrizedata);
+		},
+		//单抽品质文本
+		tagText() {
+			return this.tagTextFun(this.currPrizedata);
+		},
+		//单抽品质角标
+		tagImg() {
+			return this.tagImgFun(this.currPrizedata);
+		}
+	},
+	methods: {
+		open(prizedata) {
+			prizedata.boxImg && (this.boxImg = prizedata.boxImg);
+			//播放音乐
+			this.startAudio();
+			//打开结果
+			this.$refs.prize.open();
+			//加载动画
+			// this.openLoading = true
+			this.prizedata = prizedata;
+			this.showAnimation();
+		},
+		//播放音乐
+		startAudio() {
+			setTimeout(() => {
+				let audio = document.getElementById('result-audio').querySelector('audio');
+				audio.play();
+			});
+		},
+		showAnimation() {
+			this.boxAnimation = this.animation.export();
+			setTimeout(() => {
+				this.explode = true;
+				this.hideAnimation();
+				if (this.tag != 'normal') {
+					//开启震动
+					try {
+						uni.vibrate({
+							success: function() {
+								console.log('震动');
+							}
+						});
+					} catch (e) {
+						console.log(e);
+					}
+					//开启效果
+					// this.scale = true
+					// setTimeout(() => {
+					// 	this.scale = false
+					// }, 1000)
+				}
+			}, this.animationTime);
+		},
+		hideAnimation() {
+			setTimeout(() => {
+				this.hide = true;
+			}, 1300);
+		},
+		//关闭
+		close() {
+			this.$emit('close');
+			this.$refs.prize.close();
+		},
+		//商品品质
+		tagFun(prizeInfo) {
+			if (!prizeInfo) return null;
+			return prizeInfo.tag;
+		},
+		//品质文本
+		tagTextFun(prizeInfo) {
+			if (!prizeInfo) return null;
+			if (prizeInfo.tag == 'normal') {
+				return '普通';
+			}
+			if (prizeInfo.tag == 'rare') {
+				return '稀有';
+			}
+			if (prizeInfo.tag == 'supreme') {
+				return '史诗';
+			}
+			if (prizeInfo.tag == 'legend') {
+				return '传说';
+			}
+		},
+		//品质角标
+		tagImgFun(prizeInfo) {
+			if (!prizeInfo) return null;
+			if (prizeInfo.tag == 'normal') {
+				return '/pagesA/static/tag-1.png';
+			}
+			if (prizeInfo.tag == 'rare') {
+				return '/pagesA/static/tag-2.png';
+			}
+			if (prizeInfo.tag == 'supreme') {
+				return '/pagesA/static/tag-3.png';
+			}
+			if (prizeInfo.tag == 'legend') {
+				return '/pagesA/static/tag-4.png';
+			}
+		},
+		recycle() {
+			this.$refs.prize.close();
+			uni.showModal({
+				cancelText: '取消',
+				confirmText: '确认',
+				title: '一键回收',
+				content: '是否确认一键回收?',
+				success: res => {
+					if (res.confirm) {
+						this.$emit('recycle', this.prizedata);
+					}
+				}
+			});
+		}
+	},
+	created() {
+		let animation = uni.createAnimation();
+		this.animationTime += shake(animation);
+		this.animationTime += compress(animation);
+		this.animationTime += jump(animation);
+		this.animation = animation;
+	},
+	onLoad() {}
+};
+//盒子抖动
+function shake(animation) {
+	let time = 0;
+	const duration = 90;
+	for (var i = 0; i < 20; i++) {
+		let rotate = -10;
+		let scaleY = 1.1;
+		if (i % 2 != 0) {
+			rotate = 0;
+			scaleY = 1;
+		}
+		animation
+			.rotate(rotate)
+			.scaleY(scaleY)
+			.step({ duration });
+		time += duration;
+	}
+	return time;
+}
+//盒子压缩
+function compress(animation) {
+	const duration = 100;
+	animation
+		.translateY('5vh')
+		.scaleY(0.9)
+		.scaleX(1.2)
+		.step({ duration });
+	return duration;
+}
+//盒子跳跃
+function jump(animation) {
+	const duration = 150;
+	animation
+		.translateY('-30vh')
+		.scaleY(1.1)
+		.scaleX(0.9)
+		.step({ duration });
+	return duration;
+}
+</script>
+
+<style lang="scss">
+.onebox {
+	height: 372rpx;
+	width: 352rpx;
+	border-radius: 26rpx;
+	background-color: #fff;
+}
+.twobox {
+	height: 186rpx;
+	width: 176rpx;
+	border-radius: 13rpx;
+	background-color: #fff;
+}
+.result-popup-main {
+	z-index: 9999;
+	.result-popup {
+		background-image: url('https://chaomd.liuniu946.com/image/kjbg.jpg');
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		background-repeat: no-repeat;
+		background-size: 100% 100%;
+	}
+}
+.prize {
+	transform: scale(0);
+	opacity: 0;
+	transition: 0.2s;
+	width: 750rpx;
+	height: 100vh;
+	display: flex;
+	justify-content: center;
+	&.show {
+		transform: scale(1);
+		opacity: 1;
+	}
+	.prize_box {
+		width: 100%;
+		height: 1120rpx;
+		position: relative;
+		flex-direction: column;
+		// background: url(static/image/result/guang@2x.png) no-repeat;
+		background-size: 100% auto;
+		background-position: top;
+		background-repeat: no-repeat;
+		.prize_close {
+			width: 64rpx;
+			height: 64rpx;
+			top: 280rpx;
+			right: 30rpx;
+			position: absolute;
+		}
+		.prize-top {
+			width: 528rpx;
+			height: 270rpx;
+			position: absolute;
+			top: 120rpx;
+			left: 118rpx;
+			&-2 {
+				top: 40rpx;
+			}
+		}
+		.prize_footer {
+			width: 100%;
+			&-2 {
+				bottom: -114rpx;
+			}
+			.btn {
+				height: 84rpx;
+				border-radius: 16rpx;
+				position: absolute;
+			}
+			.btn-1 {
+				bottom: 0rpx;
+				background-image: -webkit-linear-gradient(0deg, #89f7fe 0%, #66a6ff 100%);
+				font-size: 38rpx;
+				text-align: center;
+				line-height: 86rpx;
+				width: 159px;
+				border-radius: 8px;
+				position: absolute;
+				left: 40rpx;
+				color: #ffffff;
+				font-weight: bold;
+			}
+			.btn-0 {
+				bottom: 0rpx;
+				background-image: -webkit-linear-gradient(60deg, #ffc8de 0%, #ff67a4 100%);
+				font-size: 38rpx;
+				text-align: center;
+				line-height: 86rpx;
+				right: 40rpx;
+				width: 159px;
+				color: #ffffff;
+				font-weight: bold;
+			}
+		}
+	}
+	.shiwan {
+		background: url(/static/image/open/tanchuangbeijing@2x.png) no-repeat;
+		background-size: cover;
+	}
+	.prize_ul {
+		width: 566rpx;
+		// padding: 0 92rpx;
+		display: flex;
+		flex-wrap: wrap;
+		justify-content: space-between;
+		position: absolute;
+		top: 336rpx;
+		left: 92rpx;
+		&-2 {
+			top: 360rpx;
+		}
+		&::after {
+			content: '';
+			width: 176rpx;
+		}
+	}
+	// .scroll-view {
+	// 	width: 522rpx;
+	// 	white-space:nowrap;
+	.prize_ul_li {
+		margin-bottom: 16rpx;
+		position: relative;
+		width: 176rpx;
+		height: 186rpx;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		display: flex;
+		padding: 8rpx 0;
+		// margin-right: 20rpx;
+		box-sizing: border-box;
+
+		image {
+			margin: 0 auto;
+		}
+		text {
+			font-size: 24rpx;
+			// font-family: PingFangSC-Medium, PingFang SC;
+			font-weight: 500;
+			color: #171a20;
+			width: 146rpx;
+			overflow: hidden;
+			white-space: nowrap;
+			text-overflow: ellipsis;
+		}
+		.left-top-tag {
+			width: 3rem;
+			height: 3rem;
+			.tag-text {
+				font-size: 0.6rem !important;
+			}
+		}
+	}
+	// }
+	.prize_shop {
+		transition: 1s;
+		position: absolute;
+		top: 336rpx;
+		left: 198rpx;
+		display: flex;
+		flex-direction: column;
+		.spining {
+			z-index: -1;
+			height: 150vh;
+			width: 150vh;
+			background-image: url('@/static/image/result/spining.png');
+			background-repeat: no-repeat;
+			background-size: 100% 100%;
+			position: absolute;
+		}
+		image {
+			width: 174rpx;
+			height: 235rpx;
+			margin-bottom: 20rpx;
+		}
+		text {
+			width: 70%;
+			font-size: 16rpx;
+			font-family: PingFangSC-Medium, PingFang SC;
+			font-weight: 500;
+			color: #171a20;
+			overflow: hidden;
+			white-space: nowrap;
+			text-overflow: ellipsis;
+			text-align: center;
+		}
+		.colorblack {
+			font-size: 32rpx !important;
+			font-family: PingFangSC-Medium, PingFang SC;
+			font-weight: 500;
+			color: #171a20;
+		}
+		.left-top-tag {
+			width: 5rem;
+			height: 5rem;
+			.tag-text {
+				font-size: 1rem;
+			}
+		}
+	}
+	.tag-normal {
+		box-shadow: 0 0 40rpx 18rpx #9d85ff;
+	}
+	.tag-rare {
+		box-shadow: 0 0 40rpx 18rpx #47a8ff;
+	}
+	.tag-supreme {
+		box-shadow: 0 0 40rpx 32rpx #feb337;
+	}
+	.tag-legend {
+		box-shadow: 0 0 40rpx 32rpx #fe615e;
+	}
+	.shiwan {
+		width: 399rpx;
+		height: 48rpx;
+		color: #ffffff;
+		font-size: 30rpx;
+		margin-top: 40rpx;
+		background: #000000;
+		border-radius: 27rpx 27rpx 27rpx 27rpx;
+	}
+}
+.left-top-tag {
+	z-index: 999999;
+	position: absolute;
+	left: -2px;
+	top: -3px;
+	.tag-img {
+		height: 100% !important;
+		width: 100% !important;
+	}
+	.tag-text {
+		position: absolute;
+		top: 30%;
+		left: 30%;
+		color: #fff !important;
+		width: auto !important;
+		transform: translateX(-50%) translateY(-50%) rotate(-45deg);
+	}
+}
+.prize_shop_scale {
+	transform: scale(1.3);
+}
+
+.loading-mask {
+	z-index: 999;
+	position: fixed;
+	top: -180rpx;
+	left: 0;
+	height: 100vh;
+	width: 100vw;
+	display: flex;
+	align-items: flex-end;
+	justify-content: center;
+	.animation-wrapper {
+		margin-bottom: 10vh;
+		image {
+			height: 360rpx;
+		}
+		.halo {
+			opacity: 1;
+			transform: scale(0);
+			position: absolute;
+			top: 0;
+		}
+		&.explode {
+			.box {
+				transition: 0.3s;
+				opacity: 0;
+				transform: scale(0);
+			}
+			.halo {
+				transition: 1.2s;
+				opacity: 0;
+				transform: scale(5);
+			}
+		}
+	}
+}
+@keyframes rotate {
+	from {
+		transform: rotate(0deg);
+	}
+	to {
+		transform: rotate(360deg);
+	}
+}
+
+.spining {
+	transition: 0.3s;
+	animation: rotate 10s linear infinite; /*开始动画后无限循环,用来控制rotate*/
+}
+</style>

+ 27 - 0
components/u-parse/components/wxParseAudio.vue

@@ -0,0 +1,27 @@
+<template>
+  <!--增加audio标签支持-->
+  <audio
+    :id="node.attr.id"
+    :class="node.classStr"
+    :style="node.styleStr"
+    :src="node.attr.src"
+    :loop="node.attr.loop"
+    :poster="node.attr.poster"
+    :name="node.attr.name"
+    :author="node.attr.author"
+    controls></audio>
+</template>
+
+<script>
+export default {
+  name: 'wxParseAudio',
+  props: {
+    node: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+  },
+};
+</script>

+ 86 - 0
components/u-parse/components/wxParseImg.vue

@@ -0,0 +1,86 @@
+<template>
+  <image
+    :mode="node.attr.mode"
+    :lazy-load="node.attr.lazyLoad"
+    :class="node.classStr"
+    :style="newStyleStr || node.styleStr"
+    :data-src="node.attr.src"
+    :src="node.attr.src"
+    @tap="wxParseImgTap"
+    @load="wxParseImgLoad"
+    />
+</template>
+
+<script>
+export default {
+  name: 'wxParseImg',
+  data() {
+    return {
+      newStyleStr: '',
+      preview: true,
+    };
+  },
+  props: {
+    node: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+  },
+  methods: {
+    wxParseImgTap(e) {
+      if (!this.preview) return;
+      const { src } = e.currentTarget.dataset;
+      if (!src) return;
+      let parent = this.$parent;
+      while(!parent.preview || typeof parent.preview !== 'function') {// TODO 遍历获取父节点执行方法
+      	parent = parent.$parent;
+      }
+      parent.preview(src, e);
+    },
+    // 图片视觉宽高计算函数区
+    wxParseImgLoad(e) {
+      const { src } = e.currentTarget.dataset;
+      if (!src) return;
+      const { width, height } = e.mp.detail;
+      const recal = this.wxAutoImageCal(width, height);
+      const { imageheight, imageWidth } = recal;
+      const { padding, mode } = this.node.attr;
+      const { styleStr } = this.node;
+      const imageHeightStyle = mode === 'widthFix' ? '' : `height: ${imageheight}px;`;
+      this.newStyleStr = `${styleStr}; ${imageHeightStyle}; width: ${imageWidth}px; padding: 0 ${+padding}px;`;
+    },
+    // 计算视觉优先的图片宽高
+    wxAutoImageCal(originalWidth, originalHeight) {
+      // 获取图片的原始长宽
+      const { padding } = this.node.attr;
+      const windowWidth = this.node.$screen.width - (2 * padding);
+      const results = {};
+
+      if (originalWidth < 60 || originalHeight < 60) {
+        const { src } = this.node.attr;
+				let parent = this.$parent;
+				while(!parent.preview || typeof parent.preview !== 'function') {
+					parent = parent.$parent;
+				}
+				parent.removeImageUrl(src);
+        this.preview = false;
+      }
+
+      // 判断按照那种方式进行缩放
+      if (originalWidth > windowWidth) {
+        // 在图片width大于手机屏幕width时候
+        results.imageWidth = windowWidth;
+        results.imageheight = windowWidth * (originalHeight / originalWidth);
+      } else {
+        // 否则展示原来的数据
+        results.imageWidth = originalWidth;
+        results.imageheight = originalHeight;
+      }
+
+      return results;
+    },
+  },
+};
+</script>

+ 107 - 0
components/u-parse/components/wxParseTemplate0.vue

@@ -0,0 +1,107 @@
+<template>
+	<view>
+		<!--判断是否是标签节点-->
+		<block v-if="node.node == 'element'">
+			<block v-if="node.tag == 'button'">
+				<button type="default" size="mini">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</button>
+			</block>
+
+			<!--li类型-->
+			<block v-else-if="node.tag == 'li'">
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--video类型-->
+			<block v-else-if="node.tag == 'video'">
+				<wx-parse-video :node="node" />
+			</block>
+
+			<!--audio类型-->
+			<block v-else-if="node.tag == 'audio'">
+				<wx-parse-audio :node="node" />
+			</block>
+
+			<!--img类型-->
+			<block v-else-if="node.tag == 'img'">
+				<wx-parse-img :node="node" />
+			</block>
+
+			<!--a类型-->
+			<block v-else-if="node.tag == 'a'">
+				<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--table类型-->
+			<block v-else-if="node.tag == 'table'">
+				<view :class="node.classStr" class="table" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--br类型-->
+			<block v-else-if="node.tag == 'br'">
+				<text>\n</text>
+			</block>
+
+			<!--其他标签-->
+			<block v-else>
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+		</block>
+
+		<!--判断是否是文本节点-->
+		<block v-else-if="node.node == 'text'">{{node.text}}</block>
+	</view>
+</template>
+
+<script>
+	import wxParseTemplate from './wxParseTemplate1';
+	import wxParseImg from './wxParseImg';
+	import wxParseVideo from './wxParseVideo';
+	import wxParseAudio from './wxParseAudio';
+
+	export default {
+		name: 'wxParseTemplate0',
+		props: {
+			node: {},
+		},
+		components: {
+			wxParseTemplate,
+			wxParseImg,
+			wxParseVideo,
+			wxParseAudio,
+		},
+		methods: {
+			wxParseATap(e) {
+				const {
+					href
+				} = e.currentTarget.dataset;// TODO currentTarget才有dataset
+				if (!href) return;
+				let parent = this.$parent;
+				while(!parent.preview || typeof parent.preview !== 'function') {// TODO 遍历获取父节点执行方法
+					parent = parent.$parent;
+				}
+				parent.navigate(href, e);
+			},
+		},
+	};
+</script>

+ 99 - 0
components/u-parse/components/wxParseTemplate1.vue

@@ -0,0 +1,99 @@
+<template>
+	<view :class="(node.tag == 'li' ? node.classStr : (node.node==='text'?'text':''))">
+		<!--判断是否是标签节点-->
+		<block v-if="node.node == 'element'">
+			<block v-if="node.tag == 'button'">
+				<button type="default" size="mini">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</button>
+			</block>
+
+			<!--li类型-->
+			<block v-else-if="node.tag == 'li'">
+				<!-- <view :class="node.classStr" :style="node.styleStr"> -->
+				<view :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--video类型-->
+			<block v-else-if="node.tag == 'video'">
+				<wx-parse-video :node="node" />
+			</block>
+
+			<!--audio类型-->
+			<block v-else-if="node.tag == 'audio'">
+				<wx-parse-audio :node="node" />
+			</block>
+
+			<!--img类型-->
+			<block v-else-if="node.tag == 'img'">
+				<wx-parse-img :node="node" />
+			</block>
+
+			<!--a类型-->
+			<block v-else-if="node.tag == 'a'">
+				<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--br类型-->
+			<block v-else-if="node.tag == 'br'">
+				<text>\n</text>
+			</block>
+
+			<!--其他标签-->
+			<block v-else>
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+		</block>
+
+		<!--判断是否是文本节点-->
+		<block v-else-if="node.node == 'text'">{{node.text}}</block>
+	</view>
+</template>
+
+<script>
+	import wxParseTemplate from './wxParseTemplate2';
+	import wxParseImg from './wxParseImg';
+	import wxParseVideo from './wxParseVideo';
+	import wxParseAudio from './wxParseAudio';
+
+	export default {
+		name: 'wxParseTemplate1',
+		props: {
+			node: {},
+		},
+		components: {
+			wxParseTemplate,
+			wxParseImg,
+			wxParseVideo,
+			wxParseAudio,
+		},
+		methods: {
+			wxParseATap(e) {
+				const {
+					href
+				} = e.currentTarget.dataset;
+				if (!href) return;
+				let parent = this.$parent;
+				while(!parent.preview || typeof parent.preview !== 'function') {
+					parent = parent.$parent;
+				}
+				parent.navigate(href, e);
+			},
+		},
+	};
+</script>

+ 97 - 0
components/u-parse/components/wxParseTemplate10.vue

@@ -0,0 +1,97 @@
+<template>
+	<view>
+		<!--判断是否是标签节点-->
+		<block v-if="node.node == 'element'">
+			<block v-if="node.tag == 'button'">
+				<button type="default" size="mini">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</button>
+			</block>
+
+			<!--li类型-->
+			<block v-else-if="node.tag == 'li'">
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--video类型-->
+			<block v-else-if="node.tag == 'video'">
+				<wx-parse-video :node="node" />
+			</block>
+
+			<!--audio类型-->
+			<block v-else-if="node.tag == 'audio'">
+				<wx-parse-audio :node="node" />
+			</block>
+
+			<!--img类型-->
+			<block v-else-if="node.tag == 'img'">
+				<wx-parse-img :node="node" />
+			</block>
+
+			<!--a类型-->
+			<block v-else-if="node.tag == 'a'">
+				<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--br类型-->
+			<block v-else-if="node.tag == 'br'">
+				<text>\n</text>
+			</block>
+
+			<!--其他标签-->
+			<block v-else>
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+		</block>
+
+		<!--判断是否是文本节点-->
+		<block v-else-if="node.node == 'text'">{{node.text}}</block>
+	</view>
+</template>
+
+<script>
+	import wxParseTemplate from './wxParseTemplate11';
+	import wxParseImg from './wxParseImg';
+	import wxParseVideo from './wxParseVideo';
+	import wxParseAudio from './wxParseAudio';
+
+	export default {
+		name: 'wxParseTemplate10',
+		props: {
+			node: {},
+		},
+		components: {
+			wxParseTemplate,
+			wxParseImg,
+			wxParseVideo,
+			wxParseAudio,
+		},
+		methods: {
+			wxParseATap(e) {
+				const {
+					href
+				} = e.currentTarget.dataset;
+				if (!href) return;
+				let parent = this.$parent;
+				while(!parent.preview || typeof parent.preview !== 'function') {
+					parent = parent.$parent;
+				}
+				parent.navigate(href, e);
+			},
+		},
+	};
+</script>

+ 87 - 0
components/u-parse/components/wxParseTemplate11.vue

@@ -0,0 +1,87 @@
+<template>
+	<view>
+		<!--判断是否是标签节点-->
+		<block v-if="node.node == 'element'">
+			<!--button类型-->
+			<block v-if="node.tag == 'button'">
+				<button type="default" size="mini">
+				</button>
+			</block>
+
+			<!--li类型-->
+			<block v-else-if="node.tag == 'li'">
+				<view :class="node.classStr" :style="node.styleStr">
+					{{node.text}}
+				</view>
+			</block>
+
+			<!--video类型-->
+			<block v-else-if="node.tag == 'video'">
+				<wx-parse-video :node="node" />
+			</block>
+
+			<!--audio类型-->
+			<block v-else-if="node.tag == 'audio'">
+				<wx-parse-audio :node="node" />
+			</block>
+
+			<!--img类型-->
+			<block v-else-if="node.tag == 'img'">
+				<wx-parse-img :node="node" />
+			</block>
+
+			<!--a类型-->
+			<block v-else-if="node.tag == 'a'">
+				<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
+					{{node.text}}
+				</view>
+			</block>
+
+			<!--br类型-->
+			<block v-else-if="node.tag == 'br'">
+				<text>\n</text>
+			</block>
+
+			<!--其他标签-->
+			<block v-else>
+				<view :class="node.classStr" :style="node.styleStr">
+					{{node.text}}
+				</view>
+			</block>
+		</block>
+
+		<!--判断是否是文本节点-->
+		<block v-else-if="node.node == 'text'">{{node.text}}</block>
+	</view>
+</template>
+
+<script>
+	import wxParseImg from './wxParseImg';
+	import wxParseVideo from './wxParseVideo';
+	import wxParseAudio from './wxParseAudio';
+
+	export default {
+		name: 'wxParseTemplate11',
+		props: {
+			node: {},
+		},
+		components: {
+			wxParseImg,
+			wxParseVideo,
+			wxParseAudio,
+		},
+		methods: {
+			wxParseATap(e) {
+				const {
+					href
+				} = e.currentTarget.dataset;
+				if (!href) return;
+				let parent = this.$parent;
+				while(!parent.preview || typeof parent.preview !== 'function') {
+					parent = parent.$parent;
+				}
+				parent.navigate(href, e);
+			},
+		},
+	};
+</script>

+ 98 - 0
components/u-parse/components/wxParseTemplate2.vue

@@ -0,0 +1,98 @@
+<template>
+	<view>
+		<!--判断是否是标签节点-->
+		<block v-if="node.node == 'element'">
+			<block v-if="node.tag == 'button'">
+				<button type="default" size="mini">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</button>
+			</block>
+
+			<!--li类型-->
+			<block v-else-if="node.tag == 'li'">
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--video类型-->
+			<block v-else-if="node.tag == 'video'">
+				<wx-parse-video :node="node" />
+			</block>
+
+			<!--audio类型-->
+			<block v-else-if="node.tag == 'audio'">
+				<wx-parse-audio :node="node" />
+			</block>
+
+			<!--img类型-->
+			<block v-else-if="node.tag == 'img'">
+				<wx-parse-img :node="node" />
+			</block>
+
+			<!--a类型-->
+			<block v-else-if="node.tag == 'a'">
+				<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--br类型-->
+			<block v-else-if="node.tag == 'br'">
+				<text>\n</text>
+			</block>
+
+			<!--其他标签-->
+			<block v-else>
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+		</block>
+
+		<!--判断是否是文本节点-->
+		<block v-else-if="node.node == 'text'">{{node.text}}</block>
+	</view>
+</template>
+
+<script>
+	import wxParseTemplate from './wxParseTemplate3';
+	import wxParseImg from './wxParseImg';
+	import wxParseVideo from './wxParseVideo';
+	import wxParseAudio from './wxParseAudio';
+
+	export default {
+		name: 'wxParseTemplate2',
+		props: {
+			node: {},
+		},
+		components: {
+			wxParseTemplate,
+			wxParseImg,
+			wxParseVideo,
+			wxParseAudio,
+		},
+		methods: {
+			wxParseATap(e) {
+				const {
+					href
+				} = e.currentTarget.dataset;
+				if (!href) return;
+				let parent = this.$parent;
+				while(!parent.preview || typeof parent.preview !== 'function') {
+					parent = parent.$parent;
+				}
+				parent.navigate(href, e);
+			},
+		},
+	};
+</script>

+ 98 - 0
components/u-parse/components/wxParseTemplate3.vue

@@ -0,0 +1,98 @@
+<template>
+	<view>
+		<!--判断是否是标签节点-->
+		<block v-if="node.node == 'element'">
+			<block v-if="node.tag == 'button'">
+				<button type="default" size="mini">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</button>
+			</block>
+
+			<!--li类型-->
+			<block v-else-if="node.tag == 'li'">
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--video类型-->
+			<block v-else-if="node.tag == 'video'">
+				<wx-parse-video :node="node" />
+			</block>
+
+			<!--audio类型-->
+			<block v-else-if="node.tag == 'audio'">
+				<wx-parse-audio :node="node" />
+			</block>
+
+			<!--img类型-->
+			<block v-else-if="node.tag == 'img'">
+				<wx-parse-img :node="node" />
+			</block>
+
+			<!--a类型-->
+			<block v-else-if="node.tag == 'a'">
+				<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--br类型-->
+			<block v-else-if="node.tag == 'br'">
+				<text>\n</text>
+			</block>
+
+			<!--其他标签-->
+			<block v-else>
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+		</block>
+
+		<!--判断是否是文本节点-->
+		<block v-else-if="node.node == 'text'">{{node.text}}</block>
+	</view>
+</template>
+
+<script>
+	import wxParseTemplate from './wxParseTemplate4';
+	import wxParseImg from './wxParseImg';
+	import wxParseVideo from './wxParseVideo';
+	import wxParseAudio from './wxParseAudio';
+
+	export default {
+		name: 'wxParseTemplate3',
+		props: {
+			node: {},
+		},
+		components: {
+			wxParseTemplate,
+			wxParseImg,
+			wxParseVideo,
+			wxParseAudio,
+		},
+		methods: {
+			wxParseATap(e) {
+				const {
+					href
+				} = e.currentTarget.dataset;
+				if (!href) return;
+				let parent = this.$parent;
+				while(!parent.preview || typeof parent.preview !== 'function') {
+					parent = parent.$parent;
+				}
+				parent.navigate(href, e);
+			},
+		},
+	};
+</script>

+ 98 - 0
components/u-parse/components/wxParseTemplate4.vue

@@ -0,0 +1,98 @@
+<template>
+	<view>
+		<!--判断是否是标签节点-->
+		<block v-if="node.node == 'element'">
+			<block v-if="node.tag == 'button'">
+				<button type="default" size="mini">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</button>
+			</block>
+
+			<!--li类型-->
+			<block v-else-if="node.tag == 'li'">
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--video类型-->
+			<block v-else-if="node.tag == 'video'">
+				<wx-parse-video :node="node" />
+			</block>
+
+			<!--audio类型-->
+			<block v-else-if="node.tag == 'audio'">
+				<wx-parse-audio :node="node" />
+			</block>
+
+			<!--img类型-->
+			<block v-else-if="node.tag == 'img'">
+				<wx-parse-img :node="node" />
+			</block>
+
+			<!--a类型-->
+			<block v-else-if="node.tag == 'a'">
+				<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--br类型-->
+			<block v-else-if="node.tag == 'br'">
+				<text>\n</text>
+			</block>
+
+			<!--其他标签-->
+			<block v-else>
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+		</block>
+
+		<!--判断是否是文本节点-->
+		<block v-else-if="node.node == 'text'">{{node.text}}</block>
+	</view>
+</template>
+
+<script>
+	import wxParseTemplate from './wxParseTemplate5';
+	import wxParseImg from './wxParseImg';
+	import wxParseVideo from './wxParseVideo';
+	import wxParseAudio from './wxParseAudio';
+
+	export default {
+		name: 'wxParseTemplate4',
+		props: {
+			node: {},
+		},
+		components: {
+			wxParseTemplate,
+			wxParseImg,
+			wxParseVideo,
+			wxParseAudio,
+		},
+		methods: {
+			wxParseATap(e) {
+				const {
+					href
+				} = e.currentTarget.dataset;
+				if (!href) return;
+				let parent = this.$parent;
+				while(!parent.preview || typeof parent.preview !== 'function') {
+					parent = parent.$parent;
+				}
+				parent.navigate(href, e);
+			},
+		},
+	};
+</script>

+ 98 - 0
components/u-parse/components/wxParseTemplate5.vue

@@ -0,0 +1,98 @@
+<template>
+	<view>
+		<!--判断是否是标签节点-->
+		<block v-if="node.node == 'element'">
+			<block v-if="node.tag == 'button'">
+				<button type="default" size="mini">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</button>
+			</block>
+
+			<!--li类型-->
+			<block v-else-if="node.tag == 'li'">
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--video类型-->
+			<block v-else-if="node.tag == 'video'">
+				<wx-parse-video :node="node" />
+			</block>
+
+			<!--audio类型-->
+			<block v-else-if="node.tag == 'audio'">
+				<wx-parse-audio :node="node" />
+			</block>
+
+			<!--img类型-->
+			<block v-else-if="node.tag == 'img'">
+				<wx-parse-img :node="node" />
+			</block>
+
+			<!--a类型-->
+			<block v-else-if="node.tag == 'a'">
+				<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--br类型-->
+			<block v-else-if="node.tag == 'br'">
+				<text>\n</text>
+			</block>
+
+			<!--其他标签-->
+			<block v-else>
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+		</block>
+
+		<!--判断是否是文本节点-->
+		<block v-else-if="node.node == 'text'">{{node.text}}</block>
+	</view>
+</template>
+
+<script>
+	import wxParseTemplate from './wxParseTemplate6';
+	import wxParseImg from './wxParseImg';
+	import wxParseVideo from './wxParseVideo';
+	import wxParseAudio from './wxParseAudio';
+
+	export default {
+		name: 'wxParseTemplate5',
+		props: {
+			node: {},
+		},
+		components: {
+			wxParseTemplate,
+			wxParseImg,
+			wxParseVideo,
+			wxParseAudio,
+		},
+		methods: {
+			wxParseATap(e) {
+				const {
+					href
+				} = e.currentTarget.dataset;
+				if (!href) return;
+				let parent = this.$parent;
+				while(!parent.preview || typeof parent.preview !== 'function') {
+					parent = parent.$parent;
+				}
+				parent.navigate(href, e);
+			},
+		},
+	};
+</script>

+ 98 - 0
components/u-parse/components/wxParseTemplate6.vue

@@ -0,0 +1,98 @@
+<template>
+	<view>
+		<!--判断是否是标签节点-->
+		<block v-if="node.node == 'element'">
+			<block v-if="node.tag == 'button'">
+				<button type="default" size="mini">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</button>
+			</block>
+
+			<!--li类型-->
+			<block v-else-if="node.tag == 'li'">
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--video类型-->
+			<block v-else-if="node.tag == 'video'">
+				<wx-parse-video :node="node" />
+			</block>
+
+			<!--audio类型-->
+			<block v-else-if="node.tag == 'audio'">
+				<wx-parse-audio :node="node" />
+			</block>
+
+			<!--img类型-->
+			<block v-else-if="node.tag == 'img'">
+				<wx-parse-img :node="node" />
+			</block>
+
+			<!--a类型-->
+			<block v-else-if="node.tag == 'a'">
+				<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--br类型-->
+			<block v-else-if="node.tag == 'br'">
+				<text>\n</text>
+			</block>
+
+			<!--其他标签-->
+			<block v-else>
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+		</block>
+
+		<!--判断是否是文本节点-->
+		<block v-else-if="node.node == 'text'">{{node.text}}</block>
+	</view>
+</template>
+
+<script>
+	import wxParseTemplate from './wxParseTemplate7';
+	import wxParseImg from './wxParseImg';
+	import wxParseVideo from './wxParseVideo';
+	import wxParseAudio from './wxParseAudio';
+
+	export default {
+		name: 'wxParseTemplate6',
+		props: {
+			node: {},
+		},
+		components: {
+			wxParseTemplate,
+			wxParseImg,
+			wxParseVideo,
+			wxParseAudio,
+		},
+		methods: {
+			wxParseATap(e) {
+				const {
+					href
+				} = e.currentTarget.dataset;
+				if (!href) return;
+				let parent = this.$parent;
+				while(!parent.preview || typeof parent.preview !== 'function') {
+					parent = parent.$parent;
+				}
+				parent.navigate(href, e);
+			},
+		},
+	};
+</script>

+ 98 - 0
components/u-parse/components/wxParseTemplate7.vue

@@ -0,0 +1,98 @@
+<template>
+	<view>
+		<!--判断是否是标签节点-->
+		<block v-if="node.node == 'element'">
+			<block v-if="node.tag == 'button'">
+				<button type="default" size="mini">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</button>
+			</block>
+
+			<!--li类型-->
+			<block v-else-if="node.tag == 'li'">
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--video类型-->
+			<block v-else-if="node.tag == 'video'">
+				<wx-parse-video :node="node" />
+			</block>
+
+			<!--audio类型-->
+			<block v-else-if="node.tag == 'audio'">
+				<wx-parse-audio :node="node" />
+			</block>
+
+			<!--img类型-->
+			<block v-else-if="node.tag == 'img'">
+				<wx-parse-img :node="node" />
+			</block>
+
+			<!--a类型-->
+			<block v-else-if="node.tag == 'a'">
+				<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--br类型-->
+			<block v-else-if="node.tag == 'br'">
+				<text>\n</text>
+			</block>
+
+			<!--其他标签-->
+			<block v-else>
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+		</block>
+
+		<!--判断是否是文本节点-->
+		<block v-else-if="node.node == 'text'">{{node.text}}</block>
+	</view>
+</template>
+
+<script>
+	import wxParseTemplate from './wxParseTemplate8';
+	import wxParseImg from './wxParseImg';
+	import wxParseVideo from './wxParseVideo';
+	import wxParseAudio from './wxParseAudio';
+
+	export default {
+		name: 'wxParseTemplate7',
+		props: {
+			node: {},
+		},
+		components: {
+			wxParseTemplate,
+			wxParseImg,
+			wxParseVideo,
+			wxParseAudio,
+		},
+		methods: {
+			wxParseATap(e) {
+				const {
+					href
+				} = e.currentTarget.dataset;
+				if (!href) return;
+				let parent = this.$parent;
+				while(!parent.preview || typeof parent.preview !== 'function') {
+					parent = parent.$parent;
+				}
+				parent.navigate(href, e);
+			},
+		},
+	};
+</script>

+ 98 - 0
components/u-parse/components/wxParseTemplate8.vue

@@ -0,0 +1,98 @@
+<template>
+	<view>
+		<!--判断是否是标签节点-->
+		<block v-if="node.node == 'element'">
+			<block v-if="node.tag == 'button'">
+				<button type="default" size="mini">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</button>
+			</block>
+
+			<!--li类型-->
+			<block v-else-if="node.tag == 'li'">
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--video类型-->
+			<block v-else-if="node.tag == 'video'">
+				<wx-parse-video :node="node" />
+			</block>
+
+			<!--audio类型-->
+			<block v-else-if="node.tag == 'audio'">
+				<wx-parse-audio :node="node" />
+			</block>
+
+			<!--img类型-->
+			<block v-else-if="node.tag == 'img'">
+				<wx-parse-img :node="node" />
+			</block>
+
+			<!--a类型-->
+			<block v-else-if="node.tag == 'a'">
+				<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--br类型-->
+			<block v-else-if="node.tag == 'br'">
+				<text>\n</text>
+			</block>
+
+			<!--其他标签-->
+			<block v-else>
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+		</block>
+
+		<!--判断是否是文本节点-->
+		<block v-else-if="node.node == 'text'">{{node.text}}</block>
+	</view>
+</template>
+
+<script>
+	import wxParseTemplate from './wxParseTemplate9';
+	import wxParseImg from './wxParseImg';
+	import wxParseVideo from './wxParseVideo';
+	import wxParseAudio from './wxParseAudio';
+
+	export default {
+		name: 'wxParseTemplate8',
+		props: {
+			node: {},
+		},
+		components: {
+			wxParseTemplate,
+			wxParseImg,
+			wxParseVideo,
+			wxParseAudio,
+		},
+		methods: {
+			wxParseATap(e) {
+				const {
+					href
+				} = e.currentTarget.dataset;
+				if (!href) return;
+				let parent = this.$parent;
+				while(!parent.preview || typeof parent.preview !== 'function') {
+					parent = parent.$parent;
+				}
+				parent.navigate(href, e);
+			},
+		},
+	};
+</script>

+ 98 - 0
components/u-parse/components/wxParseTemplate9.vue

@@ -0,0 +1,98 @@
+<template>
+	<view>
+		<!--判断是否是标签节点-->
+		<block v-if="node.node == 'element'">
+			<block v-if="node.tag == 'button'">
+				<button type="default" size="mini">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</button>
+			</block>
+
+			<!--li类型-->
+			<block v-else-if="node.tag == 'li'">
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--video类型-->
+			<block v-else-if="node.tag == 'video'">
+				<wx-parse-video :node="node" />
+			</block>
+
+			<!--audio类型-->
+			<block v-else-if="node.tag == 'audio'">
+				<wx-parse-audio :node="node" />
+			</block>
+
+			<!--img类型-->
+			<block v-else-if="node.tag == 'img'">
+				<wx-parse-img :node="node" />
+			</block>
+
+			<!--a类型-->
+			<block v-else-if="node.tag == 'a'">
+				<view @click="wxParseATap" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+			<!--br类型-->
+			<block v-else-if="node.tag == 'br'">
+				<text>\n</text>
+			</block>
+
+			<!--其他标签-->
+			<block v-else>
+				<view :class="node.classStr" :style="node.styleStr">
+					<block v-for="(node, index) of node.nodes" :key="index">
+						<wx-parse-template :node="node" />
+					</block>
+				</view>
+			</block>
+
+		</block>
+
+		<!--判断是否是文本节点-->
+		<block v-else-if="node.node == 'text'">{{node.text}}</block>
+	</view>
+</template>
+
+<script>
+	import wxParseTemplate from './wxParseTemplate10';
+	import wxParseImg from './wxParseImg';
+	import wxParseVideo from './wxParseVideo';
+	import wxParseAudio from './wxParseAudio';
+
+	export default {
+		name: 'wxParseTemplate9',
+		props: {
+			node: {},
+		},
+		components: {
+			wxParseTemplate,
+			wxParseImg,
+			wxParseVideo,
+			wxParseAudio,
+		},
+		methods: {
+			wxParseATap(e) {
+				const {
+					href
+				} = e.currentTarget.dataset;
+				if (!href) return;
+				let parent = this.$parent;
+				while(!parent.preview || typeof parent.preview !== 'function') {
+					parent = parent.$parent;
+				}
+				parent.navigate(href, e);
+			},
+		},
+	};
+</script>

+ 15 - 0
components/u-parse/components/wxParseVideo.vue

@@ -0,0 +1,15 @@
+<template>
+  <!--增加video标签支持,并循环添加-->
+  <view :class="node.classStr" :style="node.styleStr">
+    <video :class="node.classStr" class="video-video" :src="node.attr.src"></video>
+  </view>
+</template>
+
+<script>
+export default {
+  name: 'wxParseVideo',
+  props: {
+    node: {},
+  },
+};
+</script>

+ 261 - 0
components/u-parse/libs/html2json.js

@@ -0,0 +1,261 @@
+/**
+ * html2Json 改造来自: https://github.com/Jxck/html2json
+ *
+ *
+ * author: Di (微信小程序开发工程师)
+ * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
+ *               垂直微信小程序开发交流社区
+ *
+ * github地址: https://github.com/icindy/wxParse
+ *
+ * for: 微信小程序富文本解析
+ * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
+ */
+
+import wxDiscode from './wxDiscode';
+import HTMLParser from './htmlparser';
+
+function makeMap(str) {
+  const obj = {};
+  const items = str.split(',');
+  for (let i = 0; i < items.length; i += 1) obj[items[i]] = true;
+  return obj;
+}
+
+// Block Elements - HTML 5
+const block = makeMap('br,code,address,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video');
+
+// Inline Elements - HTML 5
+const inline = makeMap('a,abbr,acronym,applet,b,basefont,bdo,big,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var');
+
+// Elements that you can, intentionally, leave open
+// (and which close themselves)
+const closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr');
+
+function removeDOCTYPE(html) {
+  const isDocument = /<body.*>([^]*)<\/body>/.test(html);
+  return isDocument ? RegExp.$1 : html;
+}
+
+function trimHtml(html) {
+  return html
+    .replace(/<!--.*?-->/gi, '')
+    .replace(/\/\*.*?\*\//gi, '')
+    .replace(/[ ]+</gi, '<')
+    .replace(/<script[^]*<\/script>/gi, '')
+    .replace(/<style[^]*<\/style>/gi, '');
+}
+
+function getScreenInfo() {
+  const screen = {};
+  wx.getSystemInfo({
+    success: (res) => {
+      screen.width = res.windowWidth;
+      screen.height = res.windowHeight;
+    },
+  });
+  return screen;
+}
+
+function html2json(html, customHandler, imageProp, host) {
+  // 处理字符串
+  html = removeDOCTYPE(html);
+  html = trimHtml(html);
+  html = wxDiscode.strDiscode(html);
+  // 生成node节点
+  const bufArray = [];
+  const results = {
+    nodes: [],
+    imageUrls: [],
+  };
+
+	const screen = getScreenInfo();
+  function Node(tag) {
+    this.node = 'element';
+    this.tag = tag;
+		
+		this.$screen = screen;
+  }
+
+  HTMLParser(html, {
+    start(tag, attrs, unary) {
+      // node for this element
+      const node = new Node(tag);
+
+      if (bufArray.length !== 0) {
+        const parent = bufArray[0];
+        if (parent.nodes === undefined) {
+          parent.nodes = [];
+        }
+      }
+
+      if (block[tag]) {
+        node.tagType = 'block';
+      } else if (inline[tag]) {
+        node.tagType = 'inline';
+      } else if (closeSelf[tag]) {
+        node.tagType = 'closeSelf';
+      }
+
+      node.attr = attrs.reduce((pre, attr) => {
+        const { name } = attr;
+        let { value } = attr;
+        if (name === 'class') {
+          node.classStr = value;
+        }
+        // has multi attibutes
+        // make it array of attribute
+        if (name === 'style') {
+          node.styleStr = value;
+        }
+        if (value.match(/ /)) {
+          value = value.split(' ');
+        }
+
+        // if attr already exists
+        // merge it
+        if (pre[name]) {
+          if (Array.isArray(pre[name])) {
+            // already array, push to last
+            pre[name].push(value);
+          } else {
+            // single value, make it array
+            pre[name] = [pre[name], value];
+          }
+        } else {
+          // not exist, put it
+          pre[name] = value;
+        }
+
+        return pre;
+      }, {});
+
+      // 优化样式相关属性
+      if (node.classStr) {
+        node.classStr += ` ${node.tag}`;
+      } else {
+        node.classStr = node.tag;
+      }
+      if (node.tagType === 'inline') {
+        node.classStr += ' inline';
+      }
+
+      // 对img添加额外数据
+      if (node.tag === 'img') {
+        let imgUrl = node.attr.src;
+        imgUrl = wxDiscode.urlToHttpUrl(imgUrl, imageProp.domain);
+        Object.assign(node.attr, imageProp, {
+          src: imgUrl || '',
+        });
+        if (imgUrl) {
+          results.imageUrls.push(imgUrl);
+        }
+      }
+
+      // 处理a标签属性
+      if (node.tag === 'a') {
+        node.attr.href = node.attr.href || '';
+      }
+
+      // 处理font标签样式属性
+      if (node.tag === 'font') {
+        const fontSize = [
+          'x-small',
+          'small',
+          'medium',
+          'large',
+          'x-large',
+          'xx-large',
+          '-webkit-xxx-large',
+        ];
+        const styleAttrs = {
+          color: 'color',
+          face: 'font-family',
+          size: 'font-size',
+        };
+        if (!node.styleStr) node.styleStr = '';
+        Object.keys(styleAttrs).forEach((key) => {
+          if (node.attr[key]) {
+            const value = key === 'size' ? fontSize[node.attr[key] - 1] : node.attr[key];
+            node.styleStr += `${styleAttrs[key]}: ${value};`;
+          }
+        });
+      }
+
+      // 临时记录source资源
+      if (node.tag === 'source') {
+        results.source = node.attr.src;
+      }
+
+      if (customHandler.start) {
+        customHandler.start(node, results);
+      }
+
+      if (unary) {
+        // if this tag doesn't have end tag
+        // like <img src="hoge.png"/>
+        // add to parents
+        const parent = bufArray[0] || results;
+        if (parent.nodes === undefined) {
+          parent.nodes = [];
+        }
+        parent.nodes.push(node);
+      } else {
+        bufArray.unshift(node);
+      }
+    },
+    end(tag) {
+      // merge into parent tag
+      const node = bufArray.shift();
+      if (node.tag !== tag) {
+        console.error('invalid state: mismatch end tag');
+      }
+
+      // 当有缓存source资源时于于video补上src资源
+      if (node.tag === 'video' && results.source) {
+        node.attr.src = results.source;
+        delete results.source;
+      }
+
+      if (customHandler.end) {
+        customHandler.end(node, results);
+      }
+
+      if (bufArray.length === 0) {
+        results.nodes.push(node);
+      } else {
+        const parent = bufArray[0];
+        if (!parent.nodes) {
+          parent.nodes = [];
+        }
+        parent.nodes.push(node);
+      }
+    },
+    chars(text) {
+      if (!text.trim()) return;
+
+      const node = {
+        node: 'text',
+        text,
+      };
+
+      if (customHandler.chars) {
+        customHandler.chars(node, results);
+      }
+
+      if (bufArray.length === 0) {
+        results.nodes.push(node);
+      } else {
+        const parent = bufArray[0];
+        if (parent.nodes === undefined) {
+          parent.nodes = [];
+        }
+        parent.nodes.push(node);
+      }
+    },
+  });
+
+  return results;
+}
+
+export default html2json;

+ 156 - 0
components/u-parse/libs/htmlparser.js

@@ -0,0 +1,156 @@
+/**
+ *
+ * htmlParser改造自: https://github.com/blowsie/Pure-JavaScript-HTML5-Parser
+ *
+ * author: Di (微信小程序开发工程师)
+ * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
+ *               垂直微信小程序开发交流社区
+ *
+ * github地址: https://github.com/icindy/wxParse
+ *
+ * for: 微信小程序富文本解析
+ * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
+ */
+// Regular Expressions for parsing tags and attributes
+
+const startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z0-9_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/;
+const endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/;
+const attr = /([a-zA-Z0-9_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;
+
+function makeMap(str) {
+  const obj = {};
+  const items = str.split(',');
+  for (let i = 0; i < items.length; i += 1) obj[items[i]] = true;
+  return obj;
+}
+
+// Empty Elements - HTML 5
+const empty = makeMap('area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr');
+
+// Block Elements - HTML 5
+const block = makeMap('address,code,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video');
+
+// Inline Elements - HTML 5
+const inline = makeMap('a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var');
+
+// Elements that you can, intentionally, leave open
+// (and which close themselves)
+const closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr');
+
+// Attributes that have their values filled in disabled="disabled"
+const fillAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected');
+
+function HTMLParser(html, handler) {
+  let index;
+  let chars;
+  let match;
+  let last = html;
+  const stack = [];
+
+  stack.last = () => stack[stack.length - 1];
+
+  function parseEndTag(tag, tagName) {
+    // If no tag name is provided, clean shop
+    let pos;
+    if (!tagName) {
+      pos = 0;
+    } else {
+      // Find the closest opened tag of the same type
+      tagName = tagName.toLowerCase();
+      for (pos = stack.length - 1; pos >= 0; pos -= 1) {
+        if (stack[pos] === tagName) break;
+      }
+    }
+    if (pos >= 0) {
+      // Close all the open elements, up the stack
+      for (let i = stack.length - 1; i >= pos; i -= 1) {
+        if (handler.end) handler.end(stack[i]);
+      }
+
+      // Remove the open elements from the stack
+      stack.length = pos;
+    }
+  }
+
+  function parseStartTag(tag, tagName, rest, unary) {
+    tagName = tagName.toLowerCase();
+
+    if (block[tagName]) {
+      while (stack.last() && inline[stack.last()]) {
+        parseEndTag('', stack.last());
+      }
+    }
+
+    if (closeSelf[tagName] && stack.last() === tagName) {
+      parseEndTag('', tagName);
+    }
+
+    unary = empty[tagName] || !!unary;
+
+    if (!unary) stack.push(tagName);
+
+    if (handler.start) {
+      const attrs = [];
+
+      rest.replace(attr, function genAttr(matches, name) {
+        const value = arguments[2] || arguments[3] || arguments[4] || (fillAttrs[name] ? name : '');
+
+        attrs.push({
+          name,
+          value,
+          escaped: value.replace(/(^|[^\\])"/g, '$1\\"'), // "
+        });
+      });
+
+      if (handler.start) {
+        handler.start(tagName, attrs, unary);
+      }
+    }
+  }
+
+  while (html) {
+    chars = true;
+
+    if (html.indexOf('</') === 0) {
+      match = html.match(endTag);
+
+      if (match) {
+        html = html.substring(match[0].length);
+        match[0].replace(endTag, parseEndTag);
+        chars = false;
+      }
+
+      // start tag
+    } else if (html.indexOf('<') === 0) {
+      match = html.match(startTag);
+
+      if (match) {
+        html = html.substring(match[0].length);
+        match[0].replace(startTag, parseStartTag);
+        chars = false;
+      }
+    }
+
+    if (chars) {
+      index = html.indexOf('<');
+      let text = '';
+      while (index === 0) {
+        text += '<';
+        html = html.substring(1);
+        index = html.indexOf('<');
+      }
+      text += index < 0 ? html : html.substring(0, index);
+      html = index < 0 ? '' : html.substring(index);
+
+      if (handler.chars) handler.chars(text);
+    }
+
+    if (html === last) throw new Error(`Parse Error: ${html}`);
+    last = html;
+  }
+
+  // Clean up any remaining tags
+  parseEndTag();
+}
+
+export default HTMLParser;

+ 195 - 0
components/u-parse/libs/wxDiscode.js

@@ -0,0 +1,195 @@
+// HTML 支持的数学符号
+function strNumDiscode(str) {
+  str = str.replace(/&forall;/g, '∀');
+  str = str.replace(/&part;/g, '∂');
+  str = str.replace(/&exist;/g, '∃');
+  str = str.replace(/&empty;/g, '∅');
+  str = str.replace(/&nabla;/g, '∇');
+  str = str.replace(/&isin;/g, '∈');
+  str = str.replace(/&notin;/g, '∉');
+  str = str.replace(/&ni;/g, '∋');
+  str = str.replace(/&prod;/g, '∏');
+  str = str.replace(/&sum;/g, '∑');
+  str = str.replace(/&minus;/g, '−');
+  str = str.replace(/&lowast;/g, '∗');
+  str = str.replace(/&radic;/g, '√');
+  str = str.replace(/&prop;/g, '∝');
+  str = str.replace(/&infin;/g, '∞');
+  str = str.replace(/&ang;/g, '∠');
+  str = str.replace(/&and;/g, '∧');
+  str = str.replace(/&or;/g, '∨');
+  str = str.replace(/&cap;/g, '∩');
+  str = str.replace(/&cup;/g, '∪');
+  str = str.replace(/&int;/g, '∫');
+  str = str.replace(/&there4;/g, '∴');
+  str = str.replace(/&sim;/g, '∼');
+  str = str.replace(/&cong;/g, '≅');
+  str = str.replace(/&asymp;/g, '≈');
+  str = str.replace(/&ne;/g, '≠');
+  str = str.replace(/&le;/g, '≤');
+  str = str.replace(/&ge;/g, '≥');
+  str = str.replace(/&sub;/g, '⊂');
+  str = str.replace(/&sup;/g, '⊃');
+  str = str.replace(/&nsub;/g, '⊄');
+  str = str.replace(/&sube;/g, '⊆');
+  str = str.replace(/&supe;/g, '⊇');
+  str = str.replace(/&oplus;/g, '⊕');
+  str = str.replace(/&otimes;/g, '⊗');
+  str = str.replace(/&perp;/g, '⊥');
+  str = str.replace(/&sdot;/g, '⋅');
+  return str;
+}
+
+// HTML 支持的希腊字母
+function strGreeceDiscode(str) {
+  str = str.replace(/&Alpha;/g, 'Α');
+  str = str.replace(/&Beta;/g, 'Β');
+  str = str.replace(/&Gamma;/g, 'Γ');
+  str = str.replace(/&Delta;/g, 'Δ');
+  str = str.replace(/&Epsilon;/g, 'Ε');
+  str = str.replace(/&Zeta;/g, 'Ζ');
+  str = str.replace(/&Eta;/g, 'Η');
+  str = str.replace(/&Theta;/g, 'Θ');
+  str = str.replace(/&Iota;/g, 'Ι');
+  str = str.replace(/&Kappa;/g, 'Κ');
+  str = str.replace(/&Lambda;/g, 'Λ');
+  str = str.replace(/&Mu;/g, 'Μ');
+  str = str.replace(/&Nu;/g, 'Ν');
+  str = str.replace(/&Xi;/g, 'Ν');
+  str = str.replace(/&Omicron;/g, 'Ο');
+  str = str.replace(/&Pi;/g, 'Π');
+  str = str.replace(/&Rho;/g, 'Ρ');
+  str = str.replace(/&Sigma;/g, 'Σ');
+  str = str.replace(/&Tau;/g, 'Τ');
+  str = str.replace(/&Upsilon;/g, 'Υ');
+  str = str.replace(/&Phi;/g, 'Φ');
+  str = str.replace(/&Chi;/g, 'Χ');
+  str = str.replace(/&Psi;/g, 'Ψ');
+  str = str.replace(/&Omega;/g, 'Ω');
+
+  str = str.replace(/&alpha;/g, 'α');
+  str = str.replace(/&beta;/g, 'β');
+  str = str.replace(/&gamma;/g, 'γ');
+  str = str.replace(/&delta;/g, 'δ');
+  str = str.replace(/&epsilon;/g, 'ε');
+  str = str.replace(/&zeta;/g, 'ζ');
+  str = str.replace(/&eta;/g, 'η');
+  str = str.replace(/&theta;/g, 'θ');
+  str = str.replace(/&iota;/g, 'ι');
+  str = str.replace(/&kappa;/g, 'κ');
+  str = str.replace(/&lambda;/g, 'λ');
+  str = str.replace(/&mu;/g, 'μ');
+  str = str.replace(/&nu;/g, 'ν');
+  str = str.replace(/&xi;/g, 'ξ');
+  str = str.replace(/&omicron;/g, 'ο');
+  str = str.replace(/&pi;/g, 'π');
+  str = str.replace(/&rho;/g, 'ρ');
+  str = str.replace(/&sigmaf;/g, 'ς');
+  str = str.replace(/&sigma;/g, 'σ');
+  str = str.replace(/&tau;/g, 'τ');
+  str = str.replace(/&upsilon;/g, 'υ');
+  str = str.replace(/&phi;/g, 'φ');
+  str = str.replace(/&chi;/g, 'χ');
+  str = str.replace(/&psi;/g, 'ψ');
+  str = str.replace(/&omega;/g, 'ω');
+  str = str.replace(/&thetasym;/g, 'ϑ');
+  str = str.replace(/&upsih;/g, 'ϒ');
+  str = str.replace(/&piv;/g, 'ϖ');
+  str = str.replace(/&middot;/g, '·');
+  return str;
+}
+
+function strcharacterDiscode(str) {
+  // 加入常用解析
+  str = str.replace(/&nbsp;/g, ' ');
+  str = str.replace(/&ensp;/g, ' ');
+  str = str.replace(/&emsp;/g, ' ');
+  str = str.replace(/&quot;/g, "'");
+  str = str.replace(/&amp;/g, '&');
+  str = str.replace(/&lt;/g, '<');
+  str = str.replace(/&gt;/g, '>');
+  str = str.replace(/&#8226;/g, '•');
+
+  return str;
+}
+
+// HTML 支持的其他实体
+function strOtherDiscode(str) {
+  str = str.replace(/&OElig;/g, 'Œ');
+  str = str.replace(/&oelig;/g, 'œ');
+  str = str.replace(/&Scaron;/g, 'Š');
+  str = str.replace(/&scaron;/g, 'š');
+  str = str.replace(/&Yuml;/g, 'Ÿ');
+  str = str.replace(/&fnof;/g, 'ƒ');
+  str = str.replace(/&circ;/g, 'ˆ');
+  str = str.replace(/&tilde;/g, '˜');
+  str = str.replace(/&ensp;/g, '');
+  str = str.replace(/&emsp;/g, '');
+  str = str.replace(/&thinsp;/g, '');
+  str = str.replace(/&zwnj;/g, '');
+  str = str.replace(/&zwj;/g, '');
+  str = str.replace(/&lrm;/g, '');
+  str = str.replace(/&rlm;/g, '');
+  str = str.replace(/&ndash;/g, '–');
+  str = str.replace(/&mdash;/g, '—');
+  str = str.replace(/&lsquo;/g, '‘');
+  str = str.replace(/&rsquo;/g, '’');
+  str = str.replace(/&sbquo;/g, '‚');
+  str = str.replace(/&ldquo;/g, '“');
+  str = str.replace(/&rdquo;/g, '”');
+  str = str.replace(/&bdquo;/g, '„');
+  str = str.replace(/&dagger;/g, '†');
+  str = str.replace(/&Dagger;/g, '‡');
+  str = str.replace(/&bull;/g, '•');
+  str = str.replace(/&hellip;/g, '…');
+  str = str.replace(/&permil;/g, '‰');
+  str = str.replace(/&prime;/g, '′');
+  str = str.replace(/&Prime;/g, '″');
+  str = str.replace(/&lsaquo;/g, '‹');
+  str = str.replace(/&rsaquo;/g, '›');
+  str = str.replace(/&oline;/g, '‾');
+  str = str.replace(/&euro;/g, '€');
+  str = str.replace(/&trade;/g, '™');
+
+  str = str.replace(/&larr;/g, '←');
+  str = str.replace(/&uarr;/g, '↑');
+  str = str.replace(/&rarr;/g, '→');
+  str = str.replace(/&darr;/g, '↓');
+  str = str.replace(/&harr;/g, '↔');
+  str = str.replace(/&crarr;/g, '↵');
+  str = str.replace(/&lceil;/g, '⌈');
+  str = str.replace(/&rceil;/g, '⌉');
+
+  str = str.replace(/&lfloor;/g, '⌊');
+  str = str.replace(/&rfloor;/g, '⌋');
+  str = str.replace(/&loz;/g, '◊');
+  str = str.replace(/&spades;/g, '♠');
+  str = str.replace(/&clubs;/g, '♣');
+  str = str.replace(/&hearts;/g, '♥');
+
+  str = str.replace(/&diams;/g, '♦');
+  str = str.replace(/&#39;/g, "'");
+  return str;
+}
+
+function strDiscode(str) {
+  str = strNumDiscode(str);
+  str = strGreeceDiscode(str);
+  str = strcharacterDiscode(str);
+  str = strOtherDiscode(str);
+  return str;
+}
+
+function urlToHttpUrl(url, domain) {
+  if (/^\/\//.test(url)) {
+    return `https:${url}`;
+  } else if (/^\//.test(url)) {
+    return `https://${domain}${url}`;
+  }
+  return url;
+}
+
+export default {
+  strDiscode,
+  urlToHttpUrl,
+};

+ 102 - 0
components/u-parse/readme.md

@@ -0,0 +1,102 @@
+## uParse 适用于 uni-app/mpvue 的富文本解析组件
+
+> 支持 Html、Markdown 解析,Fork自: [mpvue-wxParse](https://github.com/F-loat/mpvue-wxParse)
+
+
+## 属性
+
+| 名称             | 类型          | 默认值        | 描述               |
+| -----------------|--------------- | ------------- | ----------------  |
+| loading          | Boolean        | false         | 数据加载状态       |
+| className        | String         | —             | 自定义 class 名称  |
+| content          | String         | —             | 渲染内容           |
+| noData           | String         | 数据不能为空   | 空数据时的渲染展示  |
+| startHandler     | Function       | 见源码         | 自定义 parser 函数 |
+| endHandler       | Function       | null          | 自定义 parser 函数 |
+| charsHandler     | Function       | null          | 自定义 parser 函数 |
+| imageProp        | Object         | 见下文        | 图片相关参数        |
+
+### 自定义 parser 函数具体介绍
+
+* 传入的参数为当前节点 `node` 对象及解析结果 `results` 对象,例如 `startHandler(node, results)`
+* 无需返回值,通过对传入的参数直接操作来完成需要的改动
+* 自定义函数会在原解析函数处理之后执行
+
+### imageProp 对象具体属性
+
+| 名称              | 类型           | 默认值        | 描述                |
+| -----------------|--------------- | ------------- | ------------------ |
+| mode             | String         | 'aspectFit'   | 图片裁剪、缩放的模式 |
+| padding          | Number         | 0             | 图片内边距          |
+| lazyLoad         | Boolean        | false         | 图片懒加载          |
+| domain           | String         | ''            | 图片服务域名        |
+
+## 事件
+
+| 名称             | 参数              | 描述              |
+| -----------------|----------------- | ----------------  |
+| preview          | 图片地址,原始事件 | 预览图片时触发     |
+| navigate         | 链接地址,原始事件 | 点击链接时触发     |
+
+## 基本使用方法
+
+
+``` vue
+<template>
+  <div>
+    <u-parse :content="article" @preview="preview" @navigate="navigate" />
+  </div>
+</template>
+
+<script>
+import uParse from '@/components/u-parse/u-parse.vue'
+
+export default {
+  components: {
+    uParse
+  },
+  data () {
+    return {
+      article: '<div>我是HTML代码</div>'
+    }
+  },
+  methods: {
+    preview(src, e) {
+      // do something
+    },
+    navigate(href, e) {
+      // do something
+    }
+  }
+}
+</script>
+
+<style>
+@import url("@/components/u-parse/u-parse.css");
+</style>
+```
+
+
+## 渲染 Markdown
+
+> 先将 markdown 转换为 html 即可
+
+```
+npm install marked
+```
+
+``` js
+import marked from 'marked'
+import uParse from '@/components/u-parse/u-parse.vue'
+
+export default {
+  components: {
+    uParse
+  },
+  data () {
+    return {
+      article: marked(`#hello, markdown!`)
+    }
+  }
+}
+```

+ 232 - 0
components/u-parse/u-parse.css

@@ -0,0 +1,232 @@
+/**
+ * author: Di (微信小程序开发工程师)
+ * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
+ *         垂直微信小程序开发交流社区
+ *
+ * github地址: https://github.com/icindy/wxParse
+ *
+ * for: 微信小程序富文本解析
+ * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
+ */
+
+.wxParse {
+  width: 100%;
+  font-family: Helvetica, sans-serif;
+  font-size: 30upx;
+  color: #666;
+  line-height: 1.8;
+}
+
+.wxParse view {
+  word-break: hyphenate;
+}
+
+.wxParse .inline {
+  display: inline;
+  margin: 0;
+  padding: 0;
+}
+
+.wxParse .div {
+  margin: 0;
+  padding: 0;
+}
+
+.wxParse .h1 .text {
+  font-size: 2em;
+  margin: 0.67em 0;
+}
+.wxParse .h2 .text {
+  font-size: 1.5em;
+  margin: 0.83em 0;
+}
+.wxParse .h3 .text {
+  font-size: 1.17em;
+  margin: 1em 0;
+}
+.wxParse .h4 .text {
+  margin: 1.33em 0;
+}
+.wxParse .h5 .text {
+  font-size: 0.83em;
+  margin: 1.67em 0;
+}
+.wxParse .h6 .text {
+  font-size: 0.67em;
+  margin: 2.33em 0;
+}
+
+.wxParse .h1 .text,
+.wxParse .h2 .text,
+.wxParse .h3 .text,
+.wxParse .h4 .text,
+.wxParse .h5 .text,
+.wxParse .h6 .text,
+.wxParse .b,
+.wxParse .strong {
+  font-weight: bolder;
+}
+
+
+.wxParse .p {
+  margin: 1em 0;
+}
+
+.wxParse .i,
+.wxParse .cite,
+.wxParse .em,
+.wxParse .var,
+.wxParse .address {
+  font-style: italic;
+}
+
+.wxParse .pre,
+.wxParse .tt,
+.wxParse .code,
+.wxParse .kbd,
+.wxParse .samp {
+  font-family: monospace;
+}
+.wxParse .pre {
+  overflow: auto;
+  background: #f5f5f5;
+  padding: 16upx;
+  white-space: pre;
+  margin: 1em 0upx;
+}
+.wxParse .code {
+  display: inline;
+  background: #f5f5f5;
+}
+
+.wxParse .big {
+  font-size: 1.17em;
+}
+
+.wxParse .small,
+.wxParse .sub,
+.wxParse .sup {
+  font-size: 0.83em;
+}
+
+.wxParse .sub {
+  vertical-align: sub;
+}
+.wxParse .sup {
+  vertical-align: super;
+}
+
+.wxParse .s,
+.wxParse .strike,
+.wxParse .del {
+  text-decoration: line-through;
+}
+
+.wxParse .strong,
+.wxParse .s {
+  display: inline;
+}
+
+.wxParse .a {
+  color: deepskyblue;
+}
+
+.wxParse .video {
+  text-align: center;
+  margin: 22upx 0;
+}
+
+.wxParse .video-video {
+  width: 100%;
+}
+
+.wxParse .img {
+  display: inline-block;
+  width: 0;
+  height: 0;
+  max-width: 100%;
+  overflow: hidden;
+}
+
+.wxParse .blockquote {
+  margin: 10upx 0;
+  padding: 22upx 0 22upx 22upx;
+  font-family: Courier, Calibri, "宋体";
+  background: #f5f5f5;
+  border-left: 6upx solid #dbdbdb;
+}
+.wxParse .blockquote .p {
+  margin: 0;
+}
+
+.wxParse .ul, .wxParse .ol {
+  display: block;
+  margin: 1em 0;
+  padding-left: 33upx;
+}
+.wxParse .ol {
+  list-style-type: disc;
+}
+.wxParse .ol {
+  list-style-type: decimal;
+}
+.wxParse .ol>weixin-parse-template,.wxParse .ul>weixin-parse-template {
+  display: list-item;
+  align-items: baseline;
+  text-align: match-parent;
+}
+
+.wxParse .ol>.li,.wxParse .ul>.li {
+  display: list-item;
+  align-items: baseline;
+  text-align: match-parent;
+}
+.wxParse .ul .ul, .wxParse .ol .ul {
+  list-style-type: circle;
+}
+.wxParse .ol .ol .ul, .wxParse .ol .ul .ul, .wxParse .ul .ol .ul, .wxParse .ul .ul .ul {
+    list-style-type: square;
+}
+
+.wxParse .u {
+  text-decoration: underline;
+}
+.wxParse .hide {
+  display: none;
+}
+.wxParse .del {
+  display: inline;
+}
+.wxParse .figure {
+  overflow: hidden;
+}
+
+.wxParse .table {
+  width: 100%;
+}
+.wxParse .thead, .wxParse .tfoot, .wxParse .tr {
+  display: flex;
+  flex-direction: row;
+}
+.wxParse .tr {
+  width:100%;
+  display: flex;
+  border-right: 2upx solid #e0e0e0;
+  border-bottom: 2upx solid #e0e0e0;
+}
+.wxParse .th,
+.wxParse .td {
+  display: flex;
+  width: 1276upx;
+  overflow: auto;
+  flex: 1;
+  padding: 11upx;
+  border-left: 2upx solid #e0e0e0;
+}
+.wxParse .td:last {
+  border-top: 2upx solid #e0e0e0;
+}
+.wxParse .th {
+  background: #f0f0f0;
+  border-top: 2upx solid #e0e0e0;
+}

+ 117 - 0
components/u-parse/u-parse.vue

@@ -0,0 +1,117 @@
+<!--**
+ * forked from:https://github.com/F-loat/mpvue-wxParse
+ *
+ * github地址: https://github.com/dcloudio/uParse
+ *
+ * for: uni-app框架下 富文本解析
+ */-->
+
+<template>
+<!--基础元素-->
+<div class="wxParse" :class="className" v-if="!loading">
+  <block v-for="(node,index) of nodes" :key="index">
+    <wxParseTemplate :node="node" />
+  </block>
+</div>
+</template>
+
+<script>
+import HtmlToJson from './libs/html2json';
+import wxParseTemplate from './components/wxParseTemplate0';
+
+export default {
+  name: 'wxParse',
+  props: {
+    loading: {
+      type: Boolean,
+      default: false,
+    },
+    className: {
+      type: String,
+      default: '',
+    },
+    content: {
+      type: String,
+      default: '',
+    },
+    noData: {
+      type: String,
+      default: '',
+    },
+    startHandler: {
+      type: Function,
+      default() {
+        return (node) => {
+          node.attr.class = null;
+          node.attr.style = null;
+        };
+      },
+    },
+    endHandler: {
+      type: Function,
+      default: null,
+    },
+    charsHandler: {
+      type: Function,
+      default: null,
+    },
+    imageProp: {
+      type: Object,
+      default() {
+        return {
+          mode: 'aspectFit',
+          padding: 0,
+          lazyLoad: false,
+          domain: '',
+        };
+      },
+    },
+  },
+  components: {
+    wxParseTemplate,
+  },
+  data() {
+    return {
+      imageUrls: [],
+    };
+  },
+  computed: {
+    nodes() {
+      const {
+        content,
+        noData,
+        imageProp,
+        startHandler,
+        endHandler,
+        charsHandler,
+      } = this;
+      const parseData = content || noData;
+      const customHandler = {
+        start: startHandler,
+        end: endHandler,
+        chars: charsHandler,
+      };
+      const results = HtmlToJson(parseData, customHandler, imageProp, this);
+      this.imageUrls = results.imageUrls;
+      return results.nodes;
+    },
+  },
+  methods: {
+    navigate(href, $event) {
+      this.$emit('navigate', href, $event);
+    },
+    preview(src, $event) {
+      if (!this.imageUrls.length) return;
+      wx.previewImage({
+        current: src,
+        urls: this.imageUrls,
+      });
+      this.$emit('preview', src, $event);
+    },
+    removeImageUrl(src) {
+      const { imageUrls } = this;
+      imageUrls.splice(imageUrls.indexOf(src), 1);
+    },
+  },
+};
+</script>

BIN
components/v-tabs/.DS_Store


+ 182 - 0
components/v-tabs/readme.md

@@ -0,0 +1,182 @@
+## 插件说明
+
+> 这是 `v-tabs` 插件的升级版本,参数上有很大变动,支持 `H5` `小程序` `手机端`,如果是在之前的插件上升级的话,请注意参数的变更,触发的事件没有变更。
+
+## 使用说明
+
+### 1、最基本用法
+
+- 视图文件
+
+```html
+<v-tabs v-model="current" :tabs="tabs" @change="changeTab"></v-tabs>
+```
+
+- 脚本文件
+
+```js
+export default {
+  data() {
+    return {
+      current: 0,
+      tabs: ['军事', '国内', '新闻新闻', '军事', '国内', '新闻', '军事', '国内', '新闻']
+    }
+  },
+  methods: {
+    changeTab(index) {
+      console.log('当前选中的项:' + index)
+    }
+  }
+}
+```
+
+### 2、平铺整个屏幕
+
+- 视图文件
+
+```html
+<v-tabs v-model="activeTab" :scroll="false" :tabs="['全部', '进行中', '已完成']"></v-tabs>
+```
+
+- 脚本文件
+
+```js
+export default {
+  data() {
+    return {
+      activeTab: 0
+    }
+  }
+}
+```
+
+### 3、胶囊用法
+
+- 视图文件
+
+```html
+<v-tabs v-model="current" :tabs="tabs" :pills="true" line-height="0" activeColor="#fff" @change="changeTab"></v-tabs>
+```
+
+- 脚本文件
+
+```js
+data() {
+  return {
+    current: 2,
+    tabs: [
+        '军事',
+        '国内',
+        '新闻新闻',
+        '军事',
+        '国内',
+        '新闻',
+        '军事',
+        '国内',
+        '新闻',
+      ],
+  },
+  methods: {
+    changeTab(index) {
+      console.log('当前选中索引:' + index)
+    }
+  }
+}
+```
+
+## 文档说明
+
+### 1、属性说明
+
+|       参数        |  类型   |  默认值   |                    说明                    |
+| :---------------: | :-----: | :-------: | :----------------------------------------: |
+|       value       | Number  |     0     |             必传(双向绑定的值)             |
+|       color       | String  |  '#333'   |                默认文字颜色                |
+|    activeColor    | String  | '#2979ff' |               选中文字的颜色               |
+|     fontSize      | String  |  '28rpx'  |          默认文字大小(rpx 或 px)           |
+|       bold        | Boolean |   true    |               是否加粗选中项               |
+|      scroll       | Boolean |   true    |       是否显示滚动条,平铺设置 false       |
+|      height       | String  |  '70rpx'  |            tab 高度(rpx 或 px)             |
+|    lineHeight     | String  |  '10rpx'  |            滑块高度(rpx 或 px)             |
+|     lineColor     | String  | '#2979ff' |                 滑块的颜色                 |
+|     lineScale     | Number  |    0.5    |               滑块宽度缩放值               |
+|    lineRadius     | String  |  '10rpx'  |          滑块圆角宽度(rpx 或 px)           |
+|       pills       | Boolean |   false   |                是否开启胶囊                |
+|    pillsColor     | String  | '#2979ff' |          胶囊背景颜色(rpx 或 px)           |
+| pillsBorderRadius | String  |  '10rpx'  |          胶囊圆角宽度(rpx 或 px)           |
+|       field       | String  |    ''     |  如果 tabs 子项是对象,输入需要展示的键名  |
+|      bgColor      | String  |  '#fff'   |     背景色,支持 linear-gradient 渐变      |
+|      padding      | String  |    '0'    |           整个 tab padding 属性            |
+|       fixed       | Boolean |   false   |               是否固定在顶部               |
+|    paddingItem    | String  | '0 22rpx' | 选项的边距(设置上下不生效,需要设置高度) |
+
+### 2、事件说明
+
+|  名称  | 参数  |                说明                |
+| :----: | :---: | :--------------------------------: |
+| change | index | 改变选中项触发, index 选中项的下标 |
+
+## 更新日志
+
+### 2020-09-24
+
+1. 修复 `v-tabs` 第一次可能出现第一个标签显示不完整的情况
+2. 修改了 `pages/tabs/order` 示例文件
+
+### 2020-09-21
+
+1. 修复添加 `fixed` 属性后,滚动条无效
+2. 修复选项很少的情况下,下划线计算计算错误
+3. 新增 `paddingItem` 属性,设置选项左右边距(上下边距需要设置 `height` 属性,或者设置 `padding` 属性)
+
+**写在最后:**
+欢迎各位老铁反馈 bug ,本人后端 PHP 一枚,只是应为感兴趣前端,自己琢磨,自己搞。如果你在使用的过程中有什么不合理,需要优化的,都可以在下面评论(或加我 QQ: 1207791534),本人看见后回复、修正,感谢。
+
+### 2020-09-17
+
+1. 紧急修复 bug,横向滑动不了的情况
+
+### 2020-09-16
+
+1. 新增 `fixed` 属性,是否固定在顶部,示例地址:`pages/tabs/tabs-static`
+2. 优化之前的页面结构
+
+**注意:**
+
+1. 使用 `padding` 属性的时候,尽量不要左右边距,会导致下划线位置不对
+2. 如果不绑定 `v-model` 会导致 `change` 事件改变的时候,下划线不跟随问题
+
+### 2020-09-09
+
+1. 修复 `width` 错误,dom 加载的时候没有及时获取到 `data` 属性导致的。
+
+### 2020-08-29
+
+1. 优化异步改变 `tabs` 后,下划线不初始化问题
+2. `github` 地址上有图 2 的源码,需要的自行下载,页面路径:`pages/tabs/order`
+
+### 2020-08-20
+
+1. 优化 `节点查询` 和 `选中渲染`
+2. 优化支付宝中 `createSelectorQuery()` 的影响
+
+### 2020-08-19
+
+1. 优化 `change` 事件触发机制
+
+### 2020-08-16
+
+1. 修改默认高度为 `70rpx`
+2. 新增属性 `bgColor`,可设置背景颜色,默认 `#fff`
+3. 新增整个 `tab` 的 `padding` 属性,默认 `0`
+
+### 2020-08-13
+
+1. 全新的 `v-tabs 2.0`
+2. 支持 `H5` `小程序` `APP`
+3. 属性高度可配置
+
+## 预览
+
+![v-tabs 2.0.1.gif](https://tva1.sinaimg.cn/large/007S8ZIlgy1ghsv40mj76g30ai0i2tsd.gif)
+![v-tabs 2.0.2.gif](https://img-cdn-aliyun.dcloud.net.cn/stream/plugin_screens/42f3a920-a674-11ea-8a24-ffee00625e2e_1.png?v=1597912963)

+ 339 - 0
components/v-tabs/v-tabs.vue

@@ -0,0 +1,339 @@
+<template>
+  <view :id="elId" class="v-tabs">
+    <scroll-view
+      id="scrollContainer"
+      :scroll-x="scroll"
+      :scroll-left="scroll ? scrollLeft : 0"
+      :scroll-with-animation="scroll"
+      :style="{ position: fixed ? 'fixed' : 'relative', zIndex: 1993 }"
+    >
+      <view
+        class="v-tabs__container"
+        :style="{
+          display: scroll ? 'inline-flex' : 'flex',
+          whiteSpace: scroll ? 'nowrap' : 'normal',
+          background: bgColor,
+          height,
+          padding
+        }"
+      >
+        <view
+          class="v-tabs__container-item"
+          v-for="(v, i) in tabs"
+          :key="i"
+          :style="{
+            color: current == i ? activeColor : color,
+            fontSize: current == i ? fontSize : fontSize,
+            fontWeight: bold && current == i ? 'bold' : '',
+            justifyContent: !scroll ? 'center' : '',
+            flex: scroll ? '' : 1,
+            padding: paddingItem
+          }"
+          @click="change(i)"
+        >
+          {{ field ? v[field] : v }}
+        </view>
+        <view
+          v-if="!pills"
+          class="v-tabs__container-line"
+          :style="{
+            background: lineColor,
+            width: lineWidth + 'px',
+            height: lineHeight,
+            borderRadius: lineRadius,
+            left: lineLeft + 'px',
+            transform: `translateX(-${lineWidth / 2}px)`
+          }"
+        ></view>
+        <view
+          v-else
+          class="v-tabs__container-pills"
+          :style="{
+            background: pillsColor,
+            borderRadius: pillsBorderRadius,
+            left: pillsLeft + 'px',
+            width: currentWidth + 'px',
+            height
+          }"
+        ></view>
+      </view>
+    </scroll-view>
+    <view
+      class="v-tabs__placeholder"
+      :style="{
+        height: fixed ? height : '0',
+        padding
+      }"
+    ></view>
+  </view>
+</template>
+
+<script>
+/**
+ * v-tabs
+ * @property {Number} value 选中的下标
+ * @property {Array} tabs tabs 列表
+ * @property {String} bgColor = '#fff' 背景颜色
+ * @property {String} color = '#333' 默认颜色
+ * @property {String} activeColor = '#2979ff' 选中文字颜色
+ * @property {String} fontSize = '28rpx' 默认文字大小
+ * @property {String} activeFontSize = '28rpx' 选中文字大小
+ * @property {Boolean} bold = [true | false] 选中文字是否加粗
+ * @property {Boolean} scroll = [true | false] 是否滚动
+ * @property {String} height = '60rpx' tab 的高度
+ * @property {String} lineHeight = '10rpx' 下划线的高度
+ * @property {String} lineColor = '#2979ff' 下划线的颜色
+ * @property {Number} lineScale = 0.5 下划线的宽度缩放比例
+ * @property {String} lineRadius = '10rpx' 下划线圆角
+ * @property {Boolean} pills = [true | false] 是否胶囊样式
+ * @property {String} pillsColor = '#2979ff' 胶囊背景色
+ * @property {String} pillsBorderRadius = '10rpx' 胶囊圆角大小
+ * @property {String} field 如果是对象,显示的键名
+ * @property {Boolean} fixed = [true | false] 是否固定
+ * @property {String} paddingItem = '0 22rpx' 选项的边距
+ *
+ * @event {Function(current)} change 改变标签触发
+ */
+export default {
+  props: {
+    value: {
+      type: Number,
+      default: 0
+    },
+    tabs: {
+      type: Array,
+      default() {
+        return []
+      }
+    },
+    bgColor: {
+      type: String,
+      default: '#fff'
+    },
+    padding: {
+      type: String,
+      default: '0'
+    },
+    color: {
+      type: String,
+      default: '#333'
+    },
+    activeColor: {
+      type: String,
+      default: '#2979ff'
+    },
+    fontSize: {
+      type: String,
+      default: '28rpx'
+    },
+    activeFontSize: {
+      type: String,
+      default: '32rpx'
+    },
+    bold: {
+      type: Boolean,
+      default: true
+    },
+    scroll: {
+      type: Boolean,
+      default: true
+    },
+    height: {
+      type: String,
+      default: '70rpx'
+    },
+    lineColor: {
+      type: String,
+      default: '#2979ff'
+    },
+    lineHeight: {
+      type: String,
+      default: '10rpx'
+    },
+    lineScale: {
+      type: Number,
+      default: 0.5
+    },
+    lineRadius: {
+      type: String,
+      default: '10rpx'
+    },
+    pills: {
+      type: Boolean,
+      deafult: false
+    },
+    pillsColor: {
+      type: String,
+      default: '#2979ff'
+    },
+    pillsBorderRadius: {
+      type: String,
+      default: '10rpx'
+    },
+    field: {
+      type: String,
+      default: ''
+    },
+    fixed: {
+      type: Boolean,
+      default: false
+    },
+    paddingItem: {
+      type: String,
+      default: '0 22rpx'
+    }
+  },
+  data() {
+    return {
+      elId: '',
+      lineWidth: 30,
+      currentWidth: 0, // 当前选项的宽度
+      lineLeft: 0, // 滑块距离左侧的位置
+      pillsLeft: 0, // 胶囊距离左侧的位置
+      scrollLeft: 0, // 距离左边的位置
+      containerWidth: 0, // 容器的宽度
+      current: 0 // 当前选中项
+    }
+  },
+  watch: {
+    value(newVal) {
+      this.current = newVal
+      this.$nextTick(() => {
+        this.getTabItemWidth()
+      })
+    },
+    current(newVal) {
+      this.$emit('input', newVal)
+    },
+    tabs(newVal) {
+      this.$nextTick(() => {
+        this.getTabItemWidth()
+      })
+    }
+  },
+  methods: {
+    // 产生随机字符串
+    randomString(len) {
+      len = len || 32
+      let $chars =
+        'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678' /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
+      let maxPos = $chars.length
+      let pwd = ''
+      for (let i = 0; i < len; i++) {
+        pwd += $chars.charAt(Math.floor(Math.random() * maxPos))
+      }
+      return pwd
+    },
+    // 切换事件
+    change(index) {
+      if (this.current !== index) {
+        this.current = index
+
+        this.$emit('change', index)
+      }
+    },
+    // 获取左移动位置
+    getTabItemWidth() {
+      let query = uni
+        .createSelectorQuery()
+        // #ifndef MP-ALIPAY
+        .in(this)
+      // #endif
+      // 获取容器的宽度
+      query
+        .select(`#scrollContainer`)
+        .boundingClientRect((data) => {
+          if (!this.containerWidth && data) {
+            this.containerWidth = data.width
+          }
+        })
+        .exec()
+      // 获取所有的 tab-item 的宽度
+      query
+        .selectAll('.v-tabs__container-item')
+        .boundingClientRect((data) => {
+          if (!data) {
+            return
+          }
+          let lineLeft = 0
+          let currentWidth = 0
+          if (data) {
+            for (let i = 0; i < data.length; i++) {
+              if (i < this.current) {
+                lineLeft += data[i].width
+              } else if (i == this.current) {
+                currentWidth = data[i].width
+              } else {
+                break
+              }
+            }
+          }
+          // 当前滑块的宽度
+          this.currentWidth = currentWidth
+          // 缩放后的滑块宽度
+          this.lineWidth = currentWidth * this.lineScale * 1
+          // 滑块作移动的位置
+          this.lineLeft = lineLeft + currentWidth / 2
+          // 胶囊距离左侧的位置
+          this.pillsLeft = lineLeft
+          // 计算滚动的距离左侧的位置
+          if (this.scroll) {
+            this.scrollLeft = this.lineLeft - this.containerWidth / 2
+          }
+        })
+        .exec()
+    }
+  },
+  mounted() {
+    this.elId = 'xfjpeter_' + this.randomString()
+    this.current = this.value
+    this.$nextTick(() => {
+      this.getTabItemWidth()
+    })
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.v-tabs {
+  width: 100%;
+  box-sizing: border-box;
+  overflow: hidden;
+
+  ::-webkit-scrollbar {
+    display: none;
+  }
+
+  &__container {
+    min-width: 100%;
+    position: relative;
+    display: inline-flex;
+    align-items: center;
+    white-space: nowrap;
+    overflow: hidden;
+
+    &-item {
+      display: flex;
+      align-items: center;
+      height: 100%;
+      position: relative;
+      z-index: 10;
+      // padding: 0 11px;
+      transition: all 0.3s;
+      white-space: nowrap;
+    }
+
+    &-line {
+      position: absolute;
+      bottom: 0;
+      transition: all 0.3s linear;
+    }
+
+    &-pills {
+      position: absolute;
+      transition: all 0.3s linear;
+      z-index: 9;
+    }
+  }
+}
+</style>

+ 214 - 0
http/api.js

@@ -0,0 +1,214 @@
+import url from './index.js'
+const send = (data) => url.post('/api/sms/send', data);
+const mobilelogin = (data) => url.post('/api/user/mobilelogin', data);
+const passwordLogin = (data) => url.post('/api/user/login', data);
+const resetpwd = (data) => url.post('/api/user/resetpwd', data);
+const agreement = (data) => url.post('/api/common/agreement', data);
+
+// 首页接口
+const getPayKey = (data) => url.post('/api/wechat/getPayKey', data);
+const sumuv = (data) => url.post('/api/index/sumUv', {});
+const wechatLoginRedirect = (data) => url.post('/api/wechat/wechatLoginRedirect', data);
+const baseInfo = (data) => url.post('/api/index/baseInfo', data);
+const categoryList = (data) => url.post('/api/index/categoryList', data);
+const recommend = (data) => url.post('/api/index/recommend', data);
+const newestOpen = (data) => url.post('/api/index/newestOpen', data);
+const star = (data) => url.post('/api/index/star', data);
+const boxListByCategory = (data) => url.post('/api/index/boxListByCategory', data);
+const boxDetail = (data) => url.post('/api/index/boxDetail', data);
+const hotBox = (data) => url.post('/api/index/hotBox', data);
+const cheapBox = (data) => url.post('/api/index/cheapBox', data);
+const rechargeList = (data) => url.post('/api/index/rechargeList', data);
+const openByOrderTrade = (data) => url.get('/api/index/openByOrderTrade', data);
+const createRechargeOrder = (data) => url.post('/api/index/createRechargeOrder', data);
+const getServiceInfo = (data) => url.post('/api/index/getServiceInfo', data);
+const getWechatLoginUrl = (data) => url.post('/api/index/getWechatLoginUrl', data);
+
+const tryBoxDetail = (data) => url.post('/api/index/tryBoxDetail', data);
+const haveATry = (data) => url.post('/api/index/haveATry', data);
+const createOrder = (data) => url.post('/api/index/createOrder', data);
+const coinPay = (data) => url.post('/api/index/coinPay', data);
+const cmoneyPay = (data) => url.post('/api/index/cmoneyPay', data);
+const search = (data) => url.post('/api/index/search', data);
+const priceRange = (data) => url.post('/api/index/priceRange', data);
+// 个人中心
+
+const sList = (data) => url.post('/api/index/sList', data);
+
+
+const wechatMpAuth = (data) => url.post('/api/Auth_login_with_wechat/code', data)
+const wechatMpAuthLogin = (data) => url.post('/api/Auth_login_with_wechat/login', data)
+const openRecord = (data) => url.post('/api/user/openRecord', data);
+const myBox = (data) => url.post('/api/user/myBox', data);
+const myOrderList = (data) => url.post('/api/user/myOrderList', data);
+const userinfo = (data) => url.post('/api/user/userinfo', data);
+const myStar = (data) => url.post('/api/user/myStar', data);
+const cancelStar = (data) => url.post('/api/user/cancelStar', data);
+const myBalance = (data) => url.post('/api/user/myBalance', data);
+const myCoin = (data) => url.post('/api/user/myCoin', data);
+const myAddress = (data) => url.post('/api/user/myAddress', data);
+const addAddress = (data) => url.post('/api/user/addAddress', data);
+const editAddress = (data) => url.post('/api/user/editAddress', data);
+const deleteAddress = (data) => url.post('/api/user/deleteAddress', data);
+
+const exchange = (data) => url.post('/api/user/exchange', data);
+const checkDeliveryInfo = (data) => url.post('/api/user/checkDeliveryInfo', data);
+const applyDelivery = (data) => url.post('/api/user/applyDelivery', data);
+const searchHistory = (data) => url.post('/api/user/searchHistory', data);
+const getSettingInfo = (data) => url.post('/api/user/getSettingInfo', data);
+const changeInfo = (data) => url.post('/api/user/changeInfo', data);
+const deliveryOrderDetail = (data) => url.post('/api/user/deliveryOrderDetail', data);
+const confirmReceipt = (data) => url.post('/api/user/confirmReceipt', data);
+const moneyToCoin = (data) => url.post('/api/user/moneyToCoin', data);
+const getWithdrawalSetting = (data) => url.post('/api/user/getWithdrawalSetting', data);
+const withdrawal = (data) => url.post('/api/user/withdrawal', data);
+const bindWithdrawalAccount = (data) => url.post('/api/user/bindWithdrawalAccount', data);
+const bindMobile = (data) => url.post('/api/user/bindMobile', data);
+
+const getVgoods = (data) => url.get('/api/index/getVirData', data);
+//最新开盒轮播
+const lunbobox = (data) => url.get('/api/index/lunbobox', data);
+// 分销
+const getTotalCoin = () => url.get(`/api/retail/getTotalCoin`)
+const getRetailList = ({
+	page,
+	limit
+}) => url.post(`/api/retail/getRetailList`, {
+	page,
+	limit
+})
+const getTeamList = ({
+	page,
+	limit
+}) => url.post(`/api/retail/getTeamList`, {
+	page,
+	limit
+})
+const getTixianList = ({
+	page,
+	limit
+}) => url.post(`/api/retail/getTixianList`, {
+	page,
+	limit
+})
+//卡密
+const carpassDeposit = (data) => url.post('/api/carpass/deposit', data);
+const carpassList = (data) => url.get('/api/carpass/check', data);
+//获取金币和盒子数量
+const getUserCapital = (data) => url.get('/api/user/UserCapital', data);
+//转赠
+const echargez = (data) => url.post('/api/user/echargez', data);
+//转赠列表
+const echargezs = (data) => url.get('/api/user/echargezs', data);
+//获取跳转域名
+const getServiceurl = (data) => url.get('/api/index/getServiceurl', data);
+//金币提现
+const goldDeposit = (data) => url.post('/api/user/withdrawals', data)
+//用户注册
+const register = (data) => url.post('/api/user/register', data)
+//盒子飞出图片
+const boximages = (data) => url.post('/api/index/boximages', data)
+
+//提交晒图
+const setShai = (data) => url.post('/api/index/setShai', data)
+export default {
+	sumuv, //统计uv
+	getVgoods, //假数据
+	lunbobox, //最新开盒轮播
+	send, //发送短信验证码
+	agreement, //协议规则
+	mobilelogin, //手机号登录
+	passwordLogin, //密码登录
+	resetpwd, //重置密码
+	getPayKey, //微信信息
+	wechatLoginRedirect, //微信绑定
+	baseInfo, //首页基本信息
+	categoryList, //分类列表
+	recommend, //推荐盲盒
+	newestOpen, //最新开箱盲盒列表
+	star, //	点赞/取消点赞
+	boxListByCategory, //通过分类查询盲盒列表
+	boxDetail, //盲盒详情
+	hotBox, //热门盲盒
+	cheapBox, //低价专区
+	rechargeList, //充值金额列表
+	openByOrderTrade, //通过支付订单号开箱
+	createRechargeOrder, //创建充值订单
+	getServiceInfo, //获取客服信息
+	getWechatLoginUrl, //微信登录
+	tryBoxDetail, //	试试手气盲盒详情
+	haveATry, //试玩
+	createOrder, //创建订单
+	coinPay, //金币支付
+	cmoneyPay, //余额支付
+	search, //搜索
+	priceRange, //价格区间
+	// 个人中心
+	sList,
+	wechatMpAuth, //获取oppid
+	wechatMpAuthLogin,
+	openRecord, //开箱记录
+	myBox, //我的盒柜
+	myOrderList, //我的订单
+	deliveryOrderDetail, //发货订单详情
+	confirmReceipt, //确认订单
+	userinfo, //用户信息
+	myStar, //我的收藏
+	cancelStar, //删除收藏
+	myBalance, //我的余额
+	myCoin, //我的金币
+	myAddress, //我的收货地址
+	addAddress, //添加收货地址
+	editAddress, //编辑收货地址
+	deleteAddress, //删除收货地址
+	exchange, //一键回收
+	checkDeliveryInfo, //回收信息
+	applyDelivery, //申请发货
+	searchHistory, //搜索历史
+	getSettingInfo, //获取设置信息
+	changeInfo, //修改个人信息
+	moneyToCoin, //余额转出到钱包
+	getWithdrawalSetting, //查询提现绑定信息
+	withdrawal, //申请提现
+	bindWithdrawalAccount, //绑定提现账号
+	bindMobile, //绑定手机号
+	carpassDeposit, //充值卡密
+	carpassList, //卡密充值记录
+	getUserCapital,
+	echargez, //转赠
+	echargezs, //转赠列表
+	getServiceurl, //获取跳转域名
+	goldDeposit, //金币提现
+	register, //注册
+	boximages, //盒子飞出图片
+	upload_image: (params) => {
+		return new Promise((resolve, reject) => {
+			uni.showLoading({
+				title: '文件上传中'
+			})
+			uni.uploadFile({
+				url: url.config.baseURL + '/api/common/upload', //仅为示例,非真实的接口地址
+				filePath: params.path,
+				header: {
+					'token': uni.getStorageSync('token')
+				},
+				name: 'file',
+				success: (uploadFileRes) => {
+					uni.hideLoading()
+					if (JSON.parse(uploadFileRes.data).code === 1) {
+						resolve(JSON.parse(uploadFileRes.data).data);
+					} else {
+						reject(JSON.parse(uploadFileRes.data))
+					}
+
+				}
+			});
+		});
+	},
+
+	getTotalCoin,
+	getRetailList,
+	getTeamList,
+	getTixianList,
+	setShai
+}

+ 19 - 0
http/debounce.js

@@ -0,0 +1,19 @@
+var Debounce = (fn, t) => {
+  let delay = t || 300;
+  let timer;
+  // console.log(fn)
+  // console.log(typeof fn)
+  return function () {
+    let args = arguments;
+    if(timer){
+        clearTimeout(timer);
+    }
+    timer = setTimeout(() => {
+        timer = null;
+        fn.apply(this, args);
+    }, delay);
+  }
+};
+
+
+export default Debounce

+ 142 - 0
http/index.js

@@ -0,0 +1,142 @@
+// import store from '@/store/index.js'
+import Vue from 'vue'
+import {
+	saveUrl,
+	interceptor
+} from '@/utils/loginUtils.js';
+// const bu = 'http://tests.nqr.ds888.work' // 'https://www.bzdzfw.xyz'
+// const  bu = "https://" + window.location.host;
+const bu = "https://chaomd.liuniu946.com";
+var Fly = require("flyio/dist/npm/wx");
+var request = new Fly();
+// #ifdef H5
+if (process.env.NODE_ENV === 'development') {
+	request.config.baseURL = '/api';
+} else {
+	request.config.baseURL = bu;
+}
+// #endif
+// #ifndef H5
+request.config.baseURL = bu;
+// #endif
+// request.config.baseURL = 'http://sl.ysxapp.cn/admin.php/api/';
+
+Vue.prototype.$Fly = request
+Vue.prototype.$url = bu
+const url = bu
+request.interceptors.request.use((request) => {
+	if (request.body && request.body.msg) {
+		uni.showLoading({
+			title: request.body.msg || ''
+		})
+	}
+	const token = uni.getStorageSync('token') || '';
+	request.headers["token"] = token;
+	// request.headers["Content-Type"] = 'application/x-www-form-urlencoded'
+
+	// 防止缓存
+	if (request.method === 'POST') {
+		request.body = {
+			...request.body,
+			// token, // body 添加自定义token
+			// _t: getNowFormatDate()
+		}
+	} else if (request.method === 'GET') {
+		request.params = {
+			// _t: getNowFormatDate(),
+			// token, // body 添加自定义token
+			...request.params
+		}
+	}
+	return request
+})
+
+
+request.interceptors.response.use(function(response) { //不要使用箭头函数,否则调用this.lock()时,this指向不对 
+	uni.hideLoading();
+	let errmsg = '';
+	const data = response.data;
+	if (!data || typeof data !== 'object') {
+		errmsg = '服务器响应格式错误';
+		uni.showToast({
+			title: errmsg,
+			icon: 'none'
+		})
+	} else {
+		let {
+			code,
+			msg
+		} = data
+		if (code == 401 || code == 502) { //判断code 217|218 表示没有用户id,需要重新授权登
+
+
+			uni.showModal({
+				title: "登陆",
+				content: '您未登录!是否马上登录?',
+				success: (e) => {
+					// 判断是否点击确认按钮
+					if (e.confirm) {
+						// 保存当前页面地址
+						saveUrl()
+						// 跳转页面
+						interceptor()
+					}
+				}
+			})
+			uni.setStorageSync('isLogin', 1)
+			return
+		} else if (code == 0 || code == 400) {
+			uni.showToast({
+				title: data.msg,
+				icon: 'none'
+			})
+		}
+
+	}
+	return response.data; //只返回业务数据部分
+}, function(err) {
+	let errmsg = err.message;
+	switch (err.status) {
+		case 0:
+			errmsg = "网络连接错误";
+			uni.showToast({
+				title: errmsg,
+				icon: 'none'
+			})
+			break;
+		case 401:
+			uni.redirectTo({
+				url: '/pages/Login/Login'
+			})
+			break;
+		default:
+			uni.showToast({
+				title: errmsg,
+				icon: 'none'
+			})
+	}
+	return err; //只返回业务数据部分
+})
+
+export default request
+
+// ------------------当前时间格式化 S------------------------------------------
+function getNowFormatDate() {
+	var date = new Date();
+	var seperator1 = "-";
+	var seperator2 = ":";
+	var year = date.getFullYear();
+	var month = date.getMonth() + 1;
+	var strDate = date.getDate();
+	if (month >= 1 && month <= 9) {
+		month = "0" + month;
+	}
+	if (strDate >= 0 && strDate <= 9) {
+		strDate = "0" + strDate;
+	}
+	var currentdate = year + seperator1 + month + seperator1 + strDate +
+		" " + date.getHours() + seperator2 + date.getMinutes() +
+		seperator2 + date.getSeconds();
+	return currentdate;
+}
+// ------------------当前时间格式化 E------------------------------------------

File diff suppressed because it is too large
+ 0 - 0
js_sdk/index.js


+ 15 - 0
main.js

@@ -0,0 +1,15 @@
+import Vue from 'vue'
+import App from './App'
+import http from './http/index.js'
+import api from 'http/api.js'
+Vue.config.productionTip = false
+
+App.mpType = 'app'
+Vue.prototype.$api = api
+Vue.prototype.$http = http
+Vue.prototype.$imgDomain = 'http://manghe4.tuana.cn/'
+
+const app = new Vue({
+    ...App
+})
+app.$mount()

+ 103 - 0
manifest.json

@@ -0,0 +1,103 @@
+{
+    "name" : "潮盲岛",
+    "appid" : "__UNI__599E393",
+    "description" : "",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueStyleCompiler" : "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.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.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<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.FLASHLIGHT\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ]
+            },
+            /* ios打包配置 */
+            "ios" : {},
+            /* SDK配置 */
+            "sdkConfigs" : {
+                "ad" : {}
+            },
+            "splashscreen" : {
+                "androidStyle" : "common"
+            }
+        },
+        "nvueCompiler" : "uni-app",
+        "renderer" : "native"
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "wx4adbb64583a37087",
+        "setting" : {
+            "urlCheck" : false
+        },
+        "usingComponents" : true
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    },
+    "h5" : {
+        "devServer" : {
+            "port" : 8080,
+            "disableHostCheck" : true,
+            "proxy" : {
+                "/api" : {
+                    // "target" : "http://mh.h5mhsc.cn/",
+                    // "target" : "https://he.qiandaojie.net",
+                    // "target" : "http://mh1.qiandaojie.net",
+                    "target" : "http://chaomd.liuniu946.com/",
+                    "changeOrigin" : true,
+                    "secure" : false,
+                    "pathRewrite" : {
+                        "/api" : ""
+                    }
+                }
+            },
+            "https" : false
+        },
+        "router" : {
+            "base" : "/h5/"
+        },
+        "title" : "潮盲岛",
+        "domain" : "http://chaomd.liuniu946.com/"
+    }
+}

+ 47 - 0
node_modules/lodash/LICENSE

@@ -0,0 +1,47 @@
+Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
+
+Based on Underscore.js, copyright Jeremy Ashkenas,
+DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
+
+This software consists of voluntary contributions made by many
+individuals. For exact contribution history, see the revision history
+available at https://github.com/lodash/lodash
+
+The following license applies to all parts of this software except as
+documented below:
+
+====
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+====
+
+Copyright and related rights for sample code are waived via CC0. Sample
+code is defined as all source code displayed within the prose of the
+documentation.
+
+CC0: http://creativecommons.org/publicdomain/zero/1.0/
+
+====
+
+Files located in the node_modules and vendor directories are externally
+maintained libraries used by this software which have their own
+licenses; we recommend you read them, as their terms may differ from the
+terms above.

+ 39 - 0
node_modules/lodash/README.md

@@ -0,0 +1,39 @@
+# lodash v4.17.21
+
+The [Lodash](https://lodash.com/) library exported as [Node.js](https://nodejs.org/) modules.
+
+## Installation
+
+Using npm:
+```shell
+$ npm i -g npm
+$ npm i --save lodash
+```
+
+In Node.js:
+```js
+// Load the full build.
+var _ = require('lodash');
+// Load the core build.
+var _ = require('lodash/core');
+// Load the FP build for immutable auto-curried iteratee-first data-last methods.
+var fp = require('lodash/fp');
+
+// Load method categories.
+var array = require('lodash/array');
+var object = require('lodash/fp/object');
+
+// Cherry-pick methods for smaller browserify/rollup/webpack bundles.
+var at = require('lodash/at');
+var curryN = require('lodash/fp/curryN');
+```
+
+See the [package source](https://github.com/lodash/lodash/tree/4.17.21-npm) for more details.
+
+**Note:**<br>
+Install [n_](https://www.npmjs.com/package/n_) for Lodash use in the Node.js < 6 REPL.
+
+## Support
+
+Tested in Chrome 74-75, Firefox 66-67, IE 11, Edge 18, Safari 11-12, & Node.js 8-12.<br>
+Automated [browser](https://saucelabs.com/u/lodash) & [CI](https://travis-ci.org/lodash/lodash/) test runs are available.

+ 7 - 0
node_modules/lodash/_DataView.js

@@ -0,0 +1,7 @@
+var getNative = require('./_getNative'),
+    root = require('./_root');
+
+/* Built-in method references that are verified to be native. */
+var DataView = getNative(root, 'DataView');
+
+module.exports = DataView;

+ 32 - 0
node_modules/lodash/_Hash.js

@@ -0,0 +1,32 @@
+var hashClear = require('./_hashClear'),
+    hashDelete = require('./_hashDelete'),
+    hashGet = require('./_hashGet'),
+    hashHas = require('./_hashHas'),
+    hashSet = require('./_hashSet');
+
+/**
+ * Creates a hash object.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+function Hash(entries) {
+  var index = -1,
+      length = entries == null ? 0 : entries.length;
+
+  this.clear();
+  while (++index < length) {
+    var entry = entries[index];
+    this.set(entry[0], entry[1]);
+  }
+}
+
+// Add methods to `Hash`.
+Hash.prototype.clear = hashClear;
+Hash.prototype['delete'] = hashDelete;
+Hash.prototype.get = hashGet;
+Hash.prototype.has = hashHas;
+Hash.prototype.set = hashSet;
+
+module.exports = Hash;

+ 28 - 0
node_modules/lodash/_LazyWrapper.js

@@ -0,0 +1,28 @@
+var baseCreate = require('./_baseCreate'),
+    baseLodash = require('./_baseLodash');
+
+/** Used as references for the maximum length and index of an array. */
+var MAX_ARRAY_LENGTH = 4294967295;
+
+/**
+ * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.
+ *
+ * @private
+ * @constructor
+ * @param {*} value The value to wrap.
+ */
+function LazyWrapper(value) {
+  this.__wrapped__ = value;
+  this.__actions__ = [];
+  this.__dir__ = 1;
+  this.__filtered__ = false;
+  this.__iteratees__ = [];
+  this.__takeCount__ = MAX_ARRAY_LENGTH;
+  this.__views__ = [];
+}
+
+// Ensure `LazyWrapper` is an instance of `baseLodash`.
+LazyWrapper.prototype = baseCreate(baseLodash.prototype);
+LazyWrapper.prototype.constructor = LazyWrapper;
+
+module.exports = LazyWrapper;

+ 32 - 0
node_modules/lodash/_ListCache.js

@@ -0,0 +1,32 @@
+var listCacheClear = require('./_listCacheClear'),
+    listCacheDelete = require('./_listCacheDelete'),
+    listCacheGet = require('./_listCacheGet'),
+    listCacheHas = require('./_listCacheHas'),
+    listCacheSet = require('./_listCacheSet');
+
+/**
+ * Creates an list cache object.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+function ListCache(entries) {
+  var index = -1,
+      length = entries == null ? 0 : entries.length;
+
+  this.clear();
+  while (++index < length) {
+    var entry = entries[index];
+    this.set(entry[0], entry[1]);
+  }
+}
+
+// Add methods to `ListCache`.
+ListCache.prototype.clear = listCacheClear;
+ListCache.prototype['delete'] = listCacheDelete;
+ListCache.prototype.get = listCacheGet;
+ListCache.prototype.has = listCacheHas;
+ListCache.prototype.set = listCacheSet;
+
+module.exports = ListCache;

+ 22 - 0
node_modules/lodash/_LodashWrapper.js

@@ -0,0 +1,22 @@
+var baseCreate = require('./_baseCreate'),
+    baseLodash = require('./_baseLodash');
+
+/**
+ * The base constructor for creating `lodash` wrapper objects.
+ *
+ * @private
+ * @param {*} value The value to wrap.
+ * @param {boolean} [chainAll] Enable explicit method chain sequences.
+ */
+function LodashWrapper(value, chainAll) {
+  this.__wrapped__ = value;
+  this.__actions__ = [];
+  this.__chain__ = !!chainAll;
+  this.__index__ = 0;
+  this.__values__ = undefined;
+}
+
+LodashWrapper.prototype = baseCreate(baseLodash.prototype);
+LodashWrapper.prototype.constructor = LodashWrapper;
+
+module.exports = LodashWrapper;

+ 7 - 0
node_modules/lodash/_Map.js

@@ -0,0 +1,7 @@
+var getNative = require('./_getNative'),
+    root = require('./_root');
+
+/* Built-in method references that are verified to be native. */
+var Map = getNative(root, 'Map');
+
+module.exports = Map;

+ 32 - 0
node_modules/lodash/_MapCache.js

@@ -0,0 +1,32 @@
+var mapCacheClear = require('./_mapCacheClear'),
+    mapCacheDelete = require('./_mapCacheDelete'),
+    mapCacheGet = require('./_mapCacheGet'),
+    mapCacheHas = require('./_mapCacheHas'),
+    mapCacheSet = require('./_mapCacheSet');
+
+/**
+ * Creates a map cache object to store key-value pairs.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+function MapCache(entries) {
+  var index = -1,
+      length = entries == null ? 0 : entries.length;
+
+  this.clear();
+  while (++index < length) {
+    var entry = entries[index];
+    this.set(entry[0], entry[1]);
+  }
+}
+
+// Add methods to `MapCache`.
+MapCache.prototype.clear = mapCacheClear;
+MapCache.prototype['delete'] = mapCacheDelete;
+MapCache.prototype.get = mapCacheGet;
+MapCache.prototype.has = mapCacheHas;
+MapCache.prototype.set = mapCacheSet;
+
+module.exports = MapCache;

+ 7 - 0
node_modules/lodash/_Promise.js

@@ -0,0 +1,7 @@
+var getNative = require('./_getNative'),
+    root = require('./_root');
+
+/* Built-in method references that are verified to be native. */
+var Promise = getNative(root, 'Promise');
+
+module.exports = Promise;

+ 7 - 0
node_modules/lodash/_Set.js

@@ -0,0 +1,7 @@
+var getNative = require('./_getNative'),
+    root = require('./_root');
+
+/* Built-in method references that are verified to be native. */
+var Set = getNative(root, 'Set');
+
+module.exports = Set;

+ 27 - 0
node_modules/lodash/_SetCache.js

@@ -0,0 +1,27 @@
+var MapCache = require('./_MapCache'),
+    setCacheAdd = require('./_setCacheAdd'),
+    setCacheHas = require('./_setCacheHas');
+
+/**
+ *
+ * Creates an array cache object to store unique values.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [values] The values to cache.
+ */
+function SetCache(values) {
+  var index = -1,
+      length = values == null ? 0 : values.length;
+
+  this.__data__ = new MapCache;
+  while (++index < length) {
+    this.add(values[index]);
+  }
+}
+
+// Add methods to `SetCache`.
+SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;
+SetCache.prototype.has = setCacheHas;
+
+module.exports = SetCache;

+ 27 - 0
node_modules/lodash/_Stack.js

@@ -0,0 +1,27 @@
+var ListCache = require('./_ListCache'),
+    stackClear = require('./_stackClear'),
+    stackDelete = require('./_stackDelete'),
+    stackGet = require('./_stackGet'),
+    stackHas = require('./_stackHas'),
+    stackSet = require('./_stackSet');
+
+/**
+ * Creates a stack cache object to store key-value pairs.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+function Stack(entries) {
+  var data = this.__data__ = new ListCache(entries);
+  this.size = data.size;
+}
+
+// Add methods to `Stack`.
+Stack.prototype.clear = stackClear;
+Stack.prototype['delete'] = stackDelete;
+Stack.prototype.get = stackGet;
+Stack.prototype.has = stackHas;
+Stack.prototype.set = stackSet;
+
+module.exports = Stack;

+ 6 - 0
node_modules/lodash/_Symbol.js

@@ -0,0 +1,6 @@
+var root = require('./_root');
+
+/** Built-in value references. */
+var Symbol = root.Symbol;
+
+module.exports = Symbol;

+ 6 - 0
node_modules/lodash/_Uint8Array.js

@@ -0,0 +1,6 @@
+var root = require('./_root');
+
+/** Built-in value references. */
+var Uint8Array = root.Uint8Array;
+
+module.exports = Uint8Array;

+ 7 - 0
node_modules/lodash/_WeakMap.js

@@ -0,0 +1,7 @@
+var getNative = require('./_getNative'),
+    root = require('./_root');
+
+/* Built-in method references that are verified to be native. */
+var WeakMap = getNative(root, 'WeakMap');
+
+module.exports = WeakMap;

+ 21 - 0
node_modules/lodash/_apply.js

@@ -0,0 +1,21 @@
+/**
+ * A faster alternative to `Function#apply`, this function invokes `func`
+ * with the `this` binding of `thisArg` and the arguments of `args`.
+ *
+ * @private
+ * @param {Function} func The function to invoke.
+ * @param {*} thisArg The `this` binding of `func`.
+ * @param {Array} args The arguments to invoke `func` with.
+ * @returns {*} Returns the result of `func`.
+ */
+function apply(func, thisArg, args) {
+  switch (args.length) {
+    case 0: return func.call(thisArg);
+    case 1: return func.call(thisArg, args[0]);
+    case 2: return func.call(thisArg, args[0], args[1]);
+    case 3: return func.call(thisArg, args[0], args[1], args[2]);
+  }
+  return func.apply(thisArg, args);
+}
+
+module.exports = apply;

+ 22 - 0
node_modules/lodash/_arrayAggregator.js

@@ -0,0 +1,22 @@
+/**
+ * A specialized version of `baseAggregator` for arrays.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} setter The function to set `accumulator` values.
+ * @param {Function} iteratee The iteratee to transform keys.
+ * @param {Object} accumulator The initial aggregated object.
+ * @returns {Function} Returns `accumulator`.
+ */
+function arrayAggregator(array, setter, iteratee, accumulator) {
+  var index = -1,
+      length = array == null ? 0 : array.length;
+
+  while (++index < length) {
+    var value = array[index];
+    setter(accumulator, value, iteratee(value), array);
+  }
+  return accumulator;
+}
+
+module.exports = arrayAggregator;

+ 22 - 0
node_modules/lodash/_arrayEach.js

@@ -0,0 +1,22 @@
+/**
+ * A specialized version of `_.forEach` for arrays without support for
+ * iteratee shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns `array`.
+ */
+function arrayEach(array, iteratee) {
+  var index = -1,
+      length = array == null ? 0 : array.length;
+
+  while (++index < length) {
+    if (iteratee(array[index], index, array) === false) {
+      break;
+    }
+  }
+  return array;
+}
+
+module.exports = arrayEach;

+ 21 - 0
node_modules/lodash/_arrayEachRight.js

@@ -0,0 +1,21 @@
+/**
+ * A specialized version of `_.forEachRight` for arrays without support for
+ * iteratee shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns `array`.
+ */
+function arrayEachRight(array, iteratee) {
+  var length = array == null ? 0 : array.length;
+
+  while (length--) {
+    if (iteratee(array[length], length, array) === false) {
+      break;
+    }
+  }
+  return array;
+}
+
+module.exports = arrayEachRight;

+ 23 - 0
node_modules/lodash/_arrayEvery.js

@@ -0,0 +1,23 @@
+/**
+ * A specialized version of `_.every` for arrays without support for
+ * iteratee shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {boolean} Returns `true` if all elements pass the predicate check,
+ *  else `false`.
+ */
+function arrayEvery(array, predicate) {
+  var index = -1,
+      length = array == null ? 0 : array.length;
+
+  while (++index < length) {
+    if (!predicate(array[index], index, array)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+module.exports = arrayEvery;

+ 25 - 0
node_modules/lodash/_arrayFilter.js

@@ -0,0 +1,25 @@
+/**
+ * A specialized version of `_.filter` for arrays without support for
+ * iteratee shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {Array} Returns the new filtered array.
+ */
+function arrayFilter(array, predicate) {
+  var index = -1,
+      length = array == null ? 0 : array.length,
+      resIndex = 0,
+      result = [];
+
+  while (++index < length) {
+    var value = array[index];
+    if (predicate(value, index, array)) {
+      result[resIndex++] = value;
+    }
+  }
+  return result;
+}
+
+module.exports = arrayFilter;

+ 17 - 0
node_modules/lodash/_arrayIncludes.js

@@ -0,0 +1,17 @@
+var baseIndexOf = require('./_baseIndexOf');
+
+/**
+ * A specialized version of `_.includes` for arrays without support for
+ * specifying an index to search from.
+ *
+ * @private
+ * @param {Array} [array] The array to inspect.
+ * @param {*} target The value to search for.
+ * @returns {boolean} Returns `true` if `target` is found, else `false`.
+ */
+function arrayIncludes(array, value) {
+  var length = array == null ? 0 : array.length;
+  return !!length && baseIndexOf(array, value, 0) > -1;
+}
+
+module.exports = arrayIncludes;

+ 22 - 0
node_modules/lodash/_arrayIncludesWith.js

@@ -0,0 +1,22 @@
+/**
+ * This function is like `arrayIncludes` except that it accepts a comparator.
+ *
+ * @private
+ * @param {Array} [array] The array to inspect.
+ * @param {*} target The value to search for.
+ * @param {Function} comparator The comparator invoked per element.
+ * @returns {boolean} Returns `true` if `target` is found, else `false`.
+ */
+function arrayIncludesWith(array, value, comparator) {
+  var index = -1,
+      length = array == null ? 0 : array.length;
+
+  while (++index < length) {
+    if (comparator(value, array[index])) {
+      return true;
+    }
+  }
+  return false;
+}
+
+module.exports = arrayIncludesWith;

+ 49 - 0
node_modules/lodash/_arrayLikeKeys.js

@@ -0,0 +1,49 @@
+var baseTimes = require('./_baseTimes'),
+    isArguments = require('./isArguments'),
+    isArray = require('./isArray'),
+    isBuffer = require('./isBuffer'),
+    isIndex = require('./_isIndex'),
+    isTypedArray = require('./isTypedArray');
+
+/** Used for built-in method references. */
+var objectProto = Object.prototype;
+
+/** Used to check objects for own properties. */
+var hasOwnProperty = objectProto.hasOwnProperty;
+
+/**
+ * Creates an array of the enumerable property names of the array-like `value`.
+ *
+ * @private
+ * @param {*} value The value to query.
+ * @param {boolean} inherited Specify returning inherited property names.
+ * @returns {Array} Returns the array of property names.
+ */
+function arrayLikeKeys(value, inherited) {
+  var isArr = isArray(value),
+      isArg = !isArr && isArguments(value),
+      isBuff = !isArr && !isArg && isBuffer(value),
+      isType = !isArr && !isArg && !isBuff && isTypedArray(value),
+      skipIndexes = isArr || isArg || isBuff || isType,
+      result = skipIndexes ? baseTimes(value.length, String) : [],
+      length = result.length;
+
+  for (var key in value) {
+    if ((inherited || hasOwnProperty.call(value, key)) &&
+        !(skipIndexes && (
+           // Safari 9 has enumerable `arguments.length` in strict mode.
+           key == 'length' ||
+           // Node.js 0.10 has enumerable non-index properties on buffers.
+           (isBuff && (key == 'offset' || key == 'parent')) ||
+           // PhantomJS 2 has enumerable non-index properties on typed arrays.
+           (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) ||
+           // Skip index properties.
+           isIndex(key, length)
+        ))) {
+      result.push(key);
+    }
+  }
+  return result;
+}
+
+module.exports = arrayLikeKeys;

+ 21 - 0
node_modules/lodash/_arrayMap.js

@@ -0,0 +1,21 @@
+/**
+ * A specialized version of `_.map` for arrays without support for iteratee
+ * shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns the new mapped array.
+ */
+function arrayMap(array, iteratee) {
+  var index = -1,
+      length = array == null ? 0 : array.length,
+      result = Array(length);
+
+  while (++index < length) {
+    result[index] = iteratee(array[index], index, array);
+  }
+  return result;
+}
+
+module.exports = arrayMap;

+ 20 - 0
node_modules/lodash/_arrayPush.js

@@ -0,0 +1,20 @@
+/**
+ * Appends the elements of `values` to `array`.
+ *
+ * @private
+ * @param {Array} array The array to modify.
+ * @param {Array} values The values to append.
+ * @returns {Array} Returns `array`.
+ */
+function arrayPush(array, values) {
+  var index = -1,
+      length = values.length,
+      offset = array.length;
+
+  while (++index < length) {
+    array[offset + index] = values[index];
+  }
+  return array;
+}
+
+module.exports = arrayPush;

+ 26 - 0
node_modules/lodash/_arrayReduce.js

@@ -0,0 +1,26 @@
+/**
+ * A specialized version of `_.reduce` for arrays without support for
+ * iteratee shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {*} [accumulator] The initial value.
+ * @param {boolean} [initAccum] Specify using the first element of `array` as
+ *  the initial value.
+ * @returns {*} Returns the accumulated value.
+ */
+function arrayReduce(array, iteratee, accumulator, initAccum) {
+  var index = -1,
+      length = array == null ? 0 : array.length;
+
+  if (initAccum && length) {
+    accumulator = array[++index];
+  }
+  while (++index < length) {
+    accumulator = iteratee(accumulator, array[index], index, array);
+  }
+  return accumulator;
+}
+
+module.exports = arrayReduce;

+ 24 - 0
node_modules/lodash/_arrayReduceRight.js

@@ -0,0 +1,24 @@
+/**
+ * A specialized version of `_.reduceRight` for arrays without support for
+ * iteratee shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {*} [accumulator] The initial value.
+ * @param {boolean} [initAccum] Specify using the last element of `array` as
+ *  the initial value.
+ * @returns {*} Returns the accumulated value.
+ */
+function arrayReduceRight(array, iteratee, accumulator, initAccum) {
+  var length = array == null ? 0 : array.length;
+  if (initAccum && length) {
+    accumulator = array[--length];
+  }
+  while (length--) {
+    accumulator = iteratee(accumulator, array[length], length, array);
+  }
+  return accumulator;
+}
+
+module.exports = arrayReduceRight;

+ 15 - 0
node_modules/lodash/_arraySample.js

@@ -0,0 +1,15 @@
+var baseRandom = require('./_baseRandom');
+
+/**
+ * A specialized version of `_.sample` for arrays.
+ *
+ * @private
+ * @param {Array} array The array to sample.
+ * @returns {*} Returns the random element.
+ */
+function arraySample(array) {
+  var length = array.length;
+  return length ? array[baseRandom(0, length - 1)] : undefined;
+}
+
+module.exports = arraySample;

+ 17 - 0
node_modules/lodash/_arraySampleSize.js

@@ -0,0 +1,17 @@
+var baseClamp = require('./_baseClamp'),
+    copyArray = require('./_copyArray'),
+    shuffleSelf = require('./_shuffleSelf');
+
+/**
+ * A specialized version of `_.sampleSize` for arrays.
+ *
+ * @private
+ * @param {Array} array The array to sample.
+ * @param {number} n The number of elements to sample.
+ * @returns {Array} Returns the random elements.
+ */
+function arraySampleSize(array, n) {
+  return shuffleSelf(copyArray(array), baseClamp(n, 0, array.length));
+}
+
+module.exports = arraySampleSize;

+ 15 - 0
node_modules/lodash/_arrayShuffle.js

@@ -0,0 +1,15 @@
+var copyArray = require('./_copyArray'),
+    shuffleSelf = require('./_shuffleSelf');
+
+/**
+ * A specialized version of `_.shuffle` for arrays.
+ *
+ * @private
+ * @param {Array} array The array to shuffle.
+ * @returns {Array} Returns the new shuffled array.
+ */
+function arrayShuffle(array) {
+  return shuffleSelf(copyArray(array));
+}
+
+module.exports = arrayShuffle;

+ 23 - 0
node_modules/lodash/_arraySome.js

@@ -0,0 +1,23 @@
+/**
+ * A specialized version of `_.some` for arrays without support for iteratee
+ * shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {boolean} Returns `true` if any element passes the predicate check,
+ *  else `false`.
+ */
+function arraySome(array, predicate) {
+  var index = -1,
+      length = array == null ? 0 : array.length;
+
+  while (++index < length) {
+    if (predicate(array[index], index, array)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+module.exports = arraySome;

+ 12 - 0
node_modules/lodash/_asciiSize.js

@@ -0,0 +1,12 @@
+var baseProperty = require('./_baseProperty');
+
+/**
+ * Gets the size of an ASCII `string`.
+ *
+ * @private
+ * @param {string} string The string inspect.
+ * @returns {number} Returns the string size.
+ */
+var asciiSize = baseProperty('length');
+
+module.exports = asciiSize;

+ 12 - 0
node_modules/lodash/_asciiToArray.js

@@ -0,0 +1,12 @@
+/**
+ * Converts an ASCII `string` to an array.
+ *
+ * @private
+ * @param {string} string The string to convert.
+ * @returns {Array} Returns the converted array.
+ */
+function asciiToArray(string) {
+  return string.split('');
+}
+
+module.exports = asciiToArray;

+ 15 - 0
node_modules/lodash/_asciiWords.js

@@ -0,0 +1,15 @@
+/** Used to match words composed of alphanumeric characters. */
+var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;
+
+/**
+ * Splits an ASCII `string` into an array of its words.
+ *
+ * @private
+ * @param {string} The string to inspect.
+ * @returns {Array} Returns the words of `string`.
+ */
+function asciiWords(string) {
+  return string.match(reAsciiWord) || [];
+}
+
+module.exports = asciiWords;

+ 20 - 0
node_modules/lodash/_assignMergeValue.js

@@ -0,0 +1,20 @@
+var baseAssignValue = require('./_baseAssignValue'),
+    eq = require('./eq');
+
+/**
+ * This function is like `assignValue` except that it doesn't assign
+ * `undefined` values.
+ *
+ * @private
+ * @param {Object} object The object to modify.
+ * @param {string} key The key of the property to assign.
+ * @param {*} value The value to assign.
+ */
+function assignMergeValue(object, key, value) {
+  if ((value !== undefined && !eq(object[key], value)) ||
+      (value === undefined && !(key in object))) {
+    baseAssignValue(object, key, value);
+  }
+}
+
+module.exports = assignMergeValue;

+ 28 - 0
node_modules/lodash/_assignValue.js

@@ -0,0 +1,28 @@
+var baseAssignValue = require('./_baseAssignValue'),
+    eq = require('./eq');
+
+/** Used for built-in method references. */
+var objectProto = Object.prototype;
+
+/** Used to check objects for own properties. */
+var hasOwnProperty = objectProto.hasOwnProperty;
+
+/**
+ * Assigns `value` to `key` of `object` if the existing value is not equivalent
+ * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons.
+ *
+ * @private
+ * @param {Object} object The object to modify.
+ * @param {string} key The key of the property to assign.
+ * @param {*} value The value to assign.
+ */
+function assignValue(object, key, value) {
+  var objValue = object[key];
+  if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||
+      (value === undefined && !(key in object))) {
+    baseAssignValue(object, key, value);
+  }
+}
+
+module.exports = assignValue;

+ 21 - 0
node_modules/lodash/_assocIndexOf.js

@@ -0,0 +1,21 @@
+var eq = require('./eq');
+
+/**
+ * Gets the index at which the `key` is found in `array` of key-value pairs.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} key The key to search for.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+function assocIndexOf(array, key) {
+  var length = array.length;
+  while (length--) {
+    if (eq(array[length][0], key)) {
+      return length;
+    }
+  }
+  return -1;
+}
+
+module.exports = assocIndexOf;

+ 21 - 0
node_modules/lodash/_baseAggregator.js

@@ -0,0 +1,21 @@
+var baseEach = require('./_baseEach');
+
+/**
+ * Aggregates elements of `collection` on `accumulator` with keys transformed
+ * by `iteratee` and values set by `setter`.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} setter The function to set `accumulator` values.
+ * @param {Function} iteratee The iteratee to transform keys.
+ * @param {Object} accumulator The initial aggregated object.
+ * @returns {Function} Returns `accumulator`.
+ */
+function baseAggregator(collection, setter, iteratee, accumulator) {
+  baseEach(collection, function(value, key, collection) {
+    setter(accumulator, value, iteratee(value), collection);
+  });
+  return accumulator;
+}
+
+module.exports = baseAggregator;

+ 17 - 0
node_modules/lodash/_baseAssign.js

@@ -0,0 +1,17 @@
+var copyObject = require('./_copyObject'),
+    keys = require('./keys');
+
+/**
+ * The base implementation of `_.assign` without support for multiple sources
+ * or `customizer` functions.
+ *
+ * @private
+ * @param {Object} object The destination object.
+ * @param {Object} source The source object.
+ * @returns {Object} Returns `object`.
+ */
+function baseAssign(object, source) {
+  return object && copyObject(source, keys(source), object);
+}
+
+module.exports = baseAssign;

+ 17 - 0
node_modules/lodash/_baseAssignIn.js

@@ -0,0 +1,17 @@
+var copyObject = require('./_copyObject'),
+    keysIn = require('./keysIn');
+
+/**
+ * The base implementation of `_.assignIn` without support for multiple sources
+ * or `customizer` functions.
+ *
+ * @private
+ * @param {Object} object The destination object.
+ * @param {Object} source The source object.
+ * @returns {Object} Returns `object`.
+ */
+function baseAssignIn(object, source) {
+  return object && copyObject(source, keysIn(source), object);
+}
+
+module.exports = baseAssignIn;

+ 25 - 0
node_modules/lodash/_baseAssignValue.js

@@ -0,0 +1,25 @@
+var defineProperty = require('./_defineProperty');
+
+/**
+ * The base implementation of `assignValue` and `assignMergeValue` without
+ * value checks.
+ *
+ * @private
+ * @param {Object} object The object to modify.
+ * @param {string} key The key of the property to assign.
+ * @param {*} value The value to assign.
+ */
+function baseAssignValue(object, key, value) {
+  if (key == '__proto__' && defineProperty) {
+    defineProperty(object, key, {
+      'configurable': true,
+      'enumerable': true,
+      'value': value,
+      'writable': true
+    });
+  } else {
+    object[key] = value;
+  }
+}
+
+module.exports = baseAssignValue;

+ 23 - 0
node_modules/lodash/_baseAt.js

@@ -0,0 +1,23 @@
+var get = require('./get');
+
+/**
+ * The base implementation of `_.at` without support for individual paths.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {string[]} paths The property paths to pick.
+ * @returns {Array} Returns the picked elements.
+ */
+function baseAt(object, paths) {
+  var index = -1,
+      length = paths.length,
+      result = Array(length),
+      skip = object == null;
+
+  while (++index < length) {
+    result[index] = skip ? undefined : get(object, paths[index]);
+  }
+  return result;
+}
+
+module.exports = baseAt;

+ 22 - 0
node_modules/lodash/_baseClamp.js

@@ -0,0 +1,22 @@
+/**
+ * The base implementation of `_.clamp` which doesn't coerce arguments.
+ *
+ * @private
+ * @param {number} number The number to clamp.
+ * @param {number} [lower] The lower bound.
+ * @param {number} upper The upper bound.
+ * @returns {number} Returns the clamped number.
+ */
+function baseClamp(number, lower, upper) {
+  if (number === number) {
+    if (upper !== undefined) {
+      number = number <= upper ? number : upper;
+    }
+    if (lower !== undefined) {
+      number = number >= lower ? number : lower;
+    }
+  }
+  return number;
+}
+
+module.exports = baseClamp;

+ 166 - 0
node_modules/lodash/_baseClone.js

@@ -0,0 +1,166 @@
+var Stack = require('./_Stack'),
+    arrayEach = require('./_arrayEach'),
+    assignValue = require('./_assignValue'),
+    baseAssign = require('./_baseAssign'),
+    baseAssignIn = require('./_baseAssignIn'),
+    cloneBuffer = require('./_cloneBuffer'),
+    copyArray = require('./_copyArray'),
+    copySymbols = require('./_copySymbols'),
+    copySymbolsIn = require('./_copySymbolsIn'),
+    getAllKeys = require('./_getAllKeys'),
+    getAllKeysIn = require('./_getAllKeysIn'),
+    getTag = require('./_getTag'),
+    initCloneArray = require('./_initCloneArray'),
+    initCloneByTag = require('./_initCloneByTag'),
+    initCloneObject = require('./_initCloneObject'),
+    isArray = require('./isArray'),
+    isBuffer = require('./isBuffer'),
+    isMap = require('./isMap'),
+    isObject = require('./isObject'),
+    isSet = require('./isSet'),
+    keys = require('./keys'),
+    keysIn = require('./keysIn');
+
+/** Used to compose bitmasks for cloning. */
+var CLONE_DEEP_FLAG = 1,
+    CLONE_FLAT_FLAG = 2,
+    CLONE_SYMBOLS_FLAG = 4;
+
+/** `Object#toString` result references. */
+var argsTag = '[object Arguments]',
+    arrayTag = '[object Array]',
+    boolTag = '[object Boolean]',
+    dateTag = '[object Date]',
+    errorTag = '[object Error]',
+    funcTag = '[object Function]',
+    genTag = '[object GeneratorFunction]',
+    mapTag = '[object Map]',
+    numberTag = '[object Number]',
+    objectTag = '[object Object]',
+    regexpTag = '[object RegExp]',
+    setTag = '[object Set]',
+    stringTag = '[object String]',
+    symbolTag = '[object Symbol]',
+    weakMapTag = '[object WeakMap]';
+
+var arrayBufferTag = '[object ArrayBuffer]',
+    dataViewTag = '[object DataView]',
+    float32Tag = '[object Float32Array]',
+    float64Tag = '[object Float64Array]',
+    int8Tag = '[object Int8Array]',
+    int16Tag = '[object Int16Array]',
+    int32Tag = '[object Int32Array]',
+    uint8Tag = '[object Uint8Array]',
+    uint8ClampedTag = '[object Uint8ClampedArray]',
+    uint16Tag = '[object Uint16Array]',
+    uint32Tag = '[object Uint32Array]';
+
+/** Used to identify `toStringTag` values supported by `_.clone`. */
+var cloneableTags = {};
+cloneableTags[argsTag] = cloneableTags[arrayTag] =
+cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =
+cloneableTags[boolTag] = cloneableTags[dateTag] =
+cloneableTags[float32Tag] = cloneableTags[float64Tag] =
+cloneableTags[int8Tag] = cloneableTags[int16Tag] =
+cloneableTags[int32Tag] = cloneableTags[mapTag] =
+cloneableTags[numberTag] = cloneableTags[objectTag] =
+cloneableTags[regexpTag] = cloneableTags[setTag] =
+cloneableTags[stringTag] = cloneableTags[symbolTag] =
+cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
+cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
+cloneableTags[errorTag] = cloneableTags[funcTag] =
+cloneableTags[weakMapTag] = false;
+
+/**
+ * The base implementation of `_.clone` and `_.cloneDeep` which tracks
+ * traversed objects.
+ *
+ * @private
+ * @param {*} value The value to clone.
+ * @param {boolean} bitmask The bitmask flags.
+ *  1 - Deep clone
+ *  2 - Flatten inherited properties
+ *  4 - Clone symbols
+ * @param {Function} [customizer] The function to customize cloning.
+ * @param {string} [key] The key of `value`.
+ * @param {Object} [object] The parent object of `value`.
+ * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
+ * @returns {*} Returns the cloned value.
+ */
+function baseClone(value, bitmask, customizer, key, object, stack) {
+  var result,
+      isDeep = bitmask & CLONE_DEEP_FLAG,
+      isFlat = bitmask & CLONE_FLAT_FLAG,
+      isFull = bitmask & CLONE_SYMBOLS_FLAG;
+
+  if (customizer) {
+    result = object ? customizer(value, key, object, stack) : customizer(value);
+  }
+  if (result !== undefined) {
+    return result;
+  }
+  if (!isObject(value)) {
+    return value;
+  }
+  var isArr = isArray(value);
+  if (isArr) {
+    result = initCloneArray(value);
+    if (!isDeep) {
+      return copyArray(value, result);
+    }
+  } else {
+    var tag = getTag(value),
+        isFunc = tag == funcTag || tag == genTag;
+
+    if (isBuffer(value)) {
+      return cloneBuffer(value, isDeep);
+    }
+    if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
+      result = (isFlat || isFunc) ? {} : initCloneObject(value);
+      if (!isDeep) {
+        return isFlat
+          ? copySymbolsIn(value, baseAssignIn(result, value))
+          : copySymbols(value, baseAssign(result, value));
+      }
+    } else {
+      if (!cloneableTags[tag]) {
+        return object ? value : {};
+      }
+      result = initCloneByTag(value, tag, isDeep);
+    }
+  }
+  // Check for circular references and return its corresponding clone.
+  stack || (stack = new Stack);
+  var stacked = stack.get(value);
+  if (stacked) {
+    return stacked;
+  }
+  stack.set(value, result);
+
+  if (isSet(value)) {
+    value.forEach(function(subValue) {
+      result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack));
+    });
+  } else if (isMap(value)) {
+    value.forEach(function(subValue, key) {
+      result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack));
+    });
+  }
+
+  var keysFunc = isFull
+    ? (isFlat ? getAllKeysIn : getAllKeys)
+    : (isFlat ? keysIn : keys);
+
+  var props = isArr ? undefined : keysFunc(value);
+  arrayEach(props || value, function(subValue, key) {
+    if (props) {
+      key = subValue;
+      subValue = value[key];
+    }
+    // Recursively populate clone (susceptible to call stack limits).
+    assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack));
+  });
+  return result;
+}
+
+module.exports = baseClone;

+ 18 - 0
node_modules/lodash/_baseConforms.js

@@ -0,0 +1,18 @@
+var baseConformsTo = require('./_baseConformsTo'),
+    keys = require('./keys');
+
+/**
+ * The base implementation of `_.conforms` which doesn't clone `source`.
+ *
+ * @private
+ * @param {Object} source The object of property predicates to conform to.
+ * @returns {Function} Returns the new spec function.
+ */
+function baseConforms(source) {
+  var props = keys(source);
+  return function(object) {
+    return baseConformsTo(object, source, props);
+  };
+}
+
+module.exports = baseConforms;

+ 27 - 0
node_modules/lodash/_baseConformsTo.js

@@ -0,0 +1,27 @@
+/**
+ * The base implementation of `_.conformsTo` which accepts `props` to check.
+ *
+ * @private
+ * @param {Object} object The object to inspect.
+ * @param {Object} source The object of property predicates to conform to.
+ * @returns {boolean} Returns `true` if `object` conforms, else `false`.
+ */
+function baseConformsTo(object, source, props) {
+  var length = props.length;
+  if (object == null) {
+    return !length;
+  }
+  object = Object(object);
+  while (length--) {
+    var key = props[length],
+        predicate = source[key],
+        value = object[key];
+
+    if ((value === undefined && !(key in object)) || !predicate(value)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+module.exports = baseConformsTo;

+ 30 - 0
node_modules/lodash/_baseCreate.js

@@ -0,0 +1,30 @@
+var isObject = require('./isObject');
+
+/** Built-in value references. */
+var objectCreate = Object.create;
+
+/**
+ * The base implementation of `_.create` without support for assigning
+ * properties to the created object.
+ *
+ * @private
+ * @param {Object} proto The object to inherit from.
+ * @returns {Object} Returns the new object.
+ */
+var baseCreate = (function() {
+  function object() {}
+  return function(proto) {
+    if (!isObject(proto)) {
+      return {};
+    }
+    if (objectCreate) {
+      return objectCreate(proto);
+    }
+    object.prototype = proto;
+    var result = new object;
+    object.prototype = undefined;
+    return result;
+  };
+}());
+
+module.exports = baseCreate;

+ 21 - 0
node_modules/lodash/_baseDelay.js

@@ -0,0 +1,21 @@
+/** Error message constants. */
+var FUNC_ERROR_TEXT = 'Expected a function';
+
+/**
+ * The base implementation of `_.delay` and `_.defer` which accepts `args`
+ * to provide to `func`.
+ *
+ * @private
+ * @param {Function} func The function to delay.
+ * @param {number} wait The number of milliseconds to delay invocation.
+ * @param {Array} args The arguments to provide to `func`.
+ * @returns {number|Object} Returns the timer id or timeout object.
+ */
+function baseDelay(func, wait, args) {
+  if (typeof func != 'function') {
+    throw new TypeError(FUNC_ERROR_TEXT);
+  }
+  return setTimeout(function() { func.apply(undefined, args); }, wait);
+}
+
+module.exports = baseDelay;

+ 67 - 0
node_modules/lodash/_baseDifference.js

@@ -0,0 +1,67 @@
+var SetCache = require('./_SetCache'),
+    arrayIncludes = require('./_arrayIncludes'),
+    arrayIncludesWith = require('./_arrayIncludesWith'),
+    arrayMap = require('./_arrayMap'),
+    baseUnary = require('./_baseUnary'),
+    cacheHas = require('./_cacheHas');
+
+/** Used as the size to enable large array optimizations. */
+var LARGE_ARRAY_SIZE = 200;
+
+/**
+ * The base implementation of methods like `_.difference` without support
+ * for excluding multiple arrays or iteratee shorthands.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {Array} values The values to exclude.
+ * @param {Function} [iteratee] The iteratee invoked per element.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new array of filtered values.
+ */
+function baseDifference(array, values, iteratee, comparator) {
+  var index = -1,
+      includes = arrayIncludes,
+      isCommon = true,
+      length = array.length,
+      result = [],
+      valuesLength = values.length;
+
+  if (!length) {
+    return result;
+  }
+  if (iteratee) {
+    values = arrayMap(values, baseUnary(iteratee));
+  }
+  if (comparator) {
+    includes = arrayIncludesWith;
+    isCommon = false;
+  }
+  else if (values.length >= LARGE_ARRAY_SIZE) {
+    includes = cacheHas;
+    isCommon = false;
+    values = new SetCache(values);
+  }
+  outer:
+  while (++index < length) {
+    var value = array[index],
+        computed = iteratee == null ? value : iteratee(value);
+
+    value = (comparator || value !== 0) ? value : 0;
+    if (isCommon && computed === computed) {
+      var valuesIndex = valuesLength;
+      while (valuesIndex--) {
+        if (values[valuesIndex] === computed) {
+          continue outer;
+        }
+      }
+      result.push(value);
+    }
+    else if (!includes(values, computed, comparator)) {
+      result.push(value);
+    }
+  }
+  return result;
+}
+
+module.exports = baseDifference;

+ 14 - 0
node_modules/lodash/_baseEach.js

@@ -0,0 +1,14 @@
+var baseForOwn = require('./_baseForOwn'),
+    createBaseEach = require('./_createBaseEach');
+
+/**
+ * The base implementation of `_.forEach` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array|Object} Returns `collection`.
+ */
+var baseEach = createBaseEach(baseForOwn);
+
+module.exports = baseEach;

+ 14 - 0
node_modules/lodash/_baseEachRight.js

@@ -0,0 +1,14 @@
+var baseForOwnRight = require('./_baseForOwnRight'),
+    createBaseEach = require('./_createBaseEach');
+
+/**
+ * The base implementation of `_.forEachRight` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array|Object} Returns `collection`.
+ */
+var baseEachRight = createBaseEach(baseForOwnRight, true);
+
+module.exports = baseEachRight;

+ 21 - 0
node_modules/lodash/_baseEvery.js

@@ -0,0 +1,21 @@
+var baseEach = require('./_baseEach');
+
+/**
+ * The base implementation of `_.every` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {boolean} Returns `true` if all elements pass the predicate check,
+ *  else `false`
+ */
+function baseEvery(collection, predicate) {
+  var result = true;
+  baseEach(collection, function(value, index, collection) {
+    result = !!predicate(value, index, collection);
+    return result;
+  });
+  return result;
+}
+
+module.exports = baseEvery;

+ 32 - 0
node_modules/lodash/_baseExtremum.js

@@ -0,0 +1,32 @@
+var isSymbol = require('./isSymbol');
+
+/**
+ * The base implementation of methods like `_.max` and `_.min` which accepts a
+ * `comparator` to determine the extremum value.
+ *
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} iteratee The iteratee invoked per iteration.
+ * @param {Function} comparator The comparator used to compare values.
+ * @returns {*} Returns the extremum value.
+ */
+function baseExtremum(array, iteratee, comparator) {
+  var index = -1,
+      length = array.length;
+
+  while (++index < length) {
+    var value = array[index],
+        current = iteratee(value);
+
+    if (current != null && (computed === undefined
+          ? (current === current && !isSymbol(current))
+          : comparator(current, computed)
+        )) {
+      var computed = current,
+          result = value;
+    }
+  }
+  return result;
+}
+
+module.exports = baseExtremum;

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