qdadm 1.1.7 → 1.2.0
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/package.json +1 -1
- package/src/components/{forms → edit}/FormTab.vue +8 -15
- package/src/components/{forms → edit}/FormTabs.vue +21 -15
- package/src/components/index.ts +10 -8
- package/src/components/item/FieldGroups.vue +388 -0
- package/src/components/layout/defaults/DefaultFormActions.vue +1 -1
- package/src/components/show/ShowPage.vue +24 -9
- package/src/composables/index.ts +14 -0
- package/src/composables/useEntityItemFormPage.ts +68 -141
- package/src/composables/useEntityItemShowPage.ts +39 -279
- package/src/composables/useFieldManager.ts +595 -0
- package/src/components/show/ShowGroups.vue +0 -292
- /package/src/components/{forms → edit}/FormActions.vue +0 -0
- /package/src/components/{forms → edit}/FormField.vue +0 -0
- /package/src/components/{forms → edit}/FormInput.vue +0 -0
- /package/src/components/{forms → edit}/FormPage.vue +0 -0
package/package.json
CHANGED
|
@@ -2,23 +2,16 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* FormTab - Normalized tab header component
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* @deprecated Use FieldGroups with layout="tabs" instead. FieldGroups now supports
|
|
6
|
+
* icon, badge, count, visible, and disabled options on groups.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* - icon: PrimeIcon class (e.g., 'pi-cog')
|
|
11
|
-
* - count: Number to display as badge
|
|
12
|
-
* - badge: Custom badge text/value
|
|
13
|
-
* - badgeSeverity: Badge color ('secondary', 'info', 'success', 'warn', 'danger')
|
|
14
|
-
* - visible: Show/hide tab (default: true)
|
|
15
|
-
* - disabled: Disable tab interaction
|
|
8
|
+
* ```ts
|
|
9
|
+
* // Before (deprecated)
|
|
10
|
+
* <FormTab value="general" label="General" icon="pi-cog" :count="5" />
|
|
16
11
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* <FormTab value="errors" label="Errors" :count="errors.length" badge-severity="danger" />
|
|
21
|
-
* <FormTab value="advanced" label="Advanced" :visible="isEdit" />
|
|
12
|
+
* // After (recommended)
|
|
13
|
+
* form.group('general', ['field1'], { label: 'General', icon: 'cog', count: 5 })
|
|
14
|
+
* ```
|
|
22
15
|
*/
|
|
23
16
|
|
|
24
17
|
import { type PropType } from 'vue'
|
|
@@ -2,22 +2,28 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* FormTabs - Normalized tab container for forms
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* @deprecated Use FieldGroups with layout="tabs" instead. FieldGroups now supports
|
|
6
|
+
* icon, badge, count, visible, and disabled options on groups.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* <
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
8
|
+
* ```vue
|
|
9
|
+
* <!-- Before (deprecated) -->
|
|
10
|
+
* <FormTabs v-model="activeTab">
|
|
11
|
+
* <template #tabs>
|
|
12
|
+
* <FormTab value="general" label="General" icon="pi-cog" />
|
|
13
|
+
* </template>
|
|
14
|
+
* <template #panels>
|
|
15
|
+
* <TabPanel value="general">...</TabPanel>
|
|
16
|
+
* </template>
|
|
17
|
+
* </FormTabs>
|
|
18
|
+
*
|
|
19
|
+
* <!-- After (recommended) -->
|
|
20
|
+
* form.group('general', ['field1', 'field2'], { label: 'General', icon: 'cog' })
|
|
21
|
+
* <FieldGroups :groups="form.groups.value" layout="tabs">
|
|
22
|
+
* <template #field="{ field }">
|
|
23
|
+
* <FormField :field="field" />
|
|
24
|
+
* </template>
|
|
25
|
+
* </FieldGroups>
|
|
26
|
+
* ```
|
|
21
27
|
*/
|
|
22
28
|
|
|
23
29
|
import Tabs from 'primevue/tabs'
|
package/src/components/index.ts
CHANGED
|
@@ -19,19 +19,21 @@ export { default as DefaultFooter } from './layout/defaults/DefaultFooter.vue'
|
|
|
19
19
|
export { default as DefaultUserInfo } from './layout/defaults/DefaultUserInfo.vue'
|
|
20
20
|
export { default as DefaultBreadcrumb } from './layout/defaults/DefaultBreadcrumb.vue'
|
|
21
21
|
|
|
22
|
-
//
|
|
23
|
-
export { default as
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
export { default as
|
|
27
|
-
export { default as
|
|
28
|
-
export { default as
|
|
22
|
+
// Item (shared between edit and show)
|
|
23
|
+
export { default as FieldGroups } from './item/FieldGroups.vue'
|
|
24
|
+
|
|
25
|
+
// Edit (form pages)
|
|
26
|
+
export { default as FormPage } from './edit/FormPage.vue'
|
|
27
|
+
export { default as FormField } from './edit/FormField.vue'
|
|
28
|
+
export { default as FormInput } from './edit/FormInput.vue'
|
|
29
|
+
export { default as FormActions } from './edit/FormActions.vue'
|
|
30
|
+
export { default as FormTabs } from './edit/FormTabs.vue' // Deprecated: use FieldGroups with layout="tabs"
|
|
31
|
+
export { default as FormTab } from './edit/FormTab.vue' // Deprecated: use FieldGroups with layout="tabs"
|
|
29
32
|
|
|
30
33
|
// Show (read-only detail pages)
|
|
31
34
|
export { default as ShowPage } from './show/ShowPage.vue'
|
|
32
35
|
export { default as ShowField } from './show/ShowField.vue'
|
|
33
36
|
export { default as ShowDisplay } from './show/ShowDisplay.vue'
|
|
34
|
-
export { default as ShowGroups } from './show/ShowGroups.vue'
|
|
35
37
|
|
|
36
38
|
// Lists
|
|
37
39
|
export { default as ListPage } from './lists/ListPage.vue'
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* FieldGroups - Generic field group renderer with configurable layouts
|
|
4
|
+
*
|
|
5
|
+
* Shared component for both edit (form) and show pages.
|
|
6
|
+
* Uses slots for field rendering to support different field components.
|
|
7
|
+
*
|
|
8
|
+
* Supports multiple layout modes:
|
|
9
|
+
* - flat: Simple sections with headers
|
|
10
|
+
* - sections: Fieldset-style sections
|
|
11
|
+
* - cards: Each group in a card
|
|
12
|
+
* - tabs: TabView for top-level groups
|
|
13
|
+
* - accordion: Collapsible panels
|
|
14
|
+
*
|
|
15
|
+
* ```vue
|
|
16
|
+
* <FieldGroups :groups="groups" :data="data" layout="tabs">
|
|
17
|
+
* <template #field="{ field, value }">
|
|
18
|
+
* <FormField :field="field" v-model="data[field.name]" />
|
|
19
|
+
* </template>
|
|
20
|
+
* </FieldGroups>
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
import { computed, type PropType } from 'vue'
|
|
24
|
+
import Tabs from 'primevue/tabs'
|
|
25
|
+
import TabList from 'primevue/tablist'
|
|
26
|
+
import Tab from 'primevue/tab'
|
|
27
|
+
import TabPanels from 'primevue/tabpanels'
|
|
28
|
+
import TabPanel from 'primevue/tabpanel'
|
|
29
|
+
import Accordion from 'primevue/accordion'
|
|
30
|
+
import AccordionPanel from 'primevue/accordionpanel'
|
|
31
|
+
import AccordionHeader from 'primevue/accordionheader'
|
|
32
|
+
import AccordionContent from 'primevue/accordioncontent'
|
|
33
|
+
import Fieldset from 'primevue/fieldset'
|
|
34
|
+
import Card from 'primevue/card'
|
|
35
|
+
import Tag from 'primevue/tag'
|
|
36
|
+
|
|
37
|
+
type BadgeSeverity = 'secondary' | 'info' | 'success' | 'warn' | 'danger' | 'contrast'
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Base field config interface
|
|
41
|
+
*/
|
|
42
|
+
interface BaseFieldConfig {
|
|
43
|
+
name: string
|
|
44
|
+
type: string
|
|
45
|
+
label: string
|
|
46
|
+
[key: string]: unknown
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Field group structure
|
|
51
|
+
*/
|
|
52
|
+
interface FieldGroup<T extends BaseFieldConfig = BaseFieldConfig> {
|
|
53
|
+
name: string
|
|
54
|
+
label: string
|
|
55
|
+
fields: T[]
|
|
56
|
+
children: FieldGroup<T>[]
|
|
57
|
+
parent?: string
|
|
58
|
+
// Tab/accordion options
|
|
59
|
+
icon?: string
|
|
60
|
+
badge?: string | number
|
|
61
|
+
badgeSeverity?: BadgeSeverity
|
|
62
|
+
count?: number
|
|
63
|
+
visible?: boolean
|
|
64
|
+
disabled?: boolean
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export type LayoutMode = 'flat' | 'sections' | 'cards' | 'tabs' | 'accordion'
|
|
68
|
+
|
|
69
|
+
const props = defineProps({
|
|
70
|
+
groups: {
|
|
71
|
+
type: Array as PropType<FieldGroup[]>,
|
|
72
|
+
required: true,
|
|
73
|
+
},
|
|
74
|
+
data: {
|
|
75
|
+
type: Object as PropType<Record<string, unknown> | null>,
|
|
76
|
+
default: null,
|
|
77
|
+
},
|
|
78
|
+
layout: {
|
|
79
|
+
type: String as PropType<LayoutMode>,
|
|
80
|
+
default: 'flat',
|
|
81
|
+
},
|
|
82
|
+
// Nested layout (for children groups)
|
|
83
|
+
childLayout: {
|
|
84
|
+
type: String as PropType<LayoutMode>,
|
|
85
|
+
default: 'sections',
|
|
86
|
+
},
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
// Expose slots for field rendering
|
|
90
|
+
defineSlots<{
|
|
91
|
+
field: (props: { field: BaseFieldConfig; value: unknown; groupName: string }) => unknown
|
|
92
|
+
// Optional: custom group header
|
|
93
|
+
'group-header'?: (props: { group: FieldGroup; layout: LayoutMode }) => unknown
|
|
94
|
+
// Optional: custom group content wrapper
|
|
95
|
+
'group-content'?: (props: { group: FieldGroup; layout: LayoutMode }) => unknown
|
|
96
|
+
}>()
|
|
97
|
+
|
|
98
|
+
// Filter out default group for labeling purposes and hidden groups
|
|
99
|
+
const displayGroups = computed(() => {
|
|
100
|
+
return props.groups.filter((g) => {
|
|
101
|
+
// Filter by visibility (default to true)
|
|
102
|
+
if (g.visible === false) return false
|
|
103
|
+
// Filter out empty default groups
|
|
104
|
+
return g.name !== '_default' || g.fields.length > 0
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
// Get value from data
|
|
109
|
+
function getValue(fieldName: string): unknown {
|
|
110
|
+
return props.data?.[fieldName]
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Normalize icon class
|
|
114
|
+
function getIconClass(icon: string | undefined): string | null {
|
|
115
|
+
if (!icon) return null
|
|
116
|
+
// Already has 'pi' prefix
|
|
117
|
+
if (icon.startsWith('pi')) {
|
|
118
|
+
return icon.includes(' ') ? icon : `pi ${icon}`
|
|
119
|
+
}
|
|
120
|
+
// Just icon name
|
|
121
|
+
return `pi pi-${icon}`
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Get badge value to display
|
|
125
|
+
function getBadgeValue(group: FieldGroup): string | number | null {
|
|
126
|
+
if (group.badge !== undefined && group.badge !== null) return group.badge
|
|
127
|
+
if (group.count !== undefined && group.count !== null && group.count > 0) return group.count
|
|
128
|
+
return null
|
|
129
|
+
}
|
|
130
|
+
</script>
|
|
131
|
+
|
|
132
|
+
<template>
|
|
133
|
+
<div class="field-groups" :class="`field-groups--${layout}`">
|
|
134
|
+
<!-- Flat layout: simple sections with optional headers -->
|
|
135
|
+
<template v-if="layout === 'flat'">
|
|
136
|
+
<div v-for="group in displayGroups" :key="group.name" class="field-group">
|
|
137
|
+
<slot name="group-header" :group="group" :layout="layout">
|
|
138
|
+
<h3 v-if="group.label && group.name !== '_default'" class="field-group-header">
|
|
139
|
+
{{ group.label }}
|
|
140
|
+
</h3>
|
|
141
|
+
</slot>
|
|
142
|
+
<div class="field-group-fields">
|
|
143
|
+
<template v-for="field in group.fields" :key="field.name">
|
|
144
|
+
<slot
|
|
145
|
+
name="field"
|
|
146
|
+
:field="field"
|
|
147
|
+
:value="getValue(field.name)"
|
|
148
|
+
:group-name="group.name"
|
|
149
|
+
/>
|
|
150
|
+
</template>
|
|
151
|
+
</div>
|
|
152
|
+
<!-- Recursive children -->
|
|
153
|
+
<FieldGroups
|
|
154
|
+
v-if="group.children.length > 0"
|
|
155
|
+
:groups="group.children"
|
|
156
|
+
:data="data"
|
|
157
|
+
:layout="childLayout"
|
|
158
|
+
>
|
|
159
|
+
<template #field="slotProps">
|
|
160
|
+
<slot name="field" v-bind="slotProps" />
|
|
161
|
+
</template>
|
|
162
|
+
</FieldGroups>
|
|
163
|
+
</div>
|
|
164
|
+
</template>
|
|
165
|
+
|
|
166
|
+
<!-- Sections layout: Fieldset style -->
|
|
167
|
+
<template v-else-if="layout === 'sections'">
|
|
168
|
+
<Fieldset
|
|
169
|
+
v-for="group in displayGroups"
|
|
170
|
+
:key="group.name"
|
|
171
|
+
:legend="group.label || undefined"
|
|
172
|
+
:toggleable="false"
|
|
173
|
+
>
|
|
174
|
+
<div class="field-group-fields">
|
|
175
|
+
<template v-for="field in group.fields" :key="field.name">
|
|
176
|
+
<slot
|
|
177
|
+
name="field"
|
|
178
|
+
:field="field"
|
|
179
|
+
:value="getValue(field.name)"
|
|
180
|
+
:group-name="group.name"
|
|
181
|
+
/>
|
|
182
|
+
</template>
|
|
183
|
+
</div>
|
|
184
|
+
<!-- Recursive children -->
|
|
185
|
+
<FieldGroups
|
|
186
|
+
v-if="group.children.length > 0"
|
|
187
|
+
:groups="group.children"
|
|
188
|
+
:data="data"
|
|
189
|
+
:layout="childLayout"
|
|
190
|
+
>
|
|
191
|
+
<template #field="slotProps">
|
|
192
|
+
<slot name="field" v-bind="slotProps" />
|
|
193
|
+
</template>
|
|
194
|
+
</FieldGroups>
|
|
195
|
+
</Fieldset>
|
|
196
|
+
</template>
|
|
197
|
+
|
|
198
|
+
<!-- Cards layout -->
|
|
199
|
+
<template v-else-if="layout === 'cards'">
|
|
200
|
+
<Card v-for="group in displayGroups" :key="group.name">
|
|
201
|
+
<template #title>{{ group.label }}</template>
|
|
202
|
+
<template #content>
|
|
203
|
+
<div class="field-group-fields">
|
|
204
|
+
<template v-for="field in group.fields" :key="field.name">
|
|
205
|
+
<slot
|
|
206
|
+
name="field"
|
|
207
|
+
:field="field"
|
|
208
|
+
:value="getValue(field.name)"
|
|
209
|
+
:group-name="group.name"
|
|
210
|
+
/>
|
|
211
|
+
</template>
|
|
212
|
+
</div>
|
|
213
|
+
<!-- Recursive children -->
|
|
214
|
+
<FieldGroups
|
|
215
|
+
v-if="group.children.length > 0"
|
|
216
|
+
:groups="group.children"
|
|
217
|
+
:data="data"
|
|
218
|
+
:layout="childLayout"
|
|
219
|
+
>
|
|
220
|
+
<template #field="slotProps">
|
|
221
|
+
<slot name="field" v-bind="slotProps" />
|
|
222
|
+
</template>
|
|
223
|
+
</FieldGroups>
|
|
224
|
+
</template>
|
|
225
|
+
</Card>
|
|
226
|
+
</template>
|
|
227
|
+
|
|
228
|
+
<!-- Tabs layout -->
|
|
229
|
+
<template v-else-if="layout === 'tabs'">
|
|
230
|
+
<Tabs :value="displayGroups[0]?.name || '0'">
|
|
231
|
+
<TabList>
|
|
232
|
+
<Tab
|
|
233
|
+
v-for="group in displayGroups"
|
|
234
|
+
:key="group.name"
|
|
235
|
+
:value="group.name"
|
|
236
|
+
:disabled="group.disabled"
|
|
237
|
+
>
|
|
238
|
+
<i v-if="group.icon" :class="getIconClass(group.icon)" class="tab-icon" />
|
|
239
|
+
<span class="tab-label">{{ group.label }}</span>
|
|
240
|
+
<Tag
|
|
241
|
+
v-if="getBadgeValue(group) !== null"
|
|
242
|
+
:value="String(getBadgeValue(group))"
|
|
243
|
+
:severity="group.badgeSeverity || 'secondary'"
|
|
244
|
+
class="tab-badge"
|
|
245
|
+
/>
|
|
246
|
+
</Tab>
|
|
247
|
+
</TabList>
|
|
248
|
+
<TabPanels>
|
|
249
|
+
<TabPanel v-for="group in displayGroups" :key="group.name" :value="group.name">
|
|
250
|
+
<div class="field-group-fields">
|
|
251
|
+
<template v-for="field in group.fields" :key="field.name">
|
|
252
|
+
<slot
|
|
253
|
+
name="field"
|
|
254
|
+
:field="field"
|
|
255
|
+
:value="getValue(field.name)"
|
|
256
|
+
:group-name="group.name"
|
|
257
|
+
/>
|
|
258
|
+
</template>
|
|
259
|
+
</div>
|
|
260
|
+
<!-- Recursive children -->
|
|
261
|
+
<FieldGroups
|
|
262
|
+
v-if="group.children.length > 0"
|
|
263
|
+
:groups="group.children"
|
|
264
|
+
:data="data"
|
|
265
|
+
:layout="childLayout"
|
|
266
|
+
>
|
|
267
|
+
<template #field="slotProps">
|
|
268
|
+
<slot name="field" v-bind="slotProps" />
|
|
269
|
+
</template>
|
|
270
|
+
</FieldGroups>
|
|
271
|
+
</TabPanel>
|
|
272
|
+
</TabPanels>
|
|
273
|
+
</Tabs>
|
|
274
|
+
</template>
|
|
275
|
+
|
|
276
|
+
<!-- Accordion layout -->
|
|
277
|
+
<template v-else-if="layout === 'accordion'">
|
|
278
|
+
<Accordion :value="[displayGroups[0]?.name || '0']" multiple>
|
|
279
|
+
<AccordionPanel
|
|
280
|
+
v-for="group in displayGroups"
|
|
281
|
+
:key="group.name"
|
|
282
|
+
:value="group.name"
|
|
283
|
+
:disabled="group.disabled"
|
|
284
|
+
>
|
|
285
|
+
<AccordionHeader>
|
|
286
|
+
<i v-if="group.icon" :class="getIconClass(group.icon)" class="accordion-icon" />
|
|
287
|
+
<span class="accordion-label">{{ group.label }}</span>
|
|
288
|
+
<Tag
|
|
289
|
+
v-if="getBadgeValue(group) !== null"
|
|
290
|
+
:value="String(getBadgeValue(group))"
|
|
291
|
+
:severity="group.badgeSeverity || 'secondary'"
|
|
292
|
+
class="accordion-badge"
|
|
293
|
+
/>
|
|
294
|
+
</AccordionHeader>
|
|
295
|
+
<AccordionContent>
|
|
296
|
+
<div class="field-group-fields">
|
|
297
|
+
<template v-for="field in group.fields" :key="field.name">
|
|
298
|
+
<slot
|
|
299
|
+
name="field"
|
|
300
|
+
:field="field"
|
|
301
|
+
:value="getValue(field.name)"
|
|
302
|
+
:group-name="group.name"
|
|
303
|
+
/>
|
|
304
|
+
</template>
|
|
305
|
+
</div>
|
|
306
|
+
<!-- Recursive children -->
|
|
307
|
+
<FieldGroups
|
|
308
|
+
v-if="group.children.length > 0"
|
|
309
|
+
:groups="group.children"
|
|
310
|
+
:data="data"
|
|
311
|
+
:layout="childLayout"
|
|
312
|
+
>
|
|
313
|
+
<template #field="slotProps">
|
|
314
|
+
<slot name="field" v-bind="slotProps" />
|
|
315
|
+
</template>
|
|
316
|
+
</FieldGroups>
|
|
317
|
+
</AccordionContent>
|
|
318
|
+
</AccordionPanel>
|
|
319
|
+
</Accordion>
|
|
320
|
+
</template>
|
|
321
|
+
</div>
|
|
322
|
+
</template>
|
|
323
|
+
|
|
324
|
+
<style scoped>
|
|
325
|
+
.field-groups {
|
|
326
|
+
display: flex;
|
|
327
|
+
flex-direction: column;
|
|
328
|
+
gap: 1rem;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.field-group {
|
|
332
|
+
display: flex;
|
|
333
|
+
flex-direction: column;
|
|
334
|
+
gap: 0.5rem;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.field-group-header {
|
|
338
|
+
font-size: 1rem;
|
|
339
|
+
font-weight: 600;
|
|
340
|
+
margin: 0 0 0.5rem 0;
|
|
341
|
+
padding-bottom: 0.5rem;
|
|
342
|
+
border-bottom: 1px solid var(--p-surface-200, #e2e8f0);
|
|
343
|
+
color: var(--p-text-color);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.field-group-fields {
|
|
347
|
+
display: flex;
|
|
348
|
+
flex-direction: column;
|
|
349
|
+
gap: 0.75rem;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/* Cards layout spacing */
|
|
353
|
+
.field-groups--cards {
|
|
354
|
+
gap: 1rem;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/* Tabs and accordion panels padding */
|
|
358
|
+
.field-groups--tabs :deep(.p-tabpanel),
|
|
359
|
+
.field-groups--accordion :deep(.p-accordioncontent-content) {
|
|
360
|
+
padding-top: 1rem;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/* Tab icon and badge styling */
|
|
364
|
+
.tab-icon {
|
|
365
|
+
margin-right: 0.5rem;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.tab-badge {
|
|
369
|
+
margin-left: 0.5rem;
|
|
370
|
+
font-size: 0.7rem;
|
|
371
|
+
padding: 0.15rem 0.4rem;
|
|
372
|
+
min-width: 1.25rem;
|
|
373
|
+
text-align: center;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/* Accordion icon and badge styling */
|
|
377
|
+
.accordion-icon {
|
|
378
|
+
margin-right: 0.5rem;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.accordion-badge {
|
|
382
|
+
margin-left: 0.5rem;
|
|
383
|
+
font-size: 0.7rem;
|
|
384
|
+
padding: 0.15rem 0.4rem;
|
|
385
|
+
min-width: 1.25rem;
|
|
386
|
+
text-align: center;
|
|
387
|
+
}
|
|
388
|
+
</style>
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* - qdadmFormEmit: { save, saveAndClose, cancel, delete }
|
|
16
16
|
*/
|
|
17
17
|
import { inject, computed, type Ref } from 'vue'
|
|
18
|
-
import FormActions from '../../
|
|
18
|
+
import FormActions from '../../edit/FormActions.vue'
|
|
19
19
|
|
|
20
20
|
interface FormState {
|
|
21
21
|
isEdit?: boolean
|
|
@@ -42,7 +42,8 @@ import PageHeader from '../layout/PageHeader.vue'
|
|
|
42
42
|
import Card from 'primevue/card'
|
|
43
43
|
import Button from 'primevue/button'
|
|
44
44
|
import Message from 'primevue/message'
|
|
45
|
-
import
|
|
45
|
+
import FieldGroups from '../item/FieldGroups.vue'
|
|
46
|
+
import ShowField from './ShowField.vue'
|
|
46
47
|
|
|
47
48
|
/**
|
|
48
49
|
* Page title parts for PageHeader
|
|
@@ -246,14 +247,21 @@ const fetchErrorMessage = computed<string | null>(() => {
|
|
|
246
247
|
<!-- Group layout mode -->
|
|
247
248
|
<template v-if="useGroupLayout">
|
|
248
249
|
<slot name="groups">
|
|
249
|
-
<
|
|
250
|
+
<FieldGroups
|
|
250
251
|
:groups="groups"
|
|
251
252
|
:data="data"
|
|
252
253
|
:layout="layout"
|
|
253
254
|
:child-layout="childLayout"
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
255
|
+
>
|
|
256
|
+
<template #field="{ field, value }">
|
|
257
|
+
<ShowField
|
|
258
|
+
:field="field"
|
|
259
|
+
:value="value as string | number | boolean | Date | Record<string, unknown> | unknown[]"
|
|
260
|
+
:horizontal="horizontalFields"
|
|
261
|
+
:label-width="labelWidth"
|
|
262
|
+
/>
|
|
263
|
+
</template>
|
|
264
|
+
</FieldGroups>
|
|
257
265
|
</slot>
|
|
258
266
|
</template>
|
|
259
267
|
<!-- Flat fields mode (default) -->
|
|
@@ -300,14 +308,21 @@ const fetchErrorMessage = computed<string | null>(() => {
|
|
|
300
308
|
<!-- Group layout mode -->
|
|
301
309
|
<template v-if="useGroupLayout">
|
|
302
310
|
<slot name="groups">
|
|
303
|
-
<
|
|
311
|
+
<FieldGroups
|
|
304
312
|
:groups="groups"
|
|
305
313
|
:data="data"
|
|
306
314
|
:layout="layout"
|
|
307
315
|
:child-layout="childLayout"
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
316
|
+
>
|
|
317
|
+
<template #field="{ field, value }">
|
|
318
|
+
<ShowField
|
|
319
|
+
:field="field"
|
|
320
|
+
:value="value as string | number | boolean | Date | Record<string, unknown> | unknown[]"
|
|
321
|
+
:horizontal="horizontalFields"
|
|
322
|
+
:label-width="labelWidth"
|
|
323
|
+
/>
|
|
324
|
+
</template>
|
|
325
|
+
</FieldGroups>
|
|
311
326
|
</slot>
|
|
312
327
|
</template>
|
|
313
328
|
<!-- Flat fields mode (default) -->
|
package/src/composables/index.ts
CHANGED
|
@@ -132,3 +132,17 @@ export {
|
|
|
132
132
|
type UserRecord,
|
|
133
133
|
} from './useUserImpersonator'
|
|
134
134
|
export { useCurrentEntity, type UseCurrentEntityReturn } from './useCurrentEntity'
|
|
135
|
+
export {
|
|
136
|
+
useFieldManager,
|
|
137
|
+
snakeCaseToTitle,
|
|
138
|
+
type BaseFieldDefinition,
|
|
139
|
+
type ResolvedFieldConfig as FieldManagerFieldConfig,
|
|
140
|
+
type FieldGroup,
|
|
141
|
+
type GroupDefinition,
|
|
142
|
+
type GroupOptions,
|
|
143
|
+
type GenerateFieldsOptions as FieldManagerGenerateOptions,
|
|
144
|
+
type AddFieldOptions as FieldManagerAddOptions,
|
|
145
|
+
type MoveFieldPosition,
|
|
146
|
+
type UseFieldManagerOptions,
|
|
147
|
+
type UseFieldManagerReturn,
|
|
148
|
+
} from './useFieldManager'
|