vxe-table-demonic
Version:
一个基于 vue 的 PC 端表单/表格组件,支持增删改查、虚拟列表、虚拟树、懒加载、快捷菜单、数据校验、树形结构、打印导出、表单渲染、数据分页、弹窗、自定义模板、渲染器、JSON 配置式...
285 lines (277 loc) • 12 kB
text/typescript
import { nextTick } from 'vue'
import XEUtils from 'xe-utils'
import { VXETable } from '../../v-x-e-table'
import { toFilters, handleFieldOrColumn } from '../../table/src/util'
import { getDomNode, triggerEvent } from '../../tools/dom'
import { VxeGlobalHooksHandles, TableFilterMethods, TableFilterPrivateMethods } from '../../../types/all'
const tableFilterMethodKeys: (keyof TableFilterMethods)[] = ['setFilter', 'clearFilter', 'getCheckedFilters']
const tableFilterHook: VxeGlobalHooksHandles.HookOptions = {
setupTable ($xetable) {
const { props, reactData, internalData } = $xetable
const { refTableBody, refTableFilter } = $xetable.getRefMaps()
const { computeFilterOpts, computeMouseOpts } = $xetable.getComputeMaps()
const filterPrivateMethods: TableFilterPrivateMethods = {
checkFilterOptions () {
const { filterStore } = reactData
filterStore.isAllSelected = filterStore.options.every((item: any) => item._checked)
filterStore.isIndeterminate = !filterStore.isAllSelected && filterStore.options.some((item: any) => item._checked)
},
/**
* 点击筛选事件
* 当筛选图标被点击时触发
* 更新选项是否全部状态
* 打开筛选面板
* @param {Event} evnt 事件
* @param {ColumnInfo} column 列配置
* @param {Object} params 参数
*/
triggerFilterEvent (evnt, column, params) {
const { initStore, filterStore } = reactData
if (filterStore.column === column && filterStore.visible) {
filterStore.visible = false
} else {
const { target: targetElem, pageX } = evnt
const { visibleWidth } = getDomNode()
const { filters, filterMultiple, filterRender } = column
const compConf = filterRender ? VXETable.renderer.get(filterRender.name) : null
const filterRecoverMethod = column.filterRecoverMethod || (compConf ? compConf.filterRecoverMethod : null)
internalData._currFilterParams = params
Object.assign(filterStore, {
multiple: filterMultiple,
options: filters,
column,
style: null
})
// 复原状态
filterStore.options.forEach((option: any) => {
const { _checked, checked } = option
option._checked = checked
if (!checked && _checked !== checked) {
if (filterRecoverMethod) {
filterRecoverMethod({ option, column, $table: $xetable })
}
}
})
this.checkFilterOptions()
filterStore.visible = true
initStore.filter = true
nextTick(() => {
const tableBody = refTableBody.value
const bodyElem = tableBody.$el as HTMLDivElement
const tableFilter = refTableFilter.value
const filterWrapperElem = tableFilter ? tableFilter.$el as HTMLDivElement : null
let filterWidth = 0
let filterHeight = 0
let filterHeadElem: HTMLDivElement | null = null
let filterFootElem: HTMLDivElement | null = null
if (filterWrapperElem) {
filterWidth = filterWrapperElem.offsetWidth
filterHeight = filterWrapperElem.offsetHeight
filterHeadElem = filterWrapperElem.querySelector('.vxe-table--filter-header')
filterFootElem = filterWrapperElem.querySelector('.vxe-table--filter-footer')
}
const centerWidth = filterWidth / 2
const minMargin = 10
const maxLeft = bodyElem.clientWidth - filterWidth - minMargin
let left, right
const style: any = {
top: `${targetElem.offsetTop + targetElem.offsetParent.offsetTop + targetElem.offsetHeight + 8}px`
}
// 判断面板不能大于表格高度
let maxHeight = null
if (filterHeight >= bodyElem.clientHeight) {
maxHeight = Math.max(60, bodyElem.clientHeight - (filterFootElem ? filterFootElem.offsetHeight : 0) - (filterHeadElem ? filterHeadElem.offsetHeight : 0))
}
if (column.fixed === 'left') {
left = targetElem.offsetLeft + targetElem.offsetParent.offsetLeft - centerWidth
} else if (column.fixed === 'right') {
right = (targetElem.offsetParent.offsetWidth - targetElem.offsetLeft) + (targetElem.offsetParent.offsetParent.offsetWidth - targetElem.offsetParent.offsetLeft) - column.renderWidth - centerWidth
} else {
left = targetElem.offsetLeft + targetElem.offsetParent.offsetLeft - centerWidth - bodyElem.scrollLeft
}
if (left) {
const overflowWidth = (pageX + filterWidth - centerWidth + minMargin) - visibleWidth
if (overflowWidth > 0) {
left -= overflowWidth
}
style.left = `${Math.min(maxLeft, Math.max(minMargin, left))}px`
} else if (right) {
const overflowWidth = (pageX + filterWidth - centerWidth + minMargin) - visibleWidth
if (overflowWidth > 0) {
right += overflowWidth
}
style.right = `${Math.max(minMargin, right)}px`
}
filterStore.style = style
filterStore.maxHeight = maxHeight
})
}
$xetable.dispatchEvent('filter-visible', { column, field: column.field, property: column.field, filterList: $xetable.getCheckedFilters(), visible: filterStore.visible }, evnt)
},
handleClearFilter (column) {
if (column) {
const { filters, filterRender } = column
if (filters) {
const compConf = filterRender ? VXETable.renderer.get(filterRender.name) : null
const filterResetMethod = column.filterResetMethod || (compConf ? compConf.filterResetMethod : null)
filters.forEach((item: any) => {
item._checked = false
item.checked = false
if (!filterResetMethod) {
item.data = XEUtils.clone(item.resetValue, true)
}
})
if (filterResetMethod) {
filterResetMethod({ options: filters, column, $table: $xetable })
}
}
}
},
/**
* 确认筛选
* 当筛选面板中的确定按钮被按下时触发
* @param {Event} evnt 事件
*/
confirmFilterEvent (evnt: Event) {
const { mouseConfig } = props
const { filterStore, scrollXLoad: oldScrollXLoad, scrollYLoad: oldScrollYLoad } = reactData
const filterOpts = computeFilterOpts.value
const mouseOpts = computeMouseOpts.value
const { column } = filterStore
const { field } = column
const values: any[] = []
const datas: any[] = []
column.filters.forEach((item: any) => {
if (item.checked) {
values.push(item.value)
datas.push(item.data)
}
})
const filterList = $xetable.getCheckedFilters()
const params = { $table: $xetable, $event: evnt, column, field, property: field, values, datas, filters: filterList, filterList }
// 如果是服务端筛选,则跳过本地筛选处理
if (!filterOpts.remote) {
$xetable.handleTableData(true)
$xetable.checkSelectionStatus()
}
if (mouseConfig && mouseOpts.area && $xetable.handleFilterEvent) {
$xetable.handleFilterEvent(evnt, params)
}
$xetable.dispatchEvent('filter-change', params, evnt)
$xetable.closeFilter()
$xetable.updateFooter().then(() => {
const { scrollXLoad, scrollYLoad } = reactData
if ((oldScrollXLoad || scrollXLoad) || (oldScrollYLoad || scrollYLoad)) {
if (oldScrollXLoad || scrollXLoad) {
$xetable.updateScrollXSpace()
}
if (oldScrollYLoad || scrollYLoad) {
$xetable.updateScrollYSpace()
}
return $xetable.refreshScroll()
}
}).then(() => {
$xetable.updateCellAreas()
return $xetable.recalculate(true)
}).then(() => {
// 存在滚动行为未结束情况
setTimeout(() => $xetable.recalculate(), 50)
})
}
}
const filterMethods: TableFilterMethods = {
/**
* 手动弹出筛选面板
* @param column
*/
openFilter (fieldOrColumn) {
const column = handleFieldOrColumn($xetable, fieldOrColumn)
if (column && column.filters) {
const { elemStore } = internalData
const { fixed } = column
return $xetable.scrollToColumn(column).then(() => {
const headerWrapperRef = elemStore[`${fixed || 'main'}-header-wrapper`] || elemStore['main-header-wrapper']
const headerWrapperElem = headerWrapperRef ? headerWrapperRef.value : null
if (headerWrapperElem) {
const filterBtnElem = headerWrapperElem.querySelector(`.vxe-header--column.${column.id} .vxe-filter--btn`) as HTMLElement
triggerEvent(filterBtnElem, 'click')
}
})
}
return nextTick()
},
/**
* 修改筛选条件列表
* @param {ColumnInfo} fieldOrColumn 列或字段名
* @param {Array} options 选项
*/
setFilter (fieldOrColumn, options) {
const column = handleFieldOrColumn($xetable, fieldOrColumn)
if (column && column.filters) {
column.filters = toFilters(options || [])
}
return nextTick()
},
/**
* 清空指定列的筛选条件
* 如果为空则清空所有列的筛选条件
* @param {String} fieldOrColumn 列或字段名
*/
clearFilter (fieldOrColumn) {
const { filterStore } = reactData
const { tableFullColumn } = internalData
const filterOpts = computeFilterOpts.value
let column
if (fieldOrColumn) {
column = handleFieldOrColumn($xetable, fieldOrColumn)
if (column) {
filterPrivateMethods.handleClearFilter(column)
}
} else {
tableFullColumn.forEach(filterPrivateMethods.handleClearFilter)
}
if (!fieldOrColumn || column !== filterStore.column) {
Object.assign(filterStore, {
isAllSelected: false,
isIndeterminate: false,
style: null,
options: [],
column: null,
multiple: false,
visible: false
})
}
if (!filterOpts.remote) {
return $xetable.updateData()
}
return nextTick()
},
getCheckedFilters () {
const { tableFullColumn } = internalData
const filterList: any[] = []
tableFullColumn.forEach((column) => {
const { field, filters } = column
const valueList: any[] = []
const dataList: any[] = []
if (filters && filters.length) {
filters.forEach((item) => {
if (item.checked) {
valueList.push(item.value)
dataList.push(item.data)
}
})
if (valueList.length) {
filterList.push({ column, field, property: field, values: valueList, datas: dataList })
}
}
})
return filterList
}
}
return { ...filterMethods, ...filterPrivateMethods }
},
setupGrid ($xegrid) {
return $xegrid.extendTableMethods(tableFilterMethodKeys)
}
}
export default tableFilterHook