@fe-free/core 3.0.15 → 3.0.17

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
+ ## 3.0.17
4
+
5
+ ### Patch Changes
6
+
7
+ - feat: upload
8
+ - @fe-free/tool@3.0.17
9
+
10
+ ## 3.0.16
11
+
12
+ ### Patch Changes
13
+
14
+ - feat: upload
15
+ - @fe-free/tool@3.0.16
16
+
3
17
  ## 3.0.15
4
18
 
5
19
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fe-free/core",
3
- "version": "3.0.15",
3
+ "version": "3.0.17",
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.15"
44
+ "@fe-free/tool": "3.0.17"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@ant-design/pro-components": "2.8.9",
package/src/crud/crud.tsx CHANGED
@@ -5,11 +5,11 @@ import classNames from 'classnames';
5
5
  import { isString } from 'lodash-es';
6
6
  import { useCallback, useImperativeHandle, useMemo, useRef } from 'react';
7
7
  import { Link } from 'react-router-dom';
8
- import type { TableProps } from '../table';
9
- import { getTableScroll, Table } from '../table';
10
8
  import { OperateDelete } from './crud_delete';
11
9
  import { CRUDDetail } from './crud_detail';
12
10
  import './style.scss';
11
+ import type { TableProps } from './table';
12
+ import { getTableScroll, Table } from './table';
13
13
  import type { CRUDProps } from './types';
14
14
  import { useRowSelection } from './use_row_selection';
15
15
  import { useTips } from './use_tips';
@@ -1,6 +1,6 @@
1
1
  import type { ActionType, ProFormInstance } from '@ant-design/pro-components';
2
2
  import type { MouseEvent, ReactNode, Ref, RefObject } from 'react';
3
- import type { TableProps } from '../table';
3
+ import type { TableProps } from './table';
4
4
 
