從零開始-文件資源管理器-04-面包屑導航

面包屑導航

url:domain/path/[[…path]]

目前可以通過 url 的的結(jié)構(gòu)信息定位當前目錄的情況弧烤,但是無法快速的前進與后退戚啥。所以使用“面包屑”來完成這一功能

功能實現(xiàn)

安裝依賴

cd /explorer

pnpm i axios ahooks

文件樹

explorer/src/app/path/[[...path]]/layout.tsx
explorer/src/app/path/api/readdir/route.ts
explorer/src/components/breadcrumb-dropdown-readdir/index.tsx
explorer/src/components/explorer-breadcrumb/index.tsx
explorer/src/components/use-replace-pathname.ts

文件路徑:explorer/src/app/path/api/readdir/route.ts

新增一個獲取指定目錄的接口

import { NextRequest, NextResponse } from 'next/server'
import { readdir } from '@/explorer-manager/src/main.mjs'

export type SearchParamsType = {
  path: string
  only_dir: '0' | '1'
  only_file: '0' | '1'
  has_file_stat: '0' | '1'
}

export const GET = async (req: NextRequest) => {
  const { path, only_dir, only_file, has_file_stat } = Object.fromEntries(req.nextUrl.searchParams) as SearchParamsType

  return NextResponse.json({ readdir: readdir(path, { only_dir, only_file, has_file_stat }) })
}

文件路徑:explorer/src/app/path/[[...path]]/layout.tsx

將面包屑組件添加至 layout 的 Card 組件 title 部分涵亏。

import React from 'react'
import { readdir } from '@/explorer-manager/src/main.mjs'
import { PathContextProvider } from '@/app/path/context'
import { Card } from 'antd'
import ExplorerBreadcrumb from '@/components/explorer-breadcrumb'

const Layout: React.FC<React.PropsWithChildren & { params: { path: string[] } }> = ({
  children,
  params: { path = [] },
}) => {
  const readdirList = readdir(path.join('/'), { only_dir: '0', only_file: '0', has_file_stat: '1' })

  return (
    <PathContextProvider value={readdirList}>
      <Card title={<ExplorerBreadcrumb />}>{children}</Card>
    </PathContextProvider>
  )
}

export default Layout

文件路徑:explorer/src/components/explorer-breadcrumb/index.tsx

面包屑組件,使用 Antd 的 Breadcrumb 組件谒养。跟目錄"/"使用 home icon 替代

'use client'
import React from 'react'
import Link from 'next/link'
import { HomeOutlined } from '@ant-design/icons'
import { usePathname } from 'next/navigation'
import { Breadcrumb, Space } from 'antd'
import BreadcrumbDropdownReaddir from '@/components/breadcrumb-dropdown-readdir'

type ItemType = { title: string; href: string }

const ExplorerBreadcrumb: React.FC = () => {
  const pathname = usePathname() || ''

  return (
    <Space>
      <Breadcrumb
        items={decodeURIComponent(pathname)
          .split('/')
          .filter(Boolean)
          .reduce<ItemType[]>((p, c) => {
            const last = p[p.length - 1]

            return p.concat({ title: c, href: [last?.href, c].join('/') })
          }, [])
          .map(({ title, href }, k, { length }) => {
            return {
              title:
                length === k + 1 && length !== 1 ? (
                  title
                ) : (
                  <BreadcrumbDropdownReaddir title={title} href={href}>
                    <Link href={href}>{k === 0 ? <HomeOutlined /> : title}</Link>
                  </BreadcrumbDropdownReaddir>
                ),
            }
          })}
      />
    </Space>
  )
}

export default ExplorerBreadcrumb

文件路徑:explorer/src/components/breadcrumb-dropdown-readdir/index.tsx

鼠標 hover 某個路徑時游岳,下拉出現(xiàn)當前路徑下文件夾菜單,方便快速跳轉(zhuǎn)邀泉。
當菜單超出最大高度時滾動顯示嬉挡。會高亮當前路徑上的文件钝鸽。使用 dom 的 scrollIntoView 方法,確保高亮的菜單在可視區(qū)域內(nèi)庞钢。
顯示時對菜單項進行 decodeURIComponent 處理拔恰,保證顯示的文本正確。

