@fe-free/core 3.0.7 → 3.0.8

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,12 @@
1
1
  # @fe-free/core
2
2
 
3
+ ## 3.0.8
4
+
5
+ ### Patch Changes
6
+
7
+ - feat: crud
8
+ - @fe-free/tool@3.0.8
9
+
3
10
  ## 3.0.7
4
11
 
5
12
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fe-free/core",
3
- "version": "3.0.7",
3
+ "version": "3.0.8",
4
4
  "description": "",
5
5
  "main": "./src/index.ts",
6
6
  "author": "",
@@ -41,7 +41,7 @@
41
41
  "safe-stable-stringify": "^2.5.0",
42
42
  "vanilla-jsoneditor": "^0.23.1",
43
43
  "zustand": "^4.5.4",
44
- "@fe-free/tool": "3.0.7"
44
+ "@fe-free/tool": "3.0.8"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@ant-design/pro-components": "2.8.9",
@@ -0,0 +1,12 @@
1
+ import { themeVariables } from '../theme';
2
+
3
+ const themeConfig = {
4
+ components: {
5
+ Table: {
6
+ headerBg: themeVariables.color.theme02,
7
+ headerBorderRadius: 0,
8
+ },
9
+ },
10
+ };
11
+
12
+ export { themeConfig };
@@ -2,10 +2,12 @@ import { ProConfigProvider } from '@ant-design/pro-components';
2
2
  import { useTitle } from 'ahooks';
3
3
  import { App, ConfigProvider } from 'antd';
4
4
  import zhCN from 'antd/locale/zh_CN';
5
+ import { merge } from 'lodash-es';
5
6
  import { useEffect, useMemo } from 'react';
6
7
  import { BrowserRouter as Router, useNavigate } from 'react-router-dom';
7
8
  import { routeTool } from '../route';
8
9
  import { customValueTypeMap } from '../value_type_map';
10
+ import { themeConfig } from './config';
9
11
 
