废话不说,先上效果图,如果有下层文件就会展示右边的下拉框,如果没有就不会显示,点击文件夹跳转到只会显示子文件夹的页面(因为想让大家跟我一起成长,所以讲解的很细,都是自己的心得)
( 一共两版 第一版-获取所有数据来进行无限层级编写 未改良版本 第二版是用当前层级id来通过接口获取下级文件目录,改良版本)
获取所有数据的版本 👇
![](https://img-blog.csdnimg.cn/img_convert/1e63a15babdc2315b181a12e8aacaaef.png)
由于在antd-mobile中会出现一个折叠面板,但是官方没有给我们相关的无限层级,只有一层,那么只有我们纯手写了。
由于是无限包组件
单层是这样的:
<Collapse>
<Collapse.Panel>
123
</Collapse.Panel>
</Collapse>
多层:
<Collapse>
<Collapse.Panel>
<Collapse>
<Collapse.Panel>
...一直循环下去
</Collapse.Panel>
</Collapse>
</Collapse.Panel>
</Collapse>
但是由于自带了antd中的css 所以我们还要改写下公共组件样式 ,废话不多说,直接上关键代码:
js循环关键代码:
//这边假数据直接无视给你们看一下我用的结构
![](https://img-blog.csdnimg.cn/img_convert/60072082c0794b6ba7ffcb80ce9b8769.png)
如果后端返回的数据不是这个结构,我在这边就是采用了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代码,如果不修改公共样式,无限层级的组件上下级会出现空隙,不好看(其中还有些是其他需求改的文字什么的,个别可以更改,看自身需求):
![](https://img-blog.csdnimg.cn/img_convert/d3b5fcefb89c38f894ca2a91ff3dc421.png)
获取当级目录的改良后版本 👇
效果图:
![](https://img-blog.csdnimg.cn/img_convert/2cf4eec0d4bfd76c1192b832a21726ee.png)
首先跟获取所有目录版本不一样的地方是在循环函数之前加一个获取下级目录的函数
//获取子文件接口方法
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%;
}
}