@fishawack/lab-velocity 2.0.0-beta.3 → 2.0.0-beta.30

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 (106) hide show
  1. package/README.md +441 -37
  2. package/_Build/vue/components/basic/Button.vue +1 -1
  3. package/_Build/vue/components/form/Select.vue +2 -2
  4. package/_Build/vue/components/form/Spinner.vue +5 -0
  5. package/_Build/vue/components/layout/Alert.vue +5 -5
  6. package/_Build/vue/{modules/AuthModule/components/VBreadcrumbs.vue → components/layout/Breadcrumbs.vue} +4 -4
  7. package/_Build/vue/{modules/AuthModule/components → components/layout}/Chips.vue +2 -2
  8. package/_Build/vue/components/layout/Footer.vue +11 -10
  9. package/_Build/vue/{modules/AuthModule/components/VFormFooter.vue → components/layout/FormFooter.vue} +13 -7
  10. package/_Build/vue/{modules/AuthModule/components → components/layout}/FormRole.vue +10 -8
  11. package/_Build/vue/components/layout/Layout.vue +76 -0
  12. package/_Build/vue/components/layout/Navigation.vue +77 -0
  13. package/_Build/vue/{modules/AuthModule/components/VPageHeader.vue → components/layout/PageHeader.vue} +7 -2
  14. package/_Build/vue/components/layout/SideBar.vue +26 -0
  15. package/_Build/vue/{modules/AuthModule/components/VTable.vue → components/layout/Table.vue} +32 -16
  16. package/_Build/vue/{modules/AuthModule/components/VTableSorter.vue → components/layout/TableSorter.vue} +68 -43
  17. package/_Build/vue/components/layout/pageTitle.vue +1 -1
  18. package/_Build/vue/components/navigation/MenuItem.vue +7 -2
  19. package/_Build/vue/components/navigation/MenuItemGroup.vue +7 -2
  20. package/_Build/vue/modules/AuthModule/js/axios.js +19 -0
  21. package/_Build/vue/modules/AuthModule/js/router.js +28 -88
  22. package/_Build/vue/modules/AuthModule/js/store.js +15 -6
  23. package/_Build/vue/modules/AuthModule/{adminRoutes/PCompanies/Children/partials → routes/PCompanies}/form.vue +50 -18
  24. package/_Build/vue/modules/AuthModule/routes/PCompanies/resource.js +259 -0
  25. package/_Build/vue/modules/AuthModule/routes/PTeams/resource.js +308 -0
  26. package/_Build/vue/modules/AuthModule/{adminRoutes/PUsers/Children/partials → routes/PUsers}/form.vue +30 -18
  27. package/_Build/vue/modules/AuthModule/routes/PUsers/resource.js +215 -0
  28. package/_Build/vue/modules/AuthModule/routes/account-exists.vue +2 -2
  29. package/_Build/vue/modules/AuthModule/routes/change-password.vue +23 -24
  30. package/_Build/vue/modules/AuthModule/routes/container.vue +2 -11
  31. package/_Build/vue/modules/AuthModule/routes/expired-reset.vue +4 -4
  32. package/_Build/vue/modules/AuthModule/routes/expired-verification.vue +9 -8
  33. package/_Build/vue/modules/AuthModule/routes/force-reset.vue +24 -28
  34. package/_Build/vue/modules/AuthModule/routes/forgot.vue +4 -4
  35. package/_Build/vue/modules/AuthModule/routes/login.vue +7 -11
  36. package/_Build/vue/modules/AuthModule/routes/logincallback.vue +2 -4
  37. package/_Build/vue/modules/AuthModule/routes/loginsso.vue +7 -9
  38. package/_Build/vue/modules/AuthModule/routes/logout.vue +1 -3
  39. package/_Build/vue/modules/AuthModule/routes/logoutheadless.vue +1 -3
  40. package/_Build/vue/modules/AuthModule/routes/register.vue +19 -21
  41. package/_Build/vue/modules/AuthModule/routes/reset.vue +14 -13
  42. package/_Build/vue/modules/AuthModule/routes/success-forgot.vue +8 -7
  43. package/_Build/vue/modules/AuthModule/routes/success-reset.vue +2 -2
  44. package/_Build/vue/modules/AuthModule/routes/success-verify.vue +1 -3
  45. package/_Build/vue/modules/AuthModule/routes/verify.vue +11 -14
  46. package/_Build/vue/modules/resource/Children/create.vue +70 -0
  47. package/_Build/vue/modules/resource/Children/edit.vue +92 -0
  48. package/_Build/vue/modules/resource/Children/index.vue +42 -0
  49. package/_Build/vue/modules/resource/Children/partials/form.vue +57 -0
  50. package/_Build/vue/modules/resource/Children/show.vue +133 -0
  51. package/_Build/vue/modules/resource/index.js +486 -0
  52. package/_Build/vue/modules/resource/parent.vue +63 -0
  53. package/_base.scss +0 -1
  54. package/_defaults.scss +2 -13
  55. package/_variables.scss +9 -4
  56. package/components/_alert.scss +5 -0
  57. package/components/_auth.scss +163 -0
  58. package/components/_basic.scss +55 -0
  59. package/components/_breadcrumbs.scss +39 -0
  60. package/components/_button.scss +304 -0
  61. package/components/_cascader.scss +12 -0
  62. package/components/_checkbox.scss +41 -0
  63. package/components/_chip.scss +24 -0
  64. package/components/_collapse.scss +24 -0
  65. package/components/_datepicker.scss +52 -0
  66. package/components/_descriptions.scss +2 -0
  67. package/components/_footer.scss +47 -0
  68. package/components/_form.scss +24 -0
  69. package/components/_header.scss +30 -0
  70. package/components/_icon.scss +25 -0
  71. package/components/_inputNumber.scss +22 -0
  72. package/components/_layout.scss +56 -0
  73. package/components/_link.scss +44 -0
  74. package/components/_loader.scss +43 -0
  75. package/components/_menu.scss +112 -0
  76. package/components/_modal.scss +24 -0
  77. package/components/_pageTitle.scss +8 -0
  78. package/components/_permissionLegend.scss +18 -0
  79. package/components/_select.scss +29 -0
  80. package/components/_sidebar.scss +41 -0
  81. package/components/_switch.scss +14 -0
  82. package/components/_table.scss +20 -0
  83. package/components/_tooltip.scss +4 -0
  84. package/components/_typography.scss +162 -0
  85. package/components/_upload.scss +15 -0
  86. package/components/_wysiwyg.scss +7 -0
  87. package/components/_wysiwyg2.scss +142 -0
  88. package/index.js +9 -1
  89. package/package.json +4 -2
  90. package/vendor.scss +0 -1
  91. package/_Build/vue/components/layout/sideBar.vue +0 -25
  92. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/Upload/upload.vue +0 -251
  93. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/create.vue +0 -62
  94. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/edit.vue +0 -98
  95. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/index.vue +0 -90
  96. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/show.vue +0 -262
  97. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/parent.vue +0 -36
  98. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/create.vue +0 -112
  99. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/edit.vue +0 -103
  100. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/index.vue +0 -112
  101. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/show.vue +0 -120
  102. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/parent.vue +0 -36
  103. /package/_Build/vue/{modules/AuthModule/components → components/layout}/AuthModal.vue +0 -0
  104. /package/_Build/vue/{modules/AuthModule/components → components/layout}/Chip.vue +0 -0
  105. /package/_Build/vue/{modules/AuthModule/components/VPasswordValidation.vue → components/layout/PasswordValidation.vue} +0 -0
  106. /package/_Build/vue/{modules/AuthModule/components/VRoleLegend.vue → components/layout/RoleLegend.vue} +0 -0
