import { computed, nextTick, ref, Ref, watch } from 'vue' import { FilterItem, FilterListType, IColumnConfig, IEmits, IFilterInput, IFilterSlot, IProps } from '../types'; interface IUseColumnHeaderOperationParams { props: IProps tableDomRef: any emit: IEmits; showingColumns: Ref } export function useColumnHeaderOperation({ props, tableDomRef, emit, showingColumns }: IUseColumnHeaderOperationParams) { // column如果有sortable属性,点击列头部,会直接触发排序,为了在弹窗点确定时再触发排序,需要阻止点击立即排序 // 所以,初始渲染时,将sortable设置为false,在触发排序逻辑时再设置成真实的值,再利用el-table自身的排序逻辑触发排序 const inSorting = ref(false); // 生效中的排序配置 const sortType = ref<'ascending' | 'descending' | null>(null); const sortProp = ref('') // 临时的排序配置 const tempSortType = ref<'ascending' | 'descending' | ''>(''); const tempSortProp = ref('') // 生效中的过滤配置 和 临时过滤配置 const filteredValue = ref>({}); const tempFilteredValue = ref>({}); // 生效中的统计配置 和 临时统计配置 const tempSummaryList = ref([]); const summaryList = ref([]); const isColumnFiltering = computed(() => { return Object.keys(tempFilteredValue.value).some( (key) => { const value = tempFilteredValue.value[key] if (Array.isArray(value)) return value.length return value; } ) }); const showColumnHeadSortIcon = ({ isColumnSortable, filters }: IColumnConfig) => { return isColumnSortable || (filters && Array.isArray(filters) && filters.length > 0) } watch( () => props.columnConfig, (val) => { val?.forEach(v => handleResetFilterValue(v)); tempFilteredValue.value = { ...filteredValue.value }; }, { immediate: true } ) const tableSummaryMethod = (param) => { const { columns, data } = param; const sums: (string | number)[] = [] columns.forEach((column, index) => { if (index === 0) { sums[index] = '合计'; return; } if (!summaryList.value.includes(column.property)) { sums[index] = ''; } else { const values = data.map(item => item[column.property]); // 找到对应列的summaryMethod函数 const summaryMethod = props.columnConfig.find(c => c.prop === column.property)?.summaryMethod ?? (() => ''); sums[index] = summaryMethod(values); } }) return sums } const isColumnHeadActive = ({ prop, filters, _sortable }: IColumnConfig) => { const hasFilters = filters && Array.isArray(filters) && filters.length > 0 const currentFilterActive = hasFilters && filters?.some( (item) => { if (item.type === 'doubleDatePicker' || item.type === 'monthDayPicker') { const [start, end] = item.prop return !!(filteredValue.value[start] || filteredValue.value[end]) } if (item.type === 'checkbox') { return !!(filteredValue.value[item.prop] as any[]).length } if (item.type === 'slot') { return item.active() } return !!filteredValue.value[item.prop] } ) const isUseSort = Array.isArray(_sortable) ? _sortable.some(v => v.prop === sortProp.value) : sortProp.value === prop return currentFilterActive || isUseSort || summaryList.value.includes(prop); } const handleHeaderPopoverShow = (column: IColumnConfig) => { // 关闭其他的排序和筛选弹窗(理论上不写也能关闭其他,但是就是有些列会出现两个弹窗同时出现的情况) closeSortAndFilterPopover(column.prop); tempFilteredValue.value = { ...filteredValue.value }; tempSortType.value = sortType.value || ''; tempSortProp.value = sortProp.value || ''; // 临时合计项设置成实际的合计项 tempSummaryList.value = [...summaryList.value]; } const closeSortAndFilterPopover = (exceptProp?: string) => { document.querySelectorAll('span[data-popper-name]').forEach((item: any) => { if (!exceptProp || exceptProp !== item.dataset.prop) { item?.__vue__?.doClose?.(); } }); } const handleSort = (type: 'ascending' | 'descending', prop: string) => { tempSortType.value = type; tempSortProp.value = prop; } const columnMap = computed(() => { const obj: Record = {} props.columnConfig.forEach(column => { obj[column.prop] = column }) return obj }) const emitSearch = () => { const params: Record = {}; const processFilter = (filters: FilterListType) => { filters .filter((item): item is Exclude => item.type !== 'slot') .forEach((item) => { if (item.type === 'doubleDatePicker' || item.type === 'monthDayPicker') { const [start, end] = item.prop params[start] = filteredValue.value[start]; params[end] = filteredValue.value[end]; } else { params[item.prop] = filteredValue.value[item.prop]; } }); } // 仅提交显示的列的相关数据 showingColumns.value.forEach(prop => { const { filters = [] } = columnMap.value[prop] as IColumnConfig; processFilter(filters); }); if (props.colorFilterConfig) { const { filters = [] } = props.colorFilterConfig; processFilter(filters); } Object.keys(params).forEach(key => { if (params[key] === undefined) delete params[key]; }); emit('search', { ...params, page: 1 }); }; const handleHeaderOperationConfirm = async (column: IColumnConfig) => { const inputFilters = (column.filters ?? []).filter( (item): item is IFilterInput => item.type === 'input' ); const hasValidatorError = inputFilters.some( (item) => item.validator && !item.validator((tempFilteredValue.value[item.prop] as string).trim()) ) if (hasValidatorError) { return } inputFilters.forEach( (item) => { if (!tempFilteredValue.value[item.prop]) tempFilteredValue.value[item.prop] = '' } ) summaryList.value = [...tempSummaryList.value]; sortProp.value = tempSortProp.value || ''; sortType.value = tempSortType.value || null; if (sortProp.value) { // 确认时提交排序 if (props.localSort) { // 恢复列配置的sortable属性,只有列配置的sortable为true,才能用下面的sort方法 inSorting.value = true; await nextTick(); tableDomRef.value?.sort(sortProp.value, sortType.value); inSorting.value = false } else { emit('sort-change', { order: sortType.value, prop: sortProp.value }); } } filteredValue.value = { ...tempFilteredValue.value }; emitSearch(); closeSortAndFilterPopover(); await nextTick() tableDomRef.value?.doLayout(); } const clearSort = () => { sortProp.value = ''; sortType.value = null; if (props.localSort) { // 前端过滤 tableDomRef.value?.clearSort(); } else { // 接口过滤 emit('sort-change', { order: null, prop: '' }); } } const setSort = (params: { order: 'ascending' | 'descending', prop: string }) => { const column = props.columnConfig.find(c => Array.isArray(c.sortable) ? c.sortable.some(v => v.prop === params.prop) : c.prop === params.prop); if (column) { sortProp.value = params.prop sortType.value = params.order; if (props.localSort) { tableDomRef.value?.sort(params.prop, params.order); } } } function handleResetFilterValue(column: IColumnConfig) { (column.filters || []) .filter((item): item is Exclude => item.type !== 'slot') .forEach( (item) => { if (item.type === 'radio' || item.type === 'checkbox') { if (item.defaultValue) { filteredValue.value[item.prop] = item.defaultValue } else { filteredValue.value[item.prop] = item.type === 'radio' ? '' : [] } } else if (item.type === 'doubleDatePicker' || item.type === 'monthDayPicker') { const [start, end] = item.prop filteredValue.value[start] = ''; filteredValue.value[end] = ''; } else { filteredValue.value[item.prop] = '' } } ) } const handleHeaderOperationReset = async (column: IColumnConfig) => { if ( sortProp.value && (Array.isArray(column._sortable) ? column._sortable.some(v => v.prop === sortProp.value) : sortProp.value === column.prop) ) { clearSort(); } // 合计 summaryList.value = summaryList.value.filter(item => item !== column.prop); handleResetFilterValue(column) emitSearch(); closeSortAndFilterPopover(); await nextTick(); tableDomRef.value?.doLayout(); } const setSearchParams = (params: Record) => { const _filteredValue = {}; // 设置搜索和过滤参数时,如果使用 showingColumns 遍历,会导致通过外部设置未显示的列的搜索和过滤参数丢失 [props.colorFilterConfig, ...props.columnConfig].forEach(column => { (column?.filters ?? []) .filter((item): item is Exclude => item.type !== 'slot') .forEach( (item) => { if (item.type === 'doubleDatePicker' || item.type === 'monthDayPicker') { const [start, end] = item.prop _filteredValue[start] = params[start] _filteredValue[end] = params[end] } else if (item.type === 'checkbox') { _filteredValue[item.prop] = params[item.prop] ?? item.defaultValue ?? [] } else { _filteredValue[item.prop] = params[item.prop] ?? '' } } ) }) filteredValue.value = { ...filteredValue.value, ..._filteredValue } } return { setSort, clearSort, setSearchParams, isColumnHeadActive, handleHeaderPopoverShow, handleSort, handleHeaderOperationConfirm, handleHeaderOperationReset, summaryList, tableSummaryMethod, filteredValue, showColumnHeadSortIcon, tempSortProp, tempFilteredValue, tempSummaryList, tempSortType, sortProp, isColumnFiltering, inSorting, } }