UNPKG

vxe-table

Version:

A PC-end table component based on Vxe UI, supporting copy-paste, data pivot table, and high-performance virtual list table solution.

420 lines (411 loc) 17 kB
import { nextTick } from 'vue' import XEUtils from 'xe-utils' import { VxeUI } from '../../../ui' import { toFilters, handleFieldOrColumn, getRefElem } from '../../src/util' import { toCssUnit, triggerEvent, getDomNode } from '../../../ui/src/dom' import { isEnableConf } from '../../../ui/src/utils' import type { TableFilterMethods, TableFilterPrivateMethods, VxeTableDefines } from '../../../../types' const { renderer, hooks } = VxeUI const tableFilterMethodKeys: (keyof TableFilterMethods)[] = ['openFilter', 'setFilter', 'clearFilter', 'saveFilter', 'saveFilterByEvent', 'saveFilterPanel', 'saveFilterPanelByEvent', 'resetFilter', 'resetFilterByEvent', 'resetFilterPanel', 'resetFilterPanelByEvent', 'getCheckedFilters', 'updateFilterOptionStatus'] hooks.add('tableFilterModule', { setupTable ($xeTable) { const $xeGrid = $xeTable.xeGrid const $xeGantt = $xeTable.xeGantt const $xeGGWrapper = $xeGrid || $xeGantt const { props, reactData, internalData } = $xeTable const { refElem, refTableFilter } = $xeTable.getRefMaps() const { computeFilterOpts, computeMouseOpts } = $xeTable.getComputeMaps() const filterPrivateMethods: TableFilterPrivateMethods = { checkFilterOptions () { const { filterStore } = reactData const { column } = filterStore if (column) { const filterOptions = (column.filters || []) as VxeTableDefines.FilterOption[] filterStore.isAllSelected = filterOptions.every((item) => item._checked) filterStore.isIndeterminate = !filterStore.isAllSelected && filterOptions.some((item) => item._checked) } }, /** * 点击筛选事件 * 当筛选图标被点击时触发 * 更新选项是否全部状态 * 打开筛选面板 * @param {Event} evnt 事件 * @param {ColumnInfo} column 列配置 * @param {Object} params 参数 */ triggerFilterEvent (evnt: MouseEvent, column: VxeTableDefines.ColumnInfo, params) { const { initStore, filterStore } = reactData const { elemStore } = internalData if (filterStore.column === column && filterStore.visible) { filterStore.visible = false } else { const tableEl = refElem.value const { scrollTop, scrollLeft, visibleHeight, visibleWidth } = getDomNode() const filterOpts = computeFilterOpts.value const { transfer } = filterOpts const tableRect = tableEl.getBoundingClientRect() const btnElem = evnt.currentTarget as HTMLDivElement const filterRender = column ? column.filterRender : null const compConf = filterRender && isEnableConf(filterRender) ? renderer.get(filterRender.name) : null $xeTable.handleFilterOptions(column) internalData._currFilterParams = params filterStore.style = null filterStore.visible = true initStore.filter = true nextTick(() => { const headerScrollElem = getRefElem(elemStore['main-header-scroll']) if (!headerScrollElem) { return } const tableFilter = refTableFilter.value const filterWrapperElem = tableFilter ? tableFilter.getRefMaps().refElem.value as HTMLDivElement : null if (!filterWrapperElem) { return } const btnRect = btnElem.getBoundingClientRect() const filterHeadElem = filterWrapperElem.querySelector<HTMLDivElement>('.vxe-table--filter-header') const filterFootElem = filterWrapperElem.querySelector<HTMLDivElement>('.vxe-table--filter-footer') const filterWidth = filterWrapperElem.offsetWidth const centerWidth = filterWidth / 2 let left = 0 let top = 0 let maxHeight: number = 0 if (transfer) { left = btnRect.left - centerWidth + scrollLeft top = btnRect.top + btnElem.clientHeight + scrollTop maxHeight = Math.min(Math.max(tableRect.height, Math.floor(visibleHeight / 2)), Math.max(80, visibleHeight - top - (filterHeadElem ? filterHeadElem.clientHeight : 0) - (filterFootElem ? filterFootElem.clientHeight : 0) - 28)) if (left < 16) { left = 16 } else if (left > (visibleWidth - filterWidth - 16)) { left = visibleWidth - filterWidth - 16 } } else { left = btnRect.left - tableRect.left - centerWidth top = btnRect.top - tableRect.top + btnElem.clientHeight maxHeight = Math.max(40, tableEl.clientHeight - top - (filterHeadElem ? filterHeadElem.clientHeight : 0) - (filterFootElem ? filterFootElem.clientHeight : 0) - 14) if (left < 1) { left = 1 } else if (left > (tableEl.clientWidth - filterWidth - 1)) { left = tableEl.clientWidth - filterWidth - 1 } if ($xeGGWrapper) { const wrapperEl = $xeGGWrapper.getRefMaps().refElem.value if (wrapperEl) { const wrapperRect = wrapperEl.getBoundingClientRect() top += tableRect.top - wrapperRect.top } } } filterStore.style = { top: toCssUnit(top), left: toCssUnit(left) } // 筛选面板是自适应表格高度 if (compConf ? !compConf.tableFilterAutoHeight : false) { maxHeight = 0 } // 判断面板不能大于表格高度 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 = isEnableConf(filterRender) ? renderer.get(filterRender.name) : null const frMethod = column.filterResetMethod || (compConf ? (compConf.tableFilterResetMethod || compConf.filterResetMethod) : null) filters.forEach((item: any) => { item._checked = false item.checked = false if (!frMethod) { item.data = XEUtils.clone(item.resetValue, true) } }) if (frMethod) { frMethod({ options: filters, column, $table: $xeTable }) } } } }, handleColumnConfirmFilter (column, evnt) { const { mouseConfig } = props const { scrollXLoad: oldScrollXLoad, scrollYLoad: oldScrollYLoad } = reactData const filterOpts = computeFilterOpts.value const mouseOpts = computeMouseOpts.value const { field, filters } = column const filterOptions = filters || [] const values: any[] = [] const datas: any[] = [] filterOptions.forEach((item) => { if (item.checked) { values.push(item.value) datas.push(item.data) } }) const filterList = $xeTable.getCheckedFilters() const params = { $table: $xeTable, $event: evnt as Event, 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 as Event, params) } if (evnt) { $xeTable.dispatchEvent('filter-change', params, evnt) } $xeTable.closeFilter() return $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) }) }, /** * 确认筛选 * 当筛选面板中的确定按钮被按下时触发 */ confirmFilterEvent (evnt, column) { if (column) { $xeTable.handleColumnConfirmFilter(column, evnt) } }, // (单选)筛选发生改变 handleFilterChangeRadioOption (evnt, checked, item) { const { filterStore } = reactData const { column } = filterStore if (column) { const filterOptions = (column.filters || []) as VxeTableDefines.FilterOption[] filterOptions.forEach((option) => { option._checked = false }) item._checked = checked $xeTable.checkFilterOptions() $xeTable.handleFilterConfirmFilter(evnt, column) } }, // (多选)筛选发生改变 handleFilterChangeMultipleOption (evnt, checked, item) { item._checked = checked $xeTable.checkFilterOptions() }, // 筛选发生改变 handleFilterChangeOption (evnt, checked, item) { const { filterStore } = reactData const { fullColumnIdData } = internalData let column = filterStore.column if (!column) { const colRest = fullColumnIdData[item._colId] if (colRest) { column = colRest.column filterStore.column = column } } if (column) { if (column.filterMultiple) { $xeTable.handleFilterChangeMultipleOption(evnt, checked, item) } else { $xeTable.handleFilterChangeRadioOption(evnt, checked, item) } } }, // 确认筛选 handleFilterConfirmFilter (evnt, column) { if (column) { const filterOptions = (column.filters || []) as VxeTableDefines.FilterOption[] filterOptions.forEach((option) => { option.checked = option._checked }) $xeTable.confirmFilterEvent(evnt, column) } }, /** * 重置筛选 * 当筛选面板中的重置按钮被按下时触发 */ handleFilterResetFilter (evnt: Event, column) { if (column) { $xeTable.handleClearFilter(column) $xeTable.confirmFilterEvent(evnt, column) if (evnt) { $xeTable.dispatchEvent('clear-filter', { filterList: [] }, evnt) } } } } 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 headerWrapperElem = getRefElem(elemStore[`${fixed || 'main'}-header-wrapper`] || elemStore['main-header-wrapper']) if (headerWrapperElem) { const filterBtnElem = headerWrapperElem.querySelector(`.vxe-header--column.${column.id} .vxe-cell--filter`) as HTMLElement triggerEvent(filterBtnElem, 'click') } }) } return nextTick() }, /** * 修改筛选条件列表 * @param {ColumnInfo} fieldOrColumn 列或字段名 * @param {Array} options 选项 */ setFilter (fieldOrColumn, options, isUpdate) { const { filterStore } = reactData const column = handleFieldOrColumn($xeTable, fieldOrColumn) if (column && column.filters) { column.filters = toFilters(options || [], column.id) if (isUpdate) { return $xeTable.handleColumnConfirmFilter(column, null) } else { if (filterStore.visible) { $xeTable.handleFilterOptions(column) } } } 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) { $xeTable.handleClearFilter(column) } } else { tableFullColumn.forEach($xeTable.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() }, saveFilter (fieldOrColumn) { if (fieldOrColumn) { const column = handleFieldOrColumn($xeTable, fieldOrColumn) $xeTable.handleFilterConfirmFilter(null, column) } return nextTick() }, saveFilterByEvent (evnt, fieldOrColumn) { if (fieldOrColumn) { const column = handleFieldOrColumn($xeTable, fieldOrColumn) $xeTable.handleFilterConfirmFilter(evnt, column) } return nextTick() }, resetFilter (fieldOrColumn) { if (fieldOrColumn) { const column = handleFieldOrColumn($xeTable, fieldOrColumn) $xeTable.handleFilterResetFilter(null, column) } return nextTick() }, resetFilterByEvent (evnt, fieldOrColumn) { if (fieldOrColumn) { const column = handleFieldOrColumn($xeTable, fieldOrColumn) $xeTable.handleFilterResetFilter(evnt, column) } return nextTick() }, saveFilterPanel () { const { filterStore } = reactData $xeTable.handleFilterConfirmFilter(null, filterStore.column || null) return nextTick() }, saveFilterPanelByEvent (evnt) { const { filterStore } = reactData $xeTable.handleFilterConfirmFilter(evnt, filterStore.column || null) return nextTick() }, resetFilterPanel () { const { filterStore } = reactData $xeTable.handleFilterResetFilter(null, filterStore.column || null) return nextTick() }, resetFilterPanelByEvent (evnt) { const { filterStore } = reactData $xeTable.handleFilterResetFilter(evnt, filterStore.column || null) return nextTick() }, getCheckedFilters () { const { tableFullColumn } = internalData const filterList: VxeTableDefines.FilterCheckedParams[] = [] tableFullColumn.forEach((column) => { const { field, filters } = column const filterOptions = filters || [] const valueList: any[] = [] const dataList: any[] = [] if (filterOptions) { filterOptions.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 }, updateFilterOptionStatus (item: any, checked: boolean) { item._checked = checked item.checked = checked return nextTick() } } return { ...filterMethods, ...filterPrivateMethods } }, setupGrid ($xeGrid) { return $xeGrid.extendTableMethods(tableFilterMethodKeys) }, setupGantt ($xeGantt) { return $xeGantt.extendTableMethods(tableFilterMethodKeys) } })