import React, { useRef } from 'react'
import { Dropdown, Menu } from 'antd'
import { useMount, useRequest } from 'ahooks'
import axios from 'axios'
import { ReaddirListType } from '@/explorer-manager/src/type'
import { encodePathItem, pathExp } from '@/components/use-replace-pathname'
import Link from 'next/link'
import { FolderOpenOutlined, FolderOutlined } from '@ant-design/icons'
import { usePathname } from 'next/navigation'

const ScrollIntoView: React.FC<{ is_open: boolean; className?: string }> = ({ is_open, className }) => {
  const ref = useRef<HTMLDivElement>(null)

  useMount(() => {
    is_open && ref.current?.scrollIntoView({ block: 'center', behavior: 'instant' })
  })

  return is_open ? <FolderOpenOutlined ref={ref} className={className} /> : <FolderOutlined className={className} />
}

const BreadcrumbDropdownReaddir: React.FC<React.PropsWithChildren & { title: string; href: string }> = ({
  children,
  href,
  title,
}) => {
  const pathname = usePathname() || ''

  const { data = [], runAsync } = useRequest(
    () =>
      axios
        .get<{ readdir: ReaddirListType }>('/path/api/readdir', {
          params: { path: href.replace(pathExp, ''), only_dir: 1 },
        })
        .then(({ data: { readdir } }) => {
          return readdir
        }),
    { manual: true, cacheKey: `readdir-${href}`, staleTime: 60 * 1000 },
  )

  const open_dit_text =
    decodeURIComponent(pathname).split([title, '/'].join('')).pop()?.split('/').filter(Boolean).shift() || ''

  return (
    <Dropdown
      destroyPopupOnHide={true}
      arrow={true}
      trigger={['hover']}
      dropdownRender={() => {
        return (
          <Menu
            selectedKeys={[open_dit_text]}
            style={{ maxHeight: '50vw', overflow: 'scroll' }}
            items={data?.map((item) => {
              const is_open = open_dit_text === item.name

              return {
                label: (
                  <Link href={encodePathItem(`${href}/${item.name}`)} prefetch={false}>
                    {item.name}
                  </Link>
                ),
                key: item.name,
                icon: <ScrollIntoView is_open={is_open} />,
              }
            })}
          />
        )
      }}
      onOpenChange={(open) => {
        open && runAsync().then()
      }}
    >
      {children}
    </Dropdown>
  )
}

export default BreadcrumbDropdownReaddir

文件路徑:explorer/src/components/use-replace-pathname.ts

對路徑進行操作 hooks 文件基括,對路徑字符串項 encodeURIComponent 處理

export const pathExp = /(^\/)?path/

export const encodePathItem = (path: string) => {
  return path
    .split('/')
    .map((text) => encodeURIComponent(text))
    .join('/')
}

效果

截屏2023-12-13 14.51.01.png

git-repo

yangWs29/share-explorer

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末颜懊,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子风皿,更是在濱河造成了極大的恐慌河爹,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件桐款,死亡現(xiàn)場離奇詭異咸这,居然都是意外死亡,警方通過查閱死者的電腦和手機魔眨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門媳维,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人遏暴,你說我怎么就攤上這事侄刽。” “怎么了朋凉?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵唠梨,是天一觀的道長。 經(jīng)常有香客問我侥啤,道長当叭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任盖灸,我火速辦了婚禮蚁鳖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赁炎。我一直安慰自己醉箕,他們只是感情好,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布徙垫。 她就那樣靜靜地躺著讥裤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪姻报。 梳的紋絲不亂的頭發(fā)上己英,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天,我揣著相機與錄音吴旋,去河邊找鬼损肛。 笑死厢破,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的治拿。 我是一名探鬼主播摩泪,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼劫谅!你這毒婦竟也來了见坑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤捏检,失蹤者是張志新(化名)和其女友劉穎鳄梅,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體未檩,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡戴尸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了冤狡。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片孙蒙。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖悲雳,靈堂內(nèi)的尸體忽然破棺而出挎峦,到底是詐尸還是另有隱情,我是刑警寧澤合瓢,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布坦胶,位于F島的核電站,受9級特大地震影響晴楔,放射性物質(zhì)發(fā)生泄漏顿苇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一税弃、第九天 我趴在偏房一處隱蔽的房頂上張望纪岁。 院中可真熱鬧,春花似錦则果、人聲如沸幔翰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽遗增。三九已至,卻和暖如春款青,著一層夾襖步出監(jiān)牢的瞬間做修,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留缓待,地道東北人型檀。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓锦秒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親脆淹。 傳聞我的和親對象是個殘疾皇子签杈,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容