浏览代码

页面搭建

zouling 1 年之前
父节点
当前提交
cc43ef87d7
共有 100 个文件被更改,包括 7151 次插入521 次删除
  1. 28 6
      components/footer/footer.vue
  2. 2 2
      components/nodata/nodata.vue
  3. 102 0
      components/notice/list.vue
  4. 0 0
      components/notice/popup.vue
  5. 0 129
      components/order/list.vue
  6. 0 78
      components/order/stepbar.vue
  7. 61 0
      components/swiper/notice.vue
  8. 19 12
      components/toptab/navbar.vue
  9. 3 3
      config.js
  10. 21 9
      manifest.json
  11. 84 7
      pages.json
  12. 263 124
      pages/index/index.vue
  13. 7 4
      pages/mine/index.vue
  14. 41 0
      pages/notice/index.vue
  15. 41 0
      pages/report/index.vue
  16. 25 0
      pages/s.vue
  17. 0 37
      pages/work/index.vue
  18. 二进制
      static/images/bg.png
  19. 二进制
      static/images/home/hrbga.png
  20. 二进制
      static/images/home/hrbgb.png
  21. 二进制
      static/images/home/hrbgc.png
  22. 二进制
      static/images/home/hrbgd.png
  23. 二进制
      static/images/home/hrimga.png
  24. 二进制
      static/images/home/hrimgb.png
  25. 二进制
      static/images/home/hrimgc.png
  26. 二进制
      static/images/home/hrimgd.png
  27. 二进制
      static/images/home/htopa.png
  28. 二进制
      static/images/home/htopb.png
  29. 二进制
      static/images/home/htopc.png
  30. 二进制
      static/images/home/htopd.png
  31. 二进制
      static/images/home/notice.png
  32. 二进制
      static/images/home/tit.png
  33. 二进制
      static/images/home/topbg.png
  34. 二进制
      static/images/home/tztip.png
  35. 二进制
      static/images/home/up.png
  36. 二进制
      static/images/navbg.png
  37. 二进制
      static/images/noiconp.png
  38. 二进制
      static/images/noiconps.png
  39. 二进制
      static/images/tabbar/home.png
  40. 二进制
      static/images/tabbar/home_.png
  41. 二进制
      static/images/tabbar/mine.png
  42. 二进制
      static/images/tabbar/mine_.png
  43. 二进制
      static/images/tabbar/notice.png
  44. 二进制
      static/images/tabbar/notice_.png
  45. 二进制
      static/images/tabbar/report.png
  46. 二进制
      static/images/tabbar/report_.png
  47. 二进制
      static/images/tabbar/road.png
  48. 二进制
      static/images/tabbar/road_.png
  49. 二进制
      static/images/tabbar/trends.png
  50. 二进制
      static/images/tabbar/trends_.png
  51. 二进制
      static/images/tabbar/work.png
  52. 二进制
      static/images/tabbar/work_.png
  53. 二进制
      static/images/weather/weaion.png
  54. 二进制
      static/images/weather/weaiona.png
  55. 二进制
      static/images/weather/weaionb.png
  56. 二进制
      static/images/weather/weaionc.png
  57. 二进制
      static/images/weather/weaiond.png
  58. 二进制
      static/images/weather/weaione.png
  59. 二进制
      static/images/weather/weaionf.png
  60. 二进制
      static/images/weather/weaiong.png
  61. 二进制
      static/images/weather/weaionh.png
  62. 二进制
      static/images/weather/weaioni.png
  63. 二进制
      static/images/weather/weaionj.png
  64. 40 109
      static/style.css
  65. 1 1
      uni.scss
  66. 108 0
      uni_modules/lsj-upload/changelog.md
  67. 396 0
      uni_modules/lsj-upload/components/lsj-upload/LsjFile.js
  68. 316 0
      uni_modules/lsj-upload/components/lsj-upload/lsj-upload.vue
  69. 5 0
      uni_modules/lsj-upload/hybrid/html/js/vue.min.js
  70. 191 0
      uni_modules/lsj-upload/hybrid/html/uploadFile.html
  71. 79 0
      uni_modules/lsj-upload/package.json
  72. 352 0
      uni_modules/lsj-upload/readme.md
  73. 27 0
      uni_modules/uni-table/changelog.md
  74. 455 0
      uni_modules/uni-table/components/uni-table/uni-table.vue
  75. 29 0
      uni_modules/uni-table/components/uni-tbody/uni-tbody.vue
  76. 90 0
      uni_modules/uni-table/components/uni-td/uni-td.vue
  77. 511 0
      uni_modules/uni-table/components/uni-th/filter-dropdown.vue
  78. 285 0
      uni_modules/uni-table/components/uni-th/uni-th.vue
  79. 129 0
      uni_modules/uni-table/components/uni-thead/uni-thead.vue
  80. 179 0
      uni_modules/uni-table/components/uni-tr/table-checkbox.vue
  81. 171 0
      uni_modules/uni-table/components/uni-tr/uni-tr.vue
  82. 9 0
      uni_modules/uni-table/i18n/en.json
  83. 9 0
      uni_modules/uni-table/i18n/es.json
  84. 9 0
      uni_modules/uni-table/i18n/fr.json
  85. 12 0
      uni_modules/uni-table/i18n/index.js
  86. 9 0
      uni_modules/uni-table/i18n/zh-Hans.json
  87. 9 0
      uni_modules/uni-table/i18n/zh-Hant.json
  88. 83 0
      uni_modules/uni-table/package.json
  89. 13 0
      uni_modules/uni-table/readme.md
  90. 257 0
      work/components/case/list.vue
  91. 777 0
      work/components/popup/popup.vue
  92. 65 0
      work/components/zb-table/all.js
  93. 180 0
      work/components/zb-table/components/table-checkbox.vue
  94. 78 0
      work/components/zb-table/components/table-h5-summary.vue
  95. 59 0
      work/components/zb-table/components/table-side-summary.vue
  96. 77 0
      work/components/zb-table/components/table-summary.vue
  97. 50 0
      work/components/zb-table/components/zb-load-more.vue
  98. 88 0
      work/components/zb-table/js/summary.js
  99. 51 0
      work/components/zb-table/js/util.js
  100. 1255 0
      work/components/zb-table/zb-table.vue

+ 28 - 6
components/footer/footer.vue

@@ -5,8 +5,11 @@
 	<view style="height: 100rpx;"></view>
     <view class="com_footer">
         <view class="in_item" v-for="(item,k) in footlist" :key="k" @click="gotopage(item)">
-			<image class="in_img" v-if="active == item.module" :src="item.icon_checked" mode="scaleToFill"></image>
-			<image class="in_img" v-else :src="item.icon_nochecked" mode="scaleToFill"></image>
+			<view class="im_imgs">
+				<image class="in_img" :class="item.img" v-if="active == item.module" :src="item.icon_checked" mode="scaleToFill"></image>
+				<image class="in_img" :class="item.img" v-else :src="item.icon_nochecked" mode="scaleToFill"></image>
+			</view>
+			
 			<view class="in_txt" v-if="active == item.module" :style="'color: #'+color_checked">{{item.title}}</view>
 			<view class="in_txt" v-else :style="'color: #'+color_nochecked">{{item.title}}</view>
         </view>
