Skip to content

Commit 98ac9d1

Browse files
committed
feat(catalogue): change catalogue and add some utils to CRUD treedata
1 parent 2e730f5 commit 98ac9d1

File tree

10 files changed

+544
-430
lines changed

10 files changed

+544
-430
lines changed

src/catalogue/components/catalogue.tsx

Lines changed: 51 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Dropdown, DropdownProps, Form, Input, Tabs } from 'antd';
33
import { BlockHeader } from 'dt-react-component';
44
import { IBlockHeaderProps } from 'dt-react-component/blockHeader';
55

6-
import { InputMode, ITreeNode, useTreeData } from '../useTreeData';
6+
import { ITreeNode } from '../useTreeData';
77
import { CatalogIcon, CloseIcon, DragIcon, EllipsisIcon, SearchIcon } from './icon';
88
import CatalogueTree, { ICatalogueTree } from './tree';
99

@@ -16,36 +16,39 @@ type readOnlyTab = readonly Tab[];
1616

1717
type TabKey<T extends readOnlyTab> = T[number]['key'];
1818

19-
interface NormalCatalogueProps
19+
interface NormalCatalogueProps<U extends Record<string, any> = {}>
2020
extends Pick<IBlockHeaderProps, 'tooltip' | 'addonAfter' | 'addonBefore' | 'title'>,
21-
ICatalogueTree {
21+
ICatalogueTree<U> {
2222
showSearch?: boolean;
2323
edit?: boolean;
2424
placeholder?: string;
2525
loading?: boolean;
26-
onChange?: ReturnType<typeof useTreeData>['onChange'];
27-
overlay?: (item: ITreeNode) => DropdownProps['overlay'];
26+
onCancelSave?: (item: ITreeNode<U>) => void;
27+
overlay?: (item: ITreeNode<U>) => DropdownProps['overlay'];
2828
onSearch?: (value: string) => void;
29-
onSave?: (data: ITreeNode, value: string) => Promise<string | void>;
29+
onSave?: (data: ITreeNode<U>, value: string) => Promise<string | void>;
3030
}
31-
interface TabsCatalogueProps<T extends readOnlyTab> extends NormalCatalogueProps {
31+
interface TabsCatalogueProps<U extends Record<string, any>, T extends readOnlyTab>
32+
extends NormalCatalogueProps<U> {
3233
tabList?: T;
3334
activeTabKey?: TabKey<T>;
3435
defaultTabKey?: TabKey<T>;
3536
onTabChange?: (key: TabKey<T>) => void;
3637
}
3738

38-
export type CatalogueProps<T extends readOnlyTab = any> =
39-
| TabsCatalogueProps<T>
40-
| NormalCatalogueProps;
39+
export type CatalogueProps<U extends Record<string, any> = {}, T extends readOnlyTab = any> =
40+
| TabsCatalogueProps<U, T>
41+
| NormalCatalogueProps<U>;
4142

42-
function isTabMode<T extends readOnlyTab>(
43-
props: CatalogueProps<T>
44-
): props is TabsCatalogueProps<T> {
43+
function isTabMode<U extends Record<string, any> = {}, T extends readOnlyTab = any>(
44+
props: CatalogueProps<U, T>
45+
): props is TabsCatalogueProps<U, T> {
4546
return 'tabList' in props;
4647
}
4748

48-
const Catalogue = <T extends readOnlyTab>(props: CatalogueProps<T>) => {
49+
const Catalogue = <U extends Record<string, any> = {}, T extends readOnlyTab = any>(
50+
props: CatalogueProps<U, T>
51+
) => {
4952
const {
5053
title,
5154
addonBefore = <CatalogIcon style={{ fontSize: 20 }} />,
@@ -56,38 +59,45 @@ const Catalogue = <T extends readOnlyTab>(props: CatalogueProps<T>) => {
5659
edit = true,
5760
treeData,
5861
draggable,
62+
titleRender,
5963
overlay,
60-
onChange,
6164
onSearch,
6265
onSave,
66+
onCancelSave,
6367
...rest
6468
} = props;
6569

6670
const [tabSearch, setTabSearch] = useState(false);
6771

6872
const [form] = Form.useForm();
6973

70-
const loopTree = (data: ITreeNode[]): ITreeNode[] => {
71-
return data?.map((item) => {
72-
const newItem = {
73-
...item,
74-
editable: item?.editable === undefined ? true : item?.editable,
75-
addable: item?.addable === undefined ? true : item?.addable,
76-
deletable: item?.deletable === undefined ? true : item?.deletable,
77-
};
78-
if (item.children) {
79-
return {
80-
...newItem,
81-
title: renderTitle(newItem),
82-
children: loopTree(item.children),
83-
};
84-
}
85-
return {
86-
...newItem,
87-
title: renderTitle(newItem),
88-
children: undefined,
89-
};
90-
});
74+
const defaultTitleRender = (item: ITreeNode<U>) => {
75+
if (item.edit) {
76+
return (
77+
<Form form={form} preserve={false}>
78+
<Form.Item name="catalog_input" initialValue={item?.title as string}>
79+
<Input
80+
size="small"
81+
placeholder={`请输入${title}名称`}
82+
maxLength={100}
83+
autoFocus
84+
onFocus={() => form.setFields([{ name: 'catalog_input', errors: [] }])}
85+
onClick={(e) => e.stopPropagation()}
86+
onBlur={({ target }) => handleInputSubmit(item, target.value)}
87+
onPressEnter={({ target }) =>
88+
handleInputSubmit(item, (target as any).value)
89+
}
90+
/>
91+
</Form.Item>
92+
</Form>
93+
);
94+
}
95+
return (
96+
<div className="tree__title">
97+
<div className="tree__title--text">{item.title}</div>
98+
{edit && renderNodeHover(item)}
99+
</div>
100+
);
91101
};
92102

93103
const renderHeader = () => {
@@ -138,75 +148,16 @@ const Catalogue = <T extends readOnlyTab>(props: CatalogueProps<T>) => {
138148
);
139149
};
140150

141-
const handleInputSubmit = (item: ITreeNode, value: string) => {
151+
const handleInputSubmit = (item: ITreeNode<U>, value: string) => {
142152
if (!value) {
143-
return onChange?.(undefined, undefined);
144-
}
145-
// item 为当前编辑的数据,对于 Append 的情况需要传入父级的 key
146-
if (item.inputMode === InputMode.Append) {
147-
const findAppendParents = (data: ITreeNode[], item: ITreeNode): ITreeNode | null => {
148-
let result: ITreeNode | null = null;
149-
function traverse(node: ITreeNode, parent: ITreeNode | null): void {
150-
if (node.inputMode === 'append' && node.key === item.key && parent) {
151-
result = parent;
152-
}
153-
if (Array.isArray(node.children)) {
154-
node.children.forEach((child) => traverse(child, node));
155-
}
156-
}
157-
data.forEach((item) => traverse(item, null));
158-
return result;
159-
};
160-
const parentItem = findAppendParents(treeData, item);
161-
return (
162-
parentItem &&
163-
onSave?.({ ...parentItem, inputMode: InputMode.Append }, value).then((msg) => {
164-
form.setFields([{ name: 'catalog_input', errors: msg ? [msg] : [] }]);
165-
})
166-
);
153+
return onCancelSave?.(item);
167154
}
168155
onSave?.(item, value).then((msg) => {
169156
form.setFields([{ name: 'catalog_input', errors: msg ? [msg] : [] }]);
170157
});
171158
};
172159

173-
const renderInput = (item: ITreeNode) => {
174-
return (
175-
<div className="tree__title--input">
176-
<Form form={form} preserve={false}>
177-
<Form.Item name="catalog_input">
178-
<Input
179-
defaultValue={item?.title as string}
180-
size="small"
181-
placeholder={`请输入${title}名称`}
182-
maxLength={100}
183-
autoFocus
184-
onFocus={() => form.setFields([{ name: 'catalog_input', errors: [] }])}
185-
onClick={(e) => e.stopPropagation()}
186-
onBlur={({ target }) => handleInputSubmit(item, target.value)}
187-
onPressEnter={({ target }) =>
188-
handleInputSubmit(item, (target as any).value)
189-
}
190-
/>
191-
</Form.Item>
192-
</Form>
193-
</div>
194-
);
195-
};
196-
197-
const renderTitle = (item: ITreeNode) => {
198-
if (item.inputMode) {
199-
return renderInput(item);
200-
}
201-
return (
202-
<div className="tree__title">
203-
<div className="tree__title--text">{item.title}</div>
204-
{edit && renderNodeHover(item)}
205-
</div>
206-
);
207-
};
208-
209-
const renderNodeHover = (item: ITreeNode) => {
160+
const renderNodeHover = (item: ITreeNode<U>) => {
210161
return (
211162
<div
212163
className="tree__title--operation"
@@ -238,8 +189,9 @@ const Catalogue = <T extends readOnlyTab>(props: CatalogueProps<T>) => {
238189
{renderSearch()}
239190
{renderTab()}
240191
<CatalogueTree
241-
treeData={loopTree(treeData)}
192+
treeData={treeData}
242193
draggable={draggable ? { icon: false } : false}
194+
titleRender={titleRender || defaultTitleRender}
243195
{...rest}
244196
/>
245197
</div>

src/catalogue/components/tree.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,32 @@ import { ITreeNode } from '../useTreeData';
66
import { loopTree } from '../utils';
77
import { DownTriangleIcon } from './icon';
88

9-
export interface ICatalogueTree
10-
extends Omit<TreeProps, 'showLine' | 'switcherIcon' | 'showIcon' | 'treeData'> {
9+
export interface ICatalogueTree<T extends Record<string, any> = {}>
10+
extends Omit<TreeProps, 'showLine' | 'switcherIcon' | 'showIcon' | 'treeData' | 'titleRender'> {
1111
loading?: boolean;
12-
treeData: ITreeNode[];
12+
treeData: ITreeNode<T>[];
13+
titleRender?: TreeProps<ITreeNode<T>>['titleRender'];
1314
}
1415

15-
const CatalogueTree = ({ treeData = [], loading = false, ...rest }: ICatalogueTree) => {
16-
const renderTreeData = useMemo(() => loopTree(treeData) || [], [treeData]);
16+
const CatalogueTree = <T extends Record<string, any> = {}>({
17+
treeData = [],
18+
loading = false,
19+
titleRender,
20+
...rest
21+
}: ICatalogueTree<T>) => {
22+
const renderTreeData = useMemo(() => loopTree<T>(treeData) || [], [treeData]);
1723

1824
if (!renderTreeData.length) return <Empty style={{ marginTop: 130 }} />;
1925

2026
return (
2127
<div className="dt-catalogue__tree">
2228
<Spin spinning={loading}>
23-
<Tree
29+
<Tree<ITreeNode<T>>
2430
showLine={{ showLeafIcon: false }}
2531
switcherIcon={<DownTriangleIcon style={{ fontSize: 16 }} />}
2632
showIcon
2733
treeData={renderTreeData}
34+
titleRender={titleRender}
2835
{...rest}
2936
/>
3037
</Spin>

src/catalogue/demos/basic.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export default () => {
5656
const treeData = useTreeData();
5757

5858
useEffect(() => {
59-
treeData.initData(DEFAULT_DATA);
59+
treeData.onChange(DEFAULT_DATA);
6060
}, []);
6161

6262
return (
@@ -68,7 +68,7 @@ export default () => {
6868
treeData={treeData.data}
6969
edit={false}
7070
expandedKeys={treeData.expandedKeys}
71-
onExpand={treeData.setExpandedKeys}
71+
onExpand={treeData.onExpand}
7272
/>
7373
</div>
7474
);

0 commit comments

Comments
 (0)