cmy 2 dias atrás
pai
commit
ba959d2627
100 arquivos alterados com 8673 adições e 3208 exclusões
  1. 1 1
      .hbuilderx/launch.json
  2. 189 172
      App.vue
  3. 0 28
      components/gaoyia-parse/components/wxParseAudio.vue
  4. 0 95
      components/gaoyia-parse/components/wxParseImg.vue
  5. 0 55
      components/gaoyia-parse/components/wxParseTable.vue
  6. 0 96
      components/gaoyia-parse/components/wxParseTemplate0.vue
  7. 0 88
      components/gaoyia-parse/components/wxParseTemplate1.vue
  8. 0 88
      components/gaoyia-parse/components/wxParseTemplate10.vue
  9. 0 86
      components/gaoyia-parse/components/wxParseTemplate11.vue
  10. 0 88
      components/gaoyia-parse/components/wxParseTemplate2.vue
  11. 0 88
      components/gaoyia-parse/components/wxParseTemplate3.vue
  12. 0 88
      components/gaoyia-parse/components/wxParseTemplate4.vue
  13. 0 88
      components/gaoyia-parse/components/wxParseTemplate5.vue
  14. 0 88
      components/gaoyia-parse/components/wxParseTemplate6.vue
  15. 0 88
      components/gaoyia-parse/components/wxParseTemplate7.vue
  16. 0 88
      components/gaoyia-parse/components/wxParseTemplate8.vue
  17. 0 88
      components/gaoyia-parse/components/wxParseTemplate9.vue
  18. 0 15
      components/gaoyia-parse/components/wxParseVideo.vue
  19. 0 264
      components/gaoyia-parse/libs/html2json.js
  20. 0 156
      components/gaoyia-parse/libs/htmlparser.js
  21. 0 209
      components/gaoyia-parse/libs/wxDiscode.js
  22. 0 259
      components/gaoyia-parse/parse.css
  23. 0 228
      components/gaoyia-parse/parse.vue
  24. 86 0
      components/mao-scroll/mao-scroll.nvue
  25. 164 0
      components/ui-public/customer-wiget.nvue
  26. 73 54
      components/ui-public/customer-wiget.vue
  27. 27 4
      library/Event/SocketEvent.js
  28. 2 0
      library/Http.js
  29. 20 3
      library/Request.js
  30. 1 1
      library/interceptors/After.js
  31. 17 17
      library/interceptors/Before.js
  32. 73 63
      library/upapp.js
  33. 477 372
      library/utils/Comm.js
  34. 4 9
      main.js
  35. 1 1
      manifest.json
  36. 0 4
      pages/guild/index.vue
  37. 1 4
      pages/guild/itemIndex.vue
  38. 11 7
      pages/index/home.nvue
  39. 1120 0
      pages/index/index.nvue
  40. 2 10
      pages/index/index.vue
  41. 0 2
      pages/login/forgotPassword.vue
  42. 3 4
      pages/login/index.vue
  43. 0 2
      pages/login/register.vue
  44. 1 7
      pages/news/about.vue
  45. 1 6
      pages/news/index.vue
  46. 1 7
      pages/operation/agreement.vue
  47. 1 7
      pages/operation/cancellation.vue
  48. 1 6
      pages/operation/package.vue
  49. 1 5
      pages/operation/user.vue
  50. 0 2
      pages/user/buy/balance.vue
  51. 13 13
      pages/user/index.nvue
  52. 0 2
      pages/user/index.vue
  53. 57 52
      store/index.js
  54. 13 0
      uni_modules/uv-parse/changelog.md
  55. 576 0
      uni_modules/uv-parse/components/uv-parse/node/node.vue
  56. 1335 0
      uni_modules/uv-parse/components/uv-parse/parser.js
  57. 498 0
      uni_modules/uv-parse/components/uv-parse/uv-parse.vue
  58. 87 0
      uni_modules/uv-parse/package.json
  59. 21 0
      uni_modules/uv-parse/readme.md
  60. 224 0
      uni_modules/uv-parse/static/app-plus/uv-parse/js/handler.js
  61. 19 0
      uni_modules/uv-parse/static/app-plus/uv-parse/js/uni.webview.min.js
  62. 1 0
      uni_modules/uv-parse/static/app-plus/uv-parse/local.html
  63. 76 0
      uni_modules/uv-ui-tools/changelog.md
  64. 6 0
      uni_modules/uv-ui-tools/components/uv-ui-tools/uv-ui-tools.vue
  65. 79 0
      uni_modules/uv-ui-tools/index.js
  66. 7 0
      uni_modules/uv-ui-tools/index.scss
  67. 34 0
      uni_modules/uv-ui-tools/libs/config/config.js
  68. 32 0
      uni_modules/uv-ui-tools/libs/css/color.scss
  69. 100 0
      uni_modules/uv-ui-tools/libs/css/common.scss
  70. 23 0
      uni_modules/uv-ui-tools/libs/css/components.scss
  71. 111 0
      uni_modules/uv-ui-tools/libs/css/variable.scss
  72. 40 0
      uni_modules/uv-ui-tools/libs/css/vue.scss
  73. 134 0
      uni_modules/uv-ui-tools/libs/function/colorGradient.js
  74. 29 0
      uni_modules/uv-ui-tools/libs/function/debounce.js
  75. 167 0
      uni_modules/uv-ui-tools/libs/function/digit.js
  76. 734 0
      uni_modules/uv-ui-tools/libs/function/index.js
  77. 75 0
      uni_modules/uv-ui-tools/libs/function/platform.js
  78. 287 0
      uni_modules/uv-ui-tools/libs/function/test.js
  79. 30 0
      uni_modules/uv-ui-tools/libs/function/throttle.js
  80. 132 0
      uni_modules/uv-ui-tools/libs/luch-request/adapters/index.js
  81. 51 0
      uni_modules/uv-ui-tools/libs/luch-request/core/InterceptorManager.js
  82. 201 0
      uni_modules/uv-ui-tools/libs/luch-request/core/Request.js
  83. 20 0
      uni_modules/uv-ui-tools/libs/luch-request/core/buildFullPath.js
  84. 33 0
      uni_modules/uv-ui-tools/libs/luch-request/core/defaults.js
  85. 6 0
      uni_modules/uv-ui-tools/libs/luch-request/core/dispatchRequest.js
  86. 126 0
      uni_modules/uv-ui-tools/libs/luch-request/core/mergeConfig.js
  87. 16 0
      uni_modules/uv-ui-tools/libs/luch-request/core/settle.js
  88. 64 0
      uni_modules/uv-ui-tools/libs/luch-request/helpers/buildURL.js
  89. 14 0
      uni_modules/uv-ui-tools/libs/luch-request/helpers/combineURLs.js
  90. 14 0
      uni_modules/uv-ui-tools/libs/luch-request/helpers/isAbsoluteURL.js
  91. 197 0
      uni_modules/uv-ui-tools/libs/luch-request/index.d.ts
  92. 2 0
      uni_modules/uv-ui-tools/libs/luch-request/index.js
  93. 135 0
      uni_modules/uv-ui-tools/libs/luch-request/utils.js
  94. 264 0
      uni_modules/uv-ui-tools/libs/luch-request/utils/clone.js
  95. 13 0
      uni_modules/uv-ui-tools/libs/mixin/button.js
  96. 172 0
      uni_modules/uv-ui-tools/libs/mixin/mixin.js
  97. 8 0
      uni_modules/uv-ui-tools/libs/mixin/mpMixin.js
  98. 13 0
      uni_modules/uv-ui-tools/libs/mixin/mpShare.js
  99. 47 0
      uni_modules/uv-ui-tools/libs/mixin/openType.js
  100. 59 0
      uni_modules/uv-ui-tools/libs/mixin/touch.js

+ 1 - 1
.hbuilderx/launch.json

@@ -16,7 +16,7 @@
             "type" : "uniCloud"
         },
         {
-            "playground" : "standard",
+            "playground" : "custom",
             "type" : "uni-app:app-android"
         }
     ]

+ 189 - 172
App.vue

@@ -1,5 +1,6 @@
 <script>
 	export default {
+		
 		onLaunch: function() {
 			console.log('App Launch')
 		},
@@ -14,42 +15,6 @@
 
 
 <style>
-	@import url("/components/gaoyia-parse/parse.css");
-	@import url("/components/css/animate.css");
-
-	page {
-		background: #F8F6F6;
-		-webkit-text-size-adjust: none
-	}
-
-	.clearfix {
-		zoom: 1
-	}
-
-	.clearfix:after {
-		clear: both;
-		display: block;
-		visibility: hidden;
-		overflow: hidden;
-		height: 0;
-		content: '\20'
-	}
-
-	button.nt {
-		background: transparent;
-		font-size: 12px;
-		padding: 0;
-		border-radius: 0;
-		color: #303033;
-		margin: 0px;
-		line-height: 1;
-		overflow: visible
-	}
-
-	button.nt::after {
-		border: 0;
-	}
-
 	.fx-r {
 		display: flex;
 		flex-direction: row;
@@ -85,62 +50,123 @@
 		align-items: flex-start;
 	}
 
-	.fx-g1 {
-		flex-grow: 1;
+
+	.pr {
+		position: relative;
 	}
 
-	.fx-g2 {
-		flex-grow: 2;
+	.pa {
+		position: absolute;
 	}
 
-	.fx-g3 {
-		flex-grow: 3;
+
+	.font-14 {
+		font-size: 14px !important;
 	}
 
-	.fx-g4 {
-		flex-grow: 4;
+	.font-12 {
+		font-size: 12px !important;
 	}
 
-	.fx-s {
-		flex-shrink: 0;
+
+	.text-r {
+		text-align: right
 	}
 
-	.pr {
+	.text-l {
+		text-align: left
+	}
+
+
+	.mk {
+		background: #000;
+		opacity: 0.5;
+		position: fixed;
+		top: 0px;
+		left: 0px;
+		right: 0px;
+		bottom: 0px;
+		z-index: 100
+	}
+
+
+
+	.ihover:active {
+		opacity: .5
+	}
+
+
+	.app-more {
+		text-align: center;
+		height: 30px;
+	}
+
+
+	.app-more-button {
 		position: relative;
+		height: 30px;
 	}
 
-	.pa {
+
+	.app-more-button .a-m-c {
 		position: absolute;
+		z-index: 9;
+		width: 100%;
+		height: 30px;
 	}
 
-	.fl {
-		float: left
+	.placeholder-style {
+		color: #d5d5d9;
+		font-size: 15px;
 	}
 
-	.fr {
-		float: right
+	.placeholder-style2 {
+		color: #666666;
+		font-size: 14px;
 	}
 
-	.font-14 {
-		font-size: 14px !important;
+	.placeholder-999 {
+		color: #999999;
+		font-size: 12px;
 	}
 
-	.font-12 {
-		font-size: 12px !important;
+
+
+	.bgChangColor {
+		background-image: linear-gradient(to right, #ff6879, #e81a62)
 	}
 
-	.border-none {
-		border: none !important;
+	.scrollHide::-webkit-scrollbar {
+		width: 0;
+		height: 0;
+		color: transparent;
 	}
 
-	.text-r {
-		text-align: right
+	.font-size18 {
+		font-size: 18px !important;
 	}
 
-	.text-l {
-		text-align: left
+
+
+
+	.loading.complete {
+		font-size: 14px;
+		color: #ccc;
+		padding: 10px 0;
 	}
 
+
+	/* #ifndef APP-PLUS-NVUE */
+
+	/*****超出一行省略*****/
+	.shenlueStyle {
+		overflow: hidden;
+		white-space: nowrap;
+		text-overflow: ellipsis
+	}
+
+	@import url("/components/css/animate.css");
+
 	.dn {
 		display: none !important;
 	}
@@ -149,15 +175,105 @@
 		display: block !important;
 	}
 
-	.mk {
-		background: #000;
-		opacity: 0.5;
-		position: fixed;
-		top: 0px;
-		left: 0px;
-		right: 0px;
-		bottom: 0px;
-		z-index: 100
+	.fl {
+		float: left
+	}
+
+	.fr {
+		float: right
+	}
+
+	.border-none {
+		border: none !important;
+	}
+
+	.clearfix {
+		zoom: 1
+	}
+
+	.fx-g1 {
+		flex-grow: 1;
+	}
+
+	.fx-g2 {
+		flex-grow: 2;
+	}
+
+	.fx-g3 {
+		flex-grow: 3;
+	}
+
+	.fx-g4 {
+		flex-grow: 4;
+	}
+
+	.fx-s {
+		flex-shrink: 0;
+	}
+
+	page {
+		background: #F8F6F6;
+		-webkit-text-size-adjust: none
+	}
+
+	.shenlueStyle2 {
+		text-overflow: -o-ellipsis-lastline;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		display: -webkit-box;
+		-webkit-line-clamp: 2;
+		line-clamp: 2;
+		-webkit-box-orient: vertical;
+	}
+
+	.shenlueStyle3 {
+		text-overflow: -o-ellipsis-lastline;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		display: -webkit-box;
+		-webkit-line-clamp: 3;
+		line-clamp: 3;
+		-webkit-box-orient: vertical;
+	}
+
+	.loading {
+		text-align: center;
+		height: 30px;
+		color: #4A2723;
+		line-height: 30px;
+		width: 100%;
+	}
+
+	.load-app {
+		position: absolute;
+		width: 100%;
+		height: 100%;
+		top: 0;
+		left: 0;
+	}
+
+	.clearfix:after {
+		clear: both;
+		display: block;
+		visibility: hidden;
+		overflow: hidden;
+		height: 0;
+		content: '\20'
+	}
+
+	button.nt {
+		background: transparent;
+		font-size: 12px;
+		padding: 0;
+		border-radius: 0;
+		color: #303033;
+		margin: 0px;
+		line-height: 1;
+		overflow: visible
+	}
+
+	button.nt::after {
+		border: 0;
 	}
 
 	@keyframes rotate {
@@ -170,10 +286,6 @@
 		}
 	}
 
-	.ihover:active {
-		opacity: .5
-	}
-
 	.ripple,
 	button.ripple {
 		position: relative;
@@ -220,11 +332,6 @@
 		break-inside: avoid;
 	}
 
-	.app-more {
-		text-align: center;
-		height: 30px;
-	}
-
 	.app-more image {
 		width: 20px;
 		height: 20px;
@@ -239,11 +346,6 @@
 		font-size: 12px;
 	}
 
-	.app-more-button {
-		position: relative;
-		height: 30px;
-	}
-
 	.app-more-button text {
 		padding: 0px 20px;
 		background: #f2f2f2;
@@ -258,69 +360,6 @@
 		top: 16px;
 	}
 
-	.app-more-button .a-m-c {
-		position: absolute;
-		z-index: 9;
-		width: 100%;
-		height: 30px;
-	}
-
-	.placeholder-style {
-		color: #d5d5d9;
-		font-size: 15px;
-	}
-
-	.placeholder-style2 {
-		color: #666666;
-		font-size: 14px;
-	}
-
-	.placeholder-999 {
-		color: #999999;
-		font-size: 12px;
-	}
-
-	/*****超出一行省略*****/
-	.shenlueStyle {
-		overflow: hidden;
-		white-space: nowrap;
-		text-overflow: ellipsis
-	}
-
-	.shenlueStyle2 {
-		text-overflow: -o-ellipsis-lastline;
-		overflow: hidden;
-		text-overflow: ellipsis;
-		display: -webkit-box;
-		-webkit-line-clamp: 2;
-		line-clamp: 2;
-		-webkit-box-orient: vertical;
-	}
-
-	.shenlueStyle3 {
-		text-overflow: -o-ellipsis-lastline;
-		overflow: hidden;
-		text-overflow: ellipsis;
-		display: -webkit-box;
-		-webkit-line-clamp: 3;
-		line-clamp: 3;
-		-webkit-box-orient: vertical;
-	}
-
-	.bgChangColor {
-		background: linear-gradient(to right, #ff6879, #e81a62)
-	}
-
-	.scrollHide::-webkit-scrollbar {
-		width: 0;
-		height: 0;
-		color: transparent;
-	}
-
-	.font-size18 {
-		font-size: 18px !important;
-	}
-
 	img[lazy=loading] {
 		opacity: 0;
 	}
@@ -329,14 +368,6 @@
 		transition: opacity 1s ease;
 	}
 
-	.loading {
-		text-align: center;
-		height: 30px;
-		color: #4A2723;
-		line-height: 30px;
-		width: 100%;
-	}
-
 	.loading image {
 		width: 20px;
 		height: 20px;
@@ -350,12 +381,6 @@
 		font-size: 12px;
 	}
 
-	.loading.complete {
-		font-size: 14px;
-		color: #ccc;
-		padding: 10px 0;
-	}
-
 	@keyframes rotate {
 		from {
 			transform: rotate(0deg)
@@ -382,16 +407,6 @@
 		color: transparent;
 	}
 
-
-
-	.load-app {
-		position: absolute;
-		width: 100%;
-		height: 100%;
-		top: 0;
-		left: 0;
-	}
-
 	.load-app image {
 		width: 60px;
 		height: 60px;
@@ -435,4 +450,6 @@
 			background-position: 100% 100%;
 		}
 	}
+
+	/* #endif */
 </style>

+ 0 - 28
components/gaoyia-parse/components/wxParseAudio.vue

@@ -1,28 +0,0 @@
-<template>
-	<!-- '<audio/>' 组件不再维护,建议使用能力更强的 'uni.createInnerAudioContext' 接口 有时间再改-->
-  <!--增加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>

+ 0 - 95
components/gaoyia-parse/components/wxParseImg.vue

@@ -1,95 +0,0 @@
-<template>
-	<image
-		:mode="node.attr.mode"
-		:lazy-load="node.attr.lazyLoad"
-		:class="node.classStr"
-		:style="newStyleStr || node.styleStr"
-		style="display: inline-block !important;"
-		:data-src="node.attr.src"
-		:src="node.attr.src"
-		@tap="wxParseImgTap"
-		@load="wxParseImgLoad"
-	/>
-</template>
-
-<script>
-export default {
-	name: 'wxParseImg',
-	data() {
-		return {
-			newStyleStr: '',
-			preview: true
-		};
-	},
-	inject: ['parseWidth'],
-	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;
-			let { width, height } = e.mp.detail;
-
-			const recal = this.wxAutoImageCal(width, height);
-
-			const { imageheight, imageWidth } = recal;
-			const { padding, mode } = this.node.attr;//删除padding
-			// const { 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;`;//删除padding
-			// this.newStyleStr = `${styleStr}; ${imageHeightStyle}; width: ${imageWidth}px;`;
-			console.log(this.node,'this.node')
-		},
-		// 计算视觉优先的图片宽高
-		wxAutoImageCal(originalWidth, originalHeight) {
-			// 获取图片的原始长宽
-			const windowWidth = this.parseWidth.value;
-			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>

+ 0 - 55
components/gaoyia-parse/components/wxParseTable.vue

@@ -1,55 +0,0 @@
-<template>
-	<div class='tablebox'>
-		<rich-text :nodes="nodes" :class="node.classStr" :style="'user-select:' + parseSelect"></rich-text>
-	</div>
-</template>
-<script>
-export default {
-	name: 'wxParseTable',
-	props: {
-		node: {
-			type: Object,
-			default() {
-				return {};
-			},
-		},
-	},
-	inject: ['parseSelect'],
-	data() {
-		return {
-			nodes:[]
-		};
-	},
-	mounted() {
-		this.nodes=this.loadNode([this.node]);
-	},
-	methods: {
-		loadNode(node) {
-			let obj = [];
-			for (let children of node) {
-				if (children.node=='element') {
-					let t = {
-						name:children.tag,
-						attrs: {
-							class: children.classStr,
-							// style: children.styleStr,
-						},
-						children: children.nodes?this.loadNode(children.nodes):[]
-					}
-					
-					obj.push(t)
-				} else if(children.node=='text'){
-					obj.push({
-						type: 'text',
-						text: children.text
-					})
-				}
-			}
-			return obj
-		}
-	}
-};
-</script>
-<style>
-	@import url("../parse.css");
-</style>

+ 0 - 96
components/gaoyia-parse/components/wxParseTemplate0.vue

@@ -1,96 +0,0 @@
-<template>
-	<!--判断是否是标签节点-->
-	<block v-if="node.node == 'element'">
-		<!--button类型-->
-		<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr">
-			<wx-parse-template :node="node" />
-		</button>
-		
-		<!--a类型-->
-		<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :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>
-		
-		<!--li类型-->
-		<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr">
-			<block v-for="(node, index) of node.nodes" :key="index">
-				<wx-parse-template :node="node" />
-			</block>
-		</view>
-		
-		<!--table类型-->
-		<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" />
-		
-		<!--br类型-->
-		<!-- #ifndef H5 -->
-			<text v-else-if="node.tag == 'br'">\n</text>
-		<!-- #endif -->
-		<!-- #ifdef H5 -->
-			<br v-else-if="node.tag == 'br'">
-		<!-- #endif -->
-		
-		<!--video类型-->
-		<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/>
-	
-		<!--audio类型-->
-		<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/>
-	
-		<!--img类型-->
-		<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/>
-		<!--其他标签-->
-		<view v-else :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 v-else-if="node.node == 'text'">{{node.text}}</block>
-</template>
-
-<script>
-	// #ifdef APP-PLUS | H5
-	import wxParseTemplate from './wxParseTemplate0';
-	// #endif
-	// #ifdef MP
-	import wxParseTemplate from './wxParseTemplate1';
-	// #endif
-	import wxParseImg from './wxParseImg';
-	import wxParseVideo from './wxParseVideo';
-	import wxParseAudio from './wxParseAudio';
-	import wxParseTable from './wxParseTable';
-
-	export default {
-		// #ifdef APP-PLUS | H5
-		name: 'wxParseTemplate',
-		// #endif
-		// #ifdef MP
-		name: 'wxParseTemplate0',
-		// #endif
-		props: {
-			node: {},
-		},
-		components: {
-			wxParseTemplate,
-			wxParseImg,
-			wxParseVideo,
-			wxParseAudio,
-			wxParseTable
-		},
-		methods: {
-			wxParseATap(attr,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, attr);
-			}
-		}
-	};
-</script>

+ 0 - 88
components/gaoyia-parse/components/wxParseTemplate1.vue

@@ -1,88 +0,0 @@
-<template>
-	<!--判断是否是标签节点-->
-	<block v-if="node.node == 'element'">
-		<!--button类型-->
-		<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr">
-			<wx-parse-template :node="node" />
-		</button>
-		
-		<!--a类型-->
-		<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :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>
-		
-		<!--li类型-->
-		<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr">
-			<block v-for="(node, index) of node.nodes" :key="index">
-				<wx-parse-template :node="node" />
-			</block>
-		</view>
-		
-		<!--table类型-->
-		<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" />
-		
-		<!--br类型-->
-		<!-- #ifndef H5 -->
-			<text v-else-if="node.tag == 'br'">\n</text>
-		<!-- #endif -->
-		<!-- #ifdef H5 -->
-			<br v-else-if="node.tag == 'br'">
-		<!-- #endif -->
-		
-		<!--video类型-->
-		<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/>
-	
-		<!--audio类型-->
-		<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/>
-	
-		<!--img类型-->
-		<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/>
-	
-		<!--其他标签-->
-		<view v-else :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 v-else-if="node.node == 'text'">{{node.text}}</block>
-</template>
-
-<script>
-	import wxParseTemplate from './wxParseTemplate2';
-	import wxParseImg from './wxParseImg';
-	import wxParseVideo from './wxParseVideo';
-	import wxParseAudio from './wxParseAudio';
-	import wxParseTable from './wxParseTable';
-	
-	export default {
-		name: 'wxParseTemplate1',
-		props: {
-			node: {},
-		},
-		components: {
-			wxParseTemplate,
-			wxParseImg,
-			wxParseVideo,
-			wxParseAudio,
-			wxParseTable
-		},
-		methods: {
-			wxParseATap(attr,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, attr);
-			}
-		}
-	};
-</script>

+ 0 - 88
components/gaoyia-parse/components/wxParseTemplate10.vue

@@ -1,88 +0,0 @@
-<template>
-		<!--判断是否是标签节点-->
-	<block v-if="node.node == 'element'">
-		<!--button类型-->
-		<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr">
-			<wx-parse-template :node="node" />
-		</button>
-		
-		<!--a类型-->
-		<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :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>
-		
-		<!--li类型-->
-		<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr">
-			<block v-for="(node, index) of node.nodes" :key="index">
-				<wx-parse-template :node="node" />
-			</block>
-		</view>
-		
-		<!--table类型-->
-		<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" />
-		
-		<!--br类型-->
-		<!-- #ifndef H5 -->
-			<text v-else-if="node.tag == 'br'">\n</text>
-		<!-- #endif -->
-		<!-- #ifdef H5 -->
-			<br v-else-if="node.tag == 'br'">
-		<!-- #endif -->
-		
-		<!--video类型-->
-		<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/>
-	
-		<!--audio类型-->
-		<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/>
-	
-		<!--img类型-->
-		<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/>
-	
-		<!--其他标签-->
-		<view v-else :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 v-else-if="node.node == 'text' ">{{node.text}}</block>
-</template>
-
-<script>
-	import wxParseTemplate from './wxParseTemplate11';
-	import wxParseImg from './wxParseImg';
-	import wxParseVideo from './wxParseVideo';
-	import wxParseAudio from './wxParseAudio';
-	import wxParseTable from './wxParseTable';
-	
-	export default {
-		name: 'wxParseTemplate10',
-		props: {
-			node: {},
-		},
-		components: {
-			wxParseTemplate,
-			wxParseImg,
-			wxParseVideo,
-			wxParseAudio,
-			wxParseTable
-		},
-		methods: {
-			wxParseATap(attr,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, attr);
-			}
-		}
-	};
-</script>

+ 0 - 86
components/gaoyia-parse/components/wxParseTemplate11.vue

@@ -1,86 +0,0 @@
-<template>
-		<!--判断是否是标签节点-->
-	<block v-if="node.node == 'element'">
-		<!--button类型-->
-		<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr">
-			<rich-text :nodes="node" :class="node.classStr" :style="'user-select:' + parseSelect"></rich-text>
-		</button>
-		
-		<!--a类型-->
-		<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :class="node.classStr" :data-href="node.attr.href" :style="node.styleStr">
-			<block v-for="(node, index) of node.nodes" :key="index">
-				<rich-text :nodes="node" :class="node.classStr" :style="'user-select:' + parseSelect"></rich-text>
-			</block>
-		</view>
-		
-		<!--li类型-->
-		<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr">
-			<block v-for="(node, index) of node.nodes" :key="index">
-				<rich-text :nodes="node" :class="node.classStr" :style="'user-select:' + parseSelect"></rich-text>
-			</block>
-		</view>
-		
-		<!--table类型-->
-		<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" />
-
-		<!--br类型-->
-		<!-- #ifndef H5 -->
-			<text v-else-if="node.tag == 'br'">\n</text>
-		<!-- #endif -->
-		<!-- #ifdef H5 -->
-			<br v-else-if="node.tag == 'br'">
-		<!-- #endif -->
-		
-		<!--video类型-->
-		<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/>
-	
-		<!--audio类型-->
-		<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/>
-	
-		<!--img类型-->
-		<wx-parse-img :node="node" v-else-if="node.tag == 'img'"/>
-	
-		<!--其他标签-->
-		<view v-else :class="node.classStr" :style="node.styleStr">
-			<block v-for="(node, index) of node.nodes" :key="index">
-				<rich-text :nodes="node" :class="node.classStr" :style="'user-select:' + parseSelect"></rich-text>
-			</block>
-		</view>
-	</block>
-	
-	<!--判断是否是文本节点-->
-	<block v-else-if="node.node == 'text' ">{{node.text}}</block>
-</template>
-
-<script>
-	import wxParseImg from './wxParseImg';
-	import wxParseVideo from './wxParseVideo';
-	import wxParseAudio from './wxParseAudio';
-	import wxParseTable from './wxParseTable';
-	
-	export default {
-		name: 'wxParseTemplate11',
-		props: {
-			node: {},
-		},
-		components: {
-			wxParseImg,
-			wxParseVideo,
-			wxParseAudio,
-			wxParseTable
-		},
-		methods: {
-			wxParseATap(attr,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, attr);
-			}
-		}
-	};
-</script>

+ 0 - 88
components/gaoyia-parse/components/wxParseTemplate2.vue

@@ -1,88 +0,0 @@
-<template>
-		<!--判断是否是标签节点-->
-	<block v-if="node.node == 'element'">
-		<!--button类型-->
-		<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr">
-			<wx-parse-template :node="node" />
-		</button>
-		
-		<!--a类型-->
-		<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :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>
-		
-		<!--li类型-->
-		<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr">
-			<block v-for="(node, index) of node.nodes" :key="index">
-				<wx-parse-template :node="node" />
-			</block>
-		</view>
-		
-		<!--table类型-->
-		<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" />
-		
-		<!--br类型-->
-		<!-- #ifndef H5 -->
-			<text v-else-if="node.tag == 'br'">\n</text>
-		<!-- #endif -->
-		<!-- #ifdef H5 -->
-			<br v-else-if="node.tag == 'br'">
-		<!-- #endif -->
-		
-		<!--video类型-->
-		<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/>
-	
-		<!--audio类型-->
-		<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/>
-	
-		<!--img类型-->
-		<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/>
-	
-		<!--其他标签-->
-		<view v-else :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 v-else-if="node.node == 'text'">{{node.text}}</block>
-</template>
-
-<script>
-	import wxParseTemplate from './wxParseTemplate3';
-	import wxParseImg from './wxParseImg';
-	import wxParseVideo from './wxParseVideo';
-	import wxParseAudio from './wxParseAudio';
-	import wxParseTable from './wxParseTable';
-	
-	export default {
-		name: 'wxParseTemplate2',
-		props: {
-			node: {},
-		},
-		components: {
-			wxParseTemplate,
-			wxParseImg,
-			wxParseVideo,
-			wxParseAudio,
-			wxParseTable
-		},
-		methods: {
-			wxParseATap(attr,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, attr);
-			}
-		}
-	};
-</script>

+ 0 - 88
components/gaoyia-parse/components/wxParseTemplate3.vue

@@ -1,88 +0,0 @@
-<template>
-		<!--判断是否是标签节点-->
-	<block v-if="node.node == 'element'">
-		<!--button类型-->
-		<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr">
-			<wx-parse-template :node="node" />
-		</button>
-		
-		<!--a类型-->
-		<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :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>
-		
-		<!--li类型-->
-		<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr">
-			<block v-for="(node, index) of node.nodes" :key="index">
-				<wx-parse-template :node="node" />
-			</block>
-		</view>
-		
-		<!--table类型-->
-		<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" />
-		
-		<!--br类型-->
-		<!-- #ifndef H5 -->
-			<text v-else-if="node.tag == 'br'">\n</text>
-		<!-- #endif -->
-		<!-- #ifdef H5 -->
-			<br v-else-if="node.tag == 'br'">
-		<!-- #endif -->
-		
-		<!--video类型-->
-		<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/>
-	
-		<!--audio类型-->
-		<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/>
-	
-		<!--img类型-->
-		<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/>
-	
-		<!--其他标签-->
-		<view v-else :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 v-else-if="node.node == 'text' ">{{node.text}}</block>
-</template>
-
-<script>
-	import wxParseTemplate from './wxParseTemplate4';
-	import wxParseImg from './wxParseImg';
-	import wxParseVideo from './wxParseVideo';
-	import wxParseAudio from './wxParseAudio';
-	import wxParseTable from './wxParseTable';
-	
-	export default {
-		name: 'wxParseTemplate3',
-		props: {
-			node: {},
-		},
-		components: {
-			wxParseTemplate,
-			wxParseImg,
-			wxParseVideo,
-			wxParseAudio,
-			wxParseTable
-		},
-		methods: {
-			wxParseATap(attr,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, attr);
-			}
-		}
-	};
-</script>

+ 0 - 88
components/gaoyia-parse/components/wxParseTemplate4.vue

@@ -1,88 +0,0 @@
-<template>
-		<!--判断是否是标签节点-->
-	<block v-if="node.node == 'element'">
-		<!--button类型-->
-		<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr">
-			<wx-parse-template :node="node" />
-		</button>
-		
-		<!--a类型-->
-		<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :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>
-		
-		<!--li类型-->
-		<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr">
-			<block v-for="(node, index) of node.nodes" :key="index">
-				<wx-parse-template :node="node" />
-			</block>
-		</view>
-		
-		<!--table类型-->
-		<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" />
-		
-		<!--br类型-->
-		<!-- #ifndef H5 -->
-			<text v-else-if="node.tag == 'br'">\n</text>
-		<!-- #endif -->
-		<!-- #ifdef H5 -->
-			<br v-else-if="node.tag == 'br'">
-		<!-- #endif -->
-		
-		<!--video类型-->
-		<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/>
-	
-		<!--audio类型-->
-		<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/>
-	
-		<!--img类型-->
-		<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/>
-	
-		<!--其他标签-->
-		<view v-else :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 v-else-if="node.node == 'text' ">{{node.text}}</block>
-</template>
-
-<script>
-	import wxParseTemplate from './wxParseTemplate5';
-	import wxParseImg from './wxParseImg';
-	import wxParseVideo from './wxParseVideo';
-	import wxParseAudio from './wxParseAudio';
-	import wxParseTable from './wxParseTable';
-	
-	export default {
-		name: 'wxParseTemplate4',
-		props: {
-			node: {},
-		},
-		components: {
-			wxParseTemplate,
-			wxParseImg,
-			wxParseVideo,
-			wxParseAudio,
-			wxParseTable
-		},
-		methods: {
-			wxParseATap(attr,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, attr);
-			}
-		}
-	};
-</script>

+ 0 - 88
components/gaoyia-parse/components/wxParseTemplate5.vue

@@ -1,88 +0,0 @@
-<template>
-		<!--判断是否是标签节点-->
-	<block v-if="node.node == 'element'">
-		<!--button类型-->
-		<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr">
-			<wx-parse-template :node="node" />
-		</button>
-		
-		<!--a类型-->
-		<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :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>
-		
-		<!--li类型-->
-		<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr">
-			<block v-for="(node, index) of node.nodes" :key="index">
-				<wx-parse-template :node="node" />
-			</block>
-		</view>
-		
-		<!--table类型-->
-		<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" />
-		
-		<!--br类型-->
-		<!-- #ifndef H5 -->
-			<text v-else-if="node.tag == 'br'">\n</text>
-		<!-- #endif -->
-		<!-- #ifdef H5 -->
-			<br v-else-if="node.tag == 'br'">
-		<!-- #endif -->
-		
-		<!--video类型-->
-		<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/>
-	
-		<!--audio类型-->
-		<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/>
-	
-		<!--img类型-->
-		<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/>
-	
-		<!--其他标签-->
-		<view v-else :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 v-else-if="node.node == 'text' ">{{node.text}}</block>
-</template>
-
-<script>
-	import wxParseTemplate from './wxParseTemplate6';
-	import wxParseImg from './wxParseImg';
-	import wxParseVideo from './wxParseVideo';
-	import wxParseAudio from './wxParseAudio';
-	import wxParseTable from './wxParseTable';
-	
-	export default {
-		name: 'wxParseTemplate5',
-		props: {
-			node: {},
-		},
-		components: {
-			wxParseTemplate,
-			wxParseImg,
-			wxParseVideo,
-			wxParseAudio,
-			wxParseTable
-		},
-		methods: {
-			wxParseATap(attr,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, attr);
-			}
-		}
-	};
-</script>

+ 0 - 88
components/gaoyia-parse/components/wxParseTemplate6.vue

@@ -1,88 +0,0 @@
-<template>
-		<!--判断是否是标签节点-->
-	<block v-if="node.node == 'element'">
-		<!--button类型-->
-		<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr">
-			<wx-parse-template :node="node" />
-		</button>
-		
-		<!--a类型-->
-		<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :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>
-		
-		<!--li类型-->
-		<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr">
-			<block v-for="(node, index) of node.nodes" :key="index">
-				<wx-parse-template :node="node" />
-			</block>
-		</view>
-		
-		<!--table类型-->
-		<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" />
-		
-		<!--br类型-->
-		<!-- #ifndef H5 -->
-			<text v-else-if="node.tag == 'br'">\n</text>
-		<!-- #endif -->
-		<!-- #ifdef H5 -->
-			<br v-else-if="node.tag == 'br'">
-		<!-- #endif -->
-		
-		<!--video类型-->
-		<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/>
-	
-		<!--audio类型-->
-		<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/>
-	
-		<!--img类型-->
-		<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/>
-	
-		<!--其他标签-->
-		<view v-else :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 v-else-if="node.node == 'text' ">{{node.text}}</block>
-</template>
-
-<script>
-	import wxParseTemplate from './wxParseTemplate7';
-	import wxParseImg from './wxParseImg';
-	import wxParseVideo from './wxParseVideo';
-	import wxParseAudio from './wxParseAudio';
-	import wxParseTable from './wxParseTable';
-	
-	export default {
-		name: 'wxParseTemplate6',
-		props: {
-			node: {},
-		},
-		components: {
-			wxParseTemplate,
-			wxParseImg,
-			wxParseVideo,
-			wxParseAudio,
-			wxParseTable
-		},
-		methods: {
-			wxParseATap(attr,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, attr);
-			}
-		}
-	};
-</script>

+ 0 - 88
components/gaoyia-parse/components/wxParseTemplate7.vue

@@ -1,88 +0,0 @@
-<template>
-		<!--判断是否是标签节点-->
-	<block v-if="node.node == 'element'">
-		<!--button类型-->
-		<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr">
-			<wx-parse-template :node="node" />
-		</button>
-		
-		<!--a类型-->
-		<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :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>
-		
-		<!--li类型-->
-		<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr">
-			<block v-for="(node, index) of node.nodes" :key="index">
-				<wx-parse-template :node="node" />
-			</block>
-		</view>
-		
-		<!--table类型-->
-		<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" />
-		
-		<!--br类型-->
-		<!-- #ifndef H5 -->
-			<text v-else-if="node.tag == 'br'">\n</text>
-		<!-- #endif -->
-		<!-- #ifdef H5 -->
-			<br v-else-if="node.tag == 'br'">
-		<!-- #endif -->
-		
-		<!--video类型-->
-		<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/>
-	
-		<!--audio类型-->
-		<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/>
-	
-		<!--img类型-->
-		<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/>
-	
-		<!--其他标签-->
-		<view v-else :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 v-else-if="node.node == 'text' ">{{node.text}}</block>
-</template>
-
-<script>
-	import wxParseTemplate from './wxParseTemplate8';
-	import wxParseImg from './wxParseImg';
-	import wxParseVideo from './wxParseVideo';
-	import wxParseAudio from './wxParseAudio';
-	import wxParseTable from './wxParseTable';
-	
-	export default {
-		name: 'wxParseTemplate7',
-		props: {
-			node: {},
-		},
-		components: {
-			wxParseTemplate,
-			wxParseImg,
-			wxParseVideo,
-			wxParseAudio,
-			wxParseTable
-		},
-		methods: {
-			wxParseATap(attr,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, attr);
-			}
-		}
-	};
-</script>

+ 0 - 88
components/gaoyia-parse/components/wxParseTemplate8.vue

@@ -1,88 +0,0 @@
-<template>
-		<!--判断是否是标签节点-->
-	<block v-if="node.node == 'element'">
-		<!--button类型-->
-		<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr">
-			<wx-parse-template :node="node" />
-		</button>
-		
-		<!--a类型-->
-		<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :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>
-		
-		<!--li类型-->
-		<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr">
-			<block v-for="(node, index) of node.nodes" :key="index">
-				<wx-parse-template :node="node" />
-			</block>
-		</view>
-		
-		<!--table类型-->
-		<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" />
-		
-		<!--br类型-->
-		<!-- #ifndef H5 -->
-			<text v-else-if="node.tag == 'br'">\n</text>
-		<!-- #endif -->
-		<!-- #ifdef H5 -->
-			<br v-else-if="node.tag == 'br'">
-		<!-- #endif -->
-		
-		<!--video类型-->
-		<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/>
-	
-		<!--audio类型-->
-		<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/>
-	
-		<!--img类型-->
-		<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/>
-	
-		<!--其他标签-->
-		<view v-else :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 v-else-if="node.node == 'text' ">{{node.text}}</block>
-</template>
-
-<script>
-	import wxParseTemplate from './wxParseTemplate9';
-	import wxParseImg from './wxParseImg';
-	import wxParseVideo from './wxParseVideo';
-	import wxParseAudio from './wxParseAudio';
-	import wxParseTable from './wxParseTable';
-	
-	export default {
-		name: 'wxParseTemplate8',
-		props: {
-			node: {},
-		},
-		components: {
-			wxParseTemplate,
-			wxParseImg,
-			wxParseVideo,
-			wxParseAudio,
-			wxParseTable
-		},
-		methods: {
-			wxParseATap(attr,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, attr);
-			}
-		}
-	};
-</script>

+ 0 - 88
components/gaoyia-parse/components/wxParseTemplate9.vue

@@ -1,88 +0,0 @@
-<template>
-		<!--判断是否是标签节点-->
-	<block v-if="node.node == 'element'">
-		<!--button类型-->
-		<button v-if="node.tag == 'button'" type="default" size="mini" :class="node.classStr" :style="node.styleStr">
-			<wx-parse-template :node="node" />
-		</button>
-		
-		<!--a类型-->
-		<view v-else-if="node.tag == 'a'" @click="wxParseATap(node.attr,$event)" :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>
-		
-		<!--li类型-->
-		<view v-else-if="node.tag == 'li'" :class="node.classStr" :style="node.styleStr">
-			<block v-for="(node, index) of node.nodes" :key="index">
-				<wx-parse-template :node="node" />
-			</block>
-		</view>
-		
-		<!--table类型-->
-		<wx-parse-table v-else-if="node.tag == 'table'" :class="node.classStr" :style="node.styleStr" :node="node" />
-		
-		<!--br类型-->
-		<!-- #ifndef H5 -->
-			<text v-else-if="node.tag == 'br'">\n</text>
-		<!-- #endif -->
-		<!-- #ifdef H5 -->
-			<br v-else-if="node.tag == 'br'">
-		<!-- #endif -->
-		
-		<!--video类型-->
-		<wx-parse-video :node="node" v-else-if="node.tag == 'video'"/>
-	
-		<!--audio类型-->
-		<wx-parse-audio :node="node" v-else-if="node.tag == 'audio'"/>
-	
-		<!--img类型-->
-		<wx-parse-img :node="node" v-else-if="node.tag == 'img'" :style="node.styleStr"/>
-	
-		<!--其他标签-->
-		<view v-else :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 v-else-if="node.node == 'text' ">{{node.text}}</block>
-</template>
-
-<script>
-	import wxParseTemplate from './wxParseTemplate10';
-	import wxParseImg from './wxParseImg';
-	import wxParseVideo from './wxParseVideo';
-	import wxParseAudio from './wxParseAudio';
-	import wxParseTable from './wxParseTable';
-	
-	export default {
-		name: 'wxParseTemplate9',
-		props: {
-			node: {},
-		},
-		components: {
-			wxParseTemplate,
-			wxParseImg,
-			wxParseVideo,
-			wxParseAudio,
-			wxParseTable
-		},
-		methods: {
-			wxParseATap(attr,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, attr);
-			}
-		}
-	};
-</script>

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

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

+ 0 - 264
components/gaoyia-parse/libs/html2json.js

@@ -1,264 +0,0 @@
-/**
- * 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');
-
-// #ifdef APP
-const inline = makeMap('a,abbr,acronym,applet,b,basefont,bdo,big,button,cite,del,dfn,em,font,i,iframe,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var');
-// #endif
-// #ifndef APP
-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');
-// #endif
-// 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;

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

@@ -1,156 +0,0 @@
-/**
- *
- * 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;

+ 0 - 209
components/gaoyia-parse/libs/wxDiscode.js

@@ -1,209 +0,0 @@
-// HTML 支持的数学符号
-function strNumDiscode(str) {
-str = str.replace(/&forall;|&#8704;|&#x2200;/g, '∀');
-str = str.replace(/&part;|&#8706;|&#x2202;/g, '∂');
-str = str.replace(/&exist;|&#8707;|&#x2203;/g, '∃');
-str = str.replace(/&empty;|&#8709;|&#x2205;/g, '∅');
-str = str.replace(/&nabla;|&#8711;|&#x2207;/g, '∇');
-str = str.replace(/&isin;|&#8712;|&#x2208;/g, '∈');
-str = str.replace(/&notin;|&#8713;|&#x2209;/g, '∉');
-str = str.replace(/&ni;|&#8715;|&#x220b;/g, '∋');
-str = str.replace(/&prod;|&#8719;|&#x220f;/g, '∏');
-str = str.replace(/&sum;|&#8721;|&#x2211;/g, '∑');
-str = str.replace(/&minus;|&#8722;|&#x2212;/g, '−');
-str = str.replace(/&lowast;|&#8727;|&#x2217;/g, '∗');
-str = str.replace(/&radic;|&#8730;|&#x221a;/g, '√');
-str = str.replace(/&prop;|&#8733;|&#x221d;/g, '∝');
-str = str.replace(/&infin;|&#8734;|&#x221e;/g, '∞');
-str = str.replace(/&ang;|&#8736;|&#x2220;/g, '∠');
-str = str.replace(/&and;|&#8743;|&#x2227;/g, '∧');
-str = str.replace(/&or;|&#8744;|&#x2228;/g, '∨');
-str = str.replace(/&cap;|&#8745;|&#x2229;/g, '∩');
-str = str.replace(/&cup;|&#8746;|&#x222a;/g, '∪');
-str = str.replace(/&int;|&#8747;|&#x222b;/g, '∫');
-str = str.replace(/&there4;|&#8756;|&#x2234;/g, '∴');
-str = str.replace(/&sim;|&#8764;|&#x223c;/g, '∼');
-str = str.replace(/&cong;|&#8773;|&#x2245;/g, '≅');
-str = str.replace(/&asymp;|&#8776;|&#x2248;/g, '≈');
-str = str.replace(/&ne;|&#8800;|&#x2260;/g, '≠');
-str = str.replace(/&le;|&#8804;|&#x2264;/g, '≤');
-str = str.replace(/&ge;|&#8805;|&#x2265;/g, '≥');
-str = str.replace(/&sub;|&#8834;|&#x2282;/g, '⊂');
-str = str.replace(/&sup;|&#8835;|&#x2283;/g, '⊃');
-str = str.replace(/&nsub;|&#8836;|&#x2284;/g, '⊄');
-str = str.replace(/&sube;|&#8838;|&#x2286;/g, '⊆');
-str = str.replace(/&supe;|&#8839;|&#x2287;/g, '⊇');
-str = str.replace(/&oplus;|&#8853;|&#x2295;/g, '⊕');
-str = str.replace(/&otimes;|&#8855;|&#x2297;/g, '⊗');
-str = str.replace(/&perp;|&#8869;|&#x22a5;/g, '⊥');
-str = str.replace(/&sdot;|&#8901;|&#x22c5;/g, '⋅');
-return str;
-}
-
-// HTML 支持的希腊字母
-function strGreeceDiscode(str) {
-str = str.replace(/&Alpha;|&#913;|&#x391;/g, 'Α');
-str = str.replace(/&Beta;|&#914;|&#x392;/g, 'Β');
-str = str.replace(/&Gamma;|&#915;|&#x393;/g, 'Γ');
-str = str.replace(/&Delta;|&#916;|&#x394;/g, 'Δ');
-str = str.replace(/&Epsilon;|&#917;|&#x395;/g, 'Ε');
-str = str.replace(/&Zeta;|&#918;|&#x396;/g, 'Ζ');
-str = str.replace(/&Eta;|&#919;|&#x397;/g, 'Η');
-str = str.replace(/&Theta;|&#920;|&#x398;/g, 'Θ');
-str = str.replace(/&Iota;|&#921;|&#x399;/g, 'Ι');
-str = str.replace(/&Kappa;|&#922;|&#x39a;/g, 'Κ');
-str = str.replace(/&Lambda;|&#923;|&#x39b;/g, 'Λ');
-str = str.replace(/&Mu;|&#924;|&#x39c;/g, 'Μ');
-str = str.replace(/&Nu;|&#925;|&#x39d;/g, 'Ν');
-str = str.replace(/&Xi;|&#925;|&#x39d;/g, 'Ν');
-str = str.replace(/&Omicron;|&#927;|&#x39f;/g, 'Ο');
-str = str.replace(/&Pi;|&#928;|&#x3a0;/g, 'Π');
-str = str.replace(/&Rho;|&#929;|&#x3a1;/g, 'Ρ');
-str = str.replace(/&Sigma;|&#931;|&#x3a3;/g, 'Σ');
-str = str.replace(/&Tau;|&#932;|&#x3a4;/g, 'Τ');
-str = str.replace(/&Upsilon;|&#933;|&#x3a5;/g, 'Υ');
-str = str.replace(/&Phi;|&#934;|&#x3a6;/g, 'Φ');
-str = str.replace(/&Chi;|&#935;|&#x3a7;/g, 'Χ');
-str = str.replace(/&Psi;|&#936;|&#x3a8;/g, 'Ψ');
-str = str.replace(/&Omega;|&#937;|&#x3a9;/g, 'Ω');
-
-str = str.replace(/&alpha;|&#945;|&#x3b1;/g, 'α');
-str = str.replace(/&beta;|&#946;|&#x3b2;/g, 'β');
-str = str.replace(/&gamma;|&#947;|&#x3b3;/g, 'γ');
-str = str.replace(/&delta;|&#948;|&#x3b4;/g, 'δ');
-str = str.replace(/&epsilon;|&#949;|&#x3b5;/g, 'ε');
-str = str.replace(/&zeta;|&#950;|&#x3b6;/g, 'ζ');
-str = str.replace(/&eta;|&#951;|&#x3b7;/g, 'η');
-str = str.replace(/&theta;|&#952;|&#x3b8;/g, 'θ');
-str = str.replace(/&iota;|&#953;|&#x3b9;/g, 'ι');
-str = str.replace(/&kappa;|&#954;|&#x3ba;/g, 'κ');
-str = str.replace(/&lambda;|&#955;|&#x3bb;/g, 'λ');
-str = str.replace(/&mu;|&#956;|&#x3bc;/g, 'μ');
-str = str.replace(/&nu;|&#957;|&#x3bd;/g, 'ν');
-str = str.replace(/&xi;|&#958;|&#x3be;/g, 'ξ');
-str = str.replace(/&omicron;|&#959;|&#x3bf;/g, 'ο');
-str = str.replace(/&pi;|&#960;|&#x3c0;/g, 'π');
-str = str.replace(/&rho;|&#961;|&#x3c1;/g, 'ρ');
-str = str.replace(/&sigmaf;|&#962;|&#x3c2;/g, 'ς');
-str = str.replace(/&sigma;|&#963;|&#x3c3;/g, 'σ');
-str = str.replace(/&tau;|&#964;|&#x3c4;/g, 'τ');
-str = str.replace(/&upsilon;|&#965;|&#x3c5;/g, 'υ');
-str = str.replace(/&phi;|&#966;|&#x3c6;/g, 'φ');
-str = str.replace(/&chi;|&#967;|&#x3c7;/g, 'χ');
-str = str.replace(/&psi;|&#968;|&#x3c8;/g, 'ψ');
-str = str.replace(/&omega;|&#969;|&#x3c9;/g, 'ω');
-str = str.replace(/&thetasym;|&#977;|&#x3d1;/g, 'ϑ');
-str = str.replace(/&upsih;|&#978;|&#x3d2;/g, 'ϒ');
-str = str.replace(/&piv;|&#982;|&#x3d6;/g, 'ϖ');
-str = str.replace(/&middot;|&#183;|&#xb7;/g, '·');
-return str;
-}
-
-function strcharacterDiscode(str) {
-// 加入常用解析
-
-// str = str.replace(/&nbsp;|&#32;|&#x20;/g, "&nbsp;");
-// str = str.replace(/&ensp;|&#8194;|&#x2002;/g, '&ensp;');
-// str = str.replace(/&#12288;|&#x3000;/g, '<span class=\'spaceshow\'> </span>');
-// str = str.replace(/&emsp;|&#8195;|&#x2003;/g, '&emsp;');
-// str = str.replace(/&quot;|&#34;|&#x22;/g, "\"");
-// str = str.replace(/&apos;|&#39;|&#x27;/g, "&apos;");
-// str = str.replace(/&acute;|&#180;|&#xB4;/g, "´");
-// str = str.replace(/&times;|&#215;|&#xD7;/g, "×");
-// str = str.replace(/&divide;|&#247;|&#xF7;/g, "÷");
-// str = str.replace(/&amp;|&#38;|&#x26;/g, '&amp;');
-// str = str.replace(/&lt;|&#60;|&#x3c;/g, '&lt;');
-// str = str.replace(/&gt;|&#62;|&#x3e;/g, '&gt;');
-
-
-
-
-str = str.replace(/&nbsp;|&#32;|&#x20;/g, "<span class='spaceshow'> </span>");
-str = str.replace(/&ensp;|&#8194;|&#x2002;/g, '<span class=\'spaceshow\'> </span>');
-str = str.replace(/&#12288;|&#x3000;/g, '<span class=\'spaceshow\'> </span>');
-str = str.replace(/&emsp;|&#8195;|&#x2003;/g, '<span class=\'spaceshow\'> </span>');
-str = str.replace(/&quot;|&#34;|&#x22;/g, "\"");
-str = str.replace(/&quot;|&#39;|&#x27;/g, "'");
-str = str.replace(/&acute;|&#180;|&#xB4;/g, "´");
-str = str.replace(/&times;|&#215;|&#xD7;/g, "×");
-str = str.replace(/&divide;|&#247;|&#xF7;/g, "÷");
-str = str.replace(/&amp;|&#38;|&#x26;/g, '&');
-str = str.replace(/&lt;|&#60;|&#x3c;/g, '<');
-str = str.replace(/&gt;|&#62;|&#x3e;/g, '>');
-return str;
-}
-
-// HTML 支持的其他实体
-function strOtherDiscode(str) {
-str = str.replace(/&OElig;|&#338;|&#x152;/g, 'Œ');
-str = str.replace(/&oelig;|&#339;|&#x153;/g, 'œ');
-str = str.replace(/&Scaron;|&#352;|&#x160;/g, 'Š');
-str = str.replace(/&scaron;|&#353;|&#x161;/g, 'š');
-str = str.replace(/&Yuml;|&#376;|&#x178;/g, 'Ÿ');
-str = str.replace(/&fnof;|&#402;|&#x192;/g, 'ƒ');
-str = str.replace(/&circ;|&#710;|&#x2c6;/g, 'ˆ');
-str = str.replace(/&tilde;|&#732;|&#x2dc;/g, '˜');
-str = str.replace(/&thinsp;|$#8201;|&#x2009;/g, '<span class=\'spaceshow\'> </span>');
-str = str.replace(/&zwnj;|&#8204;|&#x200C;/g, '<span class=\'spaceshow\'>‌</span>');
-str = str.replace(/&zwj;|$#8205;|&#x200D;/g, '<span class=\'spaceshow\'>‍</span>');
-str = str.replace(/&lrm;|$#8206;|&#x200E;/g, '<span class=\'spaceshow\'>‎</span>');
-str = str.replace(/&rlm;|&#8207;|&#x200F;/g, '<span class=\'spaceshow\'>‏</span>');
-str = str.replace(/&ndash;|&#8211;|&#x2013;/g, '–');
-str = str.replace(/&mdash;|&#8212;|&#x2014;/g, '—');
-str = str.replace(/&lsquo;|&#8216;|&#x2018;/g, '‘');
-str = str.replace(/&rsquo;|&#8217;|&#x2019;/g, '’');
-str = str.replace(/&sbquo;|&#8218;|&#x201a;/g, '‚');
-str = str.replace(/&ldquo;|&#8220;|&#x201c;/g, '“');
-str = str.replace(/&rdquo;|&#8221;|&#x201d;/g, '”');
-str = str.replace(/&bdquo;|&#8222;|&#x201e;/g, '„');
-str = str.replace(/&dagger;|&#8224;|&#x2020;/g, '†');
-str = str.replace(/&Dagger;|&#8225;|&#x2021;/g, '‡');
-str = str.replace(/&bull;|&#8226;|&#x2022;/g, '•');
-str = str.replace(/&hellip;|&#8230;|&#x2026;/g, '…');
-str = str.replace(/&permil;|&#8240;|&#x2030;/g, '‰');
-str = str.replace(/&prime;|&#8242;|&#x2032;/g, '′');
-str = str.replace(/&Prime;|&#8243;|&#x2033;/g, '″');
-str = str.replace(/&lsaquo;|&#8249;|&#x2039;/g, '‹');
-str = str.replace(/&rsaquo;|&#8250;|&#x203a;/g, '›');
-str = str.replace(/&oline;|&#8254;|&#x203e;/g, '‾');
-str = str.replace(/&euro;|&#8364;|&#x20ac;/g, '€');
-str = str.replace(/&trade;|&#8482;|&#x2122;/g, '™');
-str = str.replace(/&larr;|&#8592;|&#x2190;/g, '←');
-str = str.replace(/&uarr;|&#8593;|&#x2191;/g, '↑');
-str = str.replace(/&rarr;|&#8594;|&#x2192;/g, '→');
-str = str.replace(/&darr;|&#8595;|&#x2193;/g, '↓');
-str = str.replace(/&harr;|&#8596;|&#x2194;/g, '↔');
-str = str.replace(/&crarr;|&#8629;|&#x21b5;/g, '↵');
-str = str.replace(/&lceil;|&#8968;|&#x2308;/g, '⌈');
-str = str.replace(/&rceil;|&#8969;|&#x2309;/g, '⌉');
-str = str.replace(/&lfloor;|&#8970;|&#x230a;/g, '⌊');
-str = str.replace(/&rfloor;|&#8971;|&#x230b;/g, '⌋');
-str = str.replace(/&loz;|&#9674;|&#x25ca;/g, '◊');
-str = str.replace(/&spades;|&#9824;|&#x2660;/g, '♠');
-str = str.replace(/&clubs;|&#9827;|&#x2663;/g, '♣');
-str = str.replace(/&hearts;|&#9829;|&#x2665;/g, '♥');
-str = str.replace(/&diams;|&#9830;|&#x2666;/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,
-};

+ 0 - 259
components/gaoyia-parse/parse.css

@@ -1,259 +0,0 @@
-/**
- * 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 '/static/wxParse.css';
- */
-.wxParse {
-	user-select:none;
-	width: 100%;
-	font-family: Helvetica, "PingFangSC", 'Microsoft Yahei', '微软雅黑', Arial, sans-serif;
-	color: #333;
-	line-height: 1.5;
-	font-size: 1em;
-	text-align:justify;/* //左右两端对齐 */
-}
-.wxParse view ,.wxParse uni-view{
-	word-break: break-word;
-}
-.wxParse .p {
-	padding-bottom: 0.5em;
-	clear: both;
-	/* letter-spacing: 0;//字间距 */
-}
-.wxParse .inline {
-  display: inline!important;
-  white-space:inherit!important;
-  margin: 0;
-  padding: 0;
-}
-
-.wxParse .div {
-  margin: 0;
-  padding: 0;
-  display: block;
-}
-
-.wxParse .h1{
-  font-size: 2em;
-  line-height: 1.2em;
-  margin: 0.67em 0;
-}
-.wxParse .h2{
-  font-size: 1.5em;
-  margin: 0.83em 0;
-}
-.wxParse .h3{
-  font-size: 1.17em;
-  margin: 1em 0;
-}
-.wxParse .h4{
-  margin: 1.33em 0;
-}
-.wxParse .h5{
-  font-size: 0.83em;
-  margin: 1.67em 0;
-}
-.wxParse .h6{
-  font-size: 0.83em;
-  margin: 1.67em 0;
-}
-
-.wxParse .h1,
-.wxParse .h2,
-.wxParse .h3,
-.wxParse .h4,
-.wxParse .h5,
-.wxParse .h6,
-.wxParse .b,
-.wxParse .strong{
-  font-weight: bolder;
-}
-
-.wxParse .i,
-.wxParse .cite,
-.wxParse .em,
-.wxParse .var,
-.wxParse .address {
-  font-style: italic;
-}
-.wxParse .spaceshow{
-	  white-space: pre;
-}
-.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;
-  font-size: 24upx;
-}
-.wxParse .code {
-	overflow: auto;
-	padding: 16upx;
-	white-space: pre;
-	margin: 1em 0upx;
-	background: #f5f5f5;
-	font-size: 24upx;
-}
-
-.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 .text,
-.wxParse .span,
-.wxParse .s {
-  display: inline;
-}
-
-.wxParse .a {
-  color: deepskyblue;
-}
-
-.wxParse .video {
-  text-align: center;
-  margin: 22upx 0;
-}
-
-.wxParse .video-video {
-  width: 100%;
-}
-.wxParse .uni-image{
-	max-width: 100%;
-}
-.wxParse .img {
-  display: block;
-  max-width: 100%;
-  margin-bottom: 0em;/* //与p标签底部padding同时修改 */
-  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: 2em;
-}
-.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 .tablebox{
-	overflow: auto;
-	background-color: #f5f5f5;
-	background: #f5f5f5;
-	font-size: 13px;
-	padding: 8px;
-}
-.wxParse .table .table,.wxParse .table{
-	border-collapse:collapse;
-	box-sizing: border-box;
-	/* 内边框 */
-	/* width: 100%; */
-	overflow: auto;
-	white-space: pre;
-}
-.wxParse .tbody{
-	border-collapse:collapse;
-	box-sizing: border-box;
-	/* 内边框 */
-	border: 1px solid #dadada;
-}
-.wxParse .table  .thead, .wxParse  .table .tfoot, .wxParse  .table .th{
-	border-collapse:collapse;
-	box-sizing: border-box;
-	background: #ececec;
-	font-weight: 40;
-}
-.wxParse  .table .tr {
-	border-collapse:collapse;
-	box-sizing: border-box;
-	/* border: 2px solid #F0AD4E; */
-	overflow:auto;
-}
-.wxParse  .table .th,
-.wxParse  .table .td{
-	border-collapse:collapse;
-	box-sizing: border-box;
-	border: 2upx solid #dadada;
-	overflow:auto;
-}
-.wxParse .audio, .wxParse .uni-audio-default{
-	display: block;
-}

+ 0 - 228
components/gaoyia-parse/parse.vue

@@ -1,228 +0,0 @@
-<!--**
- * forked from:https://github.com/F-loat/mpvue-wxParse
- *
- * github地址: https://github.com/dcloudio/uParse
- *
- * for: uni-app框架下 富文本解析
- * 
- * 优化 by gaoyia@qq.com  https://github.com/gaoyia/parse
- */-->
-
-<template>
-	
-	<!--基础元素-->
-	<div class="wxParse" :class="className" :style="'user-select:' + userSelect">
-		<block v-for="(node, index) of nodes" :key="index" v-if="!loading">
-			<wxParseTemplate :node="node" />
-		</block>
-	</div>
-</template>
-
-<script>
-import HtmlToJson from './libs/html2json';
-import wxParseTemplate from './components/wxParseTemplate0';
-
-	
-	export default {
-		name: 'wxParse',
-		props: {
-			// user-select:none;
-			userSelect: {
-				type: String,
-				default: 'text' //none |text| all | element
-			},
-			imgOptions: {
-				type: [Object, Boolean],
-				default: function() {
-					return {
-						loop: false,
-						indicator: 'number',
-						longPressActions: false
-						// longPressActions: {
-						// 	 itemList: ['发送给朋友', '保存图片', '收藏'],
-						// 		success: function (res) {
-						// 			console.log('选中了第' + (res.tapIndex + 1) + '个按钮');
-						// 		},
-						// 		fail: function (res) {
-						// 			console.log(res.errMsg);
-						// 		}    
-						// 	}
-						// }
-					}
-				}
-			},
-			loading: {
-				type: Boolean,
-				default: false
-			},
-			className: {
-				type: String,
-				default: ''
-			},
-			content: {
-				type: String,
-				default: ''
-			},
-			noData: {
-				type: String,
-				default: '<div style="color: red;">数据不能为空</div>'
-			},
-			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 {
-				nodes: {},
-				imageUrls: [],
-				wxParseWidth: {
-					value: 0
-				}
-			};
-		},
-		computed: {},
-		mounted() {
-			this.setHtml()
-		},
-		methods: {
-			setHtml() {
-				this.getWidth().then((data) => {
-					this.wxParseWidth.value = data;
-				})
-				let {
-					content,
-					noData,
-					imageProp,
-					startHandler,
-					endHandler,
-					charsHandler
-				} = this;
-				let parseData = content || noData;
-				let customHandler = {
-					start: startHandler,
-					end: endHandler,
-					chars: charsHandler
-				};
-				let results = HtmlToJson(parseData, customHandler, imageProp, this);
-
-				this.imageUrls = results.imageUrls;
-				// this.nodes = results.nodes;
-				
-				
-				this.nodes = [];
-				results.nodes.forEach((item) => {
-					setTimeout(() => {
-						this.nodes.push(item);
-					}, 0);
-				})
-			},
-			getWidth() {
-				return new Promise((res, rej) => {
-					// #ifndef MP-ALIPAY || MP-BAIDU
-					uni.createSelectorQuery()
-						.in(this)
-						.select('.wxParse')
-						.fields({
-								size: true,
-								scrollOffset: true
-							},
-							data => {
-								res(data.width);
-							}
-						).exec();
-					// #endif
-					// #ifdef MP-BAIDU
-					const query = swan.createSelectorQuery();
-					query.select('.wxParse').boundingClientRect();
-					query.exec(obj => {
-						const rect = obj[0]
-						if (rect) {
-							res(rect.width);
-						}
-					});
-					// #endif
-					// #ifdef MP-ALIPAY
-					my.createSelectorQuery()
-						.select('.wxParse')
-						.boundingClientRect().exec((ret) => {
-							res(ret[0].width);
-						});
-					// #endif
-				});
-			},
-			navigate(href, $event, attr) {
-				console.log(href, attr);
-				this.$emit('navigate', href, $event);
-			},
-			preview(src, $event) {
-				if (!this.imageUrls.length || typeof this.imgOptions === 'boolean') {
-
-				} else {
-					uni.previewImage({
-						current: src,
-						urls: this.imageUrls,
-						loop: this.imgOptions.loop,
-						indicator: this.imgOptions.indicator,
-						longPressActions: this.imgOptions.longPressActions
-					});
-				}
-				this.$emit('preview', src, $event);
-			},
-			removeImageUrl(src) {
-				const {
-					imageUrls
-				} = this;
-				imageUrls.splice(imageUrls.indexOf(src), 1);
-			}
-		},
-		// 父组件中提供
-		provide() {
-			return {
-				parseWidth: this.wxParseWidth,
-				parseSelect: this.userSelect
-				// 提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。
-			};
-		},
-		watch: {
-			content(){
-				this.setHtml()
-			}
-			// content: {
-			// 	handler: function(newVal, oldVal) {
-			// 		if (newVal !== oldVal) {
-			// 			
-			// 		}
-			// 	},
-			// 	deep: true
-			// }
-		}
-	};
-</script>

+ 86 - 0
components/mao-scroll/mao-scroll.nvue

@@ -0,0 +1,86 @@
+<template>
+	<view>
+		<view class="maoScroll-main" :style="'height:'+(lineHeight*showLine)+'px;'">
+			<view :style="'margin-top:-'+marginTop+'px;'">
+				<view v-for="(item,index) in showdata" :key="'maoScroll'+index" :style="'height:'+lineHeight+'px;'">
+					<slot :line="item" />
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: 'maoScroll',
+		data() {
+			return {
+				showdata: [],
+				marginTop: 0,
+				showLine: 0,
+			}
+		},
+		props:{
+			data: {
+				type: Array,
+				default: []
+			},
+			showNum: {
+				type: Number,
+				default: 3,
+			},
+			lineHeight: {
+				type: Number,
+				default: 60,
+			},
+			animationScroll: {
+				type: Number,
+				default: 500,
+			},
+			animation: {
+				type: Number,
+				default: 2000,
+			}
+		},
+		methods: {
+			init: function(){
+				this.showLine = this.showNum < this.data.length ? this.showNum : this.data.length;
+				for(let i = 0; i < this.data.length; i++){
+					this.showdata.push(this.data[i]);
+				}
+				for(let i = 0; i < this.showLine; i++){
+					this.showdata.push(this.data[i]);
+				}
+				setInterval(this.animationFunc, this.animation);
+			},
+			animationFunc: function(){
+				if(this.marginTop >= this.data.length*this.lineHeight){
+					this.marginTop = 0;
+				}
+				let stepTime = this.animationScroll/this.lineHeight;
+				
+				var step = 0;
+				let self = this;
+				var index = setInterval(function(){
+					self.marginTop = self.marginTop + 1;
+					step++;
+					if (step >= self.lineHeight) {
+						clearInterval(index);
+					}
+				}, stepTime);
+			}
+		},
+		watch: {
+			data(outdata, newdata) {
+				this.init();
+			}
+		}
+	}
+</script>
+
+<style>
+	.maoScroll-main{
+	flex:1;	
+	overflow: hidden;
+	}
+</style>

+ 164 - 0
components/ui-public/customer-wiget.nvue

@@ -0,0 +1,164 @@
+<template>
+	<u-popup :show="isShow" :zIndex="20" mode="center" bgColor="transparent">
+		<view class="pop-body fx-h fx-bc">
+			<view class="top-bg">
+				<image class='image' src="/static/img/tip-bg.png"></image>
+				<text class="text">联系客服</text>
+			</view>
+			<view class="inner fx-h fx-bc">
+				<text class="title">已为您定制专属客服</text>
+				<image src="/static/img/kfu.png" class="kfu-icon"></image>
+				<view class="wx">客服VX:{{ wxName }}</view>
+
+				<view class="btns fx-r fx-bc">
+					<text class="n-btn ihover" @tap="tapCancel">取消</text>
+					<text class="copy-btn ihover" @tap="tapCopy">复制微信</text>
+				</view>
+
+			</view>
+			<view class="m-close">
+				<u-icon @tap="tapCancel" name="close-circle" size="35" color="#fff"></u-icon>
+			</view>
+		</view>
+	</u-popup>
+</template>
+<style lang="scss">
+	.pop-body {
+		position: relative;
+
+		.top-bg {
+			position: absolute;
+			top: -9vw;
+			z-index: 1;
+
+			.image {
+				width: 450rpx;
+				height: 135rpx;
+			}
+
+			.text {
+				position: absolute;
+				width: 100%;
+				height: 100%;
+				top: 0;
+				left: 0;
+				font-size: 32rpx;
+				color: #734227;
+				text-align: center;
+				line-height: 16vw;
+			}
+		}
+
+		.inner {
+			padding: 20px;
+			width: 600rpx;
+			position: relative;
+			background: #fff;
+			border-radius: 20rpx;
+
+			.title {
+				font-size: 32rpx;
+				color: #333333;
+				margin-bottom: 20rpx;
+				padding-top: 20rpx;
+			}
+
+			.wx {
+				font-size: 32rpx;
+				color: #333333;
+				margin-top: 20rpx;
+			}
+
+			.kfu-icon {
+				width: 180rpx;
+				height: 143rpx;
+			}
+
+			.btns {
+				margin-top: 30rpx;
+				flex-direction: row;
+				width: 560rpx;
+				.n-btn {
+					flex: 1;
+					border-radius: 10rpx;
+					border: 2rpx solid #DFC77C;
+					font-size: 32rpx;
+					color: #625120;
+					padding: 0 80rpx;
+					margin-right: 40rpx;
+					height: 80rpx;
+					line-height: 80rpx;
+				}
+
+				.copy-btn {
+					background-image: linear-gradient(-90deg, #D4BA6C, #F9E8A5);
+					border-radius: 10px;
+					font-size: 32rpx;
+					color: #625120;
+					padding: 0 80rpx;
+					height: 80rpx;
+					line-height: 80rpx;
+				}
+			}
+		}
+
+		.m-close {
+			margin-top: 60rpx;
+		}
+	}
+</style>
+
+<script>
+	import Request from '@/library/Request.js'
+	var swH = 0;
+	export default {
+		name: 'ui-customer',
+		props: {},
+		data() {
+			return {
+				isShow: false,
+				wxName: ""
+			}
+		},
+		methods: {
+
+			tapCancel: function() {
+				this.isShow = false;
+			},
+			open() {
+				uni.showLoading({
+					title: '获取数据中..'
+				});
+				console.log(Request, 'Request')
+				Request
+					.post("indexGuLx")
+					.then(res => {
+						uni.hideLoading();
+						if (res.code == 200) {
+							this.wxName = res.data.wxname;
+							this.isShow = true;
+						} else {
+							this.utils.Tip(res.msg);
+						}
+					})
+					.catch((res) => {
+						console.log(res);
+						uni.hideLoading();
+						uni.showModal({
+							title: '系统提示',
+							content: '加载失败,返回在尝试',
+							showCancel: false
+						});
+					});
+
+			},
+
+			tapCopy() {
+				uni.setClipboardData({
+					data: value,
+					showToast: true
+				});
+			}
+		}
+	}
+</script>

+ 73 - 54
components/ui-public/customer-wiget.vue

@@ -1,71 +1,83 @@
 <template>
-		<u-popup :show="isShow" :zIndex="20" mode="center" bgColor="transparent">
-			<view class="pop-body fx-h fx-bc">
-				<view class="top-bg">
-					<image src="/static/img/tip-bg.png"></image>
-					<view class="text">联系客服</view>
-				</view>
-				<view class="inner fx-h fx-bc">
-					<view class="title">已为您定制专属客服</view>
-					<image src="/static/img/kfu.png" class="kfu-icon"></image>
-					<view class="wx">客服VX:{{ wxName }}</view>
-					
-					<view class="btns fx-r fx-bc">
-						<view class="n-btn ihover" @tap="tapCancel">取消</view>
-						<view class="copy-btn ihover" @tap="tapCopy">复制微信</view>
-					</view>
-					
-				</view>
-				<view class="m-close">
-					<u-icon  @tap="tapCancel" name="close-circle" size="35" color="#fff"></u-icon>
+	<u-popup :show="isShow" :zIndex="20" mode="center" bgColor="transparent">
+		<view class="pop-body fx-h fx-bc">
+			<view class="top-bg">
+				<image class='image' src="/static/img/tip-bg.png"></image>
+				<text class="text">联系客服</text>
+			</view>
+			<view class="inner fx-h fx-bc">
+				<text class="title">已为您定制专属客服</text>
+				<image src="/static/img/kfu.png" class="kfu-icon"></image>
+				<view class="wx">客服VX:{{ wxName }}</view>
+
+				<view class="btns fx-r fx-bc">
+					<text class="n-btn ihover" @tap="tapCancel">取消</text>
+					<text class="copy-btn ihover" @tap="tapCopy">复制微信</text>
 				</view>
+
+			</view>
+			<view class="m-close">
+				<u-icon @tap="tapCancel" name="close-circle" size="35" color="#fff"></u-icon>
 			</view>
-		</u-popup>
+		</view>
+	</u-popup>
 </template>
 <style lang="scss">
-	.pop-body{
+	.pop-body {
 		position: relative;
-		.top-bg{
+
+		.top-bg {
 			position: absolute;
 			top: -9vw;
 			z-index: 1;
-			image{
+
+			.image {
 				width: 60vw;
 				height: 18vw;
 			}
-			.text{
-				position: absolute;width: 100%;height: 100%;top: 0;left: 0;
+
+			.text {
+				position: absolute;
+				width: 100%;
+				height: 100%;
+				top: 0;
+				left: 0;
 				font-size: 32rpx;
 				color: #734227;
 				text-align: center;
 				line-height: 16vw;
 			}
 		}
-		.inner{
+
+		.inner {
 			padding: 20px;
 			width: 80vw;
 			position: relative;
 			background: #fff;
 			border-radius: 20rpx;
-			.title{
+
+			.title {
 				font-size: 32rpx;
 				color: #333333;
 				margin-bottom: 20rpx;
 				padding-top: 20rpx;
 			}
-			.wx{
+
+			.wx {
 				font-size: 32rpx;
 				color: #333333;
 				margin-top: 20rpx;
 			}
-			.kfu-icon{
+
+			.kfu-icon {
 				width: 180rpx;
 				height: 143rpx;
 			}
-			
-			.btns{
+
+			.btns {
 				margin-top: 30rpx;
-				.n-btn{
+
+				.n-btn {
 					border-radius: 10rpx;
 					border: 2rpx solid #DFC77C;
 					font-size: 32rpx;
@@ -75,7 +87,8 @@
 					height: 80rpx;
 					line-height: 80rpx;
 				}
-				.copy-btn{
+
+				.copy-btn {
 					background: linear-gradient(-90deg, #D4BA6C 0%, #F9E8A5 100%);
 					border-radius: 10px;
 					font-size: 32rpx;
@@ -86,33 +99,35 @@
 				}
 			}
 		}
-		.m-close{
+
+		.m-close {
 			margin-top: 60rpx;
 		}
 	}
-	
 </style>
 
 <script>
+	import Request from '@/library/Request.js'
 	var swH = 0;
 	export default {
 		name: 'ui-customer',
-		props:{},
+		props: {},
 		data() {
-			return{ 
-				isShow : false,
-				wxName : ""
+			return {
+				isShow: false,
+				wxName: ""
 			}
 		},
 		methods: {
-			
-			tapCancel:function(){
+
+			tapCancel: function() {
 				this.isShow = false;
 			},
-			open(){
-				uni.showLoading({ title: '获取数据中..' });
-				this
-					.request
+			open() {
+				uni.showLoading({
+					title: '获取数据中..'
+				});
+				Request
 					.post("indexGuLx")
 					.then(res => {
 						uni.hideLoading();
@@ -123,21 +138,25 @@
 							this.utils.Tip(res.msg);
 						}
 					})
-					.catch((res)=>{
+					.catch((res) => {
 						console.log(res);
 						uni.hideLoading();
-						uni.showModal({title: '系统提示',content: '加载失败,返回在尝试',showCancel: false});
+						uni.showModal({
+							title: '系统提示',
+							content: '加载失败,返回在尝试',
+							showCancel: false
+						});
 					});
-				
+
 			},
-			
-			tapCopy(){
+
+			tapCopy() {
 				uni.setClipboardData({
-					data : value,
-					showToast : true
+					data: value,
+					showToast: true
 				});
 			}
-			
+
 		}
 	}
-</script>
+</script>

+ 27 - 4
library/Event/SocketEvent.js

@@ -7,14 +7,18 @@
 // +----------------------------------------------------------------------
 // | Author: Mr Table <admin@hjf.pw>
 // +----------------------------------------------------------------------
-import store from "../../store";
+// import store from "../../store";
 import md5 from "../vendor/md5.js";
 import Message from "../socket/Message.js";
-
+// #ifndef APP-PLUS-NVUE
+import store from "@/store/index.js";
+//#endif
 class SocketEvent {
 	app = null;
+	allApp = null;
 	constructor(app) {
 	    this.app = app;
+		this.allApp = getApp()
 	}
 	/**
 	 * 打开项目
@@ -22,7 +26,12 @@ class SocketEvent {
 	 */
 	onOpen(res) {
 		uni.$emit('socketOpen',res);
+		// #ifdef APP-PLUS-NVUE
+		let user = allApp.$store.state.user;
+		//#endif
+		// #ifndef APP-PLUS-NVUE
 		let user = store.state.user;
+		//#endif
 		if(user != null) {
 			this.app.webSocket.send(new Message(user.token,"auth",true),
 			(res)=>{
@@ -38,7 +47,12 @@ class SocketEvent {
 				
 				this.app.request.get("chatBagenum").then(res=>{
 					if(res.code == 200) {
+						// #ifdef APP-PLUS-NVUE
+						allApp.$store.commit('setTxbagenum',res.data);
+						//#endif
+						// #ifndef APP-PLUS-NVUE
 						store.commit('setTxbagenum',res.data);
+						//#endif
 					}
 				}).catch((err)=>{
 					
@@ -51,10 +65,19 @@ class SocketEvent {
 	}
 	onMessage(res) {
 		if(res.code == 'chat' || res.code == 'group_chat') {
+			// #ifdef APP-PLUS-NVUE
+			let bagenum = allApp.$store.state.txbagenum;
+			//bagenum.count ++;
+			bagenum.chatCount ++;
+			allApp.$store.commit('setTxbagenum',bagenum);
+			//#endif
+			// #ifndef APP-PLUS-NVUE
 			let bagenum = store.state.txbagenum;
 			//bagenum.count ++;
 			bagenum.chatCount ++;
 			store.commit('setTxbagenum',bagenum);
+			//#endif
+		
 		}
 		
 		//对方添加好友
@@ -62,9 +85,9 @@ class SocketEvent {
 			if(this.app.utils.isJSON(res.data)) {
 				var data= JSON.parse(res.data);
 				//消息提示
-				let bagenumAr = store.state.txbagenum;
+				let bagenumAr = allApp.$store.state.txbagenum;
 				bagenumAr.applyCount ++;
-				store.commit('setTxbagenum',bagenumAr);
+				allApp.$store.commit('setTxbagenum',bagenumAr);
 			}
 		}
 		

+ 2 - 0
library/Http.js

@@ -94,6 +94,8 @@ class Http {
 				return;
 			}
 		}
+		// console.log(this.baseUrl,'this.baseUrl')
+		// console.log(this.header,'this.header')
 		uni.request({
 			url: this.baseUrl,
 			method: method,

+ 20 - 3
library/Request.js

@@ -5,7 +5,13 @@ import Before from "./interceptors/Before.js";
 import Complete from "./interceptors/Complete.js";
 import Success from "./interceptors/Success.js";
 import Fail from "./interceptors/Fail.js";
-import store from "../store";
+// #ifdef APP-PLUS-NVUE
+const app = getApp()
+//#endif
+// #ifndef APP-PLUS-NVUE
+import store from "@/store/index.js";
+//#endif
+
 let Request = {
 	//全局 监听器 和 拦截器
 	interceptors:function(){
@@ -22,7 +28,12 @@ let Request = {
 	 * @param {Object} name
 	 */
 	getApi:function(name){
+		// #ifdef APP-PLUS-NVUE
+		return app.$store.state.http + api[name];
+		// #endif
+		// #ifndef APP-PLUS-NVUE
 		return store.state.http + api[name];
+		// #endif
 	},
 	/**
 	 * get 提交数据
@@ -84,7 +95,7 @@ let Request = {
 					http.setHeader(i,data.header[i]);
 				}
 			}
-			console.log(name,'data');
+			// console.log(name,'data');
 			//全局监听口
 			let interceptors = this.interceptors();
 			for(var i in interceptors) {
@@ -115,8 +126,14 @@ let Request = {
 			if(name.indexOf('http://') == 0 || name.indexOf("https://") == 0) {
 				url = name;
 			} else {
-				url = store.state.http + api[name];
+				// #ifdef APP-PLUS-NVUE
+				url =  app.$store.state.http + api[name];
+				// #endif
+				// #ifndef APP-PLUS-NVUE
+				 url = store.state.http + api[name];
+				// #endif
 			}
+			// console.log(url,'url')
 			if(method == 'get')
 				http.get(url,post);
 			

+ 1 - 1
library/interceptors/After.js

@@ -7,7 +7,7 @@
 // +----------------------------------------------------------------------
 // | Author: Mr Table <admin@hjf.pw>
 // +----------------------------------------------------------------------
-import store from "../../store";
+
 class After {
 	app = null;
 	constructor(http) {

+ 17 - 17
library/interceptors/Before.js

@@ -7,15 +7,15 @@
 // +----------------------------------------------------------------------
 // | Author: Mr Table <admin@hjf.pw>
 // +----------------------------------------------------------------------
-import store from "../../store";
 import md5 from "../vendor/md5.js";
 import global from "../../config/global.js";
-import { v4 as uuidv4 } from "uuid";
+// import { v4 as uuidv4 } from "uuid";
+import utils from "@/library/utils/Comm.js";
 class Before {
 	app = null;
 	http = null;
 	constructor(http) {
-	    this.app = getApp();
+		this.app = getApp();
 	}
 	/**
 	 * [监听]和[拦截]
@@ -24,41 +24,41 @@ class Before {
 		var salt = global.putoken;
 		var app = getApp();
 		var post = {};
-		post['token'] = store.state.user != null ? store.state.user.token : "";
+		post['token'] =  app.$store.state.user != null ?  app.$store.state.user.token : "";
 		//设备号
-		post['deviceId'] = store.state.uuid;
+		post['deviceId'] = app.$store.state.uuid;
 		//设备类型
 		post["deviceType"] = app.$device.platform == 'ios' ? 2 : 1;
 		//#ifdef APP-PLUS
-			post["fromApp"] = "app";
+		post["fromApp"] = "app";
 		//#endif
-			
+
 		//#ifndef APP-PLUS
-			post["fromApp"] = "h5";
+		post["fromApp"] = "h5";
 		//#endif
-		
+
 		post["mobileType"] = app.$device.model + "(" + app.$device.system + ")";
 		//版本号
 		post["version"] = global.version;
 		//内部版本号
 		post['appCode'] = global.app_code;
 		//校验标码
-		post['noction']  = uuidv4();
+		post['noction'] = utils.uuidv4();
 		//访问时间戳
-		post["timestamp"] =  Date.parse(new Date());
+		post["timestamp"] = Date.parse(new Date());
 		var signAr = [];
-		for(var i in post) {
+		for (var i in post) {
 			signAr.push(i + "=" + post[i]);
 		}
 		var signStr = md5(salt + signAr.join(','));
 		post['sign'] = signStr;
-		for(var i in post) {
-			this.setHeader(i,post[i]);
+		for (var i in post) {
+			this.setHeader(i, post[i]);
 		}
 	}
-	
-	
-	
+
+
+
 }
 
 export default Before;

+ 73 - 63
library/upapp.js

@@ -1,77 +1,87 @@
 import Request from '@/library/Request.js'
-import sotre from "@/store/index.js"
+const getStore =() => {
+	try {
+		const app = getApp()
+		if (app.$store) {
+			return Promise.resolve(app.$store)
+		}
+	} catch (err) {
+		setTimeout(() => {
+			getStore()
+		}, 200)
+	}
+}
 export default function initSys() {
 	const appType = uni.getSystemInfoSync().platform;
 	if (plus != null) {
-		plus.runtime.getProperty(plus.runtime.appid, (wgtinfo) => {
+		plus.runtime.getProperty(plus.runtime.appid, async (wgtinfo) => {
 			try {
 				let versionCode = wgtinfo.versionCode;
 				let arr1 = wgtinfo.version.split('.');
+				const sotre = await getStore();
+				// console.log(sotre,'sotre')
 				sotre.commit('chnangeSshowData', false);
-				Request.post("sysInit", {
-						code: versionCode
-					})
-					.then(res => {
-						if (res.code == 200) {
-							if (appType != "ios") {
-								if (wgtinfo.versionCode < res.data.app_code) {
-									sotre.commit('chnangeSshowData', false);
-									const upDater = uni.requireNativePlugin("CL-UpDater");
-									let options = {
-										title: "升级",
-										con: res.data.app_update,
-										downUrl: encodeURI(res.data.android_url), //必填
-										hidCancelbtn: false, //是否隐藏取消按钮;
-										btnBgColor: "#ff3300", //设置按钮背景色颜色
-										updateBtnText: "升级", //升级按钮文字,默认为立即升级
-										topImgBg: "", //非必填,用于自定义;自定义方法请参考demo,
-										downMsgTip: "资源下载中,请稍后...", //可选(android)
-									};
-									if (res.data.android_url.indexOf('.wgt') > -1) {
-										plus.io.requestFileSystem(plus.io.PUBLIC_DOWNLOADS, function(fobject) {
-											//设置文件下载根路径;	
-											let rootPath = fobject.root.fullPath;
-											upDater.wgtUpdate(options, rootPath, result => {
-												if (result) {
-													var pathNew = plus.io
-														.convertAbsoluteFileSystem(result);
-													plus.runtime.install(pathNew, {
-														force: false
-													}, function() {
-														//进行重新启动;
-														plus.runtime.restart();
-													}, (e) => {
-														uni.showToast({
-															title: '安装升级包失败' +
-																JSON
-																.stringify(
-																	e),
-															icon: 'none'
-														})
-													});
-												}
-											}, () => {
-												console.log("弹框关闭了");
+				const res = await Request.post("sysInit", {
+					code: versionCode
+				})
+				if (res.code == 200) {
+					if (appType != "ios") {
+						if (wgtinfo.versionCode < res.data.app_code) {
+							sotre.commit('chnangeSshowData', false);
+							const upDater = uni.requireNativePlugin("CL-UpDater");
+							let options = {
+								title: "升级",
+								con: res.data.app_update,
+								downUrl: encodeURI(res.data.android_url), //必填
+								hidCancelbtn: false, //是否隐藏取消按钮;
+								btnBgColor: "#ff3300", //设置按钮背景色颜色
+								updateBtnText: "升级", //升级按钮文字,默认为立即升级
+								topImgBg: "", //非必填,用于自定义;自定义方法请参考demo,
+								downMsgTip: "资源下载中,请稍后...", //可选(android)
+							};
+							if (res.data.android_url.indexOf('.wgt') > -1) {
+								plus.io.requestFileSystem(plus.io.PUBLIC_DOWNLOADS, function(fobject) {
+									//设置文件下载根路径;	
+									let rootPath = fobject.root.fullPath;
+									upDater.wgtUpdate(options, rootPath, result => {
+										if (result) {
+											var pathNew = plus.io
+												.convertAbsoluteFileSystem(result);
+											plus.runtime.install(pathNew, {
+												force: false
+											}, function() {
+												//进行重新启动;
+												plus.runtime.restart();
+											}, (e) => {
+												uni.showToast({
+													title: '安装升级包失败' +
+														JSON
+														.stringify(
+															e),
+													icon: 'none'
+												})
 											});
-										})
-									} else {
-										options.hidBackBtn = true
-										options.verCode = res.data
-											.app_version //最新apk版本号 可选  用于Android整包升级,避免用户取消安装apk时,重复下载问题,如果不传的话,apk将会重新下载,
-										options.goBackBtnText = "进入后台" //可选(android)
-										//必需提供下载地址;
-										upDater.startUpdate(options, () => {});
-									}
-								} else if (wgtinfo.versionCode > res.data.app_code) {
-									sotre.commit('chnangeSshowData', false);
-								} else {
-									sotre.commit('chnangeSshowData', true);
-								}
+										}
+									}, () => {
+										console.log("弹框关闭了");
+									});
+								})
+							} else {
+								options.hidBackBtn = true
+								options.verCode = res.data
+									.app_version //最新apk版本号 可选  用于Android整包升级,避免用户取消安装apk时,重复下载问题,如果不传的话,apk将会重新下载,
+								options.goBackBtnText = "进入后台" //可选(android)
+								//必需提供下载地址;
+								upDater.startUpdate(options, () => {});
 							}
+						} else if (wgtinfo.versionCode > res.data.app_code) {
+							sotre.commit('chnangeSshowData', false);
+						} else {
+							sotre.commit('chnangeSshowData', true);
 						}
-					}).catch((err) => {
-						console.log(err)
-					});
+					}
+				}
+
 				if (appType == "ios") {
 					const getIosUpAppUrl = 'https://itunes.apple.com/cn/lookup?id=6474850968'
 					const iosAppStroeUrl =

Diferenças do arquivo suprimidas por serem muito extensas
+ 477 - 372
library/utils/Comm.js


+ 4 - 9
main.js

@@ -3,25 +3,20 @@ import App from './App'
 import Container from "library/Container.js";
 import Base from "library/Base.js";
 import uView from '@/uni_modules/uview-ui/index.js';
-
+import store from './store'
 
 Vue.use(uView);
-
 //导航栏组件
-
-   
-
 import headers from './components/ui-header/index.vue'
-Vue.component('page-head',headers)  
-   
-  
+Vue.component('page-head',headers)
 //使用伪装容器  
 let $c = Container.getInstance();
 let base = new Base(Vue,$c);
-let store = $c.make(require("./store"),"store");
+// #ifndef APP-PLUS-NVUE
 Vue.prototype.$store = store; //stroe  
 store.commit("init",Vue.prototype);
 Vue.prototype.base = base;
+// #endif
 //设置为 false 以阻止 vue 在启动时生成生产提示
 Vue.config.productionTip = false;
 App.mpType = 'app';  

+ 1 - 1
manifest.json

@@ -3,7 +3,7 @@
     "appid" : "__UNI__EFA020E",
     "description" : "一款电商App",
     "versionName" : "3.4.2",
-    "versionCode" : 342,
+    "versionCode" : 336,
     "transformPx" : false,
     /* 5+App特有相关 */
     "app-plus" : {

+ 0 - 4
pages/guild/index.vue

@@ -231,7 +231,6 @@
 <script>
 	import appConfig from '../../config/global.js'
 	import countdown from "@/components/cz-countdown/cz-countdown.vue";
-	import uParse from '@/components/gaoyia-parse/parse.vue';
 	import {
 		mapState,
 		mapMutations
@@ -239,7 +238,6 @@
 	export default {
 		components: {
 			countdown,
-			uParse
 		},
 		computed: mapState(['user', 'sysData']),
 		data() {
@@ -451,8 +449,6 @@
 </script>
 
 <style lang="scss">
-	@import url("/components/gaoyia-parse/parse.css");
-
 	.app-bg {
 		position: absolute;
 

+ 1 - 4
pages/guild/itemIndex.vue

@@ -198,7 +198,7 @@
 			<view class="body fx-h fx-bc fx-ac">
 				<view style="font-size: 16px;color: #333;font-weight: bold;padding-bottom: 25rpx;">CBB产品订单购买协议</view>
 				<scroll-view scroll-y class="max-he">
-					<u-parse :content="topData.content" :noData="topData.content" />
+					<uv-parse :content="topData.content"></uv-parse>
 				</scroll-view>
 				<view class="btn" @tap="tapAction">已阅读并同意</view>
 			</view>
@@ -225,12 +225,10 @@
 
 <script>
 	import countdown from "@/components/cz-countdown/cz-countdown.vue";
-	import uParse from '@/components/gaoyia-parse/parse.vue';
 	import { mapState, mapMutations } from 'vuex';
 	export default {
 		components: {
 			countdown,
-			uParse
 		},
 		computed: mapState(['user','sysData']),
 		data() {
@@ -442,7 +440,6 @@
 </script>
 
 <style lang="scss">
-	@import url("/components/gaoyia-parse/parse.css");
 	.app-bg{
 		position: absolute;
 		image{width: 100vw;height: 45vw;}

+ 11 - 7
pages/index/home.nvue

@@ -4,8 +4,8 @@
 			<image class='logo' src="/static/img/logo.png"></image>
 		</view>
 		<view class="app-foot">
-			<view class='footer-text'>中国长城计算机进出口公司</view>
-			<view>京ICP备2024043848号-3A</view>
+			<text class='footer-text'>中国长城计算机进出口公司</text>
+			<text class='footer-text2'>京ICP备2024043848号-3A</text>
 		</view>
 	</view>
 </template>
@@ -20,17 +20,19 @@
 		data() {
 			return {
 				fadeIn: "fadeIn",
-				netIndex: 0
+				netIndex: 0,
+				
 			}
 		},
 		onLoad(options) {
 			this.getNetWord();
+			this.$store.commit("init",this);
 		},
 		methods: {
 			...mapMutations(['setHttp']),
 			getNetWord: function() {
 				let httpAr = global.apiHttps;
-				console.log(this.setHttp,'2333')
+				// console.log(this.setHttp,'2333')
 				if (this.netIndex >= httpAr.length) {
 					this.setHttp(httpAr[this.netIndex - 1]);
 					this.getHome();
@@ -101,13 +103,15 @@
 		position: absolute;
 		bottom: 40px;
 		width: 750rpx;
-		color: #333;
-		font-size: 14px;
 		justify-content: center;
 		align-items: center;
 	}
 	.footer-text{
-		font-size: 24rpx;
+		font-size: 24px;
+	}
+	.footer-text2{
+		color: #333;
+		font-size: 14px;
 	}
 	.fadeIn{
 		opacity: 1;

+ 1120 - 0
pages/index/index.nvue

@@ -0,0 +1,1120 @@
+<template>
+	<view class="app">
+		<view class="app-top">
+			<view class="atop1">
+				<view class="top2-bg">
+					<view class="top3-bg">
+					</view>
+				</view>
+			</view>
+			<view class="atop2"></view>
+		</view>
+		<view class="top mtop">
+			<view class="top-body">
+				<view class="statusBar" :style="'height:'  + statusBarHeight + 'px'"></view>
+				<view class="panel">
+					<view class="inputIndex">
+						<navigator url="../goods/list">
+							<view class="inputText fx-ac fx-bc fx-r">
+								<text class="text">请输入关键词搜索</text>
+							</view>
+						</navigator>
+					</view>
+					<image src="/static/img/ic_information.png" class="information"></image>
+				</view>
+			</view>
+		</view>
+		<view class="app-body">
+			<view class="swiper-img">
+				<u-swiper radius='20rpx' :list="topData.banner" keyName="img" bgColor="transparent" indicator
+					indicatorActiveColor="#DB292B" indicatorInactiveColor="#B7B7B7" previousMargin="15" nextMargin="15"
+					indicatorMode="line"></u-swiper>
+			</view>
+			<view class="grid-bg">
+				<!--九宫格-->
+				<view class="grid gridItem" v-if="sysData.is_audit == 0">
+					<view class="item" @tap="tapOpen2" data-url="/pages/user/order/index">
+						<image class="icon" mode="aspectFill" src="/static/ad/1.png"></image>
+						<text class="label">提货订单</text>
+					</view>
+
+					<view class="item" @tap="$refs.customer.open()">
+						<image class="icon" mode="aspectFill" src="/static/ad/2.png"></image>
+						<text class="label">联系客服</text>
+					</view>
+
+					<view class="item" @tap="tapOpen" data-url="/pages/merchant/list">
+						<image class="icon" mode="aspectFill" src="/static/ad/3.png"></image>
+						<text class="label">附近门店</text>
+					</view>
+
+					<view class="item" @tap="tapOpen2" data-url="/pages/guild/order/order">
+						<image class="icon" mode="aspectFill" src="/static/ad/4.png"></image>
+						<text class="label">抢购订单</text>
+					</view>
+
+					<view v-if="showData" class="item" @tap="tapOpen" data-url="/pages/news/about">
+						<image class="icon" mode="aspectFill" src="/static/ad/5.png"></image>
+						<text class="label">关于我们</text>
+					</view>
+				</view>
+				<!--消息通知-->
+				<view class="news-panel" v-if="showData">
+					<image src="/static/img/news-icon.png" class="icon"></image>
+					<text class="label">消息通知</text>
+					<maoScroll class="box-panel" :data="topData.notice" :showNum="1" :lineHeight="15"
+						:animationScroll="800" :animation="4000">
+						<template v-slot="{line}">
+							<text @tap="tapOpen" :data-url="'../news/index?id=' + line.id" class="line">{{ line.title }}
+							</text>
+						</template>
+					</maoScroll>
+					<image @tap="tapNews" src="/static/img/news-right.png" class="icon-right"></image>
+				</view>
+			</view>
+			<view v-if="sysData.is_audit == 0">
+				<!--CBB潮贝抢货通道-->
+				<view class="team-buy" @tap="tapOpen" data-url="/pages/guild/itemIndex" v-if="guData!= null">
+					<image class="teamBg" src="@/static/img/indexChatBg1.png" mode="widthFix"></image>
+					<view class="team-show">
+						<view class="labelBox">
+							<text class="label">{{ guData.name }}</text>
+						</view>
+						<view class="guNicknameBox">
+							<text class="guNickname">
+								总监:{{ guData.nickname }}
+							</text>
+						</view>
+
+					</view>
+				</view>
+			</view>
+			<!--提货好物-->
+			<view class="goods">
+				<view class="top">
+					<image class="topBgImg" src="@/static/img/index_item_bg.png" mode="widthFix"></image>
+					<view class="goodsTitle">
+						<image class="icon" src="/static/img/goods-icon.png"></image>
+						<text class="label">货款提货</text>
+						<view class="fx-g1"></view>
+						<view class="more" @tap="tapOpen" data-url="../goods/list">
+							<text class="text">查看更多></text>
+						</view>
+					</view>
+				</view>
+				<view class="goods-items">
+					<view @tap="tapOpen" :data-url="'../goods/index?id=' + item.id" class="item"
+						v-for="(item,index) in topData.product">
+						<image :src="item.img" mode="aspectFill" class="nimg"></image>
+						<text class="title">
+							{{ item.title }}
+						</text>
+						<view class="iview">
+							<view class="score">
+								<text>
+									{{ item.commission }}
+								</text>
+								<text class="tag" v-if="sysData.is_audit == 0">积分</text>
+								<text class="tag" v-else>参考价</text>
+							</view>
+							<view class="fx-g1"></view>
+							<text class="btn" v-if="sysData.is_audit == 0">提货</text>
+						</view>
+					</view>
+				</view>
+			</view>
+			<!--最新门店-->
+			<view class="shop" v-if="sysData.is_audit == 0">
+				<view class="top">
+					<image class="icon" src="/static/img/shop-icon.png"></image>
+					<text class="label">易趣C果园({{merchantCount}}家)</text>
+					<view class="fx-g1"></view>
+					<view class="more" @tap="tapOpen" data-url="../merchant/list">
+						<text class="text">查看更多></text>
+						<image class="prv" src="/static/img/ic-pev.png"></image>
+					</view>
+				</view>
+				<view class="shop-items">
+					<view class="item fx-r" @tap="tapOpen" :data-url="'../merchant/index?id=' + item.id"
+						v-for="item in merchantData">
+						<image :src="item.logo" class="img" mode="aspectFill"></image>
+						<view class="info">
+							<view class="iview-top fx-r">
+								<text class="title">{{ item.name }}</text>
+								<view class="fx-g1"></view>
+								<view class="ii">
+									<image src="/static/img/shop-small-location.png"></image>
+									<text>
+										距离{{ item.dis_km }}
+									</text>
+								</view>
+							</view>
+							<view class="address">
+								<image src="/static/img/address-shop.png" class="icon"></image>
+								<text class="value">{{ item.address }}</text>
+							</view>
+
+							<view class="tel">
+								<image src="/static/img/tel-shop.png" class="icon"></image>
+								<text class="value">{{ item.tel }}</text>
+							</view>
+
+							<view class="business">
+								营业时间: {{ item.business }}
+							</view>
+
+							<view class="wiget">
+								<view class="witem" @tap.stop="tapTel(item)">
+									<image src="/static/img/shop-tel.png"></image>
+									<text class="text">电话</text>
+								</view>
+								<view class="witem" @tap.stop="tapLbs(item)">
+									<image src="/static/img/shop-location.png"></image>
+									<text class="text">导航</text>
+								</view>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+		<customer-wiget ref="customer"></customer-wiget>
+		<view class="popwin" v-if="isGg && showData">
+			<view class="bg"></view>
+			<view class="body">
+				<image src="/static/img/index_bg.png" class="bgimg"></image>
+				<view class="inner-top"></view>
+				<view class="inner">
+					<text class="inner-title">
+						{{title}}
+					</text>
+					<scroll-view scroll-y class="max-he">
+						<uv-parse :content="content"></uv-parse>
+					</scroll-view>
+					<view class="foot">
+						<view class="btnBox">
+							<text class="btn" @tap="tapYGdBtn">未阅读,去查看></text>
+						</view>
+					</view>
+				</view>
+				<view class="m-close">
+					<image src="/static/img/btn-close.png" @tap="isGg = false" class="icon"></image>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import maoScroll from '@/components/mao-scroll/mao-scroll.nvue';
+	import customerWiget from '@/components/ui-public/customer-wiget.nvue';
+	import upApp from "@/library/upapp.js"
+	import {
+		mapState,
+		mapMutations
+	} from 'vuex';
+	import request from '@/library/Request.js'
+	import utils from "@/library/utils/Comm.js"
+	export default {
+		computed: {
+			...mapState(['user', 'lbs', 'sysData', 'showData']),
+		},
+		components: {
+			maoScroll,
+			customerWiget
+		},
+		watch: {
+			showData(newValue, oldValue) {
+				this.isShowInit(newValue);
+			}
+		},
+		data() {
+			return {
+				statusBarHeight: 20,
+				windowHeight: 0,
+				topData: {
+					banner: [],
+					notice: [],
+					product: []
+				},
+				content: "",
+				title: '',
+				isGg: false,
+				gGid: 0,
+				merchantData: [],
+				guData: null,
+				merchantCount: 0,
+				page: {
+					page: 1,
+					isLoad: false,
+					isFoot: false
+				},
+				wenAr: {},
+				isLoad: false,
+				maxheight: 0, //滚轮页面高度
+			}
+		},
+		onLoad() {
+			this.initView();
+			this.isShowInit(false);
+			upApp();
+		},
+		onReady() {
+			const that = this;
+			uni.getSystemInfo({
+				success: (resu) => {
+					try {
+						// console.log(resu,'resu')
+						that.statusBarHeight = resu.statusBarHeight;
+						// that.windowHeight = resu.windowHeight
+					} catch (err) {
+						console.log(err)
+					}
+				}
+			});
+		},
+		onReachBottom() {
+			if (this.page.isFoot || this.page.isLoad) {
+				return;
+			}
+			this.page.page++;
+			this.getData();
+		},
+		methods: {
+			// ...mapMutations(['setSys', 'getGps']),
+			AllApp() {
+				return getApp()
+			},
+			isShowInit(type) {
+				// console.log(type,'type11111111111111')
+				uni.setTabBarItem({
+					index: 1,
+					visible: type,
+				});
+				uni.setTabBarItem({
+					index: 2,
+					visible: type,
+				})
+			},
+			initView: function() {
+				const that = this
+				request
+					.post("indexInit")
+					.then(res => {
+						// console.log(res,'resssss')
+						uni.hideLoading();
+						if (res.code == 200) {
+							this.isLoad = true;
+							this.topData = res.data;
+							this.getData(true);
+							//获取文票系统
+							this.wenpiao();
+							if (this.user != null) {
+								//获取自己场馆
+								this.getGu();
+							}
+							let gongId = uni.getStorageSync('gonggao') || '';
+							if (this.topData.notice.length > 0 && gongId != this.topData.notice[0].id) {
+								this.gGid = this.topData.notice[0].id;
+								this.getNoticPop(this.topData.notice[0].id);
+							}
+
+						}
+					})
+					.catch(err => {
+						utils.Tip("加载失败,重新点击尝试!" + JSON.stringify(error));
+						uni.hideLoading();
+					});
+				this.initSys();
+			},
+			/** 
+			 * 基本逻辑
+			 */
+			initSys: function() {
+				const that = this;
+				if (plus != null) {
+					plus.runtime.getProperty(plus.runtime.appid, (wgtinfo) => {
+						let versionCode = wgtinfo.versionCode;
+						request.post("sysInit", {
+								code: versionCode
+							})
+							.then(res => {
+								if (res.code == 200) {
+									that.$store.commit('setSys', res.data)
+									if (res.data.is_audit == 0) {
+										that.$store.commit('getGps', {
+											page: that,
+											fn: (res) => {
+												console.log(res);
+												that.getData(true);
+											},
+											err: (err) => {
+												that.getData(true);
+											}
+										})
+									}
+								}
+							});
+					});
+				}
+			},
+			/**
+			 * 重要提示
+			
+			 * @param {Object} id
+			 */
+			getNoticPop: function(id) {
+				request
+					.post("getContent", {
+						id: id
+					})
+					.then(res => {
+						// console.log(res);
+						if (res.code == 200) {
+							this.title = res.data.title;
+							this.content = res.data.content;
+							console.log(this.content, 'this.content')
+							this.isGg = true;
+
+						}
+					})
+					.catch(err => {
+						utils.Tip("网络错误,请稍后尝试");
+					});
+			},
+			/**
+			 * 确认阅读公告
+			 */
+			tapYGdBtn: function() {
+				this.isGg = false;
+				uni.setStorageSync('gonggao', this.gGid);
+				uni.navigateTo({
+					url: '../news/index?id=' + this.gGid
+				});
+			},
+			wenpiao: function() {
+				request
+					.get("tradeweb")
+					.then(res => {
+						uni.hideLoading();
+						if (res.code == 200) {
+							this.wenAr = res.data;
+						}
+					});
+			},
+
+			/**
+			 * 最新门店
+			 */
+			getData: function(isPull = false) {
+				if (this.page.isLoad) return;
+				this.page.isLoad = true;
+				var post = {};
+				post.page = this.page.page;
+				//gps位置
+				if (this.lbs != null) {
+					post.lat = utils.isDefine(this.lbs.latitude) ? this.lbs.latitude : 0;
+					post.lng = utils.isDefine(this.lbs.longitude) ? this.lbs.longitude : 0;
+				}
+				request
+					.post("merchants", post)
+					.then(res => {
+						uni.hideLoading();
+						if (res.code == 200) {
+							this.merchantData = res.data.result;
+							this.merchantCount = res.data.count;
+							this.page.isFoot = true;
+							this.isLoad = true;
+						}
+					})
+					.catch(error => {
+						console.log(error);
+						uni.hideLoading();
+						utils.Tip("加载失败,重新点击尝试!");
+					});
+			},
+
+			getGu: function() {
+				request
+					.get("indexGu")
+					.then(res => {
+						uni.hideLoading();
+						if (res.code == 200) {
+							this.guData = res.data.guData;
+						}
+					});
+			},
+
+			/**
+			 * 更多
+			 */
+			tapNews: function() {
+				uni.navigateTo({
+					url: "../news/list"
+				});
+			},
+			/**
+			 * 打开
+			 * @param {Object} ev
+			 */
+			tapOpen: function(ev) {
+				let url = ev.currentTarget.dataset.url;
+				utils.navigateTo(url);
+			},
+			/**
+			 * 打开
+			 * @param {Object} ev
+			 */
+			tapOpen2: function(ev) {
+				let url = ev.currentTarget.dataset.url;
+				if (!utils.isDefine(this.user)) {
+					uni.navigateTo({
+						url: "/pages/login/index"
+					});
+					return;
+				}
+				utils.navigateTo(url);
+			},
+
+			/**
+			 * 拨打电话
+			 */
+			tapTel: function(item) {
+				if (item.tel == null) {
+					utils.showAlert({
+						title: "系统提示",
+						content: "暂无联系方式"
+					});
+					return;
+				}
+				const info = uni.getSystemInfoSync();
+				if (info.platform == 'android') {
+					uni.showModal({
+						content: "确认拨打" + item.tel,
+						confirmText: "确定",
+						cancelText: "取消",
+						success: (res) => {
+
+							if (res.confirm) {
+								this.$store.dispatch('permission/requestPermissions', 'CALL_PHONE').then(
+									res => {
+										if (res !== 1) return;
+										uni.makePhoneCall({
+											phoneNumber: item.tel,
+											fail: (res) => {
+												console.log("aaaaa");
+												uni.showModal({
+													content: JSON.stringify(res)
+												});
+											},
+											complete: (xx) => {
+												console.log(xx);
+												console.log("ad");
+											}
+										});
+									});
+
+
+							}
+						}
+					});
+				} else {
+					uni.makePhoneCall({
+						phoneNumber: item.tel,
+					});
+				}
+			},
+			tapLbs: function(item) {
+				uni.openLocation({
+					latitude: Number(item.lat),
+					longitude: Number(item.lng),
+					name: item.name,
+					address: item.address,
+					scale: 12,
+					success: function() {
+						console.log('success');
+					},
+					fail: function(res) {
+
+					},
+				})
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.swiper-box {}
+
+	.app {
+		background-color: #FEF7F5;
+	}
+
+	.mtop {
+		margin-top: -500rpx;
+		width: 750rpx;
+	}
+
+	.app-top {
+		height: 500rpx;
+		width: 750rpx;
+
+		.atop1 {
+			height: 500rpx;
+
+			.top2-bg {
+				height: 500rpx;
+				width: 750rpx;
+				background-image: linear-gradient(to right, #FEA9C8, #FFA18E);
+
+				.top3-bg {
+					height: 500rpx;
+					width: 750rpx;
+					background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0), #FEF7F5);
+				}
+			}
+		}
+
+		.atop2 {
+			background: #fff;
+		}
+	}
+
+	.top {
+		.top-body {
+			.panel {
+				flex: 1;
+				flex-direction: row;
+				align-items: center;
+				padding: 20rpx 30rpx;
+
+				.inputIndex {
+					flex: 1;
+					z-index: 2;
+					padding-right: 20rpx;
+
+					.inputText {
+						background-color: #FFF;
+						border-radius: 100px;
+						line-height: 1;
+						padding: 20rpx;
+					}
+
+					.text {
+						font-size: 24rpx;
+						color: rgba(170, 170, 170, 1);
+					}
+				}
+
+				.index-img {
+					width: 80rpx;
+					height: 48rpx;
+				}
+
+				.information {
+					width: 38rpx;
+					height: 38rpx;
+				}
+			}
+		}
+	}
+
+	//消息信息
+	.grid-bg {
+		padding: 0 32rpx;
+		margin-top: 20rpx;
+
+		.gridItem {
+			background-color: #FFF;
+			padding: 20rpx 0;
+			border-radius: 20rpx;
+			flex-direction: row;
+			justify-content: space-between;
+			align-items: center;
+		}
+
+		//9宫格
+		.grid {
+			.item {
+				width: 140rpx;
+				flex: 1;
+				align-items: center;
+
+				.icon {
+					width: 90rpx;
+					height: 90rpx;
+				}
+
+				.label {
+					font-size: 12px;
+					color: #0C1732;
+					margin-top: 16rpx;
+				}
+			}
+		}
+
+		//消息通知
+		.news-panel {
+			padding: 15rpx 20rpx;
+			background-color: #FFF;
+			margin-top: 20rpx;
+			border-radius: 20rpx;
+			flex-direction: row;
+			justify-content: center;
+			align-items: center;
+
+			.box-panel {
+				flex: 1;
+			}
+
+			.titleBorder {
+				border: 1px solid #000;
+			}
+
+			.icon {
+				width: 28rpx;
+				height: 28rpx;
+			}
+
+			.label {
+				margin: 0px 10rpx;
+				font-weight: bold;
+				font-size: 26rpx;
+				color: #FF4C4C;
+			}
+
+			.icon-right {
+				width: 30rpx;
+				height: 6rpx;
+			}
+
+			.line {
+				height: 15px;
+				line-height: 15px;
+				color: #575656;
+				font-size: 26rpx;
+				text-overflow: ellipsis;
+				lines: 1;
+			}
+		}
+	}
+
+	//抢货通道
+	.team-buy {
+		margin: 15rpx 10rpx 20rpx 30rpx;
+		height: 240rpx;
+		width: 690rpx;
+
+		.teamBg {
+			width: 690rpx;
+			height: 240rpx;
+		}
+
+		.team-show {
+			color: #fff;
+			padding-top: 70rpx;
+			margin-top: -240rpx;
+			align-items: center;
+			justify-content: center;
+
+			.labelBox {
+				padding-top: 30rpx;
+
+				.label {
+					line-height: 45rpx;
+					font-weight: bold;
+					font-size: 45rpx;
+					color: #FFF;
+				}
+			}
+
+			.guNicknameBox {
+				margin-top: 20rpx;
+				padding: 8rpx 30rpx;
+				background-color: #fffdf1;
+				border: 1px solid #EDC299;
+				border-radius: 100rpx;
+				box-shadow: 4rpx 4rpx 10rpx 2rpx #FCD3BF inset;
+			}
+
+			.guNickname {
+				font-weight: bold;
+				font-size: 28rpx;
+				color: #EA3616;
+			}
+		}
+
+	}
+
+	//产品中心
+	.goods {
+		padding: 20rpx 30rpx;
+
+		.goodsTitle {
+			margin-top: -60rpx;
+			flex:1;
+			position: relative;
+			flex-direction: row;
+			padding-left: 86rpx;
+			padding-right: 50rpx;
+		}
+
+		.topBgImg {
+			width: 690rpx;
+			height: 74rpx;
+		}
+
+		.top {
+			height: 74rpx;
+			.icon {
+				width: 96rpx;
+				height: 123rpx;
+				position: absolute;
+				top: -24rpx;
+				left: 0;
+			}
+
+			.label {
+				color: #fff;
+				font-weight: bold;
+				font-size: 34rpx;
+				margin-left: 12rpx;
+				align-self: self-start;
+				padding-top: 16rpx;
+			}
+
+			.more {
+				align-self: self-start;
+				padding-top: 30rpx;
+
+				.text {
+					color: #FFF;
+					font-size: 24rpx;
+				}
+
+				.prv {
+					width: 10rpx;
+					height: 20rpx;
+					margin-left: 8rpx;
+				}
+			}
+
+		}
+
+		.goods-items {
+			border: 2px solid rgba(255, 51, 44, 1);
+			border-bottom-left-radius: 20rpx;
+			border-bottom-right-radius: 20rpx;
+			padding: 20rpx;
+
+			.item {
+				// width: calc(50% - 10rpx);
+				margin-right: 10rpx;
+				margin-bottom: 20rpx;
+				box-shadow: 0px 0px 20px 0px rgba(50, 50, 52, 0.1);
+				border-radius: 20rpx;
+				background-color: #FFF;
+
+				.nimg {
+					// width: 100%;
+					// height: calc(50vw - 20rpx - 10rpx);
+					border-radius: 20rpx 20rpx 0 0;
+				}
+
+				.title {
+					padding: 20rpx;
+					height: 80rpx;
+					text-overflow: ellipsis;
+					lines: 1;
+					color: #333333;
+					font-weight: bold;
+					font-size: 30rpx;
+				}
+
+				.iview {
+					padding: 20rpx;
+
+					.score {
+						color: #FF4C4C;
+						font-weight: bold;
+						font-size: 36rpx;
+
+						.tag {
+							font-weight: bold;
+							font-size: 18rpx;
+							color: #FF4C4C;
+						}
+					}
+
+					.btn {
+						background-image: linear-gradient(143.2747deg, #FF6A00, #EE0979);
+						border-radius: 40rpx;
+						padding: 10rpx 28rpx;
+						font-size: 22rpx;
+						color: #FFFFFF;
+					}
+				}
+			}
+		}
+	}
+
+	//最新门店
+	.shop {
+		padding: 20rpx 30rpx;
+
+		.top {
+			height: 74rpx;
+			padding-left: 86rpx;
+			padding-right: 50rpx;
+
+
+			.icon {
+				width: 96rpx;
+				height: 123rpx;
+				position: absolute;
+				top: -24rpx;
+				left: 0;
+			}
+
+			.label {
+				color: #fff;
+				font-weight: bold;
+				font-size: 34rpx;
+				margin-left: 12rpx;
+				align-self: self-start;
+				padding-top: 16rpx;
+			}
+
+			.more {
+				align-self: self-start;
+				padding-top: 30rpx;
+
+				.text {
+					color: #FFF;
+					font-size: 24rpx;
+				}
+
+				.prv {
+					width: 10rpx;
+					height: 20rpx;
+					margin-left: 8rpx;
+				}
+			}
+
+		}
+
+		.shop-items {
+			border: 2px solid rgba(255, 51, 44, 1);
+			border-bottom-left-radius: 20rpx;
+			border-bottom-right-radius: 20rpx;
+			padding: 20rpx;
+
+			.item {
+				margin-top: 30rpx;
+
+				.img {
+					width: 180rpx;
+					height: 180rpx;
+					border-radius: 10rpx;
+				}
+
+				.info {
+					position: relative;
+					margin-left: 20rpx;
+					// width: calc(100% - 200rpx);
+					border-bottom: 1px solid #F8F8F8;
+
+					.title {
+						font-weight: bold;
+						font-size: 34rpx;
+						color: #333333;
+						// width: calc(60%);
+						text-overflow: ellipsis;
+						lines: 1;
+					}
+
+					.ii {
+						// image {
+						// 	width: 20rpx;
+						// 	height: 28rpx;
+						// 	margin-right: 8rpx
+						// }
+
+						font-weight: 400;
+						font-size: 22rpx;
+						color: #666666;
+
+					}
+
+					.address {
+						.icon {
+							width: 28rpx;
+							height: 28rpx;
+						}
+
+						.value {
+							font-size: 22rpx;
+							color: #666666;
+							opacity: 0.5;
+							margin-left: 10rpx;
+						}
+
+						margin-top: 20rpx;
+					}
+
+					.tel {
+						margin-top: 16rpx;
+
+						.icon {
+							width: 28rpx;
+							height: 28rpx;
+						}
+
+						.value {
+							font-size: 22rpx;
+							color: #666666;
+							opacity: 0.5;
+							margin-left: 10rpx;
+						}
+					}
+
+					.business {
+						font-weight: bold;
+						font-size: 24rpx;
+						color: #FF6F0F;
+						background: rgba(252, 243, 240, 0.8);
+						border-radius: 16rpx 16rpx 16rpx 0px;
+						padding: 10rpx 10rpx;
+						margin-bottom: 20rpx;
+					}
+
+					.wiget {
+						position: absolute;
+						right: 0;
+						bottom: 20rpx;
+
+						.wline {
+							margin: 0px 24rpx;
+							height: 40rpx;
+							width: 1px;
+							background: #eee;
+
+						}
+
+						.witem {
+							// image {
+							// 	width: 46rpx;
+							// 	height: 46rpx;
+							// }
+
+							.text {
+								font-size: 14px;
+								color: #666666;
+								margin-top: 8rpx;
+							}
+						}
+					}
+
+				}
+			}
+		}
+
+	}
+
+
+	/**弹出框 **/
+	.popwin {
+		position: fixed;
+		width: 750rpx;
+		top: 0;
+		left: 0;
+		bottom: 0;
+		z-index: 88;
+		justify-content: center;
+		align-items: center;
+	}
+
+	.popwin .bg {
+		background: rgba(0, 0, 0, 0.5);
+		width: 750rpx;
+		position: absolute;
+		left: 0;
+		top: 0;
+		bottom: 0;
+	}
+
+	.popwin .body {
+		position: relative;
+		border-radius: 8px;
+		width: 600rpx;
+		align-items: center;
+	}
+
+	.popwin .body .bgimg {
+		width: 600rpx;
+		height: 900rpx;
+		position: absolute;
+		top: 0;
+		bottom: 0;
+	}
+
+	.popwin .body .inner {
+		position: relative;
+		z-index: 99;
+		width: 670rpx;
+		padding: 0px 40rpx;
+		align-items: center;
+	}
+
+	.inner-title {
+		color: #FC2D47;
+		padding: 25rpx 0rpx;
+		font-size: 36rpx;
+		font-weight: bold;
+	}
+
+	.inner-top {
+		height: 225rpx
+	}
+
+	.max-he {
+		height: 322rpx;
+		margin: 0 20rpx;
+		width: 500rpx;
+	}
+
+	.foot {
+		height: 195rpx;
+		width: 337rpx;
+		margin-top: 60rpx;
+	}
+
+	.popwin .btnBox {
+		height: 66rpx;
+		align-items: center;
+		justify-content: center;
+		background-image: linear-gradient(0deg, #FAC37B, #FFF5B0);
+		box-shadow: 2px 7px 7px 0px rgba(161, 23, 36, 0.4);
+		border-radius: 15rpx;
+
+		.btn {
+			font-size: 38rpx;
+			color: #97000E;
+		}
+	}
+
+	.popwin .m-close {
+		margin-top: 10px;
+	}
+
+	.popwin .m-close .icon {
+		width: 120rpx;
+		height: 120rpx;
+	}
+
+	.app-body {
+		.swiper-img {
+			margin: 0 30rpx;
+		}
+	}
+</style>

+ 2 - 10
pages/index/index.vue

@@ -13,7 +13,6 @@
 			<view class="top-body">
 				<view class="statusBar" :style="'height:'  + statusBarHeight + 'px'"></view>
 				<view class="panel fx-r fx-bc fx-ac">
-					<image src="/static/img/logo1.png" mode="heightFix" class="index-img"></image>
 					<view class="inputIndex">
 						<navigator url="../goods/list">
 							<view class="inputText fx-ac fx-bc fx-r">
@@ -229,12 +228,7 @@
 						{{title}}
 					</view>
 					<scroll-view scroll-y class="max-he">
-						<u-parse :imageProp="{
-						mode: 'widthFix',
-						padding: 0,
-						lazyLoad: false,
-						domain: ''
-					}" :content="content" :noData="content" />
+					<uv-parse :content="content"></uv-parse>
 					</scroll-view>
 					<view class="fx-r fx-bc fx-ac foot" style="margin-top: 60rpx;">
 						<view class="btn" @tap="tapYGdBtn">未阅读,去查看></view>
@@ -249,7 +243,6 @@
 </template>
 
 <script>
-	import uParse from '@/components/gaoyia-parse/parse.vue';
 	import maoScroll from '@/components/mao-scroll/mao-scroll.vue';
 	import customerWiget from '@/components/ui-public/customer-wiget.vue';
 	// #ifdef APP
@@ -262,7 +255,6 @@
 	export default {
 		computed: mapState(['user', 'lbs', 'sysData', 'showData']),
 		components: {
-			uParse,
 			maoScroll,
 			customerWiget
 		},
@@ -649,7 +641,7 @@
 
 				.inputIndex {
 					z-index: 2;
-					padding: 20rpx;
+					padding:20rpx 20rpx 20rpx 0;
 					flex-grow: 1;
 
 					.inputText {

+ 0 - 2
pages/login/forgotPassword.vue

@@ -185,13 +185,11 @@
 </style>
 
 <script>
-	import uParse from '@/components/gaoyia-parse/parse.vue';
 	import maoScroll from '@/components/mao-scroll/mao-scroll.vue';
 	import customerWiget from '@/components/ui-public/customer-wiget.vue';
 	import { mapState, mapMutations } from 'vuex';
 	export default {
 		components: {
-			uParse,
 			maoScroll,
 			customerWiget
 		},

+ 3 - 4
pages/login/index.vue

@@ -160,13 +160,12 @@
 </style>
 
 <script>
-	import uParse from '@/components/gaoyia-parse/parse.vue';
 	import maoScroll from '@/components/mao-scroll/mao-scroll.vue';
 	import customerWiget from '@/components/ui-public/customer-wiget.vue';
-	import { mapState, mapMutations } from 'vuex';
+	import { mapMutations } from 'vuex';
+	import Request from '@/library/Request';
 	export default {
 		components: {
-			uParse,
 			maoScroll,
 			customerWiget
 		},
@@ -243,7 +242,7 @@
 				}
 				
 				uni.showLoading({ title: '登录中..' });
-				this.request
+				Request
 				.post("userLogin",this.form)
 				.then(res=>{
 					uni.hideLoading();

+ 0 - 2
pages/login/register.vue

@@ -195,13 +195,11 @@
 </style>
 
 <script>
-	import uParse from '@/components/gaoyia-parse/parse.vue';
 	import maoScroll from '@/components/mao-scroll/mao-scroll.vue';
 	import customerWiget from '@/components/ui-public/customer-wiget.vue';
 	import { mapState, mapMutations } from 'vuex';
 	export default {
 		components: {
-			uParse,
 			maoScroll,
 			customerWiget
 		},

+ 1 - 7
pages/news/about.vue

@@ -1,17 +1,13 @@
 <template>
 	<view>
 		<view id="box">
-			<u-parse :content="content" :noData="content" v-if="isShow"/>
+			<uv-parse :content="content"  v-if="isShow"></uv-parse>
 		</view>
 	</view>
 </template>
 
 <script>
-	import uParse from '@/components/gaoyia-parse/parse.vue'
 	export default {
-		components: {
-			uParse 
-		},
 		data() {
 			return {
 				content:"",
@@ -42,8 +38,6 @@
 </script>
 
 <style>
-	@import url("/components/gaoyia-parse/parse.css");
-	
 	#box {
 		z-index: -1;
 		padding: 0px 10px 0 10px;

+ 1 - 6
pages/news/index.vue

@@ -1,17 +1,13 @@
 <template>
 	<view>
 		<view id="box">
-			<u-parse :content="content" :noData="content" v-if="isShow" :imageProp="imageProp" />
+			<uv-parse :content="content"  v-if="isShow"></uv-parse>
 		</view>
 	</view>
 </template>
 
 <script>
-	import uParse from '@/components/gaoyia-parse/parse.vue'
 	export default {
-		components: {
-			uParse
-		},
 		data () {
 			return {
 				content: "",
@@ -53,7 +49,6 @@
 </script>
 
 <style lang="scss">
-	@import url("/components/gaoyia-parse/parse.css");
 	#box {
 		z-index: -1;
 		padding: 15px;

+ 1 - 7
pages/operation/agreement.vue

@@ -1,17 +1,13 @@
 <template>
 	<view>
 		<view id="box">
-			<u-parse :content="content" :noData="content" v-if="isShow"/>
+			<uv-parse :content="content"  v-if="isShow"></uv-parse>
 		</view>
 	</view>
 </template>
 
 <script>
-	import uParse from '@/components/gaoyia-parse/parse.vue'
 	export default {
-		components: {
-			uParse 
-		},
 		data() {
 			return {
 				content:"",
@@ -42,8 +38,6 @@
 </script>
 
 <style>
-	@import url("/components/gaoyia-parse/parse.css");
-	
 	#box {
 		z-index: -1;
 		padding: 0px 10px 0 10px;

+ 1 - 7
pages/operation/cancellation.vue

@@ -1,17 +1,13 @@
 <template>
 	<view>
 		<view id="box">
-			<u-parse :content="content" :noData="content" v-if="isShow"/>
+			<uv-parse :content="content"  v-if="isShow"></uv-parse>
 		</view>
 	</view>
 </template>
 
 <script>
-	import uParse from '@/components/gaoyia-parse/parse.vue'
 	export default {
-		components: {
-			uParse 
-		},
 		data() {
 			return {
 				content:"",
@@ -42,8 +38,6 @@
 </script>
 
 <style>
-	@import url("/components/gaoyia-parse/parse.css");
-	
 	#box {
 		z-index: -1;
 		padding: 0px 10px 0 10px;

+ 1 - 6
pages/operation/package.vue

@@ -1,17 +1,13 @@
 <template>
 	<view>
 		<view id="box">
-			<u-parse :content="content" :noData="content" v-if="isShow"/>
+			<uv-parse :content="content"  v-if="isShow"></uv-parse>
 		</view>
 	</view>
 </template>
 
 <script>
-	import uParse from '@/components/gaoyia-parse/parse.vue'
 	export default {
-		components: {
-			uParse 
-		},
 		data() {
 			return {
 				content:"",
@@ -42,7 +38,6 @@
 </script>
 
 <style>
-
 	#box {
 		z-index: -1;
 		padding: 0px 10px 0 10px;

+ 1 - 5
pages/operation/user.vue

@@ -1,17 +1,13 @@
 <template>
 	<view>
 		<view id="box">
-			<u-parse :content="content" :noData="content" v-if="isShow"/>
+			<uv-parse :content="content"  v-if="isShow"></uv-parse>
 		</view>
 	</view>
 </template>
 
 <script>
-	import uParse from '@/components/gaoyia-parse/parse.vue'
 	export default {
-		components: {
-			uParse 
-		},
 		data() {
 			return {
 				content:"",

+ 0 - 2
pages/user/buy/balance.vue

@@ -232,11 +232,9 @@
 <script>
 	import {mapState,mapMutations } from 'vuex'
 	var addressId = 0;
-	import uParse from '@/components/gaoyia-parse/parse.vue';
 	import uiPay from '@/components/ui-pay/index.vue';
 	export default {
 		components: {
-			uParse,
 			uiPay
 		},
 		computed: mapState(['user','sysData']),

+ 13 - 13
pages/user/index.nvue

@@ -154,7 +154,7 @@
 							<text class="name">收货地址</text>
 						</view>
 			
-						<view class="item" @tap="$refs.customer.open()">
+						<view class="item" @tap="openKf">
 							<image  class="itemsImage" src="/static/img/userico4.png"  mode="heightFix"></image>
 							<text class="name">联系客服</text>
 						</view>
@@ -324,13 +324,12 @@
 				.item {
 					width: 342rpx;
 					padding: 30rpx;
-					box-sizing: border-box;
 					line-height: 1;
 					flex-direction:row;
 					justify-content: space-between;
 					align-items: center;
 					.itemLeftBox{
-						flex-grow: 1;
+						flex: 1;
 					}
 					.rightImageIcon {
 						width: 66rpx;
@@ -534,8 +533,8 @@
 	}
 
 	.popwin .body .inner-top {
-		height: 9vw;
-		line-height: 9vw;
+		height: 68rpx;
+		line-height: 68rpx;
 		text-align: center;
 		font-weight: 500;
 		font-size: 34rpx;
@@ -543,10 +542,10 @@
 	}
 
 	.popwin .body .inner-tip {
-		margin-top: 8vw;
-		height: 7vw;
+		margin-top: 60rpx;
+		height: 53rpx;
 		text-align: center;
-		line-height: 7vw;
+		line-height: 53rpx;
 		font-weight: bold;
 		font-size: 26rpx;
 		color: #F9FAFA;
@@ -573,7 +572,7 @@
 	}
 
 	.popwin .btn {
-		background: linear-gradient(0deg, #FAC37B, #FFF5B0);
+		background-image: linear-gradient(0deg, #FAC37B, #FFF5B0);
 		box-shadow: 2px 7px 7px 0px rgba(161, 23, 36, 0.4);
 		border-radius: 15rpx;
 		height: 66rpx;
@@ -589,7 +588,7 @@
 		width: 675rpx;
 	}
 
-	.popwin .m-close image {
+	.popwin .m-close .icon {
 		width: 120rpx;
 		height: 120rpx;
 	}
@@ -597,9 +596,8 @@
 </style>
 
 <script>
-	import uParse from '@/components/gaoyia-parse/parse.vue';
 	import maoScroll from '@/components/mao-scroll/mao-scroll.vue';
-	import customerWiget from '@/components/ui-public/customer-wiget.vue';
+	import customerWiget from '@/components/ui-public/customer-wiget.nvue';
 	import {
 		mapState,
 		mapMutations
@@ -607,7 +605,6 @@
 	import utils from '@/library/utils/Comm.js'
 	export default {
 		components: {
-			uParse,
 			maoScroll,
 			customerWiget
 		},
@@ -632,6 +629,9 @@
 		},
 		methods: {
 			...mapMutations(['setSys', 'checkUserLogin']),
+			openKf(){
+				this.$refs.customer.open()
+			},
 			initView: function() {
 				uni.getSystemInfo({
 					success: (res) => {

+ 0 - 2
pages/user/index.vue

@@ -599,7 +599,6 @@
 </style>
 
 <script>
-	import uParse from '@/components/gaoyia-parse/parse.vue';
 	import maoScroll from '@/components/mao-scroll/mao-scroll.vue';
 	import customerWiget from '@/components/ui-public/customer-wiget.vue';
 	import {
@@ -608,7 +607,6 @@
 	} from 'vuex';
 	export default {
 		components: {
-			uParse,
 			maoScroll,
 			customerWiget
 		},

+ 57 - 52
store/index.js

@@ -4,9 +4,11 @@ import utils from "../library/utils/Comm.js";
 import api from "../config/api.js";
 import md5 from "../library/vendor/md5.js";
 import base64 from "@/library/vendor/base64.js";
-import {
-	v4 as uuidv4
-} from "uuid";
+import global from '@/config/global.js';
+import request from '@/library/Request.js';
+// import {
+// 	v4 as uuidv4
+// } from "uuid";
 
 import permission from './modules/permission.js';
 const namespaced = true;
@@ -41,61 +43,65 @@ const store = new Vuex.Store({
 	},
 	mutations: {
 		// 修改显示隐藏app
-		chnangeSshowData(store, data){
+		chnangeSshowData(store, data) {
 			store.showData = data;
 		},
 		init(store, page) {
-			//	try{
-			var uuid = uni.getStorageSync("uuid");
-			if (!utils.isDefine(uuid)) {
-				uuid = uuidv4();
-				uni.setStorageSync("uuid", uuid);
-			}
-			store.uuid = uuid;
-			var userStr = uni.getStorageSync(uuid + "_user");
-			var lbs = uni.getStorageSync(uuid + "_lbs");
-			// userStr = '{"token":"M2QzNDYwM2E5NDA4Yzk2MzU5NWUzMmM0NjY3YTc0MTk="}';
-			if (utils.isDefine(userStr)) {
-				this.commit('setUser', JSON.parse(userStr));
-				page.webSocket.connect();
-			}
-
-			if (utils.isDefine(lbs)) {
-				this.commit('setLbs', JSON.parse(lbs));
-			}
+			// console.log(store,page,'2323424311341234')
+			try {
+				var uuid = uni.getStorageSync("uuid");
+				if (!utils.isDefine(uuid)) {
+					uuid = utils.uuidv4();
+					uni.setStorageSync("uuid", uuid);
+				}
+				store.uuid = uuid;
+				var userStr = uni.getStorageSync(uuid + "_user");
+				var lbs = uni.getStorageSync(uuid + "_lbs");
+				// userStr = '{"token":"M2QzNDYwM2E5NDA4Yzk2MzU5NWUzMmM0NjY3YTc0MTk="}';
+				// console.log(page, 'page11111111111111111111')
+				if (utils.isDefine(userStr)) {
+					// console.log(page, 'page11111111111111111111')
+					// console.log(store, 'page111111111112222111')
+					this.commit('setUser', JSON.parse(userStr));
+					// page.webSocket.connect();
+				}
 
-			//#ifdef H5
-			var wxStr = uni.getStorageSync(uuid + "_wx");
-			if (utils.isDefine(wxStr)) {
-				this.commit('setWeixin', JSON.parse(wxStr));
-			}
-			//#endif
+				if (utils.isDefine(lbs)) {
+					this.commit('setLbs', JSON.parse(lbs));
+				}
 
-			//#ifdef H5
-			var tjUID = uni.getStorageSync(uuid + "_tj");
-			if (utils.isDefine(tjUID)) {
-				this.commit('setTjuid', tjUID);
-			}
-			//#endif
+				//#ifdef H5
+				var wxStr = uni.getStorageSync(uuid + "_wx");
+				if (utils.isDefine(wxStr)) {
+					this.commit('setWeixin', JSON.parse(wxStr));
+				}
+				//#endif
 
-			// #ifdef H5
-			this.commit("setSys", {
-				"bank": 1,
-				"ailpay": 1,
-				"wxpay": 0,
-				"app_code": 0,
-				"is_audit": 0,
-				app_update: "你有新版本要更新"
-			});
-			// #endif
+				//#ifdef H5
+				var tjUID = uni.getStorageSync(uuid + "_tj");
+				if (utils.isDefine(tjUID)) {
+					this.commit('setTjuid', tjUID);
+				}
+				//#endif
+
+				// #ifdef H5
+				this.commit("setSys", {
+					"bank": 1,
+					"ailpay": 1,
+					"wxpay": 0,
+					"app_code": 0,
+					"is_audit": 0,
+					app_update: "你有新版本要更新"
+				});
+				// #endif
 
-			this.commit("setHttp", page.global.apiHttps[0]);
+				this.commit("setHttp", global.apiHttps[0]);
 
 
 
-			//	} catch (e) {
-			//		console.log(e.message);
-			//	}
+			} catch (e) {
+				console.log(e, 'safasdfasdfas ');
+			}
 		},
 
 		setHttp: function(store, http) {
@@ -124,6 +130,7 @@ const store = new Vuex.Store({
 			app.webSocket.ttClose();
 		},
 		setSys(store, data) {
+			console.log(store,data,'data')
 			store.sysData = data;
 		},
 		/**
@@ -263,8 +270,7 @@ const store = new Vuex.Store({
 				this.commit('loginOut', '');
 				return;
 			}
-			$page
-				.request
+			request
 				.post("userInit")
 				.then(res => {
 					if (res.code == 200) {
@@ -323,8 +329,7 @@ const store = new Vuex.Store({
 				bankId: data.bankId == null ? '' : data.bankId,
 				value: 0
 			};
-			$page
-				.request
+			request
 				.post("payPayment", post)
 				.then(res => {
 					uni.hideLoading();

+ 13 - 0
uni_modules/uv-parse/changelog.md

@@ -0,0 +1,13 @@
+## 1.0.4(2023-07-17)
+1. 优化文档
+2. 优化其他
+## 1.0.3(2023-06-19)
+1. 修复nvue模式下不显示的BUG
+## 1.0.2(2023-06-02)
+1. 修复可能存在的BUG
+2. 优化
+## 1.0.1(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.0(2023-05-10)
+uv-parse 富文本解析器

+ 576 - 0
uni_modules/uv-parse/components/uv-parse/node/node.vue

@@ -0,0 +1,576 @@
+<template>
+  <view :id="attrs.id" :class="'_block _'+name+' '+attrs.class" :style="attrs.style">
+    <block v-for="(n, i) in childs" v-bind:key="i">
+      <!-- 图片 -->
+      <!-- 占位图 -->
+      <image v-if="n.name==='img'&&!n.t&&((opts[1]&&!ctrl[i])||ctrl[i]<0)" class="_img" :style="n.attrs.style" :src="ctrl[i]<0?opts[2]:opts[1]" mode="widthFix" />
+      <!-- 显示图片 -->
+      <!-- #ifdef H5 || (APP-PLUS && VUE2) -->
+      <img v-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+n.attrs.style" :src="n.attrs.src||(ctrl.load?n.attrs['data-src']:'')" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
+      <!-- #endif -->
+      <!-- #ifndef H5 || (APP-PLUS && VUE2) -->
+      <!-- 表格中的图片,使用 rich-text 防止大小不正确 -->
+      <rich-text v-if="n.name==='img'&&n.t" :style="'display:'+n.t" :nodes="[{attrs:{style:n.attrs.style,src:n.attrs.src},name:'img'}]" :data-i="i" @tap.stop="imgTap" />
+      <!-- #endif -->
+      <!-- #ifndef H5 || APP-PLUS -->
+      <image v-else-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+'width:'+(ctrl[i]||1)+'px;height:1px;'+n.attrs.style" :src="n.attrs.src" :mode="!n.h?'widthFix':(!n.w?'heightFix':'')" :lazy-load="opts[0]" :webp="n.webp" :show-menu-by-longpress="opts[3]&&!n.attrs.ignore" :image-menu-prevent="!opts[3]||n.attrs.ignore" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
+      <!-- #endif -->
+      <!-- #ifdef APP-PLUS && VUE3 -->
+      <image v-else-if="n.name==='img'" :id="n.attrs.id" :class="'_img '+n.attrs.class" :style="(ctrl[i]===-1?'display:none;':'')+'width:'+(ctrl[i]||1)+'px;'+n.attrs.style" :src="n.attrs.src||(ctrl.load?n.attrs['data-src']:'')" :mode="!n.h?'widthFix':(!n.w?'heightFix':'')" :data-i="i" @load="imgLoad" @error="mediaError" @tap.stop="imgTap" @longpress="imgLongTap" />
+      <!-- #endif -->
+      <!-- 文本 -->
+      <!-- #ifdef MP-WEIXIN -->
+      <text v-else-if="n.text" :user-select="opts[4]=='force'&&isiOS" decode>{{n.text}}</text>
+      <!-- #endif -->
+      <!-- #ifndef MP-WEIXIN || MP-BAIDU || MP-ALIPAY || MP-TOUTIAO -->
+      <text v-else-if="n.text" decode>{{n.text}}</text>
+      <!-- #endif -->
+      <text v-else-if="n.name==='br'">\n</text>
+      <!-- 链接 -->
+      <view v-else-if="n.name==='a'" :id="n.attrs.id" :class="(n.attrs.href?'_a ':'')+n.attrs.class" hover-class="_hover" :style="'display:inline;'+n.attrs.style" :data-i="i" @tap.stop="linkTap">
+        <node name="span" :childs="n.children" :opts="opts" style="display:inherit" />
+      </view>
+      <!-- 视频 -->
+      <!-- #ifdef APP-PLUS -->
+      <view v-else-if="n.html" :id="n.attrs.id" :class="'_video '+n.attrs.class" :style="n.attrs.style" v-html="n.html" @vplay.stop="play" />
+      <!-- #endif -->
+      <!-- #ifndef APP-PLUS -->
+      <video v-else-if="n.name==='video'" :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :autoplay="n.attrs.autoplay" :controls="n.attrs.controls" :loop="n.attrs.loop" :muted="n.attrs.muted" :object-fit="n.attrs['object-fit']" :poster="n.attrs.poster" :src="n.src[ctrl[i]||0]" :data-i="i" @play="play" @error="mediaError" />
+      <!-- #endif -->
+      <!-- #ifdef H5 || APP-PLUS -->
+      <iframe v-else-if="n.name==='iframe'" :style="n.attrs.style" :allowfullscreen="n.attrs.allowfullscreen" :frameborder="n.attrs.frameborder" :src="n.attrs.src" />
+      <embed v-else-if="n.name==='embed'" :style="n.attrs.style" :src="n.attrs.src" />
+      <!-- #endif -->
+      <!-- #ifndef MP-TOUTIAO || ((H5 || APP-PLUS) && VUE3) -->
+      <!-- 音频 -->
+      <audio v-else-if="n.name==='audio'" :id="n.attrs.id" :class="n.attrs.class" :style="n.attrs.style" :author="n.attrs.author" :controls="n.attrs.controls" :loop="n.attrs.loop" :name="n.attrs.name" :poster="n.attrs.poster" :src="n.src[ctrl[i]||0]" :data-i="i" @play="play" @error="mediaError" />
+      <!-- #endif -->
+      <view v-else-if="(n.name==='table'&&n.c)||n.name==='li'" :id="n.attrs.id" :class="'_'+n.name+' '+n.attrs.class" :style="n.attrs.style">
+        <node v-if="n.name==='li'" :childs="n.children" :opts="opts" />
+        <view v-else v-for="(tbody, x) in n.children" v-bind:key="x" :class="'_'+tbody.name+' '+tbody.attrs.class" :style="tbody.attrs.style">
+          <node v-if="tbody.name==='td'||tbody.name==='th'" :childs="tbody.children" :opts="opts" />
+          <block v-else v-for="(tr, y) in tbody.children" v-bind:key="y">
+            <view v-if="tr.name==='td'||tr.name==='th'" :class="'_'+tr.name+' '+tr.attrs.class" :style="tr.attrs.style">
+              <node :childs="tr.children" :opts="opts" />
+            </view>
+            <view v-else :class="'_'+tr.name+' '+tr.attrs.class" :style="tr.attrs.style">
+              <view v-for="(td, z) in tr.children" v-bind:key="z" :class="'_'+td.name+' '+td.attrs.class" :style="td.attrs.style">
+                <node :childs="td.children" :opts="opts" />
+              </view>
+            </view>
+          </block>
+        </view>
+      </view>
+      
+      <!-- 富文本 -->
+      <!-- #ifdef H5 || ((MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE2) -->
+      <rich-text v-else-if="!n.c&&!handler.isInline(n.name, n.attrs.style)" :id="n.attrs.id" :style="n.f" :user-select="opts[4]" :nodes="[n]" />
+      <!-- #endif -->
+      <!-- #ifndef H5 || ((MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE2) -->
+      <rich-text v-else-if="!n.c" :id="n.attrs.id" :style="'display:inline;'+n.f" :preview="false" :selectable="opts[4]" :user-select="opts[4]" :nodes="[n]" />
+      <!-- #endif -->
+      <!-- 继续递归 -->
+      <view v-else-if="n.c===2" :id="n.attrs.id" :class="'_block _'+n.name+' '+n.attrs.class" :style="n.f+';'+n.attrs.style">
+        <node v-for="(n2, j) in n.children" v-bind:key="j" :style="n2.f" :name="n2.name" :attrs="n2.attrs" :childs="n2.children" :opts="opts" />
+      </view>
+      <node v-else :style="n.f" :name="n.name" :attrs="n.attrs" :childs="n.children" :opts="opts" />
+    </block>
+  </view>
+</template>
+<script module="handler" lang="wxs">
+// 行内标签列表
+var inlineTags = {
+  abbr: true,
+  b: true,
+  big: true,
+  code: true,
+  del: true,
+  em: true,
+  i: true,
+  ins: true,
+  label: true,
+  q: true,
+  small: true,
+  span: true,
+  strong: true,
+  sub: true,
+  sup: true
+}
+/**
+ * @description 判断是否为行内标签
+ */
+module.exports = {
+  isInline: function (tagName, style) {
+    return inlineTags[tagName] || (style || '').indexOf('display:inline') !== -1
+  }
+}
+</script>
+<script>
+
+import node from './node'
+export default {
+  name: 'node',
+  options: {
+    // #ifdef MP-WEIXIN
+    virtualHost: true,
+    // #endif
+    // #ifdef MP-TOUTIAO
+    addGlobalClass: false
+    // #endif
+  },
+  data () {
+    return {
+      ctrl: {},
+      // #ifdef MP-WEIXIN
+      isiOS: uni.getSystemInfoSync().system.includes('iOS')
+      // #endif
+    }
+  },
+  props: {
+    name: String,
+    attrs: {
+      type: Object,
+      default () {
+        return {}
+      }
+    },
+    childs: Array,
+    opts: Array
+  },
+  components: {
+
+    // #ifndef (H5 || APP-PLUS) && VUE3
+    node
+    // #endif
+  },
+  mounted () {
+    this.$nextTick(() => {
+      for (this.root = this.$parent; this.root.$options.name !== 'uv-parse'; this.root = this.root.$parent);
+    })
+    // #ifdef H5 || APP-PLUS
+    if (this.opts[0]) {
+      let i
+      for (i = this.childs.length; i--;) {
+        if (this.childs[i].name === 'img') break
+      }
+      if (i !== -1) {
+        this.observer = uni.createIntersectionObserver(this).relativeToViewport({
+          top: 500,
+          bottom: 500
+        })
+        this.observer.observe('._img', res => {
+          if (res.intersectionRatio) {
+            this.$set(this.ctrl, 'load', 1)
+            this.observer.disconnect()
+          }
+        })
+      }
+    }
+    // #endif
+  },
+  beforeDestroy () {
+    // #ifdef H5 || APP-PLUS
+    if (this.observer) {
+      this.observer.disconnect()
+    }
+    // #endif
+  },
+  methods:{
+    // #ifdef MP-WEIXIN
+    toJSON () { return this },
+    // #endif
+    /**
+     * @description 播放视频事件
+     * @param {Event} e
+     */
+    play (e) {
+      this.root.$emit('play')
+      // #ifndef APP-PLUS
+      if (this.root.pauseVideo) {
+        let flag = false
+        const id = e.target.id
+        for (let i = this.root._videos.length; i--;) {
+          if (this.root._videos[i].id === id) {
+            flag = true
+          } else {
+            this.root._videos[i].pause() // 自动暂停其他视频
+          }
+        }
+        // 将自己加入列表
+        if (!flag) {
+          const ctx = uni.createVideoContext(id
+            // #ifndef MP-BAIDU
+            , this
+            // #endif
+          )
+          ctx.id = id
+          if (this.root.playbackRate) {
+            ctx.playbackRate(this.root.playbackRate)
+          }
+          this.root._videos.push(ctx)
+        }
+      }
+      // #endif
+    },
+
+    /**
+     * @description 图片点击事件
+     * @param {Event} e
+     */
+    imgTap (e) {
+      const node = this.childs[e.currentTarget.dataset.i]
+      if (node.a) {
+        this.linkTap(node.a)
+        return
+      }
+      if (node.attrs.ignore) return
+      // #ifdef H5 || APP-PLUS
+      node.attrs.src = node.attrs.src || node.attrs['data-src']
+      // #endif
+      this.root.$emit('imgtap', node.attrs)
+      // 自动预览图片
+      if (this.root.previewImg) {
+        uni.previewImage({
+          // #ifdef MP-WEIXIN
+          showmenu: this.root.showImgMenu,
+          // #endif
+          // #ifdef MP-ALIPAY
+          enablesavephoto: this.root.showImgMenu,
+          enableShowPhotoDownload: this.root.showImgMenu,
+          // #endif
+          current: parseInt(node.attrs.i),
+          urls: this.root.imgList
+        })
+      }
+    },
+
+    /**
+     * @description 图片长按
+     */
+    imgLongTap (e) {
+      // #ifdef APP-PLUS
+      const attrs = this.childs[e.currentTarget.dataset.i].attrs
+      if (this.opts[3] && !attrs.ignore) {
+        uni.showActionSheet({
+          itemList: ['保存图片'],
+          success: () => {
+            const save = path => {
+              uni.saveImageToPhotosAlbum({
+                filePath: path,
+                success () {
+                  uni.showToast({
+                    title: '保存成功'
+                  })
+                }
+              })
+            }
+            if (this.root.imgList[attrs.i].startsWith('http')) {
+              uni.downloadFile({
+                url: this.root.imgList[attrs.i],
+                success: res => save(res.tempFilePath)
+              })
+            } else {
+              save(this.root.imgList[attrs.i])
+            }
+          }
+        })
+      }
+      // #endif
+    },
+
+    /**
+     * @description 图片加载完成事件
+     * @param {Event} e
+     */
+    imgLoad (e) {
+      const i = e.currentTarget.dataset.i
+      /* #ifndef H5 || (APP-PLUS && VUE2) */
+      if (!this.childs[i].w) {
+        // 设置原宽度
+        this.$set(this.ctrl, i, e.detail.width)
+      } else /* #endif */ if ((this.opts[1] && !this.ctrl[i]) || this.ctrl[i] === -1) {
+        // 加载完毕,取消加载中占位图
+        this.$set(this.ctrl, i, 1)
+      }
+      this.checkReady()
+    },
+
+    /**
+     * @description 检查是否所有图片加载完毕
+     */
+    checkReady () {
+      if (this.root && !this.root.lazyLoad) {
+        this.root._unloadimgs -= 1
+        if (!this.root._unloadimgs) {
+          setTimeout(() => {
+            this.root.getRect().then(rect => {
+              this.root.$emit('ready', rect)
+            }).catch(() => {
+              this.root.$emit('ready', {})
+            })
+          }, 350)
+        }
+      }
+    },
+
+    /**
+     * @description 链接点击事件
+     * @param {Event} e
+     */
+    linkTap (e) {
+      const node = e.currentTarget ? this.childs[e.currentTarget.dataset.i] : {}
+      const attrs = node.attrs || e
+      const href = attrs.href
+      this.root.$emit('linktap', Object.assign({
+        innerText: this.root.getText(node.children || []) // 链接内的文本内容
+      }, attrs))
+      if (href) {
+        if (href[0] === '#') {
+          // 跳转锚点
+          this.root.navigateTo(href.substring(1)).catch(() => { })
+        } else if (href.split('?')[0].includes('://')) {
+          // 复制外部链接
+          if (this.root.copyLink) {
+            // #ifdef H5
+            window.open(href)
+            // #endif
+            // #ifdef MP
+            uni.setClipboardData({
+              data: href,
+              success: () =>
+                uni.showToast({
+                  title: '链接已复制'
+                })
+            })
+            // #endif
+            // #ifdef APP-PLUS
+            plus.runtime.openWeb(href)
+            // #endif
+          }
+        } else {
+          // 跳转页面
+          uni.navigateTo({
+            url: href,
+            fail () {
+              uni.switchTab({
+                url: href,
+                fail () { }
+              })
+            }
+          })
+        }
+      }
+    },
+
+    /**
+     * @description 错误事件
+     * @param {Event} e
+     */
+    mediaError (e) {
+      const i = e.currentTarget.dataset.i
+      const node = this.childs[i]
+      // 加载其他源
+      if (node.name === 'video' || node.name === 'audio') {
+        let index = (this.ctrl[i] || 0) + 1
+        if (index > node.src.length) {
+          index = 0
+        }
+        if (index < node.src.length) {
+          this.$set(this.ctrl, i, index)
+          return
+        }
+      } else if (node.name === 'img') {
+        // #ifdef H5 && VUE3
+        if (this.opts[0] && !this.ctrl.load) return
+        // #endif
+        // 显示错误占位图
+        if (this.opts[2]) {
+          this.$set(this.ctrl, i, -1)
+        }
+        this.checkReady()
+      }
+      if (this.root) {
+        this.root.$emit('error', {
+          source: node.name,
+          attrs: node.attrs,
+          // #ifndef H5 && VUE3
+          errMsg: e.detail.errMsg
+          // #endif
+        })
+      }
+    }
+  }
+}
+</script>
+<style>
+/* a 标签默认效果 */
+._a {
+  padding: 1.5px 0 1.5px 0;
+  color: #366092;
+  word-break: break-all;
+}
+
+/* a 标签点击态效果 */
+._hover {
+  text-decoration: underline;
+  opacity: 0.7;
+}
+
+/* 图片默认效果 */
+._img {
+  max-width: 100%;
+  -webkit-touch-callout: none;
+}
+
+/* 内部样式 */
+
+._block {
+  display: block;
+}
+
+._b,
+._strong {
+  font-weight: bold;
+}
+
+._code {
+  font-family: monospace;
+}
+
+._del {
+  text-decoration: line-through;
+}
+
+._em,
+._i {
+  font-style: italic;
+}
+
+._h1 {
+  font-size: 2em;
+}
+
+._h2 {
+  font-size: 1.5em;
+}
+
+._h3 {
+  font-size: 1.17em;
+}
+
+._h5 {
+  font-size: 0.83em;
+}
+
+._h6 {
+  font-size: 0.67em;
+}
+
+._h1,
+._h2,
+._h3,
+._h4,
+._h5,
+._h6 {
+  display: block;
+  font-weight: bold;
+}
+
+._image {
+  height: 1px;
+}
+
+._ins {
+  text-decoration: underline;
+}
+
+._li {
+  display: list-item;
+}
+
+._ol {
+  list-style-type: decimal;
+}
+
+._ol,
+._ul {
+  display: block;
+  padding-left: 40px;
+  margin: 1em 0;
+}
+
+._q::before {
+  content: '"';
+}
+
+._q::after {
+  content: '"';
+}
+
+._sub {
+  font-size: smaller;
+  vertical-align: sub;
+}
+
+._sup {
+  font-size: smaller;
+  vertical-align: super;
+}
+
+._thead,
+._tbody,
+._tfoot {
+  display: table-row-group;
+}
+
+._tr {
+  display: table-row;
+}
+
+._td,
+._th {
+  display: table-cell;
+  vertical-align: middle;
+}
+
+._th {
+  font-weight: bold;
+  text-align: center;
+}
+
+._ul {
+  list-style-type: disc;
+}
+
+._ul ._ul {
+  margin: 0;
+  list-style-type: circle;
+}
+
+._ul ._ul ._ul {
+  list-style-type: square;
+}
+
+._abbr,
+._b,
+._code,
+._del,
+._em,
+._i,
+._ins,
+._label,
+._q,
+._span,
+._strong,
+._sub,
+._sup {
+  display: inline;
+}
+
+/* #ifdef APP-PLUS */
+._video {
+  width: 300px;
+  height: 225px;
+}
+/* #endif */
+</style>

+ 1335 - 0
uni_modules/uv-parse/components/uv-parse/parser.js

@@ -0,0 +1,1335 @@
+/**
+ * @fileoverview html 解析器
+ */
+
+// 配置
+const config = {
+  // 信任的标签(保持标签名不变)
+  trustTags: makeMap('a,abbr,ad,audio,b,blockquote,br,code,col,colgroup,dd,del,dl,dt,div,em,fieldset,h1,h2,h3,h4,h5,h6,hr,i,img,ins,label,legend,li,ol,p,q,ruby,rt,source,span,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,title,ul,video'),
+
+  // 块级标签(转为 div,其他的非信任标签转为 span)
+  blockTags: makeMap('address,article,aside,body,caption,center,cite,footer,header,html,nav,pre,section'),
+
+  // #ifdef (MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE3
+  // 行内标签
+  inlineTags: makeMap('abbr,b,big,code,del,em,i,ins,label,q,small,span,strong,sub,sup'),
+  // #endif
+
+  // 要移除的标签
+  ignoreTags: makeMap('area,base,canvas,embed,frame,head,iframe,input,link,map,meta,param,rp,script,source,style,textarea,title,track,wbr'),
+
+  // 自闭合的标签
+  voidTags: makeMap('area,base,br,col,circle,ellipse,embed,frame,hr,img,input,line,link,meta,param,path,polygon,rect,source,track,use,wbr'),
+
+  // html 实体
+  entities: {
+    lt: '<',
+    gt: '>',
+    quot: '"',
+    apos: "'",
+    ensp: '\u2002',
+    emsp: '\u2003',
+    nbsp: '\xA0',
+    semi: ';',
+    ndash: '–',
+    mdash: '—',
+    middot: '·',
+    lsquo: '‘',
+    rsquo: '’',
+    ldquo: '“',
+    rdquo: '”',
+    bull: '•',
+    hellip: '…',
+    larr: '←',
+    uarr: '↑',
+    rarr: '→',
+    darr: '↓'
+  },
+
+  // 默认的标签样式
+  tagStyle: {
+    // #ifndef APP-PLUS-NVUE
+    address: 'font-style:italic',
+    big: 'display:inline;font-size:1.2em',
+    caption: 'display:table-caption;text-align:center',
+    center: 'text-align:center',
+    cite: 'font-style:italic',
+    dd: 'margin-left:40px',
+    mark: 'background-color:yellow',
+    pre: 'font-family:monospace;white-space:pre',
+    s: 'text-decoration:line-through',
+    small: 'display:inline;font-size:0.8em',
+    strike: 'text-decoration:line-through',
+    u: 'text-decoration:underline'
+    // #endif
+  },
+
+  // svg 大小写对照表
+  svgDict: {
+    animatetransform: 'animateTransform',
+    lineargradient: 'linearGradient',
+    viewbox: 'viewBox',
+    attributename: 'attributeName',
+    repeatcount: 'repeatCount',
+    repeatdur: 'repeatDur'
+  }
+}
+const tagSelector={}
+const {
+  windowWidth,
+  // #ifdef MP-WEIXIN
+  system
+  // #endif
+} = uni.getSystemInfoSync()
+const blankChar = makeMap(' ,\r,\n,\t,\f')
+let idIndex = 0
+
+// #ifdef H5 || APP-PLUS
+config.ignoreTags.iframe = undefined
+config.trustTags.iframe = true
+config.ignoreTags.embed = undefined
+config.trustTags.embed = true
+// #endif
+// #ifdef APP-PLUS-NVUE
+config.ignoreTags.source = undefined
+config.ignoreTags.style = undefined
+// #endif
+
+/**
+ * @description 创建 map
+ * @param {String} str 逗号分隔
+ */
+function makeMap (str) {
+  const map = Object.create(null)
+  const list = str.split(',')
+  for (let i = list.length; i--;) {
+    map[list[i]] = true
+  }
+  return map
+}
+
+/**
+ * @description 解码 html 实体
+ * @param {String} str 要解码的字符串
+ * @param {Boolean} amp 要不要解码 &amp;
+ * @returns {String} 解码后的字符串
+ */
+function decodeEntity (str, amp) {
+  let i = str.indexOf('&')
+  while (i !== -1) {
+    const j = str.indexOf(';', i + 3)
+    let code
+    if (j === -1) break
+    if (str[i + 1] === '#') {
+      // &#123; 形式的实体
+      code = parseInt((str[i + 2] === 'x' ? '0' : '') + str.substring(i + 2, j))
+      if (!isNaN(code)) {
+        str = str.substr(0, i) + String.fromCharCode(code) + str.substr(j + 1)
+      }
+    } else {
+      // &nbsp; 形式的实体
+      code = str.substring(i + 1, j)
+      if (config.entities[code] || (code === 'amp' && amp)) {
+        str = str.substr(0, i) + (config.entities[code] || '&') + str.substr(j + 1)
+      }
+    }
+    i = str.indexOf('&', i + 1)
+  }
+  return str
+}
+
+/**
+ * @description 合并多个块级标签,加快长内容渲染
+ * @param {Array} nodes 要合并的标签数组
+ */
+function mergeNodes (nodes) {
+  let i = nodes.length - 1
+  for (let j = i; j >= -1; j--) {
+    if (j === -1 || nodes[j].c || !nodes[j].name || (nodes[j].name !== 'div' && nodes[j].name !== 'p' && nodes[j].name[0] !== 'h') || (nodes[j].attrs.style || '').includes('inline')) {
+      if (i - j >= 5) {
+        nodes.splice(j + 1, i - j, {
+          name: 'div',
+          attrs: {},
+          children: nodes.slice(j + 1, i + 1)
+        })
+      }
+      i = j - 1
+    }
+  }
+}
+
+/**
+ * @description html 解析器
+ * @param {Object} vm 组件实例
+ */
+function Parser (vm) {
+  this.options = vm || {}
+  this.tagStyle = Object.assign({}, config.tagStyle, this.options.tagStyle)
+  this.imgList = vm.imgList || []
+  this.imgList._unloadimgs = 0
+  this.plugins = vm.plugins || []
+  this.attrs = Object.create(null)
+  this.stack = []
+  this.nodes = []
+  this.pre = (this.options.containerStyle || '').includes('white-space') && this.options.containerStyle.includes('pre') ? 2 : 0
+}
+
+/**
+ * @description 执行解析
+ * @param {String} content 要解析的文本
+ */
+Parser.prototype.parse = function (content) {
+  // 插件处理
+  for (let i = this.plugins.length; i--;) {
+    if (this.plugins[i].onUpdate) {
+      content = this.plugins[i].onUpdate(content, config) || content
+    }
+  }
+
+  new Lexer(this).parse(content)
+  // 出栈未闭合的标签
+  while (this.stack.length) {
+    this.popNode()
+  }
+  if (this.nodes.length > 50) {
+    mergeNodes(this.nodes)
+  }
+  return this.nodes
+}
+
+/**
+ * @description 将标签暴露出来(不被 rich-text 包含)
+ */
+Parser.prototype.expose = function () {
+  // #ifndef APP-PLUS-NVUE
+  for (let i = this.stack.length; i--;) {
+    const item = this.stack[i]
+    if (item.c || item.name === 'a' || item.name === 'video' || item.name === 'audio') return
+    item.c = 1
+  }
+  // #endif
+}
+
+/**
+ * @description 处理插件
+ * @param {Object} node 要处理的标签
+ * @returns {Boolean} 是否要移除此标签
+ */
+Parser.prototype.hook = function (node) {
+  for (let i = this.plugins.length; i--;) {
+    if (this.plugins[i].onParse && this.plugins[i].onParse(node, this) === false) {
+      return false
+    }
+  }
+  return true
+}
+
+/**
+ * @description 将链接拼接上主域名
+ * @param {String} url 需要拼接的链接
+ * @returns {String} 拼接后的链接
+ */
+Parser.prototype.getUrl = function (url) {
+  const domain = this.options.domain
+  if (url[0] === '/') {
+    if (url[1] === '/') {
+      // // 开头的补充协议名
+      url = (domain ? domain.split('://')[0] : 'http') + ':' + url
+    } else if (domain) {
+      // 否则补充整个域名
+      url = domain + url
+    } /* #ifdef APP-PLUS */ else {
+      url = plus.io.convertLocalFileSystemURL(url)
+    } /* #endif */
+  } else if (!url.includes('data:') && !url.includes('://')) {
+    if (domain) {
+      url = domain + '/' + url
+    } /* #ifdef APP-PLUS */ else {
+      url = plus.io.convertLocalFileSystemURL(url)
+    } /* #endif */
+  }
+  return url
+}
+
+/**
+ * @description 解析样式表
+ * @param {Object} node 标签
+ * @returns {Object}
+ */
+Parser.prototype.parseStyle = function (node) {
+  const attrs = node.attrs
+  const list = (this.tagStyle[node.name] || '').split(';').concat((attrs.style || '').split(';'))
+  const styleObj = {}
+  let tmp = ''
+
+  if (attrs.id && !this.xml) {
+    // 暴露锚点
+    if (this.options.useAnchor) {
+      this.expose()
+    } else if (node.name !== 'img' && node.name !== 'a' && node.name !== 'video' && node.name !== 'audio') {
+      attrs.id = undefined
+    }
+  }
+
+  // 转换 width 和 height 属性
+  if (attrs.width) {
+    styleObj.width = parseFloat(attrs.width) + (attrs.width.includes('%') ? '%' : 'px')
+    attrs.width = undefined
+  }
+  if (attrs.height) {
+    styleObj.height = parseFloat(attrs.height) + (attrs.height.includes('%') ? '%' : 'px')
+    attrs.height = undefined
+  }
+
+  for (let i = 0, len = list.length; i < len; i++) {
+    const info = list[i].split(':')
+    if (info.length < 2) continue
+    const key = info.shift().trim().toLowerCase()
+    let value = info.join(':').trim()
+    if ((value[0] === '-' && value.lastIndexOf('-') > 0) || value.includes('safe')) {
+      // 兼容性的 css 不压缩
+      tmp += `;${key}:${value}`
+    } else if (!styleObj[key] || value.includes('import') || !styleObj[key].includes('import')) {
+      // 重复的样式进行覆盖
+      if (value.includes('url')) {
+        // 填充链接
+        let j = value.indexOf('(') + 1
+        if (j) {
+          while (value[j] === '"' || value[j] === "'" || blankChar[value[j]]) {
+            j++
+          }
+          value = value.substr(0, j) + this.getUrl(value.substr(j))
+        }
+      } else if (value.includes('rpx')) {
+        // 转换 rpx(rich-text 内部不支持 rpx)
+        value = value.replace(/[0-9.]+\s*rpx/g, $ => parseFloat($) * windowWidth / 750 + 'px')
+      }
+      styleObj[key] = value
+    }
+  }
+
+  node.attrs.style = tmp
+  return styleObj
+}
+
+/**
+ * @description 解析到标签名
+ * @param {String} name 标签名
+ * @private
+ */
+Parser.prototype.onTagName = function (name) {
+  this.tagName = this.xml ? name : name.toLowerCase()
+  if (this.tagName === 'svg') {
+    this.xml = (this.xml || 0) + 1 // svg 标签内大小写敏感
+    config.ignoreTags.style = undefined // svg 标签内 style 可用
+  }
+}
+
+/**
+ * @description 解析到属性名
+ * @param {String} name 属性名
+ * @private
+ */
+Parser.prototype.onAttrName = function (name) {
+  name = this.xml ? name : name.toLowerCase()
+  if (name.substr(0, 5) === 'data-') {
+    if (name === 'data-src' && !this.attrs.src) {
+      // data-src 自动转为 src
+      this.attrName = 'src'
+    } else if (this.tagName === 'img' || this.tagName === 'a') {
+      // a 和 img 标签保留 data- 的属性,可以在 imgtap 和 linktap 事件中使用
+      this.attrName = name
+    } else {
+      // 剩余的移除以减小大小
+      this.attrName = undefined
+    }
+  } else {
+    this.attrName = name
+    this.attrs[name] = 'T' // boolean 型属性缺省设置
+  }
+}
+
+/**
+ * @description 解析到属性值
+ * @param {String} val 属性值
+ * @private
+ */
+Parser.prototype.onAttrVal = function (val) {
+  const name = this.attrName || ''
+  if (name === 'style' || name === 'href') {
+    // 部分属性进行实体解码
+    this.attrs[name] = decodeEntity(val, true)
+  } else if (name.includes('src')) {
+    // 拼接主域名
+    this.attrs[name] = this.getUrl(decodeEntity(val, true))
+  } else if (name) {
+    this.attrs[name] = val
+  }
+}
+
+/**
+ * @description 解析到标签开始
+ * @param {Boolean} selfClose 是否有自闭合标识 />
+ * @private
+ */
+Parser.prototype.onOpenTag = function (selfClose) {
+  // 拼装 node
+  const node = Object.create(null)
+  node.name = this.tagName
+  node.attrs = this.attrs
+  // 避免因为自动 diff 使得 type 被设置为 null 导致部分内容不显示
+  if (this.options.nodes.length) {
+    node.type = 'node'
+  }
+  this.attrs = Object.create(null)
+
+  const attrs = node.attrs
+  const parent = this.stack[this.stack.length - 1]
+  const siblings = parent ? parent.children : this.nodes
+  const close = this.xml ? selfClose : config.voidTags[node.name]
+
+  // 替换标签名选择器
+  if (tagSelector[node.name]) {
+    attrs.class = tagSelector[node.name] + (attrs.class ? ' ' + attrs.class : '')
+  }
+
+  // 转换 embed 标签
+  if (node.name === 'embed') {
+    // #ifndef H5 || APP-PLUS
+    const src = attrs.src || ''
+    // 按照后缀名和 type 将 embed 转为 video 或 audio
+    if (src.includes('.mp4') || src.includes('.3gp') || src.includes('.m3u8') || (attrs.type || '').includes('video')) {
+      node.name = 'video'
+    } else if (src.includes('.mp3') || src.includes('.wav') || src.includes('.aac') || src.includes('.m4a') || (attrs.type || '').includes('audio')) {
+      node.name = 'audio'
+    }
+    if (attrs.autostart) {
+      attrs.autoplay = 'T'
+    }
+    attrs.controls = 'T'
+    // #endif
+    // #ifdef H5 || APP-PLUS
+    this.expose()
+    // #endif
+  }
+
+  // #ifndef APP-PLUS-NVUE
+  // 处理音视频
+  if (node.name === 'video' || node.name === 'audio') {
+    // 设置 id 以便获取 context
+    if (node.name === 'video' && !attrs.id) {
+      attrs.id = 'v' + idIndex++
+    }
+    // 没有设置 controls 也没有设置 autoplay 的自动设置 controls
+    if (!attrs.controls && !attrs.autoplay) {
+      attrs.controls = 'T'
+    }
+    // 用数组存储所有可用的 source
+    node.src = []
+    if (attrs.src) {
+      node.src.push(attrs.src)
+      attrs.src = undefined
+    }
+    this.expose()
+  }
+  // #endif
+
+  // 处理自闭合标签
+  if (close) {
+    if (!this.hook(node) || config.ignoreTags[node.name]) {
+      // 通过 base 标签设置主域名
+      if (node.name === 'base' && !this.options.domain) {
+        this.options.domain = attrs.href
+      } /* #ifndef APP-PLUS-NVUE */ else if (node.name === 'source' && parent && (parent.name === 'video' || parent.name === 'audio') && attrs.src) {
+        // 设置 source 标签(仅父节点为 video 或 audio 时有效)
+        parent.src.push(attrs.src)
+      } /* #endif */
+      return
+    }
+
+    // 解析 style
+    const styleObj = this.parseStyle(node)
+
+    // 处理图片
+    if (node.name === 'img') {
+      if (attrs.src) {
+        // 标记 webp
+        if (attrs.src.includes('webp')) {
+          node.webp = 'T'
+        }
+        // data url 图片如果没有设置 original-src 默认为不可预览的小图片
+        if (attrs.src.includes('data:') && !attrs['original-src']) {
+          attrs.ignore = 'T'
+        }
+        if (!attrs.ignore || node.webp || attrs.src.includes('cloud://')) {
+          for (let i = this.stack.length; i--;) {
+            const item = this.stack[i]
+            if (item.name === 'a') {
+              node.a = item.attrs
+            }
+            if (item.name === 'table' && !node.webp && !attrs.src.includes('cloud://')) {
+              if (!styleObj.display || styleObj.display.includes('inline')) {
+                node.t = 'inline-block'
+              } else {
+                node.t = styleObj.display
+              }
+              styleObj.display = undefined
+            }
+            // #ifndef H5 || APP-PLUS
+            const style = item.attrs.style || ''
+            if (style.includes('flex:') && !style.includes('flex:0') && !style.includes('flex: 0') && (!styleObj.width || parseInt(styleObj.width) > 100)) {
+              styleObj.width = '100% !important'
+              styleObj.height = ''
+              for (let j = i + 1; j < this.stack.length; j++) {
+                this.stack[j].attrs.style = (this.stack[j].attrs.style || '').replace('inline-', '')
+              }
+            } else if (style.includes('flex') && styleObj.width === '100%') {
+              for (let j = i + 1; j < this.stack.length; j++) {
+                const style = this.stack[j].attrs.style || ''
+                if (!style.includes(';width') && !style.includes(' width') && style.indexOf('width') !== 0) {
+                  styleObj.width = ''
+                  break
+                }
+              }
+            } else if (style.includes('inline-block')) {
+              if (styleObj.width && styleObj.width[styleObj.width.length - 1] === '%') {
+                item.attrs.style += ';max-width:' + styleObj.width
+                styleObj.width = ''
+              } else {
+                item.attrs.style += ';max-width:100%'
+              }
+            }
+            // #endif
+            item.c = 1
+          }
+          attrs.i = this.imgList.length.toString()
+          let src = attrs['original-src'] || attrs.src
+          // #ifndef H5 || MP-ALIPAY || APP-PLUS || MP-360
+          if (this.imgList.includes(src)) {
+            // 如果有重复的链接则对域名进行随机大小写变换避免预览时错位
+            let i = src.indexOf('://')
+            if (i !== -1) {
+              i += 3
+              let newSrc = src.substr(0, i)
+              for (; i < src.length; i++) {
+                if (src[i] === '/') break
+                newSrc += Math.random() > 0.5 ? src[i].toUpperCase() : src[i]
+              }
+              newSrc += src.substr(i)
+              src = newSrc
+            }
+          }
+          // #endif
+          this.imgList.push(src)
+          if (!node.t) {
+            this.imgList._unloadimgs += 1
+          }
+          // #ifdef H5 || APP-PLUS
+          if (this.options.lazyLoad) {
+            attrs['data-src'] = attrs.src
+            attrs.src = undefined
+          }
+          // #endif
+        }
+      }
+      if (styleObj.display === 'inline') {
+        styleObj.display = ''
+      }
+      // #ifndef APP-PLUS-NVUE
+      if (attrs.ignore) {
+        styleObj['max-width'] = styleObj['max-width'] || '100%'
+        attrs.style += ';-webkit-touch-callout:none'
+      }
+      // #endif
+      // 设置的宽度超出屏幕,为避免变形,高度转为自动
+      if (parseInt(styleObj.width) > windowWidth) {
+        styleObj.height = undefined
+      }
+      // 记录是否设置了宽高
+      if (!isNaN(parseInt(styleObj.width))) {
+        node.w = 'T'
+      }
+      if (!isNaN(parseInt(styleObj.height)) && (!styleObj.height.includes('%') || (parent && (parent.attrs.style || '').includes('height')))) {
+        node.h = 'T'
+      }
+    } else if (node.name === 'svg') {
+      siblings.push(node)
+      this.stack.push(node)
+      this.popNode()
+      return
+    }
+    for (const key in styleObj) {
+      if (styleObj[key]) {
+        attrs.style += `;${key}:${styleObj[key].replace(' !important', '')}`
+      }
+    }
+    attrs.style = attrs.style.substr(1) || undefined
+    // #ifdef (MP-WEIXIN || MP-QQ) && VUE3
+    if (!attrs.style) {
+      delete attrs.style
+    }
+    // #endif
+  } else {
+    if ((node.name === 'pre' || ((attrs.style || '').includes('white-space') && attrs.style.includes('pre'))) && this.pre !== 2) {
+      this.pre = node.pre = 1
+    }
+    node.children = []
+    this.stack.push(node)
+  }
+
+  // 加入节点树
+  siblings.push(node)
+}
+
+/**
+ * @description 解析到标签结束
+ * @param {String} name 标签名
+ * @private
+ */
+Parser.prototype.onCloseTag = function (name) {
+  // 依次出栈到匹配为止
+  name = this.xml ? name : name.toLowerCase()
+  let i
+  for (i = this.stack.length; i--;) {
+    if (this.stack[i].name === name) break
+  }
+  if (i !== -1) {
+    while (this.stack.length > i) {
+      this.popNode()
+    }
+  } else if (name === 'p' || name === 'br') {
+    const siblings = this.stack.length ? this.stack[this.stack.length - 1].children : this.nodes
+    siblings.push({
+      name,
+      attrs: {
+        class: tagSelector[name] || '',
+        style: this.tagStyle[name] || ''
+      }
+    })
+  }
+}
+
+/**
+ * @description 处理标签出栈
+ * @private
+ */
+Parser.prototype.popNode = function () {
+  const node = this.stack.pop()
+  let attrs = node.attrs
+  const children = node.children
+  const parent = this.stack[this.stack.length - 1]
+  const siblings = parent ? parent.children : this.nodes
+
+  if (!this.hook(node) || config.ignoreTags[node.name]) {
+    // 获取标题
+    if (node.name === 'title' && children.length && children[0].type === 'text' && this.options.setTitle) {
+      uni.setNavigationBarTitle({
+        title: children[0].text
+      })
+    }
+    siblings.pop()
+    return
+  }
+
+  if (node.pre && this.pre !== 2) {
+    // 是否合并空白符标识
+    this.pre = node.pre = undefined
+    for (let i = this.stack.length; i--;) {
+      if (this.stack[i].pre) {
+        this.pre = 1
+      }
+    }
+  }
+
+  const styleObj = {}
+
+  // 转换 svg
+  if (node.name === 'svg') {
+    if (this.xml > 1) {
+      // 多层 svg 嵌套
+      this.xml--
+      return
+    }
+    // #ifdef APP-PLUS-NVUE
+    (function traversal (node) {
+      if (node.name) {
+        // 调整 svg 的大小写
+        node.name = config.svgDict[node.name] || node.name
+        for (const item in node.attrs) {
+          if (config.svgDict[item]) {
+            node.attrs[config.svgDict[item]] = node.attrs[item]
+            node.attrs[item] = undefined
+          }
+        }
+        for (let i = 0; i < (node.children || []).length; i++) {
+          traversal(node.children[i])
+        }
+      }
+    })(node)
+    // #endif
+    // #ifndef APP-PLUS-NVUE
+    let src = ''
+    const style = attrs.style
+    attrs.style = ''
+    attrs.xmlns = 'http://www.w3.org/2000/svg';
+    (function traversal (node) {
+      if (node.type === 'text') {
+        src += node.text
+        return
+      }
+      const name = config.svgDict[node.name] || node.name
+      src += '<' + name
+      for (const item in node.attrs) {
+        const val = node.attrs[item]
+        if (val) {
+          src += ` ${config.svgDict[item] || item}="${val}"`
+        }
+      }
+      if (!node.children) {
+        src += '/>'
+      } else {
+        src += '>'
+        for (let i = 0; i < node.children.length; i++) {
+          traversal(node.children[i])
+        }
+        src += '</' + name + '>'
+      }
+    })(node)
+    node.name = 'img'
+    node.attrs = {
+      src: 'data:image/svg+xml;utf8,' + src.replace(/#/g, '%23'),
+      style,
+      ignore: 'T'
+    }
+    node.children = undefined
+    // #endif
+    this.xml = false
+    config.ignoreTags.style = true
+    return
+  }
+
+  // #ifndef APP-PLUS-NVUE
+  // 转换 align 属性
+  if (attrs.align) {
+    if (node.name === 'table') {
+      if (attrs.align === 'center') {
+        styleObj['margin-inline-start'] = styleObj['margin-inline-end'] = 'auto'
+      } else {
+        styleObj.float = attrs.align
+      }
+    } else {
+      styleObj['text-align'] = attrs.align
+    }
+    attrs.align = undefined
+  }
+
+  // 转换 dir 属性
+  if (attrs.dir) {
+    styleObj.direction = attrs.dir
+    attrs.dir = undefined
+  }
+
+  // 转换 font 标签的属性
+  if (node.name === 'font') {
+    if (attrs.color) {
+      styleObj.color = attrs.color
+      attrs.color = undefined
+    }
+    if (attrs.face) {
+      styleObj['font-family'] = attrs.face
+      attrs.face = undefined
+    }
+    if (attrs.size) {
+      let size = parseInt(attrs.size)
+      if (!isNaN(size)) {
+        if (size < 1) {
+          size = 1
+        } else if (size > 7) {
+          size = 7
+        }
+        styleObj['font-size'] = ['x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', 'xxx-large'][size - 1]
+      }
+      attrs.size = undefined
+    }
+  }
+  // #endif
+
+  // 一些编辑器的自带 class
+  if ((attrs.class || '').includes('align-center')) {
+    styleObj['text-align'] = 'center'
+  }
+
+  Object.assign(styleObj, this.parseStyle(node))
+
+  if (node.name !== 'table' && parseInt(styleObj.width) > windowWidth) {
+    styleObj['max-width'] = '100%'
+    styleObj['box-sizing'] = 'border-box'
+  }
+
+  // #ifndef APP-PLUS-NVUE
+  if (config.blockTags[node.name]) {
+    node.name = 'div'
+  } else if (!config.trustTags[node.name] && !this.xml) {
+    // 未知标签转为 span,避免无法显示
+    node.name = 'span'
+  }
+
+  if (node.name === 'a' || node.name === 'ad'
+    // #ifdef H5 || APP-PLUS
+    || node.name === 'iframe' // eslint-disable-line
+    // #endif
+  ) {
+    this.expose()
+  } else if (node.name === 'video') {
+    if ((styleObj.height || '').includes('auto')) {
+      styleObj.height = undefined
+    }
+    /* #ifdef APP-PLUS */
+    let str = '<video style="width:100%;height:100%"'
+    for (const item in attrs) {
+      if (attrs[item]) {
+        str += ' ' + item + '="' + attrs[item] + '"'
+      }
+    }
+    if (this.options.pauseVideo) {
+      str += ' onplay="this.dispatchEvent(new CustomEvent(\'vplay\',{bubbles:!0}));for(var e=document.getElementsByTagName(\'video\'),t=0;t<e.length;t++)e[t]!=this&&e[t].pause()"'
+    }
+    str += '>'
+    for (let i = 0; i < node.src.length; i++) {
+      str += '<source src="' + node.src[i] + '">'
+    }
+    str += '</video>'
+    node.html = str
+    /* #endif */
+  } else if ((node.name === 'ul' || node.name === 'ol') && node.c) {
+    // 列表处理
+    const types = {
+      a: 'lower-alpha',
+      A: 'upper-alpha',
+      i: 'lower-roman',
+      I: 'upper-roman'
+    }
+    if (types[attrs.type]) {
+      attrs.style += ';list-style-type:' + types[attrs.type]
+      attrs.type = undefined
+    }
+    for (let i = children.length; i--;) {
+      if (children[i].name === 'li') {
+        children[i].c = 1
+      }
+    }
+  } else if (node.name === 'table') {
+    // 表格处理
+    // cellpadding、cellspacing、border 这几个常用表格属性需要通过转换实现
+    let padding = parseFloat(attrs.cellpadding)
+    let spacing = parseFloat(attrs.cellspacing)
+    const border = parseFloat(attrs.border)
+    const bordercolor = styleObj['border-color']
+    const borderstyle = styleObj['border-style']
+    if (node.c) {
+      // padding 和 spacing 默认 2
+      if (isNaN(padding)) {
+        padding = 2
+      }
+      if (isNaN(spacing)) {
+        spacing = 2
+      }
+    }
+    if (border) {
+      attrs.style += `;border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'}`
+    }
+    if (node.flag && node.c) {
+      // 有 colspan 或 rowspan 且含有链接的表格通过 grid 布局实现
+      styleObj.display = 'grid'
+      if (spacing) {
+        styleObj['grid-gap'] = spacing + 'px'
+        styleObj.padding = spacing + 'px'
+      } else if (border) {
+        // 无间隔的情况下避免边框重叠
+        attrs.style += ';border-left:0;border-top:0'
+      }
+
+      const width = [] // 表格的列宽
+      const trList = [] // tr 列表
+      const cells = [] // 保存新的单元格
+      const map = {}; // 被合并单元格占用的格子
+
+      (function traversal (nodes) {
+        for (let i = 0; i < nodes.length; i++) {
+          if (nodes[i].name === 'tr') {
+            trList.push(nodes[i])
+          } else {
+            traversal(nodes[i].children || [])
+          }
+        }
+      })(children)
+
+      for (let row = 1; row <= trList.length; row++) {
+        let col = 1
+        for (let j = 0; j < trList[row - 1].children.length; j++) {
+          const td = trList[row - 1].children[j]
+          if (td.name === 'td' || td.name === 'th') {
+            // 这个格子被上面的单元格占用,则列号++
+            while (map[row + '.' + col]) {
+              col++
+            }
+            let style = td.attrs.style || ''
+            let start = style.indexOf('width') ? style.indexOf(';width') : 0
+            // 提取出 td 的宽度
+            if (start !== -1) {
+              let end = style.indexOf(';', start + 6)
+              if (end === -1) {
+                end = style.length
+              }
+              if (!td.attrs.colspan) {
+                width[col] = style.substring(start ? start + 7 : 6, end)
+              }
+              style = style.substr(0, start) + style.substr(end)
+            }
+            // 设置竖直对齐
+            style += ';display:flex'
+            start = style.indexOf('vertical-align')
+            if (start !== -1) {
+              const val = style.substr(start + 15, 10)
+              if (val.includes('middle')) {
+                style += ';align-items:center'
+              } else if (val.includes('bottom')) {
+                style += ';align-items:flex-end'
+              }
+            } else {
+              style += ';align-items:center'
+            }
+            // 设置水平对齐
+            start = style.indexOf('text-align')
+            if (start !== -1) {
+              const val = style.substr(start + 11, 10)
+              if (val.includes('center')) {
+                style += ';justify-content: center'
+              } else if (val.includes('right')) {
+                style += ';justify-content: right'
+              }
+            }
+            style = (border ? `;border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'}` + (spacing ? '' : ';border-right:0;border-bottom:0') : '') + (padding ? `;padding:${padding}px` : '') + ';' + style
+            // 处理列合并
+            if (td.attrs.colspan) {
+              style += `;grid-column-start:${col};grid-column-end:${col + parseInt(td.attrs.colspan)}`
+              if (!td.attrs.rowspan) {
+                style += `;grid-row-start:${row};grid-row-end:${row + 1}`
+              }
+              col += parseInt(td.attrs.colspan) - 1
+            }
+            // 处理行合并
+            if (td.attrs.rowspan) {
+              style += `;grid-row-start:${row};grid-row-end:${row + parseInt(td.attrs.rowspan)}`
+              if (!td.attrs.colspan) {
+                style += `;grid-column-start:${col};grid-column-end:${col + 1}`
+              }
+              // 记录下方单元格被占用
+              for (let rowspan = 1; rowspan < td.attrs.rowspan; rowspan++) {
+                for (let colspan = 0; colspan < (td.attrs.colspan || 1); colspan++) {
+                  map[(row + rowspan) + '.' + (col - colspan)] = 1
+                }
+              }
+            }
+            if (style) {
+              td.attrs.style = style
+            }
+            cells.push(td)
+            col++
+          }
+        }
+        if (row === 1) {
+          let temp = ''
+          for (let i = 1; i < col; i++) {
+            temp += (width[i] ? width[i] : 'auto') + ' '
+          }
+          styleObj['grid-template-columns'] = temp
+        }
+      }
+      node.children = cells
+    } else {
+      // 没有使用合并单元格的表格通过 table 布局实现
+      if (node.c) {
+        styleObj.display = 'table'
+      }
+      if (!isNaN(spacing)) {
+        styleObj['border-spacing'] = spacing + 'px'
+      }
+      if (border || padding) {
+        // 遍历
+        (function traversal (nodes) {
+          for (let i = 0; i < nodes.length; i++) {
+            const td = nodes[i]
+            if (td.name === 'th' || td.name === 'td') {
+              if (border) {
+                td.attrs.style = `border:${border}px ${borderstyle || 'solid'} ${bordercolor || 'gray'};${td.attrs.style || ''}`
+              }
+              if (padding) {
+                td.attrs.style = `padding:${padding}px;${td.attrs.style || ''}`
+              }
+            } else if (td.children) {
+              traversal(td.children)
+            }
+          }
+        })(children)
+      }
+    }
+    // 给表格添加一个单独的横向滚动层
+    if (this.options.scrollTable && !(attrs.style || '').includes('inline')) {
+      const table = Object.assign({}, node)
+      node.name = 'div'
+      node.attrs = {
+        style: 'overflow:auto'
+      }
+      node.children = [table]
+      attrs = table.attrs
+    }
+  } else if ((node.name === 'td' || node.name === 'th') && (attrs.colspan || attrs.rowspan)) {
+    for (let i = this.stack.length; i--;) {
+      if (this.stack[i].name === 'table') {
+        this.stack[i].flag = 1 // 指示含有合并单元格
+        break
+      }
+    }
+  } else if (node.name === 'ruby') {
+    // 转换 ruby
+    node.name = 'span'
+    for (let i = 0; i < children.length - 1; i++) {
+      if (children[i].type === 'text' && children[i + 1].name === 'rt') {
+        children[i] = {
+          name: 'div',
+          attrs: {
+            style: 'display:inline-block;text-align:center'
+          },
+          children: [{
+            name: 'div',
+            attrs: {
+              style: 'font-size:50%;' + (children[i + 1].attrs.style || '')
+            },
+            children: children[i + 1].children
+          }, children[i]]
+        }
+        children.splice(i + 1, 1)
+      }
+    }
+  } else if (node.c) {
+    (function traversal (node) {
+      node.c = 2
+      for (let i = node.children.length; i--;) {
+        const child = node.children[i]
+        // #ifdef (MP-WEIXIN || MP-QQ || APP-PLUS || MP-360) && VUE3
+        if (child.name && (config.inlineTags[child.name] || ((child.attrs.style || '').includes('inline') && child.children)) && !child.c) {
+          traversal(child)
+        }
+        // #endif
+        if (!child.c || child.name === 'table') {
+          node.c = 1
+        }
+      }
+    })(node)
+  }
+
+  if ((styleObj.display || '').includes('flex') && !node.c) {
+    for (let i = children.length; i--;) {
+      const item = children[i]
+      if (item.f) {
+        item.attrs.style = (item.attrs.style || '') + item.f
+        item.f = undefined
+      }
+    }
+  }
+  // flex 布局时部分样式需要提取到 rich-text 外层
+  const flex = parent && ((parent.attrs.style || '').includes('flex') || (parent.attrs.style || '').includes('grid'))
+    // #ifdef MP-WEIXIN
+    // 检查基础库版本 virtualHost 是否可用
+    && !(node.c && wx.getNFCAdapter) // eslint-disable-line
+    // #endif
+    // #ifndef MP-WEIXIN || MP-QQ || MP-BAIDU || MP-TOUTIAO
+    && !node.c // eslint-disable-line
+  // #endif
+  if (flex) {
+    node.f = ';max-width:100%'
+  }
+
+  if (children.length >= 50 && node.c && !(styleObj.display || '').includes('flex')) {
+    mergeNodes(children)
+  }
+  // #endif
+
+  for (const key in styleObj) {
+    if (styleObj[key]) {
+      const val = `;${key}:${styleObj[key].replace(' !important', '')}`
+      /* #ifndef APP-PLUS-NVUE */
+      if (flex && ((key.includes('flex') && key !== 'flex-direction') || key === 'align-self' || key.includes('grid') || styleObj[key][0] === '-' || (key.includes('width') && val.includes('%')))) {
+        node.f += val
+        if (key === 'width') {
+          attrs.style += ';width:100%'
+        }
+      } else /* #endif */ {
+        attrs.style += val
+      }
+    }
+  }
+  attrs.style = attrs.style.substr(1) || undefined
+  // #ifdef (MP-WEIXIN || MP-QQ) && VUE3
+  for (const key in attrs) {
+    if (!attrs[key]) {
+      delete attrs[key]
+    }
+  }
+  // #endif
+}
+
+/**
+ * @description 解析到文本
+ * @param {String} text 文本内容
+ */
+Parser.prototype.onText = function (text) {
+  if (!this.pre) {
+    // 合并空白符
+    let trim = ''
+    let flag
+    for (let i = 0, len = text.length; i < len; i++) {
+      if (!blankChar[text[i]]) {
+        trim += text[i]
+      } else {
+        if (trim[trim.length - 1] !== ' ') {
+          trim += ' '
+        }
+        if (text[i] === '\n' && !flag) {
+          flag = true
+        }
+      }
+    }
+    // 去除含有换行符的空串
+    if (trim === ' ') {
+      if (flag) return
+      // #ifdef VUE3
+      else {
+        const parent = this.stack[this.stack.length - 1]
+        if (parent && parent.name[0] === 't') return
+      }
+      // #endif
+    }
+    text = trim
+  }
+  const node = Object.create(null)
+  node.type = 'text'
+  // #ifdef (MP-BAIDU || MP-ALIPAY || MP-TOUTIAO) && VUE3
+  node.attrs = {}
+  // #endif
+  node.text = decodeEntity(text)
+  if (this.hook(node)) {
+    // #ifdef MP-WEIXIN
+    if (this.options.selectable === 'force' && system.includes('iOS') && !uni.canIUse('rich-text.user-select')) {
+      this.expose()
+    }
+    // #endif
+    const siblings = this.stack.length ? this.stack[this.stack.length - 1].children : this.nodes
+    siblings.push(node)
+  }
+}
+
+/**
+ * @description html 词法分析器
+ * @param {Object} handler 高层处理器
+ */
+function Lexer (handler) {
+  this.handler = handler
+}
+
+/**
+ * @description 执行解析
+ * @param {String} content 要解析的文本
+ */
+Lexer.prototype.parse = function (content) {
+  this.content = content || ''
+  this.i = 0 // 标记解析位置
+  this.start = 0 // 标记一个单词的开始位置
+  this.state = this.text // 当前状态
+  for (let len = this.content.length; this.i !== -1 && this.i < len;) {
+    this.state()
+  }
+}
+
+/**
+ * @description 检查标签是否闭合
+ * @param {String} method 如果闭合要进行的操作
+ * @returns {Boolean} 是否闭合
+ * @private
+ */
+Lexer.prototype.checkClose = function (method) {
+  const selfClose = this.content[this.i] === '/'
+  if (this.content[this.i] === '>' || (selfClose && this.content[this.i + 1] === '>')) {
+    if (method) {
+      this.handler[method](this.content.substring(this.start, this.i))
+    }
+    this.i += selfClose ? 2 : 1
+    this.start = this.i
+    this.handler.onOpenTag(selfClose)
+    if (this.handler.tagName === 'script') {
+      this.i = this.content.indexOf('</', this.i)
+      if (this.i !== -1) {
+        this.i += 2
+        this.start = this.i
+      }
+      this.state = this.endTag
+    } else {
+      this.state = this.text
+    }
+    return true
+  }
+  return false
+}
+
+/**
+ * @description 文本状态
+ * @private
+ */
+Lexer.prototype.text = function () {
+  this.i = this.content.indexOf('<', this.i) // 查找最近的标签
+  if (this.i === -1) {
+    // 没有标签了
+    if (this.start < this.content.length) {
+      this.handler.onText(this.content.substring(this.start, this.content.length))
+    }
+    return
+  }
+  const c = this.content[this.i + 1]
+  if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+    // 标签开头
+    if (this.start !== this.i) {
+      this.handler.onText(this.content.substring(this.start, this.i))
+    }
+    this.start = ++this.i
+    this.state = this.tagName
+  } else if (c === '/' || c === '!' || c === '?') {
+    if (this.start !== this.i) {
+      this.handler.onText(this.content.substring(this.start, this.i))
+    }
+    const next = this.content[this.i + 2]
+    if (c === '/' && ((next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z'))) {
+      // 标签结尾
+      this.i += 2
+      this.start = this.i
+      this.state = this.endTag
+      return
+    }
+    // 处理注释
+    let end = '-->'
+    if (c !== '!' || this.content[this.i + 2] !== '-' || this.content[this.i + 3] !== '-') {
+      end = '>'
+    }
+    this.i = this.content.indexOf(end, this.i)
+    if (this.i !== -1) {
+      this.i += end.length
+      this.start = this.i
+    }
+  } else {
+    this.i++
+  }
+}
+
+/**
+ * @description 标签名状态
+ * @private
+ */
+Lexer.prototype.tagName = function () {
+  if (blankChar[this.content[this.i]]) {
+    // 解析到标签名
+    this.handler.onTagName(this.content.substring(this.start, this.i))
+    while (blankChar[this.content[++this.i]]);
+    if (this.i < this.content.length && !this.checkClose()) {
+      this.start = this.i
+      this.state = this.attrName
+    }
+  } else if (!this.checkClose('onTagName')) {
+    this.i++
+  }
+}
+
+/**
+ * @description 属性名状态
+ * @private
+ */
+Lexer.prototype.attrName = function () {
+  let c = this.content[this.i]
+  if (blankChar[c] || c === '=') {
+    // 解析到属性名
+    this.handler.onAttrName(this.content.substring(this.start, this.i))
+    let needVal = c === '='
+    const len = this.content.length
+    while (++this.i < len) {
+      c = this.content[this.i]
+      if (!blankChar[c]) {
+        if (this.checkClose()) return
+        if (needVal) {
+          // 等号后遇到第一个非空字符
+          this.start = this.i
+          this.state = this.attrVal
+          return
+        }
+        if (this.content[this.i] === '=') {
+          needVal = true
+        } else {
+          this.start = this.i
+          this.state = this.attrName
+          return
+        }
+      }
+    }
+  } else if (!this.checkClose('onAttrName')) {
+    this.i++
+  }
+}
+
+/**
+ * @description 属性值状态
+ * @private
+ */
+Lexer.prototype.attrVal = function () {
+  const c = this.content[this.i]
+  const len = this.content.length
+  if (c === '"' || c === "'") {
+    // 有冒号的属性
+    this.start = ++this.i
+    this.i = this.content.indexOf(c, this.i)
+    if (this.i === -1) return
+    this.handler.onAttrVal(this.content.substring(this.start, this.i))
+  } else {
+    // 没有冒号的属性
+    for (; this.i < len; this.i++) {
+      if (blankChar[this.content[this.i]]) {
+        this.handler.onAttrVal(this.content.substring(this.start, this.i))
+        break
+      } else if (this.checkClose('onAttrVal')) return
+    }
+  }
+  while (blankChar[this.content[++this.i]]);
+  if (this.i < len && !this.checkClose()) {
+    this.start = this.i
+    this.state = this.attrName
+  }
+}
+
+/**
+ * @description 结束标签状态
+ * @returns {String} 结束的标签名
+ * @private
+ */
+Lexer.prototype.endTag = function () {
+  const c = this.content[this.i]
+  if (blankChar[c] || c === '>' || c === '/') {
+    this.handler.onCloseTag(this.content.substring(this.start, this.i))
+    if (c !== '>') {
+      this.i = this.content.indexOf('>', this.i)
+      if (this.i === -1) return
+    }
+    this.start = ++this.i
+    this.state = this.text
+  } else {
+    this.i++
+  }
+}
+
+export default Parser

+ 498 - 0
uni_modules/uv-parse/components/uv-parse/uv-parse.vue

@@ -0,0 +1,498 @@
+<template>
+  <view id="_root" :class="(selectable?'_select ':'')+'_root'" :style="containerStyle">
+    <slot v-if="!nodes[0]" />
+    <!-- #ifndef APP-PLUS-NVUE -->
+    <node v-else :childs="nodes" :opts="[lazyLoad,loadingImg,errorImg,showImgMenu,selectable]" name="span" />
+    <!-- #endif -->
+    <!-- #ifdef APP-PLUS-NVUE -->
+    <web-view ref="web" src="/uni_modules/uv-parse/static/app-plus/uv-parse/local.html" :style="'margin-top:-2px;height:' + height + 'px'" @onPostMessage="_onMessage" />
+    <!-- #endif -->
+  </view>
+</template>
+
+<script>
+/**
+ * uv-parse v1.0.3
+ * @description 富文本组件
+ * @tutorial https://www.uvui.cn/components/parse.html
+ * @property {String} container-style 容器的样式
+ * @property {String} content 用于渲染的 html 字符串
+ * @property {Boolean} copy-link 是否允许外部链接被点击时自动复制
+ * @property {String} domain 主域名,用于拼接链接
+ * @property {String} error-img 图片出错时的占位图链接
+ * @property {Boolean} lazy-load 是否开启图片懒加载
+ * @property {string} loading-img 图片加载过程中的占位图链接
+ * @property {Boolean} pause-video 是否在播放一个视频时自动暂停其他视频
+ * @property {Boolean} preview-img 是否允许图片被点击时自动预览
+ * @property {Boolean} scroll-table 是否给每个表格添加一个滚动层使其能单独横向滚动
+ * @property {Boolean | String} selectable 是否开启长按复制
+ * @property {Boolean} set-title 是否将 title 标签的内容设置到页面标题
+ * @property {Boolean} show-img-menu 是否允许图片被长按时显示菜单
+ * @property {Object} tag-style 标签的默认样式
+ * @property {Boolean | Number} use-anchor 是否使用锚点链接
+ * @event {Function} load dom 结构加载完毕时触发
+ * @event {Function} ready 所有图片加载完毕时触发
+ * @event {Function} imgtap 图片被点击时触发
+ * @event {Function} linktap 链接被点击时触发
+ * @event {Function} play 音视频播放时触发
+ * @event {Function} error 媒体加载出错时触发
+ */
+// #ifndef APP-PLUS-NVUE
+import node from './node/node'
+// #endif
+import Parser from './parser'
+const plugins=[]
+// #ifdef APP-PLUS-NVUE
+const dom = weex.requireModule('dom')
+// #endif
+export default {
+  name: 'uv-parse',
+  data () {
+    return {
+      nodes: [],
+      // #ifdef APP-PLUS-NVUE
+      height: 3
+      // #endif
+    }
+  },
+  props: {
+    containerStyle: {
+      type: String,
+      default: ''
+    },
+    content: {
+      type: String,
+      default: ''
+    },
+    copyLink: {
+      type: [Boolean, String],
+      default: true
+    },
+    domain: String,
+    errorImg: {
+      type: String,
+      default: ''
+    },
+    lazyLoad: {
+      type: [Boolean, String],
+      default: false
+    },
+    loadingImg: {
+      type: String,
+      default: ''
+    },
+    pauseVideo: {
+      type: [Boolean, String],
+      default: true
+    },
+    previewImg: {
+      type: [Boolean, String],
+      default: true
+    },
+    scrollTable: [Boolean, String],
+    selectable: [Boolean, String],
+    setTitle: {
+      type: [Boolean, String],
+      default: true
+    },
+    showImgMenu: {
+      type: [Boolean, String],
+      default: true
+    },
+    tagStyle: Object,
+    useAnchor: [Boolean, Number]
+  },
+  // #ifdef VUE3
+  emits: ['load', 'ready', 'imgtap', 'linktap', 'play', 'error'],
+  // #endif
+  // #ifndef APP-PLUS-NVUE
+  components: {
+    node
+  },
+  // #endif
+  watch: {
+    content (content) {
+      this.setContent(content)
+    }
+  },
+  created () {
+    this.plugins = []
+    for (let i = plugins.length; i--;) {
+      this.plugins.push(new plugins[i](this))
+    }
+  },
+  mounted () {
+    if (this.content && !this.nodes.length) {
+      this.setContent(this.content)
+    }
+  },
+  beforeDestroy () {
+    this._hook('onDetached')
+  },
+  methods: {
+    /**
+     * @description 将锚点跳转的范围限定在一个 scroll-view 内
+     * @param {Object} page scroll-view 所在页面的示例
+     * @param {String} selector scroll-view 的选择器
+     * @param {String} scrollTop scroll-view scroll-top 属性绑定的变量名
+     */
+    in (page, selector, scrollTop) {
+      // #ifndef APP-PLUS-NVUE
+      if (page && selector && scrollTop) {
+        this._in = {
+          page,
+          selector,
+          scrollTop
+        }
+      }
+      // #endif
+    },
+
+    /**
+     * @description 锚点跳转
+     * @param {String} id 要跳转的锚点 id
+     * @param {Number} offset 跳转位置的偏移量
+     * @returns {Promise}
+     */
+    navigateTo (id, offset) {
+      return new Promise((resolve, reject) => {
+        if (!this.useAnchor) {
+          reject(Error('Anchor is disabled'))
+          return
+        }
+        offset = offset || parseInt(this.useAnchor) || 0
+        // #ifdef APP-PLUS-NVUE
+        if (!id) {
+          dom.scrollToElement(this.$refs.web, {
+            offset
+          })
+          resolve()
+        } else {
+          this._navigateTo = {
+            resolve,
+            reject,
+            offset
+          }
+          this.$refs.web.evalJs('uni.postMessage({data:{action:"getOffset",offset:(document.getElementById(' + id + ')||{}).offsetTop}})')
+        }
+        // #endif
+        // #ifndef APP-PLUS-NVUE
+        let deep = ' '
+        // #ifdef MP-WEIXIN || MP-QQ || MP-TOUTIAO
+        deep = '>>>'
+        // #endif
+        const selector = uni.createSelectorQuery()
+          // #ifndef MP-ALIPAY
+          .in(this._in ? this._in.page : this)
+          // #endif
+          .select((this._in ? this._in.selector : '._root') + (id ? `${deep}#${id}` : '')).boundingClientRect()
+        if (this._in) {
+          selector.select(this._in.selector).scrollOffset()
+            .select(this._in.selector).boundingClientRect()
+        } else {
+          // 获取 scroll-view 的位置和滚动距离
+          selector.selectViewport().scrollOffset() // 获取窗口的滚动距离
+        }
+        selector.exec(res => {
+          if (!res[0]) {
+            reject(Error('Label not found'))
+            return
+          }
+          const scrollTop = res[1].scrollTop + res[0].top - (res[2] ? res[2].top : 0) + offset
+          if (this._in) {
+            // scroll-view 跳转
+            this._in.page[this._in.scrollTop] = scrollTop
+          } else {
+            // 页面跳转
+            uni.pageScrollTo({
+              scrollTop,
+              duration: 300
+            })
+          }
+          resolve()
+        })
+        // #endif
+      })
+    },
+
+    /**
+     * @description 获取文本内容
+     * @return {String}
+     */
+    getText (nodes) {
+      let text = '';
+      (function traversal (nodes) {
+        for (let i = 0; i < nodes.length; i++) {
+          const node = nodes[i]
+          if (node.type === 'text') {
+            text += node.text.replace(/&amp;/g, '&')
+          } else if (node.name === 'br') {
+            text += '\n'
+          } else {
+            // 块级标签前后加换行
+            const isBlock = node.name === 'p' || node.name === 'div' || node.name === 'tr' || node.name === 'li' || (node.name[0] === 'h' && node.name[1] > '0' && node.name[1] < '7')
+            if (isBlock && text && text[text.length - 1] !== '\n') {
+              text += '\n'
+            }
+            // 递归获取子节点的文本
+            if (node.children) {
+              traversal(node.children)
+            }
+            if (isBlock && text[text.length - 1] !== '\n') {
+              text += '\n'
+            } else if (node.name === 'td' || node.name === 'th') {
+              text += '\t'
+            }
+          }
+        }
+      })(nodes || this.nodes)
+      return text
+    },
+
+    /**
+     * @description 获取内容大小和位置
+     * @return {Promise}
+     */
+    getRect () {
+      return new Promise((resolve, reject) => {
+        uni.createSelectorQuery()
+          // #ifndef MP-ALIPAY
+          .in(this)
+          // #endif
+          .select('#_root').boundingClientRect().exec(res => res[0] ? resolve(res[0]) : reject(Error('Root label not found')))
+      })
+    },
+
+    /**
+     * @description 暂停播放媒体
+     */
+    pauseMedia () {
+      for (let i = (this._videos || []).length; i--;) {
+        this._videos[i].pause()
+      }
+      // #ifdef APP-PLUS
+      const command = 'for(var e=document.getElementsByTagName("video"),i=e.length;i--;)e[i].pause()'
+      // #ifndef APP-PLUS-NVUE
+      let page = this.$parent
+      while (!page.$scope) page = page.$parent
+      page.$scope.$getAppWebview().evalJS(command)
+      // #endif
+      // #ifdef APP-PLUS-NVUE
+      this.$refs.web.evalJs(command)
+      // #endif
+      // #endif
+    },
+
+    /**
+     * @description 设置媒体播放速率
+     * @param {Number} rate 播放速率
+     */
+    setPlaybackRate (rate) {
+      this.playbackRate = rate
+      for (let i = (this._videos || []).length; i--;) {
+        this._videos[i].playbackRate(rate)
+      }
+      // #ifdef APP-PLUS
+      const command = 'for(var e=document.getElementsByTagName("video"),i=e.length;i--;)e[i].playbackRate=' + rate
+      // #ifndef APP-PLUS-NVUE
+      let page = this.$parent
+      while (!page.$scope) page = page.$parent
+      page.$scope.$getAppWebview().evalJS(command)
+      // #endif
+      // #ifdef APP-PLUS-NVUE
+      this.$refs.web.evalJs(command)
+      // #endif
+      // #endif
+    },
+
+    /**
+     * @description 设置内容
+     * @param {String} content html 内容
+     * @param {Boolean} append 是否在尾部追加
+     */
+    setContent (content, append) {
+      if (!append || !this.imgList) {
+        this.imgList = []
+      }
+      const nodes = new Parser(this).parse(content)
+      // #ifdef APP-PLUS-NVUE
+      if (this._ready) {
+        this._set(nodes, append)
+      }
+      // #endif
+      this.$set(this, 'nodes', append ? (this.nodes || []).concat(nodes) : nodes)
+
+      // #ifndef APP-PLUS-NVUE
+      this._videos = []
+      this.$nextTick(() => {
+        this._hook('onLoad')
+        this.$emit('load')
+      })
+
+      if (this.lazyLoad || this.imgList._unloadimgs < this.imgList.length / 2) {
+        // 设置懒加载,每 350ms 获取高度,不变则认为加载完毕
+        let height = 0
+        const callback = rect => {
+          if (!rect || !rect.height) rect = {}
+          // 350ms 总高度无变化就触发 ready 事件
+          if (rect.height === height) {
+            this.$emit('ready', rect)
+          } else {
+            height = rect.height
+            setTimeout(() => {
+              this.getRect().then(callback).catch(callback)
+            }, 350)
+          }
+        }
+        this.getRect().then(callback).catch(callback)
+      } else {
+        // 未设置懒加载,等待所有图片加载完毕
+        if (!this.imgList._unloadimgs) {
+          this.getRect().then(rect => {
+            this.$emit('ready', rect)
+          }).catch(() => {
+            this.$emit('ready', {})
+          })
+        }
+      }
+      // #endif
+    },
+
+    /**
+     * @description 调用插件钩子函数
+     */
+    _hook (name) {
+      for (let i = plugins.length; i--;) {
+        if (this.plugins[i][name]) {
+          this.plugins[i][name]()
+        }
+      }
+    },
+
+    // #ifdef APP-PLUS-NVUE
+    /**
+     * @description 设置内容
+     */
+    _set (nodes, append) {
+      this.$refs.web.evalJs('setContent(' + JSON.stringify(nodes).replace(/%22/g, '') + ',' + JSON.stringify([this.containerStyle.replace(/(?:margin|padding)[^;]+/g, ''), this.errorImg, this.loadingImg, this.pauseVideo, this.scrollTable, this.selectable]) + ',' + append + ')')
+    },
+
+    /**
+     * @description 接收到 web-view 消息
+     */
+    _onMessage (e) {
+      const message = e.detail.data[0]
+      switch (message.action) {
+        // web-view 初始化完毕
+        case 'onJSBridgeReady':
+          this._ready = true
+          if (this.nodes) {
+            this._set(this.nodes)
+          }
+          break
+        // 内容 dom 加载完毕
+        case 'onLoad':
+          this.height = message.height
+          this._hook('onLoad')
+          this.$emit('load')
+          break
+        // 所有图片加载完毕
+        case 'onReady':
+          this.getRect().then(res => {
+            this.$emit('ready', res)
+          }).catch(() => {
+            this.$emit('ready', {})
+          })
+          break
+        // 总高度发生变化
+        case 'onHeightChange':
+          this.height = message.height
+          break
+        // 图片点击
+        case 'onImgTap':
+          this.$emit('imgtap', message.attrs)
+          if (this.previewImg) {
+            uni.previewImage({
+              current: parseInt(message.attrs.i),
+              urls: this.imgList
+            })
+          }
+          break
+        // 链接点击
+        case 'onLinkTap': {
+          const href = message.attrs.href
+          this.$emit('linktap', message.attrs)
+          if (href) {
+            // 锚点跳转
+            if (href[0] === '#') {
+              if (this.useAnchor) {
+                dom.scrollToElement(this.$refs.web, {
+                  offset: message.offset
+                })
+              }
+            } else if (href.includes('://')) {
+              // 打开外链
+              if (this.copyLink) {
+                plus.runtime.openWeb(href)
+              }
+            } else {
+              uni.navigateTo({
+                url: href,
+                fail () {
+                  uni.switchTab({
+                    url: href
+                  })
+                }
+              })
+            }
+          }
+          break
+        }
+        case 'onPlay':
+          this.$emit('play')
+          break
+        // 获取到锚点的偏移量
+        case 'getOffset':
+          if (typeof message.offset === 'number') {
+            dom.scrollToElement(this.$refs.web, {
+              offset: message.offset + this._navigateTo.offset
+            })
+            this._navigateTo.resolve()
+          } else {
+            this._navigateTo.reject(Error('Label not found'))
+          }
+          break
+        // 点击
+        case 'onClick':
+          this.$emit('tap')
+          this.$emit('click')
+          break
+        // 出错
+        case 'onError':
+          this.$emit('error', {
+            source: message.source,
+            attrs: message.attrs
+          })
+      }
+    }
+    // #endif
+  }
+}
+</script>
+
+<style>
+/* #ifndef APP-PLUS-NVUE */
+/* 根节点样式 */
+._root {
+  padding: 1px 0;
+  overflow-x: auto;
+  overflow-y: hidden;
+  -webkit-overflow-scrolling: touch;
+}
+
+/* 长按复制 */
+._select {
+  user-select: text;
+}
+/* #endif */
+</style>

+ 87 - 0
uni_modules/uv-parse/package.json

@@ -0,0 +1,87 @@
+{
+  "id": "uv-parse",
+  "displayName": "uv-parse 富文本解析器 全面兼容vue3+2、app、h5、小程序等多端",
+  "version": "1.0.4",
+  "description": "uv-parse 该组件一般用于富文本解析场景,比如解析文章内容,商品详情,带原生HTML标签的各类字符串等,此组件和uni-app官方的rich-text组件功能有重合之处,但是也有不同的地方。",
+  "keywords": [
+    "uv-parse",
+    "uvui",
+    "uv-ui",
+    "parse",
+    "富文本"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+    	"ads": "无",
+    	"data": "插件不采集任何数据",
+    	"permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [
+			"uv-ui-tools"
+		],
+    "encrypt": [],
+    "platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "y",
+					"vue3": "y"
+				},
+				"App": {
+					"app-vue": "y",
+					"app-nvue": "y"
+				},
+				"H5-mobile": {
+					"Safari": "y",
+					"Android Browser": "y",
+					"微信浏览器(Android)": "y",
+					"QQ浏览器(Android)": "y"
+				},
+				"H5-pc": {
+					"Chrome": "y",
+					"IE": "y",
+					"Edge": "y",
+					"Firefox": "y",
+					"Safari": "y"
+				},
+				"小程序": {
+					"微信": "y",
+					"阿里": "y",
+					"百度": "y",
+					"字节跳动": "y",
+					"QQ": "y",
+					"钉钉": "u",
+					"快手": "u",
+					"飞书": "u",
+					"京东": "u"
+				},
+				"快应用": {
+					"华为": "u",
+					"联盟": "u"
+				}
+			}
+		}
+  }
+}

+ 21 - 0
uni_modules/uv-parse/readme.md

@@ -0,0 +1,21 @@
+## Parse 富文本解析器
+
+> **组件名:uv-parse**
+
+该组件一般用于富文本解析场景,比如解析文章内容,商品详情,带原生`HTML`标签的各类字符串等,此组件和`uni-app`官方的`rich-text`组件功能有重合之处,但是也有不同的地方。
+
+该插件只提供富文本的解析,该功能已经足够丰富。如果需要富文本的编辑,可使用`uniapp`官方提供的组件。
+
+# <a href="https://www.uvui.cn/components/parse.html" target="_blank">查看文档</a>
+
+## [下载完整示例项目](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+### [更多插件,请关注uv-ui组件库](https://ext.dcloud.net.cn/plugin?name=uv-ui)
+
+<a href="https://ext.dcloud.net.cn/plugin?name=uv-ui" target="_blank">
+
+![image](https://mp-a667b617-c5f1-4a2d-9a54-683a67cff588.cdn.bspapp.com/uv-ui/banner.png)
+
+</a>
+
+#### 如使用过程中有任何问题反馈,或者您对uv-ui有一些好的建议,欢迎加入uv-ui官方交流群:<a href="https://www.uvui.cn/components/addQQGroup.html" target="_blank">官方QQ群</a>

+ 224 - 0
uni_modules/uv-parse/static/app-plus/uv-parse/js/handler.js

@@ -0,0 +1,224 @@
+'use strict'
+
+// 等待初始化完毕
+document.addEventListener('UniAppJSBridgeReady', () => {
+    document.body.onclick = function () {
+        return uni.postMessage({
+            data: {
+                action: 'onClick'
+            }
+        })
+    }
+
+    uni.postMessage({
+        data: {
+            action: 'onJSBridgeReady'
+        }
+    })
+})
+let options
+let medias = []
+/**
+ * @description 获取标签的所有属性
+ * @param {Element} ele
+ */
+
+function getAttrs(ele) {
+    const attrs = Object.create(null)
+
+    for (let i = ele.attributes.length; i--;) {
+        attrs[ele.attributes[i].name] = ele.attributes[i].value
+    }
+
+    return attrs
+}
+/**
+ * @description 图片加载出错
+ */
+
+function onImgError() {
+    if (options[1]) {
+        this.src = options[1]
+        this.onerror = null
+    } // 取消监听点击
+
+    this.onclick = null
+    this.ontouchstart = null
+    uni.postMessage({
+        data: {
+            action: 'onError',
+            source: 'img',
+            attrs: getAttrs(this)
+        }
+    })
+}
+/**
+ * @description 创建 dom 结构
+ * @param {object[]} nodes 节点数组
+ * @param {Element} parent 父节点
+ * @param {string} namespace 命名空间
+ */
+
+function createDom(nodes, parent, namespace) {
+    const _loop = function _loop(i) {
+        const node = nodes[i]
+        let ele = void 0
+
+        if (!node.type || node.type == 'node') {
+            let { name } = node // svg 需要设置 namespace
+
+            if (name == 'svg') namespace = 'http://www.w3.org/2000/svg'
+            if (name == 'html' || name == 'body') name = 'div' // 创建标签
+
+            if (!namespace) ele = document.createElement(name); else ele = document.createElementNS(namespace, name) // 设置属性
+
+            for (const item in node.attrs) {
+                ele.setAttribute(item, node.attrs[item])
+            } // 递归创建子节点
+
+            if (node.children) createDom(node.children, ele, namespace) // 处理图片
+
+            if (name == 'img') {
+                if (!ele.src && ele.getAttribute('data-src')) ele.src = ele.getAttribute('data-src')
+
+                if (!node.attrs.ignore) {
+                    // 监听图片点击事件
+                    ele.onclick = function (e) {
+                        e.stopPropagation()
+                        uni.postMessage({
+                            data: {
+                                action: 'onImgTap',
+                                attrs: getAttrs(this)
+                            }
+                        })
+                    }
+                }
+
+                if (options[2]) {
+                    image = new Image()
+                    image.src = ele.src
+                    ele.src = options[2]
+
+                    image.onload = function () {
+                        ele.src = this.src
+                    }
+
+                    image.onerror = function () {
+                        ele.onerror()
+                    }
+                }
+
+                ele.onerror = onImgError
+            } // 处理链接
+            else if (name == 'a') {
+                ele.addEventListener('click', function (e) {
+                    e.stopPropagation()
+                    e.preventDefault() // 阻止默认跳转
+
+                    const href = this.getAttribute('href')
+                    let offset
+                    if (href && href[0] == '#') offset = (document.getElementById(href.substr(1)) || {}).offsetTop
+                    uni.postMessage({
+                        data: {
+                            action: 'onLinkTap',
+                            attrs: getAttrs(this),
+                            offset
+                        }
+                    })
+                }, true)
+            } // 处理音视频
+            else if (name == 'video' || name == 'audio') {
+                medias.push(ele)
+
+                if (!node.attrs.autoplay) {
+                    if (!node.attrs.controls) ele.setAttribute('controls', 'true') // 空白图占位
+
+                    if (!node.attrs.poster) ele.setAttribute('poster', "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'/>")
+                }
+
+                if (options[3]) {
+                    ele.onplay = function () {
+                        for (let _i = 0; _i < medias.length; _i++) {
+                            if (medias[_i] != this) medias[_i].pause()
+                        }
+                    }
+                }
+
+                ele.onerror = function () {
+                    uni.postMessage({
+                        data: {
+                            action: 'onError',
+                            source: name,
+                            attrs: getAttrs(this)
+                        }
+                    })
+                }
+            } // 处理表格
+            else if (name == 'table' && options[4] && !ele.style.cssText.includes('inline')) {
+                const div = document.createElement('div')
+                div.style.overflow = 'auto'
+                div.appendChild(ele)
+                ele = div
+            } else if (name == 'svg') namespace = void 0
+        } else ele = document.createTextNode(node.text.replace(/&amp;/g, '&'))
+
+        parent.appendChild(ele)
+    }
+
+    for (let i = 0; i < nodes.length; i++) {
+        var image
+
+        _loop(i)
+    }
+} // 设置 html 内容
+
+window.setContent = function (nodes, opts, append) {
+    const ele = document.getElementById('content') // 背景颜色
+
+    if (opts[0]) document.body.bgColor = opts[0] // 长按复制
+
+    if (!opts[5]) ele.style.userSelect = 'none'
+
+    if (!append) {
+        ele.innerHTML = '' // 不追加则先清空
+
+        medias = []
+    }
+
+    options = opts
+    const fragment = document.createDocumentFragment()
+    createDom(nodes, fragment)
+    ele.appendChild(fragment) // 触发事件
+
+    let height = ele.scrollHeight
+    uni.postMessage({
+        data: {
+            action: 'onLoad',
+            height
+        }
+    })
+    clearInterval(window.timer)
+    let ready = false
+    window.timer = setInterval(() => {
+        if (ele.scrollHeight != height) {
+            height = ele.scrollHeight
+            uni.postMessage({
+                data: {
+                    action: 'onHeightChange',
+                    height
+                }
+            })
+        } else if (!ready) {
+            ready = true
+            uni.postMessage({
+                data: {
+                    action: 'onReady'
+                }
+            })
+        }
+    }, 350)
+} // 回收计时器
+
+window.onunload = function () {
+    clearInterval(window.timer)
+}

+ 19 - 0
uni_modules/uv-parse/static/app-plus/uv-parse/js/uni.webview.min.js

@@ -0,0 +1,19 @@
+!(function (e, n) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = n() : typeof define === 'function' && define.amd ? define(n) : (e = e || self).uni = n() }(this, (() => {
+    'use strict'
+
+    try { const e = {}; Object.defineProperty(e, 'passive', { get() { !0 } }), window.addEventListener('test-passive', null, e) } catch (e) {} const n = Object.prototype.hasOwnProperty; function t(e, t) { return n.call(e, t) } const i = []; const a = function (e, n) { const t = { options: { timestamp: +new Date() }, name: e, arg: n }; if (window.__dcloud_weex_postMessage || window.__dcloud_weex_) { if (e === 'postMessage') { const a = { data: [n] }; return window.__dcloud_weex_postMessage ? window.__dcloud_weex_postMessage(a) : window.__dcloud_weex_.postMessage(JSON.stringify(a)) } const o = { type: 'WEB_INVOKE_APPSERVICE', args: { data: t, webviewIds: i } }; window.__dcloud_weex_postMessage ? window.__dcloud_weex_postMessageToService(o) : window.__dcloud_weex_.postMessageToService(JSON.stringify(o)) } if (!window.plus) return window.parent.postMessage({ type: 'WEB_INVOKE_APPSERVICE', data: t, pageId: '' }, '*'); if (i.length === 0) { const r = plus.webview.currentWebview(); if (!r) throw new Error('plus.webview.currentWebview() is undefined'); const d = r.parent(); let s = ''; s = d ? d.id : r.id, i.push(s) } if (plus.webview.getWebviewById('__uniapp__service'))plus.webview.postMessageToUniNView({ type: 'WEB_INVOKE_APPSERVICE', args: { data: t, webviewIds: i } }, '__uniapp__service'); else { const w = JSON.stringify(t); plus.webview.getLaunchWebview().evalJS('UniPlusBridge.subscribeHandler("'.concat('WEB_INVOKE_APPSERVICE', '",').concat(w, ',').concat(JSON.stringify(i), ');')) } }; const o = {
+        navigateTo() { const e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; const n = e.url; a('navigateTo', { url: encodeURI(n) }) }, navigateBack() { const e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; const n = e.delta; a('navigateBack', { delta: parseInt(n) || 1 }) }, switchTab() { const e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; const n = e.url; a('switchTab', { url: encodeURI(n) }) }, reLaunch() { const e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; const n = e.url; a('reLaunch', { url: encodeURI(n) }) }, redirectTo() { const e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; const n = e.url; a('redirectTo', { url: encodeURI(n) }) }, getEnv(e) { window.plus ? e({ plus: !0 }) : e({ h5: !0 }) }, postMessage() { const e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; a('postMessage', e.data || {}) }
+    }; const r = /uni-app/i.test(navigator.userAgent); const d = /Html5Plus/i.test(navigator.userAgent); const s = /complete|loaded|interactive/; const w = window.my && navigator.userAgent.indexOf('AlipayClient') > -1; const u = window.swan && window.swan.webView && /swan/i.test(navigator.userAgent); const c = window.qq && window.qq.miniProgram && /QQ/i.test(navigator.userAgent) && /miniProgram/i.test(navigator.userAgent); const g = window.tt && window.tt.miniProgram && /toutiaomicroapp/i.test(navigator.userAgent); const v = window.wx && window.wx.miniProgram && /micromessenger/i.test(navigator.userAgent) && /miniProgram/i.test(navigator.userAgent); const p = window.qa && /quickapp/i.test(navigator.userAgent); for (var l, _ = function () { window.UniAppJSBridge = !0, document.dispatchEvent(new CustomEvent('UniAppJSBridgeReady', { bubbles: !0, cancelable: !0 })) }, f = [function (e) { if (r || d) return window.__dcloud_weex_postMessage || window.__dcloud_weex_ ? document.addEventListener('DOMContentLoaded', e) : window.plus && s.test(document.readyState) ? setTimeout(e, 0) : document.addEventListener('plusready', e), o }, function (e) { if (v) return window.WeixinJSBridge && window.WeixinJSBridge.invoke ? setTimeout(e, 0) : document.addEventListener('WeixinJSBridgeReady', e), window.wx.miniProgram }, function (e) { if (c) return window.QQJSBridge && window.QQJSBridge.invoke ? setTimeout(e, 0) : document.addEventListener('QQJSBridgeReady', e), window.qq.miniProgram }, function (e) {
+            if (w) {
+                document.addEventListener('DOMContentLoaded', e); const n = window.my; return {
+                    navigateTo: n.navigateTo, navigateBack: n.navigateBack, switchTab: n.switchTab, reLaunch: n.reLaunch, redirectTo: n.redirectTo, postMessage: n.postMessage, getEnv: n.getEnv
+                }
+            }
+        }, function (e) { if (u) return document.addEventListener('DOMContentLoaded', e), window.swan.webView }, function (e) { if (g) return document.addEventListener('DOMContentLoaded', e), window.tt.miniProgram }, function (e) {
+            if (p) {
+                window.QaJSBridge && window.QaJSBridge.invoke ? setTimeout(e, 0) : document.addEventListener('QaJSBridgeReady', e); const n = window.qa; return {
+                    navigateTo: n.navigateTo, navigateBack: n.navigateBack, switchTab: n.switchTab, reLaunch: n.reLaunch, redirectTo: n.redirectTo, postMessage: n.postMessage, getEnv: n.getEnv
+                }
+            }
+        }, function (e) { return document.addEventListener('DOMContentLoaded', e), o }], m = 0; m < f.length && !(l = f[m](_)); m++);l || (l = {}); const E = typeof uni !== 'undefined' ? uni : {}; if (!E.navigateTo) for (const b in l)t(l, b) && (E[b] = l[b]); return E.webView = l, E
+})))

+ 1 - 0
uni_modules/uv-parse/static/app-plus/uv-parse/local.html

@@ -0,0 +1 @@
+<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"><style>body,html{width:100%;height:100%;overflow:hidden}body{margin:0}video{width:300px;height:225px}img{max-width:100%;-webkit-touch-callout:none}@keyframes show{0%{opacity:0}100%{opacity:1}}</style></head><body><div id="content"></div><script type="text/javascript" src="./js/uni.webview.min.js"></script><script type="text/javascript" src="./js/handler.js"></script></body>

+ 76 - 0
uni_modules/uv-ui-tools/changelog.md

@@ -0,0 +1,76 @@
+## 1.1.25(2024-01-20)
+1.1.20版本更新
+## 1.1.24(2023-12-21)
+1. luch-request更新
+## 1.1.23(2023-12-12)
+1. 1.1.19版本
+## 1.1.22(2023-11-28)
+1. 优化
+## 1.1.21(2023-11-10)
+1. 1.1.17版本
+## 1.1.20(2023-10-30)
+1. 1.1.16版本
+## 1.1.19(2023-10-13)
+1. 兼容vue3
+## 1.1.18(2023-10-12)
+1. 1.1.15版本
+## 1.1.17(2023-09-27)
+1. 1.1.14版本发布
+## 1.1.16(2023-09-15)
+1. 1.1.13版本发布
+## 1.1.15(2023-09-15)
+1. 更新button.js相关按钮支持open-type="agreePrivacyAuthorization"
+## 1.1.14(2023-09-14)
+1. 优化dayjs
+## 1.1.13(2023-09-13)
+1. 优化,$uv中增加unit参数,方便组件中使用
+## 1.1.12(2023-09-10)
+1. 升级版本
+## 1.1.11(2023-09-04)
+1. 1.1.11版本
+## 1.1.10(2023-08-31)
+1. 修复customStyle和customClass存在冲突的问题
+## 1.1.9(2023-08-27)
+1. 版本升级
+2. 优化
+## 1.1.8(2023-08-24)
+1. 版本升级
+## 1.1.7(2023-08-22)
+1. 版本升级
+## 1.1.6(2023-08-18)
+uvui版本:1.1.6
+## 1.0.15(2023-08-14)
+1. 更新uvui版本号
+## 1.0.13(2023-08-06)
+1. 优化
+## 1.0.12(2023-08-06)
+1. 修改版本号
+## 1.0.11(2023-08-06)
+1. 路由增加events参数
+2. 路由拦截修复
+## 1.0.10(2023-08-01)
+1. 优化
+## 1.0.9(2023-06-28)
+优化openType.js
+## 1.0.8(2023-06-15)
+1. 修改支付宝报错的BUG
+## 1.0.7(2023-06-07)
+1. 解决微信小程序使用uvui提示 Some selectors are not allowed in component wxss, including tag name selectors, ID selectors, and attribute selectors
+2. 解决上述提示,需要在uni.scss配置$uvui-nvue-style: false; 然后在APP.vue下面引入uvui内置的基础样式:@import '@/uni_modules/uv-ui-tools/index.scss';
+## 1.0.6(2023-06-04)
+1.  uv-ui-tools 优化工具组件,兼容更多功能
+2.  小程序分享功能优化等
+## 1.0.5(2023-06-02)
+1. 修改扩展使用mixin中方法的问题
+## 1.0.4(2023-05-23)
+1. 兼容百度小程序修改bem函数
+## 1.0.3(2023-05-16)
+1. 优化组件依赖,修改后无需全局引入,组件导入即可使用
+2. 优化部分功能
+## 1.0.2(2023-05-10)
+1. 增加Http请求封装
+2. 优化
+## 1.0.1(2023-05-04)
+1. 修改名称及备注
+## 1.0.0(2023-05-04)
+1. uv-ui工具集首次发布

+ 6 - 0
uni_modules/uv-ui-tools/components/uv-ui-tools/uv-ui-tools.vue

@@ -0,0 +1,6 @@
+<template>
+</template>
+<script>
+</script>
+<style>
+</style>

+ 79 - 0
uni_modules/uv-ui-tools/index.js

@@ -0,0 +1,79 @@
+// 全局挂载引入http相关请求拦截插件
+import Request from './libs/luch-request'
+
+// 引入全局mixin
+import mixin from './libs/mixin/mixin.js'
+// 小程序特有的mixin
+import mpMixin from './libs/mixin/mpMixin.js'
+// #ifdef MP
+import mpShare from './libs/mixin/mpShare.js'
+// #endif
+
+// 路由封装
+import route from './libs/util/route.js'
+// 公共工具函数
+import * as index from './libs/function/index.js'
+// 防抖方法
+import debounce from './libs/function/debounce.js'
+// 节流方法
+import throttle from './libs/function/throttle.js'
+// 规则检验
+import * as test from './libs/function/test.js'
+
+// 颜色渐变相关,colorGradient-颜色渐变,hexToRgb-十六进制颜色转rgb颜色,rgbToHex-rgb转十六进制
+import * as colorGradient from './libs/function/colorGradient.js'
+
+// 配置信息
+import config from './libs/config/config.js'
+// 平台
+import platform from './libs/function/platform'
+
+const $uv = {
+	route,
+	config,
+	test,
+	date: index.timeFormat, // 另名date
+	...index,
+	colorGradient: colorGradient.colorGradient,
+	hexToRgb: colorGradient.hexToRgb,
+	rgbToHex: colorGradient.rgbToHex,
+	colorToRgba: colorGradient.colorToRgba,
+	http: new Request(),
+	debounce,
+	throttle,
+	platform,
+	mixin,
+	mpMixin
+}
+uni.$uv = $uv;
+const install = (Vue,options={}) => {
+		// #ifndef APP-NVUE
+		const cloneMixin = index.deepClone(mixin);
+		delete cloneMixin?.props?.customClass;
+		delete cloneMixin?.props?.customStyle;
+		Vue.mixin(cloneMixin);
+		// #ifdef MP
+		if(options.mpShare){
+			Vue.mixin(mpShare);
+		}
+		// #endif
+		// #endif
+		// #ifdef VUE2
+		// 时间格式化,同时两个名称,date和timeFormat
+		Vue.filter('timeFormat', (timestamp, format) => uni.$uv.timeFormat(timestamp, format));
+		Vue.filter('date', (timestamp, format) => uni.$uv.timeFormat(timestamp, format));
+		// 将多久以前的方法,注入到全局过滤器
+		Vue.filter('timeFrom', (timestamp, format) => uni.$uv.timeFrom(timestamp, format));
+		// 同时挂载到uni和Vue.prototype中
+		// #ifndef APP-NVUE
+		// 只有vue,挂载到Vue.prototype才有意义,因为nvue中全局Vue.prototype和Vue.mixin是无效的
+		Vue.prototype.$uv = $uv;
+		// #endif
+		// #endif
+		// #ifdef VUE3
+		Vue.config.globalProperties.$uv = $uv;
+		// #endif
+}
+export default {
+	install
+}

+ 7 - 0
uni_modules/uv-ui-tools/index.scss

@@ -0,0 +1,7 @@
+// 引入公共基础类
+@import "./libs/css/common.scss";
+
+// 非nvue的样式
+/* #ifndef APP-NVUE */
+@import "./libs/css/vue.scss";
+/* #endif */

+ 34 - 0
uni_modules/uv-ui-tools/libs/config/config.js

@@ -0,0 +1,34 @@
+// 此版本发布于2024-01-20
+const version = '1.1.20'
+
+// 开发环境才提示,生产环境不会提示
+if (process.env.NODE_ENV === 'development') {
+	console.log(`\n %c uvui V${version} https://www.uvui.cn/ \n\n`, 'color: #ffffff; background: #3c9cff; padding:5px 0; border-radius: 5px;');
+}
+
+export default {
+    v: version,
+    version,
+    // 主题名称
+    type: [
+        'primary',
+        'success',
+        'info',
+        'error',
+        'warning'
+    ],
+    // 颜色部分,本来可以通过scss的:export导出供js使用,但是奈何nvue不支持
+    color: {
+        'uv-primary': '#2979ff',
+        'uv-warning': '#ff9900',
+        'uv-success': '#19be6b',
+        'uv-error': '#fa3534',
+        'uv-info': '#909399',
+        'uv-main-color': '#303133',
+        'uv-content-color': '#606266',
+        'uv-tips-color': '#909399',
+        'uv-light-color': '#c0c4cc'
+    },
+	// 默认单位,可以通过配置为rpx,那么在用于传入组件大小参数为数值时,就默认为rpx
+	unit: 'px'
+}

+ 32 - 0
uni_modules/uv-ui-tools/libs/css/color.scss

@@ -0,0 +1,32 @@
+$uv-main-color: #303133 !default;
+$uv-content-color: #606266 !default;
+$uv-tips-color: #909193 !default;
+$uv-light-color: #c0c4cc !default;
+$uv-border-color: #dadbde !default;
+$uv-bg-color: #f3f4f6 !default;
+$uv-disabled-color: #c8c9cc !default;
+
+$uv-primary: #3c9cff !default;
+$uv-primary-dark: #398ade !default;
+$uv-primary-disabled: #9acafc !default;
+$uv-primary-light: #ecf5ff !default;
+
+$uv-warning: #f9ae3d !default;
+$uv-warning-dark: #f1a532 !default;
+$uv-warning-disabled: #f9d39b !default;
+$uv-warning-light: #fdf6ec !default;
+
+$uv-success: #5ac725 !default;
+$uv-success-dark: #53c21d !default;
+$uv-success-disabled: #a9e08f !default;
+$uv-success-light: #f5fff0;
+
+$uv-error: #f56c6c !default;
+$uv-error-dark: #e45656 !default;
+$uv-error-disabled: #f7b2b2 !default;
+$uv-error-light: #fef0f0 !default;
+
+$uv-info: #909399 !default;
+$uv-info-dark: #767a82 !default;
+$uv-info-disabled: #c4c6c9 !default;
+$uv-info-light: #f4f4f5 !default;

+ 100 - 0
uni_modules/uv-ui-tools/libs/css/common.scss

@@ -0,0 +1,100 @@
+// 超出行数,自动显示行尾省略号,最多5行
+// 来自uvui的温馨提示:当您在控制台看到此报错,说明需要在App.vue的style标签加上【lang="scss"】
+@for $i from 1 through 5 {
+	.uv-line-#{$i} {
+		/* #ifdef APP-NVUE */
+		// nvue下,可以直接使用lines属性,这是weex特有样式
+		lines: $i;
+		text-overflow: ellipsis;
+		overflow: hidden;
+		flex: 1;
+		/* #endif */
+
+		/* #ifndef APP-NVUE */
+		// vue下,单行和多行显示省略号需要单独处理
+		@if $i == '1' {
+			overflow: hidden;
+			white-space: nowrap;
+			text-overflow: ellipsis;
+		} @else {
+			display: -webkit-box!important;
+			overflow: hidden;
+			text-overflow: ellipsis;
+			word-break: break-all;
+			-webkit-line-clamp: $i;
+			-webkit-box-orient: vertical!important;
+		}
+		/* #endif */
+	}
+}
+$uv-bordercolor: #dadbde;
+@if variable-exists(uv-border-color) {
+	$uv-bordercolor: $uv-border-color;
+}
+
+// 此处加上!important并非随意乱用,而是因为目前*.nvue页面编译到H5时,
+// App.vue的样式会被uni-app的view元素的自带border属性覆盖,导致无效
+// 综上,这是uni-app的缺陷导致我们为了多端兼容,而必须要加上!important
+// 移动端兼容性较好,直接使用0.5px去实现细边框,不使用伪元素形式实现
+.uv-border {
+	border-width: 0.5px!important;
+	border-color: $uv-bordercolor!important;
+    border-style: solid;
+}
+
+.uv-border-top {
+	border-top-width: 0.5px!important;
+	border-color: $uv-bordercolor!important;
+    border-top-style: solid;
+}
+
+.uv-border-left {
+	border-left-width: 0.5px!important;
+	border-color: $uv-bordercolor!important;
+    border-left-style: solid;
+}
+
+.uv-border-right {
+	border-right-width: 0.5px!important;
+	border-color: $uv-bordercolor!important;
+    border-right-style: solid;
+}
+
+.uv-border-bottom {
+	border-bottom-width: 0.5px!important;
+	border-color: $uv-bordercolor!important;
+    border-bottom-style: solid;
+}
+
+.uv-border-top-bottom {
+	border-top-width: 0.5px!important;
+	border-bottom-width: 0.5px!important;
+	border-color: $uv-bordercolor!important;
+    border-top-style: solid;
+    border-bottom-style: solid;
+}
+
+// 去除button的所有默认样式,让其表现跟普通的view、text元素一样
+.uv-reset-button {
+	padding: 0;
+	background-color: transparent;
+	/* #ifndef APP-PLUS */
+	font-size: inherit;
+	line-height: inherit;
+	color: inherit;
+	/* #endif */
+	/* #ifdef APP-NVUE */
+	border-width: 0;
+	/* #endif */
+}
+
+/* #ifndef APP-NVUE */
+.uv-reset-button::after {
+   border: none;
+}
+/* #endif */
+
+.uv-hover-class {
+	opacity: 0.7;
+}
+

+ 23 - 0
uni_modules/uv-ui-tools/libs/css/components.scss

@@ -0,0 +1,23 @@
+@mixin flex($direction: row) {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: $direction;
+}
+
+/* #ifndef APP-NVUE */
+// 由于uvui是基于nvue环境进行开发的,此环境中普通元素默认为flex-direction: column;
+// 所以在非nvue中,需要对元素进行重置为flex-direction: column; 否则可能会表现异常
+$uvui-nvue-style: true !default;
+@if $uvui-nvue-style == true {
+	view, scroll-view, swiper-item {
+		display: flex;
+		flex-direction: column;
+		flex-shrink: 0;
+		flex-grow: 0;
+		flex-basis: auto;
+		align-items: stretch;
+		align-content: flex-start;
+	}
+}
+/* #endif */

+ 111 - 0
uni_modules/uv-ui-tools/libs/css/variable.scss

@@ -0,0 +1,111 @@
+// 超出行数,自动显示行尾省略号,最多5行
+// 来自uvui的温馨提示:当您在控制台看到此报错,说明需要在App.vue的style标签加上【lang="scss"】
+@if variable-exists(show-lines) {
+	@for $i from 1 through 5 {
+		.uv-line-#{$i} {
+			/* #ifdef APP-NVUE */
+			// nvue下,可以直接使用lines属性,这是weex特有样式
+			lines: $i;
+			text-overflow: ellipsis;
+			overflow: hidden;
+			flex: 1;
+			/* #endif */
+
+			/* #ifndef APP-NVUE */
+			// vue下,单行和多行显示省略号需要单独处理
+			@if $i == '1' {
+				overflow: hidden;
+				white-space: nowrap;
+				text-overflow: ellipsis;
+			} @else {
+				display: -webkit-box!important;
+				overflow: hidden;
+				text-overflow: ellipsis;
+				word-break: break-all;
+				-webkit-line-clamp: $i;
+				-webkit-box-orient: vertical!important;
+			}
+			/* #endif */
+		}
+	}
+}
+@if variable-exists(show-border) {
+	$uv-bordercolor: #dadbde;
+	@if variable-exists(uv-border-color) {
+		$uv-bordercolor: $uv-border-color;
+	}
+	// 此处加上!important并非随意乱用,而是因为目前*.nvue页面编译到H5时,
+	// App.vue的样式会被uni-app的view元素的自带border属性覆盖,导致无效
+	// 综上,这是uni-app的缺陷导致我们为了多端兼容,而必须要加上!important
+	// 移动端兼容性较好,直接使用0.5px去实现细边框,不使用伪元素形式实现
+	@if variable-exists(show-border-surround) {
+		.uv-border {
+			border-width: 0.5px!important;
+			border-color: $uv-bordercolor!important;
+			border-style: solid;
+		}
+	}
+	@if variable-exists(show-border-top) {
+		.uv-border-top {
+			border-top-width: 0.5px!important;
+			border-color: $uv-bordercolor!important;
+			border-top-style: solid;
+		}
+	}
+	@if variable-exists(show-border-left) {
+		.uv-border-left {
+			border-left-width: 0.5px!important;
+			border-color: $uv-bordercolor!important;
+			border-left-style: solid;
+		}
+	}
+	@if variable-exists(show-border-right) {
+		.uv-border-right {
+			border-right-width: 0.5px!important;
+			border-color: $uv-bordercolor!important;
+			border-right-style: solid;
+		}
+	}
+	@if variable-exists(show-border-bottom) {
+		.uv-border-bottom {
+			border-bottom-width: 0.5px!important;
+			border-color: $uv-bordercolor!important;
+				border-bottom-style: solid;
+		}
+	}
+	@if variable-exists(show-border-top-bottom) {
+		.uv-border-top-bottom {
+			border-top-width: 0.5px!important;
+			border-bottom-width: 0.5px!important;
+			border-color: $uv-bordercolor!important;
+			border-top-style: solid;
+			border-bottom-style: solid;
+		}
+	}
+}
+@if variable-exists(show-reset-button) {
+	// 去除button的所有默认样式,让其表现跟普通的view、text元素一样
+	.uv-reset-button {
+		padding: 0;
+		background-color: transparent;
+		/* #ifndef APP-PLUS */
+		font-size: inherit;
+		line-height: inherit;
+		color: inherit;
+		/* #endif */
+		/* #ifdef APP-NVUE */
+		border-width: 0;
+		/* #endif */
+	}
+
+	/* #ifndef APP-NVUE */
+	.uv-reset-button::after {
+		 border: none;
+	}
+	/* #endif */
+}
+@if variable-exists(show-hover) {
+	.uv-hover-class {
+		opacity: 0.7;
+	}
+}

+ 40 - 0
uni_modules/uv-ui-tools/libs/css/vue.scss

@@ -0,0 +1,40 @@
+// 历遍生成4个方向的底部安全区
+@each $d in top, right, bottom, left {
+	.uv-safe-area-inset-#{$d} {
+		padding-#{$d}: 0;
+		padding-#{$d}: constant(safe-area-inset-#{$d});  
+		padding-#{$d}: env(safe-area-inset-#{$d});  
+	}
+}
+
+//提升H5端uni.toast()的层级,避免被uvui的modal等遮盖
+/* #ifdef H5 */
+uni-toast {
+    z-index: 10090;
+}
+uni-toast .uni-toast {
+   z-index: 10090;
+}
+/* #endif */
+
+// 隐藏scroll-view的滚动条
+::-webkit-scrollbar {
+    display: none;  
+    width: 0 !important;  
+    height: 0 !important;  
+    -webkit-appearance: none;  
+    background: transparent;  
+}
+
+$uvui-nvue-style: true !default;
+@if $uvui-nvue-style == false {
+	view, scroll-view, swiper-item {
+		display: flex;
+		flex-direction: column;
+		flex-shrink: 0;
+		flex-grow: 0;
+		flex-basis: auto;
+		align-items: stretch;
+		align-content: flex-start;
+	}
+}

+ 134 - 0
uni_modules/uv-ui-tools/libs/function/colorGradient.js

@@ -0,0 +1,134 @@
+/**
+ * 求两个颜色之间的渐变值
+ * @param {string} startColor 开始的颜色
+ * @param {string} endColor 结束的颜色
+ * @param {number} step 颜色等分的份额
+ * */
+function colorGradient(startColor = 'rgb(0, 0, 0)', endColor = 'rgb(255, 255, 255)', step = 10) {
+    const startRGB = hexToRgb(startColor, false) // 转换为rgb数组模式
+    const startR = startRGB[0]
+    const startG = startRGB[1]
+    const startB = startRGB[2]
+
+    const endRGB = hexToRgb(endColor, false)
+    const endR = endRGB[0]
+    const endG = endRGB[1]
+    const endB = endRGB[2]
+
+    const sR = (endR - startR) / step // 总差值
+    const sG = (endG - startG) / step
+    const sB = (endB - startB) / step
+    const colorArr = []
+    for (let i = 0; i < step; i++) {
+        // 计算每一步的hex值
+        let hex = rgbToHex(`rgb(${Math.round((sR * i + startR))},${Math.round((sG * i + startG))},${Math.round((sB
+			* i + startB))})`)
+        // 确保第一个颜色值为startColor的值
+        if (i === 0) hex = rgbToHex(startColor)
+        // 确保最后一个颜色值为endColor的值
+        if (i === step - 1) hex = rgbToHex(endColor)
+        colorArr.push(hex)
+    }
+    return colorArr
+}
+
+// 将hex表示方式转换为rgb表示方式(这里返回rgb数组模式)
+function hexToRgb(sColor, str = true) {
+    const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
+    sColor = String(sColor).toLowerCase()
+    if (sColor && reg.test(sColor)) {
+        if (sColor.length === 4) {
+            let sColorNew = '#'
+            for (let i = 1; i < 4; i += 1) {
+                sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1))
+            }
+            sColor = sColorNew
+        }
+        // 处理六位的颜色值
+        const sColorChange = []
+        for (let i = 1; i < 7; i += 2) {
+            sColorChange.push(parseInt(`0x${sColor.slice(i, i + 2)}`))
+        }
+        if (!str) {
+            return sColorChange
+        }
+        return `rgb(${sColorChange[0]},${sColorChange[1]},${sColorChange[2]})`
+    } if (/^(rgb|RGB)/.test(sColor)) {
+        const arr = sColor.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',')
+        return arr.map((val) => Number(val))
+    }
+    return sColor
+}
+
+// 将rgb表示方式转换为hex表示方式
+function rgbToHex(rgb) {
+    const _this = rgb
+    const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
+    if (/^(rgb|RGB)/.test(_this)) {
+        const aColor = _this.replace(/(?:\(|\)|rgb|RGB)*/g, '').split(',')
+        let strHex = '#'
+        for (let i = 0; i < aColor.length; i++) {
+            let hex = Number(aColor[i]).toString(16)
+            hex = String(hex).length == 1 ? `${0}${hex}` : hex // 保证每个rgb的值为2位
+            if (hex === '0') {
+                hex += hex
+            }
+            strHex += hex
+        }
+        if (strHex.length !== 7) {
+            strHex = _this
+        }
+        return strHex
+    } if (reg.test(_this)) {
+        const aNum = _this.replace(/#/, '').split('')
+        if (aNum.length === 6) {
+            return _this
+        } if (aNum.length === 3) {
+            let numHex = '#'
+            for (let i = 0; i < aNum.length; i += 1) {
+                numHex += (aNum[i] + aNum[i])
+            }
+            return numHex
+        }
+    } else {
+        return _this
+    }
+}
+
+/**
+* JS颜色十六进制转换为rgb或rgba,返回的格式为 rgba(255,255,255,0.5)字符串
+* sHex为传入的十六进制的色值
+* alpha为rgba的透明度
+*/
+function colorToRgba(color, alpha) {
+    color = rgbToHex(color)
+    // 十六进制颜色值的正则表达式
+    const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
+    /* 16进制颜色转为RGB格式 */
+    let sColor = String(color).toLowerCase()
+    if (sColor && reg.test(sColor)) {
+        if (sColor.length === 4) {
+            let sColorNew = '#'
+            for (let i = 1; i < 4; i += 1) {
+                sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1))
+            }
+            sColor = sColorNew
+        }
+        // 处理六位的颜色值
+        const sColorChange = []
+        for (let i = 1; i < 7; i += 2) {
+            sColorChange.push(parseInt(`0x${sColor.slice(i, i + 2)}`))
+        }
+        // return sColorChange.join(',')
+        return `rgba(${sColorChange.join(',')},${alpha})`
+    }
+
+    return sColor
+}
+
+export {
+    colorGradient,
+    hexToRgb,
+    rgbToHex,
+    colorToRgba
+}

+ 29 - 0
uni_modules/uv-ui-tools/libs/function/debounce.js

@@ -0,0 +1,29 @@
+let timeout = null
+
+/**
+ * 防抖原理:一定时间内,只有最后一次操作,再过wait毫秒后才执行函数
+ *
+ * @param {Function} func 要执行的回调函数
+ * @param {Number} wait 延时的时间
+ * @param {Boolean} immediate 是否立即执行
+ * @return null
+ */
+function debounce(func, wait = 500, immediate = false) {
+    // 清除定时器
+    if (timeout !== null) clearTimeout(timeout)
+    // 立即执行,此类情况一般用不到
+    if (immediate) {
+        const callNow = !timeout
+        timeout = setTimeout(() => {
+            timeout = null
+        }, wait)
+        if (callNow) typeof func === 'function' && func()
+    } else {
+        // 设置定时器,当最后一次操作后,timeout不会再被清除,所以在延时wait毫秒后执行func回调方法
+        timeout = setTimeout(() => {
+            typeof func === 'function' && func()
+        }, wait)
+    }
+}
+
+export default debounce

+ 167 - 0
uni_modules/uv-ui-tools/libs/function/digit.js

@@ -0,0 +1,167 @@
+let _boundaryCheckingState = true; // 是否进行越界检查的全局开关
+
+/**
+ * 把错误的数据转正
+ * @private
+ * @example strip(0.09999999999999998)=0.1
+ */
+function strip(num, precision = 15) {
+  return +parseFloat(Number(num).toPrecision(precision));
+}
+
+/**
+ * Return digits length of a number
+ * @private
+ * @param {*number} num Input number
+ */
+function digitLength(num) {
+  // Get digit length of e
+  const eSplit = num.toString().split(/[eE]/);
+  const len = (eSplit[0].split('.')[1] || '').length - +(eSplit[1] || 0);
+  return len > 0 ? len : 0;
+}
+
+/**
+ * 把小数转成整数,如果是小数则放大成整数
+ * @private
+ * @param {*number} num 输入数
+ */
+function float2Fixed(num) {
+  if (num.toString().indexOf('e') === -1) {
+    return Number(num.toString().replace('.', ''));
+  }
+  const dLen = digitLength(num);
+  return dLen > 0 ? strip(Number(num) * Math.pow(10, dLen)) : Number(num);
+}
+
+/**
+ * 检测数字是否越界,如果越界给出提示
+ * @private
+ * @param {*number} num 输入数
+ */
+function checkBoundary(num) {
+  if (_boundaryCheckingState) {
+    if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {
+      console.warn(`${num} 超出了精度限制,结果可能不正确`);
+    }
+  }
+}
+
+/**
+ * 把递归操作扁平迭代化
+ * @param {number[]} arr 要操作的数字数组
+ * @param {function} operation 迭代操作
+ * @private
+ */
+function iteratorOperation(arr, operation) {
+  const [num1, num2, ...others] = arr;
+  let res = operation(num1, num2);
+
+  others.forEach((num) => {
+    res = operation(res, num);
+  });
+
+  return res;
+}
+
+/**
+ * 高精度乘法
+ * @export
+ */
+export function times(...nums) {
+  if (nums.length > 2) {
+    return iteratorOperation(nums, times);
+  }
+
+  const [num1, num2] = nums;
+  const num1Changed = float2Fixed(num1);
+  const num2Changed = float2Fixed(num2);
+  const baseNum = digitLength(num1) + digitLength(num2);
+  const leftValue = num1Changed * num2Changed;
+
+  checkBoundary(leftValue);
+
+  return leftValue / Math.pow(10, baseNum);
+}
+
+/**
+ * 高精度加法
+ * @export
+ */
+export function plus(...nums) {
+  if (nums.length > 2) {
+    return iteratorOperation(nums, plus);
+  }
+
+  const [num1, num2] = nums;
+  // 取最大的小数位
+  const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));
+  // 把小数都转为整数然后再计算
+  return (times(num1, baseNum) + times(num2, baseNum)) / baseNum;
+}
+
+/**
+ * 高精度减法
+ * @export
+ */
+export function minus(...nums) {
+  if (nums.length > 2) {
+    return iteratorOperation(nums, minus);
+  }
+
+  const [num1, num2] = nums;
+  const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));
+  return (times(num1, baseNum) - times(num2, baseNum)) / baseNum;
+}
+
+/**
+ * 高精度除法
+ * @export
+ */
+export function divide(...nums) {
+  if (nums.length > 2) {
+    return iteratorOperation(nums, divide);
+  }
+
+  const [num1, num2] = nums;
+  const num1Changed = float2Fixed(num1);
+  const num2Changed = float2Fixed(num2);
+  checkBoundary(num1Changed);
+  checkBoundary(num2Changed);
+  // 重要,这里必须用strip进行修正
+  return times(num1Changed / num2Changed, strip(Math.pow(10, digitLength(num2) - digitLength(num1))));
+}
+
+/**
+ * 四舍五入
+ * @export
+ */
+export function round(num, ratio) {
+  const base = Math.pow(10, ratio);
+  let result = divide(Math.round(Math.abs(times(num, base))), base);
+  if (num < 0 && result !== 0) {
+    result = times(result, -1);
+  }
+  // 位数不足则补0
+  return result;
+}
+
+/**
+ * 是否进行边界检查,默认开启
+ * @param flag 标记开关,true 为开启,false 为关闭,默认为 true
+ * @export
+ */
+export function enableBoundaryChecking(flag = true) {
+  _boundaryCheckingState = flag;
+}
+
+
+export default {
+  times,
+  plus,
+  minus,
+  divide,
+  round,
+  enableBoundaryChecking,
+};
+

+ 734 - 0
uni_modules/uv-ui-tools/libs/function/index.js

@@ -0,0 +1,734 @@
+import { number, empty } from './test.js'
+import { round } from './digit.js'
+/**
+ * @description 如果value小于min,取min;如果value大于max,取max
+ * @param {number} min
+ * @param {number} max
+ * @param {number} value
+ */
+function range(min = 0, max = 0, value = 0) {
+	return Math.max(min, Math.min(max, Number(value)))
+}
+
+/**
+ * @description 用于获取用户传递值的px值  如果用户传递了"xxpx"或者"xxrpx",取出其数值部分,如果是"xxxrpx"还需要用过uni.upx2px进行转换
+ * @param {number|string} value 用户传递值的px值
+ * @param {boolean} unit
+ * @returns {number|string}
+ */
+function getPx(value, unit = false) {
+	if (number(value)) {
+		return unit ? `${value}px` : Number(value)
+	}
+	// 如果带有rpx,先取出其数值部分,再转为px值
+	if (/(rpx|upx)$/.test(value)) {
+		return unit ? `${uni.upx2px(parseInt(value))}px` : Number(uni.upx2px(parseInt(value)))
+	}
+	return unit ? `${parseInt(value)}px` : parseInt(value)
+}
+
+/**
+ * @description 进行延时,以达到可以简写代码的目的 比如: await uni.$uv.sleep(20)将会阻塞20ms
+ * @param {number} value 堵塞时间 单位ms 毫秒
+ * @returns {Promise} 返回promise
+ */
+function sleep(value = 30) {
+	return new Promise((resolve) => {
+		setTimeout(() => {
+			resolve()
+		}, value)
+	})
+}
+/**
+ * @description 运行期判断平台
+ * @returns {string} 返回所在平台(小写)
+ * @link 运行期判断平台 https://uniapp.dcloud.io/frame?id=判断平台
+ */
+function os() {
+	return uni.getSystemInfoSync().platform.toLowerCase()
+}
+/**
+ * @description 获取系统信息同步接口
+ * @link 获取系统信息同步接口 https://uniapp.dcloud.io/api/system/info?id=getsysteminfosync
+ */
+function sys() {
+	return uni.getSystemInfoSync()
+}
+
+/**
+ * @description 取一个区间数
+ * @param {Number} min 最小值
+ * @param {Number} max 最大值
+ */
+function random(min, max) {
+	if (min >= 0 && max > 0 && max >= min) {
+		const gab = max - min + 1
+		return Math.floor(Math.random() * gab + min)
+	}
+	return 0
+}
+
+/**
+ * @param {Number} len uuid的长度
+ * @param {Boolean} firstU 将返回的首字母置为"u"
+ * @param {Nubmer} radix 生成uuid的基数(意味着返回的字符串都是这个基数),2-二进制,8-八进制,10-十进制,16-十六进制
+ */
+function guid(len = 32, firstU = true, radix = null) {
+	const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('')
+	const uuid = []
+	radix = radix || chars.length
+
+	if (len) {
+		// 如果指定uuid长度,只是取随机的字符,0|x为位运算,能去掉x的小数位,返回整数位
+		for (let i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]
+	} else {
+		let r
+		// rfc4122标准要求返回的uuid中,某些位为固定的字符
+		uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'
+		uuid[14] = '4'
+
+		for (let i = 0; i < 36; i++) {
+			if (!uuid[i]) {
+				r = 0 | Math.random() * 16
+				uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]
+			}
+		}
+	}
+	// 移除第一个字符,并用u替代,因为第一个字符为数值时,该guuid不能用作id或者class
+	if (firstU) {
+		uuid.shift()
+		return `u${uuid.join('')}`
+	}
+	return uuid.join('')
+}
+
+/**
+* @description 获取父组件的参数,因为支付宝小程序不支持provide/inject的写法
+   this.$parent在非H5中,可以准确获取到父组件,但是在H5中,需要多次this.$parent.$parent.xxx
+   这里默认值等于undefined有它的含义,因为最顶层元素(组件)的$parent就是undefined,意味着不传name
+   值(默认为undefined),就是查找最顶层的$parent
+*  @param {string|undefined} name 父组件的参数名
+*/
+function $parent(name = undefined) {
+	let parent = this.$parent
+	// 通过while历遍,这里主要是为了H5需要多层解析的问题
+	while (parent) {
+		// 父组件
+		if (parent.$options && parent.$options.name !== name) {
+			// 如果组件的name不相等,继续上一级寻找
+			parent = parent.$parent
+		} else {
+			return parent
+		}
+	}
+	return false
+}
+
+/**
+ * @description 样式转换
+ * 对象转字符串,或者字符串转对象
+ * @param {object | string} customStyle 需要转换的目标
+ * @param {String} target 转换的目的,object-转为对象,string-转为字符串
+ * @returns {object|string}
+ */
+function addStyle(customStyle, target = 'object') {
+	// 字符串转字符串,对象转对象情形,直接返回
+	if (empty(customStyle) || typeof(customStyle) === 'object' && target === 'object' || target === 'string' &&
+		typeof(customStyle) === 'string') {
+		return customStyle
+	}
+	// 字符串转对象
+	if (target === 'object') {
+		// 去除字符串样式中的两端空格(中间的空格不能去掉,比如padding: 20px 0如果去掉了就错了),空格是无用的
+		customStyle = trim(customStyle)
+		// 根据";"将字符串转为数组形式
+		const styleArray = customStyle.split(';')
+		const style = {}
+		// 历遍数组,拼接成对象
+		for (let i = 0; i < styleArray.length; i++) {
+			// 'font-size:20px;color:red;',如此最后字符串有";"的话,会导致styleArray最后一个元素为空字符串,这里需要过滤
+			if (styleArray[i]) {
+				const item = styleArray[i].split(':')
+				style[trim(item[0])] = trim(item[1])
+			}
+		}
+		return style
+	}
+	// 这里为对象转字符串形式
+	let string = ''
+	for (const i in customStyle) {
+		// 驼峰转为中划线的形式,否则css内联样式,无法识别驼峰样式属性名
+		const key = i.replace(/([A-Z])/g, '-$1').toLowerCase()
+		string += `${key}:${customStyle[i]};`
+	}
+	// 去除两端空格
+	return trim(string)
+}
+
+/**
+ * @description 添加单位,如果有rpx,upx,%,px等单位结尾或者值为auto,直接返回,否则加上px单位结尾
+ * @param {string|number} value 需要添加单位的值
+ * @param {string} unit 添加的单位名 比如px
+ */
+function addUnit(value = 'auto', unit = uni?.$uv?.config?.unit ? uni?.$uv?.config?.unit : 'px') {
+	value = String(value)
+	// 用uvui内置验证规则中的number判断是否为数值
+	return number(value) ? `${value}${unit}` : value
+}
+
+/**
+ * @description 深度克隆
+ * @param {object} obj 需要深度克隆的对象
+ * @param cache 缓存
+ * @returns {*} 克隆后的对象或者原值(不是对象)
+ */
+function deepClone(obj, cache = new WeakMap()) {
+	if (obj === null || typeof obj !== 'object') return obj;
+	if (cache.has(obj)) return cache.get(obj);
+	let clone;
+	if (obj instanceof Date) {
+		clone = new Date(obj.getTime());
+	} else if (obj instanceof RegExp) {
+		clone = new RegExp(obj);
+	} else if (obj instanceof Map) {
+		clone = new Map(Array.from(obj, ([key, value]) => [key, deepClone(value, cache)]));
+	} else if (obj instanceof Set) {
+		clone = new Set(Array.from(obj, value => deepClone(value, cache)));
+	} else if (Array.isArray(obj)) {
+		clone = obj.map(value => deepClone(value, cache));
+	} else if (Object.prototype.toString.call(obj) === '[object Object]') {
+		clone = Object.create(Object.getPrototypeOf(obj));
+		cache.set(obj, clone);
+		for (const [key, value] of Object.entries(obj)) {
+			clone[key] = deepClone(value, cache);
+		}
+	} else {
+		clone = Object.assign({}, obj);
+	}
+	cache.set(obj, clone);
+	return clone;
+}
+
+/**
+ * @description JS对象深度合并
+ * @param {object} target 需要拷贝的对象
+ * @param {object} source 拷贝的来源对象
+ * @returns {object|boolean} 深度合并后的对象或者false(入参有不是对象)
+ */
+function deepMerge(target = {}, source = {}) {
+	target = deepClone(target)
+	if (typeof target !== 'object' || target === null || typeof source !== 'object' || source === null) return target;
+	const merged = Array.isArray(target) ? target.slice() : Object.assign({}, target);
+	for (const prop in source) {
+		if (!source.hasOwnProperty(prop)) continue;
+		const sourceValue = source[prop];
+		const targetValue = merged[prop];
+		if (sourceValue instanceof Date) {
+			merged[prop] = new Date(sourceValue);
+		} else if (sourceValue instanceof RegExp) {
+			merged[prop] = new RegExp(sourceValue);
+		} else if (sourceValue instanceof Map) {
+			merged[prop] = new Map(sourceValue);
+		} else if (sourceValue instanceof Set) {
+			merged[prop] = new Set(sourceValue);
+		} else if (typeof sourceValue === 'object' && sourceValue !== null) {
+			merged[prop] = deepMerge(targetValue, sourceValue);
+		} else {
+			merged[prop] = sourceValue;
+		}
+	}
+	return merged;
+}
+
+/**
+ * @description error提示
+ * @param {*} err 错误内容
+ */
+function error(err) {
+	// 开发环境才提示,生产环境不会提示
+	if (process.env.NODE_ENV === 'development') {
+		console.error(`uvui提示:${err}`)
+	}
+}
+
+/**
+ * @description 打乱数组
+ * @param {array} array 需要打乱的数组
+ * @returns {array} 打乱后的数组
+ */
+function randomArray(array = []) {
+	// 原理是sort排序,Math.random()产生0<= x < 1之间的数,会导致x-0.05大于或者小于0
+	return array.sort(() => Math.random() - 0.5)
+}
+
+// padStart 的 polyfill,因为某些机型或情况,还无法支持es7的padStart,比如电脑版的微信小程序
+// 所以这里做一个兼容polyfill的兼容处理
+if (!String.prototype.padStart) {
+	// 为了方便表示这里 fillString 用了ES6 的默认参数,不影响理解
+	String.prototype.padStart = function(maxLength, fillString = ' ') {
+		if (Object.prototype.toString.call(fillString) !== '[object String]') {
+			throw new TypeError(
+				'fillString must be String'
+			)
+		}
+		const str = this
+		// 返回 String(str) 这里是为了使返回的值是字符串字面量,在控制台中更符合直觉
+		if (str.length >= maxLength) return String(str)
+
+		const fillLength = maxLength - str.length
+		let times = Math.ceil(fillLength / fillString.length)
+		while (times >>= 1) {
+			fillString += fillString
+			if (times === 1) {
+				fillString += fillString
+			}
+		}
+		return fillString.slice(0, fillLength) + str
+	}
+}
+
+/**
+ * @description 格式化时间
+ * @param {String|Number} dateTime 需要格式化的时间戳
+ * @param {String} fmt 格式化规则 yyyy:mm:dd|yyyy:mm|yyyy年mm月dd日|yyyy年mm月dd日 hh时MM分等,可自定义组合 默认yyyy-mm-dd
+ * @returns {string} 返回格式化后的字符串
+ */
+function timeFormat(dateTime = null, formatStr = 'yyyy-mm-dd') {
+	let date
+	// 若传入时间为假值,则取当前时间
+	if (!dateTime) {
+		date = new Date()
+	}
+	// 若为unix秒时间戳,则转为毫秒时间戳(逻辑有点奇怪,但不敢改,以保证历史兼容)
+	else if (/^\d{10}$/.test(dateTime?.toString().trim())) {
+		date = new Date(dateTime * 1000)
+	}
+	// 若用户传入字符串格式时间戳,new Date无法解析,需做兼容
+	else if (typeof dateTime === 'string' && /^\d+$/.test(dateTime.trim())) {
+		date = new Date(Number(dateTime))
+	}
+	// 处理平台性差异,在Safari/Webkit中,new Date仅支持/作为分割符的字符串时间
+	// 处理 '2022-07-10 01:02:03',跳过 '2022-07-10T01:02:03'
+	else if (typeof dateTime === 'string' && dateTime.includes('-') && !dateTime.includes('T')) {
+		date = new Date(dateTime.replace(/-/g, '/'))
+	}
+	// 其他都认为符合 RFC 2822 规范
+	else {
+		date = new Date(dateTime)
+	}
+
+	const timeSource = {
+		'y': date.getFullYear().toString(), // 年
+		'm': (date.getMonth() + 1).toString().padStart(2, '0'), // 月
+		'd': date.getDate().toString().padStart(2, '0'), // 日
+		'h': date.getHours().toString().padStart(2, '0'), // 时
+		'M': date.getMinutes().toString().padStart(2, '0'), // 分
+		's': date.getSeconds().toString().padStart(2, '0') // 秒
+		// 有其他格式化字符需求可以继续添加,必须转化成字符串
+	}
+
+	for (const key in timeSource) {
+		const [ret] = new RegExp(`${key}+`).exec(formatStr) || []
+		if (ret) {
+			// 年可能只需展示两位
+			const beginIndex = key === 'y' && ret.length === 2 ? 2 : 0
+			formatStr = formatStr.replace(ret, timeSource[key].slice(beginIndex))
+		}
+	}
+
+	return formatStr
+}
+
+/**
+ * @description 时间戳转为多久之前
+ * @param {String|Number} timestamp 时间戳
+ * @param {String|Boolean} format
+ * 格式化规则如果为时间格式字符串,超出一定时间范围,返回固定的时间格式;
+ * 如果为布尔值false,无论什么时间,都返回多久以前的格式
+ * @returns {string} 转化后的内容
+ */
+function timeFrom(timestamp = null, format = 'yyyy-mm-dd') {
+	if (timestamp == null) timestamp = Number(new Date())
+	timestamp = parseInt(timestamp)
+	// 判断用户输入的时间戳是秒还是毫秒,一般前端js获取的时间戳是毫秒(13位),后端传过来的为秒(10位)
+	if (timestamp.toString().length == 10) timestamp *= 1000
+	let timer = (new Date()).getTime() - timestamp
+	timer = parseInt(timer / 1000)
+	// 如果小于5分钟,则返回"刚刚",其他以此类推
+	let tips = ''
+	switch (true) {
+		case timer < 300:
+			tips = '刚刚'
+			break
+		case timer >= 300 && timer < 3600:
+			tips = `${parseInt(timer / 60)}分钟前`
+			break
+		case timer >= 3600 && timer < 86400:
+			tips = `${parseInt(timer / 3600)}小时前`
+			break
+		case timer >= 86400 && timer < 2592000:
+			tips = `${parseInt(timer / 86400)}天前`
+			break
+		default:
+			// 如果format为false,则无论什么时间戳,都显示xx之前
+			if (format === false) {
+				if (timer >= 2592000 && timer < 365 * 86400) {
+					tips = `${parseInt(timer / (86400 * 30))}个月前`
+				} else {
+					tips = `${parseInt(timer / (86400 * 365))}年前`
+				}
+			} else {
+				tips = timeFormat(timestamp, format)
+			}
+	}
+	return tips
+}
+
+/**
+ * @description 去除空格
+ * @param String str 需要去除空格的字符串
+ * @param String pos both(左右)|left|right|all 默认both
+ */
+function trim(str, pos = 'both') {
+	str = String(str)
+	if (pos == 'both') {
+		return str.replace(/^\s+|\s+$/g, '')
+	}
+	if (pos == 'left') {
+		return str.replace(/^\s*/, '')
+	}
+	if (pos == 'right') {
+		return str.replace(/(\s*$)/g, '')
+	}
+	if (pos == 'all') {
+		return str.replace(/\s+/g, '')
+	}
+	return str
+}
+
+/**
+ * @description 对象转url参数
+ * @param {object} data,对象
+ * @param {Boolean} isPrefix,是否自动加上"?"
+ * @param {string} arrayFormat 规则 indices|brackets|repeat|comma
+ */
+function queryParams(data = {}, isPrefix = true, arrayFormat = 'brackets') {
+	const prefix = isPrefix ? '?' : ''
+	const _result = []
+	if (['indices', 'brackets', 'repeat', 'comma'].indexOf(arrayFormat) == -1) arrayFormat = 'brackets'
+	for (const key in data) {
+		const value = data[key]
+		// 去掉为空的参数
+		if (['', undefined, null].indexOf(value) >= 0) {
+			continue
+		}
+		// 如果值为数组,另行处理
+		if (value.constructor === Array) {
+			// e.g. {ids: [1, 2, 3]}
+			switch (arrayFormat) {
+				case 'indices':
+					// 结果: ids[0]=1&ids[1]=2&ids[2]=3
+					for (let i = 0; i < value.length; i++) {
+						_result.push(`${key}[${i}]=${value[i]}`)
+					}
+					break
+				case 'brackets':
+					// 结果: ids[]=1&ids[]=2&ids[]=3
+					value.forEach((_value) => {
+						_result.push(`${key}[]=${_value}`)
+					})
+					break
+				case 'repeat':
+					// 结果: ids=1&ids=2&ids=3
+					value.forEach((_value) => {
+						_result.push(`${key}=${_value}`)
+					})
+					break
+				case 'comma':
+					// 结果: ids=1,2,3
+					let commaStr = ''
+					value.forEach((_value) => {
+						commaStr += (commaStr ? ',' : '') + _value
+					})
+					_result.push(`${key}=${commaStr}`)
+					break
+				default:
+					value.forEach((_value) => {
+						_result.push(`${key}[]=${_value}`)
+					})
+			}
+		} else {
+			_result.push(`${key}=${value}`)
+		}
+	}
+	return _result.length ? prefix + _result.join('&') : ''
+}
+
+/**
+ * 显示消息提示框
+ * @param {String} title 提示的内容,长度与 icon 取值有关。
+ * @param {Number} duration 提示的延迟时间,单位毫秒,默认:2000
+ */
+function toast(title, duration = 2000) {
+	uni.showToast({
+		title: String(title),
+		icon: 'none',
+		duration
+	})
+}
+
+/**
+ * @description 根据主题type值,获取对应的图标
+ * @param {String} type 主题名称,primary|info|error|warning|success
+ * @param {boolean} fill 是否使用fill填充实体的图标
+ */
+function type2icon(type = 'success', fill = false) {
+	// 如果非预置值,默认为success
+	if (['primary', 'info', 'error', 'warning', 'success'].indexOf(type) == -1) type = 'success'
+	let iconName = ''
+	// 目前(2019-12-12),info和primary使用同一个图标
+	switch (type) {
+		case 'primary':
+			iconName = 'info-circle'
+			break
+		case 'info':
+			iconName = 'info-circle'
+			break
+		case 'error':
+			iconName = 'close-circle'
+			break
+		case 'warning':
+			iconName = 'error-circle'
+			break
+		case 'success':
+			iconName = 'checkmark-circle'
+			break
+		default:
+			iconName = 'checkmark-circle'
+	}
+	// 是否是实体类型,加上-fill,在icon组件库中,实体的类名是后面加-fill的
+	if (fill) iconName += '-fill'
+	return iconName
+}
+
+/**
+ * @description 数字格式化
+ * @param {number|string} number 要格式化的数字
+ * @param {number} decimals 保留几位小数
+ * @param {string} decimalPoint 小数点符号
+ * @param {string} thousandsSeparator 千分位符号
+ * @returns {string} 格式化后的数字
+ */
+function priceFormat(number, decimals = 0, decimalPoint = '.', thousandsSeparator = ',') {
+	number = (`${number}`).replace(/[^0-9+-Ee.]/g, '')
+	const n = !isFinite(+number) ? 0 : +number
+	const prec = !isFinite(+decimals) ? 0 : Math.abs(decimals)
+	const sep = (typeof thousandsSeparator === 'undefined') ? ',' : thousandsSeparator
+	const dec = (typeof decimalPoint === 'undefined') ? '.' : decimalPoint
+	let s = ''
+
+	s = (prec ? round(n, prec) + '' : `${Math.round(n)}`).split('.')
+	const re = /(-?\d+)(\d{3})/
+	while (re.test(s[0])) {
+		s[0] = s[0].replace(re, `$1${sep}$2`)
+	}
+
+	if ((s[1] || '').length < prec) {
+		s[1] = s[1] || ''
+		s[1] += new Array(prec - s[1].length + 1).join('0')
+	}
+	return s.join(dec)
+}
+
+/**
+ * @description 获取duration值
+ * 如果带有ms或者s直接返回,如果大于一定值,认为是ms单位,小于一定值,认为是s单位
+ * 比如以30位阈值,那么300大于30,可以理解为用户想要的是300ms,而不是想花300s去执行一个动画
+ * @param {String|number} value 比如: "1s"|"100ms"|1|100
+ * @param {boolean} unit  提示: 如果是false 默认返回number
+ * @return {string|number}
+ */
+function getDuration(value, unit = true) {
+	const valueNum = parseInt(value)
+	if (unit) {
+		if (/s$/.test(value)) return value
+		return value > 30 ? `${value}ms` : `${value}s`
+	}
+	if (/ms$/.test(value)) return valueNum
+	if (/s$/.test(value)) return valueNum > 30 ? valueNum : valueNum * 1000
+	return valueNum
+}
+
+/**
+ * @description 日期的月或日补零操作
+ * @param {String} value 需要补零的值
+ */
+function padZero(value) {
+	return `00${value}`.slice(-2)
+}
+
+/**
+ * @description 在uv-form的子组件内容发生变化,或者失去焦点时,尝试通知uv-form执行校验方法
+ * @param {*} instance
+ * @param {*} event
+ */
+function formValidate(instance, event) {
+	const formItem = $parent.call(instance, 'uv-form-item')
+	const form = $parent.call(instance, 'uv-form')
+	// 如果发生变化的input或者textarea等,其父组件中有uv-form-item或者uv-form等,就执行form的validate方法
+	// 同时将form-item的pros传递给form,让其进行精确对象验证
+	if (formItem && form) {
+		form.validateField(formItem.prop, () => {}, event)
+	}
+}
+
+/**
+ * @description 获取某个对象下的属性,用于通过类似'a.b.c'的形式去获取一个对象的的属性的形式
+ * @param {object} obj 对象
+ * @param {string} key 需要获取的属性字段
+ * @returns {*}
+ */
+function getProperty(obj, key) {
+	if (!obj) {
+		return
+	}
+	if (typeof key !== 'string' || key === '') {
+		return ''
+	}
+	if (key.indexOf('.') !== -1) {
+		const keys = key.split('.')
+		let firstObj = obj[keys[0]] || {}
+
+		for (let i = 1; i < keys.length; i++) {
+			if (firstObj) {
+				firstObj = firstObj[keys[i]]
+			}
+		}
+		return firstObj
+	}
+	return obj[key]
+}
+
+/**
+ * @description 设置对象的属性值,如果'a.b.c'的形式进行设置
+ * @param {object} obj 对象
+ * @param {string} key 需要设置的属性
+ * @param {string} value 设置的值
+ */
+function setProperty(obj, key, value) {
+	if (!obj) {
+		return
+	}
+	// 递归赋值
+	const inFn = function(_obj, keys, v) {
+		// 最后一个属性key
+		if (keys.length === 1) {
+			_obj[keys[0]] = v
+			return
+		}
+		// 0~length-1个key
+		while (keys.length > 1) {
+			const k = keys[0]
+			if (!_obj[k] || (typeof _obj[k] !== 'object')) {
+				_obj[k] = {}
+			}
+			const key = keys.shift()
+			// 自调用判断是否存在属性,不存在则自动创建对象
+			inFn(_obj[k], keys, v)
+		}
+	}
+
+	if (typeof key !== 'string' || key === '') {
+
+	} else if (key.indexOf('.') !== -1) { // 支持多层级赋值操作
+		const keys = key.split('.')
+		inFn(obj, keys, value)
+	} else {
+		obj[key] = value
+	}
+}
+
+/**
+ * @description 获取当前页面路径
+ */
+function page() {
+	const pages = getCurrentPages();
+	const route = pages[pages.length - 1]?.route;
+	// 某些特殊情况下(比如页面进行redirectTo时的一些时机),pages可能为空数组
+	return `/${route ? route : ''}`
+}
+
+/**
+ * @description 获取当前路由栈实例数组
+ */
+function pages() {
+	const pages = getCurrentPages()
+	return pages
+}
+
+/**
+ * 获取页面历史栈指定层实例
+ * @param back {number} [0] - 0或者负数,表示获取历史栈的哪一层,0表示获取当前页面实例,-1 表示获取上一个页面实例。默认0。
+ */
+function getHistoryPage(back = 0) {
+	const pages = getCurrentPages()
+	const len = pages.length
+	return pages[len - 1 + back]
+}
+
+
+
+/**
+ * @description 修改uvui内置属性值
+ * @param {object} props 修改内置props属性
+ * @param {object} config 修改内置config属性
+ * @param {object} color 修改内置color属性
+ * @param {object} zIndex 修改内置zIndex属性
+ */
+function setConfig({
+	props = {},
+	config = {},
+	color = {},
+	zIndex = {}
+}) {
+	const {
+		deepMerge,
+	} = uni.$uv
+	uni.$uv.config = deepMerge(uni.$uv.config, config)
+	uni.$uv.props = deepMerge(uni.$uv.props, props)
+	uni.$uv.color = deepMerge(uni.$uv.color, color)
+	uni.$uv.zIndex = deepMerge(uni.$uv.zIndex, zIndex)
+}
+
+export {
+	range,
+	getPx,
+	sleep,
+	os,
+	sys,
+	random,
+	guid,
+	$parent,
+	addStyle,
+	addUnit,
+	deepClone,
+	deepMerge,
+	error,
+	randomArray,
+	timeFormat,
+	timeFrom,
+	trim,
+	queryParams,
+	toast,
+	type2icon,
+	priceFormat,
+	getDuration,
+	padZero,
+	formValidate,
+	getProperty,
+	setProperty,
+	page,
+	pages,
+	getHistoryPage,
+	setConfig
+}

+ 75 - 0
uni_modules/uv-ui-tools/libs/function/platform.js

@@ -0,0 +1,75 @@
+/**
+ * 注意:
+ * 此部分内容,在vue-cli模式下,需要在vue.config.js加入如下内容才有效:
+ * module.exports = {
+ *     transpileDependencies: ['uview-v2']
+ * }
+ */
+
+let platform = 'none'
+
+// #ifdef VUE3
+platform = 'vue3'
+// #endif
+
+// #ifdef VUE2
+platform = 'vue2'
+// #endif
+
+// #ifdef APP-PLUS
+platform = 'plus'
+// #endif
+
+// #ifdef APP-NVUE
+platform = 'nvue'
+// #endif
+
+// #ifdef H5
+platform = 'h5'
+// #endif
+
+// #ifdef MP-WEIXIN
+platform = 'weixin'
+// #endif
+
+// #ifdef MP-ALIPAY
+platform = 'alipay'
+// #endif
+
+// #ifdef MP-BAIDU
+platform = 'baidu'
+// #endif
+
+// #ifdef MP-TOUTIAO
+platform = 'toutiao'
+// #endif
+
+// #ifdef MP-QQ
+platform = 'qq'
+// #endif
+
+// #ifdef MP-KUAISHOU
+platform = 'kuaishou'
+// #endif
+
+// #ifdef MP-360
+platform = '360'
+// #endif
+
+// #ifdef MP
+platform = 'mp'
+// #endif
+
+// #ifdef QUICKAPP-WEBVIEW
+platform = 'quickapp-webview'
+// #endif
+
+// #ifdef QUICKAPP-WEBVIEW-HUAWEI
+platform = 'quickapp-webview-huawei'
+// #endif
+
+// #ifdef QUICKAPP-WEBVIEW-UNION
+platform = 'quckapp-webview-union'
+// #endif
+
+export default platform

+ 287 - 0
uni_modules/uv-ui-tools/libs/function/test.js

@@ -0,0 +1,287 @@
+/**
+ * 验证电子邮箱格式
+ */
+function email(value) {
+    return /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/.test(value)
+}
+
+/**
+ * 验证手机格式
+ */
+function mobile(value) {
+    return /^1([3589]\d|4[5-9]|6[1-2,4-7]|7[0-8])\d{8}$/.test(value)
+}
+
+/**
+ * 验证URL格式
+ */
+function url(value) {
+    return /^((https|http|ftp|rtsp|mms):\/\/)(([0-9a-zA-Z_!~*'().&=+$%-]+: )?[0-9a-zA-Z_!~*'().&=+$%-]+@)?(([0-9]{1,3}.){3}[0-9]{1,3}|([0-9a-zA-Z_!~*'()-]+.)*([0-9a-zA-Z][0-9a-zA-Z-]{0,61})?[0-9a-zA-Z].[a-zA-Z]{2,6})(:[0-9]{1,4})?((\/?)|(\/[0-9a-zA-Z_!~*'().;?:@&=+$,%#-]+)+\/?)$/
+        .test(value)
+}
+
+/**
+ * 验证日期格式
+ */
+function date(value) {
+    if (!value) return false
+    // 判断是否数值或者字符串数值(意味着为时间戳),转为数值,否则new Date无法识别字符串时间戳
+    if (number(value)) value = +value
+    return !/Invalid|NaN/.test(new Date(value).toString())
+}
+
+/**
+ * 验证ISO类型的日期格式
+ */
+function dateISO(value) {
+    return /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(value)
+}
+
+/**
+ * 验证十进制数字
+ */
+function number(value) {
+    return /^[\+-]?(\d+\.?\d*|\.\d+|\d\.\d+e\+\d+)$/.test(value)
+}
+
+/**
+ * 验证字符串
+ */
+function string(value) {
+    return typeof value === 'string'
+}
+
+/**
+ * 验证整数
+ */
+function digits(value) {
+    return /^\d+$/.test(value)
+}
+
+/**
+ * 验证身份证号码
+ */
+function idCard(value) {
+    return /^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
+        value
+    )
+}
+
+/**
+ * 是否车牌号
+ */
+function carNo(value) {
+    // 新能源车牌
+    const xreg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF]$)|([DF][A-HJ-NP-Z0-9][0-9]{4}$))/
+    // 旧车牌
+    const creg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1}$/
+    if (value.length === 7) {
+        return creg.test(value)
+    } if (value.length === 8) {
+        return xreg.test(value)
+    }
+    return false
+}
+
+/**
+ * 金额,只允许2位小数
+ */
+function amount(value) {
+    // 金额,只允许保留两位小数
+    return /^[1-9]\d*(,\d{3})*(\.\d{1,2})?$|^0\.\d{1,2}$/.test(value)
+}
+
+/**
+ * 中文
+ */
+function chinese(value) {
+    const reg = /^[\u4e00-\u9fa5]+$/gi
+    return reg.test(value)
+}
+
+/**
+ * 只能输入字母
+ */
+function letter(value) {
+    return /^[a-zA-Z]*$/.test(value)
+}
+
+/**
+ * 只能是字母或者数字
+ */
+function enOrNum(value) {
+    // 英文或者数字
+    const reg = /^[0-9a-zA-Z]*$/g
+    return reg.test(value)
+}
+
+/**
+ * 验证是否包含某个值
+ */
+function contains(value, param) {
+    return value.indexOf(param) >= 0
+}
+
+/**
+ * 验证一个值范围[min, max]
+ */
+function range(value, param) {
+    return value >= param[0] && value <= param[1]
+}
+
+/**
+ * 验证一个长度范围[min, max]
+ */
+function rangeLength(value, param) {
+    return value.length >= param[0] && value.length <= param[1]
+}
+
+/**
+ * 是否固定电话
+ */
+function landline(value) {
+    const reg = /^\d{3,4}-\d{7,8}(-\d{3,4})?$/
+    return reg.test(value)
+}
+
+/**
+ * 判断是否为空
+ */
+function empty(value) {
+    switch (typeof value) {
+    case 'undefined':
+        return true
+    case 'string':
+        if (value.replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g, '').length == 0) return true
+        break
+    case 'boolean':
+        if (!value) return true
+        break
+    case 'number':
+        if (value === 0 || isNaN(value)) return true
+        break
+    case 'object':
+        if (value === null || value.length === 0) return true
+        for (const i in value) {
+            return false
+        }
+        return true
+    }
+    return false
+}
+
+/**
+ * 是否json字符串
+ */
+function jsonString(value) {
+    if (typeof value === 'string') {
+        try {
+            const obj = JSON.parse(value)
+            if (typeof obj === 'object' && obj) {
+                return true
+            }
+            return false
+        } catch (e) {
+            return false
+        }
+    }
+    return false
+}
+
+/**
+ * 是否数组
+ */
+function array(value) {
+    if (typeof Array.isArray === 'function') {
+        return Array.isArray(value)
+    }
+    return Object.prototype.toString.call(value) === '[object Array]'
+}
+
+/**
+ * 是否对象
+ */
+function object(value) {
+    return Object.prototype.toString.call(value) === '[object Object]'
+}
+
+/**
+ * 是否短信验证码
+ */
+function code(value, len = 6) {
+    return new RegExp(`^\\d{${len}}$`).test(value)
+}
+
+/**
+ * 是否函数方法
+ * @param {Object} value
+ */
+function func(value) {
+    return typeof value === 'function'
+}
+
+/**
+ * 是否promise对象
+ * @param {Object} value
+ */
+function promise(value) {
+    return object(value) && func(value.then) && func(value.catch)
+}
+
+/** 是否图片格式
+ * @param {Object} value
+ */
+function image(value) {
+    const newValue = value.split('?')[0]
+    const IMAGE_REGEXP = /\.(jpeg|jpg|gif|png|svg|webp|jfif|bmp|dpg)/i
+    return IMAGE_REGEXP.test(newValue)
+}
+
+/**
+ * 是否视频格式
+ * @param {Object} value
+ */
+function video(value) {
+    const VIDEO_REGEXP = /\.(mp4|mpg|mpeg|dat|asf|avi|rm|rmvb|mov|wmv|flv|mkv|m3u8)/i
+    return VIDEO_REGEXP.test(value)
+}
+
+/**
+ * 是否为正则对象
+ * @param {Object}
+ * @return {Boolean}
+ */
+function regExp(o) {
+    return o && Object.prototype.toString.call(o) === '[object RegExp]'
+}
+
+export {
+    email,
+    mobile,
+    url,
+    date,
+    dateISO,
+    number,
+    digits,
+    idCard,
+    carNo,
+    amount,
+    chinese,
+    letter,
+    enOrNum,
+    contains,
+    range,
+    rangeLength,
+    empty,
+    jsonString,
+    landline,
+    object,
+    array,
+    code,
+    func,
+    promise,
+    video,
+    image,
+    regExp,
+    string
+}

+ 30 - 0
uni_modules/uv-ui-tools/libs/function/throttle.js

@@ -0,0 +1,30 @@
+let timer; let
+    flag
+/**
+ * 节流原理:在一定时间内,只能触发一次
+ *
+ * @param {Function} func 要执行的回调函数
+ * @param {Number} wait 延时的时间
+ * @param {Boolean} immediate 是否立即执行
+ * @return null
+ */
+function throttle(func, wait = 500, immediate = true) {
+    if (immediate) {
+        if (!flag) {
+            flag = true
+            // 如果是立即执行,则在wait毫秒内开始时执行
+            typeof func === 'function' && func()
+            timer = setTimeout(() => {
+                flag = false
+            }, wait)
+        }
+    } else if (!flag) {
+        flag = true
+        // 如果是非立即执行,则在wait毫秒内的结束处执行
+        timer = setTimeout(() => {
+            flag = false
+            typeof func === 'function' && func()
+        }, wait)
+    }
+}
+export default throttle

+ 132 - 0
uni_modules/uv-ui-tools/libs/luch-request/adapters/index.js

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

+ 51 - 0
uni_modules/uv-ui-tools/libs/luch-request/core/InterceptorManager.js

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

+ 201 - 0
uni_modules/uv-ui-tools/libs/luch-request/core/Request.js

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

+ 20 - 0
uni_modules/uv-ui-tools/libs/luch-request/core/buildFullPath.js

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

+ 33 - 0
uni_modules/uv-ui-tools/libs/luch-request/core/defaults.js

@@ -0,0 +1,33 @@
+/**
+ * 默认的全局配置
+ */
+
+
+export default {
+  baseURL: '',
+  header: {},
+  method: 'GET',
+  dataType: 'json',
+  paramsSerializer: null,
+  // #ifndef MP-ALIPAY
+  responseType: 'text',
+  // #endif
+  custom: {},
+  // #ifdef H5 || APP-PLUS || MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO || MP-KUAISHOU
+  timeout: 60000,
+  // #endif
+  // #ifdef APP-PLUS
+  sslVerify: true,
+  // #endif
+  // #ifdef H5
+  withCredentials: false,
+  // #endif
+  // #ifdef APP-PLUS
+  firstIpv4: false,
+  // #endif
+  validateStatus: function validateStatus(status) {
+    return status >= 200 && status < 300
+  },
+  // 是否尝试将响应数据json化
+  forcedJSONParsing: true
+}

+ 6 - 0
uni_modules/uv-ui-tools/libs/luch-request/core/dispatchRequest.js

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

+ 126 - 0
uni_modules/uv-ui-tools/libs/luch-request/core/mergeConfig.js

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

+ 16 - 0
uni_modules/uv-ui-tools/libs/luch-request/core/settle.js

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

+ 64 - 0
uni_modules/uv-ui-tools/libs/luch-request/helpers/buildURL.js

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

+ 14 - 0
uni_modules/uv-ui-tools/libs/luch-request/helpers/combineURLs.js

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

+ 14 - 0
uni_modules/uv-ui-tools/libs/luch-request/helpers/isAbsoluteURL.js

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

+ 197 - 0
uni_modules/uv-ui-tools/libs/luch-request/index.d.ts

@@ -0,0 +1,197 @@
+export type HttpTask = UniApp.RequestTask | UniApp.UploadTask | UniApp.DownloadTask;
+
+export type HttpRequestTask = UniApp.RequestTask;
+
+export type HttpUploadTask = UniApp.UploadTask;
+
+export type HttpDownloadTask = UniApp.DownloadTask;
+
+export type HttpMethod =
+    "GET"
+    | "POST"
+    | "PUT"
+    | "DELETE"
+    | "CONNECT"
+    | "HEAD"
+    | "OPTIONS"
+    | "TRACE"
+    | "UPLOAD"
+    | "DOWNLOAD";
+
+export type HttpRequestHeader = Record<string, string>;
+
+export type HttpParams = Record<string, any>;
+
+export type HttpData = Record<string, any>;
+
+export type HttpResponseType = 'arraybuffer' | 'text';
+
+export type HttpCustom = Record<string, any>;
+
+export type HttpFileType = 'image' | 'video' | 'audio';
+
+export type HttpFormData = Record<string, any>;
+
+export type HttpResponseHeader = Record<string, string> & {
+    "set-cookie"?: string[]
+};
+
+export interface HttpRequestConfig<T = HttpTask> {
+    /** @desc 请求服务器接口地址 */
+    url?: string;
+    /** @desc 请求方式,默认为 GET */
+    method?: HttpMethod;
+    /** @desc 请求基地址 */
+    baseURL?: string;
+    /** @desc 请求头信息,不能设置 Referer,App、H5 端会自动带上 cookie,且 H5 端不可手动修改 */
+    header?: HttpRequestHeader;
+    /** @desc 请求查询参数,自动拼接为查询字符串 */
+    params?: HttpParams;
+    /** @desc 请求体参数 */
+    data?: HttpData;
+    /** @desc 超时时间,单位 ms,默认为 60000,仅 H5 (HBuilderX 2.9.9+)、APP (HBuilderX 2.9.9+)、微信小程序 (2.10.0)、支付宝小程序支持 */
+    timeout?: number;
+    /** @desc 跨域请求时是否携带凭证 (cookies),默认为 false,仅 H5 (HBuilderX 2.6.15+) 支持 */
+    withCredentials?: boolean;
+    /** @desc 设置响应的数据类型,支付宝小程序不支持 */
+    responseType?: HttpResponseType;
+    /** @desc 全局自定义验证器 */
+    validateStatus?: ((statusCode: number) => boolean) | null;
+
+
+    /** params 参数自定义处理 */
+    paramsSerializer?: (params: AnyObject) => string | void;
+
+    /** @desc 默认为 json,如果设为 json,会尝试对返回的数据做一次 JSON.parse */
+    dataType?: string;
+    /** @desc DNS 解析时是否优先使用 ipv4,默认为 false,仅 App-Android (HBuilderX 2.8.0+) 支持 */
+    firstIpv4?: boolean;
+    /** @desc 是否验证 SSL 证书,默认为 true,仅 App-Android (HBuilderX 2.3.3+) 支持 */
+    sslVerify?: boolean;
+
+    /** @desc 开启 http2;微信小程序 */
+    enableHttp2?: boolean;
+
+    /** @desc 开启 quic;微信小程序 */
+    enableQuic?: boolean;
+    /** @desc 开启 cache;微信小程序、字节跳动小程序 2.31.0+ */
+    enableCache?: boolean;
+    /** @desc 开启 httpDNS;微信小程序 */
+    enableHttpDNS?: boolean;
+    /** @desc httpDNS 服务商;微信小程序 */
+    httpDNSServiceId?: string;
+    /** @desc 开启 transfer-encoding chunked;微信小程序 */
+    enableChunked?: boolean;
+    /** @desc wifi下使用移动网络发送请求;微信小程序 */
+    forceCellularNetwork?: boolean;
+    /** @desc 开启后可在headers中编辑cookie;支付宝小程序 10.2.33+ */
+    enableCookie?: boolean;
+    /** @desc 是否开启云加速;百度小程序 3.310.11+ */
+    cloudCache?: boolean | object;
+    /** @desc 控制当前请求是否延时至首屏内容渲染后发送;百度小程序 3.310.11+ */
+    defer?: boolean;
+
+    /** @desc 自定义参数 */
+    custom?: HttpCustom;
+
+    /** @desc 返回当前请求的 task 和 options,不要在这里修改 options */
+    getTask?: (task: T, options: HttpRequestConfig<T>) => void;
+
+    /** @desc 需要上传的文件列表,使用 files 时,filePath 和 name 不生效,仅支持 App、H5 (2.6.15+) */
+    files?: { name?: string; file?: File; uri: string; }[];
+    /** @desc 文件类型,仅支付宝小程序支持且为必填项 */
+    fileType?: HttpFileType;
+    /** @desc 要上传的文件对象,仅 H5 (2.6.15+) 支持 */
+    file?: File;
+    /** @desc 要上传文件资源的路径,使用 files 时,filePath 和 name 不生效 */
+    filePath?: string;
+    /** @desc 文件对应的 key,开发者在服务器端通过这个 key 可以获取到文件二进制内容,使用 files 时,filePath 和 name 不生效 */
+    name?: string;
+    /** @desc 请求中其他额外的 form data */
+    formData?: HttpFormData;
+}
+
+export interface HttpResponse<T = any, D = HttpTask> {
+    data: T;
+    statusCode: number;
+    header: HttpResponseHeader;
+    config: HttpRequestConfig<D>;
+    cookies: string[];
+    errMsg: string;
+    rawData: any;
+}
+
+export interface HttpUploadResponse<T = any, D = HttpTask> {
+    data: T;
+    statusCode: number;
+    config: HttpRequestConfig<D>;
+    errMsg: string;
+    rawData: any;
+}
+
+export interface HttpDownloadResponse extends HttpResponse {
+    tempFilePath: string;
+    apFilePath?: string;
+    filePath?: string;
+    fileContent?: string;
+}
+
+export interface HttpError<T = any, D = HttpTask> {
+    data?: T;
+    statusCode?: number;
+    header?: HttpResponseHeader;
+    config: HttpRequestConfig<D>;
+    cookies?: string[];
+    errMsg: string;
+}
+
+export interface HttpPromise<T = any> extends Promise<HttpResponse<T>> {
+}
+
+export interface HttpInterceptorManager<V, E = V> {
+    use(onFulfilled?: (value: V) => V | Promise<V>, onRejected?: (error: E) => T | Promise<E>): void;
+
+    eject(id: number): void;
+}
+
+export abstract class HttpRequestAbstract {
+    constructor(config?: HttpRequestConfig);
+
+    interceptors: {
+        request: HttpInterceptorManager<HttpRequestConfig>;
+        response: HttpInterceptorManager<HttpResponse, HttpError>;
+    }
+
+    request<T = any, R = HttpResponse<T>, D = HttpRequestTask>(config: HttpRequestConfig<D>): Promise<R>;
+
+    get<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, config?: HttpRequestConfig<D>): Promise<R>;
+
+    delete<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
+
+    head<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
+
+    options<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
+
+    post<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
+
+    put<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
+
+    config: HttpRequestConfig;
+
+    setConfig<D = HttpTask>(onSend: (config: HttpRequestConfig<D>) => HttpRequestConfig<D>): void;
+
+    connect<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
+
+    trace<T = any, R = HttpResponse<T>, D = HttpRequestTask>(url: string, data?: HttpData, config?: HttpRequestConfig<D>): Promise<R>;
+
+    upload<T = any, R = HttpUploadResponse<T>, D = HttpUploadTask>(url: string, config?: HttpRequestConfig<D>): Promise<R>;
+
+    download<T = any, R = HttpDownloadResponse<T>, D = HttpDownloadTask>(url: string, config?: HttpRequestConfig<D>): Promise<R>;
+
+    middleware<T = any, R = HttpResponse<T>, D = HttpTask>(config: HttpRequestConfig<D>): Promise<R>;
+}
+
+declare class HttpRequest extends HttpRequestAbstract {
+}
+
+export default HttpRequest;

+ 2 - 0
uni_modules/uv-ui-tools/libs/luch-request/index.js

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

+ 135 - 0
uni_modules/uv-ui-tools/libs/luch-request/utils.js

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

+ 264 - 0
uni_modules/uv-ui-tools/libs/luch-request/utils/clone.js

@@ -0,0 +1,264 @@
+/* eslint-disable */
+var clone = (function() {
+  'use strict';
+
+  function _instanceof(obj, type) {
+    return type != null && obj instanceof type;
+  }
+
+  var nativeMap;
+  try {
+    nativeMap = Map;
+  } catch(_) {
+    // maybe a reference error because no `Map`. Give it a dummy value that no
+    // value will ever be an instanceof.
+    nativeMap = function() {};
+  }
+
+  var nativeSet;
+  try {
+    nativeSet = Set;
+  } catch(_) {
+    nativeSet = function() {};
+  }
+
+  var nativePromise;
+  try {
+    nativePromise = Promise;
+  } catch(_) {
+    nativePromise = function() {};
+  }
+
+  /**
+   * Clones (copies) an Object using deep copying.
+   *
+   * This function supports circular references by default, but if you are certain
+   * there are no circular references in your object, you can save some CPU time
+   * by calling clone(obj, false).
+   *
+   * Caution: if `circular` is false and `parent` contains circular references,
+   * your program may enter an infinite loop and crash.
+   *
+   * @param `parent` - the object to be cloned
+   * @param `circular` - set to true if the object to be cloned may contain
+   *    circular references. (optional - true by default)
+   * @param `depth` - set to a number if the object is only to be cloned to
+   *    a particular depth. (optional - defaults to Infinity)
+   * @param `prototype` - sets the prototype to be used when cloning an object.
+   *    (optional - defaults to parent prototype).
+   * @param `includeNonEnumerable` - set to true if the non-enumerable properties
+   *    should be cloned as well. Non-enumerable properties on the prototype
+   *    chain will be ignored. (optional - false by default)
+   */
+  function clone(parent, circular, depth, prototype, includeNonEnumerable) {
+    if (typeof circular === 'object') {
+      depth = circular.depth;
+      prototype = circular.prototype;
+      includeNonEnumerable = circular.includeNonEnumerable;
+      circular = circular.circular;
+    }
+    // maintain two arrays for circular references, where corresponding parents
+    // and children have the same index
+    var allParents = [];
+    var allChildren = [];
+
+    var useBuffer = typeof Buffer != 'undefined';
+
+    if (typeof circular == 'undefined')
+      circular = true;
+
+    if (typeof depth == 'undefined')
+      depth = Infinity;
+
+    // recurse this function so we don't reset allParents and allChildren
+    function _clone(parent, depth) {
+      // cloning null always returns null
+      if (parent === null)
+        return null;
+
+      if (depth === 0)
+        return parent;
+
+      var child;
+      var proto;
+      if (typeof parent != 'object') {
+        return parent;
+      }
+
+      if (_instanceof(parent, nativeMap)) {
+        child = new nativeMap();
+      } else if (_instanceof(parent, nativeSet)) {
+        child = new nativeSet();
+      } else if (_instanceof(parent, nativePromise)) {
+        child = new nativePromise(function (resolve, reject) {
+          parent.then(function(value) {
+            resolve(_clone(value, depth - 1));
+          }, function(err) {
+            reject(_clone(err, depth - 1));
+          });
+        });
+      } else if (clone.__isArray(parent)) {
+        child = [];
+      } else if (clone.__isRegExp(parent)) {
+        child = new RegExp(parent.source, __getRegExpFlags(parent));
+        if (parent.lastIndex) child.lastIndex = parent.lastIndex;
+      } else if (clone.__isDate(parent)) {
+        child = new Date(parent.getTime());
+      } else if (useBuffer && Buffer.isBuffer(parent)) {
+        if (Buffer.from) {
+          // Node.js >= 5.10.0
+          child = Buffer.from(parent);
+        } else {
+          // Older Node.js versions
+          child = new Buffer(parent.length);
+          parent.copy(child);
+        }
+        return child;
+      } else if (_instanceof(parent, Error)) {
+        child = Object.create(parent);
+      } else {
+        if (typeof prototype == 'undefined') {
+          proto = Object.getPrototypeOf(parent);
+          child = Object.create(proto);
+        }
+        else {
+          child = Object.create(prototype);
+          proto = prototype;
+        }
+      }
+
+      if (circular) {
+        var index = allParents.indexOf(parent);
+
+        if (index != -1) {
+          return allChildren[index];
+        }
+        allParents.push(parent);
+        allChildren.push(child);
+      }
+
+      if (_instanceof(parent, nativeMap)) {
+        parent.forEach(function(value, key) {
+          var keyChild = _clone(key, depth - 1);
+          var valueChild = _clone(value, depth - 1);
+          child.set(keyChild, valueChild);
+        });
+      }
+      if (_instanceof(parent, nativeSet)) {
+        parent.forEach(function(value) {
+          var entryChild = _clone(value, depth - 1);
+          child.add(entryChild);
+        });
+      }
+
+      for (var i in parent) {
+        var attrs = Object.getOwnPropertyDescriptor(parent, i);
+        if (attrs) {
+          child[i] = _clone(parent[i], depth - 1);
+        }
+
+        try {
+          var objProperty = Object.getOwnPropertyDescriptor(parent, i);
+          if (objProperty.set === 'undefined') {
+            // no setter defined. Skip cloning this property
+            continue;
+          }
+          child[i] = _clone(parent[i], depth - 1);
+        } catch(e){
+          if (e instanceof TypeError) {
+            // when in strict mode, TypeError will be thrown if child[i] property only has a getter
+            // we can't do anything about this, other than inform the user that this property cannot be set.
+            continue
+          } else if (e instanceof ReferenceError) {
+            //this may happen in non strict mode
+            continue
+          }
+        }
+
+      }
+
+      if (Object.getOwnPropertySymbols) {
+        var symbols = Object.getOwnPropertySymbols(parent);
+        for (var i = 0; i < symbols.length; i++) {
+          // Don't need to worry about cloning a symbol because it is a primitive,
+          // like a number or string.
+          var symbol = symbols[i];
+          var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
+          if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
+            continue;
+          }
+          child[symbol] = _clone(parent[symbol], depth - 1);
+          Object.defineProperty(child, symbol, descriptor);
+        }
+      }
+
+      if (includeNonEnumerable) {
+        var allPropertyNames = Object.getOwnPropertyNames(parent);
+        for (var i = 0; i < allPropertyNames.length; i++) {
+          var propertyName = allPropertyNames[i];
+          var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
+          if (descriptor && descriptor.enumerable) {
+            continue;
+          }
+          child[propertyName] = _clone(parent[propertyName], depth - 1);
+          Object.defineProperty(child, propertyName, descriptor);
+        }
+      }
+
+      return child;
+    }
+
+    return _clone(parent, depth);
+  }
+
+  /**
+   * Simple flat clone using prototype, accepts only objects, usefull for property
+   * override on FLAT configuration object (no nested props).
+   *
+   * USE WITH CAUTION! This may not behave as you wish if you do not know how this
+   * works.
+   */
+  clone.clonePrototype = function clonePrototype(parent) {
+    if (parent === null)
+      return null;
+
+    var c = function () {};
+    c.prototype = parent;
+    return new c();
+  };
+
+// private utility functions
+
+  function __objToStr(o) {
+    return Object.prototype.toString.call(o);
+  }
+  clone.__objToStr = __objToStr;
+
+  function __isDate(o) {
+    return typeof o === 'object' && __objToStr(o) === '[object Date]';
+  }
+  clone.__isDate = __isDate;
+
+  function __isArray(o) {
+    return typeof o === 'object' && __objToStr(o) === '[object Array]';
+  }
+  clone.__isArray = __isArray;
+
+  function __isRegExp(o) {
+    return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
+  }
+  clone.__isRegExp = __isRegExp;
+
+  function __getRegExpFlags(re) {
+    var flags = '';
+    if (re.global) flags += 'g';
+    if (re.ignoreCase) flags += 'i';
+    if (re.multiline) flags += 'm';
+    return flags;
+  }
+  clone.__getRegExpFlags = __getRegExpFlags;
+
+  return clone;
+})();
+
+export default clone

+ 13 - 0
uni_modules/uv-ui-tools/libs/mixin/button.js

@@ -0,0 +1,13 @@
+export default {
+    props: {
+        lang: String,
+        sessionFrom: String,
+        sendMessageTitle: String,
+        sendMessagePath: String,
+        sendMessageImg: String,
+        showMessageCard: Boolean,
+        appParameter: String,
+        formType: String,
+        openType: String
+    }
+}

+ 172 - 0
uni_modules/uv-ui-tools/libs/mixin/mixin.js

@@ -0,0 +1,172 @@
+import * as index from '../function/index.js';
+import * as test from '../function/test.js';
+import route from '../util/route.js';
+import debounce from '../function/debounce.js';
+import throttle from '../function/throttle.js';
+export default {
+	// 定义每个组件都可能需要用到的外部样式以及类名
+	props: {
+		// 每个组件都有的父组件传递的样式,可以为字符串或者对象形式
+		customStyle: {
+			type: [Object, String],
+			default: () => ({})
+		},
+		customClass: {
+			type: String,
+			default: ''
+		},
+		// 跳转的页面路径
+		url: {
+			type: String,
+			default: ''
+		},
+		// 页面跳转的类型
+		linkType: {
+			type: String,
+			default: 'navigateTo'
+		}
+	},
+	data() {
+		return {}
+	},
+	onLoad() {
+		// getRect挂载到$uv上,因为这方法需要使用in(this),所以无法把它独立成一个单独的文件导出
+		this.$uv.getRect = this.$uvGetRect
+	},
+	created() {
+		// 组件当中,只有created声明周期,为了能在组件使用,故也在created中将方法挂载到$uv
+		this.$uv.getRect = this.$uvGetRect
+	},
+	computed: {
+		$uv() {
+			return {
+				...index,
+				test,
+				route,
+				debounce,
+				throttle,
+				unit: uni?.$uv?.config?.unit
+			}
+		},
+		/**
+		 * 生成bem规则类名
+		 * 由于微信小程序,H5,nvue之间绑定class的差异,无法通过:class="[bem()]"的形式进行同用
+		 * 故采用如下折中做法,最后返回的是数组(一般平台)或字符串(支付宝和字节跳动平台),类似['a', 'b', 'c']或'a b c'的形式
+		 * @param {String} name 组件名称
+		 * @param {Array} fixed 一直会存在的类名
+		 * @param {Array} change 会根据变量值为true或者false而出现或者隐藏的类名
+		 * @returns {Array|string}
+		 */
+		bem() {
+			return function(name, fixed, change) {
+				// 类名前缀
+				const prefix = `uv-${name}--`
+				const classes = {}
+				if (fixed) {
+					fixed.map((item) => {
+						// 这里的类名,会一直存在
+						classes[prefix + this[item]] = true
+					})
+				}
+				if (change) {
+					change.map((item) => {
+						// 这里的类名,会根据this[item]的值为true或者false,而进行添加或者移除某一个类
+						this[item] ? (classes[prefix + item] = this[item]) : (delete classes[prefix + item])
+					})
+				}
+				return Object.keys(classes)
+					// 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效
+					// #ifdef MP-ALIPAY || MP-TOUTIAO || MP-LARK || MP-BAIDU
+					.join(' ')
+				// #endif
+			}
+		}
+	},
+	methods: {
+		// 跳转某一个页面
+		openPage(urlKey = 'url') {
+			const url = this[urlKey]
+			if (url) {
+				// 执行类似uni.navigateTo的方法
+				uni[this.linkType]({
+					url
+				})
+			}
+		},
+		// 查询节点信息
+		// 目前此方法在支付宝小程序中无法获取组件跟接点的尺寸,为支付宝的bug(2020-07-21)
+		// 解决办法为在组件根部再套一个没有任何作用的view元素
+		$uvGetRect(selector, all) {
+			return new Promise((resolve) => {
+				uni.createSelectorQuery()
+					.in(this)[all ? 'selectAll' : 'select'](selector)
+					.boundingClientRect((rect) => {
+						if (all && Array.isArray(rect) && rect.length) {
+							resolve(rect)
+						}
+						if (!all && rect) {
+							resolve(rect)
+						}
+					})
+					.exec()
+			})
+		},
+		getParentData(parentName = '') {
+			// 避免在created中去定义parent变量
+			if (!this.parent) this.parent = {}
+			// 这里的本质原理是,通过获取父组件实例(也即类似uv-radio的父组件uv-radio-group的this)
+			// 将父组件this中对应的参数,赋值给本组件(uv-radio的this)的parentData对象中对应的属性
+			// 之所以需要这么做,是因为所有端中,头条小程序不支持通过this.parent.xxx去监听父组件参数的变化
+			// 此处并不会自动更新子组件的数据,而是依赖父组件uv-radio-group去监听data的变化,手动调用更新子组件的方法去重新获取
+			this.parent = this.$uv.$parent.call(this, parentName)
+			if (this.parent.children) {
+				// 如果父组件的children不存在本组件的实例,才将本实例添加到父组件的children中
+				this.parent.children.indexOf(this) === -1 && this.parent.children.push(this)
+			}
+			if (this.parent && this.parentData) {
+				// 历遍parentData中的属性,将parent中的同名属性赋值给parentData
+				Object.keys(this.parentData).map((key) => {
+					this.parentData[key] = this.parent[key]
+				})
+			}
+		},
+		// 阻止事件冒泡
+		preventEvent(e) {
+			e && typeof(e.stopPropagation) === 'function' && e.stopPropagation()
+		},
+		// 空操作
+		noop(e) {
+			this.preventEvent(e)
+		}
+	},
+	onReachBottom() {
+		uni.$emit('uvOnReachBottom')
+	},
+	beforeDestroy() {
+		// 判断当前页面是否存在parent和chldren,一般在checkbox和checkbox-group父子联动的场景会有此情况
+		// 组件销毁时,移除子组件在父组件children数组中的实例,释放资源,避免数据混乱
+		if (this.parent && test.array(this.parent.children)) {
+			// 组件销毁时,移除父组件中的children数组中对应的实例
+			const childrenList = this.parent.children
+			childrenList.map((child, index) => {
+				// 如果相等,则移除
+				if (child === this) {
+					childrenList.splice(index, 1)
+				}
+			})
+		}
+	},
+	// 兼容vue3
+	unmounted() {
+		if (this.parent && test.array(this.parent.children)) {
+			// 组件销毁时,移除父组件中的children数组中对应的实例
+			const childrenList = this.parent.children
+			childrenList.map((child, index) => {
+				// 如果相等,则移除
+				if (child === this) {
+					childrenList.splice(index, 1)
+				}
+			})
+		}
+	}
+}

+ 8 - 0
uni_modules/uv-ui-tools/libs/mixin/mpMixin.js

@@ -0,0 +1,8 @@
+export default {
+    // #ifdef MP-WEIXIN
+    // 将自定义节点设置成虚拟的(去掉自定义组件包裹层),更加接近Vue组件的表现,能更好的使用flex属性
+    options: {
+        virtualHost: true
+    }
+    // #endif
+}

+ 13 - 0
uni_modules/uv-ui-tools/libs/mixin/mpShare.js

@@ -0,0 +1,13 @@
+export default {
+	onLoad() {
+	    // 设置默认的转发参数
+	    uni.$uv.mpShare = {
+	        title: '', // 默认为小程序名称
+	        path: '', // 默认为当前页面路径
+	        imageUrl: '' // 默认为当前页面的截图
+	    }
+	},
+	onShareAppMessage() {
+	    return uni.$uv.mpShare
+	}
+}

+ 47 - 0
uni_modules/uv-ui-tools/libs/mixin/openType.js

@@ -0,0 +1,47 @@
+export default {
+    props: {
+        openType: String
+    },
+		emits: ['getphonenumber','getuserinfo','error','opensetting','launchapp','contact','chooseavatar','addgroupapp','chooseaddress','subscribe','login','im'],
+    methods: {
+        onGetPhoneNumber(event) {
+            this.$emit('getphonenumber', event.detail)
+        },
+        onGetUserInfo(event) {
+            this.$emit('getuserinfo', event.detail)
+        },
+        onError(event) {
+            this.$emit('error', event.detail)
+        },
+        onOpenSetting(event) {
+            this.$emit('opensetting', event.detail)
+        },
+        onLaunchApp(event) {
+            this.$emit('launchapp', event.detail)
+        },
+        onContact(event) {
+            this.$emit('contact', event.detail)
+        },
+        onChooseavatar(event) {
+            this.$emit('chooseavatar', event.detail)
+        },
+        onAgreeprivacyauthorization(event) {
+            this.$emit('agreeprivacyauthorization', event.detail)
+        },
+        onAddgroupapp(event) {
+            this.$emit('addgroupapp', event.detail)
+        },
+        onChooseaddress(event) {
+            this.$emit('chooseaddress', event.detail)
+        },
+        onSubscribe(event) {
+            this.$emit('subscribe', event.detail)
+        },
+        onLogin(event) {
+            this.$emit('login', event.detail)
+        },
+        onIm(event) {
+            this.$emit('im', event.detail)
+        }
+    }
+}

+ 59 - 0
uni_modules/uv-ui-tools/libs/mixin/touch.js

@@ -0,0 +1,59 @@
+const MIN_DISTANCE = 10
+
+function getDirection(x, y) {
+    if (x > y && x > MIN_DISTANCE) {
+        return 'horizontal'
+    }
+    if (y > x && y > MIN_DISTANCE) {
+        return 'vertical'
+    }
+    return ''
+}
+
+export default {
+    methods: {
+        getTouchPoint(e) {
+            if (!e) {
+                return {
+                    x: 0,
+                    y: 0
+                }
+            } if (e.touches && e.touches[0]) {
+                return {
+                    x: e.touches[0].pageX,
+                    y: e.touches[0].pageY
+                }
+            } if (e.changedTouches && e.changedTouches[0]) {
+                return {
+                    x: e.changedTouches[0].pageX,
+                    y: e.changedTouches[0].pageY
+                }
+            }
+            return {
+                x: e.clientX || 0,
+                y: e.clientY || 0
+            }
+        },
+        resetTouchStatus() {
+            this.direction = ''
+            this.deltaX = 0
+            this.deltaY = 0
+            this.offsetX = 0
+            this.offsetY = 0
+        },
+        touchStart(event) {
+            this.resetTouchStatus()
+            const touch = this.getTouchPoint(event)
+            this.startX = touch.x
+            this.startY = touch.y
+        },
+        touchMove(event) {
+            const touch = this.getTouchPoint(event)
+            this.deltaX = touch.x - this.startX
+            this.deltaY = touch.y - this.startY
+            this.offsetX = Math.abs(this.deltaX)
+            this.offsetY = Math.abs(this.deltaY)
+            this.direction =				this.direction || getDirection(this.offsetX, this.offsetY)
+        }
+    }
+}

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff