新增useUploadFile、useDeleteFile、useGetFile、LeftTree、SelectTree
parent
9d64fd33ea
commit
cdf432f970
|
|
@ -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<string, any>[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 基础左侧树组件(不建议直接使用此组件,二次继承使用)
|
||||
*/
|
||||
declare const BasicLeftTree: FC<BasicLeftTreeProps>;
|
||||
|
||||
export default BasicLeftTree;
|
||||
|
|
@ -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 (
|
||||
<span>
|
||||
{beforeStr}
|
||||
<span style={{ color: "#f50" }}>{searchValue}</span>
|
||||
{afterStr}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
// 递归处理树节点标题显示
|
||||
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 (
|
||||
<div style={{ width: 300 }}>
|
||||
<Search
|
||||
style={{ marginBottom: 8 }}
|
||||
placeholder="输入关键字进行过滤"
|
||||
onSearch={onSearch}
|
||||
/>
|
||||
<Tree
|
||||
onExpand={handleExpand}
|
||||
onSelect={handleSelect}
|
||||
autoExpandParent={autoExpandParent}
|
||||
expandedKeys={expandedKeys}
|
||||
treeData={processedTreeData}
|
||||
fieldNames={{ title: nameKey, key: idKey, children: childrenKey }}
|
||||
{...restProps}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
BasicLeftTree.displayName = "BasicLeftTree";
|
||||
|
||||
export default BasicLeftTree;
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import type { FC } from "react";
|
||||
import type { BasicLeftTreeProps } from "../../Basic";
|
||||
|
||||
/**
|
||||
* 组件属性
|
||||
*/
|
||||
export interface DepartmentLeftTreeProps extends Omit<BasicLeftTreeProps, "treeData"> {
|
||||
/** 请求参数 */
|
||||
params?: Record<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门左侧树组件(港务局版本)
|
||||
*/
|
||||
declare const DepartmentLeftTree: FC<DepartmentLeftTreeProps>;
|
||||
|
||||
export default DepartmentLeftTree;
|
||||
|
|
@ -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 (
|
||||
<BasicLeftTree treeData={treeData} {...restProps} />
|
||||
);
|
||||
}
|
||||
|
||||
DepartmentLeftTree.displayName = "DepartmentLeftTree";
|
||||
|
||||
export default DepartmentLeftTree;
|
||||
|
|
@ -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<string, any>[]) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 基础下拉树组件(不建议直接使用此组件,二次继承使用)
|
||||
*/
|
||||
declare const BasicSelectTree: FC<BasicSelectTreeProps>;
|
||||
|
||||
export default BasicSelectTree;
|
||||
|
|
@ -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 (
|
||||
<TreeSelect
|
||||
showSearch
|
||||
style={{ width: "100%" }}
|
||||
styles={{
|
||||
popup: { root: { maxHeight: 400, overflow: "auto" } },
|
||||
}}
|
||||
placeholder={`请选择${placeholder}`}
|
||||
onSelect={handleSelect}
|
||||
allowClear
|
||||
treeData={treeData}
|
||||
fieldNames={{ label: nameKey, value: idKey, children: childrenKey }}
|
||||
{...restProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
BasicSelectTree.displayName = "BasicSelectTree";
|
||||
|
||||
export default BasicSelectTree;
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import type { FC } from "react";
|
||||
import type { BasicSelectTreeProps } from "../../Basic";
|
||||
|
||||
/**
|
||||
* 组件属性
|
||||
*/
|
||||
export interface DepartmentSelectTreeProps extends Omit<BasicSelectTreeProps, "treeData" | "placeholder"> {
|
||||
/** 请求参数 */
|
||||
params?: Record<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 部门下拉树组件(港务局版本)
|
||||
*/
|
||||
declare const DepartmentSelectTree: FC<DepartmentSelectTreeProps>;
|
||||
|
||||
export default DepartmentSelectTree;
|
||||
|
|
@ -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: <span style={{ color: "#1677ff" }}>sss</span>, id: "0-0-1-0" }],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const getData = async () => {
|
||||
const { data } = await request("/basic-info/department/listTree", "post", params);
|
||||
setTreeData(data);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getData();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<BasicLeftTree treeData={treeData} placeholder="部门" {...restProps} />
|
||||
);
|
||||
}
|
||||
|
||||
DepartmentSelectTree.displayName = "DepartmentSelectTree";
|
||||
|
||||
export default DepartmentSelectTree;
|
||||
|
|
@ -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) => {
|
||||
|
|
@ -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状态
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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();
|
||||
|
|
@ -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<T extends Record<string, any> = Record<string, any>>(
|
||||
options: GetTreeNodePathsOptions,
|
||||
): T[] | null;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue