移动端antd-mobile React如何手写无限层级折叠面板

废话不说,先上效果图,如果有下层文件就会展示右边的下拉框,如果没有就不会显示,点击文件夹跳转到只会显示子文件夹的页面(因为想让大家跟我一起成长,所以讲解的很细,都是自己的心得)

( 一共两版 第一版-获取所有数据来进行无限层级编写 未改良版本 第二版是用当前层级id来通过接口获取下级文件目录,改良版本)

获取所有数据的版本 👇

由于在antd-mobile中会出现一个折叠面板,但是官方没有给我们相关的无限层级,只有一层,那么只有我们纯手写了。

由于是无限包组件

单层是这样的:

<Collapse>

<Collapse.Panel>

123

</Collapse.Panel>

</Collapse>

多层:

<Collapse>

<Collapse.Panel>

<Collapse>

<Collapse.Panel>

...一直循环下去

</Collapse.Panel>

</Collapse>

</Collapse.Panel>

</Collapse>

但是由于自带了antd中的css 所以我们还要改写下公共组件样式 ,废话不多说,直接上关键代码:

js循环关键代码:

//这边假数据直接无视给你们看一下我用的结构

如果后端返回的数据不是这个结构,我在这边就是采用了map来形成一个新数组来处理后端的数据结构,来让下层数据的parent_id等于上一层数据的文件id,并且由于后端没有给文件夹和文件排序,我通过数据的size来判断是否为文件还是文件夹(转化后端数据为tree结构并且进行通过文件大小来排序文件夹在前文件在后的顺序)

第一个参数为数组,第二个为当前文件的id来进行和下层文件的parent_id来对比


  //转化为tree结构
  const buildTree = (arr, fid) => {
    let tree = []

    arr?.map((item) => {
      if (item.parent_id === fid) {
        item.children = buildTree(arr, item.fid)
        tree.push({
          name: item.name,
          parent_id: item.parent_id,
          children: item.children,
          fid: item.fid,
          size: item.size,
        })
      }
    })
    console.log(tree, 'tree')

    for (var i = 0; i < tree.length - 1; i++) {
      //每一轮比较要比多少次
      for (var j = 0; j < tree.length - 1 - i; j++) {
        //如果第一个比第二个大,就交换他们两个位置
        if (tree[j].size > tree[j + 1].size) {
          var temp = tree[j]
          tree[j] = tree[j + 1]
          tree[j + 1] = temp
        }
      }
    }

    return tree
  }

//这里很关键,刚开始我也漏掉了,循环的时候如果有子层级在最里面的children外面还要包一个collapse,不然会显示空值,因为每层外面都要包一个collapse

这边我路由传参了文件的parent_id和文件id,另起一个page可以获取参数来进行无限层级的文件夹跳转,另一个page中要useEffect依赖项中要加入路由中的参数(另起的page与首页同理,只不过获取接口参数变成点击的文件的参数id),这边为了让小白更好的看懂我讲解下:

(1) 判断是否有children,如果有,再判断当前item的文件id是否等于现在文件的父文件id如果是,就给他一个没有打开的文件icon,表示还在第一级,如果不一样,表示已经到了父文件的下一级文件,就显示打开的icon,并且再次循环这个函数 ,在title中加入路由跳转是因为我这边的功能是点击文件夹跳转,点击右边的箭头就下拉

(2)如果没有children,判断当前文件是文件夹还是文件,如果是文件夹,size为0,就继续跳转到下一级文件夹的页面,不需要循环

(3)如果size不为0,就表示当前为当前级的文件,跳转到金山文档展示页面进行解析,这个我会在其他博客中写出


