@fishawack/lab-velocity 2.0.0-beta.26 → 2.0.0-beta.28
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 +2 -0
- package/_Build/vue/modules/AuthModule/js/router.js +8 -0
- package/_Build/vue/modules/AuthModule/routes/PCompanies/form.vue +15 -0
- package/_Build/vue/modules/AuthModule/routes/PCompanies/resource.js +91 -32
- package/_Build/vue/modules/AuthModule/routes/PTeams/resource.js +258 -0
- package/_Build/vue/modules/AuthModule/routes/PUsers/resource.js +6 -4
- package/_Build/vue/modules/resource/Children/edit.vue +1 -1
- package/_Build/vue/modules/resource/Children/show.vue +46 -13
- package/_Build/vue/modules/resource/index.js +40 -32
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -187,6 +187,8 @@ There are two different set of sass imports for the admin and the frontend route
|
|
|
187
187
|
@import "@fishawack/lab-velocity/components/sidebar";
|
|
188
188
|
@import "@fishawack/lab-velocity/components/menu";
|
|
189
189
|
@import "@fishawack/lab-velocity/components/layout";
|
|
190
|
+
@import "element-plus/theme-chalk/el-tabs";
|
|
191
|
+
@import "element-plus/theme-chalk/el-tab-pane";
|
|
190
192
|
```
|
|
191
193
|
|
|
192
194
|
Lastly for the admin layout & navigation import & apply the Layout component to the app.vue within your project.
|
|
@@ -9,12 +9,14 @@ import { routes as resourceRoutes } from "../../resource/index.js";
|
|
|
9
9
|
|
|
10
10
|
import userResource from "../routes/PUsers/resource.js";
|
|
11
11
|
import companyResource from "../routes/PCompanies/resource.js";
|
|
12
|
+
import teamResource from "../routes/PTeams/resource.js";
|
|
12
13
|
|
|
13
14
|
// Admin routes export - minimal auth flow (headless login only)
|
|
14
15
|
export function adminRoutes(node, overrides = {}) {
|
|
15
16
|
const {
|
|
16
17
|
userResource: overrideUserResource = {},
|
|
17
18
|
companyResource: overrideCompanyResource = {},
|
|
19
|
+
teamResource: overrideTeamResource = {},
|
|
18
20
|
} = overrides;
|
|
19
21
|
|
|
20
22
|
return [
|
|
@@ -58,6 +60,12 @@ export function adminRoutes(node, overrides = {}) {
|
|
|
58
60
|
...resourceRoutes(
|
|
59
61
|
node,
|
|
60
62
|
...merge(companyResource, [undefined, overrideCompanyResource]),
|
|
63
|
+
[
|
|
64
|
+
...resourceRoutes(
|
|
65
|
+
node,
|
|
66
|
+
...merge(teamResource, [undefined, overrideTeamResource]),
|
|
67
|
+
),
|
|
68
|
+
],
|
|
61
69
|
),
|
|
62
70
|
];
|
|
63
71
|
}
|
|
@@ -75,6 +75,21 @@
|
|
|
75
75
|
</VelButton>
|
|
76
76
|
</div>
|
|
77
77
|
|
|
78
|
+
<VelBasic
|
|
79
|
+
v-model="form.seats"
|
|
80
|
+
name="seats"
|
|
81
|
+
:error="form.errors"
|
|
82
|
+
type="number"
|
|
83
|
+
placeholder="Company seats"
|
|
84
|
+
label="Company seats"
|
|
85
|
+
min="0"
|
|
86
|
+
class="mb-0 mt-2"
|
|
87
|
+
>
|
|
88
|
+
<template #label>
|
|
89
|
+
Company seats <sup class="color-status-red-100">*</sup>
|
|
90
|
+
</template>
|
|
91
|
+
</VelBasic>
|
|
92
|
+
|
|
78
93
|
<hr class="my-5 hr-muted" />
|
|
79
94
|
|
|
80
95
|
<template v-if="$store.getters.can('edit roles')">
|
|
@@ -6,6 +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 teamResource from "../PTeams/resource.js";
|
|
9
10
|
import { defaultResource, meta } from "../../../resource/index.js";
|
|
10
11
|
|
|
11
12
|
import { ElNotification } from "element-plus";
|
|
@@ -17,7 +18,7 @@ export default [
|
|
|
17
18
|
{
|
|
18
19
|
api: {
|
|
19
20
|
params: {
|
|
20
|
-
show: { include: "primary_contact" },
|
|
21
|
+
show: () => ({ include: "primary_contact" }),
|
|
21
22
|
},
|
|
22
23
|
},
|
|
23
24
|
permissions: {
|
|
@@ -33,6 +34,7 @@ export default [
|
|
|
33
34
|
name: model?.name || null,
|
|
34
35
|
primary_contact: model?.primary_contact?.id || null,
|
|
35
36
|
domains: model?.domains || [],
|
|
37
|
+
seats: model?.seats != null ? model.seats : null,
|
|
36
38
|
roles: model?.roles.map((val) => val.id) || [],
|
|
37
39
|
sso_client_id: model?.sso_client_id || undefined,
|
|
38
40
|
sso_tenant: model?.sso_tenant || undefined,
|
|
@@ -95,9 +97,13 @@ export default [
|
|
|
95
97
|
h("span", !!model.primary_contact_contacted),
|
|
96
98
|
},
|
|
97
99
|
{
|
|
98
|
-
label: "Total
|
|
100
|
+
label: "Total Users",
|
|
99
101
|
key: "user_count",
|
|
100
102
|
},
|
|
103
|
+
{
|
|
104
|
+
label: "Available Seats",
|
|
105
|
+
key: "seats",
|
|
106
|
+
},
|
|
101
107
|
],
|
|
102
108
|
},
|
|
103
109
|
index: {
|
|
@@ -146,53 +152,106 @@ export default [
|
|
|
146
152
|
],
|
|
147
153
|
layout: [
|
|
148
154
|
...defaultResource.show.layout,
|
|
149
|
-
({ model }) =>
|
|
150
|
-
|
|
155
|
+
({ model }) => ({
|
|
156
|
+
label: "Access control",
|
|
157
|
+
component: h(VelFormRole, {
|
|
151
158
|
overrides: model.overrides_roles_and_permissions,
|
|
152
159
|
form: { roles: model.roles.map((d) => d.id) },
|
|
153
160
|
readonly: true,
|
|
154
161
|
}),
|
|
155
|
-
|
|
162
|
+
}),
|
|
163
|
+
({ model, $store, $router, $route, ...rest }) => {
|
|
164
|
+
const resource = meta(...teamResource);
|
|
165
|
+
|
|
166
|
+
const props = {
|
|
167
|
+
model,
|
|
168
|
+
$store,
|
|
169
|
+
$router,
|
|
170
|
+
$route,
|
|
171
|
+
...rest,
|
|
172
|
+
resource,
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
label: "Teams",
|
|
177
|
+
component: h("div", [
|
|
178
|
+
h("div", { class: "flex justify-end items-end" }, [
|
|
179
|
+
resource.permissions.create(props) &&
|
|
180
|
+
h(
|
|
181
|
+
VelButton,
|
|
182
|
+
{
|
|
183
|
+
tag: "a",
|
|
184
|
+
type: "primary",
|
|
185
|
+
size: "large",
|
|
186
|
+
onClick: () => {
|
|
187
|
+
$router.push({
|
|
188
|
+
name: `${resource.slug}.create`,
|
|
189
|
+
});
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
() => [
|
|
193
|
+
h(resolveComponent("GIcon"), {
|
|
194
|
+
class: "fill-0 mr-0.5 icon--0.5",
|
|
195
|
+
name: "icon-plus",
|
|
196
|
+
embed: true,
|
|
197
|
+
artboard: true,
|
|
198
|
+
}),
|
|
199
|
+
`Create ${resource.singular}`,
|
|
200
|
+
],
|
|
201
|
+
),
|
|
202
|
+
]),
|
|
203
|
+
h(VelTableSorter, resource.index.structure(props)),
|
|
204
|
+
]),
|
|
205
|
+
};
|
|
206
|
+
},
|
|
207
|
+
({ model, $store, $router, $route, ...rest }) => {
|
|
156
208
|
const resource = meta(...userResource);
|
|
157
209
|
|
|
158
|
-
resource.api.params.index
|
|
210
|
+
resource.api.params.index = ({ $route }) => ({
|
|
211
|
+
include: "company",
|
|
212
|
+
"filter[company_id]": $route.params.companiesId,
|
|
213
|
+
});
|
|
159
214
|
|
|
160
215
|
const props = {
|
|
161
216
|
model,
|
|
162
217
|
$store,
|
|
163
218
|
$router,
|
|
219
|
+
$route,
|
|
164
220
|
...rest,
|
|
165
221
|
resource,
|
|
166
222
|
};
|
|
167
223
|
|
|
168
|
-
return
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
224
|
+
return {
|
|
225
|
+
label: "Users",
|
|
226
|
+
component: h("div", [
|
|
227
|
+
h("div", { class: "flex justify-end items-end" }, [
|
|
228
|
+
resource.permissions.create(props) &&
|
|
229
|
+
h(
|
|
230
|
+
VelButton,
|
|
231
|
+
{
|
|
232
|
+
tag: "a",
|
|
233
|
+
type: "primary",
|
|
234
|
+
size: "large",
|
|
235
|
+
onClick: () => {
|
|
236
|
+
$router.push({
|
|
237
|
+
name: `${resource.slug}.create`,
|
|
238
|
+
});
|
|
239
|
+
},
|
|
181
240
|
},
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
241
|
+
() => [
|
|
242
|
+
h(resolveComponent("GIcon"), {
|
|
243
|
+
class: "fill-0 mr-0.5 icon--0.5",
|
|
244
|
+
name: "icon-plus",
|
|
245
|
+
embed: true,
|
|
246
|
+
artboard: true,
|
|
247
|
+
}),
|
|
248
|
+
`Create ${resource.singular}`,
|
|
249
|
+
],
|
|
250
|
+
),
|
|
251
|
+
]),
|
|
252
|
+
h(VelTableSorter, resource.index.structure(props)),
|
|
193
253
|
]),
|
|
194
|
-
|
|
195
|
-
]);
|
|
254
|
+
};
|
|
196
255
|
},
|
|
197
256
|
],
|
|
198
257
|
},
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { merge } from "lodash";
|
|
2
|
+
import { h, resolveComponent } from "vue";
|
|
3
|
+
import axios from "axios";
|
|
4
|
+
import { throttle } from "lodash";
|
|
5
|
+
|
|
6
|
+
import { columns } from "../../../resource/index.js";
|
|
7
|
+
import userResource from "../PUsers/resource.js";
|
|
8
|
+
import { defaultResource, meta } from "../../../resource/index.js";
|
|
9
|
+
|
|
10
|
+
import VelTableSorter from "../../../../components/layout/TableSorter.vue";
|
|
11
|
+
import VelButton from "../../../../components/basic/Button.vue";
|
|
12
|
+
import VelCheckbox from "../../../../components/form/Checkbox.vue";
|
|
13
|
+
|
|
14
|
+
export default [
|
|
15
|
+
"teams",
|
|
16
|
+
{
|
|
17
|
+
icon: `icon-account-circle`,
|
|
18
|
+
api: {
|
|
19
|
+
params: {
|
|
20
|
+
index: ({ $route }) => ({
|
|
21
|
+
include: "company",
|
|
22
|
+
"filter[company_id]": $route.params.companiesId,
|
|
23
|
+
}),
|
|
24
|
+
show: () => ({
|
|
25
|
+
include: "company",
|
|
26
|
+
}),
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
permissions: {
|
|
30
|
+
create: ({ $store }) => $store.getters.can("write teams"),
|
|
31
|
+
edit: ({ $store }) => $store.getters.can("write teams"),
|
|
32
|
+
delete: ({ $store }) => $store.getters.can("delete teams"),
|
|
33
|
+
},
|
|
34
|
+
...merge(
|
|
35
|
+
columns([
|
|
36
|
+
{
|
|
37
|
+
key: "name",
|
|
38
|
+
sortable: true,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
key: "description",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
key: "company_id",
|
|
45
|
+
label: "Company",
|
|
46
|
+
class: "hidden",
|
|
47
|
+
render: {
|
|
48
|
+
read: ({ model }) => h("span", model.company.name),
|
|
49
|
+
},
|
|
50
|
+
initial: ({ $route, model }) =>
|
|
51
|
+
model?.company_id || $route.params.companiesId || null,
|
|
52
|
+
},
|
|
53
|
+
]),
|
|
54
|
+
{
|
|
55
|
+
show: {
|
|
56
|
+
layout: [
|
|
57
|
+
...defaultResource.show.layout,
|
|
58
|
+
(props) => {
|
|
59
|
+
const { model, $store, $router, $route, ...rest } =
|
|
60
|
+
props;
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
label: "Members",
|
|
64
|
+
component: h({
|
|
65
|
+
data: () => ({
|
|
66
|
+
scoped: true,
|
|
67
|
+
}),
|
|
68
|
+
mounted() {
|
|
69
|
+
this.emitter.on("reload-teams", () => {
|
|
70
|
+
this.reload();
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
beforeUnmount() {
|
|
74
|
+
this.emitter.off("reload-teams");
|
|
75
|
+
},
|
|
76
|
+
methods: {
|
|
77
|
+
reload: throttle(function () {
|
|
78
|
+
this.$refs.members.reload();
|
|
79
|
+
this.$refs.users.reload();
|
|
80
|
+
}, 1000),
|
|
81
|
+
},
|
|
82
|
+
render() {
|
|
83
|
+
return h("div", [
|
|
84
|
+
h("h3", "Members"),
|
|
85
|
+
(() => {
|
|
86
|
+
const resource = meta(
|
|
87
|
+
...userResource,
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
resource.api.params.index = ({
|
|
91
|
+
$route,
|
|
92
|
+
}) => ({
|
|
93
|
+
include: "company",
|
|
94
|
+
"filter[teams.id]":
|
|
95
|
+
$route.params.teamsId,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
resource.table.actions = [
|
|
99
|
+
({ model }) =>
|
|
100
|
+
h({
|
|
101
|
+
data: () => ({
|
|
102
|
+
loading: false,
|
|
103
|
+
}),
|
|
104
|
+
render() {
|
|
105
|
+
return h(
|
|
106
|
+
VelButton,
|
|
107
|
+
{
|
|
108
|
+
tag: "a",
|
|
109
|
+
size: "small",
|
|
110
|
+
type: "warning",
|
|
111
|
+
loading:
|
|
112
|
+
this
|
|
113
|
+
.loading,
|
|
114
|
+
onClick:
|
|
115
|
+
async () => {
|
|
116
|
+
this.loading = true;
|
|
117
|
+
|
|
118
|
+
await axios.delete(
|
|
119
|
+
`api/teams/${$route.params.teamsId}/users/${model.id}`,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
this.emitter.emit(
|
|
123
|
+
"reload-teams",
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
this.loading = false;
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
"Remove",
|
|
130
|
+
);
|
|
131
|
+
},
|
|
132
|
+
}),
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
const props = {
|
|
136
|
+
model,
|
|
137
|
+
$store,
|
|
138
|
+
$router,
|
|
139
|
+
$route,
|
|
140
|
+
...rest,
|
|
141
|
+
resource,
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
return h(
|
|
145
|
+
VelTableSorter,
|
|
146
|
+
merge(
|
|
147
|
+
resource.index.structure(
|
|
148
|
+
props,
|
|
149
|
+
),
|
|
150
|
+
{
|
|
151
|
+
ref: "members",
|
|
152
|
+
},
|
|
153
|
+
),
|
|
154
|
+
);
|
|
155
|
+
})(),
|
|
156
|
+
h("h3", "Users"),
|
|
157
|
+
h(VelCheckbox, {
|
|
158
|
+
label: "Only show users from this company",
|
|
159
|
+
class: "mt-2",
|
|
160
|
+
modelValue: this.scoped,
|
|
161
|
+
"onUpdate:modelValue": (
|
|
162
|
+
value,
|
|
163
|
+
) => {
|
|
164
|
+
this.scoped = value;
|
|
165
|
+
this.$nextTick(() => {
|
|
166
|
+
this.$refs.users.reload();
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
}),
|
|
170
|
+
(() => {
|
|
171
|
+
const resource = meta(
|
|
172
|
+
...userResource,
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
resource.api.params.index = ({
|
|
176
|
+
$route,
|
|
177
|
+
}) => ({
|
|
178
|
+
include: "company",
|
|
179
|
+
"filter[company_id]": this
|
|
180
|
+
.scoped
|
|
181
|
+
? $route.params
|
|
182
|
+
.companiesId
|
|
183
|
+
: null,
|
|
184
|
+
"filter[teams.id]": `!${$route.params.teamsId}`,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
resource.table.actions = [
|
|
188
|
+
({ model }) =>
|
|
189
|
+
h({
|
|
190
|
+
data: () => ({
|
|
191
|
+
loading: false,
|
|
192
|
+
}),
|
|
193
|
+
render() {
|
|
194
|
+
return h(
|
|
195
|
+
VelButton,
|
|
196
|
+
{
|
|
197
|
+
tag: "a",
|
|
198
|
+
size: "small",
|
|
199
|
+
type: "primary",
|
|
200
|
+
loading:
|
|
201
|
+
this
|
|
202
|
+
.loading,
|
|
203
|
+
onClick:
|
|
204
|
+
async () => {
|
|
205
|
+
this.loading = true;
|
|
206
|
+
|
|
207
|
+
await axios.post(
|
|
208
|
+
`api/teams/${$route.params.teamsId}/users`,
|
|
209
|
+
{
|
|
210
|
+
id: model.id,
|
|
211
|
+
},
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
this.emitter.emit(
|
|
215
|
+
"reload-teams",
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
this.loading = false;
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
"Add",
|
|
222
|
+
);
|
|
223
|
+
},
|
|
224
|
+
}),
|
|
225
|
+
];
|
|
226
|
+
|
|
227
|
+
const props = {
|
|
228
|
+
model,
|
|
229
|
+
$store,
|
|
230
|
+
$router,
|
|
231
|
+
$route,
|
|
232
|
+
...rest,
|
|
233
|
+
resource,
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
return h(
|
|
237
|
+
VelTableSorter,
|
|
238
|
+
merge(
|
|
239
|
+
resource.index.structure(
|
|
240
|
+
props,
|
|
241
|
+
),
|
|
242
|
+
{
|
|
243
|
+
ref: "users",
|
|
244
|
+
},
|
|
245
|
+
),
|
|
246
|
+
);
|
|
247
|
+
})(),
|
|
248
|
+
]);
|
|
249
|
+
},
|
|
250
|
+
}),
|
|
251
|
+
};
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
),
|
|
257
|
+
},
|
|
258
|
+
];
|
|
@@ -26,8 +26,8 @@ export default [
|
|
|
26
26
|
{
|
|
27
27
|
api: {
|
|
28
28
|
params: {
|
|
29
|
-
index: { include: "company" },
|
|
30
|
-
show: { include: "company" },
|
|
29
|
+
index: () => ({ include: "company" }),
|
|
30
|
+
show: () => ({ include: "company" }),
|
|
31
31
|
},
|
|
32
32
|
},
|
|
33
33
|
searchable: {
|
|
@@ -201,12 +201,14 @@ export default [
|
|
|
201
201
|
show: {
|
|
202
202
|
layout: [
|
|
203
203
|
...defaultResource.show.layout,
|
|
204
|
-
({ model }) =>
|
|
205
|
-
|
|
204
|
+
({ model }) => ({
|
|
205
|
+
label: "Access control",
|
|
206
|
+
component: h(VelFormRole, {
|
|
206
207
|
overrides: model.overrides_roles_and_permissions,
|
|
207
208
|
form: { roles: model.roles.map((d) => d.id) },
|
|
208
209
|
readonly: true,
|
|
209
210
|
}),
|
|
211
|
+
}),
|
|
210
212
|
],
|
|
211
213
|
},
|
|
212
214
|
},
|
|
@@ -14,23 +14,42 @@
|
|
|
14
14
|
:title="`${model.name ?? model.id} ${model.last_name ?? ''}`"
|
|
15
15
|
>
|
|
16
16
|
<template
|
|
17
|
-
v-for="(
|
|
17
|
+
v-for="(rendered, index) in renderedActions"
|
|
18
18
|
:key="index"
|
|
19
19
|
>
|
|
20
|
-
<component :is="
|
|
20
|
+
<component :is="rendered" />
|
|
21
21
|
</template>
|
|
22
22
|
</VelPageHeader>
|
|
23
|
+
|
|
23
24
|
<hr class="my-3 hr-muted" />
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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--0.5 mr-0.5"
|
|
37
|
+
:name="rendered.icon"
|
|
38
|
+
asis
|
|
39
|
+
embed
|
|
40
|
+
/>
|
|
41
|
+
<span>{{
|
|
42
|
+
rendered.label ||
|
|
43
|
+
`Tab ${index + 1}`
|
|
44
|
+
}}</span>
|
|
45
|
+
</span>
|
|
46
|
+
</template>
|
|
47
|
+
<component
|
|
48
|
+
:is="rendered.component || rendered"
|
|
49
|
+
/>
|
|
50
|
+
</el-tab-pane>
|
|
51
|
+
</template>
|
|
52
|
+
</el-tabs>
|
|
34
53
|
</div>
|
|
35
54
|
</template>
|
|
36
55
|
<div v-else class="absolute transform-center text-center">
|
|
@@ -45,6 +64,7 @@
|
|
|
45
64
|
import axios from "axios";
|
|
46
65
|
import VelSpinner from "../../../components/form/Spinner.vue";
|
|
47
66
|
import VelButton from "../../../components/basic/Button.vue";
|
|
67
|
+
import { ElTabs, ElTabPane } from "element-plus";
|
|
48
68
|
|
|
49
69
|
export default {
|
|
50
70
|
components: {
|
|
@@ -52,6 +72,8 @@ export default {
|
|
|
52
72
|
.default,
|
|
53
73
|
VelSpinner,
|
|
54
74
|
VelButton,
|
|
75
|
+
ElTabs,
|
|
76
|
+
ElTabPane,
|
|
55
77
|
},
|
|
56
78
|
|
|
57
79
|
props: {
|
|
@@ -72,6 +94,7 @@ export default {
|
|
|
72
94
|
data() {
|
|
73
95
|
return {
|
|
74
96
|
model: null,
|
|
97
|
+
active: 0,
|
|
75
98
|
};
|
|
76
99
|
},
|
|
77
100
|
|
|
@@ -80,6 +103,16 @@ export default {
|
|
|
80
103
|
deepestRoute() {
|
|
81
104
|
return this.depth === this.$route.matched.length;
|
|
82
105
|
},
|
|
106
|
+
|
|
107
|
+
// Compute rendered layout once
|
|
108
|
+
renderedLayout() {
|
|
109
|
+
return this.resource.show.layout.map((render) => render(this));
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
// Compute rendered actions once
|
|
113
|
+
renderedActions() {
|
|
114
|
+
return this.resource.show.actions.map((render) => render(this));
|
|
115
|
+
},
|
|
83
116
|
},
|
|
84
117
|
|
|
85
118
|
mounted() {
|
|
@@ -91,7 +124,7 @@ export default {
|
|
|
91
124
|
.get(
|
|
92
125
|
`${this.resource.api.endpoint(this)}/${this.$route.params[`${this.resource.slug}Id`]}`,
|
|
93
126
|
{
|
|
94
|
-
params: this.resource.api.params.show,
|
|
127
|
+
params: this.resource.api.params.show(this),
|
|
95
128
|
},
|
|
96
129
|
)
|
|
97
130
|
.then((res) => {
|
|
@@ -35,8 +35,8 @@ export function meta(name = "default", properties = {}) {
|
|
|
35
35
|
api: {
|
|
36
36
|
endpoint: () => `/api/${properties.slug || kebabCase(name)}`,
|
|
37
37
|
params: {
|
|
38
|
-
index: {},
|
|
39
|
-
show: {},
|
|
38
|
+
index: () => ({}),
|
|
39
|
+
show: () => ({}),
|
|
40
40
|
},
|
|
41
41
|
},
|
|
42
42
|
permissions: {
|
|
@@ -197,7 +197,7 @@ export function meta(name = "default", properties = {}) {
|
|
|
197
197
|
),
|
|
198
198
|
api: resource.api.endpoint(props),
|
|
199
199
|
},
|
|
200
|
-
apiParams: resource.api.params.index,
|
|
200
|
+
apiParams: resource.api.params.index(props),
|
|
201
201
|
idKey: resource.id,
|
|
202
202
|
"fixed-height": false,
|
|
203
203
|
displayActions: false,
|
|
@@ -324,35 +324,43 @@ export function meta(name = "default", properties = {}) {
|
|
|
324
324
|
(props) => {
|
|
325
325
|
const { resource, model } = props;
|
|
326
326
|
|
|
327
|
-
return
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
item.
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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
|
+
};
|
|
356
364
|
},
|
|
357
365
|
],
|
|
358
366
|
},
|