@@ -19,15 +22,23 @@
 	// import plugins from '../../commen/js/plugin.js'
     export default {
 		props:[
-			'footlist',
+			// 'footlist',
+			// 'color_checked',
+			// 'color_nochecked',
 			'footerindex',
-			'color_checked',
-			'color_nochecked',
 			'isHomeIndex'
 		],
         data () {
             return{
 				active :'',
+				footlist:[
+					{module:'home',title:'潜山政协',img:"imga",icon_checked:require('@/static/images/tabbar/home_.png'),icon_nochecked:require('@/static/images/tabbar/home.png')},
+					{module:'report',title:'履职报告',img:"imgb",icon_checked:require('@/static/images/tabbar/report_.png'),icon_nochecked:require('@/static/images/tabbar/report.png')},
+					{module:'notice',title:'会议通知',img:"imgc",icon_checked:require('@/static/images/tabbar/notice_.png'),icon_nochecked:require('@/static/images/tabbar/notice.png')},
+					{module:'mine',title:'个人中心',img:"imgd",icon_checked:require('@/static/images/tabbar/mine_.png'),icon_nochecked:require('@/static/images/tabbar/mine.png')},
+					],
+				color_checked :'222327',
+				color_nochecked :'AAAAAA',
             }
         },
 		mounted() {
@@ -41,6 +52,10 @@
 				let link = e.link, module = e.module, def = e.default, title = e.title;
 				if(module == 'home'){
 					this.$tab.reLaunch('/pages/index/index')
+				}else if(module == 'report'){
+					this.$tab.reLaunch('/pages/report/index')
+				}else if(module == 'notice'){
+					this.$tab.reLaunch('/pages/notice/index')
 				}else if(module == 'mine'){
 					this.$tab.reLaunch('/pages/mine/index')
 				}
@@ -52,10 +67,17 @@
 
 
 
-<style type="text/css">
+<style type="text/css" lang="scss" scoped>
     .com_footer{ display: flex;position: fixed;box-sizing: border-box;z-index: 99;width: 100%;height: 100rpx;bottom: 0;left: 0;background-color: #fff;overflow: hidden;box-shadow: 0px 0px 16rpx 0px rgba(87,87,87,0.41);}
 	.com_footer .in_item{ display: block;flex: 1;padding: 12rpx 0 0 0;overflow: hidden; }
 	.com_footer .in_img{ display: block;width: 44rpx;height: 44rpx;margin: 0 auto; }
 	.com_footer .in_txt{ font-size: 26rpx;color: #999;text-align: center; font-weight: bold;}
 	.com_footer .in_txt_on{ color: #20AD20; }
+	.im_imgs{width: 48rpx;height: 48rpx;display: flex;align-items: center;justify-content: center;margin: 0 auto;}
+	.in_img{
+		&.imga{width: 38rpx;height: 48rpx;}
+		&.imgb{width: 46rpx;height: 48rpx;}
+		&.imgc{width: 44rpx;height: 44rpx;}
+		&.imgd{width: 48rpx;height: 42rpx;}
+	}
 </style>

+ 2 - 2
components/nodata/nodata.vue

@@ -33,7 +33,7 @@
 // 无数据
 .nodata{
 	display: flex;flex-direction: column;align-items: center;box-sizing: border-box;padding-bottom: 100rpx;
-	image{width: 456rpx;height: 178rpx;margin-bottom: 28rpx;}
-	view{font-size: 30rpx;color: #AAAAAA;font-weight: bold;}
+	image{width: 198rpx;height: 194rpx;margin-bottom: 44rpx;}
+	view{font-size: 26rpx;color: #AAAAAA;font-weight: bold;}
 }	
 </style>

+ 102 - 0
components/notice/list.vue

@@ -0,0 +1,102 @@
+<template>
+  <view>
+	<view v-if="datalist.length>0">
+		<!-- 新闻 -->
+		<block v-if="type==1">
+			<view class="nlist" v-for="(ite,idx) in datalist" :key='idx' @click="getDetail(ite.reservatId)">
+				<!-- 置顶 -->
+				<block v-if="ite.isTop">
+					<view class="nrbox">
+						<image :src="topbg"></image>
+						<view>置顶</view>
+					</view>
+					<image :src="ite.img" class="titimg"></image>
+				</block>
+				<view class="tit overtwo">{{ite.tit}}</view>
+				<view class="txt flexcj">
+					<view class="flex0">来源<text>|</text></view>
+					<view class="flex1 over">{{ite.from}}</view>
+					<view class="flex0 ml6">{{ite.time}}</view>
+				</view>
+			</view>
+		</block>
+		<view class="shax" v-if="wtdt">{{wtdt}}</view>
+	</view>
+	<block v-else>
+		<no-data></no-data>
+	</block>
+  </view>
+</template>
+
+<script>
+	import { selectDictValue } from '@/utils/common.js';
+	import noData from "@/components/nodata/nodata.vue"
+  export default {
+	props:{
+		datalist: {
+			type: Array,
+			default () {
+				return []
+			}
+		},
+		wtdt:{
+			type: String,
+			default () {
+				return ''
+			}
+		},
+		type:{
+			type: [String,Number],
+			default () {
+				return ''
+			}
+		},
+	},
+	components:{
+		noData
+	},
+	data(){
+		return{
+			topbg:require("@/static/images/home/topbg.png"),
+		}
+	},
+	onLoad: function() {
+	},
+	methods:{
+		getDetail(e){
+			this.$emit('getDetail',e)
+		},
+		typeFn(data){
+			if(data){
+				var newArr=[]
+				var astr=data.split('-')
+				astr.forEach(ite=>{
+					var a=ite.substring(0,5);
+					newArr.push(a)
+				})
+				return newArr.join('-')
+			}else{
+				return ''
+			}
+		},
+	},
+	
+  }
+</script>
+
+<style lang="scss" scoped>
+.nlist{
+	position: relative;padding: 34rpx 0 30rpx;border-bottom: 2rpx solid #E6E6E6;
+	&:last-child{border: none;}
+	.nrbox{position: absolute;right: 0;top: 34rpx;z-index: 1;
+		image{width: 112rpx;height: 52rpx;}
+		view{position: absolute;right: 0;top: 0;left: 0;bottom: 0;font-weight: bold;font-size: 24rpx;color: #FFFFFF;line-height: 52rpx;padding-left: 40rpx;box-sizing: border-box;}
+	}
+	.titimg{width: 100%;height: 258rpx;margin-bottom: 26rpx;}
+	.tit{font-weight: bold;font-size: 32rpx;color: #222327;margin-bottom: 24rpx;line-height: 44rpx;}
+	.txt{font-weight: 500;font-size: 24rpx;color: #AAAAAA;
+		text{margin:0 12rpx;}
+	}
+}
+
+</style>

+ 0 - 0
components/order/popup.vue → components/notice/popup.vue


+ 0 - 129
components/order/list.vue

@@ -1,129 +0,0 @@
-<template>
-  <view>
-	<view v-if="datalist.length>0">
-		<!-- 预约 -->
-		<block v-if="type==1">
-			<view class="ylist" v-for="(ite,idx) in datalist" :key='idx' @click="getDetail(ite.reservatId)">
-				
-			</view>
-		</block>
-		<view class="shax" v-if="wtdt">{{wtdt}}</view>
-	</view>
-	<block v-else>
-		<no-data></no-data>
-	</block>
-  </view>
-</template>
-
-<script>
-	import { selectDictValue } from '@/utils/common.js';
-	import noData from "@/components/nodata/nodata.vue"
-  export default {
-	props:{
-		datalist: {
-			type: Array,
-			default () {
-				return []
-			}
-		},
-		adrlist:{
-			type: Array,
-			default () {
-				return []
-			}
-		},
-		wtdt:{
-			type: String,
-			default () {
-				return ''
-			}
-		},
-		type:{
-			type: [String,Number],
-			default () {
-				return ''
-			}
-		},
-	},
-	components:{
-		noData
-	},
-	data(){
-		return{
-		}
-	},
-	onLoad: function() {
-	},
-	methods:{
-		kaType(data, list) {
-			return selectDictValue(list, data);
-		},
-		getDelFn(id){
-			var that=this;
-			uni.showModal({
-				title: '确认删除',
-				content: "是否确认删除该预约",
-				cancelText: '取消',
-				confirmText: '确认',
-				success: function(res) {
-					if (res.confirm) {
-						that.$emit("getDelFn",id)
-					} else if (res.cancel) {
-						// console.log('用户点击取消');
-					}
-				}
-			});
-		},
-		gettypeFn(type,id){
-			var that=this;
-			var str="拒绝"
-			if(type==3){
-				str='同意'
-			}
-			uni.showModal({
-				title: '确认'+str,
-				content: "是否确认"+str+"该预约",
-				cancelText: '取消',
-				confirmText: '确认',
-				success: function(res) {
-					if (res.confirm) {
-						var newobj={
-							type:type,
-							id:id
-						}
-						that.$emit("gettypeFn",newobj)
-					} else if (res.cancel) {
-						// console.log('用户点击取消');
-					}
-				}
-			});
-		},
-		getDetail(e){
-			this.$emit('getDetail',e)
-		},
-		typeFn(data){
-			if(data){
-				var newArr=[]
-				var astr=data.split('-')
-				astr.forEach(ite=>{
-					var a=ite.substring(0,5);
-					newArr.push(a)
-				})
-				return newArr.join('-')
-			}else{
-				return ''
-			}
-			
-			
-		},
-	},
-	
-  }
-</script>
-
-<style lang="scss" scoped>
-// 预约
-.ylist{background: #FFFFFF;border-radius: 14rpx;margin-bottom: 30rpx;position: relative;
-}
-
-</style>

+ 0 - 78
components/order/stepbar.vue

@@ -1,78 +0,0 @@
-<template>
-  <view class="stepbar" :class="fixeda?'sfixed':''">
-	  <block v-for="(ite,idx) in steps" :key="idx">
-		  <view class="steps" :class="ite.status>0?'act':''" v-if="reservatType!=2&&ite.val==2||ite.val!=2">
-		  	<image :src="filln" class="img" v-if="ite.status==0"></image>
-		  	<image :src="fillin" class="img" v-if="ite.status==1"></image>
-		  	<image :src="fillf" class="img" v-if="ite.status==2"></image>
-		  	<view class="tit">{{ite.tit}}</view>
-			<image :src="line" class="line"></image>
-		  	<!-- <view class="line"></view> -->
-		  </view>
-	  </block>
-  </view>
-</template>
-
-<script>
-//fixeda   固定
-  export default {
-	props:{
-		steps: {
-			type: Array,
-			default () {
-				return []
-			}
-		},
-		fixeda:{
-			type: Boolean,
-			default () {
-				return false
-			}
-		},
-		reservatType:{
-			type: [Number,String],
-			default () {
-				return 1
-			}
-		}
-	},
-	components:{
-		
-	},
-	data(){
-		return{
-			fillin:require("@/static/images/order/come/fillin.png"),//填写
-			fillf:require("@/static/images/order/come/fillf.png"), //完成
-			filln:require("@/static/images/order/come/filln.png"),//未填
-			line:require("@/static/images/order/come/line.png"),//完成
-		}
-	},
-	onLoad: function() {
-	},
-	methods:{
-		getDetail(e){
-			this.$emit('getDetail')
-		} 
-	},
-	
-  }
-</script>
-
-<style lang="scss" scoped>
-// .sfixed{position: absolute;left: 0;right: 0;top: 0;z-index: 10;}
-.stepbar{background: transparent;display: flex;align-items: center;padding: 30rpx 0rpx;flex: 0 0 auto;
-	.steps{display: flex;flex-direction: column;flex: 1;align-items: center;position: relative;
-		.img{width: 30rpx;height: 30rpx;margin-bottom: 20rpx;}
-		.tit{font-size: 24rpx;font-weight: bold;color: #ffffff;opacity: 0.7;
-
-		}
-		.line{position: absolute;width: 102rpx;height:18rpx;top: 6rpx;right: 0;transform: translateX(50%);opacity: 0.7;}
-		&:last-of-type{.line{display: none;}}
-		&.act{
-			.line{opacity: 1;}
-			.tit{opacity: 1;}
-		}
-	}
-
-}
-</style>

+ 61 - 0
components/swiper/notice.vue

@@ -0,0 +1,61 @@
+<template>
+  <view>
+	  <view class="hswip flexc" >
+	  		<image :src="tztip"></image>
+			<swiper class="swiper" circular :autoplay="autoplay" :interval="interval" :duration="duration" vertical='true'>
+				<swiper-item>
+					<view class="flexc swipers">
+						<view class="tit over">1《关于优化我市惠企利民政策“免审即…</view>
+						<view class="time">02-27</view>
+					</view>
+				</swiper-item>
+				<swiper-item>
+					<view class="flexc swipers">
+						<view class="tit over">2《关于优化我市惠企利民政策“免审即…</view>
+						<view class="time">02-27</view>
+					</view>
+				</swiper-item>
+			</swiper>
+	  		
+	  </view>
+  </view>
+</template>
+
+<script>
+  export default {
+	  props:{
+	  	autoplay: {
+	  		type: Boolean,
+	  		default () {
+	  			return false
+	  		}
+	  	},
+	  	confdat:{}
+	  },
+	data(){
+		return{
+			tztip:require('@/static/images/home/tztip.png'),
+            interval: 2000,
+            duration: 500
+		}
+	},
+	methods:{
+		 getRoad(){
+		 	console.log(1)
+		 },
+	},
+	onLoad: function() {
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+.hswip{width: 100%;background: #F4F7FF;border-radius: 10rpx;height: 88rpx;padding: 0 20rpx 0 24rpx;box-sizing: border-box;
+	image{width: 66rpx;height: 30rpx;flex: 0 0 auto;margin-right: 30rpx;}
+	.swiper{flex: 1;height: 88rpx;
+	.swipers{height: 88rpx;}
+		.tit{flex: 1;font-weight: 500;font-size: 26rpx;color: #666666;}
+		.time{font-size: 24rpx;color: #666666;flex: 0 0 auto;margin-left: 40rpx;}
+	}
+}
+</style>

+ 19 - 12
components/toptab/navbar.vue

@@ -1,12 +1,15 @@
 <template>
-	<view>
-		<image :src="navbg" class="navbg"></image>
-		<uni-nav-bar :left-icon="leftflag?'left':''" :title="navtit" color="#ffffff"  :background-color="bgColor" :border="navborder" statusBar='true' fixed="true" @clickLeft='getBack'>
-			<!-- <block slot="default">
-				<view class="navtit">
-					<view>{{navtit}}</view>
-				</view>
-			</block> -->
+	<view class="navbox">
+		<uni-nav-bar  color="#ffffff" leftWidth='300rpx'  :background-color="bgColor" :border="false" statusBar='true' fixed="true">
+			<block slot="left">
+				<!-- <image :src="titimg" class="topl"></image> -->
+			</block>
+			<block slot="right">
+				<!-- <view class="topr">
+					<image :src="noticeimg"></image>
+					<view class="cir"></view>
+				</view> -->
+			</block>
 		</uni-nav-bar>
 	</view>
 </template>
@@ -32,11 +35,11 @@
 				default () {
 					return false
 				}
-			}
+			},
 		},
 		data(){
 			return{
-				navbg:require("@/static/images/navbg.png"),
+				navbg:require("@/static/images/bg.png"),
 				backgroundColor: "transparent",
 			}
 		},
@@ -54,7 +57,11 @@
 </script>
 
 <style scoped lang="scss">
-.navbg{width: 100%;height: 692rpx;position: fixed;left: 0;right: 0;top: 0;z-index: 1;}
-.navtit{display: flex;align-items: center;justify-content: center;font-size: 30rpx;color: #ffffff;width: 100%;
+.navbox{position: fixed;left: 0;right: 0;top: 0;z-index: 4;
+	.topl{width: 274rpx;height: 50rpx;margin-left: 14rpx;}
+	.topr{width: 36rpx;height: 36rpx;position: relative;margin-right: 10rpx;
+		image{width: 100%;height: 100%;}
+		.cir{width: 14rpx;height: 14rpx;background: #DF0024;border-radius: 50%;position: absolute;right: -7rpx;top: -7rpx;}
+	}
 }	
 </style>

+ 3 - 3
config.js

@@ -1,9 +1,9 @@
 // 应用全局配置
 module.exports = {
-  // baseUrl: 'https://vue.ruoyi.vip/prod-api',
+  baseUrl: 'https://vue.ruoyi.vip/prod-api',
   // baseUrl: 'https://lyyy.qs163.cn/prod-api',
-  baseUrl: 'http://192.168.101.168:8065',
-  // baseUrl: 'http://192.168.101.11:8089',
+  // baseUrl: 'http://192.168.101.168:8055',
+  // baseUrl: 'http://192.168.101.11:8055',
   // baseUrlimg: 'http://114.99.127.243:2001',
   Clientid:'428a8310cd442757ae699df5d894f051',//
  // https://xygl.cnzxy.cn h5链接地址

+ 21 - 9
manifest.json

@@ -18,25 +18,30 @@
             "delay" : 0
         },
         "modules" : {
-            "Geolocation" : {}
+            "Geolocation" : {},
+            "Speech" : {}
         },
         "distribute" : {
             "android" : {
                 "permissions" : [
-                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
-                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
-                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
-                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
-                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
                     "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
                     "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
                     "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
                     "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.INTERNET\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
                     "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
-                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
                     "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
-                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
-                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
                     "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
                 ]
             },
@@ -57,6 +62,13 @@
                         "appkey_ios" : "b85f0c64b3c9a082d740912cf3019c88",
                         "appkey_android" : "b85f0c64b3c9a082d740912cf3019c88"
                     }
+                },
+                "speech" : {
+                    "baidu" : {
+                        "appid" : "",
+                        "apikey" : "",
+                        "secretkey" : ""
+                    }
                 }
             },
             "icons" : {

+ 84 - 7
pages.json

@@ -16,6 +16,18 @@
 	    "navigationBarTitleText": "智慧访客预约系统",
 	    "navigationStyle": "custom"
 	  }},
+	{
+	  "path": "pages/report/index",
+	  "style": {
+	    "navigationBarTitleText": "履职报告",
+	    "navigationStyle": "custom"
+	}},
+	{
+	  "path": "pages/notice/index",
+	  "style": {
+	    "navigationBarTitleText": "会议通知",
+	    "navigationStyle": "custom"
+	}},
 	
   {
     "path": "pages/register",
@@ -28,13 +40,6 @@
 	  "style": {
 	    "navigationBarTitleText": "用户协议及隐私条款"
 	  }
-	},
-	
-  {
-    "path": "pages/work/index",
-    "style": {
-      "navigationBarTitleText": "工作台"
-    }
 	}, {
     "path": "pages/mine/index",
     "style": {
@@ -98,6 +103,78 @@
       "navigationBarTitleText": "浏览文本"
     }
   }],
+  "subPackages": [
+  	{
+  		"root": "work",
+		"pages": [
+			{
+				"path": "pages/case/add",
+				"style": {
+					"navigationBarTitleText": "提交提案",
+					"navigationBarBackgroundColor": "#1D64E2",
+					"navigationBarTextStyle": "white",
+					"h5":{"titleNView":false}
+				}
+			},
+			{
+				"path": "pages/case/talbclass",
+				"style": {
+					"navigationBarTitleText": "提案类别",
+					"navigationBarBackgroundColor": "#1D64E2",
+					"navigationBarTextStyle": "white",
+					"h5":{"titleNView":false}
+				}
+			},
+			{
+				"path": "pages/case/lmtapeople",
+				"style": {
+					"navigationBarTitleText": "联名提案人员",
+					"navigationBarBackgroundColor": "#1D64E2",
+					"navigationBarTextStyle": "white",
+					"h5":{"titleNView":false}
+				}
+			},
+			{
+				"path": "pages/case/mine",
+				"style": {
+					"navigationBarTitleText": "我的提案",
+					"navigationBarBackgroundColor": "#1D64E2",
+					"navigationBarTextStyle": "white",
+					"navigationStyle": "custom",
+					"h5":{"titleNView":false}
+				}
+			},
+			{
+				"path": "pages/case/tacheck",
+				"style": {
+					"navigationBarTitleText": "提案查重",
+					"navigationBarBackgroundColor": "#1D64E2",
+					"navigationBarTextStyle": "white",
+					"h5":{"titleNView":false}
+				}
+			},
+			{
+				"path": "pages/case/details",
+				"style": {
+					"navigationBarTitleText": "提交提案",
+					"navigationBarBackgroundColor": "#1D64E2",
+					"navigationBarTextStyle": "white",
+					"h5":{"titleNView":false}
+				}
+			}
+		]
+  	}
+  ],
+  "preloadRule": {
+  	"pages/index/index": {
+  		"network": "all",
+  		"packages": ["work"]
+  	},
+  	"pages/login": {
+  		"network": "all",
+  		"packages": ["work"]
+  	}
+  },
   // "tabBar": {
   //   "color": "#000000",
   //   "selectedColor": "#000000",

+ 263 - 124
pages/index/index.vue

@@ -1,124 +1,210 @@
 <template>
-	<view class="yybox">
-		<view class="navbox">
-			<image :src="navbg" class="navbg"></image>
-			<uni-nav-bar  color="#ffffff" leftWidth='340rpx'  :background-color="backgroundColor" :border="false" statusBar='true' fixed="true">
-				<block slot="left">
-					<view class="navleft">
-						<view>{{city}} {{daytime}}</view>
-						<view>农历{{lunar}}</view>
-					</view>
-				</block>
-				<block slot="right">
-					<view class="topr flexccc" v-if="weather">
-						<image :src="weathericon" class="navright"></image>
-						<view class="f12 cof">{{weather}} {{temperature}}­°C</view>
+	<view class="zxbox">
+		<view class="zxtop">
+			<view class="navbox">
+				<uni-nav-bar  color="#ffffff" leftWidth='340rpx'  :background-color="backgroundColor" :border="false" statusBar='true' fixed="true">
+					<block slot="left">
+						<image :src="titimg" class="topl"></image>
+					</block>
+					<block slot="right">
+						<view class="topr">
+							<image :src="noticeimg"></image>
+							<view class="cir"></view>
+						</view>
+					</block>
+				</uni-nav-bar>
+			</view>
+			<image :src="bgimg" class="bgimg"></image>
+			<view class="zxmain">
+				<view class="flexc mb27">
+					<view class="htop" @click="getAddCase">
+						<view class="imgs">
+							<image :src="htopa" class="imga"></image>
+						</view>
+						<view class="tit">提交提案</view>
 					</view>
-				</block>
-			</uni-nav-bar>
-		</view>
-		<!-- 主体 -->
-		<view class="yymain">
-			<view class="yycard">
-				<view class="top flexccc">
-					<image :src="headimg"></image>
-					<view>欢迎您,尊敬的管理员!</view>
-				</view>
-				<view class="flexcc">
-					<view class="list">
-						<view class="tit">613</view>
-						<view class="txt">来访数(位)</view>
+					<view class="htop">
+						<view class="imgs">
+							<image :src="htopb" class="imgb"></image>
+						</view>
+						<view class="tit">上报社情民意</view>
 					</view>
-					<view class="list">
-						<view class="tit">246</view>
-						<view class="txt">本月预约(位)</view>
+					<view class="htop">
+						<view class="imgs">
+							<image :src="htopc" class="imgc"></image>
+						</view>
+						<view class="tit">履职档案</view>
 					</view>
-					<view class="list">
-						<view class="tit">613</view>
-						<view class="txt">今日来访(位)</view>
+					<view class="htop">
+						<view class="imgs">
+							<image :src="htopd" class="imga"></image>
+						</view>
+						<view class="tit">创建会议</view>
 					</view>
-					<view class="list">
-						<view class="tit">613</view>
-						<view class="txt">今日预约(位)</view>
+				</view>
+				<view class="hbox">
+					<!-- 通知栏 -->
+					<h-notice></h-notice>
+					<!-- 入口 -->
+					<view class="mt22">
+						<view class="htit">快捷入口</view>
+						<view class="mt18 flexcw">
+							<view class="htbox" @click="getZxCaseFn">
+								<image :src="hrbga" class="hrbg"></image>
+								<view class="htboxa flexc">
+									<view class="flex1 mr10">
+										<view class="tit">政协提案</view>
+										<view class="txt">全方位审核委员提案</view>
+									</view>
+									<image :src="hrimga" class="flex0 imga"></image>
+								</view>
+							</view>
+							<view class="htbox">
+								<image :src="hrbgb" class="hrbg"></image>
+								<view class="htboxa flexc">
+									<view class="flex1 mr10">
+										<view class="tit">社情民意</view>
+										<view class="txt">录入社情民意信息</view>
+									</view>
+									<image :src="hrimgb" class="flex0 imgb"></image>
+								</view>
+							</view>
+							<view class="htbox">
+								<image :src="hrbgc" class="hrbg"></image>
+								<view class="htboxa flexc">
+									<view class="flex1 mr10">
+										<view class="tit">履职信息</view>
+										<view class="txt">管理委员履职信息</view>
+									</view>
+									<image :src="hrimgc" class="flex0 imgc"></image>
+								</view>
+							</view>
+							<view class="htbox">
+								<image :src="hrbgd" class="hrbg"></image>
+								<view class="htboxa flexc">
+									<view class="flex1 mr10">
+										<view class="tit">会议活动</view>
+										<view class="txt">了解会议活动信息</view>
+									</view>
+									<image :src="hrimgd" class="flex0 imgd"></image>
+								</view>
+							</view>
+						</view>
 					</view>
 					
 				</view>
 			</view>
-			<view class="yylists">
-				<view class="yyltab flexcj">
-					<view class="left flexc">待审批列表<image :src="listline"></image></view>
-					<view class="right" @click="getMoreFn">查看更多 >></view>
+		</view>
+		
+		<!-- 新闻 -->
+		<view class="hnbox">
+			<view class="hntabs flexc" :class="fixedflag?'tabfix':''"  :style="fixedflag?'top:'+nvaHeight+'px;':''">
+				<view class="flex1 flexc">
+					<view class="tit" v-for="(ite,idx) in tabList" :key="idx" :class="tabval==ite.val?'act':''" @click="getTab(ite.val)">{{ite.tit}}</view>
 				</view>
-				<!-- 列表 -->
-				<view>
-					<y-list type='1' :datalist="list" :wtdt="wtdt" @getDetail='getDetail' :adrlist="adrlist" @getDelFn="getDelFn" @gettypeFn="gettypeFn"></y-list>
+				<view class="flex0 txtr">了解更多>></view>
+			</view>
+			<view style="height:114rpx;" v-if="fixedflag"></view>
+			<!-- 列表 -->
+			<view class="mt2 plr12">
+				<notice-list :datalist="list" :wtdt="wtdt" type='1' @getDetail="getDetail"></notice-list>
+				<view class="upmore flexccc">
+					<image :src="upimg"></image>
+					<view>下拉更多</view>
 				</view>
 			</view>
 		</view>
-	
 	<footers v-if="isfootflag" :footlist="footlist" :footerindex="footerindex" :color_checked="color_checked" :color_nochecked="color_nochecked" :isHomeIndex="true"></footers>
   </view>
 </template>
 
 <script>
 	import { checkPermi, checkRole } from "@/utils/permission"; // 权限判断函数
-	import self from '@/utils/location.js';
-	import yList from "@/components/order/list.vue"
-	let { calendar } = require("@/components/lunc-calendar/calendar.js");
-	import {getReservatcountl,getReservatList,getReservatDel,getReservatSh} from "@/api/mine/order.js"
-	import {getDictionaryFn} from "@/api/mine/register.js"
+	// let { calendar } = require("@/components/lunc-calendar/calendar.js");
+	// import {getReservatcountl,getReservatList,getReservatDel,getReservatSh} from "@/api/mine/order.js"
+	// import {getDictionaryFn} from "@/api/mine/register.js"
+	
+	import hNotice from '@/components/swiper/notice.vue'
+	import noticeList from '@/components/notice/list.vue'
 	import footers from '@/components/footer/footer.vue'
   export default {
-	components:{yList,footers},
+	components:{hNotice,noticeList,footers},
 	data(){
 		return{
 			footlist:[
-				{module:'home',title:'首页',icon_checked:require('@/static/images/tabbar/home_.png'),icon_nochecked:require('@/static/images/tabbar/home.png')},
-				{module:'mine',title:'我的',icon_checked:require('@/static/images/tabbar/mine_.png'),icon_nochecked:require('@/static/images/tabbar/mine.png')},
+				{module:'home',title:'潜山政协',icon_checked:require('@/static/images/tabbar/home_.png'),icon_nochecked:require('@/static/images/tabbar/home.png')},
+				{module:'report',title:'履职报告',icon_checked:require('@/static/images/tabbar/report_.png'),icon_nochecked:require('@/static/images/tabbar/report.png')},
+				{module:'notice',title:'会议通知',icon_checked:require('@/static/images/tabbar/notice_.png'),icon_nochecked:require('@/static/images/tabbar/notice.png')},
+				{module:'mine',title:'个人中心',icon_checked:require('@/static/images/tabbar/mine_.png'),icon_nochecked:require('@/static/images/tabbar/mine.png')},
 				],
+			color_checked :'222327',
+			color_nochecked :'AAAAAA',
 			footerindex:'home',
 			isfootflag:true,
-			color_checked :'161616',
-			color_nochecked :'666666',
-			navbg:require("@/static/images/navbg.png"),
-			headimg:require("@/static/images/order/staff/head.png"),
-			listline:require("@/static/images/order/staff/listline.png"),
-			backgroundColor: "transparent",
-			city:'',
-			weather:'',
-			temperature:'',
-			list:[],
-			adrlist:[],
+			backgroundColor:'transparent',
+			bgimg:require("@/static/images/bg.png"),
+			titimg:require("@/static/images/home/tit.png"),
+			noticeimg:require("@/static/images/home/notice.png"),
+			htopa:require("@/static/images/home/htopa.png"),
+			htopb:require("@/static/images/home/htopb.png"),
+			htopc:require("@/static/images/home/htopc.png"),
+			htopd:require("@/static/images/home/htopd.png"),
+			hrbga:require("@/static/images/home/hrbga.png"),
+			hrbgb:require("@/static/images/home/hrbgb.png"),
+			hrbgc:require("@/static/images/home/hrbgc.png"),
+			hrbgd:require("@/static/images/home/hrbgd.png"),
+			hrimga:require("@/static/images/home/hrimga.png"),
+			hrimgb:require("@/static/images/home/hrimgb.png"),
+			hrimgc:require("@/static/images/home/hrimgc.png"),
+			hrimgd:require("@/static/images/home/hrimgd.png"),
+			upimg:require("@/static/images/home/up.png"),
+			tabval:'0',
+			tabList:[{tit:'政协要闻',val:'0'},{tit:'界别活动',val:'1'},],
+			list:[
+				{tit:'全国政协十四届二次会议在京开幕 习近平等党和 国家领导人到会祝贺',img:require("@/static/images/bg.png"),from:'新华网',time:'2024-03-04',isTop:true},
+				{tit:'政协全国委员会关于颁发2023年度全国政协委员 优秀履职奖的决定',img:require("@/static/images/bg.png"),from:'人民政协报',time:'2024-03-03',isTop:false},
+				{tit:'市政协办公室党支部召开党员大会',img:require("@/static/images/bg.png"),from:'人民政协报',time:'2024-03-02',isTop:false},
+			
+			],
 			pageSize: 10,
 			pageNum: 1,
-			reachflag: false,
+			reachflag: true,
 			wtdt:'',
-			daytime:'',
-			lunar:'',
-			weathericon:require("@/static/images/weather/weaionf.png"),
+			nvaHeight:44,
+			listTop:0,//距离顶部的距离
+			fixedflag:false,
 			
 		}
 	},
 	onPageScroll(e) {
 		var scrollTop = Number(e.scrollTop);
-		if (scrollTop > 0) {
-			this.backgroundColor = '#0491FD'
+		var listTop=Number(this.listTop)-Number(this.nvaHeight)
+		if (scrollTop <=this.nvaHeight) {
+			const opacity = scrollTop / 100 // 计算透明度值
+			const color = `rgba(29, 100, 226, ${opacity})`
+			this.backgroundColor = color // 更新盒子背景颜色
 		} else {
-			this.backgroundColor = 'transparent'
+			this.backgroundColor = '#1D64E2'
+		}
+		if(scrollTop>listTop){
+			this.fixedflag=true
+		}else{
+			this.fixedflag=false
 		}
 	},
 	onLoad: function() {
-		uni.$on('refreshdatalist',(e) => {
-			this.reachflag=false;
-			this.pageNum=1;
-			this.list=[];
-			this.getDataFn();
-			// this.getcount();
-		})
+		// uni.$on('refreshdatalist',(e) => {
+		// 	this.reachflag=false;
+		// 	this.pageNum=1;
+		// 	this.list=[];
+		// 	this.getDataFn();
+		// 	// this.getcount();
+		// })
 		
-		this.init()
-		// this.getcount()
-		this.getDataFn()
+		// this.init()
+		// // this.getcount()
+		// this.getDataFn()
+		this.nvaHeight=uni.getSystemInfoSync().statusBarHeight+44;
 		
 	},
 	onUnload() {
@@ -126,34 +212,56 @@
 	},
 
 	onShow() {
-		var that=this;
-		this.time();
-		// #ifndef H5
-		self.getLocation(function(res){
-			if(res==-1){
-				that.setflag=true
-			}else{
-				// that.getAdrinfoFn(res)
-				// that.location = `${res.lng},${res.lat}`
-				that.city=res.city;
-				that.temperature=res.temperature;
-				that.weather=res.weather;
-				that.weathericon=res.icon;
-			}			
-		})
-		// #endif
+		// var that=this;
+		// this.time();
 	},
 	mounted() {
+		this.getHeightFn()
 	},
 	// 上拉触底加载更多触发事件
 	onReachBottom() {
-		if (this.reachflag) {
-			this.pageNum++
-			this.getDataFn()
-		}
+		// if (this.reachflag) {
+		// 	this.pageNum++
+		// 	this.getDataFn()
+		// }
 	},
 	methods:{
 		checkPermi, checkRole,
+		getTab(val){
+			this.tabval=val;
+		},
+		getHeightFn(){
+			let query = uni.createSelectorQuery().in(this);
+				//需要给黄色区域设置一个id标识,在这里是demo
+			query.select('.zxtop').boundingClientRect(data => {
+				this.listTop = data.height//赋值,待会要用
+			}).exec();
+		},
+		getAddCase(){
+			this.$tab.navigateTo('/work/pages/case/add')
+		},
+		getZxCaseFn(){
+			this.$tab.navigateTo('/work/pages/case/mine')
+		},
+		
+		
+		
+		
+		
+		
+		
+		
+		
+		
+		
+		
+		
+		
+		
+		
+		
+		
+		
 		init(){
 			// 记录来源
 			getDictionaryFn('jluly').then(res=>{
@@ -279,33 +387,64 @@
 </script>
 
 <style lang="scss" scoped>
-.navbox{
-	.navbg{width: 100%;height: 692rpx;position: fixed;left: 0;right: 0;top: 0;z-index: 1;}	
-}
-.navleft{padding-left: 12rpx;
-		view{font-weight: bold;font-size: 26rpx;color: #FFFFFF;line-height: 36rpx;}
+.navbox{position: fixed;left: 0;right: 0;top: 0;z-index: 4;
+	.topl{width: 274rpx;height: 50rpx;margin-left: 14rpx;}
+	.topr{width: 36rpx;height: 36rpx;position: relative;margin-right: 10rpx;
+		image{width: 100%;height: 100%;}
+		.cir{width: 14rpx;height: 14rpx;background: #DF0024;border-radius: 50%;position: absolute;right: -7rpx;top: -7rpx;}
 	}
-	.navright{width: 40rpx;height: 40rpx;}
-.yymain{
-	flex: 1;z-index: 2;padding-top: 20rpx;
 }
-.yycard{width: 684rpx;height: 306rpx;background: #FFFFFF;border-radius: 14rpx;margin: 0 auto;
-	.top{
-		image{width: 132rpx;height: 132rpx;margin-bottom: 12rpx;margin-top: -56rpx;}
-		view{font-weight: bold;font-size: 30rpx;color: #161616;}
-	}
-	.list{text-align: center;flex: 1;padding-top: 44rpx;
-		.tit{font-weight: bold;font-size: 36rpx;color: #0391FD;margin-bottom: 16rpx;}
-		.txt{font-weight: bold;font-size: 18rpx;color: #666666;}
+// .zxbox /deep/ .ql-editor p{text-indent: 2rem;}
+	
+
+.zxbox{
+	.bgimg{width: 100%;height: 420rpx;z-index: -1;}
+	.zxmain{margin-top: -260rpx;z-index: 1;
+		.htop{width: 25%;display: flex;flex-direction: column;align-items: center;justify-content: center;
+			.imgs{
+				width: 74rpx;height: 74rpx;display: flex;align-items: center;justify-content: center;
+				.imga{width: 72rpx;height: 72rpx;}
+				.imgb{width: 68rpx;height: 74rpx;}
+				.imgc{width: 68rpx;height: 72rpx;}	
+			}
+			.tit{font-weight: 500;font-size: 26rpx;color: #F1F9FF;margin-top: 26rpx;}
+		}
+		.hbox{
+			background: #ffffff;border-radius: 30rpx 30rpx 0 0;padding: 38rpx 24rpx 0 24rpx;
+			.htit{font-size: 36rpx;color: #343434;font-weight: bold;}
+			.htbox{position: relative;width: 342rpx;height: 146rpx;position: relative;margin-bottom: 24rpx;margin-right: 18rpx;
+				&:nth-of-type(2n){margin-right: 0;}
+				.hrbg{width: 100%;height: 100%;}
+				.htboxa{position: absolute;left: 0;right: 0;top: 0;bottom: 0;padding: 0 18rpx 0 32rpx;box-sizing: border-box;
+					.tit{font-size: 16px;color: #222327;font-weight: bold;margin-bottom: 8rpx;line-height: 44rpx;}
+					.txt{font-weight: 500;font-size: 24rpx;color: #666666;}
+					.imga{width: 56rpx;height: 60rpx;}
+					.imgb{width: 50rpx;height: 60rpx;}
+					.imgc{width: 54rpx;height: 58rpx;}
+					.imgd{width: 70rpx;height: 56rpx;}
+				}
+			}
+			
+		}
 	}
-}
-.yylists{width: 684rpx;margin: 42rpx auto 0;
-	.yyltab{margin-bottom: 28rpx;
-		.left{font-weight: bold;font-size: 15px;color: #161616;
-			image{width: 110rpx;height: 14rpx;margin-left: 16rpx;}
+	.hnbox{background-color: #ffffff;padding-bottom: 32rpx;
+		.hntabs{padding:32rpx 24rpx;background-color: #ffffff;
+			&.tabfix{position: fixed;left: 0;right: 0;z-index: 4;box-shadow:  0rpx 4rpx 12rpx 0rpx rgba(196,191,191,0.22);}
+			.tit{font-weight: 500;font-size: 32rpx;color: #AAAAAA;position: relative;line-height: 50rpx;margin-right: 64rpx;
+				&.act{font-weight: bold;font-size: 36rpx;color: #222327;
+					&::after{
+						content: '';width: 52rpx;height: 12rpx;background: #1D64E2;border-radius: 6rpx;position: absolute;left: 50%;bottom: -32rpx;margin-left: -26rpx;
+					}
+				}
+			}
+			.txtr{font-size: 26rpx;color: #aaaaaa;font-weight:500;}
+		}
+		.upmore{
+			image{width: 40rpx;height: 32rpx;margin-bottom: 16rpx;}
+			view{font-weight: 500;font-size: 26rpx;color: #AAAAAA;}
+			
 		}
-		.right{font-size: 500;font-size: 22rpx;color: #AAAAAA;}
 	}
+	
 }
-
 </style>

+ 7 - 4
pages/mine/index.vue

@@ -69,13 +69,16 @@
     data() {
       return {
 		  footlist:[
-		  	{module:'home',title:'首页',icon_checked:require('@/static/images/tabbar/home_.png'),icon_nochecked:require('@/static/images/tabbar/home.png')},
-		  	{module:'mine',title:'我的',icon_checked:require('@/static/images/tabbar/mine_.png'),icon_nochecked:require('@/static/images/tabbar/mine.png')},
+		  	{module:'home',title:'潜山政协',icon_checked:require('@/static/images/tabbar/home_.png'),icon_nochecked:require('@/static/images/tabbar/home.png')},
+		  	{module:'report',title:'履职报告',icon_checked:require('@/static/images/tabbar/report_.png'),icon_nochecked:require('@/static/images/tabbar/report.png')},
+		  	{module:'notice',title:'会议通知',icon_checked:require('@/static/images/tabbar/notice_.png'),icon_nochecked:require('@/static/images/tabbar/notice.png')},
+		  	{module:'mine',title:'个人中心',icon_checked:require('@/static/images/tabbar/mine_.png'),icon_nochecked:require('@/static/images/tabbar/mine.png')},
 		  	],
+		  color_checked :'222327',
+		  color_nochecked :'AAAAAA',
 		  footerindex:'mine',
 		  isfootflag:true,
-		  color_checked :'161616',
-		  color_nochecked :'666666',
+		  
 		  abg:require('@/static/images/mine/habg.png'),
 		  noticimg:require('@/static/images/mine/mnotic.png'),
 		  rimg:require('@/static/images/mine/rimg.png'),

+ 41 - 0
pages/notice/index.vue

@@ -0,0 +1,41 @@
+<template>
+  <view >
+	<footers v-if="isfootflag" :footlist="footlist" :footerindex="footerindex" :color_checked="color_checked" :color_nochecked="color_nochecked" :isHomeIndex="true"></footers>
+ </view>
+</template>
+
+<script>
+	import footers from '@/components/footer/footer.vue'
+  import { checkPermi, checkRole } from "@/utils/permission"; // 权限判断函数
+  export default {
+	components:{footers},
+    data() {
+      return {
+		  footlist:[
+		  	{module:'home',title:'潜山政协',icon_checked:require('@/static/images/tabbar/home_.png'),icon_nochecked:require('@/static/images/tabbar/home.png')},
+		  	{module:'report',title:'履职报告',icon_checked:require('@/static/images/tabbar/report_.png'),icon_nochecked:require('@/static/images/tabbar/report.png')},
+		  	{module:'notice',title:'会议通知',icon_checked:require('@/static/images/tabbar/notice_.png'),icon_nochecked:require('@/static/images/tabbar/notice.png')},
+		  	{module:'mine',title:'个人中心',icon_checked:require('@/static/images/tabbar/mine_.png'),icon_nochecked:require('@/static/images/tabbar/mine.png')},
+		  	],
+		  color_checked :'222327',
+		  color_nochecked :'AAAAAA',
+		  footerindex:'notice',
+		  isfootflag:true,
+		  
+      }
+    },
+	onShow() {
+	},
+	onLoad() {
+	},
+    methods: {
+		checkPermi, checkRole,
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  page {
+    background-color: #f5f6f7;
+  }
+</style>

+ 41 - 0
pages/report/index.vue

@@ -0,0 +1,41 @@
+<template>
+  <view >
+	<footers v-if="isfootflag" :footlist="footlist" :footerindex="footerindex" :color_checked="color_checked" :color_nochecked="color_nochecked" :isHomeIndex="true"></footers>
+ </view>
+</template>
+
+<script>
+	import footers from '@/components/footer/footer.vue'
+  import { checkPermi, checkRole } from "@/utils/permission"; // 权限判断函数
+  export default {
+	components:{footers},
+    data() {
+      return {
+		  footlist:[
+		  	{module:'home',title:'潜山政协',icon_checked:require('@/static/images/tabbar/home_.png'),icon_nochecked:require('@/static/images/tabbar/home.png')},
+		  	{module:'report',title:'履职报告',icon_checked:require('@/static/images/tabbar/report_.png'),icon_nochecked:require('@/static/images/tabbar/report.png')},
+		  	{module:'notice',title:'会议通知',icon_checked:require('@/static/images/tabbar/notice_.png'),icon_nochecked:require('@/static/images/tabbar/notice.png')},
+		  	{module:'mine',title:'个人中心',icon_checked:require('@/static/images/tabbar/mine_.png'),icon_nochecked:require('@/static/images/tabbar/mine.png')},
+		  	],
+		  color_checked :'222327',
+		  color_nochecked :'AAAAAA',
+		  footerindex:'report',
+		  isfootflag:true,
+		  
+      }
+    },
+	onShow() {
+	},
+	onLoad() {
+	},
+    methods: {
+		checkPermi, checkRole,
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+  page {
+    background-color: #f5f6f7;
+  }
+</style>

+ 25 - 0
pages/s.vue

@@ -0,0 +1,25 @@
+<template>
+	<view>
+		
+	</view>
+</template>
+
+<script>
+	export default{
+		data(){
+			return{
+				bgimg:require("@/static/images/bg.png"),
+			}
+		},
+		onLoad(e) {
+			
+		},
+		methods:{
+			
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	
+</style>

+ 0 - 37
pages/work/index.vue

@@ -1,37 +0,0 @@
-<template>
-	<view class="zan">
-		<!-- <view @click="geta">保安</view>
-		<view @click="getb">被访者</view> -->
-		<view @click="getc">来访者</view>
-	</view>
-</template>
-
-<script>
-	export default{
-		data(){
-			return{
-				
-			}
-		},
-		methods:{
-			geta(){
-				this.$tab.navigateTo('/pages/order/staff')
-			},
-			getb(){
-				this.$tab.navigateTo('/pages/order/notice')
-			},
-			getc(){
-				this.$tab.navigateTo('/pages/order/come')
-			},
-		}
-	}
-</script>
-
-<style lang="scss" scoped>
-.zan{
-	display: flex;align-items: center;
-	view{
-		font-size: 32rpx;padding: 0 20rpx;flex: 1;
-	}
-}
-</style>

二进制
static/images/bg.png


二进制
static/images/home/hrbga.png


二进制
static/images/home/hrbgb.png


二进制
static/images/home/hrbgc.png


二进制
static/images/home/hrbgd.png


二进制
static/images/home/hrimga.png


二进制
static/images/home/hrimgb.png


二进制
static/images/home/hrimgc.png


二进制
static/images/home/hrimgd.png


二进制
static/images/home/htopa.png


二进制
static/images/home/htopb.png


二进制
static/images/home/htopc.png


二进制
static/images/home/htopd.png


二进制
static/images/home/notice.png


二进制
static/images/home/tit.png


二进制
static/images/home/topbg.png


二进制
static/images/home/tztip.png


二进制
static/images/home/up.png


二进制
static/images/navbg.png


二进制
static/images/noiconp.png


二进制
static/images/noiconps.png


二进制
static/images/tabbar/home.png


二进制
static/images/tabbar/home_.png


二进制
static/images/tabbar/mine.png


二进制
static/images/tabbar/mine_.png


二进制
static/images/tabbar/notice.png


二进制
static/images/tabbar/notice_.png


二进制
static/images/tabbar/report.png


二进制
static/images/tabbar/report_.png


二进制
static/images/tabbar/road.png


二进制
static/images/tabbar/road_.png


二进制
static/images/tabbar/trends.png


二进制
static/images/tabbar/trends_.png


二进制
static/images/tabbar/work.png


二进制
static/images/tabbar/work_.png


二进制
static/images/weather/weaion.png


二进制
static/images/weather/weaiona.png


二进制
static/images/weather/weaionb.png


二进制
static/images/weather/weaionc.png


二进制
static/images/weather/weaiond.png


二进制
static/images/weather/weaione.png


二进制
static/images/weather/weaionf.png


二进制
static/images/weather/weaiong.png


二进制
static/images/weather/weaionh.png


二进制
static/images/weather/weaioni.png


二进制
static/images/weather/weaionj.png


+ 40 - 109
static/style.css

@@ -3,16 +3,7 @@
 .flex1{flex: 1;}
 .flex0{flex: 0 0 auto;}
 .flex01{flex: 0 1 auto;}
-.flexjc{justify-content: center;}
-.flexcj{display: flex;align-items: center;justify-content: space-between;}
-.flexcja{display: flex;align-items: center !important;justify-content: space-between;}
-.flextj{display: flex;align-items: flex-start;justify-content: space-between;}
 .flexcc{display: flex;align-items: center;justify-content: center;}
-.flext{display: flex;align-items: flex-start;}
-.flexdc{display: flex;flex-direction: column;}
-.flexcdc{display: flex;align-items: center;flex-direction: column;}
-.flexccc{display: flex;align-items: center;flex-direction: column;justify-content: center;}
-
 .clearf::after {
   content: "";
   display: block;
@@ -35,7 +26,7 @@ image{display: block;}
 .overa{overflow: auto;}
 .over{overflow: hidden;white-space: nowrap;text-overflow: ellipsis;}
 .overtwo{word-break: break-all;text-overflow: ellipsis;overflow: hidden;display: -webkit-box;-webkit-line-clamp: 2;-webkit-box-orient: vertical;white-space: normal;}
-.bgbox{background-color: rgba(0, 0, 0, 0.5);position: fixed;left: 0;right: 0;top: 0;bottom: 0;z-index: 2;}
+.bgbox{background-color: rgba(0, 0, 0, 0.5);position: fixed;left: 0;right: 0;top: 0;bottom: 0;z-index: 5;}
 .regbox{width: 100%;min-height: 100vh;background-color: #ffffff;box-sizing: border-box;}
 .regbox .rbtn{width: 100%;height: 98rpx;background: #D32C26;border-radius: 49rpx;text-align: center;line-height: 98rpx;font-size: 32rpx;font-weight: bold;color: #FFFEFE;}
 .regbox .rimg{flex: 0 0 auto;margin-left: 12rpx;display: flex;align-items: center;justify-content: center;width: 40rpx;height: 40rpx;}
@@ -44,110 +35,50 @@ image{display: block;}
 .regbox .apllytxt	text{color: #DF0024;padding-right: 16rpx;font-size: 30rpx;}
 
 .pregbox{padding: 160rpx 56rpx 96rpx;}
-.shax {font-size: 30rpx;color: #666;text-align: center;padding: 20rpx 0;}
-
-.rebtn{height: 98rpx;background: #D32C26;border-radius: 48rpx;font-size: 34rpx;font-weight: 500;
-color: #FFFFFF;flex: 1;display: flex;align-items: center;justify-content: center;}
-.rebtn.btn1{background-color:#C6C6C6;}
-.rebtndel{height: 98rpx;border-radius: 48rpx;font-size: 34rpx;font-weight: 500;
-color: #DF0024;flex: 1;display: flex;align-items: center;justify-content: center;border: 2rpx solid #DF0024;box-sizing: border-box;}
-
-.fbtns{display: flex;align-items: center;position: fixed;left: 0;right: 0;bottom: 0;background-color: #f1f1f1;padding: 36rpx;z-index: 2;}
-
-.delboxs{font-size: 30rpx;color: #FE5A0E;padding:0 20rpx;}
-.zhaddbtn{width:100%;height: 100rpx;background: #D32C26;position: fixed;left: 0;right: 0;bottom: 0;z-index: 1;font-size: 34rpx;font-weight: 500;
-color: #FFFFFF;display: flex;align-items: center;justify-content: center;}
-
-.w13{width: 26rpx;}
-
-.cof{color: #ffffff;}
-.cod{color: #dddddd;}
-.coa{color: #AAAAAA;}
-.co47{color: #D32C26;}
-.co16{color: #161616;}
-.co6{color: #666666;}
-.co33{color: #336AD5;}
-.coed{color: #ED7B4C;}
-.co5c{color: #5CC37D;}
-.coeb{color: #EB5663;}
-.co8e{color: #8E8E8E;}
-.c37{color: #374C82;}
-.cfe{color: #FE6A7F;}
-.c6b{color: #6B7390;}
-.cff2{color: #FF2D4F;}
 
-
-.f13{font-size: 26rpx;}
-.f14{font-size: 28rpx;}
-.f15{font-size: 30rpx;}
-.f16{font-size: 32rpx;}
-.f17{font-size: 34rpx;}
-.f18{font-size: 36rpx;}
-.f19{font-size: 38rpx;}
-.f25{font-size: 50rpx;}
-.f30{font-size: 60rpx;}
-.ml2{margin-left: 4rpx;}
-.ml6{margin-left: 12rpx;}
-.ml10{margin-left: 20rpx;}
-.mr10{margin-right: 20rpx;}
-.mt12{margin-top: 24rpx;}
-.mt15{margin-top: 30rpx;}
-.mt20{margin-top: 40rpx;}
-.mt26{margin-top: 52rpx;}
-.mt30{margin-top: 60rpx;}
-.mt40{margin-top: 80rpx;}
 .mt50{margin-top: 100rpx;}
-.mb4{margin-bottom: 8rpx;}
-.mb5{margin-bottom: 10rpx;}
-.mb6{margin-bottom: 12rpx;}
-.mb8{margin-bottom: 16rpx;}
-.mb10{margin-bottom: 20rpx;}
-.mb12{margin-bottom: 24rpx;}
-.mb15{margin-bottom: 30rpx;}
-.mb16{margin-bottom: 32rpx;}
-.mb18{margin-bottom: 36rpx;}
-.mb20{margin-bottom: 40rpx;}
-.mb24{margin-bottom: 48rpx;}
-.mb28{margin-bottom: 56rpx !important;}
-.mb30{margin-bottom: 60rpx;}
-.mlr12{margin: 0 24rpx;}
 
-.lh18{line-height: 36rpx;}
-.lh20{line-height: 40rpx;}
-.lh24{line-height: 48rpx;}
-
-
-.pl8{padding-left: 16rpx;}
-.pl16{padding-left: 32rpx;}
-.pr8{padding-right: 16rpx;}
-.pt5{padding-top: 10rpx;}
-.pt12{padding-top: 24rpx;}
-.pt20{padding-top: 40rpx;}
-.pt80{padding-top: 160rpx;}
-.pb15{padding-bottom: 30rpx;}
-.pb18{padding-bottom: 36rpx;}
-.plr12{padding: 0 24rpx;}
-.plr13{padding: 0 26rpx;}
-.plr18{padding: 0 36rpx;}
-.plr28{padding: 0 56rpx;}
-.ptb4{padding: 8rpx 0;}
-.ptb6{padding: 12rpx 0;}
-.ptb10{padding: 20rpx 0;}
+.mb24{margin-bottom: 48rpx;}
 .fw{font-weight: bold;}
 .fw5{font-weight: 500;}
 .fwn{font-weight: normal;}
 
+/* 新 */
+.co34{color: #343434;}
+.co0b{color: #00B034 !important;}
+.co1d{color: #1D64E2;}
+.cof0{color: #ff0000;}
 
-/* 新写的 */
-.f12{font-size: 24rpx;}
-.cof1{color: #F1F1F1;}
-.w50{width: 50% !important;}
-.lh18{line-height: 36rpx;}
-.pt8{padding-top: 16rpx;}
-.plr15{padding:0 30rpx;}
-.mt16{margin-top: 32rpx;}
-
-
-.yybox{width: 100%;height: 100vh;box-sizing: border-box;display: flex;flex-direction: column;background-color: #F5F5F5;}
-.yydet{flex: 1;overflow: auto;z-index: 2;position: relative;display: flex;flex-direction: column;}
-/* 新写的end */
+.mb10{margin-bottom: 20rpx !important;}
+.mb14{margin-bottom: 28rpx;}
+.mb16{margin-bottom: 32rpx;}
+.mb18{margin-bottom: 36rpx;}
+.mb20{margin-bottom: 40rpx;}
+.mb22{margin-bottom: 44rpx;}
+.mb25{margin-bottom: 50rpx;}
+.mb27{margin-bottom: 54rpx;}
+.mt2{margin-top: 4rpx;}
+.mt14{margin-top: 28rpx;}
+.mt18{margin-top: 36rpx;}
+.mt22{margin-top: 44rpx;}
+.ml6{margin-left: 12rpx;}
+.mr10{margin-right: 20rpx;}
+.pl12{padding-left: 24rpx;}
+.plr12{padding: 0 24rpx;}
+.pt36{margin-top: 72rpx;}
+.pb8{padding-bottom: 16rpx;}
+.pb50{padding-bottom: 100rpx;}
+.ptb2{padding-bottom: 4rpx;padding-top: 4rpx;}
+.ptb12{padding-bottom: 24rpx;padding-top: 24rpx;}
+.flext{display: flex;align-items: flex-start;}
+.flexcw{display: flex;align-items: center;flex-wrap: wrap;}
+.flexcj{display: flex;align-items: center;justify-content: space-between;}
+.flexccc{display: flex;align-items: center;justify-content: center;flex-direction: column;}
+
+/* 搜索 */
+.search{width: 100%;height: 80rpx;background: #EFEFEF;border-radius: 38rpx;display: flex;align-items: center;justify-content: center;padding: 0 24rpx;margin-bottom: 30rpx;}
+.search image{width: 24rpx;height: 24rpx;margin-right: 26rpx;flex: 0 0 auto;}
+.search input{font-size: 26rpx;font-weight: 500;}
+.fwbtns{position: fixed;left: 0;right: 0;bottom: 0;height: 98rpx;font-weight: 500;
+font-size: 30rpx;
+color: #FFFFFF;background: #1D64E2;text-align: center;line-height: 98rpx;}

+ 1 - 1
uni.scss

@@ -2,7 +2,7 @@
  * uni-app内置的常用样式变量
  */
 //公共的颜色
-$com-cd3: #0391FD;
+$com-cd3: #1D64E2;
 /* 行为相关颜色 */
 $uni-color-primary: #007aff;
 $uni-color-success: #4cd964;

+ 108 - 0
uni_modules/lsj-upload/changelog.md

@@ -0,0 +1,108 @@
+## 2.2.6(2023-02-09)
+修复多个文件同时选择时返回多次change回调的问题
+## 2.2.5(2022-12-27)
+1.修复多选文件时未能正常校验数量的问题;
+2.app端与H5端支持单选或多选文件,通过count数量控制,超过1开启多选。
+## 2.2.4(2022-12-27)
+1.修复多选文件时未能正常校验数量的问题;
+2.app端修复多选只取到第一个文件的问题。
+## 2.2.3(2022-12-06)
+修复手动调用show()导致count失效的问题
+## 2.2.2(2022-12-01)
+Vue3自行修改兼容
+## 2.2.1(2022-10-19)
+修复childId警告提示
+## 2.2.0(2022-10-10)
+更新app端webview窗口参数clidId,默认值添加时间戳保证唯一性
+## 2.1.9(2022-07-13)
+[修复] app端选择文件后初始化设置的文件列表被清空问题
+## 2.1.8(2022-07-13)
+[新增] ref方法初始化文件列表,用于已提交后再次编辑时需带入已上传文件:setFiles(files),可传入数组或Map对象,传入格式请与组件选择返回格式保持一致,且name为必须属性。
+## 2.1.7(2022-07-12)
+修复ios端偶现创建webview初始化参数未生效的问题
+## 2.1.6(2022-07-11)
+[修复]:修复上个版本更新导致nvue窗口组件不能选择文件的问题;
+[新增]:
+1.应群友建议(填写禁止格式太多)格式限制formats由原来填写禁止选择的格式改为填写允许被选择的格式;
+2.应群友建议(增加上传结束回调事件),上传结束回调事件@uploadEnd
+3.如能帮到你请留下你的免费好评,组件使用过程中有问题可以加QQ群交流,至于Map对象怎么使用这类前端基础问题请自行百度
+## 2.1.5(2022-07-01)
+app端组件销毁时添加自动销毁webview功能,避免v-if销毁组件的情况控件还能被点击的问题
+## 2.1.4(2022-07-01)
+修复小程序端回显问题
+## 2.1.3(2022-06-30)
+回调事件返回参数新增path字段(文件临时地址),用于回显
+## 2.1.2(2022-06-16)
+修复APP端Tabbar窗口无法选择文件的问题
+## 2.1.1(2022-06-16)
+优化:
+1.组件优化为允许在v-if中使用;
+2.允许option直接在data赋值,不再强制在onRead中初始化;
+## 2.1.0(2022-06-13)
+h5 pc端更改为单次可多选
+## 2.0.9(2022-06-10)
+更新演示内容,部分同学不知道怎么获取服务端返回的数据
+## 2.0.8(2022-06-09)
+优化动态更新上传参数函数,具体查看下方说明:动态更新参数演示
+## 2.0.7(2022-06-07)
+新增wxFileType属性,用于小程序端选择附件时可选文件类型
+## 2.0.6(2022-06-07)
+修复小程序端真机选择文件提示失败的问题
+## 2.0.5(2022-06-02)
+优化小程序端调用hide()后未阻止触发文件选择问题
+## 2.0.4(2022-06-01)
+优化APP端选择器初始定位
+## 2.0.3(2022-05-31)
+修复nvue窗口选择文件报错问题 
+## 2.0.2(2022-05-20)
+修复ios端opiton设置过早未传入webview导致不自动上传问题
+## 2.0.1(2022-05-19)
+修复APP端子窗口点击选择文件不响应问题
+## 2.0.0(2022-05-18)
+此次组件更新至2.0版本,与1.0版本使用上略有差异,已使用1.0的同学请自行斟酌是否需要升级!
+部分差异:
+一、 2.0新增异步触发上传功能;
+二、2.0新增文件批量上传功能;
+三、2.0优化option,剔除属性,只保留上传接口所需字段,且允许异步更改option的值;
+四、组件增加size(文件大小限制)、count(文件个数限制)、formats(文件后缀限制)、accept(文件类型限制)、instantly(是否立即自动上传)、debug(日志打印)等属性;
+五、回调事件取消input事件、callback事件,新增change事件和progress事件;
+六、ref事件新增upload事件、clear事件;
+七、优化组件代码,show和hide函数改为显示隐藏,不再重复开关webview;
+
+## 1.2.3(2022-03-22)
+修复Demo里传入待完善功能[手动上传属性manual=true]导致不自动上传的问题,手动提交上传待下个版本更新
+## 1.2.2(2022-02-21)
+修复上版本APP优化导致H5和小程序端不自动初始化的问题,此次更新仅修复此问题。异步提交功能下个版本更新~
+## 1.2.1(2022-01-25)
+QQ1群已满,已开放2群:469580165
+## 1.2.0(2021-12-09)
+优化APP端页面中DOM重排后每次需要重新定位的问题
+## 1.1.1(2021-12-09)
+优化,与上版本使用方式有改变,请检查后确认是否需要更新,create更名为show,  close更名为hide,取消初始化时手动create, 传参方式改为props=>option
+## 1.1.0(2021-12-09)
+新增refresh方法,用于DOM发生重排时重新定位控件(APP端)
+## 1.0.9(2021-07-15)
+修复上传进度未同步渲染,直接返回100%的BUG
+## 1.0.8(2021-07-12)
+修复H5端传入height和width未生效的bug
+## 1.0.7(2021-07-07)
+修复h5和小程序端上传完成callback未返回fileName字段问题
+## 1.0.6(2021-07-07)
+修复h5端提示信息debug
+## 1.0.5(2021-06-29)
+感谢小伙伴找出bug,上传成功回调success未置为true,已修复
+## 1.0.4(2021-06-28)
+新增兼容APP,H5,小程序手动关闭控件,关闭后不再弹出文件选择框,需要重新create再次开启
+## 1.0.3(2021-06-28)
+close增加条件编译,除app端外不需要close
+## 1.0.2(2021-06-28)
+1.修复页面滚动位置后再create控件导致控件位置不正确的问题;
+2.修复nvue无法create控件;
+3.示例项目新增nvue使用案例;
+## 1.0.1(2021-06-28)
+因为有的朋友不清楚app端切换tab时应该怎么处理webview,现重新上传一版示例项目,需要做tab切换的朋友可以导入示例项目查看
+## 1.0.0(2021-06-25)
+此插件为l-file插件中上传功能改版,更新内容为:
+1. 按钮内嵌入页面,不再强制固定底部,可跟随页面滚动
+2.无需再单独弹框点击上传,减去中间层
+3.通过slot自定义按钮样式

+ 396 - 0
uni_modules/lsj-upload/components/lsj-upload/LsjFile.js

@@ -0,0 +1,396 @@
+export class LsjFile {
+	constructor(data) {
+		this.dom = null;
+		// files.type = waiting(等待上传)|| loading(上传中)|| success(成功) || fail(失败)
+		this.files = new Map();
+		this.debug = data.debug || false;
+		this.id = data.id;
+		this.width = data.width;
+		this.height = data.height;
+		this.option = data.option;
+		this.instantly = data.instantly;
+		this.prohibited = data.prohibited;
+		this.onchange = data.onchange;
+		this.onprogress = data.onprogress;
+		this.uploadHandle = this._uploadHandle;
+		// #ifdef MP-WEIXIN
+		this.uploadHandle = this._uploadHandleWX;
+		// #endif
+	}
+	
+	
+	/**
+	 * 创建File节点
+	 * @param {string}path webview地址
+	 */
+	create(path) {
+		if (!this.dom) {
+			// #ifdef H5
+				let dom = document.createElement('input');
+				dom.type = 'file'
+				dom.value = ''
+				dom.style.height = this.height
+				dom.style.width = this.width
+				dom.style.position = 'absolute'
+				dom.style.top = 0
+				dom.style.left = 0
+				dom.style.right = 0
+				dom.style.bottom = 0
+				dom.style.opacity = 0
+				dom.style.zIndex = 2
+				dom.accept = this.prohibited.accept;
+				if (this.prohibited.count > 1) {
+				dom.multiple = 'multiple';
+				}
+				dom.onchange = event => {
+					for (let file of event.target.files) {
+						if (this.files.size >= this.prohibited.count) {
+							this.toast(`只允许上传${this.prohibited.count}个文件`);
+							this.dom.value = '';
+							break;
+						}
+						this.addFile(file);
+					}
+					
+					this._uploadAfter();
+					
+					this.dom.value = '';
+				};
+				this.dom = dom;
+			// #endif
+		
+			// #ifdef APP-PLUS
+				let styles = {
+					top: '-200px',
+					left: 0,
+					width: '1px',
+					height: '200px',
+					background: 'transparent' 
+				};
+				let extras = {
+					debug: this.debug,
+					instantly: this.instantly,
+					prohibited: this.prohibited,
+				}
+				this.dom = plus.webview.create(path, this.id, styles,extras);
+				this.setData(this.option); 
+				this._overrideUrlLoading();
+			// #endif
+			return this.dom;
+		}
+	}
+	
+	
+	/**
+	 * 设置上传参数
+	 * @param {object|string}name 上传参数,支持a.b 和 a[b]
+	 */
+	setData() {
+		let [name,value = ''] = arguments;
+		if (typeof name === 'object') {
+			Object.assign(this.option,name);
+		}
+		else {
+			this._setValue(this.option,name,value);
+		}
+		
+		this.debug&&console.log(JSON.stringify(this.option));
+		
+		// #ifdef APP-PLUS
+			this.dom.evalJS(`vm.setData('${JSON.stringify(this.option)}')`);
+		// #endif
+	}
+	
+	/**
+	 * 上传
+	 * @param {string}name 文件名称
+	 */
+	async upload(name='') {
+		if (!this.option.url) {
+			throw Error('未设置上传地址');
+		}
+		
+		// #ifndef APP-PLUS
+			if (name && this.files.has(name)) {
+				await this.uploadHandle(this.files.get(name));
+			}
+			else {
+				for (let item of this.files.values()) {
+					if (item.type === 'waiting' || item.type === 'fail') {
+						await this.uploadHandle(item);
+					}
+				}
+			}
+		// #endif
+		
+		// #ifdef APP-PLUS
+			this.dom&&this.dom.evalJS(`vm.upload('${name}')`);
+		// #endif
+	}
+	
+	// 选择文件change
+	addFile(file,isCallChange) {
+		
+		let name = file.name;
+		this.debug&&console.log('文件名称',name,'大小',file.size);
+		
+		if (file) {
+			// 限制文件格式
+			let path = '';
+			let suffix = name.substring(name.lastIndexOf(".")+1).toLowerCase();
+			let formats = this.prohibited.formats.toLowerCase();
+			// #ifndef MP-WEIXIN
+				path = URL.createObjectURL(file);
+			// #endif
+			// #ifdef MP-WEIXIN
+				path = file.path;
+			// #endif
+			if (formats&&!formats.includes(suffix)) {
+				this.toast(`不支持上传${suffix.toUpperCase()}格式文件`);
+				return false;
+			}
+			// 限制文件大小
+			if (file.size > 1024 * 1024 * Math.abs(this.prohibited.size)) {
+				this.toast(`附件大小请勿超过${this.prohibited.size}M`)
+				return false;
+			}
+			this.files.set(file.name,{file,path,name: file.name,size: file.size,progress: 0,type: 'waiting'});
+			return true;
+		}
+	}
+	
+	/**
+	 * 移除文件
+	 * @param {string}name 不传name默认移除所有文件,传入name移除指定name的文件
+	 */
+	clear(name='') {
+		// #ifdef APP-PLUS
+		this.dom&&this.dom.evalJS(`vm.clear('${name}')`);
+		// #endif
+		
+		if (!name) {
+			this.files.clear();
+		}
+		else {
+			this.files.delete(name); 
+		}
+		return this.onchange(this.files);
+	}
+	
+	/**
+	 * 提示框
+	 * @param {string}msg 轻提示内容
+	 */
+	toast(msg) {
+		uni.showToast({
+			title: msg,
+			icon: 'none'
+		});
+	}
+	
+	/**
+	 * 微信小程序选择文件
+	 * @param {number}count 可选择文件数量
+	 */
+	chooseMessageFile(type,count) {
+		wx.chooseMessageFile({
+			count: count,
+			type: type,
+			success: ({ tempFiles }) => {
+				for (let file of tempFiles) {
+					this.addFile(file);
+				}
+				this._uploadAfter();
+			},
+			fail: () => {
+				this.toast(`打开失败`);
+			}
+		})
+	}
+	
+	_copyObject(obj) {
+		if (typeof obj !== "undefined") {
+			return JSON.parse(JSON.stringify(obj));
+		} else {
+			return obj;
+		}
+	}
+	
+	/**
+	 * 自动根据字符串路径设置对象中的值 支持.和[]
+	 * @param	{Object} dataObj 数据源
+	 * @param	{String} name 支持a.b 和 a[b]
+	 * @param	{String} value 值
+	 * setValue(dataObj, name, value);
+	 */
+	_setValue(dataObj, name, value) {
+		// 通过正则表达式  查找路径数据
+		let dataValue;
+		if (typeof value === "object") {
+			dataValue = this._copyObject(value);
+		} else {
+			dataValue = value;
+		}
+		let regExp = new RegExp("([\\w$]+)|\\[(:\\d)\\]", "g");
+		const patten = name.match(regExp);
+		// 遍历路径  逐级查找  最后一级用于直接赋值
+		for (let i = 0; i < patten.length - 1; i++) {
+			let keyName = patten[i];
+			if (typeof dataObj[keyName] !== "object") dataObj[keyName] = {};
+			dataObj = dataObj[keyName];
+		}
+		// 最后一级
+		dataObj[patten[patten.length - 1]] = dataValue;
+		this.debug&&console.log('参数更新后',JSON.stringify(this.option));
+	}
+	
+	_uploadAfter() {
+		this.onchange(this.files);
+		this.instantly&&this.upload();
+	}
+	
+	_overrideUrlLoading() {
+		this.dom.overrideUrlLoading({ mode: 'reject' }, e => {
+			let {retype,item,files,end} = this._getRequest(
+				e.url
+			);
+			let _this = this;
+			switch (retype) {
+				case 'updateOption':
+					this.dom.evalJS(`vm.setData('${JSON.stringify(_this.option)}')`);
+					break
+				case 'change':
+					try {
+						_this.files = new Map([..._this.files,...JSON.parse(unescape(files))]);
+					} catch (e) {
+						return console.error('出错了,请检查代码')
+					}
+					_this.onchange(_this.files);
+					break
+				case 'progress':
+					try {
+						item = JSON.parse(unescape(item));
+					} catch (e) {
+						return console.error('出错了,请检查代码')
+					}
+					_this._changeFilesItem(item,end);
+					break
+				default:
+					break
+			}
+		})
+	}
+	
+	_getRequest(url) {
+		let theRequest = new Object()
+		let index = url.indexOf('?')
+		if (index != -1) {
+			let str = url.substring(index + 1)
+			let strs = str.split('&')
+			for (let i = 0; i < strs.length; i++) {
+				theRequest[strs[i].split('=')[0]] = unescape(strs[i].split('=')[1])
+			}
+		}
+		return theRequest
+	}
+	
+	_changeFilesItem(item,end=false) {
+		this.debug&&console.log('onprogress',JSON.stringify(item));
+		this.onprogress(item,end);
+		this.files.set(item.name,item);
+	}
+	
+	_uploadHandle(item) {
+		item.type = 'loading';
+		delete item.responseText;
+		return new Promise((resolve,reject)=>{
+			this.debug&&console.log('option',JSON.stringify(this.option));
+			let {url,name,method='POST',header,formData} = this.option;
+			let form = new FormData();
+			for (let keys in formData) {
+				form.append(keys, formData[keys])
+			}
+			form.append(name, item.file);
+			let xmlRequest = new XMLHttpRequest();
+			xmlRequest.open(method, url, true);
+			for (let keys in header) {
+				xmlRequest.setRequestHeader(keys, header[keys])
+			}
+			
+			xmlRequest.upload.addEventListener(
+				'progress',
+				event => {
+					if (event.lengthComputable) {
+						let progress = Math.ceil((event.loaded * 100) / event.total)
+						if (progress <= 100) {
+							item.progress = progress;
+							this._changeFilesItem(item);
+						}
+					}
+				},
+				false
+			);
+			
+			xmlRequest.ontimeout = () => {
+				console.error('请求超时')
+				item.type = 'fail';
+				this._changeFilesItem(item,true);
+				return resolve(false);
+			}
+			
+			xmlRequest.onreadystatechange = ev => {
+				if (xmlRequest.readyState == 4) {
+					if (xmlRequest.status == 200) {
+						this.debug&&console.log('上传完成:' + xmlRequest.responseText)
+						item['responseText'] = xmlRequest.responseText;
+						item.type = 'success';
+						this._changeFilesItem(item,true);
+						return resolve(true);
+					} else if (xmlRequest.status == 0) {
+						console.error('status = 0 :请检查请求头Content-Type与服务端是否匹配,服务端已正确开启跨域,并且nginx未拦截阻止请求')
+					}
+					console.error('--ERROR--:status = ' + xmlRequest.status)
+					item.type = 'fail';
+					this._changeFilesItem(item,true);
+					return resolve(false);
+				}
+			}
+			xmlRequest.send(form)
+		});
+	}
+	
+	_uploadHandleWX(item) {
+		item.type = 'loading';
+		delete item.responseText;
+		return new Promise((resolve,reject)=>{
+			this.debug&&console.log('option',JSON.stringify(this.option));
+			let form = {filePath: item.file.path,...this.option };
+			form['fail'] = ({ errMsg = '' }) => {
+				console.error('--ERROR--:' + errMsg)
+				item.type = 'fail';
+				this._changeFilesItem(item,true);
+				return resolve(false);
+			}
+			form['success'] = res => {
+				if (res.statusCode == 200) {
+					this.debug&&console.log('上传完成,微信端返回不一定是字符串,根据接口返回格式判断是否需要JSON.parse:' + res.data)
+					item['responseText'] = res.data;
+					item.type = 'success';
+					this._changeFilesItem(item,true);
+					return resolve(true);
+				}
+				item.type = 'fail';
+				this._changeFilesItem(item,true);
+				return resolve(false);
+			}
+			
+			let xmlRequest = uni.uploadFile(form);
+			xmlRequest.onProgressUpdate(({ progress = 0 }) => {
+				if (progress <= 100) {
+					item.progress = progress;
+					this._changeFilesItem(item);
+				}
+			})
+		});
+	}
+}

+ 316 - 0
uni_modules/lsj-upload/components/lsj-upload/lsj-upload.vue

@@ -0,0 +1,316 @@
+<template>
+	<view class="lsj-file" :style="[getStyles]">
+		<view ref="lsj" class="hFile" :style="[getStyles]" @click="onClick">
+			<slot><view class="defview" :style="[getStyles]">附件上传</view></slot>
+		</view>
+	</view>
+</template>
+
+<script>
+// 查看文档:https://ext.dcloud.net.cn/plugin?id=5459
+import {LsjFile} from './LsjFile.js' 
+export default {
+	name: 'Lsj-upload',
+	props: {
+		// 打印日志
+		debug: {type: Boolean,default: false},
+		// 自动上传
+		instantly: {type: Boolean,default: false},
+		// 上传接口参数设置
+		option: {type: Object,default: ()=>{}},
+		// 文件大小上限
+		size: { type: Number, default: 10 },
+		// 文件选择个数上限,超出后不触发点击
+		count: { type: Number, default: 9 },
+		// 允许上传的文件格式(多个以逗号隔开)
+		formats: { type: String, default:''},
+		// input file选择限制
+		accept: {type: String,default: ''},
+		// 微信选择文件类型 
+		//all=从所有文件选择,
+		//video=只能选择视频文件,
+		//image=只能选择图片文件,
+		//file=可以选择除了图片和视频之外的其它的文件
+		wxFileType: { type: String, default: 'all' },
+		// webviewID需唯一,不同窗口也不要同Id
+		childId: { type: String, default: 'lsjUpload'  },
+		// 文件选择触发面宽度
+		width: { type: String, default: '100%' },
+		// 文件选择触发面高度
+		height: { type: String, default: '80rpx' },
+		
+		// top,left,bottom,right仅position=absolute时才需要传入
+		top: { type: [String, Number], default: '' },
+		left: { type: [String, Number], default: '' },
+		bottom: { type: [String, Number], default: '' },
+		right: { type: [String, Number], default: '' },
+		xmtype:{ type: [String, Number], default: '' },
+		// nvue不支持跟随窗口滚动
+		position: { 
+			type: String,
+			// #ifdef APP-NVUE
+			 default: 'absolute',
+			// #endif
+			// #ifndef APP-NVUE
+			default: 'static',
+			// #endif
+		},
+	},
+	data() {
+		return {
+			
+		}
+	},
+	watch: {
+		option(v) {
+			// #ifdef APP-PLUS
+			this.lsjFile&&this.show();
+			// #endif
+		}
+	},
+	updated() {
+		// #ifdef APP-PLUS
+			if (this.isShow) {
+				this.lsjFile&&this.show();
+			}
+		// #endif
+	},
+	computed: {
+		getStyles() {
+			let styles = {
+				width: this.width,
+				height: this.height
+			}
+			if (this.position == 'absolute') {
+				styles['top'] = this.top
+				styles['bottom'] = this.bottom
+				styles['left'] = this.left
+				styles['right'] = this.right
+				styles['position'] = 'fixed'
+			}
+
+			return styles
+		}
+	},
+	mounted() {
+		this._size = 0;
+		let WEBID = this.childId + new Date().getTime();
+		this.lsjFile = new LsjFile({
+			id: WEBID,
+			debug: this.debug,
+			width: this.width,
+			height: this.height,
+			option: this.option,
+			instantly: this.instantly,
+			// 限制条件
+			prohibited: {
+				// 大小
+				size: this.size,
+				// 允许上传的格式
+				formats: this.formats,
+				// 限制选择的格式
+				accept: this.accept,
+				count: this.count
+			},
+			onchange: this.onchange,
+			onprogress: this.onprogress,
+		});
+		this.create();
+		
+	},
+	beforeDestroy() {
+		// #ifdef APP-PLUS
+		this.lsjFile.dom.close();
+		// #endif
+	},
+	methods: {
+		setFiles(array) {
+			if (array instanceof Map) {
+				for (let [key, item] of array) {
+					item['progress'] = 100;
+					item['type'] = 'success';
+					this.lsjFile.files.set(key,item);
+				}
+			}
+			else if (Array.isArray(array)) {
+				array.forEach(item=>{
+					if (item.name) { 
+						item['progress'] = 100;
+						item['type'] = 'success';
+						this.lsjFile.files.set(item.name,item);
+					}
+				});
+			}
+			this.onchange(this.lsjFile.files);
+		},
+		setData() {
+			this.lsjFile&&this.lsjFile.setData(...arguments);
+		},
+		getDomStyles(callback) {
+			// #ifndef APP-NVUE
+			let view = uni
+				.createSelectorQuery()
+				.in(this)
+				.select('.lsj-file')
+			view.fields(
+				{
+					size: true,
+					rect: true
+				},
+				({ height, width, top, left, right, bottom }) => {
+					uni.createSelectorQuery()
+					.selectViewport()
+					.scrollOffset(({ scrollTop }) => {
+						return callback({
+							top: parseInt(top) + parseInt(scrollTop) + 'px',
+							left: parseInt(left) + 'px',
+							width: parseInt(width) + 'px',
+							height: parseInt(height) + 'px'
+						})
+					})
+					.exec()
+				}
+			).exec()
+			// #endif
+			// #ifdef APP-NVUE
+			const dom = weex.requireModule('dom')
+			dom.getComponentRect(this.$refs.lsj, ({ size: { height, width, top, left, right, bottom } }) => {
+				return callback({
+					top: parseInt(top) + 'px',
+					left: parseInt(left) + 'px',
+					width: parseInt(width) + 'px',
+					height: parseInt(height) + 'px',
+					right: parseInt(right) + 'px',
+					bottom: parseInt(bottom) + 'px'
+				})
+			})
+			// #endif
+		},
+		show() {
+			if (this._size && (this._size >= this.count)) {
+				return;
+			}
+			this.isShow = true;
+			// #ifdef APP-PLUS
+			this.lsjFile&&this.getDomStyles(styles => {
+				this.lsjFile.dom.setStyle(styles)
+			});
+			// #endif
+			// #ifdef H5
+			this.lsjFile.dom.style.display = 'inline'
+			// #endif
+		},
+		hide() {
+			this.isShow = false;
+			// #ifdef APP-PLUS
+			this.lsjFile&&this.lsjFile.dom.setStyle({
+				top: '-100px',
+				left:'0px',
+				width: '1px',
+				height: '100px',
+			});
+			// #endif
+			// #ifdef H5
+			this.lsjFile.dom.style.display = 'none'
+			// #endif
+		},
+		/**
+		 * 手动提交上传
+		 * @param {string}name 文件名称,不传则上传所有type等于waiting和fail的文件
+		 */
+		upload(name) {
+			this.lsjFile&&this.lsjFile.upload(name);
+		},
+		/**
+		 * @returns {Map} 已选择的文件Map集
+		 */
+		onchange(files) {
+			this.$emit('getFile',this.xmtype)
+			this.$emit('change',files);
+			this._size = files.size;
+			return files.size >= this.count ? this.hide() : this.show();
+		},
+		/**
+		 * @returns {object} 当前上传中的对象
+		 */
+		onprogress(item,end=false) {
+			this.$emit('progress',item);
+			if (end) {
+				setTimeout(()=>{
+					this.$emit('uploadEnd',item);
+				},0);
+			}
+		},
+		/**
+		 * 移除组件内缓存的某条数据
+		 * @param {string}name 文件名称,不指定默认清除所有文件
+		 */
+		clear(name) {
+			this.lsjFile.clear(name);
+		},
+		// 创建选择器
+		create() {
+			// 若iOS端服务端处理不了跨域就将hybrid目录内的html放到服务端去,并将此处path改成服务器上的地址
+			let path = '/uni_modules/lsj-upload/hybrid/html/uploadFile.html';
+			let dom = this.lsjFile.create(path);
+			// #ifdef H5
+			this.$refs.lsj.$el.appendChild(dom);
+			// #endif
+			// #ifndef APP-PLUS
+			this.show();
+			// #endif
+			// #ifdef APP-PLUS
+			dom.setStyle({position: this.position});
+			dom.loadURL(path);
+			setTimeout(()=>{
+				// #ifdef APP-NVUE
+				plus.webview.currentWebview().append(dom);
+				// #endif
+				// #ifndef APP-NVUE
+				this.$root.$scope.$getAppWebview().append(dom);
+				// #endif
+				this.show();
+			},300)
+			// #endif
+		},
+		// 点击选择附件
+		onClick() {
+			if (this._size >= this.count) {
+				this.toast(`只允许上传${this.count}个文件`);
+				return;
+			}
+			
+			// #ifdef MP-WEIXIN
+			if (!this.isShow) {return;}
+			let count = this.count - this._size;
+			this.lsjFile.chooseMessageFile(this.wxFileType,count);
+			// #endif
+		},
+		toast(msg) {
+			uni.showToast({
+				title: msg,
+				icon: 'none'
+			});
+		}
+	}
+}
+</script>
+
+<style scoped>
+.lsj-file {
+	display: inline-block;
+}
+.defview {
+	background-color: #007aff;
+	color: #fff;
+	border-radius: 10rpx;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	font-size: 28rpx;
+}
+.hFile {
+	position: relative;
+	overflow: hidden;
+}
+</style>

文件差异内容过多而无法显示
+ 5 - 0
uni_modules/lsj-upload/hybrid/html/js/vue.min.js


+ 191 - 0
uni_modules/lsj-upload/hybrid/html/uploadFile.html

@@ -0,0 +1,191 @@
+<!DOCTYPE html>
+<html lang="zh-cn">
+
+	<head>
+		<meta charset="UTF-8">
+		<title class="title">[文件管理器]</title>
+		<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
+		<style type="text/css">
+			.content {background: transparent;}
+			.btn {position: relative;top: 0;left: 0;bottom: 0;right: 0;}
+			.btn .file {position: fixed;z-index: 93;left: 0;right: 0;top: 0;bottom: 0;width: 100%;opacity: 0;}
+		</style>
+	</head>
+
+	<body>
+		
+		<div id="content" class="content">
+			<div class="btn">
+				<input :multiple="multiple" @change="onChange" :accept="accept" ref="file" class="file" type="file" />
+			</div>
+		</div>
+		
+		<script type="text/javascript" src="js/vue.min.js"></script>
+		<script type="text/javascript">
+			let _this;
+			var vm = new Vue({
+				el: '#content',
+				data: {
+					accept: '',
+					multiple: true,
+				},
+				mounted() {
+					console.log('加载webview');
+					_this = this;
+					this.files = new Map();
+					document.addEventListener('plusready', (e)=>{
+					let {debug,instantly,prohibited} = plus.webview.currentWebview();
+					this.debug = debug;
+					this.instantly = instantly;
+					this.prohibited = prohibited;
+					this.accept = prohibited.accept; 
+					this.multiple = prohibited.count > 1;
+					location.href = 'callback?retype=updateOption';
+					}, false);
+				},
+				methods: {
+					toast(msg) {
+						plus.nativeUI.toast(msg);
+					},
+					clear(name) {
+						if (!name) {
+							this.files.clear();
+							return;
+						}
+						this.files.delete(name);
+					},
+					setData(option='{}') {
+						this.debug&&console.log('更新参数:'+option);
+						try{
+							_this.option = JSON.parse(option);
+						}catch(e){
+							console.error('参数设置错误')
+						}
+					},
+					async upload(name=''){
+						if (name && this.files.has(name)) {
+							await this.createUpload(this.files.get(name));
+						}
+						else {
+							for (let item of this.files.values()) {
+								if (item.type === 'waiting' || item.type === 'fail') {
+									await this.createUpload(item);
+								}
+							}
+						}
+					},
+					onChange(e) {
+						let fileDom = this.$refs.file;
+						for (let file of fileDom.files) {
+							if (this.files.size >= this.prohibited.count) {
+								this.toast(`只允许上传${this.prohibited.count}个文件`);
+								fileDom.value = '';
+								break;
+							}
+							this.addFile(file);
+						}
+						this.uploadAfter();
+						fileDom.value = '';
+					},
+					addFile(file) {
+						if (file) {
+							let name = file.name;
+							this.debug&&console.log('文件名称',name,'大小',file.size);
+							// 限制文件格式
+							let suffix = name.substring(name.lastIndexOf(".")+1).toLowerCase();
+							let formats = this.prohibited.formats.toLowerCase();
+							if (formats&&!formats.includes(suffix)) {
+								this.toast(`不支持上传${suffix.toUpperCase()}格式文件`);
+								return;
+							}
+							// 限制文件大小
+							if (file.size > 1024 * 1024 * Math.abs(this.prohibited.size)) {
+								this.toast(`附件大小请勿超过${this.prohibited.size}M`)
+								return;
+							}
+							let path = URL.createObjectURL(file);
+							this.files.set(file.name,{file,path,name: file.name,size: file.size,progress: 0,type: 'waiting'});
+						}
+					},
+					/**
+					 * @returns {Map} 已选择的文件Map集
+					 */
+					callChange() {
+						location.href = 'callback?retype=change&files=' + escape(JSON.stringify([...this.files]));
+					},
+					/**
+					 * @returns {object} 正在处理的当前对象
+					 */
+					changeFilesItem(item,end='') {
+						this.files.set(item.name,item);
+						location.href = 'callback?retype=progress&end='+ end +'&item=' + escape(JSON.stringify(item));
+					},
+					uploadAfter() {
+						this.callChange();
+						this.instantly&&this.upload();
+					},
+					createUpload(item) {
+						this.debug&&console.log('准备上传,option=:'+JSON.stringify(this.option));
+						item.type = 'loading';
+						delete item.responseText;
+						return new Promise((resolve,reject)=>{
+							let {url,name,method='POST',header={},formData={}} = this.option;
+							let form = new FormData();
+							for (let keys in formData) {
+								form.append(keys, formData[keys])
+							}
+							form.append(name, item.file);
+							let xmlRequest = new XMLHttpRequest();
+							xmlRequest.open(method, url, true);
+							for (let keys in header) {
+								xmlRequest.setRequestHeader(keys, header[keys])
+							}
+							xmlRequest.upload.addEventListener(
+								'progress',
+								event => {
+									if (event.lengthComputable) {
+										let progress = Math.ceil((event.loaded * 100) / event.total)
+										if (progress <= 100) {
+											item.progress = progress;
+											this.changeFilesItem(item);
+										}
+									}
+								},
+								false
+							);
+							
+							xmlRequest.ontimeout = () => {
+								console.error('请求超时')
+								item.type = 'fail';
+								this.changeFilesItem(item,true);
+								return resolve(false);
+							}
+							
+							xmlRequest.onreadystatechange = ev => {
+								if (xmlRequest.readyState == 4) {
+									if (xmlRequest.status == 200) {
+										this.debug && console.log('上传完成:' + xmlRequest.responseText)
+										item['responseText'] = xmlRequest.responseText;
+										item.type = 'success';
+										this.changeFilesItem(item,true);
+										return resolve(true);
+									} else if (xmlRequest.status == 0) {
+										console.error('status = 0 :请检查请求头Content-Type与服务端是否匹配,服务端已正确开启跨域,并且nginx未拦截阻止请求')
+									}
+									console.error('--ERROR--:status = ' + xmlRequest.status) 
+									item.type = 'fail';
+									this.changeFilesItem(item,true);
+									return resolve(false);
+								}
+							}
+							xmlRequest.send(form)
+						});
+						
+					}
+				}
+			});
+			
+		</script>
+	</body>
+
+</html>

+ 79 - 0
uni_modules/lsj-upload/package.json

@@ -0,0 +1,79 @@
+{
+    "id": "lsj-upload",
+    "displayName": "全文件上传选择非原生2.0版",
+    "version": "2.2.6",
+    "description": "文件选择上传-支持APP-H5网页-微信小程序",
+    "keywords": [
+        "附件",
+        "file",
+        "upload",
+        "上传",
+        "文件管理器"
+    ],
+    "repository": "",
+    "engines": {
+        "HBuilderX": "^3.3.7"
+    },
+    "dcloudext": {
+        "sale": {
+            "regular": {
+                "price": "0.00"
+            },
+            "sourcecode": {
+                "price": "0.00"
+            }
+        },
+        "contact": {
+            "qq": ""
+        },
+        "declaration": {
+            "ads": "无",
+            "data": "无",
+            "permissions": "无"
+        },
+        "npmurl": "",
+        "type": "component-vue"
+    },
+    "uni_modules": {
+        "platforms": {
+            "cloud": {
+                "tcb": "y",
+                "aliyun": "y"
+            },
+            "client": {
+                "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",
+                    "阿里": "u",
+                    "百度": "u",
+                    "字节跳动": "u",
+                    "QQ": "u"
+                },
+                "快应用": {
+                    "华为": "y",
+                    "联盟": "y"
+                },
+                "Vue": {
+                    "vue2": "y",
+                    "vue3": "y"
+                }
+            }
+        }
+    }
+}

+ 352 - 0
uni_modules/lsj-upload/readme.md

@@ -0,0 +1,352 @@
+# lsj-upload
+
+### 插件地址:https://ext.dcloud.net.cn/plugin?id=5459
+
+### 不清楚使用方式可点击右侧导入示例项目运行完整示例
+### 此次更新2.0与1.0使用方式略有差异,已使用1.0的同学自行斟酌是否更新到2.0版本!!!
+
+使用插件有任何问题欢迎加入QQ讨论群:
+- 群1:701468256(已满)
+- 群2:469580165(已满)
+- 群3:667530868
+
+若能帮到你请高抬贵手点亮5颗星~
+------
+## 重要提示
+### 组件是窗口级滚动,不要在scroll-view内使用!!
+### 组件是窗口级滚动,不要在scroll-view内使用!!
+### 组件是窗口级滚动,不要在scroll-view内使用!!
+
+### 控件的height高度应与slot自定义内容高度保持一致
+### nvue窗口只能使用固定模式position=absolute
+### show() 当DOM重排后在this.$nextTick内调用show(),控件定位会更加准确
+### hide() APP端webview层级比view高,如不希望触发点击时,应调用hide隐藏控件,反之调用show
+### 若iOS端跨域服务端同学实在配置不好,可把hybrid下html目录放到服务器去,同源则不存在跨域问题。
+### 小程序端因hybrid不能使用本地HTML,所以插件提供的是从微信消息列表拉取文件并选择,请知悉。
+### file对象不是object对象,也不能转json字符串,如果你打印file那就是{},可以打印file.name和file.size。
+### 返回的path是个blob类型,仅供用于文件回显,插件已内置好上传函数,调用上传会自动提交待上传文件,若非要自己拿path去搞上传那你自己处理。
+------
+
+## 使用说明
+| 属性		| 是否必填	|  值类型	| 默认值	| 说明			|
+| --------- | -------- 	| -----: 	| --: 	| :------------:|
+| width		|	否 		| String	|100%	| 容器宽度		|
+| height	|	是 		| String	|80rpx	| 容器高度		|
+| debug		|	否 		| Boolean	|false	| 打印调试日志	|
+| option	|	是 		| Object	|-		| [文件上传接口相关参数](#p1)|
+| instantly	|	否 		| Boolean	|false	| true=自动上传	|
+| count		|	否 		| Number	|10		| 附件选择上限(个)|
+| size		|	否 		| Number	|10		| 附件大小上限(M)|
+| wxFileType	|	否 		| String	|all		| 微信小程序文件选择器格式限制(all=从所有文件选择,video=只能选择视频文件,image=只能选择图片文件,file=可以选择除了图片和视频之外的其它的文件)|
+| accept	|	否 		| String	|-		| 文件选择器input file格式限制(部分机型不兼容,建议使用formats)|
+| formats	|	否 		| String	|-		| 限制允许上传的格式,空串=不限制,默认为空,多个格式以逗号隔开,例如png,jpg,pdf|
+| childId	|	否 		| String	|lsjUpload| 控件的id(仅APP有效,应用内每个控件命名一个唯一Id,不同窗口也不要同名Id)|
+| position	|	否 		| String	|static	| 控件的定位模式(static=控件随页面滚动;absolute=控件在页面中绝对定位,不随窗口内容滚动)|
+| top,left,right,bottom	|	否 		| [Number,String]	|0		| 设置控件绝对位置,position=absolute时有效|
+| @change	|	否 		| Function	|Map	| 选择文件触发,返回所有已选择文件Map集合|
+| @progress	|	否 		| Function	|Object	| 上传过程中发生状态变化的文件对象,需通过set更新至Map集合|
+| @uploadEnd|	否 		| Function	|Object	| 上传结束回调,返回参数与progress一致|
+
+## <a id="p1">option说明</a>
+|参数 | 是否必填 |  说明|
+|---- | ---- | :--: |
+|url  |	是	| 上传接口地址|
+|name| 否	|上传接口文件key,默认为file|
+|header| 否	|上传接口请求头|
+|formData| 否	|上传接口额外参数|
+
+## ref调用
+|作用 | 方法名| 传入参数|  说明|
+|---- | --------- | -------- | :--: |
+|显示控件| show|-| 控件显示状态下可触发点击|
+|隐藏控件| hide|-| 控件隐藏状态下不触发点击|
+|动态设置文件列表| setFiles|[Array,Map] files| 传入格式请与组件选择返回格式保持一致,且name为必须属性,可查看下方演示|
+|动态更新参数| setData|[String] name,[any] value| name支持a.b 和 a[b],可查看下方演示|
+|移除选择的文件| clear|[String] name| 不传参数清空所有文件,传入文件name时删除该name的文件|
+|手动上传| upload|[String] name| 不传参数默认依次上传所有type=waiting的文件,传入文件name时不关心type是否为waiting,单独上传指定name的文件|
+
+## progress返回对象字段说明
+|字段 |  说明|
+|---- | :--: |
+|file | 文件对象|
+|name |文件名称|
+|size |文件大小|
+|type |文件上传状态:waiting(等待上传)、loading(上传中)、success(成功) 、fail(失败)|
+|responseText|上传成功后服务端返回数据(仅type为success时存在)|
+
+## 以下演示为vue窗口使用方式,nvue使用区别是必须传入控件绝对位置如top,bottom,left,right,且position只能为absolute,如不清楚可点击右侧导入示例项目有详细演示代码。
+
+### vue:
+``` javascript
+<lsj-upload 
+	ref="lsjUpload"
+	childId="upload1"
+	:width="width"
+	:height="height"
+	:option="option"
+	:size="size"
+	:formats="formats"
+	:debug="debug"
+	:instantly="instantly"
+	@progress="onprogress"
+	@change="onChange">
+		<view class="btn" :style="{width: width,height: height}">选择附件</view>
+</lsj-upload>
+
+
+<view class="padding">
+			
+	<view>已选择文件列表:</view>
+	
+	<!-- #ifndef MP-WEIXIN -->
+	<view v-for="(item,index) in files.values()" :key="index">
+		<image style="width: 100rpx;height: 100rpx;" :src="item.path" mode="widthFix"></image>
+		<text>提示:【path主要用于图片视频类文件回显,他用自行处理】:{{item.path}}</text>
+		<text>{{item.name}}</text>
+		<text style="margin-left: 10rpx;">大小:{{item.size}}</text>
+		<text style="margin-left: 10rpx;">状态:{{item.type}}</text>
+		<text style="margin-left: 10rpx;">进度:{{item.progress}}</text>
+		<text style="margin-left: 10rpx;" v-if="item.responseText">服务端返回演示:{{item.responseText}}</text>
+		<text @click="resetUpload(item.name)" v-if="item.type=='fail'" style="margin-left: 10rpx;padding: 0 10rpx;border: 1rpx solid #007AFF;">重新上传</text>
+		<text @click="clear(item.name)" style="margin-left: 10rpx;padding: 0 10rpx;border: 1rpx solid #007AFF;">删除</text>
+	</view>
+	<!-- #endif -->
+	
+	<!-- #ifdef MP-WEIXIN -->
+	<view v-for="(item,index) in wxFiles" :key="index">
+		<text>{{item.name}}</text>
+		<text style="margin-left: 10rpx;">大小:{{item.size}}</text>
+		<text style="margin-left: 10rpx;">状态:{{item.type}}</text>
+		<text style="margin-left: 10rpx;">进度:{{item.progress}}</text>
+		<view>
+			<button @click="resetUpload(item.name)">重新上传</button>
+			<button @click="clear(item.name)">删除</button>
+		</view>
+	</view>
+	<!-- #endif -->
+	
+</view>
+
+
+```
+
+---
+* 函数说明
+
+
+``` javascript
+export default {
+	data() {
+		return {
+			// 上传接口参数
+			option: {
+				// 上传服务器地址,需要替换为你的接口地址
+				url: 'http://hl.j56.com/dropbox/document/upload', // 该地址非真实路径,需替换为你项目自己的接口地址
+				// 上传附件的key
+				name: 'file',
+				// 根据你接口需求自定义请求头,默认不要写content-type,让浏览器自适配
+				header: {
+					// 示例参数可删除
+					'Authorization': 'bearer eyJhbGciOiJSUzI1NiIsI',
+					'uid': '99',
+					'client': 'app',
+					'accountid': 'DP',
+				},
+				// 根据你接口需求自定义body参数
+				formData: {
+					// 'orderId': 1000
+				}
+			},
+			// 选择文件后是否立即自动上传,true=选择后立即上传
+			instantly: true,
+			// 必传宽高且宽高应与slot宽高保持一致
+			width: '180rpx',
+			height: '180rpx',
+			// 限制允许上传的格式,空串=不限制,默认为空
+			formats: '',
+			// 文件上传大小限制
+			size: 30,
+			// 文件数量限制
+			count: 2,
+			// 文件回显列表
+			files: new Map(),
+			// 微信小程序Map对象for循环不显示,所以转成普通数组,不要问为什么,我也不知道
+			wxFiles: [],
+			// 是否打印日志
+			debug: true,
+			
+			
+			// 演示用
+			tabIndex: 0,
+			list:[], 
+		}
+	},
+	onReady() {
+		setTimeout(()=>{
+			console.log('----演示动态更新参数-----');
+			this.$refs['lsjUpload'+this.tabIndex].setData('formData.orderId','动态设置的参数'); 
+			
+			console.log('以下注释内容为-动态更新参数更多演示,放开后可查看演示效果');
+			// 修改option对象的name属性
+			// this.$refs.lsjUpload.setData('name','myFile');
+			
+			// 修改option对象的formData内的属性
+			// this.$refs.lsjUpload.setData('formData.appid','1111');
+			
+			// 替换option对象的formData
+			// this.$refs.lsjUpload.setData('formData',{appid:'222'});
+			
+			// option对象的formData新增属性
+			// this.$refs.lsjUpload.setData('formData.newkey','新插入到formData的属性');
+			
+			
+			// ---------演示初始化值,用于已提交后再次编辑时需带入已上传文件-------
+			// 方式1=传入数组
+			// let files1 = [{name: '1.png'},{name: '2.png',}];
+			
+			// 方式2=传入Map对象
+			// let files2 = new Map();
+			// files2.set('1.png',{name: '1.png'})
+			
+			// 此处调用setFiles设置初始files
+			// this.$refs.lsjUpload.setFiles(files1);
+			
+			// 初始化tab
+			this.onTab(0);
+		},2000)
+	},
+	methods: {
+		// 某文件上传结束回调(成功失败都回调)
+		onuploadEnd(item) {
+			console.log(`${item.name}已上传结束,上传状态=${item.type}`);
+			
+			// 更新当前窗口状态变化的文件
+			this.files.set(item.name,item);
+			
+			// ---可删除--演示上传完成后取服务端数据
+			if (item['responseText']) {
+				console.log('演示服务器返回的字符串JSON转Object对象');
+				this.files.get(item.name).responseText = JSON.parse(item.responseText);
+			}
+			
+			// 微信小程序Map对象for循环不显示,所以转成普通数组,
+			// 如果你用不惯Map对象,也可以像这样转普通数组,组件使用Map主要是避免反复文件去重操作
+			// #ifdef MP-WEIXIN
+			this.wxFiles = [...this.files.values()];
+			// #endif
+			
+			// 强制更新视图
+			this.$forceUpdate();
+			
+			
+			// ---可删除--演示判断是否所有文件均已上传成功
+			let isAll = [...this.files.values()].find(item=>item.type!=='success');
+			if (!isAll) {
+				console.log('已全部上传完毕');
+			}
+			else {
+				console.log(isAll.name+'待上传');
+			}
+			
+		},
+		// 上传进度回调
+		onprogress(item) {
+			// 更新当前状态变化的文件
+			this.files.set(item.name,item);
+			
+			console.log('打印对象',JSON.stringify(this.files.get(item.name)));
+			// 微信小程序Map对象for循环不显示,所以转成普通数组,不要问为什么,我也不知道
+			// #ifdef MP-WEIXIN
+			this.wxFiles = [...this.files.values()];
+			// #endif
+			
+			// 强制更新视图
+			this.$forceUpdate();
+			
+		},
+		// 文件选择回调
+		onChange(files) {
+			console.log('当前选择的文件列表:',JSON.stringify([...files.values()]));
+			// 更新选择的文件 
+			this.files = files;
+			// 强制更新视图
+			this.$forceUpdate();
+			
+			// 微信小程序Map对象for循环不显示,所以转成普通数组,不要问为什么,我也不知道
+			// #ifdef MP-WEIXIN
+			this.wxFiles = [...this.files.values()];
+			// #endif
+			
+			// ---可删除--演示重新定位覆盖层控件
+			this.$nextTick(()=>{
+				console.log('演示重新定位');
+				this.$refs.lsjUpload0.show();
+				this.$refs.lsjUpload1.show();
+				this.$refs.lsjUpload2.show();
+			});
+			
+		},
+		// 手动上传
+		upload() {
+			// name=指定文件名,不指定则上传所有type等于waiting和fail的文件
+			this.$refs['lsjUpload'+this.tabIndex].upload();
+		},
+		// 指定上传某个文件
+		resetUpload(name) {
+			this.$refs['lsjUpload'+this.tabIndex].upload(name);
+		},
+		// 移除某个文件
+		clear(name) {
+			// name=指定文件名,不传name默认移除所有文件
+			this.$refs['lsjUpload'+this.tabIndex].clear(name);
+		},
+		/**
+		 * ---可删除--演示在组件上方添加新内容DOM变化
+		 * DOM重排演示,重排后组件内部updated默认会触发show方法,若特殊情况未能触发updated也可以手动调用一次show()
+		 * 什么是DOM重排?自行百度去
+		 */
+		add() {
+			this.list.push('DOM重排测试');
+		},
+		/**
+		 * ---可删除--演示Tab切换时覆盖层是否能被点击
+		 * APP端因为是webview,层级比view高,此时若不希望点击触发选择文件,需要手动调用hide()
+		 * 手动调用hide后,需要调用show()才能恢复覆盖层的点击
+		 */
+		onTab(tabIndex) {
+			this.$refs.lsjUpload0.hide();
+			this.$refs.lsjUpload1.hide();
+			
+			this.tabIndex = tabIndex;
+			
+			this.$nextTick(()=>{
+				this.$refs['lsjUpload'+this.tabIndex].show();
+			})
+			
+		},
+		/**
+		 * 打开nvue窗口查看非跟随窗口滚动效果
+		 */
+		open() {
+			uni.navigateTo({
+				url: '/pages/nvue-demo/nvue-demo'
+			});
+		}
+	}
+}
+
+```
+
+## 温馨提示
+	
+* 文件上传
+0. 如说明表达还不够清楚,不清楚怎么使用可导入完整示例项目运行体验和查看	
+1. APP端请优先联调Android,上传成功后再运行iOS端,如iOS返回status=0则需要后端开启允许跨域;
+2. header的Content-Type类型需要与服务端要求一致,否则收不到附件(服务端若没有明文规定则可不写,使用默认匹配)
+3. 服务端不清楚怎么配置跨域可加群咨询,具体百度~
+4. 欢迎加入QQ讨论群:701468256(已满)
+5. 欢迎加入QQ讨论群:469580165(已满)
+6. 欢迎加入QQ讨论群:667530868
+7. 若能帮到你还请点亮5颗小星星以作鼓励哈~
+8. 若能帮到你还请点亮5颗小星星以作鼓励哈~
+9. 若能帮到你还请点亮5颗小星星以作鼓励哈~

+ 27 - 0
uni_modules/uni-table/changelog.md

@@ -0,0 +1,27 @@
+## 1.2.3(2023-03-28)
+- 修复 在vue3模式下可能会出现错误的问题
+## 1.2.2(2022-11-29)
+- 优化 主题样式
+## 1.2.1(2022-06-06)
+- 修复 微信小程序存在无使用组件的问题
+## 1.2.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-table](https://uniapp.dcloud.io/component/uniui/uni-table)
+## 1.1.0(2021-07-30)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.0.7(2021-07-08)
+- 新增 uni-th 支持 date 日期筛选范围
+## 1.0.6(2021-07-05)
+- 新增 uni-th 支持 range 筛选范围
+## 1.0.5(2021-06-28)
+- 新增 uni-th 筛选功能
+## 1.0.4(2021-05-12)
+- 新增 示例地址
+- 修复 示例项目缺少组件的Bug
+## 1.0.3(2021-04-16)
+- 新增 sortable 属性,是否开启单列排序
+- 优化 表格多选逻辑
+## 1.0.2(2021-03-22)
+- uni-tr 添加 disabled 属性,用于 type=selection 时,设置某行是否可由全选按钮控制
+## 1.0.1(2021-02-05)
+- 调整为uni_modules目录规范

+ 455 - 0
uni_modules/uni-table/components/uni-table/uni-table.vue

@@ -0,0 +1,455 @@
+<template>
+	<view class="uni-table-scroll" :class="{ 'table--border': border, 'border-none': !noData }">
+		<!-- #ifdef H5 -->
+		<table class="uni-table" border="0" cellpadding="0" cellspacing="0" :class="{ 'table--stripe': stripe }" :style="{ 'min-width': minWidth + 'px' }">
+			<slot></slot>
+			<tr v-if="noData" class="uni-table-loading">
+				<td class="uni-table-text" :class="{ 'empty-border': border }">{{ emptyText }}</td>
+			</tr>
+			<view v-if="loading" class="uni-table-mask" :class="{ 'empty-border': border }"><div class="uni-table--loader"></div></view>
+		</table>
+		<!-- #endif -->
+		<!-- #ifndef H5 -->
+		<view class="uni-table" :style="{ 'min-width': minWidth + 'px' }" :class="{ 'table--stripe': stripe }">
+			<slot></slot>
+			<view v-if="noData" class="uni-table-loading">
+				<view class="uni-table-text" :class="{ 'empty-border': border }">{{ emptyText }}</view>
+			</view>
+			<view v-if="loading" class="uni-table-mask" :class="{ 'empty-border': border }"><div class="uni-table--loader"></div></view>
+		</view>
+		<!-- #endif -->
+	</view>
+</template>
+
+<script>
+/**
+ * Table 表格
+ * @description 用于展示多条结构类似的数据
+ * @tutorial https://ext.dcloud.net.cn/plugin?id=3270
+ * @property {Boolean} 	border 				是否带有纵向边框
+ * @property {Boolean} 	stripe 				是否显示斑马线
+ * @property {Boolean} 	type 					是否开启多选
+ * @property {String} 	emptyText 			空数据时显示的文本内容
+ * @property {Boolean} 	loading 			显示加载中
+ * @event {Function} 	selection-change 	开启多选时,当选择项发生变化时会触发该事件
+ */
+export default {
+	name: 'uniTable',
+	options: {
+		virtualHost: true
+	},
+	emits:['selection-change'],
+	props: {
+		data: {
+			type: Array,
+			default() {
+				return []
+			}
+		},
+		// 是否有竖线
+		border: {
+			type: Boolean,
+			default: false
+		},
+		// 是否显示斑马线
+		stripe: {
+			type: Boolean,
+			default: false
+		},
+		// 多选
+		type: {
+			type: String,
+			default: ''
+		},
+		// 没有更多数据
+		emptyText: {
+			type: String,
+			default: '没有更多数据'
+		},
+		loading: {
+			type: Boolean,
+			default: false
+		},
+		rowKey: {
+			type: String,
+			default: ''
+		}
+	},
+	data() {
+		return {
+			noData: true,
+			minWidth: 0,
+			multiTableHeads: []
+		}
+	},
+	watch: {
+		loading(val) {},
+		data(newVal) {
+			let theadChildren = this.theadChildren
+			let rowspan = 1
+			if (this.theadChildren) {
+				rowspan = this.theadChildren.rowspan
+			}
+			
+			// this.trChildren.length - rowspan
+			this.noData = false
+			// this.noData = newVal.length === 0 
+		}
+	},
+	created() {
+		// 定义tr的实例数组
+		this.trChildren = []
+		this.thChildren = []
+		this.theadChildren = null
+		this.backData = []
+		this.backIndexData = []
+	},
+
+	methods: {
+		isNodata() {
+			let theadChildren = this.theadChildren
+			let rowspan = 1
+			if (this.theadChildren) {
+				rowspan = this.theadChildren.rowspan
+			}
+			this.noData = this.trChildren.length - rowspan <= 0
+		},
+		/**
+		 * 选中所有
+		 */
+		selectionAll() {
+			let startIndex = 1
+			let theadChildren = this.theadChildren
+			if (!this.theadChildren) {
+				theadChildren = this.trChildren[0]
+			} else {
+				startIndex = theadChildren.rowspan - 1
+			}
+			let isHaveData = this.data && this.data.length > 0
+			theadChildren.checked = true
+			theadChildren.indeterminate = false
+			this.trChildren.forEach((item, index) => {
+				if (!item.disabled) {
+					item.checked = true
+					if (isHaveData && item.keyValue) {
+						const row = this.data.find(v => v[this.rowKey] === item.keyValue)
+						if (!this.backData.find(v => v[this.rowKey] === row[this.rowKey])) {
+							this.backData.push(row)
+						}
+					}
+					if (index > (startIndex - 1) && this.backIndexData.indexOf(index - startIndex) === -1) {
+						this.backIndexData.push(index - startIndex)
+					}
+				}
+			})
+			// this.backData = JSON.parse(JSON.stringify(this.data))
+			this.$emit('selection-change', {
+				detail: {
+					value: this.backData,
+					index: this.backIndexData
+				}
+			})
+		},
+		/**
+		 * 用于多选表格,切换某一行的选中状态,如果使用了第二个参数,则是设置这一行选中与否(selected 为 true 则选中)
+		 */
+		toggleRowSelection(row, selected) {
+			// if (!this.theadChildren) return
+			row = [].concat(row)
+
+			this.trChildren.forEach((item, index) => {
+				// if (item.keyValue) {
+
+				const select = row.findIndex(v => {
+					//
+					if (typeof v === 'number') {
+						return v === index - 1
+					} else {
+						return v[this.rowKey] === item.keyValue
+					}
+				})
+				let ischeck = item.checked
+				if (select !== -1) {
+					if (typeof selected === 'boolean') {
+						item.checked = selected
+					} else {
+						item.checked = !item.checked
+					}
+					if (ischeck !== item.checked) {
+						this.check(item.rowData||item, item.checked, item.rowData?item.keyValue:null, true)
+					}
+				}
+				// }
+			})
+			this.$emit('selection-change', {
+				detail: {
+					value: this.backData,
+					index:this.backIndexData
+				}
+			})
+		},
+
+		/**
+		 * 用于多选表格,清空用户的选择
+		 */
+		clearSelection() {
+			let theadChildren = this.theadChildren
+			if (!this.theadChildren) {
+				theadChildren = this.trChildren[0]
+			}
+			// if (!this.theadChildren) return
+			theadChildren.checked = false
+			theadChildren.indeterminate = false
+			this.trChildren.forEach(item => {
+				// if (item.keyValue) {
+					item.checked = false
+				// }
+			})
+			this.backData = []
+			this.backIndexData = []
+			this.$emit('selection-change', {
+				detail: {
+					value: [],
+					index: []
+				}
+			})
+		},
+		/**
+		 * 用于多选表格,切换所有行的选中状态
+		 */
+		toggleAllSelection() {
+			let list = []
+			let startIndex = 1
+			let theadChildren = this.theadChildren
+			if (!this.theadChildren) {
+				theadChildren = this.trChildren[0]
+			} else {
+				startIndex = theadChildren.rowspan - 1
+			}
+			this.trChildren.forEach((item, index) => {
+				if (!item.disabled) {
+					if (index > (startIndex - 1) ) {
+						list.push(index-startIndex)
+					}
+				}
+			})
+			this.toggleRowSelection(list)
+		},
+
+		/**
+		 * 选中\取消选中
+		 * @param {Object} child
+		 * @param {Object} check
+		 * @param {Object} rowValue
+		 */
+		check(child, check, keyValue, emit) {
+			let theadChildren = this.theadChildren
+			if (!this.theadChildren) {
+				theadChildren = this.trChildren[0]
+			}
+			
+			
+			
+			let childDomIndex = this.trChildren.findIndex((item, index) => child === item)
+			if(childDomIndex < 0){
+				childDomIndex = this.data.findIndex(v=>v[this.rowKey] === keyValue) + 1
+			}
+			const dataLen = this.trChildren.filter(v => !v.disabled && v.keyValue).length
+			if (childDomIndex === 0) {
+				check ? this.selectionAll() : this.clearSelection()
+				return
+			}
+
+			if (check) {
+				if (keyValue) {
+					this.backData.push(child)
+				}
+				this.backIndexData.push(childDomIndex - 1)
+			} else {
+				const index = this.backData.findIndex(v => v[this.rowKey] === keyValue)
+				const idx = this.backIndexData.findIndex(item => item === childDomIndex - 1)
+				if (keyValue) {
+					this.backData.splice(index, 1)
+				}
+				this.backIndexData.splice(idx, 1)
+			}
+
+			const domCheckAll = this.trChildren.find((item, index) => index > 0 && !item.checked && !item.disabled)
+			if (!domCheckAll) {
+				theadChildren.indeterminate = false
+				theadChildren.checked = true
+			} else {
+				theadChildren.indeterminate = true
+				theadChildren.checked = false
+			}
+
+			if (this.backIndexData.length === 0) {
+				theadChildren.indeterminate = false
+			}
+
+			if (!emit) {
+				this.$emit('selection-change', {
+					detail: {
+						value: this.backData,
+						index: this.backIndexData
+					}
+				})
+			}
+		}
+	}
+}
+</script>
+
+<style lang="scss">
+$border-color: #ebeef5;
+
+.uni-table-scroll {
+	width: 100%;
+	/* #ifndef APP-NVUE */
+	overflow-x: auto;
+	/* #endif */
+}
+
+.uni-table {
+	position: relative;
+	width: 100%;
+	border-radius: 5px;
+	// box-shadow: 0px 0px 3px 1px rgba(0, 0, 0, 0.1);
+	background-color: #fff;
+	/* #ifndef APP-NVUE */
+	box-sizing: border-box;
+	display: table;
+	overflow-x: auto;
+	::v-deep .uni-table-tr:nth-child(n + 2) {
+		&:hover {
+			background-color: #f5f7fa;
+		}
+	}
+	::v-deep .uni-table-thead {
+		.uni-table-tr {
+			// background-color: #f5f7fa;
+			&:hover {
+				background-color:#fafafa;
+			}
+		}
+	}
+	/* #endif */
+}
+
+.table--border {
+	border: 1px $border-color solid;
+	border-right: none;
+}
+
+.border-none {
+	/* #ifndef APP-NVUE */
+	border-bottom: none;
+	/* #endif */
+}
+
+.table--stripe {
+	/* #ifndef APP-NVUE */
+	::v-deep .uni-table-tr:nth-child(2n + 3) {
+		background-color: #fafafa;
+	}
+	/* #endif */
+}
+
+/* 表格加载、无数据样式 */
+.uni-table-loading {
+	position: relative;
+	/* #ifndef APP-NVUE */
+	display: table-row;
+	/* #endif */
+	height: 50px;
+	line-height: 50px;
+	overflow: hidden;
+	box-sizing: border-box;
+}
+.empty-border {
+	border-right: 1px $border-color solid;
+}
+.uni-table-text {
+	position: absolute;
+	right: 0;
+	left: 0;
+	text-align: center;
+	font-size: 14px;
+	color: #999;
+}
+
+.uni-table-mask {
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	background-color: rgba(255, 255, 255, 0.8);
+	z-index: 99;
+	/* #ifndef APP-NVUE */
+	display: flex;
+	margin: auto;
+	transition: all 0.5s;
+	/* #endif */
+	justify-content: center;
+	align-items: center;
+}
+
+.uni-table--loader {
+	width: 30px;
+	height: 30px;
+	border: 2px solid #aaa;
+	// border-bottom-color: transparent;
+	border-radius: 50%;
+	/* #ifndef APP-NVUE */
+	animation: 2s uni-table--loader linear infinite;
+	/* #endif */
+	position: relative;
+}
+
+@keyframes uni-table--loader {
+	0% {
+		transform: rotate(360deg);
+	}
+
+	10% {
+		border-left-color: transparent;
+	}
+
+	20% {
+		border-bottom-color: transparent;
+	}
+
+	30% {
+		border-right-color: transparent;
+	}
+
+	40% {
+		border-top-color: transparent;
+	}
+
+	50% {
+		transform: rotate(0deg);
+	}
+
+	60% {
+		border-top-color: transparent;
+	}
+
+	70% {
+		border-left-color: transparent;
+	}
+
+	80% {
+		border-bottom-color: transparent;
+	}
+
+	90% {
+		border-right-color: transparent;
+	}
+
+	100% {
+		transform: rotate(-360deg);
+	}
+}
+</style>

+ 29 - 0
uni_modules/uni-table/components/uni-tbody/uni-tbody.vue

@@ -0,0 +1,29 @@
+<template>
+	<!-- #ifdef H5 -->
+	<tbody>
+		<slot></slot>
+	</tbody>
+	<!-- #endif -->
+	<!-- #ifndef H5 -->
+	<view><slot></slot></view>
+	<!-- #endif -->
+</template>
+
+<script>
+export default {
+	name: 'uniBody',
+	options: {
+		virtualHost: true
+	},
+	data() {
+		return {
+			
+		}
+	},
+	created() {},
+	methods: {}
+}
+</script>
+
+<style>
+</style>

+ 90 - 0
uni_modules/uni-table/components/uni-td/uni-td.vue

@@ -0,0 +1,90 @@
+<template>
+	<!-- #ifdef H5 -->
+	<td class="uni-table-td" :rowspan="rowspan" :colspan="colspan" :class="{'table--border':border}" :style="{width:width + 'px','text-align':align}">
+		<slot></slot>
+	</td>
+	<!-- #endif -->
+	<!-- #ifndef H5 -->
+	<!-- :class="{'table--border':border}"  -->
+	<view class="uni-table-td" :class="{'table--border':border}" :style="{width:width + 'px','text-align':align}">
+		<slot></slot>
+	</view>
+	<!-- #endif -->
+	
+</template>
+
+<script>
+	/**
+	 * Td 单元格
+	 * @description 表格中的标准单元格组件
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=3270
+	 * @property {Number} 	align = [left|center|right]	单元格对齐方式
+	 */
+	export default {
+		name: 'uniTd',
+		options: {
+			virtualHost: true
+		},
+		props: {
+			width: {
+				type: [String, Number],
+				default: ''
+			},
+			align: {
+				type: String,
+				default: 'left'
+			},
+			rowspan: {
+				type: [Number,String],
+				default: 1
+			},
+			colspan: {
+					type: [Number,String],
+				default: 1
+			}
+		},
+		data() {
+			return {
+				border: false
+			};
+		},
+		created() {
+			this.root = this.getTable()
+			this.border = this.root.border
+		},
+		methods: {
+			/**
+			 * 获取父元素实例
+			 */
+			getTable() {
+				let parent = this.$parent;
+				let parentName = parent.$options.name;
+				while (parentName !== 'uniTable') {
+					parent = parent.$parent;
+					if (!parent) return false;
+					parentName = parent.$options.name;
+				}
+				return parent;
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	$border-color:#EBEEF5;
+
+	.uni-table-td {
+		display: table-cell;
+		padding: 8px 10px;
+		font-size: 14px;
+		border-bottom: 1px $border-color solid;
+		font-weight: 400;
+		color: #606266;
+		line-height: 23px;
+		box-sizing: border-box;
+	}
+
+	.table--border {
+		border-right: 1px $border-color solid;
+	}
+</style>

+ 511 - 0
uni_modules/uni-table/components/uni-th/filter-dropdown.vue

@@ -0,0 +1,511 @@
+<template>
+	<view class="uni-filter-dropdown">
+		<view class="dropdown-btn" @click="onDropdown">
+			<view class="icon-select" :class="{active: canReset}" v-if="isSelect || isRange"></view>
+			<view class="icon-search" :class="{active: canReset}" v-if="isSearch">
+				<view class="icon-search-0"></view>
+				<view class="icon-search-1"></view>
+			</view>
+			<view class="icon-calendar" :class="{active: canReset}" v-if="isDate">
+				<view class="icon-calendar-0"></view>
+				<view class="icon-calendar-1"></view>
+			</view>
+		</view>
+		<view class="uni-dropdown-cover" v-if="isOpened" @click="handleClose"></view>
+		<view class="dropdown-popup dropdown-popup-right" v-if="isOpened" @click.stop>
+			<!-- select-->
+			<view v-if="isSelect" class="list">
+				<label class="flex-r a-i-c list-item" v-for="(item,index) in dataList" :key="index"
+					@click="onItemClick($event, index)">
+					<check-box class="check" :checked="item.checked" />
+					<view class="checklist-content">
+						<text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text>
+					</view>
+				</label>
+			</view>
+			<view v-if="isSelect" class="flex-r opera-area">
+				<view class="flex-f btn btn-default" :class="{disable: !canReset}" @click="handleSelectReset">
+					{{resource.reset}}</view>
+				<view class="flex-f btn btn-submit" @click="handleSelectSubmit">{{resource.submit}}</view>
+			</view>
+			<!-- search -->
+			<view v-if="isSearch" class="search-area">
+				<input class="search-input" v-model="filterValue" />
+			</view>
+			<view v-if="isSearch" class="flex-r opera-area">
+				<view class="flex-f btn btn-submit" @click="handleSearchSubmit">{{resource.search}}</view>
+				<view class="flex-f btn btn-default" :class="{disable: !canReset}" @click="handleSearchReset">
+					{{resource.reset}}</view>
+			</view>
+			<!-- range -->
+			<view v-if="isRange">
+				<view class="input-label">{{resource.gt}}</view>
+				<input class="input" v-model="gtValue" />
+				<view class="input-label">{{resource.lt}}</view>
+				<input class="input" v-model="ltValue" />
+			</view>
+			<view v-if="isRange" class="flex-r opera-area">
+				<view class="flex-f btn btn-default" :class="{disable: !canReset}" @click="handleRangeReset">
+					{{resource.reset}}</view>
+				<view class="flex-f btn btn-submit" @click="handleRangeSubmit">{{resource.submit}}</view>
+			</view>
+			<!-- date -->
+			<view v-if="isDate">
+				<uni-datetime-picker ref="datetimepicker" :value="dateRange" type="datetimerange" return-type="timestamp" @change="datetimechange" @maskClick="timepickerclose">
+					<view></view>
+				</uni-datetime-picker>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import checkBox from '../uni-tr/table-checkbox.vue'
+
+	const resource = {
+		"reset": "重置",
+		"search": "搜索",
+		"submit": "确定",
+		"filter": "筛选",
+		"gt": "大于等于",
+		"lt": "小于等于",
+		"date": "日期范围"
+	}
+
+	const DropdownType = {
+		Select: "select",
+		Search: "search",
+		Range: "range",
+		Date: "date",
+		Timestamp: "timestamp"
+	}
+
+	export default {
+		name: 'FilterDropdown',
+		emits:['change'],
+		components: {
+			checkBox
+		},
+		options: {
+			virtualHost: true
+		},
+		props: {
+			filterType: {
+				type: String,
+				default: DropdownType.Select
+			},
+			filterData: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			mode: {
+				type: String,
+				default: 'default'
+			},
+			map: {
+				type: Object,
+				default () {
+					return {
+						text: 'text',
+						value: 'value'
+					}
+				}
+			},
+			filterDefaultValue: {
+				type: [Array,String],
+				default () {
+					return ""
+				}
+			}
+		},
+		computed: {
+			canReset() {
+				if (this.isSearch) {
+					return this.filterValue.length > 0
+				}
+				if (this.isSelect) {
+					return this.checkedValues.length > 0
+				}
+				if (this.isRange) {
+					return (this.gtValue.length > 0 && this.ltValue.length > 0)
+				}
+				if (this.isDate) {
+					return this.dateSelect.length > 0
+				}
+				return false
+			},
+			isSelect() {
+				return this.filterType === DropdownType.Select
+			},
+			isSearch() {
+				return this.filterType === DropdownType.Search
+			},
+			isRange() {
+				return this.filterType === DropdownType.Range
+			},
+			isDate() {
+				return (this.filterType === DropdownType.Date || this.filterType === DropdownType.Timestamp)
+			}
+		},
+		watch: {
+			filterData(newVal) {
+				this._copyFilters()
+			},
+			indeterminate(newVal) {
+				this.isIndeterminate = newVal
+			}
+		},
+		data() {
+			return {
+				resource,
+				enabled: true,
+				isOpened: false,
+				dataList: [],
+				filterValue: this.filterDefaultValue,
+				checkedValues: [],
+				gtValue: '',
+				ltValue: '',
+				dateRange: [],
+				dateSelect: []
+			};
+		},
+		created() {
+			this._copyFilters()
+		},
+		methods: {
+			_copyFilters() {
+				let dl = JSON.parse(JSON.stringify(this.filterData))
+				for (let i = 0; i < dl.length; i++) {
+					if (dl[i].checked === undefined) {
+						dl[i].checked = false
+					}
+				}
+				this.dataList = dl
+			},
+			openPopup() {
+				this.isOpened = true
+				if (this.isDate) {
+					this.$nextTick(() => {
+						if (!this.dateRange.length) {
+							this.resetDate()
+						}
+						this.$refs.datetimepicker.show()
+					})
+				}
+			},
+			closePopup() {
+				this.isOpened = false
+			},
+			handleClose(e) {
+				this.closePopup()
+			},
+			resetDate() {
+				let date = new Date()
+				let dateText = date.toISOString().split('T')[0]
+				this.dateRange = [dateText + ' 0:00:00', dateText + ' 23:59:59']
+			},
+			onDropdown(e) {
+				this.openPopup()
+			},
+			onItemClick(e, index) {
+				let items = this.dataList
+				let listItem = items[index]
+				if (listItem.checked === undefined) {
+					items[index].checked = true
+				} else {
+					items[index].checked = !listItem.checked
+				}
+
+				let checkvalues = []
+				for (let i = 0; i < items.length; i++) {
+					const item = items[i]
+					if (item.checked) {
+						checkvalues.push(item.value)
+					}
+				}
+				this.checkedValues = checkvalues
+			},
+			datetimechange(e) {
+				this.closePopup()
+				this.dateRange = e
+				this.dateSelect = e
+				this.$emit('change', {
+					filterType: this.filterType,
+					filter: e
+				})
+			},
+			timepickerclose(e) {
+				this.closePopup()
+			},
+			handleSelectSubmit() {
+				this.closePopup()
+				this.$emit('change', {
+					filterType: this.filterType,
+					filter: this.checkedValues
+				})
+			},
+			handleSelectReset() {
+				if (!this.canReset) {
+					return;
+				}
+				var items = this.dataList
+				for (let i = 0; i < items.length; i++) {
+					let item = items[i]
+					this.$set(item, 'checked', false)
+				}
+				this.checkedValues = []
+				this.handleSelectSubmit()
+			},
+			handleSearchSubmit() {
+				this.closePopup()
+				this.$emit('change', {
+					filterType: this.filterType,
+					filter: this.filterValue
+				})
+			},
+			handleSearchReset() {
+				if (!this.canReset) {
+					return;
+				}
+				this.filterValue = ''
+				this.handleSearchSubmit()
+			},
+			handleRangeSubmit(isReset) {
+				this.closePopup()
+				this.$emit('change', {
+					filterType: this.filterType,
+					filter: isReset === true ? [] : [parseInt(this.gtValue), parseInt(this.ltValue)]
+				})
+			},
+			handleRangeReset() {
+				if (!this.canReset) {
+					return;
+				}
+				this.gtValue = ''
+				this.ltValue = ''
+				this.handleRangeSubmit(true)
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	$uni-primary: #1890ff !default;
+	
+	.flex-r {
+		display: flex;
+		flex-direction: row;
+	}
+
+	.flex-f {
+		flex: 1;
+	}
+
+	.a-i-c {
+		align-items: center;
+	}
+
+	.j-c-c {
+		justify-content: center;
+	}
+
+	.icon-select {
+		width: 14px;
+		height: 16px;
+		border: solid 6px transparent;
+		border-top: solid 6px #ddd;
+		border-bottom: none;
+		background-color: #ddd;
+		background-clip: content-box;
+		box-sizing: border-box;
+	}
+
+	.icon-select.active {
+		background-color: $uni-primary;
+		border-top-color: $uni-primary;
+	}
+
+	.icon-search {
+		width: 12px;
+		height: 16px;
+		position: relative;
+	}
+
+	.icon-search-0 {
+		border: 2px solid #ddd;
+		border-radius: 8px;
+		width: 7px;
+		height: 7px;
+	}
+
+	.icon-search-1 {
+		position: absolute;
+		top: 8px;
+		right: 0;
+		width: 1px;
+		height: 7px;
+		background-color: #ddd;
+		transform: rotate(-45deg);
+	}
+
+	.icon-search.active .icon-search-0 {
+		border-color: $uni-primary;
+	}
+
+	.icon-search.active .icon-search-1 {
+		background-color: $uni-primary;
+	}
+
+	.icon-calendar {
+		color: #ddd;
+		width: 14px;
+		height: 16px;
+	}
+
+	.icon-calendar-0 {
+		height: 4px;
+		margin-top: 3px;
+		margin-bottom: 1px;
+		background-color: #ddd;
+		border-radius: 2px 2px 1px 1px;
+		position: relative;
+	}
+	.icon-calendar-0:before, .icon-calendar-0:after {
+		content: '';
+		position: absolute;
+		top: -3px;
+		width: 4px;
+		height: 3px;
+		border-radius: 1px;
+		background-color: #ddd;
+	}
+	.icon-calendar-0:before {
+		left: 2px;
+	}
+	.icon-calendar-0:after {
+		right: 2px;
+	}
+
+	.icon-calendar-1 {
+		height: 9px;
+		background-color: #ddd;
+		border-radius: 1px 1px 2px 2px;
+	}
+
+	.icon-calendar.active {
+		color: $uni-primary;
+	}
+
+	.icon-calendar.active .icon-calendar-0,
+	.icon-calendar.active .icon-calendar-1,
+	.icon-calendar.active .icon-calendar-0:before,
+	.icon-calendar.active .icon-calendar-0:after {
+		background-color: $uni-primary;
+	}
+
+	.uni-filter-dropdown {
+		position: relative;
+		font-weight: normal;
+	}
+
+	.dropdown-popup {
+		position: absolute;
+		top: 100%;
+		background-color: #fff;
+		box-shadow: 0 3px 6px -4px #0000001f, 0 6px 16px #00000014, 0 9px 28px 8px #0000000d;
+		min-width: 150px;
+		z-index: 1000;
+	}
+
+	.dropdown-popup-left {
+		left: 0;
+	}
+
+	.dropdown-popup-right {
+		right: 0;
+	}
+
+	.uni-dropdown-cover {
+		position: fixed;
+		left: 0;
+		top: 0;
+		right: 0;
+		bottom: 0;
+		background-color: transparent;
+		z-index: 100;
+	}
+
+	.list {
+		margin-top: 5px;
+		margin-bottom: 5px;
+	}
+
+	.list-item {
+		padding: 5px 10px;
+		text-align: left;
+	}
+
+	.list-item:hover {
+		background-color: #f0f0f0;
+	}
+
+	.check {
+		margin-right: 5px;
+	}
+
+	.search-area {
+		padding: 10px;
+	}
+
+	.search-input {
+		font-size: 12px;
+		border: 1px solid #f0f0f0;
+		border-radius: 3px;
+		padding: 2px 5px;
+		min-width: 150px;
+		text-align: left;
+	}
+
+	.input-label {
+		margin: 10px 10px 5px 10px;
+		text-align: left;
+	}
+
+	.input {
+		font-size: 12px;
+		border: 1px solid #f0f0f0;
+		border-radius: 3px;
+		margin: 10px;
+		padding: 2px 5px;
+		min-width: 150px;
+		text-align: left;
+	}
+
+	.opera-area {
+		cursor: default;
+		border-top: 1px solid #ddd;
+		padding: 5px;
+	}
+
+	.opera-area .btn {
+		font-size: 12px;
+		border-radius: 3px;
+		margin: 5px;
+		padding: 4px 4px;
+	}
+
+	.btn-default {
+		border: 1px solid #ddd;
+	}
+
+	.btn-default.disable {
+		border-color: transparent;
+	}
+
+	.btn-submit {
+		background-color: $uni-primary;
+		color: #ffffff;
+	}
+</style>

+ 285 - 0
uni_modules/uni-table/components/uni-th/uni-th.vue

@@ -0,0 +1,285 @@
+<template>
+	<!-- #ifdef H5 -->
+	<th :rowspan="rowspan" :colspan="colspan" class="uni-table-th" :class="{ 'table--border': border }" :style="{ width: customWidth + 'px', 'text-align': align }">
+		<view class="uni-table-th-row">
+			<view class="uni-table-th-content" :style="{ 'justify-content': contentAlign }" @click="sort">
+				<slot></slot>
+				<view v-if="sortable" class="arrow-box">
+					<text class="arrow up" :class="{ active: ascending }" @click.stop="ascendingFn"></text>
+					<text class="arrow down" :class="{ active: descending }" @click.stop="descendingFn"></text>
+				</view>
+			</view>
+			<dropdown v-if="filterType || filterData.length" :filterDefaultValue="filterDefaultValue" :filterData="filterData" :filterType="filterType" @change="ondropdown"></dropdown>
+		</view>
+	</th>
+	<!-- #endif -->
+	<!-- #ifndef H5 -->
+	<view class="uni-table-th" :class="{ 'table--border': border }" :style="{ width: customWidth + 'px', 'text-align': align }"><slot></slot></view>
+	<!-- #endif -->
+</template>
+
+<script>
+	// #ifdef H5
+	import dropdown from './filter-dropdown.vue'
+	// #endif
+/**
+ * Th 表头
+ * @description 表格内的表头单元格组件
+ * @tutorial https://ext.dcloud.net.cn/plugin?id=3270
+ * @property {Number | String} 	width 	单元格宽度(支持纯数字、携带单位px或rpx)
+ * @property {Boolean} 	sortable 					是否启用排序
+ * @property {Number} 	align = [left|center|right]	单元格对齐方式
+ * @value left   	单元格文字左侧对齐
+ * @value center	单元格文字居中
+ * @value right		单元格文字右侧对齐
+ * @property {Array}	filterData 筛选数据
+ * @property {String}	filterType	[search|select] 筛选类型
+ * @value search	关键字搜素
+ * @value select	条件选择
+ * @event {Function} sort-change 排序触发事件
+ */
+export default {
+	name: 'uniTh',
+	options: {
+		virtualHost: true
+	},
+	components: {
+		// #ifdef H5
+		dropdown
+		// #endif
+	},
+	emits:['sort-change','filter-change'],
+	props: {
+		width: {
+			type: [String, Number],
+			default: ''
+		},
+		align: {
+			type: String,
+			default: 'left'
+		},
+		rowspan: {
+			type: [Number, String],
+			default: 1
+		},
+		colspan: {
+			type: [Number, String],
+			default: 1
+		},
+		sortable: {
+			type: Boolean,
+			default: false
+		},
+		filterType: {
+			type: String,
+			default: ""
+		},
+		filterData: {
+			type: Array,
+			default () {
+				return []
+			}
+		},
+		filterDefaultValue: {
+			type: [Array,String],
+			default () {
+				return ""
+			}
+		}
+	},
+	data() {
+		return {
+			border: false,
+			ascending: false,
+			descending: false
+		}
+	},
+	computed: {
+		// 根据props中的width属性 自动匹配当前th的宽度(px)
+		customWidth(){
+			if(typeof this.width === 'number'){
+				return this.width
+			} else if(typeof this.width === 'string') {
+				let regexHaveUnitPx = new RegExp(/^[1-9][0-9]*px$/g)
+				let regexHaveUnitRpx = new RegExp(/^[1-9][0-9]*rpx$/g)
+				let regexHaveNotUnit = new RegExp(/^[1-9][0-9]*$/g)
+				if (this.width.match(regexHaveUnitPx) !== null) { // 携带了 px
+					return this.width.replace('px', '')
+				} else if (this.width.match(regexHaveUnitRpx) !== null) { // 携带了 rpx
+					let numberRpx = Number(this.width.replace('rpx', ''))
+					let widthCoe = uni.getSystemInfoSync().screenWidth / 750
+					return Math.round(numberRpx * widthCoe)
+				} else if (this.width.match(regexHaveNotUnit) !== null) { // 未携带 rpx或px 的纯数字 String
+					return this.width
+				} else { // 不符合格式
+					return ''
+				}
+			} else {
+				return ''
+			}
+		},
+		contentAlign() {
+			let align = 'left'
+			switch (this.align) {
+				case 'left':
+					align = 'flex-start'
+					break
+				case 'center':
+					align = 'center'
+					break
+				case 'right':
+					align = 'flex-end'
+					break
+			}
+			return align
+		}
+	},
+	created() {
+		this.root = this.getTable('uniTable')
+		this.rootTr = this.getTable('uniTr')
+		this.rootTr.minWidthUpdate(this.customWidth ? this.customWidth : 140)
+		this.border = this.root.border
+		this.root.thChildren.push(this)
+	},
+	methods: {
+		sort() {
+			if (!this.sortable) return
+			this.clearOther()
+			if (!this.ascending && !this.descending) {
+				this.ascending = true
+				this.$emit('sort-change', { order: 'ascending' })
+				return
+			}
+			if (this.ascending && !this.descending) {
+				this.ascending = false
+				this.descending = true
+				this.$emit('sort-change', { order: 'descending' })
+				return
+			}
+
+			if (!this.ascending && this.descending) {
+				this.ascending = false
+				this.descending = false
+				this.$emit('sort-change', { order: null })
+			}
+		},
+		ascendingFn() {
+			this.clearOther()
+			this.ascending = !this.ascending
+			this.descending = false
+			this.$emit('sort-change', { order: this.ascending ? 'ascending' : null })
+		},
+		descendingFn() {
+			this.clearOther()
+			this.descending = !this.descending
+			this.ascending = false
+			this.$emit('sort-change', { order: this.descending ? 'descending' : null })
+		},
+		clearOther() {
+			this.root.thChildren.map(item => {
+				if (item !== this) {
+					item.ascending = false
+					item.descending = false
+				}
+				return item
+			})
+		},
+		ondropdown(e) {
+			this.$emit("filter-change", e)
+		},
+		/**
+		 * 获取父元素实例
+		 */
+		getTable(name) {
+			let parent = this.$parent
+			let parentName = parent.$options.name
+			while (parentName !== name) {
+				parent = parent.$parent
+				if (!parent) return false
+				parentName = parent.$options.name
+			}
+			return parent
+		}
+	}
+}
+</script>
+
+<style lang="scss">
+$border-color: #ebeef5;
+$uni-primary: #007aff !default;
+
+.uni-table-th {
+	padding: 12px 10px;
+	/* #ifndef APP-NVUE */
+	display: table-cell;
+	box-sizing: border-box;
+	/* #endif */
+	font-size: 14px;
+	font-weight: bold;
+	color: #909399;
+	border-bottom: 1px $border-color solid;
+}
+
+.uni-table-th-row {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	/* #endif */
+	flex-direction: row;
+}
+
+.table--border {
+	border-right: 1px $border-color solid;
+}
+.uni-table-th-content {
+	display: flex;
+	align-items: center;
+	flex: 1;
+}
+.arrow-box {
+}
+.arrow {
+	display: block;
+	position: relative;
+	width: 10px;
+	height: 8px;
+	// border: 1px red solid;
+	left: 5px;
+	overflow: hidden;
+	cursor: pointer;
+}
+.down {
+	top: 3px;
+	::after {
+		content: '';
+		width: 8px;
+		height: 8px;
+		position: absolute;
+		left: 2px;
+		top: -5px;
+		transform: rotate(45deg);
+		background-color: #ccc;
+	}
+	&.active {
+		::after {
+			background-color: $uni-primary;
+		}
+	}
+}
+.up {
+	::after {
+		content: '';
+		width: 8px;
+		height: 8px;
+		position: absolute;
+		left: 2px;
+		top: 5px;
+		transform: rotate(45deg);
+		background-color: #ccc;
+	}
+	&.active {
+		::after {
+			background-color: $uni-primary;
+		}
+	}
+}
+</style>

+ 129 - 0
uni_modules/uni-table/components/uni-thead/uni-thead.vue

@@ -0,0 +1,129 @@
+<template>
+	<!-- #ifdef H5 -->
+	<thead class="uni-table-thead">
+		<tr class="uni-table-tr">
+			<th :rowspan="rowspan" colspan="1" class="checkbox" :class="{ 'tr-table--border': border }">
+				<table-checkbox :indeterminate="indeterminate" :checked="checked" @checkboxSelected="checkboxSelected"></table-checkbox>
+			</th>
+		</tr>
+		<slot></slot>
+	</thead>
+	<!-- #endif -->
+	<!-- #ifndef H5 -->
+	<view class="uni-table-thead"><slot></slot></view>
+	<!-- #endif -->
+</template>
+
+<script>
+import tableCheckbox from '../uni-tr/table-checkbox.vue'
+export default {
+	name: 'uniThead',
+	components: {
+		tableCheckbox
+	},
+	options: {
+		virtualHost: true
+	},
+	data() {
+		return {
+			border: false,
+			selection: false,
+			rowspan: 1,
+			indeterminate: false,
+			checked: false
+		}
+	},
+	created() {
+		this.root = this.getTable()
+		// #ifdef H5
+		this.root.theadChildren = this
+		// #endif
+		this.border = this.root.border
+		this.selection = this.root.type
+	},
+	methods: {
+		init(self) {
+			this.rowspan++
+		},
+		checkboxSelected(e) {
+			this.indeterminate = false
+			const backIndexData = this.root.backIndexData
+			const data = this.root.trChildren.filter(v => !v.disabled && v.keyValue)
+			if (backIndexData.length === data.length) {
+				this.checked = false
+				this.root.clearSelection()
+			} else {
+				this.checked = true
+				this.root.selectionAll()
+			}
+		},
+		/**
+		 * 获取父元素实例
+		 */
+		getTable(name = 'uniTable') {
+			let parent = this.$parent
+			let parentName = parent.$options.name
+			while (parentName !== name) {
+				parent = parent.$parent
+				if (!parent) return false
+				parentName = parent.$options.name
+			}
+			return parent
+		}
+	}
+}
+</script>
+
+<style lang="scss">
+$border-color: #ebeef5;
+
+.uni-table-thead {
+	display: table-header-group;
+}
+
+.uni-table-tr {
+	/* #ifndef APP-NVUE */
+	display: table-row;
+	transition: all 0.3s;
+	box-sizing: border-box;
+	/* #endif */
+	border: 1px red solid;
+	background-color: #fafafa;
+}
+
+.checkbox {
+	padding: 0 8px;
+	width: 26px;
+	padding-left: 12px;
+	/* #ifndef APP-NVUE */
+	display: table-cell;
+	vertical-align: middle;
+	/* #endif */
+	color: #333;
+	font-weight: 500;
+	border-bottom: 1px $border-color solid;
+	font-size: 14px;
+	// text-align: center;
+}
+
+.tr-table--border {
+	border-right: 1px $border-color solid;
+}
+
+/* #ifndef APP-NVUE */
+.uni-table-tr {
+	::v-deep .uni-table-th {
+		&.table--border:last-child {
+			// border-right: none;
+		}
+	}
+
+	::v-deep .uni-table-td {
+		&.table--border:last-child {
+			// border-right: none;
+		}
+	}
+}
+
+/* #endif */
+</style>

+ 179 - 0
uni_modules/uni-table/components/uni-tr/table-checkbox.vue

@@ -0,0 +1,179 @@
+<template>
+	<view class="uni-table-checkbox" @click="selected">
+		<view v-if="!indeterminate" class="checkbox__inner" :class="{'is-checked':isChecked,'is-disable':isDisabled}">
+			<view class="checkbox__inner-icon"></view>
+		</view>
+		<view v-else class="checkbox__inner checkbox--indeterminate">
+			<view class="checkbox__inner-icon"></view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: 'TableCheckbox',
+		emits:['checkboxSelected'],
+		props: {
+			indeterminate: {
+				type: Boolean,
+				default: false
+			},
+			checked: {
+				type: [Boolean,String],
+				default: false
+			},
+			disabled: {
+				type: Boolean,
+				default: false
+			},
+			index: {
+				type: Number,
+				default: -1
+			},
+			cellData: {
+				type: Object,
+				default () {
+					return {}
+				}
+			}
+		},
+		watch:{
+			checked(newVal){
+				if(typeof this.checked === 'boolean'){
+					this.isChecked = newVal
+				}else{
+					this.isChecked = true
+				}
+			},
+			indeterminate(newVal){
+				this.isIndeterminate = newVal
+			}
+		},
+		data() {
+			return {
+				isChecked: false,
+				isDisabled: false,
+				isIndeterminate:false
+			}
+		},
+		created() {
+			if(typeof this.checked === 'boolean'){
+				this.isChecked = this.checked
+			}
+			this.isDisabled = this.disabled
+		},
+		methods: {
+			selected() {
+				if (this.isDisabled) return
+				this.isIndeterminate = false
+				this.isChecked = !this.isChecked
+				this.$emit('checkboxSelected', {
+					checked: this.isChecked,
+					data: this.cellData
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	$uni-primary: #007aff !default;
+	$border-color: #DCDFE6;
+	$disable:0.4;
+
+	.uni-table-checkbox {
+		display: flex;
+		flex-direction: row;
+		align-items: center;
+		justify-content: center;
+		position: relative;
+		margin: 5px 0;
+		cursor: pointer;
+
+		// 多选样式
+		.checkbox__inner {
+			/* #ifndef APP-NVUE */
+			flex-shrink: 0;
+			box-sizing: border-box;
+			/* #endif */
+			position: relative;
+			width: 16px;
+			height: 16px;
+			border: 1px solid $border-color;
+			border-radius: 2px;
+			background-color: #fff;
+			z-index: 1;
+
+			.checkbox__inner-icon {
+				position: absolute;
+				/* #ifdef APP-NVUE */
+				top: 2px;
+				/* #endif */
+				/* #ifndef APP-NVUE */
+				top: 2px;
+				/* #endif */
+				left: 5px;
+				height: 7px;
+				width: 3px;
+				border: 1px solid #fff;
+				border-left: 0;
+				border-top: 0;
+				opacity: 0;
+				transform-origin: center;
+				transform: rotate(45deg);
+				box-sizing: content-box;
+			}
+
+			&.checkbox--indeterminate {
+				border-color: $uni-primary;
+				background-color: $uni-primary;
+
+				.checkbox__inner-icon {
+					position: absolute;
+					opacity: 1;
+					transform: rotate(0deg);
+					height: 2px;
+					top: 0;
+					bottom: 0;
+					margin: auto;
+					left: 0px;
+					right: 0px;
+					bottom: 0;
+					width: auto;
+					border: none;
+					border-radius: 2px;
+					transform: scale(0.5);
+					background-color: #fff;
+				}
+			}
+			&:hover{
+				border-color: $uni-primary;
+			}
+			// 禁用
+			&.is-disable {
+				/* #ifdef H5 */
+				cursor: not-allowed;
+				/* #endif */
+				background-color: #F2F6FC;
+				border-color: $border-color;
+			}
+
+			// 选中
+			&.is-checked {
+				border-color: $uni-primary;
+				background-color: $uni-primary;
+
+				.checkbox__inner-icon {
+					opacity: 1;
+					transform: rotate(45deg);
+				}
+
+				// 选中禁用
+				&.is-disable {
+					opacity: $disable;
+				}
+			}
+			
+		}
+	}
+</style>

+ 171 - 0
uni_modules/uni-table/components/uni-tr/uni-tr.vue

@@ -0,0 +1,171 @@
+<template>
+	<!-- #ifdef H5 -->
+	<tr class="uni-table-tr">
+		<th v-if="selection === 'selection' && ishead" class="checkbox" :class="{ 'tr-table--border': border }">
+			<table-checkbox :checked="checked" :indeterminate="indeterminate" :disabled="disabled" @checkboxSelected="checkboxSelected"></table-checkbox>
+		</th>
+		<slot></slot>
+		<!-- <uni-th class="th-fixed">123</uni-th> -->
+	</tr>
+	<!-- #endif -->
+	<!-- #ifndef H5 -->
+	<view class="uni-table-tr">
+		<view v-if="selection === 'selection' " class="checkbox" :class="{ 'tr-table--border': border }">
+			<table-checkbox :checked="checked" :indeterminate="indeterminate" :disabled="disabled" @checkboxSelected="checkboxSelected"></table-checkbox>
+		</view>
+		<slot></slot>
+	</view>
+	<!-- #endif -->
+</template>
+
+<script>
+	import tableCheckbox from './table-checkbox.vue'
+/**
+ * Tr 表格行组件
+ * @description 表格行组件 仅包含 th,td 组件
+ * @tutorial https://ext.dcloud.net.cn/plugin?id=
+ */
+export default {
+	name: 'uniTr',
+	components: { tableCheckbox },
+	props: {
+		disabled: {
+			type: Boolean,
+			default: false
+		},
+		keyValue: {
+			type: [String, Number],
+			default: ''
+		}
+	},
+	options: {
+		virtualHost: true
+	},
+	data() {
+		return {
+			value: false,
+			border: false,
+			selection: false,
+			widthThArr: [],
+			ishead: true,
+			checked: false,
+			indeterminate:false
+		}
+	},
+	created() {
+		this.root = this.getTable()
+		this.head = this.getTable('uniThead')
+		if (this.head) {
+			this.ishead = false
+			this.head.init(this)
+		}
+		this.border = this.root.border
+		this.selection = this.root.type
+		this.root.trChildren.push(this)
+		const rowData = this.root.data.find(v => v[this.root.rowKey] === this.keyValue)
+		if(rowData){
+			this.rowData = rowData
+		}
+		this.root.isNodata()
+	},
+	mounted() {
+		if (this.widthThArr.length > 0) {
+			const selectionWidth = this.selection === 'selection' ? 50 : 0
+			this.root.minWidth = this.widthThArr.reduce((a, b) => Number(a) + Number(b)) + selectionWidth
+		}
+	},
+	// #ifndef VUE3
+	destroyed() {
+		const index = this.root.trChildren.findIndex(i => i === this)
+		this.root.trChildren.splice(index, 1)
+		this.root.isNodata()
+	},
+	// #endif
+	// #ifdef VUE3
+	unmounted() {
+		const index = this.root.trChildren.findIndex(i => i === this)
+		this.root.trChildren.splice(index, 1)
+		this.root.isNodata()
+	},
+	// #endif
+	methods: {
+		minWidthUpdate(width) {
+			this.widthThArr.push(width)
+		},
+		// 选中
+		checkboxSelected(e) {
+			let rootData = this.root.data.find(v => v[this.root.rowKey] === this.keyValue)
+			this.checked = e.checked
+			this.root.check(rootData||this, e.checked,rootData? this.keyValue:null)
+		},
+		change(e) {
+			this.root.trChildren.forEach(item => {
+				if (item === this) {
+					this.root.check(this, e.detail.value.length > 0 ? true : false)
+				}
+			})
+		},
+		/**
+		 * 获取父元素实例
+		 */
+		getTable(name = 'uniTable') {
+			let parent = this.$parent
+			let parentName = parent.$options.name
+			while (parentName !== name) {
+				parent = parent.$parent
+				if (!parent) return false
+				parentName = parent.$options.name
+			}
+			return parent
+		}
+	}
+}
+</script>
+
+<style lang="scss">
+$border-color: #ebeef5;
+
+.uni-table-tr {
+	/* #ifndef APP-NVUE */
+	display: table-row;
+	transition: all 0.3s;
+	box-sizing: border-box;
+	/* #endif */
+}
+
+.checkbox {
+	padding: 0 8px;
+	width: 26px;
+	padding-left: 12px;
+	/* #ifndef APP-NVUE */
+	display: table-cell;
+	vertical-align: middle;
+	/* #endif */
+	color: #333;
+	font-weight: 500;
+	border-bottom: 1px $border-color solid;
+	font-size: 14px;
+	// text-align: center;
+}
+
+.tr-table--border {
+	border-right: 1px $border-color solid;
+}
+
+/* #ifndef APP-NVUE */
+.uni-table-tr {
+	::v-deep .uni-table-th {
+		&.table--border:last-child {
+			// border-right: none;
+		}
+	}
+
+	::v-deep .uni-table-td {
+		&.table--border:last-child {
+			// border-right: none;
+		}
+	}
+}
+
+/* #endif */
+</style>

+ 9 - 0
uni_modules/uni-table/i18n/en.json

@@ -0,0 +1,9 @@
+{
+	"filter-dropdown.reset": "Reset",
+	"filter-dropdown.search": "Search",
+	"filter-dropdown.submit": "Submit",
+	"filter-dropdown.filter": "Filter",
+	"filter-dropdown.gt": "Greater or equal to",
+	"filter-dropdown.lt": "Less than or equal to",
+	"filter-dropdown.date": "Date"
+}

+ 9 - 0
uni_modules/uni-table/i18n/es.json

@@ -0,0 +1,9 @@
+{
+	"filter-dropdown.reset": "Reiniciar",
+	"filter-dropdown.search": "Búsqueda",
+	"filter-dropdown.submit": "Entregar",
+	"filter-dropdown.filter": "Filtrar",
+	"filter-dropdown.gt": "Mayor o igual a",
+	"filter-dropdown.lt": "Menos que o igual a",
+	"filter-dropdown.date": "Fecha"
+}

+ 9 - 0
uni_modules/uni-table/i18n/fr.json

@@ -0,0 +1,9 @@
+{
+	"filter-dropdown.reset": "Réinitialiser",
+	"filter-dropdown.search": "Chercher",
+	"filter-dropdown.submit": "Soumettre",
+	"filter-dropdown.filter": "Filtre",
+	"filter-dropdown.gt": "Supérieur ou égal à",
+	"filter-dropdown.lt": "Inférieur ou égal à",
+	"filter-dropdown.date": "Date"
+}

+ 12 - 0
uni_modules/uni-table/i18n/index.js

@@ -0,0 +1,12 @@
+import en from './en.json'
+import es from './es.json'
+import fr from './fr.json'
+import zhHans from './zh-Hans.json'
+import zhHant from './zh-Hant.json'
+export default {
+	en,
+	es,
+	fr,
+	'zh-Hans': zhHans,
+	'zh-Hant': zhHant
+}

+ 9 - 0
uni_modules/uni-table/i18n/zh-Hans.json

@@ -0,0 +1,9 @@
+{
+	"filter-dropdown.reset": "重置",
+	"filter-dropdown.search": "搜索",
+	"filter-dropdown.submit": "确定",
+	"filter-dropdown.filter": "筛选",
+	"filter-dropdown.gt": "大于等于",
+	"filter-dropdown.lt": "小于等于",
+	"filter-dropdown.date": "日期范围"
+}

+ 9 - 0
uni_modules/uni-table/i18n/zh-Hant.json

@@ -0,0 +1,9 @@
+{
+	"filter-dropdown.reset": "重置",
+	"filter-dropdown.search": "搜索",
+	"filter-dropdown.submit": "確定",
+	"filter-dropdown.filter": "篩選",
+	"filter-dropdown.gt": "大於等於",
+	"filter-dropdown.lt": "小於等於",
+	"filter-dropdown.date": "日期範圍"
+}

+ 83 - 0
uni_modules/uni-table/package.json

@@ -0,0 +1,83 @@
+{
+  "id": "uni-table",
+  "displayName": "uni-table 表格",
+  "version": "1.2.3",
+  "description": "表格组件,多用于展示多条结构类似的数据,如",
+  "keywords": [
+    "uni-ui",
+    "uniui",
+    "table",
+    "表格"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": ""
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+"dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
+    "type": "component-vue"
+  },
+  "uni_modules": {
+    "dependencies": ["uni-scss","uni-datetime-picker"],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "n"
+        },
+        "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",
+          "字节跳动": "n",
+          "QQ": "y"
+        },
+        "快应用": {
+          "华为": "n",
+          "联盟": "n"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "y"
+        }
+      }
+    }
+  }
+}

+ 13 - 0
uni_modules/uni-table/readme.md

@@ -0,0 +1,13 @@
+
+
+## Table 表单
+> 组件名:``uni-table``,代码块: `uTable`。
+
+用于展示多条结构类似的数据
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-table)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 
+
+
+
+

+ 257 - 0
work/components/case/list.vue

@@ -0,0 +1,257 @@
+<template>
+  <view>
+	<view v-if="datalist.length>0">
+		<!-- 我的提案 -->
+		<block v-if="type==1">
+			<view class="caselist" v-for="(ite,idx) in datalist" :key='idx' @click="getDetail(ite.reservatId)">
+				<view class="tit overtwo">关于拓展市校合作,助推高质量发展的建议</view>
+				<view class="ftxt">
+					<text>提案类别</text>
+					<view>社会建设类/教育事业</view>
+				</view>
+				<view class="ftxt">
+					<text>提交时间</text>
+					<view>2024-02-22 14:12:38</view>
+				</view>
+				<view class="ftxt">
+					<text>提案状态</text>
+					<view class="co0b">审核中</view>
+				</view>
+				<view class="casebtn flexc">
+					<view class="ftit">办理状态<text>待反馈</text></view>
+					<view class="btn btn2">反馈意见</view>
+				</view>
+			</view>
+		</block>
+		<!-- 联名提案 -->
+		<block v-if="type==2">
+			<view class="caselist" v-for="(ite,idx) in datalist" :key='idx' @click="getDetail(ite.reservatId)">
+				<view class="tit overtwo">关于拓展市校合作,助推高质量发展的建议</view>
+				<view class="flex">
+					<view class="ftxt w50">
+						<text>提案者</text>
+						<view>何潇潇</view>
+					</view>
+					<view class="ftxt w50">
+						<text>案号</text>
+						<view>222</view>
+					</view>
+				</view>
+				<view class="ftxt">
+					<text>提案类别</text>
+					<view>社会建设类/教育事业</view>
+				</view>
+				<view class="ftxt">
+					<text>提交时间</text>
+					<view>2024-02-22 14:12:38</view>
+				</view>
+				<view class="ftxt">
+					<text>联名状态</text>
+					<view class="co0b">办理中</view>
+				</view>
+				<view class="casebtn flexc">
+					<view class="lftit">您的联名提案未反馈</view>
+					<!-- <view class="lftit">您的联名提案已反馈,查看<text class="lfdet">反馈详情</text></view> -->
+					<view class="btn  btn1">反对</view>
+					<view class="btn btn2">同意</view>
+					<view class="btn btn3">已同意</view>
+				</view>
+			</view>
+		</block>
+		<!-- 推荐优秀 -->
+		<block v-if="type==3">
+			<view class="caselist" v-for="(ite,idx) in datalist" :key='idx' @click="getDetail(ite.reservatId)">
+				<view class="tit overtwo">关于拓展市校合作,助推高质量发展的建议</view>
+				<view class="flex">
+					<view class="ftxt w50">
+						<text>提案者</text>
+						<view>何潇潇</view>
+					</view>
+					<view class="ftxt w50">
+						<text>案号</text>
+						<view>222</view>
+					</view>
+				</view>
+				<view class="ftxt">
+					<text>推荐截止</text>
+					<view>2024-02-22</view>
+				</view>
+				<view class="casebtn flexc">
+					<view class="lftit">该提案暂未推荐为优秀</view>
+					<!-- <view class="lftit">该提案已推荐为优秀,查看<text class="lfdet">推荐理由</text></view> -->
+					<!-- <view class="btn btn2"><image :src="zanimg" class="zanimg"></image>推荐优秀</view> -->
+					<view class="btn btn3" @click="getTuiFn('tjyx')"><image :src="nzanimg" class="zanimg"></image>推荐优秀</view>
+				</view>
+			</view>
+		</block>
+		<!-- 推荐重点 -->
+		<block v-if="type==4">
+			<view class="caselist" v-for="(ite,idx) in datalist" :key='idx' @click="getDetail(ite.reservatId)">
+				<view class="tit overtwo">关于拓展市校合作,助推高质量发展的建议</view>
+				<view class="flex">
+					<view class="ftxt w50">
+						<text>提案者</text>
+						<view>何潇潇</view>
+					</view>
+					<view class="ftxt w50">
+						<text>案号</text>
+						<view>222</view>
+					</view>
+				</view>
+				<view class="ftxt">
+					<text>推荐截止</text>
+					<view>2024-02-22</view>
+				</view>
+				<view class="casebtn flexc">
+					<view class="lftit">该提案暂未推荐为重点</view>
+					<!-- <view class="lftit">该提案已推荐为重点,查看<text class="lfdet">推荐理由</text></view> -->
+					<view class="btn btn2"><image :src="start" class="staimg"></image>推荐重点</view>
+					<!-- <view class="btn btn3"><image :src="nstart" class="staimg"></image>推荐重点</view> -->
+				</view>
+			</view>
+		</block>
+		<!-- 公开提案 -->
+		<block v-if="type==5">
+			<view class="caselist" v-for="(ite,idx) in datalist" :key='idx' @click="getDetail(ite.reservatId)">
+				<view class="tit overtwo">关于拓展市校合作,助推高质量发展的建议</view>
+				<view class="ftxt">
+					<text>案号</text>
+					<view>222</view>
+				</view>
+				<view class="ftxt">
+					<text>提案类别</text>
+					<view>社会建设类/教育事业</view>
+				</view>
+				<view class="ftxt">
+					<text>提交时间</text>
+					<view>2024-02-22 14:12:38</view>
+				</view>
+				<view class="ftxt">
+					<text>办理状态</text>
+					<view class="co0b">审核中</view>
+				</view>
+			</view>
+		</block>
+		<!-- 办理质量评议 -->
+		<block v-if="type==6">
+			<view class="caselist" v-for="(ite,idx) in datalist" :key='idx' @click="getDetail(ite.reservatId)">
+				<view class="tit overtwo">关于拓展市校合作,助推高质量发展的建议</view>
+				<view class="ftxt">
+					<text>案号</text>
+					<view>222</view>
+				</view>
+				<view class="ftxt">
+					<text>承办单位</text>
+					<view>市教育局</view>
+				</view>
+				<view class="casebtn flexc">
+					<view class="lftit">该提案暂未进行质量评议</view>
+					<!-- <view class="lftit">该提案已评议,查看<text class="lfdet">评议详情</text></view> -->
+					<view class="btn btn2" @click="getTuiFn('zlpy')"><image :src="flower" class="staimg"></image>质量评议</view>
+					<!-- <view class="btn btn3"><image :src="nflower" class="staimg"></image>已评议</view> -->
+				</view>
+			</view>
+		</block>
+		<view class="shax" v-if="wtdt">{{wtdt}}</view>
+	</view>
+	<block v-else>
+		<no-data></no-data>
+	</block>
+  </view>
+</template>
+
+<script>
+	import { selectDictValue } from '@/utils/common.js';
+	import noData from "@/components/nodata/nodata.vue"
+  export default {
+	props:{
+		datalist: {
+			type: Array,
+			default () {
+				return []
+			}
+		},
+		wtdt:{
+			type: String,
+			default () {
+				return ''
+			}
+		},
+		type:{
+			type: [String,Number],
+			default () {
+				return ''
+			}
+		},
+	},
+	components:{
+		noData
+	},
+	data(){
+		return{
+			start:require("@/work/static/images/start.png"),
+			nstart:require("@/work/static/images/nstart.png"),
+			zanimg:require("@/work/static/images/zanico.png"),
+			nzanimg:require("@/work/static/images/nzanico.png"),
+			flower:require("@/work/static/images/flower.png"),
+			nflower:require("@/work/static/images/nflower.png"),
+		}
+	},
+	onLoad: function() {
+	},
+	methods:{
+		getDetail(e){
+			this.$emit('getDetail',e)
+		},
+		getTuiFn(txt){
+			var obj={
+				type:txt,
+				id:1,
+			}
+			this.$emit('getTuiFn',obj)
+		},
+		typeFn(data){
+			if(data){
+				var newArr=[]
+				var astr=data.split('-')
+				astr.forEach(ite=>{
+					var a=ite.substring(0,5);
+					newArr.push(a)
+				})
+				return newArr.join('-')
+			}else{
+				return ''
+			}
+		},
+	},
+	
+  }
+</script>
+
+<style lang="scss" scoped>
+.caselist{background: #FFFFFF;border-radius: 30rpx;margin-top: 24rpx;padding: 38rpx 24rpx 8rpx;
+	.tit{font-weight: bold;font-size: 32rpx;color: #222327;margin-bottom: 42rpx;}
+	.ftxt{display: flex;align-items: flex-start;margin-bottom: 32rpx;
+		text{font-weight: bold;font-size: 26rpx;color: #CACACA;flex: 0 0 auto;min-width: 104rpx;text-align-last: justify;}
+		view{flex: 1;color: #343434;font-size: 26rpx;margin-left: 20rpx;line-height: 34rpx;}
+	}
+	.casebtn{padding: 24rpx 0;border-top: 2rpx solid #E6E6E6;
+		.ftit{font-weight: bold;font-size: 26rpx;color: #CACACA;flex: 1;
+			text{color: $com-cd3;margin-left: 20rpx;font-weight:500;min-width: 104rpx;text-align-last: justify;}
+		}
+		.lftit{font-weight: 500;font-size: 26rpx;color: #666666;flex:1;
+			.lfdet{color: $com-cd3;text-decoration: underline;}
+		}
+		.zanimg{width: 22rpx;height: 20rpx;margin-right: 14rpx;}
+		.staimg{width: 24rpx;height: 24rpx;margin-right: 14rpx;}
+		.btn{min-width: 118rpx;height: 52rpx;border-radius: 26rpx;font-weight: bold;flex: 0 0 auto;display: flex;align-items: center;
+font-size: 26rpx;margin-left: 26rpx;line-height: 52rpx;text-align: center;padding: 0 32rpx;box-sizing: border-box;
+		&.btn1{background: #FFEDED;color: #FF4141;}
+		&.btn2{background: #E4EEFF;color: #1D64E2;}
+		&.btn3{background: #ECECEC;color: #666666;}
+		
+		}
+	}
+}
+
+</style>

+ 777 - 0
work/components/popup/popup.vue

@@ -0,0 +1,777 @@
+<template>
+	<view>
+		<!-- 弹窗 -->
+		<view class="bgbox" @click="getClose" v-if="type"></view>
+		<!-- 弹窗 -->
+		<!-- 推荐优秀 -->
+		<view class="fixedbox" v-if="type=='tjyx'">
+			<view class="ttit">推荐为优秀</view>
+			<view  class="boxs">
+				<view class="flexcj mb14">
+					<view class="ttxt"><text class="cof0">*</text>推荐意见</view>
+					<view class="flexcc speech" @click="getSeep">
+						<image :src="speechimgs"></image>
+						<view>语音输入</view>
+					</view>
+				</view>
+				<textarea class="textar mb25" @blur="getBlur" v-model="yxly" placeholder="请输入推荐意见,也可点击右上角进行 语音输入…" maxlength="1000" ></textarea>
+			</view>
+			<view class="fixedbtn flexcj">
+				<view class="btns btn1" @click="getClose">取消</view>
+				<view class="btns btn2" @click="getyjSubmit">提交</view>
+			</view>
+		</view>
+		<!-- 质量评议 -->
+		<view class="fixedbox" v-if="type=='zlpy'">
+			<view class="ttit">办理质量评议</view>
+			<view  class="boxs">
+				<view class="mb20">
+					<view class="ttxt mb18"><text class="cof0">*</text> 推荐意见</view>
+					<uni-section title="本地数据" type="line">
+					     <uni-data-select
+					       v-model="value"
+					       :localdata="range"
+					       @change="change"
+					     ></uni-data-select>
+					</uni-section>
+				</view>
+				<view class="flexcj mb14">
+					<view class="ttxt"><text class="cof0">*</text>推荐意见</view>
+					<view class="flexcc speech" @click="getSeep">
+						<image :src="speechimgs"></image>
+						<view>语音输入</view>
+					</view>
+				</view>
+				<textarea class="textar mb25" @blur="getBlur" v-model="yxly" placeholder="请对办理情况进行评价,也可点击右上 角进行语音输入…" maxlength="1000" ></textarea>
+			</view>
+			<view class="fixedbtn flexcj">
+				<view class="btns btn1" @click="getClose">取消</view>
+				<view class="btns btn2" @click="getyjSubmit">提交</view>
+			</view>
+		</view>
+		<!-- 提案并案 -->
+		<view class="fixedbox" v-if="type=='taba'">
+			<view class="ttit">提案并案</view>
+			<view  class="boxs">
+				<view class="mb16">
+					<view class="ttxt mb18"><text class="cof0">*</text>是否并案</view>
+					<uni-section title="本地数据" type="line">
+					     <uni-data-select
+					       v-model="value"
+					       :localdata="sfrange"
+					       @change="change"
+					     ></uni-data-select>
+					</uni-section>
+				</view>
+				<view class="mb16">
+					<view class="ttxt mb18"><text class="cof0">*</text>并案后标题</view>
+					<input class="input"  placeholder="请输入并案后的标题"/>
+				</view>
+				<view class="mb16">
+					<view class="ttxt mb18"><text class="cof0">*</text>并案后理由</view>
+					<textarea class="textar " style="height: 186rpx;" @blur="getBlur" v-model="yxly" placeholder="请输入并案理由" maxlength="1000" ></textarea>
+				</view>
+				<view class="mb16">
+					<view class="flexcj mb10">
+						<view class="ttxt"><text class="cof0">*</text>添加附件</view>
+						<view class="fjadd"><lsj-upload
+							    ref="lsjUpload"
+							    childId="upload1"
+							    :width="width"
+							    :height="height"
+							    :option="option"
+							    :size="size"
+							    :formats="formats"
+							    :debug="debug"
+							    :instantly="instantly"
+							    @progress=""
+								@uploadEnd="onuploadEnd" >
+							        <view class="btn" :style="{width: width,height: height}">上传附件</view>
+							</lsj-upload>
+						</view>
+					</view>
+					<view class="ptb2" v-if="filelist&&filelist.length">
+						<view class="fjlists"  v-for="(ite,idx) in filelist" :key='idx'>
+							<view class="flext f15 c6" @click="getDown(ite.path)">
+								<view class="imgl"><image :src="fjimg" ></image></view>
+								<view class="tit">{{ite.fjName}}</view>
+							</view>
+							<!-- 删除 -->
+							<view class="delimg flex0" @click.stop="getDelFj(idx)">
+								<image :src="delimg"></image>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view class="fixedbtn flexcj">
+				<view class="btns btn1" @click="getClose">取消</view>
+				<view class="btns btn2" @click="getyjSubmit">提交</view>
+			</view>
+		</view>
+		
+		<!-- 语音识别 -->
+		<view class="bgbox bgvoice"  v-if="voiceflag"  @click="getClosevoice"></view>
+		<view class="voice" v-if="voiceflag">
+			<!-- <image :src="closeimg" class="choseimg" @click="getClosevoice"></image> -->
+			<view v-if="isShow">
+				<view v-if="sendLock" class="tip">
+					<view class="txt">试试这样说</view>
+					<view class="txt-bt">科技项目</view>
+				</view>
+				<view v-else>
+					<!-- @click="resultClick" -->
+					
+					<view class="res-txt" >
+						<!-- 实时翻译 -->
+						<text :style="{
+							color: (resultText == '正在识别中2<strong>.</strong>..' || resultText == '未检测到语音,请重试') ? 
+							'#919098' : 
+							'#2979ff'}">
+							{{resultText}}
+						</text>
+						<!-- <image v-if="resultText != '正在识别中.1..' && resultText != '未检测到语音,请重试'"
+							src="/static/img/xiaoshou.png" mode="widthFix"></image> -->
+					</view>
+				</view>
+			</view>
+		 
+			<view v-else class="tip">
+				<view v-if="!sendLock" class="tipbox">{{voicetext}}</view>
+				<view v-html="text" class="txt" style="color: #8e8d9a;"></view>
+				
+				<view v-if="!sendLock" class="prompt-loader">
+					<view class="em" :style="randomRgb()" v-for="(item,index) in 30" :key="index"></view>
+				</view>
+				<view v-else class="prompt-loader"></view>
+			</view>
+		 
+			<view class="btn" @longpress="handleRecordStart" @touchmove="handleTouchMove" @touchend="handleRecordStop">
+				<view class="btn-cont">
+					长按开始语音搜索
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { checkPermi, checkRole } from "@/utils/permission"; // 权限判断函数
+	const recorderManager = uni.getRecorderManager();
+	//播放录音
+	const innerAudioContext = uni.createInnerAudioContext();
+	innerAudioContext.autoplay = true;
+	import config from '@/config'
+	const baseUrl = config.baseUrl
+	import { getToken } from '@/utils/auth'
+	export default{
+		props:{
+			type: {
+				type: String,
+				default () {
+					return ''
+				}
+			},
+			status:{
+				type: String,
+				default () {
+					return 'add'
+				}
+			},
+			iteminfo:{},
+			xmjzinfo:{},
+		},
+		data(){
+			return{
+				//附件
+				option: {
+				    // 上传服务器地址,需要替换为你的接口地址
+				    // url: baseUrl+'/common/upload', // 该地址非真实路径,需替换为你项目自己的接口地址
+				    // 上传附件的key
+				    name: 'file',
+				    // 根据你接口需求自定义请求头,默认不要写content-type,让浏览器自适配
+				    header: {
+				        // 示例参数可删除
+				        'Authorization':  'Bearer ' + getToken(),
+				    },
+				    // 根据你接口需求自定义body参数
+				    formData: {}
+				},
+				// 选择文件后是否立即自动上传,true=选择后立即上传
+				instantly: true,
+				// 必传宽高且宽高应与slot宽高保持一致
+				width: '',
+				height: '48rpx',
+				// 限制允许上传的格式,空串=不限制,默认为空
+				formats: 'doc,docx,xls,ppt,txt,pdf,zip,rar,word',
+				// 文件上传大小限制
+				size: 100,
+				// 文件数量限制 默认10
+				count: 5,
+				// 文件回显列表
+				files: new Map(),
+				// 微信小程序Map对象for循环不显示,所以转成普通数组,不要问为什么,我也不知道
+				wxFiles: [],
+				// 是否打印日志
+				debug: false,
+				filelist:[{fjName:'关于加强危化品安全监管的建议文档.word',path:"123"}], 
+				fjimg:require("@/work/static/images/fjimg.png"),
+				delimg:require("@/work/static/images/delimg.png"),
+				
+				speechimgs:require('@/work/static/images/voice.png'),
+				range: [{ value: 0, text: "满意" },{ value: 1, text: "不满意" },],
+				sfrange: [{ value: 0, text: "是" },{ value: 1, text: "否" },],
+				value: 0,
+				
+				
+				voiceflag:false,
+				voiceToken: '',
+				timer: null,
+				text: '',
+				resultText: '正在识别中...',
+				startPoint: {},
+				sendLock: true,
+				isShow: true,
+				
+				voicetext:'',
+				options: {}, // 语音转文字的设置
+				partialResult:'',//临时语音
+			}
+		},
+		watch:{
+			// iteminfo(val){
+			// 	var that=this;
+			// 	that.psnr='';
+			// 	// console.log(val,0)
+			// 	if(val.zcpsnr){
+			// 		that.psnr=val.zcpsnr;
+			// 	}else if(val.psnr){
+			// 		that.psnr=val.psnr;
+			// 	}
+			// },
+			sendLock(newVal, oldVal) {
+				var that=this;
+				recorderManager.onStop(res => {
+					if (newVal) return //上锁不发送
+					//解锁发送网络请求
+					setTimeout(function(res){
+						if(!that.voicetext&&!that.partialResult){
+							that.resultText='未检测到语音,请重试'
+						}else{
+							that.resultText=that.voicetext||that.partialResult;
+							var cursor=that.cursor;
+							if(cursor==0){
+								if(that.seeptype=='sgll'){
+									that.sgll=that.sgll+that.resultText;
+								}else{
+									that.psnr=that.psnr+that.resultText;
+								}
+								
+							}else{
+								if(that.seeptype=='sgll'){
+									that.sgll=that.sgll.slice(0,cursor)+that.resultText+that.sgll.slice(cursor);
+								}else{
+									that.psnr=that.psnr.slice(0,cursor)+that.resultText+that.psnr.slice(cursor);
+								}
+								
+							}
+							
+							
+							that.voiceflag=false;
+						}
+						that.sendLock=true;
+						
+					},1000)
+					// console.log(res.tempFilePath, '获取录音文件')
+				});
+			},
+		},
+		onShow() {
+			
+		},
+		mounted() {
+			var that=this;
+					// #ifdef APP-PLUS
+					// 监听语音识别事件
+					plus.speech.addEventListener('recognizing', this.onRecognizing, false);
+					// #endif	
+							  
+					recorderManager.onStop(function(res) {
+						//录音后的回调函数
+						if(!this.sendLock){
+							console.log(that.voicetext,that.partialResult)
+						}
+						console.log('recorder stop' + JSON.stringify(res));
+					});  
+							  
+		},
+		methods:{
+			checkPermi, checkRole,
+			onuploadEnd(item) {
+				var newobj={}
+				var responseText=JSON.parse(item.responseText)
+				newobj.fjName=responseText.originalFilename;
+				newobj.path=responseText.fileName;
+				this.filelist.push(newobj)
+					this.datainfo.zsyzFjList=JSON.parse(JSON.stringify(this.filelist))
+			},
+			getDelFj(idx){
+				var that=this;
+				uni.showModal({
+					title: '确认删除',
+					content: "是否确认删除",
+					cancelText: '取消',
+					confirmText: '确认',
+					success: function(res) {
+						if (res.confirm) {
+							that.filelist.splice(idx,1)
+							that.datainfo.zsyzFjList=JSON.parse(JSON.stringify(that.filelist))
+						} else if (res.cancel) {
+						}
+					}
+				});
+			},
+			getDown(e){
+					  uni.showLoading({
+					  	title: '加载中'
+					  });
+				var url=baseUrl+e;
+				uni.downloadFile({
+					url: url,//文件的下载路径
+					success(result) {
+							uni.hideLoading()
+						var filePath = result.tempFilePath;
+						   uni.openDocument({
+						     filePath: filePath,
+						     showMenu: true,
+						     success: function (res) {
+						       // console.log('打开文档成功');
+						     }
+						   });
+					},
+					fail(res) {uni.hideLoading()}
+				})
+			},
+			
+			
+			getBlur(e){
+				this.cursor=e.detail.cursor;
+			},
+			// 语音转文字
+			getClosevoice(){
+				this.voiceflag=false
+			},
+			onRecognizing(e){
+				this.partialResult=e.partialResult;
+			},
+			// 录音转文字
+			handleVoice() {
+				// console.log('语音输入')
+				let _this = this;
+				// _this.psnr = '';
+				this.options.engine = 'baidu'
+				// this.options.timeout = 60 * 1000; //超时时间
+				this.options.continue = true;//语音识别是否采用持续模式
+				this.options.punctuation = true; // 是否需要标点符号 
+				this.options.userInterface = false; // 是否显示语音界面
+				plus.speech.startRecognize(this.options, (s) => {
+					_this.voicetext+=s;
+					console.log(s,9)
+					// plus.speech.stopRecognize(); // 关
+				});
+			},
+			//长按录音方法
+			handleRecordStart(e) {
+				this.voicetext='';
+				this.partialResult='';
+				this.startPoint = e.touches[0]; //记录长按时开始点信息,后面用于计算上划取消时手指滑动的距离。
+				recorderManager.start({duration: 60000}); //开始录音
+				this.handleVoice()
+				this.text = `<text style="color:#333">上划取消搜索</text>`;
+				this.sendLock = false; //长按时不上锁。
+				this.isShow = false;
+				this.resultText = '正在识别中...';
+				// 按钮
+				// console.log(67)
+			},
+			//结束录音 (手指松开)时触发
+			handleRecordStop(e) {
+				var that=this;
+				this.isShow = true;
+				setTimeout(function(){
+					plus.speech.stopRecognize();
+					recorderManager.stop(); //结束录音
+				},1300)
+				
+			},
+			//上划取消搜索
+			handleTouchMove(e) {
+				let moveLenght = e.touches[e.touches.length - 1].clientY - this.startPoint.clientY;
+				if (Math.abs(moveLenght) > 50) {
+					this.text = `松开手指,<text style="color:#333">取消搜索</text>`;
+					this.sendLock = true; //触发了上滑取消搜索,上锁
+					this.isShow = false;
+				} else {
+					this.text = `<text style="color:#333">上划取消搜索</text>`;
+					this.sendLock = false; //上划距离不足,可以搜索,不上锁
+					this.isShow = false;
+				}
+			},
+			//获取录音结果子传父
+			resultClick() {
+				if (this.resultText == '正在识别中...' || this.resultText == '未检测到语音,请重试') return;
+				
+				this.$emit('voiceResult', this.resultText)
+			},
+			//弹窗关闭之后的操作,点击遮罩层或关闭按钮
+			// afterHide() {
+			// 	this.sendLock = true;
+			// 	this.$emit('closePopup');
+			// 	clearInterval(this.timer);
+			// 	this.resultText = '正在识别中...';
+			// },
+			randomRgb() {
+				let R = Math.floor(Math.random() * 130 + 110);
+				let G = Math.floor(Math.random() * 130 + 110);
+				let B = Math.floor(Math.random() * 130 + 110);
+				return {
+					background: `rgb(${R},${G},${B}, 1)`
+				};
+			},
+			// 语音转文字
+			getSeep(e){
+				this.seeptype=e||'';
+				this.voicetext='';
+				this.partialResult='';
+				this.voiceflag=true;		
+			},
+			ontStart() {
+				// recorderManager.start();
+						console.log('startRecognize');
+			
+					},
+			onEnd() {
+				// recorderManager.stop();
+						console.log('endRecognize');
+			
+					},
+			getClose(){
+				this.$emit("getClose")
+			},
+			// 意见
+			getyjSubmit(){
+				var data={};
+				var that=this;
+				var str=""
+				if(!this.shenflag){
+					data.shjg='3';
+					str='不通过'
+					if(!this.psnr){
+						this.$toast('请输入反馈意见')
+						return
+					}else{
+						data.shyj=this.psnr
+					}
+				}else{
+					str='通过'
+					data.shjg='2',
+					data.shyj=this.psnr
+				}
+				// 确认框
+				uni.showModal({
+					title: '确认审核',
+					content: "是否确认"+str+'审核',
+					cancelText: '取消',
+					confirmText: '确认',
+					success: function(res) {
+						if (res.confirm) {
+							that.$emit("getyjSubmit",data)
+						} else if (res.cancel) {
+							// console.log('用户点击取消');
+						}
+					}
+				});
+				
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+// 弹窗
+// .choseimg{width: 34rpx;height: 34rpx;position: absolute;left: 36rpx;top: 54rpx;z-index: 22;}
+ // .uni-stat__select{background-color: #EEEEEE;}
+.fixedbox /deep/ .uni-select{border: none;background-color: #EEEEEE;height: 88rpx;}
+.fixedbox{position: fixed;left: 48rpx;right: 48rpx;background: #fff;border-radius: 10rpx;min-height: 468rpx;top: 50%;transform: translateY(-50%);z-index: 10;padding: 32rpx 40rpx 50rpx;max-height: calc(100vh - 300rpx);overflow: auto;box-sizing: border-box;
+	.ttit{position: relative;text-align: center;font-weight: bold;font-size: 36rpx;color: #222327;margin-bottom: 38rpx;}
+	.boxs{
+		.ttxt{font-weight: bold;font-size: 30rpx;color: #222327;
+			text{margin-right: 6rpx;}
+		}
+		// 语音按钮
+		.speech{
+			width: 190rpx;height:56rpx;background: linear-gradient(180deg, #4480EB 0%, #1D64E2 100%);border-radius: 28rpx;
+			image{width: 20rpx;height: 26rpx;margin-right: 12rpx;}
+			view{font-size: 26rpx;font-weight: 500;color: #ffffff;}
+		}
+		.input{height: 88rpx;width: 100%;background: #EEEEEE;border-radius: 10rpx;font-size: 30rpx;font-weight: 500;color: #222327;padding: 0 24rpx;}
+		.textar{width: 100%;border-radius: 10rpx;padding: 26rpx 30rpx;font-size: 30rpx;color: #222327;height: 400rpx;background: #EEEEEE;}
+	}
+	.fixedbtn{
+		.btns{width: 254rpx;height: 80rpx;border-radius: 40rpx;font-weight: bold;
+font-size: 30rpx;display: flex;align-items: center;justify-content: center;box-sizing: border-box;
+			&.btn1{color: #1D64E2;border: 2rpx solid #1D64E2;}
+			&.btn2{color: #ffffff;background: #1D64E2;}
+		}
+		
+	}
+}
+// 附件
+.fjadd{
+	.btn{font-weight: bold;font-size: 30rpx;color: #1D64E2;margin: 0;display: flex;align-items: center;}
+}
+// 附件
+.fjlists {display: flex;align-items: flex-start;justify-content: space-between;margin-bottom: 12rpx;
+		image{margin-right: 18rpx;flex: 0 0 auto;}
+		.imgl{width: 40rpx;height: 40rpx;display: flex;align-items: center;justify-content: center;margin-right: 16rpx;
+			image{width: 26rpx;height: 24rpx;}
+			}
+			.tit{font-size: 26rpx;color: #343434;font-weight: 500;margin-top: 4rpx;}
+		.delimg{width: 40rpx;height:40rpx;margin-left: 16rpx;display: flex;align-items: center;justify-content: center;
+			image{width: 26rpx;height: 26rpx;}
+		}
+	}
+
+// 语音识别
+.bgvoice{z-index: 1900;}
+.voice {
+		min-height: 500rpx;
+		padding: 100rpx 60rpx 0 60rpx;
+		position: relative;
+		background-color: #fff;
+		position: fixed;
+		left:0;right:0;bottom:0;z-index: 2000;
+		padding-bottom: 180rpx;
+		.res-txt {
+			text-align: center;
+			margin-top: 40rpx;
+			font-size: 36rpx;
+			color: #919098;
+ 
+			image {
+				display: block;
+				margin: auto;
+				margin-top: 10rpx;
+				width: 60rpx;
+				animation: bounce-down 2.6s linear infinite;
+			}
+		}
+ 
+		.tip {
+			margin-top: 15rpx;
+			text-align: center;
+ 
+			.txt {
+				font-size: 36rpx;
+				color: #151823;
+			}
+ 
+			.txt-bt {
+				margin-top: 20rpx;
+				color: #919098;
+			}
+		}
+ 
+		.btn {
+			width: 50%;
+			height: 80rpx;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			color: #fff;
+			border-radius: 50rpx;
+			background: #3484fd;
+			position: absolute;
+			bottom: 80rpx;
+			left: 50%;
+			transform: translateX(-50%);
+ 
+			.btn-cont {
+				display: flex;
+				align-items: center;
+			}
+		}
+	}
+ 
+	@-webkit-keyframes bounce-down {
+		25% {
+			-webkit-transform: translateY(-10px);
+		}
+ 
+		50%,
+		100% {
+			-webkit-transform: translateY(0);
+		}
+ 
+		75% {
+			-webkit-transform: translateY(13px);
+		}
+	}
+	.content{background-color: #f5f5f5;position: fixed;left: 0;right: 0;bottom: 0;z-index: 10000;height: 500rpx;}
+	/* 语音动画 */
+	.prompt-loader {
+		width: 100%;
+		height: 35px;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		margin: 30rpx auto;
+	}
+ 
+	.prompt-loader .em {
+		height: 15%;
+		width: 2px;
+		float: left;
+		display: block;
+		background: #333333;
+	}
+ 
+	.prompt-loader .em:last-child {
+		margin-right: 0px;
+	}
+ 
+	.prompt-loader .em:nth-child(1) {
+		animation: load 1.3s 0.4s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(2) {
+		animation: load 1.3s 0.2s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(3) {
+		animation: load 1.3s 0.6s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(4) {
+		animation: load 1.3s 0.8s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(5) {
+		animation: load 1.3s 0.6s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(6) {
+		animation: load 1.3s 0.4s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(7) {
+		animation: load 1.3s 0.2s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(8) {
+		animation: load 1.3s 0.6s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(9) {
+		animation: load 1.3s 0.2s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(10) {
+		animation: load 1.3s 0.4s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(11) {
+		animation: load 1.3s 0.6s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(12) {
+		animation: load 1.3s 0.8s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(13) {
+		animation: load 1.3s 1s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(14) {
+		animation: load 1.3s 0.2s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(15) {
+		animation: load 1.3s 0.6s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(16) {
+		animation: load 1.3s 0.6s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(17) {
+		animation: load 1.3s 0.8s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(18) {
+		animation: load 1.3s 0.2s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(19) {
+		animation: load 1.3s 0.4s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(20) {
+		animation: load 1.3s 0.6s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(21) {
+		animation: load 1.3s 0.5s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(22) {
+		animation: load 1.3s 0.2s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(23) {
+		animation: load 1.3s 0.4s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(24) {
+		animation: load 1.3s 0.6s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(25) {
+		animation: load 1.3s 0.8s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(26) {
+		animation: load 1.3s 0.2s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(27) {
+		animation: load 1.3s 0.4s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(28) {
+		animation: load 1.3s 0.1s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(29) {
+		animation: load 1.3s 0.3s infinite linear;
+	}
+ 
+	.prompt-loader .em:nth-child(30) {
+		animation: load 1.3s 0.6s infinite linear;
+	}
+ 
+	@keyframes load {
+		0% {
+			height: 15%;
+		}
+ 
+		50% {
+			height: 100%;
+		}
+ 
+		100% {
+			height: 15%;
+		}
+	}
+
+</style>

+ 65 - 0
work/components/zb-table/all.js

@@ -0,0 +1,65 @@
+export const column1 = [
+    { type:'index', fixed:true,width:100 },
+    { name: 'name', label: '姓名',fixed:true,width:160,emptyString:'--' },
+    { name: 'yw', label: '语文',sorter:true},
+    // { name: 'sex', label: '性别',filters:{0:'男',1:'女'}},
+    { name: 'sx', label: '数学',sorter:true },
+    { name: 'yy', label: '英语',sorter:true },
+	{ name: 'operation', type:'operation',label: '操作',renders:[
+		{
+		    name:'编辑',
+		    func:'edit' // func 代表子元素点击的事件 父元素接收的事件 父元素 @edit
+		  },
+		  {
+		    name:'删除',
+		    type:'warn',
+		    func:"dele"
+		  },
+	]},
+
+]
+// 历史
+export const column2 = [
+    { name: 'updateTime', label: '更新日期',fixed:true,width:180,},
+    { name: 'height', label: '身高(cm)'},
+    { name: 'weight', label: '体重(kg)'},
+    // { name: 'degreeMyopia', label: '视力'},
+]
+export const column3 = [
+    { type:'selection', fixed:true,width:100 },
+    { name: 'name', label: '姓名',fixed:true,width:160,emptyString:'--' },
+    { name: 'age', label: '年纪',},
+    { name: 'sex', label: '性别',filters:{0:'男',1:'女'}},
+    { name: 'address', label: '地址' },
+    { name: 'date', label: '日期', },
+    { name: 'province', label: '省份' },
+    { name: 'city', label: '城市' },
+    { name: 'zip', label: '邮编' },
+]
+
+// 添加的时候
+export const column4 = [
+    // { type:'selection', fixed:true,width:50 },
+    { type:'index', fixed:true,width:60,align:'center', },
+    { name: 'scoreDataName', label: '姓名',fixed:true,width:140,emptyString:'--',align:'center' },
+    { name: 'yuwen', label: '语文',sorter:true,align:'center',width:112},
+    { name: 'shuxue', label: '数学',sorter:true,align:'center',width:112 },
+    { name: 'yingyu', label: '英语',sorter:true,align:'center',width:112 },
+    { name: 'wuli', label: '物理',sorter:true,align:'center',width:112 },
+    { name: 'huaxue', label: '化学',sorter:true,align:'center',width:112 },
+    { name: 'shengwu', label: '生物',sorter:true,align:'center',width:112 },
+    { name: 'lishi', label: '历史',sorter:true,align:'center',width:112 },
+    { name: 'dili', label: '地理',sorter:true,align:'center',width:112 },
+    { name: 'zhengzhi', label: '政治',sorter:true,align:'center',width:112 },
+    { name: 'tiyu', label: '体育',sorter:true,align:'center',width:112 },
+    { name: 'kexue', label: '科学',sorter:true,align:'center',width:112 },
+    { name: 'meishu', label: '美术',sorter:true,align:'center',width:112 },
+    { name: 'yinyue', label: '音乐',sorter:true,align:'center',width:112 },
+    { name: 'yishu', label: '艺术',sorter:true,align:'center',width:112 },
+    { name: 'wenzong', label: '文综',sorter:true,align:'center',width:112 },
+    { name: 'lizong', label: '理综',sorter:true,align:'center',width:112 },
+    { name: 'zongfen', label: '总分',sorter:true,align:'center',width:112 },
+    { name: 'scoreSort', label: '排名',sorter:true,align:'center',width:112 },
+]
+
+

+ 180 - 0
work/components/zb-table/components/table-checkbox.vue

@@ -0,0 +1,180 @@
+<template>
+	<view class="uni-table-checkbox" @click.stop="selected">
+		<view v-if="!indeterminate" class="checkbox__inner" :class="{'is-checked':isChecked,'is-disable':isDisabled}">
+			<view class="checkbox__inner-icon"></view>
+		</view>
+		<view v-else class="checkbox__inner checkbox--indeterminate">
+			<view class="checkbox__inner-icon"></view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: 'TableCheckbox',
+		emits:['checkboxSelected'],
+		props: {
+			indeterminate: {
+				type: Boolean,
+				default: false
+			},
+			checked: {
+				type: [Boolean,String],
+				default: false
+			},
+			disabled: {
+				type: Boolean,
+				default: false
+			},
+			index: {
+				type: Number,
+				default: -1
+			},
+			cellData: {
+				type: Object,
+				default () {
+					return {}
+				}
+			}
+		},
+		watch:{
+			checked(newVal){
+				if(typeof this.checked === 'boolean'){
+					this.isChecked = newVal
+				}else{
+					this.isChecked = true
+				}
+			},
+			indeterminate(newVal){
+				this.isIndeterminate = newVal
+			}
+		},
+		data() {
+			return {
+				isChecked: false,
+				isDisabled: false,
+				isIndeterminate:false
+			}
+		},
+		created() {
+			if(typeof this.checked === 'boolean'){
+				this.isChecked = this.checked
+			}
+			this.isDisabled = this.disabled
+		},
+		methods: {
+			selected() {
+				if (this.isDisabled) return
+				this.isIndeterminate = false
+				this.isChecked = !this.isChecked
+        console.log('===',this.indeterminate,this.isChecked)
+				this.$emit('checkboxSelected', {
+					checked: this.isChecked,
+					data: this.cellData
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	$checked-color: #007aff;
+	$border-color: #DCDFE6;
+	$disable:0.4;
+
+	.uni-table-checkbox {
+		display: flex;
+		flex-direction: row;
+		align-items: center;
+		justify-content: center;
+		position: relative;
+		margin: 10rpx 0;
+		cursor: pointer;
+
+		// 多选样式
+		.checkbox__inner {
+			/* #ifndef APP-NVUE */
+			flex-shrink: 0;
+			box-sizing: border-box;
+			/* #endif */
+			position: relative;
+			width: 32rpx;
+			height: 32rpx;
+			border: 2rpx solid $border-color;
+			border-radius: 4rpx;
+			background-color: #fff;
+			z-index: 1;
+
+			.checkbox__inner-icon {
+				position: absolute;
+				/* #ifdef APP-NVUE */
+				top: 4rpx;
+				/* #endif */
+				/* #ifndef APP-NVUE */
+				top: 4rpx;
+				/* #endif */
+				left: 10rpx;
+				height: 14rpx;
+				width: 6rpx;
+				border: 2rpx solid #fff;
+				border-left: 0;
+				border-top: 0;
+				opacity: 0;
+				transform-origin: center;
+				transform: rotate(45deg);
+				box-sizing: content-box;
+			}
+
+			&.checkbox--indeterminate {
+				border-color: $checked-color;
+				background-color: $checked-color;
+
+				.checkbox__inner-icon {
+					position: absolute;
+					opacity: 1;
+					transform: rotate(0deg);
+					height: 4rpx;
+					top: 0;
+					bottom: 0;
+					margin: auto;
+					left: 0px;
+					right: 0px;
+					bottom: 0;
+					width: auto;
+					border: none;
+					border-radius: 4rpx;
+					transform: scale(0.5);
+					background-color: #fff;
+				}
+			}
+			&:hover{
+				border-color: $checked-color;
+			}
+			// 禁用
+			&.is-disable {
+				/* #ifdef H5 */
+				cursor: not-allowed;
+				/* #endif */
+				background-color: #F2F6FC;
+				border-color: $border-color;
+			}
+
+			// 选中
+			&.is-checked {
+				border-color: $checked-color;
+				background-color: $checked-color;
+
+				.checkbox__inner-icon {
+					opacity: 1;
+					transform: rotate(45deg);
+				}
+
+				// 选中禁用
+				&.is-disable {
+					opacity: $disable;
+				}
+			}
+
+		}
+	}
+</style>

+ 78 - 0
work/components/zb-table/components/table-h5-summary.vue

@@ -0,0 +1,78 @@
+<template>
+  <view class="table-h5-footer top-header-uni" :style="{paddingRight:`${scrollbarSize}rpx`}">
+    <scroll-view class="zb-table-headers"
+                 @scroll="handleFooterTableScrollLeft"
+                 scroll-x="true"
+                 scroll-y="false"
+                 id="tableFooterHeaders"
+                 scroll-anchoring="true"
+                 :scroll-left="headerFooterTableLeft"
+                 style="padding-bottom: 0px;
+						background: #fafafa;height: 100%">
+      <view class="zb-table-fixed" >
+        <view class="zb-table-thead" style="position: relative;" >
+          <view class="item-tr">
+            <view
+                class="item-th"
+                :style="{
+	                              width:`${item.width?item.width:'200'}rpx`,
+															  flex:index===transColumns.length-1?1:'none',
+															  minWidth:`${item.width?item.width:'200'}rpx`,
+															  borderRight:`${border?'2rpx solid #e8e8e8':''}`,
+															  borderTop:`${border?'2rpx solid #e8e8e8':''}`,
+															  textAlign:item.align||'left'
+														  }"
+                v-for="(item,index) in transColumns" :key="index">
+              {{ sums[index] }}
+            </view>
+          </view>
+        </view>
+      </view>
+    </scroll-view>
+  </view>
+</template>
+<script>
+import summary from '../js/summary.js'
+export default {
+  name:'table-footer',
+  mixins:[summary],
+}
+</script>
+<style lang="scss" scoped>
+.table-h5-footer {
+  background: #fafafa;
+  /*每个页面公共css */
+  scroll-view ::-webkit-scrollbar {
+    display: none !important;
+    width: 0 !important;
+    height: 0 !important;
+    -webkit-appearance: none;
+    background: transparent;
+  }
+  //第二种
+  ::-webkit-scrollbar{
+    display: none;
+  }
+  .item-tr{
+    display: flex;
+  }
+  .item-th{
+    padding-left: 8rpx;
+    line-height: 78rpx;
+    height: 80rpx;
+    //display: flex;
+    //align-items: center;
+    box-sizing: border-box;
+    flex-shrink: 0;
+    width: 200rpx;
+    padding-right: 8rpx;
+    word-break: keep-all;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    overflow-wrap: break-word;
+    border-bottom: 2rpx solid #e8e8e8;
+  }
+
+}
+</style>

+ 59 - 0
work/components/zb-table/components/table-side-summary.vue

@@ -0,0 +1,59 @@
+<template>
+  <view class="zb-table-header" style="display: flex;" >
+    <view class="item-tr" >
+      <view class='item-td'
+            :style="{
+	                       width:`${item.width?item.width:'200'}rpx`,
+	                       borderRight:`${border?'2rpx solid #e8e8e8':''}`,
+	                       textAlign:item.align||'left'
+	                      }"
+            :key="`${index}`"
+            v-for="(item,index) in fixedLeftColumns">
+        <template >
+          {{sums[index]}}
+        </template>
+      </view>
+    </view>
+  </view>
+
+</template>
+<script>
+import summary from '../js/summary.js'
+export default {
+  mixins:[summary]
+}
+</script>
+<style lang="scss" scoped>
+.zb-table-header {
+  overflow: hidden;
+  background: #fafafa;
+  .item-th{
+    padding-left: 8rpx;
+    line-height: 78rpx;
+    height: 80rpx;
+    //display: flex;
+    //align-items: center;
+    box-sizing: border-box;
+  }
+}
+.item-tr{
+  display: flex;
+  box-sizing: border-box;
+}
+.item-td{
+  flex-shrink: 0;
+  width: 200rpx;
+  padding-left:8rpx;
+  height: 80rpx;
+  line-height: 80rpx;
+  padding-right: 8rpx;
+  box-sizing: border-box;
+  word-break: keep-all;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  overflow-wrap: break-word;
+  border-bottom: 2rpx solid #e8e8e8;
+  background: rgb(250, 250, 250);
+}
+</style>

+ 77 - 0
work/components/zb-table/components/table-summary.vue

@@ -0,0 +1,77 @@
+<template>
+  <view class="zb-table-footer" style="height: 80rpx;">
+    <view class="zb-table-fixed" >
+      <view class="zb-table-thead" style="position: relative;" >
+        <view class="item-tr">
+          <view
+              :class="['item-th',index <fixedLeftColumns.length&&'zb-stick-side']"
+              :style="{
+	                              left:`${item.left}rpx`,
+	                              width:`${item.width?item.width:'200'}rpx`,
+															  flex:index===transColumns.length-1?1:'none',
+															  minWidth:`${item.width?item.width:'200'}rpx`,
+															   borderRight:`${border?'2rpx solid #e8e8e8':''}`,
+															  borderTop:`${border?'2rpx solid #e8e8e8':''}`,
+															   textAlign:item.align||'left'
+														  }"
+              v-for="(item,index) in transColumns" :key="index">
+            <template>
+              {{ sums[index]||item.emptyString }}
+            </template>
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+<script>
+  import summary from '../js/summary.js'
+  export default {
+    mixins:[summary]
+  }
+</script>
+<style lang="scss" scoped>
+  .zb-table-footer {
+    background: #fafafa;
+    width: fit-content;
+    min-width: 100%;
+    position: sticky;
+    bottom: 0;
+    z-index: 2;
+    .item-tr{
+      display: flex;
+      min-width: 100%;
+    }
+    .item-th{
+      padding-left: 8rpx;
+      line-height: 78rpx;
+      height: 80rpx;
+      //display: flex;
+      //align-items: center;
+      box-sizing: border-box;
+      flex-shrink: 0;
+      width: 200rpx;
+      padding-right: 8rpx;
+      word-break: keep-all;
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      overflow-wrap: break-word;
+      border-bottom: 2rpx solid #e8e8e8;
+    }
+    .zb-table-fixed{
+      min-width: 100%;
+
+    }
+    .zb-stick-side{
+      position: sticky;
+      bottom:0 ;
+      left: 0;
+      z-index: 2;
+      //border-right: solid 1rpx #dbdbdb;
+      box-sizing: border-box;
+      background: #fafafa;
+      //box-shadow: 6px 0 6px -4px #ccc;
+    }
+  }
+</style>

+ 50 - 0
work/components/zb-table/components/zb-load-more.vue

@@ -0,0 +1,50 @@
+<template >
+  <view class="zb-load-more">
+    <image :src="base64Flower" style="" class="loading-custom-image"></image>
+    <text>正在加载中...</text>
+  </view>
+</template>
+<script>
+const base64Flower = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkBAMAAACCzIhnAAAAKlBMVEVHcEzDw8Ovr6+pqamUlJTCwsKenp61tbWxsbGysrLNzc2bm5u5ubmjo6MpovhuAAAACnRSTlMA/P79/sHDhiZS0DxZowAABBBJREFUWMPtl89rE0EUx7ctTXatB3MI1SWnDbUKPUgXqh4ED8Uf7KUVSm3ooVSpSii0Fn/gD4j4o+APiEoVmos9FO2celiqZVgwgaKHPQiCCkv+F99kM7Ozm5kxq1dfD91k9pPve9/3ZjbRNHHok/mKli4eIPNgSuRObuN9SqSEzM20iGnm0yIbqCuV7NSSSIV7uyPM6JMBYdeTOanh/QihJYZsUCSby+VkMj2AvOt0rAeQAwqE3lfKMZVlQCZk1QOCKkkVPadITCfIRNKxfoJI5+0OIFtJx14CMSg1mRSDko7VAfksRQzEbGYqxOJcVTWMCH2I1/IACNW0PWU2M8cmAVHtnH5mM1VRWtwKZjOd5JbF6s1IbaYqaotjNlPHgDAnlAizubTR6ovMYn052g/U5qcmOpi0WL8xTS/3IfSet5m8MEr5ajjF5le6dq/OJpobrdY0t3i9QgefWrxW9/1BLhk0E9m8FeUMhhXal499iD0eQRfDF+ts/tttORRerfp+oV7f4xJj82iUYm1Yzod+ZQEAlS/8mMBwKebVmCVp1f0JLS6zKd17+iwRKTARVg2SHtz3iEbBH+Q+U28zW2Jiza8Tjb1YFoYZMsJyjDqp3M9XBQdSdPLFdxEpvOB37JrHcmR/y9+LgoTlCFGZEa2sc6d4PGlweEa2JSVPoVm+IfGG3ZL037iV9oH+P+Jxc4HGVflNq1M0pivao/EopO4b/ojVCP9GjmiXOeS0DOn1o/iiccT4ORnyvBGF3yUywkQajW4Ti0SGuiy/wVSg/L8w+X/8Q+hvUx8Xd90z4oV5a1i88MbFWHz0WZZ1UrTwBGPX3Rat9AFiXRMRjoMdIdJLEOt2h7jrYOzgOamKZSWSNspOS0X8SAqRYmxRL7sg4eLzYmNehcxh3uoyud/BH2Udux4ywxFTc1xC7Mgf4vMhc5S+kSH3Y7yj+qpwIWSoPTVCOOPVthGx9FbGqrwFw6wSFxJr+17zeKcztt3u+2roAEVgUjDd+AHGuxHy2rZHaa8JMkTHEeyi85ANPO9j9BVuBRD2FY5LDMo/Sz/2hReqGIs/KiFin+CsPsYO/yvM3jL2vE8EbX7/Bf8ejtr2GLN65bioAdgLd8Bis/mD5GmP2qeqyo2ZwQEOtAjRIDH7mBKpUcMoApbZJ5UIxkEwxyMZyMxW/uKFvHCFR3SSmerHyDNQ2dF4JG6zIMpBgLfjSF9x1D6smFcYnGApjmSLICO3ecCDWrQ48geba9DI3STy2i7ax6WIB62fSyIZIiO3GFQqSURp8wCo7GhJBGwuSovJBNjb7kT6FPVnIa9qJ2Ko+l9mefGIdinaMp0yC1URYiwsdfNE45EuA5Cx9EhalfvN5s+UyItm81vaB3p4joniN+SCP7Qc1hblAAAAAElFTkSuQmCC';
+export default {
+  data(){
+    return{
+      base64Flower
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+.zb-load-more {
+  width: 100%;
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  z-index: 999;
+  background: white;
+  display: flex;
+  height: 80rpx;
+  flex-shrink: 0;
+  align-items: center;
+  justify-content: center;
+  .loading-custom-image{
+    color: #a4a4a4;
+    margin-right: 8rpx;
+    width: 48rpx;
+    height: 48rpx;
+    /* #ifndef APP-NVUE */
+    animation: loading-circle 1s linear infinite;
+    /* #endif */
+  }
+  @keyframes loading-circle {
+    0% {
+      -webkit-transform: rotate(0deg);
+      transform: rotate(0deg);
+    }
+    100% {
+      -webkit-transform: rotate(360deg);
+      transform: rotate(360deg);
+    }
+  }
+}
+</style>

+ 88 - 0
work/components/zb-table/js/summary.js

@@ -0,0 +1,88 @@
+export default {
+    props:{
+        scrollbarSize:{
+            type:Number,
+            default:0
+        },
+        fixedLeftColumns:{
+            type:Array,
+            default:()=>[]
+        },
+        data:{
+            type:Array,
+            default:()=>[]
+        },
+        transColumns:{
+            type:Array,
+            default:()=>[]
+        },
+        border:{
+            type:Boolean,
+            default:false
+        },
+        showSummary:{
+            type:Boolean,
+            default:false
+        },
+        summaryMethod:{
+            type:Function
+        },
+        sumText:{
+            type:String,
+            default:'合计'
+        },
+        headerFooterTableLeft:{
+            type:Number,
+            default:0
+        },
+        handleFooterTableScrollLeft:Function,
+    },
+    data(){
+        return{
+            sums:[]
+        }
+    },
+    watch:{
+        'data':{
+            deep:true,
+            immediate:true,
+            handler(newValue,oldValue){
+                let sums = [];
+                if (this.summaryMethod) {
+                    sums = this.summaryMethod({ columns: this.transColumns, data: this.data });
+                } else {
+                    this.transColumns.forEach((column, index) => {
+                        if (index === 0) {
+                            sums[index] = this.sumText;
+                            return;
+                        }
+                        const values = this.data.map(item => Number(item[column.name]));
+                        const precisions = [];
+                        let notNumber = true;
+                        values.forEach(value => {
+                            if (!isNaN(value)) {
+                                notNumber = false;
+                                let decimal = ('' + value).split('.')[1];
+                                precisions.push(decimal ? decimal.length : 0);
+                            }
+                        });
+                        const precision = Math.max.apply(null, precisions);
+                        if (!notNumber) {
+                            sums[index] = values.reduce((prev, curr) => {
+                                const value = Number(curr);
+                                if (!isNaN(value)) {
+                                    return parseFloat((prev + curr).toFixed(Math.min(precision, 20)));
+                                } else {
+                                    return prev;
+                                }
+                            }, 0);
+                        } else {
+                            sums[index] = '';
+                        }
+                    });
+                }
+                this.sums = sums
+            },
+        }
+    }
+}

+ 51 - 0
work/components/zb-table/js/util.js

@@ -0,0 +1,51 @@
+/**
+ * 获取滚动条宽度
+ */
+let cached = undefined;
+
+export const getScrollbarSize = fresh => {
+	// #ifdef H5
+	
+    if (fresh || cached === undefined) {
+        let inner = document.createElement("div");
+        let innerStyle = inner.style;
+
+        innerStyle.width = "100%";
+        innerStyle.height = "200px";
+
+        let outer = document.createElement("div");
+        let outerStyle = outer.style;
+
+        outerStyle.position = "absolute";
+        outerStyle.top = 0;
+        outerStyle.left = 0;
+        outerStyle.pointerEvents = "none";
+        outerStyle.width = "200px";
+        outerStyle.height = "150px";
+        outerStyle.visibility = "hidden";
+
+        outer.appendChild(inner);
+        document.body.appendChild(outer);
+
+        // 设置子元素超出部分隐藏
+        outerStyle.overflow = "hidden";
+
+        let width1 = inner.offsetWidth;
+
+        // 设置子元素超出部分滚动
+        outer.style.overflow = "scroll";
+
+        let width2 = inner.offsetWidth;
+
+        if (width1 === width2) {
+            width2 = outer.clientWidth;
+        }
+
+        document.body.removeChild(outer);
+
+        cached = width1 - width2;
+    }
+	//#endif
+
+    return cached;
+};

+ 1255 - 0
work/components/zb-table/zb-table.vue

@@ -0,0 +1,1255 @@
+<template>
+	<!-- #ifdef H5 || APP-PLUS -->
+	<view :class="['zb-table','zb-table-fixed-header',!border&&(bodyTableLeft>50||headerTableLeft>50)&&'scroll-left-fixed']">
+	  <view class="zb-table-content" style="flex: 1">
+	    <view class="zb-table-scroll" style="height: 100%;">
+	      <template v-if="showHeader">
+	        <view class="zb-table-header top-header-uni" :style="{paddingRight:`${scrollbarSize}px`}"
+          >
+	          <scroll-view class="zb-table-headers"
+							@touchmove.stop="getMove"
+	                       @scroll="handleTableScrollLeft"
+	                       scroll-x="true"
+	                       scroll-y="false"
+	                       id="tableHeaders"
+	                       scroll-anchoring="true"
+	                       :scroll-left="headerTableLeft"
+	                       style="
+						height: 100%">
+	            <view class="zb-table-fixed" >
+	              <view class="zb-table-thead" style="position: relative;" >
+	                <view class="item-tr">
+	                  <view
+	                      @click.stop="sortAction(item,index)"
+	                      class="item-th"
+	                      :style="[{
+	                              width:`${item.width?item.width:'100'}px`,
+															  flex:index===transColumns.length-1?1:'none',
+															  minWidth:`${item.width?item.width:'100'}px`,
+															  borderRight:`${border?'1px solid #e8e8e8':''}`,
+															  borderRight:`${(scrollbarSize&&index===transColumns.length-1)?'':border?'1px solid #e8e8e8':''}`,
+															  borderTop:`${border?'1px solid #e8e8e8':''}`,
+															  textAlign:item.align||'left'
+														  },getHeaderCellStyle(item,index)]"
+	                      v-for="(item,index) in transColumns" :key="index">
+                      <template v-if="item.type==='selection'">
+                        <view class="checkbox-item">
+                          <tableCheckbox
+                              :indeterminate="indeterminate" :checked="checkedAll" @checkboxSelected="checkboxSelectedAll"></tableCheckbox>
+                        </view>
+                      </template>
+                      <template v-else>
+                        {{ item.label }}
+                        <view class="sorter-table" v-if="item.sorter">
+                          <view :class="['sorter-table-icon',item.sorterMode==='_asc'&&`sorting${item.sorterMode||''}`]"></view>
+                          <view :class="['sorter-table-icon',item.sorterMode==='_desc'&&`sorting${item.sorterMode||''}`]"></view>
+                        </view>
+                      </template>
+                    </view>
+	                </view>
+	              </view>
+	            </view>
+	          </scroll-view>
+	        </view>
+	      </template>
+	      <template v-if="!data.length">
+	        <view class="no-data">暂无数据~~</view>
+	      </template>
+	      <scroll-view
+            class="zb-table-body" ref="tableBody"	scroll-x="true"	scroll-y="true"	id="tableBody"
+	                   :lower-threshold="40"
+	                   :upper-threshold="10"
+					   @touchmove.stop="getMove"
+                     @scrolltolower="scrolltolower"
+	                   @scrolltoupper="(e)=>debounce(scrollToLeft)(e)"
+                     @scroll="handleBodyScroll"	:scroll-left="bodyTableLeft"	:scroll-top="bodyScrollTop"
+                     :style=" `height: calc(100% - ${showSummary?80:40}px)`" >
+	          <view class="zb-table-fixed">
+	            <view class="zb-table-tbody">
+	              <view  :class="['item-tr',highlight&&isHighlight(item,index)?'current-row':'']"
+                       @click.stop="rowClick(item,index)"
+	                     v-for="(item,index) in transData" :key="item.key" >
+	                <view
+	                    :style="[{
+									              width:`${ite.width?ite.width:'100'}px`,
+															  flex:i===transColumns.length-1?1:'none',
+															  minWidth:`${ite.width?ite.width:'100'}px`,
+															  borderRight:`${border?'1px solid #e8e8e8':''}`,
+                                textAlign:ite.align||'left',
+														  },cellStyle&&getCellStyle(item,ite,index,i)]"
+
+	                    :class="['item-td',stripe?(index % 2) != 0?'odd':'even':'']"
+	                    v-for="(ite,i) in transColumns" :key="i">
+	                  <template  v-if="ite.type==='operation'">
+	                    <view style="display: flex;align-items: center;height: 100%">
+	                      <view
+                            v-for="ren,ind in permission(item,ite.renders,index)"
+	                          :key="ind"
+	                          @click.stop="$emit(ren.func,item,index)"
+	                          :style="{
+	                          display:'flex',
+	                          alignItems: 'center',
+	                          marginRight:ite.renders.length>1?'8px':'0'
+	                        }">
+                          <template v-if="ren.type==='custom'">
+                            <view :class="ren.class||''" style="cursor: pointer">
+                              {{ren.name}}
+                            </view>
+                          </template>
+                          <template v-else>
+                            <button
+                                :class="ren.class||''"
+                                :type="ren.type||'primary'" :size="ren.size||'mini'">{{ren.name}}</button>
+                          </template>
+	                      </view>
+	                    </view>
+	                  </template>
+                    <template v-else-if="ite.type==='selection'">
+                      <view class="checkbox-item">
+                        <tableCheckbox @checkboxSelected="(e)=>checkboxSelected(e,item)" :cellData="item" :checked="item.checked"/>
+                      </view>
+                    </template>
+                    <template v-else-if="ite.type==='index'">
+                      {{index+1}}
+                    </template>
+                  <template v-else-if="ite.type==='img'">
+                    <view class="checkbox-item">
+                      <image
+                    @click.stop="previewImage(item,item[ite.name],index)"
+                    v-if="item[ite.name]"
+                    :show-menu-by-longpress="false"
+                    :src="item[ite.name]" style="width: 40px;height:30px; " mode="aspectFit"></image>
+                    <text v-else>{{ite.emptyString}}</text>
+                    </view>
+                  </template>
+	                  <template  v-else>
+<!--                      {{ ite.filters?itemFilter(item,ite):(item[ite.name]==null||item[ite.name]==='')?ite.emptyString:item[ite.name] }}-->
+                      {{ ite.filters?itemFilter(item,ite):formatterAction(item,ite,index,i) }}
+	                  </template>
+	                </view>
+	              </view>
+	            </view>
+	          </view>
+	        </scroll-view>
+       <table-h5-summary
+           :scrollbarSize="scrollbarSize"
+           :data="data"
+           :handleFooterTableScrollLeft="handleFooterTableScrollLeft"
+           :headerFooterTableLeft="headerFooterTableLeft"
+           v-if="showSummary"
+           :showSummary="showSummary"
+           :transColumns="transColumns"
+           :border="border"
+           :summary-method="summaryMethod"
+           :sumText="sumText"
+           :fixedLeftColumns="fixedLeftColumns"/>
+	    </view>
+	    <view class="zb-table-fixed-left"
+            v-if="isFixedLeft"
+            :style=" {height:  `calc(100% - ${scrollbarSize}px)`}"
+      >
+	      <template v-if="showHeader">
+	        <view class="zb-table-header" style="display: flex">
+	          <view class="item-tr"
+                  style=""
+                  @click.stop="rowClick(item,index)"
+                  v-for="(item,index) in fixedLeftColumns" :key="index">
+	            <view
+	                :style="{
+	               width:`${item.width?item.width:'100'}px`,
+	               borderRight:`${border?'1px solid #e8e8e8':''}`,
+	               borderTop:`${border?'1px solid #e8e8e8':''}`,
+                textAlign:item.align||'left'
+	            }"
+	                @click.stop="sortAction(item,index)"
+	                class="item-th"
+	            >
+                <template v-if="item.type==='selection'">
+                  <view class="checkbox-item">
+                    <tableCheckbox
+                        :indeterminate="indeterminate" :checked="checkedAll" @checkboxSelected="checkboxSelectedAll"></tableCheckbox>
+                  </view>
+                </template>
+                <template v-else>
+                  {{ item.label }}
+                  <view class="sorter-table" v-if="item.sorter">
+                    <view :class="['sorter-table-icon',item.sorterMode==='_asc'&&`sorting${item.sorterMode||''}`]"></view>
+                    <view :class="['sorter-table-icon',item.sorterMode==='_desc'&&`sorting${item.sorterMode||''}`]"></view>
+                  </view>
+                </template>
+              </view>
+	          </view>
+
+	        </view>
+	      </template>
+        <scroll-view
+            scroll-y="true"
+            id="leftTableFixed"
+            :upper-threshold="15"
+            @scrolltoupper="(e)=>scrollToFixedLeft(e)"
+            @scroll="leftFixedScrollAction"
+            :scroll-top="leftFiexScrollTop"
+            class="zb-table-body-inner"
+            :style=" `height: calc(100% - ${showSummary?80:40}px)`">
+          <view class="zb-table-fixed">
+            <view class="zb-table-tbody">
+              <view
+                  :class="['item-tr',stripe?(i % 2) != 0?'odd':'even':'',highlight&&isHighlight(ite,i)?'current-row':'']"
+                    v-for="(ite,i) in transData"
+                    @click.stop="rowClick(ite,i)"
+                    :key="ite.key"
+                    style="">
+                <view class='item-td'
+                      :style="[{
+	                       width:`${item.width?item.width:'100'}px`,
+	                       borderRight:`${border?'1px solid #e8e8e8':''}`,
+	                       textAlign:item.align||'left'
+	                      },cellStyle&&getCellStyle(ite,item,i,index)]"
+                      :key="index"
+                      v-for="(item,index) in fixedLeftColumns">
+                  <template v-if="item.type==='selection'">
+                    <view class="checkbox-item">
+                      <tableCheckbox @checkboxSelected="(e)=>checkboxSelected(e,ite)" :cellData="ite" :checked="ite.checked"/>
+                    </view>
+                  </template>
+                  <template v-else-if="item.type==='index'">
+                    {{i+1}}
+                  </template>
+                  <template v-else>
+                    {{ite[item.name]||item.emptyString}}
+                  </template>
+                </view>
+              </view>
+            </view>
+          </view>
+        </scroll-view>
+        <table-side-summary
+            :scrollbarSize="scrollbarSize"
+            v-if="showSummary&&!(scrollbarSize>0)"
+            :data="data"
+            :showSummary="showSummary"
+            :transColumns="transColumns"
+            :border="border"
+            :summary-method="summaryMethod"
+            :sumText="sumText"
+            :fixedLeftColumns="fixedLeftColumns"/>
+	    </view>
+	  </view>
+    <zb-load-more v-if="isLoadMore&&!completeLoading"/>
+	</view>
+	<!-- #endif -->
+	<!-- #ifndef H5 || APP-PLUS -->
+	<view class="zb-table-applet">
+	  <view class="zb-table-content">
+      <scroll-view
+		<!-- #ifdef MP-ALIPAY -->
+		@scroll="scrollAlipay"
+		<!-- #endif  -->
+
+          @scrolltolower="scrolltolower"
+					<!-- #ifdef MP-ALIPAY -->
+                   style=" height: 100%;overflow-x:scroll"
+				   <!-- #endif  -->
+				   <!-- #ifndef MP-ALIPAY -->
+				   style=" height: 100%"
+				   <!-- #endif  -->
+                   scroll-y="true"
+				   scroll-x="true">
+	    <view class="zb-table-scroll" >
+	      <template v-if="showHeader">
+	        <view class="zb-table-header top-header-uni" style="">
+	            <view class="zb-table-fixed" >
+	              <view class="zb-table-thead" style="position: relative;" >
+	                <view class="item-tr">
+	                  <view
+	                      @click.stop="sortAction(item,index)"
+	                      :class="['item-th',index <fixedLeftColumns.length&&'zb-stick-side']"
+	                      :style="{
+	                              left:`${item.left}px`,
+	                              width:`${item.width?item.width:'100'}px`,
+															  flex:index===transColumns.length-1?1:'none',
+															  minWidth:`${item.width?item.width:'100'}px`,
+															   borderRight:`${border?'1px solid #e8e8e8':''}`,
+															  borderTop:`${border?'1px solid #e8e8e8':''}`,
+															   textAlign:item.align||'left'
+														  }"
+	                      v-for="(item,index) in transColumns" :key="index">
+                      <template v-if="item.type==='selection'">
+                        <view class="checkbox-item">
+                          <tableCheckbox
+                              :indeterminate="indeterminate" :checked="checkedAll" @checkboxSelected="checkboxSelectedAll"></tableCheckbox>
+                        </view>
+                      </template>
+                      <template v-else>
+                        {{ item.label||'' }}
+                        <view class="sorter-table" v-if="item.sorter">
+                          <view :class="['sorter-table-icon',item.sorterMode==='_asc'&&`sorting${item.sorterMode||''}`]"></view>
+                          <view :class="['sorter-table-icon',item.sorterMode==='_desc'&&`sorting${item.sorterMode||''}`]"></view>
+                        </view>
+                      </template>
+                    </view>
+	                </view>
+	              </view>
+	            </view>
+	        </view>
+	      </template>
+	      <template v-if="!data.length">
+	        <view class="no-data">暂无数据~~</view>
+	      </template>
+          <view class="zb-table-fixed">
+            <view class="zb-table-tbody">
+              <view  :class="['item-tr',highlight&&isHighlight(item,index)?'current-row':'']"
+                     @click.stop="rowClick(item,index)"
+                     v-for="(item,index) in transData" :key="item.key" >
+                <view
+                    :style="[{
+                      left:`${ite.left}px`,
+                      width:`${ite.width?ite.width:'100'}px`,
+                      flex:i===transColumns.length-1?1:'none',
+                      minWidth:`${ite.width?ite.width:'100'}px`,
+                      borderRight:`${border?'1px solid #e8e8e8':''}`,
+                      textAlign:ite.align||'left',
+                    },getCellStyle(item,ite,index,i)]"
+                    :class="['item-td', i <fixedLeftColumns.length&&'zb-stick-side',stripe?(index % 2) != 0?'odd':'even':'']"
+                    v-for="(ite,i) in transColumns" :key="i">
+                  <template  v-if="ite.type==='operation'">
+                    <view style="display: flex;align-items: center;height: 100%">
+                      <view
+                          v-for="ren,ind in permission(item,ite.renders,index)"
+                          :key="ind"
+                          @click.stop="$emit(ren.func,item,index)"
+                          :style="{
+	                          display:'flex',
+	                          alignItems: 'center',
+	                          marginRight:ite.renders.length>1?'8px':'0'
+	                        }">
+                        <template v-if="ren.type==='custom'">
+                          <view :class="ren.class||''" style="cursor: pointer">
+                            {{ren.name}}
+                          </view>
+                        </template>
+                        <template v-else>
+                          <button
+                              :class="ren.class||''"
+                              :type="ren.type||'primary'" :size="ren.size||'mini'">{{ren.name}}</button>
+                        </template>
+                      </view>
+                    </view>
+                  </template>
+                  <template v-else-if="ite.type==='selection'">
+                    <view class="checkbox-item">
+                      <tableCheckbox @checkboxSelected="(e)=>checkboxSelected(e,item)" :cellData="item" :checked="item.checked"/>
+                    </view>
+                  </template>
+                  <template v-else-if="ite.type==='img'">
+                    <image
+                        @click.stop="previewImage(item,item[ite.name],index)"
+                        v-if="item[ite.name]"
+                        :show-menu-by-longpress="false"
+                        :src="item[ite.name]" style="width: 40px;height:30px; " mode="aspectFit"></image>
+                    <text v-else>{{ite.emptyString}}</text>
+                  </template>
+                  <template v-else-if="ite.type==='index'">
+                    {{index+1}}
+                  </template>
+                  <template  v-else>
+<!--                    {{ ite.filters?itemFilter(item,ite):(item[ite.name]==null||item[ite.name]==='')?ite.emptyString:item[ite.name] }}-->
+                    {{ ite.filters?itemFilter(item,ite):formatterAction(item,ite,index,i) }}
+                  </template>
+                </view>
+              </view>
+            </view>
+          </view>
+        <table-summary
+            v-if="showSummary"
+            :data="data"
+            :showSummary="showSummary"
+            :fixedLeftColumns="fixedLeftColumns"
+            :transColumns="transColumns"
+            :border="border"
+            :summary-method="summaryMethod"
+            :sumText="sumText"
+        />
+	    </view>
+      </scroll-view>
+	  </view>
+    <zb-load-more v-if="isLoadMore&&!completeLoading"/>
+	</view>
+	<!-- #endif -->
+</template>
+<script>
+import TableCheckbox from './components/table-checkbox.vue'
+import TableSummary from "./components/table-summary.vue";
+import TableSideSummary from "./components/table-side-summary.vue";
+import TableH5Summary from './components/table-h5-summary'
+import ZbLoadMore from './components/zb-load-more'
+
+import {getScrollbarSize} from "./js/util";
+export default {
+  components:{
+    TableCheckbox,
+    TableSummary,
+    TableSideSummary,
+    TableH5Summary,
+    ZbLoadMore
+  },
+  props:{
+    highlight:{
+      type:Boolean,
+      default:false
+    },
+    itemDate:{
+      type:Object,
+      default:()=>{}
+    },
+    columns:{
+      type:Array,
+      default:()=>[]
+    },
+    showSummary:{
+      type:Boolean,
+      default:false
+    },
+    isShowLoadMore:{
+      type:Boolean,
+      default:false
+    },
+    data:{
+      type:[Object,Array],
+      default:()=>[]
+    },
+    sumText:{
+      type:String,
+      default:'合计'
+    },
+    showHeader:{
+      type:Boolean,
+      default:true
+    },
+    border:{
+      type:Boolean,
+      default:false
+    },
+    stripe:{
+      type:Boolean,
+      default:true
+    },
+    fit:{
+      type:Boolean,
+      default:false
+    },
+    rowKey:[String, Function],
+    summaryMethod:Function,
+    pullUpLoading:Function,
+    formatter:Function,
+    cellStyle:Function,
+    cellHeaderStyle:Function,
+    permissionBtn:Function,
+  },
+  computed:{
+    loadMoreHeight(){
+      return this.isLoadMore?40:0
+    },
+    fixedLeftColumns(){
+      let arr = []
+      for(let i=0;i<this.columns.length;i++){
+        let item = this.columns[i]
+        if(item.fixed){
+          arr.push(item)
+        }else {
+          break
+        }
+      }
+      return arr
+    },
+    itemfilters(){
+      return(item,ite)=>{
+        if(item[ite.name]==null){
+          return ite.emptyString
+        }
+        return item[ite.name]
+      }
+    },
+    scrollbarSize(){
+		// #ifdef H5
+      return getScrollbarSize()
+	  // #endif
+
+	  // #ifndef H5
+	  return 0
+	  // #endif
+    },
+    isFixedLeft(){
+      if(!this.columns.length){
+        return false
+      }
+      if(!this.data.length){
+        return false
+      }
+      let [firstArr] = this.columns
+      return !!firstArr.fixed;
+    },
+    transColumns(){
+      if(this.fit){
+        this.columns.forEach(column=>{
+          if(column.type==="operation"&&column.renders){
+			      let str = ''
+            column.renders.map((item)=>{
+              str+=item.name
+            })
+            column.width = this.getTextWidth(str)+column.renders.length*40
+          }else if(column.type==="img"){
+			   }else if(column.type==="selection"){
+			}else{
+			let arr = [this.getTextWidth(column.label)]
+            this.data.forEach(data=>{
+              let str = (data[column.name]+'')
+			  if(str==='undefined'){
+				   arr.push(30)
+			  }else{
+				   let width = this.getTextWidth(str)
+				   arr.push(width)
+			  }
+            })
+			      column.width = Math.max(...arr)+20
+          }
+        })
+      }
+      let number = 0
+      this.columns.forEach((item,index)=>{
+        if(item.type==="operation"&&item.renders&&!item.width){
+          let str = ''
+          item.renders.map((item)=>{
+            str+=item.name
+          })
+          item.width = this.getTextWidth(str)+item.renders.length*40
+        }
+        if(item.fixed){
+          if(index===0){
+            item.left = 0
+            number+=item.width
+          }else {
+            item.left = number
+            number+=item.width
+          }
+        }
+        item.emptyString = item.emptyString||'--'
+      })
+      return this.columns
+    },
+    transData(){
+      let flag = this.columns.some(item=>item.type==='selection')
+      this.data.forEach((item,index)=>{
+        if(flag){
+          if(item.checked){
+            if(!this.selectArr.length){
+              this.selectArr.push(item)
+            }
+          }
+        }
+        if(this.rowKey){
+          if(typeof this.rowKey==='function'){
+            item.key = Object.freeze(this.rowKey(item))||Date.now()
+          }else {
+            item.key = Object.freeze(item[this.rowKey])||Date.now()
+          }
+        }else {
+          item.key = index
+        }
+      })
+      if(flag&&this.data.length){
+        let le = this.data.filter(item=>item.checked).length
+        if(le){
+          if(le===this.data.length){
+            this.checkedAll = true
+          }else {
+            this.indeterminate = true
+          }
+        }
+      }
+      return this.data
+    },
+    isHighlight(){
+      return (item,index)=>{
+        if(this.rowKey){
+          return item.key === this.currentRow['key']
+        }else{
+          return index === this.currentRowIndex
+        }
+      }
+    },
+    getHeaderCellStyle() {
+      return (column,  columnIndex,childIndex)=>{
+        const cellStyle = this.cellHeaderStyle;
+        if(typeof cellStyle==='function'){
+          return cellStyle({ column, columnIndex})
+        }
+        return {}
+      }
+    },
+    getCellStyle() {
+     return (row, column, rowIndex, columnIndex)=>{
+       const cellStyle = this.cellStyle;
+       if(typeof cellStyle==='function'){
+         return cellStyle({row, column, rowIndex, columnIndex})
+       }
+       return {}
+     }
+    },
+  },
+  data() {
+    return {
+      button:[],
+	    alipayScrollTop:0,
+      alipayScrollOldTop:0,
+      alipayFlag:false,
+      bodyTableLeft:0,
+      headerTableLeft:0,
+      lastScrollLeft:0,
+      isLoadMore:false,
+      headerFooterTableLeft:0,
+      leftFiexScrollTop:0,
+      bodyScrollTop:0,
+      currentDriver:null,
+      currentDriver1:null,
+      bodyTime:null,
+      currentRowIndex:null,
+      currentRow: {},
+      bodyTime1:null,
+      headerTime:null,
+      debounceTime:null,
+      operation:{},
+      completedFlag:false,
+      selectArr:[],
+      indeterminate:false,
+      checkedAll:false,
+      completeLoading:false,
+      aliTime:null,
+    }
+  },
+  created(){
+  },
+  mounted(){
+  },
+  methods: {
+getMove(){
+	
+},
+    formatterAction(row,column,rowIndex,columnIndex){
+      if(column.formatter&&typeof this.formatter==='function'){
+        return this.formatter(row,column,rowIndex,columnIndex)
+      }
+      return (row[column.name]==null||row[column.name]==='')?column.emptyString:row[column.name]
+    },
+    permission(item,renders,index){
+      if(this.permissionBtn&&typeof this.permissionBtn==='function'){
+        return this.permissionBtn(item,renders,index)
+      }
+      return renders
+    },
+    pullUpCompleteLoading(type){
+      this.isLoadMore = false
+      if(type==='ok'){
+        this.completeLoading = true
+      }
+    },
+    scrollAlipay(e){
+
+      if(!this.alipayScrollOldTop){
+        this.alipayScrollOldTop = e.detail.scrollTop
+      }
+      this.aliTime&&clearTimeout(this.aliTime)
+      this.aliTime = setTimeout(()=>{
+
+        if(this.alipayFlag&&e.detail.scrollTop>this.alipayScrollOldTop){
+          this.pullLoad()
+        }
+        this.alipayFlag = false
+        this.alipayScrollOldTop = null
+      },500)
+    },
+    pullLoad(){
+      if(this.isShowLoadMore){
+        this.isLoadMore = true
+        this.$emit('pullUpLoading')
+        let that = this
+        this.pullUpLoading&&this.pullUpLoading.call(this.$parent.$parent, (type)=>{
+          that.isLoadMore = false
+          if(type==='ok'){
+            that.completeLoading=true
+          }
+        })
+      }
+
+    },
+    scrolltolower(e){
+      this.alipayFlag = true
+      if(e.detail.direction==='bottom'){
+        this.pullLoad()
+      }
+
+      // this.pullUpLoading.call(this.$parent)
+    },
+	  previewImage(item,url,current){
+		  uni.previewImage({
+			  current,
+			  urls:[url]
+		  })
+	  },
+    resetHighlight(){
+      this.currentRowIndex = null
+      this.currentRow = {}
+    },
+    rowClick(row,index){
+      if(this.highlight){
+        this.currentRowIndex = index
+        this.currentRow = row
+        this.$emit('currentChange',row,index)
+      }
+      this.$emit('rowClick',row,index)
+    },
+    checkboxSelectedAll(e){
+      this.indeterminate = false
+      if(e.checked){
+        this.selectArr = []
+        this.checkedAll = true
+        this.data.forEach(item=>{
+          // this.$set(item,'checked',true)
+          item.checked = true
+          this.selectArr.push(item)
+        })
+      }else{
+        this.checkedAll = false
+        this.data.forEach(item=>{
+          this.$set(item,'checked',false)
+        })
+        this.selectArr = []
+      }
+      // #ifndef H5 || APP-PLUS
+      this.$forceUpdate()
+      // #endif
+      this.$emit('toggleAllSelection',e.checked,this.selectArr)
+    },
+    checkboxSelected(e,item){
+      // #ifdef H5 || APP-PLUS
+      this.$set(item,'checked',e.checked)
+      // #endif
+      // #ifndef H5 || APP-PLUS
+      this.data.forEach(item=>{
+        if(item.key===e.data.key){
+          item.checked = e.checked
+        }
+      })
+      // #endif
+      item.checked = e.checked
+      e.data.checked = e.checked
+      if(e.checked){
+        this.selectArr.push(e.data)
+      }else{
+        this.selectArr = this.selectArr.filter(item=>item.key!==e.data.key)
+      }
+      if(this.selectArr.length===this.transData.length){
+        this.indeterminate = false
+        this.checkedAll = true
+      }else{
+        this.indeterminate = true
+        this.checkedAll = false
+      }
+      if(!this.selectArr.length){
+        this.checkedAll = false
+        this.indeterminate = false
+      }
+      // #ifndef H5 || APP-PLUS
+      this.$forceUpdate()
+      // #endif
+      this.$emit('toggleRowSelection',e.checked,this.selectArr)
+    },
+    itemFilter(item,ite){
+      if(ite.filters&&ite.name){
+        let key = item[ite.name]
+        return ite.filters[key]||''
+      }
+      return item[ite.name]||ite.emptyString
+    },
+    // 默认字体为微软雅黑 Microsoft YaHei,字体大小为 14px
+    getTextWidth(str) {
+      if(str.length<3){
+        return 40
+      }
+      let regx = /^[0-9]+.?[0-9]*$/
+      let flexWidth = 0
+      for (const char of str) {
+        if ((char >= 'A' && char <= 'Z') || (char >= 'a' && char <= 'z')) {
+          // 如果是英文字符,为字符分配8个单位宽度
+          flexWidth += 10
+        } else if (char >= '\u4e00' && char <= '\u9fa5') {
+          // 如果是中文字符,为字符分配15个单位宽度
+          flexWidth += 15
+        } else if(regx.test(char)){
+          flexWidth += 9
+        }else {
+          // 其他种类字符,为字符分配8个单位宽度
+          flexWidth += 7
+        }
+      }
+      return flexWidth
+    },
+    width(item){
+      return `${item.width?item.width:'100'}px`
+    },
+    showStripe(index){
+      if(this.currentDriver)return
+      if(this.stripe){
+        return (index % 2) != 0?'odd':'even'
+      }else{
+        return ''
+      }
+    },
+    //验证字符串是否是数字
+    checkNumber(theObj) {
+      var reg = /^[0-9]+.?[0-9]*$/;
+      if (reg.test(theObj)) {
+        return true;
+      }
+      return false;
+    },
+    isDate(data){
+      if(isNaN(data)&&!isNaN(Date.parse(data))){
+        return true
+      }
+      return false
+    },
+    sortAction(item,index){
+      if(!item.sorter){return false}
+      this.$set(item,'sorterMode',item.sorterMode==='_asc'?'_desc':'_asc')
+      if(item.sorter==='custom'){
+        this.$emit('sort-change',item,item.sorterMode.replace('_',''),index)
+      }else {
+        this.sortData(item)
+      }
+      // #ifndef H5 || APP-PLUS
+      this.$forceUpdate()
+      // #endif
+    },
+    sortData(item){
+      let key = item.name
+
+      if(item.sorterMode==='_asc'){
+        this.data.sort((a,b)=>{
+          if(this.checkNumber(a[key])){
+            return a[key]-b[key]
+          }
+          if(this.isDate(a[key])){
+            let a1 = new Date(a[key]).getTime()
+            let b1 = new Date(b[key]).getTime()
+            return a1-b1
+          }
+        })
+      }else {
+        this.data.sort((a,b)=>{
+          if(this.checkNumber(a[key])){
+            return b[key]-a[key]
+          }
+          if(this.isDate(a[key])){
+            let a1 = new Date(a[key]).getTime()
+            let b1 = new Date(b[key]).getTime()
+            return b1-a1
+          }
+        })
+      }
+    },
+    throttle(method,delay=60){
+      let time = null
+      return (...args)=>{
+        if(!time){
+          time = setTimeout(()=>{
+            method(...args)
+            time = null;
+          },delay)
+        }
+      }
+    },
+    debounce(method,delay=1000){
+      return (...args)=>{
+        this.debounceTime&&clearTimeout(this.debounceTime)
+        this.debounceTime = setTimeout(()=>{
+          method(...args)
+        },delay)
+      }
+    },
+    handleBodyScroll(e){
+      if(this.currentDriver&&this.currentDriver!==e.currentTarget.id)return
+      this.currentDriver = e.currentTarget.id
+      this.headerTableLeft = e.detail.scrollLeft
+      this.headerFooterTableLeft = e.detail.scrollLeft
+      this.leftFiexScrollTop = e.detail.scrollTop
+      this.bodyTime&&clearTimeout(this.bodyTime)
+      this.bodyTime = setTimeout(()=>{
+        this.currentDriver=null
+      },200)
+
+    },
+    leftFixedScrollAction(e){
+      if(this.currentDriver&&this.currentDriver!==e.currentTarget.id)return
+      this.currentDriver = e.currentTarget.id
+      this.bodyScrollTop = e.detail.scrollTop
+      this.bodyTime&&clearTimeout(this.bodyTime)
+      this.bodyTime = setTimeout(()=>{
+        this.currentDriver=null
+      },200)
+    },
+    scrollToLeft(e){
+      if(this.currentDriver1&&this.currentDriver1!==e.currentTarget.id)return
+      this.currentDriver1 = e.currentTarget.id
+      if(e.detail.direction==='left'&&this.headerTableLeft<10){
+        this.headerTableLeft = 0
+      }else if(e.detail.direction==='top'&&this.leftFiexScrollTop<10){
+        this.leftFiexScrollTop = 0
+      }
+      this.bodyTime&&clearTimeout(this.bodyTime)
+      this.bodyTime = setTimeout(()=>{
+        this.currentDriver1=null
+      },200)
+    },
+    scrollToFixedLeft(e){
+      if(this.currentDriver1&&this.currentDriver1!==e.currentTarget.id)return
+      this.currentDriver1 = e.currentTarget.id
+      if(e.detail.direction==='top'&&this.bodyScrollTop<10){
+        this.bodyScrollTop = 0
+      }
+      this.bodyTime&&clearTimeout(this.bodyTime)
+      this.bodyTime = setTimeout(()=>{
+        this.currentDriver1=null
+      },200)
+    },
+    handleTableScrollLeft(e,type){
+      if(this.currentDriver&&this.currentDriver!==e.currentTarget.id)return
+      this.currentDriver = e.currentTarget.id
+      this.bodyTableLeft = e.detail.scrollLeft
+      this.headerFooterTableLeft = e.detail.scrollLeft
+      this.bodyTime&&clearTimeout(this.bodyTime)
+      this.bodyTime = setTimeout(()=>{
+        this.currentDriver=null
+      },200)
+    },
+    handleFooterTableScrollLeft(e){
+      if(this.currentDriver&&this.currentDriver!==e.currentTarget.id)return
+      this.currentDriver = e.currentTarget.id
+      this.bodyTableLeft = e.detail.scrollLeft
+      this.headerTableLeft = e.detail.scrollLeft
+      this.bodyTime&&clearTimeout(this.bodyTime)
+      this.bodyTime = setTimeout(()=>{
+        this.currentDriver=null
+      },200)
+    }
+  }
+}
+</script>
+<style lang="scss">
+.zb-table-fixed-left{
+  /*去除左边滚动条 */
+  scroll-view ::-webkit-scrollbar {
+    display: none !important;
+    width: 0 !important;
+    height: 0 !important;
+    -webkit-appearance: none;
+    background: transparent;
+  }
+}
+.zb-table-header{
+  ///*去除头部滚动条 */
+  scroll-view ::-webkit-scrollbar {
+    display: none !important;
+    width: 0 !important;
+    height: 0 !important;
+    -webkit-appearance: none;
+    background: transparent;
+  }
+}
+
+</style>
+<style lang="scss" scoped>
+.sorter-table{
+  position: absolute;
+  right: 6px;
+  top:50%;
+  transform:translateY(-50%);
+  .sorter-table-icon{
+    width: 0;
+    height: 0;
+    color: #dcdcdc;
+    border-right: 4px solid transparent;
+    border-left: 4px solid transparent;
+  }
+  .sorter-table-icon:first-child{
+    border-bottom: 5px solid currentColor;
+  }
+  .sorter-table-icon:last-child{
+    margin-top: 1.5px;
+    border-top: 5px solid currentColor;
+  }
+  .sorting_desc{
+    color: #2979ff;
+  }
+  .sorting_asc{
+    color: #2979ff;
+  }
+}
+.checkbox-item{
+  display: flex;align-items: center;justify-content: center;width: 100%;height: 100%
+}
+.no-data{
+  width: 100%;
+  height: 80rpx;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-bottom: 1px solid #e8e8e8;
+}
+.item-th{
+  position: relative;
+  flex-shrink: 0;
+  width: 100px;
+  overflow-wrap: break-word;
+  border-bottom: 1px solid #e8e8e8;
+  transition: background 0.3s;
+  padding-right: 4px;
+  word-break:keep-all;           /* 不换行 */
+  white-space:nowrap;          /* 不换行 */
+  overflow:hidden;               /* 内容超出宽度时隐藏超出部分的内容 */
+  text-overflow:ellipsis;         /* 当对象内文本溢出时显示省略标记(...) ;需与overflow:hidden;一起使用。*/
+  overflow-wrap: break-word;
+}
+.zb-table{
+  height: 100%;
+  overflow: hidden;
+  width: 100%;
+  display: flex;
+  flex-direction: column;
+  font-size: 12px;
+  position: relative;
+  .zb-table-content{
+    //height: 100%;
+    //flex: 1;
+    position: relative;
+    overflow: hidden;
+  }
+  .zb-table-fixed{
+    min-width: 100%;
+  }
+  .zb-table-body{
+    position: relative;
+    background: #fff;
+    transition: opacity 0.3s;
+  }
+  .item-tr{
+    display: flex;
+    //height: 41px;
+  }
+  .item-td{
+    flex-shrink: 0;
+    width: 100px;
+    padding-left: 4px;
+    height: 40px;
+    line-height: 40px;
+	padding-right: 4px;
+    box-sizing: border-box;
+    word-break:keep-all;           /* 不换行 */
+    white-space:nowrap;          /* 不换行 */
+    overflow:hidden;               /* 内容超出宽度时隐藏超出部分的内容 */
+    text-overflow:ellipsis;         /* 当对象内文本溢出时显示省略标记(...) ;需与overflow:hidden;一起使用。*/
+    overflow-wrap: break-word;
+    border-bottom: 1px solid #e8e8e8;
+    //transition: background 0.3s;
+  }
+
+  .zb-table-fixed-left .zb-table-header{
+    overflow-y: hidden;
+  }
+  .zb-table-header {
+    overflow: hidden;
+    background: #fafafa;
+    .item-th{
+      padding-left: 4px;
+      line-height: 39px;
+      height: 40px;
+      //display: flex;
+      //align-items: center;
+      box-sizing: border-box;
+    }
+  }
+  .zb-table-fixed-left .zb-table-fixed{
+    background: #fff;
+  }
+  .zb-table-fixed-right .zb-table-fixed{
+    background: #fff;
+  }
+  .zb-table-body-inner{
+    height: 100%;
+    // overflow: scroll;
+  }
+  .zb-table-fixed-left{
+    position: absolute;
+    top: 0;
+    z-index: 1;
+    overflow: hidden;
+    border-radius: 0;
+    height: 100%;
+    //transition: box-shadow 0.3s ease;
+  }
+  .odd{
+    background-color:rgba(249,249,249,0.6);
+    //height: 100%;
+    width: 100%;
+  }
+  .even{
+    background-color:white ;
+    //height: 100%;
+    width: 100%;
+  }
+}
+.scroll-left-fixed{
+  .zb-table-fixed-left {
+    left: 0;
+    box-shadow: 6px 0 6px -4px #ccc;
+  }
+}
+.zb-table-applet{
+  height: 100%;
+  //overflow: hidden;
+  width: 100%;
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  font-size: 12px;
+  .zb-table-content{
+    //height: 100%;
+    flex: 1;
+    overflow: hidden;
+    position: relative;
+
+  }
+  .zb-table-fixed{
+    min-width: 100%;
+    width: fit-content;
+  }
+
+  .zb-table-body{
+    position: relative;
+    background: #fff;
+    transition: opacity 0.3s;
+  }
+  .item-tr{
+    display: flex;
+    //height: 41px;
+  }
+  .item-td{
+    flex-shrink: 0;
+    width: 100px;
+    padding-left: 4px;
+    height: 40px;
+    line-height: 40px;
+	padding-right:4px;
+    box-sizing: border-box;
+    word-break:keep-all;           /* 不换行 */
+    white-space:nowrap;          /* 不换行 */
+    overflow:hidden;               /* 内容超出宽度时隐藏超出部分的内容 */
+    text-overflow:ellipsis;         /* 当对象内文本溢出时显示省略标记(...) ;需与overflow:hidden;一起使用。*/
+    overflow-wrap: break-word;
+    border-bottom: 1px solid #e8e8e8;
+    //transition: background 0.3s;
+  }
+  .zb-table-header {
+    //overflow: hidden;
+    position: sticky;
+    top: 0;
+    z-index: 2;
+    //width: fit-content;
+    .item-th{
+      padding-left: 4px;
+      line-height: 39px;
+      height: 40px;
+      box-sizing: border-box;
+      background: #fafafa;
+    }
+    .zb-stick-side{
+      position: sticky;
+      top: 0;
+      left: 0;
+      z-index: 2;
+      //border-right: solid 1rpx #dbdbdb;
+      box-sizing: border-box;
+      background: #fafafa;
+      //box-shadow: 6px 0 6px -4px #ccc;
+    }
+  }
+  .zb-table-fixed-left .zb-table-fixed{
+    background: #fff;
+  }
+  .zb-table-fixed-right .zb-table-fixed{
+    background: #fff;
+  }
+  .zb-table-fixed-header .zb-table-body-inner{
+    height: 100%;
+    // overflow: scroll;
+  }
+  .zb-table-fixed-left{
+    position: absolute;
+    top: 0;
+    z-index: 1;
+    overflow: hidden;
+    border-radius: 0;
+    height: 100%;
+    //transition: box-shadow 0.3s ease;
+  }
+  .scroll-left-fixed{
+    .zb-table-fixed-left {
+      left: 0;
+      box-shadow: 6px 0 6px -4px #ccc;
+    }
+  }
+  .odd{
+    background-color:rgba(249,249,249,0.6);
+    //height: 100%;
+    width: 100%;
+  }
+  .even{
+    background-color:white ;
+    //height: 100%;
+    width: 100%;
+  }
+  .zb-table-tbody {
+    .zb-stick-side{
+      position: sticky;
+      left: 0;
+      z-index: 1;
+      box-sizing: border-box;
+      background:white;
+      //box-shadow: 6px 0 6px -2px #ccc;
+    }
+    .odd{
+      background:#f9f9f9;
+      //height: 100%;
+      width: 100%;
+    }
+    .even{
+      background:white ;
+      //height: 100%;
+      width: 100%;
+    }
+  }
+  .current-row{
+    .item-td{
+      background-color: #ecf5ff;
+    }
+  }
+}
+.current-row{
+  .item-td{
+    background-color: #ecf5ff;
+  }
+}
+.zb-table-header{
+  height: 40px;
+}
+</style>

部分文件因为文件数量过多而无法显示