JavaScript 的 array sort 方法对字符串的排序有些问题。

纯数字

[1, 2, 3, 11].sort()
=> [1, 11, 2, 3]

字符串

['a', 'e', 'b', 'c', 'd'].sort()
=> ['a', 'b', 'c', 'd', 'e']

数字加字符串

['a-1', 'a-2', 'a-2', 'a-11'].sort()
=> ['a-1', 'a-11', 'a-2', 'a-3']

上面的例子发现,字符串的可以排序正常,数字的 11 会拍在 2 的前面。纯数字的可以通过 sort((a, b) => a - b) 的回调控制挣钱排序。

如果涉及到"字符串+数字"的时候,简单的 a - b 无法完成排序任务。

这里引用一个自然排序的依赖 natural-compare-lite可以正确处理文件名的排序问题。

开发

explorer

安装依赖

pnpm i natural-compare-lite

pnpm i @types/natural-compare-lite -D

创建 sort 排序 context 组件,并创建四个排序方法

  • 时间的正排序与逆排序
  • 文件名正排序与逆排序

对文件名的排序使用 naturalCompare 依赖方法。

'use client'
import naturalCompare from 'natural-compare-lite'
import { ReaddirItemType } from '@/explorer-manager/src/type'
import createCtx from '@/lib/create-ctx'
import React from 'react'

export type SortAction = (a: ReaddirItemType, b: ReaddirItemType) => number

export const nameAscSort: SortAction = (a, b) => naturalCompare(a.name, b.name)
export const dateAscSort: SortAction = (a, b) => (a?.stat?.mtimeMs || 0) - (b?.stat?.mtimeMs || 0)
export const nameDescSort: SortAction = (a, b) => naturalCompare(b.name, a.name)
export const dateDescSort: SortAction = (a, b) => (b?.stat?.mtimeMs || 0) - (a?.stat?.mtimeMs || 0)

export const sortMap = {
  asc_name: nameAscSort,
  asc_date: dateAscSort,
  desc_name: nameDescSort,
  desc_date: dateDescSort,
}

export const SortContext = createCtx<'asc_name' | 'asc_date' | 'desc_name' | 'desc_date'>()

export const useSortStore = SortContext.useStore

export const useSortDispatch = SortContext.useDispatch

export const SortProvider: React.FC<React.PropsWithChildren> = ({ children }) => {
  return <SortContext.ContextProvider value="asc_name">{children}</SortContext.ContextProvider>
}

创建一个控制排序的下拉菜单组件

'use client'
import React from 'react'
import { Button, Dropdown } from 'antd'
import { SortAscendingOutlined, SortDescendingOutlined } from '@ant-design/icons'
import { useSortDispatch, useSortStore } from '@/components/readdir-sort/sort-context'

const ReaddirSort: React.FC = () => {
  const sort = useSortStore()
  const sortDispatch = useSortDispatch()

  return (
    <Dropdown
      placement="top"
      arrow={true}
      trigger={['hover', 'click']}
      menu={{
        selectedKeys: [sort],
        items: [
          {
            key: 'asc_name',
            icon: <SortAscendingOutlined />,
            onClick: () => {
              sortDispatch('asc_name')
            },
            label: '名称正排',
          },
          {
            key: 'desc_name',
            icon: <SortDescendingOutlined />,
            onClick: () => {
              sortDispatch('desc_name')
            },
            label: '名称倒排',
          },
          {
            key: 'asc_date',
            icon: <SortAscendingOutlined />,
            onClick: () => {
              sortDispatch('asc_date')
            },
            label: '时间正排',
          },
          {
            key: 'desc_date',
            icon: <SortDescendingOutlined />,
            onClick: () => {
              sortDispatch('desc_date')
            },
            label: '时间倒排',
          },
        ],
      }}
    >
      <Button icon={<SortAscendingOutlined />} />
    </Dropdown>
  )
}

export default ReaddirSort

为 readdir-context 内的 useReaddirContext 读取 sort 属性对 readdir\_list 进行 sort 排序

export const useReaddirContext = () => {
  const readdir_list = ReaddirContext.useStore()
  const sort = useSortStore()

  return readdir_list.sort(sortMap[sort])
}

为保证能正确读取到 useSortStore 属性。需要将 SortProvider 插入 ReaddirProvider 上下文组件之前。

...

export const PathContextProvider: React.FC<React.ProviderProps<ReaddirListType>> = ({ value, children }) => {
  return (
    <SortProvider>
      <ReaddirProvider value={value}>
        ...
                        <VideoInfoProvider>{children}</VideoInfoProvider>
        ...
      </ReaddirProvider>
    </SortProvider>
  )
}

到此,对文件排序功能完成

效果

git-repo

yangWs29/share-explorer


寒露
18 声望0 粉丝