公司后台使用 antd-ProTable 搭建,几乎每个页面有相同的查询条件,于是想抽出公共部分查询条件配置,省的一通复制。
以上是系统常用的通用条件。
逻辑每次会有微妙区别
说说逻辑:
- 学校是单选,可搜索过滤,默认是第一个值
- 部门是单选,默认值可能是中学,也可能是第一个值
- 学校决定部门,请求学校的接口返回的是
[{label:'北京学校',value:1,children:[label:'中学',value:1]}]
,学校切换的时候,需要修改部门列表的选项和默认值 - 年级是多选,默认值是全部,学校和部门共同决定年级,切换学校或者部门需请求接口,返回的是
[label:'一年级',value:1]}
- 科目是多选,默认值是全部,同学校接口,返回的是
[label:'语文',value:1]}
- 年级和科目,有全部互斥的逻辑:点击选择,如果选择全部的话,只有全部;如果选择不是全部,那么有全部的话,直接去掉全部
- 查询条件不一定有部门,不一定有年级,不一定有科目,所以灵活返回
- 既然几乎每个页面都有,就把返回学校科目的接口,在
initialState
处理
使用思维导图来梳理实现:
import { useEffect, useRef, useState } from 'react';
import { useModel } from 'umi';
import { apiGradeOptions } from '@/services/apiBaseOptions';
import { useRequest } from 'ahooks';
import dayjs from 'dayjs';
const SUBJECT_ALL = '';
const GRADE_ALL = 0;
export const DEPT = {
KID: '35',
MIDDLE: '36',
HIGH_CLASS: '37',
HIGH_ONE_ON_ONE: '38',
};
type IParams = {
priorityDeptValue?: string;
// 部分页面跳转的时候,需要带上一个页面的学校部门年级科目等
formValues?: object;
};
const defaultValues: IParams = {
// 优先部门值,多数没有优先部门,如果有,优先显示
priorityDeptValue: '',
};
export function useSearchBaseColumns(params: IParams = defaultValues) {
const {
priorityDeptValue
formValues,
} = { ...defaultValues, ...params };
// !至关重要的一点,formRef,使用该hook的时候,将其绑定到ProTable上,从而让这里的赋值生效
// !至关重要的一点,formRef,使用该hook的时候,将其绑定到ProTable上,从而让这里的赋值生效
// !至关重要的一点,formRef,使用该hook的时候,将其绑定到ProTable上,从而让这里的赋值生效
const formRef = useRef<any>(null);
const { initialState } = useModel('@@initialState');
const baseOptions = initialState?.baseOptions;
const { schoolList = [], subjectList = [] } = baseOptions || {};
// 这里跳部分页面的时候,有时候需要默认值,这里设置默认值
useEffect(() => {
if (!formValues) return;
formRef.current?.setFieldsValue(formValues);
// 这里需要手动提交一次,因为setFieldsValue不会触发onFinish
formRef.current?.submit()
}, [formValues]);
const schoolInitValue = schoolList[0]?.value;
// 部门的选项由学校决定
const [deptList, setDeptList] = useState(
schoolList.find((item) => item.value === schoolInitValue)?.deptList
);
// 部门的默认值,看有没有设置,如果不设置默认是第一个,否则按照设置的来
const deptInitValue = getDefaultDeptCode(deptList, priorityDeptValue);
// 科目是多选,所以初始值是数组
const subjectInitValue = [subjectList[0]?.value];
// 年级是由 学校和部门 请求接口决定的,选项一开始是空数组
const [gradeList, setGradeList] = useState<
{ label: string; value: number }[]
>([]);
// 年级的默认值 接口还没回来 先设置空数组
const [gradeInitValue, setGradeInitValue] = [];
// 设置请求年级的接口 使用ahooks,官网https://ahooks-v2.js.org/zh-CN/hooks/async
const { run: requestGradeOptions }: { run: any } = useRequest(
apiGradeOptions,
{
// @ts-ignore
defaultParams: { schoolId: schoolInitValue, deptCode: deptInitValue },
// @ts-ignore
onSuccess: (result: { label: string; value: number }[]) => {
// 接口回来之后,手动加上全部
if (result.length > 0) {
result.unshift({ label: '全部', value: GRADE_ALL });
}
// 设置年级选项
setGradeList(result);
// 设置年级初始值
setGradeInitValue([result[0]?.value]);
// 后期每次请求需要设置值
formRef.current?.setFieldsValue({ gradeCodes: [result[0]?.value] });
},
}
);
// column定义官网 https://pro-components.antdigital.dev/components/table#columns-%E5%88%97%E5%AE%9A%E4%B9%89
const schoolColumnSearch = {
title: '学校',
dataIndex: 'schoolId',
// 单选下拉框
valueType: 'select',
fieldProps: {
// 不允许清空
allowClear: false,
// 可搜索
showSearch: true,
placeholder: '请选择学校',
// schoolList放这里
options: schoolList,
// 学校变化
onChange: (value: number) => {
const deptList = schoolList.find(
(item) => item.value === value
)?.deptList;
// 设置部门选项
setDeptList(deptList || []);
const deptCode = getDefaultDeptCode(deptList, priorityDeptValue);
// 设置部门的值
formRef.current?.setFieldValue('deptCode', deptCode);
// 请求年级的接口
requestGradeOptions({
schoolId: value,
deptCode: deptCode,
});
},
},
initialValue: schoolInitValue,
hideInTable: true,
};
const deptColumnSearch = {
title: '部门',
dataIndex: 'deptCode',
// 单选下拉框
valueType: 'select',
fieldProps: {
// 不允许清空
allowClear: false,
placeholder: '请选择部门',
options: deptList,
onChange: (value: string) => {
// 部门变化的时候,请求年级接口
requestGradeOptions({
schoolId: formRef.current?.getFieldValue('schoolId'),
deptCode: value,
});
},
},
initialValue: deptInitValue,
hideInTable: true,
};
const gradeColumnSearch = {
title: '年级',
dataIndex: 'gradeCodes',
// 多选下拉框
valueType: 'select',
fieldProps: {
// 多选
mode: 'multiple',
// 不允许搜索
showSearch: false,
// 不允许清空
allowClear: false,
placeholder: '请选择年级(多选)',
options: gradeList,
onChange: (value: number[]) => {
// 年级变化的时候,有全部互斥的逻辑,所以需要这里处理下
const valueHandledAll = setMultipleSelectValue(value, GRADE_ALL);
formRef.current?.setFieldsValue({ gradeCodes: valueHandledAll });
},
},
initialValue: gradeInitValue,
hideInTable: true,
};
const subjectColumnSearch = {
title: '科目',
dataIndex: 'subjectCodes',
// 多选下拉框
valueType: 'select',
fieldProps: {
// 多选
mode: 'multiple',
// 不允许搜索
showSearch: false,
// 不允许清空
allowClear: false,
placeholder: '请选择科目(多选)',
options: subjectList,
onChange: (value: string[]) => {
// 科目也同样是处理全部互斥的逻辑
const valueHandledAll = setMultipleSelectValue(value, SUBJECT_ALL);
formRef.current?.setFieldsValue({ subjectCodes: valueHandledAll });
},
},
initialValue: subjectInitValue,
hideInTable: true,
};
return {
formRef,
schoolColumnSearch,
deptColumnSearch,
gradeColumnSearch,
subjectColumnSearch,
};
}
// deptList如果有值是'36',那么返回'36',否则返回deptList[0]?.value
function getDefaultDeptCode(
deptList: { label: string; value: string }[],
priorityDeptValue?: string
) {
if (!priorityDeptValue) {
return deptList[0]?.value;
}
// const MIDDLE = '36'
const hasPriorityDept = deptList.some(
(item) => item.value === priorityDeptValue
);
return hasPriorityDept ? priorityDeptValue : deptList[0]?.value;
}
/**
* 当前选中的值,如果有all值,且all值不在最后,删除all值,否则只保留all值。
* 没有all值,直接返回
* @param checkedValue
* @param valueAll
* @returns
*/
function setMultipleSelectValue(
checkedValue: string[] | number[],
valueAll: string | number
) {
/** 本次的全部选中项 */
let value = [...checkedValue];
if (value.length > 0) {
/** all值的位置 */
const hasAll = value.includes(valueAll);
// 如果没有all值,直接返回
if (!hasAll) {
return value;
}
// 有all值,如果all值在最后,直接返回[all],否则删除all值
const allIndex = value.indexOf(valueAll);
const allIsInLast = allIndex === value.length - 1;
if (allIsInLast) {
value = [valueAll];
return value;
}
value = value.filter((item) => item !== valueAll);
return value;
}
return value;
}
使用的时候,就狠狠方便了!
使用的时候,就狠狠方便了!
使用的时候,就狠狠方便了!
import { ProTable } from "@ant-design/pro-components"
import React from "react"
import { useSearchBaseColumns } from '@/hooks/useSearchBaseColumns'
type DemoTableProps = {}
const DemoTable: React.FC<DemoTableProps> = () => {
const { formRef, schoolColumnSearch, deptColumnSearch, gradeColumnSearch, subjectColumnSearch } = useSearchBaseColumns()
const columns = [
schoolColumnSearch,
deptColumnSearch,
gradeColumnSearch,
subjectColumnSearch,
// 其他列
]
return <ProTable formRef={formRef} columns={columns} /* 其他属性。。 *//>
}
DemoTable.displayName = "DemoTable"
export default DemoTable
效果:
抛砖引玉,如果有类似需求的,可以参考(^▽^)