【React.js】实现下拉列表多级联动、与优先级添加、删除功能

需求:

1.下拉列表第一级选中的,二级不能再选中,三级不能选中一二级选中的,以此类推

2. 点击添加优先级,至多添加6个优先级

3. 最后一层优先级的删除功能

主要使用技术:React + typescript

思路:根据优先级获取对应的下拉列表数据源,当触发onChange事件的时候,对数据源信息进行筛选

代码范例

·index.tsx

import React, {
  useState,
  useEffect,
  Fragment,
  useImperativeHandle,
} from 'react';
import { Select } from 'antd';
import { useRequest } from 'ahooks';
import { ExclamationCircleFilled } from '@ant-design/icons';
import { dealConfig, dealSelect } from './utils';
import { request } from '../../../utils/request';
import './index.less';
import search from '../search';

const { Option } = Select;

// 数字转化
const SortPanel = React.forwardRef((props: any, ref) => {
  const { searchData } = props;
  // console.log("searchData", searchData)
  const iconStyle = {
    color: '#1658dc',
    height: 11,
    width: 11,
  };
  // 需要展示的 fieldConfigList
  const [fieldConfigList, setFieldConfigList] = useState([]);
  // 下拉列表框数据源 fieldList
  const [fieldList, setFieldList] = useState<any>([]);
  // 下拉列表框 每一个优先级对应的数据源 fieldListObj
  const [fieldListObj, setFieldListObj] = useState<any>({});

  // 添加按钮的 展示隐藏
  const [visible, setIsVisible] = useState(true);

  // 排序的回调,当弹窗点击确定时。当弹窗点击确定时需要发送请求
  useImperativeHandle(ref, () => ({
    submit: () => {
      // console.log('====submit fieldConfigList===', fieldConfigList);
      editReportFieldsConfig.run();
      return true;
    },
  }));
  // 联动,更改 下拉选择列表的数据源
  const changeFieldConfig = (
    fieldConfig: any,
    fieldList: any,
    priority?: number,
  ) => {
    let obj = dealConfig(fieldConfig, fieldList, priority, fieldListObj);
    // console.log('changeFieldConfig setFieldListObj', obj);
    setFieldListObj(obj);
  };

  // 初始化弹框数据回调函数
  const setManualReportFieldsConfig = (data: any) => {
    // console.log("setManualReportFieldsConfig", data)
    // 设置fieldConfigList
    setFieldConfigList(data.fieldConfigList);
    // 设置fieldList
    setFieldList(data.fieldList);
    // 初始化下拉选择数据 处理
    changeFieldConfig(data.fieldConfigList, data.fieldList);
  };

  // 调用接口初始化排序弹框数据
  // const queryManualReportFieldsConfig = request(
  //   {
  //     url: 'queryManualReportFieldsConfig',
  //     source: 'cbooking',
  //     accountId: searchData.accountId,
  //     monthReportType: searchData.monthReportType,
  //     accountPayType: searchData.accountPayType,
  //   },
  //   setManualReportFieldsConfig,
  // );

  const queryManualReportFieldsConfig = useRequest(
    {
      url: '/Front/Cbooking/API/Cbooking/queryManualReportFieldsConfig',
      method: 'GET',
      headers: new Headers({
        'Content-Type': 'application/json',
      }),
    },
    {
      manual: true,
      throwOnError: true,
      onSuccess: (data: any, params: any) => {
        //console.log('queryBatch', data, params);
        // 设置fieldConfigList
        setFieldConfigList(data.fieldConfigList);

        // 设置fieldList
        setFieldList(data.fieldList);
        // 初始化下拉选择数据 处理
        changeFieldConfig(data.fieldConfigList, data.fieldList);
      },
    },
  );
  // 调用接口获取报表字段更新回调函数
  const setEditReportFieldsConfig = (data: any) => {
    console.log('setEditReportFieldsConfig', data);
  };

  // 调用接口获取报表字段更新
  const editReportFieldsConfig = request(
    {
      url: 'editReportFieldsConfig',
      source: 'cbooking',
      accountId: searchData.accountId,
      list: fieldConfigList,
    },
    setEditReportFieldsConfig,
  );

  useEffect(() => {
    queryManualReportFieldsConfig.run();
  }, []);

  // select 下拉列表触发事件,实现多级联动,即前面选中的项目后面不可以再选择
  const fieldListChange = (value: string, priority: any) => {
    // console.log(`selected ${value}`);
    // 拷贝fieldConfigList到新数组
    let newfieldConfigList: any = dealSelect(value, priority, fieldConfigList);
    // 设置configList select后,值需要更新
    setFieldConfigList(newfieldConfigList);
    // 修改configList
    changeFieldConfig(newfieldConfigList, fieldList, priority);
  };

  // 添加优先级
  const addPriority = () => {
    let newFieldConfigList: any = [...fieldConfigList];
    let num: number = newFieldConfigList.length - 1;
    if (newFieldConfigList.length >= 5) {
      setIsVisible(false);
    }
    if (newFieldConfigList.length >= 6) {
      return;
    }
    let newObj: any = { ...newFieldConfigList[num] };
    newObj.priority += 1;
    newObj.fieldName = '';
    newFieldConfigList.push(newObj);
    setFieldConfigList(newFieldConfigList);
    // 联动,下拉选择列表的数据源
    changeFieldConfig(newFieldConfigList, fieldList);
  };

  // 抽离select 组件,附上对应的数据源。
  const SelectCom = (fieldName: any, priority: any) => {
    const fieldListArr = fieldListObj[priority];
    return (
      <Select
        value={fieldName}
        onChange={(val) => fieldListChange(val, priority)}
        className="selectComponent"
      >
        {fieldListArr?.map((item: any, index: number) => {
          return (
            <Option value={item.fieldName} key={`${item.fieldName}-${index}`}>
              {item.fieldNameCn}
            </Option>
          );
        })}
      </Select>
    );
  };

  // 删除 list
  const deleteList = (index: number) => {
    let newFieldConfigList: any = [...fieldConfigList];
    // 删除操作
    newFieldConfigList.splice(index, 1);
    // 更新fieldConfigList
    setFieldConfigList(newFieldConfigList);
    // 展示添加优先级按钮
    if (newFieldConfigList.length <= 5) {
      setIsVisible(true);
    }
  };

  return (
    <Fragment>
      <div className="warning">
        <i style={iconStyle}>
          <ExclamationCircleFilled />
        </i>
        &nbsp; 至多添加6个优先级
      </div>
      {fieldConfigList.map((fieldConfigListItem: any, idx: number) => {
        return (
          <div className="fieldList" key={fieldConfigListItem.priority}>
            <span className="priority">
              第{fieldConfigListItem.priority}优先级
            </span>
            {SelectCom(
              fieldConfigListItem.fieldName,
              fieldConfigListItem.priority,
            )}
            {idx == fieldConfigList.length - 1 &&
            fieldConfigList.length !== 1 ? (
              <span className="delete" onClick={() => deleteList(idx)}>
                删除
              </span>
            ) : (
              ''
            )}
          </div>
        );
      })}
      {visible ? (
        <p className="addPriority" onClick={() => addPriority()}>
          +添加优先级
        </p>
      ) : (
        ''
      )}
    </Fragment>
  );
});