5
5
  /**
6
6
  * create 创建
@@ -210,7 +210,7 @@ function customRequest(option: any) {
210
210
  },
211
211
  });
212
212
  }
213
- }, 100);
213
+ }, 1000);
214
214
 
215
215
  // 返回 abort 方法,用于取消上传
216
216
  return {
@@ -239,6 +239,30 @@ export const ProFormUploadComponent: Story = {
239
239
  name="files_dragger"
240
240
  fieldProps={{ multiple: true, maxCount: 2, customRequest }}
241
241
  />
242
+ <ProFormUploadDragger
243
+ label="knowledge_files_dragger"
244
+ name="knowledge_files_dragger"
245
+ fieldProps={{
246
+ directory: true,
247
+ accept: '.doc,.docx,.pdf,.ppt,.jpg,.jpeg,.png,.mp3,.mp4,.txt,.markdown,.excel',
248
+ multiple: true,
249
+ maxCount: 100,
250
+ customRequest,
251
+ showStatus: true,
252
+ description: (
253
+ <div>
254
+ <div className="text-01">
255
+ 支持doc、docx、pdf、ppt、jpg、png、mp3、mp4、txt、markdown、excel格式文件
256
+ </div>
257
+ <div className="text-03">
258
+ pdf
259
+ 文件不超过100MiB、图片文件不超过10MiB、音频文件不超过300MiB、视频文件不超过400MiB
260
+ 其他单文件不超过50MiB 单次最多上传 100 个文件
261
+ </div>
262
+ </div>
263
+ ),
264
+ }}
265
+ />
242
266
  <ProFormUpload
243
267
  label="files"
244
268
  name="files"
@@ -1,5 +1,7 @@
1
1
  // 避免循环引用
2
2
  import { ProForm, type ProFormItemProps } from '@ant-design/pro-components';
3
+ import { App } from 'antd';
4
+ import { useState } from 'react';
3
5
  import type {
4
6
  AvatarImageUploadProps,
5
7
  ImageUploadDraggerProps,
@@ -27,9 +29,38 @@ function ProFormUpload(props: ProFormItemProps<UploadProps>) {
27
29
 
28
30
  function ProFormUploadDragger(props: ProFormItemProps<UploadDraggerProps>) {
29
31
  const { fieldProps, ...rest } = props;
32
+ const { message } = App.useApp();
33
+
34
+ const [info, setInfo] = useState<any>(undefined);
35
+
36
+ const successList = info?.fileList
37
+ ?.map((item) => item.url || item.response?.data.url)
38
+ .filter(Boolean);
39
+
30
40
  return (
31
- <ProForm.Item {...rest}>
32
- <UploadDragger {...fieldProps} />
41
+ <ProForm.Item
42
+ {...rest}
43
+ validateTrigger={['onSubmit']}
44
+ rules={[
45
+ ...(props.rules || []),
46
+ {
47
+ validator: () => {
48
+ if (successList?.length !== info?.fileList?.length) {
49
+ message.error('存在未成功的文件,请处理!');
50
+ return Promise.reject('');
51
+ }
52
+ return Promise.resolve();
53
+ },
54
+ },
55
+ ]}
56
+ >
57
+ <UploadDragger
58
+ {...fieldProps}
59
+ onChangeOriginal={(info) => {
60
+ setInfo(info);
61
+ fieldProps?.onChangeOriginal?.(info);
62
+ }}
63
+ />
33
64
  </ProForm.Item>
34
65
  );
35
66
  }
package/src/index.ts CHANGED
@@ -57,8 +57,6 @@ export type { RecordArrayProps, RecordProps } from './record';
57
57
  export { routeTool } from './route';
58
58
  export { NumberSlider, PercentageSlider } from './slider';
59
59
  export type { NumberSliderProps, PercentageSliderProps } from './slider';
60
- export { Table } from './table';
61
- export type { TableProps } from './table';
62
60
  export { Tabs } from './tabs';
63
61
  export type { TabsProps } from './tabs';
64
62
  export { themeVariables } from './theme';
@@ -1,9 +1,11 @@
1
- // 避免循环引用
2
1
  import { DeleteOutlined, InboxOutlined, PlusOutlined, UploadOutlined } from '@ant-design/icons';
3
2
  import type { UploadProps as AntdUploadProps, UploadFile } from 'antd';
4
3
  import { Upload as AntdUpload, Avatar, Button, message } from 'antd';
4
+ import type { UploadChangeParam } from 'antd/es/upload';
5
5
  import classNames from 'classnames';
6
+ import type { ReactNode } from 'react';
6
7
  import { useCallback, useMemo, useState } from 'react';
8
+ import './style.scss';
7
9
 
8
10
  interface UploadBaseProps {
9
11
  value?: string[] | string;
@@ -14,14 +16,17 @@ interface UploadBaseProps {
14
16
  customRequest?: AntdUploadProps['customRequest'];
15
17
  listType?: AntdUploadProps['listType'];
16
18
  accept?: string;
19
+ directory?: AntdUploadProps['directory'];
17
20
  }
18
21
 
19
22
  interface UploadProps extends UploadBaseProps {
20
23
  showCount?: boolean;
21
24
  }
22
25
 
23
- function useUpload(props: ImageUploadProps) {
24
- const { value, onChange, multiple, maxCount } = props;
26
+ function useUpload(
27
+ props: ImageUploadProps & { onChangeOriginal?: (info: UploadChangeParam<UploadFile>) => void },
28
+ ) {
29
+ const { value, onChange, multiple, maxCount, onChangeOriginal } = props;
25
30
  // 转换成 Upload 格式。
26
31
  const defaultFileList = useMemo(() => {
27
32
  if (!value) {
@@ -35,6 +40,9 @@ function useUpload(props: ImageUploadProps) {
35
40
  // 存起来,已选的文件。以便做一些判断。
36
41
  const [fileList, setFileList] = useState<UploadFile[]>(defaultFileList);
37
42
 
43
+ // 找到真正上传成功的。
44
+ const successList = fileList.map((item) => item.url || item.response?.data.url).filter(Boolean);
45
+
38
46
  const handleChange = useCallback(
39
47
  (info) => {
40
48
  setFileList(info.fileList);
@@ -45,8 +53,10 @@ function useUpload(props: ImageUploadProps) {
45
53
  .filter(Boolean);
46
54
 
47
55
  onChange?.(multiple ? newValue : newValue[0]);
56
+
57
+ onChangeOriginal?.(info);
48
58
  },
49
- [multiple, onChange],
59
+ [multiple, onChange, onChangeOriginal],
50
60
  );
51
61
 
52
62
  // 选文件还是可能多选,如果多选,则提示。
@@ -78,11 +88,13 @@ function useUpload(props: ImageUploadProps) {
78
88
  beforeUpload: handleBeforeUpload,
79
89
  isDisabled,
80
90
  fileList,
91
+ successList,
81
92
  };
82
93
  }
83
94
 
84
95
  function Upload(props: ImageUploadProps) {
85
- const { multiple, maxCount, showCount, action, customRequest, listType, accept } = props;
96
+ const { multiple, maxCount, showCount, action, customRequest, listType, accept, directory } =
97
+ props;
86
98
  const { onChange, beforeUpload, isDisabled, fileList } = useUpload(props);
87
99
 
88
100
  return (
@@ -96,6 +108,7 @@ function Upload(props: ImageUploadProps) {
96
108
  maxCount={multiple ? maxCount : 1}
97
109
  multiple={multiple}
98
110
  beforeUpload={beforeUpload}
111
+ directory={directory}
99
112
  // 不可,否则会没法删除
100
113
  // disabled={isDisabled}
101
114
  >
@@ -115,11 +128,44 @@ function Upload(props: ImageUploadProps) {
115
128
 
116
129
  interface UploadDraggerProps extends UploadBaseProps {
117
130
  title?: string;
118
- description?: string;
131
+ description?: string | ReactNode;
132
+ onChangeOriginal?: (info: UploadChangeParam<UploadFile>) => void;
133
+ showStatus?: boolean;
119
134
  }
120
135
  function UploadDragger(props: UploadDraggerProps) {
121
- const { multiple, maxCount, action, customRequest, listType, accept, title, description } = props;
122
- const { onChange, beforeUpload, isDisabled, fileList } = useUpload(props);
136
+ const {
137
+ multiple,
138
+ maxCount,
139
+ action,
140
+ customRequest,
141
+ listType,
142
+ accept,
143
+ title,
144
+ description,
145
+ showStatus,
146
+ directory,
147
+ } = props;
148
+ const { onChange, beforeUpload, isDisabled, fileList, successList } = useUpload(props);
149
+
150
+ const itemRender = useCallback(
151
+ (originNode, file, fileList) => {
152
+ const isFirst = fileList.findIndex((item) => item.uid === file.uid) === 0;
153
+
154
+ if (maxCount && maxCount > 1 && showStatus && isFirst) {
155
+ return (
156
+ <div>
157
+ <div className="py-1">
158
+ 文件数量 ({successList.length}/{fileList.length})
159
+ </div>
160
+ {originNode}
161
+ </div>
162
+ );
163
+ }
164
+
165
+ return originNode;
166
+ },
167
+ [maxCount, showStatus, successList.length],
168
+ );
123
169
 
124
170
  return (
125
171
  <AntdUpload.Dragger
@@ -132,8 +178,13 @@ function UploadDragger(props: UploadDraggerProps) {
132
178
  maxCount={multiple ? maxCount : 1}
133
179
  multiple={multiple}
134
180
  beforeUpload={beforeUpload}
181
+ directory={directory}
135
182
  // 不可,否则会没法删除
136
183
  // disabled={isDisabled}
184
+ className={classNames('fec-upload-dragger', {
185
+ 'fec-upload-dragger-count-ten': fileList.length >= 10,
186
+ })}
187
+ itemRender={itemRender}
137
188
  >
138
189
  <div
139
190
  className={classNames({
@@ -0,0 +1,8 @@
1
+ .fec-upload-dragger {
2
+ &.fec-upload-dragger-count-ten {
3
+ .ant-upload-list.ant-upload-list-text {
4
+ max-height: calc(10 * 30px);
5
+ overflow: auto;
6
+ }
7
+ }
8
+ }
@@ -1,95 +0,0 @@
1
- import type { ProColumns } from '@ant-design/pro-components';
2
- import { Table } from '@fe-free/core';
3
- import type { Meta, StoryObj } from '@storybook/react-vite';
4
- import { fakeData } from '../crud/demo/data';
5
-
6
- const meta: Meta<typeof Table> = {
7
- title: '@fe-free/core/Table',
8
- component: Table,
9
- tags: ['autodocs'],
10
- parameters: {
11
- docs: {
12
- description: {
13
- component: `Table 基于 ProTable 做了一些封装:<br>
14
- - props column 需要显示的提供 search: true;
15
- - 列宽默认 120,和滚动条
16
- - 搜索样式做了默认设置,具体见代码
17
- - 页码做了默认设置,具体见代码
18
- `,
19
- },
20
- },
21
- },
22
- };
23
-
24
- export default meta;
25
- type Story = StoryObj<typeof Table>;
26
-
27
- // 定义列配置
28
- const columns: ProColumns[] = [
29
- {
30
- title: 'id',
31
- dataIndex: 'id',
32
- search: true,
33
- },
34
- {
35
- title: '名字(省略)',
36
- dataIndex: 'name',
37
- search: true,
38
- ellipsis: true,
39
- },
40
- {
41
- title: 'city',
42
- dataIndex: 'city',
43
- },
44
- {
45
- title: 'area',
46
- dataIndex: 'area',
47
- },
48
- ];
49
-
50
- export const Basic: Story = {
51
- render: () => (
52
- <Table
53
- rowKey="id"
54
- columns={columns}
55
- request={async (params) => {
56
- console.log(params);
57
- return {
58
- data: fakeData,
59
- success: true,
60
- total: fakeData.length,
61
- };
62
- }}
63
- />
64
- ),
65
- };
66
-
67
- export const ScrollX: Story = {
68
- render: () => (
69
- <div className="w-[400px]">
70
- <Table
71
- rowKey="id"
72
- columns={[
73
- {
74
- title: 'id',
75
- dataIndex: 'id',
76
- },
77
- {
78
- title: '名字(省略)',
79
- dataIndex: 'name',
80
-
81
- ellipsis: true,
82
- },
83
- {
84
- title: 'city',
85
- dataIndex: 'city',
86
- },
87
- {
88
- title: 'area',
89
- dataIndex: 'area',
90
- },
91
- ]}
92
- />
93
- </div>
94
- ),
95
- };
File without changes
File without changes