@h-ai/ui 0.1.0-alpha.22 → 0.1.0-alpha.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -5
- package/auto-import.d.ts +2 -2
- package/auto-import.js +2 -2
- package/dist/components/compounds/DataTable.svelte +4 -1
- package/dist/components/compounds/DataTable.svelte.d.ts +2 -0
- package/dist/components/compounds/Drawer.svelte +7 -2
- package/dist/components/compounds/Modal.svelte +2 -0
- package/dist/components/compounds/Pagination.svelte +35 -8
- package/dist/components/scenes/crud/{CrudDetailDrawer.svelte → CrudDetailPanel.svelte} +60 -23
- package/dist/components/scenes/crud/{CrudDetailDrawer.svelte.d.ts → CrudDetailPanel.svelte.d.ts} +12 -6
- package/dist/components/scenes/crud/CrudEditPanel.svelte +265 -0
- package/dist/components/scenes/crud/{CrudEditDrawer.svelte.d.ts → CrudEditPanel.svelte.d.ts} +12 -6
- package/dist/components/scenes/crud/CrudPage.svelte +84 -49
- package/dist/components/scenes/crud/CrudPage.svelte.d.ts +6 -4
- package/dist/components/scenes/crud/crud-types.d.ts +36 -2
- package/dist/components/scenes/crud/index.d.ts +3 -3
- package/dist/components/scenes/crud/index.js +3 -3
- package/dist/messages/en-US.json +3 -2
- package/dist/messages/zh-CN.json +3 -2
- package/dist/types.d.ts +10 -0
- package/package.json +2 -2
- package/dist/components/scenes/crud/CrudEditDrawer.svelte +0 -225
package/README.md
CHANGED
|
@@ -274,11 +274,21 @@ components/
|
|
|
274
274
|
|
|
275
275
|
| 组件 | 描述 | 主要属性 / 能力 |
|
|
276
276
|
| -------------------- | ------------ | ---------------- |
|
|
277
|
-
| `CrudPage` | CRUD 主页面 | `
|
|
278
|
-
| `CrudFilterBar` | 过滤工具栏 | `
|
|
279
|
-
| `
|
|
280
|
-
| `
|
|
281
|
-
| `CrudDeleteConfirm` | 删除确认框 | `open`, `
|
|
277
|
+
| `CrudPage` | CRUD 主页面 | `crud`, `data`, `permissions`, `form`, `pagination`, `density`, `nav` |
|
|
278
|
+
| `CrudFilterBar` | 过滤工具栏 | `filterFields`, `searchValue`, `onsearch`, `onfilterchange` |
|
|
279
|
+
| `CrudDetailPanel` | 详情面板(抽屉/弹窗) | `open`, `item`, `fields`, `variant`, `drawerWidth`, `modalSize` |
|
|
280
|
+
| `CrudEditPanel` | 编辑面板(抽屉/弹窗) | `open`, `fields`, `variant`, `drawerWidth`, `modalSize`, `onsubmit` |
|
|
281
|
+
| `CrudDeleteConfirm` | 删除确认框 | `open`, `loading`, `onconfirm` |
|
|
282
|
+
|
|
283
|
+
> `CrudPage` 通过 `form` 配置新建/编辑的展示形式:
|
|
284
|
+
>
|
|
285
|
+
> - `form.variant`:`'drawer'`(抽屉,默认)或 `'modal'`(弹出窗口)。
|
|
286
|
+
> - 抽屉:`form.drawerSize` 选预设尺寸,`form.drawerWidth` 传任意 CSS 宽度(优先级更高)。
|
|
287
|
+
> - 弹窗:`form.modalSize` 选预设尺寸,`form.modalWidth` / `form.modalHeight` 传任意 CSS 尺寸。
|
|
288
|
+
>
|
|
289
|
+
> `pagination` 配置分页栏(始终显示):`showSizeChanger`、`pageSizeOptions`、`showJumper`、`showTotal` 默认开启。
|
|
290
|
+
>
|
|
291
|
+
> `density` 配置列表密度:`'normal'`(默认)或 `'compact'`(紧凑)。
|
|
282
292
|
|
|
283
293
|
## 使用示例
|
|
284
294
|
|
|
@@ -368,6 +378,30 @@ components/
|
|
|
368
378
|
<ToastContainer />
|
|
369
379
|
```
|
|
370
380
|
|
|
381
|
+
### 声明式 CRUD 页面(CrudPage)
|
|
382
|
+
|
|
383
|
+
```svelte
|
|
384
|
+
<script lang='ts'>
|
|
385
|
+
import { createSvelteKitNavAdapter } from '@h-ai/kit/client'
|
|
386
|
+
import { CrudPage } from '@h-ai/ui'
|
|
387
|
+
|
|
388
|
+
let { data } = $props()
|
|
389
|
+
const nav = createSvelteKitNavAdapter()
|
|
390
|
+
</script>
|
|
391
|
+
|
|
392
|
+
<CrudPage
|
|
393
|
+
crud={roleCrud}
|
|
394
|
+
{data}
|
|
395
|
+
permissions={{ create: true, update: true, delete: true }}
|
|
396
|
+
form={{ variant: 'modal', modalSize: 'lg' }}
|
|
397
|
+
pagination={{ showSizeChanger: true, showJumper: true, pageSizeOptions: [10, 20, 50] }}
|
|
398
|
+
density='compact'
|
|
399
|
+
{nav}
|
|
400
|
+
/>
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
> 抽屉形式可改用 `form={{ variant: 'drawer', drawerWidth: '40rem' }}`。
|
|
404
|
+
|
|
371
405
|
### 登录页面
|
|
372
406
|
|
|
373
407
|
```svelte
|
package/auto-import.d.ts
CHANGED
|
@@ -110,8 +110,8 @@ declare global {
|
|
|
110
110
|
|
|
111
111
|
// scenes - crud
|
|
112
112
|
const CrudDeleteConfirm: typeof import('@h-ai/ui')['CrudDeleteConfirm']
|
|
113
|
-
const
|
|
114
|
-
const
|
|
113
|
+
const CrudDetailPanel: typeof import('@h-ai/ui')['CrudDetailPanel']
|
|
114
|
+
const CrudEditPanel: typeof import('@h-ai/ui')['CrudEditPanel']
|
|
115
115
|
const CrudFilterBar: typeof import('@h-ai/ui')['CrudFilterBar']
|
|
116
116
|
const CrudPage: typeof import('@h-ai/ui')['CrudPage']
|
|
117
117
|
}
|
package/auto-import.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
@prop {boolean} loading - 是否加载中
|
|
11
11
|
@prop {boolean} striped - 是否斑马纹
|
|
12
12
|
@prop {boolean} hoverable - 是否悬停高亮
|
|
13
|
+
@prop {'normal' | 'compact'} density - 显示密度,compact 为紧凑布局
|
|
13
14
|
|
|
14
15
|
@example
|
|
15
16
|
<DataTable
|
|
@@ -39,6 +40,7 @@
|
|
|
39
40
|
loading = false,
|
|
40
41
|
striped = true,
|
|
41
42
|
hoverable = true,
|
|
43
|
+
density = 'normal',
|
|
42
44
|
class: className = '',
|
|
43
45
|
}: {
|
|
44
46
|
data: T[]
|
|
@@ -49,6 +51,7 @@
|
|
|
49
51
|
loading?: boolean
|
|
50
52
|
striped?: boolean
|
|
51
53
|
hoverable?: boolean
|
|
54
|
+
density?: 'normal' | 'compact'
|
|
52
55
|
class?: string
|
|
53
56
|
} = $props()
|
|
54
57
|
|
|
@@ -79,7 +82,7 @@
|
|
|
79
82
|
</script>
|
|
80
83
|
|
|
81
84
|
<div class='overflow-x-auto {className}'>
|
|
82
|
-
<table class='table w-full text-[13px]' class:table-zebra={striped}>
|
|
85
|
+
<table class='table w-full {density === `compact` ? `table-sm text-xs` : `text-[13px]`}' class:table-zebra={striped}>
|
|
83
86
|
<thead>
|
|
84
87
|
<tr class='text-xs text-base-content/50 border-b border-base-content/6'>
|
|
85
88
|
{#each columns as col (String(col.key))}
|
|
@@ -15,6 +15,7 @@ declare function $$render<T>(): {
|
|
|
15
15
|
loading?: boolean;
|
|
16
16
|
striped?: boolean;
|
|
17
17
|
hoverable?: boolean;
|
|
18
|
+
density?: "normal" | "compact";
|
|
18
19
|
class?: string;
|
|
19
20
|
};
|
|
20
21
|
exports: {};
|
|
@@ -48,6 +49,7 @@ interface $$IsomorphicComponent {
|
|
|
48
49
|
* @prop {boolean} loading - 是否加载中
|
|
49
50
|
* @prop {boolean} striped - 是否斑马纹
|
|
50
51
|
* @prop {boolean} hoverable - 是否悬停高亮
|
|
52
|
+
* @prop {'normal' | 'compact'} density - 显示密度,compact 为紧凑布局
|
|
51
53
|
*
|
|
52
54
|
* @example
|
|
53
55
|
* <DataTable
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
title = '',
|
|
21
21
|
position = 'right',
|
|
22
22
|
size = 'md',
|
|
23
|
+
width,
|
|
23
24
|
closeOnBackdrop = true,
|
|
24
25
|
showClose = true,
|
|
25
26
|
class: className = '',
|
|
@@ -40,6 +41,10 @@
|
|
|
40
41
|
'4xl': 'w-[42rem]',
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
// width 优先:传入自定义 CSS 宽度时通过 inline style 覆盖预设宽度类
|
|
45
|
+
const hasCustomWidth = $derived(typeof width === 'string' && width.trim().length > 0)
|
|
46
|
+
const widthStyle = $derived(hasCustomWidth ? `width: ${width!.trim()}; max-width: 100vw` : undefined)
|
|
47
|
+
|
|
43
48
|
const drawerClass = $derived(
|
|
44
49
|
cn(
|
|
45
50
|
'drawer',
|
|
@@ -56,7 +61,7 @@
|
|
|
56
61
|
const contentClass = $derived(
|
|
57
62
|
cn(
|
|
58
63
|
'menu bg-base-200 text-base-content min-h-full p-4',
|
|
59
|
-
sizeMap[size],
|
|
64
|
+
!hasCustomWidth && sizeMap[size],
|
|
60
65
|
className,
|
|
61
66
|
),
|
|
62
67
|
)
|
|
@@ -86,7 +91,7 @@
|
|
|
86
91
|
onkeydown={e => e.key === 'Enter' && handleBackdropClick()}
|
|
87
92
|
></div>
|
|
88
93
|
|
|
89
|
-
<div class={contentClass}>
|
|
94
|
+
<div class={contentClass} style={widthStyle}>
|
|
90
95
|
<div class='flex items-center justify-between mb-4'>
|
|
91
96
|
{#if title}
|
|
92
97
|
<h3 class='text-lg font-bold'>{title}</h3>
|
|
@@ -210,6 +210,7 @@
|
|
|
210
210
|
max-height: calc(100vh - clamp(2rem, 5.2vw, 4rem));
|
|
211
211
|
max-height: calc(100dvh - clamp(2rem, 5.2vw, 4rem));
|
|
212
212
|
border-radius: 1.4rem;
|
|
213
|
+
overflow: hidden;
|
|
213
214
|
color: var(--color-base-content);
|
|
214
215
|
outline: none;
|
|
215
216
|
box-shadow:
|
|
@@ -224,6 +225,7 @@
|
|
|
224
225
|
|
|
225
226
|
.hai-modal__footer {
|
|
226
227
|
box-shadow: inset 0 1px 0 color-mix(in srgb, var(--color-base-content) 6%, transparent);
|
|
228
|
+
border-radius: 0 0 1.4rem 1.4rem;
|
|
227
229
|
}
|
|
228
230
|
|
|
229
231
|
.hai-modal__close {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import BareButton from '../primitives/BareButton.svelte'
|
|
15
15
|
|
|
16
16
|
import Input from '../primitives/Input.svelte'
|
|
17
|
+
import Select from '../primitives/Select.svelte'
|
|
17
18
|
|
|
18
19
|
let {
|
|
19
20
|
page = $bindable(1),
|
|
@@ -22,15 +23,18 @@
|
|
|
22
23
|
size = 'md',
|
|
23
24
|
showTotal = true,
|
|
24
25
|
showJumper = false,
|
|
26
|
+
showSizeChanger = false,
|
|
27
|
+
pageSizeOptions = [10, 20, 50, 100],
|
|
25
28
|
labels = {},
|
|
26
29
|
class: className = '',
|
|
27
30
|
onchange,
|
|
31
|
+
onpagesizechange,
|
|
28
32
|
}: PaginationProps = $props()
|
|
29
33
|
|
|
30
34
|
// labels 优先,缺省回退到内置消息 uiM(...)
|
|
31
35
|
|
|
32
|
-
//
|
|
33
|
-
const totalPages = $derived(Math.ceil(total / pageSize))
|
|
36
|
+
// 计算总页数(至少 1 页,保证分页栏在空数据时仍可渲染)
|
|
37
|
+
const totalPages = $derived(Math.max(1, Math.ceil(total / pageSize)))
|
|
34
38
|
|
|
35
39
|
// 生成页码列表
|
|
36
40
|
const pages = $derived(() => {
|
|
@@ -99,15 +103,38 @@
|
|
|
99
103
|
jumperValue = ''
|
|
100
104
|
}
|
|
101
105
|
}
|
|
106
|
+
|
|
107
|
+
function handlePageSizeChange(value: string) {
|
|
108
|
+
const next = Number.parseInt(value, 10)
|
|
109
|
+
if (!Number.isNaN(next) && next !== pageSize) {
|
|
110
|
+
onpagesizechange?.(next)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
102
113
|
</script>
|
|
103
114
|
|
|
104
|
-
<div class='flex items-center gap-4'>
|
|
115
|
+
<div class='flex flex-wrap items-center gap-4'>
|
|
105
116
|
{#if showTotal}
|
|
106
117
|
<span class='text-sm text-base-content/70'>
|
|
107
118
|
{(labels.total ?? uiM('pagination_total')).replace('{count}', String(total))}
|
|
108
119
|
</span>
|
|
109
120
|
{/if}
|
|
110
121
|
|
|
122
|
+
{#if showSizeChanger}
|
|
123
|
+
<div class='w-28 shrink-0'>
|
|
124
|
+
<Select
|
|
125
|
+
size='sm'
|
|
126
|
+
value={String(pageSize)}
|
|
127
|
+
onchange={handlePageSizeChange}
|
|
128
|
+
>
|
|
129
|
+
{#each pageSizeOptions as opt (opt)}
|
|
130
|
+
<option value={String(opt)}>
|
|
131
|
+
{(labels.pageSize ?? uiM('pagination_page_size')).replace('{size}', String(opt))}
|
|
132
|
+
</option>
|
|
133
|
+
{/each}
|
|
134
|
+
</Select>
|
|
135
|
+
</div>
|
|
136
|
+
{/if}
|
|
137
|
+
|
|
111
138
|
<div class={joinClass}>
|
|
112
139
|
<BareButton
|
|
113
140
|
class={btnClass}
|
|
@@ -140,18 +167,18 @@
|
|
|
140
167
|
</div>
|
|
141
168
|
|
|
142
169
|
{#if showJumper}
|
|
143
|
-
<div class='flex items-center gap-2'>
|
|
144
|
-
<span class='text-sm'>{labels.jumpTo ?? uiM('pagination_jump_to')}</span>
|
|
170
|
+
<div class='flex min-w-fit items-center gap-2 whitespace-nowrap'>
|
|
171
|
+
<span class='shrink-0 text-sm'>{labels.jumpTo ?? uiM('pagination_jump_to')}</span>
|
|
145
172
|
<Input
|
|
146
173
|
type='number'
|
|
147
|
-
size='
|
|
148
|
-
class='w-
|
|
174
|
+
size='xs'
|
|
175
|
+
class='w-14 shrink-0'
|
|
149
176
|
min={1}
|
|
150
177
|
max={totalPages}
|
|
151
178
|
bind:value={jumperValue}
|
|
152
179
|
onkeydown={(e: KeyboardEvent & { currentTarget: HTMLInputElement }) => e.key === 'Enter' && handleJump()}
|
|
153
180
|
/>
|
|
154
|
-
<span class='text-sm'>{labels.page ?? uiM('pagination_page')}</span>
|
|
181
|
+
<span class='shrink-0 text-sm'>{labels.page ?? uiM('pagination_page')}</span>
|
|
155
182
|
</div>
|
|
156
183
|
{/if}
|
|
157
184
|
</div>
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
@component
|
|
3
|
-
CRUD
|
|
2
|
+
@component CrudDetailPanel
|
|
3
|
+
CRUD 详情组件(只读展示),支持抽屉(drawer)与弹窗(modal)两种展示形式
|
|
4
4
|
|
|
5
5
|
使用 Svelte 5 Runes ($props, $derived)
|
|
6
|
-
使用 compounds 组件:Drawer, Badge
|
|
6
|
+
使用 compounds 组件:Drawer, Modal, Badge
|
|
7
7
|
-->
|
|
8
8
|
<script lang='ts'>
|
|
9
9
|
import type { Snippet } from 'svelte'
|
|
10
10
|
import type { Size } from '../../../types.js'
|
|
11
|
+
import type { CrudFormVariant } from './crud-types.js'
|
|
11
12
|
import { uiM } from '../../../messages.js'
|
|
12
13
|
import Drawer from '../../compounds/Drawer.svelte'
|
|
14
|
+
import Modal from '../../compounds/Modal.svelte'
|
|
13
15
|
import Badge from '../../primitives/Badge.svelte'
|
|
14
16
|
import Button from '../../primitives/Button.svelte'
|
|
15
17
|
|
|
@@ -28,7 +30,12 @@
|
|
|
28
30
|
item = null,
|
|
29
31
|
fields = [],
|
|
30
32
|
title = '',
|
|
33
|
+
variant = 'drawer' as CrudFormVariant,
|
|
31
34
|
size = '2xl' as Size,
|
|
35
|
+
drawerWidth,
|
|
36
|
+
modalSize = '2xl' as Size | 'full',
|
|
37
|
+
modalWidth,
|
|
38
|
+
modalHeight,
|
|
32
39
|
canEdit = false,
|
|
33
40
|
onedit,
|
|
34
41
|
onclose,
|
|
@@ -38,7 +45,12 @@
|
|
|
38
45
|
item?: Record<string, unknown> | null
|
|
39
46
|
fields?: FieldDef[]
|
|
40
47
|
title?: string
|
|
48
|
+
variant?: CrudFormVariant
|
|
41
49
|
size?: Size
|
|
50
|
+
drawerWidth?: string
|
|
51
|
+
modalSize?: Size | 'full'
|
|
52
|
+
modalWidth?: string
|
|
53
|
+
modalHeight?: string
|
|
42
54
|
canEdit?: boolean
|
|
43
55
|
onedit?: () => void
|
|
44
56
|
onclose?: () => void
|
|
@@ -63,14 +75,12 @@
|
|
|
63
75
|
return field.render(value, item)
|
|
64
76
|
}
|
|
65
77
|
|
|
66
|
-
// 选项类型:将 value 映射为 label
|
|
67
78
|
if (field.type === 'select' || field.type === 'radio') {
|
|
68
79
|
const opts = resolveOptions(field.options)
|
|
69
80
|
const found = opts.find(o => String(o.value) === String(value))
|
|
70
81
|
return found?.label ?? String(value)
|
|
71
82
|
}
|
|
72
83
|
|
|
73
|
-
// 多选类型
|
|
74
84
|
if (field.type === 'multi-select' && Array.isArray(value)) {
|
|
75
85
|
const opts = resolveOptions(field.options)
|
|
76
86
|
return value.map((v) => {
|
|
@@ -79,12 +89,10 @@
|
|
|
79
89
|
}).join(', ')
|
|
80
90
|
}
|
|
81
91
|
|
|
82
|
-
// 布尔
|
|
83
92
|
if (field.type === 'boolean' || field.type === 'checkbox') {
|
|
84
93
|
return value ? '✓' : '✗'
|
|
85
94
|
}
|
|
86
95
|
|
|
87
|
-
// 日期/时间
|
|
88
96
|
if (field.type === 'date' && (typeof value === 'string' || typeof value === 'number' || value instanceof Date)) {
|
|
89
97
|
return new Date(value as string | number).toLocaleDateString()
|
|
90
98
|
}
|
|
@@ -107,9 +115,9 @@
|
|
|
107
115
|
}
|
|
108
116
|
</script>
|
|
109
117
|
|
|
110
|
-
|
|
118
|
+
{#snippet detailBody()}
|
|
111
119
|
{#if item}
|
|
112
|
-
<div class='space-y-4
|
|
120
|
+
<div class='space-y-4'>
|
|
113
121
|
{#each detailFields as field (field.id)}
|
|
114
122
|
{@const value = item[field.id]}
|
|
115
123
|
<div>
|
|
@@ -138,18 +146,47 @@
|
|
|
138
146
|
</div>
|
|
139
147
|
{/if}
|
|
140
148
|
</div>
|
|
141
|
-
|
|
142
|
-
<!-- 底部操作栏 -->
|
|
143
|
-
<div class='absolute bottom-0 left-0 right-0 p-4 bg-base-200 border-t border-base-content/10 flex justify-end gap-2'>
|
|
144
|
-
{#if canEdit}
|
|
145
|
-
<Button variant='primary' onclick={onedit}>
|
|
146
|
-
<span class='icon-[tabler--edit] size-4 mr-1'></span>
|
|
147
|
-
{uiM('crud_edit')}
|
|
148
|
-
</Button>
|
|
149
|
-
{/if}
|
|
150
|
-
<Button variant='ghost' onclick={handleClose}>
|
|
151
|
-
{uiM('crud_close')}
|
|
152
|
-
</Button>
|
|
153
|
-
</div>
|
|
154
149
|
{/if}
|
|
155
|
-
|
|
150
|
+
{/snippet}
|
|
151
|
+
|
|
152
|
+
{#snippet footerActions()}
|
|
153
|
+
{#if canEdit}
|
|
154
|
+
<Button variant='primary' onclick={onedit}>
|
|
155
|
+
<span class='icon-[tabler--edit] size-4 mr-1'></span>
|
|
156
|
+
{uiM('crud_edit')}
|
|
157
|
+
</Button>
|
|
158
|
+
{/if}
|
|
159
|
+
<Button variant='ghost' onclick={handleClose}>
|
|
160
|
+
{uiM('crud_close')}
|
|
161
|
+
</Button>
|
|
162
|
+
{/snippet}
|
|
163
|
+
|
|
164
|
+
{#if variant === 'modal'}
|
|
165
|
+
<Modal
|
|
166
|
+
bind:open
|
|
167
|
+
{title}
|
|
168
|
+
size={modalSize}
|
|
169
|
+
width={modalWidth}
|
|
170
|
+
height={modalHeight}
|
|
171
|
+
onclose={handleClose}
|
|
172
|
+
>
|
|
173
|
+
{@render detailBody()}
|
|
174
|
+
|
|
175
|
+
{#snippet footer()}
|
|
176
|
+
{@render footerActions()}
|
|
177
|
+
{/snippet}
|
|
178
|
+
</Modal>
|
|
179
|
+
{:else}
|
|
180
|
+
<Drawer bind:open {title} position='right' {size} width={drawerWidth} onclose={handleClose}>
|
|
181
|
+
{#if item}
|
|
182
|
+
<div class='pb-20'>
|
|
183
|
+
{@render detailBody()}
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
<!-- 底部操作栏 -->
|
|
187
|
+
<div class='absolute bottom-0 left-0 right-0 p-4 bg-base-200 border-t border-base-content/10 flex justify-end gap-2'>
|
|
188
|
+
{@render footerActions()}
|
|
189
|
+
</div>
|
|
190
|
+
{/if}
|
|
191
|
+
</Drawer>
|
|
192
|
+
{/if}
|
package/dist/components/scenes/crud/{CrudDetailDrawer.svelte.d.ts → CrudDetailPanel.svelte.d.ts}
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
import type { Size } from '../../../types.js';
|
|
3
|
+
import type { CrudFormVariant } from './crud-types.js';
|
|
3
4
|
type FieldDef = {
|
|
4
5
|
id: string;
|
|
5
6
|
label: string | (() => string);
|
|
@@ -20,19 +21,24 @@ type $$ComponentProps = {
|
|
|
20
21
|
item?: Record<string, unknown> | null;
|
|
21
22
|
fields?: FieldDef[];
|
|
22
23
|
title?: string;
|
|
24
|
+
variant?: CrudFormVariant;
|
|
23
25
|
size?: Size;
|
|
26
|
+
drawerWidth?: string;
|
|
27
|
+
modalSize?: Size | 'full';
|
|
28
|
+
modalWidth?: string;
|
|
29
|
+
modalHeight?: string;
|
|
24
30
|
canEdit?: boolean;
|
|
25
31
|
onedit?: () => void;
|
|
26
32
|
onclose?: () => void;
|
|
27
33
|
detailExtra?: Snippet<[Record<string, unknown>]>;
|
|
28
34
|
};
|
|
29
35
|
/**
|
|
30
|
-
*
|
|
31
|
-
* CRUD
|
|
36
|
+
* CrudDetailPanel
|
|
37
|
+
* CRUD 详情组件(只读展示),支持抽屉(drawer)与弹窗(modal)两种展示形式
|
|
32
38
|
*
|
|
33
39
|
* 使用 Svelte 5 Runes ($props, $derived)
|
|
34
|
-
* 使用 compounds 组件:Drawer, Badge
|
|
40
|
+
* 使用 compounds 组件:Drawer, Modal, Badge
|
|
35
41
|
*/
|
|
36
|
-
declare const
|
|
37
|
-
type
|
|
38
|
-
export default
|
|
42
|
+
declare const CrudDetailPanel: import("svelte").Component<$$ComponentProps, {}, "open">;
|
|
43
|
+
type CrudDetailPanel = ReturnType<typeof CrudDetailPanel>;
|
|
44
|
+
export default CrudDetailPanel;
|