import { FolderFilled, FolderOpenFilled } from '@ant-design/icons'
import { Collapse } from 'antd-mobile'
import { UnorderedListOutline } from 'antd-mobile-icons'  
//无限层级
  const moreLevel = (arr, id) => {
    return arr?.map((item, index) => {
      if (item?.children?.length > 0) {
        console.log(item.children.length, item, '123')
        return (
          <Collapse accordion>
            <Collapse.Panel
              key={index}
              title={
                <div
                  onClick={() => {
                    history.push(
                      `/mobile/nextFolder/${item.parent_id}/${item.fid}`
                    )
                  }}
                >
                  {item.parent_id == fid ? (
                    <FolderFilled
                      style={{ color: '#6BB9FF' }}
                      className={styles.singleFolderImg}
                    />
                  ) : (
                    <FolderOpenFilled
                      style={{ color: '#6BB9FF' }}
                      className={styles.singleFolderImg}
                    />
                  )}
                  {item.name}
                </div>
              }
            >
              {moreLevel(item.children)}
            </Collapse.Panel>
          </Collapse>
        )
      } else {
        if (item.size === '0') {
          return (
            <div
              className={styles.singleFolder}
              onClick={() => {
                history.push(`/mobile/nextFolder/${item.parent_id}/${item.fid}`)
              }}
            >
              {' '}
              {item.parent_id == fid ? (
                <FolderFilled
                  style={{ color: '#6BB9FF' }}
                  className={styles.singleFolderImg}
                />
              ) : (
                <FolderOpenFilled
                  className={styles.singleFolderImg}
                  style={{ color: '#6BB9FF' }}
                />
              )}
              {item.name}
            </div>
          )
        } else {
          return (
            <div
              className={styles.singleFolder}
              onClick={() => {
                history.push(`/mobile/viewPage/${item.parent_id}/${item.fid}`)
              }}
            >
              <img
                className={styles.singleFolderImg}
                alt="example"
                  //这边用的是判断文件名的后缀来个他添加不同的图片,这个组件会在另一个博客中写出
                src={iconList[Distinguish.typeSeparate(item.name) - 1]}
              />{' '}
              {/* <MailFill /> */}
              {item.name}
            </div>
          )
        }
      }
    })
  }

css代码,如果不修改公共样式,无限层级的组件上下级会出现空隙,不好看(其中还有些是其他需求改的文字什么的,个别可以更改,看自身需求):

获取当级目录的改良后版本 👇

效果图:

首先跟获取所有目录版本不一样的地方是在循环函数之前加一个获取下级目录的函数

//获取子文件接口方法
const DynamicContent = (ids, curId) => {

  const { id } = ids
  const { currentId } = curId
//这个是循环传入函数的当前文件id通过当前id来获取下级目录
//curId是来给函数判断下级目录是否要给他展示成打开文件的icon
  const [finished, setFinished] = useState(false)
  const [nextDate, setNextDate] = useState([])
  const [nextDateFile, setNextDateFile] = useState([])

  useEffect(() => {
    const loadData = async () => {
      const detail = await getNextFolder(id)
  
      setFinished(true)
//这里由于后端穿得文件夹和文件分开了 所以我在下面要进行给他一个合并
      setNextDate(detail?.dir_list)
      setNextDateFile(detail?.file_list)
    }

    loadData()
  }, [])

  return finished ? (
//进行合并并传入当前点击id来显示下级目录不一样的展示
    moreLevel(nextDate?.concat(nextDateFile), currentId)
  ) : (
//增加了动态加载效果
    <DotLoading />
  )
}
改良版的转化tree接口增加了hasChild来判断是否有权限(需求不同写法不同)

  //转化为tree结构

  const buildTree = (arr, parent_id) => {
    let tree = []

    arr?.map((item) => {
      if (item?.parent_id === parent_id) {
        item.children = buildTree(arr, item.file_id)
        tree.push({
          name: item.name,
          parent_id: item.parent_id,
          children: item.children,
          file_id: item.file_id,
          size: item.size,
          wiki_name: item.wiki_name,
       //新增
          has_child: item.has_child,
        })
      }
    })
    console.log(tree, 'tree')

    for (var i = 0; i < tree.length - 1; i++) {
      //每一轮比较要比多少次
      for (var j = 0; j < tree.length - 1 - i; j++) {
        //如果第一个比第二个大,就交换他们两个位置
        if (tree[j].size > tree[j + 1].size) {
          var temp = tree[j]
          tree[j] = tree[j + 1]
          tree[j + 1] = temp
        }
      }
    }

    return tree
  }
在第一版本的无限层级改良进行了获取下层的函数并且要增加父文件id的判断 就是上面传入的currentId,不然回展示错误

