@cfmm/umi-plugins-ui-v2 0.0.16 → 0.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.
@@ -887,7 +887,7 @@ function CrudTable<T extends Record<string, any>, U = {}, C = {}>(
887
887
  label: (
888
888
  <ImportExecl key="importMultiLang" rangeStart={{ c: 0, r: 2 }} onChange={importMultiLang}>
889
889
  <span>
890
- <UploadOutlined style={{ marginRight: 8 }} />
890
+ <UploadOutlined style={{ marginRight: 6 }} />
891
891
  {formatMessage({ id: 'cfmmUI.common.button.importMultiLang', defaultMessage: '导入多语言' })}
892
892
  </span>
893
893
  </ImportExecl>
@@ -926,7 +926,7 @@ function CrudTable<T extends Record<string, any>, U = {}, C = {}>(
926
926
  {...(importData?.attributes || {})}
927
927
  >
928
928
  <div style={{ color: !actionLoading ? 'rgba(0, 0, 0, 0.88)' : 'rgba(0, 0, 0, 0.25)' }}>
929
- <UploadOutlined style={{ marginRight: 8 }} />
929
+ <UploadOutlined style={{ marginRight: 6 }} />
930
930
  {formatMessage({ id: 'cfmmUI.common.button.importData', defaultMessage: '导入数据' })}
931
931
  </div>
932
932
  </ImportExecl>
@@ -14,6 +14,8 @@ const ImportExecl = <T,>(props: ImportExeclProps<T>) => {
14
14
  max,
15
15
  header,
16
16
  rangeStart,
17
+ defval,
18
+ blankrows,
17
19
  uploadProps,
18
20
  buttonProps,
19
21
  children,
@@ -36,7 +38,7 @@ const ImportExecl = <T,>(props: ImportExeclProps<T>) => {
36
38
  0,
37
39
  );
38
40
 
39
- const data = await importExexl({ file: info.file, rangeStart, max, header });
41
+ const data = await importExexl({ file: info.file, rangeStart, max, header, defval, blankrows });
40
42
  const result = await onChange?.(data as T, info.file as UploadFile);
41
43
 
42
44
  hide?.();
@@ -1 +1 @@
1
- export declare const ImportExeclTypes = "\nexport interface ImportExeclProps<T = any[]> {\n accept?: string;\n /**\n * \u4E0A\u4F20\u4E8B\u4EF6\uFF0C\u83B7\u53D6execl\u6570\u636E\u5217\u8868\uFF0C\u51FD\u6570\u8FD4\u56DEpromise\u5E76\u4E14showDefautlMessage\u4E0D\u662Ffalse\u65F6\u6709\u9ED8\u8BA4message\u63D0\u793A\n */\n onChange: (list: T, fileInfo: Pick<UploadFile, 'name' | 'size' | 'type' | 'uid'>) => Promise<{ success: boolean; failMsg?: string; data?: any } | void> | void;\n /**\n * \u663E\u793A\u9ED8\u8BA4\u7684\u63D0\u793A\n */\n showDefautlMessage?: boolean;\n /**\n * \u6309\u94AE\u6587\u672C\n */\n title?: string;\n /**\n * \u8BFB\u53D6\u5DE5\u4F5C\u7C3F\u6570\u91CF\uFF0C\u9ED8\u8BA41\n */\n max?: number;\n /**\n * \u4EE5\u7B2C\u51E0\u884C\u4E3A\u8868\u5934\uFF0C\u9ED8\u8BA4\u7B2C\u4E00\u884C\n */\n header?: number;\n /**\n * \u4EE5\u7B2C\u51E0\u5217\u7B2C\u51E0\u884C\u5F00\u59CB\u8BFB\u53D6\uFF0C\u9ED8\u8BA4\u7B2C\u4E00\u884C\u7B2C\u4E00\u5217\n */\n rangeStart?: { c: number; r: number };\n /**\n * \u4FDD\u7559\u7A7A\u884C\uFF0C\u9ED8\u8BA4false\n */\n blankrows?: boolean;\n /**\n * \u81EA\u5B9A\u4E49\u6309\u94AE\u5185\u5BB9\n */\n children?: React.ReactNode;\n /**\n * Upload\u7EC4\u4EF6\u5C5E\u6027\n */\n uploadProps?: UploadProps;\n /**\n * Button\u7EC4\u4EF6\u5C5E\u6027\n */\n buttonProps?: ButtonProps;\n}\n";
1
+ export declare const ImportExeclTypes = "\nexport interface ImportExeclProps<T = any[]> {\n accept?: string;\n /**\n * \u4E0A\u4F20\u4E8B\u4EF6\uFF0C\u83B7\u53D6execl\u6570\u636E\u5217\u8868\uFF0C\u51FD\u6570\u8FD4\u56DEpromise\u5E76\u4E14showDefautlMessage\u4E0D\u662Ffalse\u65F6\u6709\u9ED8\u8BA4message\u63D0\u793A\n */\n onChange: (list: T, fileInfo: Pick<UploadFile, 'name' | 'size' | 'type' | 'uid'>) => Promise<{ success: boolean; failMsg?: string; data?: any } | void> | void;\n /**\n * \u663E\u793A\u9ED8\u8BA4\u7684\u63D0\u793A\n */\n showDefautlMessage?: boolean;\n /**\n * \u6309\u94AE\u6587\u672C\n */\n title?: string;\n /**\n * \u8BFB\u53D6\u5DE5\u4F5C\u7C3F\u6570\u91CF\uFF0C\u9ED8\u8BA41\n */\n max?: number;\n /**\n * \u4EE5\u7B2C\u51E0\u884C\u4E3A\u8868\u5934\uFF0C\u9ED8\u8BA4\u7B2C\u4E00\u884C\n */\n header?: number;\n /**\n * \u4EE5\u7B2C\u51E0\u5217\u7B2C\u51E0\u884C\u5F00\u59CB\u8BFB\u53D6\uFF0C\u9ED8\u8BA4\u7B2C\u4E00\u884C\u7B2C\u4E00\u5217\n */\n rangeStart?: { c: number; r: number };\n /**\n * \u7A7A\u5355\u5143\u683C\u5360\u4F4D\u503C\uFF1B\u4F20\u5165\u540E\u6BCF\u884C\u5BF9\u8C61\u4F1A\u5305\u542B\u4E0E\u8868\u5934\u5BF9\u9F50\u7684\u5168\u90E8 key\uFF08\u7A7A\u5355\u5143\u683C\u4E3A\u8BE5\u503C\uFF09\uFF0C\u5426\u5219 SheetJS \u4F1A\u7701\u7565\u7A7A\u5355\u5143\u683C\u5BF9\u5E94\u5B57\u6BB5\n */\n defval?: unknown;\n /**\n * \u4FDD\u7559\u7A7A\u884C\uFF0C\u9ED8\u8BA4false\n */\n blankrows?: boolean;\n /**\n * \u81EA\u5B9A\u4E49\u6309\u94AE\u5185\u5BB9\n */\n children?: React.ReactNode;\n /**\n * Upload\u7EC4\u4EF6\u5C5E\u6027\n */\n uploadProps?: UploadProps;\n /**\n * Button\u7EC4\u4EF6\u5C5E\u6027\n */\n buttonProps?: ButtonProps;\n}\n";
@@ -4,4 +4,4 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.ImportExeclTypes = void 0;
7
- var ImportExeclTypes = exports.ImportExeclTypes = "\nexport interface ImportExeclProps<T = any[]> {\n accept?: string;\n /**\n * \u4E0A\u4F20\u4E8B\u4EF6\uFF0C\u83B7\u53D6execl\u6570\u636E\u5217\u8868\uFF0C\u51FD\u6570\u8FD4\u56DEpromise\u5E76\u4E14showDefautlMessage\u4E0D\u662Ffalse\u65F6\u6709\u9ED8\u8BA4message\u63D0\u793A\n */\n onChange: (list: T, fileInfo: Pick<UploadFile, 'name' | 'size' | 'type' | 'uid'>) => Promise<{ success: boolean; failMsg?: string; data?: any } | void> | void;\n /**\n * \u663E\u793A\u9ED8\u8BA4\u7684\u63D0\u793A\n */\n showDefautlMessage?: boolean;\n /**\n * \u6309\u94AE\u6587\u672C\n */\n title?: string;\n /**\n * \u8BFB\u53D6\u5DE5\u4F5C\u7C3F\u6570\u91CF\uFF0C\u9ED8\u8BA41\n */\n max?: number;\n /**\n * \u4EE5\u7B2C\u51E0\u884C\u4E3A\u8868\u5934\uFF0C\u9ED8\u8BA4\u7B2C\u4E00\u884C\n */\n header?: number;\n /**\n * \u4EE5\u7B2C\u51E0\u5217\u7B2C\u51E0\u884C\u5F00\u59CB\u8BFB\u53D6\uFF0C\u9ED8\u8BA4\u7B2C\u4E00\u884C\u7B2C\u4E00\u5217\n */\n rangeStart?: { c: number; r: number };\n /**\n * \u4FDD\u7559\u7A7A\u884C\uFF0C\u9ED8\u8BA4false\n */\n blankrows?: boolean;\n /**\n * \u81EA\u5B9A\u4E49\u6309\u94AE\u5185\u5BB9\n */\n children?: React.ReactNode;\n /**\n * Upload\u7EC4\u4EF6\u5C5E\u6027\n */\n uploadProps?: UploadProps;\n /**\n * Button\u7EC4\u4EF6\u5C5E\u6027\n */\n buttonProps?: ButtonProps;\n}\n";
7
+ var ImportExeclTypes = exports.ImportExeclTypes = "\nexport interface ImportExeclProps<T = any[]> {\n accept?: string;\n /**\n * \u4E0A\u4F20\u4E8B\u4EF6\uFF0C\u83B7\u53D6execl\u6570\u636E\u5217\u8868\uFF0C\u51FD\u6570\u8FD4\u56DEpromise\u5E76\u4E14showDefautlMessage\u4E0D\u662Ffalse\u65F6\u6709\u9ED8\u8BA4message\u63D0\u793A\n */\n onChange: (list: T, fileInfo: Pick<UploadFile, 'name' | 'size' | 'type' | 'uid'>) => Promise<{ success: boolean; failMsg?: string; data?: any } | void> | void;\n /**\n * \u663E\u793A\u9ED8\u8BA4\u7684\u63D0\u793A\n */\n showDefautlMessage?: boolean;\n /**\n * \u6309\u94AE\u6587\u672C\n */\n title?: string;\n /**\n * \u8BFB\u53D6\u5DE5\u4F5C\u7C3F\u6570\u91CF\uFF0C\u9ED8\u8BA41\n */\n max?: number;\n /**\n * \u4EE5\u7B2C\u51E0\u884C\u4E3A\u8868\u5934\uFF0C\u9ED8\u8BA4\u7B2C\u4E00\u884C\n */\n header?: number;\n /**\n * \u4EE5\u7B2C\u51E0\u5217\u7B2C\u51E0\u884C\u5F00\u59CB\u8BFB\u53D6\uFF0C\u9ED8\u8BA4\u7B2C\u4E00\u884C\u7B2C\u4E00\u5217\n */\n rangeStart?: { c: number; r: number };\n /**\n * \u7A7A\u5355\u5143\u683C\u5360\u4F4D\u503C\uFF1B\u4F20\u5165\u540E\u6BCF\u884C\u5BF9\u8C61\u4F1A\u5305\u542B\u4E0E\u8868\u5934\u5BF9\u9F50\u7684\u5168\u90E8 key\uFF08\u7A7A\u5355\u5143\u683C\u4E3A\u8BE5\u503C\uFF09\uFF0C\u5426\u5219 SheetJS \u4F1A\u7701\u7565\u7A7A\u5355\u5143\u683C\u5BF9\u5E94\u5B57\u6BB5\n */\n defval?: unknown;\n /**\n * \u4FDD\u7559\u7A7A\u884C\uFF0C\u9ED8\u8BA4false\n */\n blankrows?: boolean;\n /**\n * \u81EA\u5B9A\u4E49\u6309\u94AE\u5185\u5BB9\n */\n children?: React.ReactNode;\n /**\n * Upload\u7EC4\u4EF6\u5C5E\u6027\n */\n uploadProps?: UploadProps;\n /**\n * Button\u7EC4\u4EF6\u5C5E\u6027\n */\n buttonProps?: ButtonProps;\n}\n";
@@ -1,10 +1,10 @@
1
1
  // replaceTo:tsx
2
- import { UTC_FORMAT } from '../constants';
3
2
  import { getIntl } from '@umijs/max';
4
3
  import { Modal, Tag } from 'antd';
5
4
  import dayjs from 'dayjs';
6
5
  import { parse } from 'querystring';
7
6
  import * as XLSX from 'xlsx';
7
+ import { UTC_FORMAT } from '../constants';
8
8
 
9
9
  /**
10
10
  * 提取括号中的值
@@ -22,6 +22,8 @@ export const getParenthesisValue = (str: string) => {
22
22
  * @param max 最大读取行数
23
23
  * @param header 哪行是表头,默认第一行
24
24
  * @param rangeStart 开始读取行数{ c: 1, r: 2 } 从第2列第3行开始读取
25
+ * @param defval 缺省单元格占位值;传入后空单元格也会出现在每行对象中(如 `''` / `null`),不传则 SheetJS 默认会省略空字段的 key
26
+ * @param blankrows 是否输出完全空白的行,与 SheetJS `sheet_to_json` 的 `blankrows` 一致;不传则使用库默认
25
27
  * @return Promise<T = any[]>
26
28
  */
27
29
  export function importExexl<T = any>({
@@ -29,11 +31,17 @@ export function importExexl<T = any>({
29
31
  max = 1,
30
32
  rangeStart,
31
33
  header,
34
+ defval = null,
35
+ blankrows,
32
36
  }: {
33
37
  file: any;
34
38
  max?: number;
35
39
  header?: number;
36
40
  rangeStart?: { c: number; r: number };
41
+ /** 空单元格占位,传入后不过滤空字段(与表头对齐的 key 都会存在) */
42
+ defval?: unknown;
43
+ /** 是否输出空白行 */
44
+ blankrows?: boolean;
37
45
  }): Promise<T[]> {
38
46
  return new Promise((resolve, reject) => {
39
47
  // 通过FileReader对象读取文件
@@ -61,6 +69,9 @@ export function importExexl<T = any>({
61
69
  XLSX.utils.sheet_to_json(workbook.Sheets[sheet], {
62
70
  header,
63
71
  dateNF: 'YYYY-MM-DDTHH:mm:ss[Z]',
72
+ ...(blankrows !== undefined ? { blankrows } : {}),
73
+ // 有 defval 时空单元格也会生成对应字段,否则缺省单元格不会出现在对象里
74
+ ...(defval !== undefined ? { defval } : {}),
64
75
  // 属性s从第1列第3行开始读取,属性c到最后一行最后一列结束(Excel 行号从1开始)
65
76
  range: rangeStart
66
77
  ? {
@@ -145,23 +156,23 @@ function convertIndexedPropertiesToArrays(obj: Record<string, any>): any {
145
156
  * @returns
146
157
  */
147
158
  export const handleImportExeclCellValue = (value: unknown, type?: string) => {
148
- if (!value && value !== 0 && value !== '0') return;
159
+ if (!value && value !== 0 && value !== '0') return value;
149
160
 
150
161
  if (type === 'parenthesisCode') {
151
162
  const newValue = getParenthesisValue(value as string);
152
163
  return typeof newValue === 'string' ? newValue.trim() : newValue;
153
164
  }
154
- if (value instanceof Date || (type === 'date' && typeof value === 'string')) {
165
+ if (value instanceof Date || (type === 'date' && typeof value === 'string')) {
155
166
  if (value instanceof Date) {
156
167
  const corrected = new Date(value);
157
168
  const seconds = corrected.getSeconds();
158
-
169
+
159
170
  // 修正读取execl日期精度问题,如果秒数大于10,加1分钟并清零分钟和秒
160
171
  if (seconds > 10) {
161
172
  corrected.setMinutes(corrected.getMinutes() + 1);
162
173
  corrected.setSeconds(0, 0); // 清零秒和毫秒
163
174
  }
164
-
175
+
165
176
  const dateStr = dayjs.utc(corrected).format(UTC_FORMAT);
166
177
  return dateStr;
167
178
  }
@@ -187,7 +198,7 @@ export const createNestedObjectAndSetValue = (
187
198
  keys: string | string[],
188
199
  value: any,
189
200
  obj: Record<string, any>,
190
- options: Omit<HandleImportListOptions, 'hasRowNum'>
201
+ options: Omit<HandleImportListOptions, 'hasRowNum'>,
191
202
  ) => {
192
203
  if (!keys.length) return obj;
193
204
  // 过滤没有定义code的字段
@@ -207,7 +218,7 @@ export const createNestedObjectAndSetValue = (
207
218
  const handleValue = needHandleCenterlineValue ? (value === '-' ? null : value) : value;
208
219
 
209
220
  // 转为嵌套对象
210
- newKeys.forEach((key) => {
221
+ newKeys.forEach((key, idx) => {
211
222
  if (current[key] === undefined) {
212
223
  current[key] = {};
213
224
  }
@@ -887,7 +887,7 @@ function CrudTable<T extends Record<string, any>, U = {}, C = {}>(
887
887
  label: (
888
888
  <ImportExecl key="importMultiLang" rangeStart={{ c: 0, r: 2 }} onChange={importMultiLang}>
889
889
  <span>
890
- <UploadOutlined style={{ marginRight: 8 }} />
890
+ <UploadOutlined style={{ marginRight: 6 }} />
891
891
  {formatMessage({ id: 'cfmmUI.common.button.importMultiLang', defaultMessage: '导入多语言' })}
892
892
  </span>
893
893
  </ImportExecl>
@@ -926,7 +926,7 @@ function CrudTable<T extends Record<string, any>, U = {}, C = {}>(
926
926
  {...(importData?.attributes || {})}
927
927
  >
928
928
  <div style={{ color: !actionLoading ? 'rgba(0, 0, 0, 0.88)' : 'rgba(0, 0, 0, 0.25)' }}>
929
- <UploadOutlined style={{ marginRight: 8 }} />
929
+ <UploadOutlined style={{ marginRight: 6 }} />
930
930
  {formatMessage({ id: 'cfmmUI.common.button.importData', defaultMessage: '导入数据' })}
931
931
  </div>
932
932
  </ImportExecl>
@@ -14,6 +14,8 @@ const ImportExecl = <T,>(props: ImportExeclProps<T>) => {
14
14
  max,
15
15
  header,
16
16
  rangeStart,
17
+ defval,
18
+ blankrows,
17
19
  uploadProps,
18
20
  buttonProps,
19
21
  children,
@@ -36,7 +38,7 @@ const ImportExecl = <T,>(props: ImportExeclProps<T>) => {
36
38
  0,
37
39
  );
38
40
 
39
- const data = await importExexl({ file: info.file, rangeStart, max, header });
41
+ const data = await importExexl({ file: info.file, rangeStart, max, header, defval, blankrows });
40
42
  const result = await onChange?.(data as T, info.file as UploadFile);
41
43
 
42
44
  hide?.();
@@ -1 +1 @@
1
- export declare const ImportExeclTypes = "\nexport interface ImportExeclProps<T = any[]> {\n accept?: string;\n /**\n * \u4E0A\u4F20\u4E8B\u4EF6\uFF0C\u83B7\u53D6execl\u6570\u636E\u5217\u8868\uFF0C\u51FD\u6570\u8FD4\u56DEpromise\u5E76\u4E14showDefautlMessage\u4E0D\u662Ffalse\u65F6\u6709\u9ED8\u8BA4message\u63D0\u793A\n */\n onChange: (list: T, fileInfo: Pick<UploadFile, 'name' | 'size' | 'type' | 'uid'>) => Promise<{ success: boolean; failMsg?: string; data?: any } | void> | void;\n /**\n * \u663E\u793A\u9ED8\u8BA4\u7684\u63D0\u793A\n */\n showDefautlMessage?: boolean;\n /**\n * \u6309\u94AE\u6587\u672C\n */\n title?: string;\n /**\n * \u8BFB\u53D6\u5DE5\u4F5C\u7C3F\u6570\u91CF\uFF0C\u9ED8\u8BA41\n */\n max?: number;\n /**\n * \u4EE5\u7B2C\u51E0\u884C\u4E3A\u8868\u5934\uFF0C\u9ED8\u8BA4\u7B2C\u4E00\u884C\n */\n header?: number;\n /**\n * \u4EE5\u7B2C\u51E0\u5217\u7B2C\u51E0\u884C\u5F00\u59CB\u8BFB\u53D6\uFF0C\u9ED8\u8BA4\u7B2C\u4E00\u884C\u7B2C\u4E00\u5217\n */\n rangeStart?: { c: number; r: number };\n /**\n * \u4FDD\u7559\u7A7A\u884C\uFF0C\u9ED8\u8BA4false\n */\n blankrows?: boolean;\n /**\n * \u81EA\u5B9A\u4E49\u6309\u94AE\u5185\u5BB9\n */\n children?: React.ReactNode;\n /**\n * Upload\u7EC4\u4EF6\u5C5E\u6027\n */\n uploadProps?: UploadProps;\n /**\n * Button\u7EC4\u4EF6\u5C5E\u6027\n */\n buttonProps?: ButtonProps;\n}\n";
1
+ export declare const ImportExeclTypes = "\nexport interface ImportExeclProps<T = any[]> {\n accept?: string;\n /**\n * \u4E0A\u4F20\u4E8B\u4EF6\uFF0C\u83B7\u53D6execl\u6570\u636E\u5217\u8868\uFF0C\u51FD\u6570\u8FD4\u56DEpromise\u5E76\u4E14showDefautlMessage\u4E0D\u662Ffalse\u65F6\u6709\u9ED8\u8BA4message\u63D0\u793A\n */\n onChange: (list: T, fileInfo: Pick<UploadFile, 'name' | 'size' | 'type' | 'uid'>) => Promise<{ success: boolean; failMsg?: string; data?: any } | void> | void;\n /**\n * \u663E\u793A\u9ED8\u8BA4\u7684\u63D0\u793A\n */\n showDefautlMessage?: boolean;\n /**\n * \u6309\u94AE\u6587\u672C\n */\n title?: string;\n /**\n * \u8BFB\u53D6\u5DE5\u4F5C\u7C3F\u6570\u91CF\uFF0C\u9ED8\u8BA41\n */\n max?: number;\n /**\n * \u4EE5\u7B2C\u51E0\u884C\u4E3A\u8868\u5934\uFF0C\u9ED8\u8BA4\u7B2C\u4E00\u884C\n */\n header?: number;\n /**\n * \u4EE5\u7B2C\u51E0\u5217\u7B2C\u51E0\u884C\u5F00\u59CB\u8BFB\u53D6\uFF0C\u9ED8\u8BA4\u7B2C\u4E00\u884C\u7B2C\u4E00\u5217\n */\n rangeStart?: { c: number; r: number };\n /**\n * \u7A7A\u5355\u5143\u683C\u5360\u4F4D\u503C\uFF1B\u4F20\u5165\u540E\u6BCF\u884C\u5BF9\u8C61\u4F1A\u5305\u542B\u4E0E\u8868\u5934\u5BF9\u9F50\u7684\u5168\u90E8 key\uFF08\u7A7A\u5355\u5143\u683C\u4E3A\u8BE5\u503C\uFF09\uFF0C\u5426\u5219 SheetJS \u4F1A\u7701\u7565\u7A7A\u5355\u5143\u683C\u5BF9\u5E94\u5B57\u6BB5\n */\n defval?: unknown;\n /**\n * \u4FDD\u7559\u7A7A\u884C\uFF0C\u9ED8\u8BA4false\n */\n blankrows?: boolean;\n /**\n * \u81EA\u5B9A\u4E49\u6309\u94AE\u5185\u5BB9\n */\n children?: React.ReactNode;\n /**\n * Upload\u7EC4\u4EF6\u5C5E\u6027\n */\n uploadProps?: UploadProps;\n /**\n * Button\u7EC4\u4EF6\u5C5E\u6027\n */\n buttonProps?: ButtonProps;\n}\n";
@@ -1 +1 @@
1
- export var ImportExeclTypes = "\nexport interface ImportExeclProps<T = any[]> {\n accept?: string;\n /**\n * \u4E0A\u4F20\u4E8B\u4EF6\uFF0C\u83B7\u53D6execl\u6570\u636E\u5217\u8868\uFF0C\u51FD\u6570\u8FD4\u56DEpromise\u5E76\u4E14showDefautlMessage\u4E0D\u662Ffalse\u65F6\u6709\u9ED8\u8BA4message\u63D0\u793A\n */\n onChange: (list: T, fileInfo: Pick<UploadFile, 'name' | 'size' | 'type' | 'uid'>) => Promise<{ success: boolean; failMsg?: string; data?: any } | void> | void;\n /**\n * \u663E\u793A\u9ED8\u8BA4\u7684\u63D0\u793A\n */\n showDefautlMessage?: boolean;\n /**\n * \u6309\u94AE\u6587\u672C\n */\n title?: string;\n /**\n * \u8BFB\u53D6\u5DE5\u4F5C\u7C3F\u6570\u91CF\uFF0C\u9ED8\u8BA41\n */\n max?: number;\n /**\n * \u4EE5\u7B2C\u51E0\u884C\u4E3A\u8868\u5934\uFF0C\u9ED8\u8BA4\u7B2C\u4E00\u884C\n */\n header?: number;\n /**\n * \u4EE5\u7B2C\u51E0\u5217\u7B2C\u51E0\u884C\u5F00\u59CB\u8BFB\u53D6\uFF0C\u9ED8\u8BA4\u7B2C\u4E00\u884C\u7B2C\u4E00\u5217\n */\n rangeStart?: { c: number; r: number };\n /**\n * \u4FDD\u7559\u7A7A\u884C\uFF0C\u9ED8\u8BA4false\n */\n blankrows?: boolean;\n /**\n * \u81EA\u5B9A\u4E49\u6309\u94AE\u5185\u5BB9\n */\n children?: React.ReactNode;\n /**\n * Upload\u7EC4\u4EF6\u5C5E\u6027\n */\n uploadProps?: UploadProps;\n /**\n * Button\u7EC4\u4EF6\u5C5E\u6027\n */\n buttonProps?: ButtonProps;\n}\n";
1
+ export var ImportExeclTypes = "\nexport interface ImportExeclProps<T = any[]> {\n accept?: string;\n /**\n * \u4E0A\u4F20\u4E8B\u4EF6\uFF0C\u83B7\u53D6execl\u6570\u636E\u5217\u8868\uFF0C\u51FD\u6570\u8FD4\u56DEpromise\u5E76\u4E14showDefautlMessage\u4E0D\u662Ffalse\u65F6\u6709\u9ED8\u8BA4message\u63D0\u793A\n */\n onChange: (list: T, fileInfo: Pick<UploadFile, 'name' | 'size' | 'type' | 'uid'>) => Promise<{ success: boolean; failMsg?: string; data?: any } | void> | void;\n /**\n * \u663E\u793A\u9ED8\u8BA4\u7684\u63D0\u793A\n */\n showDefautlMessage?: boolean;\n /**\n * \u6309\u94AE\u6587\u672C\n */\n title?: string;\n /**\n * \u8BFB\u53D6\u5DE5\u4F5C\u7C3F\u6570\u91CF\uFF0C\u9ED8\u8BA41\n */\n max?: number;\n /**\n * \u4EE5\u7B2C\u51E0\u884C\u4E3A\u8868\u5934\uFF0C\u9ED8\u8BA4\u7B2C\u4E00\u884C\n */\n header?: number;\n /**\n * \u4EE5\u7B2C\u51E0\u5217\u7B2C\u51E0\u884C\u5F00\u59CB\u8BFB\u53D6\uFF0C\u9ED8\u8BA4\u7B2C\u4E00\u884C\u7B2C\u4E00\u5217\n */\n rangeStart?: { c: number; r: number };\n /**\n * \u7A7A\u5355\u5143\u683C\u5360\u4F4D\u503C\uFF1B\u4F20\u5165\u540E\u6BCF\u884C\u5BF9\u8C61\u4F1A\u5305\u542B\u4E0E\u8868\u5934\u5BF9\u9F50\u7684\u5168\u90E8 key\uFF08\u7A7A\u5355\u5143\u683C\u4E3A\u8BE5\u503C\uFF09\uFF0C\u5426\u5219 SheetJS \u4F1A\u7701\u7565\u7A7A\u5355\u5143\u683C\u5BF9\u5E94\u5B57\u6BB5\n */\n defval?: unknown;\n /**\n * \u4FDD\u7559\u7A7A\u884C\uFF0C\u9ED8\u8BA4false\n */\n blankrows?: boolean;\n /**\n * \u81EA\u5B9A\u4E49\u6309\u94AE\u5185\u5BB9\n */\n children?: React.ReactNode;\n /**\n * Upload\u7EC4\u4EF6\u5C5E\u6027\n */\n uploadProps?: UploadProps;\n /**\n * Button\u7EC4\u4EF6\u5C5E\u6027\n */\n buttonProps?: ButtonProps;\n}\n";
@@ -1,10 +1,10 @@
1
1
  // replaceTo:tsx
2
- import { UTC_FORMAT } from '../constants';
3
2
  import { getIntl } from '@umijs/max';
4
3
  import { Modal, Tag } from 'antd';
5
4
  import dayjs from 'dayjs';
6
5
  import { parse } from 'querystring';
7
6
  import * as XLSX from 'xlsx';
7
+ import { UTC_FORMAT } from '../constants';
8
8
 
9
9
  /**
10
10
  * 提取括号中的值
@@ -22,6 +22,8 @@ export const getParenthesisValue = (str: string) => {
22
22
  * @param max 最大读取行数
23
23
  * @param header 哪行是表头,默认第一行
24
24
  * @param rangeStart 开始读取行数{ c: 1, r: 2 } 从第2列第3行开始读取
25
+ * @param defval 缺省单元格占位值;传入后空单元格也会出现在每行对象中(如 `''` / `null`),不传则 SheetJS 默认会省略空字段的 key
26
+ * @param blankrows 是否输出完全空白的行,与 SheetJS `sheet_to_json` 的 `blankrows` 一致;不传则使用库默认
25
27
  * @return Promise<T = any[]>
26
28
  */
27
29
  export function importExexl<T = any>({
@@ -29,11 +31,17 @@ export function importExexl<T = any>({
29
31
  max = 1,
30
32
  rangeStart,
31
33
  header,
34
+ defval = null,
35
+ blankrows,
32
36
  }: {
33
37
  file: any;
34
38
  max?: number;
35
39
  header?: number;
36
40
  rangeStart?: { c: number; r: number };
41
+ /** 空单元格占位,传入后不过滤空字段(与表头对齐的 key 都会存在) */
42
+ defval?: unknown;
43
+ /** 是否输出空白行 */
44
+ blankrows?: boolean;
37
45
  }): Promise<T[]> {
38
46
  return new Promise((resolve, reject) => {
39
47
  // 通过FileReader对象读取文件
@@ -61,6 +69,9 @@ export function importExexl<T = any>({
61
69
  XLSX.utils.sheet_to_json(workbook.Sheets[sheet], {
62
70
  header,
63
71
  dateNF: 'YYYY-MM-DDTHH:mm:ss[Z]',
72
+ ...(blankrows !== undefined ? { blankrows } : {}),
73
+ // 有 defval 时空单元格也会生成对应字段,否则缺省单元格不会出现在对象里
74
+ ...(defval !== undefined ? { defval } : {}),
64
75
  // 属性s从第1列第3行开始读取,属性c到最后一行最后一列结束(Excel 行号从1开始)
65
76
  range: rangeStart
66
77
  ? {
@@ -145,23 +156,23 @@ function convertIndexedPropertiesToArrays(obj: Record<string, any>): any {
145
156
  * @returns
146
157
  */
147
158
  export const handleImportExeclCellValue = (value: unknown, type?: string) => {
148
- if (!value && value !== 0 && value !== '0') return;
159
+ if (!value && value !== 0 && value !== '0') return value;
149
160
 
150
161
  if (type === 'parenthesisCode') {
151
162
  const newValue = getParenthesisValue(value as string);
152
163
  return typeof newValue === 'string' ? newValue.trim() : newValue;
153
164
  }
154
- if (value instanceof Date || (type === 'date' && typeof value === 'string')) {
165
+ if (value instanceof Date || (type === 'date' && typeof value === 'string')) {
155
166
  if (value instanceof Date) {
156
167
  const corrected = new Date(value);
157
168
  const seconds = corrected.getSeconds();
158
-
169
+
159
170
  // 修正读取execl日期精度问题,如果秒数大于10,加1分钟并清零分钟和秒
160
171
  if (seconds > 10) {
161
172
  corrected.setMinutes(corrected.getMinutes() + 1);
162
173
  corrected.setSeconds(0, 0); // 清零秒和毫秒
163
174
  }
164
-
175
+
165
176
  const dateStr = dayjs.utc(corrected).format(UTC_FORMAT);
166
177
  return dateStr;
167
178
  }
@@ -187,7 +198,7 @@ export const createNestedObjectAndSetValue = (
187
198
  keys: string | string[],
188
199
  value: any,
189
200
  obj: Record<string, any>,
190
- options: Omit<HandleImportListOptions, 'hasRowNum'>
201
+ options: Omit<HandleImportListOptions, 'hasRowNum'>,
191
202
  ) => {
192
203
  if (!keys.length) return obj;
193
204
  // 过滤没有定义code的字段
@@ -207,7 +218,7 @@ export const createNestedObjectAndSetValue = (
207
218
  const handleValue = needHandleCenterlineValue ? (value === '-' ? null : value) : value;
208
219
 
209
220
  // 转为嵌套对象
210
- newKeys.forEach((key) => {
221
+ newKeys.forEach((key, idx) => {
211
222
  if (current[key] === undefined) {
212
223
  current[key] = {};
213
224
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cfmm/umi-plugins-ui-v2",
3
3
  "author": "ysj <411367308@qq.com>",
4
- "version": "0.0.16",
4
+ "version": "0.0.17",
5
5
  "main": "dist/cjs/index.js",
6
6
  "types": "dist/cjs/index.d.ts",
7
7
  "publishConfig": {