@fe-free/core 2.1.0 → 2.1.2

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,19 @@
1
1
  # @fe-free/core
2
2
 
3
+ ## 2.1.2
4
+
5
+ ### Patch Changes
6
+
7
+ - feat: crud
8
+ - @fe-free/tool@2.1.2
9
+
10
+ ## 2.1.1
11
+
12
+ ### Patch Changes
13
+
14
+ - feat: crud
15
+ - @fe-free/tool@2.1.1
16
+
3
17
  ## 2.1.0
4
18
 
5
19
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fe-free/core",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
4
4
  "description": "",
5
5
  "main": "./src/index.ts",
6
6
  "author": "",
@@ -29,6 +29,7 @@
29
29
  "axios": "^1.6.5",
30
30
  "classnames": "^2.5.1",
31
31
  "github-markdown-css": "^5.8.1",
32
+ "localforage": "^1.10.0",
32
33
  "lodash-es": "^4.17.21",
33
34
  "react-ace": "^11.0.1",
34
35
  "react-markdown": "^9.1.0",
@@ -38,7 +39,7 @@
38
39
  "remark-gfm": "^4.0.1",
39
40
  "vanilla-jsoneditor": "^0.23.1",
40
41
  "zustand": "^4.5.4",
41
- "@fe-free/tool": "2.1.0"
42
+ "@fe-free/tool": "2.1.2"
42
43
  },
43
44
  "peerDependencies": {
44
45
  "@ant-design/pro-components": "^2.8.7",
@@ -183,6 +183,14 @@ export const MoreCustom: Story = {
183
183
  },
184
184
  }}
185
185
  createButton={<Button type="primary">自定义新建文本</Button>}
186
+ readProps={{
187
+ operateIsDisabled: (record) => {
188
+ if (record.id % 3) {
189
+ return false;
190
+ }
191
+ return true;
192
+ },
193
+ }}
186
194
  requestDeleteByRecord={fakeDeleteByRecord}
187
195
  deleteProps={{
188
196
  nameIndex: 'name',
@@ -214,6 +222,12 @@ export const MoreCustom: Story = {
214
222
  }
215
223
  return true;
216
224
  },
225
+ operateIsHidden: (record) => {
226
+ if (record.id % 4) {
227
+ return false;
228
+ }
229
+ return true;
230
+ },
217
231
  }}
218
232
  />
219
233
  );
package/src/crud/crud.tsx CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { ActionType } from '@ant-design/pro-components';
2
2
  import { Button, message } from 'antd';
3
+ import classNames from 'classnames';
3
4
  import { isString } from 'lodash-es';
4
5
  import { forwardRef, useCallback, useImperativeHandle, useMemo, useRef } from 'react';
5
6
  import { Link } from 'react-router-dom';
@@ -112,67 +113,97 @@ function CRUDComponent<
112
113
  const btns: React.ReactNode[] = [];
113
114
 
114
115
  if (actions.includes('read')) {
115
- btns.push(
116
- <CRUDDetail
117
- key="read"
118
- id={record[idField]}
119
- record={record}
120
- onSuccess={handleReload}
121
- trigger={<a>{readProps?.operateText || '查看'}</a>}
122
- action="read"
123
- {...detailProps}
124
- />,
125
- );
116
+ const hidden = readProps?.operateIsHidden?.(record) || false;
117
+ if (!hidden) {
118
+ const disabled = readProps?.operateIsDisabled?.(record) || false;
119
+ if (disabled) {
120
+ btns.push(
121
+ <span key="read" className="text-desc cursor-not-allowed">
122
+ {readProps?.operateText || '查看'}
123
+ </span>,
124
+ );
125
+ } else {
126
+ btns.push(
127
+ <CRUDDetail
128
+ key="read"
129
+ id={record[idField]}
130
+ record={record}
131
+ onSuccess={handleReload}
132
+ trigger={<a>{readProps?.operateText || '查看'}</a>}
133
+ action="read"
134
+ {...detailProps}
135
+ />,
136
+ );
137
+ }
138
+ }
126
139
  }
