@fe-free/core 1.4.19 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/package.json +2 -2
- package/src/button/button.stories.ts +9 -1
- package/src/crud/crud.stories.tsx +54 -1
- package/src/crud/crud.tsx +4 -4
- package/src/crud/crud_detail.tsx +3 -3
- package/src/crud/crud_of_simple.stories.tsx +1 -1
- package/src/crud/crud_of_simple.tsx +1 -1
- package/src/crud/style.scss +3 -3
- package/src/editor/editor.stories.tsx +1 -1
- package/src/editor_javascript/editor_javascript.stories.tsx +1 -1
- package/src/editor_json/editor_json.stories.tsx +1 -1
- package/src/editor_logs/editor_logs.stories.tsx +1 -1
- package/src/editor_markdown/editor_markdown.stories.tsx +1 -1
- package/src/form/form.stories.tsx +43 -8
- package/src/form/form_list/form_list.tsx +127 -0
- package/src/form/form_list/form_list_helper.tsx +74 -0
- package/src/form/form_list/form_list_modal_helper.tsx +147 -0
- package/src/form/index.tsx +4 -0
- package/src/form/pro_form_editor.tsx +2 -3
- package/src/form/pro_form_javascript.tsx +2 -3
- package/src/form/pro_form_json.tsx +2 -3
- package/src/form/pro_form_switch_number.tsx +1 -2
- package/src/index.ts +4 -0
- package/src/table/index.tsx +9 -5
- package/src/table/table.stories.tsx +41 -26
- package/src/value_type_map/value_type_map.stories.tsx +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @fe-free/core
|
|
2
2
|
|
|
3
|
+
## 1.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- feat: form
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- @fe-free/tool@1.5.0
|
|
12
|
+
|
|
13
|
+
## 1.4.20
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- feat: crud
|
|
18
|
+
- @fe-free/tool@1.4.20
|
|
19
|
+
|
|
3
20
|
## 1.4.19
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fe-free/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"author": "",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"react-syntax-highlighter": "^15.5.0",
|
|
37
37
|
"vanilla-jsoneditor": "^0.23.1",
|
|
38
38
|
"zustand": "^4.5.4",
|
|
39
|
-
"@fe-free/tool": "1.
|
|
39
|
+
"@fe-free/tool": "1.5.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"@ant-design/pro-components": "^2.8.7",
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { LoadingButton } from '@fe-free/core';
|
|
2
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
3
3
|
|
|
4
4
|
const meta: Meta<typeof LoadingButton> = {
|
|
5
5
|
title: '@fe-free/core/LoadingButton',
|
|
6
6
|
component: LoadingButton,
|
|
7
7
|
tags: ['autodocs'],
|
|
8
|
+
parameters: {
|
|
9
|
+
docs: {
|
|
10
|
+
description: {
|
|
11
|
+
component:
|
|
12
|
+
'LoadingButton 是一个带有加载状态的按钮组件,适用于异步操作场景。<br/>区别于 antd Button 需要手动传 loading props。',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
8
16
|
};
|
|
9
17
|
|
|
10
18
|
export default meta;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ProColumns } from '@ant-design/pro-components';
|
|
2
2
|
import { ProForm, ProFormSwitch, ProFormText } from '@ant-design/pro-components';
|
|
3
3
|
import { CRUD, proFormSelectSearchProps } from '@fe-free/core';
|
|
4
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
4
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
5
5
|
import { Button } from 'antd';
|
|
6
6
|
import { useRef } from 'react';
|
|
7
7
|
import {
|
|
@@ -500,3 +500,56 @@ export const RowSelection: Story = {
|
|
|
500
500
|
);
|
|
501
501
|
},
|
|
502
502
|
};
|
|
503
|
+
|
|
504
|
+
export const ExpandedRowRender: Story = {
|
|
505
|
+
render: () => {
|
|
506
|
+
const columns = [
|
|
507
|
+
{
|
|
508
|
+
title: 'id',
|
|
509
|
+
dataIndex: 'id',
|
|
510
|
+
search: true,
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
title: '名字(省略)',
|
|
514
|
+
dataIndex: 'name',
|
|
515
|
+
search: true,
|
|
516
|
+
ellipsis: true,
|
|
517
|
+
},
|
|
518
|
+
];
|
|
519
|
+
|
|
520
|
+
return (
|
|
521
|
+
<CRUD
|
|
522
|
+
actions={['create', 'read', 'delete', 'update']}
|
|
523
|
+
tableProps={{
|
|
524
|
+
columns,
|
|
525
|
+
request: fakeRequest,
|
|
526
|
+
defaultExpandAllRows: true,
|
|
527
|
+
expandable: {
|
|
528
|
+
expandedRowKeys: ['0', '1'],
|
|
529
|
+
expandedRowRender: () => {
|
|
530
|
+
return <div>123</div>;
|
|
531
|
+
},
|
|
532
|
+
},
|
|
533
|
+
}}
|
|
534
|
+
requestDeleteByRecord={fakeDeleteByRecord}
|
|
535
|
+
deleteProps={{
|
|
536
|
+
nameIndex: 'name',
|
|
537
|
+
}}
|
|
538
|
+
detailForm={() => (
|
|
539
|
+
<>
|
|
540
|
+
<ProFormText
|
|
541
|
+
name="name"
|
|
542
|
+
label="名字"
|
|
543
|
+
required
|
|
544
|
+
rules={[{ required: true }]}
|
|
545
|
+
extra="extra extra extra extra"
|
|
546
|
+
/>
|
|
547
|
+
</>
|
|
548
|
+
)}
|
|
549
|
+
requestGetByRecord={fakeGetByRecord}
|
|
550
|
+
requestCreateByValues={fakeCreate}
|
|
551
|
+
requestUpdateById={fakeUpdateById}
|
|
552
|
+
/>
|
|
553
|
+
);
|
|
554
|
+
},
|
|
555
|
+
};
|
package/src/crud/crud.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ActionType } from '@ant-design/pro-components';
|
|
2
|
-
import { Button, message
|
|
2
|
+
import { Button, message } from 'antd';
|
|
3
3
|
import { isString } from 'lodash-es';
|
|
4
4
|
import { forwardRef, useCallback, useImperativeHandle, useMemo, useRef } from 'react';
|
|
5
5
|
import { Link } from 'react-router-dom';
|
|
@@ -176,11 +176,11 @@ function CRUDComponent<
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
return (
|
|
179
|
-
<
|
|
179
|
+
<div className="fec-crud-operate-column flex gap-2">
|
|
180
180
|
{operateColumnProps?.moreOperator && operateColumnProps.moreOperator(record)}
|
|
181
181
|
{btns}
|
|
182
182
|
{operateColumnProps?.moreOperatorAfter && operateColumnProps.moreOperatorAfter(record)}
|
|
183
|
-
</
|
|
183
|
+
</div>
|
|
184
184
|
);
|
|
185
185
|
},
|
|
186
186
|
};
|
|
@@ -259,7 +259,7 @@ function CRUDComponent<
|
|
|
259
259
|
});
|
|
260
260
|
|
|
261
261
|
return (
|
|
262
|
-
<div className="crud
|
|
262
|
+
<div className="fec-crud">
|
|
263
263
|
<Table<DataSource>
|
|
264
264
|
rowKey="id"
|
|
265
265
|
{...tableProps}
|
package/src/crud/crud_detail.tsx
CHANGED
|
@@ -172,8 +172,8 @@ function CRUDDetail(props: CRUDDetailProps) {
|
|
|
172
172
|
|
|
173
173
|
return (
|
|
174
174
|
<div
|
|
175
|
-
className={classNames('crud-detail', `crud-detail-action-${action}`, {
|
|
176
|
-
'crud-detail-hide-extra': action === 'read',
|
|
175
|
+
className={classNames('fec-crud-detail', `fec-crud-detail-action-${action}`, {
|
|
176
|
+
'fec-crud-detail-hide-extra': action === 'read',
|
|
177
177
|
})}
|
|
178
178
|
>
|
|
179
179
|
{detailForm({ readonly: action === 'read' && !!id }, { action })}
|
|
@@ -183,7 +183,7 @@ function CRUDDetail(props: CRUDDetailProps) {
|
|
|
183
183
|
|
|
184
184
|
const drawerProps = useMemo(() => {
|
|
185
185
|
return {
|
|
186
|
-
|
|
186
|
+
destroyOnHidden: true,
|
|
187
187
|
};
|
|
188
188
|
}, []);
|
|
189
189
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ProFormText } from '@ant-design/pro-components';
|
|
2
2
|
import { CRUDOfSimple } from '@fe-free/core';
|
|
3
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
4
4
|
import { fakeCreate, fakeDeleteByRecord, fakeRequest } from './demo/data';
|
|
5
5
|
|
|
6
6
|
const meta: Meta<typeof CRUDOfSimple> = {
|
|
@@ -89,7 +89,7 @@ function CRUDOfSimpleComponent(props: CRUDOfSimpleProps, ref: React.ForwardedRef
|
|
|
89
89
|
}, [debouncedSearchValue, simpleSearchProps, tableProps.params]);
|
|
90
90
|
|
|
91
91
|
return (
|
|
92
|
-
<div className="crud-of-simple">
|
|
92
|
+
<div className="fec-crud-of-simple">
|
|
93
93
|
<CRUD
|
|
94
94
|
ref={ref}
|
|
95
95
|
{...rest}
|
package/src/crud/style.scss
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
.crud-detail {
|
|
1
|
+
.fec-crud-detail {
|
|
2
2
|
// 查看情况下隐藏 FormItem 的 extra
|
|
3
|
-
&.crud-detail-hide-extra {
|
|
3
|
+
&.fec-crud-detail-hide-extra {
|
|
4
4
|
.ant-form-item {
|
|
5
5
|
.ant-form-item-extra {
|
|
6
6
|
display: none;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
.crud-of-simple {
|
|
12
|
+
.fec-crud-of-simple {
|
|
13
13
|
.ant-pro-table-list-toolbar {
|
|
14
14
|
border-bottom: 1px solid #f0f0f0;
|
|
15
15
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { EditorProps } from '@fe-free/core';
|
|
2
2
|
import { Editor } from '@fe-free/core';
|
|
3
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
4
4
|
import { useState } from 'react';
|
|
5
5
|
|
|
6
6
|
const meta: Meta<typeof Editor> = {
|
|
@@ -1,15 +1,31 @@
|
|
|
1
1
|
import { ProForm } from '@ant-design/pro-components';
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
ProFormEditor,
|
|
4
|
+
ProFormJSON,
|
|
5
|
+
ProFormJavascript,
|
|
6
|
+
ProFormListNumber,
|
|
7
|
+
ProFormListText,
|
|
8
|
+
ProFormSwitchNumber,
|
|
9
|
+
} from '@fe-free/core';
|
|
10
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
4
11
|
import { useState } from 'react';
|
|
5
12
|
|
|
6
|
-
const meta: Meta = {
|
|
13
|
+
const meta: Meta<typeof ProForm> = {
|
|
7
14
|
title: '@fe-free/core/Form',
|
|
15
|
+
component: ProForm,
|
|
8
16
|
tags: ['autodocs'],
|
|
17
|
+
parameters: {
|
|
18
|
+
docs: {
|
|
19
|
+
description: {
|
|
20
|
+
component:
|
|
21
|
+
'Form 组件基于 Ant Design ProForm,支持多种表单类型和扩展字段,适用于复杂表单场景。',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
9
25
|
};
|
|
10
26
|
|
|
11
27
|
export default meta;
|
|
12
|
-
type Story = StoryObj<typeof
|
|
28
|
+
type Story = StoryObj<typeof ProForm>;
|
|
13
29
|
|
|
14
30
|
function ProFormBase({
|
|
15
31
|
children,
|
|
@@ -23,9 +39,11 @@ function ProFormBase({
|
|
|
23
39
|
return (
|
|
24
40
|
<ProForm
|
|
25
41
|
initialValues={initialValues}
|
|
26
|
-
onValuesChange={(
|
|
27
|
-
|
|
28
|
-
|
|
42
|
+
onValuesChange={(newValues) => {
|
|
43
|
+
setValues({
|
|
44
|
+
...values,
|
|
45
|
+
...newValues,
|
|
46
|
+
});
|
|
29
47
|
}}
|
|
30
48
|
onFinish={(values) => {
|
|
31
49
|
console.log('values', values);
|
|
@@ -46,7 +64,7 @@ console.log('hello', name);
|
|
|
46
64
|
`,
|
|
47
65
|
}}
|
|
48
66
|
>
|
|
49
|
-
<ProFormEditor name="code" fieldProps={{ language: 'javascript', height: '
|
|
67
|
+
<ProFormEditor name="code" fieldProps={{ language: 'javascript', height: '400px' }} />
|
|
50
68
|
</ProFormBase>
|
|
51
69
|
),
|
|
52
70
|
};
|
|
@@ -101,3 +119,20 @@ export const ProFormSwitchNumberComponent: Story = {
|
|
|
101
119
|
</ProFormBase>
|
|
102
120
|
),
|
|
103
121
|
};
|
|
122
|
+
|
|
123
|
+
export const ProFormListTextComponent: Story = {
|
|
124
|
+
render: () => (
|
|
125
|
+
<ProFormBase>
|
|
126
|
+
<ProFormListText name="listText" />
|
|
127
|
+
</ProFormBase>
|
|
128
|
+
),
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export const ProFormListNumberComponent: Story = {
|
|
132
|
+
render: () => (
|
|
133
|
+
<ProFormBase>
|
|
134
|
+
<ProFormListNumber name="listNumber" label="listNumber" />
|
|
135
|
+
<ProFormListNumber name="listInteger" label="listInteger" fieldProps={{ precision: 0 }} />
|
|
136
|
+
</ProFormBase>
|
|
137
|
+
),
|
|
138
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { ProFormItemProps } from '@ant-design/pro-components';
|
|
2
|
+
import { ProForm } from '@ant-design/pro-components';
|
|
3
|
+
import { Input, InputNumber } from 'antd';
|
|
4
|
+
|
|
5
|
+
import { ProFormListHelper } from './form_list_helper';
|
|
6
|
+
|
|
7
|
+
interface ListTextProps {
|
|
8
|
+
value?: (string | { value: string; label: string })[];
|
|
9
|
+
onChange?: (value: (string | { value: string; label: string })[]) => void;
|
|
10
|
+
isValueLabel?: boolean;
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function ListText(props: ListTextProps) {
|
|
15
|
+
const { isValueLabel, placeholder } = props;
|
|
16
|
+
return (
|
|
17
|
+
<ProFormListHelper value={props.value} onChange={props.onChange} getAdd={() => ''}>
|
|
18
|
+
{({ item, onItemChange }) => {
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
const value = isValueLabel ? item.value : item;
|
|
21
|
+
return (
|
|
22
|
+
<Input
|
|
23
|
+
placeholder={placeholder}
|
|
24
|
+
value={value}
|
|
25
|
+
onChange={(e) => {
|
|
26
|
+
if (isValueLabel) {
|
|
27
|
+
onItemChange({ value: e.target.value, label: e.target.value });
|
|
28
|
+
} else {
|
|
29
|
+
onItemChange(e.target.value);
|
|
30
|
+
}
|
|
31
|
+
}}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}}
|
|
35
|
+
</ProFormListHelper>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface ListNumberProps {
|
|
40
|
+
value?: (number | { value: number; label: string })[];
|
|
41
|
+
onChange?: (value: (number | { value: number; label: string })[]) => void;
|
|
42
|
+
isValueLabel?: boolean;
|
|
43
|
+
placeholder?: string;
|
|
44
|
+
precision?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function ListNumber(props: ListNumberProps) {
|
|
48
|
+
const { isValueLabel, placeholder, precision } = props;
|
|
49
|
+
return (
|
|
50
|
+
<ProFormListHelper value={props.value} onChange={props.onChange} getAdd={() => 0}>
|
|
51
|
+
{({ item, onItemChange }) => {
|
|
52
|
+
// @ts-ignore
|
|
53
|
+
const value = isValueLabel ? item.value : item;
|
|
54
|
+
return (
|
|
55
|
+
<InputNumber
|
|
56
|
+
value={value}
|
|
57
|
+
onChange={(nv) => {
|
|
58
|
+
if (isValueLabel) {
|
|
59
|
+
onItemChange({ value: nv, label: nv.toString() });
|
|
60
|
+
} else {
|
|
61
|
+
onItemChange(nv);
|
|
62
|
+
}
|
|
63
|
+
}}
|
|
64
|
+
precision={precision}
|
|
65
|
+
style={{ width: '100%' }}
|
|
66
|
+
placeholder={placeholder}
|
|
67
|
+
/>
|
|
68
|
+
);
|
|
69
|
+
}}
|
|
70
|
+
</ProFormListHelper>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function ProFormListBase(props) {
|
|
75
|
+
const { fieldProps, ...rest } = props;
|
|
76
|
+
|
|
77
|
+
const isValueLabel = fieldProps?.isValueLabel;
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<ProForm.Item
|
|
81
|
+
{...rest}
|
|
82
|
+
required={props.required ?? true}
|
|
83
|
+
rules={[
|
|
84
|
+
...(props.rules || []),
|
|
85
|
+
// { required: true },
|
|
86
|
+
{
|
|
87
|
+
validator: async (_, value) => {
|
|
88
|
+
if (isValueLabel) {
|
|
89
|
+
if (value?.some((item) => item.value === undefined)) {
|
|
90
|
+
return Promise.reject('每个选项都不能为空');
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
if (value?.some((item) => item === undefined)) {
|
|
94
|
+
return Promise.reject('每个选项都不能为空');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return Promise.resolve();
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
]}
|
|
101
|
+
>
|
|
102
|
+
{props.children}
|
|
103
|
+
</ProForm.Item>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function ProFormListText(props: ProFormItemProps<ListTextProps>) {
|
|
108
|
+
const { fieldProps, ...rest } = props;
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<ProFormListBase {...rest}>
|
|
112
|
+
<ListText {...fieldProps} />
|
|
113
|
+
</ProFormListBase>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function ProFormListNumber(props: ProFormItemProps<ListNumberProps>) {
|
|
118
|
+
const { fieldProps, ...rest } = props;
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<ProFormListBase {...rest}>
|
|
122
|
+
<ListNumber {...fieldProps} />
|
|
123
|
+
</ProFormListBase>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export { ProFormListNumber, ProFormListText };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
|
|
2
|
+
import { Button } from 'antd';
|
|
3
|
+
|
|
4
|
+
interface ProFormListHelperProps<T> {
|
|
5
|
+
value?: T[];
|
|
6
|
+
onChange?: (value: T[]) => void;
|
|
7
|
+
children: (props: {
|
|
8
|
+
value?: T[];
|
|
9
|
+
item: T;
|
|
10
|
+
index: number;
|
|
11
|
+
onItemChange: (newItem: T) => void;
|
|
12
|
+
}) => React.ReactNode;
|
|
13
|
+
addText?: string;
|
|
14
|
+
getAdd: () => T;
|
|
15
|
+
readOnly?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const emptyArr = [];
|
|
19
|
+
function ProFormListHelper<T = any>(props: ProFormListHelperProps<T>) {
|
|
20
|
+
const options = props.value || emptyArr;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div className="flex flex-col gap-2">
|
|
24
|
+
<div className="flex flex-col gap-2">
|
|
25
|
+
{options.map((item, index) => {
|
|
26
|
+
return (
|
|
27
|
+
<div key={index} className="flex">
|
|
28
|
+
<div className="flex-1 overflow-hidden">
|
|
29
|
+
{props.children({
|
|
30
|
+
value: props.value,
|
|
31
|
+
item,
|
|
32
|
+
index,
|
|
33
|
+
onItemChange: (newItem) => {
|
|
34
|
+
const newOptions = [...options];
|
|
35
|
+
newOptions[index] = newItem;
|
|
36
|
+
props.onChange?.(newOptions);
|
|
37
|
+
},
|
|
38
|
+
})}
|
|
39
|
+
</div>
|
|
40
|
+
{!props.readOnly && (
|
|
41
|
+
<Button
|
|
42
|
+
icon={<DeleteOutlined />}
|
|
43
|
+
type="text"
|
|
44
|
+
onClick={() => {
|
|
45
|
+
const newOptions = [...options];
|
|
46
|
+
newOptions.splice(index, 1);
|
|
47
|
+
props.onChange?.(newOptions);
|
|
48
|
+
}}
|
|
49
|
+
/>
|
|
50
|
+
)}
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
})}
|
|
54
|
+
</div>
|
|
55
|
+
{!props.readOnly && (
|
|
56
|
+
<div className="flex justify-center">
|
|
57
|
+
<Button
|
|
58
|
+
size="small"
|
|
59
|
+
icon={<PlusOutlined />}
|
|
60
|
+
onClick={() => {
|
|
61
|
+
const newOptions = [...options];
|
|
62
|
+
newOptions.push(props.getAdd());
|
|
63
|
+
props.onChange?.(newOptions);
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
{props.addText || '添加'}
|
|
67
|
+
</Button>
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export { ProFormListHelper };
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { DeleteOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons';
|
|
2
|
+
import { ModalForm } from '@ant-design/pro-components';
|
|
3
|
+
import { Button } from 'antd';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
|
|
6
|
+
const emptyArr = [];
|
|
7
|
+
|
|
8
|
+
function Edit<T>(props: {
|
|
9
|
+
children: JSX.Element;
|
|
10
|
+
values?: T;
|
|
11
|
+
onChange: (values: T) => void;
|
|
12
|
+
detailForm: JSX.Element;
|
|
13
|
+
editForm?: any;
|
|
14
|
+
disabledAddFormSubmitter?: boolean;
|
|
15
|
+
}) {
|
|
16
|
+
const handleFinish = async (newValues) => {
|
|
17
|
+
props.onChange(newValues as T);
|
|
18
|
+
|
|
19
|
+
return true;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<ModalForm
|
|
24
|
+
title={props.values ? '编辑' : '添加'}
|
|
25
|
+
trigger={props.children}
|
|
26
|
+
onFinish={handleFinish}
|
|
27
|
+
formRef={props?.editForm}
|
|
28
|
+
initialValues={props.values || undefined}
|
|
29
|
+
submitter={
|
|
30
|
+
props.disabledAddFormSubmitter
|
|
31
|
+
? false
|
|
32
|
+
: {
|
|
33
|
+
render: (props, defaultDoms) => {
|
|
34
|
+
return [
|
|
35
|
+
<div
|
|
36
|
+
key="cancel"
|
|
37
|
+
className="pr-[20px] w-[500px] pt-[12px]"
|
|
38
|
+
style={{ borderTop: '1px solid rgba(0, 0, 0, 0.1)' }}
|
|
39
|
+
>
|
|
40
|
+
<div className="custom-button-container">
|
|
41
|
+
{React.Children.map(defaultDoms, (dom) => {
|
|
42
|
+
return React.cloneElement(dom, {
|
|
43
|
+
className:
|
|
44
|
+
'w-[88px] h-[40px] text-base rounded-[0.5rem] ml-3' +
|
|
45
|
+
(dom.props?.className || ''),
|
|
46
|
+
});
|
|
47
|
+
})}
|
|
48
|
+
</div>
|
|
49
|
+
</div>,
|
|
50
|
+
];
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
>
|
|
55
|
+
{props.detailForm}
|
|
56
|
+
</ModalForm>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface ProFormListModalHelperProps<T> {
|
|
61
|
+
value?: T[];
|
|
62
|
+
onChange?: (value: T[]) => void;
|
|
63
|
+
children: (props: { value?: T[]; item: T; index: number }) => React.ReactNode;
|
|
64
|
+
readOnly?: boolean;
|
|
65
|
+
detailForm: JSX.Element;
|
|
66
|
+
// 如果 addForm 存在,则使用 addForm 作为添加按钮
|
|
67
|
+
addForm?: JSX.Element;
|
|
68
|
+
// 禁用添加的提交按钮
|
|
69
|
+
disabledAddFormSubmitter?: boolean;
|
|
70
|
+
|
|
71
|
+
disabledAdd?: boolean;
|
|
72
|
+
// 触发添加
|
|
73
|
+
addTrigger?: JSX.Element;
|
|
74
|
+
|
|
75
|
+
editForm?: any;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function ProFormListModalHelper<T = any>(props: ProFormListModalHelperProps<T>) {
|
|
79
|
+
const options = props.value || emptyArr;
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<div className="flex flex-col gap-2">
|
|
83
|
+
<div className="flex flex-col gap-2">
|
|
84
|
+
{options.map((item, index) => {
|
|
85
|
+
return (
|
|
86
|
+
<div key={index} className="group relative">
|
|
87
|
+
<div className="flex-1">
|
|
88
|
+
{props.children({
|
|
89
|
+
value: props.value,
|
|
90
|
+
item,
|
|
91
|
+
index,
|
|
92
|
+
})}
|
|
93
|
+
</div>
|
|
94
|
+
{!props.readOnly && (
|
|
95
|
+
<div className="absolute right-1 top-1 hidden items-center bg-white group-hover:flex">
|
|
96
|
+
<Edit<T>
|
|
97
|
+
values={item}
|
|
98
|
+
onChange={(newValues) => {
|
|
99
|
+
const newOptions = [...options];
|
|
100
|
+
newOptions[index] = newValues;
|
|
101
|
+
props.onChange?.(newOptions);
|
|
102
|
+
}}
|
|
103
|
+
detailForm={props.detailForm}
|
|
104
|
+
editForm={props?.editForm}
|
|
105
|
+
>
|
|
106
|
+
<Button icon={<EditOutlined />} type="text" size="small" />
|
|
107
|
+
</Edit>
|
|
108
|
+
<Button
|
|
109
|
+
icon={<DeleteOutlined />}
|
|
110
|
+
type="text"
|
|
111
|
+
size="small"
|
|
112
|
+
onClick={() => {
|
|
113
|
+
const newOptions = [...options];
|
|
114
|
+
newOptions.splice(index, 1);
|
|
115
|
+
props.onChange?.(newOptions);
|
|
116
|
+
}}
|
|
117
|
+
/>
|
|
118
|
+
</div>
|
|
119
|
+
)}
|
|
120
|
+
</div>
|
|
121
|
+
);
|
|
122
|
+
})}
|
|
123
|
+
</div>
|
|
124
|
+
{!props.readOnly && !props.disabledAdd && (
|
|
125
|
+
<div className="flex justify-center">
|
|
126
|
+
<Edit<T>
|
|
127
|
+
onChange={(newValues) => {
|
|
128
|
+
props.onChange?.([...options, newValues]);
|
|
129
|
+
}}
|
|
130
|
+
detailForm={props.addForm || props.detailForm}
|
|
131
|
+
editForm={props?.editForm}
|
|
132
|
+
disabledAddFormSubmitter={props.disabledAddFormSubmitter}
|
|
133
|
+
>
|
|
134
|
+
{props.addTrigger || (
|
|
135
|
+
<Button size="small" icon={<PlusOutlined />}>
|
|
136
|
+
添加
|
|
137
|
+
</Button>
|
|
138
|
+
)}
|
|
139
|
+
</Edit>
|
|
140
|
+
</div>
|
|
141
|
+
)}
|
|
142
|
+
</div>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export { ProFormListModalHelper };
|
|
147
|
+
export type { ProFormListModalHelperProps };
|
package/src/form/index.tsx
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
export { ProFormListNumber, ProFormListText } from './form_list/form_list';
|
|
2
|
+
export { ProFormListHelper } from './form_list/form_list_helper';
|
|
3
|
+
export { ProFormListModalHelper } from './form_list/form_list_modal_helper';
|
|
4
|
+
|
|
1
5
|
export { ProFormEditor } from './pro_form_editor';
|
|
2
6
|
export { ProFormJavascript } from './pro_form_javascript';
|
|
3
7
|
export { ProFormJSON } from './pro_form_json';
|
|
@@ -8,12 +8,11 @@ function EditorItem(props: EditorProps) {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
function ProFormEditor(props: ProFormItemProps<EditorProps>) {
|
|
11
|
-
|
|
12
|
-
const { fieldProps, readonly, ...rest } = props;
|
|
11
|
+
const { fieldProps, ...rest } = props;
|
|
13
12
|
|
|
14
13
|
return (
|
|
15
14
|
<ProForm.Item {...rest}>
|
|
16
|
-
<EditorItem
|
|
15
|
+
<EditorItem {...fieldProps} />
|
|
17
16
|
</ProForm.Item>
|
|
18
17
|
);
|
|
19
18
|
}
|
|
@@ -12,12 +12,11 @@ function JavascriptItem(props: EditorJavascriptProps) {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
function ProFormJavascript(props: ProFormItemProps<EditorJavascriptProps>) {
|
|
15
|
-
|
|
16
|
-
const { fieldProps, readonly, ...rest } = props;
|
|
15
|
+
const { fieldProps, ...rest } = props;
|
|
17
16
|
|
|
18
17
|
return (
|
|
19
18
|
<ProForm.Item {...rest}>
|
|
20
|
-
<JavascriptItem
|
|
19
|
+
<JavascriptItem {...(fieldProps as EditorJavascriptProps)} />
|
|
21
20
|
</ProForm.Item>
|
|
22
21
|
);
|
|
23
22
|
}
|
|
@@ -12,12 +12,11 @@ function JSONItem(props: EditorJSONProps) {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
function ProFormJSON(props: ProFormItemProps<EditorJSONProps>) {
|
|
15
|
-
|
|
16
|
-
const { fieldProps, readonly, ...rest } = props;
|
|
15
|
+
const { fieldProps, ...rest } = props;
|
|
17
16
|
|
|
18
17
|
return (
|
|
19
18
|
<ProForm.Item {...rest}>
|
|
20
|
-
<JSONItem
|
|
19
|
+
<JSONItem {...(fieldProps as EditorJSONProps)} />
|
|
21
20
|
</ProForm.Item>
|
|
22
21
|
);
|
|
23
22
|
}
|
|
@@ -25,8 +25,7 @@ function SwitchNumber(props: SwitchNumberProps) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
function ProFormSwitchNumber(props: ProFormItemProps<SwitchNumberProps>) {
|
|
28
|
-
|
|
29
|
-
const { fieldProps, readonly, ...rest } = props;
|
|
28
|
+
const { fieldProps, ...rest } = props;
|
|
30
29
|
|
|
31
30
|
return (
|
|
32
31
|
<ProForm.Item {...rest}>
|
package/src/index.ts
CHANGED
package/src/table/index.tsx
CHANGED
|
@@ -47,11 +47,15 @@ function Table<
|
|
|
47
47
|
columns={newColumns}
|
|
48
48
|
scroll={getTableScroll(newColumns)}
|
|
49
49
|
search={
|
|
50
|
-
hasSearch
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
hasSearch
|
|
51
|
+
? {
|
|
52
|
+
labelWidth: 'auto',
|
|
53
|
+
defaultCollapsed: false,
|
|
54
|
+
...search,
|
|
55
|
+
}
|
|
56
|
+
: {
|
|
57
|
+
...search,
|
|
58
|
+
}
|
|
55
59
|
}
|
|
56
60
|
{...rest}
|
|
57
61
|
/>
|
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
import type { ProColumns } from '@ant-design/pro-components';
|
|
2
2
|
import { Table } from '@fe-free/core';
|
|
3
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
4
4
|
import { fakeData } from '../crud/demo/data';
|
|
5
5
|
|
|
6
|
-
const meta: Meta = {
|
|
6
|
+
const meta: Meta<typeof Table> = {
|
|
7
7
|
title: '@fe-free/core/Table',
|
|
8
|
+
component: Table,
|
|
8
9
|
tags: ['autodocs'],
|
|
10
|
+
parameters: {
|
|
11
|
+
docs: {
|
|
12
|
+
description: {
|
|
13
|
+
component: `Table 基于 ProTable 做了一些封装:<br>
|
|
14
|
+
- props column 需要显示的提供 search: true;
|
|
15
|
+
- 列宽默认 120,和滚动条
|
|
16
|
+
- 搜索样式做了默认设置,具体见代码
|
|
17
|
+
- 页码做了默认设置,具体见代码
|
|
18
|
+
`,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
9
22
|
};
|
|
10
23
|
|
|
11
24
|
export default meta;
|
|
12
|
-
type Story = StoryObj<typeof
|
|
25
|
+
type Story = StoryObj<typeof Table>;
|
|
13
26
|
|
|
14
27
|
// 定义列配置
|
|
15
28
|
const columns: ProColumns[] = [
|
|
@@ -51,30 +64,32 @@ export const Basic: Story = {
|
|
|
51
64
|
),
|
|
52
65
|
};
|
|
53
66
|
|
|
54
|
-
export const
|
|
67
|
+
export const ScrollX: Story = {
|
|
55
68
|
render: () => (
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
69
|
+
<div className="w-[400px]">
|
|
70
|
+
<Table
|
|
71
|
+
rowKey="id"
|
|
72
|
+
columns={[
|
|
73
|
+
{
|
|
74
|
+
title: 'id',
|
|
75
|
+
dataIndex: 'id',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
title: '名字(省略)',
|
|
79
|
+
dataIndex: 'name',
|
|
65
80
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
ellipsis: true,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
title: 'city',
|
|
85
|
+
dataIndex: 'city',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
title: 'area',
|
|
89
|
+
dataIndex: 'area',
|
|
90
|
+
},
|
|
91
|
+
]}
|
|
92
|
+
/>
|
|
93
|
+
</div>
|
|
79
94
|
),
|
|
80
95
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ProConfigProvider } from '@ant-design/pro-components';
|
|
2
2
|
import { CRUD, CustomValueTypeEnum, customValueTypeMap } from '@fe-free/core';
|
|
3
|
-
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
4
4
|
import dayjs from 'dayjs';
|
|
5
5
|
import { range } from 'lodash-es';
|
|
6
6
|
|