Files
gjt_mini/components/no-bad-table/table.vue
2025-12-30 09:44:46 +08:00

842 lines
26 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view
class="no-bad-table-wrap"
:class="[
tableHeight != 'auto' ? 'fix-height' : '',
celCenter ? 'td-center' : '',
]"
>
<view class="table_box_big" :style="{ height: tableHeight }">
<view class="table_box">
<!-- 头部内容 -->
<view class="div-table div-table-head">
<view class="thead">
<view class="tr">
<view
class="td selection"
v-if="selection == 'mulit'"
:style="{ width: selectionTdWidth, height: thTdHeight + 'px' }"
>
<view
:class="['td_wrap']"
:style="{
width: selectionTdWidth,
height: thTdHeight + 'px',
}"
>
<checkbox-group @change="checkboxChangeAll">
<checkbox
value="all"
color="#999"
:checked="switchAllCheckBox"
style="transform: scale(0.7)"
/>
</checkbox-group>
</view>
</view>
<view
class="td"
:style="{
width: countHeadColspanWidth(item, index),
height: thTdHeight + 'px',
}"
v-for="(item, index) in columns"
:key="item.key"
>
<view
class="td_wrap"
:style="{
width: countHeadColspanWidth(item, index, true),
height: thTdHeight + 'px',
}"
>{{ item.title }}</view
>
</view>
</view>
</view>
</view>
<!-- 头部内容 -->
<!-- 有数据情况 -->
<template v-if="list.length">
<view class="table_tbody_box" :style="{ height: talbeBodyHeight }">
<view class="div-table div-table-body">
<checkbox-group @change="checkboxChange">
<template v-for="(item, index) in list">
<view
:class="[
'tr',
rowClassNamePlus(item, index),
selection == 'single' && checkBoxList[index].$checked
? 'selected'
: '',
selection == 'single' && checkBoxList[index].$disabled
? 'disabled'
: '',
]"
@click="selectRow(item, index)"
:key="item.id"
>
<!-- 多选操作 -->
<view
class="td selection"
v-if="selection == 'mulit'"
:style="{
width: selectionTdWidth,
height: tdHeight + 'px',
}"
>
<view
:class="['td_wrap']"
:style="{
width: selectionTdWidth,
height: tdHeight + 'px',
}"
>
<checkbox
:value="checkBoxList[index].id"
color="#999"
:disabled="checkBoxList[index].$disabled"
:checked="checkBoxList[index].$checked"
style="transform: scale(0.7)"
/>
</view>
</view>
<template v-for="(tdItem, tdItemIndex) in columns">
<view
class="td"
:class="[
item.cellClassName && item.cellClassName[tdItem.key]
? item.cellClassName[tdItem.key]
: '',
spanMethod(item, tdItem, index, tdItemIndex)[
'rowspan'
] == 0
? 'empty-cells-for-rowspan'
: '',
spanMethod(item, tdItem, index, tdItemIndex)[
'colspan'
] == 0
? 'empty-cells-for-celspan'
: '',
spanMethod(item, tdItem, index, tdItemIndex)[
'rowspan'
] > 1
? 'rowspan'
: '',
spanMethod(item, tdItem, index, tdItemIndex)[
'colspan'
] > 1
? 'colspan'
: '',
]"
:style="{
height: countRowspanHeight(
item,
tdItem,
index,
tdItemIndex
),
width: countColspanWidth(
item,
tdItem,
index,
tdItemIndex
),
}"
:key="tdItem.key"
>
<view
:class="['td_wrap']"
:style="{
height: countRowspanHeight(
item,
tdItem,
index,
tdItemIndex
),
width: countColspanWidth(
item,
tdItem,
index,
tdItemIndex,
true
),
}"
>
<slot
:row="item"
:index="index"
:col="tdItem"
:isRead="isRead"
v-if="slotCols.indexOf(tdItem.key) > -1"
></slot>
<template v-else-if="tdItem.$operateList">
<template v-for="btn in tdItem.$operateList">
<button
:class="[btn.styles ? btn.styles : '']"
v-bind:style="{
padding: '2px 5px',
fontSize: '12px',
lineHeight: '1.2',
display: 'inline-block',
}"
@click="
pullEvent(btn.event, {
row: item,
index: index,
})
"
type="primary"
size="min"
:key="btn.id"
>
{{ btn.label }}
</button>
</template>
</template>
<template v-else>{{ item[tdItem.key] }} </template>
</view>
</view>
</template>
</view>
</template>
</checkbox-group>
</view>
</view>
</template>
<template v-else>
<view
class="table_tbody_box empty-data-body-box div-table-body"
:style="{ height: emptyColHeight, width: emptyColWidth }"
>
<view
class="tr"
:style="{ height: emptyColHeight, width: emptyColWidth }"
>
<view
class="td"
:style="{ height: emptyColHeight, width: emptyColWidth }"
>
<view
:class="['td_wrap']"
:style="{
height: parseInt(emptyColHeight - 2) + 'px',
width: emptyColWidth,
lineHeight: parseInt(emptyColHeight - 2) + 'px',
}"
>
<text @click="emptyClickCallBack">{{ emptyText }}</text>
</view>
</view>
</view>
</view>
</template>
<template v-if="list.length">
<!-- 固定左边一列 -->
<view
class="fixed-left"
v-if="
columnsFixedLeft[0] || (selection == 'mulit' && fixedCheckbox)
"
>
<view class="tr fixed-thead-tr">
<!-- 多选且固定左边 【-->
<view
class="td selection"
v-if="selection == 'mulit' && fixedCheckbox"
:style="{ width: selectionTdWidth, height: thTdHeight + 'px' }"
>
<view
:class="['td_wrap']"
:style="{
width: selectionTdWidth,
height: thTdHeight + 'px',
}"
>
<checkbox-group @change="checkboxChangeAll">
<checkbox
value="all"
color="#999"
:checked="switchAllCheckBox"
style="transform: scale(0.7)"
/>
</checkbox-group>
</view>
</view>
<!-- 多选且固定左边 】-->
<!-- 普通固定左边 【-->
<view
class="td"
v-if="columnsFixedLeft[0]"
:style="{
height: fixedHeight(columnsFixedLeft[0]),
width: fixedWidth(columnsFixedLeft),
}"
>
<view
class="td_wrap"
:style="{
height: fixedHeight(columnsFixedLeft[0]),
width: fixedWidth(columnsFixedLeft),
}"
>
{{ columnsFixedLeft[0].title }}
</view>
</view>
<!-- 普通固定左边 】-->
</view>
<view
v-for="(item, index) in list"
:key="item.id"
:class="[
'tr',
selection == 'single' && checkBoxList[index].$checked
? 'selected'
: '',
selection == 'single' && checkBoxList[index].$disabled
? 'disabled'
: '',
]"
@click="selectRow(item, index)"
>
<!-- 多选且固定左边 】-->
<view
class="td selection fixed-td"
v-if="selection == 'mulit' && fixedCheckbox"
:style="{ width: selectionTdWidth, height: tdHeight + 'px' }"
>
<view
:class="['td_wrap']"
:style="{ width: selectionTdWidth, height: tdHeight + 'px' }"
>
<checkbox
:value="checkBoxList[index].id"
color="#999"
:disabled="checkBoxList[index].$disabled"
:checked="checkBoxList[index].$checked"
style="transform: scale(0.7)"
/>
</view>
</view>
<!-- 普通固定左边 【-->
<view
class="td fixed-td"
v-if="columnsFixedLeft[0]"
:style="{
height: fixedHeight(columnsFixedLeft[0]),
width: fixedWidth(columnsFixedLeft),
}"
>
<view
class="td_wrap fixed-wrap"
:style="{
height: fixedHeight(columnsFixedLeft[0]),
width: fixedWidth(columnsFixedLeft[0]),
}"
>
<!-- td内容 【-->
<slot
:row="item"
v-if="
slotCols.indexOf(
columnsFixedLeft[0] && columnsFixedLeft[0].key
) > -1
"
></slot>
<template v-if="columnsFixedLeft[0].$operateList">
<template v-for="btn in columnsFixedLeft[0].$operateList">
<button
:class="[btn.styles ? btn.styles : '']"
v-bind:style="{
padding: '2px 5px',
fontSize: '12px',
lineHeight: '1.2',
display: 'inline-block',
}"
@click="
pullEvent(btn.event, { row: item, index: index })
"
type="primary"
size="min"
:key="btn.id"
>
{{ btn.label }}
</button>
</template>
</template>
<template v-else
>{{ item[columnsFixedLeft[0].key] }}
</template>
<!-- td内容 】-->
</view>
</view>
<!-- 普通固定左边 】-->
</view>
</view>
<!-- 固定左边一列 】-->
<!-- 固定右边一列 【-->
<view class="fixed-right" v-if="columnsFixedRight[0]">
<view class="tr fixed-thead-tr">
<view
class="td"
:style="{
height: fixedHeight(columnsFixedLeft[0]),
width: fixedWidth(columnsFixedLeft),
}"
>
<view
class="td_wrap"
:style="{
height: fixedHeight(columnsFixedLeft[0]),
width: fixedWidth(columnsFixedLeft),
}"
>
{{ columnsFixedRight[0].title }}
</view>
</view>
</view>
<view
v-for="(item, index) in list"
:key="item.id"
:class="[
'tr',
selection == 'single' && checkBoxList[index].$checked
? 'selected'
: '',
selection == 'single' && checkBoxList[index].$disabled
? 'disabled'
: '',
]"
@click="selectRow(item, index)"
>
<view
class="td fixed-td"
:style="{
height: fixedHeight(columnsFixedRight[0]),
width: fixedWidth(columnsFixedRight[0]),
}"
>
<view
class="td_wrap fixed-wrap"
:style="{
height: fixedHeight(columnsFixedRight[0]),
width: fixedWidth(columnsFixedRight[0]),
}"
>
<!-- td内容 【-->
<slot
:row="item"
v-if="
slotCols.indexOf(
columnsFixedRight[0] && columnsFixedRight[0].key
) > -1
"
></slot>
<template
v-if="
columnsFixedRight[0] && columnsFixedRight[0].$operateList
"
>
<template v-for="btn in columnsFixedRight[0].$operateList">
<button
:class="[btn.styles ? btn.styles : '']"
v-bind:style="{
padding: '2px 5px',
fontSize: '12px',
lineHeight: '1.2',
display: 'inline-block',
}"
@click="
pullEvent(btn.event, { row: item, index: index })
"
type="primary"
size="min"
:key="btn.id"
>
{{ btn.label }}
</button>
</template>
</template>
<template v-else
>{{ item[columnsFixedRight[0].key] }}
</template>
<!-- td内容 】-->
</view>
</view>
</view>
</view>
<!-- 固定右边一列 】-->
</template>
</view>
</view>
<loading-component v-if="loading" />
</view>
</template>
<script>
import loadingComponent from "./loading.vue";
export default {
components: { loadingComponent },
props: {
//显示列
columns: {
type: Array,
required: true,
},
//数据
list: {
type: Array,
required: true,
},
//单元格样式居中
"cel-center": {
type: Boolean,
default: true,
},
//自定义行和列样式
rowClassName: {
type: [String, Function],
default: "",
},
//自定义列元素
"slot-cols": {
type: Array,
default: () => {
return [];
},
},
//行列合并函数
"span-method": {
type: Function,
default: () => {
return () => {
return {
rowspan: 1,
colspan: 1,
};
};
},
},
spanArr: {
type: Array,
default: [],
},
isRead: {
type: Number,
default: 0,
},
//是否可选 mulit=>多选 single=》单选
selection: {
type: String,
default: "none",
},
"fixed-checkbox": {
type: Boolean,
default: false,
},
loading: {
type: Boolean,
default: false,
},
height: {
type: Number,
default: undefined,
},
"td-width": {
type: Number,
default: 110,
},
"td-height": {
type: Number,
default: 50,
},
"th-td-height": {
type: Number,
default: 50,
},
"td-padding": {
type: Number,
default: 10,
},
"border-color": {
type: String,
default: "#666",
},
emptyText: {
type: String,
default: "数据为空",
},
//空提示点击事件
emptyClickFn: {
type: Function,
default() {
return () => {};
},
},
},
computed: {
columnsFixedRight() {
let t = this.columns.filter((item) => item.$fixed == "right");
return t.length ? [t[0]] : [];
},
columnsFixedLeft() {
let t = this.columns.filter((item) => item.$fixed == "left");
return t.length ? [t[0]] : [];
},
//表格高度
tableHeight() {
return Number(this.height) && Number(this.height) > this.tdHeight * 3
? this.height + "px"
: "auto";
},
//表格主体高度
talbeBodyHeight() {
let t =
this.tableHeight !== "auto"
? parseInt(this.tableHeight) - this.tdHeight - 3 + "px"
: "auto";
return t;
},
//可选的列表长度
allCheckBoxAbledLen() {
return this.checkBoxList.filter((item) => !item.$disabled).length;
},
//没数据时候主体高度
emptyColHeight() {
let t = this.height ? this.height - this.thTdHeight - 30 + "px" : "100px";
// console.log("emptyColHeight",this.height, this.thTdHeight,t)
return t;
},
//没数据时候,主体的宽度
emptyColWidth() {
let t = this.tdWidth * this.columns.length;
if (this.selection == "mulit") {
t = parseInt(this.selectionTdWidth) + t;
}
return t + "px";
},
},
data() {
return {
checkBoxList: [], //多选=》选中列表
switchAllCheckBox: false, //多选=》全选
selectionTdWidth: "750rpx", //多选列宽
singleSelect: {}, //单选,选中行
};
},
watch: {
list() {
this.asyncCheckBoxList();
},
},
created() {
this.asyncCheckBoxList();
},
methods: {
//获取数据副本,轻拷贝
asyncCheckBoxList() {
this.checkBoxList = this.list.map((item) => {
return { ...item };
});
},
//自定义行样式
rowClassNamePlus(row, index) {
if (typeof this.rowClassName === "string") {
return this.rowClassName;
} else if (typeof this.rowClassName === "function") {
return this.rowClassName(row, index);
}
},
//事件触发
pullEvent(event, data) {
this.$emit(event, data);
},
/**
* 计算单列宽
* iswrap 是否是内容容器
*/
countColspanWidth(item, tdItem, index, tdItemIndex, iswrap = false) {
let borderLeft = iswrap && tdItemIndex > 0 ? 1 : 0;
//是否跨列,返回跨列个数1为不跨列
let moreThanOne =
this.spanMethod(item, tdItem, index, tdItemIndex) &&
this.spanMethod(item, tdItem, index, tdItemIndex)["colspan"];
//console.log(`第${index}行,第${tdItemIndex}列 是跨列么?${moreThanOne}`);
let t =
moreThanOne > 1
? moreThanOne * this.tdWidth - borderLeft + "px"
: this.tdWidth - borderLeft + "px";
//跨列
if (moreThanOne > 1) {
let countWidth = 0;
for (let i = tdItemIndex; i < tdItemIndex + (moreThanOne - 1); i++) {
countWidth +=
this.columns[i].$width && parseInt(this.columns[i].$width)
? parseInt(moreThanOne * this.columns[i].$width) - borderLeft
: this.tdWidth * moreThanOne;
}
return countWidth + "px";
} else {
//不跨列
let tmp =
this.columns[tdItemIndex].width &&
parseInt(this.columns[tdItemIndex].width)
? parseInt(this.columns[tdItemIndex].width)
: this.tdWidth;
return tmp + "rpx";
}
return t;
},
/**
* 固定列宽
*/
fixedWidth(fixedWidth) {
return fixedWidth && fixedWidth.$width
? fixedWidth + "px"
: this.tdWidth + "px";
},
/*
* 固定列-》列高
*/
fixedHeight(fixedHeight) {
return fixedHeight && fixedHeight.$height
? fixedHeight + "px"
: this.tdHeight + "px";
},
/**
* 计算头部td的宽度
* */
countHeadColspanWidth(item, index, iswrap = false) {
let borderLeft = iswrap && index > 0 ? 1 : 0;
let tmp = item.width ? parseInt(item.width) : this.tdWidth;
return tmp + "rpx";
},
/**
* 计算单列高
* */
countRowspanHeight(item, tdItem, index, tdItemIndex) {
//是否跨行
let moreThanOne =
this.spanMethod(item, tdItem, index, tdItemIndex) &&
this.spanMethod(item, tdItem, index, tdItemIndex)["rowspan"] > 1;
let t = moreThanOne
? this.spanMethod(item, tdItem, index, tdItemIndex)["rowspan"] *
this.tdHeight +
"px"
: this.tdHeight + "px";
return t;
},
/*
* 单选行
* */
selectRow(item, index) {
if (item.$disabled) {
return;
}
//非单选方式
if (this.selection != "single") {
return;
}
this.checkBoxList = this.checkBoxList.map((sitem, sindex) => {
if (index === sindex) {
sitem.$checked = true;
} else {
sitem.$checked = false;
}
return sitem;
});
if (this.selection) {
this.$emit("on-selection-change", {
old: this.singleSelect,
new: {
index,
item,
},
});
}
this.singleSelect = {
index,
item,
};
},
/*
* 多选
* */
checkboxChange(e) {
let val = e.detail.value;
let before = [];
for (let v = 0; v < this.allCheckBoxAbledLen; v++) {
if (this.checkBoxList[v].$checked === true) {
before.push({ ...this.checkBoxList[v] });
}
}
if (val.length == this.allCheckBoxAbledLen) {
this.switchAllCheckBox = true;
this.checkBoxList = this.checkBoxList.map((item) => {
if (!item.$disabled) {
item.$checked = true;
}
return item;
});
} else {
this.switchAllCheckBox = false;
this.checkBoxList = this.checkBoxList.map((item) => {
if (val.indexOf(item.id) > -1) {
item.$checked = true;
} else {
item.$checked = false;
}
return item;
});
}
this.$emit("on-selection-change", {
old: before,
new: this.checkBoxList.filter((item) => item.$checked === true),
});
},
/*
* 全选
* */
checkboxChangeAll(e) {
let val = e.detail.value;
let before = [];
for (let v = 0; v < this.allCheckBoxAbledLen; v++) {
if (this.checkBoxList[v].$checked === true) {
before.push({ ...this.checkBoxList[v] });
}
}
if (val && val[0] == "all") {
this.switchAllCheckBox = true;
this.checkBoxList = this.checkBoxList.map((item) => {
if (!item.$disabled) {
item.$checked = true;
}
return item;
});
} else {
this.switchAllCheckBox = false;
this.checkBoxList = this.checkBoxList.map((item) => {
item.$checked = false;
return item;
});
}
this.$emit("on-selection-change", {
old: before,
new: this.checkBoxList.filter((item) => item.$checked === true),
});
},
/*
* 空提示点击事件
* */
emptyClickCallBack() {
typeof this.emptyClickFn == "function" ? this.emptyClickFn() : "";
},
},
};
</script>
<style lang="scss">
@import "components/no-bad-table/table.scss";
</style>