const moreLevel = (arr, id) => {
  return arr?.map((item, index) => {
    //获取子文件接口获取方法
    // if (item?.has_child) {
    // if (item?.children?.length > 0) {
    if (item?.has_child) {
      return (
        <Collapse
          accordion
          // onChange={() => {
          //   console.log(111)
          // }}
        >
          <Collapse.Panel
            key={index}
            title={
              <div
                style={{ display: 'flex', alignItems: 'center' }}
                onClick={() => {
                  history.push(
                    `/mobile/nextFolder/${item.parent_id}/${item.file_id}`
                  )
                }}
              >
                {item.parent_id == id ? (
                  <FolderFilled
                    style={{ color: '#6BB9FF' }}
                    className={styles.singleFolderImg}
                  />
                ) : (
                  <FolderOpenFilled
                    style={{ color: '#6BB9FF' }}
                    className={styles.singleFolderImg}
                  />
                )}
                <div>{item.name}</div>
              </div>
            }
          >
            {/* {moreLevel(item.children)} */}
            {/* 获取子文件接口方法 */}
            <DynamicContent id={item.file_id} currentId={id} />
          </Collapse.Panel>
        </Collapse>
      )
    } else {
      if (item.size === '0') {
        return (
          <div
            className={styles.singleFolder}
            style={{ paddingLeft: '0.24rem' }}
            onClick={() => {
              history.push(
                `/mobile/nextFolder/${item.parent_id}/${item.file_id}`
              )
            }}
          >
            {' '}
            {item.parent_id == id ? (
              <FolderFilled
                style={{ color: '#6BB9FF' }}
                className={styles.singleFolderImg}
              />
            ) : (
              <FolderOpenFilled
                className={styles.singleFolderImg}
                style={{ color: '#6BB9FF', paddingLeft: '0.24rem' }}
              />
            )}
            {item.name}
          </div>
        )
      } else {
        return (
          <div
            className={styles.singleFolder}
            onClick={() => {
              history.push(`/mobile/viewPage/${item.parent_id}/${item.file_id}`)
            }}
            style={{ paddingLeft: '0.24rem' }}
          >
            <img
              className={styles.singleFolderImg}
              alt="example"
              src={iconList[Distinguish.typeSeparate(item.name) - 1]}
            />{' '}
            {/* <MailFill /> */}
            <div>{item?.wiki_name === '' ? item?.name : item?.wiki_name}</div>
          </div>
        )
      }
    }
  })
}

第二版css代码:


