@tarojs/components-react 4.1.7-beta.1 → 4.1.7-beta.3
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/dist/components/button/index.js +20 -7
- package/dist/components/button/index.js.map +1 -1
- package/dist/index.css +1 -1
- package/dist/original/components/button/index.js +100 -0
- package/dist/original/components/button/index.js.map +1 -0
- package/dist/original/components/button/style/index.css +3 -0
- package/dist/original/components/button/style/index.css.map +1 -0
- package/dist/original/components/icon/index.js +36 -0
- package/dist/original/components/icon/index.js.map +1 -0
- package/dist/original/components/icon/style/index.css +3 -0
- package/dist/original/components/icon/style/index.css.map +1 -0
- package/dist/original/components/image/index.js +146 -0
- package/dist/original/components/image/index.js.map +1 -0
- package/dist/original/components/image/style/index.css +3 -0
- package/dist/original/components/image/style/index.css.map +1 -0
- package/dist/original/components/input/index.js +233 -0
- package/dist/original/components/input/index.js.map +1 -0
- package/dist/original/components/input/style/index.css +3 -0
- package/dist/original/components/input/style/index.css.map +1 -0
- package/dist/original/components/picker/index.js +788 -0
- package/dist/original/components/picker/index.js.map +1 -0
- package/dist/original/components/picker/picker-group.js +491 -0
- package/dist/original/components/picker/picker-group.js.map +1 -0
- package/dist/{components/picker/react-style/style.css → original/components/picker/style/index.css} +2 -1
- package/dist/original/components/picker/style/index.css.map +1 -0
- package/dist/original/components/pull-down-refresh/index.js +320 -0
- package/dist/original/components/pull-down-refresh/index.js.map +1 -0
- package/dist/original/components/pull-down-refresh/style/index.css +3 -0
- package/dist/original/components/pull-down-refresh/style/index.css.map +1 -0
- package/dist/original/components/refresher/index.js +7 -0
- package/dist/original/components/refresher/index.js.map +1 -0
- package/dist/original/components/scroll-view/index.js +189 -0
- package/dist/original/components/scroll-view/index.js.map +1 -0
- package/dist/original/components/scroll-view/style/index.css +3 -0
- package/dist/original/components/scroll-view/style/index.css.map +1 -0
- package/dist/original/components/swiper/index.js +461 -0
- package/dist/original/components/swiper/index.js.map +1 -0
- package/dist/original/components/swiper/style/index.css +3 -0
- package/dist/original/components/swiper/style/index.css.map +1 -0
- package/dist/original/components/text/index.js +28 -0
- package/dist/original/components/text/index.js.map +1 -0
- package/dist/original/components/text/style/index.css +3 -0
- package/dist/original/components/text/style/index.css.map +1 -0
- package/dist/original/components/view/index.js +80 -0
- package/dist/original/components/view/index.js.map +1 -0
- package/dist/original/index.css +2 -0
- package/dist/original/index.css.map +1 -0
- package/dist/original/index.js +15 -0
- package/dist/original/index.js.map +1 -0
- package/dist/original/utils/hooks.react.js +15 -0
- package/dist/original/utils/hooks.react.js.map +1 -0
- package/dist/original/utils/index.js +162 -0
- package/dist/original/utils/index.js.map +1 -0
- package/dist/solid/components/button/index.js +20 -7
- package/dist/solid/components/button/index.js.map +1 -1
- package/dist/solid/index.css +1 -1
- package/package.json +8 -6
- package/dist/components/picker/react-style/style.css.map +0 -1
- package/dist/components/picker/react-style/style.js +0 -4
- package/dist/components/picker/react-style/style.js.map +0 -1
|
@@ -0,0 +1,788 @@
|
|
|
1
|
+
import { __rest } from 'tslib';
|
|
2
|
+
import './style/index.css';
|
|
3
|
+
import { View } from '@tarojs/components';
|
|
4
|
+
import classNames from 'classnames';
|
|
5
|
+
import React__default from 'react';
|
|
6
|
+
import { verifyValue, verifyTime, hoursRange, minutesRange, verifyDate, getYearRange, getMonthRange, getDayRange, compareTime, omit } from '../../utils/index.js';
|
|
7
|
+
import { PickerGroup } from './picker-group.js';
|
|
8
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
9
|
+
|
|
10
|
+
const EMPTY_ARRAY = [];
|
|
11
|
+
const EMPTY_OBJECT = {};
|
|
12
|
+
// 语言映射函数
|
|
13
|
+
function getLanguageText(lang) {
|
|
14
|
+
const isEnglish = lang === 'en-US' || lang === 'en-GB';
|
|
15
|
+
return {
|
|
16
|
+
confirm: isEnglish ? 'Confirm' : '确定',
|
|
17
|
+
cancel: isEnglish ? 'Cancel' : '取消',
|
|
18
|
+
year: isEnglish ? 'Year ' : '年',
|
|
19
|
+
month: isEnglish ? 'Month ' : '月',
|
|
20
|
+
day: isEnglish ? 'Day ' : '日'
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
// 数据验证函数
|
|
24
|
+
function validateRegionData(data) {
|
|
25
|
+
let componentName = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'Picker';
|
|
26
|
+
if (!data) {
|
|
27
|
+
return {
|
|
28
|
+
valid: false,
|
|
29
|
+
error: `${componentName}: regionData is required for region mode`
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (!Array.isArray(data)) {
|
|
33
|
+
return {
|
|
34
|
+
valid: false,
|
|
35
|
+
error: `${componentName}: regionData must be an array`
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (data.length === 0) {
|
|
39
|
+
return {
|
|
40
|
+
valid: false,
|
|
41
|
+
error: `${componentName}: regionData cannot be empty`
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
// 检查数据结构
|
|
45
|
+
const validateItem = (item, path) => {
|
|
46
|
+
if (!item || typeof item !== 'object') {
|
|
47
|
+
return {
|
|
48
|
+
valid: false,
|
|
49
|
+
error: `${componentName}: Invalid item at ${path}`
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (!item.value || typeof item.value !== 'string') {
|
|
53
|
+
return {
|
|
54
|
+
valid: false,
|
|
55
|
+
error: `${componentName}: Missing or invalid 'value' field at ${path}`
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (!item.code || typeof item.code !== 'string') {
|
|
59
|
+
return {
|
|
60
|
+
valid: false,
|
|
61
|
+
error: `${componentName}: Missing or invalid 'code' field at ${path}`
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
if (item.postcode !== undefined && typeof item.postcode !== 'string') {
|
|
65
|
+
return {
|
|
66
|
+
valid: false,
|
|
67
|
+
error: `${componentName}: Invalid 'postcode' field at ${path}`
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (item.children && !Array.isArray(item.children)) {
|
|
71
|
+
return {
|
|
72
|
+
valid: false,
|
|
73
|
+
error: `${componentName}: 'children' must be an array at ${path}`
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (item.children) {
|
|
77
|
+
for (let i = 0; i < item.children.length; i++) {
|
|
78
|
+
const childResult = validateItem(item.children[i], `${path}.children[${i}]`);
|
|
79
|
+
if (!childResult.valid) return childResult;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
valid: true
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
for (let i = 0; i < data.length; i++) {
|
|
87
|
+
const result = validateItem(data[i], `regionData[${i}]`);
|
|
88
|
+
if (!result.valid) return result;
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
valid: true
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// 普通函数
|
|
95
|
+
function getRegionColumnsCount(level) {
|
|
96
|
+
switch (level) {
|
|
97
|
+
case 'province':
|
|
98
|
+
return 1;
|
|
99
|
+
case 'city':
|
|
100
|
+
return 2;
|
|
101
|
+
case 'region':
|
|
102
|
+
return 3;
|
|
103
|
+
default:
|
|
104
|
+
return 3;
|
|
105
|
+
// 默认显示省市区/县三列
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const Picker = /*#__PURE__*/React__default.forwardRef((props, ref) => {
|
|
109
|
+
var _a, _b;
|
|
110
|
+
const {
|
|
111
|
+
mode = 'selector',
|
|
112
|
+
disabled = false,
|
|
113
|
+
range = EMPTY_ARRAY,
|
|
114
|
+
rangeKey,
|
|
115
|
+
value,
|
|
116
|
+
start = '',
|
|
117
|
+
end = '',
|
|
118
|
+
fields = 'day',
|
|
119
|
+
headerText,
|
|
120
|
+
level = 'region',
|
|
121
|
+
regionData,
|
|
122
|
+
textProps = EMPTY_OBJECT,
|
|
123
|
+
colors = EMPTY_OBJECT,
|
|
124
|
+
onChange,
|
|
125
|
+
onColumnChange,
|
|
126
|
+
onCancel,
|
|
127
|
+
children,
|
|
128
|
+
formType,
|
|
129
|
+
lang
|
|
130
|
+
} = props,
|
|
131
|
+
restProps = __rest(props, ["mode", "disabled", "range", "rangeKey", "value", "start", "end", "fields", "headerText", "level", "regionData", "textProps", "colors", "onChange", "onColumnChange", "onCancel", "children", "formType", "lang"]);
|
|
132
|
+
const indexRef = React__default.useRef([]);
|
|
133
|
+
const pickerDateRef = React__default.useRef();
|
|
134
|
+
// 记录是否是用户滚动
|
|
135
|
+
const isInitializationCompletedRef = React__default.useRef(false);
|
|
136
|
+
const [state, setState] = React__default.useState({
|
|
137
|
+
pickerValue: value || EMPTY_ARRAY,
|
|
138
|
+
selectedIndices: EMPTY_ARRAY.slice(),
|
|
139
|
+
// 索引数组
|
|
140
|
+
hidden: true,
|
|
141
|
+
fadeOut: false,
|
|
142
|
+
isWillLoadCalled: false,
|
|
143
|
+
timestamp: 0 // 用以部分模式下强制刷新组件的的时间戳,多用于反复限位
|
|
144
|
+
});
|
|
145
|
+
// 在组件内部
|
|
146
|
+
const [columnsCount, setColumnsCount] = React__default.useState(() => getRegionColumnsCount(level));
|
|
147
|
+
// 只在level变化时更新列数
|
|
148
|
+
React__default.useEffect(() => {
|
|
149
|
+
setColumnsCount(getRegionColumnsCount(level));
|
|
150
|
+
}, [level]);
|
|
151
|
+
// 获取当前索引数组
|
|
152
|
+
const getIndices = React__default.useCallback(() => {
|
|
153
|
+
return indexRef.current;
|
|
154
|
+
}, []);
|
|
155
|
+
// 处理属性变化
|
|
156
|
+
const handleProps = React__default.useCallback(() => {
|
|
157
|
+
var _a;
|
|
158
|
+
if (mode === 'selector') {
|
|
159
|
+
const val = value;
|
|
160
|
+
indexRef.current = [verifyValue(val, range) ? Math.floor(val) : 0];
|
|
161
|
+
} else if (mode === 'multiSelector') {
|
|
162
|
+
const val = value;
|
|
163
|
+
indexRef.current = [];
|
|
164
|
+
range.forEach((rangeItem, index) => {
|
|
165
|
+
const valItem = val === null || val === void 0 ? void 0 : val[index];
|
|
166
|
+
const item = verifyValue(valItem, rangeItem) ? Math.floor(valItem) : 0;
|
|
167
|
+
indexRef.current.push(item);
|
|
168
|
+
});
|
|
169
|
+
} else if (mode === 'time') {
|
|
170
|
+
let val = value;
|
|
171
|
+
if (!verifyTime(val)) {
|
|
172
|
+
console.warn('time picker value illegal');
|
|
173
|
+
val = '0:0';
|
|
174
|
+
}
|
|
175
|
+
const time = val.split(':').map(n => +n);
|
|
176
|
+
// 在 hoursRange 和 minutesRange 中找到对应的索引
|
|
177
|
+
const hourIndex = hoursRange.findIndex(item => parseInt(item) === time[0]);
|
|
178
|
+
const minuteIndex = minutesRange.findIndex(item => parseInt(item) === time[1]);
|
|
179
|
+
// 确保索引在有效范围内
|
|
180
|
+
const safeHourIndex = hourIndex >= 0 ? hourIndex : 0; // 默认为第一项
|
|
181
|
+
const safeMinuteIndex = minuteIndex >= 0 ? minuteIndex : 0; // 默认为第一项
|
|
182
|
+
indexRef.current = [Math.max(0, Math.min(safeHourIndex, hoursRange.length - 1)), Math.max(0, Math.min(safeMinuteIndex, minutesRange.length - 1))];
|
|
183
|
+
} else if (mode === 'date') {
|
|
184
|
+
const val = value;
|
|
185
|
+
let _value = verifyDate(val) || new Date(new Date().setHours(0, 0, 0, 0));
|
|
186
|
+
const _start = verifyDate(start) || new Date('1875/01/01');
|
|
187
|
+
const _end = verifyDate(end) || new Date('2100/01/01');
|
|
188
|
+
if (!(_start <= _end)) {
|
|
189
|
+
throw new Error(`Picker start time must be less than end time.`);
|
|
190
|
+
}
|
|
191
|
+
if (!(_value >= _start && _value <= _end)) {
|
|
192
|
+
_value = _start;
|
|
193
|
+
}
|
|
194
|
+
const currentYear = _value.getFullYear();
|
|
195
|
+
const currentMonth = _value.getMonth() + 1;
|
|
196
|
+
const currentDay = _value.getDate();
|
|
197
|
+
const yearRange = getYearRange(_start.getFullYear(), _end.getFullYear());
|
|
198
|
+
const monthRange = getMonthRange(_start, _end, currentYear);
|
|
199
|
+
const dayRange = getDayRange(_start, _end, currentYear, currentMonth);
|
|
200
|
+
indexRef.current = [yearRange.indexOf(currentYear), monthRange.indexOf(currentMonth), dayRange.indexOf(currentDay)];
|
|
201
|
+
if (!pickerDateRef.current || pickerDateRef.current._value.getTime() !== _value.getTime() || pickerDateRef.current._start.getTime() !== _start.getTime() || pickerDateRef.current._end.getTime() !== _end.getTime()) {
|
|
202
|
+
pickerDateRef.current = {
|
|
203
|
+
_value,
|
|
204
|
+
_start,
|
|
205
|
+
_end,
|
|
206
|
+
_updateValue: [currentYear, currentMonth, currentDay]
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
} else if (mode === 'region') {
|
|
210
|
+
// region 模式处理 - 验证数据
|
|
211
|
+
if (!regionData) {
|
|
212
|
+
console.error('Picker: regionData is required for region mode');
|
|
213
|
+
indexRef.current = [0];
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const validation = validateRegionData(regionData, 'Picker');
|
|
217
|
+
if (!validation.valid) {
|
|
218
|
+
console.error(validation.error);
|
|
219
|
+
indexRef.current = [0];
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
// 获取列数
|
|
223
|
+
const val = Array.isArray(value) ? value : [];
|
|
224
|
+
// 根据level和当前值确定索引
|
|
225
|
+
indexRef.current = [];
|
|
226
|
+
let currentData = regionData;
|
|
227
|
+
for (let i = 0; i < columnsCount; i++) {
|
|
228
|
+
if (!(currentData === null || currentData === void 0 ? void 0 : currentData.length)) {
|
|
229
|
+
indexRef.current.push(0);
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
let idx = 0;
|
|
233
|
+
if (typeof val[i] === 'number') {
|
|
234
|
+
const rawIdx = val[i];
|
|
235
|
+
idx = rawIdx >= 0 && rawIdx < currentData.length ? rawIdx : 0;
|
|
236
|
+
} else if (typeof val[i] === 'string') {
|
|
237
|
+
const parsed = parseInt(val[i], 10);
|
|
238
|
+
idx = parsed >= 0 && parsed < currentData.length ? parsed : 0;
|
|
239
|
+
}
|
|
240
|
+
indexRef.current.push(idx);
|
|
241
|
+
currentData = ((_a = currentData[idx]) === null || _a === void 0 ? void 0 : _a.children) || [];
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
throw new Error(`Picker not support "${mode}" mode.`);
|
|
245
|
+
}
|
|
246
|
+
// 更新索引值
|
|
247
|
+
const newIndices = getIndices();
|
|
248
|
+
setState(prev => Object.assign(Object.assign({}, prev), {
|
|
249
|
+
selectedIndices: newIndices,
|
|
250
|
+
pickerValue: value || EMPTY_ARRAY
|
|
251
|
+
}));
|
|
252
|
+
}, [mode, range, value, start, end, fields, regionData, level, columnsCount, getIndices]);
|
|
253
|
+
// 组件初始化
|
|
254
|
+
React__default.useEffect(() => {
|
|
255
|
+
setState(prev => Object.assign(Object.assign({}, prev), {
|
|
256
|
+
isWillLoadCalled: true
|
|
257
|
+
}));
|
|
258
|
+
handleProps();
|
|
259
|
+
}, []);
|
|
260
|
+
// 属性变化监听 - 添加 value 依赖以支持联动选择器
|
|
261
|
+
React__default.useEffect(() => {
|
|
262
|
+
if (state.isWillLoadCalled) {
|
|
263
|
+
handleProps();
|
|
264
|
+
}
|
|
265
|
+
}, [handleProps, state.isWillLoadCalled, JSON.stringify(value)]);
|
|
266
|
+
// 显示 Picker
|
|
267
|
+
const showPicker = React__default.useCallback(() => {
|
|
268
|
+
if (disabled) return;
|
|
269
|
+
isInitializationCompletedRef.current = false;
|
|
270
|
+
const newIndices = getIndices();
|
|
271
|
+
setState(prev => Object.assign(Object.assign({}, prev), {
|
|
272
|
+
selectedIndices: newIndices,
|
|
273
|
+
hidden: false
|
|
274
|
+
}));
|
|
275
|
+
}, [disabled, getIndices]);
|
|
276
|
+
// 隐藏 Picker
|
|
277
|
+
const hidePicker = React__default.useCallback(() => {
|
|
278
|
+
isInitializationCompletedRef.current = false;
|
|
279
|
+
// 动画暂时不支持,暂时屏蔽相关样式挂载逻辑
|
|
280
|
+
// setState(prev => ({ ...prev, fadeOut: true }))
|
|
281
|
+
// setTimeout(() => {
|
|
282
|
+
// setState(prev => ({
|
|
283
|
+
// ...prev,
|
|
284
|
+
// hidden: true,
|
|
285
|
+
// fadeOut: false
|
|
286
|
+
// }))
|
|
287
|
+
// }, 350)
|
|
288
|
+
setState(prev => Object.assign(Object.assign({}, prev), {
|
|
289
|
+
hidden: true
|
|
290
|
+
}));
|
|
291
|
+
}, []);
|
|
292
|
+
// 更新索引
|
|
293
|
+
const updateIndex = React__default.useCallback(function (index, columnId) {
|
|
294
|
+
let needRevise = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
295
|
+
let isUserScrollRef = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
|
|
296
|
+
const columnIndex = Number(columnId);
|
|
297
|
+
let finalIndices = [...state.selectedIndices];
|
|
298
|
+
finalIndices[columnIndex] = index;
|
|
299
|
+
let hasLimited = false;
|
|
300
|
+
// region 模式的级联更新逻辑
|
|
301
|
+
if (mode === 'region' && regionData) {
|
|
302
|
+
if (isUserScrollRef && !isInitializationCompletedRef.current) {
|
|
303
|
+
isInitializationCompletedRef.current = true;
|
|
304
|
+
}
|
|
305
|
+
if (!isInitializationCompletedRef.current) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
// 重置后续列
|
|
309
|
+
for (let i = columnIndex + 1; i < columnsCount; i++) {
|
|
310
|
+
finalIndices[i] = 0;
|
|
311
|
+
}
|
|
312
|
+
setState(prev => Object.assign(Object.assign({}, prev), {
|
|
313
|
+
selectedIndices: finalIndices
|
|
314
|
+
}));
|
|
315
|
+
return; // 直接返回
|
|
316
|
+
}
|
|
317
|
+
// time picker
|
|
318
|
+
if (needRevise && mode === 'time') {
|
|
319
|
+
let startTime = start;
|
|
320
|
+
let endTime = end;
|
|
321
|
+
if (!verifyTime(startTime)) startTime = '00:00';
|
|
322
|
+
if (!verifyTime(endTime)) endTime = '23:59';
|
|
323
|
+
if (!compareTime(startTime, endTime)) return false;
|
|
324
|
+
const timeRanges = [hoursRange.slice(), minutesRange.slice()];
|
|
325
|
+
// 然后基于更新后的索引数组计算时间
|
|
326
|
+
const timeStr = finalIndices.map((idx, i) => {
|
|
327
|
+
const rangeIdx = Math.max(0, Math.min(idx, timeRanges[i].length - 1));
|
|
328
|
+
return timeRanges[i][rangeIdx] || '00';
|
|
329
|
+
}).join(':');
|
|
330
|
+
// 检查是否需要限位
|
|
331
|
+
if (!compareTime(startTime, timeStr)) {
|
|
332
|
+
// 修正到 start
|
|
333
|
+
const startParts = startTime.split(':').map(part => parseInt(part));
|
|
334
|
+
const newIndices = startParts.map((time, i) => {
|
|
335
|
+
const idx = timeRanges[i].findIndex(item => parseInt(item) === time);
|
|
336
|
+
return idx >= 0 ? idx : 0;
|
|
337
|
+
});
|
|
338
|
+
finalIndices = [...newIndices];
|
|
339
|
+
hasLimited = true;
|
|
340
|
+
} else if (!compareTime(timeStr, endTime)) {
|
|
341
|
+
// 修正到 end
|
|
342
|
+
const endParts = endTime.split(':').map(part => parseInt(part));
|
|
343
|
+
const newIndices = endParts.map((time, i) => {
|
|
344
|
+
const idx = timeRanges[i].findIndex(item => parseInt(item) === time);
|
|
345
|
+
return idx >= 0 ? idx : 0;
|
|
346
|
+
});
|
|
347
|
+
finalIndices = [...newIndices];
|
|
348
|
+
hasLimited = true;
|
|
349
|
+
}
|
|
350
|
+
// 触发限位,更新状态,其它状态不用主动触发滚动
|
|
351
|
+
if (hasLimited) {
|
|
352
|
+
setState(prev => Object.assign(Object.assign({}, prev), {
|
|
353
|
+
selectedIndices: finalIndices,
|
|
354
|
+
timestamp: Date.now()
|
|
355
|
+
}));
|
|
356
|
+
} else {
|
|
357
|
+
setState(prev => Object.assign(Object.assign({}, prev), {
|
|
358
|
+
selectedIndices: finalIndices
|
|
359
|
+
}));
|
|
360
|
+
}
|
|
361
|
+
return hasLimited;
|
|
362
|
+
}
|
|
363
|
+
// 常规更新
|
|
364
|
+
finalIndices[columnIndex] = index;
|
|
365
|
+
setState(prev => Object.assign(Object.assign({}, prev), {
|
|
366
|
+
selectedIndices: finalIndices
|
|
367
|
+
}));
|
|
368
|
+
return false; // 没有限位
|
|
369
|
+
}, [start, end, mode, regionData, state.selectedIndices, columnsCount]);
|
|
370
|
+
// 更新日期
|
|
371
|
+
const updateDay = React__default.useCallback((value, fields) => {
|
|
372
|
+
if (!pickerDateRef.current) return;
|
|
373
|
+
const {
|
|
374
|
+
_start,
|
|
375
|
+
_end,
|
|
376
|
+
_updateValue
|
|
377
|
+
} = pickerDateRef.current;
|
|
378
|
+
// 更新当前字段的值
|
|
379
|
+
_updateValue[fields] = value;
|
|
380
|
+
// 获取当前年月日
|
|
381
|
+
const currentYear = _updateValue[0];
|
|
382
|
+
const currentMonth = _updateValue[1];
|
|
383
|
+
const currentDay = _updateValue[2];
|
|
384
|
+
// 保存原始值用于后面比较
|
|
385
|
+
const originalValues = [..._updateValue];
|
|
386
|
+
// 准备最终的索引数组 - 复制当前索引状态作为起点
|
|
387
|
+
const finalIndices = [...state.selectedIndices];
|
|
388
|
+
// 获取基础范围数据
|
|
389
|
+
const yearRange = getYearRange(_start.getFullYear(), _end.getFullYear());
|
|
390
|
+
const monthRange = getMonthRange(_start, _end, currentYear);
|
|
391
|
+
let dayRange = getDayRange(_start, _end, currentYear, currentMonth);
|
|
392
|
+
// 根据修改的字段进行不同处理
|
|
393
|
+
if (fields === 0) {
|
|
394
|
+
// 年份索引直接更新
|
|
395
|
+
finalIndices[0] = yearRange.indexOf(currentYear);
|
|
396
|
+
// 月份限位处理
|
|
397
|
+
if (monthRange.length > 0) {
|
|
398
|
+
if (currentMonth > monthRange[monthRange.length - 1]) {
|
|
399
|
+
_updateValue[1] = monthRange[monthRange.length - 1];
|
|
400
|
+
}
|
|
401
|
+
if (currentMonth < monthRange[0]) {
|
|
402
|
+
_updateValue[1] = monthRange[0];
|
|
403
|
+
}
|
|
404
|
+
// 更新月份索引
|
|
405
|
+
finalIndices[1] = monthRange.indexOf(_updateValue[1]);
|
|
406
|
+
// 重新计算日期范围
|
|
407
|
+
dayRange = getDayRange(_start, _end, currentYear, _updateValue[1]);
|
|
408
|
+
// 日期限位处理
|
|
409
|
+
if (dayRange.length > 0) {
|
|
410
|
+
if (currentDay > dayRange[dayRange.length - 1]) {
|
|
411
|
+
_updateValue[2] = dayRange[dayRange.length - 1];
|
|
412
|
+
}
|
|
413
|
+
if (currentDay < dayRange[0]) {
|
|
414
|
+
_updateValue[2] = dayRange[0];
|
|
415
|
+
}
|
|
416
|
+
// 更新日期索引
|
|
417
|
+
finalIndices[2] = dayRange.indexOf(_updateValue[2]);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
} else if (fields === 1) {
|
|
421
|
+
// 月份变化
|
|
422
|
+
// 月份索引直接更新
|
|
423
|
+
finalIndices[1] = monthRange.indexOf(currentMonth);
|
|
424
|
+
// 日期限位处理
|
|
425
|
+
if (dayRange.length > 0) {
|
|
426
|
+
if (currentDay > dayRange[dayRange.length - 1]) {
|
|
427
|
+
_updateValue[2] = dayRange[dayRange.length - 1];
|
|
428
|
+
}
|
|
429
|
+
if (currentDay < dayRange[0]) {
|
|
430
|
+
_updateValue[2] = dayRange[0];
|
|
431
|
+
}
|
|
432
|
+
// 更新日期索引
|
|
433
|
+
finalIndices[2] = dayRange.indexOf(_updateValue[2]);
|
|
434
|
+
}
|
|
435
|
+
} else if (fields === 2) {
|
|
436
|
+
// 日期变化
|
|
437
|
+
// 日期索引直接更新
|
|
438
|
+
finalIndices[2] = dayRange.indexOf(currentDay);
|
|
439
|
+
}
|
|
440
|
+
// 只在有实际变化时更新状态
|
|
441
|
+
if (JSON.stringify(originalValues) !== JSON.stringify(_updateValue) || JSON.stringify(finalIndices) !== JSON.stringify(state.selectedIndices)) {
|
|
442
|
+
// 一次性更新状态
|
|
443
|
+
setState(prev => Object.assign(Object.assign({}, prev), {
|
|
444
|
+
selectedIndices: finalIndices
|
|
445
|
+
}));
|
|
446
|
+
}
|
|
447
|
+
}, [state.selectedIndices]);
|
|
448
|
+
// 处理确认
|
|
449
|
+
const handleChange = React__default.useCallback(() => {
|
|
450
|
+
const newIndices = [...state.selectedIndices];
|
|
451
|
+
indexRef.current = newIndices;
|
|
452
|
+
let newValue = newIndices.length && mode !== 'selector' ? newIndices : newIndices[0];
|
|
453
|
+
if (mode === 'time') {
|
|
454
|
+
const range = [hoursRange.slice(), minutesRange.slice()];
|
|
455
|
+
// 安全的时间处理,添加边界检查
|
|
456
|
+
const timeArr = newIndices.map((idx, i) => {
|
|
457
|
+
const index = Math.max(0, Math.min(idx, range[i].length - 1));
|
|
458
|
+
return range[i][index] || (i === 0 ? '00' : '00');
|
|
459
|
+
});
|
|
460
|
+
// 确保时间值有效
|
|
461
|
+
const validTimeArr = timeArr.map(time => {
|
|
462
|
+
const num = parseInt(time);
|
|
463
|
+
return isNaN(num) ? '00' : time;
|
|
464
|
+
});
|
|
465
|
+
indexRef.current = validTimeArr.map(item => parseInt(item));
|
|
466
|
+
newValue = validTimeArr.join(':');
|
|
467
|
+
}
|
|
468
|
+
if (mode === 'date') {
|
|
469
|
+
if (!pickerDateRef.current) return;
|
|
470
|
+
const {
|
|
471
|
+
_start,
|
|
472
|
+
_end,
|
|
473
|
+
_updateValue
|
|
474
|
+
} = pickerDateRef.current;
|
|
475
|
+
const currentYear = _updateValue[0];
|
|
476
|
+
const currentMonth = _updateValue[1];
|
|
477
|
+
const yearRange = getYearRange(_start.getFullYear(), _end.getFullYear());
|
|
478
|
+
const monthRange = getMonthRange(_start, _end, currentYear);
|
|
479
|
+
const dayRange = getDayRange(_start, _end, currentYear, currentMonth);
|
|
480
|
+
// 添加边界检查,确保索引有效
|
|
481
|
+
const yearIndex = Math.min(Math.max(Math.floor(newIndices[0]), 0), yearRange.length - 1);
|
|
482
|
+
const monthIndex = Math.min(Math.max(Math.floor(newIndices[1]), 0), monthRange.length - 1);
|
|
483
|
+
const dayIndex = Math.min(Math.max(Math.floor(newIndices[2]), 0), dayRange.length - 1);
|
|
484
|
+
const year = yearRange[yearIndex];
|
|
485
|
+
const month = monthRange[monthIndex];
|
|
486
|
+
const day = dayRange[dayIndex];
|
|
487
|
+
// 确保所有值都存在
|
|
488
|
+
if (year === undefined || month === undefined || day === undefined) {
|
|
489
|
+
console.warn('Date picker: invalid date values', {
|
|
490
|
+
year,
|
|
491
|
+
month,
|
|
492
|
+
day
|
|
493
|
+
});
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
if (fields === 'year') {
|
|
497
|
+
newValue = year.toString();
|
|
498
|
+
} else if (fields === 'month') {
|
|
499
|
+
// YYYY-MM 格式
|
|
500
|
+
newValue = `${year}-${month < 10 ? `0${month}` : month}`;
|
|
501
|
+
} else {
|
|
502
|
+
// YYYY-MM-DD 格式
|
|
503
|
+
newValue = `${year}-${month < 10 ? `0${month}` : month}-${day < 10 ? `0${day}` : day}`;
|
|
504
|
+
}
|
|
505
|
+
hidePicker();
|
|
506
|
+
setState(prev => Object.assign(Object.assign({}, prev), {
|
|
507
|
+
pickerValue: newValue
|
|
508
|
+
}));
|
|
509
|
+
onChange === null || onChange === void 0 ? void 0 : onChange({
|
|
510
|
+
detail: {
|
|
511
|
+
value: newValue
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
if (mode === 'region') {
|
|
517
|
+
if (!regionData) {
|
|
518
|
+
console.error('Picker: regionData is required for region mode');
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
const validation = validateRegionData(regionData, 'Picker');
|
|
522
|
+
if (!validation.valid) {
|
|
523
|
+
console.error(validation.error);
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
// 直接使用索引数组
|
|
527
|
+
const selectedCodes = [];
|
|
528
|
+
let postcode = '';
|
|
529
|
+
// 安全获取选中值
|
|
530
|
+
let currentData = regionData;
|
|
531
|
+
for (let i = 0; i < columnsCount; i++) {
|
|
532
|
+
if (!currentData || currentData.length === 0) break;
|
|
533
|
+
const index = newIndices[i] || 0;
|
|
534
|
+
if (index < 0 || index >= currentData.length) break;
|
|
535
|
+
const item = currentData[index];
|
|
536
|
+
selectedCodes.push(item.code);
|
|
537
|
+
// 如果是最后一项,获取邮政编码
|
|
538
|
+
if (i === columnsCount - 1 && item.postcode) {
|
|
539
|
+
postcode = item.postcode;
|
|
540
|
+
}
|
|
541
|
+
// 准备下一级数据
|
|
542
|
+
currentData = item.children || [];
|
|
543
|
+
}
|
|
544
|
+
// 检查索引数组长度是否符合要求
|
|
545
|
+
if (newIndices.length < columnsCount) {
|
|
546
|
+
console.warn('Region picker: incomplete selection');
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
// 触发 onChange 事件,包含 code 信息
|
|
550
|
+
hidePicker();
|
|
551
|
+
setState(prev => Object.assign(Object.assign({}, prev), {
|
|
552
|
+
pickerValue: newIndices
|
|
553
|
+
}));
|
|
554
|
+
onChange === null || onChange === void 0 ? void 0 : onChange({
|
|
555
|
+
detail: {
|
|
556
|
+
value: newIndices,
|
|
557
|
+
code: selectedCodes.join(','),
|
|
558
|
+
postcode
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
hidePicker();
|
|
564
|
+
setState(prev => Object.assign(Object.assign({}, prev), {
|
|
565
|
+
pickerValue: newValue
|
|
566
|
+
}));
|
|
567
|
+
// 触发 onChange 事件,格式与原始组件一致
|
|
568
|
+
onChange === null || onChange === void 0 ? void 0 : onChange({
|
|
569
|
+
detail: {
|
|
570
|
+
value: newValue
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
}, [hidePicker, state.selectedIndices, mode, fields, onChange, regionData, columnsCount]);
|
|
574
|
+
// 处理列变化
|
|
575
|
+
const handleColumnChange = React__default.useCallback(e => {
|
|
576
|
+
const {
|
|
577
|
+
columnId,
|
|
578
|
+
index
|
|
579
|
+
} = e;
|
|
580
|
+
onColumnChange === null || onColumnChange === void 0 ? void 0 : onColumnChange({
|
|
581
|
+
detail: {
|
|
582
|
+
column: Number(columnId),
|
|
583
|
+
value: index
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
}, [onColumnChange]);
|
|
587
|
+
// 处理取消
|
|
588
|
+
const handleCancel = React__default.useCallback(() => {
|
|
589
|
+
hidePicker();
|
|
590
|
+
onCancel === null || onCancel === void 0 ? void 0 : onCancel();
|
|
591
|
+
}, [hidePicker, onCancel]);
|
|
592
|
+
// 渲染选择器组
|
|
593
|
+
const renderPickerGroup = React__default.useMemo(() => {
|
|
594
|
+
switch (mode) {
|
|
595
|
+
case 'multiSelector':
|
|
596
|
+
{
|
|
597
|
+
return range.map((rangeItem, index) => /*#__PURE__*/jsx(PickerGroup, {
|
|
598
|
+
range: rangeItem,
|
|
599
|
+
rangeKey: rangeKey,
|
|
600
|
+
updateIndex: updateIndex,
|
|
601
|
+
onColumnChange: handleColumnChange,
|
|
602
|
+
columnId: String(index),
|
|
603
|
+
selectedIndex: state.selectedIndices[index] // 传递对应列的selectedIndex
|
|
604
|
+
,
|
|
605
|
+
colors: colors
|
|
606
|
+
}, index));
|
|
607
|
+
}
|
|
608
|
+
case 'time':
|
|
609
|
+
{
|
|
610
|
+
return [/*#__PURE__*/jsx(PickerGroup, {
|
|
611
|
+
mode: "time",
|
|
612
|
+
range: hoursRange,
|
|
613
|
+
updateIndex: updateIndex,
|
|
614
|
+
columnId: "0",
|
|
615
|
+
selectedIndex: state.selectedIndices[0] // 传递小时列的selectedIndex
|
|
616
|
+
,
|
|
617
|
+
colors: colors
|
|
618
|
+
}, `hour-${state.timestamp}`), /*#__PURE__*/jsx(PickerGroup, {
|
|
619
|
+
mode: "time",
|
|
620
|
+
range: minutesRange,
|
|
621
|
+
updateIndex: updateIndex,
|
|
622
|
+
columnId: "1",
|
|
623
|
+
selectedIndex: state.selectedIndices[1] // 传递分钟列的selectedIndex
|
|
624
|
+
,
|
|
625
|
+
colors: colors
|
|
626
|
+
}, `minute-${state.timestamp}`)];
|
|
627
|
+
}
|
|
628
|
+
case 'date':
|
|
629
|
+
{
|
|
630
|
+
if (!pickerDateRef.current) return null;
|
|
631
|
+
const {
|
|
632
|
+
_start,
|
|
633
|
+
_end,
|
|
634
|
+
_updateValue
|
|
635
|
+
} = pickerDateRef.current;
|
|
636
|
+
const currentYear = _updateValue[0];
|
|
637
|
+
const currentMonth = _updateValue[1];
|
|
638
|
+
const langText = getLanguageText(lang);
|
|
639
|
+
const isEnglish = lang === 'en-US' || lang === 'en-GB';
|
|
640
|
+
const yearRange = getYearRange(_start.getFullYear(), _end.getFullYear()).map(item => isEnglish ? `${langText.year}${item}` : `${item}${langText.year}`);
|
|
641
|
+
const monthRange = getMonthRange(_start, _end, currentYear).map(item => isEnglish ? `${langText.month}${item < 10 ? `0${item}` : item}` : `${item < 10 ? `0${item}` : item}${langText.month}`);
|
|
642
|
+
const dayRange = getDayRange(_start, _end, currentYear, currentMonth).map(item => isEnglish ? `${langText.day}${item < 10 ? `0${item}` : item}` : `${item < 10 ? `0${item}` : item}${langText.day}`);
|
|
643
|
+
const renderView = [/*#__PURE__*/jsx(PickerGroup, {
|
|
644
|
+
mode: "date",
|
|
645
|
+
range: yearRange,
|
|
646
|
+
updateDay: updateDay,
|
|
647
|
+
updateIndex: updateIndex,
|
|
648
|
+
columnId: "0",
|
|
649
|
+
selectedIndex: state.selectedIndices[0] // 传递年份列的selectedIndex
|
|
650
|
+
,
|
|
651
|
+
colors: colors
|
|
652
|
+
}, `year`)];
|
|
653
|
+
if (fields === 'month' || fields === 'day') {
|
|
654
|
+
renderView.push(/*#__PURE__*/jsx(PickerGroup, {
|
|
655
|
+
mode: "date",
|
|
656
|
+
range: monthRange,
|
|
657
|
+
updateDay: updateDay,
|
|
658
|
+
updateIndex: updateIndex,
|
|
659
|
+
columnId: "1",
|
|
660
|
+
selectedIndex: state.selectedIndices[1] // 传递月份列的selectedIndex
|
|
661
|
+
,
|
|
662
|
+
colors: colors
|
|
663
|
+
}, `month`));
|
|
664
|
+
}
|
|
665
|
+
if (fields === 'day') {
|
|
666
|
+
renderView.push(/*#__PURE__*/jsx(PickerGroup, {
|
|
667
|
+
mode: "date",
|
|
668
|
+
range: dayRange,
|
|
669
|
+
updateDay: updateDay,
|
|
670
|
+
updateIndex: updateIndex,
|
|
671
|
+
columnId: "2",
|
|
672
|
+
selectedIndex: state.selectedIndices[2] // 传递日期列的selectedIndex
|
|
673
|
+
,
|
|
674
|
+
colors: colors
|
|
675
|
+
}, `day`));
|
|
676
|
+
}
|
|
677
|
+
return renderView;
|
|
678
|
+
}
|
|
679
|
+
case 'region':
|
|
680
|
+
{
|
|
681
|
+
// region 模式处理 - 自动识别数据层级
|
|
682
|
+
if (!regionData) {
|
|
683
|
+
console.error('Picker: regionData is required for region mode');
|
|
684
|
+
return null;
|
|
685
|
+
}
|
|
686
|
+
// 简化验证逻辑
|
|
687
|
+
if (!validateRegionData(regionData, 'Picker').valid) {
|
|
688
|
+
return null;
|
|
689
|
+
}
|
|
690
|
+
const columns = [];
|
|
691
|
+
let currentData = regionData;
|
|
692
|
+
for (let i = 0; i < columnsCount; i++) {
|
|
693
|
+
if (i > 0 && (currentData === null || currentData === void 0 ? void 0 : currentData.length)) {
|
|
694
|
+
// 获取上一级选中项的children作为当前列数据
|
|
695
|
+
const prevIndex = state.selectedIndices[i - 1] || 0;
|
|
696
|
+
if (prevIndex >= 0 && prevIndex < currentData.length) {
|
|
697
|
+
currentData = currentData[prevIndex].children || [];
|
|
698
|
+
} else {
|
|
699
|
+
currentData = [];
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
columns.push(/*#__PURE__*/jsx(PickerGroup, {
|
|
703
|
+
mode: "region",
|
|
704
|
+
range: currentData,
|
|
705
|
+
rangeKey: "value",
|
|
706
|
+
updateIndex: updateIndex,
|
|
707
|
+
columnId: String(i),
|
|
708
|
+
selectedIndex: state.selectedIndices[i],
|
|
709
|
+
colors: colors
|
|
710
|
+
}, `region-${i}`));
|
|
711
|
+
}
|
|
712
|
+
return columns;
|
|
713
|
+
}
|
|
714
|
+
default:
|
|
715
|
+
return /*#__PURE__*/jsx(PickerGroup, {
|
|
716
|
+
range: range,
|
|
717
|
+
rangeKey: rangeKey,
|
|
718
|
+
updateIndex: updateIndex,
|
|
719
|
+
columnId: "0",
|
|
720
|
+
selectedIndex: state.selectedIndices[0] // 传递selector模式的selectedIndex
|
|
721
|
+
,
|
|
722
|
+
colors: colors
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
}, [mode, range, rangeKey, fields, updateIndex, updateDay, handleColumnChange, pickerDateRef.current, level, regionData, state.selectedIndices, columnsCount, lang, colors]);
|
|
726
|
+
// 动画类名控制逻辑
|
|
727
|
+
const clsMask = classNames('taro-picker__mask-overlay', 'taro-picker__animate-fade-in', {
|
|
728
|
+
'taro-picker__animate-fade-out': state.fadeOut
|
|
729
|
+
});
|
|
730
|
+
const clsSlider = classNames('taro-picker', 'taro-picker__animate-slide-up', {
|
|
731
|
+
'taro-picker__animate-slide-down': state.fadeOut
|
|
732
|
+
});
|
|
733
|
+
// 暴露方法给外部
|
|
734
|
+
React__default.useImperativeHandle(ref, () => ({
|
|
735
|
+
showPicker,
|
|
736
|
+
hidePicker
|
|
737
|
+
}));
|
|
738
|
+
// 获取语言文本
|
|
739
|
+
const langText = getLanguageText(lang);
|
|
740
|
+
return /*#__PURE__*/jsxs(View, {
|
|
741
|
+
ref: ref // 直接绑定 ref
|
|
742
|
+
,
|
|
743
|
+
...(formType ? {
|
|
744
|
+
'data-form-type': formType
|
|
745
|
+
} : {}),
|
|
746
|
+
...omit(restProps, ['mode', 'disabled', 'range', 'rangeKey', 'value', 'start', 'end', 'fields', 'name', 'textProps', 'onChange', 'onColumnChange', 'onCancel', 'children', 'formType']),
|
|
747
|
+
children: [/*#__PURE__*/jsx(View, {
|
|
748
|
+
onClick: showPicker,
|
|
749
|
+
children: children
|
|
750
|
+
}), !state.hidden && /*#__PURE__*/jsxs(View, {
|
|
751
|
+
className: "taro-picker__overlay",
|
|
752
|
+
children: [/*#__PURE__*/jsx(View, {
|
|
753
|
+
className: clsMask,
|
|
754
|
+
onClick: handleCancel
|
|
755
|
+
}), /*#__PURE__*/jsxs(View, {
|
|
756
|
+
className: clsSlider,
|
|
757
|
+
children: [/*#__PURE__*/jsxs(View, {
|
|
758
|
+
className: "taro-picker__hd",
|
|
759
|
+
children: [/*#__PURE__*/jsx(View, {
|
|
760
|
+
className: "taro-picker__action",
|
|
761
|
+
onClick: handleCancel,
|
|
762
|
+
style: {
|
|
763
|
+
color: colors.cancelButtonColor
|
|
764
|
+
},
|
|
765
|
+
children: (_a = textProps.cancelText) !== null && _a !== void 0 ? _a : langText.cancel
|
|
766
|
+
}), headerText && /*#__PURE__*/jsx(View, {
|
|
767
|
+
className: "taro-picker__title",
|
|
768
|
+
children: headerText
|
|
769
|
+
}), /*#__PURE__*/jsx(View, {
|
|
770
|
+
className: "taro-picker__action",
|
|
771
|
+
onClick: handleChange,
|
|
772
|
+
style: {
|
|
773
|
+
color: colors.confirmButtonColor
|
|
774
|
+
},
|
|
775
|
+
children: (_b = textProps.okText) !== null && _b !== void 0 ? _b : langText.confirm
|
|
776
|
+
})]
|
|
777
|
+
}), /*#__PURE__*/jsx(View, {
|
|
778
|
+
className: "taro-picker__bd",
|
|
779
|
+
children: renderPickerGroup
|
|
780
|
+
})]
|
|
781
|
+
})]
|
|
782
|
+
})]
|
|
783
|
+
});
|
|
784
|
+
});
|
|
785
|
+
Picker.displayName = 'Picker';
|
|
786
|
+
|
|
787
|
+
export { Picker as default };
|
|
788
|
+
//# sourceMappingURL=index.js.map
|