127
140
 
128
141
  if (actions.includes('read_detail')) {
129
- btns.push(
130
- <Link
131
- key="read_detail"
132
- to={`./detail/${record[detailIdIndex || 'id']}`}
133
- target={readProps?.target}
134
- >
135
- {readProps?.operateText || '查看'}
136
- </Link>,
137
- );
142
+ const hidden = readProps?.operateIsHidden?.(record) || false;
143
+ if (!hidden) {
144
+ const disabled = readProps?.operateIsDisabled?.(record) || false;
145
+ if (disabled) {
146
+ btns.push(
147
+ <span key="read" className="text-desc cursor-not-allowed">
148
+ {readProps?.operateText || '查看'}
149
+ </span>,
150
+ );
151
+ } else {
152
+ btns.push(
153
+ <Link
154
+ key="read_detail"
155
+ to={`./detail/${record[detailIdIndex || 'id']}`}
156
+ target={readProps?.target}
157
+ >
158
+ {readProps?.operateText || '查看'}
159
+ </Link>,
160
+ );
161
+ }
162
+ }
138
163
  }
139
164
 
140
165
  if (actions.includes('update')) {
141
- const disabled = updateProps?.operateIsDisabled?.(record) || false;
166
+ const hidden = updateProps?.operateIsHidden?.(record) || false;
167
+ if (!hidden) {
168
+ const disabled = updateProps?.operateIsDisabled?.(record) || false;
142
169
 
143
- if (disabled) {
144
- btns.push(
145
- <span key="update" className="text-desc cursor-not-allowed">
146
- {updateProps?.operateText || '编辑'}
147
- </span>,
148
- );
149
- } else {
150
- btns.push(
151
- <CRUDDetail
152
- key="update"
153
- id={record[idField]}
154
- record={record}
155
- onSuccess={handleReload}
156
- trigger={<a>{updateProps?.operateText || '编辑'}</a>}
157
- action="update"
158
- {...detailProps}
159
- />,
160
- );
170
+ if (disabled) {
171
+ btns.push(
172
+ <span key="update" className="text-desc cursor-not-allowed">
173
+ {updateProps?.operateText || '编辑'}
174
+ </span>,
175
+ );
176
+ } else {
177
+ btns.push(
178
+ <CRUDDetail
179
+ key="update"
180
+ id={record[idField]}
181
+ record={record}
182
+ onSuccess={handleReload}
183
+ trigger={<a>{updateProps?.operateText || '编辑'}</a>}
184
+ action="update"
185
+ {...detailProps}
186
+ />,
187
+ );
188
+ }
161
189
  }
162
190
  }
163
191
 
164
192
  if (actions.includes('delete') && deleteProps) {
165
- const disabled = deleteProps?.operateIsDisabled?.(record) || false;
166
- btns.push(
167
- <OperateDelete
168
- key="delete"
169
- name={record[deleteProps.nameIndex]}
170
- desc={deleteProps.desc}
171
- operateText={deleteProps.operateText}
172
- disabled={disabled}
173
- onDelete={getHandleDelete(record)}
174
- />,
175
- );
193
+ const hidden = deleteProps?.operateIsHidden?.(record) || false;
194
+ if (!hidden) {
195
+ const disabled = deleteProps?.operateIsDisabled?.(record) || false;
196
+ btns.push(
197
+ <OperateDelete
198
+ key="delete"
199
+ name={record[deleteProps.nameIndex]}
200
+ desc={deleteProps.desc}
201
+ operateText={deleteProps.operateText}
202
+ disabled={disabled}
203
+ onDelete={getHandleDelete(record)}
204
+ />,
205
+ );
206
+ }
176
207
  }
177
208
 
178
209
  return (
@@ -206,9 +237,8 @@ function CRUDComponent<
206
237
  operateColumnProps,
207
238
  actions,
208
239
  deleteProps,
240
+ readProps,
209
241
  handleReload,
210
- readProps?.operateText,
211
- readProps?.target,
212
242
  detailProps,
213
243
  detailIdIndex,
214
244
  updateProps,
@@ -259,7 +289,7 @@ function CRUDComponent<
259
289
  });