@@ -0,0 +1,133 @@
1
+ <template>
2
+ <!-- Render nested router view if this isn't the deepest route -->
3
+ <router-view v-if="!deepestRoute" v-slot="{ Component }">
4
+ <component :is="Component" :depth="depth + 1" />
5
+ </router-view>
6
+
7
+ <template v-else>
8
+ <div class="container px-6 tablet:px-4 mobile:px-2 mb-8 ml-0 mr-0">
9
+ <div class="grid__1/1">
10
+ <template v-if="model">
11
+ <div class="bg-0 p-3 box-shadow-1 border-r-4 mb-6">
12
+ <VelPageHeader
13
+ :icon="resource.icon"
14
+ :title="`${model.name ?? model.id} ${model.last_name ?? ''}`"
15
+ >
16
+ <template
17
+ v-for="(rendered, index) in renderedActions"
18
+ :key="index"
19
+ >
20
+ <component :is="rendered" />
21
+ </template>
22
+ </VelPageHeader>
23
+
24
+ <hr class="my-3 hr-muted" />
25
+
26
+ <el-tabs v-model="active" type="card">
27
+ <template
28
+ v-for="(rendered, index) in renderedLayout"
29
+ :key="index"
30
+ >
31
+ <el-tab-pane :name="index">
32
+ <template #label>
33
+ <span class="align-middle-dive">
34
+ <GIcon
35
+ v-if="rendered.icon"
36
+ class="icon icon--text mr"
37
+ :name="rendered.icon"
38
+ />
39
+ <span>{{
40
+ rendered.label ||
41
+ `Tab ${index + 1}`
42
+ }}</span>
43
+ </span>
44
+ </template>
45
+ <component
46
+ :is="rendered.component || rendered"
47
+ />
48
+ </el-tab-pane>
49
+ </template>
50
+ </el-tabs>
51
+ </div>
52
+ </template>
53
+ <div v-else class="absolute transform-center text-center">
54
+ <VelSpinner />
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </template>
59
+ </template>
60
+
61
+ <script>
62
+ import axios from "axios";
63
+ import VelSpinner from "../../../components/form/Spinner.vue";
64
+ import VelButton from "../../../components/basic/Button.vue";
65
+ import { ElTabs, ElTabPane } from "element-plus";
66
+
67
+ export default {
68
+ components: {
69
+ VelPageHeader: require("../../../components/layout/PageHeader.vue")
70
+ .default,
71
+ VelSpinner,
72
+ VelButton,
73
+ ElTabs,
74
+ ElTabPane,
75
+ },
76
+
77
+ props: {
78
+ breadcrumbs: {
79
+ type: Array,
80
+ required: true,
81
+ },
82
+ resource: {
83
+ type: Object,
84
+ required: true,
85
+ },
86
+ depth: {
87
+ type: Number,
88
+ required: true,
89
+ },
90
+ },
91
+
92
+ data() {
93
+ return {
94
+ model: null,
95
+ active: 0,
96
+ };
97
+ },
98
+
99
+ computed: {
100
+ // This boolean helps determine if we are the final depth of route being rendered
101
+ deepestRoute() {
102
+ return this.depth === this.$route.matched.length;
103
+ },
104
+
105
+ // Compute rendered layout once
106
+ renderedLayout() {
107
+ return this.resource.show.layout.map((render) => render(this));
108
+ },
109
+
110
+ // Compute rendered actions once
111
+ renderedActions() {
112
+ return this.resource.show.actions.map((render) => render(this));
113
+ },
114
+ },
115
+
116
+ mounted() {
117
+ if (!this.deepestRoute) {
118
+ return;
119
+ }
120
+
121
+ axios
122
+ .get(
123
+ `${this.resource.api.endpoint(this)}/${this.$route.params[`${this.resource.slug}Id`]}`,
124
+ {
125
+ params: this.resource.api.params.show(this),
126
+ },
127
+ )
128
+ .then((res) => {
129
+ this.model = res.data.data;
130
+ });
131
+ },
132
+ };
133
+ </script>
@@ -0,0 +1,486 @@
1
+ "use strict";
2
+
3
+ import { merge, kebabCase, 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
+
16
+ export const defaultResource = meta();
17
+
18
+ export function meta(name = "default", properties = {}) {
19
+ const singular = properties.singular || name.slice(0, -1);
20
+ const slug = properties.slug || kebabCase(name);
21
+
22
+ return merge(
23
+ {
24
+ name,
25
+ title: properties.title || name[0].toUpperCase() + name.slice(1),
26
+ singular,
27
+ singularTitle:
28
+ properties.singularTitle ||
29
+ singular[0].toUpperCase() + singular.slice(1),
30
+ label: singular,
31
+ multiLabel: name,
32
+ slug,
33
+ id: properties.id || `${slug}Id`,
34
+ icon: `icon-${singular}`,
35
+ api: {
36
+ endpoint: () => `/api/${properties.slug || kebabCase(name)}`,
37
+ params: {
38
+ index: () => ({}),
39
+ show: () => ({}),
40
+ },
41
+ },
42
+ permissions: {
43
+ create: () => true,
44
+ edit: () => true,
45
+ delete: () => false,
46
+ },
47
+ searchable: {
48
+ value: "name",
49
+ label: `Search ${name}`,
50
+ },
51
+ form: {
52
+ component: null,
53
+ fields: () => ({}),
54
+ structure: [],
55
+ },
56
+ table: {
57
+ actions: [
58
+ ({ model, resource }, { $router }) =>
59
+ h(
60
+ VelButton,
61
+ {
62
+ tag: "a",
63
+ size: "small",
64
+ type: "primary",
65
+ onClick: () => {
66
+ $router.push({
67
+ name: `${resource.slug}.show`,
68
+ params: {
69
+ [resource.id]: model.id,
70
+ },
71
+ });
72
+ },
73
+ },
74
+ () => "View",
75
+ ),
76
+ ({ model, resource }, props) => {
77
+ const { $router } = props;
78
+
79
+ if (resource.permissions.edit(props, { model })) {
80
+ return h(
81
+ VelButton,
82
+ {
83
+ tag: "a",
84
+ size: "small",
85
+ onClick: () => {
86
+ $router.push({
87
+ name: `${resource.slug}.edit`,
88
+ params: {
89
+ [resource.id]: model.id,
90
+ },
91
+ });
92
+ },
93
+ },
94
+ () => "Edit",
95
+ );
96
+ }
97
+ },
98
+ ({ model, resource }, props) => {
99
+ const { $emit } = props;
100
+
101
+ if (resource.permissions.delete(props, { model })) {
102
+ return h({
103
+ data: () => ({
104
+ loading: false,
105
+ }),
106
+ render() {
107
+ return h(
108
+ ElPopconfirm,
109
+ {
110
+ title: `Are you sure you want to delete this ${resource.singular}?`,
111
+ confirmButtonText: "Delete",
112
+ cancelButtonText: "Cancel",
113
+ confirmButtonType: "danger",
114
+ onConfirm: async () => {
115
+ this.loading = true;
116
+
117
+ await axios.delete(
118
+ `${resource.api.endpoint(props)}/${model.id}`,
119
+ );
120
+
121
+ $emit("reload");
122
+
123
+ ElNotification({
124
+ title: "Success",
125
+ message: `${resource.singularTitle} with id ${model.id} deleted.`,
126
+ type: "success",
127
+ });
128
+
129
+ this.loading = false;
130
+ },
131
+ },
132
+ {
133
+ reference: () =>
134
+ h(
135
+ VelButton,
136
+ {
137
+ tag: "a",
138
+ type: "danger",
139
+ size: "small",
140
+ loading: this.loading,
141
+ },
142
+ () => `Delete`,
143
+ ),
144
+ },
145
+ );
146
+ },
147
+ });
148
+ }
149
+ },
150
+ ],
151
+ structure: [
152
+ {
153
+ key: "id",
154
+ },
155
+ ],
156
+ },
157
+ description: {
158
+ structure: [
159
+ {
160
+ key: "id",
161
+ },
162
+ ],
163
+ },
164
+ index: {
165
+ structure: (props) => {
166
+ const { resource } = props;
167
+
168
+ return {
169
+ key: "PIndex",
170
+ "json-data": {
171
+ ...resource,
172
+ tableStructure: resource.table.structure.concat(
173
+ resource.table.actions.length
174
+ ? [
175
+ {
176
+ key: "actions",
177
+ render: ({ model }, props) =>
178
+ h(
179
+ "div",
180
+ {
181
+ class: "flex gap-2",
182
+ },
183
+ resource.table.actions.map(
184
+ (d) =>
185
+ d(
186
+ {
187
+ model,
188
+ resource,
189
+ },
190
+ props,
191
+ ),
192
+ ),
193
+ ),
194
+ },
195
+ ]
196
+ : [],
197
+ ),
198
+ api: resource.api.endpoint(props),
199
+ },
200
+ apiParams: resource.api.params.index(props),
201
+ idKey: resource.id,
202
+ "fixed-height": false,
203
+ displayActions: false,
204
+ };
205
+ },
206
+ actions: [
207
+ (props) => {
208
+ const { resource, $router } = props;
209
+
210
+ if (resource.permissions.create(props)) {
211
+ return h(
212
+ VelButton,
213
+ {
214
+ tag: "a",
215
+ type: "primary",
216
+ size: "large",
217
+ onClick: () => {
218
+ $router.push({
219
+ name: `${resource.slug}.create`,
220
+ });
221
+ },
222
+ },
223
+ () => [
224
+ h(resolveComponent("GIcon"), {
225
+ class: "fill-0 mr-0.5 icon--0.5",
226
+ name: "icon-plus",
227
+ embed: true,
228
+ artboard: true,
229
+ }),
230
+ `Create ${resource.singular}`,
231
+ ],
232
+ );
233
+ }
234
+ },
235
+ ],
236
+ layout: [
237
+ (props) => {
238
+ const { resource } = props;
239
+
240
+ return h(
241
+ VelTableSorter,
242
+ resource.index.structure(props),
243
+ );
244
+ },
245
+ ],
246
+ },
247
+ show: {
248
+ actions: [
249
+ (props) => {
250
+ const { resource, model, $router } = props;
251
+
252
+ if (resource.permissions.edit(props, { model })) {
253
+ return h(
254
+ VelButton,
255
+ {
256
+ tag: "a",
257
+ type: "primary",
258
+ onClick: () => {
259
+ $router.push({
260
+ name: `${resource.slug}.edit`,
261
+ params: {
262
+ [resource.id]: model.id,
263
+ },
264
+ });
265
+ },
266
+ },
267
+ () => [
268
+ h(resolveComponent("GIcon"), {
269
+ class: "fill-0 mr-0.5 icon--0.5",
270
+ name: "icon-edit",
271
+ embed: true,
272
+ artboard: true,
273
+ }),
274
+ `Edit ${resource.singular}`,
275
+ ],
276
+ );
277
+ }
278
+ },
279
+ (props) => {
280
+ const { resource, model, $router } = props;
281
+
282
+ if (resource.permissions.delete(props, { model })) {
283
+ return h(
284
+ ElPopconfirm,
285
+ {
286
+ title: `Are you sure you want to delete this ${resource.singular}?`,
287
+ confirmButtonText: "Delete",
288
+ cancelButtonText: "Cancel",
289
+ confirmButtonType: "danger",
290
+ onConfirm: async () => {
291
+ await axios.delete(
292
+ `${resource.api.endpoint(props)}/${model.id}`,
293
+ );
294
+
295
+ $router.push({
296
+ name: `${resource.slug}.index`,
297
+ });
298
+ },
299
+ },
300
+ {
301
+ reference: () =>
302
+ h(
303
+ VelButton,
304
+ {
305
+ tag: "a",
306
+ type: "danger",
307
+ },
308
+ () => [
309
+ h(resolveComponent("GIcon"), {
310
+ class: "fill-0 mr-0.5 icon--0.5",
311
+ name: "icon-trash",
312
+ embed: true,
313
+ artboard: true,
314
+ }),
315
+ `Delete ${resource.singular}`,
316
+ ],
317
+ ),
318
+ },
319
+ );
320
+ }
321
+ },
322
+ ],
323
+ layout: [
324
+ (props) => {
325
+ const { resource, model } = props;
326
+
327
+ return {
328
+ label: "Details",
329
+ component: h(
330
+ ElDescriptions,
331
+ {
332
+ border: true,
333
+ column: 1,
334
+ },
335
+ () =>
336
+ resource.description.structure.map(
337
+ (item, index) =>
338
+ h(
339
+ ElDescriptionsItem,
340
+ {
341
+ key: index,
342
+ labelWidth: "20%",
343
+ },
344
+ {
345
+ label: () =>
346
+ item.label ||
347
+ item.key[0].toUpperCase() +
348
+ item.key.slice(1),
349
+ default: () =>
350
+ item.render
351
+ ? h(
352
+ item.render(
353
+ props,
354
+ ),
355
+ )
356
+ : model?.[
357
+ item.key
358
+ ] || "",
359
+ },
360
+ ),
361
+ ),
362
+ ),
363
+ };
364
+ },
365
+ ],
366
+ },
367
+ },
368
+ properties,
369
+ );
370
+ }
371
+
372
+ export function columns(columns = []) {
373
+ return {
374
+ table: {
375
+ structure: columns
376
+ .filter((column) => !column.filter?.table)
377
+ .map((column) => ({
378
+ ...column,
379
+ render: column.render?.read || column.render?.table,
380
+ })),
381
+ },
382
+ description: {
383
+ structure: columns
384
+ .filter((column) => !column.filter?.description)
385
+ .map((column) => ({
386
+ ...column,
387
+ render: column.render?.read || column.render?.description,
388
+ })),
389
+ },
390
+ form: {
391
+ fields: (props) =>
392
+ columns
393
+ .filter((column) => !column.filter?.form)
394
+ .reduce((fields, column) => {
395
+ fields[column.key] = column.initial
396
+ ? column.initial(props)
397
+ : (props.model?.[column.key] ?? null);
398
+ return fields;
399
+ }, {}),
400
+ structure: columns
401
+ .filter((column) => !column.filter?.form)
402
+ .map((column) => ({
403
+ ...column,
404
+ render: column.render?.write || column.render?.form,
405
+ })),
406
+ },
407
+ };
408
+ }
409
+
410
+ // Export resource
411
+ export function routes(
412
+ node,
413
+ name,
414
+ properties = {},
415
+ children = [],
416
+ isChild = false,
417
+ ) {
418
+ const resource = meta(name, properties);
419
+
420
+ return [
421
+ {
422
+ path: `${isChild ? "" : "/"}${resource.slug}`,
423
+ component: node ? "" : require("../resource/parent.vue").default,
424
+ name,
425
+ meta: {
426
+ resource,
427
+ title: resource.title,
428
+ icon: resource.icon,
429
+ breadcrumb: () => resource.title,
430
+ },
431
+ children: [
432
+ {
433
+ path: "",
434
+ component: node
435
+ ? ""
436
+ : require("../resource/Children/index.vue").default,
437
+ name: `${resource.slug}.index`,
438
+ },
439
+ {
440
+ path: "create",
441
+ component: node
442
+ ? ""
443
+ : require("../resource/Children/create.vue").default,
444
+ name: `${resource.slug}.create`,
445
+ },
446
+ {
447
+ path: `:${resource.id}`,
448
+ component: node
449
+ ? ""
450
+ : require("../resource/Children/show.vue").default,
451
+ name: `${resource.slug}.show`,
452
+ // Remove leading / for nested routes or they'll resolve to the root of the site
453
+ children: cloneDeepWith(children, (value, key) => {
454
+ if (
455
+ key === "path" &&
456
+ typeof value === "string" &&
457
+ value.startsWith("/")
458
+ ) {
459
+ return value.slice(1);
460
+ }
461
+ }),
462
+ meta: {
463
+ breadcrumb: ({ $route }) => $route.params[resource.id],
464
+ },
465
+ },
466
+ {
467
+ path: `:${resource.id}/edit`,
468
+ component: node
469
+ ? ""
470
+ : require("../resource/Children/edit.vue").default,
471
+ name: `${resource.slug}.edit`,
472
+ meta: {
473
+ breadcrumb: ({ $route }) => $route.params[resource.id],
474
+ },
475
+ },
476
+ ],
477
+ },
478
+ ];
479
+ }
480
+
481
+ export default {
482
+ routes,
483
+ meta,
484
+ columns,
485
+ defaultResource,
486
+ };
@@ -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>
package/_base.scss CHANGED
@@ -3,4 +3,3 @@
3
3
  @import "element-plus/theme-chalk/el-message";
4
4
  @import "element-plus/theme-chalk/el-notification";
5
5
  @import "element-plus/theme-chalk/el-overlay";
6
- @import "./components/input.scss";