antd-ProTable后台系统怎么抽离公共查询条件

388 阅读5分钟

公司后台使用 antd-ProTable 搭建,几乎每个页面有相同的查询条件,于是想抽出公共部分查询条件配置,省的一通复制。

use_common_1.png

以上是系统常用的通用条件。

逻辑每次会有微妙区别 antd_search1.png

说说逻辑:

  • 学校是单选,可搜索过滤,默认是第一个值
  • 部门是单选,默认值可能是中学,也可能是第一个值
  • 学校决定部门,请求学校的接口返回的是 [{label:'北京学校',value:1,children:[label:'中学',value:1]}],学校切换的时候,需要修改部门列表的选项和默认值
  • 年级是多选,默认值是全部,学校和部门共同决定年级,切换学校或者部门需请求接口,返回的是[label:'一年级',value:1]}
  • 科目是多选,默认值是全部,同学校接口,返回的是[label:'语文',value:1]}
  • 年级和科目,有全部互斥的逻辑:点击选择,如果选择全部的话,只有全部;如果选择不是全部,那么有全部的话,直接去掉全部
  • 查询条件不一定有部门,不一定有年级,不一定有科目,所以灵活返回
  • 既然几乎每个页面都有,就把返回学校科目的接口,在initialState处理

使用思维导图来梳理实现:

useSearchBaseColumns.png

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

效果:

use_common_search.gif

抛砖引玉,如果有类似需求的,可以参考(^▽^)