260
290
 
261
291
  return (
262
- <div className="fec-crud">
292
+ <div className={classNames('fec-crud')}>
263
293
  <Table<DataSource>
264
294
  rowKey="id"
265
295
  {...tableProps}
@@ -17,10 +17,45 @@ export const Normal: Story = {
17
17
  render: () => {
18
18
  const columns = [
19
19
  {
20
- title: 'id',
21
- dataIndex: 'id',
20
+ title: '名字(省略)',
21
+ dataIndex: 'name',
22
22
  search: true,
23
+ ellipsis: true,
23
24
  },
25
+ ];
26
+
27
+ return (
28
+ <CRUDOfSimple
29
+ actions={['create', 'delete']}
30
+ tableProps={{
31
+ columns,
32
+ request: fakeRequest,
33
+ pagination: false,
34
+ }}
35
+ requestDeleteByRecord={fakeDeleteByRecord}
36
+ deleteProps={{
37
+ nameIndex: 'name',
38
+ }}
39
+ detailForm={() => (
40
+ <>
41
+ <ProFormText
42
+ name="name"
43
+ label="名字"
44
+ required
45
+ rules={[{ required: true }]}
46
+ extra="extra extra extra extra"
47
+ />
48
+ </>
49
+ )}
50
+ requestCreateByValues={fakeCreate}
51
+ />
52
+ );
53
+ },
54
+ };
55
+
56
+ export const WithSearch: Story = {
57
+ render: () => {
58
+ const columns = [
24
59
  {
25
60
  title: '名字(省略)',
26
61
  dataIndex: 'name',
@@ -53,19 +88,59 @@ export const Normal: Story = {
53
88
  </>
54
89
  )}
55
90
  requestCreateByValues={fakeCreate}
91
+ simpleSearchProps={{
92
+ name: 'name',
93
+ widthFull: true,
94
+ }}
56
95
  />
57
96
  );
58
97
  },
59
98
  };
60
99
 
61
- export const WithSearch: Story = {
100
+ export const HoverShow: Story = {
62
101
  render: () => {
63
102
  const columns = [
64
103
  {
65
- title: 'id',
66
- dataIndex: 'id',
104
+ title: '名字(省略)',
105
+ dataIndex: 'name',
67
106
  search: true,
107
+ ellipsis: true,
68
108
  },
109
+ ];
110
+
111
+ return (
112
+ <CRUDOfSimple
113
+ actions={['create', 'delete']}
114
+ tableProps={{
115
+ columns,
116
+ request: fakeRequest,
117
+ pagination: false,
118
+ }}
119
+ requestDeleteByRecord={fakeDeleteByRecord}
120
+ deleteProps={{
121
+ nameIndex: 'name',
122
+ }}
123
+ detailForm={() => (
124
+ <>
125
+ <ProFormText
126
+ name="name"
127
+ label="名字"
128
+ required
129
+ rules={[{ required: true }]}
130
+ extra="extra extra extra extra"
131
+ />
132
+ </>
133
+ )}
134
+ requestCreateByValues={fakeCreate}
135
+ simpleOperateHoverShow
136
+ />
137
+ );
138
+ },
139
+ };
140
+
141
+ export const JustSearch: Story = {
142
+ render: () => {
143
+ const columns = [
69
144
  {
70
145
  title: '名字(省略)',
71
146
  dataIndex: 'name',
@@ -76,7 +151,7 @@ export const WithSearch: Story = {
76
151
 
77
152
  return (
78
153
  <CRUDOfSimple
79
- actions={['create', 'delete']}
154
+ actions={['delete']}
80
155
  tableProps={{
81
156
  columns,
82
157
  request: fakeRequest,
@@ -99,7 +174,8 @@ export const WithSearch: Story = {
99
174
  )}
100
175
  requestCreateByValues={fakeCreate}
101
176
  simpleSearchProps={{
102
- name: 'id',
177
+ name: 'name',
178
+ widthFull: true,
103
179
  }}
104
180
  />
105
181
  );
@@ -1,5 +1,6 @@
1
1
  import { useDebounce } from 'ahooks';
2
2
  import { Input } from 'antd';
3
+ import classNames from 'classnames';
3
4
  import { forwardRef, useCallback, useEffect, useMemo, useState } from 'react';
4
5
  import { CRUD } from './crud';
5
6
  import type { CRUDMethods, CRUDProps } from './types';
@@ -8,12 +9,15 @@ interface CRUDOfSimpleProps<
8
9
  DataSource extends Record<string, any> = any,
9
10
  Key extends string | number = string,
10
11
  > extends CRUDProps<DataSource, Key> {
12
+ simpleOperateHoverShow?: boolean;
11
13
  // 传才开启搜索
12
14
  simpleSearchProps?: {
13
15
  /** 搜索项的名称,默认 keywords */
14
16
  name: string;
15
17
  /** 搜索项的 placeholder,默认 请输入 */
16
18
  placeholder?: string;
19
+ /** 占满宽度 */
20
+ widthFull?: boolean;
17
21
  };
18
22
  }
19
23
 
@@ -39,12 +43,13 @@ function SearchRender(props: {
39
43
  allowClear
40
44
  value={props.value}
41
45
  onChange={(e) => props.onChange(e.target.value)}
46
+ className="w-full"
42
47
  />
43
48
  );
44
49
  }
45
50
 
46
51
  function CRUDOfSimpleComponent(props: CRUDOfSimpleProps, ref: React.ForwardedRef<CRUDMethods>) {
47
- const { simpleSearchProps, tableProps, ...rest } = props;
52
+ const { simpleSearchProps, tableProps, simpleOperateHoverShow, ...rest } = props;
48
53
 
49
54
  useTips(props);
50
55
  const [searchValue, setSearchValue] = useState<string>('');
@@ -61,15 +66,14 @@ function CRUDOfSimpleComponent(props: CRUDOfSimpleProps, ref: React.ForwardedRef
61
66
  const toolBarRender = useCallback(
62
67
  (...args) => {
63
68
  return [
64
- <div key="search">
65
- {simpleSearchProps && (
66
- <SearchRender
67
- placeholder={simpleSearchProps.placeholder}
68
- value={searchValue}
69
- onChange={(value) => setSearchValue(value)}
70
- />
71
- )}
72
- </div>,
69
+ simpleSearchProps && (
70
+ <SearchRender
71
+ key="search-input"
72
+ placeholder={simpleSearchProps.placeholder}
73
+ value={searchValue}
74
+ onChange={(value) => setSearchValue(value)}
75
+ />
76
+ ),
73
77
  // @ts-ignore
74
78
  ...(tableProps.toolBarRender ? tableProps.toolBarRender(...args) : []),
75
79
  ];
@@ -89,7 +93,12 @@ function CRUDOfSimpleComponent(props: CRUDOfSimpleProps, ref: React.ForwardedRef
89
93
  }, [debouncedSearchValue, simpleSearchProps, tableProps.params]);
90
94
 
91
95
  return (
92
- <div className="fec-crud-of-simple">
96
+ <div
97
+ className={classNames('fec-crud-of-simple', {
98
+ 'fec-crud-of-simple-hover-show': simpleOperateHoverShow,
99
+ 'fec-crud-of-simple-search-width-full': simpleSearchProps?.widthFull,
100
+ })}
101
+ >
93
102
  <CRUD
94
103
  ref={ref}
95
104
  {...rest}
@@ -104,6 +113,11 @@ function CRUDOfSimpleComponent(props: CRUDOfSimpleProps, ref: React.ForwardedRef
104
113
  // 简单的隐藏搜索栏
105
114
  search: false,
106
115
  }}
116
+ operateColumnProps={{
117
+ // hoverShow 情况下,默认 width 1
118
+ width: simpleOperateHoverShow ? 1 : undefined,
119
+ ...props.operateColumnProps,
120
+ }}
107
121
  />
108
122
  </div>
109
123
  );
@@ -13,4 +13,34 @@
13
13
  .ant-pro-table-list-toolbar {
14
14
  border-bottom: 1px solid #f0f0f0;
15
15
  }
16
+
17
+ &.fec-crud-of-simple-hover-show {
18
+ .ant-table-cell-fix-right {
19
+ position: absolute !important;
20
+ display: none;
21
+ }
22
+
23
+ .ant-table-row {
24
+ &:hover {
25
+ .ant-table-cell-fix-right {
26
+ display: block;
27
+ }
28
+ }
29
+ }
30
+ }
31
+
32
+ &.fec-crud-of-simple-search-width-full {
33
+ .ant-pro-table-list-toolbar-container {
34
+ justify-content: unset;
35
+
36
+ .ant-pro-table-list-toolbar-right {
37
+ justify-content: unset;
38
+
39
+ & > div {
40
+ flex: 1;
41
+ margin-right: 8px;
42
+ }
43
+ }
44
+ }
45
+ }
16
46
  }
@@ -62,6 +62,10 @@ interface CRUDProps<DataSource = any, Key = string> {
62
62
  readProps?: {
63
63
  /** 文本 */
64
64
  operateText?: string;
65
+ /** ”查看”是否禁用 */
66
+ operateIsDisabled?: (record: DataSource) => boolean;
67
+ /** ”查看”是否隐藏 */
68
+ operateIsHidden?: (record: DataSource) => boolean;
65
69
  /** 打开方式, action 为 read_detail 有效 */
66
70
  target?: '_blank';
67
71
  /** 保存按钮文本 */
@@ -81,6 +85,8 @@ interface CRUDProps<DataSource = any, Key = string> {
81
85
  operateText?: string;
82
86
  /** ”编辑”是否禁用 */
83
87
  operateIsDisabled?: (record: DataSource) => boolean;
88
+ /** ”编辑”是否隐藏 */
89
+ operateIsHidden?: (record: DataSource) => boolean;
84
90
  /** 保存按钮文本 */
85
91
  submitText?: string;
86
92
  /** 重置按钮文本 */
@@ -99,6 +105,8 @@ interface CRUDProps<DataSource = any, Key = string> {
99
105
  operateText?: string;
100
106
  /** “删除”是否禁用 */
101
107
  operateIsDisabled?: (record: DataSource) => boolean;
108
+ /** ”删除”是否隐藏 */
109
+ operateIsHidden?: (record: DataSource) => boolean;
102
110
  /** 显示名称索引 */
103
111
  nameIndex: keyof DataSource;
104
112
  /** 删除确认描述 */
package/src/index.ts CHANGED
@@ -29,4 +29,5 @@ export {
29
29
  export { Markdown } from './markdown';
30
30
  export { Table } from './table';
31
31
  export type { TableProps } from './table';
32
+ export { useLocalforageState } from './use_localforage_state';
32
33
  export { CustomValueTypeEnum, customValueTypeMap } from './value_type_map';
@@ -0,0 +1,30 @@
1
+ import localforage from 'localforage';
2
+ import { useEffect, useState } from 'react';
3
+
4
+ const useLocalforageState = <T = any,>(
5
+ key: string,
6
+ options: {
7
+ defaultValue?: T;
8
+ },
9
+ ): [T | undefined, (value: T) => void, boolean] => {
10
+ const [ready, setReady] = useState(false);
11
+ const [value, setValue] = useState<T | undefined>(options.defaultValue);
12
+
13
+ useEffect(() => {
14
+ localforage.getItem(key).then((v) => {
15
+ if (v !== undefined) {
16
+ setValue(v as T);
17
+ }
18
+ setReady(true);
19
+ });
20
+ }, [key]);
21
+
22
+ const setValueAndSave = (v: T) => {
23
+ setValue(v);
24
+ localforage.setItem(key, v);
25
+ };
26
+
27
+ return [value, setValueAndSave, ready];
28
+ };
29
+
30
+ export { useLocalforageState };