@fishawack/lab-velocity 2.0.0-beta.13 → 2.0.0-beta.14
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 +118 -43
- package/_Build/vue/components/layout/Layout.vue +9 -7
- package/_Build/vue/modules/AuthModule/routes/PCompanies/resource.js +92 -106
- package/_Build/vue/modules/AuthModule/routes/PUsers/resource.js +35 -48
- package/_Build/vue/modules/resource/Children/index.vue +4 -8
- package/_Build/vue/modules/resource/Children/show.vue +6 -35
- package/_Build/vue/modules/resource/index.js +66 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -252,8 +252,8 @@ export function meta(name, properties = {}) {
|
|
|
252
252
|
pageLink: name,
|
|
253
253
|
api: `/api/${name}`,
|
|
254
254
|
permissions: {
|
|
255
|
-
create: true,
|
|
256
|
-
edit: true,
|
|
255
|
+
create: () => true,
|
|
256
|
+
edit: () => true,
|
|
257
257
|
},
|
|
258
258
|
searchable: {
|
|
259
259
|
value: "name",
|
|
@@ -264,30 +264,73 @@ export function meta(name, properties = {}) {
|
|
|
264
264
|
fields: () => ({}),
|
|
265
265
|
},
|
|
266
266
|
table: {
|
|
267
|
-
structure: [
|
|
267
|
+
structure: [
|
|
268
|
+
{
|
|
269
|
+
key: "id",
|
|
270
|
+
},
|
|
271
|
+
],
|
|
268
272
|
},
|
|
269
|
-
|
|
270
|
-
actions: [],
|
|
273
|
+
description: {
|
|
271
274
|
structure: [
|
|
272
275
|
{
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
276
|
+
key: "id",
|
|
277
|
+
},
|
|
278
|
+
],
|
|
279
|
+
},
|
|
280
|
+
index: {
|
|
281
|
+
actions: [],
|
|
282
|
+
layout: [
|
|
283
|
+
(props) => {
|
|
284
|
+
const { resource } = props;
|
|
285
|
+
|
|
286
|
+
return h(VTableSorter, {
|
|
287
|
+
key: "PIndex",
|
|
288
|
+
"json-data": {
|
|
289
|
+
...resource,
|
|
290
|
+
tableStructure: resource.table.structure,
|
|
291
|
+
},
|
|
292
|
+
defaults: resource.defaults,
|
|
293
|
+
"fixed-height": false,
|
|
294
|
+
"display-edit-action":
|
|
295
|
+
resource.permissions.create(props),
|
|
296
|
+
});
|
|
285
297
|
},
|
|
286
298
|
],
|
|
287
299
|
},
|
|
288
300
|
show: {
|
|
289
301
|
actions: [],
|
|
290
|
-
|
|
302
|
+
layout: [
|
|
303
|
+
(props) => {
|
|
304
|
+
const { resource, model } = props;
|
|
305
|
+
|
|
306
|
+
return h(
|
|
307
|
+
ElDescriptions,
|
|
308
|
+
{
|
|
309
|
+
border: true,
|
|
310
|
+
column: 1,
|
|
311
|
+
},
|
|
312
|
+
resource.description.structure.map((item, index) =>
|
|
313
|
+
h(
|
|
314
|
+
ElDescriptionsItem,
|
|
315
|
+
{
|
|
316
|
+
key: index,
|
|
317
|
+
labelWidth: "20%",
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
label: () =>
|
|
321
|
+
item.label ||
|
|
322
|
+
item.key[0].toUpperCase() +
|
|
323
|
+
item.key.slice(1),
|
|
324
|
+
default: () =>
|
|
325
|
+
item.render
|
|
326
|
+
? h(item.render(props))
|
|
327
|
+
: model?.[item.key] || "",
|
|
328
|
+
},
|
|
329
|
+
),
|
|
330
|
+
),
|
|
331
|
+
);
|
|
332
|
+
},
|
|
333
|
+
],
|
|
291
334
|
},
|
|
292
335
|
defaults: "",
|
|
293
336
|
icon: `icon-${singular}`,
|
|
@@ -297,7 +340,7 @@ export function meta(name, properties = {}) {
|
|
|
297
340
|
}
|
|
298
341
|
```
|
|
299
342
|
|
|
300
|
-
|
|
343
|
+
Layout is an array of functions that return render functions
|
|
301
344
|
|
|
302
345
|
```js
|
|
303
346
|
import { h, resolveComponent } from "vue";
|
|
@@ -305,34 +348,66 @@ import { h, resolveComponent } from "vue";
|
|
|
305
348
|
{
|
|
306
349
|
// ...
|
|
307
350
|
show: {
|
|
351
|
+
layout: [
|
|
352
|
+
({ model }) =>
|
|
353
|
+
h(resolveComponent("router-link"), {
|
|
354
|
+
class: "underline",
|
|
355
|
+
to: {
|
|
356
|
+
name: "companies.show",
|
|
357
|
+
params: { id: model.company_id },
|
|
358
|
+
},
|
|
359
|
+
text: model.company.name,
|
|
360
|
+
})
|
|
361
|
+
],
|
|
362
|
+
},
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
You can see above that the show & index route have a default render function returned for a table and description block. If you want to keep this you can grab the existing render function from the `defaultResource` export.
|
|
367
|
+
|
|
368
|
+
```js
|
|
369
|
+
import { defaultResource, meta } from "../../../resource/index.js";
|
|
370
|
+
|
|
371
|
+
{
|
|
372
|
+
// ...
|
|
373
|
+
index: {
|
|
374
|
+
layout: [
|
|
375
|
+
() => h("div", "I appear above"),
|
|
376
|
+
...defaultResource.index.layout,
|
|
377
|
+
() => h("div", "I appear below"),
|
|
378
|
+
],
|
|
379
|
+
},
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
Structure arrays take objects. The objects require a key only but have other optional properties. A render function can also be passed to fully customize the rendering that happens.
|
|
384
|
+
|
|
385
|
+
```js
|
|
386
|
+
{
|
|
387
|
+
table: {
|
|
308
388
|
structure: [
|
|
309
|
-
[
|
|
310
|
-
{
|
|
311
|
-
label: "Email",
|
|
312
|
-
key: "email",
|
|
313
|
-
},
|
|
314
|
-
{
|
|
315
|
-
label: "Company",
|
|
316
|
-
render: ({ model }) =>
|
|
317
|
-
h(resolveComponent("router-link"), {
|
|
318
|
-
class: "underline",
|
|
319
|
-
to: {
|
|
320
|
-
name: "companies.show",
|
|
321
|
-
params: { id: model.company_id },
|
|
322
|
-
},
|
|
323
|
-
text: model.company.name,
|
|
324
|
-
}),
|
|
325
|
-
},
|
|
326
|
-
],
|
|
327
389
|
{
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
390
|
+
key: "name",
|
|
391
|
+
sortable: true,
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
key: "role",
|
|
395
|
+
render: (row) => h("div", "Custom template"),
|
|
334
396
|
},
|
|
335
397
|
],
|
|
336
398
|
},
|
|
399
|
+
form: {
|
|
400
|
+
structure: [
|
|
401
|
+
{
|
|
402
|
+
key: "name",
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
key: "provider_name",
|
|
406
|
+
label: "Provider",
|
|
407
|
+
render: ({ model }) => h("span", model?.provider_name.label ?? ""),
|
|
408
|
+
initial: ({ model }) => model?.provider_name.value ?? null,
|
|
409
|
+
}
|
|
410
|
+
]
|
|
411
|
+
}
|
|
337
412
|
}
|
|
338
413
|
```
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="vel-app">
|
|
3
3
|
<VelHeader class="justify-end-dive">
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
<router-link :to="{ name: 'index' }">
|
|
5
|
+
<GSvg
|
|
6
|
+
class="logo"
|
|
7
|
+
style="width: 180px"
|
|
8
|
+
embed
|
|
9
|
+
asis
|
|
10
|
+
:name="$store.state.auth.logo"
|
|
11
|
+
/>
|
|
12
|
+
</router-link>
|
|
11
13
|
<template #links>
|
|
12
14
|
<div class="flex items-center pr">
|
|
13
15
|
<GIcon
|
|
@@ -6,7 +6,7 @@ import VelTableSorter from "../../../../components/layout/TableSorter.vue";
|
|
|
6
6
|
import VelRoleLegend from "../../../../components/layout/RoleLegend.vue";
|
|
7
7
|
import component from "./form.vue";
|
|
8
8
|
import userResource from "../PUsers/resource.js";
|
|
9
|
-
import { meta } from "../../../resource/index.js";
|
|
9
|
+
import { defaultResource, meta } from "../../../resource/index.js";
|
|
10
10
|
|
|
11
11
|
import { ElNotification } from "element-plus";
|
|
12
12
|
import { h } from "vue";
|
|
@@ -16,6 +16,10 @@ export default [
|
|
|
16
16
|
"companies",
|
|
17
17
|
{
|
|
18
18
|
defaults: "include=primary_contact",
|
|
19
|
+
permissions: {
|
|
20
|
+
create: ({ $store }) => $store.getters.can("write companies"),
|
|
21
|
+
edit: ({ $store }) => $store.getters.can("write companies"),
|
|
22
|
+
},
|
|
19
23
|
singular: "company",
|
|
20
24
|
icon: "icon-cases",
|
|
21
25
|
form: {
|
|
@@ -34,18 +38,16 @@ export default [
|
|
|
34
38
|
table: {
|
|
35
39
|
structure: [
|
|
36
40
|
{
|
|
37
|
-
label: "Name",
|
|
38
41
|
key: "name",
|
|
39
42
|
sortable: true,
|
|
40
43
|
},
|
|
41
44
|
{
|
|
42
|
-
label: "Total users",
|
|
43
45
|
key: "user_count",
|
|
44
|
-
|
|
46
|
+
label: "Total users",
|
|
45
47
|
width: "150",
|
|
46
48
|
},
|
|
47
49
|
{
|
|
48
|
-
|
|
50
|
+
key: "role",
|
|
49
51
|
render: (row) =>
|
|
50
52
|
h(
|
|
51
53
|
row.roles.length === 1 ? Chip : Chips,
|
|
@@ -59,120 +61,104 @@ export default [
|
|
|
59
61
|
},
|
|
60
62
|
],
|
|
61
63
|
},
|
|
62
|
-
|
|
64
|
+
description: {
|
|
63
65
|
structure: [
|
|
64
66
|
{
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
key: "PIndex",
|
|
68
|
-
"json-data": {
|
|
69
|
-
...resource,
|
|
70
|
-
tableStructure: resource.table.structure,
|
|
71
|
-
},
|
|
72
|
-
defaults: resource.defaults,
|
|
73
|
-
"fixed-height": false,
|
|
74
|
-
"display-edit-action":
|
|
75
|
-
$store.getters.can("write companies"),
|
|
76
|
-
}),
|
|
67
|
+
key: "domains",
|
|
68
|
+
render: ({ model }) => h("span", model.domains.join(", ")),
|
|
77
69
|
},
|
|
78
70
|
{
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
class: "mt-5",
|
|
82
|
-
}),
|
|
71
|
+
key: "sso_enabled",
|
|
72
|
+
label: "SSO Enabled",
|
|
83
73
|
},
|
|
84
|
-
],
|
|
85
|
-
},
|
|
86
|
-
show: {
|
|
87
|
-
actions: [
|
|
88
74
|
{
|
|
75
|
+
key: "primary_contact",
|
|
76
|
+
label: "Primary Contact",
|
|
89
77
|
render: ({ model }) =>
|
|
90
|
-
model.primary_contact
|
|
91
|
-
h(
|
|
92
|
-
VelButton,
|
|
93
|
-
{
|
|
94
|
-
type: "primary",
|
|
95
|
-
async onClick() {
|
|
96
|
-
try {
|
|
97
|
-
const res = await axios.post(
|
|
98
|
-
`/api/companies/${model.id}/welcome`,
|
|
99
|
-
);
|
|
100
|
-
ElNotification({
|
|
101
|
-
title: "Success",
|
|
102
|
-
message: res.data.message,
|
|
103
|
-
type: "success",
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
model.primary_contact_contacted = true;
|
|
107
|
-
} catch (e) {
|
|
108
|
-
ElNotification({
|
|
109
|
-
title: "Warning",
|
|
110
|
-
message:
|
|
111
|
-
e.response?.data?.message ||
|
|
112
|
-
e.message,
|
|
113
|
-
type: "warning",
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
},
|
|
118
|
-
"Send welcome email",
|
|
119
|
-
),
|
|
78
|
+
h("span", model.primary_contact?.name),
|
|
120
79
|
},
|
|
121
|
-
],
|
|
122
|
-
structure: [
|
|
123
|
-
[
|
|
124
|
-
{
|
|
125
|
-
label: "Domains",
|
|
126
|
-
render: ({ model }) =>
|
|
127
|
-
h("span", model.domains.join(", ")),
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
label: "SSO Enabled",
|
|
131
|
-
key: "sso_enabled",
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
label: "Primary Contact",
|
|
135
|
-
render: ({ model }) =>
|
|
136
|
-
h("span", model.primary_contact?.name),
|
|
137
|
-
},
|
|
138
|
-
{
|
|
139
|
-
label: "Primary Contact Email",
|
|
140
|
-
render: ({ model }) =>
|
|
141
|
-
h("span", model.primary_contact?.email),
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
label: "Primary Contact Contacted",
|
|
145
|
-
render: ({ model }) =>
|
|
146
|
-
h("span", !!model.primary_contact_contacted),
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
label: "Total users",
|
|
150
|
-
key: "user_count",
|
|
151
|
-
},
|
|
152
|
-
],
|
|
153
80
|
{
|
|
81
|
+
key: "primary_contact_email",
|
|
82
|
+
label: "Primary Contact Email",
|
|
154
83
|
render: ({ model }) =>
|
|
155
|
-
h(
|
|
156
|
-
overrides: model.overrides_roles_and_permissions,
|
|
157
|
-
form: { roles: model.roles.map((d) => d.id) },
|
|
158
|
-
readonly: true,
|
|
159
|
-
}),
|
|
84
|
+
h("span", model.primary_contact?.email),
|
|
160
85
|
},
|
|
161
86
|
{
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
87
|
+
key: "primary_contact_contacted",
|
|
88
|
+
label: "Primary Contact Contacted",
|
|
89
|
+
render: ({ model }) =>
|
|
90
|
+
h("span", !!model.primary_contact_contacted),
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
label: "Total users",
|
|
94
|
+
key: "user_count",
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
index: {
|
|
99
|
+
layout: [
|
|
100
|
+
...defaultResource.index.layout,
|
|
101
|
+
() =>
|
|
102
|
+
h(VelRoleLegend, {
|
|
103
|
+
class: "mt-5",
|
|
104
|
+
}),
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
show: {
|
|
108
|
+
actions: [
|
|
109
|
+
({ model }) =>
|
|
110
|
+
model.primary_contact &&
|
|
111
|
+
h(
|
|
112
|
+
VelButton,
|
|
113
|
+
{
|
|
114
|
+
type: "primary",
|
|
115
|
+
async onClick() {
|
|
116
|
+
try {
|
|
117
|
+
const res = await axios.post(
|
|
118
|
+
`/api/companies/${model.id}/welcome`,
|
|
119
|
+
);
|
|
120
|
+
ElNotification({
|
|
121
|
+
title: "Success",
|
|
122
|
+
message: res.data.message,
|
|
123
|
+
type: "success",
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
model.primary_contact_contacted = true;
|
|
127
|
+
} catch (e) {
|
|
128
|
+
ElNotification({
|
|
129
|
+
title: "Warning",
|
|
130
|
+
message:
|
|
131
|
+
e.response?.data?.message ||
|
|
132
|
+
e.message,
|
|
133
|
+
type: "warning",
|
|
134
|
+
});
|
|
135
|
+
}
|
|
169
136
|
},
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
137
|
+
},
|
|
138
|
+
"Send welcome email",
|
|
139
|
+
),
|
|
140
|
+
],
|
|
141
|
+
layout: [
|
|
142
|
+
...defaultResource.show.layout,
|
|
143
|
+
({ model }) =>
|
|
144
|
+
h(VelFormRole, {
|
|
145
|
+
overrides: model.overrides_roles_and_permissions,
|
|
146
|
+
form: { roles: model.roles.map((d) => d.id) },
|
|
147
|
+
readonly: true,
|
|
148
|
+
}),
|
|
149
|
+
({ model, $store }) => {
|
|
150
|
+
const resource = meta(...userResource);
|
|
151
|
+
return h(VelTableSorter, {
|
|
152
|
+
key: "PIndex",
|
|
153
|
+
"json-data": {
|
|
154
|
+
...resource,
|
|
155
|
+
tableStructure: resource.table.structure,
|
|
156
|
+
},
|
|
157
|
+
defaults: `include=company&filter[company_id]=${model.id}`,
|
|
158
|
+
"fixed-height": false,
|
|
159
|
+
"display-edit-action":
|
|
160
|
+
$store.getters.can("write users"),
|
|
161
|
+
});
|
|
176
162
|
},
|
|
177
163
|
],
|
|
178
164
|
},
|
|
@@ -4,6 +4,7 @@ import Chips from "../../../../components/layout/Chips.vue";
|
|
|
4
4
|
import VelTableSorter from "../../../../components/layout/TableSorter.vue";
|
|
5
5
|
import VelRoleLegend from "../../../../components/layout/RoleLegend.vue";
|
|
6
6
|
import component from "./form.vue";
|
|
7
|
+
import { defaultResource } from "../../../resource/index.js";
|
|
7
8
|
|
|
8
9
|
import { ElMessageBox } from "element-plus";
|
|
9
10
|
import { ElNotification } from "element-plus";
|
|
@@ -27,6 +28,10 @@ export default [
|
|
|
27
28
|
searchable: {
|
|
28
29
|
value: "email",
|
|
29
30
|
},
|
|
31
|
+
permissions: {
|
|
32
|
+
create: ({ $store }) => $store.getters.can("write users"),
|
|
33
|
+
edit: ({ $store }) => $store.getters.can("write users"),
|
|
34
|
+
},
|
|
30
35
|
form: {
|
|
31
36
|
async submit({ model, form, $router, $store, method }) {
|
|
32
37
|
try {
|
|
@@ -111,17 +116,14 @@ export default [
|
|
|
111
116
|
table: {
|
|
112
117
|
structure: [
|
|
113
118
|
{
|
|
114
|
-
label: "Name",
|
|
115
119
|
key: "name",
|
|
116
120
|
sortable: true,
|
|
117
121
|
},
|
|
118
122
|
{
|
|
119
|
-
label: "Email",
|
|
120
123
|
key: "email",
|
|
121
|
-
sortable: true,
|
|
122
124
|
},
|
|
123
125
|
{
|
|
124
|
-
|
|
126
|
+
key: "company",
|
|
125
127
|
sortable: true,
|
|
126
128
|
render: (model) =>
|
|
127
129
|
h(resolveComponent("router-link"), {
|
|
@@ -134,7 +136,7 @@ export default [
|
|
|
134
136
|
}),
|
|
135
137
|
},
|
|
136
138
|
{
|
|
137
|
-
|
|
139
|
+
key: "role",
|
|
138
140
|
render: (row) =>
|
|
139
141
|
h(
|
|
140
142
|
!row.overrides_roles_and_permissions ||
|
|
@@ -156,58 +158,43 @@ export default [
|
|
|
156
158
|
},
|
|
157
159
|
],
|
|
158
160
|
},
|
|
159
|
-
|
|
161
|
+
description: {
|
|
160
162
|
structure: [
|
|
161
163
|
{
|
|
162
|
-
|
|
163
|
-
h(VelTableSorter, {
|
|
164
|
-
key: "PIndex",
|
|
165
|
-
"json-data": {
|
|
166
|
-
...resource,
|
|
167
|
-
tableStructure: resource.table.structure,
|
|
168
|
-
},
|
|
169
|
-
defaults: resource.defaults,
|
|
170
|
-
"fixed-height": false,
|
|
171
|
-
"display-edit-action":
|
|
172
|
-
$store.getters.can("write users"),
|
|
173
|
-
}),
|
|
164
|
+
key: "email",
|
|
174
165
|
},
|
|
175
166
|
{
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
167
|
+
key: "company",
|
|
168
|
+
render: ({ model }) =>
|
|
169
|
+
h(resolveComponent("router-link"), {
|
|
170
|
+
class: "underline",
|
|
171
|
+
to: {
|
|
172
|
+
name: "companies.show",
|
|
173
|
+
params: { id: model.company_id },
|
|
174
|
+
},
|
|
175
|
+
text: model.company.name,
|
|
179
176
|
}),
|
|
180
177
|
},
|
|
181
178
|
],
|
|
182
179
|
},
|
|
180
|
+
index: {
|
|
181
|
+
layout: [
|
|
182
|
+
...defaultResource.index.layout,
|
|
183
|
+
() =>
|
|
184
|
+
h(VelRoleLegend, {
|
|
185
|
+
class: "mt-5",
|
|
186
|
+
}),
|
|
187
|
+
],
|
|
188
|
+
},
|
|
183
189
|
show: {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
render: ({ model }) =>
|
|
193
|
-
h(resolveComponent("router-link"), {
|
|
194
|
-
class: "underline",
|
|
195
|
-
to: {
|
|
196
|
-
name: "companies.show",
|
|
197
|
-
params: { id: model.company_id },
|
|
198
|
-
},
|
|
199
|
-
text: model.company.name,
|
|
200
|
-
}),
|
|
201
|
-
},
|
|
202
|
-
],
|
|
203
|
-
{
|
|
204
|
-
render: ({ model }) =>
|
|
205
|
-
h(VelFormRole, {
|
|
206
|
-
overrides: model.overrides_roles_and_permissions,
|
|
207
|
-
form: { roles: model.roles.map((d) => d.id) },
|
|
208
|
-
readonly: true,
|
|
209
|
-
}),
|
|
210
|
-
},
|
|
190
|
+
layout: [
|
|
191
|
+
...defaultResource.show.layout,
|
|
192
|
+
({ model }) =>
|
|
193
|
+
h(VelFormRole, {
|
|
194
|
+
overrides: model.overrides_roles_and_permissions,
|
|
195
|
+
form: { roles: model.roles.map((d) => d.id) },
|
|
196
|
+
readonly: true,
|
|
197
|
+
}),
|
|
211
198
|
],
|
|
212
199
|
},
|
|
213
200
|
},
|
|
@@ -9,22 +9,18 @@
|
|
|
9
9
|
|
|
10
10
|
<div class="flex gap items-center justify-end">
|
|
11
11
|
<template
|
|
12
|
-
v-for="(
|
|
12
|
+
v-for="(render, index) in resource.index.actions"
|
|
13
13
|
:key="index"
|
|
14
14
|
>
|
|
15
|
-
<
|
|
16
|
-
<component :is="row.render(this)" />
|
|
17
|
-
</template>
|
|
15
|
+
<component :is="render(this)" />
|
|
18
16
|
</template>
|
|
19
17
|
</div>
|
|
20
18
|
|
|
21
19
|
<template
|
|
22
|
-
v-for="(
|
|
20
|
+
v-for="(render, index) in resource.index.layout"
|
|
23
21
|
:key="index"
|
|
24
22
|
>
|
|
25
|
-
<
|
|
26
|
-
<component :is="row.render(this)" />
|
|
27
|
-
</template>
|
|
23
|
+
<component :is="render(this)" />
|
|
28
24
|
</template>
|
|
29
25
|
</div>
|
|
30
26
|
</div>
|
|
@@ -14,15 +14,13 @@
|
|
|
14
14
|
:title="`${model.name} ${model.last_name ?? ''}`"
|
|
15
15
|
>
|
|
16
16
|
<template
|
|
17
|
-
v-for="(
|
|
17
|
+
v-for="(render, index) in resource.show.actions"
|
|
18
18
|
:key="index"
|
|
19
19
|
>
|
|
20
|
-
<
|
|
21
|
-
<component :is="row.render(this)" />
|
|
22
|
-
</template>
|
|
20
|
+
<component :is="render(this)" />
|
|
23
21
|
</template>
|
|
24
22
|
<VelButton
|
|
25
|
-
v-if="resource.permissions.edit"
|
|
23
|
+
v-if="resource.permissions.edit(this)"
|
|
26
24
|
tag="a"
|
|
27
25
|
type="primary"
|
|
28
26
|
@click="
|
|
@@ -45,37 +43,13 @@
|
|
|
45
43
|
<hr class="my-3 hr-muted" />
|
|
46
44
|
|
|
47
45
|
<template
|
|
48
|
-
v-for="(
|
|
46
|
+
v-for="(render, index) in resource.show.layout"
|
|
49
47
|
:key="index"
|
|
50
48
|
>
|
|
51
|
-
<
|
|
52
|
-
<component :is="row.render(this)" />
|
|
53
|
-
</template>
|
|
54
|
-
<el-descriptions v-else border column="1">
|
|
55
|
-
<el-descriptions-item
|
|
56
|
-
v-for="(item, i) in row"
|
|
57
|
-
:key="i"
|
|
58
|
-
:label-width="'20%'"
|
|
59
|
-
>
|
|
60
|
-
<template #label>
|
|
61
|
-
{{
|
|
62
|
-
item.label ||
|
|
63
|
-
item.key[0].toUpperCase() +
|
|
64
|
-
item.key.slice(1)
|
|
65
|
-
}}
|
|
66
|
-
</template>
|
|
67
|
-
|
|
68
|
-
<template v-if="item.render">
|
|
69
|
-
<component :is="item.render(this)" />
|
|
70
|
-
</template>
|
|
71
|
-
<template v-else>
|
|
72
|
-
{{ model[item.key] }}
|
|
73
|
-
</template>
|
|
74
|
-
</el-descriptions-item>
|
|
75
|
-
</el-descriptions>
|
|
49
|
+
<component :is="render(this)" />
|
|
76
50
|
|
|
77
51
|
<hr
|
|
78
|
-
v-if="index < resource.show.
|
|
52
|
+
v-if="index < resource.show.layout.length - 1"
|
|
79
53
|
class="my-3 hr-muted"
|
|
80
54
|
/>
|
|
81
55
|
</template>
|
|
@@ -93,7 +67,6 @@
|
|
|
93
67
|
import axios from "axios";
|
|
94
68
|
import VelSpinner from "../../../components/form/Spinner.vue";
|
|
95
69
|
import VelButton from "../../../components/basic/Button.vue";
|
|
96
|
-
import { ElDescriptions, ElDescriptionsItem } from "element-plus";
|
|
97
70
|
|
|
98
71
|
export default {
|
|
99
72
|
components: {
|
|
@@ -103,8 +76,6 @@ export default {
|
|
|
103
76
|
.default,
|
|
104
77
|
VelSpinner,
|
|
105
78
|
VelButton,
|
|
106
|
-
ElDescriptions,
|
|
107
|
-
ElDescriptionsItem,
|
|
108
79
|
},
|
|
109
80
|
|
|
110
81
|
props: {
|
|
@@ -5,8 +5,11 @@ import { merge } from "lodash";
|
|
|
5
5
|
import { h } from "vue";
|
|
6
6
|
|
|
7
7
|
import VTableSorter from "../../components/layout/TableSorter.vue";
|
|
8
|
+
import { ElDescriptions, ElDescriptionsItem } from "element-plus";
|
|
8
9
|
|
|
9
|
-
export
|
|
10
|
+
export const defaultResource = meta();
|
|
11
|
+
|
|
12
|
+
export function meta(name = "default", properties = {}) {
|
|
10
13
|
const singular = properties.singular || name.slice(0, -1);
|
|
11
14
|
|
|
12
15
|
return merge(
|
|
@@ -19,8 +22,8 @@ export function meta(name, properties = {}) {
|
|
|
19
22
|
pageLink: name,
|
|
20
23
|
api: `/api/${name}`,
|
|
21
24
|
permissions: {
|
|
22
|
-
create: true,
|
|
23
|
-
edit: true,
|
|
25
|
+
create: () => true,
|
|
26
|
+
edit: () => true,
|
|
24
27
|
},
|
|
25
28
|
searchable: {
|
|
26
29
|
value: "name",
|
|
@@ -29,32 +32,76 @@ export function meta(name, properties = {}) {
|
|
|
29
32
|
form: {
|
|
30
33
|
component: null,
|
|
31
34
|
fields: () => ({}),
|
|
35
|
+
structure: [],
|
|
32
36
|
},
|
|
33
37
|
table: {
|
|
34
|
-
structure: [
|
|
38
|
+
structure: [
|
|
39
|
+
{
|
|
40
|
+
key: "id",
|
|
41
|
+
},
|
|
42
|
+
],
|
|
35
43
|
},
|
|
36
|
-
|
|
37
|
-
actions: [],
|
|
44
|
+
description: {
|
|
38
45
|
structure: [
|
|
39
46
|
{
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
key: "id",
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
index: {
|
|
52
|
+
actions: [],
|
|
53
|
+
layout: [
|
|
54
|
+
(props) => {
|
|
55
|
+
const { resource } = props;
|
|
56
|
+
|
|
57
|
+
return h(VTableSorter, {
|
|
58
|
+
key: "PIndex",
|
|
59
|
+
"json-data": {
|
|
60
|
+
...resource,
|
|
61
|
+
tableStructure: resource.table.structure,
|
|
62
|
+
},
|
|
63
|
+
defaults: resource.defaults,
|
|
64
|
+
"fixed-height": false,
|
|
65
|
+
"display-edit-action":
|
|
66
|
+
resource.permissions.create(props),
|
|
67
|
+
});
|
|
52
68
|
},
|
|
53
69
|
],
|
|
54
70
|
},
|
|
55
71
|
show: {
|
|
56
72
|
actions: [],
|
|
57
|
-
|
|
73
|
+
layout: [
|
|
74
|
+
(props) => {
|
|
75
|
+
const { resource, model } = props;
|
|
76
|
+
|
|
77
|
+
return h(
|
|
78
|
+
ElDescriptions,
|
|
79
|
+
{
|
|
80
|
+
border: true,
|
|
81
|
+
column: 1,
|
|
82
|
+
},
|
|
83
|
+
resource.description.structure.map((item, index) =>
|
|
84
|
+
h(
|
|
85
|
+
ElDescriptionsItem,
|
|
86
|
+
{
|
|
87
|
+
key: index,
|
|
88
|
+
labelWidth: "20%",
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
label: () =>
|
|
92
|
+
item.label ||
|
|
93
|
+
item.key[0].toUpperCase() +
|
|
94
|
+
item.key.slice(1),
|
|
95
|
+
default: () =>
|
|
96
|
+
item.render
|
|
97
|
+
? h(item.render(props))
|
|
98
|
+
: model?.[item.key] || "",
|
|
99
|
+
},
|
|
100
|
+
),
|
|
101
|
+
),
|
|
102
|
+
);
|
|
103
|
+
},
|
|
104
|
+
],
|
|
58
105
|
},
|
|
59
106
|
defaults: "",
|
|
60
107
|
icon: `icon-${singular}`,
|