<script> export default { name: "SuperTable", props: { // 数据 value: { type: [Array], require: true, }, // 字典 dict: { type: [Object], require: true, }, // 分页 page: { type: [Object], require: false, }, // 模板 columns: { type: [Array], require: true, }, // 是否显示序号 index: { type: Boolean, default: false, }, // 是否显示单选 radio: { type: Boolean, default: false, }, // 是否显示多选 checkbox: { type: Boolean, default: false, }, // 是否显示分页 pagination: { type: Boolean, default: false, }, // 是否列操作 convenitentOperation: { type: Boolean, default: false, }, // 是否禁止选择 selectable: { type: Function, default: () => {}, }, // storageKey: { type: String, }, showSummary:{ type:Boolean, default:false, }, }, components: { ElDictTag: () => import("@/components/DictTag/index.vue"), ElDraggable: () => import("@/components/draggable/index.vue"), ElFilePreview: () => import("@/components/file-preview/index.vue"), ElComputedInput: () => import("@/components/computed-input/index.vue"), ElPopoverSelectV2: () => import("@/components/popover-select-v2/index.vue"), ElPopoverMultipleSelectV2: () => import("@/components/popover-select-v2/multiple.vue"), ElComputedInputV2: () => import("@/components/computed-input-v2/index.vue"), ElPopoverTreeSelect: () => import("@/components/popover-tree-select/index.vue"), ButtonHide: () => import("./hide.vue"), ButtonFreeze: () => import("./freeze.vue"), IconHide: () => import("./once/hide.vue"), IconSort: () => import("./once/sort.vue"), IconFreeze: () => import("./once/freeze.vue"), IconFilter: () => import("./once/filters.vue"), }, data() { const { columns, storageKey } = this.$props; const localColumns = localStorage.getItem(storageKey); const innerColumns = storageKey && localColumns ? JSON.parse(localColumns) : columns.map(({ item, attr }) => ({ attr, item: { hidden: true, ...item }, })); return { innerColumns: innerColumns, rowKey: "id", // 选择 selectData: [], selectState: false, // 过滤 filterData: [], filterState: false, }; }, computed: { innerValue: { get() { if (this.filterState) { return this.filterData; } else if (this.selectState) { return this.selectData; } else { return this.$props.value; } }, set(value) { this.$emit("input", value); }, }, showColumns: { get() { return this.innerColumns.filter(({ item }) => item.hidden); }, set() {}, }, filterRules: { get() { return Object.fromEntries( this.innerColumns .filter(({ item }) => item.filter && !!item.filter.length) .map(({ item }) => [item.key, item.filter]) ); }, set() {}, }, }, watch: { filterRules: { handler: function (newValue) { function multiFilter(array, filters) { const filterKeys = Object.keys(filters); // filters all elements passing the criteria return array.filter((item) => { // dynamically validate all filter criteria return filterKeys.every((key) => { //ignore when the filter is empty Anne if (!filters[key].length) return true; return !!~filters[key].indexOf(item[key]); }); }); } this.filterState = JSON.stringify(newValue) !== "{}"; this.filterData = multiFilter(this.$props.value, newValue); }, }, value:{ handler: function (newValue) { if(this.value.length > 0){ this.$refs.superTable&& this.$refs.superTable.clearSelection(); } }, immediate: true, deep:true } }, methods: { // onSelectionChange(value) { this.selectData = value; this.$emit("row-select", this.selectData); }, // onRowClick(row, column, event) { const { radio, checkbox } = this.$props; // 单选 if (radio) { this.$emit("row-select", [row]); } // 多选 if (checkbox) { this.$refs.superTable.toggleRowSelection( this.innerValue.find((item) => item.id === row.id) ); } }, // 宽度 onWidth(newProp, oldProp, column) { this.innerColumns = this.innerColumns.map(({ item, attr }) => ({ attr, item: { ...item, width: item.key === column.property ? newProp : item.width, }, })); if (this.$props.storageKey) { localStorage.setItem( this.$props.storageKey, JSON.stringify(this.innerColumns) ); } }, // 冻结 onHide(prop) { this.$nextTick(() => { this.$refs.superTable.doLayout(); if (this.$props.storageKey) { localStorage.setItem( this.$props.storageKey, JSON.stringify(this.innerColumns) ); } }); }, // 排序 onSort(prop) { const { key, sort } = prop; this.$nextTick(() => { this.$refs.superTable.sort(key, sort); this.$refs.superTable.doLayout(); if (this.$props.storageKey) { localStorage.setItem( this.$props.storageKey, JSON.stringify(this.innerColumns) ); } }); }, // 冻结 onFreeze() { this.$nextTick(() => { this.$refs.superTable.doLayout(); if (this.$props.storageKey) { localStorage.setItem( this.$props.storageKey, JSON.stringify(this.innerColumns) ); } }); }, // 过滤 onFilter() { this.$nextTick(() => { this.$refs.superTable.doLayout(); if (this.$props.storageKey) { localStorage.setItem( this.$props.storageKey, JSON.stringify(this.innerColumns) ); } }); }, onFilters(value) { const { item: { key }, attr: { dictName }, } = value; let dataList = []; const dict = this.dict.type[dictName]; dataList = Array.from( new Set(this.innerValue.map((item) => item[key]).filter((item) => item)) ).map((item) => ({ text: dictName ? (dict.find((dictItem) => dictItem.value == item) || {}).label : item, value: item, })); return dataList; }, // 继承el-table的Method extendMethod() { const refMethod = Object.entries(this.$refs["superTable"]); for (const [key, value] of refMethod) { if (!(key.includes("$") || key.includes("_"))) { this[key] = value; } } }, getSummaries(param){ if(this.showSummary){ const { columns, data } = param; const sums = []; columns.forEach((column, index) => { if (index === 0 && !column.property) { sums[index] = '合计'; return; } const values = data.map(item => Number(item[column.property])); // if (column.property == 'businessProportion' ||column.property =='rankMagnitude' ) { let sumColumn = this.showColumns.filter(({item,attr}) => attr.isSummary && item.key === column.property); if (sumColumn.length) { sums[index] = values.reduce((prev, curr) => { const value = Number(curr); if (!isNaN(value)) { return prev + curr; } else { return prev + 0; } }, 0); sums[index] = sums[index] && sums[index].toFixed(2) // 保留2位小数,解决小数合计列; } }); return sums; } } }, created() {}, mounted() { this.extendMethod(); }, updated() { this.$nextTick(()=>{ this.$refs.superTable.doLayout(); }) }, destroyed() {}, }; </script> <template> <div class="el-super-table"> <el-table border size="mini" height="auto" ref="superTable" v-bind="$attrs" v-on="$listeners" :row-key="rowKey" :data="innerValue" :show-summary="showSummary" :summary-method="getSummaries" :highlight-current-row="radio" @row-click="onRowClick" @header-dragend="onWidth" @selection-change="onSelectionChange" style="flex: 1" > <!-- 多选 --> <el-table-column v-if="checkbox" :column-key="rowKey" fixed width="60" align="center" type="selection" reserve-selection > </el-table-column> <!-- 序号 --> <el-table-column v-if="index" :resizable="false" fixed width="50" label="序号" align="center" class="is-index" > <template slot-scope="scope"> {{ scope.$index + 1 }} </template> </el-table-column> <el-table-column v-for="({ item, attr }, index) in showColumns" :key="item.key + index" :prop="item.key" :label="item.title" :fixed="item.fixed" :width="item.width || 200" show-overflow-tooltip > <template slot="header" slot-scope="scope"> <template> <span v-if="item.require" style="color: #ff4949">*</span> <span :style="{ color: item.sort || item.fixed || (item.filter && !!item.filter.length) ? '#1890ff' : '', }" > {{ item.title }} </span> <template> <icon-sort v-if="item.sortabled" v-model="item.sort" @sort="onSort(item)" ></icon-sort> <icon-freeze v-if="item.fixedabled" v-model="item.fixed" @freeze="onFreeze" ></icon-freeze> <icon-filter v-if="item.filterabled" v-model="item.filter" :filters="onFilters({ item, attr })" @filter="onFilter" ></icon-filter> <icon-hide v-if="item.hiddenabled" v-model="item.hidden" @hide="onHide" ></icon-hide> </template> </template> </template> <template slot-scope="scope"> <slot :name="item.key" v-bind="scope" :item="item" :attr="attr"> <template v-if="attr.is"> <component v-if="attr.is === 'el-dict-tag'" v-bind="attr" :size="$attrs.size" :value="scope.row[item.key]" :options="dict.type[attr.dictName]" ></component> <component v-else-if="attr.is === 'el-popover-select-v2'" v-bind="attr" v-model="scope.row[item.key]" :title="item.title" :size="$attrs.size" :source.sync="scope.row" > </component> <component v-else-if="attr.is === 'el-popover-multiple-select-v2'" v-bind="attr" v-model="scope.row[item.key]" :title="item.title" :size="$attrs.size" :source.sync="scope.row" > </component> <component v-else-if="attr.is === 'el-select'" v-bind="attr" v-model="scope.row[item.key]" :size="$attrs.size" > <template> <el-option v-for="item in dict.type[attr.dictName]" :key="item.value" :label="item.label" :value="item.value" > </el-option> </template> </component> <component v-else v-bind="attr" v-model="scope.row[item.key]" :size="$attrs.size" style="width: 100%" > </component ></template> <template v-else> <component v-if="attr.formatter" is="span">{{ attr.formatter(scope.row) }}</component> <component v-else is="span">{{ scope.row[item.key] || "--" }}</component> </template> </slot> </template> </el-table-column> <slot></slot> </el-table> <div style=" height: 50px; display: flex; justify-content: space-between; align-items: center; " :style="{ height: checkbox || pagination ? '50px' : '0px', }" > <div class="mr-4"> <!-- <template v-if="checkbox"> <el-button v-if="selectState" size="mini" @click="selectState = !selectState" > 所有列 </el-button> <el-button v-else :disabled="!selectData.length" size="mini" @click="selectState = !selectState" > 选择列 {{ selectData.length ? ` :${selectData.length}` : "" }} </el-button> </template> --> <template v-if="convenitentOperation"> <button-hide v-model="innerColumns" @change="onHide"></button-hide> </template> </div> <pagination v-if="pagination" v-show="!selectState" :total="page.total" :page.sync="page.pageNum" :limit.sync="page.pageSize" @pagination="$emit('pagination', { ...$event })" style="height: 32px; padding: 0 !important; flex: 1; overflow-x: auto" /> </div> </div> </template> <style lang="scss" scoped> .el-super-table { position: relative; flex: 1; display: flex; flex-direction: column; overflow: auto; } ::v-deep.el-super-table .el-table__header .cell { word-break: keep-all; white-space: nowrap; .icon-sort { display: none; } &:hover .icon-sort { display: inline-block; } .icon-freeze { display: none; } &:hover .icon-freeze { display: inline-block; } .icon-filter { display: none; } &:hover .icon-filter { display: inline-block; } .icon-hide { display: none; } &:hover .icon-hide { display: inline-block; } } </style>