@zat-design/sisyphus-react 4.5.4 → 4.5.5

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.
@@ -98,14 +98,17 @@ function reducer(state, action) {
98
98
  }
99
99
  const ThemeAwareConfigProvider = ({
100
100
  locale,
101
+ theme,
101
102
  children
102
103
  }) => {
103
104
  const {
104
105
  state: themeState
105
106
  } = useTheme();
107
+ // 外部传入的 theme 与 ThemeContext 的 antdConfig 深度合并,外部 theme 优先覆盖默认值
108
+ const mergedTheme = merge({}, themeState.antdConfig, theme);
106
109
  return /*#__PURE__*/_jsx(ConfigProvider, {
107
110
  locale: locale,
108
- theme: themeState.antdConfig,
111
+ theme: mergedTheme,
109
112
  children: /*#__PURE__*/_jsx("div", {
110
113
  children: children
111
114
  })
@@ -149,6 +152,7 @@ export const ProConfigProvider = props => {
149
152
  },
150
153
  children: /*#__PURE__*/_jsx(ThemeAwareConfigProvider, {
151
154
  locale: antdLangMap[lang] ?? zhCN,
155
+ theme: props.theme,
152
156
  children: props.children
153
157
  })
154
158
  })
@@ -396,8 +396,11 @@ const RenderField = ({
396
396
  // 使用useCallback优化debounceValidate函数
397
397
  const debounceValidate = useCallback((validateFieldKeys = []) => {
398
398
  setTimeout(() => {
399
+ // 行内联动被动触发的重校验:只校验、不滚动到报错位置(全局/主动保存校验仍会滚动)
400
+ // @ts-ignore 内部选项
399
401
  form.validateFields(validateFieldKeys, {
400
- recursive: true
402
+ recursive: true,
403
+ __skipScrollOnError: true
401
404
  });
402
405
  }, 100);
403
406
  }, [form]);
@@ -418,7 +421,11 @@ const RenderField = ({
418
421
  if (dependencies?.length) {
419
422
  const validateFieldKeys = dependencies?.map?.(key => [...rowPath, key]);
420
423
  setTimeout(() => {
421
- form.validateFields(validateFieldKeys);
424
+ // 依赖字段联动重校验:只校验、不滚动
425
+ // @ts-ignore 内部选项
426
+ form.validateFields(validateFieldKeys, {
427
+ __skipScrollOnError: true
428
+ });
422
429
  }, 100);
423
430
  }
424
431
  return;
@@ -1,6 +1,6 @@
1
1
  import { useEffect, useMemo, useRef } from 'react';
2
2
  import { useDeepCompareEffect, useRequest as useRequestFunc } from 'ahooks';
3
- import { diffCode, getEnumData, setEnumData, removeEnumData, cacheFieldNames, baseCacheKey, baseStorage } from "../utils";
3
+ import { diffCode, getEnumData, setEnumData, removeEnumData, cacheFieldNames, cleanEnumResponse, baseCacheKey, baseStorage } from "../utils";
4
4
  import locale from "../../locale";
5
5
  import "../utils/eventCenter";
6
6
  const useEnumRequest = (props, dispatch) => {
@@ -209,19 +209,12 @@ const useEnumRequest = (props, dispatch) => {
209
209
  } else if (res.status === 200) {
210
210
  response = res.data;
211
211
  }
212
- // 只返回fieldNames对象的key
213
- if (clear) {
214
- Object.keys(response).forEach(key => {
215
- const options = [...response[key]];
216
- if (ignoreCodes.includes(key)) {
217
- response[key] = options;
218
- } else {
219
- response[key] = options.map(item => {
220
- return cacheFieldNames(fieldNames, item);
221
- });
222
- }
223
- });
224
- }
212
+ // 只返回fieldNames对象的key(非数组值会被安全跳过,避免展开报错)
213
+ response = cleanEnumResponse(response, {
214
+ clear,
215
+ ignoreCodes,
216
+ fieldNames
217
+ });
225
218
  cacheData.data = {
226
219
  ...cacheData.data,
227
220
  ...response
@@ -4,7 +4,7 @@ import _isString from "lodash/isString";
4
4
  import _isFunction from "lodash/isFunction";
5
5
  import _cloneDeep from "lodash/cloneDeep";
6
6
  import { useDeepCompareEffect, useRequest as useRequestFunc } from 'ahooks';
7
- import { message, Radio, Checkbox, Space } from 'antd';
7
+ import { message, Radio, Checkbox, Space, Segmented } from 'antd';
8
8
  import React, { useState, useEffect, useMemo } from 'react';
9
9
  import ProSelect from "../ProSelect";
10
10
  import { useProConfig } from "../ProConfigProvider";
@@ -217,6 +217,17 @@ const ProEnum = props => {
217
217
  fieldValue: fieldValue,
218
218
  dataSource: list
219
219
  });
220
+ case 'Segmented':
221
+ return /*#__PURE__*/_jsx(Segmented, {
222
+ ..._omit(enumProps, ['fieldNames', 'showCodeName']),
223
+ options: list.map(item => ({
224
+ label: item[label],
225
+ value: item[fieldValue],
226
+ disabled: !!item.disabled
227
+ })),
228
+ value: value,
229
+ onChange: onChange
230
+ });
220
231
  default:
221
232
  return null;
222
233
  }
@@ -313,4 +313,38 @@ export function cacheFieldNames(fieldNames, dataSource) {
313
313
  });
314
314
  }
315
315
  return result;
316
+ }
317
+
318
+ /**
319
+ * 对枚举请求返回的数据按 clear/fieldNames 进行清洗
320
+ * @param response 枚举数据 { code: DataOption[] }
321
+ * @param options clear 是否清洗、ignoreCodes 忽略清洗的 code、fieldNames 字段映射
322
+ * @returns 清洗后的新对象(不修改入参)
323
+ * @remark 某个 code 的值非数组时(脏缓存/异常返回)跳过清洗并原样透传,避免展开非可迭代值报错
324
+ */
325
+ export function cleanEnumResponse(response, options) {
326
+ const {
327
+ clear = true,
328
+ ignoreCodes = [],
329
+ fieldNames
330
+ } = options;
331
+ if (!clear || typeof response !== 'object' || response === null) {
332
+ return response;
333
+ }
334
+ const result = {};
335
+ Object.keys(response).forEach(key => {
336
+ const value = response[key];
337
+ // 非数组值(脏缓存、异常返回等)无法展开清洗,原样透传,避免 not iterable 报错
338
+ if (!Array.isArray(value)) {
339
+ result[key] = value;
340
+ return;
341
+ }
342
+ const list = [...value];
343
+ if (ignoreCodes.includes(key)) {
344
+ result[key] = list;
345
+ } else {
346
+ result[key] = list.map(item => cacheFieldNames(fieldNames, item));
347
+ }
348
+ });
349
+ return result;
316
350
  }
@@ -50,6 +50,15 @@ export const useForm = function (originForm, options) {
50
50
  return getFieldsValue(nameList, filterFunc);
51
51
  };
52
52
  const nextValidateFields = async (nameList, ...rest) => {
53
+ // 内部选项:行内联动等被动校验只想触发校验、不想滚动到报错位置;
54
+ // 全局/主动提交校验不传,保持滚动。提取后从传给 antd 底层的选项中剔除,避免污染原生 options。
55
+ // @ts-ignore
56
+ const skipScrollOnError = !!rest?.[0]?.__skipScrollOnError;
57
+ // @ts-ignore
58
+ const restForNative = rest?.[0] ? [(({
59
+ __skipScrollOnError,
60
+ ...opts
61
+ }) => opts)(rest[0]), ...rest.slice(1)] : rest;
53
62
  try {
54
63
  const isRecursive = rest?.[0]?.recursive;
55
64
  const stopOnFirstError = form?.__INTERNAL_CONFIG__?.stopOnFirstError;
@@ -62,18 +71,19 @@ export const useForm = function (originForm, options) {
62
71
  // eslint-disable-next-line no-restricted-syntax
63
72
  for (const field of allFields) {
64
73
  try {
65
- // 创建不包含 stopOnFirstError 的选项
74
+ // 创建不包含 stopOnFirstError / 内部选项的选项
66
75
  // @ts-ignore
67
76
  const options = rest?.[0] ? {
68
77
  ...rest[0]
69
78
  } : {};
70
79
  delete options.stopOnFirstError;
80
+ delete options.__skipScrollOnError;
71
81
  // 验证单个字段
72
82
  // eslint-disable-next-line no-await-in-loop
73
83
  await validateFields([field], options);
74
84
  } catch (error) {
75
- // 遇到错误,滚动到错误位置
76
- if (scrollToError && error?.errorFields?.length) {
85
+ // 遇到错误,滚动到错误位置(skipScrollOnError 时跳过)
86
+ if (!skipScrollOnError && scrollToError && error?.errorFields?.length) {
77
87
  form.scrollToField(error.errorFields[0]?.name, {
78
88
  block: 'center',
79
89
  behavior: 'smooth'
@@ -112,7 +122,7 @@ export const useForm = function (originForm, options) {
112
122
  }
113
123
 
114
124
  // @ts-ignore
115
- return await validateFields(validateNames, ...rest).then(values => {
125
+ return await validateFields(validateNames, ...restForNative).then(values => {
116
126
  return filterInternalFields(values, optimize);
117
127
  });
118
128
  }
@@ -124,17 +134,20 @@ export const useForm = function (originForm, options) {
124
134
  tablePagination?.click?.();
125
135
  }
126
136
  }
127
- return await validateFields(nameList, ...rest).then(values => {
137
+ return await validateFields(nameList, ...restForNative).then(values => {
128
138
  return nameList ? values : nextGetFieldsValue();
129
139
  });
130
140
  } catch (error) {
131
- if (scrollToError && error?.errorFields?.length && source === 'ProForm') {
132
- form.scrollToField(error.errorFields[0]?.name, {
133
- block: 'center',
134
- behavior: 'smooth'
135
- });
141
+ // skipScrollOnError(行内联动等被动校验)时不滚动,仅抛出错误
142
+ if (!skipScrollOnError) {
143
+ if (scrollToError && error?.errorFields?.length && source === 'ProForm') {
144
+ form.scrollToField(error.errorFields[0]?.name, {
145
+ block: 'center',
146
+ behavior: 'smooth'
147
+ });
148
+ }
149
+ handleScrollToErrorProEditTable();
136
150
  }
137
- handleScrollToErrorProEditTable();
138
151
  throw error;
139
152
  }
140
153
  };
@@ -1,4 +1,4 @@
1
- import { Tabs, Form } from 'antd';
1
+ import { Tabs, Form, Segmented } from 'antd';
2
2
  import { useMemo, useState, useEffect, memo } from 'react';
3
3
  import useEnum from "../../../ProEnum/hooks/useEnum";
4
4
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -8,6 +8,8 @@ const RenderTabs = props => {
8
8
  dataSource = [],
9
9
  transformResponse,
10
10
  tabsProps,
11
+ segmentedProps,
12
+ mode = 'Segmented',
11
13
  name,
12
14
  formTableProps,
13
15
  transformParams
@@ -52,25 +54,38 @@ const RenderTabs = props => {
52
54
  if (!tabItems?.length) {
53
55
  return null;
54
56
  }
55
- return /*#__PURE__*/_jsx(Tabs, {
56
- className: "pro-table-tabs",
57
- onChange: key => {
58
- const fieldsValues = form?.getFieldsValue() || {};
59
- setActiveKey(key);
60
- form.setFieldValue(name, key);
61
- let params = {
62
- ...fieldsValues,
63
- [name]: key
64
- };
65
- if (transformParams && typeof transformParams === 'function') {
66
- params = transformParams(params);
67
- }
68
- onSearch?.(params);
69
- },
70
- type: "card",
71
- ...tabsProps,
72
- items: tabItems,
73
- activeKey: activeKey
57
+ const handleChange = key => {
58
+ const fieldsValues = form?.getFieldsValue() || {};
59
+ setActiveKey(key);
60
+ form.setFieldValue(name, key);
61
+ let params = {
62
+ ...fieldsValues,
63
+ [name]: key
64
+ };
65
+ if (transformParams && typeof transformParams === 'function') {
66
+ params = transformParams(params);
67
+ }
68
+ onSearch?.(params);
69
+ };
70
+ if (mode === 'Tab') {
71
+ return /*#__PURE__*/_jsx(Tabs, {
72
+ className: "pro-table-tabs",
73
+ onChange: handleChange,
74
+ type: "card",
75
+ ...tabsProps,
76
+ items: tabItems,
77
+ activeKey: activeKey
78
+ });
79
+ }
80
+ return /*#__PURE__*/_jsx(Segmented, {
81
+ className: "pro-table-segmented",
82
+ options: tabItems.map(item => ({
83
+ label: item.label,
84
+ value: item.key
85
+ })),
86
+ ...segmentedProps,
87
+ onChange: value => handleChange(value),
88
+ value: activeKey
74
89
  });
75
90
  };
76
91
  export default /*#__PURE__*/memo(RenderTabs);
@@ -644,3 +644,8 @@
644
644
  }
645
645
  }
646
646
  }
647
+
648
+ // tabs mode 为 Segmented 时:宽度按内容自适应(靠左),与表格保持 8px 间距
649
+ .pro-table-segmented {
650
+ margin-bottom: 8px;
651
+ }
@@ -1,6 +1,6 @@
1
1
  // 导入语句
2
- import { CheckOutlined, CloseOutlined } from '@ant-design/icons';
3
- import { Button, Drawer, Radio, Switch } from 'antd';
2
+ import { CloseOutlined } from '@ant-design/icons';
3
+ import { Button, ColorPicker, Drawer, Radio, Switch } from 'antd';
4
4
  import React, { useState, useEffect } from 'react';
5
5
  import { setThemes } from "../../utils/index";
6
6
  import { useProConfig } from "../../../ProConfigProvider";
@@ -31,46 +31,24 @@ const ProTools = ({
31
31
  } = useProConfig();
32
32
 
33
33
  // 状态和 Refs
34
- const [active, setActive] = useState([{
35
- color: '#006AFF',
36
- active: true
37
- }, {
38
- color: '#00BC70',
39
- active: false
40
- }, {
41
- color: '#FF8C16',
42
- active: false
43
- }, {
44
- color: '#A00F20',
45
- active: false
46
- }, {
47
- color: '#31AF96',
48
- active: false
49
- }]);
34
+ const [color, setColor] = useState('#006AFF');
50
35
 
51
36
  // 事件处理函数
52
- const handleThemeColorClick = item => {
53
- const array = active.map(activeItem => {
54
- activeItem.active = false;
55
- return {
56
- color: activeItem.color,
57
- active: item.color === activeItem.color
58
- };
59
- });
60
- setActive(array);
37
+ const handleColorChange = hex => {
38
+ setColor(hex);
61
39
  setState({
62
40
  ...state,
63
- zauiBrand: item.color
41
+ zauiBrand: hex
64
42
  });
65
43
  setThemes({
66
- 'zaui-brand': item.color
44
+ 'zaui-brand': hex
67
45
  });
68
46
  dispatch({
69
47
  type: 'set',
70
48
  payload: {
71
49
  theme: {
72
- primaryColor: item.color,
73
- qiankunPrimaryColor: item.color
50
+ primaryColor: hex,
51
+ qiankunPrimaryColor: hex
74
52
  },
75
53
  forms: {}
76
54
  }
@@ -123,22 +101,7 @@ const ProTools = ({
123
101
  });
124
102
  };
125
103
  const handleResetClick = () => {
126
- setActive([{
127
- color: '#006AFF',
128
- active: true
129
- }, {
130
- color: '#00BC70',
131
- active: false
132
- }, {
133
- color: '#FF8C16',
134
- active: false
135
- }, {
136
- color: '#A00F20',
137
- active: false
138
- }, {
139
- color: '#31AF96',
140
- active: false
141
- }]);
104
+ setColor('#006AFF');
142
105
  onReset();
143
106
  };
144
107
 
@@ -155,14 +118,7 @@ const ProTools = ({
155
118
  // 优先使用 CSS 变量的值
156
119
  const finalZauiBrand = existingZauiBrand || zauiBrand;
157
120
  if (finalZauiBrand) {
158
- const newArray = active.map(activeItem => {
159
- activeItem.active = false;
160
- return {
161
- color: activeItem.color,
162
- active: activeItem.color === finalZauiBrand.toLocaleUpperCase()
163
- };
164
- });
165
- setActive(newArray);
121
+ setColor(finalZauiBrand);
166
122
  dispatch({
167
123
  type: 'set',
168
124
  payload: {
@@ -193,15 +149,10 @@ const ProTools = ({
193
149
  className: "pro-theme-tools-space",
194
150
  children: [/*#__PURE__*/_jsx("p", {
195
151
  children: `${locale.ProThemeTools.themeColor}`
196
- }), /*#__PURE__*/_jsx("ul", {
197
- className: "pro-theme-color",
198
- children: active.map((item, index) => {
199
- return /*#__PURE__*/_jsx("li", {
200
- className: item.active ? 'active' : '',
201
- onClick: () => handleThemeColorClick(item),
202
- children: item.active ? /*#__PURE__*/_jsx(CheckOutlined, {}) : null
203
- }, item.color);
204
- })
152
+ }), /*#__PURE__*/_jsx(ColorPicker, {
153
+ value: color,
154
+ showText: true,
155
+ onChangeComplete: value => handleColorChange(value.toHexString())
205
156
  })]
206
157
  }), /*#__PURE__*/_jsxs("div", {
207
158
  className: "pro-theme-tools-space",
@@ -35,73 +35,6 @@
35
35
  }
36
36
  }
37
37
 
38
- .pro-theme-color {
39
- display: flex;
40
-
41
- li {
42
- display: flex;
43
- align-items: center;
44
- justify-content: center;
45
- width: 38px;
46
- height: 38px;
47
- margin-right: 34px;
48
- color: #fff;
49
- background: #fff;
50
- border: 6px solid #fff;
51
- border-radius: 50%;
52
-
53
- span {
54
- font-size: var(--zaui-font-size, 14px);
55
- vertical-align: -7px;
56
- }
57
-
58
- &:nth-child(1) {
59
- background: #006aff;
60
-
61
- &.active,
62
- &:hover {
63
- border-color: #d6e7ff;
64
- }
65
- }
66
-
67
- &:nth-child(2) {
68
- background: #00bc70;
69
-
70
- &.active,
71
- &:hover {
72
- border-color: #e0f5e8;
73
- }
74
- }
75
-
76
- &:nth-child(3) {
77
- background: #ff8c16;
78
-
79
- &.active,
80
- &:hover {
81
- border-color: #ffecdd;
82
- }
83
- }
84
-
85
- &:nth-child(4) {
86
- background: #a00f20;
87
-
88
- &.active,
89
- &:hover {
90
- border-color: #f2dbdb;
91
- }
92
- }
93
- &:nth-child(5) {
94
- margin-right: 0;
95
- background: #31af96;
96
-
97
- &.active,
98
- &:hover {
99
- border-color: #f2dbdb;
100
- }
101
- }
102
- }
103
- }
104
-
105
38
  .anticon-question-circle {
106
39
  color: var(--zaui-brand, #006aff);
107
40
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zat-design/sisyphus-react",
3
- "version": "4.5.4",
3
+ "version": "4.5.5",
4
4
  "license": "MIT",
5
5
  "files": [
6
6
  "es",