roose 4 years ago
parent
commit
f4fd3f5107

+ 86 - 0
components/t-table/t-table.vue

@@ -0,0 +1,86 @@
+<template>
+	<view class="t-table" :style="{ 'border-width': border + 'px', 'border-color': borderColor }">
+		<slot />
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			border: {
+				type: String,
+				default: '1'
+			},
+			borderColor: {
+				type: String,
+				default: '#d0dee5'
+			},
+			isCheck: {
+				type: Boolean,
+				default: false
+			}
+		},
+		provide() {
+			return {
+				table: this
+			};
+		},
+		data() {
+			return {};
+		},
+		created() {
+			this.childrens = [];
+			this.index = 0;
+		},
+		methods: {
+			fire(e, index, len) {
+				let childrens = this.childrens;
+				console.log(childrens);
+				// 全选
+				if (index === 0) {
+					childrens.map((vm, index) => {
+						vm.checkboxData.checked = e;
+						return vm;
+					});
+				} else {
+					let isAll = childrens.find((n, ids) => ids !== 0 && !n.checkboxData.checked);
+					childrens[0].checkboxData.checked = isAll ? false : true;
+				}
+
+				let fireArr = [];
+				for (let i = 0; i < childrens.length; i++) {
+					if (childrens[i].checkboxData.checked && i !== 0) {
+						fireArr.push(childrens[i].checkboxData.value - 1);
+					}
+				}
+				this.$emit('change', {
+					detail: fireArr
+				});
+			}
+		}
+	};
+</script>
+
+<style scoped>
+	.t-table {
+		width: 100%;
+		border: 1px #d0dee5 solid;
+		border-left: none;
+		border-top: none;
+		box-sizing: border-box;
+	}
+
+	.t-table>>>t-tr {
+		display: flex;
+	}
+
+	.t-table>>>t-tr:nth-child(2n) {
+		background: #f5f5f5;
+	}
+
+	/* #ifdef H5 */
+	.t-table>>>.t-tr:nth-child(2n) {
+		background: #f5f5f5;
+	}
+	/* #endif */
+</style>

+ 71 - 0
components/t-table/t-td.vue

@@ -0,0 +1,71 @@
+<template>
+	<view class="t-td" :style="{ 'border-width': thBorder + 'px','border-color':borderColor ,'font-size':fontSize+'px' ,'color':color,'justify-content':tdAlignCpd}">
+		<slot></slot>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			align: String
+		},
+		data() {
+			return {
+				thBorder: '1',
+				borderColor: '#d0dee5',
+				fontSize: '14',
+				color: '#555c60',
+				tdAlign: 'center'
+			};
+		},
+		inject: ['table', 'tr'],
+
+		created() {
+			this.thBorder = this.table.border;
+			this.borderColor = this.table.borderColor;
+			this.fontSize = this.tr.fontSize;
+			this.color = this.tr.color;
+			if (this.align) {
+				this.tdAlign = this.align;
+			} else {
+				this.tdAlign = this.tr.align
+			}
+		},
+		computed: {
+			tdAlignCpd() {
+				let nameAlign = '';
+				switch (this.tdAlign) {
+					case 'left':
+						nameAlign = 'flex-start'
+						break;
+					case 'center':
+						nameAlign = 'center'
+						break;
+					case 'right':
+						nameAlign = 'flex-end'
+						break;
+					default:
+						nameAlign = 'center'
+						break;
+				}
+				return nameAlign
+			}
+		}
+	};
+</script>
+
+<style>
+	.t-td {
+		flex: 1;
+		display: flex;
+		align-items: center;
+		width: 100%;
+		padding: 14upx;
+		border-top: 1px #d0dee5 solid;
+		border-left: 1px #d0dee5 solid;
+		text-align: center;
+		color: #555c60;
+		font-size: 28upx;
+
+	}
+</style>

+ 71 - 0
components/t-table/t-th.vue

