@fishawack/lab-velocity 2.0.0-beta.23 → 2.0.0-beta.25
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/_Build/vue/components/layout/Table.vue +7 -2
- package/_Build/vue/components/layout/TableSorter.vue +31 -20
- package/_Build/vue/modules/AuthModule/routes/PCompanies/form.vue +3 -1
- package/_Build/vue/modules/AuthModule/routes/PCompanies/resource.js +46 -16
- package/_Build/vue/modules/AuthModule/routes/PUsers/resource.js +21 -10
- package/_Build/vue/modules/resource/Children/create.vue +4 -10
- package/_Build/vue/modules/resource/Children/edit.vue +6 -24
- package/_Build/vue/modules/resource/Children/index.vue +4 -15
- package/_Build/vue/modules/resource/Children/show.vue +50 -45
- package/_Build/vue/modules/resource/index.js +198 -26
- package/_Build/vue/modules/resource/parent.vue +36 -14
- package/index.js +2 -0
- package/package.json +1 -1
|
@@ -23,7 +23,12 @@
|
|
|
23
23
|
<!-- Support a custom render function -->
|
|
24
24
|
<template v-if="item.render" #default="scope">
|
|
25
25
|
<component
|
|
26
|
-
:is="
|
|
26
|
+
:is="
|
|
27
|
+
item.render({
|
|
28
|
+
model: scope.row,
|
|
29
|
+
$emit,
|
|
30
|
+
})
|
|
31
|
+
"
|
|
27
32
|
/>
|
|
28
33
|
</template>
|
|
29
34
|
</el-table-column>
|
|
@@ -118,7 +123,7 @@ export default {
|
|
|
118
123
|
default: true,
|
|
119
124
|
},
|
|
120
125
|
},
|
|
121
|
-
emits: ["sort"],
|
|
126
|
+
emits: ["sort", "reload"],
|
|
122
127
|
|
|
123
128
|
methods: {
|
|
124
129
|
handleSort(data) {
|
|
@@ -67,19 +67,20 @@
|
|
|
67
67
|
:target-action="
|
|
68
68
|
(item) => ({
|
|
69
69
|
name: `${jsonData.slug}.show`,
|
|
70
|
-
params: {
|
|
70
|
+
params: { [idKey]: item.id },
|
|
71
71
|
})
|
|
72
72
|
"
|
|
73
73
|
:edit-action="
|
|
74
74
|
(item) => ({
|
|
75
75
|
name: `${jsonData.slug}.edit`,
|
|
76
|
-
params: {
|
|
76
|
+
params: { [idKey]: item.id },
|
|
77
77
|
})
|
|
78
78
|
"
|
|
79
79
|
:display-actions="displayActions"
|
|
80
80
|
:display-show-action="displayShowAction"
|
|
81
81
|
:display-edit-action="displayEditAction"
|
|
82
82
|
@sort="handleSort"
|
|
83
|
+
@reload="reload"
|
|
83
84
|
>
|
|
84
85
|
<slot name="table-content"></slot>
|
|
85
86
|
</VelTable>
|
|
@@ -128,10 +129,10 @@ export default {
|
|
|
128
129
|
default: true,
|
|
129
130
|
type: Boolean,
|
|
130
131
|
},
|
|
131
|
-
|
|
132
|
+
apiParams: {
|
|
132
133
|
required: false,
|
|
133
|
-
type:
|
|
134
|
-
default:
|
|
134
|
+
type: Object,
|
|
135
|
+
default: () => ({}),
|
|
135
136
|
},
|
|
136
137
|
displayActions: {
|
|
137
138
|
type: Boolean,
|
|
@@ -149,13 +150,17 @@ export default {
|
|
|
149
150
|
type: Boolean,
|
|
150
151
|
default: true,
|
|
151
152
|
},
|
|
153
|
+
idKey: {
|
|
154
|
+
type: String,
|
|
155
|
+
default: "id",
|
|
156
|
+
},
|
|
152
157
|
},
|
|
153
158
|
|
|
154
159
|
data() {
|
|
155
160
|
return {
|
|
156
161
|
search: null,
|
|
157
162
|
sort: "-id",
|
|
158
|
-
query:
|
|
163
|
+
query: {},
|
|
159
164
|
table_data: [],
|
|
160
165
|
table_meta: null,
|
|
161
166
|
};
|
|
@@ -172,13 +177,21 @@ export default {
|
|
|
172
177
|
},
|
|
173
178
|
|
|
174
179
|
methods: {
|
|
180
|
+
reload() {
|
|
181
|
+
this.fetchData({ page: this.table_meta.current_page }).then(
|
|
182
|
+
(data) => {
|
|
183
|
+
this.table_data = data.data;
|
|
184
|
+
this.table_meta = data.meta;
|
|
185
|
+
},
|
|
186
|
+
);
|
|
187
|
+
},
|
|
175
188
|
handleSearch(data) {
|
|
176
189
|
if (data === null || data.length < 3) {
|
|
177
|
-
this.query =
|
|
190
|
+
this.query = {};
|
|
178
191
|
} else {
|
|
179
|
-
this.query =
|
|
180
|
-
`filter[${this.$refs.search.$el.dataset.key}]
|
|
181
|
-
|
|
192
|
+
this.query = {
|
|
193
|
+
[`filter[${this.$refs.search.$el.dataset.key}]`]: data,
|
|
194
|
+
};
|
|
182
195
|
}
|
|
183
196
|
this.fetchData({}).then((data) => {
|
|
184
197
|
this.table_data = data.data;
|
|
@@ -199,17 +212,15 @@ export default {
|
|
|
199
212
|
},
|
|
200
213
|
|
|
201
214
|
fetchData: function ({ page = "1" }) {
|
|
202
|
-
const mergedQuery = [
|
|
203
|
-
...this.query,
|
|
204
|
-
this.defaults,
|
|
205
|
-
`sort=${this.sort}`,
|
|
206
|
-
`page=${page}`,
|
|
207
|
-
];
|
|
208
|
-
|
|
209
|
-
const query = mergedQuery.join("&");
|
|
210
|
-
|
|
211
215
|
return axios
|
|
212
|
-
.get(`${this.$props.jsonData.api}
|
|
216
|
+
.get(`${this.$props.jsonData.api}`, {
|
|
217
|
+
params: {
|
|
218
|
+
sort: this.sort,
|
|
219
|
+
page: page,
|
|
220
|
+
...this.query,
|
|
221
|
+
...this.apiParams,
|
|
222
|
+
},
|
|
223
|
+
})
|
|
213
224
|
.then((res) => res.data)
|
|
214
225
|
.catch(console.log);
|
|
215
226
|
},
|
|
@@ -179,7 +179,9 @@ export default {
|
|
|
179
179
|
|
|
180
180
|
mounted() {
|
|
181
181
|
axios
|
|
182
|
-
.getAll(
|
|
182
|
+
.getAll(
|
|
183
|
+
`/api/users?filter[company_id]=${this.$route.params.companiesId}`,
|
|
184
|
+
)
|
|
183
185
|
.then((res) => {
|
|
184
186
|
this.users = res.data.data;
|
|
185
187
|
});
|
|
@@ -9,13 +9,17 @@ import userResource from "../PUsers/resource.js";
|
|
|
9
9
|
import { defaultResource, meta } from "../../../resource/index.js";
|
|
10
10
|
|
|
11
11
|
import { ElNotification } from "element-plus";
|
|
12
|
-
import { h } from "vue";
|
|
12
|
+
import { h, resolveComponent } from "vue";
|
|
13
13
|
import axios from "axios";
|
|
14
14
|
|
|
15
15
|
export default [
|
|
16
16
|
"companies",
|
|
17
17
|
{
|
|
18
|
-
|
|
18
|
+
api: {
|
|
19
|
+
params: {
|
|
20
|
+
show: { include: "primary_contact" },
|
|
21
|
+
},
|
|
22
|
+
},
|
|
19
23
|
permissions: {
|
|
20
24
|
create: ({ $store }) => $store.getters.can("write companies"),
|
|
21
25
|
edit: ({ $store }) => $store.getters.can("write companies"),
|
|
@@ -148,21 +152,47 @@ export default [
|
|
|
148
152
|
form: { roles: model.roles.map((d) => d.id) },
|
|
149
153
|
readonly: true,
|
|
150
154
|
}),
|
|
151
|
-
({ model, $store }) => {
|
|
155
|
+
({ model, $store, $router, ...rest }) => {
|
|
152
156
|
const resource = meta(...userResource);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
157
|
+
|
|
158
|
+
resource.api.params.index["filter[company_id]"] = model.id;
|
|
159
|
+
|
|
160
|
+
const props = {
|
|
161
|
+
model,
|
|
162
|
+
$store,
|
|
163
|
+
$router,
|
|
164
|
+
...rest,
|
|
165
|
+
resource,
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
return h("div", [
|
|
169
|
+
h("div", { class: "flex justify-end items-end" }, [
|
|
170
|
+
resource.permissions.create(props) &&
|
|
171
|
+
h(
|
|
172
|
+
VelButton,
|
|
173
|
+
{
|
|
174
|
+
tag: "a",
|
|
175
|
+
type: "primary",
|
|
176
|
+
size: "large",
|
|
177
|
+
onClick: () => {
|
|
178
|
+
$router.push({
|
|
179
|
+
name: `${resource.slug}.create`,
|
|
180
|
+
});
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
() => [
|
|
184
|
+
h(resolveComponent("GIcon"), {
|
|
185
|
+
class: "fill-0 mr-0.5 icon--0.5",
|
|
186
|
+
name: "icon-plus",
|
|
187
|
+
embed: true,
|
|
188
|
+
artboard: true,
|
|
189
|
+
}),
|
|
190
|
+
`Create ${resource.singular}`,
|
|
191
|
+
],
|
|
192
|
+
),
|
|
193
|
+
]),
|
|
194
|
+
h(VelTableSorter, resource.index.structure(props)),
|
|
195
|
+
]);
|
|
166
196
|
},
|
|
167
197
|
],
|
|
168
198
|
},
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import VelFormRole from "../../../../components/layout/FormRole.vue";
|
|
2
2
|
import Chip from "../../../../components/layout/Chip.vue";
|
|
3
3
|
import Chips from "../../../../components/layout/Chips.vue";
|
|
4
|
-
import VelTableSorter from "../../../../components/layout/TableSorter.vue";
|
|
5
4
|
import VelRoleLegend from "../../../../components/layout/RoleLegend.vue";
|
|
6
5
|
import component from "./form.vue";
|
|
7
|
-
import
|
|
6
|
+
import companyResource from "../PCompanies/resource.js";
|
|
7
|
+
import { defaultResource, meta } from "../../../resource/index.js";
|
|
8
8
|
|
|
9
9
|
import { ElMessageBox } from "element-plus";
|
|
10
10
|
import { ElNotification } from "element-plus";
|
|
@@ -24,7 +24,12 @@ function generatePassword(
|
|
|
24
24
|
export default [
|
|
25
25
|
"users",
|
|
26
26
|
{
|
|
27
|
-
|
|
27
|
+
api: {
|
|
28
|
+
params: {
|
|
29
|
+
index: { include: "company" },
|
|
30
|
+
show: { include: "company" },
|
|
31
|
+
},
|
|
32
|
+
},
|
|
28
33
|
searchable: {
|
|
29
34
|
value: "email",
|
|
30
35
|
},
|
|
@@ -34,7 +39,7 @@ export default [
|
|
|
34
39
|
delete: ({ $store }) => $store.getters.can("delete companies"),
|
|
35
40
|
},
|
|
36
41
|
form: {
|
|
37
|
-
async submit({ model, form, $router, $store, method }) {
|
|
42
|
+
async submit({ model, form, $router, $store, method, resource }) {
|
|
38
43
|
try {
|
|
39
44
|
if (method === "post") {
|
|
40
45
|
if (form.set_password) {
|
|
@@ -57,7 +62,7 @@ export default [
|
|
|
57
62
|
.then(() => {
|
|
58
63
|
$router.replace({
|
|
59
64
|
name: "users.show",
|
|
60
|
-
params: { id: res.data.id },
|
|
65
|
+
params: { [resource.id]: res.data.id },
|
|
61
66
|
});
|
|
62
67
|
})
|
|
63
68
|
.catch(() => {});
|
|
@@ -71,7 +76,7 @@ export default [
|
|
|
71
76
|
|
|
72
77
|
$router.replace({
|
|
73
78
|
name: "users.show",
|
|
74
|
-
params: { id: res.data.id },
|
|
79
|
+
params: { [resource.id]: res.data.id },
|
|
75
80
|
});
|
|
76
81
|
}
|
|
77
82
|
} else {
|
|
@@ -84,7 +89,7 @@ export default [
|
|
|
84
89
|
|
|
85
90
|
$router.replace({
|
|
86
91
|
name: "users.show",
|
|
87
|
-
params: { id: res.data.id },
|
|
92
|
+
params: { [resource.id]: res.data.id },
|
|
88
93
|
});
|
|
89
94
|
}
|
|
90
95
|
} catch (e) {
|
|
@@ -131,9 +136,12 @@ export default [
|
|
|
131
136
|
class: "underline",
|
|
132
137
|
to: {
|
|
133
138
|
name: "companies.show",
|
|
134
|
-
params: {
|
|
139
|
+
params: {
|
|
140
|
+
[meta(...companyResource).id]:
|
|
141
|
+
model.company_id,
|
|
142
|
+
},
|
|
135
143
|
},
|
|
136
|
-
text: model.company
|
|
144
|
+
text: model.company?.name,
|
|
137
145
|
}),
|
|
138
146
|
},
|
|
139
147
|
{
|
|
@@ -171,7 +179,10 @@ export default [
|
|
|
171
179
|
class: "underline",
|
|
172
180
|
to: {
|
|
173
181
|
name: "companies.show",
|
|
174
|
-
params: {
|
|
182
|
+
params: {
|
|
183
|
+
[meta(...companyResource).id]:
|
|
184
|
+
model.company_id,
|
|
185
|
+
},
|
|
175
186
|
},
|
|
176
187
|
text: model.company.name,
|
|
177
188
|
}),
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<VBreadcrumbs :items="breadcrumbs" class="mb-8" container-classes="m-0" />
|
|
3
|
-
|
|
4
2
|
<div class="container px-6 tablet:px-4 mobile:px-2 mb-8 ml-0 mr-0">
|
|
5
3
|
<div class="grid__1/1">
|
|
6
4
|
<div class="grid__1/1 mb-4">
|
|
@@ -25,16 +23,10 @@ import Form from "form-backend-validation";
|
|
|
25
23
|
|
|
26
24
|
export default {
|
|
27
25
|
components: {
|
|
28
|
-
VBreadcrumbs: require("../../../components/layout/Breadcrumbs.vue")
|
|
29
|
-
.default,
|
|
30
26
|
XForm: require("./partials/form.vue").default,
|
|
31
27
|
},
|
|
32
28
|
|
|
33
29
|
props: {
|
|
34
|
-
breadcrumbs: {
|
|
35
|
-
type: Array,
|
|
36
|
-
required: true,
|
|
37
|
-
},
|
|
38
30
|
resource: {
|
|
39
31
|
type: Object,
|
|
40
32
|
required: true,
|
|
@@ -60,11 +52,13 @@ export default {
|
|
|
60
52
|
await this.resource.form.submit(this);
|
|
61
53
|
} else {
|
|
62
54
|
try {
|
|
63
|
-
let res = await this.form.post(
|
|
55
|
+
let res = await this.form.post(
|
|
56
|
+
`${this.resource.api.endpoint(this)}`,
|
|
57
|
+
);
|
|
64
58
|
|
|
65
59
|
this.$router.replace({
|
|
66
60
|
name: `${this.resource.slug}.show`,
|
|
67
|
-
params: { id: res.data.id },
|
|
61
|
+
params: { [this.resource.id]: res.data.id },
|
|
68
62
|
});
|
|
69
63
|
} catch (e) {
|
|
70
64
|
console.log(e);
|
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<VBreadcrumbs
|
|
3
|
-
:items="addBreadcrumbs"
|
|
4
|
-
class="mb-8"
|
|
5
|
-
container-classes="m-0"
|
|
6
|
-
/>
|
|
7
|
-
|
|
8
2
|
<div class="container px-6 tablet:px-4 mobile:px-2 mb-8 ml-0 mr-0">
|
|
9
3
|
<div class="grid__1/1">
|
|
10
4
|
<div class="grid__1/1 mb-4">
|
|
@@ -30,16 +24,10 @@ import Form from "form-backend-validation";
|
|
|
30
24
|
|
|
31
25
|
export default {
|
|
32
26
|
components: {
|
|
33
|
-
VBreadcrumbs: require("../../../components/layout/Breadcrumbs.vue")
|
|
34
|
-
.default,
|
|
35
27
|
XForm: require("./partials/form.vue").default,
|
|
36
28
|
},
|
|
37
29
|
|
|
38
30
|
props: {
|
|
39
|
-
breadcrumbs: {
|
|
40
|
-
type: Array,
|
|
41
|
-
required: true,
|
|
42
|
-
},
|
|
43
31
|
resource: {
|
|
44
32
|
type: Object,
|
|
45
33
|
required: true,
|
|
@@ -50,7 +38,6 @@ export default {
|
|
|
50
38
|
return {
|
|
51
39
|
form: null,
|
|
52
40
|
model: null,
|
|
53
|
-
addBreadcrumbs: [...this.$props.breadcrumbs],
|
|
54
41
|
method: "patch",
|
|
55
42
|
};
|
|
56
43
|
},
|
|
@@ -64,7 +51,10 @@ export default {
|
|
|
64
51
|
async mounted() {
|
|
65
52
|
window.axios
|
|
66
53
|
.get(
|
|
67
|
-
`${this.resource.api}/${this.$route.params
|
|
54
|
+
`${this.resource.api.endpoint(this)}/${this.$route.params[this.resource.id]}`,
|
|
55
|
+
{
|
|
56
|
+
params: this.resource.api.params.show,
|
|
57
|
+
},
|
|
68
58
|
)
|
|
69
59
|
.then((res) => {
|
|
70
60
|
this.model = res.data.data;
|
|
@@ -75,14 +65,6 @@ export default {
|
|
|
75
65
|
this.form[key] = value;
|
|
76
66
|
},
|
|
77
67
|
);
|
|
78
|
-
|
|
79
|
-
this.addBreadcrumbs.push({
|
|
80
|
-
href: {
|
|
81
|
-
name: `${this.resource.slug}.show`,
|
|
82
|
-
param: this.model.id,
|
|
83
|
-
},
|
|
84
|
-
text: this.model.name,
|
|
85
|
-
});
|
|
86
68
|
});
|
|
87
69
|
},
|
|
88
70
|
|
|
@@ -93,12 +75,12 @@ export default {
|
|
|
93
75
|
} else {
|
|
94
76
|
try {
|
|
95
77
|
let res = await this.form.patch(
|
|
96
|
-
`${this.resource.api}/${this.model.id}`,
|
|
78
|
+
`${this.resource.api.endpoint(this)}/${this.model.id}`,
|
|
97
79
|
);
|
|
98
80
|
|
|
99
81
|
this.$router.replace({
|
|
100
82
|
name: `${this.resource.slug}.show`,
|
|
101
|
-
params: { id: res.data.id },
|
|
83
|
+
params: { [this.resource.id]: res.data.id },
|
|
102
84
|
});
|
|
103
85
|
} catch (e) {
|
|
104
86
|
console.log(e);
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<VBreadcrumbs :items="breadcrumbs" />
|
|
3
|
-
|
|
4
2
|
<div class="container px-6 tablet:px-4 mobile:px-2 mb-8 ml-0 mr-0">
|
|
5
3
|
<div class="grid__1/1">
|
|
6
|
-
<h2 class="h1 pb-4">
|
|
7
|
-
{{ breadcrumbs[breadcrumbs.length - 1].text }}
|
|
8
|
-
</h2>
|
|
9
|
-
|
|
10
4
|
<div class="flex gap items-center justify-end">
|
|
11
5
|
<template
|
|
12
6
|
v-for="(render, index) in resource.index.actions"
|
|
@@ -28,20 +22,15 @@
|
|
|
28
22
|
|
|
29
23
|
<script>
|
|
30
24
|
export default {
|
|
31
|
-
components: {
|
|
32
|
-
VBreadcrumbs: require("../../../components/layout/Breadcrumbs.vue")
|
|
33
|
-
.default,
|
|
34
|
-
},
|
|
35
|
-
|
|
36
25
|
props: {
|
|
37
|
-
breadcrumbs: {
|
|
38
|
-
type: Array,
|
|
39
|
-
required: true,
|
|
40
|
-
},
|
|
41
26
|
resource: {
|
|
42
27
|
type: Object,
|
|
43
28
|
required: true,
|
|
44
29
|
},
|
|
30
|
+
depth: {
|
|
31
|
+
type: Number,
|
|
32
|
+
required: true,
|
|
33
|
+
},
|
|
45
34
|
},
|
|
46
35
|
};
|
|
47
36
|
</script>
|
|
@@ -1,47 +1,44 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
/>
|
|
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>
|
|
7
6
|
|
|
8
|
-
<
|
|
9
|
-
<div class="
|
|
10
|
-
<
|
|
11
|
-
<
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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="(render, index) in resource.show.actions"
|
|
18
|
+
:key="index"
|
|
19
|
+
>
|
|
20
|
+
<component :is="render(this)" />
|
|
21
|
+
</template>
|
|
22
|
+
</VelPageHeader>
|
|
23
|
+
<hr class="my-3 hr-muted" />
|
|
16
24
|
<template
|
|
17
|
-
v-for="(render, index) in resource.show.
|
|
25
|
+
v-for="(render, index) in resource.show.layout"
|
|
18
26
|
:key="index"
|
|
19
27
|
>
|
|
20
28
|
<component :is="render(this)" />
|
|
29
|
+
<hr
|
|
30
|
+
v-if="index < resource.show.layout.length - 1"
|
|
31
|
+
class="my-3 hr-muted"
|
|
32
|
+
/>
|
|
21
33
|
</template>
|
|
22
|
-
</
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
<template
|
|
27
|
-
v-for="(render, index) in resource.show.layout"
|
|
28
|
-
:key="index"
|
|
29
|
-
>
|
|
30
|
-
<component :is="render(this)" />
|
|
31
|
-
|
|
32
|
-
<hr
|
|
33
|
-
v-if="index < resource.show.layout.length - 1"
|
|
34
|
-
class="my-3 hr-muted"
|
|
35
|
-
/>
|
|
36
|
-
</template>
|
|
34
|
+
</div>
|
|
35
|
+
</template>
|
|
36
|
+
<div v-else class="absolute transform-center text-center">
|
|
37
|
+
<VelSpinner />
|
|
37
38
|
</div>
|
|
38
|
-
</template>
|
|
39
|
-
|
|
40
|
-
<div v-else class="absolute transform-center text-center">
|
|
41
|
-
<VelSpinner />
|
|
42
39
|
</div>
|
|
43
40
|
</div>
|
|
44
|
-
</
|
|
41
|
+
</template>
|
|
45
42
|
</template>
|
|
46
43
|
|
|
47
44
|
<script>
|
|
@@ -51,8 +48,6 @@ import VelButton from "../../../components/basic/Button.vue";
|
|
|
51
48
|
|
|
52
49
|
export default {
|
|
53
50
|
components: {
|
|
54
|
-
VBreadcrumbs: require("../../../components/layout/Breadcrumbs.vue")
|
|
55
|
-
.default,
|
|
56
51
|
VelPageHeader: require("../../../components/layout/PageHeader.vue")
|
|
57
52
|
.default,
|
|
58
53
|
VelSpinner,
|
|
@@ -68,29 +63,39 @@ export default {
|
|
|
68
63
|
type: Object,
|
|
69
64
|
required: true,
|
|
70
65
|
},
|
|
66
|
+
depth: {
|
|
67
|
+
type: Number,
|
|
68
|
+
required: true,
|
|
69
|
+
},
|
|
71
70
|
},
|
|
72
71
|
|
|
73
72
|
data() {
|
|
74
73
|
return {
|
|
75
74
|
model: null,
|
|
76
|
-
addBreadcrumbs: [...this.$props.breadcrumbs],
|
|
77
75
|
};
|
|
78
76
|
},
|
|
79
77
|
|
|
78
|
+
computed: {
|
|
79
|
+
// This boolean helps determine if we are the final depth of route being rendered
|
|
80
|
+
deepestRoute() {
|
|
81
|
+
return this.depth === this.$route.matched.length;
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
|
|
80
85
|
mounted() {
|
|
86
|
+
if (!this.deepestRoute) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
81
90
|
axios
|
|
82
91
|
.get(
|
|
83
|
-
`${this.resource.api}/${this.$route.params
|
|
92
|
+
`${this.resource.api.endpoint(this)}/${this.$route.params[`${this.resource.slug}Id`]}`,
|
|
93
|
+
{
|
|
94
|
+
params: this.resource.api.params.show,
|
|
95
|
+
},
|
|
84
96
|
)
|
|
85
97
|
.then((res) => {
|
|
86
98
|
this.model = res.data.data;
|
|
87
|
-
this.addBreadcrumbs.push({
|
|
88
|
-
href: {
|
|
89
|
-
name: `${this.resource.slug}.show`,
|
|
90
|
-
param: this.model.id,
|
|
91
|
-
},
|
|
92
|
-
text: this.model.name,
|
|
93
|
-
});
|
|
94
99
|
});
|
|
95
100
|
},
|
|
96
101
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import { merge, kebabCase } from "lodash";
|
|
3
|
+
import { merge, kebabCase, cloneDeepWith } from "lodash";
|
|
4
4
|
import axios from "axios";
|
|
5
5
|
import { h, resolveComponent } from "vue";
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import VelTableSorter from "../../components/layout/TableSorter.vue";
|
|
8
8
|
import { ElDescriptions, ElDescriptionsItem, ElPopconfirm } from "element-plus";
|
|
9
9
|
import VelButton from "../../components/basic/Button.vue";
|
|
10
10
|
|
|
@@ -12,6 +12,7 @@ export const defaultResource = meta();
|
|
|
12
12
|
|
|
13
13
|
export function meta(name = "default", properties = {}) {
|
|
14
14
|
const singular = properties.singular || name.slice(0, -1);
|
|
15
|
+
const slug = properties.slug || kebabCase(name);
|
|
15
16
|
|
|
16
17
|
return merge(
|
|
17
18
|
{
|
|
@@ -20,8 +21,16 @@ export function meta(name = "default", properties = {}) {
|
|
|
20
21
|
singular,
|
|
21
22
|
label: singular,
|
|
22
23
|
multiLabel: name,
|
|
23
|
-
slug
|
|
24
|
-
|
|
24
|
+
slug,
|
|
25
|
+
id: properties.id || `${slug}Id`,
|
|
26
|
+
icon: `icon-${singular}`,
|
|
27
|
+
api: {
|
|
28
|
+
endpoint: () => `/api/${properties.slug || kebabCase(name)}`,
|
|
29
|
+
params: {
|
|
30
|
+
index: {},
|
|
31
|
+
show: {},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
25
34
|
permissions: {
|
|
26
35
|
create: () => true,
|
|
27
36
|
edit: () => true,
|
|
@@ -37,6 +46,82 @@ export function meta(name = "default", properties = {}) {
|
|
|
37
46
|
structure: [],
|
|
38
47
|
},
|
|
39
48
|
table: {
|
|
49
|
+
actions: [
|
|
50
|
+
({ $router, resource, model }) =>
|
|
51
|
+
h(
|
|
52
|
+
VelButton,
|
|
53
|
+
{
|
|
54
|
+
tag: "a",
|
|
55
|
+
size: "small",
|
|
56
|
+
type: "primary",
|
|
57
|
+
onClick: () => {
|
|
58
|
+
$router.push({
|
|
59
|
+
name: `${resource.slug}.show`,
|
|
60
|
+
params: {
|
|
61
|
+
[resource.id]: model.id,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
() => "View",
|
|
67
|
+
),
|
|
68
|
+
(props) => {
|
|
69
|
+
const { $router, resource, model } = props;
|
|
70
|
+
|
|
71
|
+
if (resource.permissions.edit(props)) {
|
|
72
|
+
return h(
|
|
73
|
+
VelButton,
|
|
74
|
+
{
|
|
75
|
+
tag: "a",
|
|
76
|
+
size: "small",
|
|
77
|
+
onClick: () => {
|
|
78
|
+
$router.push({
|
|
79
|
+
name: `${resource.slug}.edit`,
|
|
80
|
+
params: {
|
|
81
|
+
[resource.id]: model.id,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
() => "Edit",
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
(props) => {
|
|
91
|
+
const { resource, model, $emit } = props;
|
|
92
|
+
|
|
93
|
+
if (resource.permissions.delete(props)) {
|
|
94
|
+
return h(
|
|
95
|
+
ElPopconfirm,
|
|
96
|
+
{
|
|
97
|
+
title: `Are you sure you want to delete this ${resource.singular}?`,
|
|
98
|
+
confirmButtonText: "Delete",
|
|
99
|
+
cancelButtonText: "Cancel",
|
|
100
|
+
confirmButtonType: "danger",
|
|
101
|
+
onConfirm: async () => {
|
|
102
|
+
await axios.delete(
|
|
103
|
+
`${resource.api.endpoint(props)}/${model.id}`,
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
$emit("reload");
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
reference: () =>
|
|
111
|
+
h(
|
|
112
|
+
VelButton,
|
|
113
|
+
{
|
|
114
|
+
tag: "a",
|
|
115
|
+
type: "danger",
|
|
116
|
+
size: "small",
|
|
117
|
+
},
|
|
118
|
+
() => `Delete`,
|
|
119
|
+
),
|
|
120
|
+
},
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
],
|
|
40
125
|
structure: [
|
|
41
126
|
{
|
|
42
127
|
key: "id",
|
|
@@ -51,24 +136,88 @@ export function meta(name = "default", properties = {}) {
|
|
|
51
136
|
],
|
|
52
137
|
},
|
|
53
138
|
index: {
|
|
54
|
-
|
|
139
|
+
structure: (props) => {
|
|
140
|
+
const { resource, $router, $route, $store, ...rest } =
|
|
141
|
+
props;
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
key: "PIndex",
|
|
145
|
+
"json-data": {
|
|
146
|
+
...resource,
|
|
147
|
+
tableStructure: resource.table.structure.concat(
|
|
148
|
+
resource.table.actions.length
|
|
149
|
+
? [
|
|
150
|
+
{
|
|
151
|
+
key: "actions",
|
|
152
|
+
render: ({ model, $emit }) =>
|
|
153
|
+
h(
|
|
154
|
+
"div",
|
|
155
|
+
{
|
|
156
|
+
class: "flex gap-2",
|
|
157
|
+
},
|
|
158
|
+
resource.table.actions.map(
|
|
159
|
+
(d) =>
|
|
160
|
+
d({
|
|
161
|
+
resource,
|
|
162
|
+
$router,
|
|
163
|
+
$route,
|
|
164
|
+
$store,
|
|
165
|
+
...rest,
|
|
166
|
+
model,
|
|
167
|
+
$emit,
|
|
168
|
+
}),
|
|
169
|
+
),
|
|
170
|
+
),
|
|
171
|
+
},
|
|
172
|
+
]
|
|
173
|
+
: [],
|
|
174
|
+
),
|
|
175
|
+
api: resource.api.endpoint(props),
|
|
176
|
+
},
|
|
177
|
+
apiParams: resource.api.params.index,
|
|
178
|
+
idKey: resource.id,
|
|
179
|
+
"fixed-height": false,
|
|
180
|
+
displayActions: false,
|
|
181
|
+
};
|
|
182
|
+
},
|
|
183
|
+
actions: [
|
|
184
|
+
(props) => {
|
|
185
|
+
const { resource, $router } = props;
|
|
186
|
+
|
|
187
|
+
if (resource.permissions.create(props)) {
|
|
188
|
+
return h(
|
|
189
|
+
VelButton,
|
|
190
|
+
{
|
|
191
|
+
tag: "a",
|
|
192
|
+
type: "primary",
|
|
193
|
+
size: "large",
|
|
194
|
+
onClick: () => {
|
|
195
|
+
$router.push({
|
|
196
|
+
name: `${resource.slug}.create`,
|
|
197
|
+
});
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
() => [
|
|
201
|
+
h(resolveComponent("GIcon"), {
|
|
202
|
+
class: "fill-0 mr-0.5 icon--0.5",
|
|
203
|
+
name: "icon-plus",
|
|
204
|
+
embed: true,
|
|
205
|
+
artboard: true,
|
|
206
|
+
}),
|
|
207
|
+
`Create ${resource.singular}`,
|
|
208
|
+
],
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
],
|
|
55
213
|
layout: [
|
|
56
214
|
(props) => {
|
|
57
215
|
const { resource } = props;
|
|
58
216
|
|
|
59
|
-
return h(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
tableStructure: resource.table.structure,
|
|
64
|
-
},
|
|
65
|
-
defaults: resource.defaults,
|
|
66
|
-
"fixed-height": false,
|
|
67
|
-
"display-create-action":
|
|
68
|
-
resource.permissions.create(props),
|
|
69
|
-
"display-edit-action":
|
|
70
|
-
resource.permissions.edit(props),
|
|
71
|
-
});
|
|
217
|
+
return h(
|
|
218
|
+
VelTableSorter,
|
|
219
|
+
resource.index.structure(props),
|
|
220
|
+
);
|
|
72
221
|
},
|
|
73
222
|
],
|
|
74
223
|
},
|
|
@@ -86,7 +235,9 @@ export function meta(name = "default", properties = {}) {
|
|
|
86
235
|
onClick: () => {
|
|
87
236
|
$router.push({
|
|
88
237
|
name: `${resource.slug}.edit`,
|
|
89
|
-
params: {
|
|
238
|
+
params: {
|
|
239
|
+
[resource.id]: model.id,
|
|
240
|
+
},
|
|
90
241
|
});
|
|
91
242
|
},
|
|
92
243
|
},
|
|
@@ -115,7 +266,7 @@ export function meta(name = "default", properties = {}) {
|
|
|
115
266
|
confirmButtonType: "danger",
|
|
116
267
|
onConfirm: async () => {
|
|
117
268
|
await axios.delete(
|
|
118
|
-
`${resource.api}/${model.id}`,
|
|
269
|
+
`${resource.api.endpoint(props)}/${model.id}`,
|
|
119
270
|
);
|
|
120
271
|
|
|
121
272
|
$router.push({
|
|
@@ -182,8 +333,6 @@ export function meta(name = "default", properties = {}) {
|
|
|
182
333
|
},
|
|
183
334
|
],
|
|
184
335
|
},
|
|
185
|
-
defaults: "",
|
|
186
|
-
icon: `icon-${singular}`,
|
|
187
336
|
},
|
|
188
337
|
properties,
|
|
189
338
|
);
|
|
@@ -228,18 +377,25 @@ export function columns(columns = []) {
|
|
|
228
377
|
}
|
|
229
378
|
|
|
230
379
|
// Export resource
|
|
231
|
-
export function routes(
|
|
380
|
+
export function routes(
|
|
381
|
+
node,
|
|
382
|
+
name,
|
|
383
|
+
properties = {},
|
|
384
|
+
children = [],
|
|
385
|
+
isChild = false,
|
|
386
|
+
) {
|
|
232
387
|
const resource = meta(name, properties);
|
|
233
388
|
|
|
234
389
|
return [
|
|
235
390
|
{
|
|
236
|
-
path:
|
|
391
|
+
path: `${isChild ? "" : "/"}${resource.slug}`,
|
|
237
392
|
component: node ? "" : require("../resource/parent.vue").default,
|
|
238
393
|
name,
|
|
239
394
|
meta: {
|
|
240
395
|
resource,
|
|
241
396
|
title: resource.title,
|
|
242
397
|
icon: resource.icon,
|
|
398
|
+
breadcrumb: () => resource.title,
|
|
243
399
|
},
|
|
244
400
|
children: [
|
|
245
401
|
{
|
|
@@ -257,18 +413,34 @@ export function routes(node, name, properties = {}) {
|
|
|
257
413
|
name: `${resource.slug}.create`,
|
|
258
414
|
},
|
|
259
415
|
{
|
|
260
|
-
path:
|
|
416
|
+
path: `:${resource.id}`,
|
|
261
417
|
component: node
|
|
262
418
|
? ""
|
|
263
419
|
: require("../resource/Children/show.vue").default,
|
|
264
420
|
name: `${resource.slug}.show`,
|
|
421
|
+
// Remove leading / for nested routes or they'll resolve to the root of the site
|
|
422
|
+
children: cloneDeepWith(children, (value, key) => {
|
|
423
|
+
if (
|
|
424
|
+
key === "path" &&
|
|
425
|
+
typeof value === "string" &&
|
|
426
|
+
value.startsWith("/")
|
|
427
|
+
) {
|
|
428
|
+
return value.slice(1);
|
|
429
|
+
}
|
|
430
|
+
}),
|
|
431
|
+
meta: {
|
|
432
|
+
breadcrumb: ({ $route }) => $route.params[resource.id],
|
|
433
|
+
},
|
|
265
434
|
},
|
|
266
435
|
{
|
|
267
|
-
path:
|
|
436
|
+
path: `:${resource.id}/edit`,
|
|
268
437
|
component: node
|
|
269
438
|
? ""
|
|
270
439
|
: require("../resource/Children/edit.vue").default,
|
|
271
440
|
name: `${resource.slug}.edit`,
|
|
441
|
+
meta: {
|
|
442
|
+
breadcrumb: ({ $route }) => $route.params[resource.id],
|
|
443
|
+
},
|
|
272
444
|
},
|
|
273
445
|
],
|
|
274
446
|
},
|
|
@@ -1,19 +1,39 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
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>
|
|
3
11
|
|
|
4
12
|
<router-view
|
|
5
13
|
:key="$route.path"
|
|
14
|
+
v-slot="{ Component }"
|
|
6
15
|
:breadcrumbs="breadcrumbs"
|
|
7
16
|
:resource="resource"
|
|
8
|
-
|
|
17
|
+
>
|
|
18
|
+
<component :is="Component" :depth="depth + 1" />
|
|
19
|
+
</router-view>
|
|
9
20
|
</template>
|
|
10
21
|
|
|
11
22
|
<script>
|
|
12
|
-
import
|
|
23
|
+
import VelPageTitle from "../../components/layout/pageTitle.vue";
|
|
24
|
+
import VelBreadcrumbs from "../../components/layout/Breadcrumbs.vue";
|
|
13
25
|
|
|
14
26
|
export default {
|
|
15
27
|
components: {
|
|
16
|
-
|
|
28
|
+
VelPageTitle,
|
|
29
|
+
VelBreadcrumbs,
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
props: {
|
|
33
|
+
depth: {
|
|
34
|
+
type: Number,
|
|
35
|
+
default: 1,
|
|
36
|
+
},
|
|
17
37
|
},
|
|
18
38
|
|
|
19
39
|
computed: {
|
|
@@ -23,18 +43,20 @@ export default {
|
|
|
23
43
|
breadcrumbs() {
|
|
24
44
|
return [
|
|
25
45
|
{
|
|
26
|
-
href: {
|
|
27
|
-
name: "index",
|
|
28
|
-
},
|
|
29
46
|
text: "Home",
|
|
47
|
+
href: { name: "index" },
|
|
30
48
|
},
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
+
);
|
|
38
60
|
},
|
|
39
61
|
},
|
|
40
62
|
};
|
package/index.js
CHANGED
|
@@ -26,6 +26,8 @@ export { default as Wysiwyg2 } from "./_Build/vue/components/form/Wysiwyg2.vue";
|
|
|
26
26
|
export { default as Upload } from "./_Build/vue/components/form/Upload.vue";
|
|
27
27
|
export { default as InputNumber } from "./_Build/vue/components/form/InputNumber.vue";
|
|
28
28
|
|
|
29
|
+
export { default as RoleLegend } from "./_Build/vue/components/layout/RoleLegend.vue";
|
|
30
|
+
export { default as TableSorter } from "./_Build/vue/components/layout/TableSorter.vue";
|
|
29
31
|
export { default as Chip } from "./_Build/vue/components/layout/Chip.vue";
|
|
30
32
|
export { default as Chips } from "./_Build/vue/components/layout/Chips.vue";
|
|
31
33
|
export { default as SideBar } from "./_Build/vue/components/layout/SideBar.vue";
|