@fe-free/core 1.2.3 → 1.3.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 CHANGED
@@ -1,5 +1,23 @@
1
1
  # @fe-free/core
2
2
 
3
+ ## 1.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - feat: some
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies
12
+ - @fe-free/tool@1.3.0
13
+
14
+ ## 1.2.4
15
+
16
+ ### Patch Changes
17
+
18
+ - Updated dependencies
19
+ - @fe-free/tool@1.2.4
20
+
3
21
  ## 1.2.3
4
22
 
5
23
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fe-free/core",
3
- "version": "1.2.3",
3
+ "version": "1.3.0",
4
4
  "description": "",
5
5
  "main": "./src/index.ts",
6
6
  "author": "",
@@ -29,11 +29,11 @@
29
29
  "react-syntax-highlighter": "^15.5.0",
30
30
  "vanilla-jsoneditor": "^0.23.1",
31
31
  "zustand": "^4.5.4",
32
- "@fe-free/tool": "1.2.3"
32
+ "@fe-free/tool": "1.3.0"
33
33
  },
34
34
  "peerDependencies": {
35
- "@ant-design/pro-components": "^2.7.15",
36
- "antd": "^5.20.0",
35
+ "@ant-design/pro-components": "^2.8.7",
36
+ "antd": "^5.25.1",
37
37
  "react": "^18.2.0"
38
38
  },
39
39
  "scripts": {
@@ -0,0 +1,34 @@
1
+ import { LoadingButton } from '@fe-free/core';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+
4
+ const meta: Meta<typeof LoadingButton> = {
5
+ title: '@fe-free/core/LoadingButton',
6
+ component: LoadingButton,
7
+ tags: ['autodocs'],
8
+ };
9
+
10
+ export default meta;
11
+
12
+ type Story = StoryObj<typeof LoadingButton>;
13
+
14
+ export const Resolve: Story = {
15
+ args: {
16
+ children: 'click and resolve',
17
+ onClick: () => {
18
+ return new Promise((resolve) => {
19
+ setTimeout(resolve, 1000);
20
+ });
21
+ },
22
+ },
23
+ };
24
+
25
+ export const Reject = {
26
+ args: {
27
+ children: 'click and reject',
28
+ onClick: () => {
29
+ return new Promise((_, reject) => {
30
+ setTimeout(reject, 1000);
31
+ });
32
+ },
33
+ },
34
+ };
@@ -13,7 +13,7 @@ function LoadingButton({ onClick, ...rest }: ButtonProps) {
13
13
  setLoading(false);
14
14
  });
15
15
  },
16
- [onClick]
16
+ [onClick],
17
17
  );
18
18
 
19
19
  return <Button loading={loading} {...rest} onClick={handleClick} />;