@@ -0,0 +1,71 @@
+<template>
+	<view class="t-th" :style="{ 'border-width': thBorder + 'px' ,'border-color':borderColor,'font-size':fontSize+'px' ,'color':color,'justify-content':thAlignCpd}">
+		<slot></slot>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			align: String,
+		},
+		data() {
+			return {
+				thBorder: '1',
+				borderColor: '#d0dee5',
+				fontSize: '15',
+				color: '#3b4246',
+				thAlign: 'center'
+			};
+		},
+		inject: ['table', 'tr'],
+
+		created() {
+			this.thBorder = this.table.border;
+			this.borderColor = this.table.borderColor;
+			this.fontSize = this.tr.fontSize;
+			this.color = this.tr.color;
+			if (this.align) {
+				this.thAlign = this.align;
+			} else {
+				this.thAlign = this.tr.align
+			}
+		},
+
+		computed: {
+			thAlignCpd() {
+				let nameAlign = '';
+				switch (this.thAlign) {
+					case 'left':
+						nameAlign = 'flex-start'
+						break;
+					case 'center':
+						nameAlign = 'center'
+						break;
+					case 'right':
+						nameAlign = 'flex-end'
+						break;
+					default:
+						nameAlign = 'center'
+						break;
+				}
+				return nameAlign
+			}
+		}
+	};
+</script>
+
+<style>
+	.t-th {
+		flex: 1;
+		display: flex;
+		align-items: center;
+		font-size: 30upx;
+		font-weight: bold;
+		text-align: center;
+		color: #3b4246;
+		border-left: 1px #d0dee5 solid;
+		border-top: 1px #d0dee5 solid;
+		padding: 15upx;
+	}
+</style>

+ 81 - 0
components/t-table/t-tr.vue

@@ -0,0 +1,81 @@
+<template>
+	<view class="t-tr">
+		<view v-if="isCheck" class="t-check-box" :style="{ 'border-width': thBorder + 'px' ,'border-color':borderColor}">
+			<checkbox-group @change="checkboxChange">
+				<checkbox :value="checkboxData.value + ''" :checked="checkboxData.checked" />
+			</checkbox-group>
+		</view>
+		<slot></slot>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			fontSize: String,
+			color: String,
+			align: String
+		},
+		inject: ['table'],
+		provide() {
+			return {
+				tr: this
+			};
+		},
+		data() {
+			return {
+				isCheck: false,
+				checkboxData: {
+					value: 0,
+					checked: false
+				},
+				checked: false,
+				thBorder: '1',
+				borderColor: '#d0dee5'
+			};
+		},
+		created() {
+			this.thBorder = this.table.border;
+			this.borderColor = this.table.borderColor;
+			this.table.childrens.push(this);
+			this.checkboxData.value = this.table.index++;
+			this.isCheck = this.table.isCheck;
+
+		},
+		methods: {
+			checkboxChange(e) {
+				this.checkboxData.checked = !this.checkboxData.checked;
+				this.table.childrens[this.checkboxData.value] = this;
+				this.table.fire(e.detail.value[0] ? true : false, this.checkboxData.value, this.table.index);
+			}
+		}
+	};
+</script>
+
+<style>
+	.t-tr {
+		width: 100%;
+		display: flex;
+	}
+
+	.t-tr t-th,
+	.t-tr t-td {
+		display: flex;
+		flex: 1;
+	}
+
+	.t-tr .t-check-box {
+		flex-shrink: 0;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		width: 80upx;
+		color: #3b4246;
+		border-left: 1px #d0dee5 solid;
+		border-top: 1px #d0dee5 solid;
+	}
+
+	.t-tr .t-check-box checkbox {
+		transform: scale(0.8);
+	}
+</style>

+ 13 - 0
components/z-table/table-render.js

@@ -0,0 +1,13 @@
+import Vue from 'vue'
+
+Vue.mixin({
+	methods: {
+		tableNameRender(h, {
+			row,
+			col
+		}) {
+			console.log(row)
+			return h('view', row.name)
+		}
+	}
+})