.knowledgeList {
  padding-left: 0.2rem;
  padding-right: 0.32rem;
  padding-bottom: 0.98rem;
  :global {
    .adm-list-item-content-main {
      padding: 0.2rem 0;
    }
    .adm-collapse-panel-content {
      color: #333;
      font-size: 0.28rem;
    }
    .adm-list {
      --border-inner: 0px;
      --border-top: 0px;
      --border-bottom: 0px;
      font-size: 0.28rem;
      --padding-right: 0px;
    }
    .adm-list-item {
      line-height: 0.5rem;

      // padding-left: 0;
    }
    .adm-list-body {
      font-size: 0.28rem;
    }
    .adm-list-item-content-main {
      // align-items: center;
      // display: flex;
    }
  }
}
.singleFolder {
  padding: 0.1rem 0;
  line-height: 0.7rem;
  font-size: 0.28rem;
  display: flex;
  align-items: center;
  div {
    align-items: center;
    /*第一步: 溢出隐藏 */
    overflow: hidden;
    /* 第二步:让文本不会换行, 在同一行继续 */
    white-space: nowrap;
    /* 第三步:用省略号来代表未显示完的文本 */
    text-overflow: ellipsis;
  }
}
.knowledge {
  // padding-left: 0.2rem;
  padding-top: 0.2rem;
}
.singleFolderImg {
  // padding-right: var(--prefix-padding-right);
  padding-right: 0.24rem;
  padding-left: 0.24rem;
  width: 0.5rem;
  height: 0.5rem;
  svg {
    width: 100%;
    height: 100%;
  }
}
第一次我是进行的第一版 后面根须需求 有优化成了第二版 如果有哪些地方不对的 请给我指出来
谢谢观看

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: antd-mobile-vue是一个基于Vue.js框架的移动端组件库,它提供了一系列UI组件,用于开发高质量的移动端应用。 与其它UI组件库相比,antd-mobile-vue具有以下优势和特点: 1. 高质量的组件:antd-mobile-vue提供了丰富的移动端UI组件,如按钮、表单、弹窗、导航等,这些组件都经过了精心设计和开发,具有统一的风格和良好的用户体验。 2. 灵活的布局:antd-mobile-vue提供了灵活的布局组件,如栅格布局、Flex布局,可帮助开发者快速搭建页面结构,并自适应不同的屏幕尺寸。 3. 易于使用和扩展:antd-mobile-vue的组件使用简单,开发者可以通过简单的配置和参数就可以实现复杂的交互效果。而且,antd-mobile-vue的组件提供了丰富的扩展能力,可以根据项目需求进行个性化的定制。 4. 生态丰富:antd-mobile-vue拥有庞大的开发者社区和活跃的维护团队,开发者可以通过官方文档和社区资源获取帮助和支持。此外,antd-mobile-vue还与其它Vue.js生态工具和库良好地兼容,如Vue Router、Vue CLI等。 5. 支持国际化:antd-mobile-vue提供了多语言支持,开发者可以根据项目需求灵活地切换多种语言环境。 总之,antd-mobile-vue是一个功能强大、易于使用和扩展的移动端组件库,它可以帮助开发者快速构建高质量的移动端应用,提高开发效率和用户体验。 ### 回答2: antd-mobile-vue是一种基于Vue.js框架的移动端UI库。它是对Ant Design Mobile的Vue组件实现的封装和扩展,旨在为开发者提供高质量、易用性的移动端组件库,帮助快速开发移动应用程序。 antd-mobile-vue提供了丰富的移动端UI组件,如按钮、导航栏、标签栏、列表、表单等,可以满足日常开发中绝大部分的界面需求。这些组件都经过精心设计和优化,在视觉和交互上都符合当前移动端的设计原则和用户体验。而且,它还提供了灵活的定制和扩展能力,允许开发者根据具体需求进行个性化定制,提高开发效率和用户体验。 除了UI组件外,antd-mobile-vue还提供了一些实用的工具和功能,如样式工具库、语言国际化、路由管理等。这些工具和功能都是为了让开发者更方便地进行移动应用开发,减少重复性的工作,提高开发效率。 antd-mobile-vue拥有广泛的社区支持和文档资料,开发者可以从社区中获取帮助和解决问题,学习和掌握使用该库的技巧和最佳实践。同时,antd-mobile-vue还提供了详细的官方文档和示例代码,方便开发者快速入手和上手该库。 总之,antd-mobile-vue是一款功能强大、易用性强的移动端UI库,适用于各种移动应用的开发。无论是个人开发者还是团队开发,都可以通过使用antd-mobile-vue来快速构建高质量的移动应用程序。 ### 回答3: antd-mobile-vue 是一个基于 Vue.js移动端 UI 组件库,它提供了丰富的移动端组件和样式风格,可以帮助开发者快速构建优雅的移动端应用。 antd-mobile-vue 的特点有以下几个方面: 1. 高度可定制:antd-mobile-vue 提供了大量的组件,涵盖了移动端常见的UI元素,如按钮、导航栏、表单等,这些组件的样式和交互行为都可以通过配置进行定制,满足不同项目的需求。 2. 兼容性强:antd-mobile-vue 提供了对不同移动端浏览器和操作系统的支持,保证组件在不同环境下的正常运行和展示,同时也保证了用户的使用体验。 3. 特色设计:antd-mobile-vue 的设计风格简洁、现代,符合移动端用户的审美要求,同时也遵循了 Material Design 和 iOS Human Interface Guidelines 等设计准则,保证了用户的熟悉感和易用性。 4. 文档丰富:antd-mobile-vue 提供了详细的文档和示例代码,开发者可以根据文档了解组件的使用方法和配置参数,快速上手使用。 5. 生态丰富:antd-mobile-vue 是基于 Ant Design Mobileantd-mobile)的 Vue 实现,可以与其它 Vue 生态工具和插件无缝集成,如 Vue Router、Vuex 等,方便开发者构建复杂的移动应用。 总之,antd-mobile-vue 是一个强大而灵活的移动端 UI 组件库,它可以帮助开发者节省时间和精力,快速开发出高质量的移动应用。无论是个人项目还是企业应用,都可以考虑使用这个库来提升开发效率和用户体验。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值