@@ -0,0 +1,350 @@
1
+ import type { ProColumns } from '@ant-design/pro-components';
2
+ import { ProForm, ProFormSwitch, ProFormText } from '@ant-design/pro-components';
3
+ import { CRUD, proFormSelectSearchProps } from '@fe-free/core';
4
+ import type { Meta, StoryObj } from '@storybook/react';
5
+ import { Button } from 'antd';
6
+ import { useRef } from 'react';
7
+ import {
8
+ fakeCreate,
9
+ fakeDeleteByRecord,
10
+ fakeGetByRecord,
11
+ fakeRequest,
12
+ fakeRequestArea,
13
+ fakeRequestCity,
14
+ fakeRequestSchool,
15
+ fakeUpdateById,
16
+ levels,
17
+ } from './demo/data';
18
+
19
+ const meta: Meta<typeof CRUD> = {
20
+ title: '@fe-free/core/CRUD',
21
+ component: CRUD,
22
+ tags: ['autodocs'],
23
+ };
24
+
25
+ export default meta;
26
+ type Story = StoryObj<typeof CRUD>;
27
+
28
+ // 基础用法
29
+ export const Normal: Story = {
30
+ render: () => {
31
+ const columns = [
32
+ {
33
+ title: 'id',
34
+ dataIndex: 'id',
35
+ search: true,
36
+ },
37
+ {
38
+ title: '名字(省略)',
39
+ dataIndex: 'name',
40
+ search: true,
41
+ ellipsis: true,
42
+ },
43
+ {
44
+ title: 'city',
45
+ dataIndex: 'city',
46
+ },
47
+ {
48
+ title: 'area',
49
+ dataIndex: 'area',
50
+ },
51
+ ];
52
+
53
+ return (
54
+ <CRUD
55
+ actions={['create', 'read', 'delete', 'update']}
56
+ tableProps={{
57
+ columns,
58
+ request: fakeRequest,
59
+ }}
60
+ requestDeleteByRecord={fakeDeleteByRecord}
61
+ deleteProps={{
62
+ nameIndex: 'name',
63
+ }}
64
+ detailForm={(formProps) => (
65
+ <>
66
+ <ProFormText
67
+ {...formProps}
68
+ name="name"
69
+ label="名字"
70
+ required
71
+ rules={[{ required: true }]}
72
+ extra="extra extra extra extra"
73
+ />
74
+ </>
75
+ )}
76
+ requestGetByRecord={fakeGetByRecord}
77
+ requestCreateByValues={fakeCreate}
78
+ requestUpdateById={fakeUpdateById}
79
+ />
80
+ );
81
+ },
82
+ };
83
+
84
+ // 详情页查看
85
+ export const ReadDetail: Story = {
86
+ render: () => {
87
+ const columns = [
88
+ {
89
+ title: 'id',
90
+ dataIndex: 'id',
91
+ search: true,
92
+ },
93
+ {
94
+ title: '名字',
95
+ dataIndex: 'name',
96
+ search: true,
97
+ },
98
+ ];
99
+
100
+ return (
101
+ <CRUD
102
+ actions={['read_detail']}
103
+ tableProps={{
104
+ columns,
105
+ request: fakeRequest,
106
+ }}
107
+ />
108
+ );
109
+ },
110
+ };
111
+
112
+ // 表格表单和详情表单 ref
113
+ const RefComponent = () => {
114
+ const formRef = useRef<any>();
115
+ const [detailFormInstance] = ProForm.useForm();
116
+
117
+ const columns = [
118
+ {
119
+ title: 'id',
120
+ dataIndex: 'id',
121
+ search: true,
122
+ },
123
+ {
124
+ title: '名字',
125
+ dataIndex: 'name',
126
+ search: true,
127
+ },
128
+ ];
129
+
130
+ return (
131
+ <CRUD
132
+ actions={['create', 'read', 'update']}
133
+ tableProps={{
134
+ formRef,
135
+ columns,
136
+ request: fakeRequest,
137
+ }}
138
+ detailFormInstance={detailFormInstance}
139
+ detailForm={(formProps) => (
140
+ <>
141
+ <ProFormText
142
+ {...formProps}
143
+ name="name"
144
+ label="名字"
145
+ required
146
+ rules={[{ required: true }]}
147
+ initialValue={'default'}
148
+ />
149
+ <ProFormSwitch {...formProps} name="status" label="开启" initialValue={false} />
150
+ </>
151
+ )}
152
+ requestGetByRecord={fakeGetByRecord}
153
+ requestCreateByValues={fakeCreate}
154
+ requestUpdateByValues={fakeUpdateById}
155
+ />
156
+ );
157
+ };
158
+
159
+ export const Ref: Story = {
160
+ render: () => <RefComponent />,
161
+ };
162
+
163
+ // 通过 ref 获取 actionRef
164
+ const ActionRefComponent = () => {
165
+ const ref = useRef<any>();
166
+
167
+ const columns = [
168
+ {
169
+ title: 'id',
170
+ dataIndex: 'id',
171
+ search: true,
172
+ },
173
+ {
174
+ title: '名字',
175
+ dataIndex: 'name',
176
+ search: true,
177
+ },
178
+ ];
179
+
180
+ return (
181
+ <>
182
+ <Button onClick={() => ref.current.getActionRef().current?.reload()}>reload</Button>
183
+ <CRUD
184
+ ref={ref}
185
+ actions={[]}
186
+ tableProps={{
187
+ columns,
188
+ request: fakeRequest,
189
+ }}
190
+ />
191
+ </>
192
+ );
193
+ };
194
+
195
+ export const ActionRef: Story = {
196
+ render: () => <ActionRefComponent />,
197
+ };
198
+
199
+ // 数据 本地&远程&依赖
200
+ export const RemoteData: Story = {
201
+ render: () => {
202
+ const columns: ProColumns<any>[] = [
203
+ {
204
+ title: 'id',
205
+ dataIndex: 'id',
206
+ search: true,
207
+ },
208
+ {
209
+ title: '名字',
210
+ dataIndex: 'name',
211
+ search: true,
212
+ },
213
+ {
214
+ title: '等级(本地数据)',
215
+ dataIndex: 'level',
216
+ search: true,
217
+ valueEnum: levels,
218
+ ...proFormSelectSearchProps,
219
+ },
220
+ {
221
+ title: 'city(远端数据)',
222
+ dataIndex: 'city',
223
+ search: true,
224
+ request: async () => {
225
+ const res = await fakeRequestCity();
226
+ return res.map((item) => ({
227
+ label: item,
228
+ value: item,
229
+ }));
230
+ },
231
+ ...proFormSelectSearchProps,
232
+ },
233
+ {
234
+ title: 'area(联动 city)',
235
+ dataIndex: 'area',
236
+ search: true,
237
+ request: async (params) => {
238
+ const res = await fakeRequestArea(params);
239
+ return res.map((item) => ({
240
+ label: item,
241
+ value: item,
242
+ }));
243
+ },
244
+ dependencies: ['city'],
245
+ ...proFormSelectSearchProps,
246
+ },
247
+ {
248
+ title: '学校(远端数据 label value)',
249
+ dataIndex: 'school',
250
+ search: true,
251
+ valueType: 'select' as const,
252
+ request: () => fakeRequestSchool(),
253
+ ...proFormSelectSearchProps,
254
+ },
255
+ ];
256
+
257
+ return (
258
+ <CRUD
259
+ actions={[]}
260
+ tableProps={{
261
+ columns,
262
+ request: fakeRequest,
263
+ }}
264
+ />
265
+ );
266
+ },
267
+ };
268
+
269
+ // 没有搜索
270
+ export const NoSearch: Story = {
271
+ render: () => {
272
+ const columns = [
273
+ {
274
+ title: 'id',
275
+ dataIndex: 'id',
276
+ },
277
+ {
278
+ title: '名字',
279
+ dataIndex: 'name',
280
+ },
281
+ ];
282
+
283
+ return (
284
+ <CRUD
285
+ actions={[]}
286
+ tableProps={{
287
+ columns,
288
+ request: fakeRequest,
289
+ search: false,
290
+ }}
291
+ />
292
+ );
293
+ },
294
+ };
295
+
296
+ // 自定义文案
297
+ export const CustomText: Story = {
298
+ render: () => {
299
+ const columns = [
300
+ {
301
+ title: 'id',
302
+ dataIndex: 'id',
303
+ search: true,
304
+ },
305
+ {
306
+ title: '名字',
307
+ dataIndex: 'name',
308
+ search: true,
309
+ },
310
+ ];
311
+
312
+ return (
313
+ <CRUD
314
+ actions={['create', 'read', 'delete', 'update']}
315
+ tableProps={{
316
+ columns,
317
+ request: fakeRequest,
318
+ }}
319
+ createButton={<Button type="primary">新建</Button>}
320
+ readProps={{
321
+ operateText: '查看',
322
+ }}
323
+ deleteProps={{
324
+ nameIndex: 'name',
325
+ operateText: '删除',
326
+ desc: '确定要删除吗?',
327
+ }}
328
+ updateProps={{
329
+ operateText: '编辑',
330
+ successText: '编辑成功',
331
+ }}
332
+ requestDeleteByRecord={fakeDeleteByRecord}
333
+ detailForm={(formProps) => (
334
+ <>
335
+ <ProFormText
336
+ {...formProps}
337
+ name="name"
338
+ label="名字"
339
+ required
340
+ rules={[{ required: true }]}
341
+ />
342
+ </>
343
+ )}
344
+ requestGetByRecord={fakeGetByRecord}
345
+ requestCreateByValues={fakeCreate}
346
+ requestUpdateByValues={fakeUpdateById}
347
+ />
348
+ );
349
+ },
350
+ };
package/src/crud/crud.tsx CHANGED
@@ -1,8 +1,7 @@
1
- import type { ProFormInstance, ActionType } from '@ant-design/pro-components';
2
- import { Space, Button } from 'antd';
1
+ import type { ActionType, ProFormInstance } from '@ant-design/pro-components';
2
+ import { Button, Space } from 'antd';
3
3
  import type { ReactNode } from 'react';
