@ticatec/uniface-element 0.2.4 → 0.3.0
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/accordion/README.md +169 -0
- package/dist/accordion/README_CN.md +176 -0
- package/dist/action-bar/README.md +275 -0
- package/dist/action-bar/README_CN.md +285 -0
- package/dist/app-layout/README.md +374 -0
- package/dist/app-layout/README_CN.md +381 -0
- package/dist/app-top-bar/README.md +357 -0
- package/dist/app-top-bar/README_CN.md +365 -0
- package/dist/attachment-files/AttachmentFilesField.svelte +67 -1
- package/dist/attachment-files/AttachmentFilesField.svelte.d.ts +8 -1
- package/dist/base-calendar/CalendarBase.svelte +0 -3
- package/dist/box/README.md +385 -0
- package/dist/box/README_CN.md +393 -0
- package/dist/breadcrumbs/README.md +442 -0
- package/dist/breadcrumbs/README_CN.md +442 -0
- package/dist/button/Button.svelte +27 -7
- package/dist/button/Button.svelte.d.ts +3 -1
- package/dist/button/IconButton.svelte +26 -6
- package/dist/button/IconButton.svelte.d.ts +3 -1
- package/dist/button/IconButton_README.md +513 -0
- package/dist/button/IconButton_README_CN.md +513 -0
- package/dist/button/README.md +428 -0
- package/dist/button/README_CN.md +428 -0
- package/dist/button/TextButton.svelte +26 -6
- package/dist/button/TextButton.svelte.d.ts +3 -1
- package/dist/button/TextButton_README.md +571 -0
- package/dist/button/TextButton_README_CN.md +571 -0
- package/dist/card/README.md +666 -0
- package/dist/card/README_CN.md +666 -0
- package/dist/cascade-options-select/CascadeOptionSelect.svelte +43 -7
- package/dist/cascade-options-select/CascadeOptionSelect.svelte.d.ts +3 -4
- package/dist/cascade-options-select/README.md +663 -2
- package/dist/cascade-options-select/README_CN.md +664 -0
- package/dist/checkbox/CheckBox.svelte +20 -6
- package/dist/checkbox/CheckBox.svelte.d.ts +4 -2
- package/dist/checkbox/README.md +562 -0
- package/dist/checkbox/README_CN.md +727 -0
- package/dist/common-editor/NumberInput.svelte +39 -26
- package/dist/common-editor/NumberInput.svelte.d.ts +5 -1
- package/dist/concise-data-table/ConciseListTable.svelte +133 -36
- package/dist/concise-data-table/ConciseListTable.svelte.d.ts +8 -1
- package/dist/concise-data-table/DataCell.svelte +5 -6
- package/dist/concise-data-table/DataRow.svelte +22 -2
- package/dist/concise-data-table/DataRow.svelte.d.ts +1 -0
- package/dist/concise-data-table/README.md +606 -0
- package/dist/concise-data-table/README_CN.md +611 -0
- package/dist/context-menu/ContextMenu.svelte +138 -21
- package/dist/context-menu/ContextMenu.svelte.d.ts +5 -0
- package/dist/context-menu/ContextMenuItem.d.ts +20 -4
- package/dist/context-menu/README.md +551 -0
- package/dist/context-menu/README_CN.md +551 -0
- package/dist/criteria-field/CriteriaField.svelte +3 -5
- package/dist/criteria-field/README.md +532 -0
- package/dist/criteria-field/README_CN.md +532 -0
- package/dist/data-table/README.md +712 -0
- package/dist/data-table/README_CN.md +712 -0
- package/dist/date-picker/DatePicker.svelte +44 -7
- package/dist/date-picker/DatePicker.svelte.d.ts +3 -4
- package/dist/date-picker/DateTimePicker.svelte +32 -8
- package/dist/date-picker/DateTimePicker.svelte.d.ts +3 -4
- package/dist/date-picker/README.md +326 -0
- package/dist/date-picker/README_CN.md +316 -0
- package/dist/date-picker/ScrollBar.svelte +18 -8
- package/dist/date-picker/ScrollBar.svelte.d.ts +1 -1
- package/dist/date-picker/TimePanel.svelte +5 -5
- package/dist/date-picker/TimePanel.svelte.d.ts +1 -1
- package/dist/date-range/README.md +374 -0
- package/dist/date-range/README_CN.md +374 -0
- package/dist/dialog/README.md +596 -0
- package/dist/dialog/README_CN.md +596 -0
- package/dist/drawer/README.md +679 -1
- package/dist/drawer/README_CN.md +679 -0
- package/dist/group-box/group-check-box/GroupCheckBox.svelte +62 -5
- package/dist/group-box/group-check-box/GroupCheckBox.svelte.d.ts +7 -2
- package/dist/group-box/group-check-box/README.md +749 -0
- package/dist/group-box/group-check-box/README_CN.md +750 -0
- package/dist/group-box/group-radio-box/GroupRadioBox.svelte +69 -4
- package/dist/group-box/group-radio-box/GroupRadioBox.svelte.d.ts +8 -2
- package/dist/group-box/group-radio-box/README.md +920 -0
- package/dist/group-box/group-radio-box/README_CN.md +920 -0
- package/dist/image-files/README.md +1346 -0
- package/dist/image-files/README_CN.md +1346 -0
- package/dist/input-options-select/InputOptionsSelect.svelte +161 -65
- package/dist/input-options-select/InputOptionsSelect.svelte.d.ts +4 -1
- package/dist/lib/forwardAllEvents.d.ts +3 -0
- package/dist/lib/forwardAllEvents.js +24 -0
- package/dist/list-box/README.md +579 -0
- package/dist/list-box/README_CN.md +682 -0
- package/dist/lookup-editor/LookupEditor.svelte +15 -3
- package/dist/lookup-editor/LookupEditor.svelte.d.ts +3 -4
- package/dist/lookup-editor/README.md +684 -0
- package/dist/lookup-editor/README_CN.md +684 -0
- package/dist/memo-editor/MemoEditor.svelte +77 -27
- package/dist/memo-editor/MemoEditor.svelte.d.ts +6 -3
- package/dist/memo-editor/README.md +819 -0
- package/dist/memo-editor/README_CN.md +819 -0
- package/dist/message-box/README.md +398 -0
- package/dist/message-box/README_CN.md +398 -0
- package/dist/number-editor/NumberEditor.svelte +2 -2
- package/dist/number-editor/NumberEditor.svelte.d.ts +1 -1
- package/dist/number-editor/README.md +353 -22
- package/dist/number-editor/README_CN.md +357 -0
- package/dist/number-range/README.md +443 -0
- package/dist/number-range/README_CN.md +443 -0
- package/dist/options-multi-select/OptionsMultiSelect.svelte +87 -12
- package/dist/options-multi-select/OptionsMultiSelect.svelte.d.ts +3 -1
- package/dist/options-multi-select/README.md +480 -0
- package/dist/options-multi-select/README_CN.md +480 -0
- package/dist/options-select/OptionsSelect.svelte +36 -5
- package/dist/options-select/OptionsSelect.svelte.d.ts +3 -4
- package/dist/options-select/README.md +632 -0
- package/dist/options-select/README_CN.md +632 -0
- package/dist/page/README.md +605 -0
- package/dist/page/README_CN.md +605 -0
- package/dist/pagination/README.md +946 -0
- package/dist/pagination/README_CN.md +946 -0
- package/dist/pagination-panel/README.md +1075 -0
- package/dist/pagination-panel/README_CN.md +1075 -0
- package/dist/popup-hint/README.md +841 -0
- package/dist/popup-hint/README_CN.md +840 -0
- package/dist/progress-bar/README.md +242 -0
- package/dist/progress-bar/README_CN.md +242 -0
- package/dist/progress-step-bar/README.md +316 -0
- package/dist/progress-step-bar/README_CN.md +316 -0
- package/dist/prompts-text-editor/PromptsTextEditor.svelte +6 -3
- package/dist/prompts-text-editor/PromptsTextEditor.svelte.d.ts +3 -1
- package/dist/prompts-text-editor/README.md +329 -0
- package/dist/prompts-text-editor/README_CN.md +329 -0
- package/dist/property-editor/README.md +969 -0
- package/dist/property-editor/README_CN.md +969 -0
- package/dist/radio-button/README.md +372 -0
- package/dist/radio-button/README_CN.md +372 -0
- package/dist/radio-button/RadioButton.svelte +32 -3
- package/dist/radio-button/RadioButton.svelte.d.ts +12 -1
- package/dist/search-box/README.md +425 -0
- package/dist/search-box/README_CN.md +424 -0
- package/dist/search-box/SearchBox.svelte +22 -6
- package/dist/search-box/SearchBox.svelte.d.ts +4 -1
- package/dist/tabs/README.md +919 -0
- package/dist/tabs/README_CN.md +920 -0
- package/dist/tag/README.md +922 -0
- package/dist/tag/README_CN.md +922 -0
- package/dist/text-editor/PasswordEditor.svelte +17 -2
- package/dist/text-editor/PasswordEditor.svelte.d.ts +3 -3
- package/dist/text-editor/PasswordEditor_README.md +551 -0
- package/dist/text-editor/PasswordEditor_README_CN.md +551 -0
- package/dist/text-editor/README.md +466 -0
- package/dist/text-editor/README_CN.md +466 -0
- package/dist/text-editor/TextEditor.svelte +51 -19
- package/dist/text-editor/TextEditor.svelte.d.ts +0 -1
- package/dist/time-editor/README.md +498 -24
- package/dist/time-editor/README_CN.md +498 -0
- package/dist/time-editor/TimeEditor.svelte +7 -3
- package/dist/time-editor/TimeEditor.svelte.d.ts +3 -1
- package/dist/toast/README.md +667 -0
- package/dist/toast/README_CN.md +667 -0
- package/dist/transfer/README.md +795 -0
- package/dist/transfer/Transfer.svelte +6 -4
- package/dist/transfer/Transfer.svelte.d.ts +3 -1
- package/dist/tree-view/README.md +761 -0
- package/dist/tree-view/README_CN.md +761 -0
- package/dist/tree-view/TreeView.svelte +5 -3
- package/dist/tree-view/TreeView.svelte.d.ts +3 -1
- package/dist/unit-number-editor/README.md +546 -20
- package/dist/unit-number-editor/README_CN.md +525 -0
- package/dist/unit-number-editor/UnitNumberEditor.svelte +62 -8
- package/dist/unit-number-editor/UnitNumberEditor.svelte.d.ts +5 -1
- package/docs/form-field/cn/README_CN.md +364 -0
- package/docs/form-field/en/README.md +364 -0
- package/docs/form-panel/flex-form/cn/README_CN.md +586 -0
- package/docs/form-panel/flex-form/en/README.md +586 -0
- package/docs/form-panel/flex-row-form/cn/README_CN.md +787 -0
- package/docs/form-panel/flex-row-form/en/README.md +787 -0
- package/docs/form-panel/grid-form/en/README.md +744 -0
- package/package.json +1 -1
- package/dist/drawer/README-CN.md +0 -1
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
# NumberRange 数字范围
|
|
2
|
+
|
|
3
|
+
用于指定数字范围的双数字输入组件,具有自动验证功能,确保"起始"值不超过"结束"值。
|
|
4
|
+
|
|
5
|
+
## 功能特性
|
|
6
|
+
|
|
7
|
+
- **范围输入**: 两个连接的数字输入框,用于"起始"和"结束"值
|
|
8
|
+
- **自动验证**: 确保逻辑范围顺序(起始 ≤ 结束)
|
|
9
|
+
- **负数支持**: 可选的负数值支持
|
|
10
|
+
- **边界约束**: 为整个范围设置最小/最大限制
|
|
11
|
+
- **多种变体**: 支持轮廓、填充、朴素和默认样式
|
|
12
|
+
- **紧凑模式**: 用于密集表单的节省空间布局
|
|
13
|
+
- **清除功能**: 内置清除按钮重置两个值
|
|
14
|
+
- **实时验证**: 输入过程中的即时反馈
|
|
15
|
+
- **焦点管理**: 输入框之间的自动焦点处理
|
|
16
|
+
|
|
17
|
+
## 安装
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @ticatec/uniface-element
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 基本用法
|
|
24
|
+
|
|
25
|
+
```svelte
|
|
26
|
+
<script>
|
|
27
|
+
import NumberRange from '@ticatec/uniface-element/NumberRange';
|
|
28
|
+
|
|
29
|
+
let fromValue = 10;
|
|
30
|
+
let toValue = 50;
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<NumberRange bind:fromValue bind:toValue />
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## API 参考
|
|
37
|
+
|
|
38
|
+
### 属性
|
|
39
|
+
|
|
40
|
+
| 属性 | 类型 | 默认值 | 描述 |
|
|
41
|
+
|------|------|---------|-------------|
|
|
42
|
+
| `fromValue` | `number \| null` | `null` | 范围的"起始"(最小)值 |
|
|
43
|
+
| `toValue` | `number \| null` | `null` | 范围的"结束"(最大)值 |
|
|
44
|
+
| `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | 视觉样式变体 |
|
|
45
|
+
| `compact` | `boolean` | `false` | 启用紧凑显示模式 |
|
|
46
|
+
| `style` | `string` | `''` | 自定义CSS样式 |
|
|
47
|
+
| `min` | `number \| null` | `null` | 两个输入框的最小允许值 |
|
|
48
|
+
| `max` | `number \| null` | `null` | 两个输入框的最大允许值 |
|
|
49
|
+
| `allowNegative` | `boolean` | `false` | 允许两个输入框输入负数 |
|
|
50
|
+
| `class` | `string` | `''` | CSS类名 |
|
|
51
|
+
|
|
52
|
+
## 示例
|
|
53
|
+
|
|
54
|
+
### 基本范围选择
|
|
55
|
+
|
|
56
|
+
```svelte
|
|
57
|
+
<script>
|
|
58
|
+
import NumberRange from '@ticatec/uniface-element/NumberRange';
|
|
59
|
+
import FormField from '@ticatec/uniface-element/FormField';
|
|
60
|
+
|
|
61
|
+
let ageFrom = 18;
|
|
62
|
+
let ageTo = 65;
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
<FormField label="年龄范围">
|
|
66
|
+
<NumberRange
|
|
67
|
+
bind:fromValue={ageFrom}
|
|
68
|
+
bind:toValue={ageTo}
|
|
69
|
+
variant="outlined"
|
|
70
|
+
min={0}
|
|
71
|
+
max={120}
|
|
72
|
+
/>
|
|
73
|
+
</FormField>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 价格范围过滤器
|
|
77
|
+
|
|
78
|
+
```svelte
|
|
79
|
+
<script>
|
|
80
|
+
import NumberRange from '@ticatec/uniface-element/NumberRange';
|
|
81
|
+
|
|
82
|
+
let priceMin = 100;
|
|
83
|
+
let priceMax = 1000;
|
|
84
|
+
|
|
85
|
+
$: console.log(`价格范围: ¥${priceMin} - ¥${priceMax}`);
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<div class="price-filter">
|
|
89
|
+
<label>价格范围 (¥)</label>
|
|
90
|
+
<NumberRange
|
|
91
|
+
bind:fromValue={priceMin}
|
|
92
|
+
bind:toValue={priceMax}
|
|
93
|
+
variant="filled"
|
|
94
|
+
min={0}
|
|
95
|
+
max={10000}
|
|
96
|
+
/>
|
|
97
|
+
<div class="range-display">
|
|
98
|
+
¥{priceMin || 0} - ¥{priceMax || 0}
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 温度范围(支持负值)
|
|
104
|
+
|
|
105
|
+
```svelte
|
|
106
|
+
<script>
|
|
107
|
+
import NumberRange from '@ticatec/uniface-element/NumberRange';
|
|
108
|
+
|
|
109
|
+
let tempMin = -10;
|
|
110
|
+
let tempMax = 25;
|
|
111
|
+
</script>
|
|
112
|
+
|
|
113
|
+
<div class="temperature-range">
|
|
114
|
+
<label>温度范围 (°C)</label>
|
|
115
|
+
<NumberRange
|
|
116
|
+
bind:fromValue={tempMin}
|
|
117
|
+
bind:toValue={tempMax}
|
|
118
|
+
variant="outlined"
|
|
119
|
+
allowNegative={true}
|
|
120
|
+
min={-50}
|
|
121
|
+
max={50}
|
|
122
|
+
/>
|
|
123
|
+
</div>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 搜索条件表单
|
|
127
|
+
|
|
128
|
+
```svelte
|
|
129
|
+
<script>
|
|
130
|
+
import NumberRange from '@ticatec/uniface-element/NumberRange';
|
|
131
|
+
import CriteriaField from '@ticatec/uniface-element/CriteriaField';
|
|
132
|
+
|
|
133
|
+
let criteria = {
|
|
134
|
+
salaryFrom: null,
|
|
135
|
+
salaryTo: null,
|
|
136
|
+
experienceFrom: 0,
|
|
137
|
+
experienceTo: 10,
|
|
138
|
+
ageFrom: 22,
|
|
139
|
+
ageTo: 60
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
function handleSearch() {
|
|
143
|
+
console.log('搜索条件:', criteria);
|
|
144
|
+
}
|
|
145
|
+
</script>
|
|
146
|
+
|
|
147
|
+
<div class="search-form">
|
|
148
|
+
<CriteriaField label="薪资范围" size="x30">
|
|
149
|
+
<NumberRange
|
|
150
|
+
bind:fromValue={criteria.salaryFrom}
|
|
151
|
+
bind:toValue={criteria.salaryTo}
|
|
152
|
+
variant="outlined"
|
|
153
|
+
min={0}
|
|
154
|
+
max={500000}
|
|
155
|
+
/>
|
|
156
|
+
</CriteriaField>
|
|
157
|
+
|
|
158
|
+
<CriteriaField label="工作年限" size="x30">
|
|
159
|
+
<NumberRange
|
|
160
|
+
bind:fromValue={criteria.experienceFrom}
|
|
161
|
+
bind:toValue={criteria.experienceTo}
|
|
162
|
+
variant="outlined"
|
|
163
|
+
min={0}
|
|
164
|
+
max={50}
|
|
165
|
+
/>
|
|
166
|
+
</CriteriaField>
|
|
167
|
+
|
|
168
|
+
<CriteriaField label="年龄范围" size="x30">
|
|
169
|
+
<NumberRange
|
|
170
|
+
bind:fromValue={criteria.ageFrom}
|
|
171
|
+
bind:toValue={criteria.ageTo}
|
|
172
|
+
variant="outlined"
|
|
173
|
+
min={16}
|
|
174
|
+
max={80}
|
|
175
|
+
/>
|
|
176
|
+
</CriteriaField>
|
|
177
|
+
|
|
178
|
+
<button on:click={handleSearch}>搜索</button>
|
|
179
|
+
</div>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 表格紧凑布局
|
|
183
|
+
|
|
184
|
+
```svelte
|
|
185
|
+
<script>
|
|
186
|
+
import NumberRange from '@ticatec/uniface-element/NumberRange';
|
|
187
|
+
|
|
188
|
+
let filters = [
|
|
189
|
+
{ label: '分数', from: 80, to: 100 },
|
|
190
|
+
{ label: '数量', from: 1, to: 999 },
|
|
191
|
+
{ label: '评级', from: 3, to: 5 }
|
|
192
|
+
];
|
|
193
|
+
</script>
|
|
194
|
+
|
|
195
|
+
<table class="filter-table">
|
|
196
|
+
<thead>
|
|
197
|
+
<tr>
|
|
198
|
+
<th>过滤器</th>
|
|
199
|
+
<th>范围</th>
|
|
200
|
+
</tr>
|
|
201
|
+
</thead>
|
|
202
|
+
<tbody>
|
|
203
|
+
{#each filters as filter}
|
|
204
|
+
<tr>
|
|
205
|
+
<td>{filter.label}</td>
|
|
206
|
+
<td>
|
|
207
|
+
<NumberRange
|
|
208
|
+
bind:fromValue={filter.from}
|
|
209
|
+
bind:toValue={filter.to}
|
|
210
|
+
compact
|
|
211
|
+
variant="outlined"
|
|
212
|
+
min={0}
|
|
213
|
+
/>
|
|
214
|
+
</td>
|
|
215
|
+
</tr>
|
|
216
|
+
{/each}
|
|
217
|
+
</tbody>
|
|
218
|
+
</table>
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### 带验证的动态范围
|
|
222
|
+
|
|
223
|
+
```svelte
|
|
224
|
+
<script>
|
|
225
|
+
import NumberRange from '@ticatec/uniface-element/NumberRange';
|
|
226
|
+
|
|
227
|
+
let budgetMin = 1000;
|
|
228
|
+
let budgetMax = 5000;
|
|
229
|
+
let isValidRange = true;
|
|
230
|
+
|
|
231
|
+
$: isValidRange = budgetMin <= budgetMax;
|
|
232
|
+
$: rangeSize = budgetMax - budgetMin;
|
|
233
|
+
|
|
234
|
+
function resetRange() {
|
|
235
|
+
budgetMin = 1000;
|
|
236
|
+
budgetMax = 5000;
|
|
237
|
+
}
|
|
238
|
+
</script>
|
|
239
|
+
|
|
240
|
+
<div class="budget-selector">
|
|
241
|
+
<label>预算范围</label>
|
|
242
|
+
<NumberRange
|
|
243
|
+
bind:fromValue={budgetMin}
|
|
244
|
+
bind:toValue={budgetMax}
|
|
245
|
+
variant="outlined"
|
|
246
|
+
min={0}
|
|
247
|
+
max={100000}
|
|
248
|
+
/>
|
|
249
|
+
|
|
250
|
+
<div class="range-info" class:error={!isValidRange}>
|
|
251
|
+
{#if isValidRange}
|
|
252
|
+
<span>范围大小: ¥{rangeSize}</span>
|
|
253
|
+
{:else}
|
|
254
|
+
<span>无效范围: 最小值不能超过最大值</span>
|
|
255
|
+
{/if}
|
|
256
|
+
</div>
|
|
257
|
+
|
|
258
|
+
<button on:click={resetRange}>重置</button>
|
|
259
|
+
</div>
|
|
260
|
+
|
|
261
|
+
<style>
|
|
262
|
+
.range-info.error {
|
|
263
|
+
color: #d32f2f;
|
|
264
|
+
}
|
|
265
|
+
</style>
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### 统计分析范围
|
|
269
|
+
|
|
270
|
+
```svelte
|
|
271
|
+
<script>
|
|
272
|
+
import NumberRange from '@ticatec/uniface-element/NumberRange';
|
|
273
|
+
|
|
274
|
+
let dataRange = { min: -100, max: 100 };
|
|
275
|
+
let outlierThreshold = { from: -50, to: 50 };
|
|
276
|
+
|
|
277
|
+
$: normalDataPercentage = calculateNormalData(dataRange, outlierThreshold);
|
|
278
|
+
|
|
279
|
+
function calculateNormalData(range, threshold) {
|
|
280
|
+
const totalRange = range.max - range.min;
|
|
281
|
+
const normalRange = threshold.to - threshold.from;
|
|
282
|
+
return ((normalRange / totalRange) * 100).toFixed(1);
|
|
283
|
+
}
|
|
284
|
+
</script>
|
|
285
|
+
|
|
286
|
+
<div class="analysis-panel">
|
|
287
|
+
<h3>数据分析</h3>
|
|
288
|
+
|
|
289
|
+
<div class="range-control">
|
|
290
|
+
<label>数据范围</label>
|
|
291
|
+
<NumberRange
|
|
292
|
+
bind:fromValue={dataRange.min}
|
|
293
|
+
bind:toValue={dataRange.max}
|
|
294
|
+
variant="filled"
|
|
295
|
+
allowNegative={true}
|
|
296
|
+
min={-1000}
|
|
297
|
+
max={1000}
|
|
298
|
+
/>
|
|
299
|
+
</div>
|
|
300
|
+
|
|
301
|
+
<div class="range-control">
|
|
302
|
+
<label>正常范围(排除异常值)</label>
|
|
303
|
+
<NumberRange
|
|
304
|
+
bind:fromValue={outlierThreshold.from}
|
|
305
|
+
bind:toValue={outlierThreshold.to}
|
|
306
|
+
variant="outlined"
|
|
307
|
+
allowNegative={true}
|
|
308
|
+
min={dataRange.min}
|
|
309
|
+
max={dataRange.max}
|
|
310
|
+
/>
|
|
311
|
+
</div>
|
|
312
|
+
|
|
313
|
+
<div class="statistics">
|
|
314
|
+
<p>正常数据覆盖率: {normalDataPercentage}%</p>
|
|
315
|
+
</div>
|
|
316
|
+
</div>
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### 库存管理
|
|
320
|
+
|
|
321
|
+
```svelte
|
|
322
|
+
<script>
|
|
323
|
+
import NumberRange from '@ticatec/uniface-element/NumberRange';
|
|
324
|
+
|
|
325
|
+
let stockLevels = {
|
|
326
|
+
lowStock: 0,
|
|
327
|
+
highStock: 100,
|
|
328
|
+
reorderFrom: 10,
|
|
329
|
+
reorderTo: 90
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
$: isValidReorderRange = (
|
|
333
|
+
stockLevels.reorderFrom >= stockLevels.lowStock &&
|
|
334
|
+
stockLevels.reorderTo <= stockLevels.highStock &&
|
|
335
|
+
stockLevels.reorderFrom <= stockLevels.reorderTo
|
|
336
|
+
);
|
|
337
|
+
</script>
|
|
338
|
+
|
|
339
|
+
<div class="inventory-config">
|
|
340
|
+
<h3>库存配置</h3>
|
|
341
|
+
|
|
342
|
+
<div class="config-row">
|
|
343
|
+
<label>库存水平范围</label>
|
|
344
|
+
<NumberRange
|
|
345
|
+
bind:fromValue={stockLevels.lowStock}
|
|
346
|
+
bind:toValue={stockLevels.highStock}
|
|
347
|
+
variant="outlined"
|
|
348
|
+
min={0}
|
|
349
|
+
max={10000}
|
|
350
|
+
/>
|
|
351
|
+
</div>
|
|
352
|
+
|
|
353
|
+
<div class="config-row">
|
|
354
|
+
<label>补货范围</label>
|
|
355
|
+
<NumberRange
|
|
356
|
+
bind:fromValue={stockLevels.reorderFrom}
|
|
357
|
+
bind:toValue={stockLevels.reorderTo}
|
|
358
|
+
variant="filled"
|
|
359
|
+
min={stockLevels.lowStock}
|
|
360
|
+
max={stockLevels.highStock}
|
|
361
|
+
/>
|
|
362
|
+
</div>
|
|
363
|
+
|
|
364
|
+
<div class="validation" class:valid={isValidReorderRange}>
|
|
365
|
+
{isValidReorderRange ? '✓ 配置有效' : '✗ 补货范围无效'}
|
|
366
|
+
</div>
|
|
367
|
+
</div>
|
|
368
|
+
|
|
369
|
+
<style>
|
|
370
|
+
.validation.valid {
|
|
371
|
+
color: #2e7d32;
|
|
372
|
+
}
|
|
373
|
+
.validation:not(.valid) {
|
|
374
|
+
color: #d32f2f;
|
|
375
|
+
}
|
|
376
|
+
</style>
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## 验证逻辑
|
|
380
|
+
|
|
381
|
+
NumberRange 组件自动执行逻辑约束:
|
|
382
|
+
|
|
383
|
+
1. **范围顺序**: "起始"值不能超过"结束"值
|
|
384
|
+
2. **边界遵守**: 两个值都遵守全局最小/最大约束
|
|
385
|
+
3. **交叉验证**: 当一个值改变时,必要时另一个值会调整
|
|
386
|
+
4. **实时反馈**: 在输入过程中进行验证
|
|
387
|
+
|
|
388
|
+
## 最佳实践
|
|
389
|
+
|
|
390
|
+
1. **设置适当的边界**: 使用 min/max 防止不现实的值
|
|
391
|
+
2. **提供清晰的标签**: 指明范围代表什么
|
|
392
|
+
3. **处理空状态**: 考虑值为 null 时的情况
|
|
393
|
+
4. **明智使用紧凑模式**: 在密集布局或表格中
|
|
394
|
+
5. **验证范围逻辑**: 确保满足业务规则
|
|
395
|
+
6. **考虑用户工作流**: 用户通常应该先填写哪个输入框
|
|
396
|
+
7. **提供反馈**: 显示范围计算或验证消息
|
|
397
|
+
|
|
398
|
+
## 样式
|
|
399
|
+
|
|
400
|
+
NumberRange 组件使用以下 CSS 结构:
|
|
401
|
+
|
|
402
|
+
```css
|
|
403
|
+
.uniface-range-editor {
|
|
404
|
+
display: flex;
|
|
405
|
+
align-items: center;
|
|
406
|
+
border: 1px solid #e0e0e0;
|
|
407
|
+
border-radius: 4px;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.uniface-range-editor .divider {
|
|
411
|
+
width: 8px;
|
|
412
|
+
height: 1px;
|
|
413
|
+
background: #ccc;
|
|
414
|
+
margin: 0 4px;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.uniface-range-editor.compact {
|
|
418
|
+
padding: 4px 8px;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.uniface-range-editor.outlined {
|
|
422
|
+
border: 2px solid #1976d2;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.uniface-range-editor.filled {
|
|
426
|
+
background-color: #f5f5f5;
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
## 无障碍功能
|
|
431
|
+
|
|
432
|
+
- 为屏幕阅读器提供适当的 ARIA 标签
|
|
433
|
+
- 输入框之间的键盘导航
|
|
434
|
+
- 焦点管理和视觉指示器
|
|
435
|
+
- 支持高对比度模式
|
|
436
|
+
- 逻辑的 Tab 顺序
|
|
437
|
+
|
|
438
|
+
## 相关组件
|
|
439
|
+
|
|
440
|
+
- [NumberEditor](../number-editor/README.md) - 用于单个数字输入
|
|
441
|
+
- [DateRange](../date-range/README.md) - 用于日期范围选择
|
|
442
|
+
- [CriteriaField](../criteria-field/README.md) - 用于搜索表单布局
|
|
443
|
+
- [FormField](../form-field/README.md) - 用于表单字段标签和布局
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
|
|
3
3
|
import {DisplayMode} from "../common/DisplayMode";
|
|
4
|
-
import CheckBox from "../checkbox/CheckBox.svelte";
|
|
5
4
|
import CommonPicker from "../common/CommonPicker.svelte";
|
|
6
5
|
import utils from "../common/utils";
|
|
7
6
|
import type {OnChangeHandler} from "../common/OnChangeHandler";
|
|
@@ -24,7 +23,9 @@
|
|
|
24
23
|
export let disableOptions: Array<string> = [];
|
|
25
24
|
export let hideOptions: Array<string> = [];
|
|
26
25
|
export let displayMode: DisplayMode = DisplayMode.Edit;
|
|
27
|
-
export let
|
|
26
|
+
export let onchange: OnChangeHandler<Array<string>> = null as unknown as OnChangeHandler<Array<string>>;
|
|
27
|
+
export let onfocus: (() => void) | null = null;
|
|
28
|
+
export let onblur: (() => void) | null = null;
|
|
28
29
|
export let tagColor: string = '';
|
|
29
30
|
export let tagVariant: 'borderless' | 'border' | 'round' = 'border';
|
|
30
31
|
|
|
@@ -35,12 +36,15 @@
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
let selectedList: Array<string> = [];
|
|
38
|
-
|
|
39
39
|
let textValue: string;
|
|
40
40
|
let mh;
|
|
41
|
+
let hasFocus: boolean = false;
|
|
41
42
|
|
|
42
43
|
|
|
43
|
-
const
|
|
44
|
+
const handleCheckboxChange = (key: string) => (event: Event) => {
|
|
45
|
+
const target = event.target as HTMLInputElement;
|
|
46
|
+
const checked = target.checked;
|
|
47
|
+
|
|
44
48
|
if (checked) {
|
|
45
49
|
if (selectedList.indexOf(key) < 0) {
|
|
46
50
|
selectedList.push(key);
|
|
@@ -50,9 +54,23 @@
|
|
|
50
54
|
} else {
|
|
51
55
|
removeSelectCode(key);
|
|
52
56
|
}
|
|
53
|
-
|
|
54
|
-
|
|
57
|
+
onchange?.(selectedList);
|
|
58
|
+
|
|
59
|
+
// 确保焦点状态正确
|
|
60
|
+
if (!hasFocus) {
|
|
61
|
+
editor.focus();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
55
64
|
|
|
65
|
+
const handleOptionClick = (key: string) => (event: MouseEvent) => {
|
|
66
|
+
if (disableOptions.indexOf(key) > -1) return;
|
|
67
|
+
|
|
68
|
+
const checkbox = event.currentTarget.querySelector('input[type="checkbox"]') as HTMLInputElement;
|
|
69
|
+
if (checkbox && !checkbox.disabled) {
|
|
70
|
+
checkbox.checked = !checkbox.checked;
|
|
71
|
+
handleCheckboxChange(key)({ target: checkbox } as Event);
|
|
72
|
+
}
|
|
73
|
+
event.preventDefault();
|
|
56
74
|
}
|
|
57
75
|
|
|
58
76
|
const getOptionItem = (code: string) => {
|
|
@@ -81,6 +99,15 @@
|
|
|
81
99
|
|
|
82
100
|
const removeOption = (code: string) => async (e: MouseEvent) => {
|
|
83
101
|
removeSelectCode(code);
|
|
102
|
+
onchange?.(selectedList);
|
|
103
|
+
|
|
104
|
+
// 删除tag后确保获得焦点
|
|
105
|
+
if (!hasFocus) {
|
|
106
|
+
setTimeout(() => {
|
|
107
|
+
editor?.focus();
|
|
108
|
+
}, 10);
|
|
109
|
+
}
|
|
110
|
+
|
|
84
111
|
e.stopPropagation();
|
|
85
112
|
e.preventDefault();
|
|
86
113
|
}
|
|
@@ -91,9 +118,30 @@
|
|
|
91
118
|
}
|
|
92
119
|
}
|
|
93
120
|
|
|
121
|
+
const handleFocus = () => {
|
|
122
|
+
if (!hasFocus) {
|
|
123
|
+
hasFocus = true;
|
|
124
|
+
onfocus?.();
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const handleBlur = (event: FocusEvent) => {
|
|
129
|
+
if (!showPopup) {
|
|
130
|
+
hasFocus = false;
|
|
131
|
+
onblur?.();
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
94
135
|
const cleanData = () => {
|
|
95
136
|
value = '';
|
|
96
|
-
|
|
137
|
+
onchange?.([]);
|
|
138
|
+
|
|
139
|
+
// 清除所有选项后确保获得焦点
|
|
140
|
+
if (!hasFocus) {
|
|
141
|
+
setTimeout(() => {
|
|
142
|
+
editor?.focus();
|
|
143
|
+
}, 10);
|
|
144
|
+
}
|
|
97
145
|
}
|
|
98
146
|
|
|
99
147
|
let showPopup: boolean = false;
|
|
@@ -108,13 +156,35 @@
|
|
|
108
156
|
editor.focus();
|
|
109
157
|
}
|
|
110
158
|
|
|
159
|
+
// 监听popup关闭,如果是点击外部关闭且之前有焦点,触发blur
|
|
160
|
+
$: if (!showPopup && hasFocus) {
|
|
161
|
+
// 延迟检查,如果input仍然有焦点,说明是选择项目导致的关闭
|
|
162
|
+
setTimeout(() => {
|
|
163
|
+
if (document.activeElement !== editor) {
|
|
164
|
+
hasFocus = false;
|
|
165
|
+
onblur?.();
|
|
166
|
+
}
|
|
167
|
+
}, 50);
|
|
168
|
+
}
|
|
169
|
+
|
|
111
170
|
</script>
|
|
112
171
|
|
|
113
172
|
<CommonPicker {displayMode} {variant} {style} {compact} className="multiple" dropDownIcon="icon_google_arrow_drop_down"
|
|
114
173
|
bind:showPopup canClean={value!=null} autoFit clean={cleanData} {textValue} {readonly} {disabled}>
|
|
115
174
|
|
|
116
|
-
<div class="option-field"
|
|
117
|
-
|
|
175
|
+
<div class="option-field" on:click={()=>{
|
|
176
|
+
if (!hasFocus) {
|
|
177
|
+
showPopup = true;
|
|
178
|
+
setTimeout(() => {
|
|
179
|
+
editor?.focus();
|
|
180
|
+
}, 10);
|
|
181
|
+
} else {
|
|
182
|
+
showPopup = true;
|
|
183
|
+
}
|
|
184
|
+
}}>
|
|
185
|
+
<input bind:this={editor} style="position: absolute; left: 0; top: 0; width: 1px; height: 1px; opacity: 0; pointer-events: none"
|
|
186
|
+
tabindex="-1" aria-label="Focus management input"
|
|
187
|
+
on:keydown={handleKeyDown} on:focus={handleFocus} on:blur={handleBlur}/>
|
|
118
188
|
<div style="width: 100%; overflow: hidden; display: flex; flex-direction: row; gap: 6px">
|
|
119
189
|
{#if selectedList.length > 0 }
|
|
120
190
|
{#each selectedList as code}
|
|
@@ -131,9 +201,14 @@
|
|
|
131
201
|
<div class="options-popover" style={mh} slot="popup-panel">
|
|
132
202
|
{#each options as op}
|
|
133
203
|
{#if hideOptions.indexOf(op[keyField]) < 0}
|
|
134
|
-
<
|
|
135
|
-
|
|
136
|
-
|
|
204
|
+
<div class="option-item" class:disabled={disableOptions.indexOf(op[keyField])>-1}
|
|
205
|
+
on:click={handleOptionClick(op[keyField])}>
|
|
206
|
+
<input type="checkbox"
|
|
207
|
+
checked={selectedList.indexOf(op[keyField])>-1}
|
|
208
|
+
disabled={disableOptions.indexOf(op[keyField])>-1}
|
|
209
|
+
on:change={handleCheckboxChange(op[keyField])}/>
|
|
210
|
+
<span>{op[textField]}</span>
|
|
211
|
+
</div>
|
|
137
212
|
{/if}
|
|
138
213
|
{/each}
|
|
139
214
|
</div>
|
|
@@ -30,7 +30,9 @@ declare const OptionsMultiSelect: $$__sveltets_2_IsomorphicComponent<{
|
|
|
30
30
|
disableOptions?: Array<string>;
|
|
31
31
|
hideOptions?: Array<string>;
|
|
32
32
|
displayMode?: DisplayMode;
|
|
33
|
-
|
|
33
|
+
onchange?: OnChangeHandler<Array<string>>;
|
|
34
|
+
onfocus?: (() => void) | null;
|
|
35
|
+
onblur?: (() => void) | null;
|
|
34
36
|
tagColor?: string;
|
|
35
37
|
tagVariant?: "borderless" | "border" | "round";
|
|
36
38
|
setFocus?: () => void;
|