@ticatec/uniface-element 0.2.0 → 0.2.2

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.
Files changed (209) hide show
  1. package/README.md +320 -1
  2. package/README_CN.md +392 -0
  3. package/dist/accordion/Accordion.svelte +1 -1
  4. package/dist/action-bar/ActionBar.svelte +7 -1
  5. package/dist/attachment-files/FileRender.svelte +1 -1
  6. package/dist/attachment-files/FileUploadBar.svelte +7 -6
  7. package/dist/attachment-files/FileUploadPanel.svelte +2 -2
  8. package/dist/base-calendar/DateContext.js +5 -4
  9. package/dist/base-calendar/YearPickupPanel.svelte +4 -4
  10. package/dist/card/Card.svelte +1 -1
  11. package/dist/card/Card.svelte.d.ts +1 -1
  12. package/dist/cascade-options-select/CascadeOptionSelect.svelte +2 -3
  13. package/dist/cascade-options-select/CascadeOptionSelect.svelte.d.ts +46 -1
  14. package/dist/cascade-options-select/CascadePanel.svelte +1 -1
  15. package/dist/checkbox/CheckBox.svelte +1 -1
  16. package/dist/color-picker/ColorPicker.svelte +9 -9
  17. package/dist/color-picker/ColorPicker.svelte.d.ts +0 -2
  18. package/dist/color-picker/ColorPickerPanel.svelte +11 -15
  19. package/dist/color-picker/ColorPickerPanel.svelte.d.ts +1 -1
  20. package/dist/common/CommonPicker.svelte +2 -2
  21. package/dist/common/CommonPicker.svelte.d.ts +1 -1
  22. package/dist/common/MouseClickHandler.d.ts +1 -1
  23. package/dist/common/Popover.svelte +28 -6
  24. package/dist/common/Popover.svelte.d.ts +9 -3
  25. package/dist/common/uniface-utils.js +6 -4
  26. package/dist/common-editor/CommonEditor.svelte +1 -1
  27. package/dist/common-editor/CommonEditor.svelte.d.ts +1 -1
  28. package/dist/common-editor/NumberInput.svelte +1 -1
  29. package/dist/common-editor/NumberInput.svelte.d.ts +1 -1
  30. package/dist/concise-data-table/ConciseListTable.svelte +7 -2
  31. package/dist/concise-data-table/ConciseListTable.svelte.d.ts +1 -0
  32. package/dist/concise-data-table/DataRow.svelte +1 -1
  33. package/dist/concise-data-table/TableOptions.d.ts +1 -1
  34. package/dist/data-table/DataTable.svelte +1 -1
  35. package/dist/data-table/parts/ActionsPanel.svelte +4 -3
  36. package/dist/data-table/parts/ActionsRow.svelte +3 -3
  37. package/dist/data-table/parts/ContentPanel.svelte +3 -2
  38. package/dist/data-table/parts/FixedColumnsPanel.svelte +0 -1
  39. package/dist/data-table/parts/FixedColumnsPanel.svelte.d.ts +1 -2
  40. package/dist/data-table/parts/FixedHeaderPanel.svelte +2 -2
  41. package/dist/data-table/parts/FixedRow.svelte +2 -1
  42. package/dist/data-table/parts/FixedRow.svelte.d.ts +1 -1
  43. package/dist/data-table/parts/HrefCell.svelte +23 -2
  44. package/dist/data-table/parts/PopupMenu.svelte +25 -2
  45. package/dist/data-table/parts/PopupMenu.svelte.d.ts +8 -1
  46. package/dist/date-picker/DatePicker.svelte +0 -1
  47. package/dist/date-picker/DateTimePicker.svelte +3 -3
  48. package/dist/date-picker/ScrollBar.svelte +4 -7
  49. package/dist/date-range/DateRangeEditor.svelte +1 -1
  50. package/dist/dialog/CommonDialog.svelte +8 -5
  51. package/dist/dialog/CommonDialog.svelte.d.ts +2 -1
  52. package/dist/dialog/Dialog.svelte +37 -12
  53. package/dist/dialog/Dialog.svelte.d.ts +4 -3
  54. package/dist/dialog/DialogBoard.svelte +11 -8
  55. package/dist/dialog/DialogWrapper.svelte +10 -0
  56. package/dist/{common-editor/DateInput.svelte.d.ts → dialog/DialogWrapper.svelte.d.ts} +6 -13
  57. package/dist/dialog/Dialogs.js +1 -1
  58. package/dist/dialog/ModalResult.d.ts +5 -0
  59. package/dist/dialog/ModalResult.js +5 -0
  60. package/dist/drawer/Drawer.svelte +1 -1
  61. package/dist/form-field/FormField.svelte +2 -1
  62. package/dist/form-field/FormField.svelte.d.ts +1 -0
  63. package/dist/form-panel/flex-row-form/FormContainer.svelte +1 -1
  64. package/dist/form-panel/grid-form/EmptyCell.svelte +8 -7
  65. package/dist/form-panel/grid-form/EmptyCell.svelte.d.ts +2 -2
  66. package/dist/form-panel/grid-form/GridField.svelte +1 -1
  67. package/dist/i18nRes/i18nRes.d.ts +2 -0
  68. package/dist/i18nRes/i18nRes.js +44 -0
  69. package/dist/i18nRes/index.d.ts +2 -0
  70. package/dist/i18nRes/index.js +2 -0
  71. package/dist/image-files/ImageFilesField.svelte +1 -1
  72. package/dist/indicator/IndicatorBoard.svelte +1 -1
  73. package/dist/inline-cell-editor/InvalidEditor.svelte +1 -0
  74. package/dist/input-options-select/InputOptionsSelect.svelte +6 -6
  75. package/dist/input-options-select/InputOptionsSelect.svelte.d.ts +1 -1
  76. package/dist/layout/flex/FlexBlock.svelte +1 -1
  77. package/dist/lookup-editor/LookupEditor.svelte +1 -1
  78. package/dist/memo-editor/MemoEditor.svelte +2 -2
  79. package/dist/message-box/IMessageBox.d.ts +2 -4
  80. package/dist/message-box/IMessageBox.js +4 -6
  81. package/dist/message-box/MessageBoxBoard.svelte +1 -3
  82. package/dist/options-multi-select/OptionsMultiSelect.svelte +1 -1
  83. package/dist/prompts-text-editor/PromptsTextEditor.svelte +2 -2
  84. package/dist/prompts-text-editor/PromptsTextEditor.svelte.d.ts +1 -1
  85. package/dist/property-editor/PropertyEditor.svelte +3 -3
  86. package/dist/text-editor/PasswordEditor.svelte +1 -1
  87. package/dist/transfer/Transfer.svelte +17 -12
  88. package/dist/transfer/Transfer.svelte.d.ts +0 -1
  89. package/dist/unit-number-editor/UnitNumberEditor.svelte +2 -2
  90. package/docs/buttons/cn/ActionBar.md +459 -0
  91. package/docs/buttons/cn/Button.md +228 -0
  92. package/docs/buttons/cn/IconButton.md +294 -0
  93. package/docs/buttons/cn/README.md +600 -0
  94. package/docs/buttons/cn/TextButton.md +163 -0
  95. package/docs/buttons/en/ActionBar.md +459 -0
  96. package/docs/buttons/en/Button.md +228 -0
  97. package/docs/buttons/en/IconButton.md +294 -0
  98. package/docs/buttons/en/README.md +600 -0
  99. package/docs/buttons/en/TextButton.md +163 -0
  100. package/docs/containers/cn/accordion.md +622 -0
  101. package/docs/containers/cn/box.md +445 -0
  102. package/docs/containers/cn/card.md +404 -0
  103. package/docs/containers/cn/page.md +576 -0
  104. package/docs/containers/cn/tabs.md +686 -0
  105. package/docs/containers/en/accordion.md +622 -0
  106. package/docs/containers/en/box.md +445 -0
  107. package/docs/containers/en/card.md +404 -0
  108. package/docs/containers/en/page.md +576 -0
  109. package/docs/containers/en/tabs.md +686 -0
  110. package/docs/data-display/cn/concise-data-table.md +731 -0
  111. package/docs/data-display/cn/listbox.md +786 -0
  112. package/docs/data-display/cn/treeview.md +863 -0
  113. package/docs/data-display/en/concise-data-table.md +732 -0
  114. package/docs/data-display/en/listbox.md +786 -0
  115. package/docs/data-display/en/treeview.md +863 -0
  116. package/docs/data-table/cn/README.md +596 -0
  117. package/docs/data-table/en/README.md +596 -0
  118. package/docs/dialog/cn/README.md +767 -0
  119. package/docs/dialog/en/README.md +767 -0
  120. package/docs/form/cn/README.md +847 -0
  121. package/docs/form/en/README.md +847 -0
  122. package/docs/form-components/cn/README.md +495 -0
  123. package/docs/form-components/cn/attachmentfilesfield.md +757 -0
  124. package/docs/form-components/cn/cascade-options-select.md +374 -0
  125. package/docs/form-components/cn/checkbox.md +58 -0
  126. package/docs/form-components/cn/date-picker.md +138 -0
  127. package/docs/form-components/cn/datetime-picker.md +228 -0
  128. package/docs/form-components/cn/group-checkbox.md +405 -0
  129. package/docs/form-components/cn/group-radiobox.md +525 -0
  130. package/docs/form-components/cn/imagefilesfield.md +651 -0
  131. package/docs/form-components/cn/input-options-select.md +250 -0
  132. package/docs/form-components/cn/lookup-editor.md +406 -0
  133. package/docs/form-components/cn/memoeditor.md +409 -0
  134. package/docs/form-components/cn/number-editor.md +261 -0
  135. package/docs/form-components/cn/options-multi-select.md +177 -0
  136. package/docs/form-components/cn/options-select.md +83 -0
  137. package/docs/form-components/cn/passwordeditor.md +538 -0
  138. package/docs/form-components/cn/prompts-text-editor.md +241 -0
  139. package/docs/form-components/cn/radiobutton.md +84 -0
  140. package/docs/form-components/cn/switch.md +178 -0
  141. package/docs/form-components/cn/text-editor.md +142 -0
  142. package/docs/form-components/cn/time-editor.md +159 -0
  143. package/docs/form-components/cn/unit-number-editor.md +164 -0
  144. package/docs/form-components/en/README.md +463 -0
  145. package/docs/form-components/en/attachmentfilesfield.md +757 -0
  146. package/docs/form-components/en/cascade-options-select.md +358 -0
  147. package/docs/form-components/en/checkbox.md +59 -0
  148. package/docs/form-components/en/date-picker.md +138 -0
  149. package/docs/form-components/en/datetime-picker.md +228 -0
  150. package/docs/form-components/en/group-checkbox.md +395 -0
  151. package/docs/form-components/en/group-radiobox.md +498 -0
  152. package/docs/form-components/en/imagefilesfield.md +651 -0
  153. package/docs/form-components/en/input-options-select.md +239 -0
  154. package/docs/form-components/en/lookup-editor.md +389 -0
  155. package/docs/form-components/en/memoeditor.md +409 -0
  156. package/docs/form-components/en/number-editor.md +249 -0
  157. package/docs/form-components/en/options-multi-select.md +177 -0
  158. package/docs/form-components/en/options-select.md +83 -0
  159. package/docs/form-components/en/passwordeditor.md +538 -0
  160. package/docs/form-components/en/prompts-text-editor.md +241 -0
  161. package/docs/form-components/en/radiobutton.md +84 -0
  162. package/docs/form-components/en/switch.md +180 -0
  163. package/docs/form-components/en/text-editor.md +142 -0
  164. package/docs/form-components/en/time-editor.md +159 -0
  165. package/docs/form-components/en/unit-number-editor.md +164 -0
  166. package/docs/form-layout/cn/criteria-field.md +652 -0
  167. package/docs/form-layout/cn/flex-form.md +454 -0
  168. package/docs/form-layout/cn/flex-row-form.md +542 -0
  169. package/docs/form-layout/cn/form-field.md +923 -0
  170. package/docs/form-layout/cn/grid-form.md +676 -0
  171. package/docs/form-layout/en/criteria-field.md +652 -0
  172. package/docs/form-layout/en/flex-form.md +454 -0
  173. package/docs/form-layout/en/flex-row-form.md +542 -0
  174. package/docs/form-layout/en/form-field.md +923 -0
  175. package/docs/form-layout/en/grid-form.md +676 -0
  176. package/docs/global/cn/README.md +534 -0
  177. package/docs/global/en/README.md +534 -0
  178. package/docs/layout/cn/classiclayout.md +460 -0
  179. package/docs/layout/cn/headerlayout.md +524 -0
  180. package/docs/layout/cn/sidebarlayout.md +263 -0
  181. package/docs/layout/en/classiclayout.md +951 -0
  182. package/docs/layout/en/headerlayout.md +524 -0
  183. package/docs/layout/en/sidebarlayout.md +262 -0
  184. package/docs/misc/cn/README.md +1217 -0
  185. package/docs/misc/en/README.md +1217 -0
  186. package/docs/navigation/cn/breadcrumb.md +291 -0
  187. package/docs/navigation/cn/navigator.md +322 -0
  188. package/docs/navigation/cn/navigatormenu.md +512 -0
  189. package/docs/navigation/cn/progressbar.md +199 -0
  190. package/docs/navigation/cn/progressstepbar.md +299 -0
  191. package/docs/navigation/en/breadcrumb.md +291 -0
  192. package/docs/navigation/en/navigator.md +322 -0
  193. package/docs/navigation/en/navigatormenu.md +512 -0
  194. package/docs/navigation/en/progressbar.md +199 -0
  195. package/docs/navigation/en/progressstepbar.md +299 -0
  196. package/docs/search-filters/cn/daterange.md +1156 -0
  197. package/docs/search-filters/cn/numberrange.md +688 -0
  198. package/docs/search-filters/cn/searchbox.md +1582 -0
  199. package/docs/search-filters/en/daterange.md +1156 -0
  200. package/docs/search-filters/en/numberrange.md +688 -0
  201. package/docs/search-filters/en/searchbox.md +1582 -0
  202. package/package.json +53 -13
  203. package/dist/common-editor/DateInput.svelte +0 -27
  204. package/dist/i18n_resources/index.d.ts +0 -3
  205. package/dist/i18n_resources/index.js +0 -3
  206. /package/dist/{i18n_resources → i18nRes}/uniface_cn_resource.d.ts +0 -0
  207. /package/dist/{i18n_resources → i18nRes}/uniface_cn_resource.js +0 -0
  208. /package/dist/{i18n_resources → i18nRes}/uniface_en_resource.d.ts +0 -0
  209. /package/dist/{i18n_resources → i18nRes}/uniface_en_resource.js +0 -0