10
12
  function getPathname(src?: string) {
11
13
  if (!src) {
@@ -122,6 +124,10 @@ function CoreApp(props: {
122
124
  }
123
125
  }, []);
124
126
 
127
+ const theme = useMemo(() => {
128
+ return merge(themeConfig, configProviderProps?.theme);
129
+ }, [configProviderProps?.theme]);
130
+
125
131
  return (
126
132
  <ProConfigProvider
127
133
  {...proConfigProviderProps}
@@ -129,7 +135,11 @@ function CoreApp(props: {
129
135
  valueTypeMap={{ ...customValueTypeMap, ...proConfigProviderProps?.valueTypeMap }}
130
136
  >
131
137
  {/* 集成好 locale */}
132
- <ConfigProvider {...configProviderProps} locale={configProviderProps?.locale || zhCN}>
138
+ <ConfigProvider
139
+ {...configProviderProps}
140
+ locale={configProviderProps?.locale || zhCN}
141
+ theme={theme}
142
+ >
133
143
  <App {...appProps}>
134
144
  <Router {...routerProps} basename={basename}>
135
145
  <SetRouteTool basename={basename} />
@@ -570,3 +570,60 @@ export const ExpandedRowRender: Story = {
570
570
  );
571
571
  },
572
572
  };
573
+
574
+ export const FullPage: Story = {
575
+ render: () => {
576
+ const columns = [
577
+ {
578
+ title: 'id',
579
+ dataIndex: 'id',
580
+ search: true,
581
+ },
582
+ {
583
+ title: '名字(省略)',
584
+ dataIndex: 'name',
585
+ search: true,
586
+ ellipsis: true,
587
+ },
588
+ {
589
+ title: 'city',
590
+ dataIndex: 'city',
591
+ },
592
+ {
593
+ title: 'area',
594
+ dataIndex: 'area',
595
+ },
596
+ ];
597
+
598
+ return (
599
+ <div className="h-[800px] border border-red-500">
600
+ <CRUD
601
+ fullPage
602
+ actions={['create', 'read', 'delete', 'update']}
603
+ tableProps={{
604
+ columns,
605
+ request: fakeRequest,
606
+ }}
607
+ requestDeleteByRecord={fakeDeleteByRecord}
608
+ deleteProps={{
609
+ nameIndex: 'name',
610
+ }}
611
+ detailForm={() => (
612
+ <>
613
+ <ProFormText
614
+ name="name"
615
+ label="名字"
616
+ required
617
+ rules={[{ required: true }]}
618
+ extra="extra extra extra extra"
619
+ />
620
+ </>
621
+ )}
622
+ requestGetByRecord={fakeGetByRecord}
623
+ requestCreateByValues={fakeCreate}
624
+ requestUpdateById={fakeUpdateById}
625
+ />
626
+ </div>
627
+ );
628
+ },
629
+ };
package/src/crud/crud.tsx CHANGED
@@ -1,3 +1,4 @@
1
+ import { EditOutlined, EyeOutlined } from '@ant-design/icons';
1
2
  import type { ActionType } from '@ant-design/pro-components';
2
3
  import { Button, message } from 'antd';
3
4
  import classNames from 'classnames';
@@ -5,7 +6,7 @@ import { isString } from 'lodash-es';
5
6
  import { forwardRef, useCallback, useImperativeHandle, useMemo, useRef } from 'react';
6
7
  import { Link } from 'react-router-dom';
7
8
  import type { TableProps } from '../table';
8
- import { Table } from '../table';
9
+ import { getTableScroll, Table } from '../table';
9
10
  import { OperateDelete } from './crud_delete';
10
11
  import { CRUDDetail } from './crud_detail';
11
12
  import './style.scss';
@@ -36,6 +37,8 @@ function CRUDComponent<
36
37
  detailFormInstance,
37
38
  requestDeleteByRecords,
38
39
  batchActions: originBatchActions,
40
+ fullPage,
41
+ className,
39
42
  } = props;
40
43
 
41
44
  useTips(props);
@@ -108,7 +111,7 @@ function CRUDComponent<
108
111
  const operateColumn = {
109
112
  title: '操作',
110
113
  fixed: 'right',
111
- width: operateColumnProps?.width || 120,
114
+ width: operateColumnProps?.width || 80,
112
115
  render: function (_, record) {
113
116
  const btns: React.ReactNode[] = [];
114
117
 
@@ -119,7 +122,7 @@ function CRUDComponent<
119
122
  if (disabled) {
120
123
  btns.push(
121
124
  <span key="read" className="cursor-not-allowed text-03">
122
- {readProps?.operateText || '查看'}
125
+ {readProps?.operateText || <EyeOutlined />}
123
126
  </span>,
124
127
  );
125
128
  } else {
@@ -129,7 +132,7 @@ function CRUDComponent<
129
132
  id={record[idField]}
130
133
  record={record}
131
134
  onSuccess={handleReload}
132
- trigger={<a>{readProps?.operateText || '查看'}</a>}
135
+ trigger={<a>{readProps?.operateText || <EyeOutlined />}</a>}
133
136
  action="read"
134
137
  {...detailProps}
135
138
  />,
@@ -145,7 +148,7 @@ function CRUDComponent<
145
148
  if (disabled) {
146
149
  btns.push(
147
150
  <span key="read" className="cursor-not-allowed text-03">
148
- {readProps?.operateText || '查看'}
151
+ {readProps?.operateText || <EyeOutlined />}
149
152
  </span>,
150
153
  );
151
154
  } else {
@@ -155,7 +158,7 @@ function CRUDComponent<
155
158
  to={`./detail/${record[detailIdIndex || 'id']}`}
156
159
  target={readProps?.target}
157
160
  >
158
- {readProps?.operateText || '查看'}
161
+ {readProps?.operateText || <EyeOutlined />}
159
162
  </Link>,
160
163
  );
161
164
  }
@@ -170,7 +173,7 @@ function CRUDComponent<
170
173
  if (disabled) {
171
174
  btns.push(
172
175
  <span key="update" className="cursor-not-allowed text-03">
173
- {updateProps?.operateText || '编辑'}
176
+ {updateProps?.operateText || <EditOutlined />}
174
177
  </span>,
175
178
  );
176
179
  } else {
@@ -180,7 +183,7 @@ function CRUDComponent<
180
183
  id={record[idField]}
181
184
  record={record}
182
185
  onSuccess={handleReload}
183
- trigger={<a>{updateProps?.operateText || '编辑'}</a>}
186
+ trigger={<a>{updateProps?.operateText || <EditOutlined />}</a>}
184
187
  action="update"
185
188
  {...detailProps}
186
189
  />,
@@ -289,11 +292,33 @@ function CRUDComponent<
289
292
  actionRef,
290
293
  });
291
294
 
295
+ const newTableProps = useMemo(() => {
296
+ if (fullPage) {
297
+ return {
298
+ ...tableProps,
299
+ scroll: {
300
+ ...getTableScroll(tableProps.columns),
301
+ y: '100%',
302
+ },
303
+ };
304
+ }
305
+
306
+ return tableProps;
307
+ }, [tableProps, fullPage]);
308
+
292
309
  return (
293
- <div className={classNames('fec-crud')}>
310
+ <div
311
+ className={classNames(
312
+ 'fec-crud',
313
+ {
314
+ 'fec-crud-full-page': fullPage,
315
+ },
316
+ className,
317
+ )}
318
+ >
294
319
  <Table<DataSource>
295
320
  rowKey="id"
296
- {...tableProps}
321
+ {...newTableProps}
297
322
  actionRef={actionRef}
298
323
  toolBarRender={toolBarRender}
299
324
  columns={newColumns}
@@ -1,3 +1,4 @@
1
+ import { DeleteOutlined } from '@ant-design/icons';
1
2
  import { Modal } from 'antd';
2
3
  import { useCallback } from 'react';
3
4
 
@@ -39,12 +40,12 @@ function OperateDelete(props: Params) {
39
40
  const { doDelete } = useDelete({ name, desc, onDelete, operateText });
40
41
 
41
42
  if (disabled) {
42
- return <span className="cursor-not-allowed text-03">{operateText || '删除'}</span>;
43
+ return <span className="cursor-not-allowed text-03">{operateText || <DeleteOutlined />}</span>;
43
44
  }
44
45
 
45
46
  return (
46
47
  <a style={{ color: 'red' }} onClick={doDelete}>
47
- {operateText || '删除'}
48
+ {operateText || <DeleteOutlined />}
48
49
  </a>
49
50
  );
50
51
  }
@@ -8,3 +8,65 @@
8
8
  }
9
9
  }
10
10
  }
