cloud-ui.vusion
Version:
Vusion Cloud UI
726 lines (698 loc) • 32.2 kB
JavaScript
import { getStyle, getScrollSize } from '../base/utils/style';
import { ellipsisTitle } from 'proto-ui.vusion/src/base/directives';
import { deepCopy } from '../base/utils/index';
export default {
name: 'u-table-view',
props: {
title: String,
data: Array,
allChecked: { type: Boolean, default: false },
defaultSort: {
type: Object,
default() {
return {
title: undefined,
order: undefined,
};
},
},
defaultFilter: {
type: Object,
default() {
return {
title: undefined,
value: undefined,
column: undefined,
};
},
},
noDataText: { type: String, default: '暂无数据' },
loading: { type: Boolean, default: false },
height: [String, Number],
maxHeight: [String, Number],
minHeight: [String, Number],
layout: {
type: String,
default: 'fixed',
},
border: { type: Boolean, default: false },
xScroll: { type: Boolean, default: false }, // 用来处理当表格出现水平滚动条时,默认scroll事件走表格的水平滚动
width: [String, Number],
visible: { type: Boolean, default: true },
pattern: { type: String, default: 'normal' }, // 特殊显示内容情形
limit: { type: Number, default: 5 }, // 用来默认显示limit条数据
limitText: { type: String, default: '查看更多' },
allText: { type: String, default: '收起' },
defaultText: { type: String, default: '-' },
expandPattern: { type: String, default: 'toggle' },
// mode: { type: String, default: 'self' }, // fixed布局的时候计算方式是走原生表格的还是走自定义计算规则配置项
showHeader: { type: Boolean, default: true }, // 展示表格头部
loadText: { type: String, default: '' }, // 加载状态显示的文字
rowClassName: { type: Function, default() { return ''; } }, // 自定义表格单行的样式
color: String,
forceFilter: { type: Boolean, default: true },
},
data() {
return {
columns: [],
tdata: [],
allSel: this.allChecked,
columnsWidth: [],
fixedRightWidth: [],
copyTdata: [], // tdata的复制版本主要用来过滤
tableWidth: undefined, // display值为none的时候需要特殊处理这个值
bodyHeight: undefined,
maxBodyHeight: undefined,
minBodyHeight: undefined,
fixedTableHeight: undefined, // 当固定列和表格的高度一起使用的时候
fixedMaxTableHeight: undefined,
fixedMinTableHeight: undefined,
bodyWidth: undefined, // 当出现垂直滚动条的时候,需要减去滚动条的宽度,确保不会出现水平滚动条
scrollWidth: undefined,
over: false, // 当mouseover在表格时,此值为true
fixedHeight: [], // 当fixed时表格行的高度值
fixedLeftWidth: null, // fixed 时表格左部分宽度和的值
rightColumns: [], // fixed值是right时需要重构columns顺序
rightColumnsWidth: [], // fixed值是right时需要重构columnsWidth顺序
isXScroll: false, // 判断是否会出现水平滚动条的情况
isYScroll: false, // 判断添加height属性后,垂直方向是否应该添加滚动条
fixedHover: false, // 用来实现党左右列固定的时候,hover到左右列的时候,阴影效果能够同步实现
// filterValue: undefined, // 用来记录当前filter选项的值,方便在过滤的时候点击更多显示正确的数据
// filterColumn: undefined, // 用来记录当前filter列,方便在过滤的时候点击更多显示正确的数据
filterTdata: undefined, // 用来记录当前filter列过滤后符合条件的所有数据
currentSortColumn: undefined, // 表示当前排序列
currentSort: this.defaultSort,
};
},
directives: { ellipsisTitle },
created() {
this.$on('add-item-vm', (itemVM) => {
itemVM.parentVM = this;
this.columns.push(itemVM);
});
this.$on('remove-item-vm', (itemVM) => {
itemVM.parentVM = undefined;
this.columns.splice(this.columns.indexOf(itemVM), 1);
});
},
mounted() {
if (this.pattern === 'limit')
this.tdata = this.initTableData(this.limit);
else
this.tdata = this.initTableData();
// this.copyTdata = this.initTableData();
this.handleResize();
window.addEventListener('resize', this.onResize, false);
if (this.xScroll)
document.addEventListener('mousewheel', this.onMouseWheel, false);
},
computed: {
fixedLeftColumns() {
return this.columns.filter((column) => column.fixed === 'left');
},
fixedRightColumns() {
const rightCols = [];
const other = [];
this.columns.forEach((col) => {
if (col.fixed && col.fixed === 'right') {
rightCols.push(col);
} else {
other.push(col);
}
});
this.rightColumns = rightCols.concat(other);
return rightCols;
},
expandedColumn() {
return this.columns.filter((column) => column.type === 'expand')[0];
},
allDisabled() {
return this.tdata.every((item) => item.disabled);
},
},
watch: {
data: {
deep: true,
handler(newValue) {
if (this.pattern === 'limit')
this.tdata = this.initTableData(this.limit);
else
this.tdata = this.initTableData();
// this.copyTdata = this.initTableData();
const flag = this.columns.some((column) => column.filter);
if (flag && this.forceFilter) {
// 在有filter列的情况下 数据如果发生变化是需要对数据进行过滤显示的
let columnIndex;
if (this.defaultFilter.title === undefined) {
this.columns.some((item, index) => {
if (item.filter) {
this.defaultFilter.title = item.title;
this.defaultFilter.value = item.value;
this.defaultFilter.column = item;
columnIndex = index;
return true;
}
return false;
});
} else {
this.columns.some((column, index) => {
if (column.title === this.defaultFilter.title) {
this.defaultFilter.column = column;
columnIndex = index;
return true;
}
return false;
});
}
const column = this.defaultFilter.column;
const value = this.defaultFilter.value;
this.filterTdata = this.tdata = this.copyTdata.filter((item) => {
if (column.filterMethod)
return column.filterMethod(value, item[column.label], item, column);
else
return item[column.label] === value;
});
// 去掉,会导致陷入死循环中
// this.$emit('filter-change', {
// column,
// value,
// index: columnIndex,
// });
}
if (this.pattern === 'limit')
this.tdata = this.tdata.slice(0, this.limit);
if (this.currentSortColumn && !this.currentSortColumn.sortRemoteMethod) {
const order = this.currentSort.order === 'asc' ? -1 : 1;
this.sortData(this.currentSortColumn, order, 'change');
}
this.handleResize();
},
},
allChecked(newValue) {
this.allSel = newValue;
},
columnsWidth(newValue) {
const leftIndexs = [];
const rightIndexs = [];
this.rightColumnsWidth = [];
this.fixedLeftColumns && this.fixedLeftColumns.forEach((item) => {
const index = this.columns.indexOf(item);
leftIndexs.push(index);
});
this.fixedRightColumns && this.fixedRightColumns.forEach((item) => {
const index = this.columns.indexOf(item);
rightIndexs.push(index);
newValue[index] && this.rightColumnsWidth.push(newValue[index]);
});
this.columns.forEach((item, index) => {
if (rightIndexs.indexOf(index) === -1 && newValue[index]) {
this.rightColumnsWidth.push(newValue[index]);
}
});
this.fixedLeftWidth = null;
this.fixedRightWidth = null;
leftIndexs.forEach((item) => {
if (newValue[item])
this.fixedLeftWidth += parseFloat(newValue[item]);
});
rightIndexs.forEach((item) => {
if (newValue[item])
this.fixedRightWidth += parseFloat(newValue[item]);
});
if (this.isYScroll) {
this.fixedRightWidth -= this.scrollWidth;
}
},
visible(newValue) {
this.handleResize();
},
loading(newVal) {
// 比较复杂情况下,可能会先赋值data,再将loading设为false
this.handleResize();
},
defaultSort(newValue) {
this.currentSort = newValue;
},
columns() {
// 列表的列修改会导致变化 列表设置
this.handleResize();
},
},
methods: {
rowClsName(index) {
return this.rowClassName(index, this.tdata[index]);
},
showExpandIcon(column, value) {
if (column.expandStrict) {
if (!value)
return false;
if (Array.isArray(value))
return value.length;
if (typeof value === 'object')
return Object.keys(value).length;
return value;
}
return true;
},
// 展示表格单元格具体内容函数 现在规则是row[column.label]是对象,数组全部不展示内容,只展示基本类型
showContent(column, value) {
if (value === 0)
return value;
else if (!value && column.defaultText === '')
return '';
else {
if (Array.isArray(value) || typeof value === 'object' && column.defaultText === '')
return column.defaultText;
if (Array.isArray(value) || typeof value === 'object')
return column.defaultText || this.defaultText;
return value || column.defaultText || this.defaultText;
}
},
setCellWidth(column, index) {
let width = '';
if (column.currentWidth)
width = column.currentWidth;
else if (this.columnsWidth[index])
width = this.columnsWidth[index].width;
// when browser has scrollBar,set a width to resolve scroll position bug
if (width === '0')
width = '';
return width;
},
handleSort(column) {
if (column.sortable) {
if (column.title === this.currentSort.title)
this.currentSort.order = this.currentSort.order === 'asc' ? 'desc' : 'asc';
else {
this.currentSort.title = column.title;
this.currentSort.order = 'desc';
}
this.currentSortColumn = column;
const order = this.currentSort.order === 'asc' ? -1 : 1;
this.sortData(column, order);
}
},
sortData(column, order, type) {
// type 字段在data发生变化时传入,此时不能抛sort-change方法,防止死循环
const label = column.label;
if (column.sortRemoteMethod) {
// 异步执行排序方法
column.sortRemoteMethod(label, this.currentSort.order, column);
} else {
if (column.sortMethod)
this.copyTdata.sort((value1, value2) => column.sortMethod(value1[label], value2[label]) ? order : -order);
else {
this.copyTdata.sort((value1, value2) => {
if (value1[label] === value2[label])
return 0;
return value1[label] < value2[label] ? order : -order;
});
}
if (this.pattern === 'limit')
this.tdata = this.copyTdata.slice(0, this.limit);
else
this.tdata = this.copyTdata;
}
if (!type) {
this.$emit('sort-change', {
column,
label,
order: this.currentSort.order,
});
}
},
getSelection(value) {
const data = value || this.tdata;
const selectionIndexes = [];
let noDisabledCount = 0;
data.forEach((row, index) => {
if (row.selected)
selectionIndexes.push(index);
if (!row.disabled)
noDisabledCount++;
});
// 这里有坑 行数据的checkbox有disabled状态 点击全选 可以是全选的
if (selectionIndexes.length === noDisabledCount && selectionIndexes.length !== 0)
this.allSel = true;
else
this.allSel = false;
this.$emit('update:allChecked', this.allSel);
return this.data.filter((data, index) => selectionIndexes.indexOf(index) > -1);
},
allSelected() {
this.$nextTick(() => {
const flag = this.allSel;
const copydata = this.tdata.concat();
copydata.forEach((item) => {
if (!item.disabled)
item.selected = flag;
});
this.tdata = copydata;
const selection = this.getSelection();
if (flag)
this.$emit('select-all', selection);
this.$emit('selection-change', selection);
});
},
initTableData(value) {
let tdata = [];
const copyData = deepCopy([], this.data);
this.copyTdata = copyData;
copyData.forEach((item, index) => {
/* eslint-disable */
item.original_data = this.data[index];
item.original_index = index;
});
const selection = this.columns && this.columns.some((item) => item.type && item.type === 'selection');
const expand = this.columns && this.columns.some((item) => item.type && item.type === 'expand');
if (selection && expand) {
copyData.forEach((item) => {
if (item.selected === undefined)
item.selected = false;
if (item.expanded === undefined)
item.expanded = false;
if (item.iconName === undefined)
item.iconName = 'right';
if (item.disabled === undefined)
item.disabled = false;
tdata.push(item);
});
} else if (selection) {
copyData.forEach((item) => {
if (item.selected === undefined)
item.selected = false;
if (item.disabled === undefined)
item.disabled = false;
tdata.push(item);
});
} else if (expand) {
copyData.forEach((item) => {
if (item.expanded === undefined)
item.expanded = false;
if (item.iconName === undefined)
item.iconName = 'right';
tdata.push(item);
});
} else {
copyData.forEach((item) => {
tdata.push(item);
});
}
if (!copyData.length)
this.allSel = false;
// 固定左右列同步阴影实现方案
if (this.fixedLeftColumns.length > 0 || this.fixedRightColumns.length > 0)
tdata.forEach((item) => item.hover = false);
if (value)
tdata = tdata.slice(0, value);
const selectionArr = this.getSelection(this.data);
// if (selectionArr.length !== 0)
this.$emit('selection-change', selectionArr);
return tdata;
},
handleResize() {
if (this.layout !== 'auto') {
this.$nextTick(() => {
// 判断是否会出现水平滚动条
let parentWidth;
parentWidth = this.$el.offsetWidth;
let tableWidth = this.$refs.body.offsetWidth;
if (parentWidth === 0) {
// 初始表格是隐藏的需要特殊处理的,此时上面两个值默认是0
parentWidth = tableWidth = this.$refs.root.parentNode.offsetWidth;
}
// 分别获取有百分比 具体数值 和无width的column集合
// 获取具体数值和非数值的列集合
const percentColumns = [];
const valueColumns = [];
const noWidthColumns = [];
this.columns.forEach((item) => {
const width = item.copyWidth ? item.copyWidth + '' : undefined;
if (width && width.indexOf('%') !== -1)
percentColumns.push(item);
else if (width)
valueColumns.push(item);
else if (!width)
noWidthColumns.push(item);
});
let leaveWidth = 0;
// 全部都是百分数
if (percentColumns.length === this.columns.length) {
let sumWidth = 0;
this.columns.forEach((item) => {
sumWidth += parseFloat(item.copyWidth);
});
if (sumWidth !== 100) {
percentColumns.forEach((item) => {
item.currentWidth = item.copyWidth = parseFloat(item.copyWidth) / sumWidth * 100 + '%';
});
}
}
let percentWidthSum = 0;
percentColumns.forEach((item) => {
const width = parseFloat(item.copyWidth) * parentWidth / 100;
item.currentWidth = width;
percentWidthSum += width;
});
let valueWidthSum = 0;
valueColumns.forEach((item) => valueWidthSum += parseFloat(item.copyWidth));
leaveWidth = parentWidth - percentWidthSum - valueWidthSum;
if (leaveWidth > 0 && noWidthColumns.length > 0) {
const width = leaveWidth / noWidthColumns.length;
noWidthColumns.forEach((item) => item.currentWidth = width);
}
const allWidth = !this.columns.some((cell) => !cell.copyWidth); // each column set a width
if (allWidth) {
this.tableWidth = this.columns.map((cell) => {
if ((cell.copyWidth + '').indexOf('%') !== -1)
return parseFloat(cell.copyWidth) * parentWidth / 100;
else
return parseFloat(cell.currentWidth);
}).reduce((a, b) => a + b, 0);
} else if (getStyle(this.$el, 'width') === 'auto') {
let parentNode = this.$el.parentNode;
while (getStyle(parentNode, 'width') === 'auto')
parentNode = parentNode.parentNode;
this.tableWidth = parseFloat(getStyle(parentNode, 'width')) + 'px';
} else
this.tableWidth = parseFloat(getStyle(this.$el, 'width')) + 'px';
// 由于百分数可能带来小数点问题,引起浮点数精度问题 典型的0.2+0.1不等于0.3问题,需要特殊处理这里的比较
if (parseFloat(this.tableWidth) - parentWidth <= 0) {
this.tableWidth = parentWidth;
this.isXScroll = false;
} else
this.isXScroll = Math.abs(parseFloat(this.tableWidth) - parentWidth) > 0.001;
this.scrollWidth = getScrollSize();
const titleHeight = parseFloat(getStyle(this.$refs.title, 'height')) || 0;
const headHeight = parseFloat(getStyle(this.$refs.head, 'height')) || 0;
const tableHeight = this.$refs.body.offsetHeight;
if (this.height && !this.loading && this.data.length) {
// this.bodyWidth = parseFloat(this.tableWidth) - this.scrollWidth;
this.bodyHeight = this.height - titleHeight - headHeight;
this.isYScroll = tableHeight > this.bodyHeight;
} else {
this.bodyWidth = this.tableWidth;
// this.bodyHeight = parseFloat(getStyle(this.$refs.body, 'height')) || 0;
}
if (this.maxHeight && !this.loading && this.data.length) {
// this.bodyWidth = parseFloat(this.tableWidth) - this.scrollWidth;
this.fixedMaxTableHeight = this.maxBodyHeight = this.maxHeight - titleHeight - headHeight;
this.isYScroll = tableHeight > this.maxBodyHeight;
}
if (this.minHeight && !this.loading && this.data.length) {
// this.bodyWidth = parseFloat(this.tableWidth) - this.scrollWidth;
this.fixedMinTableHeight = this.minBodyHeight = this.minHeight - titleHeight - headHeight;
}
if (this.loading && tableWidth > parentWidth) {
this.fixedTableHeight = parseFloat(getStyle(this.$refs.body, 'height')) || 0;
// this.$refs.body.parentNode.scrollLeft = (tableWidth - parentWidth) / 2;
} else if (tableWidth > parentWidth) {
this.fixedTableHeight = this.bodyHeight - this.scrollWidth;
// this.$refs.body.parentNode.scrollLeft = (tableWidth - parentWidth) / 2;
} else
this.fixedTableHeight = this.bodyHeight;
this.columnsWidth = [];
this.columns.forEach((item, index) => {
// 存储item.currentWidth可能变化前的值,是由于如果出现水平滚动条,会导致item.currentWidth的值发生变化,
// 这时候,组成tbody的表格对应的col最后一个的宽度应该是本身宽度减去滚动条的宽度,不然会导致对不齐的问题出现
this.columnsWidth.push(item.currentWidth);
if (this.height && index === (this.columns.length - 1) && this.isYScroll) {
item.currentWidth = parseFloat(item.currentWidth) - this.scrollWidth;
item.fixedWidth = item.currentWidth;
}
if (this.maxHeight && index === (this.columns.length - 1) && this.isYScroll) {
item.currentWidth = parseFloat(item.currentWidth) - this.scrollWidth;
item.fixedWidth = item.currentWidth;
}
});
});
}
},
select(option, column, index) {
this.$refs.popper && this.$refs.popper[0] && this.$refs.popper[0].toggle(false);
column.selectValue = option.value;
this.defaultFilter.title = column.title;
this.defaultFilter.value = option.value;
this.defaultFilter.column = column;
// 需要考虑数据为空 进行过滤 异步加载数据的情况
this.filterTdata = this.tdata = this.copyTdata.filter((item) => {
if (column.filterMethod)
return column.filterMethod(option.value, item[column.label], item, column);
else
return item[column.label] === option.value;
});
if (this.pattern === 'limit')
this.tdata = this.tdata.slice(0, this.limit);
this.$emit('filter-change', {
column,
value: option.value,
index,
});
},
/**
* 选中或者取消事件
* @param {row} 当前选中行数据
*/
changeSelect(row) {
const selection = this.getSelection();
if (row.selected)
this.$emit('select', selection);
else
this.$emit('select-cancel', selection);
this.$emit('selection-change', selection);
},
rowClick(row, index) {
this.$emit('row-click', {
data: row,
index,
});
},
onResize() {
this.handleResize();
},
translateTime(value, format) {
if (!value)
return this.defaultText;
const self = this;
const maps = {
YYYY(date) {
return date.getFullYear();
},
MM(date) {
return self.fixDate(date.getMonth() + 1);
},
DD(date) {
return self.fixDate(date.getDate());
},
HH(date) {
return self.fixDate(date.getHours());
},
mm(date) {
return self.fixDate(date.getMinutes());
},
ss(date) {
return self.fixDate(date.getSeconds());
},
};
const date = new Date(value);
const pattern = new RegExp(Object.keys(maps).join('|'), 'g');
return format.replace(pattern, (capture) => maps[capture] ? maps[capture](date) : '');
},
fixDate(value) {
value = '' + value;
return value.length <= 1 ? '0' + value : value;
},
onMouseWheel(e) {
const direction = e.wheelDelta / 120 > 0 ? -1 : 1;
const parentWidth = this.$refs.root.offsetWidth;
const tableWidth = this.$refs.body.offsetWidth;
const diffWidth = tableWidth - parentWidth;
if (tableWidth > parentWidth && this.over) {
e.preventDefault();
if (this.$refs.body.parentNode.scrollLeft >= diffWidth && direction === 1)
this.$refs.body.parentNode.scrollLeft = diffWidth;
else if (this.$refs.body.parentNode.scrollLeft < 0 && direction === -1)
this.$refs.body.parentNode.scrollLeft = 0;
else if (direction === -1)
this.$refs.body.parentNode.scrollLeft += -50;
else
this.$refs.body.parentNode.scrollLeft += 50;
}
},
mouseover() {
this.over = true;
},
mouseleave() {
this.over = false;
},
toggleExpand(index) {
if (this.expandPattern === 'toggle') {
this.tdata.forEach((item, kindex) => {
if (kindex !== index) {
item.expanded = false;
item.iconName = 'right';
}
});
}
const copyRowData = this.tdata[index];
copyRowData.expanded = !copyRowData.expanded;
if (!copyRowData.expanded)
copyRowData.iconName = 'right';
else
copyRowData.iconName = 'down';
this.tdata.splice(index, 1, copyRowData);
// 由于点击会导致页面出现滚动条 表格fixed布局不会有变化导致表格显示有问题 需要重新计算下布局
this.handleResize();
this.$emit('toggle-expand', {
index,
direction: copyRowData.iconName,
row: copyRowData.iconName,
});
},
bodyScroll(e) {
this.$refs.head.scrollLeft = e.target.scrollLeft;
if (this.fixedLeftColumns.length > 0)
this.$refs.lefttable.scrollTop = e.target.scrollTop;
if (this.fixedRightColumns.length > 0)
this.$refs.righttable.scrollTop = e.target.scrollTop;
this.$refs.popper && this.$refs.popper[0] && this.$refs.popper[0].toggle(false);
},
fixmouseover(value) {
if (value === -1)
this.fixedHover = true;
else {
const obj = this.tdata[value];
obj.hover = true;
this.tdata.splice(value, 1, obj);
}
},
fixmouseleave(value) {
if (value === -1)
this.fixedHover = false;
else {
const obj = this.tdata[value];
obj.hover = false;
this.tdata.splice(value, 1, obj);
}
},
showAll() {
const filterValue = this.defaultFilter.value;
if (this.defaultFilter.value) {
this.tdata = this.copyTdata.filter((item) => {
const filterColumn = this.defaultFilter.column;
if (filterColumn.filterMethod)
return filterColumn.filterMethod(filterValue, item[filterColumn.label], item, filterColumn);
else
return item[filterColumn.label] === filterValue;
});
} else
this.tdata = this.copyTdata;
},
showLimit() {
this.tdata = this.tdata.slice(0, this.limit);
},
},
destroyed() {
window.removeEventListener('resize', this.onResize, false);
},
};