@@ -0,0 +1,786 @@
1
+ # ListBox 列表框组件
2
+
3
+ 一个多功能的列表显示组件,提供项目选择、过滤、懒加载和多种选择模式,用于显示数据集合。
4
+
5
+ ## 概述
6
+
7
+ ListBox 组件为显示项目列表提供了全面的解决方案,具有丰富的交互功能。它支持单选和多选模式、实时过滤、大数据集的懒加载以及自定义项目渲染。非常适合用户选择界面、数据浏览器和内容管理系统。
8
+
9
+ ## 安装
10
+
11
+ ```typescript
12
+ import ListBox from '@ticatec/uniface-element/ListBox';
13
+ import type { LazyLoader, FunFilter, LoadResult } from '@ticatec/uniface-element/ListBox';
14
+ ```
15
+
16
+ ## 基本用法
17
+
18
+ ```svelte
19
+ <script lang="ts">
20
+ import ListBox from '@ticatec/uniface-element/ListBox';
21
+
22
+ // 简单的项目渲染器组件
23
+ const ItemRenderer = ({ item }) => `
24
+ <div class="list-item">
25
+ <h4>${item.title}</h4>
26
+ <p>${item.description}</p>
27
+ </div>
28
+ `;
29
+
30
+ let items = [
31
+ { id: 1, title: '项目 1', description: '第一个项目描述' },
32
+ { id: 2, title: '项目 2', description: '第二个项目描述' },
33
+ { id: 3, title: '项目 3', description: '第三个项目描述' }
34
+ ];
35
+
36
+ let selectedItem = null;
37
+ </script>
38
+
39
+ <ListBox
40
+ list={items}
41
+ itemRender={ItemRenderer}
42
+ selectMode="single"
43
+ bind:selectedItem
44
+ />
45
+ ```
46
+
47
+ ## 属性
48
+
49
+ | 属性 | 类型 | 默认值 | 描述 |
50
+ |------|------|--------|------|
51
+ | `readonly` | `boolean` | `false` | 设置列表为只读模式 |
52
+ | `style` | `string \| null` | `null` | 容器的自定义 CSS 样式 |
53
+ | `header$style` | `string \| null` | `null` | 头部区域的自定义样式 |
54
+ | `footer$style` | `string \| null` | `null` | 底部区域的自定义样式 |
55
+ | `list` | `Array<any>` | - | 要显示的项目数组 |
56
+ | `itemRender` | `any` | - | 用于渲染每个项目的组件 |
57
+ | `selectMode` | `'none' \| 'single' \| 'multiple'` | `'none'` | 选择行为模式 |
58
+ | `filter` | `FunFilter \| null` | `null` | 项目过滤函数 |
59
+ | `lazyLoader` | `LazyLoader \| null` | `null` | 懒加载配置 |
60
+ | `selectedItem` | `any` | `null` | 当前选中的项目(单选模式) |
61
+ | `selectedList` | `Array<any>` | `[]` | 选中的项目(多选模式) |
62
+ | `class` | `string` | `''` | 额外的 CSS 类名 |
63
+ | `item$props` | `any` | `null` | 传递给项目渲染器的额外属性 |
64
+ | `onSelectChange` | `OnSelectChange` | `null` | 选择变更回调 |
65
+ | `onItemDblClick` | `OnItemDblClick` | `null` | 项目双击回调 |
66
+ | `onItemClick` | `OnItemClick` | `null` | 项目点击回调 |
67
+ | `title` | `string` | `null` | 头部标题 |
68
+ | `round` | `boolean` | `false` | 应用圆角 |
69
+
70
+ ## 插槽
71
+
72
+ | 插槽 | 描述 |
73
+ |------|------|
74
+ | `header` | 自定义头部内容 |
75
+ | `footer` | 自定义底部内容 |
76
+ | `loadMoreIndicator` | 懒加载的自定义加载指示器 |
77
+
78
+ ## 类型定义
79
+
80
+ ```typescript
81
+ /**
82
+ * 项目过滤函数
83
+ */
84
+ export type FunFilter = (item: any, text: string) => boolean;
85
+
86
+ /**
87
+ * 懒加载结果结构
88
+ */
89
+ export interface LoadResult {
90
+ hasMore: boolean;
91
+ list: Array<any>;
92
+ }
93
+
94
+ /**
95
+ * 懒加载函数
96
+ */
97
+ export type LazyLoader = (text: string, pageNo: number) => Promise<LoadResult>;
98
+
99
+ /**
100
+ * 事件处理器类型
101
+ */
102
+ type OnSelectChange = (item: any) => void;
103
+ type OnItemDblClick = (item: any) => void;
104
+ type OnItemClick = (item: any) => void;
105
+ ```
106
+
107
+ ## 使用示例
108
+
109
+ ### 单选列表
110
+
111
+ ```svelte
112
+ <script lang="ts">
113
+ import ListBox from '@ticatec/uniface-element/ListBox';
114
+
115
+ // 自定义项目渲染器
116
+ const UserRenderer = ({ item, selected }) => `
117
+ <div class="user-item ${selected ? 'selected' : ''}">
118
+ <div class="user-avatar">
119
+ <img src="${item.avatar}" alt="${item.name}" width="32" height="32" />
120
+ </div>
121
+ <div class="user-info">
122
+ <div class="user-name">${item.name}</div>
123
+ <div class="user-role">${item.role}</div>
124
+ </div>
125
+ </div>
126
+ `;
127
+
128
+ let users = [
129
+ { id: 1, name: '张小明', role: '开发工程师', avatar: '/avatars/zhang.jpg' },
130
+ { id: 2, name: '李小红', role: '设计师', avatar: '/avatars/li.jpg' },
131
+ { id: 3, name: '王小刚', role: '项目经理', avatar: '/avatars/wang.jpg' }
132
+ ];
133
+
134
+ let selectedUser = null;
135
+
136
+ function handleUserSelect(user: any) {
137
+ console.log('选中用户:', user);
138
+ }
139
+ </script>
140
+
141
+ <ListBox
142
+ title="选择用户"
143
+ list={users}
144
+ itemRender={UserRenderer}
145
+ selectMode="single"
146
+ bind:selectedItem={selectedUser}
147
+ onSelectChange={handleUserSelect}
148
+ style="width: 300px; height: 400px; border: 1px solid #ddd;"
149
+ />
150
+
151
+ <style>
152
+ :global(.user-item) {
153
+ display: flex;
154
+ align-items: center;
155
+ gap: 12px;
156
+ padding: 12px;
157
+ border-bottom: 1px solid #f0f0f0;
158
+ cursor: pointer;
159
+ transition: background-color 0.2s;
160
+ }
161
+
162
+ :global(.user-item:hover) {
163
+ background-color: #f8f9fa;
164
+ }
165
+
166
+ :global(.user-item.selected) {
167
+ background-color: #e3f2fd;
168
+ }
169
+
170
+ :global(.user-avatar img) {
171
+ border-radius: 50%;
172
+ }
173
+
174
+ :global(.user-name) {
175
+ font-weight: 500;
176
+ margin-bottom: 2px;
177
+ }
178
+
179
+ :global(.user-role) {
180
+ font-size: 0.875rem;
181
+ color: #666;
182
+ }
183
+ </style>
184
+ ```
185
+
186
+ ### 多选列表
187
+
188
+ ```svelte
189
+ <script lang="ts">
190
+ import ListBox from '@ticatec/uniface-element/ListBox';
191
+
192
+ const TaskRenderer = ({ item }) => `
193
+ <div class="task-item">
194
+ <div class="task-title">${item.title}</div>
195
+ <div class="task-meta">
196
+ <span class="task-priority priority-${item.priority.toLowerCase()}">${item.priority === 'High' ? '高' : item.priority === 'Medium' ? '中' : '低'}</span>
197
+ <span class="task-due">截止: ${new Date(item.dueDate).toLocaleDateString()}</span>
198
+ </div>
199
+ </div>
200
+ `;
201
+
202
+ let tasks = [
203
+ { id: 1, title: '审查拉取请求', priority: 'High', dueDate: '2024-03-20' },
204
+ { id: 2, title: '更新文档', priority: 'Medium', dueDate: '2024-03-22' },
205
+ { id: 3, title: '修复小错误', priority: 'Low', dueDate: '2024-03-25' },
206
+ { id: 4, title: '规划下个冲刺', priority: 'High', dueDate: '2024-03-18' }
207
+ ];
208
+
209
+ let selectedTasks = [];
210
+
211
+ $: console.log('选中的任务:', selectedTasks);
212
+ </script>
213
+
214
+ <ListBox
215
+ title="选择任务"
216
+ list={tasks}
217
+ itemRender={TaskRenderer}
218
+ selectMode="multiple"
219
+ bind:selectedList={selectedTasks}
220
+ style="width: 400px; height: 500px;"
221
+ round={true}
222
+ />
223
+
224
+ <div class="selection-summary">
225
+ <p>已选择: {selectedTasks.length} 个任务</p>
226
+ {#if selectedTasks.length > 0}
227
+ <ul>
228
+ {#each selectedTasks as task}
229
+ <li>{task.title}</li>
230
+ {/each}
231
+ </ul>
232
+ {/if}
233
+ </div>
234
+
235
+ <style>
236
+ :global(.task-item) {
237
+ padding: 12px;
238
+ border-bottom: 1px solid #eee;
239
+ }
240
+
241
+ :global(.task-title) {
242
+ font-weight: 500;
243
+ margin-bottom: 6px;
244
+ }
245
+
246
+ :global(.task-meta) {
247
+ display: flex;
248
+ justify-content: space-between;
249
+ align-items: center;
250
+ font-size: 0.875rem;
251
+ }
252
+
253
+ :global(.task-priority) {
254
+ padding: 2px 8px;
255
+ border-radius: 12px;
256
+ font-size: 0.75rem;
257
+ font-weight: 500;
258
+ }
259
+
260
+ :global(.priority-high) {
261
+ background: #ffebee;
262
+ color: #c62828;
263
+ }
264
+
265
+ :global(.priority-medium) {
266
+ background: #fff3e0;
267
+ color: #ef6c00;
268
+ }
269
+
270
+ :global(.priority-low) {
271
+ background: #e8f5e8;
272
+ color: #2e7d32;
273
+ }
274
+
275
+ :global(.task-due) {
276
+ color: #666;
277
+ }
278
+
279
+ .selection-summary {
280
+ margin-top: 16px;
281
+ padding: 12px;
282
+ background: #f8f9fa;
283
+ border-radius: 4px;
284
+ }
285
+ </style>
286
+ ```
287
+
288
+ ### 带过滤的列表
289
+
290
+ ```svelte
291
+ <script lang="ts">
292
+ import ListBox from '@ticatec/uniface-element/ListBox';
293
+ import type { FunFilter } from '@ticatec/uniface-element/ListBox';
294
+
295
+ const ProductRenderer = ({ item }) => `
296
+ <div class="product-item">
297
+ <img src="${item.image}" alt="${item.name}" class="product-image" />
298
+ <div class="product-info">
299
+ <div class="product-name">${item.name}</div>
300
+ <div class="product-category">${item.category}</div>
301
+ <div class="product-price">¥${item.price.toFixed(2)}</div>
302
+ </div>
303
+ </div>
304
+ `;
305
+
306
+ let products = [
307
+ { id: 1, name: '笔记本电脑', category: '电子产品', price: 8999.99, image: '/images/laptop.jpg' },
308
+ { id: 2, name: '咖啡机', category: '家用电器', price: 599.99, image: '/images/coffee.jpg' },
309
+ { id: 3, name: '智能手机', category: '电子产品', price: 4699.99, image: '/images/phone.jpg' },
310
+ { id: 4, name: '办公椅', category: '家具', price: 1699.99, image: '/images/chair.jpg' },
311
+ { id: 5, name: '平板电脑', category: '电子产品', price: 2199.99, image: '/images/tablet.jpg' }
312
+ ];
313
+
314
+ const productFilter: FunFilter = (item, text) => {
315
+ if (!text) return true;
316
+ const searchText = text.toLowerCase();
317
+ return (
318
+ item.name.toLowerCase().includes(searchText) ||
319
+ item.category.toLowerCase().includes(searchText)
320
+ );
321
+ };
322
+
323
+ let selectedProduct = null;
324
+ </script>
325
+
326
+ <ListBox
327
+ title="产品目录"
328
+ list={products}
329
+ itemRender={ProductRenderer}
330
+ selectMode="single"
331
+ filter={productFilter}
332
+ bind:selectedItem={selectedProduct}
333
+ style="width: 350px; height: 600px;"
334
+ header$style="padding: 16px;"
335
+ />
336
+
337
+ {#if selectedProduct}
338
+ <div class="product-details">
339
+ <h3>选中的产品</h3>
340
+ <p><strong>名称:</strong> {selectedProduct.name}</p>
341
+ <p><strong>分类:</strong> {selectedProduct.category}</p>
342
+ <p><strong>价格:</strong> ¥{selectedProduct.price.toFixed(2)}</p>
343
+ </div>
344
+ {/if}
345
+
346
+ <style>
347
+ :global(.product-item) {
348
+ display: flex;
349
+ align-items: center;
350
+ gap: 12px;
351
+ padding: 12px;
352
+ border-bottom: 1px solid #f0f0f0;
353
+ cursor: pointer;
354
+ }
355
+
356
+ :global(.product-item:hover) {
357
+ background-color: #f8f9fa;
358
+ }
359
+
360
+ :global(.product-image) {
361
+ width: 48px;
362
+ height: 48px;
363
+ object-fit: cover;
364
+ border-radius: 4px;
365
+ }
366
+
367
+ :global(.product-name) {
368
+ font-weight: 500;
369
+ margin-bottom: 2px;
370
+ }
371
+
372
+ :global(.product-category) {
373
+ font-size: 0.875rem;
374
+ color: #666;
375
+ margin-bottom: 2px;
376
+ }
377
+
378
+ :global(.product-price) {
379
+ font-weight: 600;
380
+ color: #007bff;
381
+ }
382
+
383
+ .product-details {
384
+ margin-top: 20px;
385
+ padding: 16px;
386
+ background: #f8f9fa;
387
+ border-radius: 4px;
388
+ width: 350px;
389
+ }
390
+ </style>
391
+ ```
392
+
393
+ ### 懒加载列表
394
+
395
+ ```svelte
396
+ <script lang="ts">
397
+ import ListBox from '@ticatec/uniface-element/ListBox';
398
+ import type { LazyLoader, LoadResult } from '@ticatec/uniface-element/ListBox';
399
+
400
+ const MessageRenderer = ({ item }) => `
401
+ <div class="message-item">
402
+ <div class="message-header">
403
+ <span class="sender">${item.sender}</span>
404
+ <span class="timestamp">${new Date(item.timestamp).toLocaleTimeString()}</span>
405
+ </div>
406
+ <div class="message-subject">${item.subject}</div>
407
+ <div class="message-preview">${item.preview}</div>
408
+ </div>
409
+ `;
410
+
411
+ // 模拟 API 调用进行懒加载
412
+ const messageLoader: LazyLoader = async (searchText, pageNo) => {
413
+ console.log(`加载第 ${pageNo} 页,搜索条件: "${searchText}"`);
414
+
415
+ // 模拟网络延迟
416
+ await new Promise(resolve => setTimeout(resolve, 1000));
417
+
418
+ const pageSize = 10;
419
+ const totalMessages = 100;
420
+
421
+ // 生成模拟消息
422
+ const messages = Array.from({ length: pageSize }, (_, i) => {
423
+ const messageId = (pageNo - 1) * pageSize + i + 1;
424
+ return {
425
+ id: messageId,
426
+ sender: `用户${messageId}`,
427
+ subject: searchText
428
+ ? `关于${searchText}的消息 #${messageId}`
429
+ : `消息主题 ${messageId}`,
430
+ preview: `这是消息 ${messageId} 的预览。它包含一些示例内容...`,
431
+ timestamp: new Date(Date.now() - Math.random() * 86400000).toISOString()
432
+ };
433
+ });
434
+
435
+ // 如果提供了搜索文本则过滤消息
436
+ const filteredMessages = searchText
437
+ ? messages.filter(msg =>
438
+ msg.subject.toLowerCase().includes(searchText.toLowerCase()) ||
439
+ msg.sender.toLowerCase().includes(searchText.toLowerCase())
440
+ )
441
+ : messages;
442
+
443
+ return {
444
+ list: filteredMessages,
445
+ hasMore: pageNo * pageSize < totalMessages
446
+ };
447
+ };
448
+
449
+ let selectedMessage = null;
450
+ </script>
451
+
452
+ <ListBox
453
+ title="消息(懒加载)"
454
+ itemRender={MessageRenderer}
455
+ selectMode="single"
456
+ lazyLoader={messageLoader}
457
+ bind:selectedItem={selectedMessage}
458
+ style="width: 400px; height: 500px;"
459
+ />
460
+
461
+ <div slot="loadMoreIndicator" class="load-more">
462
+ <div class="loading-spinner">正在加载更多消息...</div>
463
+ </div>
464
+
465
+ {#if selectedMessage}
466
+ <div class="message-details">
467
+ <h3>选中的消息</h3>
468
+ <p><strong>发件人:</strong> {selectedMessage.sender}</p>
469
+ <p><strong>主题:</strong> {selectedMessage.subject}</p>
470
+ <p><strong>时间:</strong> {new Date(selectedMessage.timestamp).toLocaleString()}</p>
471
+ <div class="message-content">
472
+ <p>{selectedMessage.preview}</p>
473
+ </div>
474
+ </div>
475
+ {/if}
476
+
477
+ <style>
478
+ :global(.message-item) {
479
+ padding: 12px;
480
+ border-bottom: 1px solid #eee;
481
+ cursor: pointer;
482
+ transition: background-color 0.2s;
483
+ }
484
+
485
+ :global(.message-item:hover) {
486
+ background-color: #f8f9fa;
487
+ }
488
+
489
+ :global(.message-header) {
490
+ display: flex;
491
+ justify-content: space-between;
492
+ align-items: center;
493
+ margin-bottom: 4px;
494
+ }
495
+
496
+ :global(.sender) {
497
+ font-weight: 500;
498
+ color: #333;
499
+ }
500
+
501
+ :global(.timestamp) {
502
+ font-size: 0.75rem;
503
+ color: #666;
504
+ }
505
+
506
+ :global(.message-subject) {
507
+ font-weight: 500;
508
+ margin-bottom: 4px;
509
+ }
510
+
511
+ :global(.message-preview) {
512
+ font-size: 0.875rem;
513
+ color: #666;
514
+ display: -webkit-box;
515
+ -webkit-line-clamp: 2;
516
+ -webkit-box-orient: vertical;
517
+ overflow: hidden;
518
+ }
519
+
520
+ .load-more {
521
+ padding: 16px;
522
+ text-align: center;
523
+ color: #666;
524
+ }
525
+
526
+ .loading-spinner {
527
+ display: flex;
528
+ align-items: center;
529
+ justify-content: center;
530
+ gap: 8px;
531
+ }
532
+
533
+ .message-details {
534
+ margin-top: 20px;
535
+ padding: 16px;
536
+ background: #f8f9fa;
537
+ border-radius: 4px;
538
+ width: 400px;
539
+ }
540
+
541
+ .message-content {
542
+ margin-top: 12px;
543
+ padding-top: 12px;
544
+ border-top: 1px solid #dee2e6;
545
+ }
546
+ </style>
547
+ ```
548
+
549
+ ### 自定义项目渲染器
550
+
551
+ ```svelte
552
+ <script lang="ts">
553
+ import ListBox from '@ticatec/uniface-element/ListBox';
554
+
555
+ // 用于项目渲染的自定义 Svelte 组件
556
+ import FileItemRenderer from './FileItemRenderer.svelte';
557
+
558
+ let files = [
559
+ { id: 1, name: '文档.pdf', size: 2048000, type: 'pdf', modified: '2024-03-15T10:30:00Z' },
560
+ { id: 2, name: '图片.jpg', size: 1536000, type: 'image', modified: '2024-03-14T15:45:00Z' },
561
+ { id: 3, name: '脚本.js', size: 8192, type: 'code', modified: '2024-03-13T09:20:00Z' },
562
+ { id: 4, name: '数据.csv', size: 512000, type: 'data', modified: '2024-03-12T14:10:00Z' }
563
+ ];
564
+
565
+ let selectedFiles = [];
566
+
567
+ function handleFileDoubleClick(file: any) {
568
+ console.log('打开文件:', file.name);
569
+ // 实现文件打开逻辑
570
+ }
571
+ </script>
572
+
573
+ <ListBox
574
+ title="文件浏览器"
575
+ list={files}
576
+ itemRender={FileItemRenderer}
577
+ selectMode="multiple"
578
+ bind:selectedList={selectedFiles}
579
+ onItemDblClick={handleFileDoubleClick}
580
+ style="width: 500px; height: 400px;"
581
+ item$props={{ showDetails: true, allowPreview: true }}
582
+ >
583
+ <div slot="footer" class="file-footer">
584
+ <span>{files.length} 个项目,已选择 {selectedFiles.length} 个</span>
585
+ </div>
586
+ </ListBox>
587
+ ```
588
+
589
+ FileItemRenderer.svelte:
590
+ ```svelte
591
+ <script lang="ts">
592
+ export let item: any;
593
+ export let selected: boolean = false;
594
+ export let readonly: boolean = false;
595
+ export let showDetails: boolean = false;
596
+ export let allowPreview: boolean = false;
597
+
598
+ function formatFileSize(bytes: number) {
599
+ const sizes = ['B', 'KB', 'MB', 'GB'];
600
+ if (bytes === 0) return '0 B';
601
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
602
+ return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
603
+ }
604
+
605
+ function getFileIcon(type: string) {
606
+ const icons = {
607
+ pdf: '📄',
608
+ image: '🖼️',
609
+ code: '💻',
610
+ data: '📊',
611
+ default: '📁'
612
+ };
613
+ return icons[type] || icons.default;
614
+ }
615
+ </script>
616
+
617
+ <div class="file-item" class:selected class:readonly>
618
+ <div class="file-icon">
619
+ {getFileIcon(item.type)}
620
+ </div>
621
+
622
+ <div class="file-info">
623
+ <div class="file-name">{item.name}</div>
624
+ {#if showDetails}
625
+ <div class="file-details">
626
+ <span class="file-size">{formatFileSize(item.size)}</span>
627
+ <span class="file-modified">修改时间: {new Date(item.modified).toLocaleDateString()}</span>
628
+ </div>
629
+ {/if}
630
+ </div>
631
+
632
+ {#if allowPreview && item.type === 'image'}
633
+ <button class="preview-btn" on:click|stopPropagation={() => console.log('预览', item.name)}>
634
+ 👁️
635
+ </button>
636
+ {/if}
637
+ </div>
638
+
639
+ <style>
640
+ .file-item {
641
+ display: flex;
642
+ align-items: center;
643
+ gap: 12px;
644
+ padding: 8px 12px;
645
+ border-bottom: 1px solid #f0f0f0;
646
+ cursor: pointer;
647
+ transition: background-color 0.2s;
648
+ }
649
+
650
+ .file-item:hover:not(.readonly) {
651
+ background-color: #f8f9fa;
652
+ }
653
+
654
+ .file-item.selected {
655
+ background-color: #e3f2fd;
656
+ }
657
+
658
+ .file-item.readonly {
659
+ cursor: default;
660
+ opacity: 0.7;
661
+ }
662
+
663
+ .file-icon {
664
+ font-size: 24px;
665
+ flex-shrink: 0;
666
+ }
667
+
668
+ .file-info {
669
+ flex: 1;
670
+ min-width: 0;
671
+ }
672
+
673
+ .file-name {
674
+ font-weight: 500;
675
+ white-space: nowrap;
676
+ overflow: hidden;
677
+ text-overflow: ellipsis;
678
+ }
679
+
680
+ .file-details {
681
+ display: flex;
682
+ gap: 16px;
683
+ font-size: 0.875rem;
684
+ color: #666;
685
+ margin-top: 2px;
686
+ }
687
+
688
+ .preview-btn {
689
+ background: none;
690
+ border: 1px solid #ddd;
691
+ border-radius: 4px;
692
+ padding: 4px 8px;
693
+ cursor: pointer;
694
+ opacity: 0.7;
695
+ transition: opacity 0.2s;
696
+ }
697
+
698
+ .preview-btn:hover {
699
+ opacity: 1;
700
+ }
701
+ </style>
702
+ ```
703
+
704
+ ## 样式
705
+
706
+ ListBox 组件使用以下 CSS 类:
707
+
708
+ ```css
709
+ .uniface-listbox {
710
+ /* 主要列表容器 */
711
+ }
712
+
713
+ .uniface-listbox.round {
714
+ /* 圆角变体 */
715
+ }
716
+
717
+ .box-header {
718
+ /* 头部容器 */
719
+ }
720
+
721
+ .header-search {
722
+ /* 搜索框容器 */
723
+ }
724
+
725
+ .listbox-content {
726
+ /* 主要内容区域 */
727
+ }
728
+
729
+ .listbox-content.selectable {
730
+ /* 可选择列表样式 */
731
+ }
732
+
733
+ .listview-item {
734
+ /* 单个列表项 */
735
+ }
736
+
737
+ .listview-item.selected {
738
+ /* 选中项样式 */
739
+ }
740
+
741
+ .box-footer {
742
+ /* 底部容器 */
743
+ }
744
+
745
+ .list-view-mask {
746
+ /* 加载遮罩 */
747
+ }
748
+ ```
749
+
750
+ ## API 参考
751
+
752
+ ### ListBox 组件属性
753
+
754
+ ```typescript
755
+ interface ListBoxProps {
756
+ readonly?: boolean; // 只读模式
757
+ style?: string | null; // 容器样式
758
+ header$style?: string | null; // 头部样式
759
+ footer$style?: string | null; // 底部样式
760
+ list: Array<any>; // 要显示的项目
761
+ itemRender: any; // 项目渲染器组件
762
+ selectMode: 'none' | 'single' | 'multiple'; // 选择模式
763
+ filter?: FunFilter | null; // 过滤函数
764
+ lazyLoader?: LazyLoader | null; // 懒加载配置
765
+ selectedItem?: any; // 选中的项目(单选模式)
766
+ selectedList?: Array<any>; // 选中的项目(多选模式)
767
+ class?: string; // CSS 类
768
+ item$props?: any; // 项目渲染器的属性
769
+ onSelectChange?: OnSelectChange; // 选择回调
770
+ onItemDblClick?: OnItemDblClick; // 双击回调
771
+ onItemClick?: OnItemClick; // 点击回调
772
+ title?: string; // 头部标题
773
+ round?: boolean; // 圆角
774
+ }
775
+ ```
776
+
777
+ ## 最佳实践
778
+
779
+ 1. **性能优化** - 对于大数据集(>100项)使用懒加载
780
+ 2. **项目渲染器** - 保持项目渲染组件轻量级和专注
781
+ 3. **过滤功能** - 实现高效的过滤函数以获得响应式搜索
782
+ 4. **选择管理** - 始终绑定到 selectedItem/selectedList 进行状态跟踪
783
+ 5. **可访问性** - 确保项目渲染器支持键盘导航
784
+ 6. **错误处理** - 在懒加载器中优雅地处理加载错误
785
+ 7. **视觉反馈** - 为选择和加载状态提供清晰的视觉指示器
786
+ 8. **移动端支持** - 考虑触摸友好的项目尺寸和交互