+ 784 - 0
components/z-table/z-table.vue

@@ -0,0 +1,784 @@
+<template>
+	<view class="z-table">
+		<view class="z-table-main" :style="compluteHeight">
+			<view v-if="!tableLoaded && (!tableData || !columns)" :class="['z-loading', {ztableLoading: tableShow}]">
+				<view class="z-loading-animate"></view>
+			</view>
+			<view class="z-table-container">
+				<view class="z-table-pack">
+					<view class="z-table-title">
+						<view class="z-table-title-item" :class="{ 'z-table-stick-side': stickSide && index == 0 }" :style="{ width: item.width ? item.width + 'rpx' : '200rpx' }"
+						 v-for="(item, index) in columns" :key="index" @click="sort(item.key, index)">
+							<view v-if="showSelect && !singleSelect && index === 0" class="select-box" @click="doSelect(true)">
+								<view :class="['select-tip', {'selected': selectAll}]"></view>
+							</view>
+							<view :class="['z-table-col-text', {'text-left': titleTextAlign === 'left', 'text-center': titleTextAlign === 'center', 'text-right': titleTextAlign === 'right'}]">
+								<view v-html="getTitleText(item.title)"></view>
+								<view v-if="item.hasOwnProperty('key') && item.hasOwnProperty('sort') && tableData.length" class="sort">
+									<view class="up-arrow" :class="{ action: nowSortKey == item.key && sortType == 'asc' }"></view>
+									<view class="down-arrow" :class="{ action: nowSortKey == item.key && sortType == 'desc' }"></view>
+								</view>
+							</view>
+						</view>
+					</view>
+					<view v-if="tableData.length" :class="['table-container-box', {'short-table': !longTable && showBottomSum}]">
+						<view class="z-table-container-row" :class="{ 'z-table-has-bottom': showBottomSum }" v-for="(row, iIndex) in tableData"
+						 :key="iIndex">
+							<view :class="['z-table-container-col', { 'z-table-stick-side': stickSide && jIndex == 0 }]" :style="{ width: col.width ? col.width + 'rpx' : '200rpx' }"
+							 v-for="(col, jIndex) in columns" :key="jIndex" @click="itemClick(row, col)">
+								<view v-if="showSelect && jIndex === 0" class="select-box" @click="doSelect(false, iIndex)">
+									<view :class="['select-tip', {'selected': selectArr.includes(iIndex)}]"></view>
+								</view>
+								<view :class="['z-table-col-text', {'text-left': textAlign === 'left', 'text-center': textAlign === 'center', 'text-right': textAlign === 'right'}]">
+									<view v-if="!col.isLink" v-html="getRowContent(row, col)">
+										<!-- <view v-if="!col.render" v-html="getRowContent(row, col)"></view> -->
+										<!-- <renderComponents v-else :row="row" :col="col" /> -->
+									</view>
+									<!-- #ifdef H5 -->
+									<router-link v-else-if="setUrl(row, col).indexOf('http') != 0" :to="setUrl(row, col)" v-html="getRowContent(row, col)"></router-link>
+									<a v-else-if="col.isLink" :href="setUrl(row, col)" v-html="getRowContent(row, col)"></a>
+									<!-- #endif -->
+									<!-- #ifndef H5 -->
+									<navigator v-else-if="col.isLink" :url="setUrl(row, col)" v-html="getRowContent(row, col)"></navigator>
+									<!-- #endif -->
+								</view>
+							</view>
+						</view>
+					</view>
+					<view :class="['z-table-bottom', {'long-table': longTable}]" v-if="showBottomSum && tableData.length">
+						<view class="z-table-bottom-col" :class="{ 'z-table-stick-side': stickSide && sumIndex == 0 }" :style="{ width: sumCol.width ? sumCol.width + 'rpx' : '200rpx' }"
+						 v-for="(sumCol, sumIndex) in columns" :key="sumIndex">
+							<view class="z-table-bottom-text">
+								<!-- <view v-if="sumIndex != 0" class="z-table-bottom-text-title">{{ sumCol.title }}</view> -->
+								<text :class="{ sum: sumIndex == 0 }">{{ sumIndex == 0 ? '总计' : dosum(sumCol) }}</text>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view v-if="tableData && tableData.length == 0 && !tableLoaded" class="table-empty">
+				<!-- image v-if="!showLoading" class="empty-img" src="../static/empty.png"></image -->
+				<view v-html="showLoading ? '' : emptyText"></view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	/*
+	 * 表格使用
+	 * 注意如果需要异步加载,需要把tableData初始值设为false,当没有数据的时候值为空数组
+	 * props: tableData [Array | Boolean] | 表格数据 如果为false则显示loading
+	 * 		 columns [Array | Boolean] | 数据映射表 如果为false则显示loading 每列params => title(表头文字可以是html字符串模版), width(每列宽度) [, key(对应tableData的字段名) || format(自定义内容), sort(是否要排序), isLink(是否显示为超链接Object)]
+	 * 										   format格式: {template: 字符串模版用#key#表示需要被替换的数据,names: 对应template属性内要被替换的内容的key}
+	 * 										   isLink格式: {url: 链接地址, params: 地址带的参数Array[key|value, key|value, ...]每一项都是key和value以'|'链接,如果不带'|'默认键值同名
+	 * 										   listenerClick(是否监听点击事件Boolean)}
+	 * 		 stickSide Boolean | 是否固定右侧首栏 默认不显示
+	 * 		 showBottomSum Boolean | 是否显示底部统计 默认不显示
+	 * 		 showLoading Boolean | 是否首次加载首次加载不显示暂无数据内容
+	 * 		 emptyText String | 空数据显示的文字内容
+	 *		 tableHeight Number | 设置表格高度会滚动
+	 *		 sort Boolean | 开启排序
+	 * 		 showSelect Boolean | 开启选择
+	 *		 singleSelect Boolean | 在开启选择的状态下是否开起单选
+	 * 		 textAlign String | 内容对齐方式 left center right
+	 * 		 titleTextAlign String | 表头对齐方式 left center right
+	 *
+	 * event: onSort | 排序事件 返回{key: 被排序列的字段名, type: 正序'asc'/倒序'desc'}
+	 *		  onSelect | 选中时触发 返回选择的行的下标
+	 * 		  onClick | 单元格点击事件 返回点击单元格所属行的数据
+	 *
+	 * function: resetSort | 调用后重置排序 *注意:不会触发sort事件
+	 *
+	 * */
+	import Vue from 'vue'
+	// import tableRender from './table-render'
+
+	export default {
+		data() {
+			return {
+				version: '1.1.3',
+				nowSortKey: '',
+				sortType: 'desc', // asc/desc 升序/降序
+				longTable: true,
+				lineHeight: uni.upx2px(64),
+				tableLoaded: false,
+				tableShow: true,
+				selectAll: false,
+				selectArr: []
+			}
+		},
+		// mixin: [tableRender],
+		computed: {
+			compluteHeight() {
+				return this.tableHeight ?
+					'height: ' + uni.upx2px(this.tableHeight) + 'px' :
+					''
+			}
+		},
+		props: {
+			tableData: {
+				type: [Array, Boolean],
+				default () {
+					return false
+				}
+			},
+			columns: {
+				/*
+				 *
+				 * [{title: xxx, key: 当前列展示对象名, width: 列宽, render: function}]
+				 *
+				 * */
+				type: [Array, Boolean],
+				required: true
+			},
+			stickSide: {
+				type: Boolean,
+				default: false
+			},
+			showBottomSum: {
+				type: Boolean,
+				default: false
+			},
+			showLoading: {
+				type: Boolean,
+				default: true
+			},
+			emptyText: {
+				type: String,
+				default: '暂无数据'
+			},
+			tableHeight: {
+				type: [Number, Boolean],
+				default: 0
+			},
+			showSelect: {
+				type: Boolean,
+				default: false
+			},
+			singleSelect: {
+				type: Boolean,
+				default: false
+			},
+			textAlign: {
+				type: String,
+				default: 'left' // right|center|left
+			},
+			titleTextAlign: {
+				type: String,
+				default: 'left' // right|center|left
+			}
+		},
+		mounted() {
+			this.init()
+		},
+		// components: {
+		// 	renderComponents: {
+		// 		functional: true,
+		// 		props: {
+		// 			row: {
+		// 				type: Object,
+		// 				required: true
+		// 			},
+		// 			col: {
+		// 				type: Object,
+		// 				required: true
+		// 			}
+		// 		},
+		// 		render: function(h, ctx) {
+		// 			return _this[ctx.props.col.render](h, ctx.props)
+		// 		}
+		// 	}
+		// },
+		watch: {
+			columns() {
+				this.init()
+			},
+			tableData() {
+				this.init()
+			}
+		},
+		methods: {
+			async init() {
+				// 重置选择内容
+				this.selectAll = false
+				this.selectArr = []
+				this.tableLoaded = false
+				this.tableShow = true
+				let _this = this
+				let container = await _this.getPageSize('.z-table-container'),
+					pack = await _this.getPageSize('.z-table-pack')
+				_this.timer && clearTimeout(_this.timer)
+				if (container && pack) {
+					_this.$nextTick(function() {
+						if (_this.tableData && _this.tableData.length) {
+							_this.tableShow = false
+							_this.timer = setTimeout(function() {
+								_this.tableLoaded = true
+							}, 300)
+						}
+					})
+					if (container.height != pack.height) {
+						_this.longTable = true
+					} else {
+						_this.longTable = false
+					}
+				} else {
+					_this.tableLoaded = false
+					_this.$nextTick(function() {
+						_this.tableShow = true
+					})
+				}
+			},
+			getPageSize(selecter) {
+				// 获取元素信息
+				let query = uni.createSelectorQuery().in(this),
+					_this = this
+				return new Promise((resolve, reject) => {
+					query
+						.select(selecter)
+						.boundingClientRect(res => {
+							resolve(res)
+						})
+						.exec()
+				})
+			},
+			dosum({key, noSum = false, formatNum = true}) {
+				let sum = '-'
+				if (noSum) return sum
+				if (this.tableData) {
+					if (
+						this.tableData.every(item => {
+							return !Number.isNaN(item[key] - 0)
+						})
+					) {
+						sum = 0
+						this.tableData.map((item, index) => {
+							if (!key && index != 0) {
+								sum = '-'
+							} else {
+								let val = item[key] - 0
+								if (Number.isNaN(val)) {
+									sum += 0
+								} else {
+									sum += val
+								}
+							}
+						})
+					}
+				}
+				// sum = sum == 0 ? "-" : sum
+				return formatNum ? this.numTransform(sum) : sum
+			},
+			getRowContent(row, col) {
+				// 表格值处理函数
+				// 如果columns带了key则显示对应的key
+				// 如果columns带的format则按规定返回format后的html
+				// format规定: params names <Array> 对应tableData的键名,作为匹配template中两个#之间动态内容的名字
+				//			   params template <String> html字符串模版
+				let tempHTML = ''
+				let rowKey = row[col.key]
+				if ([null, ''].includes(rowKey)) {
+					rowKey = '-'
+				}
+				let { formatNum = true } = col
+				if (rowKey || rowKey === 0) {
+					tempHTML = isNaN(rowKey - 0) || !formatNum ?
+						rowKey :
+						this.numTransform(rowKey - 0)
+					// tempHTML = tempHTML == 0 ? "-" : tempHTML
+				} else if (!!col.format) {
+					let tempFormat = col.format.template
+					col.format.names.map(item => {
+						let regexp = new RegExp(`\#${item}\#`, 'mg')
+						tempFormat = tempFormat.replace(regexp, row[item])
+					})
+					tempHTML = tempFormat
+				} else if (!col.render) {
+					let error = new Error('数据的key或format值至少一个不为空')
+					throw error
+				}
+				// console.log(tempHTML)
+				return tempHTML.toString()
+			},
+			sort(key, index) {
+				if (!key || !this.columns[index].sort) {
+					return
+				}
+				// 排序功能: 如果点击的排序按钮是原先的 那么更改排序类型
+				//			如果点击的另一个排序按钮 那么选择当前排序并且排序类型改为降序(desc)
+				if (key != this.nowSortKey) {
+					this.nowSortKey = key
+					this.sortType = 'desc'
+				} else {
+					this.toggleSort()
+				}
+				this.$emit('onSort', {
+					key: this.nowSortKey,
+					type: this.sortType
+				})
+			},
+			toggleSort() {
+				this.sortType = this.sortType == 'asc' ? 'desc' : 'asc'
+			},
+			numTransform(n) {
+				if (Number.isNaN(n - 0)) {
+					return n
+				}
+				if (Math.abs(n) >= 100000000) {
+					n = Number((n / 100000000).toFixed(1)) + '亿'
+				} else if (Math.abs(n) >= 10000) {
+					n = Number((n / 10000).toFixed(1)) + '万'
+				}
+				return n.toString()
+			},
+			resetSort() {
+				// 重置排序状态
+				this.nowSortKey = ''
+				this.sortType = 'desc'
+			},
+			setUrl(row, col) {
+				if (!col.isLink) {
+					return
+				}
+				let urlParam = {}
+				let {
+					isLink: {
+						url,
+						params = []
+					}
+				} = col
+				params.forEach(item => {
+					if (~item.indexOf('|')) {
+						let temp = item.split('|')
+						urlParam[temp[0]] = row[temp[1]]
+					} else {
+						urlParam[item] = row[item]
+					}
+				})
+				url = this.setUrlParams(url, urlParam)
+				return url
+			},
+			setUrlParams(url, params) {
+				let tempUrl = url,
+					keyArr = Object.keys(params)
+				keyArr.forEach(item => {
+					tempUrl += `&${item}=${params[item]}`
+				})
+				tempUrl = tempUrl.replace(/\&/, '?')
+				return tempUrl
+			},
+			itemClick(row, col) {
+				if (col.listenerClick) {
+					this.$emit('onClick', row)
+				}
+			},
+			doSelect(isAll = false, index) {
+				let temp = new Set()
+				if (isAll) {
+					// 全选
+					if (!this.selectAll) {
+						for (let i = 0; i < this.tableData.length; i++) {
+							temp.add(i)
+						}
+					}
+				} else {
+					// if (!this.singleSelect) {
+					// 	this.selectArr.forEach(item => {
+					// 		temp.add(item)
+					// 	})
+					// }
+					this.selectArr.forEach(item => {
+						temp.add(item)
+					})
+					if (temp.has(index)) {
+						temp.delete(index)
+					} else {
+						if (this.singleSelect) {
+							temp.clear()
+						}
+						temp.add(index)
+					}
+				}
+				this.selectArr = Array.from(temp)
+				// console.log(this.selectArr)
+				if (this.selectArr.length == this.tableData.length) {
+					this.selectAll = true
+				} else {
+					this.selectAll = false
+				}
+				
+				this.$emit('onSelect', this.selectArr)
+			},
+			// 1.1.1
+			getTitleText(title) {
+				// 自定义表头
+				let tempHTML = title
+				return tempHTML.toString()
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	.navigator-hover {
+		background: transparent;
+		opacity: 1;
+	}
+
+	@mixin ellipsis($num: 1) {
+		overflow: hidden;
+		text-overflow: ellipsis;
+
+		@if $num==1 {
+			white-space: nowrap;
+		}
+
+		@else {
+			display: -webkit-box;
+			-webkit-line-clamp: $num;
+			/* autoprefixer: off */
+			-webkit-box-orient: vertical;
+			/* autoprefixer: on */
+		}
+	}
+
+	// 三角形
+	%triangle-basic {
+		content: '';
+		height: 0;
+		width: 0;
+		overflow: hidden;
+	}
+
+	@mixin triangle($direction, $size, $borderColor) {
+		@extend %triangle-basic;
+
+		@if $direction==top {
+			border-bottom: $size solid $borderColor;
+			border-left: $size dashed transparent;
+			border-right: $size dashed transparent;
+			border-top: 0;
+		}
+
+		@else if $direction==right {
+			border-left: $size solid $borderColor;
+			border-top: $size dashed transparent;
+			border-bottom: $size dashed transparent;
+			border-right: 0;
+		}
+
+		@else if $direction==bottom {
+			border-top: $size solid $borderColor;
+			border-left: $size dashed transparent;
+			border-right: $size dashed transparent;
+			border-bottom: 0;
+		}
+
+		@else if $direction==left {
+			border-right: $size solid $borderColor;
+			border-top: $size dashed transparent;
+			border-bottom: $size dashed transparent;
+			border-left: 0;
+		}
+	}
+
+	a {
+		text-decoration: none;
+	}
+
+	.z-table {
+		position: relative;
+		display: inline-block;
+		height: 100%;
+		min-height: 130rpx;
+		width: 100%;
+		background: #fff;
+		border: solid 2rpx #ccc;
+		font-size: $uni-font-size-sm;
+		box-sizing: border-box;
+		transform: translateZ(0);
+
+		.z-table-main {
+			height: 100%;
+			box-sizing: border-box;
+		}
+
+		.z-table-container {
+			height: 100%;
+			overflow: scroll;
+			box-sizing: border-box;
+		}
+
+		.z-table-pack {
+			position: relative;
+			min-height: 100%;
+			width: fit-content;
+		}
+
+		.z-table-title {
+			position: sticky;
+			top: 0;
+			height: 64rpx;
+			z-index: 1;
+
+			.z-table-title-item {
+				border-bottom: solid 1rpx #dbdbdb;
+				background: #f8f8f8;
+			}
+
+			.z-table-stick-side {
+				position: sticky;
+				top: 0;
+				left: 0;
+				border-right: solid 1rpx #dbdbdb;
+				box-sizing: border-box;
+			}
+		}
+
+		.table-container-box.short-table {
+			padding-bottom: 48rpx;
+		}
+
+		.z-table-title,
+		.z-table-container-row {
+			display: flex;
+			width: fit-content;
+			white-space: nowrap;
+			box-sizing: border-box;
+
+			.z-table-title-item,
+			.z-table-container-col {
+				@include ellipsis();
+				display: inline-flex;
+				padding: 0 16rpx;
+				height: 64rpx;
+				align-items: center;
+				line-height: 64rpx;
+				box-sizing: border-box;
+			}
+		}
+
+		.z-table-container-row {
+			z-index: 0;
+			border-bottom: solid 1rpx #f4f4f4;
+			box-sizing: border-box;
+		}
+
+		.z-table-stick-side {
+			position: sticky;
+			left: 0;
+			background: #f7f9ff;
+			border-right: solid 1rpx #dbdbdb;
+			box-sizing: border-box;
+		}
+
+		.z-table-bottom {
+			position: absolute;
+			bottom: 0;
+			z-index: 9;
+			display: flex;
+			justify-items: center;
+			width: fit-content;
+			background: #4298f7 !important;
+			color: #fff !important;
+			white-space: nowrap;
+			box-sizing: border-box;
+
+			&.long-table {
+				position: sticky;
+			}
+
+			.z-table-stick-side {
+				background: #4298f7 !important;
+				box-sizing: border-box;
+			}
+
+			.z-table-bottom-col {
+				display: inline-flex;
+				align-items: center;
+				text-align: center;
+				padding: 16rpx;
+				box-sizing: border-box;
+			}
+
+			.z-table-bottom-text {
+				line-height: 100%;
+				box-sizing: border-box;
+			}
+
+			.z-table-bottom-text-title {
+				margin-bottom: 10rpx;
+				font-size: 22rpx;
+				color: #aad0ff;
+				box-sizing: border-box;
+			}
+
+			.sum {
+				margin-left: 14rpx;
+				font-size: 28rpx;
+				box-sizing: border-box;
+			}
+		}
+
+		.table-empty {
+			position: absolute;
+			top: 64rpx;
+			height: 64rpx;
+			line-height: 64rpx;
+			width: 100%;
+			text-align: center;
+		}
+
+		.sort {
+			display: flex;
+			padding: 5rpx;
+			flex-direction: column;
+			justify-content: center;
+
+			.up-arrow {
+				@include triangle(top, 10rpx, #ccc);
+				display: block;
+				margin-bottom: 5rpx;
+
+				&.action {
+					@include triangle(top, 10rpx, #4298f7);
+				}
+			}
+
+			.down-arrow {
+				@include triangle(bottom, 10rpx, #ccc);
+				display: block;
+
+				&.action {
+					@include triangle(bottom, 10rpx, #4298f7);
+				}
+			}
+		}
+
+		// 1.0.5
+		.z-loading {
+			position: absolute;
+			top: 0;
+			left: 0;
+			z-index: 2;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			height: 100%;
+			width: 100%;
+			background: #fff;
+			opacity: 0;
+			transition: all 0.3s;
+
+			&.ztableLoading {
+				opacity: 1;
+			}
+
+			.z-loading-animate {
+				position: relative;
+				display: inline-block;
+				width: 30rpx;
+				height: 30rpx;
+				margin-right: 20rpx;
+				border-radius: 100%;
+				border: solid 6rpx #ccc;
+				vertical-align: middle;
+				animation: rotate 1s ease-in-out infinite;
+
+				&::after {
+					content: '';
+					display: block;
+					position: absolute;
+					top: -10rpx;
+					z-index: 1;
+					background: #fff;
+					width: 20rpx;
+					height: 20rpx;
+					border-radius: 10rpx;
+				}
+			}
+
+			@keyframes rotate {
+				from {
+					transform: rotate(0deg);
+				}
+
+				to {
+					transform: rotate(360deg);
+				}
+			}
+		}
+
+		// 1.1.0
+		.select-box {
+			display: inline-block;
+			width: 26rpx;
+			height: 26rpx;
+			line-height: 14rpx;
+			margin-right: 15rpx;
+			border: solid 2rpx #4298f7;
+			border-radius: 4rpx;
+			background: #fff;
+			text-align: center;
+		}
+
+		.select-tip {
+			display: inline-block;
+			opacity: 0;
+			transform: rotate(90deg);
+			transition: all .3s;
+
+			&.selected {
+				position: relative;
+				top: 4rpx;
+				left: -4rpx;
+				height: 4rpx;
+				background: #4298f7;
+				width: 10rpx;
+				opacity: 1;
+				transform: rotate(45deg);
+
+				&:before,
+				&:after {
+					content: '';
+					position: absolute;
+					display: block;
+					height: 4rpx;
+					background: #4298f7;
+				}
+
+				&:before {
+					bottom: -2rpx;
+					left: -4rpx;
+					width: 8rpx;
+					transform: rotate(-90deg);
+				}
+
+				&:after {
+					bottom: 16rpx;
+					right: -16rpx;
+					width: 34rpx;
+					transform: rotate(-90deg);
+				}
+			}
+		}
+		
+		// 1.1.1
+		.z-table-col-text {
+			display: flex;
+			width: 100%;
+			flex: 1;
+			justify-content: flex-start;
+			align-content: center;
+			
+			&.text-center {
+				justify-content: center;
+			}
+			
+			&.text-right {
+				justify-content: flex-end;
+			}
+		}
+	}
+</style>