@conduction/nextcloud-vue 0.1.0-beta.1 → 0.1.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +226 -0
- package/dist/nextcloud-vue.cjs.js +7039 -2409
- package/dist/nextcloud-vue.cjs.js.map +1 -1
- package/dist/nextcloud-vue.css +237 -52
- package/dist/nextcloud-vue.esm.js +7012 -2386
- package/dist/nextcloud-vue.esm.js.map +1 -1
- package/package.json +2 -4
- package/src/components/CnActionsBar/CnActionsBar.vue +225 -0
- package/src/components/CnActionsBar/index.js +1 -0
- package/src/components/CnCopyDialog/CnCopyDialog.vue +250 -0
- package/src/components/CnCopyDialog/index.js +1 -0
- package/src/components/CnDataTable/CnDataTable.vue +0 -5
- package/src/components/CnDeleteDialog/CnDeleteDialog.vue +170 -0
- package/src/components/CnDeleteDialog/index.js +1 -0
- package/src/components/CnFormDialog/CnFormDialog.vue +629 -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 +434 -300
- package/src/components/CnIndexSidebar/CnIndexSidebar.vue +484 -0
- package/src/components/CnIndexSidebar/index.js +1 -0
- package/src/components/CnPageHeader/CnPageHeader.vue +57 -0
- package/src/components/CnPageHeader/index.js +1 -0
- package/src/components/CnRegisterMapping/CnRegisterMapping.vue +792 -0
- package/src/components/CnRegisterMapping/index.js +1 -0
- package/src/components/index.js +8 -4
- package/src/constants/metadata.js +30 -0
- package/src/css/actions-bar.css +48 -0
- package/src/css/badge.css +4 -4
- package/src/css/card.css +23 -23
- package/src/css/detail.css +13 -13
- package/src/css/index-page.css +32 -0
- package/src/css/index-sidebar.css +187 -0
- package/src/css/index.css +4 -0
- package/src/css/layout.css +14 -14
- package/src/css/page-header.css +33 -0
- package/src/css/pagination.css +12 -12
- package/src/css/table.css +21 -22
- package/src/css/utilities.css +2 -2
- package/src/index.js +11 -8
- package/src/store/plugins/index.js +1 -0
- package/src/store/plugins/registerMapping.js +185 -0
- package/src/store/useObjectStore.js +122 -61
- package/src/utils/headers.js +7 -1
- package/src/utils/index.js +1 -1
- package/src/utils/schema.js +133 -1
- 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,62 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="cn-index-page">
|
|
3
|
-
<!-- Header -->
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
+
<!-- Actions bar -->
|
|
11
|
+
<CnActionsBar
|
|
12
|
+
:pagination="pagination"
|
|
13
|
+
:object-count="objects.length"
|
|
14
|
+
:selectable="selectable"
|
|
15
|
+
:selected-ids="internalSelectedIds"
|
|
16
|
+
:add-label="resolvedAddLabel"
|
|
17
|
+
:add-icon="resolvedIcon"
|
|
18
|
+
:inline-action-count="inlineActionCount"
|
|
19
|
+
:show-mass-import="showMassImport"
|
|
20
|
+
:show-mass-export="showMassExport"
|
|
21
|
+
:show-mass-copy="showMassCopy"
|
|
22
|
+
:show-mass-delete="showMassDelete"
|
|
23
|
+
:view-mode="currentViewMode"
|
|
24
|
+
:show-view-toggle="showViewToggle"
|
|
25
|
+
@add="onAddClick"
|
|
26
|
+
@refresh="$emit('refresh')"
|
|
27
|
+
@show-import="showImportDialog = true"
|
|
28
|
+
@show-export="showExportDialog = true"
|
|
29
|
+
@show-copy="showMassCopyDialog = true"
|
|
30
|
+
@show-delete="showMassDeleteDialog = true"
|
|
31
|
+
@view-mode-change="onViewModeChange">
|
|
32
|
+
<template v-if="$scopedSlots['mass-actions']" #mass-actions="{ count, selectedIds: ids }">
|
|
33
|
+
<slot name="mass-actions" :count="count" :selected-ids="ids" />
|
|
34
|
+
</template>
|
|
35
|
+
<template v-if="$scopedSlots['action-items']" #action-items>
|
|
36
|
+
<slot name="action-items" />
|
|
37
|
+
</template>
|
|
38
|
+
<template v-if="$scopedSlots['header-actions']" #header-actions>
|
|
34
39
|
<slot name="header-actions" />
|
|
35
|
-
</
|
|
36
|
-
</
|
|
40
|
+
</template>
|
|
41
|
+
</CnActionsBar>
|
|
37
42
|
|
|
38
43
|
<!-- Mass delete dialog -->
|
|
39
44
|
<CnMassDeleteDialog
|
|
40
|
-
v-if="
|
|
41
|
-
ref="
|
|
45
|
+
v-if="showMassDeleteDialog"
|
|
46
|
+
ref="massDeleteDialog"
|
|
42
47
|
:items="selectedObjects"
|
|
43
48
|
:name-field="massActionNameField"
|
|
44
49
|
@confirm="onMassDeleteConfirm"
|
|
45
|
-
@close="
|
|
50
|
+
@close="showMassDeleteDialog = false" />
|
|
46
51
|
|
|
47
52
|
<!-- Mass copy dialog -->
|
|
48
53
|
<CnMassCopyDialog
|
|
49
|
-
v-if="
|
|
50
|
-
ref="
|
|
54
|
+
v-if="showMassCopyDialog"
|
|
55
|
+
ref="massCopyDialog"
|
|
51
56
|
:items="selectedObjects"
|
|
52
57
|
:name-field="massActionNameField"
|
|
53
58
|
@confirm="onMassCopyConfirm"
|
|
54
|
-
@close="
|
|
59
|
+
@close="showMassCopyDialog = false" />
|
|
55
60
|
|
|
56
61
|
<!-- Mass export dialog -->
|
|
57
62
|
<CnMassExportDialog
|
|
@@ -73,35 +78,60 @@
|
|
|
73
78
|
</template>
|
|
74
79
|
</CnMassImportDialog>
|
|
75
80
|
|
|
76
|
-
<!--
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
81
|
+
<!-- Single delete dialog (overridable via slot) -->
|
|
82
|
+
<slot
|
|
83
|
+
name="delete-dialog"
|
|
84
|
+
:item="actionTargetItem"
|
|
85
|
+
:close="closeSingleDelete">
|
|
86
|
+
<CnDeleteDialog
|
|
87
|
+
v-if="showSingleDeleteDialog && actionTargetItem"
|
|
88
|
+
ref="singleDeleteDialog"
|
|
89
|
+
:item="actionTargetItem"
|
|
90
|
+
:name-field="massActionNameField"
|
|
91
|
+
@confirm="onSingleDeleteConfirm"
|
|
92
|
+
@close="closeSingleDelete" />
|
|
93
|
+
</slot>
|
|
94
|
+
|
|
95
|
+
<!-- Single copy dialog (overridable via slot) -->
|
|
96
|
+
<slot
|
|
97
|
+
name="copy-dialog"
|
|
98
|
+
:item="actionTargetItem"
|
|
99
|
+
:close="closeSingleCopy">
|
|
100
|
+
<CnCopyDialog
|
|
101
|
+
v-if="showSingleCopyDialog && actionTargetItem"
|
|
102
|
+
ref="singleCopyDialog"
|
|
103
|
+
:item="actionTargetItem"
|
|
104
|
+
:name-field="massActionNameField"
|
|
105
|
+
@confirm="onSingleCopyConfirm"
|
|
106
|
+
@close="closeSingleCopy" />
|
|
107
|
+
</slot>
|
|
108
|
+
|
|
109
|
+
<!-- Form dialog for create/edit (overridable via slot) -->
|
|
110
|
+
<slot
|
|
111
|
+
name="form-dialog"
|
|
112
|
+
:item="editItem"
|
|
113
|
+
:schema="schema"
|
|
114
|
+
:close="closeFormDialog">
|
|
115
|
+
<CnFormDialog
|
|
116
|
+
v-if="showFormDialogVisible"
|
|
117
|
+
ref="formDialog"
|
|
118
|
+
:schema="schema"
|
|
119
|
+
:item="editItem"
|
|
120
|
+
:exclude-fields="excludeFields"
|
|
121
|
+
:include-fields="includeFields"
|
|
122
|
+
:field-overrides="fieldOverrides"
|
|
123
|
+
:name-field="massActionNameField"
|
|
124
|
+
@confirm="onFormConfirm"
|
|
125
|
+
@close="closeFormDialog">
|
|
126
|
+
<template v-if="$scopedSlots['form-fields']" #form="scope">
|
|
127
|
+
<slot name="form-fields" v-bind="scope" />
|
|
128
|
+
</template>
|
|
129
|
+
</CnFormDialog>
|
|
130
|
+
</slot>
|
|
131
|
+
|
|
132
|
+
<!-- Body -->
|
|
133
|
+
<div class="cn-index-page__body">
|
|
93
134
|
<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
135
|
<!-- Loading state -->
|
|
106
136
|
<div v-if="loading" class="cn-index-page__loading">
|
|
107
137
|
<NcLoadingIcon :size="32" />
|
|
@@ -112,7 +142,8 @@
|
|
|
112
142
|
<slot name="empty">
|
|
113
143
|
<NcEmptyContent :name="emptyText">
|
|
114
144
|
<template #icon>
|
|
115
|
-
<
|
|
145
|
+
<CnIcon v-if="resolvedIcon" :name="resolvedIcon" :size="64" />
|
|
146
|
+
<DatabaseSearch v-else :size="64" />
|
|
116
147
|
</template>
|
|
117
148
|
</NcEmptyContent>
|
|
118
149
|
</slot>
|
|
@@ -127,7 +158,7 @@
|
|
|
127
158
|
:sort-key="sortKey"
|
|
128
159
|
:sort-order="sortOrder"
|
|
129
160
|
:selectable="selectable"
|
|
130
|
-
:selected-ids="
|
|
161
|
+
:selected-ids="internalSelectedIds"
|
|
131
162
|
:row-key="rowKey"
|
|
132
163
|
:empty-text="emptyText"
|
|
133
164
|
:exclude-columns="excludeColumns"
|
|
@@ -135,7 +166,7 @@
|
|
|
135
166
|
:column-overrides="columnOverrides"
|
|
136
167
|
:row-class="rowClass"
|
|
137
168
|
@sort="$emit('sort', $event)"
|
|
138
|
-
@select="
|
|
169
|
+
@select="onSelect"
|
|
139
170
|
@row-click="$emit('row-click', $event)">
|
|
140
171
|
<!-- Pass through column slots -->
|
|
141
172
|
<template
|
|
@@ -148,8 +179,7 @@
|
|
|
148
179
|
<template v-if="hasRowActions" #row-actions="{ row }">
|
|
149
180
|
<slot name="row-actions" :row="row">
|
|
150
181
|
<CnRowActions
|
|
151
|
-
|
|
152
|
-
:actions="actions"
|
|
182
|
+
:actions="mergedActions"
|
|
153
183
|
:row="row"
|
|
154
184
|
@action="$emit('action', $event)" />
|
|
155
185
|
</slot>
|
|
@@ -162,19 +192,18 @@
|
|
|
162
192
|
:objects="objects"
|
|
163
193
|
:schema="schema"
|
|
164
194
|
:selectable="selectable"
|
|
165
|
-
:selected-ids="
|
|
195
|
+
:selected-ids="internalSelectedIds"
|
|
166
196
|
:row-key="rowKey"
|
|
167
197
|
:empty-text="emptyText"
|
|
168
198
|
@click="$emit('row-click', $event)"
|
|
169
|
-
@select="
|
|
199
|
+
@select="onSelect">
|
|
170
200
|
<template v-if="$scopedSlots.card" #card="{ object, selected }">
|
|
171
201
|
<slot name="card" :object="object" :selected="selected" />
|
|
172
202
|
</template>
|
|
173
203
|
<template v-if="hasRowActions" #card-actions="{ object }">
|
|
174
204
|
<slot name="row-actions" :row="object">
|
|
175
205
|
<CnRowActions
|
|
176
|
-
|
|
177
|
-
:actions="actions"
|
|
206
|
+
:actions="mergedActions"
|
|
178
207
|
:row="object"
|
|
179
208
|
@action="$emit('action', $event)" />
|
|
180
209
|
</slot>
|
|
@@ -199,83 +228,88 @@
|
|
|
199
228
|
<script>
|
|
200
229
|
import { NcLoadingIcon, NcEmptyContent } from '@nextcloud/vue'
|
|
201
230
|
import DatabaseSearch from 'vue-material-design-icons/DatabaseSearch.vue'
|
|
231
|
+
import Eye from 'vue-material-design-icons/Eye.vue'
|
|
232
|
+
import Pencil from 'vue-material-design-icons/Pencil.vue'
|
|
233
|
+
import ContentCopy from 'vue-material-design-icons/ContentCopy.vue'
|
|
234
|
+
import TrashCanOutline from 'vue-material-design-icons/TrashCanOutline.vue'
|
|
235
|
+
import { CnPageHeader } from '../CnPageHeader/index.js'
|
|
236
|
+
import { CnActionsBar } from '../CnActionsBar/index.js'
|
|
237
|
+
import { CnIcon, ICON_MAP } from '../CnIcon/index.js'
|
|
202
238
|
import { CnDataTable } from '../CnDataTable/index.js'
|
|
203
239
|
import { CnCardGrid } from '../CnCardGrid/index.js'
|
|
204
240
|
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
241
|
import { CnRowActions } from '../CnRowActions/index.js'
|
|
209
|
-
import { CnMassActionBar } from '../CnMassActionBar/index.js'
|
|
210
242
|
import { CnMassDeleteDialog } from '../CnMassDeleteDialog/index.js'
|
|
211
243
|
import { CnMassCopyDialog } from '../CnMassCopyDialog/index.js'
|
|
212
244
|
import { CnMassExportDialog } from '../CnMassExportDialog/index.js'
|
|
213
245
|
import { CnMassImportDialog } from '../CnMassImportDialog/index.js'
|
|
246
|
+
import { CnDeleteDialog } from '../CnDeleteDialog/index.js'
|
|
247
|
+
import { CnCopyDialog } from '../CnCopyDialog/index.js'
|
|
248
|
+
import { CnFormDialog } from '../CnFormDialog/index.js'
|
|
214
249
|
|
|
215
250
|
/**
|
|
216
251
|
* CnIndexPage — Top-level schema-driven index page component.
|
|
217
252
|
*
|
|
218
|
-
* Assembles
|
|
219
|
-
*
|
|
220
|
-
*
|
|
253
|
+
* Assembles sub-components (CnPageHeader, CnActionsBar, table, cards,
|
|
254
|
+
* pagination, mass actions, single-object dialogs) into a single
|
|
255
|
+
* zero-config page.
|
|
256
|
+
*
|
|
257
|
+
* Dialogs are overridable via named slots:
|
|
258
|
+
* - `#form-dialog` — Replace the create/edit dialog entirely
|
|
259
|
+
* - `#delete-dialog` — Replace the single-item delete dialog
|
|
260
|
+
* - `#copy-dialog` — Replace the single-item copy dialog
|
|
261
|
+
* - `#form-fields` — Replace only the form content inside the built-in form dialog
|
|
221
262
|
*
|
|
222
|
-
* @example Minimal usage
|
|
263
|
+
* @example Minimal usage (auto-generated dialogs from schema)
|
|
223
264
|
* <CnIndexPage
|
|
224
|
-
* title="
|
|
265
|
+
* title="Clients"
|
|
225
266
|
* :schema="schema"
|
|
226
|
-
* :objects="
|
|
267
|
+
* :objects="clients"
|
|
227
268
|
* :pagination="pagination"
|
|
228
269
|
* :loading="loading"
|
|
229
|
-
*
|
|
230
|
-
* @
|
|
231
|
-
* @
|
|
270
|
+
* @create="onCreate"
|
|
271
|
+
* @edit="onEdit"
|
|
272
|
+
* @delete="onDelete"
|
|
273
|
+
* @refresh="fetchClients"
|
|
274
|
+
* @row-click="openClient"
|
|
232
275
|
* @page-changed="onPage" />
|
|
233
276
|
*
|
|
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>
|
|
277
|
+
* @example With custom form dialog
|
|
278
|
+
* <CnIndexPage ...>
|
|
279
|
+
* <template #form-dialog="{ item, schema, close }">
|
|
280
|
+
* <MyCustomFormDialog :item="item" @close="close" />
|
|
257
281
|
* </template>
|
|
258
282
|
* </CnIndexPage>
|
|
259
283
|
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
* }
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
284
|
+
* @event {void} add — Add button clicked (backward compat, only if listener attached)
|
|
285
|
+
* @event {object} create — Form dialog create confirmed. Payload: formData object
|
|
286
|
+
* @event {object} edit — Form dialog edit confirmed. Payload: formData object (includes id)
|
|
287
|
+
* @event {string} delete — Single delete confirmed. Payload: item ID
|
|
288
|
+
* @event {{ id: string, newName: string }} copy — Single copy confirmed
|
|
289
|
+
* @event {string[]} mass-delete — Mass delete confirmed. Payload: array of IDs
|
|
290
|
+
* @event {object} mass-copy — Mass copy confirmed. Payload: { ids, pattern }
|
|
291
|
+
* @event {object} mass-export — Mass export confirmed. Payload: { ids, format }
|
|
292
|
+
* @event {object} mass-import — Mass import confirmed. Payload: import data
|
|
293
|
+
* @event {void} refresh — Refresh button clicked
|
|
294
|
+
* @event {object} row-click — Table row or card clicked. Payload: row object
|
|
295
|
+
* @event {{ key: string, order: string }} sort — Column sort changed
|
|
296
|
+
* @event {number} page-changed — Pagination page changed
|
|
297
|
+
* @event {number} page-size-changed — Pagination page size changed
|
|
298
|
+
* @event {string[]} select — Selection changed. Payload: array of selected IDs
|
|
299
|
+
* @event {object} action — Row action triggered. Payload: { action, row }
|
|
300
|
+
*
|
|
301
|
+
* @slot mass-actions — Extra mass action buttons (shown when items are selected)
|
|
302
|
+
* @slot action-items — Extra action bar buttons
|
|
303
|
+
* @slot header-actions — Extra buttons in the page header
|
|
304
|
+
* @slot delete-dialog — Replace the single-item delete dialog. Scope: `{ item, close }`
|
|
305
|
+
* @slot copy-dialog — Replace the single-item copy dialog. Scope: `{ item, close }`
|
|
306
|
+
* @slot form-dialog — Replace the create/edit form dialog. Scope: `{ item, schema, close }`
|
|
307
|
+
* @slot form-fields — Replace form content inside the built-in CnFormDialog. Scope: `{ fields, formData, errors, updateField }`
|
|
308
|
+
* @slot import-fields — Extra fields in the import dialog
|
|
309
|
+
* @slot empty — Custom empty state content
|
|
310
|
+
* @slot card — Custom card template for card view. Scope: `{ row }`
|
|
311
|
+
* @slot row-actions — Custom row actions. Scope: `{ row }`
|
|
312
|
+
* @slot column-{key} — Custom cell renderer for a specific column. Scope: `{ row, value }`
|
|
279
313
|
*/
|
|
280
314
|
export default {
|
|
281
315
|
name: 'CnIndexPage',
|
|
@@ -284,18 +318,20 @@ export default {
|
|
|
284
318
|
NcLoadingIcon,
|
|
285
319
|
NcEmptyContent,
|
|
286
320
|
DatabaseSearch,
|
|
321
|
+
CnPageHeader,
|
|
322
|
+
CnActionsBar,
|
|
323
|
+
CnIcon,
|
|
287
324
|
CnDataTable,
|
|
288
325
|
CnCardGrid,
|
|
289
326
|
CnPagination,
|
|
290
|
-
CnFilterBar,
|
|
291
|
-
CnFacetSidebar,
|
|
292
|
-
CnViewModeToggle,
|
|
293
327
|
CnRowActions,
|
|
294
|
-
CnMassActionBar,
|
|
295
328
|
CnMassDeleteDialog,
|
|
296
329
|
CnMassCopyDialog,
|
|
297
330
|
CnMassExportDialog,
|
|
298
331
|
CnMassImportDialog,
|
|
332
|
+
CnDeleteDialog,
|
|
333
|
+
CnCopyDialog,
|
|
334
|
+
CnFormDialog,
|
|
299
335
|
},
|
|
300
336
|
|
|
301
337
|
props: {
|
|
@@ -304,6 +340,24 @@ export default {
|
|
|
304
340
|
type: String,
|
|
305
341
|
required: true,
|
|
306
342
|
},
|
|
343
|
+
/** Optional description shown below the title */
|
|
344
|
+
description: {
|
|
345
|
+
type: String,
|
|
346
|
+
default: '',
|
|
347
|
+
},
|
|
348
|
+
/**
|
|
349
|
+
* Whether to show the page header (icon, title, description) inline.
|
|
350
|
+
* When false (default), the title is shown in the sidebar header instead.
|
|
351
|
+
*/
|
|
352
|
+
showTitle: {
|
|
353
|
+
type: Boolean,
|
|
354
|
+
default: false,
|
|
355
|
+
},
|
|
356
|
+
/** Optional MDI icon name. Defaults to schema.icon when a schema is provided. */
|
|
357
|
+
icon: {
|
|
358
|
+
type: String,
|
|
359
|
+
default: '',
|
|
360
|
+
},
|
|
307
361
|
/** Schema definition */
|
|
308
362
|
schema: {
|
|
309
363
|
type: Object,
|
|
@@ -329,36 +383,6 @@ export default {
|
|
|
329
383
|
type: Boolean,
|
|
330
384
|
default: false,
|
|
331
385
|
},
|
|
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
386
|
/** Whether rows/cards can be selected */
|
|
363
387
|
selectable: {
|
|
364
388
|
type: Boolean,
|
|
@@ -405,7 +429,7 @@ export default {
|
|
|
405
429
|
type: Object,
|
|
406
430
|
default: () => ({}),
|
|
407
431
|
},
|
|
408
|
-
/** Row action definitions */
|
|
432
|
+
/** Row action definitions (app-provided, merged with built-in actions) */
|
|
409
433
|
actions: {
|
|
410
434
|
type: Array,
|
|
411
435
|
default: () => [],
|
|
@@ -415,21 +439,21 @@ export default {
|
|
|
415
439
|
type: String,
|
|
416
440
|
default: 'No items found',
|
|
417
441
|
},
|
|
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
442
|
/** Function returning CSS class(es) for a row */
|
|
429
443
|
rowClass: {
|
|
430
444
|
type: Function,
|
|
431
445
|
default: null,
|
|
432
446
|
},
|
|
447
|
+
/** Override label for the Add button. Defaults to "Add {schema.title}" */
|
|
448
|
+
addLabel: {
|
|
449
|
+
type: String,
|
|
450
|
+
default: '',
|
|
451
|
+
},
|
|
452
|
+
/** How many action buttons to show inline (rest go in overflow dropdown) */
|
|
453
|
+
inlineActionCount: {
|
|
454
|
+
type: Number,
|
|
455
|
+
default: 2,
|
|
456
|
+
},
|
|
433
457
|
/** Whether to show the built-in mass Import action */
|
|
434
458
|
showMassImport: {
|
|
435
459
|
type: Boolean,
|
|
@@ -450,7 +474,7 @@ export default {
|
|
|
450
474
|
type: Boolean,
|
|
451
475
|
default: true,
|
|
452
476
|
},
|
|
453
|
-
/** Property name used to display item names in
|
|
477
|
+
/** Property name used to display item names in dialogs */
|
|
454
478
|
massActionNameField: {
|
|
455
479
|
type: String,
|
|
456
480
|
default: 'title',
|
|
@@ -468,41 +492,146 @@ export default {
|
|
|
468
492
|
type: Array,
|
|
469
493
|
default: () => [],
|
|
470
494
|
},
|
|
495
|
+
/** Whether to show the built-in form dialog for Add/Edit */
|
|
496
|
+
showFormDialog: {
|
|
497
|
+
type: Boolean,
|
|
498
|
+
default: true,
|
|
499
|
+
},
|
|
500
|
+
/** Whether to add an Edit action to row actions */
|
|
501
|
+
showEditAction: {
|
|
502
|
+
type: Boolean,
|
|
503
|
+
default: true,
|
|
504
|
+
},
|
|
505
|
+
/** Whether to add a Copy action to row actions */
|
|
506
|
+
showCopyAction: {
|
|
507
|
+
type: Boolean,
|
|
508
|
+
default: true,
|
|
509
|
+
},
|
|
510
|
+
/** Whether to add a Delete action to row actions */
|
|
511
|
+
showDeleteAction: {
|
|
512
|
+
type: Boolean,
|
|
513
|
+
default: true,
|
|
514
|
+
},
|
|
515
|
+
/** Field keys to exclude from the form dialog */
|
|
516
|
+
excludeFields: {
|
|
517
|
+
type: Array,
|
|
518
|
+
default: () => [],
|
|
519
|
+
},
|
|
520
|
+
/** Field keys to include in the form dialog (whitelist mode) */
|
|
521
|
+
includeFields: {
|
|
522
|
+
type: Array,
|
|
523
|
+
default: null,
|
|
524
|
+
},
|
|
525
|
+
/** Per-field overrides passed to CnFormDialog */
|
|
526
|
+
fieldOverrides: {
|
|
527
|
+
type: Object,
|
|
528
|
+
default: () => ({}),
|
|
529
|
+
},
|
|
530
|
+
/** Whether to show the Cards/Table view toggle in the actions bar */
|
|
531
|
+
showViewToggle: {
|
|
532
|
+
type: Boolean,
|
|
533
|
+
default: true,
|
|
534
|
+
},
|
|
471
535
|
},
|
|
472
536
|
|
|
473
537
|
data() {
|
|
474
538
|
return {
|
|
475
539
|
currentViewMode: this.viewMode,
|
|
476
|
-
|
|
477
|
-
|
|
540
|
+
internalSelectedIds: [...this.selectedIds],
|
|
541
|
+
// Mass action dialogs
|
|
542
|
+
showMassDeleteDialog: false,
|
|
543
|
+
showMassCopyDialog: false,
|
|
478
544
|
showExportDialog: false,
|
|
479
545
|
showImportDialog: false,
|
|
546
|
+
// Single-object dialogs
|
|
547
|
+
showSingleDeleteDialog: false,
|
|
548
|
+
showSingleCopyDialog: false,
|
|
549
|
+
showFormDialogVisible: false,
|
|
550
|
+
// Dialog targets
|
|
551
|
+
actionTargetItem: null,
|
|
552
|
+
editItem: null,
|
|
480
553
|
}
|
|
481
554
|
},
|
|
482
555
|
|
|
483
556
|
computed: {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
557
|
+
/** Resolved icon — explicit prop overrides schema.icon */
|
|
558
|
+
resolvedIcon() {
|
|
559
|
+
if (this.icon) return this.icon
|
|
560
|
+
return this.schema?.icon || ''
|
|
561
|
+
},
|
|
562
|
+
|
|
563
|
+
/** Resolved schema icon component for View action */
|
|
564
|
+
schemaIconComponent() {
|
|
565
|
+
if (this.resolvedIcon && ICON_MAP[this.resolvedIcon]) {
|
|
566
|
+
return ICON_MAP[this.resolvedIcon]
|
|
567
|
+
}
|
|
568
|
+
return Eye
|
|
569
|
+
},
|
|
570
|
+
|
|
571
|
+
/** Built-in row actions based on show*Action props */
|
|
572
|
+
defaultActions() {
|
|
573
|
+
const builtIn = []
|
|
574
|
+
if (this.$listeners && this.$listeners['row-click']) {
|
|
575
|
+
builtIn.push({
|
|
576
|
+
label: 'View',
|
|
577
|
+
icon: this.schemaIconComponent,
|
|
578
|
+
handler: (row) => {
|
|
579
|
+
this.$emit('row-click', row)
|
|
580
|
+
},
|
|
581
|
+
})
|
|
582
|
+
}
|
|
583
|
+
if (this.showEditAction) {
|
|
584
|
+
builtIn.push({
|
|
585
|
+
label: 'Edit',
|
|
586
|
+
icon: Pencil,
|
|
587
|
+
handler: (row) => {
|
|
588
|
+
this.editItem = row
|
|
589
|
+
this.showFormDialogVisible = true
|
|
590
|
+
},
|
|
591
|
+
})
|
|
592
|
+
}
|
|
593
|
+
if (this.showCopyAction) {
|
|
594
|
+
builtIn.push({
|
|
595
|
+
label: 'Copy',
|
|
596
|
+
icon: ContentCopy,
|
|
597
|
+
handler: (row) => {
|
|
598
|
+
this.actionTargetItem = row
|
|
599
|
+
this.showSingleCopyDialog = true
|
|
600
|
+
},
|
|
601
|
+
})
|
|
602
|
+
}
|
|
603
|
+
if (this.showDeleteAction) {
|
|
604
|
+
builtIn.push({
|
|
605
|
+
label: 'Delete',
|
|
606
|
+
icon: TrashCanOutline,
|
|
607
|
+
destructive: true,
|
|
608
|
+
handler: (row) => {
|
|
609
|
+
this.actionTargetItem = row
|
|
610
|
+
this.showSingleDeleteDialog = true
|
|
611
|
+
},
|
|
612
|
+
})
|
|
613
|
+
}
|
|
614
|
+
return builtIn
|
|
487
615
|
},
|
|
488
616
|
|
|
489
|
-
|
|
490
|
-
|
|
617
|
+
/** Merged actions: app-provided first, then built-in defaults */
|
|
618
|
+
mergedActions() {
|
|
619
|
+
return [...this.actions, ...this.defaultActions]
|
|
491
620
|
},
|
|
492
621
|
|
|
493
622
|
hasRowActions() {
|
|
494
|
-
return this.$scopedSlots['row-actions'] || this.
|
|
623
|
+
return this.$scopedSlots['row-actions'] || this.mergedActions.length > 0
|
|
495
624
|
},
|
|
496
625
|
|
|
497
626
|
/** Whether all visible items are selected */
|
|
498
627
|
allSelected() {
|
|
499
|
-
if (this.objects.length === 0 || this.
|
|
500
|
-
return this.objects.every((o) => this.
|
|
628
|
+
if (this.objects.length === 0 || this.internalSelectedIds.length === 0) return false
|
|
629
|
+
return this.objects.every((o) => this.internalSelectedIds.includes(o[this.rowKey]))
|
|
501
630
|
},
|
|
502
631
|
|
|
503
632
|
/** Full objects for the selected IDs (used by mass action dialogs) */
|
|
504
633
|
selectedObjects() {
|
|
505
|
-
return this.objects.filter((o) => this.
|
|
634
|
+
return this.objects.filter((o) => this.internalSelectedIds.includes(o[this.rowKey]))
|
|
506
635
|
},
|
|
507
636
|
|
|
508
637
|
/** Column slot names that the parent has provided (for pass-through) */
|
|
@@ -511,172 +640,177 @@ export default {
|
|
|
511
640
|
.filter((name) => name.startsWith('column-'))
|
|
512
641
|
.map((name) => name.replace('column-', ''))
|
|
513
642
|
},
|
|
643
|
+
|
|
644
|
+
/** Add button label — derived from schema.title if not explicitly set */
|
|
645
|
+
resolvedAddLabel() {
|
|
646
|
+
if (this.addLabel) return this.addLabel
|
|
647
|
+
return 'Add ' + (this.schema?.title || 'Item')
|
|
648
|
+
},
|
|
514
649
|
},
|
|
515
650
|
|
|
516
651
|
watch: {
|
|
517
652
|
viewMode(val) {
|
|
518
653
|
this.currentViewMode = val
|
|
519
654
|
},
|
|
655
|
+
selectedIds(val) {
|
|
656
|
+
this.internalSelectedIds = [...val]
|
|
657
|
+
},
|
|
520
658
|
},
|
|
521
659
|
|
|
522
660
|
methods: {
|
|
661
|
+
/**
|
|
662
|
+
* Handle the Add button click. If the consumer listens to @add,
|
|
663
|
+
* emit the event (backward compatible). Otherwise open the form dialog.
|
|
664
|
+
*/
|
|
665
|
+
onAddClick() {
|
|
666
|
+
if (this.$listeners && this.$listeners.add) {
|
|
667
|
+
this.$emit('add')
|
|
668
|
+
} else if (this.showFormDialog) {
|
|
669
|
+
this.editItem = null
|
|
670
|
+
this.showFormDialogVisible = true
|
|
671
|
+
}
|
|
672
|
+
},
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Handle view mode toggle.
|
|
676
|
+
* @param {string} mode 'table' or 'cards'
|
|
677
|
+
*/
|
|
523
678
|
onViewModeChange(mode) {
|
|
524
679
|
this.currentViewMode = mode
|
|
525
680
|
this.$emit('view-mode-change', mode)
|
|
526
681
|
},
|
|
527
682
|
|
|
528
683
|
/**
|
|
529
|
-
* Handle
|
|
530
|
-
*
|
|
531
|
-
* @param {Array} ids Array of
|
|
684
|
+
* Handle selection changes from CnDataTable/CnCardGrid.
|
|
685
|
+
* Updates internal state and re-emits for parent.
|
|
686
|
+
* @param {Array} ids Array of selected row IDs
|
|
532
687
|
*/
|
|
688
|
+
onSelect(ids) {
|
|
689
|
+
this.internalSelectedIds = ids
|
|
690
|
+
this.$emit('select', ids)
|
|
691
|
+
},
|
|
692
|
+
|
|
693
|
+
// --- Mass action handlers ---
|
|
694
|
+
|
|
533
695
|
onMassDeleteConfirm(ids) {
|
|
534
696
|
this.$emit('mass-delete', ids)
|
|
535
697
|
},
|
|
536
698
|
|
|
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
699
|
onMassCopyConfirm(payload) {
|
|
543
700
|
this.$emit('mass-copy', payload)
|
|
544
701
|
},
|
|
545
702
|
|
|
546
|
-
/**
|
|
547
|
-
* Set the result of a mass delete operation. Call from parent after API call.
|
|
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
|
-
}
|
|
555
|
-
},
|
|
556
|
-
|
|
557
|
-
/**
|
|
558
|
-
* Set the result of a mass copy operation. Call from parent after API call.
|
|
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
|
-
}
|
|
566
|
-
},
|
|
567
|
-
|
|
568
|
-
/**
|
|
569
|
-
* Handle mass export confirm.
|
|
570
|
-
* @param {{ format: string }} payload
|
|
571
|
-
*/
|
|
572
703
|
onMassExportConfirm(payload) {
|
|
573
704
|
this.$emit('mass-export', payload)
|
|
574
705
|
},
|
|
575
706
|
|
|
576
|
-
/**
|
|
577
|
-
* Handle mass import confirm.
|
|
578
|
-
* @param {{ file: File, options: object }} payload
|
|
579
|
-
*/
|
|
580
707
|
onMassImportConfirm(payload) {
|
|
581
708
|
this.$emit('mass-import', payload)
|
|
582
709
|
},
|
|
583
710
|
|
|
584
|
-
/**
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
711
|
+
/** @public Forward result to mass delete dialog */
|
|
712
|
+
setMassDeleteResult(resultData) {
|
|
713
|
+
if (this.$refs.massDeleteDialog) {
|
|
714
|
+
this.$refs.massDeleteDialog.setResult(resultData)
|
|
715
|
+
}
|
|
716
|
+
},
|
|
717
|
+
|
|
718
|
+
/** @public Forward result to mass copy dialog */
|
|
719
|
+
setMassCopyResult(resultData) {
|
|
720
|
+
if (this.$refs.massCopyDialog) {
|
|
721
|
+
this.$refs.massCopyDialog.setResult(resultData)
|
|
722
|
+
}
|
|
723
|
+
},
|
|
724
|
+
|
|
725
|
+
/** @public Forward result to export dialog */
|
|
589
726
|
setExportResult(resultData) {
|
|
590
727
|
if (this.$refs.exportDialog) {
|
|
591
728
|
this.$refs.exportDialog.setResult(resultData)
|
|
592
729
|
}
|
|
593
730
|
},
|
|
594
731
|
|
|
595
|
-
/**
|
|
596
|
-
* Set the result of a mass import operation.
|
|
597
|
-
* @param {{ success?: boolean, error?: string, summary?: object }} resultData
|
|
598
|
-
* @public
|
|
599
|
-
*/
|
|
732
|
+
/** @public Forward result to import dialog */
|
|
600
733
|
setImportResult(resultData) {
|
|
601
734
|
if (this.$refs.importDialog) {
|
|
602
735
|
this.$refs.importDialog.setResult(resultData)
|
|
603
736
|
}
|
|
604
737
|
},
|
|
605
|
-
},
|
|
606
|
-
}
|
|
607
|
-
</script>
|
|
608
738
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
margin-bottom: 16px;
|
|
619
|
-
flex-wrap: wrap;
|
|
620
|
-
gap: 12px;
|
|
621
|
-
}
|
|
739
|
+
// --- Backward-compatible aliases ---
|
|
740
|
+
/** @public @deprecated Use setMassDeleteResult instead */
|
|
741
|
+
setDeleteResult(resultData) {
|
|
742
|
+
this.setMassDeleteResult(resultData)
|
|
743
|
+
},
|
|
744
|
+
/** @public @deprecated Use setMassCopyResult instead */
|
|
745
|
+
setCopyResult(resultData) {
|
|
746
|
+
this.setMassCopyResult(resultData)
|
|
747
|
+
},
|
|
622
748
|
|
|
623
|
-
|
|
624
|
-
display: flex;
|
|
625
|
-
align-items: baseline;
|
|
626
|
-
gap: 8px;
|
|
627
|
-
}
|
|
749
|
+
// --- Single-object dialog handlers ---
|
|
628
750
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
font-weight: 700;
|
|
633
|
-
}
|
|
751
|
+
onSingleDeleteConfirm(id) {
|
|
752
|
+
this.$emit('delete', id)
|
|
753
|
+
},
|
|
634
754
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
}
|
|
755
|
+
onSingleCopyConfirm(payload) {
|
|
756
|
+
this.$emit('copy', payload)
|
|
757
|
+
},
|
|
639
758
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
759
|
+
onFormConfirm(formData) {
|
|
760
|
+
if (this.editItem) {
|
|
761
|
+
this.$emit('edit', formData)
|
|
762
|
+
} else {
|
|
763
|
+
this.$emit('create', formData)
|
|
764
|
+
}
|
|
765
|
+
},
|
|
645
766
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
}
|
|
767
|
+
closeSingleDelete() {
|
|
768
|
+
this.showSingleDeleteDialog = false
|
|
769
|
+
this.actionTargetItem = null
|
|
770
|
+
},
|
|
650
771
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
772
|
+
closeSingleCopy() {
|
|
773
|
+
this.showSingleCopyDialog = false
|
|
774
|
+
this.actionTargetItem = null
|
|
775
|
+
},
|
|
654
776
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
777
|
+
closeFormDialog() {
|
|
778
|
+
this.showFormDialogVisible = false
|
|
779
|
+
this.editItem = null
|
|
780
|
+
},
|
|
658
781
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
782
|
+
/** @public Forward result to single delete dialog */
|
|
783
|
+
setSingleDeleteResult(resultData) {
|
|
784
|
+
if (this.$refs.singleDeleteDialog) {
|
|
785
|
+
this.$refs.singleDeleteDialog.setResult(resultData)
|
|
786
|
+
}
|
|
787
|
+
},
|
|
663
788
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
789
|
+
/** @public Forward result to single copy dialog */
|
|
790
|
+
setSingleCopyResult(resultData) {
|
|
791
|
+
if (this.$refs.singleCopyDialog) {
|
|
792
|
+
this.$refs.singleCopyDialog.setResult(resultData)
|
|
793
|
+
}
|
|
794
|
+
},
|
|
667
795
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
}
|
|
796
|
+
/** @public Forward result to form dialog */
|
|
797
|
+
setFormResult(resultData) {
|
|
798
|
+
if (this.$refs.formDialog) {
|
|
799
|
+
this.$refs.formDialog.setResult(resultData)
|
|
800
|
+
}
|
|
801
|
+
},
|
|
673
802
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
803
|
+
/**
|
|
804
|
+
* Programmatically open the form dialog.
|
|
805
|
+
* @param {object|null} item Pass null for create mode, or an object for edit mode
|
|
806
|
+
* @public
|
|
807
|
+
*/
|
|
808
|
+
openFormDialog(item = null) {
|
|
809
|
+
this.editItem = item
|
|
810
|
+
this.showFormDialogVisible = true
|
|
811
|
+
},
|
|
812
|
+
},
|
|
677
813
|
}
|
|
814
|
+
</script>
|
|
678
815
|
|
|
679
|
-
|
|
680
|
-
margin-top: 16px;
|
|
681
|
-
}
|
|
682
|
-
</style>
|
|
816
|
+
<!-- Styles in css/index-page.css -->
|