移动端 react + antd-mobile 二级联动(一二级均多选)

前提:数据结构为树形结构(二级数据带一级ID)

功能说明:默认展示一级下拉菜单,选完一级之后,二级的列表数据为选好的所有一级的二级数据,最后点确定之后,展示全部的选择(包括一级和二级)。展示出来的部分可以删除及全部删除。

!!!注意注意:选了一级,没选择二级,会在最后点确定的时候带出全部的二级,如果这个一级下面选了二级,最后只会展示已选择的二级

你需要准备的变量
  const [cascadeData, setCascadeData] = useState([] as any[]);// 品类的全部数据
  const [cascadeId, setCascadeId] = useState([] as string[]);// 选定的全部品类
  const [cascadeVisible, setCascadeVisible] = useState(false);// 一级品类
  const [secondVisible, setSecondVisible] = useState(false);// 二级品类
  const [secondLevel, setSecondLevel] = useState([]as any[]);// 二级列表
  const [selectCategory, setSelectCategory] = useState([] as any[]);// 选定的全部品类对象
  const [selectedFirstObj, setSelectedFirstObj] = useState([] as any[]);// 选定的一级品类
  const [selectedFirstId, setSelectedFirstId] = useState([] as any[]);// 选定的一级品类id
  const [selectedSecondObj, setSelectedSecondObj] = useState([] as any[]);// 选定的二级品类
  const [selectedSecondId, setSelectedSecondId] = useState([] as any[]);// 选定的二级品类id
页面结构
 <Space>
	<div
	  onClick={() => {
	    setCascadeVisible(true);
	  }}
	  className={style.cascadeBtn}
	>
	  {selectedFirstId.length ? 
	    `已选${selectedFirstId.length}` : '一级品类'} <DownOutline />
	  {/* 一级品类  */}
	</div>
	{!!secondLevel.length && 
	  <div
	    onClick={() => {
	      setSecondVisible(true);
	    }}
	    className={style.cascadeBtn}
	  >
	    {selectedSecondId.length ? 
	      `已选${selectedSecondId.length}` : '二级品类'} <DownOutline />
	    {/* 二级品类  */}
	  </div>}
	{!!secondLevel.length && 
	  <Button
	    color="primary"
	    size="mini"
	    onClick={() => handlerSelectLevel()}
	  >确定
	  </Button>}
</Space>


<span className={`${style.cardLabel} ${style.cardLabelTitle}`}>
 筛选结果
</span>          
{!!cascadeId.length &&
 <div className={style.categoryItem}>
    <span className={style.cardLabel}>已选品类:</span>
    <Space wrap className={style.categorySpace}>             
      {selectCategory.map((item)=>(
        <Tag className={style.categoryTag} key={item.categoryId}>
          {/*  */}
          <img 
            src={imgDelete} 
            alt="" 
            className={style.categoryTagClose} 
            onClick={() => onClose(item.categoryId)}
          />
          {item.categoryName}
        </Tag>
      ))}
      <Button size="mini" color="primary" onClick={clearCategory}>全部取消</Button>
    </Space>
  </div>}
  
两个弹窗
<Mask
visible={cascadeVisible}
>
  <div className={style.panel}>
    <div className={`${style.content} ${style.columns}`}>
      <div className={style.checkList}>
        <CheckList onChange={setSelectedFirstObj} multiple value={selectedFirstObj}>
          { cascadeData.map((category: any) => (
            <CheckList.Item 
              key={category.categoryId} 
              value={category}
            >
              {category.categoryName}
            </CheckList.Item>
          ))}
        </CheckList>
      </div>
    </div>
    <div className={style.btns}>
      <div onClick={handlerLevelCancel} className={style.cancelBtn}>
        取消
      </div>
      <div onClick={handlerSelectFirst} className={style.confirmBtn}>确定</div>
    </div>
  </div>
</Mask>

<Mask
  visible={secondVisible}
>
  <div className={style.panel}>
    <div className={`${style.content} ${style.columns}`}>
      <div className={style.checkList}>
        <CheckList onChange={setSelectedSecondObj} multiple value={selectedSecondObj}>
          { secondLevel.map((item: any) => (
            <CheckList.Item 
              key={item.categoryId} 
              value={item}
            >
              {item.categoryName}
            </CheckList.Item>
          ))}
        </CheckList>
      </div> 
    </div>
    <div className={style.btns}>
      <div onClick={handlerLevelCancel} className={style.cancelBtn}>
        取消
      </div>
      <div onClick={handlerSelectSecond} className={style.confirmBtn}>确定</div>
    </div>
  </div>
