@fe-free/core 1.2.4 → 1.3.1

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.
@@ -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,18 +7,24 @@ 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
 
13
- const doDelete = useCallback(() => {
14
- Modal.confirm({
15
- title: `确认删除 “${name}” 吗?`,
16
- content: desc || '删除后不可恢复,请谨慎操作',
17
- okText: '确定',
18
- cancelText: '取消',
19
- onOk: () => {
20
- onDelete();
21
- },
14
+ const doDelete = useCallback(async () => {
15
+ await new Promise((resolve) => {
16
+ Modal.confirm({
17
+ title: `确认删除 “${name}” 吗?`,
18
+ content: desc || '删除后不可恢复,请谨慎操作',
19
+ okText: '确定',
20
+ cancelText: '取消',
21
+ onOk: () => {
22
+ resolve(onDelete());
23
+ },
24
+ onCancel: () => {
25
+ resolve(undefined);
26
+ },
27
+ });
22
28
  });
23
29
  }, [name, desc, onDelete]);
24
30
 
@@ -29,23 +35,13 @@ function useDelete(params: Params) {
29
35
 
30
36
  function OperateDelete(props: Params) {
31
37
  const { name, desc, onDelete, operateText } = props;
32
- const handleClick = useCallback(() => {
33
- Modal.confirm({
34
- title: `确认删除 “${name}” 吗?`,
35
- content: desc || '删除后不可恢复,请谨慎操作',
36
- okText: '确定',
37
- cancelText: '取消',
38
- onOk: () => {
39
- onDelete();
40
- },
41
- });
42
- }, [name, desc, onDelete]);
38
+ const { doDelete } = useDelete({ name, desc, onDelete, operateText });
43
39
 
44
40
  return (
45
- <a style={{ color: 'red' }} onClick={handleClick}>
41
+ <a style={{ color: 'red' }} onClick={doDelete}>
46
42
  {operateText || '删除'}
47
43
  </a>
48
44
  );
49
45
  }
50
46
 
51
- export { useDelete, OperateDelete };
47
+ 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,5 +1,5 @@
1
1
  export { CRUD } from './crud';
2
- export type { CRUDProps, CRUDMethods } from './crud';
2
+ export { OperateDelete, useDelete } from './crud_delete';
3
3
  export { CRUDDetail } from './crud_detail';
4
4
  export type { CRUDDetailProps } from './crud_detail';
5
- export { useDelete, OperateDelete } from './crud_delete';
5
+ export type { CRUDMethods, CRUDProps } from './types';
@@ -0,0 +1,98 @@
1
+ import type { ActionType, ProFormInstance } from '@ant-design/pro-components';
2
+ import type { ReactNode } from 'react';
3
+ import type { TableProps } from '../table';
4
+
5
+ /**
6
+ * create 创建
7
+ * read 查看
8
+ * read_detail 详情页查看
9
+ * update 编辑
10
+ * delete 删除
11
+ */
12
+ type CrudAction = 'create' | 'read' | 'read_detail' | 'update' | 'delete';
13
+
14
+ interface CRUDProps<
15
+ DataSource extends Record<string, any> = any,
16
+ Key extends string | number = string,
17
+ > {
18
+ actions: CrudAction[];
19
+
20
+ /** 新建按钮,默认新建 */
21
+ createButton?: ReactNode;
22
+
23
+ /** 表格相关 */
24
+ tableProps: TableProps<DataSource>;
25
+ operateColumnProps?: {
26
+ width?: number;
27
+ /** 扩展操作区域,再其他操作之前 */
28
+ moreOperator?: (record: DataSource) => ReactNode;
29
+ /** 扩展操作区域,在其他操作之后 */
30
+ moreOperatorAfter?: (record: DataSource) => ReactNode;
31
+ };
32
+ readProps?: {
33
+ /** 文本 */
34
+ operateText?: string;
35
+ /** 打开方式, action 为 read_detail 有效 */
36
+ target?: '_blank';
37
+ };
38
+
39
+ /** 删除接口 */
40
+ requestDeleteByRecord?: (record: DataSource) => Promise<any>;
41
+ /** 删除相关 */
42
+ deleteProps?: {
43
+ /** 显示名称索引 */
44
+ nameIndex: keyof DataSource;
45
+ /** 删除确认描述 */
46
+ desc?: string;
47
+ /** 文本 */
48
+ operateText?: string;
49
+ };
50
+
51
+ /** 弹窗表单 */
52
+ detailForm?: (formProps: { readonly: boolean }, info: { action: CrudAction }) => ReactNode;
53
+ /** detailForm 的 formRef */
54
+ detailFormInstance?: ProFormInstance;
55
+
56
+ /** 新增接口 */
57
+ requestCreateByValues?: (values: Partial<DataSource>) => Promise<any>;
58
+ createProps?: {
59
+ /** 成功文案 */
60
+ successText?: string | (() => string);
61
+ };
62
+
63
+ /** 更新接口 */
64
+ requestUpdateByValues?: (values: Partial<DataSource>) => Promise<any>;
65
+ /** @deprecated 请使用 requestUpdateByValues 替代 */
66
+ requestUpdateById?: (values: Partial<DataSource>) => Promise<any>;
67
+ updateProps?: {
68
+ /** 文本 */
69
+ operateText?: string;
70
+ /** 成功文案 */
71
+ successText?: string | (() => string);
72
+ };
73
+
74
+ /** 获取详情接口 */
75
+ requestGetByRecord?: (record: DataSource) => Promise<any>;
76
+
77
+ /** 跳转到详情的索引 ,默认 id */
78
+ detailIdIndex?: string;
79
+
80
+ /** 批量操作 */
81
+ batchActions?: {
82
+ /** 按钮文本 */
83
+ btnText: string;
84
+ /** 红色,且有确认框 */
85
+ danger?: boolean;
86
+ /** 批量操作接口 */
87
+ onClick: (
88
+ event: React.MouseEvent<HTMLElement>,
89
+ options: { selectedRowKeys: Key[]; selectedRows: DataSource[] },
90
+ ) => Promise<any>;
91
+ }[];
92
+ }
93
+
94
+ interface CRUDMethods {
95
+ getActionRef: () => React.MutableRefObject<ActionType | undefined>;
96
+ }
97
+
98
+ export type { CRUDMethods, CRUDProps };
@@ -0,0 +1,84 @@
1
+ import { Modal } from 'antd';
2
+ import { useCallback, useMemo } from 'react';
3
+ import { LoadingButton } from '../button';
4
+ import type { CRUDProps } from './types';
5
+
6
+ function useRowSelection<
7
+ DataSource extends Record<string, any> = any,
8
+ Key extends string | number = string,
9
+ >({ batchActions }: { batchActions?: CRUDProps<DataSource, Key>['batchActions'] }) {
10
+ const rowSelection = useMemo(() => ({}), []);
11
+
12
+ const tableAlertRender = useCallback(({ selectedRowKeys, onCleanSelected }) => {
13
+ return (
14
+ <div>
15
+ <span>
16
+ 已选 {selectedRowKeys.length} 项
17
+ <a style={{ marginInlineStart: 8 }} onClick={onCleanSelected}>
18
+ 取消选择
19
+ </a>
20
+ </span>
21
+ </div>
22
+ );
23
+ }, []);
24
+
25
+ const tableAlertOptionRender = useCallback(
26
+ ({ selectedRowKeys, selectedRows }) => {
27
+ return (
28
+ <div className="flex gap-2 items-center">
29
+ {batchActions?.map((action) => (
30
+ <LoadingButton
31
+ key={action.btnText}
32
+ type="link"
33
+ danger={action.danger}
34
+ onClick={async (event) => {
35
+ if (action.danger) {
36
+ await new Promise((resolve) => {
37
+ Modal.confirm({
38
+ title: `确定要执行 ${action.btnText} 吗?`,
39
+ onOk: () => {
40
+ resolve(
41
+ action.onClick(event, {
42
+ selectedRowKeys,
43
+ selectedRows,
44
+ }),
45
+ );
46
+ },
47
+ onCancel: () => {
48
+ resolve(undefined);
49
+ },
50
+ });
51
+ });
52
+ } else {
53
+ await action.onClick(event, {
54
+ selectedRowKeys,
55
+ selectedRows,
56
+ });
57
+ }
58
+ }}
59
+ >
60
+ {action.btnText}
61
+ </LoadingButton>
62
+ ))}
63
+ </div>
64
+ );
65
+ },
66
+ [batchActions],
67
+ );
68
+
69
+ if (!batchActions || batchActions.length === 0) {
70
+ return {
71
+ rowSelection: undefined,
72
+ tableAlertRender: undefined,
73
+ tableAlertOptionRender: undefined,
74
+ };
75
+ }
76
+
77
+ return {
78
+ rowSelection,
79
+ tableAlertRender,
80
+ tableAlertOptionRender,
81
+ };
82
+ }
83
+
84
+ export { useRowSelection };
@@ -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
+ };
@@ -1,8 +1,8 @@
1
1
  import AceEditor from 'react-ace';
2
2
 
3
+ import 'ace-builds/src-noconflict/ext-language_tools';
3
4
  import 'ace-builds/src-noconflict/mode-javascript';
4
5
  import 'ace-builds/src-noconflict/theme-monokai';
5
- import 'ace-builds/src-noconflict/ext-language_tools';
6
6
 
7
7
  const fullStyle = { width: '100%', height: '100%' };
8
8
 
@@ -34,3 +34,4 @@ function EditorJavascript({ value, onChange, readonly }: EditorJavascriptProps)
34
34
  }
35
35
 
36
36
  export { EditorJavascript };
37
+ export type { EditorJavascriptProps };
@@ -1,27 +1,26 @@
1
- ---
2
- group: 'core'
3
- toc: content
4
- ---
5
-
6
- # EditorJSON
7
-
8
- ## 代码演示
1
+ import { EditorJSON } 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 EditorJSON> = {
6
+ title: '@fe-free/core/EditorJSON',
7
+ component: EditorJSON,
8
+ tags: ['autodocs'],
9
+ };
11
10
 
12
- ```tsx
13
- import { useState } from 'react';
14
- import { EditorJSON } from '@fe-free/core';
11
+ export default meta;
12
+ type Story = StoryObj<typeof EditorJSON>;
15
13
 
16
- function Demo() {
14
+ // 基础示例
15
+ const BasicDemo = () => {
17
16
  const [value, setValue] = useState(
18
17
  JSON.stringify(
19
18
  {
20
19
  name: 'world',
21
20
  },
22
21
  null,
23
- 2
24
- )
22
+ 2,
23
+ ),
25
24
  );
26
25
 
27
26
  return (
@@ -29,26 +28,22 @@ function Demo() {
29
28
  <EditorJSON value={value} onChange={setValue} />
30
29
  </div>
31
30
  );
32
- }
31
+ };
33
32
 
34
- export default Demo;
35
- ```
33
+ export const Basic: Story = {
34
+ render: () => <BasicDemo />,
35
+ };
36
36
 
37
- ### 只读
38
-
39
- ```tsx
40
- import { useState } from 'react';
41
- import { EditorJSON } from '@fe-free/core';
42
-
43
- function Demo() {
37
+ // 只读模式
38
+ const ReadonlyDemo = () => {
44
39
  const [value, setValue] = useState(
45
40
  JSON.stringify(
46
41
  {
47
42
  name: 'world',
48
43
  },
49
44
  null,
50
- 2
51
- )
45
+ 2,
46
+ ),
52
47
  );
53
48
 
54
49
  return (
@@ -56,26 +51,22 @@ function Demo() {
56
51
  <EditorJSON value={value} onChange={setValue} readonly />
57
52
  </div>
58
53
  );
59
- }
60
-
61
- export default Demo;
62
- ```
54
+ };
63
55
 
64
- ### mode
56
+ export const Readonly: Story = {
57
+ render: () => <ReadonlyDemo />,
58
+ };
65
59
 
66
- ```tsx
67
- import { useState } from 'react';
68
- import { EditorJSON } from '@fe-free/core';
69
-
70
- function Demo() {
60
+ // 树形模式
61
+ const TreeModeDemo = () => {
71
62
  const [value, setValue] = useState(
72
63
  JSON.stringify(
73
64
  {
74
65
  name: 'world',
75
66
  },
76
67
  null,
77
- 2
78
- )
68
+ 2,
69
+ ),
79
70
  );
80
71
 
81
72
  return (
@@ -83,26 +74,22 @@ function Demo() {
83
74
  <EditorJSON value={value} onChange={setValue} mode="tree" readonly />
84
75
  </div>
85
76
  );
86
- }
87
-
88
- export default Demo;
89
- ```
90
-
91
- ### readonly mainMenuBar mode
77
+ };
92
78
 
93
- ```tsx
94
- import { useState } from 'react';
95
- import { EditorJSON } from '@fe-free/core';
79
+ export const TreeMode: Story = {
80
+ render: () => <TreeModeDemo />,
81
+ };
96
82
 
97
- function Demo() {
83
+ // 无菜单栏的只读树形模式
84
+ const NoMenuBarDemo = () => {
98
85
  const [value, setValue] = useState(
99
86
  JSON.stringify(
100
87
  {
101
88
  name: 'world',
102
89
  },
103
90
  null,
104
- 2
105
- )
91
+ 2,
92
+ ),
106
93
  );
107
94
 
108
95
  return (
@@ -110,20 +97,8 @@ function Demo() {
110
97
  <EditorJSON value={value} onChange={setValue} readonly mode="tree" mainMenuBar={false} />
111
98
  </div>
112
99
  );
113
- }
114
-
115
- export default Demo;
116
- ```
117
-
118
- ## API
119
-
120
- ```tsx | pure
121
- import type { JSONEditor } from 'vanilla-jsoneditor';
100
+ };
122
101
 
123
- interface EditorJSONProps {
124
- value: string;
125
- onChange: (value: string, event?: any) => void;
126
- readonly?: boolean;
127
- mode?: JSONEditor['mode'];
128
- }
129
- ```
102
+ export const NoMenuBar: Story = {
103
+ render: () => <NoMenuBarDemo />,
104
+ };
@@ -46,3 +46,4 @@ function EditorJSON({ value, onChange, readonly, mode, mainMenuBar }: EditorJSON
46
46
  }
47
47
 
48
48
  export { EditorJSON };
49
+ export type { EditorJSONProps };
@@ -0,0 +1,47 @@
1
+ import { EditorLogs } from '@fe-free/core';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+
4
+ const meta: Meta<typeof EditorLogs> = {
5
+ title: '@fe-free/core/EditorLogs',
6
+ component: EditorLogs,
7
+ tags: ['autodocs'],
8
+ parameters: {
9
+ docs: {
10
+ description: {
11
+ component:
12
+ '一个用于显示日志内容的 React 组件。它使用 CodeMirror 编辑器来呈现日志,提供了语法高亮和主题支持。',
13
+ },
14
+ },
15
+ },
16
+ };
17
+
18
+ export default meta;
19
+ type Story = StoryObj<typeof EditorLogs>;
20
+
21
+ export const Basic: Story = {
22
+ args: {
23
+ logs: [
24
+ {
25
+ timestamp: '2023-01-01 12:00:00',
26
+ level: 'info',
27
+ message: 'This is an info log message.',
28
+ },
29
+ {
30
+ timestamp: '2023-01-01 12:00:00',
31
+ level: 'warn',
32
+ message: 'This is a warning log message.',
33
+ },
34
+ {
35
+ timestamp: '2023-01-01 12:00:00',
36
+ level: 'error',
37
+ message: 'This is an error log message.',
38
+ },
39
+ {
40
+ timestamp: '2023-01-01 12:00:00',
41
+ level: 'system',
42
+ message:
43
+ 'This is a debug log message. This is a debug log message This is a debug log message This is a debug log message This is a debug log message',
44
+ },
45
+ ],
46
+ },
47
+ };
@@ -1,8 +1,8 @@
1
- import React from 'react';
2
- import CodeMirror from '@uiw/react-codemirror';
3
1
  import { StreamLanguage } from '@codemirror/language';
4
- import { createTheme } from '@uiw/codemirror-themes';
5
2
  import { tags as t } from '@lezer/highlight';
3
+ import { createTheme } from '@uiw/codemirror-themes';
4
+ import CodeMirror from '@uiw/react-codemirror';
5
+ import React from 'react';
6
6
 
7
7
  interface EditorLogsProps {
8
8
  logs: {
@@ -67,3 +67,4 @@ const EditorLogs: React.FC<EditorLogsProps> = ({ logs }) => {
67
67
  };
68
68
 
69
69
  export { EditorLogs };
70
+ export type { EditorLogsProps };