@h-ai/ui 0.1.0-alpha5

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 (234) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +488 -0
  3. package/auto-import.d.ts +96 -0
  4. package/auto-import.js +259 -0
  5. package/dist/components/compounds/Accordion.svelte +142 -0
  6. package/dist/components/compounds/Accordion.svelte.d.ts +25 -0
  7. package/dist/components/compounds/ActionSheet.svelte +116 -0
  8. package/dist/components/compounds/ActionSheet.svelte.d.ts +22 -0
  9. package/dist/components/compounds/Alert.svelte +67 -0
  10. package/dist/components/compounds/Alert.svelte.d.ts +8 -0
  11. package/dist/components/compounds/AppBar.svelte +77 -0
  12. package/dist/components/compounds/AppBar.svelte.d.ts +20 -0
  13. package/dist/components/compounds/BottomNav.svelte +75 -0
  14. package/dist/components/compounds/BottomNav.svelte.d.ts +18 -0
  15. package/dist/components/compounds/Breadcrumb.svelte +49 -0
  16. package/dist/components/compounds/Breadcrumb.svelte.d.ts +4 -0
  17. package/dist/components/compounds/Calendar.svelte +116 -0
  18. package/dist/components/compounds/Calendar.svelte.d.ts +47 -0
  19. package/dist/components/compounds/Card.svelte +77 -0
  20. package/dist/components/compounds/Card.svelte.d.ts +8 -0
  21. package/dist/components/compounds/Combobox.svelte +327 -0
  22. package/dist/components/compounds/Combobox.svelte.d.ts +69 -0
  23. package/dist/components/compounds/Confirm.svelte +90 -0
  24. package/dist/components/compounds/Confirm.svelte.d.ts +4 -0
  25. package/dist/components/compounds/DataTable.svelte +133 -0
  26. package/dist/components/compounds/DataTable.svelte.d.ts +69 -0
  27. package/dist/components/compounds/DatePicker.svelte +135 -0
  28. package/dist/components/compounds/DatePicker.svelte.d.ts +46 -0
  29. package/dist/components/compounds/Drawer.svelte +113 -0
  30. package/dist/components/compounds/Drawer.svelte.d.ts +4 -0
  31. package/dist/components/compounds/Dropdown.svelte +91 -0
  32. package/dist/components/compounds/Dropdown.svelte.d.ts +8 -0
  33. package/dist/components/compounds/Empty.svelte +83 -0
  34. package/dist/components/compounds/Empty.svelte.d.ts +4 -0
  35. package/dist/components/compounds/Form.svelte +96 -0
  36. package/dist/components/compounds/Form.svelte.d.ts +7 -0
  37. package/dist/components/compounds/FormField.svelte +56 -0
  38. package/dist/components/compounds/FormField.svelte.d.ts +4 -0
  39. package/dist/components/compounds/InfiniteScroll.svelte +82 -0
  40. package/dist/components/compounds/InfiniteScroll.svelte.d.ts +22 -0
  41. package/dist/components/compounds/Modal.svelte +123 -0
  42. package/dist/components/compounds/Modal.svelte.d.ts +8 -0
  43. package/dist/components/compounds/PageHeader.svelte +44 -0
  44. package/dist/components/compounds/PageHeader.svelte.d.ts +28 -0
  45. package/dist/components/compounds/Pagination.svelte +157 -0
  46. package/dist/components/compounds/Pagination.svelte.d.ts +4 -0
  47. package/dist/components/compounds/Popover.svelte +137 -0
  48. package/dist/components/compounds/Popover.svelte.d.ts +4 -0
  49. package/dist/components/compounds/PullRefresh.svelte +98 -0
  50. package/dist/components/compounds/PullRefresh.svelte.d.ts +16 -0
  51. package/dist/components/compounds/Result.svelte +84 -0
  52. package/dist/components/compounds/Result.svelte.d.ts +4 -0
  53. package/dist/components/compounds/SafeArea.svelte +37 -0
  54. package/dist/components/compounds/SafeArea.svelte.d.ts +12 -0
  55. package/dist/components/compounds/Skeleton.svelte +75 -0
  56. package/dist/components/compounds/Skeleton.svelte.d.ts +8 -0
  57. package/dist/components/compounds/Steps.svelte +111 -0
  58. package/dist/components/compounds/Steps.svelte.d.ts +4 -0
  59. package/dist/components/compounds/SwipeCell.svelte +115 -0
  60. package/dist/components/compounds/SwipeCell.svelte.d.ts +19 -0
  61. package/dist/components/compounds/Tabs.svelte +79 -0
  62. package/dist/components/compounds/Tabs.svelte.d.ts +8 -0
  63. package/dist/components/compounds/TagInput.svelte +108 -0
  64. package/dist/components/compounds/TagInput.svelte.d.ts +4 -0
  65. package/dist/components/compounds/Timeline.svelte +167 -0
  66. package/dist/components/compounds/Timeline.svelte.d.ts +25 -0
  67. package/dist/components/compounds/ToastContainer.svelte +59 -0
  68. package/dist/components/compounds/ToastContainer.svelte.d.ts +3 -0
  69. package/dist/components/compounds/Tooltip.svelte +41 -0
  70. package/dist/components/compounds/Tooltip.svelte.d.ts +4 -0
  71. package/dist/components/compounds/accordion-types.d.ts +16 -0
  72. package/dist/components/compounds/accordion-types.js +5 -0
  73. package/dist/components/compounds/action-sheet-types.d.ts +14 -0
  74. package/dist/components/compounds/action-sheet-types.js +5 -0
  75. package/dist/components/compounds/bottom-nav-types.d.ts +17 -0
  76. package/dist/components/compounds/bottom-nav-types.js +5 -0
  77. package/dist/components/compounds/index.d.ts +44 -0
  78. package/dist/components/compounds/index.js +54 -0
  79. package/dist/components/compounds/swipe-cell-types.d.ts +14 -0
  80. package/dist/components/compounds/swipe-cell-types.js +5 -0
  81. package/dist/components/compounds/timeline-types.d.ts +20 -0
  82. package/dist/components/compounds/timeline-types.js +5 -0
  83. package/dist/components/index.d.ts +9 -0
  84. package/dist/components/index.js +12 -0
  85. package/dist/components/primitives/Avatar.svelte +102 -0
  86. package/dist/components/primitives/Avatar.svelte.d.ts +8 -0
  87. package/dist/components/primitives/Badge.svelte +33 -0
  88. package/dist/components/primitives/Badge.svelte.d.ts +8 -0
  89. package/dist/components/primitives/BareButton.svelte +45 -0
  90. package/dist/components/primitives/BareButton.svelte.d.ts +4 -0
  91. package/dist/components/primitives/BareInput.svelte +101 -0
  92. package/dist/components/primitives/BareInput.svelte.d.ts +4 -0
  93. package/dist/components/primitives/Button.svelte +60 -0
  94. package/dist/components/primitives/Button.svelte.d.ts +8 -0
  95. package/dist/components/primitives/Checkbox.svelte +58 -0
  96. package/dist/components/primitives/Checkbox.svelte.d.ts +4 -0
  97. package/dist/components/primitives/IconButton.svelte +105 -0
  98. package/dist/components/primitives/IconButton.svelte.d.ts +4 -0
  99. package/dist/components/primitives/Input.svelte +130 -0
  100. package/dist/components/primitives/Input.svelte.d.ts +8 -0
  101. package/dist/components/primitives/Progress.svelte +58 -0
  102. package/dist/components/primitives/Progress.svelte.d.ts +4 -0
  103. package/dist/components/primitives/Radio.svelte +60 -0
  104. package/dist/components/primitives/Radio.svelte.d.ts +25 -0
  105. package/dist/components/primitives/Range.svelte +117 -0
  106. package/dist/components/primitives/Range.svelte.d.ts +27 -0
  107. package/dist/components/primitives/Rating.svelte +148 -0
  108. package/dist/components/primitives/Rating.svelte.d.ts +27 -0
  109. package/dist/components/primitives/Select.svelte +95 -0
  110. package/dist/components/primitives/Select.svelte.d.ts +25 -0
  111. package/dist/components/primitives/Spinner.svelte +54 -0
  112. package/dist/components/primitives/Spinner.svelte.d.ts +4 -0
  113. package/dist/components/primitives/Switch.svelte +53 -0
  114. package/dist/components/primitives/Switch.svelte.d.ts +4 -0
  115. package/dist/components/primitives/Tag.svelte +64 -0
  116. package/dist/components/primitives/Tag.svelte.d.ts +4 -0
  117. package/dist/components/primitives/Textarea.svelte +92 -0
  118. package/dist/components/primitives/Textarea.svelte.d.ts +4 -0
  119. package/dist/components/primitives/ToggleCheckbox.svelte +36 -0
  120. package/dist/components/primitives/ToggleCheckbox.svelte.d.ts +4 -0
  121. package/dist/components/primitives/ToggleInput.svelte +36 -0
  122. package/dist/components/primitives/ToggleInput.svelte.d.ts +4 -0
  123. package/dist/components/primitives/ToggleRadio.svelte +36 -0
  124. package/dist/components/primitives/ToggleRadio.svelte.d.ts +4 -0
  125. package/dist/components/primitives/index.d.ts +26 -0
  126. package/dist/components/primitives/index.js +30 -0
  127. package/dist/components/scenes/ai/AiDocumentDownloadMenu.svelte +387 -0
  128. package/dist/components/scenes/ai/AiDocumentDownloadMenu.svelte.d.ts +38 -0
  129. package/dist/components/scenes/ai/AiDocumentEditor.svelte +2259 -0
  130. package/dist/components/scenes/ai/AiDocumentEditor.svelte.d.ts +4 -0
  131. package/dist/components/scenes/ai/MarkdownRenderer.svelte +460 -0
  132. package/dist/components/scenes/ai/MarkdownRenderer.svelte.d.ts +4 -0
  133. package/dist/components/scenes/ai/document-download.d.ts +40 -0
  134. package/dist/components/scenes/ai/document-download.js +300 -0
  135. package/dist/components/scenes/ai/document-parse.d.ts +28 -0
  136. package/dist/components/scenes/ai/document-parse.js +243 -0
  137. package/dist/components/scenes/ai/document-types.d.ts +164 -0
  138. package/dist/components/scenes/ai/document-types.js +1 -0
  139. package/dist/components/scenes/ai/index.d.ts +17 -0
  140. package/dist/components/scenes/ai/index.js +13 -0
  141. package/dist/components/scenes/ai/markdown-parse.d.ts +32 -0
  142. package/dist/components/scenes/ai/markdown-parse.js +161 -0
  143. package/dist/components/scenes/app/FeedbackModal.svelte +160 -0
  144. package/dist/components/scenes/app/FeedbackModal.svelte.d.ts +47 -0
  145. package/dist/components/scenes/app/LanguageSwitch.svelte +136 -0
  146. package/dist/components/scenes/app/LanguageSwitch.svelte.d.ts +50 -0
  147. package/dist/components/scenes/app/SettingsModal.svelte +174 -0
  148. package/dist/components/scenes/app/SettingsModal.svelte.d.ts +31 -0
  149. package/dist/components/scenes/app/ThemeSelector.svelte +176 -0
  150. package/dist/components/scenes/app/ThemeSelector.svelte.d.ts +31 -0
  151. package/dist/components/scenes/app/ThemeToggle.svelte +76 -0
  152. package/dist/components/scenes/app/ThemeToggle.svelte.d.ts +29 -0
  153. package/dist/components/scenes/app/index.d.ts +11 -0
  154. package/dist/components/scenes/app/index.js +11 -0
  155. package/dist/components/scenes/crud/CrudDeleteConfirm.svelte +39 -0
  156. package/dist/components/scenes/crud/CrudDeleteConfirm.svelte.d.ts +18 -0
  157. package/dist/components/scenes/crud/CrudDetailDrawer.svelte +155 -0
  158. package/dist/components/scenes/crud/CrudDetailDrawer.svelte.d.ts +38 -0
  159. package/dist/components/scenes/crud/CrudEditDrawer.svelte +222 -0
  160. package/dist/components/scenes/crud/CrudEditDrawer.svelte.d.ts +48 -0
  161. package/dist/components/scenes/crud/CrudFilterBar.svelte +96 -0
  162. package/dist/components/scenes/crud/CrudFilterBar.svelte.d.ts +31 -0
  163. package/dist/components/scenes/crud/CrudPage.svelte +499 -0
  164. package/dist/components/scenes/crud/CrudPage.svelte.d.ts +114 -0
  165. package/dist/components/scenes/crud/crud-types.d.ts +135 -0
  166. package/dist/components/scenes/crud/crud-types.js +7 -0
  167. package/dist/components/scenes/crud/index.d.ts +12 -0
  168. package/dist/components/scenes/crud/index.js +11 -0
  169. package/dist/components/scenes/crypto/EncryptedInput.svelte +156 -0
  170. package/dist/components/scenes/crypto/EncryptedInput.svelte.d.ts +32 -0
  171. package/dist/components/scenes/crypto/HashDisplay.svelte +97 -0
  172. package/dist/components/scenes/crypto/HashDisplay.svelte.d.ts +4 -0
  173. package/dist/components/scenes/crypto/SignatureDisplay.svelte +161 -0
  174. package/dist/components/scenes/crypto/SignatureDisplay.svelte.d.ts +4 -0
  175. package/dist/components/scenes/crypto/index.d.ts +9 -0
  176. package/dist/components/scenes/crypto/index.js +11 -0
  177. package/dist/components/scenes/iam/ChangePasswordForm.svelte +134 -0
  178. package/dist/components/scenes/iam/ChangePasswordForm.svelte.d.ts +8 -0
  179. package/dist/components/scenes/iam/ForgotPasswordForm.svelte +147 -0
  180. package/dist/components/scenes/iam/ForgotPasswordForm.svelte.d.ts +4 -0
  181. package/dist/components/scenes/iam/LoginForm.svelte +191 -0
  182. package/dist/components/scenes/iam/LoginForm.svelte.d.ts +8 -0
  183. package/dist/components/scenes/iam/PasswordInput.svelte +203 -0
  184. package/dist/components/scenes/iam/PasswordInput.svelte.d.ts +49 -0
  185. package/dist/components/scenes/iam/PermGuard.svelte +69 -0
  186. package/dist/components/scenes/iam/PermGuard.svelte.d.ts +16 -0
  187. package/dist/components/scenes/iam/RegisterForm.svelte +269 -0
  188. package/dist/components/scenes/iam/RegisterForm.svelte.d.ts +8 -0
  189. package/dist/components/scenes/iam/ResetPasswordForm.svelte +153 -0
  190. package/dist/components/scenes/iam/ResetPasswordForm.svelte.d.ts +8 -0
  191. package/dist/components/scenes/iam/UserProfile.svelte +251 -0
  192. package/dist/components/scenes/iam/UserProfile.svelte.d.ts +4 -0
  193. package/dist/components/scenes/iam/index.d.ts +16 -0
  194. package/dist/components/scenes/iam/index.js +18 -0
  195. package/dist/components/scenes/iam/password-utils.d.ts +15 -0
  196. package/dist/components/scenes/iam/password-utils.js +19 -0
  197. package/dist/components/scenes/iam/permission-context.svelte.d.ts +90 -0
  198. package/dist/components/scenes/iam/permission-context.svelte.js +114 -0
  199. package/dist/components/scenes/index.d.ts +13 -0
  200. package/dist/components/scenes/index.js +20 -0
  201. package/dist/components/scenes/storage/AvatarUpload.svelte +218 -0
  202. package/dist/components/scenes/storage/AvatarUpload.svelte.d.ts +4 -0
  203. package/dist/components/scenes/storage/FileList.svelte +260 -0
  204. package/dist/components/scenes/storage/FileList.svelte.d.ts +4 -0
  205. package/dist/components/scenes/storage/FileUpload.svelte +400 -0
  206. package/dist/components/scenes/storage/FileUpload.svelte.d.ts +7 -0
  207. package/dist/components/scenes/storage/ImageUpload.svelte +285 -0
  208. package/dist/components/scenes/storage/ImageUpload.svelte.d.ts +4 -0
  209. package/dist/components/scenes/storage/index.d.ts +10 -0
  210. package/dist/components/scenes/storage/index.js +12 -0
  211. package/dist/components/scenes/types.d.ts +583 -0
  212. package/dist/components/scenes/types.js +7 -0
  213. package/dist/i18n.svelte.d.ts +44 -0
  214. package/dist/i18n.svelte.js +108 -0
  215. package/dist/index.d.ts +13 -0
  216. package/dist/index.js +19 -0
  217. package/dist/messages/en-US.json +242 -0
  218. package/dist/messages/zh-CN.json +242 -0
  219. package/dist/messages.d.ts +29 -0
  220. package/dist/messages.js +42 -0
  221. package/dist/styles/design-tokens.css +58 -0
  222. package/dist/styles/global.css +218 -0
  223. package/dist/styles/mobile.css +105 -0
  224. package/dist/theme-config.d.ts +92 -0
  225. package/dist/theme-config.js +122 -0
  226. package/dist/toast.svelte.d.ts +45 -0
  227. package/dist/toast.svelte.js +77 -0
  228. package/dist/types.d.ts +944 -0
  229. package/dist/types.js +7 -0
  230. package/dist/utils/platform.svelte.d.ts +54 -0
  231. package/dist/utils/platform.svelte.js +87 -0
  232. package/dist/utils.d.ts +46 -0
  233. package/dist/utils.js +100 -0
  234. package/package.json +69 -0