11
+
12
+ .fec-crud.fec-crud-full-page {
13
+ height: 100%;
14
+ display: flex;
15
+ flex-direction: column;
16
+
17
+ .fec-table {
18
+ height: 100%;
19
+ display: flex;
20
+ flex-direction: column;
21
+
22
+ & > .ant-pro-table-search {
23
+ flex-shrink: 0;
24
+ }
25
+
26
+ & > .ant-pro-card:not(.ant-pro-table-search) {
27
+ flex: 1;
28
+ overflow: auto;
29
+ display: flex;
30
+
31
+ & > .ant-pro-card-body {
32
+ height: 100%;
33
+ display: flex;
34
+ flex-direction: column;
35
+ }
36
+ }
37
+
38
+ .ant-table-wrapper {
39
+ flex: 1;
40
+ overflow: auto;
41
+
42
+ .ant-spin-nested-loading {
43
+ height: 100%;
44
+
45
+ .ant-spin-container {
46
+ height: 100%;
47
+ display: flex;
48
+ flex-direction: column;
49
+ }
50
+ }
51
+ }
52
+
53
+ .ant-table {
54
+ flex: 1;
55
+ overflow: auto;
56
+
57
+ .ant-table-container {
58
+ height: 100%;
59
+ display: flex;
60
+ flex-direction: column;
61
+
62
+ .ant-table-header {
63
+ flex-shrink: 0;
64
+ }
65
+
66
+ .ant-table-body {
67
+ scrollbar-width: thin;
68
+ }
69
+ }
70
+ }
71
+ }
72
+ }
@@ -135,6 +135,9 @@ interface CRUDProps<DataSource = any, Key = string> {
135
135
  options: { selectedRowKeys: Key[]; selectedRows: DataSource[] },
136
136
  ) => Promise<void>;
137
137
  }[];
138
+
139
+ fullPage?: boolean;
140
+ className?: string;
138
141
  }
139
142
 
140
143
  interface CRUDMethods {
@@ -84,33 +84,34 @@ function CRUDOfListComponent(props: CRUDOfListProps, ref: React.ForwardedRef<CRU
84
84
  }, [debouncedSearchValue, searchDataIndex, tableProps.params]);
85
85
 
