@ticatec/uniface-element 0.2.0 → 0.2.1
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/README.md +273 -1
- package/README_CN.md +341 -0
- package/dist/common/MouseClickHandler.d.ts +1 -1
- package/dist/dialog/CommonDialog.svelte +6 -3
- package/dist/dialog/CommonDialog.svelte.d.ts +2 -1
- package/dist/dialog/Dialog.svelte +36 -10
- package/dist/dialog/Dialog.svelte.d.ts +4 -3
- package/dist/dialog/DialogBoard.svelte +11 -8
- package/dist/dialog/DialogWrapper.svelte +10 -0
- package/dist/dialog/DialogWrapper.svelte.d.ts +22 -0
- package/dist/dialog/Dialogs.js +1 -1
- package/dist/dialog/ModalResult.d.ts +5 -0
- package/dist/dialog/ModalResult.js +5 -0
- package/docs/buttons/cn/ActionBar.md +459 -0
- package/docs/buttons/cn/Button.md +228 -0
- package/docs/buttons/cn/IconButton.md +294 -0
- package/docs/buttons/cn/README.md +600 -0
- package/docs/buttons/cn/TextButton.md +163 -0
- package/docs/buttons/en/ActionBar.md +459 -0
- package/docs/buttons/en/Button.md +228 -0
- package/docs/buttons/en/IconButton.md +294 -0
- package/docs/buttons/en/README.md +600 -0
- package/docs/buttons/en/TextButton.md +163 -0
- package/docs/containers/cn/accordion.md +622 -0
- package/docs/containers/cn/box.md +445 -0
- package/docs/containers/cn/card.md +404 -0
- package/docs/containers/cn/page.md +576 -0
- package/docs/containers/cn/tabs.md +686 -0
- package/docs/containers/en/accordion.md +622 -0
- package/docs/containers/en/box.md +445 -0
- package/docs/containers/en/card.md +404 -0
- package/docs/containers/en/page.md +576 -0
- package/docs/containers/en/tabs.md +686 -0
- package/docs/data-display/cn/concise-data-table.md +731 -0
- package/docs/data-display/cn/listbox.md +786 -0
- package/docs/data-display/cn/treeview.md +863 -0
- package/docs/data-display/en/concise-data-table.md +732 -0
- package/docs/data-display/en/listbox.md +786 -0
- package/docs/data-display/en/treeview.md +863 -0
- package/docs/data-table/cn/README.md +596 -0
- package/docs/data-table/en/README.md +596 -0
- package/docs/dialog/cn/README.md +767 -0
- package/docs/dialog/en/README.md +767 -0
- package/docs/form/cn/README.md +847 -0
- package/docs/form/en/README.md +847 -0
- package/docs/form-components/cn/README.md +495 -0
- package/docs/form-components/cn/attachmentfilesfield.md +757 -0
- package/docs/form-components/cn/cascade-options-select.md +374 -0
- package/docs/form-components/cn/checkbox.md +58 -0
- package/docs/form-components/cn/date-picker.md +138 -0
- package/docs/form-components/cn/datetime-picker.md +228 -0
- package/docs/form-components/cn/group-checkbox.md +405 -0
- package/docs/form-components/cn/group-radiobox.md +525 -0
- package/docs/form-components/cn/imagefilesfield.md +651 -0
- package/docs/form-components/cn/input-options-select.md +250 -0
- package/docs/form-components/cn/lookup-editor.md +406 -0
- package/docs/form-components/cn/memoeditor.md +409 -0
- package/docs/form-components/cn/number-editor.md +261 -0
- package/docs/form-components/cn/options-multi-select.md +177 -0
- package/docs/form-components/cn/options-select.md +83 -0
- package/docs/form-components/cn/passwordeditor.md +538 -0
- package/docs/form-components/cn/prompts-text-editor.md +241 -0
- package/docs/form-components/cn/radiobutton.md +84 -0
- package/docs/form-components/cn/switch.md +178 -0
- package/docs/form-components/cn/text-editor.md +142 -0
- package/docs/form-components/cn/time-editor.md +159 -0
- package/docs/form-components/cn/unit-number-editor.md +164 -0
- package/docs/form-components/en/README.md +463 -0
- package/docs/form-components/en/attachmentfilesfield.md +757 -0
- package/docs/form-components/en/cascade-options-select.md +358 -0
- package/docs/form-components/en/checkbox.md +59 -0
- package/docs/form-components/en/date-picker.md +138 -0
- package/docs/form-components/en/datetime-picker.md +228 -0
- package/docs/form-components/en/group-checkbox.md +395 -0
- package/docs/form-components/en/group-radiobox.md +498 -0
- package/docs/form-components/en/imagefilesfield.md +651 -0
- package/docs/form-components/en/input-options-select.md +239 -0
- package/docs/form-components/en/lookup-editor.md +389 -0
- package/docs/form-components/en/memoeditor.md +409 -0
- package/docs/form-components/en/number-editor.md +249 -0
- package/docs/form-components/en/options-multi-select.md +177 -0
- package/docs/form-components/en/options-select.md +83 -0
- package/docs/form-components/en/passwordeditor.md +538 -0
- package/docs/form-components/en/prompts-text-editor.md +241 -0
- package/docs/form-components/en/radiobutton.md +84 -0
- package/docs/form-components/en/switch.md +180 -0
- package/docs/form-components/en/text-editor.md +142 -0
- package/docs/form-components/en/time-editor.md +159 -0
- package/docs/form-components/en/unit-number-editor.md +164 -0
- package/docs/form-layout/cn/criteria-field.md +652 -0
- package/docs/form-layout/cn/flex-form.md +454 -0
- package/docs/form-layout/cn/flex-row-form.md +542 -0
- package/docs/form-layout/cn/form-field.md +923 -0
- package/docs/form-layout/cn/grid-form.md +676 -0
- package/docs/form-layout/en/criteria-field.md +652 -0
- package/docs/form-layout/en/flex-form.md +454 -0
- package/docs/form-layout/en/flex-row-form.md +542 -0
- package/docs/form-layout/en/form-field.md +923 -0
- package/docs/form-layout/en/grid-form.md +676 -0
- package/docs/global/cn/README.md +534 -0
- package/docs/global/en/README.md +534 -0
- package/docs/layout/cn/classiclayout.md +460 -0
- package/docs/layout/cn/headerlayout.md +524 -0
- package/docs/layout/cn/sidebarlayout.md +263 -0
- package/docs/layout/en/classiclayout.md +951 -0
- package/docs/layout/en/headerlayout.md +524 -0
- package/docs/layout/en/sidebarlayout.md +262 -0
- package/docs/misc/cn/README.md +1217 -0
- package/docs/misc/en/README.md +1217 -0
- package/docs/navigation/cn/breadcrumb.md +291 -0
- package/docs/navigation/cn/navigator.md +322 -0
- package/docs/navigation/cn/navigatormenu.md +512 -0
- package/docs/navigation/cn/progressbar.md +199 -0
- package/docs/navigation/cn/progressstepbar.md +299 -0
- package/docs/navigation/en/breadcrumb.md +291 -0
- package/docs/navigation/en/navigator.md +322 -0
- package/docs/navigation/en/navigatormenu.md +512 -0
- package/docs/navigation/en/progressbar.md +199 -0
- package/docs/navigation/en/progressstepbar.md +299 -0
- package/docs/search-filters/cn/daterange.md +1156 -0
- package/docs/search-filters/cn/numberrange.md +688 -0
- package/docs/search-filters/cn/searchbox.md +1582 -0
- package/docs/search-filters/en/daterange.md +1156 -0
- package/docs/search-filters/en/numberrange.md +688 -0
- package/docs/search-filters/en/searchbox.md +1582 -0
- package/package.json +38 -5
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
# CascadeOptionsSelect 组件
|
|
2
|
+
|
|
3
|
+
级联选择器组件,支持多级联动选择。
|
|
4
|
+
|
|
5
|
+
## 类型定义
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
type OnSelectOption = (item: any) => Promise<Array<any>>;
|
|
9
|
+
type IsLeafDetermine = (item: any) => boolean;
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## 属性
|
|
13
|
+
|
|
14
|
+
| 属性 | 类型 | 默认值 | 描述 |
|
|
15
|
+
|------|------|--------|------|
|
|
16
|
+
| `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | 外观变体 |
|
|
17
|
+
| `disabled` | `boolean` | `false` | 是否禁用 |
|
|
18
|
+
| `readonly` | `boolean` | `false` | 是否只读 |
|
|
19
|
+
| `compact` | `boolean` | `false` | 是否紧凑模式 |
|
|
20
|
+
| `mandatory` | `boolean` | `false` | 是否必填 |
|
|
21
|
+
| `value` | `any` | `null` | 选中的值 |
|
|
22
|
+
| `keyField` | `string` | `'code'` | 值字段名 |
|
|
23
|
+
| `textField` | `string` | `'text'` | 显示字段名 |
|
|
24
|
+
| `abbrField` | `string` | `'abbr'` | 缩写字段名 |
|
|
25
|
+
| `childrenField` | `string` | `'children'` | 子节点字段名 |
|
|
26
|
+
| `placeholder` | `string` | `''` | 占位符文本 |
|
|
27
|
+
| `displayMode` | `DisplayMode` | `DisplayMode.Edit` | 显示模式 |
|
|
28
|
+
| `emptyText` | `string \| null` | `null` | 空值显示文本 |
|
|
29
|
+
| `checkLeaf` | `IsLeafDetermine \| null` | `null` | 叶子节点判断函数 |
|
|
30
|
+
| `text` | `string` | `''` | 显示文本 |
|
|
31
|
+
| `nodes` | `Array<any>` | `[]` | 根节点数组 |
|
|
32
|
+
| `menu$height` | `number` | `150` | 下拉菜单高度 |
|
|
33
|
+
| `onSelect` | `OnSelectOption` | - | 选项选择回调(异步加载子节点) |
|
|
34
|
+
| `box$style` | `string` | `''` | 选项框样式 |
|
|
35
|
+
| `style` | `string` | `''` | 自定义样式 |
|
|
36
|
+
| `onChange` | `OnChangeHandler<any>` | - | 值变化回调 |
|
|
37
|
+
|
|
38
|
+
## 使用示例
|
|
39
|
+
|
|
40
|
+
```svelte
|
|
41
|
+
<script lang="ts">
|
|
42
|
+
import CascadeOptionsSelect, { type OnSelectOption, type IsLeafDetermine } from '@ticatec/uniface-element/CascadeOptionsSelect';
|
|
43
|
+
|
|
44
|
+
interface CascadeNode {
|
|
45
|
+
code: string;
|
|
46
|
+
text: string;
|
|
47
|
+
abbr: string;
|
|
48
|
+
children?: CascadeNode[];
|
|
49
|
+
level?: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let selectedRegion: string | null = null;
|
|
53
|
+
let selectedCategory: string | null = null;
|
|
54
|
+
|
|
55
|
+
// 地区级联数据
|
|
56
|
+
const regions: CascadeNode[] = [
|
|
57
|
+
{
|
|
58
|
+
code: 'beijing',
|
|
59
|
+
text: '北京市',
|
|
60
|
+
abbr: '京',
|
|
61
|
+
children: [
|
|
62
|
+
{ code: 'dongcheng', text: '东城区', abbr: '东城' },
|
|
63
|
+
{ code: 'xicheng', text: '西城区', abbr: '西城' },
|
|
64
|
+
{ code: 'chaoyang', text: '朝阳区', abbr: '朝阳' }
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
code: 'shanghai',
|
|
69
|
+
text: '上海市',
|
|
70
|
+
abbr: '沪',
|
|
71
|
+
children: [
|
|
72
|
+
{ code: 'huangpu', text: '黄浦区', abbr: '黄浦' },
|
|
73
|
+
{ code: 'xuhui', text: '徐汇区', abbr: '徐汇' },
|
|
74
|
+
{ code: 'changning', text: '长宁区', abbr: '长宁' }
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
// 商品分类数据(异步加载)
|
|
80
|
+
const categories: CascadeNode[] = [
|
|
81
|
+
{ code: 'electronics', text: '电子产品', abbr: '电子' },
|
|
82
|
+
{ code: 'clothing', text: '服装服饰', abbr: '服装' },
|
|
83
|
+
{ code: 'books', text: '图书文化', abbr: '图书' }
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
// 异步加载子分类
|
|
87
|
+
const loadSubCategories: OnSelectOption = async (item: CascadeNode): Promise<CascadeNode[]> => {
|
|
88
|
+
// 模拟API调用
|
|
89
|
+
await new Promise<void>(resolve => setTimeout(resolve, 500));
|
|
90
|
+
|
|
91
|
+
const subcategories: Record<string, CascadeNode[]> = {
|
|
92
|
+
'electronics': [
|
|
93
|
+
{ code: 'phone', text: '手机通讯', abbr: '手机', level: 2 },
|
|
94
|
+
{ code: 'computer', text: '电脑办公', abbr: '电脑', level: 2 },
|
|
95
|
+
{ code: 'camera', text: '摄影摄像', abbr: '摄像', level: 2 }
|
|
96
|
+
],
|
|
97
|
+
'clothing': [
|
|
98
|
+
{ code: 'men', text: '男装', abbr: '男装', level: 2 },
|
|
99
|
+
{ code: 'women', text: '女装', abbr: '女装', level: 2 },
|
|
100
|
+
{ code: 'shoes', text: '鞋靴', abbr: '鞋靴', level: 2 }
|
|
101
|
+
],
|
|
102
|
+
'books': [
|
|
103
|
+
{ code: 'literature', text: '文学小说', abbr: '文学', level: 2 },
|
|
104
|
+
{ code: 'technical', text: '科技图书', abbr: '科技', level: 2 },
|
|
105
|
+
{ code: 'education', text: '教育考试', abbr: '教育', level: 2 }
|
|
106
|
+
]
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return subcategories[item.code] || [];
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// 判断是否为叶子节点
|
|
113
|
+
const isLeafNode: IsLeafDetermine = (item: CascadeNode): boolean => {
|
|
114
|
+
// 如果是第三级节点,则为叶子节点
|
|
115
|
+
return item.level === 3;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const handleRegionChange = (value: string): void => {
|
|
119
|
+
selectedRegion = value;
|
|
120
|
+
console.log('选择的地区:', value);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const handleCategoryChange = (value: string): void => {
|
|
124
|
+
selectedCategory = value;
|
|
125
|
+
console.log('选择的分类:', value);
|
|
126
|
+
};
|
|
127
|
+
</script>
|
|
128
|
+
|
|
129
|
+
<!-- 地区级联选择 -->
|
|
130
|
+
<CascadeOptionsSelect
|
|
131
|
+
placeholder="请选择地区"
|
|
132
|
+
nodes={regions}
|
|
133
|
+
keyField="code"
|
|
134
|
+
textField="text"
|
|
135
|
+
abbrField="abbr"
|
|
136
|
+
bind:value={selectedRegion}
|
|
137
|
+
onChange={handleRegionChange}
|
|
138
|
+
/>
|
|
139
|
+
|
|
140
|
+
<!-- 商品分类级联选择(异步加载) -->
|
|
141
|
+
<CascadeOptionsSelect
|
|
142
|
+
placeholder="请选择商品分类"
|
|
143
|
+
nodes={categories}
|
|
144
|
+
keyField="code"
|
|
145
|
+
textField="text"
|
|
146
|
+
abbrField="abbr"
|
|
147
|
+
onSelect={loadSubCategories}
|
|
148
|
+
checkLeaf={isLeafNode}
|
|
149
|
+
menu$height={200}
|
|
150
|
+
bind:value={selectedCategory}
|
|
151
|
+
onChange={handleCategoryChange}
|
|
152
|
+
/>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## 高级用法
|
|
156
|
+
|
|
157
|
+
### 组织架构选择
|
|
158
|
+
```svelte
|
|
159
|
+
<script lang="ts">
|
|
160
|
+
interface OrgNode {
|
|
161
|
+
id: string;
|
|
162
|
+
name: string;
|
|
163
|
+
abbr: string;
|
|
164
|
+
type: 'company' | 'department' | 'team';
|
|
165
|
+
children?: OrgNode[];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let selectedOrg: string | null = null;
|
|
169
|
+
|
|
170
|
+
const orgStructure: OrgNode[] = [
|
|
171
|
+
{
|
|
172
|
+
id: 'company-1',
|
|
173
|
+
name: 'TechCorp 科技公司',
|
|
174
|
+
abbr: 'TechCorp',
|
|
175
|
+
type: 'company',
|
|
176
|
+
children: [
|
|
177
|
+
{
|
|
178
|
+
id: 'dept-1',
|
|
179
|
+
name: '研发部',
|
|
180
|
+
abbr: 'R&D',
|
|
181
|
+
type: 'department',
|
|
182
|
+
children: [
|
|
183
|
+
{ id: 'team-1', name: '前端团队', abbr: '前端', type: 'team' },
|
|
184
|
+
{ id: 'team-2', name: '后端团队', abbr: '后端', type: 'team' },
|
|
185
|
+
{ id: 'team-3', name: '测试团队', abbr: '测试', type: 'team' }
|
|
186
|
+
]
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
id: 'dept-2',
|
|
190
|
+
name: '产品部',
|
|
191
|
+
abbr: 'Product',
|
|
192
|
+
type: 'department',
|
|
193
|
+
children: [
|
|
194
|
+
{ id: 'team-4', name: '产品设计', abbr: '设计', type: 'team' },
|
|
195
|
+
{ id: 'team-5', name: '用户研究', abbr: '用研', type: 'team' }
|
|
196
|
+
]
|
|
197
|
+
}
|
|
198
|
+
]
|
|
199
|
+
}
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
// 动态加载子部门(模拟从API获取)
|
|
203
|
+
const loadSubOrgs: OnSelectOption = async (item: OrgNode): Promise<OrgNode[]> => {
|
|
204
|
+
console.log('加载子部门:', item);
|
|
205
|
+
|
|
206
|
+
// 如果已有子节点,直接返回
|
|
207
|
+
if (item.children) {
|
|
208
|
+
return item.children;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// 模拟API调用
|
|
212
|
+
await new Promise<void>(resolve => setTimeout(resolve, 800));
|
|
213
|
+
|
|
214
|
+
// 根据不同类型返回不同的子节点
|
|
215
|
+
if (item.type === 'company') {
|
|
216
|
+
return [
|
|
217
|
+
{ id: `dept-${Date.now()}`, name: '新部门', abbr: '新部门', type: 'department' as const }
|
|
218
|
+
];
|
|
219
|
+
} else if (item.type === 'department') {
|
|
220
|
+
return [
|
|
221
|
+
{ id: `team-${Date.now()}`, name: '新团队', abbr: '新团队', type: 'team' as const }
|
|
222
|
+
];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return [];
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// 判断是否为最终可选节点
|
|
229
|
+
const isSelectableNode: IsLeafDetermine = (item: OrgNode): boolean => {
|
|
230
|
+
return item.type === 'team'; // 只有团队级别才能被选择
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const handleOrgChange = (value: string): void => {
|
|
234
|
+
selectedOrg = value;
|
|
235
|
+
console.log('选择的组织:', value);
|
|
236
|
+
};
|
|
237
|
+
</script>
|
|
238
|
+
|
|
239
|
+
<CascadeOptionsSelect
|
|
240
|
+
placeholder="请选择组织架构"
|
|
241
|
+
nodes={orgStructure}
|
|
242
|
+
keyField="id"
|
|
243
|
+
textField="name"
|
|
244
|
+
abbrField="abbr"
|
|
245
|
+
onSelect={loadSubOrgs}
|
|
246
|
+
checkLeaf={isSelectableNode}
|
|
247
|
+
menu$height={250}
|
|
248
|
+
bind:value={selectedOrg}
|
|
249
|
+
onChange={handleOrgChange}
|
|
250
|
+
/>
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### 地理位置三级联动
|
|
254
|
+
```svelte
|
|
255
|
+
<script lang="ts">
|
|
256
|
+
interface LocationNode {
|
|
257
|
+
code: string;
|
|
258
|
+
name: string;
|
|
259
|
+
level: number;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
let selectedLocation: string | null = null;
|
|
263
|
+
|
|
264
|
+
const provinces: LocationNode[] = [
|
|
265
|
+
{ code: '110000', name: '北京市', level: 1 },
|
|
266
|
+
{ code: '310000', name: '上海市', level: 1 },
|
|
267
|
+
{ code: '440000', name: '广东省', level: 1 },
|
|
268
|
+
{ code: '320000', name: '江苏省', level: 1 }
|
|
269
|
+
];
|
|
270
|
+
|
|
271
|
+
// 模拟地理位置API
|
|
272
|
+
const loadLocationChildren: OnSelectOption = async (item: LocationNode): Promise<LocationNode[]> => {
|
|
273
|
+
const response: Response = await fetch(`/api/locations/${item.code}/children`);
|
|
274
|
+
const children: LocationNode[] = await response.json() as LocationNode[];
|
|
275
|
+
|
|
276
|
+
return children.map((child: LocationNode): LocationNode => ({
|
|
277
|
+
code: child.code,
|
|
278
|
+
name: child.name,
|
|
279
|
+
level: item.level + 1
|
|
280
|
+
}));
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
// 区县级别为最终选择
|
|
284
|
+
const isCountyLevel: IsLeafDetermine = (item: LocationNode): boolean => {
|
|
285
|
+
return item.level === 3;
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const handleLocationChange = (value: string): void => {
|
|
289
|
+
selectedLocation = value;
|
|
290
|
+
console.log('选择的地区代码:', value);
|
|
291
|
+
};
|
|
292
|
+
</script>
|
|
293
|
+
|
|
294
|
+
<CascadeOptionsSelect
|
|
295
|
+
placeholder="请选择省/市/区"
|
|
296
|
+
nodes={provinces}
|
|
297
|
+
keyField="code"
|
|
298
|
+
textField="name"
|
|
299
|
+
onSelect={loadLocationChildren}
|
|
300
|
+
checkLeaf={isCountyLevel}
|
|
301
|
+
bind:value={selectedLocation}
|
|
302
|
+
onChange={handleLocationChange}
|
|
303
|
+
/>
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## 最佳实践
|
|
307
|
+
|
|
308
|
+
### 1. 异步加载优化
|
|
309
|
+
```typescript
|
|
310
|
+
// 使用缓存避免重复请求
|
|
311
|
+
interface CacheItem {
|
|
312
|
+
code: string;
|
|
313
|
+
[key: string]: any;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const cache = new Map<string, CacheItem[]>();
|
|
317
|
+
|
|
318
|
+
const cachedLoader: OnSelectOption = async (item: CacheItem): Promise<CacheItem[]> => {
|
|
319
|
+
const cacheKey: string = item.code;
|
|
320
|
+
|
|
321
|
+
if (cache.has(cacheKey)) {
|
|
322
|
+
return cache.get(cacheKey)!;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const children: CacheItem[] = await fetchChildren(item.code);
|
|
326
|
+
cache.set(cacheKey, children);
|
|
327
|
+
|
|
328
|
+
return children;
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const fetchChildren = async (code: string): Promise<CacheItem[]> => {
|
|
332
|
+
// 实现异步获取子节点逻辑
|
|
333
|
+
const response = await fetch(`/api/children/${code}`);
|
|
334
|
+
return await response.json() as CacheItem[];
|
|
335
|
+
};
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### 2. 错误处理
|
|
339
|
+
```typescript
|
|
340
|
+
interface ApiItem {
|
|
341
|
+
code: string;
|
|
342
|
+
[key: string]: any;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const robustLoader: OnSelectOption = async (item: ApiItem): Promise<ApiItem[]> => {
|
|
346
|
+
try {
|
|
347
|
+
const response: Response = await fetch(`/api/cascade/${item.code}`);
|
|
348
|
+
if (!response.ok) {
|
|
349
|
+
throw new Error(`HTTP ${response.status}`);
|
|
350
|
+
}
|
|
351
|
+
return await response.json() as ApiItem[];
|
|
352
|
+
} catch (error: unknown) {
|
|
353
|
+
console.error('加载子节点失败:', error);
|
|
354
|
+
return []; // 返回空数组而不是抛出错误
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### 3. 显示加载状态
|
|
360
|
+
组件内部已经有加载状态指示,当异步加载子节点时会显示加载动画。
|
|
361
|
+
|
|
362
|
+
### 4. 合理设置菜单高度
|
|
363
|
+
```svelte
|
|
364
|
+
<!-- 根据层级数量调整高度 -->
|
|
365
|
+
<CascadeOptionsSelect
|
|
366
|
+
menu$height={200} // 2-3级
|
|
367
|
+
nodes={shallowData}
|
|
368
|
+
/>
|
|
369
|
+
|
|
370
|
+
<CascadeOptionsSelect
|
|
371
|
+
menu$height={300} // 4-5级
|
|
372
|
+
nodes={deepData}
|
|
373
|
+
/>
|
|
374
|
+
```
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# CheckBox 组件
|
|
2
|
+
|
|
3
|
+
复选框组件,用于布尔值的输入。
|
|
4
|
+
|
|
5
|
+
## 属性
|
|
6
|
+
|
|
7
|
+
| 属性 | 类型 | 默认值 | 描述 |
|
|
8
|
+
|------|------|--------|------|
|
|
9
|
+
| `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | 外观变体 |
|
|
10
|
+
| `disabled` | `boolean` | `false` | 是否禁用 |
|
|
11
|
+
| `readonly` | `boolean` | `false` | 是否只读 |
|
|
12
|
+
| `value` | `boolean` | `false` | 选中状态 |
|
|
13
|
+
| `label` | `string` | `''` | 标签文本 |
|
|
14
|
+
| `indeterminate` | `boolean` | `false` | 是否为不确定状态 |
|
|
15
|
+
| `displayMode` | `DisplayMode` | `DisplayMode.Edit` | 显示模式 |
|
|
16
|
+
| `style` | `string` | `''` | 自定义样式 |
|
|
17
|
+
| `onChange` | `OnChangeHandler<boolean>` | - | 值变化回调 |
|
|
18
|
+
|
|
19
|
+
## 使用示例
|
|
20
|
+
|
|
21
|
+
```svelte
|
|
22
|
+
<script lang="ts">
|
|
23
|
+
import CheckBox from '@ticatec/uniface-element/Checkbox';
|
|
24
|
+
|
|
25
|
+
let agreeTerms: boolean = false;
|
|
26
|
+
let enableNotifications: boolean = true;
|
|
27
|
+
let partialSelection: boolean = false;
|
|
28
|
+
|
|
29
|
+
const handleAgreeChange = (checked: boolean): void => {
|
|
30
|
+
agreeTerms = checked;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const handleNotificationChange = (checked: boolean): void => {
|
|
34
|
+
enableNotifications = checked;
|
|
35
|
+
};
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<!-- 同意条款 -->
|
|
39
|
+
<CheckBox
|
|
40
|
+
label="我同意服务条款和隐私政策"
|
|
41
|
+
bind:value={agreeTerms}
|
|
42
|
+
onChange={handleAgreeChange}
|
|
43
|
+
/>
|
|
44
|
+
|
|
45
|
+
<!-- 通知设置 -->
|
|
46
|
+
<CheckBox
|
|
47
|
+
label="启用邮件通知"
|
|
48
|
+
bind:value={enableNotifications}
|
|
49
|
+
onChange={handleNotificationChange}
|
|
50
|
+
/>
|
|
51
|
+
|
|
52
|
+
<!-- 不确定状态的复选框 -->
|
|
53
|
+
<CheckBox
|
|
54
|
+
label="部分选中状态"
|
|
55
|
+
indeterminate={true}
|
|
56
|
+
bind:value={partialSelection}
|
|
57
|
+
/>
|
|
58
|
+
```
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# DatePicker 组件
|
|
2
|
+
|
|
3
|
+
日期选择器组件。
|
|
4
|
+
|
|
5
|
+
## 属性
|
|
6
|
+
|
|
7
|
+
| 属性 | 类型 | 默认值 | 描述 |
|
|
8
|
+
|------|------|--------|------|
|
|
9
|
+
| `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | 外观变体 |
|
|
10
|
+
| `disabled` | `boolean` | `false` | 是否禁用 |
|
|
11
|
+
| `readonly` | `boolean` | `false` | 是否只读 |
|
|
12
|
+
| `compact` | `boolean` | `false` | 是否紧凑模式 |
|
|
13
|
+
| `mandatory` | `boolean` | `false` | 是否必填 |
|
|
14
|
+
| `value` | `any` | `null` | 日期值 |
|
|
15
|
+
| `style` | `string` | `''` | 自定义样式 |
|
|
16
|
+
| `placeholder` | `string` | `''` | 占位符文本 |
|
|
17
|
+
| `displayMode` | `DisplayMode` | `DisplayMode.Edit` | 显示模式 |
|
|
18
|
+
| `min` | `UniDate` | `null` | 最小日期 |
|
|
19
|
+
| `max` | `UniDate` | `null` | 最大日期 |
|
|
20
|
+
| `onChange` | `OnChangeHandler<Date>` | - | 值变化回调 |
|
|
21
|
+
|
|
22
|
+
## 使用示例
|
|
23
|
+
|
|
24
|
+
```svelte
|
|
25
|
+
<script lang="ts">
|
|
26
|
+
import DatePicker from '@ticatec/uniface-element/DatePicker';
|
|
27
|
+
|
|
28
|
+
let birthDate: any = null;
|
|
29
|
+
let startDate: any = new Date();
|
|
30
|
+
let endDate: any = null;
|
|
31
|
+
|
|
32
|
+
const handleBirthDateChange = (date: Date): void => {
|
|
33
|
+
birthDate = date;
|
|
34
|
+
console.log('出生日期:', date);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const handleStartDateChange = (date: Date): void => {
|
|
38
|
+
startDate = date;
|
|
39
|
+
console.log('开始日期:', date);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const handleEndDateChange = (date: Date): void => {
|
|
43
|
+
endDate = date;
|
|
44
|
+
console.log('结束日期:', date);
|
|
45
|
+
};
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<!-- 基本日期选择器 -->
|
|
49
|
+
<DatePicker
|
|
50
|
+
placeholder="选择日期"
|
|
51
|
+
bind:value={birthDate}
|
|
52
|
+
onChange={handleBirthDateChange}
|
|
53
|
+
/>
|
|
54
|
+
|
|
55
|
+
<!-- 带最小日期限制的开始日期 -->
|
|
56
|
+
<DatePicker
|
|
57
|
+
placeholder="选择开始日期"
|
|
58
|
+
min={new Date()}
|
|
59
|
+
bind:value={startDate}
|
|
60
|
+
onChange={handleStartDateChange}
|
|
61
|
+
/>
|
|
62
|
+
|
|
63
|
+
<!-- 带日期范围的结束日期 -->
|
|
64
|
+
<DatePicker
|
|
65
|
+
placeholder="选择结束日期"
|
|
66
|
+
min={startDate}
|
|
67
|
+
bind:value={endDate}
|
|
68
|
+
onChange={handleEndDateChange}
|
|
69
|
+
/>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## 高级用法
|
|
73
|
+
|
|
74
|
+
### 日期范围选择
|
|
75
|
+
```svelte
|
|
76
|
+
<script lang="ts">
|
|
77
|
+
let startDate: any = null;
|
|
78
|
+
let endDate: any = null;
|
|
79
|
+
|
|
80
|
+
const handleStartChange = (date: Date) => {
|
|
81
|
+
startDate = date;
|
|
82
|
+
// 如果结束日期早于开始日期,重置结束日期
|
|
83
|
+
if (endDate && new Date(endDate) < new Date(date)) {
|
|
84
|
+
endDate = null;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const handleEndChange = (date: Date) => {
|
|
89
|
+
endDate = date;
|
|
90
|
+
};
|
|
91
|
+
</script>
|
|
92
|
+
|
|
93
|
+
<div class="date-range">
|
|
94
|
+
<DatePicker
|
|
95
|
+
placeholder="开始日期"
|
|
96
|
+
bind:value={startDate}
|
|
97
|
+
onChange={handleStartChange}
|
|
98
|
+
/>
|
|
99
|
+
|
|
100
|
+
<DatePicker
|
|
101
|
+
placeholder="结束日期"
|
|
102
|
+
min={startDate}
|
|
103
|
+
bind:value={endDate}
|
|
104
|
+
onChange={handleEndChange}
|
|
105
|
+
/>
|
|
106
|
+
</div>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## 最佳实践
|
|
110
|
+
|
|
111
|
+
### 1. 合理设置日期范围
|
|
112
|
+
```svelte
|
|
113
|
+
<script lang="ts">
|
|
114
|
+
const today = new Date();
|
|
115
|
+
const futureDate = new Date();
|
|
116
|
+
futureDate.setDate(futureDate.getDate() + 1);
|
|
117
|
+
</script>
|
|
118
|
+
|
|
119
|
+
<!-- 只允许未来日期 -->
|
|
120
|
+
<DatePicker
|
|
121
|
+
placeholder="预订日期"
|
|
122
|
+
min={futureDate}
|
|
123
|
+
/>
|
|
124
|
+
|
|
125
|
+
<!-- 允许过去日期 -->
|
|
126
|
+
<DatePicker
|
|
127
|
+
placeholder="出生日期"
|
|
128
|
+
max={today}
|
|
129
|
+
/>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 2. 使用 mandatory 标记必填字段
|
|
133
|
+
```svelte
|
|
134
|
+
<DatePicker
|
|
135
|
+
mandatory={true}
|
|
136
|
+
placeholder="必填日期字段"
|
|
137
|
+
/>
|
|
138
|
+
```
|