</Mask>
方法
// 关闭品类弹窗
  const handlerLevelCancel = () => {
    setCascadeVisible(false);
    setSecondVisible(false);
  };
  // 一级品类确定
  const handlerSelectFirst = () => {
    console.log('一级品类,setSelectedFirstObj',selectedFirstObj);
    const secondData: React.SetStateAction<any[]> = [];
    const firstId: string[] = [];
    selectedFirstObj.forEach(item => {
      firstId.push(item.categoryId);
      secondData.push(...item.childCategoryList);
    });
    console.log('secondData==>',secondData);
    setSelectedFirstId(firstId);// 已选一级id
    setSecondLevel(secondData);
    handlerLevelCancel();
  };

  // 二级品类确定
  const handlerSelectSecond = () => {
    console.log('一级品类,setSelectedSecondObj',selectedSecondObj);
    const secondIds = 
    selectedSecondObj.length ? selectedSecondObj.map((item) => item.categoryId) : [];
    setSelectedSecondId(secondIds);// 已选二级id
    handlerLevelCancel();
  };

  // 全部品类的id
  const handlerSelectLevel = () => {
    console.log(selectedFirstObj);
    console.log(selectedSecondObj);
    console.log(secondLevel);
    let firstIds: any[] = [];
    let secondIds: any[] = [];
    let childObj: any[] = [];
    if (selectedFirstObj.length) {
      firstIds = selectedFirstObj.map(item=>item.categoryId);

      if (selectedSecondObj.length) {
        console.log('firstIds==>', firstIds);
        const pIds = selectedSecondObj.map(item=>item.parentCategoryId);

        childObj = secondLevel.filter(item=>!pIds.includes(item.parentCategoryId));
        console.log('childObj===>',childObj);
      } else {
        childObj = secondLevel;
      }
      
      secondIds = [...selectedSecondObj, ...childObj].map(item=>item.categoryId);
      console.log('secondIds===>',secondIds);
      setSelectedFirstId(firstIds);// 已选一级id
      setSelectedSecondId(secondIds);// 已选二级id
  
      setCascadeId([...firstIds, ...secondIds]);// 全部id
      setSelectCategory([...selectedFirstObj, ...selectedSecondObj, ...childObj]);// 全部的品类
      console.log(selectCategory);
    } else {
      Toast.show('请选择品类');
    }
  };
  // 去掉已选品类
  const onClose = (id:string) => {
    const newCategoryId = cascadeId.filter(item=>item !== id);
    const newSelectedFirstObj = selectedFirstObj.filter(item => item.categoryId !== id);
    const firstIds = selectedFirstId.filter(item => item !== id);
    const newSelectedSecondObj = selectedSecondObj.filter(item => item.categoryId !== id);
    const secondIds = selectedSecondId.filter(item => item !== id);
    const newCategoryObj = selectCategory.filter(item => item.categoryId !== id);
    setCascadeId(newCategoryId);// 全部id
    setSelectedFirstObj(newSelectedFirstObj);// 一级对象
    setSelectedFirstId(firstIds);// 已选一级id
    setSelectedSecondObj(newSelectedSecondObj);// 二级对象
    setSelectedSecondId(secondIds);// 已选二级id
    setSelectCategory(newCategoryObj);// 全部的品类
  };

  // 取消选择的所有品类
  const clearCategory = () => {
    setCascadeId([]);// 全部id
    setSelectedFirstObj([]);// 一级对象
    setSelectedFirstId([]);// 已选一级id
    setSelectedSecondObj([]);// 二级对象
    setSelectedSecondId([]);// 已选二级id
    
    setSelectCategory([]);// 全部的品类
    setSecondLevel([]);// 全部二级
  };
  // 反显已选的品类
  const categoryShow = () => {
    if ( cascadeId && cascadeId.length && cascadeData && cascadeData.length) {      
      const seLevel: any[] = [];
      let seIds: any[] = [...cascadeId];
      const firstObj: any[] = [];
      const firstId: string[] = [];
      const secondObj: React.SetStateAction<any[]> = [];

      cascadeData.forEach((item:any) => {
        if (cascadeId.includes(item.categoryId)) {
          firstId.push(item.categoryId);
          firstObj.push(item);
          seLevel.push(...item.childCategoryList);
          seIds = seIds.filter(seIns=>seIns !== item.categoryId);
        }
      });
      
      seLevel.forEach((item:any) => {
        if (seIds.includes(item.categoryId)) {
          secondObj.push(item);
        }
      });
      setSelectedFirstObj(firstObj);// 已选一级
      setSelectedFirstId(firstId);// 已选一级id
      setSecondLevel(seLevel);// 全部二级
      setSelectedSecondObj(secondObj);// 已选二级
      setSelectedSecondId(seIds);// 已选二级id
      setSelectCategory([...firstObj,...secondObj]);// 已选全部
    }
  };

移动端 react + antd-mobile 二级联动(一二级均多选)_第1张图片

你可能感兴趣的:(react,antd-mobile,react.js,javascript,前端)