86
86
  return (
87
- <div
88
- className={classNames('fec-crud-of-list', {
89
- // 先这样实现
90
- 'fec-crud-of-list-no-toolbar': !searchDataIndex && !props.actions.includes('create'),
91
- })}
92
- >
93
- <CRUD
94
- ref={ref}
95
- {...rest}
96
- tableProps={{
97
- cardBordered: false,
98
- showHeader: false,
99
- ghost: true,
100
- // 简单的隐藏搜索栏
101
- search: false,
102
- pagination: false,
103
- ...tableProps,
104
- params: newParams,
105
- columns: newColumns,
106
- toolBarRender,
107
- }}
108
- operateColumnProps={{
109
- width: 1,
110
- ...props.operateColumnProps,
111
- }}
112
- />
113
- </div>
87
+ <CRUD
88
+ ref={ref}
89
+ {...rest}
90
+ className={classNames(
91
+ 'fec-crud-of-list',
92
+ {
93
+ // 先这样实现
94
+ 'fec-crud-of-list-no-toolbar': !searchDataIndex && !props.actions.includes('create'),
95
+ },
96
+ props.className,
97
+ )}
98
+ tableProps={{
99
+ cardBordered: false,
100
+ showHeader: false,
101
+ ghost: true,
102
+ // 简单的隐藏搜索栏
103
+ search: false,
104
+ pagination: false,
105
+ ...tableProps,
106
+ params: newParams,
107
+ columns: newColumns,
108
+ toolBarRender,
109
+ }}
110
+ operateColumnProps={{
111
+ width: 1,
112
+ ...props.operateColumnProps,
113
+ }}
114
+ />
114
115
  );
115
116
  }
116
117
 
@@ -55,7 +55,6 @@ export const Basic: Story = {
55
55
  tableProps={{
56
56
  columns,
57
57
  request: fakeRequest,
58
- pagination: false,
59
58
  search: {
60
59
  optionRender: (_, __, dom) => {
61
60
  return [
@@ -312,3 +311,70 @@ export const SpecialToolbar2: Story = {
312
311
  );
313
312
  },
314
313
  };
314
+
315
+ export const FullPage: Story = {
316
+ render: () => {
317
+ const columns = [
318
+ {
319
+ title: 'id',
320
+ dataIndex: 'id',
321
+ search: true,
322
+ },
323
+ {
324
+ title: '名字(省略)',
325
+ dataIndex: 'name',
326
+ search: true,
327
+ ellipsis: true,
328
+ },
329
+ {
330
+ title: 'city',
331
+ dataIndex: 'city',
332
+ },
333
+ {
334
+ title: 'area',
335
+ dataIndex: 'area',
336
+ },
337
+ ];
338
+
339
+ return (
340
+ <div className="h-[800px] border border-red-500">
341
+ <CRUDOfPure
342
+ fullPage
343
+ specialSearch
344
+ // specialToolbar
345
+ actions={['create', 'delete']}
346
+ tableProps={{
347
+ columns,
348
+ request: fakeRequest,
349
+ search: {
350
+ optionRender: (_, __, dom) => {
351
+ return [
352
+ ...dom,
353
+ <Button key="1" type="primary" className="ml-2">
354
+ 额外的按钮
355
+ </Button>,
356
+ ];
357
+ },
358
+ },
359
+ }}
360
+ requestDeleteByRecord={fakeDeleteByRecord}
361
+ deleteProps={{
362
+ nameIndex: 'id',
363
+ }}
364
+ detailForm={() => (
365
+ <>
366
+ <ProFormText
367
+ name="name"
368
+ label="名字"
369
+ required
370
+ rules={[{ required: true }]}
371
+ extra="extra extra extra extra"
372
+ />
373
+ </>
374
+ )}
375
+ requestCreateByValues={fakeCreate}
376
+ />
377
+ </div>
378
+ );
379
+ },
380
+ };
@@ -9,6 +9,7 @@ interface CRUDOfPureProps<
9
9
  DataSource extends Record<string, any> = any,
10
10
  Key extends string | number = string,
11
11
  > extends CRUDProps<DataSource, Key> {
12
+ specialSearch?: boolean;
12
13
  /** 特殊位置的 toolbar,向上 margin,是的 search 和 toolbar 一起。仅适用于 search 很少的情况。 */
13
14
  specialToolbar?: boolean;
14
15
  }
@@ -39,30 +40,32 @@ function CRUDOfPureComponent(props: CRUDOfPureProps, ref: React.ForwardedRef<CRU
39
40
  }, [props.tableProps.columns]);
40
41
 
41
42
  return (
42
- <div
43
- className={classNames('fec-crud-of-pure', {
44
- 'fec-crud-of-pure-no-search': noSearch,
45
- 'fec-crud-of-pure-special-toolbar': props.specialToolbar,
46
- })}
47
- >
48
- <CRUD
49
- ref={ref}
50
- {...props}
51
- tableProps={{
52
- cardBordered: false,
53
- ...props.tableProps,
54
- columns: newColumns,
55
- toolBarRender: (...args) => {
56
- let originRender: React.ReactNode[] = [];
43
+ <CRUD
44
+ ref={ref}
45
+ {...props}
46
+ className={classNames(
47
+ 'fec-crud-of-pure',
48
+ {
49
+ 'fec-crud-of-pure-no-search': noSearch,
50
+ 'fec-crud-of-pure-special-search': props.specialSearch,
51
+ 'fec-crud-of-pure-special-toolbar': props.specialToolbar,
52
+ },
53
+ props.className,
54
+ )}
55
+ tableProps={{
56
+ cardBordered: false,
57
+ ...props.tableProps,
58
+ columns: newColumns,
59
+ toolBarRender: (...args) => {
60
+ let originRender: React.ReactNode[] = [];
57
61
 
58
- if (typeof props.tableProps.toolBarRender === 'function') {
59
- originRender = props.tableProps.toolBarRender(...args);
60
- }
61
- return [...originRender, <div key="fake" style={{ height: '32px' }} />];
62
- },
63
- }}
64
- />
65
- </div>
62
+ if (typeof props.tableProps.toolBarRender === 'function') {
63
+ originRender = props.tableProps.toolBarRender(...args);
64
+ }
65
+ return [...originRender, <div key="fake" style={{ height: '32px' }} />];
66
+ },
67
+ }}
68
+ />
66
69
  );
67
70
  }
68
71
 
@@ -49,11 +49,18 @@
49
49
  }
50
50
  }
