@conduction/nextcloud-vue 0.1.0-beta.1 → 0.1.0-beta.10
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 +226 -0
- package/css/index.css +5 -0
- package/dist/nextcloud-vue.cjs +67614 -0
- package/dist/nextcloud-vue.cjs.js +76311 -5905
- package/dist/nextcloud-vue.cjs.js.map +1 -1
- package/dist/nextcloud-vue.cjs.map +1 -0
- package/dist/nextcloud-vue.css +3279 -203
- package/dist/nextcloud-vue.esm.js +76240 -5882
- package/dist/nextcloud-vue.esm.js.map +1 -1
- package/package.json +89 -63
- package/src/components/CnActionsBar/CnActionsBar.vue +254 -0
- package/src/components/CnActionsBar/index.js +1 -0
- package/src/components/CnAdvancedFormDialog/CnAdvancedFormDialog.vue +569 -0
- package/src/components/CnAdvancedFormDialog/CnDataTab.vue +217 -0
- package/src/components/CnAdvancedFormDialog/CnMetadataTab.vue +121 -0
- package/src/components/CnAdvancedFormDialog/CnPropertiesTab.vue +422 -0
- package/src/components/CnAdvancedFormDialog/CnPropertyValueCell.vue +247 -0
- package/src/components/CnAdvancedFormDialog/index.js +1 -0
- package/src/components/CnCard/CnCard.vue +415 -0
- package/src/components/CnCard/index.js +1 -0
- package/src/components/CnCardGrid/CnCardGrid.vue +23 -20
- package/src/components/CnCardGrid/index.js +1 -1
- package/src/components/CnCellRenderer/index.js +1 -1
- package/src/components/CnChartWidget/CnChartWidget.vue +318 -0
- package/src/components/CnChartWidget/index.js +1 -0
- package/src/components/CnConfigurationCard/index.js +1 -1
- package/src/components/CnContextMenu/CnContextMenu.vue +142 -0
- package/src/components/CnContextMenu/index.js +1 -0
- package/src/components/CnCopyDialog/CnCopyDialog.vue +257 -0
- package/src/components/CnCopyDialog/index.js +1 -0
- package/src/components/CnDashboardGrid/CnDashboardGrid.vue +229 -0
- package/src/components/CnDashboardGrid/index.js +1 -0
- package/src/components/CnDashboardPage/CnDashboardPage.vue +396 -0
- package/src/components/CnDashboardPage/index.js +1 -0
- package/src/components/CnDataTable/CnDataTable.vue +24 -16
- package/src/components/CnDataTable/index.js +1 -1
- package/src/components/CnDeleteDialog/CnDeleteDialog.vue +177 -0
- package/src/components/CnDeleteDialog/index.js +1 -0
- package/src/components/CnDetailCard/CnDetailCard.vue +225 -0
- package/src/components/CnDetailCard/index.js +1 -0
- package/src/components/CnDetailGrid/CnDetailGrid.vue +254 -0
- package/src/components/CnDetailGrid/index.js +1 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +431 -0
- package/src/components/CnDetailPage/index.js +1 -0
- package/src/components/CnFacetSidebar/CnFacetSidebar.vue +12 -2
- package/src/components/CnFacetSidebar/index.js +1 -1
- package/src/components/CnFilterBar/index.js +1 -1
- package/src/components/CnFormDialog/CnFormDialog.vue +934 -0
- package/src/components/CnFormDialog/index.js +1 -0
- package/src/components/CnIcon/CnIcon.vue +89 -0
- package/src/components/CnIcon/index.js +1 -0
- package/src/components/CnIndexPage/CnIndexPage.vue +589 -291
- package/src/components/CnIndexPage/index.js +1 -1
- package/src/components/CnIndexSidebar/CnIndexSidebar.vue +535 -0
- package/src/components/CnIndexSidebar/index.js +1 -0
- package/src/components/CnInfoWidget/CnInfoWidget.vue +219 -0
- package/src/components/CnInfoWidget/index.js +1 -0
- package/src/components/CnItemCard/CnItemCard.vue +134 -0
- package/src/components/CnItemCard/index.js +1 -0
- package/src/components/CnJsonViewer/CnJsonViewer.vue +283 -0
- package/src/components/CnJsonViewer/index.js +1 -0
- package/src/components/CnKpiGrid/CnKpiGrid.vue +5 -1
- package/src/components/CnKpiGrid/index.js +1 -1
- package/src/components/CnMassActionBar/CnMassActionBar.vue +6 -5
- package/src/components/CnMassActionBar/index.js +1 -1
- package/src/components/CnMassCopyDialog/CnMassCopyDialog.vue +16 -9
- package/src/components/CnMassCopyDialog/index.js +1 -1
- package/src/components/CnMassDeleteDialog/CnMassDeleteDialog.vue +16 -9
- package/src/components/CnMassDeleteDialog/index.js +1 -1
- package/src/components/CnMassExportDialog/CnMassExportDialog.vue +8 -7
- package/src/components/CnMassExportDialog/index.js +1 -1
- package/src/components/CnMassImportDialog/CnMassImportDialog.vue +20 -17
- package/src/components/CnMassImportDialog/index.js +1 -1
- package/src/components/CnNoteCard/CnNoteCard.vue +149 -0
- package/src/components/CnNoteCard/index.js +1 -0
- package/src/components/CnNotesCard/CnNotesCard.vue +415 -0
- package/src/components/CnNotesCard/index.js +1 -0
- package/src/components/CnObjectCard/CnObjectCard.vue +3 -1
- package/src/components/CnObjectCard/index.js +1 -1
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +853 -0
- package/src/components/CnObjectDataWidget/index.js +1 -0
- package/src/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue +288 -0
- package/src/components/CnObjectMetadataWidget/index.js +1 -0
- package/src/components/CnObjectSidebar/CnAuditTrailTab.vue +368 -0
- package/src/components/CnObjectSidebar/CnFilesTab.vue +286 -0
- package/src/components/CnObjectSidebar/CnNotesTab.vue +249 -0
- package/src/components/CnObjectSidebar/CnObjectSidebar.vue +254 -0
- package/src/components/CnObjectSidebar/CnTagsTab.vue +258 -0
- package/src/components/CnObjectSidebar/CnTasksTab.vue +482 -0
- package/src/components/CnObjectSidebar/index.js +6 -0
- package/src/components/CnPageHeader/CnPageHeader.vue +61 -0
- package/src/components/CnPageHeader/index.js +1 -0
- package/src/components/CnPagination/CnPagination.vue +7 -6
- package/src/components/CnPagination/index.js +1 -1
- package/src/components/CnProgressBar/CnProgressBar.vue +262 -0
- package/src/components/CnProgressBar/index.js +1 -0
- package/src/components/CnRegisterMapping/CnRegisterMapping.vue +792 -0
- package/src/components/CnRegisterMapping/index.js +1 -0
- package/src/components/CnRowActions/CnRowActions.vue +25 -3
- package/src/components/CnRowActions/index.js +1 -1
- package/src/components/CnSchemaFormDialog/CnSchemaConfigurationTab.vue +226 -0
- package/src/components/CnSchemaFormDialog/CnSchemaFormDialog.vue +787 -0
- package/src/components/CnSchemaFormDialog/CnSchemaPropertiesTab.vue +305 -0
- package/src/components/CnSchemaFormDialog/CnSchemaPropertyActions.vue +1398 -0
- package/src/components/CnSchemaFormDialog/CnSchemaSecurityTab.vue +236 -0
- package/src/components/CnSchemaFormDialog/index.js +1 -0
- package/src/components/CnSettingsCard/index.js +1 -1
- package/src/components/CnSettingsSection/index.js +1 -1
- package/src/components/CnStatsBlock/CnStatsBlock.vue +89 -19
- package/src/components/CnStatsBlock/index.js +1 -1
- package/src/components/CnStatsPanel/CnStatsPanel.vue +320 -0
- package/src/components/CnStatsPanel/index.js +1 -0
- package/src/components/CnStatusBadge/CnStatusBadge.vue +15 -2
- package/src/components/CnStatusBadge/index.js +1 -1
- package/src/components/CnTabbedFormDialog/CnTabbedFormDialog.vue +544 -0
- package/src/components/CnTabbedFormDialog/index.js +1 -0
- package/src/components/CnTableWidget/CnTableWidget.vue +332 -0
- package/src/components/CnTableWidget/index.js +1 -0
- package/src/components/CnTasksCard/CnTasksCard.vue +373 -0
- package/src/components/CnTasksCard/index.js +1 -0
- package/src/components/CnTileWidget/CnTileWidget.vue +159 -0
- package/src/components/CnTileWidget/index.js +1 -0
- package/src/components/CnTimelineStages/CnTimelineStages.vue +292 -0
- package/src/components/CnTimelineStages/index.js +1 -0
- package/src/components/CnUserActionMenu/CnUserActionMenu.vue +435 -0
- package/src/components/CnUserActionMenu/index.js +1 -0
- package/src/components/CnVersionInfoCard/index.js +1 -1
- package/src/components/CnWidgetRenderer/CnWidgetRenderer.vue +180 -0
- package/src/components/CnWidgetRenderer/index.js +1 -0
- package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +246 -0
- package/src/components/CnWidgetWrapper/index.js +1 -0
- package/src/components/index.js +57 -25
- package/src/composables/index.js +5 -3
- package/src/composables/useContextMenu.js +126 -0
- package/src/composables/useDashboardView.js +286 -0
- package/src/composables/useDetailView.js +290 -132
- package/src/composables/useListView.js +364 -153
- package/src/composables/useSubResource.js +142 -142
- package/src/constants/metadata.js +30 -0
- package/src/css/CnSchemaFormDialog.css +546 -0
- package/src/css/__sample_nextcloud_tokens.css +110 -0
- package/src/css/actions-bar.css +54 -0
- package/src/css/badge.css +83 -51
- package/src/css/card.css +129 -128
- package/src/css/context-menu.css +20 -0
- package/src/css/dashboard.css +70 -0
- package/src/css/detail-page.css +235 -0
- package/src/css/detail.css +68 -68
- package/src/css/index-page.css +44 -0
- package/src/css/index-sidebar.css +193 -0
- package/src/css/index.css +17 -8
- package/src/css/layout.css +90 -90
- package/src/css/page-header.css +35 -0
- package/src/css/pagination.css +72 -72
- package/src/css/table.css +142 -143
- package/src/css/timeline-stages.css +220 -0
- package/src/css/utilities.css +46 -46
- package/src/index.js +91 -50
- package/src/mixins/gridLayout.js +118 -0
- package/src/store/createCrudStore.js +360 -0
- package/src/store/createSubResourcePlugin.js +125 -135
- package/src/store/index.js +4 -3
- package/src/store/plugins/auditTrails.js +357 -17
- package/src/store/plugins/files.js +250 -186
- package/src/store/plugins/index.js +7 -4
- package/src/store/plugins/lifecycle.js +180 -180
- package/src/store/plugins/registerMapping.js +195 -0
- package/src/store/plugins/relations.js +68 -68
- package/src/store/plugins/search.js +385 -0
- package/src/store/plugins/selection.js +104 -0
- package/src/store/useObjectStore.js +823 -625
- package/src/types/auditTrail.d.ts +32 -32
- package/src/types/file.d.ts +23 -23
- package/src/types/index.d.ts +35 -35
- package/src/types/notification.d.ts +36 -36
- package/src/types/object.d.ts +40 -40
- package/src/types/organisation.d.ts +41 -41
- package/src/types/register.d.ts +25 -25
- package/src/types/schema.d.ts +39 -39
- package/src/types/shared.d.ts +79 -79
- package/src/types/source.d.ts +14 -14
- package/src/types/task.d.ts +31 -31
- package/src/utils/errors.js +96 -96
- package/src/utils/getTheme.js +9 -0
- package/src/utils/headers.js +80 -44
- package/src/utils/id.js +13 -0
- package/src/utils/index.js +4 -3
- package/src/utils/schema.js +422 -287
- package/src/utils/widgetVisibility.js +162 -0
- package/src/components/CnDetailViewLayout/CnDetailViewLayout.vue +0 -88
- package/src/components/CnDetailViewLayout/index.js +0 -1
- package/src/components/CnEmptyState/CnEmptyState.vue +0 -78
- package/src/components/CnEmptyState/index.js +0 -1
- package/src/components/CnListViewLayout/CnListViewLayout.vue +0 -80
- package/src/components/CnListViewLayout/index.js +0 -1
- package/src/components/CnViewModeToggle/CnViewModeToggle.vue +0 -77
- package/src/components/CnViewModeToggle/index.js +0 -1
|
@@ -1,57 +1,73 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="cn-index-page">
|
|
3
|
-
<!-- Header -->
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
<CnMassActionBar
|
|
14
|
-
v-if="selectable"
|
|
15
|
-
:selected-ids="selectedIds"
|
|
16
|
-
:count="selectedIds.length"
|
|
17
|
-
:show-import="showMassImport"
|
|
18
|
-
:show-export="showMassExport"
|
|
19
|
-
:show-copy="showMassCopy"
|
|
20
|
-
:show-delete="showMassDelete"
|
|
21
|
-
@mass-import="showImportDialog = true"
|
|
22
|
-
@mass-export="showExportDialog = true"
|
|
23
|
-
@mass-copy="showCopyDialog = true"
|
|
24
|
-
@mass-delete="showDeleteDialog = true">
|
|
25
|
-
<template #actions="{ count: selCount, selectedIds: selIds }">
|
|
26
|
-
<slot name="mass-actions" :count="selCount" :selected-ids="selIds" />
|
|
27
|
-
</template>
|
|
28
|
-
</CnMassActionBar>
|
|
29
|
-
|
|
30
|
-
<CnViewModeToggle
|
|
31
|
-
v-if="showViewToggle"
|
|
32
|
-
:value="currentViewMode"
|
|
33
|
-
@input="onViewModeChange" />
|
|
34
|
-
<slot name="header-actions" />
|
|
35
|
-
</div>
|
|
3
|
+
<!-- Header (hidden by default — shown in sidebar instead) -->
|
|
4
|
+
<CnPageHeader
|
|
5
|
+
v-if="showTitle"
|
|
6
|
+
:title="title"
|
|
7
|
+
:description="description"
|
|
8
|
+
:icon="resolvedIcon" />
|
|
9
|
+
|
|
10
|
+
<!-- Optional content below header, above actions bar -->
|
|
11
|
+
<div v-if="$scopedSlots['below-header']" class="cn-index-page__below-header">
|
|
12
|
+
<slot name="below-header" />
|
|
36
13
|
</div>
|
|
37
14
|
|
|
15
|
+
<!-- Actions bar -->
|
|
16
|
+
<CnActionsBar
|
|
17
|
+
:pagination="pagination"
|
|
18
|
+
:object-count="objects.length"
|
|
19
|
+
:selectable="selectable"
|
|
20
|
+
:selected-ids="internalSelectedIds"
|
|
21
|
+
:add-label="resolvedAddLabel"
|
|
22
|
+
:add-icon="resolvedIcon"
|
|
23
|
+
:inline-action-count="inlineActionCount"
|
|
24
|
+
:show-mass-import="showMassImport"
|
|
25
|
+
:show-mass-export="showMassExport"
|
|
26
|
+
:show-mass-copy="showMassCopy"
|
|
27
|
+
:show-mass-delete="showMassDelete"
|
|
28
|
+
:view-mode="currentViewMode"
|
|
29
|
+
:show-view-toggle="showViewToggle"
|
|
30
|
+
:refreshing="refreshing"
|
|
31
|
+
:refresh-disabled="refreshDisabled"
|
|
32
|
+
:add-disabled="addDisabled"
|
|
33
|
+
:show-add="showAdd"
|
|
34
|
+
@add="onAddClick"
|
|
35
|
+
@refresh="$emit('refresh')"
|
|
36
|
+
@show-import="showImportDialog = true"
|
|
37
|
+
@show-export="showExportDialog = true"
|
|
38
|
+
@show-copy="showMassCopyDialog = true"
|
|
39
|
+
@show-delete="showMassDeleteDialog = true"
|
|
40
|
+
@view-mode-change="onViewModeChange">
|
|
41
|
+
<template v-if="$scopedSlots['mass-actions']" #mass-actions="{ count, selectedIds: ids }">
|
|
42
|
+
<slot name="mass-actions" :count="count" :selected-ids="ids" />
|
|
43
|
+
</template>
|
|
44
|
+
<template v-if="$scopedSlots['action-items']" #action-items>
|
|
45
|
+
<slot name="action-items" />
|
|
46
|
+
</template>
|
|
47
|
+
<template v-if="$scopedSlots['actions']" #actions>
|
|
48
|
+
<slot name="actions" />
|
|
49
|
+
</template>
|
|
50
|
+
</CnActionsBar>
|
|
51
|
+
|
|
38
52
|
<!-- Mass delete dialog -->
|
|
39
53
|
<CnMassDeleteDialog
|
|
40
|
-
v-if="
|
|
41
|
-
ref="
|
|
54
|
+
v-if="showMassDeleteDialog"
|
|
55
|
+
ref="massDeleteDialog"
|
|
42
56
|
:items="selectedObjects"
|
|
43
57
|
:name-field="massActionNameField"
|
|
58
|
+
:name-formatter="nameFormatter"
|
|
44
59
|
@confirm="onMassDeleteConfirm"
|
|
45
|
-
@close="
|
|
60
|
+
@close="showMassDeleteDialog = false" />
|
|
46
61
|
|
|
47
62
|
<!-- Mass copy dialog -->
|
|
48
63
|
<CnMassCopyDialog
|
|
49
|
-
v-if="
|
|
50
|
-
ref="
|
|
64
|
+
v-if="showMassCopyDialog"
|
|
65
|
+
ref="massCopyDialog"
|
|
51
66
|
:items="selectedObjects"
|
|
52
67
|
:name-field="massActionNameField"
|
|
68
|
+
:name-formatter="nameFormatter"
|
|
53
69
|
@confirm="onMassCopyConfirm"
|
|
54
|
-
@close="
|
|
70
|
+
@close="showMassCopyDialog = false" />
|
|
55
71
|
|
|
56
72
|
<!-- Mass export dialog -->
|
|
57
73
|
<CnMassExportDialog
|
|
@@ -73,35 +89,73 @@
|
|
|
73
89
|
</template>
|
|
74
90
|
</CnMassImportDialog>
|
|
75
91
|
|
|
76
|
-
<!--
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
<!-- Single delete dialog (overridable via slot) -->
|
|
93
|
+
<slot
|
|
94
|
+
name="delete-dialog"
|
|
95
|
+
:item="actionTargetItem"
|
|
96
|
+
:close="closeSingleDelete">
|
|
97
|
+
<CnDeleteDialog
|
|
98
|
+
v-if="showSingleDeleteDialog && actionTargetItem"
|
|
99
|
+
ref="singleDeleteDialog"
|
|
100
|
+
:item="actionTargetItem"
|
|
101
|
+
:name-field="massActionNameField"
|
|
102
|
+
:name-formatter="nameFormatter"
|
|
103
|
+
@confirm="onSingleDeleteConfirm"
|
|
104
|
+
@close="closeSingleDelete" />
|
|
105
|
+
</slot>
|
|
106
|
+
|
|
107
|
+
<!-- Single copy dialog (overridable via slot) -->
|
|
108
|
+
<slot
|
|
109
|
+
name="copy-dialog"
|
|
110
|
+
:item="actionTargetItem"
|
|
111
|
+
:close="closeSingleCopy">
|
|
112
|
+
<CnCopyDialog
|
|
113
|
+
v-if="showSingleCopyDialog && actionTargetItem"
|
|
114
|
+
ref="singleCopyDialog"
|
|
115
|
+
:item="actionTargetItem"
|
|
116
|
+
:name-field="massActionNameField"
|
|
117
|
+
:name-formatter="nameFormatter"
|
|
118
|
+
@confirm="onSingleCopyConfirm"
|
|
119
|
+
@close="closeSingleCopy" />
|
|
120
|
+
</slot>
|
|
121
|
+
|
|
122
|
+
<!-- Form dialog for create/edit (overridable via slot) -->
|
|
123
|
+
<slot
|
|
124
|
+
name="form-dialog"
|
|
125
|
+
:item="editItem"
|
|
126
|
+
:schema="schema"
|
|
127
|
+
:close="closeFormDialog">
|
|
128
|
+
<CnFormDialog
|
|
129
|
+
v-if="showFormDialogVisible && !useAdvancedFormDialog"
|
|
130
|
+
ref="formDialog"
|
|
131
|
+
:schema="schema"
|
|
132
|
+
:item="editItem"
|
|
133
|
+
:exclude-fields="excludeFields"
|
|
134
|
+
:include-fields="includeFields"
|
|
135
|
+
:field-overrides="fieldOverrides"
|
|
136
|
+
:name-field="massActionNameField"
|
|
137
|
+
@confirm="onFormConfirm"
|
|
138
|
+
@close="closeFormDialog">
|
|
139
|
+
<template v-if="$scopedSlots['form-fields']" #form="scope">
|
|
140
|
+
<slot name="form-fields" v-bind="scope" />
|
|
141
|
+
</template>
|
|
142
|
+
</CnFormDialog>
|
|
143
|
+
<CnAdvancedFormDialog
|
|
144
|
+
v-if="showFormDialogVisible && useAdvancedFormDialog"
|
|
145
|
+
ref="formDialog"
|
|
146
|
+
:schema="schema"
|
|
147
|
+
:item="editItem"
|
|
148
|
+
:exclude-fields="excludeFields"
|
|
149
|
+
:include-fields="includeFields"
|
|
150
|
+
:field-overrides="fieldOverrides"
|
|
151
|
+
:name-field="massActionNameField"
|
|
152
|
+
@confirm="onFormConfirm"
|
|
153
|
+
@close="closeFormDialog" />
|
|
154
|
+
</slot>
|
|
155
|
+
|
|
156
|
+
<!-- Body -->
|
|
157
|
+
<div class="cn-index-page__body">
|
|
93
158
|
<div class="cn-index-page__main">
|
|
94
|
-
<!-- Search bar -->
|
|
95
|
-
<div v-if="showSearch" class="cn-index-page__search">
|
|
96
|
-
<CnFilterBar
|
|
97
|
-
:search-value="searchValue"
|
|
98
|
-
:search-placeholder="searchPlaceholder"
|
|
99
|
-
:filters="inlineFilters"
|
|
100
|
-
:show-clear-all="false"
|
|
101
|
-
@search="$emit('search', $event)"
|
|
102
|
-
@filter-change="$emit('filter-change', $event)" />
|
|
103
|
-
</div>
|
|
104
|
-
|
|
105
159
|
<!-- Loading state -->
|
|
106
160
|
<div v-if="loading" class="cn-index-page__loading">
|
|
107
161
|
<NcLoadingIcon :size="32" />
|
|
@@ -112,7 +166,8 @@
|
|
|
112
166
|
<slot name="empty">
|
|
113
167
|
<NcEmptyContent :name="emptyText">
|
|
114
168
|
<template #icon>
|
|
115
|
-
<
|
|
169
|
+
<CnIcon v-if="resolvedIcon" :name="resolvedIcon" :size="64" />
|
|
170
|
+
<DatabaseSearch v-else :size="64" />
|
|
116
171
|
</template>
|
|
117
172
|
</NcEmptyContent>
|
|
118
173
|
</slot>
|
|
@@ -127,7 +182,7 @@
|
|
|
127
182
|
:sort-key="sortKey"
|
|
128
183
|
:sort-order="sortOrder"
|
|
129
184
|
:selectable="selectable"
|
|
130
|
-
:selected-ids="
|
|
185
|
+
:selected-ids="internalSelectedIds"
|
|
131
186
|
:row-key="rowKey"
|
|
132
187
|
:empty-text="emptyText"
|
|
133
188
|
:exclude-columns="excludeColumns"
|
|
@@ -135,8 +190,9 @@
|
|
|
135
190
|
:column-overrides="columnOverrides"
|
|
136
191
|
:row-class="rowClass"
|
|
137
192
|
@sort="$emit('sort', $event)"
|
|
138
|
-
@select="
|
|
139
|
-
@row-click="
|
|
193
|
+
@select="onSelect"
|
|
194
|
+
@row-click="onRowClick"
|
|
195
|
+
@row-context-menu="onRowContextMenu">
|
|
140
196
|
<!-- Pass through column slots -->
|
|
141
197
|
<template
|
|
142
198
|
v-for="col in slotColumns"
|
|
@@ -148,8 +204,7 @@
|
|
|
148
204
|
<template v-if="hasRowActions" #row-actions="{ row }">
|
|
149
205
|
<slot name="row-actions" :row="row">
|
|
150
206
|
<CnRowActions
|
|
151
|
-
|
|
152
|
-
:actions="actions"
|
|
207
|
+
:actions="mergedActions"
|
|
153
208
|
:row="row"
|
|
154
209
|
@action="$emit('action', $event)" />
|
|
155
210
|
</slot>
|
|
@@ -162,25 +217,32 @@
|
|
|
162
217
|
:objects="objects"
|
|
163
218
|
:schema="schema"
|
|
164
219
|
:selectable="selectable"
|
|
165
|
-
:selected-ids="
|
|
220
|
+
:selected-ids="internalSelectedIds"
|
|
166
221
|
:row-key="rowKey"
|
|
167
222
|
:empty-text="emptyText"
|
|
168
|
-
@click="
|
|
169
|
-
@select="
|
|
223
|
+
@click="onRowClick"
|
|
224
|
+
@select="onSelect">
|
|
170
225
|
<template v-if="$scopedSlots.card" #card="{ object, selected }">
|
|
171
226
|
<slot name="card" :object="object" :selected="selected" />
|
|
172
227
|
</template>
|
|
173
228
|
<template v-if="hasRowActions" #card-actions="{ object }">
|
|
174
229
|
<slot name="row-actions" :row="object">
|
|
175
230
|
<CnRowActions
|
|
176
|
-
|
|
177
|
-
:actions="actions"
|
|
231
|
+
:actions="mergedActions"
|
|
178
232
|
:row="object"
|
|
179
233
|
@action="$emit('action', $event)" />
|
|
180
234
|
</slot>
|
|
181
235
|
</template>
|
|
182
236
|
</CnCardGrid>
|
|
183
237
|
|
|
238
|
+
<!-- Right-click context menu (positioned at cursor via CSS) -->
|
|
239
|
+
<CnContextMenu
|
|
240
|
+
:open.sync="contextMenuOpen"
|
|
241
|
+
:actions="mergedActions"
|
|
242
|
+
:target-item="contextMenuRow"
|
|
243
|
+
@action="$emit('action', $event)"
|
|
244
|
+
@close="closeContextMenu" />
|
|
245
|
+
|
|
184
246
|
<!-- Pagination -->
|
|
185
247
|
<CnPagination
|
|
186
248
|
v-if="pagination && pagination.pages > 1"
|
|
@@ -199,83 +261,93 @@
|
|
|
199
261
|
<script>
|
|
200
262
|
import { NcLoadingIcon, NcEmptyContent } from '@nextcloud/vue'
|
|
201
263
|
import DatabaseSearch from 'vue-material-design-icons/DatabaseSearch.vue'
|
|
264
|
+
import Eye from 'vue-material-design-icons/Eye.vue'
|
|
265
|
+
import Pencil from 'vue-material-design-icons/Pencil.vue'
|
|
266
|
+
import ContentCopy from 'vue-material-design-icons/ContentCopy.vue'
|
|
267
|
+
import TrashCanOutline from 'vue-material-design-icons/TrashCanOutline.vue'
|
|
268
|
+
import { CnPageHeader } from '../CnPageHeader/index.js'
|
|
269
|
+
import { CnActionsBar } from '../CnActionsBar/index.js'
|
|
270
|
+
import { CnIcon, ICON_MAP } from '../CnIcon/index.js'
|
|
202
271
|
import { CnDataTable } from '../CnDataTable/index.js'
|
|
203
272
|
import { CnCardGrid } from '../CnCardGrid/index.js'
|
|
204
273
|
import { CnPagination } from '../CnPagination/index.js'
|
|
205
|
-
import { CnFilterBar } from '../CnFilterBar/index.js'
|
|
206
|
-
import { CnFacetSidebar } from '../CnFacetSidebar/index.js'
|
|
207
|
-
import { CnViewModeToggle } from '../CnViewModeToggle/index.js'
|
|
208
274
|
import { CnRowActions } from '../CnRowActions/index.js'
|
|
209
|
-
import { CnMassActionBar } from '../CnMassActionBar/index.js'
|
|
210
275
|
import { CnMassDeleteDialog } from '../CnMassDeleteDialog/index.js'
|
|
211
276
|
import { CnMassCopyDialog } from '../CnMassCopyDialog/index.js'
|
|
212
277
|
import { CnMassExportDialog } from '../CnMassExportDialog/index.js'
|
|
213
278
|
import { CnMassImportDialog } from '../CnMassImportDialog/index.js'
|
|
279
|
+
import { CnDeleteDialog } from '../CnDeleteDialog/index.js'
|
|
280
|
+
import { CnCopyDialog } from '../CnCopyDialog/index.js'
|
|
281
|
+
import { CnFormDialog } from '../CnFormDialog/index.js'
|
|
282
|
+
import { CnAdvancedFormDialog } from '../CnAdvancedFormDialog/index.js'
|
|
283
|
+
import { CnContextMenu } from '../CnContextMenu/index.js'
|
|
284
|
+
import { useContextMenu } from '../../composables/index.js'
|
|
214
285
|
|
|
215
286
|
/**
|
|
216
287
|
* CnIndexPage — Top-level schema-driven index page component.
|
|
217
288
|
*
|
|
218
|
-
* Assembles
|
|
219
|
-
*
|
|
220
|
-
*
|
|
289
|
+
* Assembles sub-components (CnPageHeader, CnActionsBar, table, cards,
|
|
290
|
+
* pagination, mass actions, single-object dialogs) into a single
|
|
291
|
+
* zero-config page.
|
|
221
292
|
*
|
|
222
|
-
*
|
|
293
|
+
* Dialogs are overridable via named slots:
|
|
294
|
+
* - `#form-dialog` — Replace the create/edit dialog entirely
|
|
295
|
+
* - `#delete-dialog` — Replace the single-item delete dialog
|
|
296
|
+
* - `#copy-dialog` — Replace the single-item copy dialog
|
|
297
|
+
* - `#form-fields` — Replace only the form content inside the built-in form dialog (CnFormDialog only)
|
|
298
|
+
*
|
|
299
|
+
* Use the `useAdvancedFormDialog` prop to use CnAdvancedFormDialog for create/edit (properties table, JSON tab, optional metadata).
|
|
300
|
+
*
|
|
301
|
+
* @example Minimal usage (auto-generated dialogs from schema)
|
|
223
302
|
* <CnIndexPage
|
|
224
|
-
* title="
|
|
303
|
+
* title="Clients"
|
|
225
304
|
* :schema="schema"
|
|
226
|
-
* :objects="
|
|
305
|
+
* :objects="clients"
|
|
227
306
|
* :pagination="pagination"
|
|
228
307
|
* :loading="loading"
|
|
229
|
-
*
|
|
230
|
-
* @
|
|
231
|
-
* @
|
|
308
|
+
* @create="onCreate"
|
|
309
|
+
* @edit="onEdit"
|
|
310
|
+
* @delete="onDelete"
|
|
311
|
+
* @refresh="fetchClients"
|
|
312
|
+
* @row-click="openClient"
|
|
232
313
|
* @page-changed="onPage" />
|
|
233
314
|
*
|
|
234
|
-
* @example
|
|
235
|
-
* <CnIndexPage
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
* :schema="caseSchema"
|
|
239
|
-
* :objects="cases"
|
|
240
|
-
* :pagination="pagination"
|
|
241
|
-
* :loading="loading"
|
|
242
|
-
* :search-value="search"
|
|
243
|
-
* :selected-ids="selectedIds"
|
|
244
|
-
* :facet-data="facetData"
|
|
245
|
-
* :active-filters="filters"
|
|
246
|
-
* :actions="[{ label: 'Edit', handler: editCase }]"
|
|
247
|
-
* @search="onSearch"
|
|
248
|
-
* @select="selectedIds = $event"
|
|
249
|
-
* @row-click="openCase"
|
|
250
|
-
* @mass-delete="onMassDelete"
|
|
251
|
-
* @mass-copy="onMassCopy">
|
|
252
|
-
* <template #header-actions>
|
|
253
|
-
* <NcButton type="primary" @click="createCase">New case</NcButton>
|
|
254
|
-
* </template>
|
|
255
|
-
* <template #mass-actions="{ count, selectedIds }">
|
|
256
|
-
* <NcButton @click="exportSelected(selectedIds)">Export {{ count }}</NcButton>
|
|
315
|
+
* @example With custom form dialog
|
|
316
|
+
* <CnIndexPage ...>
|
|
317
|
+
* <template #form-dialog="{ item, schema, close }">
|
|
318
|
+
* <MyCustomFormDialog :item="item" @close="close" />
|
|
257
319
|
* </template>
|
|
258
320
|
* </CnIndexPage>
|
|
259
321
|
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
* }
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
322
|
+
* @event {void} add — Add button clicked (backward compat, only if listener attached)
|
|
323
|
+
* @event {object} create — Form dialog create confirmed. Payload: formData object
|
|
324
|
+
* @event {object} edit — Form dialog edit confirmed. Payload: formData object (includes id)
|
|
325
|
+
* @event {string} delete — Single delete confirmed. Payload: item ID
|
|
326
|
+
* @event {{ id: string, newName: string }} copy — Single copy confirmed
|
|
327
|
+
* @event {string[]} mass-delete — Mass delete confirmed. Payload: array of IDs
|
|
328
|
+
* @event {object} mass-copy — Mass copy confirmed. Payload: { ids, pattern }
|
|
329
|
+
* @event {object} mass-export — Mass export confirmed. Payload: { ids, format }
|
|
330
|
+
* @event {object} mass-import — Mass import confirmed. Payload: import data
|
|
331
|
+
* @event {void} refresh — Refresh button clicked
|
|
332
|
+
* @event {object} row-click — Table row or card clicked. Payload: row object
|
|
333
|
+
* @event {{ key: string, order: string }} sort — Column sort changed
|
|
334
|
+
* @event {number} page-changed — Pagination page changed
|
|
335
|
+
* @event {number} page-size-changed — Pagination page size changed
|
|
336
|
+
* @event {string[]} select — Selection changed. Payload: array of selected IDs
|
|
337
|
+
* @event {object} action — Row action triggered. Payload: { action, row }
|
|
338
|
+
*
|
|
339
|
+
* @slot mass-actions — Extra mass action buttons (shown when items are selected)
|
|
340
|
+
* @slot action-items — Extra action bar buttons
|
|
341
|
+
* @slot header-actions — Extra buttons in the page header
|
|
342
|
+
* @slot delete-dialog — Replace the single-item delete dialog. Scope: `{ item, close }`
|
|
343
|
+
* @slot copy-dialog — Replace the single-item copy dialog. Scope: `{ item, close }`
|
|
344
|
+
* @slot form-dialog — Replace the create/edit form dialog. Scope: `{ item, schema, close }`
|
|
345
|
+
* @slot form-fields — Replace form content inside the built-in CnFormDialog. Scope: `{ fields, formData, errors, updateField }`
|
|
346
|
+
* @slot import-fields — Extra fields in the import dialog
|
|
347
|
+
* @slot empty — Custom empty state content
|
|
348
|
+
* @slot card — Custom card template for card view. Scope: `{ row }`
|
|
349
|
+
* @slot row-actions — Custom row actions. Scope: `{ row }`
|
|
350
|
+
* @slot column-{key} — Custom cell renderer for a specific column. Scope: `{ row, value }`
|
|
279
351
|
*/
|
|
280
352
|
export default {
|
|
281
353
|
name: 'CnIndexPage',
|
|
@@ -284,18 +356,22 @@ export default {
|
|
|
284
356
|
NcLoadingIcon,
|
|
285
357
|
NcEmptyContent,
|
|
286
358
|
DatabaseSearch,
|
|
359
|
+
CnPageHeader,
|
|
360
|
+
CnActionsBar,
|
|
361
|
+
CnIcon,
|
|
287
362
|
CnDataTable,
|
|
288
363
|
CnCardGrid,
|
|
289
364
|
CnPagination,
|
|
290
|
-
CnFilterBar,
|
|
291
|
-
CnFacetSidebar,
|
|
292
|
-
CnViewModeToggle,
|
|
293
365
|
CnRowActions,
|
|
294
|
-
CnMassActionBar,
|
|
295
366
|
CnMassDeleteDialog,
|
|
296
367
|
CnMassCopyDialog,
|
|
297
368
|
CnMassExportDialog,
|
|
298
369
|
CnMassImportDialog,
|
|
370
|
+
CnDeleteDialog,
|
|
371
|
+
CnCopyDialog,
|
|
372
|
+
CnFormDialog,
|
|
373
|
+
CnAdvancedFormDialog,
|
|
374
|
+
CnContextMenu,
|
|
299
375
|
},
|
|
300
376
|
|
|
301
377
|
props: {
|
|
@@ -304,6 +380,24 @@ export default {
|
|
|
304
380
|
type: String,
|
|
305
381
|
required: true,
|
|
306
382
|
},
|
|
383
|
+
/** Optional description shown below the title */
|
|
384
|
+
description: {
|
|
385
|
+
type: String,
|
|
386
|
+
default: '',
|
|
387
|
+
},
|
|
388
|
+
/**
|
|
389
|
+
* Whether to show the page header (icon, title, description) inline.
|
|
390
|
+
* When false (default), the title is shown in the sidebar header instead.
|
|
391
|
+
*/
|
|
392
|
+
showTitle: {
|
|
393
|
+
type: Boolean,
|
|
394
|
+
default: false,
|
|
395
|
+
},
|
|
396
|
+
/** Optional MDI icon name. Defaults to schema.icon when a schema is provided. */
|
|
397
|
+
icon: {
|
|
398
|
+
type: String,
|
|
399
|
+
default: '',
|
|
400
|
+
},
|
|
307
401
|
/** Schema definition */
|
|
308
402
|
schema: {
|
|
309
403
|
type: Object,
|
|
@@ -329,36 +423,6 @@ export default {
|
|
|
329
423
|
type: Boolean,
|
|
330
424
|
default: false,
|
|
331
425
|
},
|
|
332
|
-
/** Current search term */
|
|
333
|
-
searchValue: {
|
|
334
|
-
type: String,
|
|
335
|
-
default: '',
|
|
336
|
-
},
|
|
337
|
-
/** Search input placeholder */
|
|
338
|
-
searchPlaceholder: {
|
|
339
|
-
type: String,
|
|
340
|
-
default: 'Search...',
|
|
341
|
-
},
|
|
342
|
-
/** Inline filter definitions (shown in the search bar) */
|
|
343
|
-
inlineFilters: {
|
|
344
|
-
type: Array,
|
|
345
|
-
default: () => [],
|
|
346
|
-
},
|
|
347
|
-
/** Facet data from API: { fieldName: { values: [{value, count}] } } */
|
|
348
|
-
facetData: {
|
|
349
|
-
type: Object,
|
|
350
|
-
default: null,
|
|
351
|
-
},
|
|
352
|
-
/** Current active facet filters: { fieldName: [values] } */
|
|
353
|
-
activeFilters: {
|
|
354
|
-
type: Object,
|
|
355
|
-
default: () => ({}),
|
|
356
|
-
},
|
|
357
|
-
/** Whether facet data is loading */
|
|
358
|
-
facetLoading: {
|
|
359
|
-
type: Boolean,
|
|
360
|
-
default: false,
|
|
361
|
-
},
|
|
362
426
|
/** Whether rows/cards can be selected */
|
|
363
427
|
selectable: {
|
|
364
428
|
type: Boolean,
|
|
@@ -405,7 +469,7 @@ export default {
|
|
|
405
469
|
type: Object,
|
|
406
470
|
default: () => ({}),
|
|
407
471
|
},
|
|
408
|
-
/** Row action definitions */
|
|
472
|
+
/** Row action definitions (app-provided, merged with built-in actions) */
|
|
409
473
|
actions: {
|
|
410
474
|
type: Array,
|
|
411
475
|
default: () => [],
|
|
@@ -415,21 +479,21 @@ export default {
|
|
|
415
479
|
type: String,
|
|
416
480
|
default: 'No items found',
|
|
417
481
|
},
|
|
418
|
-
/** Whether to show the view mode toggle */
|
|
419
|
-
showViewToggle: {
|
|
420
|
-
type: Boolean,
|
|
421
|
-
default: true,
|
|
422
|
-
},
|
|
423
|
-
/** Whether to show the search bar */
|
|
424
|
-
showSearch: {
|
|
425
|
-
type: Boolean,
|
|
426
|
-
default: true,
|
|
427
|
-
},
|
|
428
482
|
/** Function returning CSS class(es) for a row */
|
|
429
483
|
rowClass: {
|
|
430
484
|
type: Function,
|
|
431
485
|
default: null,
|
|
432
486
|
},
|
|
487
|
+
/** Override label for the Add button. Defaults to "Add {schema.title}" */
|
|
488
|
+
addLabel: {
|
|
489
|
+
type: String,
|
|
490
|
+
default: '',
|
|
491
|
+
},
|
|
492
|
+
/** How many action buttons to show inline (rest go in overflow dropdown) */
|
|
493
|
+
inlineActionCount: {
|
|
494
|
+
type: Number,
|
|
495
|
+
default: 2,
|
|
496
|
+
},
|
|
433
497
|
/** Whether to show the built-in mass Import action */
|
|
434
498
|
showMassImport: {
|
|
435
499
|
type: Boolean,
|
|
@@ -450,11 +514,16 @@ export default {
|
|
|
450
514
|
type: Boolean,
|
|
451
515
|
default: true,
|
|
452
516
|
},
|
|
453
|
-
/** Property name used to display item names in
|
|
517
|
+
/** Property name used to display item names in dialogs */
|
|
454
518
|
massActionNameField: {
|
|
455
519
|
type: String,
|
|
456
520
|
default: 'title',
|
|
457
521
|
},
|
|
522
|
+
/** Optional function to format item names in dialogs. Receives the item, returns a string. Overrides massActionNameField when provided. */
|
|
523
|
+
nameFormatter: {
|
|
524
|
+
type: Function,
|
|
525
|
+
default: null,
|
|
526
|
+
},
|
|
458
527
|
/** Available export formats for the export dialog */
|
|
459
528
|
exportFormats: {
|
|
460
529
|
type: Array,
|
|
@@ -468,41 +537,199 @@ export default {
|
|
|
468
537
|
type: Array,
|
|
469
538
|
default: () => [],
|
|
470
539
|
},
|
|
540
|
+
/** Whether to show the built-in form dialog for Add/Edit */
|
|
541
|
+
showFormDialog: {
|
|
542
|
+
type: Boolean,
|
|
543
|
+
default: true,
|
|
544
|
+
},
|
|
545
|
+
/** Use CnAdvancedFormDialog (properties table, JSON tab, optional metadata) instead of CnFormDialog for Add/Edit */
|
|
546
|
+
useAdvancedFormDialog: {
|
|
547
|
+
type: Boolean,
|
|
548
|
+
default: false,
|
|
549
|
+
},
|
|
550
|
+
/** Whether to add an Edit action to row actions */
|
|
551
|
+
showEditAction: {
|
|
552
|
+
type: Boolean,
|
|
553
|
+
default: true,
|
|
554
|
+
},
|
|
555
|
+
/** Whether to add a Copy action to row actions */
|
|
556
|
+
showCopyAction: {
|
|
557
|
+
type: Boolean,
|
|
558
|
+
default: true,
|
|
559
|
+
},
|
|
560
|
+
/** Whether to add a Delete action to row actions */
|
|
561
|
+
showDeleteAction: {
|
|
562
|
+
type: Boolean,
|
|
563
|
+
default: true,
|
|
564
|
+
},
|
|
565
|
+
/** Field keys to exclude from the form dialog */
|
|
566
|
+
excludeFields: {
|
|
567
|
+
type: Array,
|
|
568
|
+
default: () => [],
|
|
569
|
+
},
|
|
570
|
+
/** Field keys to include in the form dialog (whitelist mode) */
|
|
571
|
+
includeFields: {
|
|
572
|
+
type: Array,
|
|
573
|
+
default: null,
|
|
574
|
+
},
|
|
575
|
+
/** Per-field overrides passed to CnFormDialog */
|
|
576
|
+
fieldOverrides: {
|
|
577
|
+
type: Object,
|
|
578
|
+
default: () => ({}),
|
|
579
|
+
},
|
|
580
|
+
/** Whether to show the Cards/Table view toggle in the actions bar */
|
|
581
|
+
showViewToggle: {
|
|
582
|
+
type: Boolean,
|
|
583
|
+
default: true,
|
|
584
|
+
},
|
|
585
|
+
/** Whether the refresh action is currently in progress */
|
|
586
|
+
refreshing: {
|
|
587
|
+
type: Boolean,
|
|
588
|
+
default: false,
|
|
589
|
+
},
|
|
590
|
+
/** Whether the refresh action is disabled (e.g. when required selections are missing) */
|
|
591
|
+
refreshDisabled: {
|
|
592
|
+
type: Boolean,
|
|
593
|
+
default: false,
|
|
594
|
+
},
|
|
595
|
+
/** Whether the Add button is disabled (e.g. when required selections are missing) */
|
|
596
|
+
addDisabled: {
|
|
597
|
+
type: Boolean,
|
|
598
|
+
default: false,
|
|
599
|
+
},
|
|
600
|
+
/** Whether to show the Add button in the actions bar */
|
|
601
|
+
showAdd: {
|
|
602
|
+
type: Boolean,
|
|
603
|
+
default: true,
|
|
604
|
+
},
|
|
605
|
+
/**
|
|
606
|
+
* Store instance for automatic save integration. When provided alongside
|
|
607
|
+
* objectType, the form dialog saves directly to the store instead of
|
|
608
|
+
* emitting create/edit events. The object type must already be registered
|
|
609
|
+
* in the store via registerObjectType() before passing the store here.
|
|
610
|
+
*/
|
|
611
|
+
store: { type: Object, default: null },
|
|
612
|
+
/**
|
|
613
|
+
* Object type slug for store integration (e.g. `${registerId}-${schemaId}`).
|
|
614
|
+
* Required when store is set — a console warning is emitted if missing.
|
|
615
|
+
*/
|
|
616
|
+
objectType: { type: String, default: '' },
|
|
617
|
+
},
|
|
618
|
+
|
|
619
|
+
setup() {
|
|
620
|
+
const {
|
|
621
|
+
isOpen: contextMenuOpen,
|
|
622
|
+
targetItem: contextMenuRow,
|
|
623
|
+
open: openContextMenu,
|
|
624
|
+
close: closeContextMenu,
|
|
625
|
+
} = useContextMenu()
|
|
626
|
+
|
|
627
|
+
return {
|
|
628
|
+
contextMenuOpen,
|
|
629
|
+
contextMenuRow,
|
|
630
|
+
openContextMenu,
|
|
631
|
+
closeContextMenu,
|
|
632
|
+
}
|
|
471
633
|
},
|
|
472
634
|
|
|
473
635
|
data() {
|
|
474
636
|
return {
|
|
475
637
|
currentViewMode: this.viewMode,
|
|
476
|
-
|
|
477
|
-
|
|
638
|
+
internalSelectedIds: [...this.selectedIds],
|
|
639
|
+
// Mass action dialogs
|
|
640
|
+
showMassDeleteDialog: false,
|
|
641
|
+
showMassCopyDialog: false,
|
|
478
642
|
showExportDialog: false,
|
|
479
643
|
showImportDialog: false,
|
|
644
|
+
// Single-object dialogs
|
|
645
|
+
showSingleDeleteDialog: false,
|
|
646
|
+
showSingleCopyDialog: false,
|
|
647
|
+
showFormDialogVisible: false,
|
|
648
|
+
// Dialog targets
|
|
649
|
+
actionTargetItem: null,
|
|
650
|
+
editItem: null,
|
|
480
651
|
}
|
|
481
652
|
},
|
|
482
653
|
|
|
483
654
|
computed: {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
655
|
+
/** Resolved icon — explicit prop overrides schema.icon */
|
|
656
|
+
resolvedIcon() {
|
|
657
|
+
if (this.icon) return this.icon
|
|
658
|
+
return this.schema?.icon || ''
|
|
487
659
|
},
|
|
488
660
|
|
|
489
|
-
|
|
490
|
-
|
|
661
|
+
/** Resolved schema icon component for View action */
|
|
662
|
+
schemaIconComponent() {
|
|
663
|
+
if (this.resolvedIcon && ICON_MAP[this.resolvedIcon]) {
|
|
664
|
+
return ICON_MAP[this.resolvedIcon]
|
|
665
|
+
}
|
|
666
|
+
return Eye
|
|
667
|
+
},
|
|
668
|
+
|
|
669
|
+
/** Built-in row actions based on show*Action props */
|
|
670
|
+
defaultActions() {
|
|
671
|
+
const builtIn = []
|
|
672
|
+
if (this.$listeners && this.$listeners['row-click']) {
|
|
673
|
+
builtIn.push({
|
|
674
|
+
label: 'View',
|
|
675
|
+
icon: this.schemaIconComponent,
|
|
676
|
+
handler: (row) => {
|
|
677
|
+
this.onRowClick(row)
|
|
678
|
+
},
|
|
679
|
+
})
|
|
680
|
+
}
|
|
681
|
+
if (this.showEditAction) {
|
|
682
|
+
builtIn.push({
|
|
683
|
+
label: 'Edit',
|
|
684
|
+
icon: Pencil,
|
|
685
|
+
handler: (row) => {
|
|
686
|
+
this.editItem = row
|
|
687
|
+
this.showFormDialogVisible = true
|
|
688
|
+
},
|
|
689
|
+
})
|
|
690
|
+
}
|
|
691
|
+
if (this.showCopyAction) {
|
|
692
|
+
builtIn.push({
|
|
693
|
+
label: 'Copy',
|
|
694
|
+
icon: ContentCopy,
|
|
695
|
+
handler: (row) => {
|
|
696
|
+
this.actionTargetItem = row
|
|
697
|
+
this.showSingleCopyDialog = true
|
|
698
|
+
},
|
|
699
|
+
})
|
|
700
|
+
}
|
|
701
|
+
if (this.showDeleteAction) {
|
|
702
|
+
builtIn.push({
|
|
703
|
+
label: 'Delete',
|
|
704
|
+
icon: TrashCanOutline,
|
|
705
|
+
destructive: true,
|
|
706
|
+
handler: (row) => {
|
|
707
|
+
this.actionTargetItem = row
|
|
708
|
+
this.showSingleDeleteDialog = true
|
|
709
|
+
},
|
|
710
|
+
})
|
|
711
|
+
}
|
|
712
|
+
return builtIn
|
|
713
|
+
},
|
|
714
|
+
|
|
715
|
+
/** Merged actions: app-provided first, then built-in defaults */
|
|
716
|
+
mergedActions() {
|
|
717
|
+
return [...this.actions, ...this.defaultActions]
|
|
491
718
|
},
|
|
492
719
|
|
|
493
720
|
hasRowActions() {
|
|
494
|
-
return this.$scopedSlots['row-actions'] || this.
|
|
721
|
+
return this.$scopedSlots['row-actions'] || this.mergedActions.length > 0
|
|
495
722
|
},
|
|
496
723
|
|
|
497
724
|
/** Whether all visible items are selected */
|
|
498
725
|
allSelected() {
|
|
499
|
-
if (this.objects.length === 0 || this.
|
|
500
|
-
return this.objects.every((o) => this.
|
|
726
|
+
if (this.objects.length === 0 || this.internalSelectedIds.length === 0) return false
|
|
727
|
+
return this.objects.every((o) => this.internalSelectedIds.includes(o[this.rowKey]))
|
|
501
728
|
},
|
|
502
729
|
|
|
503
730
|
/** Full objects for the selected IDs (used by mass action dialogs) */
|
|
504
731
|
selectedObjects() {
|
|
505
|
-
return this.objects.filter((o) => this.
|
|
732
|
+
return this.objects.filter((o) => this.internalSelectedIds.includes(o[this.rowKey]))
|
|
506
733
|
},
|
|
507
734
|
|
|
508
735
|
/** Column slot names that the parent has provided (for pass-through) */
|
|
@@ -511,79 +738,104 @@ export default {
|
|
|
511
738
|
.filter((name) => name.startsWith('column-'))
|
|
512
739
|
.map((name) => name.replace('column-', ''))
|
|
513
740
|
},
|
|
741
|
+
|
|
742
|
+
/** Add button label — derived from schema.title if not explicitly set */
|
|
743
|
+
resolvedAddLabel() {
|
|
744
|
+
if (this.addLabel) return this.addLabel
|
|
745
|
+
return 'Add ' + (this.schema?.title || 'Item')
|
|
746
|
+
},
|
|
514
747
|
},
|
|
515
748
|
|
|
516
749
|
watch: {
|
|
517
750
|
viewMode(val) {
|
|
518
751
|
this.currentViewMode = val
|
|
519
752
|
},
|
|
753
|
+
selectedIds(val) {
|
|
754
|
+
this.internalSelectedIds = [...val]
|
|
755
|
+
},
|
|
520
756
|
},
|
|
521
757
|
|
|
522
758
|
methods: {
|
|
759
|
+
/**
|
|
760
|
+
* Handle row click — emits row-click event for the parent to handle navigation.
|
|
761
|
+
* @param {object} row The clicked row object
|
|
762
|
+
*/
|
|
763
|
+
onRowClick(row) {
|
|
764
|
+
this.$emit('row-click', row)
|
|
765
|
+
},
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* Handle the Add button click. If the consumer listens to @add,
|
|
769
|
+
* emit the event (backward compatible). Otherwise open the form dialog.
|
|
770
|
+
*/
|
|
771
|
+
onAddClick() {
|
|
772
|
+
if (this.$listeners && this.$listeners.add) {
|
|
773
|
+
this.$emit('add')
|
|
774
|
+
} else if (this.showFormDialog) {
|
|
775
|
+
this.editItem = null
|
|
776
|
+
this.showFormDialogVisible = true
|
|
777
|
+
}
|
|
778
|
+
},
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Handle view mode toggle.
|
|
782
|
+
* @param {string} mode 'table' or 'cards'
|
|
783
|
+
*/
|
|
523
784
|
onViewModeChange(mode) {
|
|
524
785
|
this.currentViewMode = mode
|
|
525
786
|
this.$emit('view-mode-change', mode)
|
|
526
787
|
},
|
|
527
788
|
|
|
528
789
|
/**
|
|
529
|
-
* Handle
|
|
530
|
-
*
|
|
531
|
-
* @param {Array} ids Array of
|
|
790
|
+
* Handle selection changes from CnDataTable/CnCardGrid.
|
|
791
|
+
* Updates internal state and re-emits for parent.
|
|
792
|
+
* @param {Array} ids Array of selected row IDs
|
|
532
793
|
*/
|
|
794
|
+
onSelect(ids) {
|
|
795
|
+
this.internalSelectedIds = ids
|
|
796
|
+
this.$emit('select', ids)
|
|
797
|
+
},
|
|
798
|
+
|
|
799
|
+
// --- Mass action handlers ---
|
|
800
|
+
|
|
533
801
|
onMassDeleteConfirm(ids) {
|
|
534
802
|
this.$emit('mass-delete', ids)
|
|
535
803
|
},
|
|
536
804
|
|
|
537
|
-
/**
|
|
538
|
-
* Handle mass copy confirm. Emits 'mass-copy' with the payload.
|
|
539
|
-
* Parent should call `this.$refs.indexPage.setCopyResult(...)` when done.
|
|
540
|
-
* @param {{ ids: Array, getName: Function }} payload
|
|
541
|
-
*/
|
|
542
805
|
onMassCopyConfirm(payload) {
|
|
543
806
|
this.$emit('mass-copy', payload)
|
|
544
807
|
},
|
|
545
808
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
* @param {{ success?: boolean, error?: string }} resultData
|
|
549
|
-
* @public
|
|
550
|
-
*/
|
|
551
|
-
setDeleteResult(resultData) {
|
|
552
|
-
if (this.$refs.deleteDialog) {
|
|
553
|
-
this.$refs.deleteDialog.setResult(resultData)
|
|
554
|
-
}
|
|
809
|
+
onMassExportConfirm(payload) {
|
|
810
|
+
this.$emit('mass-export', payload)
|
|
555
811
|
},
|
|
556
812
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
* @param {{ success?: boolean, error?: string }} resultData
|
|
560
|
-
* @public
|
|
561
|
-
*/
|
|
562
|
-
setCopyResult(resultData) {
|
|
563
|
-
if (this.$refs.copyDialog) {
|
|
564
|
-
this.$refs.copyDialog.setResult(resultData)
|
|
565
|
-
}
|
|
813
|
+
onMassImportConfirm(payload) {
|
|
814
|
+
this.$emit('mass-import', payload)
|
|
566
815
|
},
|
|
567
816
|
|
|
568
817
|
/**
|
|
569
|
-
*
|
|
570
|
-
* @
|
|
818
|
+
* @param {*} resultData Result data to pass to the dialog
|
|
819
|
+
* @public
|
|
571
820
|
*/
|
|
572
|
-
|
|
573
|
-
this.$
|
|
821
|
+
setMassDeleteResult(resultData) {
|
|
822
|
+
if (this.$refs.massDeleteDialog) {
|
|
823
|
+
this.$refs.massDeleteDialog.setResult(resultData)
|
|
824
|
+
}
|
|
574
825
|
},
|
|
575
826
|
|
|
576
827
|
/**
|
|
577
|
-
*
|
|
578
|
-
* @
|
|
828
|
+
* @param {*} resultData Result data to pass to the dialog
|
|
829
|
+
* @public
|
|
579
830
|
*/
|
|
580
|
-
|
|
581
|
-
this.$
|
|
831
|
+
setMassCopyResult(resultData) {
|
|
832
|
+
if (this.$refs.massCopyDialog) {
|
|
833
|
+
this.$refs.massCopyDialog.setResult(resultData)
|
|
834
|
+
}
|
|
582
835
|
},
|
|
583
836
|
|
|
584
837
|
/**
|
|
585
|
-
*
|
|
586
|
-
* @param {{ success?: boolean, error?: string }} resultData
|
|
838
|
+
* @param {*} resultData Result data to pass to the dialog
|
|
587
839
|
* @public
|
|
588
840
|
*/
|
|
589
841
|
setExportResult(resultData) {
|
|
@@ -593,8 +845,7 @@ export default {
|
|
|
593
845
|
},
|
|
594
846
|
|
|
595
847
|
/**
|
|
596
|
-
*
|
|
597
|
-
* @param {{ success?: boolean, error?: string, summary?: object }} resultData
|
|
848
|
+
* @param {*} resultData Result data to pass to the dialog
|
|
598
849
|
* @public
|
|
599
850
|
*/
|
|
600
851
|
setImportResult(resultData) {
|
|
@@ -602,81 +853,128 @@ export default {
|
|
|
602
853
|
this.$refs.importDialog.setResult(resultData)
|
|
603
854
|
}
|
|
604
855
|
},
|
|
605
|
-
},
|
|
606
|
-
}
|
|
607
|
-
</script>
|
|
608
856
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
857
|
+
// --- Backward-compatible aliases ---
|
|
858
|
+
/**
|
|
859
|
+
* @param {*} resultData Result data to pass to the dialog
|
|
860
|
+
* @public
|
|
861
|
+
*/
|
|
862
|
+
setDeleteResult(resultData) {
|
|
863
|
+
this.setMassDeleteResult(resultData)
|
|
864
|
+
},
|
|
865
|
+
/**
|
|
866
|
+
* @param {*} resultData Result data to pass to the dialog
|
|
867
|
+
* @public
|
|
868
|
+
*/
|
|
869
|
+
setCopyResult(resultData) {
|
|
870
|
+
this.setMassCopyResult(resultData)
|
|
871
|
+
},
|
|
613
872
|
|
|
614
|
-
|
|
615
|
-
display: flex;
|
|
616
|
-
justify-content: space-between;
|
|
617
|
-
align-items: center;
|
|
618
|
-
margin-bottom: 16px;
|
|
619
|
-
flex-wrap: wrap;
|
|
620
|
-
gap: 12px;
|
|
621
|
-
}
|
|
873
|
+
// --- Single-object dialog handlers ---
|
|
622
874
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
gap: 8px;
|
|
627
|
-
}
|
|
875
|
+
onSingleDeleteConfirm(id) {
|
|
876
|
+
this.$emit('delete', id)
|
|
877
|
+
},
|
|
628
878
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
font-weight: 700;
|
|
633
|
-
}
|
|
879
|
+
onSingleCopyConfirm(payload) {
|
|
880
|
+
this.$emit('copy', payload)
|
|
881
|
+
},
|
|
634
882
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
883
|
+
async onFormConfirm(formData) {
|
|
884
|
+
if (this.store) {
|
|
885
|
+
if (!this.objectType) {
|
|
886
|
+
console.warn('[CnIndexPage] store prop is set but objectType is missing. Cannot save to store.')
|
|
887
|
+
return
|
|
888
|
+
}
|
|
889
|
+
const saved = await this.store.saveObject(this.objectType, formData)
|
|
890
|
+
if (saved) {
|
|
891
|
+
this.setFormResult({ success: true })
|
|
892
|
+
this.$emit(this.editItem ? 'edit' : 'create', saved)
|
|
893
|
+
} else {
|
|
894
|
+
const err = this.store.getError?.(this.objectType)
|
|
895
|
+
this.setFormResult({ error: (err && err.message) || 'Save failed' })
|
|
896
|
+
}
|
|
897
|
+
return
|
|
898
|
+
}
|
|
899
|
+
if (this.editItem) {
|
|
900
|
+
this.$emit('edit', formData)
|
|
901
|
+
} else {
|
|
902
|
+
this.$emit('create', formData)
|
|
903
|
+
}
|
|
904
|
+
},
|
|
639
905
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
}
|
|
906
|
+
closeSingleDelete() {
|
|
907
|
+
this.showSingleDeleteDialog = false
|
|
908
|
+
this.actionTargetItem = null
|
|
909
|
+
},
|
|
645
910
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
}
|
|
911
|
+
closeSingleCopy() {
|
|
912
|
+
this.showSingleCopyDialog = false
|
|
913
|
+
this.actionTargetItem = null
|
|
914
|
+
},
|
|
650
915
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
916
|
+
closeFormDialog() {
|
|
917
|
+
this.showFormDialogVisible = false
|
|
918
|
+
this.editItem = null
|
|
919
|
+
},
|
|
654
920
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
921
|
+
/**
|
|
922
|
+
* @param {*} resultData Result data to pass to the dialog
|
|
923
|
+
* @public
|
|
924
|
+
*/
|
|
925
|
+
setSingleDeleteResult(resultData) {
|
|
926
|
+
if (this.$refs.singleDeleteDialog) {
|
|
927
|
+
this.$refs.singleDeleteDialog.setResult(resultData)
|
|
928
|
+
}
|
|
929
|
+
},
|
|
658
930
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
931
|
+
/**
|
|
932
|
+
* @param {*} resultData Result data to pass to the dialog
|
|
933
|
+
* @public
|
|
934
|
+
*/
|
|
935
|
+
setSingleCopyResult(resultData) {
|
|
936
|
+
if (this.$refs.singleCopyDialog) {
|
|
937
|
+
this.$refs.singleCopyDialog.setResult(resultData)
|
|
938
|
+
}
|
|
939
|
+
},
|
|
663
940
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
941
|
+
/**
|
|
942
|
+
* @param {*} resultData Result data to pass to the dialog
|
|
943
|
+
* @public
|
|
944
|
+
*/
|
|
945
|
+
setFormResult(resultData) {
|
|
946
|
+
if (this.$refs.formDialog) {
|
|
947
|
+
this.$refs.formDialog.setResult(resultData)
|
|
948
|
+
}
|
|
949
|
+
},
|
|
667
950
|
|
|
668
|
-
|
|
669
|
-
display: flex;
|
|
670
|
-
justify-content: center;
|
|
671
|
-
padding: 60px;
|
|
672
|
-
}
|
|
951
|
+
// --- Context menu handlers ---
|
|
673
952
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
}
|
|
953
|
+
onRowContextMenu({ row, event }) {
|
|
954
|
+
this.openContextMenu({ item: row, event })
|
|
955
|
+
},
|
|
678
956
|
|
|
679
|
-
|
|
680
|
-
|
|
957
|
+
/**
|
|
958
|
+
* Programmatically open the form dialog.
|
|
959
|
+
* @param {object|null} item Pass null for create mode, or an object for edit mode
|
|
960
|
+
* @public
|
|
961
|
+
*/
|
|
962
|
+
openFormDialog(item = null) {
|
|
963
|
+
this.editItem = item
|
|
964
|
+
this.showFormDialogVisible = true
|
|
965
|
+
},
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* Programmatically open the single-item delete dialog.
|
|
969
|
+
* @param {object} item The item to delete
|
|
970
|
+
* @public
|
|
971
|
+
*/
|
|
972
|
+
openDeleteDialog(item) {
|
|
973
|
+
this.actionTargetItem = item
|
|
974
|
+
this.showSingleDeleteDialog = true
|
|
975
|
+
},
|
|
976
|
+
},
|
|
681
977
|
}
|
|
682
|
-
</
|
|
978
|
+
</script>
|
|
979
|
+
|
|
980
|
+
<!-- Styles in css/index-page.css -->
|