export default SortPanel;

util.ts

export const dealConfig = (
  fieldConfigList: any,
  fieldList: any,
  curPriority?: number,
  curSelectObj?: any,
) => {
  const selectObj: any = curSelectObj ? { ...curSelectObj } : {};
  const fieldNameArr: any = [];
  fieldConfigList.forEach((config: any) => {
    let priority = config.priority;
    // 把前面优先级的 选择项保存下来
    if (priority == 1) {
      selectObj[priority] = fieldList;
    } else {
      // on select change,比优先级低的selectList发生变化
      if (!curPriority || (curPriority && priority > curPriority))
        selectObj[priority] = addPriority(fieldNameArr, fieldList);
    }
    fieldNameArr.push(config.fieldName);
    // 新增操作时
    let len = fieldNameArr.length;
    if (priority && priority == len && fieldNameArr[len - 1] == '') {
      selectObj[priority] = addPriority(fieldNameArr, fieldList);
    }
  });
  return selectObj;
};

// 过滤已选则的filedList
export const addPriority = (fieldNameArr: any, fieldList: any) => {
  let newFieldList = [...fieldList];
  newFieldList = newFieldList.filter((item: any, index: number) => {
    return fieldNameArr.indexOf(item.fieldName) == -1;
  });
  return newFieldList;
};