4
4
  import { forwardRef, useCallback, useImperativeHandle, useMemo, useRef } from 'react';
5
- import { Link, useLocation } from 'react-router-dom';
6
5
  import type { TableProps } from '../table';
7
6
  import { Table } from '../table';
8
7
  import { OperateDelete } from './crud_delete';
@@ -65,6 +64,8 @@ interface CRUDProps {
65
64
  };
66
65
 
67
66
  /** 更新接口 */
67
+ requestUpdateByValues?: (values) => Promise<any>;
68
+ /** @deprecated 请使用 requestUpdateByValues 替代 */
68
69
  requestUpdateById?: (values) => Promise<any>;
69
70
  updateProps?: {
70
71
  /** 文本 */
@@ -99,22 +100,20 @@ const CRUD = forwardRef<CRUDMethods, CRUDProps>(function CRUD(props, ref) {
99
100
  createProps,
100
101
  requestCreateByValues,
101
102
  updateProps,
102
- requestUpdateById,
103
+ requestUpdateById: originalRequestUpdateById,
104
+ requestUpdateByValues: originalRequestUpdateByValues,
103
105
  detailFormInstance,
104
106
  } = props;
105
107
 
108
+ const requestUpdateById = originalRequestUpdateByValues || originalRequestUpdateById;
109
+
106
110
  const actionRef = useRef<ActionType>();
107
- const location = useLocation();
108
111
 
109
- useImperativeHandle(
110
- ref,
111
- () => {
112
- return {
113
- getActionRef: () => actionRef,
114
- };
115
- },
116
- [actionRef]
117
- );
112
+ useImperativeHandle(ref, () => {
113
+ return {
114
+ getActionRef: () => actionRef,
115
+ };
116
+ }, [actionRef]);
118
117
 
119
118
  const detailProps = useMemo(
120
119
  () => ({
@@ -134,7 +133,7 @@ const CRUD = forwardRef<CRUDMethods, CRUDProps>(function CRUD(props, ref) {
134
133
  detailFormInstance,
135
134
  createProps,
136
135
  updateProps,
137
- ]
136
+ ],
138
137
  );
139
138
 
140
139
  const getHandleDelete = useCallback(
@@ -149,7 +148,7 @@ const CRUD = forwardRef<CRUDMethods, CRUDProps>(function CRUD(props, ref) {
149
148
  throw new Error('没有传 requestDeleteByRecord');
150
149
  };
151
150
  },
152
- [requestDeleteByRecord]
151
+ [requestDeleteByRecord],
153
152
  );
154
153
 
155
154
  const handleReload = useCallback(() => {
@@ -176,12 +175,9 @@ const CRUD = forwardRef<CRUDMethods, CRUDProps>(function CRUD(props, ref) {
176
175
  />
177
176
  )}
178
177
  {actions.includes('read_detail') && (
179
- <Link
180
- to={`${location.pathname}/detail/${record[detailIdIndex || 'id']}`}
181
- target={readProps?.target}
182
- >
178
+ <a href={`./detail/${record[detailIdIndex || 'id']}`} target={readProps?.target}>
183
179
  {readProps?.operateText || '查看'}
184
- </Link>
180
+ </a>
185
181
  )}
186
182
  {actions.includes('update') && (
187
183
  <CRUDDetail
@@ -230,7 +226,6 @@ const CRUD = forwardRef<CRUDMethods, CRUDProps>(function CRUD(props, ref) {
230
226
  readProps?.operateText,
231
227
  readProps?.target,
232
228
  detailProps,
233
- location.pathname,
234
229
  detailIdIndex,
235
230
  updateProps?.operateText,
236
231
  deleteProps,
@@ -239,6 +234,7 @@ const CRUD = forwardRef<CRUDMethods, CRUDProps>(function CRUD(props, ref) {
239
234
 
240
235
  const toolBarRender = useCallback(
241
236
  (...args) => [
237
+ // @ts-ignore
242
238
  ...(tableProps.toolBarRender ? tableProps.toolBarRender(...args) : []),
243
239
  actions.includes('create') && (
244
240
  <CRUDDetail
@@ -249,7 +245,7 @@ const CRUD = forwardRef<CRUDMethods, CRUDProps>(function CRUD(props, ref) {
249
245
  />
250
246
  ),
251
247
  ],
252
- [actions, createButton, detailProps, handleReload, tableProps]
248
+ [actions, createButton, detailProps, handleReload, tableProps],
253
249
  );
254
250
 
255
251
  return (
@@ -266,4 +262,4 @@ const CRUD = forwardRef<CRUDMethods, CRUDProps>(function CRUD(props, ref) {
266
262
  });
267
263
 
268
264
  export { CRUD };
269
- export type { CRUDProps, CRUDMethods };
265
+ export type { CRUDMethods, CRUDProps };
@@ -1,5 +1,5 @@
1
- import { useCallback } from 'react';
2
1
  import { Modal } from 'antd';
2
+ import { useCallback } from 'react';
3
3
 
4
4
  interface Params {
5
5
  name: string;
@@ -7,6 +7,7 @@ interface Params {
7
7
  operateText?: string;
8
8
  onDelete: () => Promise<any>;
9
9
  }
10
+
10
11
  function useDelete(params: Params) {
11
12
  const { name, desc, onDelete } = params;
12
13
 
@@ -48,4 +49,4 @@ function OperateDelete(props: Params) {
48
49
  );
49
50
  }
50
51
 
51
- export { useDelete, OperateDelete };
52
+ export { OperateDelete, useDelete };
@@ -1,9 +1,9 @@
1
- import { message, Spin } from 'antd';
2
1
  import { DrawerForm, ProForm } from '@ant-design/pro-components';
2
+ import { message, Spin } from 'antd';
3
+ import classNames from 'classnames';
4
+ import { isString } from 'lodash-es';
3
5
  import { useCallback, useMemo, useState } from 'react';
4
6
  import type { CRUDProps } from './crud';
5
- import { isString } from 'lodash-es';
6
- import classNames from 'classnames';
7
7
 
8
8
  /**
9
9
  * create 创建
@@ -100,13 +100,13 @@ function CRUDDetail(props: CRUDDetailProps) {
100
100
  return true;
101
101
  }
102
102
  } catch (e) {
103
- // 由于 onFinish 吃掉了 error,所以这里自行抛出
103
+ // 由于 onFinish 吃掉了 error,所以这里自行抛出到全局
104
104
  setTimeout(() => {
105
105
  throw e;
106
106
  }, 10);
107
107
  }
108
108
  },
109
- [action, requestCreateByValues, requestUpdateById, onSuccess, createProps, id, updateProps]
109
+ [action, requestCreateByValues, requestUpdateById, onSuccess, createProps, id, updateProps],
110
110
  );
111
111
 
112
112
  const handleOpenChange = useCallback(
@@ -132,7 +132,7 @@ function CRUDDetail(props: CRUDDetailProps) {
132
132
 
133
133
  return;
134
134
  },
135
- [form, id, requestGetByRecord, record]
135
+ [form, id, requestGetByRecord, record],
136
136
  );
137
137
 
138
138
  const children = useMemo(() => {
@@ -43,7 +43,7 @@ function makeData(count) {
43
43
 
44
44
  return {
45
45
  id: `${id}`,
46
- name: `这是名字这是名字这是名字 ${id}`,
46
+ name: `这是名字这是名字这是名字这是名字这是名字 ${id}`,
47
47
  city,
48
48
  area,
49
49
  level: randomLevel(),
@@ -131,14 +131,14 @@ function fakeRequestSchool() {
131
131
  }
132
132
 
133
133
  export {
134
+ fakeCreate,
134
135
  fakeData,
135
- levels,
136
- fakeRequest,
137
136
  fakeDeleteByRecord,
138
137
  fakeGetByRecord,
139
- fakeCreate,
140
- fakeUpdateById,
141
- fakeRequestCity,
138
+ fakeRequest,
142
139
  fakeRequestArea,
140
+ fakeRequestCity,
143
141
  fakeRequestSchool,
142
+ fakeUpdateById,
143
+ levels,
144
144
  };