@@ -0,0 +1,251 @@
1
+ <!--
2
+ =============================================================================
3
+ @h-ai/ui - UserProfile 组件
4
+ =============================================================================
5
+ 用户个人信息展示/编辑组件
6
+
7
+ 使用 Svelte 5 Runes ($props, $state, $derived)
8
+ 使用 primitives/compounds 组件:Input, Button, Textarea, Avatar, Alert
9
+ =============================================================================
10
+ -->
11
+ <script lang='ts'>
12
+ import type { UserProfileField, UserProfileProps, UserProfileSubmitData } from '../types.js'
13
+ import { uiM } from '../../../messages.js'
14
+ import { cn } from '../../../utils.js'
15
+ import Alert from '../../compounds/Alert.svelte'
16
+ import Avatar from '../../primitives/Avatar.svelte'
17
+ import BareInput from '../../primitives/BareInput.svelte'
18
+ import Button from '../../primitives/Button.svelte'
19
+ import Input from '../../primitives/Input.svelte'
20
+ import Textarea from '../../primitives/Textarea.svelte'
21
+
22
+ const {
23
+ user,
24
+ editable = false,
25
+ alwaysEditable = false,
26
+ loading = false,
27
+ fields = ['avatar', 'username', 'email', 'displayName', 'phone'],
28
+ class: className = '',
29
+ errors = {},
30
+ onsubmit,
31
+ onavatarchange,
32
+ }: UserProfileProps = $props()
33
+
34
+ let editMode = $state(false)
35
+ let formData = $state<UserProfileSubmitData>({
36
+ username: '',
37
+ email: '',
38
+ displayName: '',
39
+ phone: '',
40
+ bio: '',
41
+ })
42
+
43
+ // 初始化表单数据
44
+ $effect(() => {
45
+ if (user) {
46
+ formData = {
47
+ username: user.username || '',
48
+ email: user.email || '',
49
+ displayName: user.displayName || user.nickname || '',
50
+ phone: user.phone || '',
51
+ bio: user.bio || '',
52
+ }
53
+ }
54
+ })
55
+
56
+ const containerClass = $derived(
57
+ cn(
58
+ 'user-profile',
59
+ className,
60
+ ),
61
+ )
62
+
63
+ // 获取字段标签
64
+ function getFieldLabel(field: UserProfileField): string {
65
+ const labelMap: Record<UserProfileField, () => string> = {
66
+ avatar: () => uiM('user_profile_avatar'),
67
+ username: () => uiM('user_profile_username'),
68
+ email: () => uiM('user_profile_email'),
69
+ nickname: () => uiM('user_profile_nickname'),
70
+ displayName: () => uiM('user_profile_display_name'),
71
+ phone: () => uiM('user_profile_phone'),
72
+ bio: () => uiM('user_profile_bio'),
73
+ }
74
+ return labelMap[field]?.() || field
75
+ }
76
+
77
+ /**
78
+ * 将展示字段名映射为提交数据字段名。
79
+ *
80
+ * @param field 展示字段名
81
+ * @returns 提交数据中的字段名
82
+ */
83
+ function getModelKey(field: UserProfileField): keyof UserProfileSubmitData {
84
+ if (field === 'nickname' || field === 'displayName') {
85
+ return 'displayName'
86
+ }
87
+ if (field === 'email') {
88
+ return 'email'
89
+ }
90
+ if (field === 'phone') {
91
+ return 'phone'
92
+ }
93
+ if (field === 'bio') {
94
+ return 'bio'
95
+ }
96
+ return 'username'
97
+ }
98
+
99
+ /**
100
+ * 进入编辑态(仅 alwaysEditable=false 时生效)。
101
+ */
102
+ function startEdit() {
103
+ editMode = true
104
+ }
105
+
106
+ /**
107
+ * 退出编辑态,并使用当前用户数据重置表单。
108
+ */
109
+ function cancelEdit() {
110
+ editMode = false
111
+ // 重置表单数据
112
+ if (user) {
113
+ formData = {
114
+ username: user.username || '',
115
+ email: user.email || '',
116
+ displayName: user.displayName || user.nickname || '',
117
+ phone: user.phone || '',
118
+ bio: user.bio || '',
119
+ }
120
+ }
121
+ }
122
+
123
+ /**
124
+ * 提交资料数据,成功后退出编辑态。
125
+ *
126
+ * @param e 表单提交事件
127
+ * @returns 无返回值
128
+ */
129
+ async function handleSubmit(e: SubmitEvent) {
130
+ e.preventDefault()
131
+ if (loading)
132
+ return
133
+
134
+ await onsubmit?.(formData)
135
+ editMode = false
136
+ }
137
+
138
+ /**
139
+ * 将选中的头像文件透传给父组件回调。
140
+ *
141
+ * @param e 文件输入事件
142
+ */
143
+ function handleAvatarChange(e: Event & { currentTarget: HTMLInputElement }) {
144
+ const file = e.currentTarget.files?.[0]
145
+ if (file) {
146
+ onavatarchange?.(file)
147
+ }
148
+ }
149
+ </script>
150
+
151
+ <div class={containerClass}>
152
+ <form onsubmit={handleSubmit}>
153
+ <div class='space-y-6'>
154
+ {#each fields as field (field)}
155
+ {@const modelKey = getModelKey(field)}
156
+ {#if field === 'avatar'}
157
+ <!-- 头像 -->
158
+ <div class='flex items-center gap-4'>
159
+ <Avatar
160
+ src={user?.avatarUrl ?? user?.avatar}
161
+ alt={uiM('user_profile_avatar')}
162
+ size='lg'
163
+ ring
164
+ class='shadow-sm'
165
+ placeholder={user?.username?.charAt(0).toUpperCase() || 'U'}
166
+ />
167
+ {#if editable && (editMode || alwaysEditable)}
168
+ <div>
169
+ <BareInput
170
+ type='file'
171
+ class='file-input file-input-sm w-full max-w-xs'
172
+ accept='image/*'
173
+ onchange={handleAvatarChange}
174
+ />
175
+ <p class='text-xs text-base-content/60 mt-1'>{uiM('user_profile_avatar_hint')}</p>
176
+ </div>
177
+ {/if}
178
+ </div>
179
+ {:else}
180
+ <!-- 其他字段 -->
181
+ <div class='fieldset'>
182
+ <legend class='fieldset-legend font-medium'>{getFieldLabel(field)}</legend>
183
+ {#if editable && (editMode || alwaysEditable)}
184
+ {#if field === 'bio'}
185
+ <Textarea
186
+ name={modelKey}
187
+ rows={3}
188
+ bind:value={formData[modelKey]}
189
+ disabled={loading}
190
+ error={errors[field]}
191
+ />
192
+ {:else}
193
+ <Input
194
+ type={field === 'email' ? 'email' : field === 'phone' ? 'tel' : 'text'}
195
+ name={modelKey}
196
+ bind:value={formData[modelKey]}
197
+ disabled={loading}
198
+ error={errors[field]}
199
+ />
200
+ {/if}
201
+ {:else}
202
+ <p class='py-2 px-1 text-base-content'>
203
+ {formData[modelKey] || '-'}
204
+ </p>
205
+ {/if}
206
+ </div>
207
+ {/if}
208
+ {/each}
209
+
210
+ <!-- 通用错误 -->
211
+ {#if errors.general}
212
+ <Alert variant='error'>
213
+ {errors.general}
214
+ </Alert>
215
+ {/if}
216
+
217
+ <!-- 操作按钮 -->
218
+ {#if editable}
219
+ <div class='flex gap-2 pt-4'>
220
+ {#if editMode || alwaysEditable}
221
+ <Button
222
+ type='submit'
223
+ variant='primary'
224
+ {loading}
225
+ disabled={loading}
226
+ >
227
+ {uiM('user_profile_save')}
228
+ </Button>
229
+ <Button
230
+ type='button'
231
+ variant='ghost'
232
+ onclick={cancelEdit}
233
+ disabled={loading}
234
+ >
235
+ {uiM('user_profile_cancel')}
236
+ </Button>
237
+ {/if}
238
+ {#if !editMode && !alwaysEditable}
239
+ <Button
240
+ type='button'
241
+ variant='outline'
242
+ onclick={startEdit}
243
+ >
244
+ {uiM('user_profile_edit')}
245
+ </Button>
246
+ {/if}
247
+ </div>
248
+ {/if}
249
+ </div>
250
+ </form>
251
+ </div>
@@ -0,0 +1,4 @@
1
+ import type { UserProfileProps } from '../types.js';
2
+ declare const UserProfile: import("svelte").Component<UserProfileProps, {}, "">;
3
+ type UserProfile = ReturnType<typeof UserProfile>;
4
+ export default UserProfile;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @h-ai/ui — IAM 场景组件导出
3
+ *
4
+ * 身份认证相关的场景化组件
5
+ * @module index
6
+ */
7
+ export { default as ChangePasswordForm } from './ChangePasswordForm.svelte';
8
+ export { default as ForgotPasswordForm } from './ForgotPasswordForm.svelte';
9
+ export { default as LoginForm } from './LoginForm.svelte';
10
+ export { default as PasswordInput } from './PasswordInput.svelte';
11
+ export { default as PermGuard } from './PermGuard.svelte';
12
+ export { matchPermission, setPermissionContext, usePermission, } from './permission-context.svelte.js';
13
+ export type { PermissionContext } from './permission-context.svelte.js';
14
+ export { default as RegisterForm } from './RegisterForm.svelte';
15
+ export { default as ResetPasswordForm } from './ResetPasswordForm.svelte';
16
+ export { default as UserProfile } from './UserProfile.svelte';
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @h-ai/ui — IAM 场景组件导出
3
+ *
4
+ * 身份认证相关的场景化组件
5
+ * @module index
6
+ */
7
+ export { default as ChangePasswordForm } from './ChangePasswordForm.svelte';
8
+ export { default as ForgotPasswordForm } from './ForgotPasswordForm.svelte';
9
+ // 认证表单
10
+ export { default as LoginForm } from './LoginForm.svelte';
11
+ export { default as PasswordInput } from './PasswordInput.svelte';
12
+ // 权限控制
13
+ export { default as PermGuard } from './PermGuard.svelte';
14
+ export { matchPermission, setPermissionContext, usePermission, } from './permission-context.svelte.js';
15
+ export { default as RegisterForm } from './RegisterForm.svelte';
16
+ export { default as ResetPasswordForm } from './ResetPasswordForm.svelte';
17
+ // 用户信息
18
+ export { default as UserProfile } from './UserProfile.svelte';
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @h-ai/ui — IAM 密码工具
3
+ *
4
+ * 密码规范化与一致性比较工具
5
+ * @module password-utils
6
+ */
7
+ /**
8
+ * 规范化密码字符串
9
+ * - 使用 NFKC 统一兼容字符
10
+ */
11
+ export declare function normalizePassword(value: string): string;
12
+ /**
13
+ * 比较两次密码是否一致
14
+ */
15
+ export declare function arePasswordsEqual(password: string, confirmPassword: string): boolean;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @h-ai/ui — IAM 密码工具
3
+ *
4
+ * 密码规范化与一致性比较工具
5
+ * @module password-utils
6
+ */
7
+ /**
8
+ * 规范化密码字符串
9
+ * - 使用 NFKC 统一兼容字符
10
+ */
11
+ export function normalizePassword(value) {
12
+ return value.normalize('NFKC');
13
+ }
14
+ /**
15
+ * 比较两次密码是否一致
16
+ */
17
+ export function arePasswordsEqual(password, confirmPassword) {
18
+ return normalizePassword(password) === normalizePassword(confirmPassword);
19
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * @h-ai/ui — 权限上下文(Svelte 5)
3
+ *
4
+ * 提供基于 Svelte Context 的权限判断能力,支持精确匹配与通配符匹配。
5
+ * 在根布局中通过 `setPermissionContext` 注入,子组件通过 `usePermission` 消费。
6
+ *
7
+ * @example
8
+ * ```svelte
9
+ * <!-- +layout.svelte -->
10
+ * <script>
11
+ * import { setPermissionContext } from '@h-ai/ui'
12
+ * setPermissionContext(() => data.user?.permissions ?? [])
13
+ * </script>
14
+ *
15
+ * <!-- 子组件 -->
16
+ * <script>
17
+ * import { usePermission } from '@h-ai/ui'
18
+ * const { hasPerm, hasAnyPerm, hasAllPerms } = usePermission()
19
+ * </script>
20
+ * {#if hasPerm('user:create')}
21
+ * <button>新建用户</button>
22
+ * {/if}
23
+ * ```
24
+ *
25
+ * @module permission-context.svelte
26
+ */
27
+ /**
28
+ * 判断单个权限是否匹配用户权限列表
29
+ *
30
+ * 支持三种匹配模式:
31
+ * - 精确匹配:`user:create` === `user:create`
32
+ * - 超级权限:`*` 匹配一切
33
+ * - 通配符前缀:`user:*` 匹配 `user:create`、`user:delete` 等
34
+ *
35
+ * @param required - 需要检查的权限码
36
+ * @param userPermissions - 用户拥有的权限列表
37
+ * @returns 是否匹配
38
+ */
39
+ export declare function matchPermission(required: string, userPermissions: string[]): boolean;
40
+ /** 权限上下文消费接口 */
41
+ export interface PermissionContext {
42
+ /** 当前用户权限列表(响应式) */
43
+ readonly permissions: string[];
44
+ /** 判断是否拥有指定权限 */
45
+ hasPerm: (permission: string) => boolean;
46
+ /** 判断是否拥有任一权限 */
47
+ hasAnyPerm: (...permissions: string[]) => boolean;
48
+ /** 判断是否拥有全部权限 */
49
+ hasAllPerms: (...permissions: string[]) => boolean;
50
+ }
51
+ /**
52
+ * 在组件树中注入权限上下文
53
+ *
54
+ * 应在根布局或需要权限控制的最外层组件中调用。
55
+ * `getPermissions` 参数应返回响应式数据(如 `$derived` 值),
56
+ * 以便权限变化时自动触发子组件更新。
57
+ *
58
+ * @param getPermissions - 返回当前用户权限列表的函数
59
+ *
60
+ * @example
61
+ * ```svelte
62
+ * <script>
63
+ * import { setPermissionContext } from '@h-ai/ui'
64
+ * let { data } = $props()
65
+ * const userPermissions = $derived(data.user?.permissions ?? [])
66
+ * setPermissionContext(() => userPermissions)
67
+ * </script>
68
+ * ```
69
+ */
70
+ export declare function setPermissionContext(getPermissions: () => string[]): void;
71
+ /**
72
+ * 在子组件中消费权限上下文
73
+ *
74
+ * 返回一组权限判断工具函数,基于 `setPermissionContext` 注入的数据。
75
+ * 若未注入上下文,所有权限检查均返回 `false`。
76
+ *
77
+ * @returns 权限上下文消费接口
78
+ *
79
+ * @example
80
+ * ```svelte
81
+ * <script>
82
+ * import { usePermission } from '@h-ai/ui'
83
+ * const { hasPerm } = usePermission()
84
+ * </script>
85
+ * {#if hasPerm('role:create')}
86
+ * <button>创建角色</button>
87
+ * {/if}
88
+ * ```
89
+ */
90
+ export declare function usePermission(): PermissionContext;
@@ -0,0 +1,114 @@
1
+ /**
2
+ * @h-ai/ui — 权限上下文(Svelte 5)
3
+ *
4
+ * 提供基于 Svelte Context 的权限判断能力,支持精确匹配与通配符匹配。
5
+ * 在根布局中通过 `setPermissionContext` 注入,子组件通过 `usePermission` 消费。
6
+ *
7
+ * @example
8
+ * ```svelte
9
+ * <!-- +layout.svelte -->
10
+ * <script>
11
+ * import { setPermissionContext } from '@h-ai/ui'
12
+ * setPermissionContext(() => data.user?.permissions ?? [])
13
+ * </script>
14
+ *
15
+ * <!-- 子组件 -->
16
+ * <script>
17
+ * import { usePermission } from '@h-ai/ui'
18
+ * const { hasPerm, hasAnyPerm, hasAllPerms } = usePermission()
19
+ * </script>
20
+ * {#if hasPerm('user:create')}
21
+ * <button>新建用户</button>
22
+ * {/if}
23
+ * ```
24
+ *
25
+ * @module permission-context.svelte
26
+ */
27
+ import { getContext, setContext } from 'svelte';
28
+ // ─── Context Key ───
29
+ const PERMISSION_CONTEXT_KEY = Symbol('hai-permission-context');
30
+ // ─── 权限匹配(纯函数) ───
31
+ /**
32
+ * 判断单个权限是否匹配用户权限列表
33
+ *
34
+ * 支持三种匹配模式:
35
+ * - 精确匹配:`user:create` === `user:create`
36
+ * - 超级权限:`*` 匹配一切
37
+ * - 通配符前缀:`user:*` 匹配 `user:create`、`user:delete` 等
38
+ *
39
+ * @param required - 需要检查的权限码
40
+ * @param userPermissions - 用户拥有的权限列表
41
+ * @returns 是否匹配
42
+ */
43
+ export function matchPermission(required, userPermissions) {
44
+ for (const userPerm of userPermissions) {
45
+ if (userPerm === required || userPerm === '*')
46
+ return true;
47
+ if (userPerm.endsWith(':*') && required.startsWith(userPerm.slice(0, -1)))
48
+ return true;
49
+ }
50
+ return false;
51
+ }
52
+ // ─── Provider / Consumer ───
53
+ /**
54
+ * 在组件树中注入权限上下文
55
+ *
56
+ * 应在根布局或需要权限控制的最外层组件中调用。
57
+ * `getPermissions` 参数应返回响应式数据(如 `$derived` 值),
58
+ * 以便权限变化时自动触发子组件更新。
59
+ *
60
+ * @param getPermissions - 返回当前用户权限列表的函数
61
+ *
62
+ * @example
63
+ * ```svelte
64
+ * <script>
65
+ * import { setPermissionContext } from '@h-ai/ui'
66
+ * let { data } = $props()
67
+ * const userPermissions = $derived(data.user?.permissions ?? [])
68
+ * setPermissionContext(() => userPermissions)
69
+ * </script>
70
+ * ```
71
+ */
72
+ export function setPermissionContext(getPermissions) {
73
+ setContext(PERMISSION_CONTEXT_KEY, { getPermissions });
74
+ }
75
+ /**
76
+ * 在子组件中消费权限上下文
77
+ *
78
+ * 返回一组权限判断工具函数,基于 `setPermissionContext` 注入的数据。
79
+ * 若未注入上下文,所有权限检查均返回 `false`。
80
+ *
81
+ * @returns 权限上下文消费接口
82
+ *
83
+ * @example
84
+ * ```svelte
85
+ * <script>
86
+ * import { usePermission } from '@h-ai/ui'
87
+ * const { hasPerm } = usePermission()
88
+ * </script>
89
+ * {#if hasPerm('role:create')}
90
+ * <button>创建角色</button>
91
+ * {/if}
92
+ * ```
93
+ */
94
+ export function usePermission() {
95
+ const ctx = getContext(PERMISSION_CONTEXT_KEY);
96
+ /** 获取权限列表,未注入上下文时返回空数组 */
97
+ const getPerms = () => ctx?.getPermissions() ?? [];
98
+ return {
99
+ get permissions() {
100
+ return getPerms();
101
+ },
102
+ hasPerm(permission) {
103
+ return matchPermission(permission, getPerms());
104
+ },
105
+ hasAnyPerm(...permissions) {
106
+ const perms = getPerms();
107
+ return permissions.some(p => matchPermission(p, perms));
108
+ },
109
+ hasAllPerms(...permissions) {
110
+ const perms = getPerms();
111
+ return permissions.every(p => matchPermission(p, perms));
112
+ },
113
+ };
114
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @h-ai/ui — 场景组件 (Scenes)
3
+ *
4
+ * 面向具体业务场景的完整 UI 流程,通常对应一个完整的用户任务。
5
+ * @module index
6
+ */
7
+ export * from './ai/index.js';
8
+ export * from './app/index.js';
9
+ export * from './crud/index.js';
10
+ export * from './crypto/index.js';
11
+ export * from './iam/index.js';
12
+ export * from './storage/index.js';
13
+ export * from './types.js';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @h-ai/ui — 场景组件 (Scenes)
3
+ *
4
+ * 面向具体业务场景的完整 UI 流程,通常对应一个完整的用户任务。
5
+ * @module index
6
+ */
7
+ // AI 场景组件
8
+ export * from './ai/index.js';
9
+ // App 应用级场景组件
10
+ export * from './app/index.js';
11
+ // CRUD 场景组件
12
+ export * from './crud/index.js';
13
+ // Crypto 场景组件
14
+ export * from './crypto/index.js';
15
+ // IAM 场景组件
16
+ export * from './iam/index.js';
17
+ // Storage 场景组件
18
+ export * from './storage/index.js';
19
+ // 场景组件类型
20
+ export * from './types.js';