@fishawack/lab-velocity 2.0.0-beta.6 → 2.0.0-beta.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/README.md +467 -36
  2. package/_Build/js/libs/build-id.js +14 -0
  3. package/_Build/js/libs/filters.js +36 -0
  4. package/_Build/js/libs/globals.js +7 -0
  5. package/_Build/js/libs/router.js +22 -0
  6. package/_Build/js/libs/routes.js +29 -0
  7. package/_Build/js/libs/store.js +21 -0
  8. package/_Build/js/libs/utility.js +161 -0
  9. package/_Build/vue/components/basic/Button.vue +1 -1
  10. package/_Build/vue/components/form/Avatar.vue +90 -0
  11. package/_Build/vue/components/form/Checkbox.vue +10 -0
  12. package/_Build/vue/components/form/InputNumber.vue +1 -1
  13. package/_Build/vue/components/form/Select.vue +221 -31
  14. package/_Build/vue/components/form/Wysiwyg2.vue +87 -2
  15. package/_Build/vue/components/form/wysiwyg2-video.js +41 -0
  16. package/_Build/vue/components/layout/Alert.vue +5 -5
  17. package/_Build/vue/components/layout/Audit.vue +159 -0
  18. package/_Build/vue/{modules/AuthModule/components/VBreadcrumbs.vue → components/layout/Breadcrumbs.vue} +4 -4
  19. package/_Build/vue/{modules/AuthModule/components → components/layout}/Chips.vue +2 -2
  20. package/_Build/vue/components/layout/Footer.vue +11 -10
  21. package/_Build/vue/{modules/AuthModule/components/VFormFooter.vue → components/layout/FormFooter.vue} +2 -2
  22. package/_Build/vue/{modules/AuthModule/components → components/layout}/FormRole.vue +8 -8
  23. package/_Build/vue/components/layout/Layout.vue +94 -0
  24. package/_Build/vue/components/layout/Navigation.vue +83 -0
  25. package/_Build/vue/{modules/AuthModule/components/VPageHeader.vue → components/layout/PageHeader.vue} +14 -8
  26. package/_Build/vue/components/layout/SideBar.vue +26 -0
  27. package/_Build/vue/{modules/AuthModule/components/VTable.vue → components/layout/Table.vue} +24 -12
  28. package/_Build/vue/{modules/AuthModule/components/VTableSorter.vue → components/layout/TableSorter.vue} +92 -48
  29. package/_Build/vue/components/layout/TokenDisplay.vue +52 -0
  30. package/_Build/vue/components/layout/pageTitle.vue +1 -1
  31. package/_Build/vue/components/navigation/Menu.vue +6 -1
  32. package/_Build/vue/components/navigation/MenuItem.vue +7 -2
  33. package/_Build/vue/components/navigation/MenuItemGroup.vue +7 -2
  34. package/_Build/vue/modules/AuthModule/js/axios.js +21 -1
  35. package/_Build/vue/modules/AuthModule/js/clear-cookies.js +34 -0
  36. package/_Build/vue/modules/AuthModule/js/guest-request.js +32 -0
  37. package/_Build/vue/modules/AuthModule/js/impersonation-banner.js +102 -0
  38. package/_Build/vue/modules/AuthModule/js/router.js +91 -114
  39. package/_Build/vue/modules/AuthModule/js/store.js +23 -6
  40. package/_Build/vue/modules/AuthModule/routes/PCompanies/columns.js +268 -0
  41. package/_Build/vue/modules/AuthModule/routes/PCompanies/resource.js +213 -0
  42. package/_Build/vue/modules/AuthModule/routes/PIntegrations/columns.js +58 -0
  43. package/_Build/vue/modules/AuthModule/routes/PIntegrations/resource.js +79 -0
  44. package/_Build/vue/modules/AuthModule/routes/PTeams/columns.js +78 -0
  45. package/_Build/vue/modules/AuthModule/routes/PTeams/resource.js +251 -0
  46. package/_Build/vue/modules/AuthModule/routes/PUsers/SetPasswordAction.vue +51 -0
  47. package/_Build/vue/modules/AuthModule/routes/PUsers/SetPasswordDialog.vue +138 -0
  48. package/_Build/vue/modules/AuthModule/routes/PUsers/columns.js +349 -0
  49. package/_Build/vue/modules/AuthModule/routes/PUsers/resource.js +239 -0
  50. package/_Build/vue/modules/AuthModule/routes/account-exists.vue +2 -2
  51. package/_Build/vue/modules/AuthModule/routes/change-password.vue +28 -32
  52. package/_Build/vue/modules/AuthModule/routes/container.vue +2 -11
  53. package/_Build/vue/modules/AuthModule/routes/expired-reset.vue +4 -4
  54. package/_Build/vue/modules/AuthModule/routes/expired-verification.vue +10 -9
  55. package/_Build/vue/modules/AuthModule/routes/force-reset.vue +44 -58
  56. package/_Build/vue/modules/AuthModule/routes/forgot.vue +10 -5
  57. package/_Build/vue/modules/AuthModule/routes/login.vue +12 -19
  58. package/_Build/vue/modules/AuthModule/routes/logincallback.vue +1 -3
  59. package/_Build/vue/modules/AuthModule/routes/loginsso.vue +14 -10
  60. package/_Build/vue/modules/AuthModule/routes/logout.vue +14 -5
  61. package/_Build/vue/modules/AuthModule/routes/logoutheadless.vue +16 -5
  62. package/_Build/vue/modules/AuthModule/routes/register.vue +24 -28
  63. package/_Build/vue/modules/AuthModule/routes/reset.vue +20 -14
  64. package/_Build/vue/modules/AuthModule/routes/success-forgot.vue +14 -8
  65. package/_Build/vue/modules/AuthModule/routes/success-reset.vue +2 -2
  66. package/_Build/vue/modules/AuthModule/routes/success-verify.vue +1 -3
  67. package/_Build/vue/modules/AuthModule/routes/verify.vue +11 -14
  68. package/_Build/vue/modules/resource/Children/create.vue +99 -0
  69. package/_Build/vue/modules/resource/Children/edit.vue +124 -0
  70. package/_Build/vue/modules/resource/Children/index.vue +42 -0
  71. package/_Build/vue/modules/resource/Children/partials/form.vue +111 -0
  72. package/_Build/vue/modules/resource/Children/show.vue +170 -0
  73. package/_Build/vue/modules/resource/index.js +562 -0
  74. package/_Build/vue/modules/resource/parent.vue +63 -0
  75. package/_Build/vue/modules/resource/trashable.js +104 -0
  76. package/_base.scss +0 -1
  77. package/_defaults.scss +2 -2
  78. package/components/_auth.scss +19 -61
  79. package/components/_datepicker.scss +1 -0
  80. package/components/_descriptions.scss +2 -0
  81. package/components/_footer.scss +1 -0
  82. package/components/_form.scss +18 -0
  83. package/components/_header.scss +3 -27
  84. package/components/_layout.scss +56 -0
  85. package/components/_menu.scss +0 -5
  86. package/components/_sidebar.scss +12 -27
  87. package/components/_table.scss +3 -0
  88. package/components/_token-display.scss +41 -0
  89. package/components/_wysiwyg2.scss +10 -0
  90. package/general.scss +1 -0
  91. package/index.js +31 -1
  92. package/package.json +7 -4
  93. package/_Build/vue/components/layout/sideBar.vue +0 -25
  94. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/Upload/upload.vue +0 -259
  95. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/create.vue +0 -62
  96. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/edit.vue +0 -98
  97. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/index.vue +0 -90
  98. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/partials/form.vue +0 -181
  99. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/show.vue +0 -266
  100. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/parent.vue +0 -36
  101. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/create.vue +0 -112
  102. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/edit.vue +0 -103
  103. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/index.vue +0 -112
  104. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/partials/form.vue +0 -173
  105. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/show.vue +0 -123
  106. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/parent.vue +0 -36
  107. package/components/_input.scss +0 -0
  108. /package/_Build/vue/{modules/AuthModule/components → components/layout}/AuthModal.vue +0 -0
  109. /package/_Build/vue/{modules/AuthModule/components → components/layout}/Chip.vue +0 -0
  110. /package/_Build/vue/{modules/AuthModule/components/VPasswordValidation.vue → components/layout/PasswordValidation.vue} +0 -0
  111. /package/_Build/vue/{modules/AuthModule/components/VRoleLegend.vue → components/layout/RoleLegend.vue} +0 -0
