【fix 66426】完成JTable的隐藏冻结列控制

This commit is contained in:
danshihao
2023-03-15 10:34:40 +08:00
parent 61c230cb87
commit 7d64a86314
+299
View File
@@ -0,0 +1,299 @@
<template>
<a-table
ref='table'
:table-key="tableKey"
:columns="resultColumns"
v-bind="$attrs"
v-on="$listeners">
<template v-for="(slotItem) of slots" :slot="slotItem" slot-scope="text, record, index">
<slot :name="slotItem" v-bind="{text,record,index}"></slot>
</template>
<div slot="settingDropdown">
<a-card>
<!-- scroll.x 要大于表格所有固定列宽的合每个列都要有width否则定位后宽度会出现变形否则会出现问题 -->
<a-table
rowKey="key"
:style="settingStyle"
tableLayout="fixed"
size="small"
bordered
:pagination="false"
:scroll="settingScroll"
:data-source="settingDataSource"
:columns="settingColumns">
<a-checkbox slot="hide" slot-scope="text, record"
:checked="text"
:disabled="record.disabledHide"
@change="changeSetting(text, 'hide', record)"></a-checkbox>
<a-checkbox slot="freeze" slot-scope="text, record"
:checked="text"
:disabled="record.disabledFreeze"
@change="changeSetting(text, 'freeze', record)"></a-checkbox>
</a-table>
</a-card>
</div>
<a-icon ref='settingBtn' slot="settingIcon" type='setting' :style="{ fontSize:'16px', color: '#108ee9' }" />
</a-table>
</template>
<script>
import Vue from 'vue'
import { cloneDeep } from 'lodash'
// 操作列的key或dataIndex
const actionKey = 'action'
// 保存localStorage中的后缀
const saveSuffix = ':JTable'
// 拿到column的key
const getKey = col => col.key || col.dataIndex
// 所有JTable的缓存key
const J_TABLE_KEYS = 'J_TABLE_KEYS'
/**
* 以下 表格为实际要展示的表、配置表为设置冻结和隐藏的表
* 执行流程梳理
* 1.mounted挂载时去拿配置
* 1.1.从localStorage取历史配置:取到直接进入2 没取到就进1.2
* 1.2.从父组件获取columns初始化配置 并存储到localStorage
*
* 2.拿到配置后
* 2.1.watch进行监听配置,然后生成 配置表格的数据,再调用setResultColumns来更新表格
* 2.2.将columns根据配置转换成resultColumns进行渲染表格
*
* 3.修改配置触发changeSetting来对配置进行更新,更新后watch会监听到跳转2如此循环
*/
export default {
name: 'JTable',
props: {
tableKey: {
type: String,
required: true
},
columns: {
type: Array,
required: true
},
// 配置表的样式
settingStyle: {
type: Object,
required: false,
default: () => ({ width: '300px' })
},
// 配置表的scroll
settingScroll: {
type: Object,
required: false,
default: () => ({ y: 300 })
}
},
data () {
return {
// 设置表格
settingColumns: [
{ title: '列名', dataIndex: 'title', align: 'center', ellipsis: true, width: 150 },
{ title: '隐藏', dataIndex: 'hide', align: 'center', ellipsis: true, width: 60, scopedSlots: { customRender: 'hide' } },
{ title: '冻结', dataIndex: 'freeze', align: 'center', ellipsis: true, width: 60, scopedSlots: { customRender: 'freeze' } }
],
settingDataSource: [],
settingColumnsObj: { hide: [], freeze: [] }, // 本地存储的配置对象
resultColumns: []
}
},
computed: {
slots () {
const slots = []
for (const column of this.columns) {
if (column.scopedSlots && column.scopedSlots.customRender) {
slots.push(column.scopedSlots.customRender)
}
}
return slots
},
},
watch: {
// 配置列改变,将配置转换成配置表格数据
settingColumnsObj: {
deep: true, // 深度监听
immediate: true, // 挂载先执行一次
handler () {
const dataSource = []
this.columns.forEach(col => {
const tempKey = getKey(col)
// 操作栏不可配置
if (tempKey === actionKey) {
return
}
// hideSettingColumn: true 可以让不显示字段控制
if (col.hideSettingColumn) {
return
}
dataSource.push({
title: col.title, // 用于显示的字段
key: tempKey, // 用于存储的字段
hide: this.settingColumnsObj.hide.includes(tempKey), // 是否隐藏
freeze: this.settingColumnsObj.freeze.includes(tempKey), // 是否冻结
disabledHide: col.disabledHide, // 禁用隐藏
disabledFreeze: col.disabledFreeze // 禁用冻结
})
})
this.settingDataSource = dataSource
this.setResultColumns()
}
}
},
methods: {
/**
* 将父组件的columns进行处理:增加设置列按钮
* 冻结列要有width,否则会有问题,建议使用minWidthscroll.x要大于所有列width的和
*/
setResultColumns () {
// 深度克隆,防止直接修改父组件数据
const columns = cloneDeep(this.columns)
// 设置按钮插槽配置
const settingOption = {
scopedSlots: {
filterDropdown: 'settingDropdown',
filterIcon: 'settingIcon',
customRender: 'action'
}
}
const findActionIndex = columns.findIndex(col => col && (getKey(col) === actionKey))
// 如果找到操作栏,就把设置加到操作栏上
if (findActionIndex !== -1) {
// 添加设置插槽配置,并固定右侧
Object.assign(columns[findActionIndex], settingOption, { fixed: 'right' })
} else {
// 如果没有找到操作栏,就把最后列加上设置插槽配置
Object.assign(columns[columns.length - 1], settingOption)
}
// 最后过滤掉需要隐藏的字段,按定位进行排序
const startArr = [] // 冻结左侧的列
const midArr = [] // 没有冻结的列
const endArr = [] // 冻结右侧的列(只能是action,暂时不能自定义固定右侧)
columns.forEach(col => {
const key = getKey(col)
const fixed = this.settingColumnsObj.freeze.includes(key)
col.hide = this.settingColumnsObj.hide.includes(key)
col.fixed = key === actionKey ? 'right' : fixed
if (!col.hide) {
// 如果没有宽度就取minWidth(不添加原数据的width属性,这里只对需要固定的minWidth生效)
const minWidth = col.width || col.minWidth
switch (col.fixed) {
case true:
case 'left':
startArr.push({ ...col, width: minWidth })
break
case 'right':
endArr.push({ ...col, width: minWidth })
break
default:
midArr.push(col)
}
}
})
// 更新父组件的数据,将排序后的进行渲染
this.$emit('update:columns', columns)
this.resultColumns = [...startArr, ...midArr, ...endArr]
},
/**
* 配置表中复选框改变状态的事件
* @param text 修改前的状态
* @param type 隐藏还是冻结
* @param record 当前行数据
*/
changeSetting (text, type, record) {
const checked = !text // text是点击前的状态,取反就是要修改的状态
console.log('修改了配置', checked, type, record)
// 修改当前行配置
record[type] = !checked
// 勾选就添加,取消就删除
if (checked) {
this.settingColumnsObj[type].push(record.key)
} else {
this.settingColumnsObj[type].splice(this.settingColumnsObj[type].findIndex(item => item === record.key), 1)
}
this.saveSetting()
/**
* 弹框消失问题 冻结列会导致dom结构改变而从局部更新丢失弹框
* 情况1:原本显示没有冻结列,新增第一个冻结列
* 情况2:原本有且只剩一个冻结列,取消最后一个冻结列
* 解决方案,刷新前获取dom,刷新后再次获取,如果获取不一致就触发点击事件点开弹框
*/
const beforeIsFixed = !!this.$refs.table.$el.querySelector('.ant-table-fixed-left')
this.$nextTick(() => {
const afterIsFixed = !!this.$refs.table.$el.querySelector('.ant-table-fixed-left')
// console.log(beforeIsFixed, afterIsFixed)
if (beforeIsFixed !== afterIsFixed) {
this.$refs.settingBtn.$el.click()
}
})
},
// 初始化配置,从columns里获取
initSetting () {
const hide = []
const freeze = []
this.columns.forEach(col => {
const key = getKey(col)
// hide属性控制隐藏
if (col.hide) {
hide.push(key)
}
// fixed冻结到左侧
if (col.fixed === true || col.fixed === 'left') {
freeze.push(key)
}
})
this.settingColumnsObj = { hide, freeze }
this.saveSetting()
},
// 配置保存到本地
saveSetting () {
// 获取所有的jtable缓存的key数组(用于清除缓存,没有就初始化数组)
const jTableKeys = Vue.ls.get(J_TABLE_KEYS) || []
const saveKey = this.tableKey + saveSuffix
// 如果不在数组里面就追加进去
if (!jTableKeys.includes(saveKey)) {
jTableKeys.push(saveKey)
}
Vue.ls.set(J_TABLE_KEYS, jTableKeys, 7 * 24 * 60 * 60 * 10)
Vue.ls.set(saveKey, this.settingColumnsObj, 7 * 24 * 60 * 60 * 10)
},
// 清除本地的配置(当前的JTable)
clearSetting () {
this.settingColumnsObj = { hide: [], freeze: [] }
Vue.ls.remove(this.tableKey + saveSuffix)
this.$message.success('成功清除当前JTable的缓存!')
},
// 清除所有缓存
clearAllCacheSetting () {
// 获取到之后遍历删除,最后把keys数组删除
const jTableKeys = Vue.ls.get(J_TABLE_KEYS)
jTableKeys && jTableKeys.forEach(key => {
Vue.ls.remove(key)
})
Vue.ls.remove(J_TABLE_KEYS)
this.$message.success('成功清除全局JTable的缓存!')
},
},
mounted () {
const settingColumnsObj = Vue.ls.get(this.tableKey + saveSuffix)
// 第一次进页面或清空缓存进行初始化
if (settingColumnsObj) {
this.settingColumnsObj = settingColumnsObj
} else {
this.initSetting()
}
},
created () {
}
}
</script>
<style lang='less' scoped>
</style>