@fe-free/core 1.2.4 → 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,16 @@
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
+
3
14
  ## 1.2.4
4
15
 
5
16
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fe-free/core",
3
- "version": "1.2.4",
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.4"
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": {
@@ -1,12 +1,17 @@
1
1
  import { LoadingButton } from '@fe-free/core';
2
+ import type { Meta, StoryObj } from '@storybook/react';
2
3
 
3
- export default {
4
+ const meta: Meta<typeof LoadingButton> = {
4
5
  title: '@fe-free/core/LoadingButton',
5
6
  component: LoadingButton,
6
7
  tags: ['autodocs'],
7
8
  };
8
9
 
9
- export const Default = {
10
+ export default meta;
11
+
12
+ type Story = StoryObj<typeof LoadingButton>;
13
+
14
+ export const Resolve: Story = {
10
15
  args: {
11
16
  children: 'click and resolve',
12
17
  onClick: () => {
@@ -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
  };
@@ -1,23 +1,22 @@
1
- ---
2
- group: 'core'
3
- toc: content
4
- ---
5
-
6
- # EditorJavascript
7
-
8
- ## 代码演示
1
+ import { EditorJavascript } from '@fe-free/core';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import { useState } from 'react';
9
4
 
10
- ### 常规
5
+ const meta: Meta<typeof EditorJavascript> = {
6
+ title: '@fe-free/core/EditorJavascript',
7
+ component: EditorJavascript,
8
+ tags: ['autodocs'],
9
+ };
11
10
 
12
- ```tsx
13
- import { useState } from 'react';
14
- import { EditorJavascript } from '@fe-free/core';
11
+ export default meta;
12
+ type Story = StoryObj<typeof EditorJavascript>;
15
13
 
16
- function Demo() {
14
+ // 常规示例
15
+ const BasicDemo = () => {
17
16
  const [value, setValue] = useState(
18
17
  `const name = 'world';
19
18
  console.log('hello', name);
20
- `
19
+ `,
21
20
  );
22
21
 
23
22
  return (
@@ -25,22 +24,18 @@ console.log('hello', name);
25
24
  <EditorJavascript value={value} onChange={setValue} />
26
25
  </div>
27
26
  );
28
- }
27
+ };
29
28
 
30
- export default Demo;
31
- ```
29
+ export const Basic: Story = {
30
+ render: () => <BasicDemo />,
31
+ };
32
32
 
33
- ### readonly
34
-
35
- ```tsx
36
- import { useState } from 'react';
37
- import { EditorJavascript } from '@fe-free/core';
38
-
39
- function Demo() {
33
+ // readonly 示例
34
+ const ReadonlyDemo = () => {
40
35
  const [value, setValue] = useState(
41
36
  `const name = 'world';
42
37
  console.log('hello', name);
43
- `
38
+ `,
44
39
  );
45
40
 
46
41
  return (
@@ -48,17 +43,8 @@ console.log('hello', name);
48
43
  <EditorJavascript value={value} onChange={setValue} readonly />
49
44
  </div>
50
45
  );
51
- }
52
-
53
- export default Demo;
54
- ```
55
-
56
- ## API
46
+ };
57
47
 
58
- ```tsx | pure
59
- interface EditorJavascriptProps {
60
- value: string;
61
- onChange: (value: string, event?: any) => void;
62
- readonly?: boolean;
63
- }
64
- ```
48
+ export const Readonly: Story = {
49
+ render: () => <ReadonlyDemo />,
50
+ };