<template>
<div class="w-data-table" v-loading="tableLoading">
<div class="dt-search" v-if="$slots.search">
<el-form
ref="searchform"
size="mini"
class="dt-search-form-inline"
:inline="true"
:model="searchParams"
@submit.native.prevent="search({ _fromBar: true, _begin: 0 })"
>
<slot name="search" />
<button type="submit" style="display: none;"></button>
</el-form>
</div>
<div
class="dt-toolbar"
v-if="$slots.toolbar || $slots.search || $slots['toolbar-right']"
>
<slot name="toolbar" />
<el-button
v-if="exportable"
size="mini"
type="primary"
icon="el-icon-download"
plain
@click="doExport()"
>
导出
</el-button>
<template v-if="$slots.search">
<el-divider
v-if="($slots.toolbar || exportable) && (showSearch || showReset)"
direction="vertical"
></el-divider>
<el-button
type="primary"
size="mini"
icon="el-icon-search"
plain
v-if="showSearch"
class="search"
@click="search({ _fromBar: true, _begin: 0 })"
>
查询
</el-button>
<el-button
type="default"
size="mini"
icon="el-icon-refresh-left"
plain
v-if="showReset"
class="reset"
@click="clearSearch()"
>
重置
</el-button>
</template>
<template v-if="$slots['toolbar-right']">
<div class="dt-toolbar_right">
<slot name="toolbar-right" />
</div>
</template>
</div>
<el-table
ref="table"
size="medium"
style="width: 100%"
class="dt-table"
header-cell-class-name="header-compact-cell"
cell-class-name="compact-cell"
:highlight-current-row="highlightCurrentRow"
:border="showBorder"
:data="data"
:height="returnHeight || tableHeight"
:row-key="rowKey"
:default-expand-all="defaultExpandAll"
:tree-props="treeProps"
@row-click="clickRow"
@select="onTabelSelect"
@select-all="onTabelSelectAll"
:load="load"
lazy
>
<el-table-column v-if="showSelect" type="selection" width="55">
</el-table-column>
<el-table-column
v-if="showIndex"
type="index"
width="50"
></el-table-column>
<template
v-for="({ hide, render, tag, dict, comp, cols, ...attrs },
index) in column"
>
<template v-if="!hide">
<el-table-column
v-if="attrs.type"
v-bind="{ ...columnAttrs, ...attrs }"
:key="index"
:selectable="checkboxT"
></el-table-column>
<el-table-column
v-else-if="render"
v-bind="{ ...columnAttrs, ...attrs }"
:key="index"
>
<template slot-scope="scope">
<w-render :render="render" :params="scope" :data="data" />
</template>
</el-table-column>
<el-table-column
v-else-if="cols"
v-bind="{ ...columnAttrs, ...attrs }"
:key="index"
>
<template v-for="(cattrs, index) in cols">
<el-table-column
v-bind="{ ...columnAttrs, ...cattrs }"
:key="index"
></el-table-column>
</template>
</el-table-column>
<el-table-column
v-else
v-bind="{ ...columnAttrs, ...attrs }"
:key="index"
>
<template slot-scope="scope">
<template v-if="tag">
<template v-if="tag.dict">
<el-tag size="mini" :type="tag.type(scope)">
<!-- {{scope.row[attrs.prop]}} -->
{{
scope.row[attrs.prop]
| fmtDict(dicts[tag.dict.code || attrs.prop])
}}
</el-tag>
</template>
<template v-else>
<el-tag size="mini" :type="tag.type(scope)">
{{ tag.text(scope) }}
</el-tag>
</template>
</template>
<template v-else-if="comp">
<component
:is="comp.name"
v-bind="{ ...comp.attr }"
v-on="{ ...comp.on(scope) }"
>
<template v-if="comp.text">{{ comp.text }}</template>
</component>
</template>
<template v-else-if="dict">
{{
scope.row[dict.prop || attrs.prop]
| fmtDict(dicts[dict.code || dict.prop])
}}
</template>
<template v-else>
{{ scope.row[attrs.prop] }}
</template>
</template>
</el-table-column>
</template>
</template>
<el-table-column :min-width="1"></el-table-column>
<el-table-column v-if="$scopedSlots.action" :width="1">
<template v-slot:default="scope">
<div class="float-action-wrp" :style="actionStyle">
<div class="float-action">
<slot name="action" v-bind="scope" />
</div>
</div>
</template>
</el-table-column>
</el-table>
<el-pagination
v-if="pageable"
small
background
layout="sizes, total, prev, pager, next "
class="dt-pager"
:current-page="page.current"
:total="page.total"
:page-sizes="[10, 50, 100]"
:page-size="page.size"
@current-change="onCurrentChange"
@size-change="onSizeChange"
>
</el-pagination>
</div>
</template>
<script>
import _ from "lodash";
import {
Table,
TableColumn,
Pagination,
Link,
Button,
Tag,
Form,
FormItem,
Row,
Col,
Divider,
} from "element-ui";
import { PAGE_BEGIN, PAGE_SIZE } from "@/constants/common";
export default {
name: "w-data-table",
components: {
[Table.name]: Table,
[TableColumn.name]: TableColumn,
[Pagination.name]: Pagination,
[Link.name]: Link,
[Button.name]: Button,
[Tag.name]: Tag,
[Form.name]: Form,
[FormItem.name]: FormItem,
[Row.name]: Row,
[Col.name]: Col,
[Divider.name]: Divider,
},
props: {
loading: Boolean,
serverId: String,
resultKey: {
type: String,
default: "ROWS",
},
afterSearch: Function,
returnSearch: {
type: Boolean,
default: false,
},
searchParams: {
type: Object,
default: () => ({}),
},
dicts: Object,
column: Array,
pageable: {
type: Boolean,
default: true,
},
treeProps: {
type: Object,
default: () => ({}),
},
exportable: Boolean,
highlightCurrentRow: {
type: Boolean,
default: false,
},
resetSearchType: {
type: String,
default: "search",
validator: (value) => {
return _.values(["search", "cleandata"]).indexOf(value) > -1;
},
},
resetSearchAfter: Function,
changeSearchRefs: {
type: Array,
default: () => [],
},
showBorder: {
type: Boolean,
default: false,
},
defaultExpandAll: {
type: Boolean,
default: false,
},
rowKey: String,
showSelect: {
type: Boolean,
default: false,
},
returnHeight: String,
resetAble: {
type: Boolean,
default: true,
},
searchAble: {
type: Boolean,
default: true,
},
load: Function,
clearForm: Function,
page: {
type: Object,
default: () => ({
total: 0,
current: PAGE_BEGIN + 1,
size: PAGE_SIZE,
}),
},
showIndex: {
type: Boolean,
default: false,
},
},
data() {
return {
tableLoading: this.loading,
tableHeight: null,
data: [],
columnAttrs: {
align: "left",
"show-overflow-tooltip": true,
},
exportFields: "",
actionStyle: {
right: 0,
},
selecte: {},
showReset: this.resetAble,
showSearch: this.searchAble,
};
},
watch: {
loading() {
this.tableLoading = !!this.loading;
},
searchAble() {
this.showSearch = !!this.searchAble;
},
resetAble() {
this.showReset = !!this.resetAble;
},
},
mounted() {
this.$bus.$on("page.init", ({ height }) => {
this.calcHeight(height);
});
this.$bus.$on("page.resize", ({ height }) => {
this.calcHeight(height);
});
this.$nextTick(() => {
const $wrp = this.$el.querySelector(".el-table__body-wrapper");
const { scrollWidth } = $wrp;
const rightWidth = scrollWidth - $wrp.parentElement.scrollWidth;
this.actionStyle.right = `${rightWidth}px`;
$wrp.addEventListener(
"scroll",
_.debounce((e) => {
const r = rightWidth - e.target.scrollLeft + 20;
this.actionStyle.right = `${r ? r : 0}px`;
}, 300)
);
});
},
methods: {
search(params = {}, fn = (rows) => rows) {
const { _fromBar, _begin, noErrorMsg, ...otherParams } = params;
if (_begin >= 0) {
this.page.current = _begin + 1;
}
const { current, size } = this.page;
this.$emit("BeforesearchParams");
const defaultParams = {
BEGIN: (current - 1) * size,
COUNT: size,
...this.searchParams,
...otherParams,
};
this.tableLoading = true;
return axios
.post(this.serverId, defaultParams)
.then((res) => {
this.$emit("searchBefore", res[this.resultKey]);
if (res[this.resultKey]) {
this.data = fn(res[this.resultKey]);
this.$emit("searchAfter", res);
} else {
this.data = [];
}
if (this.pageable) {
this.page.total = res.PAGE ? res.PAGE.TOTAL : res.page.TOTAL;
}
if (this.exportable) {
if (res.EXCEL_TITLE) {
this.exportFields = res.EXCEL_TITLE;
} else {
try {
const headJson = JSON.parse(res.HEAD_JSON);
if (headJson.EXCEL_TITLE) {
this.exportFields = headJson.EXCEL_TITLE;
}
} catch (err) {}
}
}
if (this.returnSearch) {
this.afterSearch(res);
}
return res[this.resultKey];
})
.catch((err) => {
if (!noErrorMsg) {
this.$message.error(err.MESSAGE || "查询出错");
}
this.data.length = 0;
if (!_fromBar) {
return Promise.reject({});
}
})
.finally(() => {
setTimeout(() => {
this.tableLoading = false;
}, 500);
});
},
getData() {
return this.data;
},
getSelectedRows() {
return this.$refs.table.selection;
},
clearSelection() {
this.$refs.table.clearSelection();
},
getClickRows() {
return this.selecte;
},
clickRow(row) {
this.selecte = row;
this.$emit("getClickRow", row);
},
onTabelSelect(rows, row) {
this.$emit("tabelSelect", rows, row);
},
onTabelSelectAll(rows) {
this.$emit("tabelSelectAll", rows);
},
checkboxT(row) {
if (row.DISABLED) {
return 0;
} else {
return 1;
}
},
clearSearch() {
this.$emit("reset");
if (this.searchParams.INSTID) {
this.searchParams.INSTID = "";
}
this.$refs.searchform.resetFields();
if (_.isFunction(this.resetSearchBefore)) {
this.resetSearchBefore();
}
if (_.isFunction(this.clearForm)) {
this.clearForm();
}
if (this.resetSearchType === "search") {
this.search({ _fromBar: true, _begin: 0 });
} else if (this.resetSearchType === "cleandata") {
this.data.length = 0;
}
this.$emit("resetAfter");
},
doExport() {
if (!(this.data && this.data.length > 0)) {
return this.$message.error("导出列表不能为空");
}
this.$emit("exportBefore");
download.excel(this.serverId, this.searchParams, this.exportFields);
},
addRow(index) {
if (index != undefined) {
if (JSON.stringify(this.data[index]) == "{}") {
return;
}
this.data.splice(index + 1, 0, {});
} else {
this.data.splice(index + 1, 0, {});
}
},
removeRow(index) {
if (this.data.length == 1) {
this.data.splice(index, 1);
this.addRow();
} else {
this.data.splice(index, 1);
}
},
onCurrentChange(current) {
this.$emit("current-change", current);
this.page.current = current;
this.search().then((list) => {
this.$emit("current-change-after", list);
});
},
onSizeChange(size) {
this.$emit("size-change", size);
this.page.size = size;
this.search();
},
calcHeight(height) {
const $search = this.$el.querySelector(".dt-search");
const $toolbar = this.$el.querySelector(".dt-toolbar");
const $pager = this.$el.querySelector(".dt-pager");
const searchH = $search ? $search.offsetHeight : 0;
const toolbarH = $toolbar ? $toolbar.offsetHeight : 0;
const pagerH = $pager ? $pager.offsetHeight : 0;
this.tableHeight = `${height - searchH - toolbarH - pagerH - 10}px`;
},
},
};
</script>
<style lang="less">
.w-data-table {
.dt-search {
box-sizing: border-box;
padding: 8px 0 5px;
border-bottom: 1px solid #ebeef5;
.dt-search-form-inline {
.el-form-item {
margin-bottom: 5px;
&:last-child {
margin-right: 0;
}
.el-input.el-input--mini {
width: 150px;
}
}
}
}
.dt-toolbar {
box-sizing: border-box;
padding: 8px 0;
border-bottom: 1px solid #ebeef5;
&::after {
clear: both;
}
.el-divider {
margin: 0 15px;
}
.w-upload {
& + .el-button {
margin-left: 10px;
}
}
.dt-toolbar_right {
float: right;
line-height: 28px;
font-size: 13px;
}
.el-button {
min-width: 100px;
}
}
.el-table {
.header-compact-cell {
padding: 6px 0;
color: #333;
font-size: 14px;
}
.compact-cell {
padding: 6px 0;
color: #333;
font-size: 13px;
a {
color: @primary;
text-decoration: none;
&:hover {
border-bottom: 1px solid @primary;
}
}
.el-link--inner {
font-size: 13px;
}
}
.el-table__row {
.float-action-wrp {
display: none;
position: absolute;
right: 0;
top: 0;
bottom: 0;
z-index: 99;
transition: right, 0.2s;
.float-action {
display: block;
padding: 0 6px 5px 6px;
border-left: 1px solid #ebeef5;
border-bottom: 1px solid #ebeef5;
border-radius: 0 0 0 2px;
white-space: nowrap;
position: absolute;
right: 0;
bottom: -34px;
background-color: #fff;
.el-button + .el-button {
margin-left: 5px;
}
}
}
&:hover td {
.float-action-wrp {
display: block;
.float-action {
background-color: #f5f7fa;
border-left: 1px solid #ebeef5;
border-bottom: 1px solid #ebeef5;
}
}
}
&:last-child:not(:first-child) td {
.float-action-wrp {
.float-action {
padding: 5px 6px 0 6px;
top: -34px;
bottom: auto;
border-top: 1px solid #ebeef5;
border-bottom: 0;
border-radius: 2px 0 0 0;
}
}
}
}
}
.dt-pager {
box-sizing: border-box;
padding: 5px 0 0 0;
text-align: right;
}
}
</style>
我们介绍一下用法
<template>
<w-page>
<w-data-table
ref="datatable"
:column="tableColumn"
:loading="loading"
:server-id="serverId"
:dicts="dicts"
:search-params="searchParams"
:result-key="'ROWS'"
:exportable="true"
>
<template slot="search">
<el-form-item label="中介人代码" prop="COMPANYCODE" label-width="130px">
<el-input
:disabled="COMPANYTYPE == '02' && $route.query.type == 'company'"
v-model="searchParams.COMPANYCODE"
placeholder="请填写中介人代码"
></el-input>
</el-form-item>
<el-form-item label="中介人名称" prop="COMPANYNAME" label-width="130px">
<el-tooltip
effect="dark"
:content="searchParams.COMPANYNAME"
:disabled="!searchParams.COMPANYNAME"
>
<el-input
:disabled="COMPANYTYPE == '02' && $route.query.type == 'company'"
v-model="searchParams.COMPANYNAME"
placeholder="请填写中介人名称"
></el-input>
</el-tooltip>
</el-form-item>
<el-form-item label="团队代码" prop="SALETEAMCODE" label-width="130px">
<el-input
v-model="searchParams.SALETEAMCODE"
placeholder="请填写团队代码"
></el-input>
</el-form-item>
<el-form-item label="团队名称" prop="SALETEAMNAME" label-width="130px">
<el-input
v-model="searchParams.SALETEAMNAME"
placeholder="请填写团队名称"
></el-input>
</el-form-item>
<el-form-item
label="团队生效日期起期"
label-width="130px"
prop="EFFEDATEBEGIN"
>
<el-date-picker
v-model="searchParams.EFFEDATEBEGIN"
value-format="yyyy-MM-dd"
placeholder="请选择团队生效日期起期"
type="date"
@change="changeTime"
>
</el-date-picker>
</el-form-item>
<el-form-item
label="团队生效日期止期"
label-width="130px"
prop="EFFEDATEEND"
>
<el-date-picker
v-model="searchParams.EFFEDATEEND"
value-format="yyyy-MM-dd"
placeholder="请选择团队生效日期止期"
type="date"
@change="changeTime"
>
</el-date-picker>
</el-form-item>
<el-form-item label="状态" prop="SALETEAMSTATUS" label-width="130px">
<el-select v-model="searchParams.SALETEAMSTATUS" placeholder="请选择">
<el-option
v-for="(item, index) in dicts.TEAMSTATUS"
:key="index"
:label="item.NODEDESC"
:value="item.NODECODE"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item prop="INST_ID"></el-form-item>
</template>
<template class="centerBtn" slot="toolbar">
<el-button
size="mini"
type="primary"
>新增
</el-button>
</template>
<template v-slot:action="{ row }">
<el-button
plain
size="mini"
type="primary"
>
团队成员
</el-button>
</template>
</w-data-table>
</w-page>
</template>
data(){
return{
searchParams: {
INST_CODE: "",
INST_NAME: "",
SALETEAMCODE: "",
SALETEAMNAME: "",
START_TIME: "",
END_TIME: "",
EFFEDATEBEGIN: "",
EFFEDATEEND: "",
SUPERSALETEAMCODE: "",
COMPANYCODE: "",
COMPANYNAME: "",
SALETEAMSTATUS: "",
CHANNELTYPE: "",
},
tableColumn: [
{
type: "index",
label: "序号",
align: "left",
width: "50",
},
{ prop: "COMPANYNAME", label: "中介人名称", width: "240" },
{
prop: "INST_CODE",
label: "归属机构代码",
width: "120",
},
{
prop: "INST_NAME",
label: "归属机构名称",
width: "380",
align: "center",
},
{
prop: "SALETEAMCODE",
label: "团队代码",
width: "160px",
},
{
prop: "SALETEAMNAME",
label: "团队名称",
width: "420",
align: "center",
},
{ prop: "EMPINNAME", label: "主管", width: "120" },
{
prop: "SUPERSALETEAMNAME",
label: "归属团队名称",
width: "420",
align: "center",
},
{
prop: "EFFEDATE",
label: "生效日期",
width: "140",
},
{
prop: "SALETEAMSTATUS",
label: "状态",
width: "120",
},
],
}
}