import { Box, Button, createDisclosure, HStack, Icon, Input, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, Spinner, Text, VStack, } from "@hope-ui/solid" import { BiSolidRightArrow, BiSolidFolderOpen } from "solid-icons/bi" import { Accessor, createContext, createSignal, useContext, Show, For, Setter, createEffect, on, JSXElement, } from "solid-js" import { useFetch, useT, useUtil } from "~/hooks" import { getMainColor, password, me } from "~/store" import { Obj } from "~/types" import { pathBase, handleResp, hoverColor, pathJoin, fsList, createMatcher, } from "~/utils" import { useRouter } from "~/hooks" // 添加权限路径类型 interface PermPath { path: string permission: number } export type FolderTreeHandler = { setPath: Setter } export interface FolderTreeProps { onChange: (path: string) => void forceRoot?: boolean autoOpen?: boolean handle?: (handler: FolderTreeHandler) => void showEmptyIcon?: boolean showHiddenFolder?: boolean defaultValue?: string } interface FolderTreeContext extends Omit { value: Accessor permPaths?: PermPath[] } const context = createContext() // 检查路径是否在权限范围内 const isPathInPermissions = (path: string, permPaths: PermPath[]) => { return permPaths.some((perm) => { // 如果是权限路径本身或其子路径 return path === perm.path || path.startsWith(perm.path + "/") }) } export const FolderTree = (props: FolderTreeProps) => { const [path, setPath] = createSignal(props.defaultValue ?? "/") const permPaths = me().permissions || [] const userRole = me().role || [] // 判断是否为管理员(role包含2) const isAdmin = userRole.includes(2) return ( { setPath(val) props.onChange(val) }, autoOpen: props.autoOpen ?? false, forceRoot: props.forceRoot ?? false, showEmptyIcon: props.showEmptyIcon ?? false, showHiddenFolder: props.showHiddenFolder ?? true, permPaths: permPaths, }} > 0}> {(perm) => } ) } const FolderTreeNode = (props: { path: string; isRoot?: boolean }) => { const { isHidePath } = useUtil() const [children, setChildren] = createSignal() const { value, onChange, forceRoot, autoOpen, showEmptyIcon, showHiddenFolder, permPaths = [], } = useContext(context)! const emptyIconVisible = () => Boolean(showEmptyIcon && children() !== undefined && !children()?.length) const [loading, fetchList] = useFetch(() => { return fsList(props.path, password(), 1, 0, false) }) let isLoaded = false const load = async () => { if (children()?.length) return const resp = await fetchList() handleResp( resp, (data) => { isLoaded = true // 只保留文件夹类型的项目 let filteredDirs = data.content.filter((item) => item.is_dir) // 如果不是管理员,才进行权限过滤 const userRole = me().role || [] const isAdmin = userRole.includes(2) if (!isAdmin && permPaths.length > 0) { filteredDirs = filteredDirs.filter((item) => { const fullPath = pathJoin(props.path, item.name) return isPathInPermissions(fullPath, permPaths) }) } setChildren(filteredDirs) }, () => { if (isOpen()) onToggle() }, ) } const { isOpen, onToggle } = createDisclosure() const active = () => value() === props.path const isMatchedFolder = createMatcher(props.path) const checkIfShouldOpen = async (pathname: string) => { if (!autoOpen) return if (isMatchedFolder(pathname)) { if (!isOpen()) onToggle() if (!isLoaded) load() } } createEffect(on(value, checkIfShouldOpen)) const isHiddenFolder = () => isHidePath(props.path) && !isMatchedFolder(value()) return ( } > } > { onToggle() if (isOpen()) { load() } }} /> { onChange(props.path) }} > {props.path === "/" ? "root" : pathBase(props.path)} {(item) => ( )} ) } export type ModalFolderChooseProps = { opened: boolean onClose: () => void onSubmit?: (text: string) => void type?: string defaultValue?: string loading?: boolean footerSlot?: JSXElement children?: JSXElement header: string } export const ModalFolderChoose = (props: ModalFolderChooseProps) => { const t = useT() const [value, setValue] = createSignal(props.defaultValue ?? "/") const [handler, setHandler] = createSignal() createEffect(() => { if (!props.opened) return handler()?.setPath(value()) }) return ( {/* */} {props.header} {props.children} setHandler(h)} autoOpen defaultValue={value()} /> {props.footerSlot} ) } export const FolderChooseInput = (props: { value: string onChange: (path: string) => void id?: string onlyFolder?: boolean }) => { const { isOpen, onOpen, onClose } = createDisclosure() const t = useT() return ( <> props.onChange(e.currentTarget.value)} readOnly={props.onlyFolder} onClick={props.onlyFolder ? onOpen : () => {}} placeholder={t( `global.${ props.onlyFolder ? "choose_folder" : "choose_or_input_path" }`, )} /> {t("global.choose_folder")} ) }