@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,250 @@
1
+ # InputOptionsSelect 组件
2
+
3
+ 带输入和异步搜索功能的选择组件。
4
+
5
+ ## 类型定义
6
+
7
+ ```typescript
8
+ interface LoaderResult<T = any> {
9
+ list: T[];
10
+ hasMore: boolean;
11
+ }
12
+
13
+ type LazyLoader<T = any> = (keyword: string, pageNo: number) => Promise<LoaderResult<T>>;
14
+ ```
15
+
16
+ ## 属性
17
+
18
+ | 属性 | 类型 | 默认值 | 描述 |
19
+ |------|------|--------|------|
20
+ | `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | 外观变体 |
21
+ | `disabled` | `boolean` | `false` | 是否禁用 |
22
+ | `readonly` | `boolean` | `false` | 是否只读 |
23
+ | `compact` | `boolean` | `false` | 是否紧凑模式 |
24
+ | `text` | `string` | `''` | 显示文本 |
25
+ | `value` | `T | null` | `null` | 选中的值 |
26
+ | `keyField` | `string` | `'code'` | 值字段名 |
27
+ | `textField` | `string` | `'text'` | 显示字段名 |
28
+ | `menu$height` | `number` | `0` | 下拉菜单高度 |
29
+ | `placeholder` | `string` | `''` | 占位符文本 |
30
+ | `displayMode` | `DisplayMode` | `DisplayMode.Edit` | 显示模式 |
31
+ | `itemRender` | `ComponentType` | `null` | 自定义选项渲染组件 |
32
+ | `lazyLoader` | `LazyLoader<T>` | - | 异步数据加载器 |
33
+ | `style` | `string` | `''` | 自定义样式 |
34
+ | `onChange` | `OnChangeHandler<T | null>` | - | 值变化回调 |
35
+
36
+ ## 使用示例
37
+
38
+ ```svelte
39
+ <script lang="ts">
40
+ import InputOptionsSelect, { type LazyLoader } from '@ticatec/uniface-element/InputOptionsSelect';
41
+
42
+ interface User {
43
+ id: string;
44
+ name: string;
45
+ email: string;
46
+ department: string;
47
+ }
48
+
49
+ interface Product {
50
+ code: string;
51
+ text: string;
52
+ category: string;
53
+ price: number;
54
+ }
55
+
56
+ let selectedUser: User | null = null;
57
+ let selectedProduct: Product | null = null;
58
+
59
+ // 用户搜索
60
+ const searchUsers: LazyLoader<User> = async (keyword: string, pageNo: number): Promise<LoaderResult<User>> => {
61
+ const response: Response = await fetch(`/api/users/search?q=${encodeURIComponent(keyword)}&page=${pageNo}`);
62
+ const data = await response.json();
63
+ return {
64
+ list: data.users,
65
+ hasMore: data.hasMore
66
+ };
67
+ };
68
+
69
+ // 产品搜索
70
+ const searchProducts: LazyLoader<Product> = async (keyword: string, pageNo: number): Promise<LoaderResult<Product>> => {
71
+ // 模拟API延迟
72
+ await new Promise<void>(resolve => setTimeout(resolve, 300));
73
+
74
+ const mockProducts: Product[] = [
75
+ { code: 'P001', text: 'iPhone 15 Pro', category: '手机', price: 7999 },
76
+ { code: 'P002', text: 'MacBook Air M2', category: '笔记本', price: 8999 },
77
+ { code: 'P003', text: 'iPad Pro 12.9', category: '平板', price: 6999 }
78
+ ].filter((p: Product): boolean => p.text.toLowerCase().includes(keyword.toLowerCase()));
79
+
80
+ return {
81
+ list: mockProducts,
82
+ hasMore: false
83
+ };
84
+ };
85
+
86
+ const handleUserChange = (user: User): void => {
87
+ selectedUser = user;
88
+ console.log('选择的用户:', user);
89
+ };
90
+
91
+ const handleProductChange = (product: Product): void => {
92
+ selectedProduct = product;
93
+ console.log('选择的产品:', product);
94
+ };
95
+ </script>
96
+
97
+ <!-- 用户搜索选择 -->
98
+ <InputOptionsSelect
99
+ placeholder="请输入用户名搜索"
100
+ lazyLoader={searchUsers}
101
+ keyField="id"
102
+ textField="name"
103
+ onChange={handleUserChange}
104
+ />
105
+
106
+ <!-- 产品搜索选择 -->
107
+ <InputOptionsSelect
108
+ placeholder="请输入产品名称搜索"
109
+ lazyLoader={searchProducts}
110
+ keyField="code"
111
+ textField="text"
112
+ onChange={handleProductChange}
113
+ />
114
+ ```
115
+
116
+ ## 高级用法
117
+
118
+ ### 自定义选项渲染
119
+ ```svelte
120
+ <script lang="ts">
121
+ import type { ComponentType } from 'svelte';
122
+ import UserItemRenderer from './UserItemRenderer.svelte';
123
+
124
+ const searchUsersWithDetails: LazyLoader<User> = async (keyword: string, pageNo: number): Promise<LoaderResult<User>> => {
125
+ const response: Response = await fetch(`/api/users/search-detailed?q=${keyword}&page=${pageNo}`);
126
+ return await response.json();
127
+ };
128
+ </script>
129
+
130
+ <InputOptionsSelect
131
+ placeholder="搜索用户"
132
+ lazyLoader={searchUsersWithDetails}
133
+ keyField="id"
134
+ textField="name"
135
+ itemRender={UserItemRenderer}
136
+ onChange={handleUserChange}
137
+ />
138
+
139
+ <!-- UserItemRenderer.svelte -->
140
+ <script lang="ts">
141
+ interface User {
142
+ id: string;
143
+ name: string;
144
+ email: string;
145
+ department: string;
146
+ avatar: string;
147
+ }
148
+
149
+ export let item: User;
150
+ </script>
151
+
152
+ <div class="user-item">
153
+ <div class="user-avatar">
154
+ <img src={item.avatar} alt={item.name} />
155
+ </div>
156
+ <div class="user-info">
157
+ <div class="user-name">{item.name}</div>
158
+ <div class="user-email">{item.email}</div>
159
+ <div class="user-department">{item.department}</div>
160
+ </div>
161
+ </div>
162
+ ```
163
+
164
+ ### 带缓存的搜索
165
+ ```svelte
166
+ <script lang="ts">
167
+ interface SearchCache {
168
+ [key: string]: {
169
+ data: any[];
170
+ timestamp: number;
171
+ };
172
+ }
173
+
174
+ let searchCache: SearchCache = {};
175
+ const cacheTimeout: number = 5 * 60 * 1000; // 5分钟缓存
176
+
177
+ const cachedSearchLoader: LazyLoader = async (keyword: string, pageNo: number): Promise<LoaderResult> => {
178
+ const cacheKey: string = `${keyword}-${pageNo}`;
179
+ const cached = searchCache[cacheKey];
180
+
181
+ // 检查缓存
182
+ if (cached && Date.now() - cached.timestamp < cacheTimeout) {
183
+ console.log('使用缓存数据');
184
+ return {
185
+ list: cached.data,
186
+ hasMore: false
187
+ };
188
+ }
189
+
190
+ // 从API获取数据
191
+ const response: Response = await fetch(`/api/search?q=${keyword}&page=${pageNo}`);
192
+ const result: LoaderResult = await response.json();
193
+
194
+ // 缓存结果
195
+ searchCache[cacheKey] = {
196
+ data: result.list,
197
+ timestamp: Date.now()
198
+ };
199
+
200
+ return result;
201
+ };
202
+ </script>
203
+ ```
204
+
205
+ ## 最佳实践
206
+
207
+ ### 1. 防抖搜索
208
+ 组件内部已经实现了防抖机制,默认延迟500ms后执行搜索。
209
+
210
+ ### 2. 错误处理
211
+ ```svelte
212
+ <script lang="ts">
213
+ const robustSearchLoader: LazyLoader = async (keyword: string, pageNo: number): Promise<LoaderResult> => {
214
+ try {
215
+ const response: Response = await fetch(`/api/search?q=${keyword}&page=${pageNo}`);
216
+
217
+ if (!response.ok) {
218
+ throw new Error(`搜索失败: ${response.status}`);
219
+ }
220
+
221
+ return await response.json();
222
+ } catch (error: unknown) {
223
+ console.error('搜索出错:', error);
224
+ return {
225
+ list: [],
226
+ hasMore: false
227
+ };
228
+ }
229
+ };
230
+ </script>
231
+ ```
232
+
233
+ ### 3. 分页加载
234
+ ```svelte
235
+ <script lang="ts">
236
+ const paginatedLoader: LazyLoader = async (keyword: string, pageNo: number): Promise<LoaderResult> => {
237
+ const pageSize: number = 20;
238
+ const response: Response = await fetch(
239
+ `/api/search?q=${keyword}&page=${pageNo}&size=${pageSize}`
240
+ );
241
+
242
+ const data = await response.json();
243
+
244
+ return {
245
+ list: data.items,
246
+ hasMore: data.total > pageNo * pageSize
247
+ };
248
+ };
249
+ </script>
250
+ ```
@@ -0,0 +1,406 @@
1
+ # LookupEditor 组件
2
+
3
+ 查找编辑器组件,通过模态对话框进行复杂的查找和选择操作。
4
+
5
+ ## 类型定义
6
+
7
+ ```typescript
8
+ type OnActionHandler = () => void;
9
+ ```
10
+
11
+ ## 属性
12
+
13
+ | 属性 | 类型 | 默认值 | 描述 |
14
+ |------|------|--------|------|
15
+ | `variant` | `'' \| 'plain' \| 'outlined' \| 'filled'` | `''` | 外观变体 |
16
+ | `disabled` | `boolean` | `false` | 是否禁用 |
17
+ | `readonly` | `boolean` | `false` | 是否只读 |
18
+ | `compact` | `boolean` | `false` | 是否紧凑模式 |
19
+ | `mandatory` | `boolean` | `false` | 是否必填 |
20
+ | `value` | `T | null` | `null` | 选中的值 |
21
+ | `text` | `string` | `''` | 显示文本 |
22
+ | `placeholder` | `string` | `''` | 占位符文本 |
23
+ | `displayMode` | `DisplayMode` | `DisplayMode.Edit` | 显示模式 |
24
+ | `style` | `string` | `''` | 自定义样式 |
25
+ | `onChange` | `OnChangeHandler<T | null>` | - | 值变化回调 |
26
+ | `onAction` | `OnActionHandler` | - | 查找动作回调 |
27
+
28
+ ## 使用示例
29
+
30
+ ```svelte
31
+ <script lang="ts">
32
+ import LookupEditor from '@ticatec/uniface-element/LookupEditor';
33
+ import CustomerLookupModal from './CustomerLookupModal.svelte';
34
+ import ProductLookupModal from './ProductLookupModal.svelte';
35
+
36
+ interface Customer {
37
+ id: string;
38
+ name: string;
39
+ code: string;
40
+ email: string;
41
+ }
42
+
43
+ interface Product {
44
+ id: string;
45
+ name: string;
46
+ price: number;
47
+ category: string;
48
+ }
49
+
50
+ let selectedCustomer: string | null = null;
51
+ let customerText: string = '';
52
+ let selectedProduct: string | null = null;
53
+ let productText: string = '';
54
+ let showCustomerModal: boolean = false;
55
+ let showProductModal: boolean = false;
56
+
57
+ // 客户查找
58
+ const handleCustomerLookup = (): void => {
59
+ showCustomerModal = true;
60
+ };
61
+
62
+ const handleCustomerSelect = (customer: Customer): void => {
63
+ selectedCustomer = customer.id;
64
+ customerText = `${customer.name} (${customer.code})`;
65
+ showCustomerModal = false;
66
+ };
67
+
68
+ const handleCustomerChange = (value: string | null): void => {
69
+ selectedCustomer = value;
70
+ };
71
+
72
+ // 产品查找
73
+ const handleProductLookup = (): void => {
74
+ showProductModal = true;
75
+ };
76
+
77
+ const handleProductSelect = (product: Product): void => {
78
+ selectedProduct = product.id;
79
+ productText = `${product.name} - ¥${product.price}`;
80
+ showProductModal = false;
81
+ };
82
+
83
+ const handleProductChange = (value: string | null): void => {
84
+ selectedProduct = value;
85
+ };
86
+ </script>
87
+
88
+ <!-- 客户查找 -->
89
+ <LookupEditor
90
+ placeholder="请选择客户"
91
+ bind:value={selectedCustomer}
92
+ bind:text={customerText}
93
+ onAction={handleCustomerLookup}
94
+ onChange={handleCustomerChange}
95
+ />
96
+
97
+ <!-- 产品查找 -->
98
+ <LookupEditor
99
+ placeholder="请选择产品"
100
+ mandatory={true}
101
+ bind:value={selectedProduct}
102
+ bind:text={productText}
103
+ onAction={handleProductLookup}
104
+ onChange={handleProductChange}
105
+ />
106
+
107
+ <!-- 客户查找模态框 -->
108
+ {#if showCustomerModal}
109
+ <CustomerLookupModal
110
+ onSelect={handleCustomerSelect}
111
+ onClose={() => showCustomerModal = false}
112
+ />
113
+ {/if}
114
+
115
+ <!-- 产品查找模态框 -->
116
+ {#if showProductModal}
117
+ <ProductLookupModal
118
+ onSelect={handleProductSelect}
119
+ onClose={() => showProductModal = false}
120
+ />
121
+ {/if}
122
+ ```
123
+
124
+ ## 高级用法
125
+
126
+ ### 多步骤查找流程
127
+ ```svelte
128
+ <script lang="ts">
129
+ interface Supplier {
130
+ id: string;
131
+ name: string;
132
+ country: string;
133
+ }
134
+
135
+ interface SupplierProduct {
136
+ id: string;
137
+ name: string;
138
+ supplier_id: string;
139
+ price: number;
140
+ }
141
+
142
+ let selectedSupplier: string | null = null;
143
+ let supplierText: string = '';
144
+ let selectedSupplierProduct: string | null = null;
145
+ let supplierProductText: string = '';
146
+ let showSupplierModal: boolean = false;
147
+ let showSupplierProductModal: boolean = false;
148
+
149
+ const handleSupplierLookup = (): void => {
150
+ showSupplierModal = true;
151
+ };
152
+
153
+ const handleSupplierSelect = (supplier: Supplier): void => {
154
+ selectedSupplier = supplier.id;
155
+ supplierText = `${supplier.name} (${supplier.country})`;
156
+ showSupplierModal = false;
157
+
158
+ // 重置供应商产品选择
159
+ selectedSupplierProduct = null;
160
+ supplierProductText = '';
161
+ };
162
+
163
+ const handleSupplierProductLookup = (): void => {
164
+ if (!selectedSupplier) {
165
+ alert('请先选择供应商');
166
+ return;
167
+ }
168
+ showSupplierProductModal = true;
169
+ };
170
+
171
+ const handleSupplierProductSelect = (product: SupplierProduct): void => {
172
+ selectedSupplierProduct = product.id;
173
+ supplierProductText = `${product.name} - ¥${product.price}`;
174
+ showSupplierProductModal = false;
175
+ };
176
+ </script>
177
+
178
+ <div class="lookup-flow">
179
+ <div class="form-field">
180
+ <label>供应商:</label>
181
+ <LookupEditor
182
+ placeholder="请选择供应商"
183
+ bind:value={selectedSupplier}
184
+ bind:text={supplierText}
185
+ onAction={handleSupplierLookup}
186
+ />
187
+ </div>
188
+
189
+ <div class="form-field">
190
+ <label>供应商产品:</label>
191
+ <LookupEditor
192
+ placeholder="请选择产品"
193
+ disabled={!selectedSupplier}
194
+ bind:value={selectedSupplierProduct}
195
+ bind:text={supplierProductText}
196
+ onAction={handleSupplierProductLookup}
197
+ />
198
+ </div>
199
+ </div>
200
+ ```
201
+
202
+ ### 带缓存的查找
203
+ ```svelte
204
+ <script lang="ts">
205
+ interface CacheEntry<T> {
206
+ data: T;
207
+ timestamp: number;
208
+ }
209
+
210
+ class LookupCache {
211
+ private cache = new Map<string, CacheEntry<any>>();
212
+ private ttl = 5 * 60 * 1000; // 5分钟缓存
213
+
214
+ get<T>(key: string): T | null {
215
+ const entry = this.cache.get(key);
216
+ if (!entry) return null;
217
+
218
+ if (Date.now() - entry.timestamp > this.ttl) {
219
+ this.cache.delete(key);
220
+ return null;
221
+ }
222
+
223
+ return entry.data;
224
+ }
225
+
226
+ set<T>(key: string, data: T): void {
227
+ this.cache.set(key, {
228
+ data,
229
+ timestamp: Date.now()
230
+ });
231
+ }
232
+
233
+ clear(): void {
234
+ this.cache.clear();
235
+ }
236
+ }
237
+
238
+ const cache = new LookupCache();
239
+
240
+ let selectedUser: string | null = null;
241
+ let userText: string = '';
242
+ let showUserModal: boolean = false;
243
+
244
+ const handleUserLookup = (): void => {
245
+ // 检查缓存
246
+ const cachedUsers = cache.get('users');
247
+ if (cachedUsers) {
248
+ console.log('使用缓存的用户数据');
249
+ }
250
+
251
+ showUserModal = true;
252
+ };
253
+
254
+ interface User {
255
+ id: string;
256
+ name: string;
257
+ email: string;
258
+ }
259
+
260
+ const handleUserSelect = (user: User): void => {
261
+ selectedUser = user.id;
262
+ userText = `${user.name} (${user.email})`;
263
+
264
+ // 缓存用户数据
265
+ cache.set(`user:${user.id}`, user);
266
+
267
+ showUserModal = false;
268
+ };
269
+ </script>
270
+ ```
271
+
272
+ ### 外部API集成
273
+ ```svelte
274
+ <script lang="ts">
275
+ interface ApiResponse<T> {
276
+ data: T[];
277
+ total: number;
278
+ page: number;
279
+ }
280
+
281
+ interface ApiUser {
282
+ id: number;
283
+ name: string;
284
+ email: string;
285
+ department: string;
286
+ }
287
+
288
+ let selectedApiUser: number | null = null;
289
+ let apiUserText: string = '';
290
+ let showApiUserModal: boolean = false;
291
+ let isLoading: boolean = false;
292
+
293
+ const searchUsers = async (query: string, page: number = 1): Promise<ApiResponse<ApiUser>> => {
294
+ const response: Response = await fetch(`/api/users/search?q=${encodeURIComponent(query)}&page=${page}`);
295
+ if (!response.ok) {
296
+ throw new Error('搜索用户失败');
297
+ }
298
+ return await response.json();
299
+ };
300
+
301
+ const handleApiUserLookup = (): void => {
302
+ showApiUserModal = true;
303
+ };
304
+
305
+ const handleApiUserSelect = async (userId: number): Promise<void> => {
306
+ isLoading = true;
307
+ try {
308
+ const response: Response = await fetch(`/api/users/${userId}`);
309
+ const user: ApiUser = await response.json();
310
+
311
+ selectedApiUser = user.id;
312
+ apiUserText = `${user.name} - ${user.department}`;
313
+ showApiUserModal = false;
314
+ } catch (error: unknown) {
315
+ console.error('获取用户详情失败:', error);
316
+ } finally {
317
+ isLoading = false;
318
+ }
319
+ };
320
+ </script>
321
+
322
+ <LookupEditor
323
+ placeholder="请选择API用户"
324
+ disabled={isLoading}
325
+ bind:value={selectedApiUser}
326
+ bind:text={apiUserText}
327
+ onAction={handleApiUserLookup}
328
+ />
329
+
330
+ {#if isLoading}
331
+ <div class="loading-indicator">加载中...</div>
332
+ {/if}
333
+ ```
334
+
335
+ ## 最佳实践
336
+
337
+ ### 1. 提供清晰的显示文本
338
+ ```typescript
339
+ interface DisplayItem {
340
+ name: string;
341
+ code: string;
342
+ category: string;
343
+ }
344
+
345
+ const formatDisplayText = (item: DisplayItem): string => {
346
+ return `${item.name} (${item.code}) - ${item.category}`;
347
+ };
348
+ ```
349
+
350
+ ### 2. 处理必填验证
351
+ ```svelte
352
+ <script lang="ts">
353
+ let value: string | null = null;
354
+ let error: string = '';
355
+
356
+ const validate = (): boolean => {
357
+ if (!value) {
358
+ error = '此字段为必填项';
359
+ return false;
360
+ }
361
+ error = '';
362
+ return true;
363
+ };
364
+ </script>
365
+
366
+ <LookupEditor
367
+ mandatory={true}
368
+ bind:value={value}
369
+ onAction={handleLookup}
370
+ />
371
+ {#if error}
372
+ <div class="error-message">{error}</div>
373
+ {/if}
374
+ ```
375
+
376
+ ### 3. 响应式模态框
377
+ ```svelte
378
+ <script lang="ts">
379
+ import { onMount } from 'svelte';
380
+
381
+ let isMobile: boolean = false;
382
+ let showModal: boolean = false;
383
+
384
+ const checkMobile = (): void => {
385
+ isMobile = window.innerWidth < 768;
386
+ };
387
+
388
+ const handleSelect = (item: any): void => {
389
+ // Handle selection logic
390
+ };
391
+
392
+ onMount((): (() => void) => {
393
+ checkMobile();
394
+ window.addEventListener('resize', checkMobile);
395
+ return (): void => window.removeEventListener('resize', checkMobile);
396
+ });
397
+ </script>
398
+
399
+ {#if showModal}
400
+ <LookupModal
401
+ fullscreen={isMobile}
402
+ onSelect={handleSelect}
403
+ onClose={() => showModal = false}
404
+ />
405
+ {/if}
406
+ ```