Files
shanxia2.0/web-admin/src/composables/useColumnFacetOptions.ts
T
2026-10-15 08:50:25 +08:00

94 lines
3.5 KiB
TypeScript

/**
* 列头动态 facet(搜选项 + 勾选)选项加载。
* 与 useServerColumnFilters 配合:defs 中目标列 type=enum,打开漏斗时拉去重可选值。
* 可选 getContextParams:附带列表上下文(固定条件 + 顶部筛选 + 列头),并排除本列条件(Excel 级联)。
*/
import { reactive } from 'vue'
import { ElMessage } from 'element-plus'
import type { ColumnFilterDef } from '@/composables/useClientColumnFilters'
import { stripSelfColumnParams } from '@/composables/columnFacetContext'
export type FacetOption = { value: string; label: string; count: number }
export function useColumnFacetOptions(options: {
/** 需要动态拉可选值的列 prop(与 defs.prop 一致,亦作为 field 传给接口) */
facetFields: string[]
/** 请求可选值;应返回 { list: FacetOption[] } 或已解包的 list 结构 */
fetchFacet: (payload: {
field: string
keyword?: string
limit?: number
[key: string]: unknown
}) => Promise<unknown>
/** 非 facet 的静态 enum(字典等)回退;亦用于剥离本列 query 键 */
columnFilterDefs?: ColumnFilterDef[]
/** 当前列表查询上下文(固定条件 + 顶部筛选 + 列头);打开漏斗时会排除本列键 */
getContextParams?: () => Record<string, unknown>
limit?: number
}) {
const facetFieldSet = new Set(options.facetFields)
const facetOptionsMap = reactive<Record<string, FacetOption[]>>({})
const facetLoadingMap = reactive<Record<string, boolean>>({})
const limit = options.limit ?? 500
function enumOptionsFor(prop: string): FacetOption[] {
if (Object.prototype.hasOwnProperty.call(facetOptionsMap, prop)) {
return facetOptionsMap[prop]
}
const def = options.columnFilterDefs?.find((d) => d.prop === prop)
return (def?.enumOptions || []).map((o) => ({
value: String(o.value),
label: o.label,
count: (o as any).count ?? 0
}))
}
function optionsLoadingFor(prop: string) {
return Boolean(facetLoadingMap[prop])
}
async function onFilterOpen(prop: string) {
if (!facetFieldSet.has(prop)) return
facetLoadingMap[prop] = true
// 上下文(含审批 scope)变化时避免短暂展示上一轮可选值
facetOptionsMap[prop] = []
try {
const rawContext = options.getContextParams?.() || {}
const context = options.columnFilterDefs?.length
? stripSelfColumnParams(rawContext, prop, options.columnFilterDefs)
: { ...rawContext }
// 排序与导出无关字段不传给 facet
delete context.sortField
delete context.sortOrder
delete context.includeDocumentAttachments
delete context.includeArrangeCopyFlags
delete context.exportFormat
delete context.page
delete context.pageSize
const res = await options.fetchFacet({ ...context, field: prop, limit })
const data = (res as any)?.list ? res : (res as any)?.data || res || {}
const list = (data as any).list || []
facetOptionsMap[prop] = (Array.isArray(list) ? list : []).map((item: any) => ({
value: String(item?.value ?? ''),
label: String(item?.label ?? item?.value ?? ''),
count: Number(item?.count ?? 0)
}))
} catch (e: any) {
facetOptionsMap[prop] = []
ElMessage.error(e?.message || '加载可选值失败')
} finally {
facetLoadingMap[prop] = false
}
}
return {
facetOptionsMap,
facetLoadingMap,
enumOptionsFor,
optionsLoadingFor,
onFilterOpen,
isFacetField: (prop: string) => facetFieldSet.has(prop)
}
}