1503 lines
41 KiB
Vue
Raw Normal View History

2025-03-10 13:49:13 +08:00
<template>
<view class="z-table">
<view class="z-table-main" :style="[{ 'background-color': this.tempbackgroundColor }, { color: this.temptextColor }, { height: tableHeight + 'px' }]">
<view v-if="!tableLoaded && (!tableData || !columns)" :class="['z-loading', { ztableLoading: tableShow }]"><view class="z-loading-animate"></view></view>
<!-- 查询过滤器 -->
<view v-if="tbName.length > 0 || searchBox" class="pagination" :style="[{ 'border-bottom': '1rpx solid ' + templineColor }]">
<uni-lr-row :layout="searchBoxTW" style="height: 100%;width: 100%;padding:0">
<uni-input-row
slot="left"
v-if="tbName.length > 0 || searchBox"
type="hint"
:inputBorder="false"
v-model="tbName"
style="height: 100%;"
:after="false"
:hintIcons="false"
inputIcon="info"
:placeholderColor="temptitleColor"
:alignment="titleAlignment"
:iconColor="tempiconColor"
:readonlyColor="tempbackgroundColor"
:showButton="searchBox"
buttonIcon="search"
@buttonClick="buttonClick"
></uni-input-row>
<uni-lr-row v-if="searchBox" slot="right" :layout="40" style="height: 100%;width: 100%;">
<uni-input-row
slot="left"
type="combox"
rowHeight="100%"
:after="false"
:inputBorder="false"
:textColor="temptextColor"
:iconColor="tempiconColor"
:defaultColor="tempbackgroundColor"
:selectorColor="tempbackgroundColor"
:verify="true"
:clearable="true"
v-model="searchCol"
:range="searchColData"
rangeKey="field"
rangeVal="title"
placeholder="列名"
></uni-input-row>
<uni-input-row
slot="right"
type="text"
rowHeight="100%"
:after="false"
:inputBorder="false"
:textColor="temptextColor"
:iconColor="tempiconColor"
:defaultColor="tempbackgroundColor"
:buttonColor="tempbuttonColor"
clearable
showButton
btConfirm
buttonIcon="search"
v-model="searchVal"
@clear="onSearchClear"
@confirm="onSearch($event)"
placeholder="搜索内容"
></uni-input-row>
</uni-lr-row>
</uni-lr-row>
</view>
<!-- 表格内容 -->
<!-- "{ height: (searchBox || tbName.length > 0 ? 0 : 10) + (pagination ? 0 : 10) + 80 + '%' }" -->
<scroll-view scroll-y="true" scroll-x="true" :style="tbHeight">
<view class="z-table-pack" :style="[{ 'background-color': tempbackgroundColor }, { color: temptextColor }]">
<!-- 行头部 -->
<view class="z-table-title" :style="[{ 'border-right': '1rpx solid ' + templineColor }, { 'border-bottom': '1rpx solid ' + templineColor }]">
<!-- 添加行编号 -->
<view
v-if="rowNumbers"
:class="['z-table-title-item', { 'z-table-stick-side': fixedSelectBox }]"
:style="[
{ 'border-left': '1rpx solid ' + templineColor },
{ 'border-right': '1rpx solid ' + (showSelect ? 'rgb(0, 0, 0, 0)' : templineColor) },
{ 'border-top': '1rpx solid ' + templineColor },
{ width: rowNW + 'rpx' },
{ 'background-color': tempbackgroundColor },
{ color: temptextColor }
]"
>
<view class="z-table-col-text text-center">
<view v-if="rowCount">{{ tableData.length }}</view>
</view>
</view>
<!-- 单选图片显示 -->
<view
v-if="showSelect && singleSelect"
:class="['z-table-title-item', { 'z-table-stick-side': fixedSelectBox }]"
:style="[
{ 'border-left': '1rpx solid ' + templineColor },
{ 'border-right': '1rpx solid ' + templineColor },
{ 'border-top': '1rpx solid ' + templineColor },
{ left: rowNumbers ? rowNW + 'rpx' : '0' },
{ width: showSW + 'rpx' },
{ 'background-color': tempbackgroundColor },
{ color: temptextColor }
]"
>
<uni-icons :color="tempiconColor" type="circle-filled" size="18"></uni-icons>
</view>
<!-- 多选复选框显示 -->
<view
v-if="showSelect && !singleSelect"
:class="['z-table-title-item', { 'z-table-stick-side': fixedSelectBox }]"
:style="[
{ 'border-left': '1rpx solid ' + templineColor },
{ 'border-right': '1rpx solid ' + templineColor },
{ 'border-top': '1rpx solid ' + templineColor },
{ left: rowNumbers ? rowNW + 'rpx' : '0' },
{ width: showSW + 'rpx' },
{ 'background-color': tempbackgroundColor },
{ color: temptextColor }
]"
>
<view class="select-box" @click="doSelect(true)"><view :class="['select-tip', { selected: selectAll }]"></view></view>
</view>
<!-- 列集合遍历 -->
<view
class="z-table-title-item"
:class="{ 'z-table-stick-side': stickSide && index == 0 }"
:style="[
{ 'border-left': '1rpx solid ' + (index === 0 ? 'rgb(0, 0, 0, 0)' : templineColor) },
{ 'border-top': '1rpx solid ' + templineColor },
{ left: stickSide ? (rowNumbers ? rowNW : 0) + (showSelect ? showSW : 0) + 'rpx' : '0' },
{ width: item.width ? item.width + 'rpx' : '200rpx' },
{ 'background-color': tempbackgroundColor },
{ color: temptextColor }
]"
v-for="(item, index) in columns"
:key="index"
v-if="!item.hidden"
@click="sort(item.field, index)"
>
<view v-if="item.group" style="height: 100%;width: 100%;">
<!-- 显示列分组标题明细 -->
<view v-for="(gCol, gIndex) in item.columns" :key="gIndex">
<view
v-if="gIndex === 0 && item.gTitle && item.gTitle.length > 0"
:class="[
'z-table-col-text',
{
'text-left': item.talign === 'left' || talign === 'left',
'text-center': item.talign === 'center' || talign === 'center',
'text-right': item.talign === 'right' || talign === 'right'
}
]"
>
<view v-html="getTitleText(item)"></view>
</view>
<view
v-if="gCol.title && gCol.title.length > 0"
:class="[
'z-table-col-text',
{
'text-left': gCol.talign === 'left' || talign === 'left',
'text-center': gCol.talign === 'center' || talign === 'center',
'text-right': gCol.talign === 'right' || talign === 'right'
}
]"
>
<view v-html="getTitleText(gCol)"></view>
</view>
</view>
</view>
<view
v-else
:class="[
'z-table-col-text',
{
'text-left': item.talign === 'left' || talign === 'left',
'text-center': item.talign === 'center' || talign === 'center',
'text-right': item.talign === 'right' || talign === 'right'
}
]"
>
<view v-if="item.title && item.title.length > 0" v-html="getTitleText(item)"></view>
<view v-if="item.hasOwnProperty('field') && item.hasOwnProperty('sort') && tableData.length" class="sort">
<view
class="up-arrow"
:style="{ 'border-bottom': '4px solid ' + (nowSortKey == item.field && sortType == 'asc' ? temptitleColor : temptextColor) }"
></view>
<view
class="down-arrow"
:style="{ 'border-top': '4px solid ' + (nowSortKey == item.field && sortType == 'desc' ? temptitleColor : temptextColor) }"
></view>
</view>
</view>
</view>
</view>
<!-- 行内容 -->
<view
v-if="tableData.length"
:style="[{ 'border-right': '1rpx solid ' + templineColor }]"
:class="['table-container-box', { 'short-table': !longTable && showBottomSum }]"
>
<view
v-if="row.rowSearch && (!pagination || (current * pageSize - pageSize <= iIndex && iIndex < current * pageSize))"
class="z-table-container-row"
:class="{ 'z-table-has-bottom': showBottomSum }"
v-for="(row, iIndex) in tableData"
:key="iIndex"
:style="onRowStyle(row, iIndex)"
>
<!-- 添加行编号 -->
<view
v-if="rowNumbers"
:class="['z-table-container-col', { 'z-table-stick-side': fixedSelectBox }]"
:style="[
{ 'border-left': '1rpx solid ' + templineColor },
{ 'border-right': '1rpx solid ' + (showSelect ? 'rgb(0, 0, 0, 0)' : templineColor) },
{ 'border-top': '1rpx solid ' + (iIndex === 0 ? 'rgb(0, 0, 0, 0)' : templineColor) },
{ 'border-bottom': (!fillRow && iIndex == tableData.length - 1 ? '1' : '0') + 'rpx solid ' + templineColor },
{ width: rowNW + 'rpx' },
{ 'background-color': tempbackgroundColor }
]"
>
<view class="z-table-col-text text-center">
<view>{{ iIndex + 1 }}</view>
</view>
</view>
<!-- 添加复选框 -->
<view
v-if="showSelect"
:class="['z-table-container-col', { 'z-table-stick-side': fixedSelectBox }]"
:style="[
{ 'border-left': '1rpx solid ' + templineColor },
{ 'border-right': '1rpx solid ' + templineColor },
{ 'border-top': '1rpx solid ' + (iIndex === 0 ? 'rgb(0, 0, 0, 0)' : templineColor) },
{ 'border-bottom': (!fillRow && iIndex == tableData.length - 1 ? '1' : '0') + 'rpx solid ' + templineColor },
{ left: rowNumbers ? rowNW + 'rpx' : '0' },
{ width: showSW + 'rpx' },
{ 'background-color': tempbackgroundColor }
]"
>
<view class="select-box" @click="doSelect(false, iIndex, row)"><view :class="['select-tip', { selected: row['rowChecked'] }]"></view></view>
</view>
<!-- 行数据遍历 -->
<view
:class="['z-table-container-col', { 'z-table-stick-side': stickSide && jIndex == 0 }]"
:style="[
{ 'border-left': '1rpx solid ' + (jIndex === 0 ? 'rgb(0, 0, 0, 0)' : templineColor) },
{ 'border-top': '1rpx solid ' + (iIndex === 0 ? 'rgb(0, 0, 0, 0)' : templineColor) },
{ 'border-bottom': (!fillRow && iIndex == tableData.length - 1 ? '1' : '0') + 'rpx solid ' + templineColor },
{ left: stickSide ? (rowNumbers ? rowNW : 0) + (showSelect ? showSW : 0) + 'rpx' : '0' },
{ width: col.width ? col.width + 'rpx' : '200rpx' }
]"
v-for="(col, jIndex) in columns"
:key="jIndex"
v-if="!col.hidden"
@click="itemClick(iIndex, row, col)"
>
<!-- 多分组的时候是否需要把内容填充 style="height: 100%;width: 100%;" -->
<view v-if="col.group">
<view v-for="(gCol, gIndex) in col['columns']" :key="gIndex">
<view
v-html="getRowContent(row, gCol, iIndex)"
:class="[
'z-table-col-text',
{
'text-left': col.align === 'left' || align === 'left',
'text-center': col.align === 'center' || align === 'center',
'text-right': col.align === 'right' || align === 'right'
}
]"
></view>
</view>
</view>
<view
v-else
v-html="getRowContent(row, col, iIndex)"
:class="[
'z-table-col-text',
{
'text-left': col.align === 'left' || align === 'left',
'text-center': col.align === 'center' || align === 'center',
'text-right': col.align === 'right' || align === 'right'
}
]"
></view>
</view>
</view>
<!-- 空白行 -->
<view
v-if="fillRow && tableData.length < fillCount"
class="z-table-container-row"
:class="{ 'z-table-has-bottom': showBottomSum }"
v-for="(row, marginIndex) in fillCount"
>
<!-- 添加行编号 -->
<view
v-if="rowNumbers"
:class="['z-table-container-col', { 'z-table-stick-side': fixedSelectBox }]"
:style="[
{ 'border-left': '1rpx solid ' + templineColor },
{ 'border-right': '1rpx solid ' + (showSelect ? 'rgb(0, 0, 0, 0)' : templineColor) },
{ width: rowNW + 'rpx' },
{ 'background-color': (tableData.length + marginIndex) % 2 === 0 ? tempbackgroundColor2 : tempbackgroundColor }
]"
>
<view class="z-table-col-text text-center"><view></view></view>
</view>
<!-- 添加复选框 -->
<view
v-if="showSelect"
:class="['z-table-container-col', { 'z-table-stick-side': fixedSelectBox }]"
:style="[
{ 'border-left': '1rpx solid ' + templineColor },
{ 'border-right': '1rpx solid ' + templineColor },
{ left: rowNumbers ? rowNW + 'rpx' : '0' },
{ width: showSW + 'rpx' },
{ 'background-color': (tableData.length + marginIndex) % 2 === 0 ? tempbackgroundColor2 : tempbackgroundColor }
]"
>
<view><view class="select-tip"></view></view>
</view>
<!-- 行数据遍历 -->
<view
:class="['z-table-container-col', { 'z-table-stick-side': stickSide && jIndex == 0 }]"
:style="[
{ 'border-left': '1rpx solid ' + (marginjIndex === 0 ? 'rgb(0, 0, 0, 0)' : templineColor) },
{ left: stickSide ? (rowNumbers ? rowNW : 0) + (showSelect ? showSW : 0) + 'rpx' : '0' },
{ width: col.width ? col.width + 'rpx' : '200rpx' },
{ 'background-color': (tableData.length + marginIndex) % 2 === 0 ? tempbackgroundColor2 : tempbackgroundColor }
]"
v-for="(col, marginjIndex) in columns"
v-if="!col.hidden"
>
<view class="z-table-col-text"></view>
</view>
</view>
</view>
</view>
<!-- 页脚 -->
<view
:class="['z-table-bottom', { 'long-table': longTable }]"
:style="[{ 'border-right': '1rpx solid ' + templineColor }, { 'border-bottom': '1rpx solid ' + templineColor }]"
v-if="showBottomSum && tableData.length"
>
<!-- 行号和复选框站的宽度 -->
<view
class="z-table-bottom-col z-table-stick-side"
:style="[
{ 'border-left': '1rpx solid ' + templineColor },
{ 'border-right': '1rpx solid ' + templineColor },
{ 'border-top': '1rpx solid ' + templineColor },
{ width: (rowNumbers ? rowNW : 0) + (showSelect ? showSW : 0) + (rowNumbers && showSelect ? -1 : 0) + 'rpx' },
{ 'background-color': tempfooterColor },
{ color: temptextColor }
]"
>
<view class="z-table-bottom-text"><uni-icons :color="tempiconColor" type="huizong" size="20" /></view>
</view>
<!-- 遍历列数分别计算合计值 -->
<view
class="z-table-bottom-col"
:class="{ 'z-table-stick-side': stickSide && sumIndex == 0 }"
:style="[
{ 'border-left': '1rpx solid ' + (sumIndex === 0 ? 'rgb(0, 0, 0, 0)' : templineColor) },
{ 'border-top': '1rpx solid ' + templineColor },
{ left: stickSide ? (rowNumbers ? rowNW : 0) + (showSelect ? showSW : 0) + 'rpx' : '0' },
{ width: sumCol.width ? sumCol.width + 'rpx' : '200rpx' },
{ 'background-color': tempfooterColor },
{ color: temptextColor }
]"
v-for="(sumCol, sumIndex) in columns"
:key="sumIndex"
v-if="!sumCol['hidden']"
>
<view
class="z-table-bottom-text"
:class="[
'z-table-col-text',
{
'text-left': sumCol.align === 'left' || align === 'left',
'text-center': sumCol.align === 'center' || align === 'center',
'text-right': sumCol.align === 'right' || align === 'right'
}
]"
>
<!-- 默认总计后面扩展平均值最大最小值都可以 -->
<text>{{ dosum(sumCol) }}</text>
</view>
</view>
</view>
</scroll-view>
<!-- 分页器 -->
<view v-if="pagination" class="pagination" :style="[{ 'border-top': '1rpx solid ' + templineColor }]">
<uni-pagination
style="height: 100%;"
show-icon="true"
:pageSize="pageSize"
:iconColor="tempiconColor"
:textColor1="temptitleColor"
:textColor="temptextColor"
:buttonColor="tempbuttonColor"
:total="searchLen > 0 ? searchLen : tableData.length"
:current="current"
@change="paginationChange"
></uni-pagination>
</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以'|'链接,如果不带'|'默认键值同名
* cellClick(是否监听点击事件Boolean)}
* stickSide Boolean | 是否固定右侧首栏 默认不显示
* showBottomSum Boolean | 是否显示底部统计 默认不显示
* showLoading Boolean | 是否首次加载首次加载不显示暂无数据内容
* emptyText String | 空数据显示的文字内容
* tableHeight Number | 设置表格高度会滚动
* sort Boolean | 开启排序
* showSelect Boolean | 开启选择
* singleSelect Boolean | 在开启选择的状态下是否开起单选
* align String | 内容对齐方式 left center right
* talign String | 表头对齐方式 left center right
*
* event: onSort | 排序事件 返回{field: 被排序列的字段名, type: 正序'asc'/倒序'desc'}
* onSelect | 选中时触发 返回选择的行的下标
* onClick | 单元格点击事件 返回点击单元格所属行的数据
*
* function: resetSort | 调用后重置排序 *注意:不会触发sort事件
*
* */
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: [],
//分页器,每页数据量
pageSize: 100,
//分页器,当前页
current: 1,
//查询过滤后的数据源总数
searchLen: 0,
//查询列下拉框数据源
searchColData: [],
//查询列下拉框选中行数据
searchCol: {},
//查询过滤输入框输入的内容
searchVal: '',
//查询管理器中的宽度比例 title width
searchBoxTW: 100,
//按钮颜色
tempbuttonColor: '#ffffff',
//选择行背景颜色
tempselectRowColor: '#ffe48d',
//图片按钮颜色
tempiconColor: '#000000',
//标题类字体颜色
temptitleColor: '#5500ff',
//文本颜色/线颜色
temptextColor: '#000000',
//线颜色
templineColor: '#d4d4d4',
//主背景颜色
tempbackgroundColor: '#f3f3f3',
//次背景颜色
tempbackgroundColor2: '#ffffff',
//页脚背景颜色
tempfooterColor: '#d4e6ff',
fillCount: 15
};
},
computed: {
//表格高度样式设置,计算是否显示表头和分页器,然后计算出行内容高度
tbHeight() {
let h=0;
if(this.searchBox || this.tbName.length > 0){
h+=33;
}
if(this.pagination)
{
h+=33;
}
return {height:this.tableHeight-h+"px"};
}
},
props: {
tbName: {
type: String,
default: ''
},
tableData: {
type: [Array, Boolean],
default() {
return false;
}
},
columns: {
/*
*
* [{title: xxx, field: 当前列展示对象名, 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: 500
},
//开启选择
showSelect: {
type: Boolean,
default: false
},
//在开启选择的状态下是否开起单选
singleSelect: {
type: Boolean,
default: true
},
//内容对齐方式 left center right
align: {
type: String,
default: 'left' // right|center|left
},
//表头对齐方式 left center right
talign: {
type: String,
default: 'left' // right|center|left
},
//如果为true当用户点击行的时候该复选框就会被选中或取消选中。
//如果为false当用户仅在点击该复选框的时候才会被选中或取消。
checkOnSelect: {
type: Boolean,
default: true
},
//是否固定复选框
fixedSelectBox: {
type: Boolean,
default: true
},
//是否显示总行数。
rowCount: {
type: Boolean,
default: false
},
//如果为true则显示一个行号列。
rowNumbers: {
type: Boolean,
default: false
},
//表格数据查询操作
searchBox: {
type: Boolean,
default: false
},
//表格数据分页操作
pagination: {
type: Boolean,
default: false
},
//行序号宽度 rowNumbersWidth
rowNW: {
type: Number,
default: 50
},
//复选框宽度 showSelectWidth
showSW: {
type: Number,
default: 50
},
// 主题颜色,默认白色
theme: {
type: String,
default: ''
},
//按钮背景颜色
buttonColor: {
type: String,
default: '#ffffff'
},
//选择行背景颜色
selectRowColor: {
type: String,
default: '#ffe48d'
},
//图片按钮颜色
iconColor: {
type: String,
default: '#000000'
},
//标题类字体颜色
titleColor: {
type: String,
default: '#5500ff'
},
//标题类字体对齐方式 默认左对齐
titleAlignment: {
type: String,
default: 'left'
},
//文本颜色
textColor: {
type: String,
default: '#000000'
},
//线条颜色
lineColor: {
type: String,
default: '#d4d4d4'
},
//主背景颜色
backgroundColor: {
type: String,
default: '#f3f3f3'
},
//次背景颜色
backgroundColor2: {
type: String,
default: '#ffffff'
},
//页脚颜色
footerColor: {
type: String,
default: '#d4e6ff'
},
//是否填充空行
fillRow: {
type: Boolean,
default: false
}
},
created() {},
mounted() {
//初始化表格数据
this.init();
//设置主题颜色
if (this.theme === 'white') {
//白色
//按钮颜色
this.tempbuttonColor = '#ffffff';
//选择行背景颜色
this.tempselectRowColor = '#ffe48d';
//图片按钮颜色
this.tempiconColor = '#000000';
//标题类字体颜色
this.temptitleColor = '#5500ff';
//文本颜色/线颜色
this.temptextColor = '#000000';
//线颜色
this.templineColor = '#d4d4d4';
//主背景颜色
this.tempbackgroundColor = '#f3f3f3';
//次背景颜色
this.tempbackgroundColor2 = '#ffffff';
//页脚颜色
this.tempfooterColor = '#d4e6ff';
} else if (this.theme === 'black') {
//黑色
this.tempbuttonColor = '#5e6576';
this.tempselectRowColor = '#5e6434';
this.tempiconColor = '#bcc1d6';
this.temptitleColor = '#2488ea';
this.temptextColor = '#bcc1d6';
this.templineColor = '#8e93a1';
this.tempbackgroundColor = '#0f1628';
this.tempbackgroundColor2 = '#1c263f';
this.tempfooterColor = '#545834';
} else {
//自定义
this.tempbuttonColor = this.buttonColor;
this.tempselectRowColor = this.selectRowColor;
this.tempiconColor = this.iconColor;
this.temptitleColor = this.titleColor;
this.temptextColor = this.textColor;
this.templineColor = this.lineColor;
this.tempbackgroundColor = this.backgroundColor;
this.tempbackgroundColor2 = this.backgroundColor2;
this.tempfooterColor = this.footerColor;
}
},
watch: {
columns(e) {
this.init();
},
tableData() {
this.init();
},
buttonColor(newVal, oldVal) {
this.tempbuttonColor = newVal;
},
selectRowColor(newVal, oldVal) {
this.tempselectRowColor = newVal;
},
iconColor(newVal, oldVal) {
this.tempiconColor = newVal;
},
titleColor(newVal, oldVal) {
this.temptitleColor = newVal;
},
textColor(newVal, oldVal) {
this.temptextColor = newVal;
},
backgroundColor(newVal, oldVal) {
this.tempbackgroundColor = newVal;
},
backgroundColor2(newVal, oldVal) {
this.tempbackgroundColor2 = newVal;
}
// tableHeight(newVal,oldVal){
// console.log(newVal+'高度改变'+oldVal);
// if (newVal != oldVal) {
// console.log('高度改变');
// }
// },
},
methods: {
//行样式操作
onRowStyle(row, index) {
let backgroundColor = row['rowChecked'] ? this.tempselectRowColor : index % 2 === 0 ? this.tempbackgroundColor2 : this.tempbackgroundColor;
let css = {
'background-color': backgroundColor,
color: this.temptextColor
};
this.$emit('onRowStyle', { index: index, row: row }, res => {
var assignObj = Object.assign(css, res);
});
return css;
},
async init() {
// 重置选择内容
this.selectAll = false;
this.selectArr = [];
this.tableLoaded = false;
this.tableShow = true;
this.searchLen = 0;
this.pageSize = 100;
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;
});
}
if (_this.tableData && _this.tableData.length) {
_this.tableData.forEach((item, index) => {
this.$set(item, 'rowChecked', false);
this.$set(item, 'rowSearch', true);
this.$set(item, 'rowIndex', index);
});
}
let tempSearchColData = [];
if (_this.columns && _this.columns.length) {
_this.columns.forEach((item, index) => {
if (item['group'] && item['columns'].length > 0) {
item['columns'].forEach((gItem, gIndex) => {
if (gItem.field && gItem.title) {
this.$set(gItem, 'index', index + gIndex);
tempSearchColData.push(gItem);
}
});
} else {
if (item.field && item.title) {
this.$set(item, 'index', index);
tempSearchColData.push(item);
}
}
});
this.searchColData = tempSearchColData;
}
},
getPageSize(selecter) {
// 获取元素信息
let query = uni.createSelectorQuery().in(this),
_this = this;
return new Promise((resolve, reject) => {
query
.select(selecter)
.boundingClientRect(res => {
resolve(res);
})
.exec();
});
},
//页脚计算数量
dosum({ field, noSum = false, formatNum = true }) {
let sum = '-';
if (noSum) return sum;
if (this.tableData) {
if (
this.tableData.every(item => {
return !Number.isNaN(item[field] - 0);
})
) {
sum = 0;
this.tableData.map((item, index) => {
if (!field && index != 0) {
sum = '-';
} else {
let val = item[field] - 0;
if (Number.isNaN(val) || !item['rowSearch']) {
sum += 0;
} else {
sum += val;
}
}
});
}
}
// sum = sum == 0 ? "-" : sum
// return formatNum ? this.numTransform(sum) : sum
return sum;
},
//得到单元格html内容
getRowContent(row, col, index) {
let tempHTML = '';
//得到内容
let rowKey = row[col.field];
if ([null, ''].includes(rowKey)) {
rowKey = '-';
}
let { formatNum = true } = col;
if (rowKey || rowKey === 0) {
tempHTML = rowKey;
// tempHTML = isNaN(rowKey - 0) || !formatNum
// ? rowKey
// : this.numTransform(rowKey - 0);
}
//设置自定义内容
if (col.format) {
tempHTML = col.format(rowKey, row, index);
}
tempHTML = "<view style='white-space:normal;word-break:break-all;word-wrap:break-word;'>" + tempHTML + '</view>';
//设置超链接路径
if (col.link) {
let link = col.link(tempHTML, row, index);
let urlParam = {};
if (link.params && link.params.length > 0) {
link.params.forEach(item => {
if (~item.indexOf('|')) {
let temp = item.split('|');
urlParam[temp[0]] = row[temp[1]];
} else {
urlParam[item] = row[item];
}
});
}
let url = this.setUrlParams(link.url, urlParam); //得到超链接参数
tempHTML = '<a style="color:' + this.temptitleColor + '" href="' + url + '">' + tempHTML + '</a>';
}
//设置自定义单元格样式
if (col.styler) {
let align = 'flex-start'; //默认 left
if (col.align === 'center' || align === 'center') {
align = 'center';
}
if (col.align === 'right' || align === 'right') {
align = 'flex-end';
}
let style = ' display: flex;' + 'width: 100%;' + 'height: 100%;' + 'flex: 1;' + 'justify-content: ' + align + ';' + 'align-content:center; ';
style += col.styler(rowKey, row, index);
tempHTML = '<view style="' + style + '"><view>' + tempHTML + '</view></view>';
}
return tempHTML.toString();
},
// 得到超链接参数
setUrlParams(url, params) {
let tempUrl = url,
keyArr = Object.keys(params);
keyArr.forEach(item => {
tempUrl += `&${item}=${params[item]}`;
});
tempUrl = tempUrl.replace(/\&/, '?');
return tempUrl;
},
//排序操作
sort(field, index) {
if (!field || !this.columns[index].sort) {
return;
}
// 排序功能: 如果点击的排序按钮是原先的 那么更改排序类型
// 如果点击的另一个排序按钮 那么选择当前排序并且排序类型改为降序(desc)
if (field != this.nowSortKey) {
this.nowSortKey = field;
this.sortType = 'desc';
} else {
this.toggleSort();
}
//优先先本地排序如果需要重新从数据库查询可调用onSort事件
let arr = this.tableData;
for (let j = 0; j < arr.length - 1; j++) {
//外层循环,控制趟数,每一次找到一个最大值
for (let i = 0; i < arr.length - 1 - j; i++) {
// 内层循环,控制比较的次数,并且判断两个数的大小
if (this.sortType == 'asc') {
if (arr[i][this.nowSortKey] > arr[i + 1][this.nowSortKey]) {
let temp = arr[i + 1];
arr[i + 1] = arr[i];
arr[i] = temp;
}
} else if (arr[i][this.nowSortKey] < arr[i + 1][this.nowSortKey]) {
let temp = arr[i + 1];
arr[i + 1] = arr[i];
arr[i] = temp;
}
}
}
this.$emit('onSort', {
field: 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';
},
// 单元格点击事件
itemClick(index, row, col) {
//隐藏查询工具栏
if (this.searchBoxTW === 0) {
this.buttonClick();
}
//点击就选中行
if (this.checkOnSelect) {
this.doSelect(false, index, row);
}
//监听单元格点击事件
if (col.cellClick) {
this.$emit('onClick', { index: index, row: row, col: col });
}
},
//选中行事件 isAll:是否为全选
doSelect(isAll = false, thisIndex, thisRow) {
if (isAll) {
this.selectArr = [];
this.selectAll = !this.selectAll;
this.tableData.forEach((item, index) => {
if (this.selectAll) {
if (item['rowSearch']) {
if (!item['rowChecked']) {
this.$set(item, 'rowChecked', true);
}
this.selectArr.push(index);
}
} else {
if (item['rowChecked']) {
this.$set(item, 'rowChecked', false);
}
}
});
} else {
const thisCheck = this.tableData[thisIndex]['rowChecked'] || false;
this.$set(this.tableData[thisIndex], 'rowChecked', !thisCheck);
//检查是否已经为全部选中状态
var tempSelectAll = true;
//单选
if (this.singleSelect) {
//判断是不是原来选择的索引如果就直接清空不需要在push到选择数组中
if (this.selectArr.length > 0 && this.selectArr[0] == thisIndex) {
this.selectArr = [];
} else {
this.selectArr = [];
this.selectArr.push(thisIndex);
}
tempSelectAll = false;
}
this.selectArr = [];
for (let i = 0; i < this.tableData.length; i++) {
if (this.singleSelect && thisIndex !== i) {
//单选
//除了当前选中的,其他全部为未选中状态
this.$set(this.tableData[i], 'rowChecked', false);
} else {
//多选
if (tempSelectAll && !this.tableData[i]['rowChecked']) {
tempSelectAll = false;
}
if (this.tableData[i]['rowChecked']) {
this.selectArr.push(i);
}
}
}
this.selectAll = tempSelectAll;
}
const index = this.singleSelect ? (this.selectArr.length > 0 ? this.selectArr[0] : -1) : this.selectArr;
this.$emit('onSelect', { index: index, row: thisRow });
},
// 返回表头标签内容
getTitleText(col) {
let tempHTML = '';
if (col.titler) {
tempHTML = col.titler();
} else if (col.title) {
tempHTML = col.title;
} else {
tempHTML = col.gTitle;
}
return tempHTML.toString();
},
//分页器点击事件
paginationChange(e) {
this.current = e.current;
// console.log(JSON.stringify(e)+this.current);
},
//搜索内容提交事件
onSearch(e) {
//搜索内容为空的时候,默认全选
if (e === '') {
this.tableData.forEach((item, index) => {
this.$set(item, 'rowSearch', true);
});
this.searchLen = this.tableData.length;
this.pageSize = 100;
return;
}
//过滤后的数据总行数
var len = 0;
this.tableData.forEach((item, index) => {
//指定列过滤
if (this.searchCol['index'] >= 0) {
var cellVal = item[this.searchCol['key']] + '';
if (cellVal.length > 0 && cellVal.indexOf(e) > -1) {
this.$set(item, 'rowSearch', true);
len += 1;
} else {
this.$set(item, 'rowSearch', false);
}
} else {
//全列过滤
this.$set(item, 'rowSearch', false);
let exist = false;
for (var i = 0; i < this.searchColData.length; i++) {
//列集合中不存在field
if (!this.searchColData[i]['field']) {
continue;
}
var cellVal = item[this.searchColData[i]['field']] + '';
if (cellVal.length > 0 && cellVal.indexOf(e) > -1) {
this.$set(item, 'rowSearch', true);
exist = true;
}
}
len += exist ? 1 : 0;
}
});
this.searchLen = len;
//查询后的数据不进行分页
this.current = 1;
this.pageSize = this.tableData.length;
},
//搜索清空按钮事件
onSearchClear() {
this.onSearch('');
},
// 展开查询框按钮事件
buttonClick() {
this.searchBoxTW = this.searchBoxTW === 0 ? 100 : 0;
}
}
};
</script>
<style lang="scss">
.m-input-row {
padding: 0;
}
.pagination {
// height: 10%;
height: 33px;
}
.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%;
width: 100%;
min-height: 130rpx;
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-pack {
position: relative;
min-height: 100%;
width: fit-content;
}
.z-table-title {
position: sticky;
width: 100% !important;
top: 0;
min-height: 64rpx;
z-index: 1;
.z-table-title-item {
// border-bottom: solid 1rpx #dbdbdb;
// border-right: 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 3rpx;
min-height: 64rpx;
align-items: center;
line-height: 64rpx;
box-sizing: border-box;
// flex-direction:column;
// border-right: solid 1rpx #dbdbdb;
}
}
.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 0 16rpx 0;
box-sizing: border-box;
}
.z-table-bottom-text {
line-height: 100%;
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: 30rpx;
height: 30rpx;
line-height: 14rpx;
margin: 0 5rpx;
border: solid 2rpx #4298f7;
border-radius: 4rpx;
background: #fff;
text-align: center;
}
.select-tip {
display: inline-block;
opacity: 0;
transform: rotate(90deg);
transition: all 0.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%;
height: 100%;
flex: 1;
justify-content: flex-start;
align-content: center;
&.text-center {
justify-content: center;
}
&.text-right {
justify-content: flex-end;
}
}
}
</style>