export const dealSelect = (
  fieldName: string,
  priority: number,
  fieldConfigList: any,
) => {
  if (fieldConfigList && fieldConfigList.length == 0) return [];
  let newfieldConfigList = [...fieldConfigList];
  newfieldConfigList.forEach((item: any, index: number) => {
    if (priority == item.priority) item.fieldName = fieldName;
  });
  // 若前面优先级选择了后面的fieldName,那么后面优先级的fieldName置为空
  let len = newfieldConfigList.length;
  for (let i = 0; i < len; i++) {
    // 从左到右
    for (let j = len - 1; j < len && j > i; j--) {
      // 从右到左
      if (newfieldConfigList[i].fieldName == newfieldConfigList[j].fieldName) {
        // todo list 这里虽然fieldName置为空了,但是select上还有默认显示的原始值
        newfieldConfigList[j].fieldName = '';
      }
    }
  }
  //   console.log('newfieldConfigList', newfieldConfigList);
  return newfieldConfigList;
};

index.less

.warning {
  color: #4d5580;
  font-family: 'PingFang SC';
  font-size: 14px;
  font-weight: normal;
  height: 22px;
  padding-bottom: 50px;
}

.fieldList {
  margin-bottom: 20px;
  .priority {
    color: #1b234d;
    font-family: 'PingFang SC';
    font-size: 14px;
    font-weight: 500;
    height: 22px;
    width: 70px;
  }

  .selectComponent {
    background: #ffffff;
    border-radius: 1px 1px 1px 1px;
    border: 1px solid #ebebf5;
    height: 32px;
    width: 250px;
    margin-left: 14px;
  }

  .delete {
    color: #3d6af2;
    font-family: 'PingFang SC';
    font-size: 12px;
    font-weight: normal;
    height: 20px;
    letter-spacing: px;
    width: 24px;
    cursor: pointer;
    padding-left: 8px;
  }
}
.addPriority {
  color: #3d6af2;
  font-family: 'PingFang SC';
  font-size: 12px;
  font-weight: normal;
  height: 20px;
  margin: 25px 0 26px 0;
  cursor: pointer;
}

数据模拟:

let queryManualReportFieldsConfig = {
  ResponseStatus: {
    Timestamp: '/Date(1623831946825+0800)/',
    Ack: 'Success',
    Errors: [],
    Extension: [],
  },
  responseCode: 20000,
  responseDesc: 'Success',
  fieldConfigList: [
    {
      accountId: 3558,
      department: 'flight',
      priority: 1,
      fieldName: 'PassengerName',
    },
    {
      accountId: 3558,
      department: 'flight',
      priority: 2,
      fieldName: 'OrderID',
    },
    {
      accountId: 3558,
      department: 'flight',
      priority: 3,
      fieldName: 'Sequence',
    },
  ],
  fieldList: [
    {
      fieldName: 'OrderID',
      fieldNameCn: '订单号',
    },
    {
      fieldName: 'PassengerName',
      fieldNameCn: '乘机人',
    },
    {
      fieldName: 'Sequence',
      fieldNameCn: '航段号',
    },
    {
      fieldName: 'Dept1',
      fieldNameCn: '部门1',
    },
    {
      fieldName: 'Dept2',
      fieldNameCn: '部门2',
    },
    {
      fieldName: 'Dept3',
      fieldNameCn: '部门3',
    },
    {
      fieldName: 'Dept4',
      fieldNameCn: '部门4',
    },
    {
      fieldName: 'Dept5',
      fieldNameCn: '部门5',
    },
    {
      fieldName: 'Dept6',
      fieldNameCn: '部门6',
    },
    {
      fieldName: 'CostCenter',
      fieldNameCn: '成本中心1',
    },
    {
      fieldName: 'CostCenter2',
      fieldNameCn: '成本中心2',
    },
    {
      fieldName: 'CostCenter3',
      fieldNameCn: '成本中心3',
    },
    {
      fieldName: 'CostCenter4',
      fieldNameCn: '成本中心4',
    },
    {
      fieldName: 'CostCenter5',
      fieldNameCn: '成本中心5',
    },
    {
      fieldName: 'CostCenter6',
      fieldNameCn: '成本中心6',
    },
  ],
};

export default {
  'GET /Front/Cbooking/API/Cbooking/queryManualReportFieldsConfig': queryManualReportFieldsConfig,
};

 

 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值