51
51
 
52
- // &.fec-crud-of-pure-no-toolbar {
53
- // .ant-pro-table-list-toolbar-container {
54
- // padding-block: 0;
55
- // }
56
- // }
52
+ &.fec-crud-of-pure-special-search {
53
+ .ant-pro-table-search {
54
+ position: absolute;
55
+ z-index: 1;
56
+ background: transparent;
57
+ width: auto;
58
+ }
59
+
60
+ .ant-pro-table-list-toolbar-container {
61
+ padding-block-start: 16px;
62
+ }
63
+ }
57
64
 
58
65
  &.fec-crud-of-pure-special-toolbar {
59
66
  .ant-pro-table-list-toolbar {
@@ -1,6 +1,8 @@
1
1
  import type { ParamsType, ProTableProps } from '@ant-design/pro-components';
2
2
  import { ProTable } from '@ant-design/pro-components';
3
+ import classNames from 'classnames';
3
4
  import { useMemo } from 'react';
5
+ import './style.scss';
4
6
 
5
7
  interface TableProps<DataSource = any, Params = any, ValueType = 'text'>
6
8
  extends ProTableProps<DataSource, Params, ValueType> {
@@ -30,9 +32,11 @@ function Table<
30
32
  const newPagination = useMemo(() => {
31
33
  return {
32
34
  showSizeChanger: true,
33
- showQuickJumper: true,
35
+ showTotal: false,
36
+ size: 'default',
37
+ // showQuickJumper: true,
34
38
  ...rest.pagination,
35
- };
39
+ } as ProTableProps<DataSource, Params>['pagination'];
36
40
  }, [rest.pagination]);
37
41
 
38
42
  const hasSearch = !!newColumns?.find((column) => column.search);
@@ -72,6 +76,7 @@ function Table<
72
76
  scroll={getTableScroll(newColumns)}
73
77
  search={newSearch}
74
78
  {...rest}
79
+ className={classNames('fec-table', rest.className)}
75
80
  />
76
81
  );
77
82
  }
@@ -88,5 +93,5 @@ function getTableScroll(columns: ProTableProps<any, any>['columns'], defaultWidt
88
93
  return scroll;
89
94
  }
90
95
 
91
- export { Table };
96
+ export { getTableScroll, Table };
92
97
  export type { TableProps };
@@ -0,0 +1,24 @@
1
+ .fec-table {
2
+ .ant-table.ant-table-middle {
3
+ .ant-table-thead > tr > th {
4
+ padding: 8px;
5
+ }
6
+ }
7
+
8
+ .ant-pagination {
9
+ // gap: 8px;
10
+
11
+ .ant-pagination-options {
12
+ order: -1;
13
+ margin-right: 8px;
14
+ }
15
+
16
+ .ant-pagination-item-link {
17
+ border: 1px solid #e2e7f0;
18
+ }
19
+
20
+ .ant-pagination-item {
21
+ border: 1px solid #e2e7f0;
22
+ }
23
+ }
24
+ }