<template>
|
<div class="data-table" ref="refDataTable">
|
<div v-if="showForm" class="forms" :class="{ collapse: !formExpand }">
|
<Form :model="form" v-bind="formObject.options" ref="refForm">
|
<Row>
|
<Col
|
v-for="(col, index) in formList"
|
:key="`${index}`"
|
:span="col.span"
|
>
|
<FormItem :label="`${col.label}`">
|
<Input
|
v-if="col.type == 'input'"
|
v-model="form[col.field]"
|
size="small"
|
>
|
<template v-if="col.suffix" #suffix>
|
<Icon :type="col.suffix" @click="col.onSuffixClick" />
|
</template>
|
</Input>
|
<DatePicker
|
v-else-if="col.type == 'daterange'"
|
type="daterange"
|
v-model="form[col.field]"
|
separator=" 至 "
|
size="small"
|
style="width: 100%"
|
@on-change="col.onChange"
|
></DatePicker>
|
<DatePicker
|
v-else-if="col.type == 'date'"
|
type="date"
|
v-model="form[field]"
|
size="small"
|
@on-change="col.onChange"
|
/>
|
<Select
|
v-else-if="col.type == 'select'"
|
v-model="form[col.field]"
|
size="small"
|
>
|
<Option
|
v-for="(opt, i) in col.options"
|
:key="i"
|
:value="col.value"
|
>{{ col.label }}</Option
|
>
|
</Select>
|
<Checkbox
|
v-else-if="col.type == 'checkbox'"
|
v-model="form[col.field]"
|
size="small"
|
>{{ form.label }}</Checkbox
|
>
|
</FormItem>
|
</Col>
|
</Row>
|
</Form>
|
<div class="btns">
|
<Button type="primary" size="small"
|
><Icon type="md-settings" @click="onShowFormDialog"
|
/></Button>
|
<Button
|
v-if="formExpand"
|
v-show="showFormExpand"
|
type="primary"
|
size="small"
|
@click="formExpand = !formExpand"
|
><Icon type="ios-arrow-up"
|
/></Button>
|
<Button
|
v-else
|
v-show="showFormExpand"
|
type="primary"
|
size="small"
|
@click="formExpand = !formExpand"
|
><Icon type="ios-arrow-down"
|
/></Button>
|
</div>
|
</div>
|
<div class="buttons" v-if="showTopButtons">
|
<Space>
|
<Button
|
v-for="(btn, i) in buttons"
|
:key="i"
|
type="primary"
|
size="small"
|
@click="btn.onClick"
|
>{{ btn.showName }}</Button
|
>
|
</Space>
|
<Space class="ivu-fr">
|
<Poptip title="分页器位置" placement="left-start">
|
<Tooltip content="设置分页器位置" transfer>
|
<Button size="small"><Icon type="md-apps" /></Button>
|
</Tooltip>
|
<template #content>
|
<RadioGroup
|
v-model="paganationPlacement"
|
@on-change="onPaganationPlacementChange"
|
>
|
<Radio label="top">
|
<Icon type="md-at"></Icon>
|
<span>上</span>
|
</Radio>
|
<Radio label="bottom">
|
<Icon type="md-basketball"></Icon>
|
<span>下</span>
|
</Radio>
|
</RadioGroup>
|
</template>
|
</Poptip>
|
<Poptip popper-class="col-list" placement="left-start">
|
<Tooltip content="调整列" transfer>
|
<Button size="small"><Icon type="md-settings" /></Button>
|
</Tooltip>
|
<template #title>
|
<Checkbox
|
:indeterminate="columnListIndeterminate"
|
:model-value="columnCheckAll"
|
@click.prevent="onColumnCheckAll"
|
>全选</Checkbox
|
>
|
</template>
|
<template #content>
|
<CheckboxGroup
|
v-model="columnShowList"
|
@on-change="onShowColumnChange"
|
>
|
<Checkbox v-for="(col, i) in columns" :key="i" :label="col.key">{{
|
toColumnLabel(col)
|
}}</Checkbox>
|
</CheckboxGroup>
|
</template>
|
</Poptip>
|
<Poptip title="设置字体大小" placement="left-start">
|
<Tooltip content="调整字体" transfer>
|
<Button size="small"><Icon type="md-apps" /></Button>
|
</Tooltip>
|
<template #content>
|
<RadioGroup v-model="fontSize">
|
<Radio label="small">
|
<Icon type="md-at"></Icon>
|
<span>小</span>
|
</Radio>
|
<Radio label="default">
|
<Icon type="md-basketball"></Icon>
|
<span>中</span>
|
</Radio>
|
<Radio label="large">
|
<Icon type="ios-briefcase"></Icon>
|
<span>大</span>
|
</Radio>
|
</RadioGroup>
|
</template>
|
</Poptip>
|
<Tooltip content="打印" transfer>
|
<Button size="small"><Icon type="ios-print" /></Button>
|
</Tooltip>
|
</Space>
|
<Page
|
v-if="paged && paganationPlacement == 'top'"
|
class="absolute"
|
:model-value="page"
|
:total="total"
|
:page-size="limit"
|
:page-size-opts="limits"
|
show-sizer
|
show-total
|
show-elevator
|
transfer
|
simple
|
@on-change="onPageChange"
|
@on-page-size-change="onPageSizeChange"
|
@on-prev="onPrev"
|
@on-next="onNext"
|
/>
|
</div>
|
<Table
|
class="col-gap-none"
|
:row-class-name="() => ['no-wrap']"
|
:columns="tableColumns"
|
:data="data"
|
:height="tableHeight"
|
:highlight-row="highlightRow"
|
:size="fontSize"
|
@on-row-click="onRowClick"
|
@on-current-change="onCurrentChange"
|
@on-selection-change="onSelectionChange"
|
ref="refTable"
|
></Table>
|
<Page
|
v-if="paged && paganationPlacement == 'bottom'"
|
class="text-center"
|
:model-value="page"
|
:total="total"
|
:page-size="limit"
|
:page-size-opts="limits"
|
show-sizer
|
show-total
|
show-elevator
|
transfer
|
@on-change="onPageChange"
|
@on-page-size-change="onPageSizeChange"
|
@on-prev="onPrev"
|
@on-next="onNext"
|
ref="refPage"
|
/>
|
<Spin fix :show="loading" />
|
<FormDialog
|
v-model="formDialogVisible"
|
:list="formObject.items"
|
@ok="onFormDialogOK"
|
></FormDialog>
|
</div>
|
</template>
|
|
<script>
|
import { ref } from "vue";
|
import FormDialog from "./form-dialog.vue";
|
export default {
|
name: "DataTable",
|
components: {
|
FormDialog,
|
},
|
props: {
|
// 容器高度
|
height: Number | String,
|
// 常规顶部按钮
|
buttons: {
|
type: Array,
|
default: () => [],
|
},
|
// 是否分页
|
paged: {
|
type: Boolean,
|
default: () => true,
|
},
|
// 列定义
|
columns: {
|
type: Array,
|
default: () => [],
|
},
|
// 数据源
|
data: {
|
type: Array,
|
default: () => [],
|
},
|
// 数据总数
|
total: {
|
type: Number,
|
default: () => 0,
|
},
|
// 当前页
|
page: {
|
type: Number,
|
default: () => 1,
|
},
|
// 每页几条数据
|
limit: {
|
type: Number,
|
default: () => 10,
|
},
|
// limit列表
|
limits: {
|
type: Array,
|
default: () => [10, 20, 30, 40],
|
},
|
// 记录加载状态
|
loading: Boolean,
|
// 单选模式
|
highlightRow: Boolean,
|
showForm: {
|
type: Boolean,
|
default: () => true,
|
},
|
showTopButtons: {
|
type: Boolean,
|
default: () => true,
|
},
|
size: {
|
type: String,
|
default: () => "default",
|
},
|
pagePlacement: {
|
type: String,
|
default: () => "bottom", // top/bottom
|
},
|
formObject: {
|
type: Object,
|
default: () => ({}),
|
},
|
},
|
data() {
|
return {
|
// 配置哪些列显示
|
columnListIndeterminate: false,
|
columnCheckAll: true,
|
columnShowList: [],
|
// 配置字体
|
fontSize: this.size,
|
// 查询表单
|
// 查询表单的定义
|
form: {},
|
formExpand: false,
|
showFormExpand: true,
|
formDialogVisible: false,
|
formShowList: [],
|
paganationPlacement: this.pagePlacement,
|
};
|
},
|
computed: {
|
// 数据列表高度
|
tableHeight() {
|
let formHeight = this.showForm
|
? this.refDataTable?.querySelector(".forms")?.clientHeight + 9
|
: 0;
|
let buttonHeight = this.showTopButtons
|
? this.refDataTable?.querySelector(".buttons")?.clientHeight
|
: 0;
|
let pageHeight =
|
this.paged && this.paganationPlacement == "bottom" ? 70 - 18 : 0;
|
return this.height - formHeight - buttonHeight - pageHeight;
|
},
|
// 实际要显示的列
|
tableColumns() {
|
return this.columns.filter((col) =>
|
this.columnShowList.includes(col.key)
|
);
|
},
|
// 实际要显示的表单项
|
formList() {
|
return (
|
this.formObject.items?.filter((l) =>
|
this.formShowList.includes(l.field)
|
) || []
|
);
|
},
|
},
|
methods: {
|
toColumnLabel(col) {
|
if (col.key == "index") return "序号";
|
else if (col.key == "row_button") return "操作按钮";
|
else return col.title;
|
},
|
onPaganationPlacementChange() {
|
this.$nextTick(() => this.resize());
|
},
|
// 配置列-全选
|
onColumnCheckAll() {
|
if (this.columnListIndeterminate) {
|
this.columnCheckAll = false;
|
} else {
|
this.columnCheckAll = !this.columnCheckAll;
|
}
|
this.columnListIndeterminate = false;
|
|
if (this.columnCheckAll) {
|
this.columnShowList = this.columns.map((c) => c.key);
|
} else {
|
this.columnShowList = [];
|
}
|
},
|
// 配置列-单选
|
onShowColumnChange(data) {
|
if (data.length == this.columns.length) {
|
this.columnListIndeterminate = false;
|
this.columnCheckAll = true;
|
} else if (data.length > 0) {
|
this.columnListIndeterminate = true;
|
this.columnCheckAll = false;
|
} else {
|
this.columnListIndeterminate = false;
|
this.columnCheckAll = false;
|
}
|
},
|
onPageChange(page) {
|
this.$emit("on-change", page);
|
},
|
onPageSizeChange(limit) {
|
this.$emit("on-page-size-change", limit);
|
},
|
onPrev(page) {
|
this.$emit("on-prev", page);
|
},
|
onNext(page) {
|
this.$emit("on-next", page);
|
},
|
onSortChange(param) {
|
if (param.order == "normal") param.order = "asc";
|
this.columns
|
.filter((c) => c.sortType)
|
.forEach((c) => (c.sortType = undefined));
|
this.columns.find((c) => c.key == param.key).sortType = param.order;
|
this.$emit("on-sort-change", param);
|
},
|
onSelectionChange(selection) {
|
this.$emit("on-selection-change", selection);
|
},
|
onCurrentChange(currentRow) {
|
this.$emit("on-current-change", currentRow);
|
},
|
onRowClick(row, index) {
|
this.$emit("on-row-click", row, index);
|
},
|
onSelect(selection, row) {
|
this.$emit("on-select", selection, row);
|
},
|
onSelectCancel(selection, row) {
|
this.$emit("on-select-cancel", selection, row);
|
},
|
onSelectAll(selection) {
|
this.$emit("on-select-all", selection);
|
},
|
onSelectAllCancel(selection) {
|
this.$emit("on-select-all-cancel", selection);
|
},
|
onColumnWidthResize(newWidth, oldWidth, column, event) {
|
this.$emit("on-column-width-resize", newWidth, oldWidth, column, event);
|
},
|
onShowFormDialog() {
|
this.formDialogVisible = true;
|
},
|
resize() {
|
this.$nextTick(() => {
|
let height = this.refForm?.$el?.clientHeight;
|
if (height < 50) {
|
this.formExpand = false;
|
this.showFormExpand = false;
|
} else {
|
this.formExpand = true;
|
this.showFormExpand = true;
|
}
|
});
|
},
|
onFormDialogOK(showList) {
|
this.formShowList = showList;
|
},
|
},
|
setup() {
|
const refDataTable = ref(null);
|
const refForm = ref(null);
|
const refTable = ref(null);
|
const refPage = ref(null);
|
return { refDataTable, refForm, refTable, refPage };
|
},
|
watch: {
|
columns(columns) {
|
this.columnShowList = columns.map((c) => c.key);
|
},
|
["formObject.items"](list) {
|
this.formShowList = list.map((l) => l.field);
|
},
|
formShowList() {
|
this.resize();
|
},
|
},
|
};
|
</script>
|
|
<style lang="less">
|
.data-table {
|
height: 100%;
|
position: relative;
|
.forms {
|
margin-bottom: 9px;
|
padding: 0 10px;
|
position: relative;
|
&.collapse {
|
height: 30px;
|
padding-right: 100px;
|
overflow: hidden;
|
}
|
.btns {
|
position: absolute;
|
right: 10px;
|
bottom: 0;
|
display: flex;
|
flex-direction: row;
|
gap: 10px;
|
z-index: 1;
|
}
|
.ivu-form .ivu-form-item-label,
|
.ivu-checkbox-wrapper {
|
font-size: 12px;
|
}
|
.ivu-input-wrapper {
|
.ivu-icon {
|
cursor: pointer;
|
}
|
}
|
}
|
.buttons {
|
padding: 0 9px;
|
line-height: 33px;
|
position: relative;
|
}
|
.text-center {
|
text-align: center;
|
}
|
}
|
.col-list {
|
.ivu-checkbox-group {
|
display: flex;
|
flex-direction: column;
|
}
|
}
|
.ivu-page {
|
padding: 9px;
|
}
|
.absolute {
|
position: absolute;
|
top: 0;
|
right: 180px;
|
padding: 0px;
|
}
|
</style>
|