@@ -0,0 +1,562 @@
1
+ "use strict";
2
+
3
+ import { merge, kebabCase, snakeCase, cloneDeepWith } from "lodash";
4
+ import axios from "axios";
5
+ import { h, resolveComponent } from "vue";
6
+
7
+ import VelTableSorter from "../../components/layout/TableSorter.vue";
8
+ import {
9
+ ElDescriptions,
10
+ ElDescriptionsItem,
11
+ ElPopconfirm,
12
+ ElNotification,
13
+ } from "element-plus";
14
+ import VelButton from "../../components/basic/Button.vue";
15
+ import VelAudit from "../../components/layout/Audit.vue";
16
+ import { applyTrashable } from "./trashable.js";
17
+
18
+ export const defaultResource = meta();
19
+
20
+ export function meta(name = "default", properties = {}) {
21
+ const singular = properties.singular || name.slice(0, -1);
22
+ const slug = properties.slug || kebabCase(name);
23
+
24
+ const result = merge(
25
+ {
26
+ name,
27
+ title: properties.title || name[0].toUpperCase() + name.slice(1),
28
+ singular,
29
+ singularTitle:
30
+ properties.singularTitle ||
31
+ singular[0].toUpperCase() + singular.slice(1),
32
+ label: singular,
33
+ multiLabel: name,
34
+ slug,
35
+ path: properties.path || `/${slug}`,
36
+ routeName: properties.routeName || slug,
37
+ id: properties.id || `${snakeCase(slug)}Id`,
38
+ icon: `icon-${singular}`,
39
+ api: {
40
+ endpoint: () => `/api/${properties.slug || kebabCase(name)}`,
41
+ params: {
42
+ index: () => ({}),
43
+ show: () => ({}),
44
+ },
45
+ },
46
+ permissions: {
47
+ create: () => true,
48
+ edit: () => true,
49
+ delete: () => false,
50
+ },
51
+ searchable: {
52
+ value: "name",
53
+ label: `Search ${name}`,
54
+ },
55
+ auditable: false,
56
+ trashable: false,
57
+ form: {
58
+ component: null,
59
+ submit: null,
60
+ redirect: null,
61
+ class: "grid__1/2",
62
+ fields: () => ({}),
63
+ preparation: ({ form }) => form.data(),
64
+ structure: [],
65
+ groups: {},
66
+ },
67
+ table: {
68
+ actions: [
69
+ ({ model, resource }, { $router }) =>
70
+ !model.deleted_at &&
71
+ h(
72
+ VelButton,
73
+ {
74
+ tag: "a",
75
+ size: "small",
76
+ type: "primary",
77
+ onClick: () => {
78
+ $router.push({
79
+ name: `${resource.routeName}.show`,
80
+ params: {
81
+ [resource.id]: model.id,
82
+ },
83
+ });
84
+ },
85
+ },
86
+ () => "View",
87
+ ),
88
+ ({ model, resource }, props) => {
89
+ const { $router } = props;
90
+
91
+ if (resource.permissions.edit(props, { model })) {
92
+ return (
93
+ !model.deleted_at &&
94
+ h(
95
+ VelButton,
96
+ {
97
+ tag: "a",
98
+ size: "small",
99
+ onClick: () => {
100
+ $router.push({
101
+ name: `${resource.routeName}.edit`,
102
+ params: {
103
+ [resource.id]: model.id,
104
+ },
105
+ });
106
+ },
107
+ },
108
+ () => "Edit",
109
+ )
110
+ );
111
+ }
112
+ },
113
+ ({ model, resource }, props) => {
114
+ const { $emit } = props;
115
+
116
+ if (resource.permissions.delete(props, { model })) {
117
+ return (
118
+ !model.deleted_at &&
119
+ h({
120
+ data: () => ({
121
+ loading: false,
122
+ }),
123
+ render() {
124
+ return h(
125
+ ElPopconfirm,
126
+ {
127
+ title: `Are you sure you want to delete this ${resource.singular}?`,
128
+ confirmButtonText: "Delete",
129
+ cancelButtonText: "Cancel",
130
+ confirmButtonType: "danger",
131
+ onConfirm: async () => {
132
+ this.loading = true;
133
+
134
+ await axios.delete(
135
+ `${resource.api.endpoint(props)}/${model.id}`,
136
+ );
137
+
138
+ $emit("reload");
139
+
140
+ ElNotification({
141
+ title: "Success",
142
+ message: `${resource.singularTitle} with id ${model.id} deleted.`,
143
+ type: "success",
144
+ });
145
+
146
+ this.loading = false;
147
+ },
148
+ },
149
+ {
150
+ reference: () =>
151
+ h(
152
+ VelButton,
153
+ {
154
+ tag: "a",
155
+ type: "danger",
156
+ size: "small",
157
+ loading:
158
+ this.loading,
159
+ },
160
+ () => `Delete`,
161
+ ),
162
+ },
163
+ );
164
+ },
165
+ })
166
+ );
167
+ }
168
+ },
169
+ ],
170
+ structure: [
171
+ {
172
+ key: "id",
173
+ },
174
+ ],
175
+ },
176
+ description: {
177
+ structure: [
178
+ {
179
+ key: "id",
180
+ },
181
+ ],
182
+ },
183
+ index: {
184
+ structure: (props) => {
185
+ const { resource } = props;
186
+
187
+ return {
188
+ key: "PIndex",
189
+ "json-data": {
190
+ ...resource,
191
+ tableStructure: resource.table
192
+ .structure(props)
193
+ .concat(
194
+ resource.table.actions.length
195
+ ? [
196
+ {
197
+ key: "actions",
198
+ render: ({ model }, props) =>
199
+ h(
200
+ "div",
201
+ {
202
+ class: "flex gap-2",
203
+ },
204
+ resource.table.actions.map(
205
+ (d) =>
206
+ d(
207
+ {
208
+ model,
209
+ resource,
210
+ },
211
+ props,
212
+ ),
213
+ ),
214
+ ),
215
+ },
216
+ ]
217
+ : [],
218
+ ),
219
+ api: resource.api.endpoint(props),
220
+ },
221
+ apiParams: resource.api.params.index(props),
222
+ idKey: resource.id,
223
+ "fixed-height": false,
224
+ displayActions: false,
225
+ };
226
+ },
227
+ actions: [
228
+ (props) => {
229
+ const { resource, $router } = props;
230
+
231
+ if (resource.permissions.create(props)) {
232
+ return h(
233
+ VelButton,
234
+ {
235
+ tag: "a",
236
+ type: "primary",
237
+ size: "large",
238
+ onClick: () => {
239
+ $router.push({
240
+ name: `${resource.routeName}.create`,
241
+ });
242
+ },
243
+ },
244
+ () => [
245
+ h(resolveComponent("GIcon"), {
246
+ class: "fill-0 mr-0.5 icon--0.5",
247
+ name: "icon-plus",
248
+ embed: true,
249
+ artboard: true,
250
+ }),
251
+ `Create ${resource.singular}`,
252
+ ],
253
+ );
254
+ }
255
+ },
256
+ ],
257
+ layout: [
258
+ (props) => {
259
+ const { resource } = props;
260
+
261
+ return h(
262
+ VelTableSorter,
263
+ resource.index.structure(props),
264
+ );
265
+ },
266
+ ],
267
+ },
268
+ show: {
269
+ actions: [
270
+ (props) => {
271
+ const { resource, model, $router } = props;
272
+
273
+ if (resource.permissions.edit(props, { model })) {
274
+ return h(
275
+ VelButton,
276
+ {
277
+ tag: "a",
278
+ type: "primary",
279
+ onClick: () => {
280
+ $router.push({
281
+ name: `${resource.routeName}.edit`,
282
+ params: {
283
+ [resource.id]: model.id,
284
+ },
285
+ });
286
+ },
287
+ },
288
+ () => [
289
+ h(resolveComponent("GIcon"), {
290
+ class: "fill-0 mr-0.5 icon--0.5",
291
+ name: "icon-edit",
292
+ embed: true,
293
+ artboard: true,
294
+ }),
295
+ `Edit ${resource.singular}`,
296
+ ],
297
+ );
298
+ }
299
+ },
300
+ (props) => {
301
+ const { resource, model, $router } = props;
302
+
303
+ if (resource.permissions.delete(props, { model })) {
304
+ return h(
305
+ ElPopconfirm,
306
+ {
307
+ title: `Are you sure you want to delete this ${resource.singular}?`,
308
+ confirmButtonText: "Delete",
309
+ cancelButtonText: "Cancel",
310
+ confirmButtonType: "danger",
311
+ onConfirm: async () => {
312
+ await axios.delete(
313
+ `${resource.api.endpoint(props)}/${model.id}`,
314
+ );
315
+
316
+ $router.push({
317
+ name: `${resource.routeName}.index`,
318
+ });
319
+ },
320
+ },
321
+ {
322
+ reference: () =>
323
+ h(
324
+ VelButton,
325
+ {
326
+ tag: "a",
327
+ type: "danger",
328
+ },
329
+ () => [
330
+ h(resolveComponent("GIcon"), {
331
+ class: "fill-0 mr-0.5 icon--0.5",
332
+ name: "icon-trash",
333
+ embed: true,
334
+ artboard: true,
335
+ }),
336
+ `Delete ${resource.singular}`,
337
+ ],
338
+ ),
339
+ },
340
+ );
341
+ }
342
+ },
343
+ ],
344
+ tabs: [
345
+ (props) => {
346
+ const { resource } = props;
347
+
348
+ return {
349
+ label: "Details",
350
+ component: h(
351
+ "div",
352
+ resource.show.layout
353
+ .map((render) => render(props))
354
+ .filter((d) => d),
355
+ ),
356
+ };
357
+ },
358
+ ],
359
+ layout: [
360
+ (props) => {
361
+ const { resource, model } = props;
362
+
363
+ return h(
364
+ ElDescriptions,
365
+ {
366
+ border: true,
367
+ column: 1,
368
+ },
369
+ () =>
370
+ resource.description
371
+ .structure(props)
372
+ .map((item, index) =>
373
+ h(
374
+ ElDescriptionsItem,
375
+ {
376
+ key: index,
377
+ labelWidth: "20%",
378
+ },
379
+ {
380
+ label: () =>
381
+ item.label ||
382
+ item.key[0].toUpperCase() +
383
+ item.key.slice(1),
384
+ default: () =>
385
+ item.render
386
+ ? h(item.render(props))
387
+ : model?.[item.key] ||
388
+ "",
389
+ },
390
+ ),
391
+ ),
392
+ );
393
+ },
394
+ ],
395
+ },
396
+ },
397
+ properties,
398
+ );
399
+
400
+ if (result.trashable) {
401
+ applyTrashable(result);
402
+ }
403
+
404
+ return result;
405
+ }
406
+
407
+ export function columns(columns = []) {
408
+ return {
409
+ table: {
410
+ structure: (props) =>
411
+ columns
412
+ .filter(
413
+ (column) =>
414
+ resolveProperty(column.condition?.table, props) !==
415
+ false,
416
+ )
417
+ .map((column) => ({
418
+ ...column,
419
+ render: column.render?.read || column.render?.table,
420
+ })),
421
+ },
422
+ description: {
423
+ structure: (props) =>
424
+ columns
425
+ .filter(
426
+ (column) =>
427
+ resolveProperty(
428
+ column.condition?.description,
429
+ props,
430
+ ) !== false,
431
+ )
432
+ .map((column) => ({
433
+ ...column,
434
+ render:
435
+ column.render?.read || column.render?.description,
436
+ })),
437
+ },
438
+ form: {
439
+ fields: (props) =>
440
+ columns
441
+ .filter(
442
+ (column) =>
443
+ resolveProperty(column.condition?.form, props) !==
444
+ false,
445
+ )
446
+ .reduce((fields, column) => {
447
+ fields[column.key] = column.initial
448
+ ? column.initial(props)
449
+ : (props.model?.[column.key] ?? null);
450
+ return fields;
451
+ }, {}),
452
+ preparation: (props) =>
453
+ columns
454
+ .filter(
455
+ (column) =>
456
+ resolveProperty(column.condition?.form, props) !==
457
+ false,
458
+ )
459
+ .reduce((fields, column) => {
460
+ fields[column.key] = column.preparation
461
+ ? column.preparation(props)
462
+ : props.form[column.key];
463
+ return fields;
464
+ }, {}),
465
+ structure: (props) =>
466
+ columns
467
+ .filter(
468
+ (column) =>
469
+ resolveProperty(
470
+ column.condition?.form?.write ??
471
+ column.condition?.form,
472
+ props,
473
+ ) !== false,
474
+ )
475
+ .map((column) => ({
476
+ ...column,
477
+ render: column.render?.write || column.render?.form,
478
+ })),
479
+ },
480
+ };
481
+ }
482
+
483
+ // Export resource
484
+ export function routes(node, name, properties = {}, children = []) {
485
+ const resource = meta(name, properties);
486
+
487
+ return [
488
+ {
489
+ path: resource.path,
490
+ component: node ? "" : require("../resource/parent.vue").default,
491
+ name: `${resource.routeName}`,
492
+ meta: {
493
+ resource,
494
+ title: resource.title,
495
+ icon: resource.icon,
496
+ breadcrumb: () => resource.title,
497
+ ...properties.meta,
498
+ },
499
+ children: [
500
+ {
501
+ path: "",
502
+ component: node
503
+ ? ""
504
+ : resource.index.component ||
505
+ require("../resource/Children/index.vue").default,
506
+ name: `${resource.routeName}.index`,
507
+ },
508
+ {
509
+ path: "create",
510
+ component: node
511
+ ? ""
512
+ : resource.create?.component ||
513
+ require("../resource/Children/create.vue").default,
514
+ name: `${resource.routeName}.create`,
515
+ },
516
+ {
517
+ path: `:${resource.id}`,
518
+ component: node
519
+ ? ""
520
+ : resource.show.component ||
521
+ require("../resource/Children/show.vue").default,
522
+ name: `${resource.routeName}.show`,
523
+ // Remove leading / for nested routes or they'll resolve to the root of the site
524
+ children: cloneDeepWith(children, (value, key) => {
525
+ if (
526
+ key === "path" &&
527
+ typeof value === "string" &&
528
+ value.startsWith("/")
529
+ ) {
530
+ return value.slice(1);
531
+ }
532
+ }),
533
+ meta: {
534
+ breadcrumb: ({ $route }) => $route.params[resource.id],
535
+ },
536
+ },
537
+ {
538
+ path: `:${resource.id}/edit`,
539
+ component: node
540
+ ? ""
541
+ : resource.edit?.component ||
542
+ require("../resource/Children/edit.vue").default,
543
+ name: `${resource.routeName}.edit`,
544
+ meta: {
545
+ breadcrumb: ({ $route }) => $route.params[resource.id],
546
+ },
547
+ },
548
+ ],
549
+ },
550
+ ];
551
+ }
552
+
553
+ export function resolveProperty(value, context) {
554
+ return typeof value === "function" ? value(context) : value;
555
+ }
556
+
557
+ export default {
558
+ routes,
559
+ meta,
560
+ columns,
561
+ defaultResource,
562
+ };
@@ -0,0 +1,63 @@
1
+ <template>
2
+ <!-- Only render title once at the shallowest route -->
3
+ <template v-if="depth === 1">
4
+ <VelPageTitle :title="resource.title" />
5
+ <VelBreadcrumbs
6
+ :items="breadcrumbs"
7
+ class="mb-8"
8
+ container-classes="m-0"
9
+ />
10
+ </template>
11
+
12
+ <router-view
13
+ :key="$route.path"
14
+ v-slot="{ Component }"
15
+ :breadcrumbs="breadcrumbs"
16
+ :resource="resource"
17
+ >
18
+ <component :is="Component" :depth="depth + 1" />
19
+ </router-view>
20
+ </template>
21
+
22
+ <script>
23
+ import VelPageTitle from "../../components/layout/pageTitle.vue";
24
+ import VelBreadcrumbs from "../../components/layout/Breadcrumbs.vue";
25
+
26
+ export default {
27
+ components: {
28
+ VelPageTitle,
29
+ VelBreadcrumbs,
30
+ },
31
+
32
+ props: {
33
+ depth: {
34
+ type: Number,
35
+ default: 1,
36
+ },
37
+ },
38
+
39
+ computed: {
40
+ resource() {
41
+ return this.$route.meta.resource;
42
+ },
43
+ breadcrumbs() {
44
+ return [
45
+ {
46
+ text: "Home",
47
+ href: { name: "index" },
48
+ },
49
+ ].concat(
50
+ this.$route.matched
51
+ .filter((route) => route.meta?.breadcrumb)
52
+ .map((route) => ({
53
+ text: route.meta.breadcrumb(this),
54
+ href: {
55
+ name: route.name,
56
+ params: this.$route.params,
57
+ },
58
+ })),
59
+ );
60
+ },
61
+ },
62
+ };
63
+ </script>