@hostlink/nuxt-light 0.0.63 → 0.0.64
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/dist/module.json +1 -1
- package/dist/runtime/components/l-app.vue +6 -7
- package/dist/runtime/components/l-file-manager-preview.vue +1 -0
- package/dist/runtime/components/l-file-manager.vue +84 -30
- package/dist/runtime/components/l-form.vue +11 -3
- package/dist/runtime/components/l-page.vue +4 -2
- package/dist/runtime/pages/Permission/add.vue +4 -3
- package/dist/runtime/pages/Permission/export.vue +65 -0
- package/dist/runtime/pages/Permission/index.vue +1 -1
- package/dist/runtime/pages/Role/index.vue +0 -17
- package/dist/runtime/pages/System/test.vue +6 -1
- package/dist/runtime/pages/User/profile.vue +2 -4
- package/dist/runtime/pages/User/setting/password.vue +3 -5
- package/dist/runtime/pages/User/setting.vue +6 -3
- package/dist/runtime/pages/User/update-password.vue +2 -6
- package/dist/runtime/pages/UserLog/index.vue +5 -2
- package/dist/runtime/routes.mjs +10 -0
- package/package.json +4 -3
package/dist/module.json
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { useRuntimeConfig } from 'nuxt/app'
|
|
2
|
+
import { useRuntimeConfig } from 'nuxt/app'
|
|
3
3
|
import { setApiUrl } from '@hostlink/light'
|
|
4
|
-
import {
|
|
4
|
+
import { useRoute } from 'vue-router'
|
|
5
|
+
import { q } from '../'
|
|
6
|
+
const route = useRoute()
|
|
5
7
|
const config = useRuntimeConfig();
|
|
6
8
|
setApiUrl(config?.public?.apiBase ?? '/api/');
|
|
7
9
|
|
|
@@ -12,11 +14,8 @@ const { app } = await q({ app: ['company', 'companyLogo', 'logged', 'twoFactorAu
|
|
|
12
14
|
<q-layout v-if="!app.logged">
|
|
13
15
|
<q-page-container class="bg-grey-2" style="color:#1f1f1f">
|
|
14
16
|
<q-page padding>
|
|
15
|
-
<l-login
|
|
16
|
-
|
|
17
|
-
:company-logo="app.companyLogo"
|
|
18
|
-
:twoFactorAuthentication="app.twoFactorAuthentication"
|
|
19
|
-
></l-login>
|
|
17
|
+
<l-login :company="app.company" :company-logo="app.companyLogo"
|
|
18
|
+
:twoFactorAuthentication="app.twoFactorAuthentication"></l-login>
|
|
20
19
|
</q-page>
|
|
21
20
|
</q-page-container>
|
|
22
21
|
</q-layout>
|
|
@@ -4,12 +4,18 @@ import { useI18n } from "vue-i18n";
|
|
|
4
4
|
import { ref, watch, computed } from 'vue';
|
|
5
5
|
import { useQuasar } from 'quasar';
|
|
6
6
|
import { q, m } from '../';
|
|
7
|
+
import {
|
|
8
|
+
fsListFolders, fsCreateFolder, fsDeleteFolder, fsDeleteFile, fsRenameFile, fsRenameFolder, fsReadFile,
|
|
9
|
+
granted
|
|
10
|
+
|
|
11
|
+
} from "@hostlink/light";
|
|
12
|
+
|
|
7
13
|
|
|
8
14
|
const i18n = useI18n();
|
|
9
15
|
const quasar = useQuasar();
|
|
10
16
|
const emit = defineEmits(["input", "close"]);
|
|
11
17
|
|
|
12
|
-
defineProps({
|
|
18
|
+
const props = defineProps({
|
|
13
19
|
closable: Boolean,
|
|
14
20
|
height: {
|
|
15
21
|
type: String,
|
|
@@ -23,6 +29,10 @@ defineProps({
|
|
|
23
29
|
default: false,
|
|
24
30
|
type: Boolean,
|
|
25
31
|
},
|
|
32
|
+
base: {
|
|
33
|
+
default: "/",
|
|
34
|
+
type: String,
|
|
35
|
+
},
|
|
26
36
|
});
|
|
27
37
|
|
|
28
38
|
const loading = ref(false);
|
|
@@ -88,10 +98,10 @@ function toggleLeftDrawer() {
|
|
|
88
98
|
leftDrawerOpen.value = !leftDrawerOpen.value;
|
|
89
99
|
}
|
|
90
100
|
|
|
91
|
-
const path = ref(
|
|
101
|
+
const path = ref(props.base);
|
|
92
102
|
|
|
93
103
|
const onLazyLoad = async ({ node, key, done, fail }) => {
|
|
94
|
-
const data = await
|
|
104
|
+
const data = await fsListFolders(node.path);
|
|
95
105
|
data.map((item) => {
|
|
96
106
|
item.lazy = true;
|
|
97
107
|
return item;
|
|
@@ -123,7 +133,7 @@ const loadItems = async () => {
|
|
|
123
133
|
|
|
124
134
|
let folders = [];
|
|
125
135
|
if (!label.value && !localSearch.value) {
|
|
126
|
-
folders = await
|
|
136
|
+
folders = await fsListFolders(path.value);
|
|
127
137
|
folders = folders.map((item) => {
|
|
128
138
|
item.type = "folder";
|
|
129
139
|
item.lazy = true;
|
|
@@ -134,8 +144,6 @@ const loadItems = async () => {
|
|
|
134
144
|
|
|
135
145
|
loading.value = false;
|
|
136
146
|
|
|
137
|
-
|
|
138
|
-
|
|
139
147
|
return {
|
|
140
148
|
files,
|
|
141
149
|
folders,
|
|
@@ -154,8 +162,6 @@ const label = ref(null);
|
|
|
154
162
|
const initItems = await loadItems();
|
|
155
163
|
const folders = ref(initItems.folders);
|
|
156
164
|
|
|
157
|
-
|
|
158
|
-
|
|
159
165
|
watch(label, () => {
|
|
160
166
|
loadItems();
|
|
161
167
|
})
|
|
@@ -163,13 +169,18 @@ watch(label, () => {
|
|
|
163
169
|
const selected = ref([]);
|
|
164
170
|
|
|
165
171
|
const breadcrumbs = computed(() => {
|
|
166
|
-
let breadcrumbs = [
|
|
172
|
+
let breadcrumbs = [
|
|
173
|
+
{
|
|
174
|
+
label: i18n.t("Storage"),
|
|
175
|
+
path: props.base,
|
|
176
|
+
},
|
|
177
|
+
];
|
|
167
178
|
|
|
168
179
|
if (path.value.toString() == "") {
|
|
169
|
-
return
|
|
180
|
+
return breadcrumbs;
|
|
170
181
|
}
|
|
171
182
|
|
|
172
|
-
let paths = path.value.split(
|
|
183
|
+
let paths = path.value.split(props.base);
|
|
173
184
|
|
|
174
185
|
let ps = [];
|
|
175
186
|
for (let p of paths) {
|
|
@@ -186,6 +197,11 @@ const breadcrumbs = computed(() => {
|
|
|
186
197
|
});
|
|
187
198
|
|
|
188
199
|
const onClickRow = (evt, row, index) => {
|
|
200
|
+
if (row.type == "folder") {
|
|
201
|
+
preview.value = null;
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
189
205
|
preview.value = row;
|
|
190
206
|
}
|
|
191
207
|
|
|
@@ -197,7 +213,6 @@ const onDblclickRow = (evt, row, index) => {
|
|
|
197
213
|
return;
|
|
198
214
|
}
|
|
199
215
|
if (row.type == "file") {
|
|
200
|
-
|
|
201
216
|
emit("input", row.path);
|
|
202
217
|
}
|
|
203
218
|
}
|
|
@@ -238,9 +253,9 @@ const onDeleteSelected = () => {
|
|
|
238
253
|
}).onOk(async () => {
|
|
239
254
|
for (let row of selected.value) {
|
|
240
255
|
if (row.type == "folder") {
|
|
241
|
-
await
|
|
256
|
+
await fsDeleteFolder(row.path)
|
|
242
257
|
} else {
|
|
243
|
-
await
|
|
258
|
+
await fsDeleteFile(row.path);
|
|
244
259
|
}
|
|
245
260
|
}
|
|
246
261
|
selected.value = [];
|
|
@@ -251,7 +266,6 @@ const onDeleteSelected = () => {
|
|
|
251
266
|
}
|
|
252
267
|
|
|
253
268
|
const onNewFolder = () => {
|
|
254
|
-
|
|
255
269
|
quasar.dialog({
|
|
256
270
|
title: "New Folder",
|
|
257
271
|
prompt: {
|
|
@@ -259,8 +273,7 @@ const onNewFolder = () => {
|
|
|
259
273
|
},
|
|
260
274
|
cancel: true,
|
|
261
275
|
}).onOk(async (name) => {
|
|
262
|
-
|
|
263
|
-
await m("fsCreateFolder", { path: p });
|
|
276
|
+
await fsCreateFolder(path.value + "/" + name)
|
|
264
277
|
const items = await loadItems();
|
|
265
278
|
reloadTreeFolder(path.value, items.folders);
|
|
266
279
|
});
|
|
@@ -273,7 +286,7 @@ const onDeleteRow = (row) => {
|
|
|
273
286
|
message: "Are you sure you want to delete this file?",
|
|
274
287
|
cancel: true,
|
|
275
288
|
}).onOk(async () => {
|
|
276
|
-
await
|
|
289
|
+
await fsDeleteFile(row.path);
|
|
277
290
|
const items = await loadItems();
|
|
278
291
|
reloadTreeFolder(path.value, items.folders);
|
|
279
292
|
});
|
|
@@ -283,7 +296,7 @@ const onDeleteRow = (row) => {
|
|
|
283
296
|
message: "Are you sure you want to delete this folder?",
|
|
284
297
|
cancel: true,
|
|
285
298
|
}).onOk(async () => {
|
|
286
|
-
await
|
|
299
|
+
await fsDeleteFolder(row.path);
|
|
287
300
|
const items = await loadItems();
|
|
288
301
|
reloadTreeFolder(path.value, items.folders);
|
|
289
302
|
});
|
|
@@ -301,9 +314,9 @@ const onRenameRow = (row) => {
|
|
|
301
314
|
}).onOk(async (name) => {
|
|
302
315
|
try {
|
|
303
316
|
if (row.type == "file") {
|
|
304
|
-
await
|
|
317
|
+
await fsRenameFile(row.path, name)
|
|
305
318
|
} else {
|
|
306
|
-
await
|
|
319
|
+
await fsRenameFolder(row.path, name);
|
|
307
320
|
}
|
|
308
321
|
} catch (e) {
|
|
309
322
|
quasar.dialog({
|
|
@@ -385,23 +398,64 @@ const filesGrid = computed(() => {
|
|
|
385
398
|
});
|
|
386
399
|
});
|
|
387
400
|
|
|
401
|
+
const onDownloadRow = async (row) => {
|
|
402
|
+
|
|
403
|
+
const resp = await q("fsFile", {
|
|
404
|
+
path: row.path
|
|
405
|
+
}, ["base64Content"]);
|
|
406
|
+
|
|
407
|
+
const downloadLink = document.createElement("a");
|
|
408
|
+
downloadLink.href = `data:application/octet-stream;base64,${resp.base64Content}`;
|
|
409
|
+
downloadLink.download = row.name;
|
|
410
|
+
downloadLink.click();
|
|
411
|
+
|
|
388
412
|
|
|
389
|
-
const onDownloadRow = (row) => {
|
|
390
|
-
// window.open("/api/fs/download?path=" + row.path);
|
|
391
413
|
}
|
|
392
414
|
|
|
393
415
|
const search = ref(null);
|
|
394
416
|
|
|
395
417
|
const reloadStorage = async () => {
|
|
396
|
-
path.value =
|
|
418
|
+
path.value = props.base;
|
|
397
419
|
|
|
398
420
|
search.value = "";
|
|
399
421
|
localSearch.value = "";
|
|
400
422
|
label.value = null;
|
|
401
423
|
const items = await loadItems();
|
|
402
424
|
reloadTreeFolder(path.value, items.folders);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const permission = await granted([
|
|
428
|
+
'fs.folder.create', 'fs.folder.delete', 'fs.folder.rename',
|
|
429
|
+
'fs.file.delete', 'fs.file.rename', 'fs.file.upload'
|
|
430
|
+
]);
|
|
431
|
+
|
|
432
|
+
const canDeleteRow = (row) => {
|
|
433
|
+
if (row.type == "folder" && permission.includes("fs.folder.delete")) {
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (row.type == "file" && permission.includes("fs.file.delete")) {
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const canRenameRow = (row) => {
|
|
445
|
+
if (row.type == "folder" && permission.includes("fs.folder.rename")) {
|
|
446
|
+
return true;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (row.type == "file" && permission.includes("fs.file.rename")) {
|
|
450
|
+
return true;
|
|
451
|
+
}
|
|
403
452
|
|
|
453
|
+
return false;
|
|
404
454
|
}
|
|
455
|
+
|
|
456
|
+
/* const perm = await q("granted", {
|
|
457
|
+
rights: ["fs.folder.create"]
|
|
458
|
+
}) */
|
|
405
459
|
</script>
|
|
406
460
|
<template>
|
|
407
461
|
<q-layout view="hHh lpR fFf" class="bg-white" container :style="{ 'min-height': height }">
|
|
@@ -435,14 +489,15 @@ const reloadStorage = async () => {
|
|
|
435
489
|
<q-btn icon="add" outline rounded color="primary" :label="$t('New')">
|
|
436
490
|
<q-menu>
|
|
437
491
|
<q-list>
|
|
438
|
-
<q-item clickable v-close-popup @click="onNewFolder">
|
|
492
|
+
<q-item clickable v-close-popup @click="onNewFolder" v-if="permission.includes('fs.folder.create')">
|
|
439
493
|
<q-item-section avatar>
|
|
440
494
|
<q-icon name="sym_o_create_new_folder"></q-icon>
|
|
441
495
|
</q-item-section>
|
|
442
496
|
<q-item-section>{{ $t('Folder') }}</q-item-section>
|
|
443
497
|
</q-item>
|
|
444
498
|
<q-separator />
|
|
445
|
-
<q-item clickable v-close-popup @click="showUploadFiles = true"
|
|
499
|
+
<q-item clickable v-close-popup @click="showUploadFiles = true"
|
|
500
|
+
v-if="permission.includes('fs.file.upload')">
|
|
446
501
|
<q-item-section avatar>
|
|
447
502
|
<q-icon name="sym_o_upload_file"></q-icon>
|
|
448
503
|
</q-item-section>
|
|
@@ -509,7 +564,6 @@ const reloadStorage = async () => {
|
|
|
509
564
|
|
|
510
565
|
<q-toolbar>
|
|
511
566
|
<q-breadcrumbs>
|
|
512
|
-
<q-breadcrumbs-el :label="$t('Storage')" @click="path = ''" href="javascript:void(0)"></q-breadcrumbs-el>
|
|
513
567
|
<q-breadcrumbs-el v-for="(b, index) in breadcrumbs" :label="b.label" :key="index" @click="path = b.path"
|
|
514
568
|
href="javascript:void(0)"></q-breadcrumbs-el>
|
|
515
569
|
</q-breadcrumbs>
|
|
@@ -583,11 +637,11 @@ const reloadStorage = async () => {
|
|
|
583
637
|
<q-btn flat icon="sym_o_more_vert" round dense>
|
|
584
638
|
<q-menu>
|
|
585
639
|
<q-list>
|
|
586
|
-
<q-item clickable v-close-popup @click="onDeleteRow(props.row)">
|
|
640
|
+
<q-item clickable v-close-popup @click="onDeleteRow(props.row)" v-if="canDeleteRow(props.row)">
|
|
587
641
|
<q-item-section avatar>
|
|
588
642
|
<q-icon name="sym_o_delete"></q-icon>
|
|
589
643
|
</q-item-section>
|
|
590
|
-
<q-item-section>{{ $t('Delete') }}</q-item-section>
|
|
644
|
+
<q-item-section>{{ $t('Delete') }} </q-item-section>
|
|
591
645
|
</q-item>
|
|
592
646
|
|
|
593
647
|
<q-item v-if="props.row.type == 'file'" clickable v-close-popup @click="onDownloadRow(props.row)">
|
|
@@ -597,7 +651,7 @@ const reloadStorage = async () => {
|
|
|
597
651
|
<q-item-section>Download</q-item-section>
|
|
598
652
|
</q-item>
|
|
599
653
|
|
|
600
|
-
<q-item clickable v-close-popup @click="onRenameRow(props.row)">
|
|
654
|
+
<q-item clickable v-close-popup @click="onRenameRow(props.row)" v-if="canRenameRow(props.row)">
|
|
601
655
|
<q-item-section avatar>
|
|
602
656
|
<q-icon name="sym_o_edit"></q-icon>
|
|
603
657
|
</q-item-section>
|
|
@@ -6,7 +6,6 @@ import { addObject, updateObject } from '../';
|
|
|
6
6
|
|
|
7
7
|
const route = useRoute();
|
|
8
8
|
const router = useRouter();
|
|
9
|
-
const module = route.path.split("/")[1];
|
|
10
9
|
|
|
11
10
|
const form = ref(null);
|
|
12
11
|
const props = defineProps({
|
|
@@ -20,17 +19,26 @@ const props = defineProps({
|
|
|
20
19
|
gutter: {
|
|
21
20
|
type: String,
|
|
22
21
|
default: "md"
|
|
22
|
+
},
|
|
23
|
+
submitLabel: {
|
|
24
|
+
type: String,
|
|
25
|
+
default: "Save"
|
|
26
|
+
},
|
|
27
|
+
submitIcon: {
|
|
28
|
+
type: String,
|
|
29
|
+
default: "sym_o_save"
|
|
23
30
|
}
|
|
24
31
|
});
|
|
25
32
|
|
|
26
33
|
const que = useQuasar();
|
|
27
|
-
const emit = defineEmits(["save"]);
|
|
34
|
+
const emit = defineEmits(["save", "submit"]);
|
|
28
35
|
const save = async () => {
|
|
29
36
|
|
|
30
37
|
let valid = await form.value.validate();
|
|
31
38
|
if (!valid) return;
|
|
32
39
|
if (valid) {
|
|
33
40
|
emit("save");
|
|
41
|
+
emit('submit');
|
|
34
42
|
}
|
|
35
43
|
|
|
36
44
|
|
|
@@ -82,7 +90,7 @@ const onSubmit = (e) => {
|
|
|
82
90
|
</q-card-section>
|
|
83
91
|
|
|
84
92
|
<q-card-actions align="right">
|
|
85
|
-
<l-
|
|
93
|
+
<l-btn color="primary" :icon="submitIcon" :label="submitLabel" @click="save" />
|
|
86
94
|
</q-card-actions>
|
|
87
95
|
</l-card>
|
|
88
96
|
</q-form>
|
|
@@ -92,11 +92,13 @@ const onDelete = async () => {
|
|
|
92
92
|
<l-add-btn v-if="showAddBtn" />
|
|
93
93
|
<l-edit-btn v-if="showEditBtn" />
|
|
94
94
|
<l-delete-btn v-if="showDeleteBtn" @submit="onDelete" />
|
|
95
|
-
|
|
96
|
-
<slot name="header"></slot>
|
|
97
95
|
<q-toolbar-title>{{ i18n.t(title) }}</q-toolbar-title>
|
|
98
96
|
</q-toolbar>
|
|
99
97
|
|
|
98
|
+
<div class="q-gutter-sm q-mb-sm">
|
|
99
|
+
<slot name="header"></slot>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
100
102
|
<slot></slot>
|
|
101
103
|
</q-page>
|
|
102
104
|
</template>
|
|
@@ -31,9 +31,10 @@ const onSave = async () => {
|
|
|
31
31
|
|
|
32
32
|
<l-input label="Permission name" v-model="obj.value" required></l-input>
|
|
33
33
|
|
|
34
|
-
<q-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
<q-field label="Roles" stack-label>
|
|
35
|
+
<q-option-group type="checkbox" :options="roles" v-model="obj.roles" inline>
|
|
36
|
+
</q-option-group>
|
|
37
|
+
</q-field>
|
|
37
38
|
|
|
38
39
|
</l-form>
|
|
39
40
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { reactive } from 'vue'
|
|
3
|
+
import { utils, writeFileXLSX } from 'xlsx';
|
|
4
|
+
import { q } from '../../'
|
|
5
|
+
const obj = reactive({
|
|
6
|
+
roles: []
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const app = await q("app", ["permissions"])
|
|
10
|
+
let allData = await q("listRole", ["name", "permissions"]);
|
|
11
|
+
|
|
12
|
+
let roles = allData.map((role) => {
|
|
13
|
+
return {
|
|
14
|
+
label: role.name,
|
|
15
|
+
value: role.name,
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const submit = () => {
|
|
20
|
+
|
|
21
|
+
//filter roles
|
|
22
|
+
let e = allData.filter((role) => {
|
|
23
|
+
return obj.roles.indexOf(role.name) != -1;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
let data = [];
|
|
27
|
+
|
|
28
|
+
//push permissions to rows
|
|
29
|
+
app.permissions.forEach(permission => {
|
|
30
|
+
let row = {
|
|
31
|
+
permission: permission
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
e.forEach(role => {
|
|
35
|
+
if (role.permissions.indexOf(permission) != -1) {
|
|
36
|
+
//tick checkbox
|
|
37
|
+
row[role.name] = "Yes"
|
|
38
|
+
} else {
|
|
39
|
+
row[role.name] = ''
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
data.push(row);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
const ws = utils.json_to_sheet(data)
|
|
48
|
+
const wb = utils.book_new()
|
|
49
|
+
utils.book_append_sheet(wb, ws, "Roles")
|
|
50
|
+
writeFileXLSX(wb, "permission.xlsx")
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
</script>
|
|
54
|
+
<template>
|
|
55
|
+
<l-page>
|
|
56
|
+
<l-form submit-label="Export" submit-icon="sym_o_download" @submit="submit">
|
|
57
|
+
<q-field label="Roles" stack-label>
|
|
58
|
+
<q-option-group type="checkbox" :options="roles" v-model="obj.roles" inline>
|
|
59
|
+
</q-option-group>
|
|
60
|
+
</q-field>
|
|
61
|
+
</l-form>
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
</l-page>
|
|
65
|
+
</template>
|
|
@@ -62,24 +62,7 @@ const columns = [
|
|
|
62
62
|
</q-td>
|
|
63
63
|
</template>
|
|
64
64
|
|
|
65
|
-
|
|
66
65
|
</q-table>
|
|
67
66
|
|
|
68
|
-
<!-- q-table :data="roles">
|
|
69
|
-
|
|
70
|
-
<el-table-column width="60px" #default="{ row }">
|
|
71
|
-
<q-btn v-if="row.canDelete" flat round dense icon="sym_o_delete" @click="onDelete(row.name)" />
|
|
72
|
-
</el-table-column>
|
|
73
|
-
<el-table-column prop="name" label="Name" sortable width="200px"></el-table-column>
|
|
74
|
-
<el-table-column label="Parent">
|
|
75
|
-
|
|
76
|
-
<template #default="{ row }">
|
|
77
|
-
|
|
78
|
-
<el-table :data="row.parents">
|
|
79
|
-
<el-table-column prop="name" label="Name"></el-table-column>
|
|
80
|
-
</el-table>
|
|
81
|
-
</template>
|
|
82
|
-
</el-table-column>
|
|
83
|
-
</q-table -->
|
|
84
67
|
</l-page>
|
|
85
68
|
</template>
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { reactive } from 'vue'
|
|
3
3
|
import { m } from "../../"
|
|
4
4
|
const obj = reactive({
|
|
5
|
-
input1: 'test'
|
|
5
|
+
input1: 'test',
|
|
6
|
+
editor: 'test',
|
|
6
7
|
})
|
|
7
8
|
const onSave = async () => {
|
|
8
9
|
console.log(obj)
|
|
@@ -15,9 +16,13 @@ const onSave = async () => {
|
|
|
15
16
|
</script>
|
|
16
17
|
<template>
|
|
17
18
|
<l-page>
|
|
19
|
+
{{ obj }}
|
|
18
20
|
<l-form v-model="obj">
|
|
19
21
|
<l-input v-model="obj.input1" label="Input1" />
|
|
20
22
|
<q-file v-model="obj.file" label="File" accept=".txt" />
|
|
23
|
+
|
|
24
|
+
<q-editor v-model="obj.editor" />
|
|
25
|
+
|
|
21
26
|
</l-form>
|
|
22
27
|
</l-page>
|
|
23
28
|
</template>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { q } from '../../'
|
|
3
|
-
const my = await q("my", ["username", "first_name", "last_name", "email", "phone", "roles", "addr1", "addr2", "addr3", "join_date"])
|
|
4
|
-
|
|
3
|
+
const my = await q("my", ["user_id", "username", "first_name", "last_name", "email", "phone", "roles", "addr1", "addr2", "addr3", "join_date"])
|
|
5
4
|
</script>
|
|
6
5
|
<template>
|
|
7
6
|
<l-page title="User profile">
|
|
@@ -19,9 +18,8 @@ const my = await q("my", ["username", "first_name", "last_name", "email", "phone
|
|
|
19
18
|
<l-item label="Address">{{ my.addr1 }} {{ my.addr2 }} {{ my.addr3 }}</l-item>
|
|
20
19
|
<l-item label="Join date">{{ my.join_date }}</l-item>
|
|
21
20
|
<l-item label="Roles">{{ my.roles.join(",") }}</l-item>
|
|
22
|
-
|
|
23
|
-
|
|
24
21
|
</l-list>
|
|
25
22
|
</l-card>
|
|
23
|
+
|
|
26
24
|
</l-page>
|
|
27
25
|
</template>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { reactive } from "vue"
|
|
3
|
-
import {
|
|
3
|
+
import { notify } from '../../../'
|
|
4
|
+
import { updatePassword } from "@hostlink/light"
|
|
4
5
|
|
|
5
6
|
const obj = reactive({
|
|
6
7
|
old_password: "",
|
|
@@ -9,10 +10,7 @@ const obj = reactive({
|
|
|
9
10
|
})
|
|
10
11
|
|
|
11
12
|
const onSave = async () => {
|
|
12
|
-
if (await
|
|
13
|
-
old_password: obj.old_password,
|
|
14
|
-
new_password: obj.new_password,
|
|
15
|
-
})) {
|
|
13
|
+
if (await updatePassword(obj.old_password, obj.new_password)) {
|
|
16
14
|
notify("Your password has been updated")
|
|
17
15
|
} else {
|
|
18
16
|
notify("Old password is incorrect", "red")
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { ref } from "vue"
|
|
3
|
+
import { useRoute } from "vue-router"
|
|
3
4
|
const tab = ref("general")
|
|
5
|
+
const route = useRoute()
|
|
4
6
|
</script>
|
|
5
7
|
|
|
6
8
|
<template>
|
|
@@ -8,7 +10,7 @@ const tab = ref("general")
|
|
|
8
10
|
<l-card>
|
|
9
11
|
<q-splitter unit="px" :model-value="120">
|
|
10
12
|
<template #before>
|
|
11
|
-
<q-tabs v-model="tab" vertical
|
|
13
|
+
<q-tabs v-model="tab" vertical>
|
|
12
14
|
<q-route-tab name="general" icon="sym_o_info" :label="$t('General')" to="/User/setting" exact
|
|
13
15
|
replace />
|
|
14
16
|
<q-route-tab name="information" icon="sym_o_info" :label="$t('Information')"
|
|
@@ -17,14 +19,15 @@ const tab = ref("general")
|
|
|
17
19
|
to="/User/setting/password" exact replace />
|
|
18
20
|
<q-route-tab name="style" icon="sym_o_style" :label="$t('Style')" to="/User/setting/style" exact
|
|
19
21
|
replace />
|
|
20
|
-
<q-route-tab name="2fa" icon="sym_o_key" :label="$t('2FA')" to="/User/setting/two-factor-auth" exact
|
|
22
|
+
<q-route-tab name="2fa" icon="sym_o_key" :label="$t('2FA')" to="/User/setting/two-factor-auth" exact
|
|
23
|
+
replace />
|
|
21
24
|
<q-route-tab name="bio" icon="sym_o_fingerprint" :label="$t('Bio')" to="/User/setting/bio-auth"
|
|
22
25
|
exact replace />
|
|
23
26
|
</q-tabs>
|
|
24
27
|
</template>
|
|
25
28
|
|
|
26
29
|
<template #after>
|
|
27
|
-
<router-view>
|
|
30
|
+
<router-view :key="route.path">
|
|
28
31
|
</router-view>
|
|
29
32
|
</template>
|
|
30
33
|
</q-splitter>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { useQuasar } from 'quasar';
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import { updatePassword } from '@hostlink/light';
|
|
5
4
|
|
|
6
5
|
const obj = reactive({
|
|
7
6
|
old_password: "",
|
|
@@ -13,10 +12,7 @@ const qua = useQuasar();
|
|
|
13
12
|
const router = useRouter();
|
|
14
13
|
|
|
15
14
|
const onSave = async () => {
|
|
16
|
-
if (await
|
|
17
|
-
old_password: obj.old_password,
|
|
18
|
-
new_password: obj.new_password,
|
|
19
|
-
})) {
|
|
15
|
+
if (await updatePassword(obj.old_password, obj.new_password)) {
|
|
20
16
|
//back
|
|
21
17
|
router.back();
|
|
22
18
|
} else {
|
|
@@ -8,15 +8,18 @@ const columns = [
|
|
|
8
8
|
},
|
|
9
9
|
{
|
|
10
10
|
label: "User",
|
|
11
|
-
name: "username"
|
|
11
|
+
name: "username",
|
|
12
|
+
sortable: true,
|
|
13
|
+
searchable: true,
|
|
12
14
|
},
|
|
13
15
|
{
|
|
14
16
|
label: "Login time",
|
|
15
17
|
name: "login_dt",
|
|
16
18
|
sortable: true,
|
|
17
19
|
searchable: true,
|
|
20
|
+
searchType: "date",
|
|
18
21
|
}, {
|
|
19
|
-
label: "
|
|
22
|
+
label: "Logout time",
|
|
20
23
|
name: "logout_dt",
|
|
21
24
|
sortable: true,
|
|
22
25
|
searchable: true,
|
package/dist/runtime/routes.mjs
CHANGED
|
@@ -52,6 +52,11 @@ function Permission_all() {
|
|
|
52
52
|
/* webpackChunkName: "Permission-all" */ './pages/Permission/all.vue'
|
|
53
53
|
)
|
|
54
54
|
}
|
|
55
|
+
function Permission_export() {
|
|
56
|
+
return import(
|
|
57
|
+
/* webpackChunkName: "Permission-export" */ './pages/Permission/export.vue'
|
|
58
|
+
)
|
|
59
|
+
}
|
|
55
60
|
function Role_add() {
|
|
56
61
|
return import(/* webpackChunkName: "Role-add" */ './pages/Role/add.vue')
|
|
57
62
|
}
|
|
@@ -228,6 +233,11 @@ export default [
|
|
|
228
233
|
path: '/Permission/all',
|
|
229
234
|
component: Permission_all,
|
|
230
235
|
},
|
|
236
|
+
{
|
|
237
|
+
name: 'Permission-export',
|
|
238
|
+
path: '/Permission/export',
|
|
239
|
+
component: Permission_export,
|
|
240
|
+
},
|
|
231
241
|
{
|
|
232
242
|
name: 'Role-add',
|
|
233
243
|
path: '/Role/add',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hostlink/nuxt-light",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.64",
|
|
4
4
|
"description": "HostLink Nuxt Light Framework",
|
|
5
5
|
"repository": "@hostlink/nuxt-light",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"route-gen": "node route-generate.mjs"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@hostlink/light": "^0.0.
|
|
37
|
+
"@hostlink/light": "^0.0.23",
|
|
38
38
|
"@nuxt/kit": "^3.7.0",
|
|
39
39
|
"@quasar/extras": "^1.16.6",
|
|
40
40
|
"axios": "^1.5.0",
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"json-to-graphql-query": "^2.2.5",
|
|
43
43
|
"quasar": "^2.12.5",
|
|
44
44
|
"unplugin-auto-import": "^0.16.6",
|
|
45
|
-
"vue-i18n": "^9.2.2"
|
|
45
|
+
"vue-i18n": "^9.2.2",
|
|
46
|
+
"xlsx": "^0.18.5"
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|
|
48
49
|
"@nuxt/content": "^2.8.2",
|