From cdf432f970109ead40fecf8217bcd9befc9f1f04 Mon Sep 17 00:00:00 2001 From: LiuJiaNan <15703339975@163.com> Date: Mon, 3 Nov 2025 16:50:34 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9EuseUploadFile=E3=80=81useDele?= =?UTF-8?q?teFile=E3=80=81useGetFile=E3=80=81LeftTree=E3=80=81SelectTree?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/LeftTree/Basic/index.d.ts | 25 +++ components/LeftTree/Basic/index.js | 184 ++++++++++++++++++ components/LeftTree/Department/Gwj/index.d.ts | 17 ++ components/LeftTree/Department/Gwj/index.js | 32 +++ components/SelectTree/Basic/index.d.ts | 25 +++ components/SelectTree/Basic/index.js | 50 +++++ .../SelectTree/Department/Gwj/index.d.ts | 17 ++ components/SelectTree/Department/Gwj/index.js | 58 ++++++ enum/uploadFile/{ => gwj}/index.js | 0 .../hooks => hooks}/useDeleteFile/index.d.ts | 0 {todo/hooks => hooks}/useDeleteFile/index.js | 4 +- {todo/hooks => hooks}/useGetFile/index.d.ts | 0 {todo/hooks => hooks}/useGetFile/index.js | 4 +- .../hooks => hooks}/useUploadFile/index.d.ts | 22 ++- {todo/hooks => hooks}/useUploadFile/index.js | 11 +- utils/index.d.ts | 23 +++ utils/index.js | 36 ++++ 17 files changed, 489 insertions(+), 19 deletions(-) create mode 100644 components/LeftTree/Basic/index.d.ts create mode 100644 components/LeftTree/Basic/index.js create mode 100644 components/LeftTree/Department/Gwj/index.d.ts create mode 100644 components/LeftTree/Department/Gwj/index.js create mode 100644 components/SelectTree/Basic/index.d.ts create mode 100644 components/SelectTree/Basic/index.js create mode 100644 components/SelectTree/Department/Gwj/index.d.ts create mode 100644 components/SelectTree/Department/Gwj/index.js rename enum/uploadFile/{ => gwj}/index.js (100%) rename {todo/hooks => hooks}/useDeleteFile/index.d.ts (100%) rename {todo/hooks => hooks}/useDeleteFile/index.js (85%) rename {todo/hooks => hooks}/useGetFile/index.d.ts (100%) rename {todo/hooks => hooks}/useGetFile/index.js (94%) rename {todo/hooks => hooks}/useUploadFile/index.d.ts (78%) rename {todo/hooks => hooks}/useUploadFile/index.js (88%) diff --git a/components/LeftTree/Basic/index.d.ts b/components/LeftTree/Basic/index.d.ts new file mode 100644 index 0000000..f00afda --- /dev/null +++ b/components/LeftTree/Basic/index.d.ts @@ -0,0 +1,25 @@ +import type { TreeProps } from "antd/es/tree"; +import type { FC } from "react"; + +/** + * 组件属性 + */ +export interface BasicLeftTreeProps extends TreeProps { + /** 树形数据 title 字段,默认 name */ + nameKey?: string; + /** 树形数据 key 字段,默认 id */ + idKey?: string; + /** 树形数据 children 字段,默认 childrenList */ + childrenKey?: string; + /** 决定 onGetNodePaths 是否包含自身节点,默认 true */ + onGetNodePathsIsIncludeOneself?: boolean; + /** 获取父级节点 */ + onGetNodePaths?: () => Record[]; +} + +/** + * 基础左侧树组件(不建议直接使用此组件,二次继承使用) + */ +declare const BasicLeftTree: FC; + +export default BasicLeftTree; diff --git a/components/LeftTree/Basic/index.js b/components/LeftTree/Basic/index.js new file mode 100644 index 0000000..42280b6 --- /dev/null +++ b/components/LeftTree/Basic/index.js @@ -0,0 +1,184 @@ +import { Input, Tree } from "antd"; +import { useEffect, useState } from "react"; + +const { Search } = Input; + +/** + * 基础左侧树组件(不建议直接使用此组件,二次继承使用) + */ +const BasicLeftTree = (props) => { + const { + onSelect, + onGetNodePaths, + onGetNodePathsIsIncludeOneself = true, + expandedKeys: externalExpandedKeys, + treeData = [], + nameKey = "name", + idKey = "id", + childrenKey = "childrenList", + ...restProps + } = props; + + const [expandedKeys, setExpandedKeys] = useState([]); + const [searchValue, setSearchValue] = useState(""); + const [autoExpandParent, setAutoExpandParent] = useState(true); + + useEffect(() => { + setExpandedKeys(externalExpandedKeys); + }, [externalExpandedKeys]); + + // 展开所有包含匹配项的父节点 + const getAllExpandedKeys = (data, searchValue, keys = []) => { + data.forEach((node) => { + if (node[childrenKey]) { + if (node[nameKey].includes(searchValue) + || node[childrenKey].some(child => child[nameKey].includes(searchValue))) { + keys.push(node[idKey]); + } + getAllExpandedKeys(node[childrenKey], searchValue, keys); + } + }); + return keys; + }; + + // 过滤树数据,只保留匹配的节点 + const filterTreeData = (data, searchValue) => { + if (!searchValue) { + return data; + } + + return data.reduce((acc, node) => { + // 检查当前节点是否匹配 + const isMatch = node[nameKey].includes(searchValue); + + // 递归处理子节点 + const filteredChildren = node[childrenKey] ? filterTreeData(node[childrenKey], searchValue) : []; + + // 如果当前节点匹配或者有匹配的子节点,则保留该节点 + if (isMatch || filteredChildren.length > 0) { + acc.push({ + ...node, + [childrenKey]: filteredChildren.length > 0 ? filteredChildren : undefined, + }); + } + + return acc; + }, []); + }; + + const handleExpand = (newExpandedKeys) => { + setExpandedKeys(newExpandedKeys); + setAutoExpandParent(false); + }; + + const getNodePaths = (data, targetId, idKey, childrenKey, path = [], isIncludeOneself) => { + for (let i = 0; i < data.length; i++) { + const node = data[i]; + const newPath = [...path, node]; + + // 找到目标节点,根据isIncludeOneself决定是否包含自身 + if (node[idKey] === targetId) { + if (isIncludeOneself) + return newPath; // 包含自身 + else + return path; // 不包含自身,只返回父节点路径 + } + + // 递归查找子节点 + if (node[childrenKey] && node[childrenKey].length > 0) { + const result = getNodePaths(node[childrenKey], targetId, idKey, childrenKey, newPath, isIncludeOneself); + if (result) { + return result; + } + } + } + + return null; + }; + + const handleSelect = (selectedKeys, event) => { + if (selectedKeys.length > 0) { + const selectedNodeId = selectedKeys[0]; + const parentNodes = getNodePaths(treeData, selectedNodeId, idKey, childrenKey, onGetNodePathsIsIncludeOneself); + onGetNodePaths?.(parentNodes); + } + onSelect?.(selectedKeys, event); + }; + + const onFilterTreeData = (value) => { + setSearchValue(value); + setAutoExpandParent(true); + + if (!value) { + setExpandedKeys([]); + return; + } + + const expandedKeys = getAllExpandedKeys(treeData, value); + + setExpandedKeys(expandedKeys); + }; + + const onSearch = async (value) => { + if (value === searchValue) + return; + onFilterTreeData(value); + }; + + // 渲染带高亮的标题 + const renderTitle = (name) => { + if (!searchValue) + return name; + + const index = name.indexOf(searchValue); + if (index === -1) + return name; + + const beforeStr = name.substring(0, index); + const afterStr = name.substring(index + searchValue.length); + + return ( + + {beforeStr} + {searchValue} + {afterStr} + + ); + }; + + // 递归处理树节点标题显示 + const processTreeData = (data) => { + return data.map(node => ({ + ...node, + [nameKey]: renderTitle(node[nameKey]), + [childrenKey]: node[childrenKey] ? processTreeData(node[childrenKey]) : undefined, + })); + }; + + // 过滤并处理树数据 + const filteredTreeData = filterTreeData(treeData, searchValue); + const processedTreeData = processTreeData(filteredTreeData); + + return ( +
+ + +
+ ); +}; + +BasicLeftTree.displayName = "BasicLeftTree"; + +export default BasicLeftTree; diff --git a/components/LeftTree/Department/Gwj/index.d.ts b/components/LeftTree/Department/Gwj/index.d.ts new file mode 100644 index 0000000..177ff53 --- /dev/null +++ b/components/LeftTree/Department/Gwj/index.d.ts @@ -0,0 +1,17 @@ +import type { FC } from "react"; +import type { BasicLeftTreeProps } from "../../Basic"; + +/** + * 组件属性 + */ +export interface DepartmentLeftTreeProps extends Omit { + /** 请求参数 */ + params?: Record; +} + +/** + * 部门左侧树组件(港务局版本) + */ +declare const DepartmentLeftTree: FC; + +export default DepartmentLeftTree; diff --git a/components/LeftTree/Department/Gwj/index.js b/components/LeftTree/Department/Gwj/index.js new file mode 100644 index 0000000..aae5925 --- /dev/null +++ b/components/LeftTree/Department/Gwj/index.js @@ -0,0 +1,32 @@ +import { request } from "@cqsjjb/jjb-common-lib/http"; +import { useEffect, useState } from "react"; +import BasicLeftTree from "../../Basic"; + +/** + * 部门左侧树组件(港务局版本) + */ +function DepartmentLeftTree(props) { + const { + params = {}, + ...restProps + } = props; + + const [treeData, setTreeData] = useState([]); + + const getData = async () => { + const { data } = await request("/basic-info/department/listTree", "post", params); + setTreeData(data); + }; + + useEffect(() => { + getData(); + }, []); + + return ( + + ); +} + +DepartmentLeftTree.displayName = "DepartmentLeftTree"; + +export default DepartmentLeftTree; diff --git a/components/SelectTree/Basic/index.d.ts b/components/SelectTree/Basic/index.d.ts new file mode 100644 index 0000000..57c8ce9 --- /dev/null +++ b/components/SelectTree/Basic/index.d.ts @@ -0,0 +1,25 @@ +import type { TreeSelectProps } from "antd/es/tree-select"; +import type { FC } from "react"; + +/** + * 组件属性 + */ +export interface BasicSelectTreeProps extends TreeSelectProps { + /** 树形数据 label 字段,默认 name */ + nameKey?: string; + /** 树形数据 value 字段,默认 id */ + idKey?: string; + /** 树形数据 children 字段,默认 childrenList */ + childrenKey?: string; + /** 决定 onGetNodePaths 是否包含自身节点,默认 true */ + onGetNodePathsIsIncludeOneself?: boolean; + /** 获取父级节点 */ + onGetNodePaths?: (nodes: Record[]) => void; +} + +/** + * 基础下拉树组件(不建议直接使用此组件,二次继承使用) + */ +declare const BasicSelectTree: FC; + +export default BasicSelectTree; diff --git a/components/SelectTree/Basic/index.js b/components/SelectTree/Basic/index.js new file mode 100644 index 0000000..8a8cfba --- /dev/null +++ b/components/SelectTree/Basic/index.js @@ -0,0 +1,50 @@ +import { TreeSelect } from "antd"; +import { getTreeNodePaths } from "../../../utils"; + +function BasicSelectTree(props) { + const { + onSelect, + onGetNodePaths, + onGetNodePathsIsIncludeOneself = true, + placeholder = "", + treeData = [], + nameKey = "name", + idKey = "id", + childrenKey = "childrenList", + ...restProps + } = props; + + const handleSelect = (value, node, extra) => { + if (value.length > 0) { + const parentNodes = getTreeNodePaths({ + data: treeData, + targetId: value, + idKey, + childrenKey, + isIncludeOneself: onGetNodePathsIsIncludeOneself + }); + onGetNodePaths?.(parentNodes); + } + onSelect?.(value, node, extra); + }; + + return ( + + ); +} + +BasicSelectTree.displayName = "BasicSelectTree"; + +export default BasicSelectTree; diff --git a/components/SelectTree/Department/Gwj/index.d.ts b/components/SelectTree/Department/Gwj/index.d.ts new file mode 100644 index 0000000..3cebc2d --- /dev/null +++ b/components/SelectTree/Department/Gwj/index.d.ts @@ -0,0 +1,17 @@ +import type { FC } from "react"; +import type { BasicSelectTreeProps } from "../../Basic"; + +/** + * 组件属性 + */ +export interface DepartmentSelectTreeProps extends Omit { + /** 请求参数 */ + params?: Record; +} + +/** + * 部门下拉树组件(港务局版本) + */ +declare const DepartmentSelectTree: FC; + +export default DepartmentSelectTree; diff --git a/components/SelectTree/Department/Gwj/index.js b/components/SelectTree/Department/Gwj/index.js new file mode 100644 index 0000000..f327f62 --- /dev/null +++ b/components/SelectTree/Department/Gwj/index.js @@ -0,0 +1,58 @@ +import { request } from "@cqsjjb/jjb-common-lib/http"; +import { useEffect, useState } from "react"; +import BasicLeftTree from "../../Basic"; + +/** + * 部门左侧树组件(港务局版本) + */ +function DepartmentSelectTree(props) { + const { + params = {}, + ...restProps + } = props; + + const [treeData, setTreeData] = useState([ + { + name: "parent 1", + id: "0-0", + childrenList: [ + { + name: "parent 1-0", + id: "0-0-0", + childrenList: [ + { + name: "leaf", + id: "0-0-0-0", + }, + { + name: "leaf", + id: "0-0-0-1", + }, + ], + }, + { + name: "parent 1-1", + id: "0-0-1", + childrenList: [{ name: sss, id: "0-0-1-0" }], + }, + ], + }, + ]); + + const getData = async () => { + const { data } = await request("/basic-info/department/listTree", "post", params); + setTreeData(data); + }; + + useEffect(() => { + getData(); + }, []); + + return ( + + ); +} + +DepartmentSelectTree.displayName = "DepartmentSelectTree"; + +export default DepartmentSelectTree; diff --git a/enum/uploadFile/index.js b/enum/uploadFile/gwj/index.js similarity index 100% rename from enum/uploadFile/index.js rename to enum/uploadFile/gwj/index.js diff --git a/todo/hooks/useDeleteFile/index.d.ts b/hooks/useDeleteFile/index.d.ts similarity index 100% rename from todo/hooks/useDeleteFile/index.d.ts rename to hooks/useDeleteFile/index.d.ts diff --git a/todo/hooks/useDeleteFile/index.js b/hooks/useDeleteFile/index.js similarity index 85% rename from todo/hooks/useDeleteFile/index.js rename to hooks/useDeleteFile/index.js index 344ce62..c93192f 100644 --- a/todo/hooks/useDeleteFile/index.js +++ b/hooks/useDeleteFile/index.js @@ -2,7 +2,7 @@ import { request } from "@cqsjjb/jjb-common-lib/http"; import { useState } from "react"; /** - * 删除文件 TODO + * 删除文件 */ function useDeleteFile(returnType = "object") { // loading状态 @@ -23,7 +23,7 @@ function useDeleteFile(returnType = "object") { // 发送请求 request( - single ? `/basic-info/imgFiles/${files[0].filePath}` : `/basic-info/imgFiles/ids?ids=${files.map(f => f.id)}`, + single ? `/basic-info/imgFiles/delete?filePath=${files[0].filePath}` : `/basic-info/imgFiles/ids?ids=${files.map(f => f.id)}`, "delete", ) .then((res) => { diff --git a/todo/hooks/useGetFile/index.d.ts b/hooks/useGetFile/index.d.ts similarity index 100% rename from todo/hooks/useGetFile/index.d.ts rename to hooks/useGetFile/index.d.ts diff --git a/todo/hooks/useGetFile/index.js b/hooks/useGetFile/index.js similarity index 94% rename from todo/hooks/useGetFile/index.js rename to hooks/useGetFile/index.js index 587472c..5181f04 100644 --- a/todo/hooks/useGetFile/index.js +++ b/hooks/useGetFile/index.js @@ -1,10 +1,10 @@ import { request } from "@cqsjjb/jjb-common-lib/http"; import { useState } from "react"; -import { UPLOAD_FILE_TYPE_ENUM } from "../../enum/uploadFile"; +import { UPLOAD_FILE_TYPE_ENUM } from "../../enum/uploadFile/gwj"; import { addingPrefixToFile } from "../../utils"; /** - * 获取文件 TODO + * 获取文件 */ function useGetFile(returnType = "object") { // loading状态 diff --git a/todo/hooks/useUploadFile/index.d.ts b/hooks/useUploadFile/index.d.ts similarity index 78% rename from todo/hooks/useUploadFile/index.d.ts rename to hooks/useUploadFile/index.d.ts index 80b0500..3de751e 100644 --- a/todo/hooks/useUploadFile/index.d.ts +++ b/hooks/useUploadFile/index.d.ts @@ -8,25 +8,31 @@ export interface UploadFile { [key: string]: any; } -export interface Params { +export interface BaseParams { /** 文件类型 */ type: number; - /** 所属端 */ - path: string; /** 企业id */ - corpinfoId: string; - /** 外键id */ - foreignKey: string; + corpinfoId?: string; [key: string]: any; } +export interface SingleParams extends BaseParams { + /** 外键id - 单文件上传时可选 */ + foreignKey?: string; +} + +export interface MultipleParams extends BaseParams { + /** 外键id - 多文件上传时必填 */ + foreignKey: string; +} + export interface SingleUploadFileOptions { /** 要上传的文件 */ files: UploadFile[]; /** 是否单文件上传 */ single?: true; /** 上传的参数 */ - params: Params; + params: SingleParams; } export interface MultipleUploadFileOptions { @@ -35,7 +41,7 @@ export interface MultipleUploadFileOptions { /** 是否单文件上传 */ single: false; /** 上传的参数 */ - params: Params; + params: MultipleParams; } export interface MultipleFileResponse { diff --git a/todo/hooks/useUploadFile/index.js b/hooks/useUploadFile/index.js similarity index 88% rename from todo/hooks/useUploadFile/index.js rename to hooks/useUploadFile/index.js index 41c0d7c..b72cd3b 100644 --- a/todo/hooks/useUploadFile/index.js +++ b/hooks/useUploadFile/index.js @@ -1,9 +1,9 @@ import { request } from "@cqsjjb/jjb-common-lib/http"; import { useState } from "react"; -import { UPLOAD_FILE_PATH_ENUM, UPLOAD_FILE_TYPE_ENUM } from "../../enum/uploadFile"; +import { UPLOAD_FILE_PATH_ENUM, UPLOAD_FILE_TYPE_ENUM } from "../../enum/uploadFile/gwj"; /** - * 上传文件 TODO + * 上传文件 */ function useUploadFile(returnType = "object") { // loading状态 @@ -35,11 +35,8 @@ function useUploadFile(returnType = "object") { if (!path) throw new Error(`未找到 type ${params.type} 对应的 path `); - // if (!params.path) - // throw new Error("请传入 options.params.path"); - if (params.corpinfoId === undefined || params.corpinfoId === null) - throw new Error("请传入 options.params.corpinfoId"); - if (params.foreignKey === undefined || params.foreignKey === null) + // 当single为false时,foreignKey是必需的 + if (!single && (params.foreignKey === undefined || params.foreignKey === null)) throw new Error("请传入 options.params.foreignKey"); const formData = new FormData(); diff --git a/utils/index.d.ts b/utils/index.d.ts index fc2a7cc..f6ea4bf 100644 --- a/utils/index.d.ts +++ b/utils/index.d.ts @@ -123,6 +123,22 @@ interface IsEmptyToWhetherOptions { yesValue?: string | number; } +// 为 getTreeNodePaths 函数定义接口类型 +interface GetTreeNodePathsOptions { + /** 树形数据 */ + data: any[]; + /** 目标节点ID */ + targetId: string | number; + /** id字段名 */ + idKey: string; + /** 子节点字段名 */ + childrenKey: string; + /** 路径数组 */ + path?: any[]; + /** 是否包含自身 */ + isIncludeOneself?: boolean; +} + /** * 计算序号 */ @@ -281,3 +297,10 @@ export function getIndexColumn(pagination: false | BasePaginationConfig): { * 获取文件url */ export function getFileUrl(): string; + +/** + * 获取树形节点路径 + */ +export function getTreeNodePaths = Record>( + options: GetTreeNodePathsOptions, +): T[] | null; diff --git a/utils/index.js b/utils/index.js index aef6506..57eb577 100644 --- a/utils/index.js +++ b/utils/index.js @@ -394,6 +394,42 @@ export function getIndexColumn(pagination) { }; } +/** + * 获取树形节点路径 + */ +export function getTreeNodePaths(options) { + const { data, targetId, idKey, childrenKey, path = [], isIncludeOneself } = options; + for (let i = 0; i < data.length; i++) { + const node = data[i]; + const newPath = [...path, node]; + + // 找到目标节点,根据isIncludeOneself决定是否包含自身 + if (node[idKey] === targetId) { + if (isIncludeOneself) + return newPath; // 包含自身 + else + return path; // 不包含自身,只返回父节点路径 + } + + // 递归查找子节点 + if (node[childrenKey] && node[childrenKey].length > 0) { + const result = getTreeNodePaths({ + data: node[childrenKey], + targetId, + idKey, + childrenKey, + path: newPath, + isIncludeOneself, + }); + if (result) { + return result; + } + } + } + + return null; +} + /** * 获取文件url */