@hostlink/nuxt-light 1.5.2 → 1.6.2
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-main.vue +3 -1
- package/dist/runtime/components/l-checkbox.vue +1 -1
- package/dist/runtime/components/l-customizer.vue +4 -5
- package/dist/runtime/components/l-fav-menu.vue +3 -1
- package/dist/runtime/components/l-file-manager-labels.vue +37 -36
- package/dist/runtime/components/l-file-manager-preview.vue +20 -3
- package/dist/runtime/components/l-file-manager.vue +38 -33
- package/dist/runtime/components/l-file-upload.vue +4 -29
- package/dist/runtime/components/l-login.vue +16 -10
- package/dist/runtime/components/l-menu.vue +29 -47
- package/dist/runtime/components/l-select.vue +2 -0
- package/dist/runtime/components/l-table.vue +6 -6
- package/dist/runtime/formkit/OptionGroup.vue +1 -1
- package/dist/runtime/formkit/Select.vue +0 -2
- package/dist/runtime/locales/zh-hk.json +4 -0
- package/dist/runtime/pages/Permission/all.vue +5 -8
- package/dist/runtime/pages/Role/index.vue +5 -4
- package/dist/runtime/pages/System/database/backup.vue +11 -1
- package/dist/runtime/pages/System/database/table.vue +5 -6
- package/dist/runtime/pages/System/fs.vue +32 -26
- package/dist/runtime/pages/System/menu/index.vue +7 -5
- package/dist/runtime/pages/System/setting.vue +6 -6
- package/dist/runtime/pages/Translate/index.vue +5 -4
- package/dist/runtime/pages/User/add.vue +0 -2
- package/dist/runtime/pages/User/profile.vue +3 -2
- package/dist/runtime/pages/User/setting/open_id.vue +28 -20
- package/dist/runtime/plugin.mjs +3 -2
- package/package.json +2 -2
package/dist/module.json
CHANGED
|
@@ -34,6 +34,8 @@ const light = useLight({
|
|
|
34
34
|
});
|
|
35
35
|
light.init(my.styles);
|
|
36
36
|
|
|
37
|
+
quasar.addressbarColor.set(light.color);
|
|
38
|
+
|
|
37
39
|
//set permission
|
|
38
40
|
light.setPermissions(my.permissions);
|
|
39
41
|
|
|
@@ -396,7 +398,7 @@ const c1 = computed(() => {
|
|
|
396
398
|
|
|
397
399
|
<div class="q-mx-sm">
|
|
398
400
|
<l-fav-menu :value="myFavorites" :dense="style.dense" v-if="myFavoritesCount > 0" />
|
|
399
|
-
<l-menu
|
|
401
|
+
<l-menu :value="menus" :dense="style.dense" />
|
|
400
402
|
|
|
401
403
|
</div>
|
|
402
404
|
|
|
@@ -18,7 +18,7 @@ const localValue = computed({
|
|
|
18
18
|
});
|
|
19
19
|
</script>
|
|
20
20
|
<template>
|
|
21
|
-
<q-checkbox v-bind="$props" v-model="localValue" :label="$t($props.label ?? '')">
|
|
21
|
+
<q-checkbox v-bind="$props" v-model="localValue" :label="$t($props.label ?? '')" :color="$light.color">
|
|
22
22
|
<slot></slot>
|
|
23
23
|
</q-checkbox>
|
|
24
24
|
</template>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import {
|
|
2
|
+
import { ref } from 'vue'
|
|
3
3
|
const menuWidth = defineModel('menuWidth', {
|
|
4
4
|
type: Number,
|
|
5
5
|
default: 280
|
|
@@ -35,7 +35,6 @@ const COLORS = [
|
|
|
35
35
|
]
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
|
|
39
38
|
defineEmits(['update:theme', 'update:menuOverlayHeader', 'update:dense', 'update:color', 'update:miniState', 'update:footer'])
|
|
40
39
|
|
|
41
40
|
const props = defineProps({
|
|
@@ -161,12 +160,12 @@ setInterval(() => {
|
|
|
161
160
|
</q-item-section>
|
|
162
161
|
</q-item>
|
|
163
162
|
<q-separator />
|
|
164
|
-
<q-item-label header>{{ $t('Menu width') }}</q-item-label>
|
|
165
163
|
<q-item>
|
|
166
164
|
<q-item-section>
|
|
167
|
-
<q-
|
|
165
|
+
<q-item-label>{{ $t('Menu width') }}</q-item-label>
|
|
166
|
+
<q-item-label><q-slider v-model="menuWidth" :min="200" :max="360" label
|
|
167
|
+
:color="$light.color" /></q-item-label>
|
|
168
168
|
</q-item-section>
|
|
169
|
-
|
|
170
169
|
</q-item>
|
|
171
170
|
<q-separator />
|
|
172
171
|
|
|
@@ -16,13 +16,14 @@ const color2 = computed(() => {
|
|
|
16
16
|
return c;
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
+
|
|
19
20
|
</script>
|
|
20
21
|
|
|
21
22
|
<style scoped>
|
|
22
23
|
.menu-list .q-item{border-radius:12px 12px 12px 12px}.menu-list .q-router-link--exact-active{background:linear-gradient(118deg,v-bind(color1),v-bind(color2));color:#fff}
|
|
23
24
|
</style>
|
|
24
25
|
<template>
|
|
25
|
-
<q-expansion-item :label="$t('My favorite')" :dense="dense"
|
|
26
|
+
<q-expansion-item :label="$t('My favorite')" :dense="dense" icon="sym_o_favorite">
|
|
26
27
|
<q-list class="q-pl-md menu-list">
|
|
27
28
|
<q-item v-ripple :to="item.path" v-for="item in value">
|
|
28
29
|
<q-item-section avatar>
|
|
@@ -31,6 +32,7 @@ const color2 = computed(() => {
|
|
|
31
32
|
<q-item-section>
|
|
32
33
|
<q-item-label v-text="$t(item.label)"></q-item-label>
|
|
33
34
|
</q-item-section>
|
|
35
|
+
|
|
34
36
|
</q-item>
|
|
35
37
|
</q-list>
|
|
36
38
|
</q-expansion-item>
|
|
@@ -1,55 +1,56 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
type: String,
|
|
6
|
-
default: ""
|
|
7
|
-
}
|
|
2
|
+
const modelValue = defineModel({
|
|
3
|
+
type: String,
|
|
4
|
+
default: ""
|
|
8
5
|
})
|
|
9
|
-
|
|
10
6
|
const emit = defineEmits(['update:modelValue'])
|
|
11
7
|
const toggleLabel = (type) => {
|
|
12
|
-
|
|
13
|
-
if (props.modelValue == type) {
|
|
8
|
+
if (modelValue.value == type) {
|
|
14
9
|
emit('update:modelValue', null)
|
|
15
10
|
} else {
|
|
16
11
|
emit('update:modelValue', type)
|
|
17
12
|
}
|
|
18
13
|
}
|
|
19
14
|
|
|
15
|
+
const LABLES = [
|
|
16
|
+
{
|
|
17
|
+
type: 'document',
|
|
18
|
+
icon: 'sym_o_article',
|
|
19
|
+
label: 'Documents'
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
type: 'image',
|
|
23
|
+
icon: 'sym_o_insert_photo',
|
|
24
|
+
label: 'Images'
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
type: 'video',
|
|
28
|
+
icon: 'sym_o_videocam',
|
|
29
|
+
label: 'Videos'
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
type: 'audio',
|
|
33
|
+
icon: 'sym_o_audiotrack',
|
|
34
|
+
label: 'Audio'
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: 'other',
|
|
38
|
+
icon: 'sym_o_text_snippet',
|
|
39
|
+
label: 'Archives'
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
|
|
20
43
|
</script>
|
|
21
44
|
<template>
|
|
22
45
|
<q-item-label header>
|
|
23
46
|
{{ $t('Labels') }}
|
|
24
47
|
</q-item-label>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
</q-item-section>
|
|
29
|
-
<q-item-section> {{ $t('Documents') }} </q-item-section>
|
|
30
|
-
</q-item>
|
|
31
|
-
<q-item clickable :active="modelValue == 'image'" @click="toggleLabel('image')">
|
|
32
|
-
<q-item-section avatar>
|
|
33
|
-
<q-icon name="sym_o_insert_photo" />
|
|
34
|
-
</q-item-section>
|
|
35
|
-
<q-item-section> {{ $t('Images') }} </q-item-section>
|
|
36
|
-
</q-item>
|
|
37
|
-
<q-item clickable :active="modelValue == 'video'" @click="toggleLabel('video')">
|
|
38
|
-
<q-item-section avatar>
|
|
39
|
-
<q-icon name="sym_o_videocam" />
|
|
40
|
-
</q-item-section>
|
|
41
|
-
<q-item-section> {{ $t('Videos') }} </q-item-section>
|
|
42
|
-
</q-item>
|
|
43
|
-
<q-item clickable :active="modelValue == 'audio'" @click="toggleLabel('audio')">
|
|
44
|
-
<q-item-section avatar>
|
|
45
|
-
<q-icon name="sym_o_audiotrack" />
|
|
46
|
-
</q-item-section>
|
|
47
|
-
<q-item-section> {{ $t('Audio') }} </q-item-section>
|
|
48
|
-
</q-item>
|
|
49
|
-
<q-item clickable :active="modelValue == 'other'" @click="toggleLabel('other')">
|
|
48
|
+
|
|
49
|
+
<q-item v-for="label in LABLES" :key="label.type" clickable :active="modelValue == label.type"
|
|
50
|
+
:active-class="`text-${$light.color}`" @click="toggleLabel(label.type)">
|
|
50
51
|
<q-item-section avatar>
|
|
51
|
-
<q-icon name="
|
|
52
|
+
<q-icon :name="label.icon" />
|
|
52
53
|
</q-item-section>
|
|
53
|
-
<q-item-section> {{ $t(
|
|
54
|
+
<q-item-section> {{ $t(label.label) }} </q-item-section>
|
|
54
55
|
</q-item>
|
|
55
56
|
</template>
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
+
import { format } from "quasar"
|
|
2
3
|
import { q } from '../';
|
|
4
|
+
const { humanStorageSize } = format
|
|
5
|
+
|
|
3
6
|
const props = defineProps(["modelValue"]);
|
|
4
7
|
|
|
5
8
|
|
|
@@ -7,6 +10,8 @@ const file = await q("fsFile", {
|
|
|
7
10
|
path: props.modelValue.path
|
|
8
11
|
}, ["canPreview", "imagePath"])
|
|
9
12
|
|
|
13
|
+
const size = humanStorageSize(props.modelValue.size)
|
|
14
|
+
|
|
10
15
|
</script >
|
|
11
16
|
<template>
|
|
12
17
|
<template v-if="file.canPreview">
|
|
@@ -17,19 +22,31 @@ const file = await q("fsFile", {
|
|
|
17
22
|
<q-item-section>
|
|
18
23
|
<q-item-label>Name</q-item-label>
|
|
19
24
|
</q-item-section>
|
|
20
|
-
<q-item-section side>
|
|
25
|
+
<q-item-section side>
|
|
26
|
+
<q-item-label lines="1">
|
|
27
|
+
{{ modelValue.name }}
|
|
28
|
+
</q-item-label>
|
|
29
|
+
</q-item-section>
|
|
21
30
|
</q-item>
|
|
22
31
|
<q-item>
|
|
23
32
|
<q-item-section>
|
|
24
33
|
<q-item-label>Size</q-item-label>
|
|
25
34
|
</q-item-section>
|
|
26
|
-
<q-item-section side>{{ modelValue.size }} </q-item-section>
|
|
35
|
+
<q-item-section side>{{ size }} ({{ modelValue.size }}) </q-item-section>
|
|
27
36
|
</q-item>
|
|
28
37
|
<q-item>
|
|
29
38
|
<q-item-section>
|
|
30
39
|
<q-item-label>Location</q-item-label>
|
|
31
40
|
</q-item-section>
|
|
32
|
-
<q-item-section side>
|
|
41
|
+
<q-item-section side>
|
|
42
|
+
<q-item-label lines="1">{{ modelValue.path }}</q-item-label>
|
|
43
|
+
</q-item-section>
|
|
44
|
+
</q-item>
|
|
45
|
+
<q-item>
|
|
46
|
+
<q-item-section>
|
|
47
|
+
<q-item-label>Last modified</q-item-label>
|
|
48
|
+
</q-item-section>
|
|
49
|
+
<q-item-section side>{{ modelValue.lastModifiedHuman }}</q-item-section>
|
|
33
50
|
</q-item>
|
|
34
51
|
</q-list>
|
|
35
52
|
</template>
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import { VariableType } from "json-to-graphql-query";
|
|
3
3
|
import { useI18n } from "vue-i18n";
|
|
4
4
|
import { ref, watch, computed } from 'vue';
|
|
5
|
-
import { useQuasar } from 'quasar';
|
|
5
|
+
import { useQuasar, format } from 'quasar';
|
|
6
6
|
import { q, m, useLight } from '#imports';
|
|
7
7
|
import {
|
|
8
8
|
fsListFolders, fsCreateFolder, fsDeleteFolder, fsDeleteFile, fsRenameFile, fsRenameFolder, fsReadFile,
|
|
9
9
|
granted
|
|
10
10
|
|
|
11
11
|
} from "@hostlink/light";
|
|
12
|
-
|
|
12
|
+
const { humanStorageSize } = format
|
|
13
13
|
|
|
14
14
|
const light = useLight();
|
|
15
15
|
const i18n = useI18n();
|
|
@@ -17,7 +17,7 @@ const quasar = useQuasar();
|
|
|
17
17
|
const emit = defineEmits(["input", "close"]);
|
|
18
18
|
|
|
19
19
|
const props = defineProps({
|
|
20
|
-
|
|
20
|
+
closeable: Boolean,
|
|
21
21
|
height: {
|
|
22
22
|
type: String,
|
|
23
23
|
default: "700px",
|
|
@@ -60,14 +60,20 @@ const columns = ref([
|
|
|
60
60
|
{
|
|
61
61
|
name: "last_modified",
|
|
62
62
|
label: i18n.t("Last Modified"),
|
|
63
|
-
field: "
|
|
63
|
+
field: "lastModifiedHuman",
|
|
64
64
|
align: "left",
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
67
|
name: "size_display",
|
|
68
68
|
label: i18n.t("Size"),
|
|
69
69
|
field: "size",
|
|
70
|
-
align: "
|
|
70
|
+
align: "right",
|
|
71
|
+
format: (val) => {
|
|
72
|
+
if (val == null) {
|
|
73
|
+
return "";
|
|
74
|
+
}
|
|
75
|
+
return humanStorageSize(val)
|
|
76
|
+
}
|
|
71
77
|
},
|
|
72
78
|
{ name: "action" },
|
|
73
79
|
]);
|
|
@@ -126,7 +132,7 @@ const loadItems = async () => {
|
|
|
126
132
|
filesParams.search = localSearch.value;
|
|
127
133
|
}
|
|
128
134
|
|
|
129
|
-
let files = await q("fsListFiles", filesParams, ["name", "size", "mime", "path"]);
|
|
135
|
+
let files = await q("fsListFiles", filesParams, ["name", "size", "mime", "path", "canPreview", "imagePath", "lastModified", "lastModifiedHuman"]);
|
|
130
136
|
files = files.map((item) => {
|
|
131
137
|
item.type = "file";
|
|
132
138
|
return item;
|
|
@@ -189,7 +195,7 @@ const breadcrumbs = computed(() => {
|
|
|
189
195
|
ps.push(p);
|
|
190
196
|
breadcrumbs.push({
|
|
191
197
|
label: p,
|
|
192
|
-
path:
|
|
198
|
+
path: ps.join("/"),
|
|
193
199
|
});
|
|
194
200
|
}
|
|
195
201
|
}
|
|
@@ -484,7 +490,7 @@ const isDark = computed(() => light.isDarkMode());
|
|
|
484
490
|
|
|
485
491
|
<div>
|
|
486
492
|
<q-input outlined :placeholder="$t('Search for file name')" dense @keyup.enter.native="submitSearch"
|
|
487
|
-
v-model="search">
|
|
493
|
+
v-model="search" :color="$light.color">
|
|
488
494
|
<template v-slot:append>
|
|
489
495
|
<q-btn flat dense round icon="sym_o_search"></q-btn>
|
|
490
496
|
</template>
|
|
@@ -492,7 +498,7 @@ const isDark = computed(() => light.isDarkMode());
|
|
|
492
498
|
</div>
|
|
493
499
|
|
|
494
500
|
<q-space></q-space>
|
|
495
|
-
<q-btn v-if="
|
|
501
|
+
<q-btn v-if="closeable" icon="close" flat round @click="$emit('close')"></q-btn>
|
|
496
502
|
</q-toolbar>
|
|
497
503
|
</q-header>
|
|
498
504
|
|
|
@@ -524,7 +530,7 @@ const isDark = computed(() => light.isDarkMode());
|
|
|
524
530
|
</q-item-section>
|
|
525
531
|
</q-item>
|
|
526
532
|
|
|
527
|
-
<q-list>
|
|
533
|
+
<q-list dense>
|
|
528
534
|
<q-item>
|
|
529
535
|
<q-item-section avatar>
|
|
530
536
|
<q-icon name="sym_o_storage" />
|
|
@@ -534,13 +540,9 @@ const isDark = computed(() => light.isDarkMode());
|
|
|
534
540
|
<q-btn dense round flat icon="sym_o_refresh" @click="reloadStorage"></q-btn>
|
|
535
541
|
</q-item-section>
|
|
536
542
|
</q-item>
|
|
537
|
-
<q-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
@lazy-load="onLazyLoad" v-model:selected="path" no-selection-unset>
|
|
541
|
-
</q-tree>
|
|
542
|
-
</q-item-section>
|
|
543
|
-
</q-item>
|
|
543
|
+
<q-tree ref="folderTree" class="q-pl-md" :nodes="folders" node-key="path" label-key="name"
|
|
544
|
+
@lazy-load="onLazyLoad" v-model:selected="path" no-selection-unset>
|
|
545
|
+
</q-tree>
|
|
544
546
|
</q-list>
|
|
545
547
|
<!-- q-expansion-item :label="$t('Storage')" default-expand-all default-opened icon="sym_o_storage" selectable
|
|
546
548
|
@click="path = '/'">
|
|
@@ -557,7 +559,6 @@ const isDark = computed(() => light.isDarkMode());
|
|
|
557
559
|
</q-drawer>
|
|
558
560
|
|
|
559
561
|
<q-drawer side="right" show-if-above bordered>
|
|
560
|
-
|
|
561
562
|
<l-file-manager-preview v-model="preview" v-if="preview" :key="preview.path" />
|
|
562
563
|
</q-drawer>
|
|
563
564
|
|
|
@@ -566,19 +567,18 @@ const isDark = computed(() => light.isDarkMode());
|
|
|
566
567
|
<q-dialog v-model="showUploadFiles" persistent transition-show="scale" transition-hide="scale">
|
|
567
568
|
<l-card style="width:300px">
|
|
568
569
|
<q-card-section>
|
|
569
|
-
|
|
570
|
-
<q-file ref="file" v-model="uploadFiles" multiple name="file" label="Files"></q-file>
|
|
570
|
+
<q-file ref="file" v-model="uploadFiles" multiple name="file" label="Files" :color="light.color"></q-file>
|
|
571
571
|
</q-card-section>
|
|
572
572
|
|
|
573
573
|
<q-card-actions align="right">
|
|
574
|
-
<q-btn flat label="Cancel" :color="light.color" v-close-popup></q-btn>
|
|
575
|
-
<q-btn flat label="Upload" :color="light.color" @click="onUploadFiles"></q-btn>
|
|
574
|
+
<q-btn flat :label="$t('Cancel')" :color="light.color" v-close-popup></q-btn>
|
|
575
|
+
<q-btn flat :label="$t('Upload')" :color="light.color" @click="onUploadFiles"></q-btn>
|
|
576
576
|
</q-card-actions>
|
|
577
577
|
</l-card>
|
|
578
578
|
</q-dialog>
|
|
579
579
|
|
|
580
580
|
<q-toolbar>
|
|
581
|
-
<q-breadcrumbs>
|
|
581
|
+
<q-breadcrumbs :active-color="$light.color">
|
|
582
582
|
<q-breadcrumbs-el v-for="(b, index) in breadcrumbs" :label="b.label" :key="index" @click="path = b.path"
|
|
583
583
|
href="javascript:void(0)"></q-breadcrumbs-el>
|
|
584
584
|
</q-breadcrumbs>
|
|
@@ -605,10 +605,11 @@ const isDark = computed(() => light.isDarkMode());
|
|
|
605
605
|
</q-toolbar>
|
|
606
606
|
|
|
607
607
|
<template v-if="grid">
|
|
608
|
-
<q-table title="Folders" flat
|
|
608
|
+
<q-table :title="$t('Folders')" flat grid :columns="columns" :rows="foldersGrid" hide-pagination
|
|
609
|
+
:pagination="{ rowsPerPage: 0 }">
|
|
609
610
|
<template v-slot:item="props">
|
|
610
|
-
<div class="q-pa-xs col-xs-12 col-sm-6 col-md-4">
|
|
611
|
-
<q-card>
|
|
611
|
+
<div class="q-pa-xs col-xs-12 col-sm-6 col-md-4" @click="onDblclickRow(null, props.row, null)">
|
|
612
|
+
<q-card flat bordered>
|
|
612
613
|
<q-item>
|
|
613
614
|
<q-item-section avatar>
|
|
614
615
|
<q-icon name="sym_o_folder" size="sm"></q-icon>
|
|
@@ -617,7 +618,7 @@ const isDark = computed(() => light.isDarkMode());
|
|
|
617
618
|
{{ props.row.name }}
|
|
618
619
|
</q-item-section>
|
|
619
620
|
<q-item-section avatar>
|
|
620
|
-
<q-checkbox v-model="selected" :val="props.row" />
|
|
621
|
+
<q-checkbox v-model="selected" :val="props.row" :color="$light.color" />
|
|
621
622
|
</q-item-section>
|
|
622
623
|
</q-item>
|
|
623
624
|
</q-card>
|
|
@@ -625,22 +626,26 @@ const isDark = computed(() => light.isDarkMode());
|
|
|
625
626
|
</template>
|
|
626
627
|
</q-table>
|
|
627
628
|
|
|
628
|
-
<q-table title="Files" flat
|
|
629
|
+
<q-table :title="$t('Files')" flat grid :columns="columns" :rows="filesGrid" hide-pagination
|
|
630
|
+
:pagination="{ rowsPerPage: 0 }">
|
|
629
631
|
|
|
630
632
|
<template v-slot:item="props">
|
|
631
|
-
<div class="q-pa-xs col-xs-12 col-sm-6 col-md-4">
|
|
632
|
-
<q-card>
|
|
633
|
+
<div class="q-pa-xs col-xs-12 col-sm-6 col-md-4" @click="onClickRow(null, props.row, null)">
|
|
634
|
+
<q-card flat bordered>
|
|
633
635
|
<q-item>
|
|
634
636
|
<q-item-section avatar>
|
|
635
637
|
<q-icon name="sym_o_description" size="sm"></q-icon>
|
|
636
638
|
</q-item-section>
|
|
637
|
-
<q-item-section>
|
|
639
|
+
<q-item-section no-wrap>
|
|
638
640
|
{{ props.row.name }}
|
|
639
641
|
</q-item-section>
|
|
640
642
|
<q-item-section avatar>
|
|
641
|
-
<q-checkbox v-model="selected" :val="props.row" />
|
|
643
|
+
<q-checkbox v-model="selected" :val="props.row" :color="$light.color" />
|
|
642
644
|
</q-item-section>
|
|
643
645
|
</q-item>
|
|
646
|
+
|
|
647
|
+
<q-img v-if="props.row.canPreview" :src="props.row.imagePath"></q-img>
|
|
648
|
+
|
|
644
649
|
</q-card>
|
|
645
650
|
</div>
|
|
646
651
|
</template>
|
|
@@ -648,7 +653,7 @@ const isDark = computed(() => light.isDarkMode());
|
|
|
648
653
|
</template>
|
|
649
654
|
<template v-else>
|
|
650
655
|
|
|
651
|
-
<q-table flat
|
|
656
|
+
<q-table flat :columns="columns" :rows="items" @row-dblclick="onDblclickRow" @row-click="onClickRow"
|
|
652
657
|
:pagination="pagination" row-key="path" selection="multiple" v-model:selected="selected" dense
|
|
653
658
|
:loading="loading" :loading-label="$t('Loading...')" :no-data-label="$t('No data available')">
|
|
654
659
|
<template #body-cell-icon="props">
|
|
@@ -4,7 +4,8 @@ import { ref, computed, useAttrs } from "vue";
|
|
|
4
4
|
import { useLight, m, q } from '#imports';
|
|
5
5
|
import { Loading, Dialog } from "quasar";
|
|
6
6
|
import { useI18n } from "vue-i18n";
|
|
7
|
-
|
|
7
|
+
import { format } from 'quasar'
|
|
8
|
+
const { humanStorageSize } = format
|
|
8
9
|
const light = useLight();
|
|
9
10
|
const i18n = useI18n();
|
|
10
11
|
|
|
@@ -65,32 +66,6 @@ const attrs = computed(() => {
|
|
|
65
66
|
});
|
|
66
67
|
|
|
67
68
|
|
|
68
|
-
//@param bytes Number of bytes.
|
|
69
|
-
//@param si True to use metric (SI) units, aka powers of 1000. False to use binary (IEC), aka powers of 1024.
|
|
70
|
-
//@param dp Number of decimal places to display.
|
|
71
|
-
const humanFileSize = (bytesSize, si = false, dp = 1) => {
|
|
72
|
-
const thresh = si ? 1000 : 1024;
|
|
73
|
-
|
|
74
|
-
if (Math.abs(bytesSize) < thresh) {
|
|
75
|
-
return bytesSize + ' B';
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const units = si
|
|
79
|
-
? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
|
80
|
-
: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; //: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
|
|
81
|
-
let u = -1;
|
|
82
|
-
const r = 10 ** dp;
|
|
83
|
-
|
|
84
|
-
do {
|
|
85
|
-
bytesSize /= thresh;
|
|
86
|
-
++u;
|
|
87
|
-
} while (Math.round(Math.abs(bytesSize) * r) / r >= thresh && u < units.length - 1);
|
|
88
|
-
|
|
89
|
-
return bytesSize.toFixed(dp) + ' ' + units[u];
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
69
|
const uploadFile = ref<File | null>(null);
|
|
95
70
|
const rename = ref(true);
|
|
96
71
|
|
|
@@ -109,7 +84,7 @@ const onUploadFile = async () => {
|
|
|
109
84
|
if (uploadFile.value.size > system.maxUploadSize) {
|
|
110
85
|
Dialog.create({
|
|
111
86
|
title: "Error",
|
|
112
|
-
message: "File size is too big. Max size is " +
|
|
87
|
+
message: "File size is too big. Max size is " + humanStorageSize(system.maxUploadSize),
|
|
113
88
|
color: "negative"
|
|
114
89
|
});
|
|
115
90
|
return;
|
|
@@ -162,7 +137,7 @@ const onUploadFile = async () => {
|
|
|
162
137
|
<l-card style="width:300px">
|
|
163
138
|
<q-card-section>
|
|
164
139
|
<q-file ref="file" v-model="uploadFile" name="file" :label="$t('File')" :accept="accept"
|
|
165
|
-
:hint="`Max upload size: ${
|
|
140
|
+
:hint="`Max upload size: ${humanStorageSize(system.maxUploadSize)}`"></q-file>
|
|
166
141
|
<!-- q-checkbox v-model="rename" :label="$t('Rename file if exists')"></q-checkbox-->
|
|
167
142
|
</q-card-section>
|
|
168
143
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { useLight } from "#imports";
|
|
3
|
-
import { ref, reactive, onMounted
|
|
3
|
+
import { ref, reactive, onMounted } from 'vue'
|
|
4
4
|
import { useQuasar, Notify, Dialog } from 'quasar';
|
|
5
5
|
import { useI18n } from 'vue-i18n';
|
|
6
6
|
import { m, notify } from '#imports';
|
|
@@ -170,13 +170,8 @@ onMounted(() => {
|
|
|
170
170
|
logo_alignment: 'left'
|
|
171
171
|
}
|
|
172
172
|
);
|
|
173
|
-
|
|
174
173
|
}
|
|
175
|
-
|
|
176
174
|
})
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
175
|
</script>
|
|
181
176
|
|
|
182
177
|
<template>
|
|
@@ -187,18 +182,29 @@ onMounted(() => {
|
|
|
187
182
|
<q-img src="https://raw.githubusercontent.com/HostLink/.github/master/profile/logo.webp" class="full-width" />
|
|
188
183
|
</template>
|
|
189
184
|
</q-img>
|
|
190
|
-
|
|
191
185
|
<div class="text-h6">
|
|
192
186
|
{{ light.getCompany() }}
|
|
193
187
|
</div>
|
|
194
188
|
<q-form ref="form1">
|
|
195
189
|
<div class="q-gutter-sm">
|
|
196
190
|
<l-input v-model.trim="data.username" label="Username" :rules="[v => !!v || $t('Username is required')]"
|
|
197
|
-
clearable :outlined="false" stackLabel autocomplete="username"
|
|
191
|
+
clearable :outlined="false" stackLabel autocomplete="username">
|
|
192
|
+
<template v-slot:prepend>
|
|
193
|
+
<q-icon name="sym_o_person" />
|
|
194
|
+
</template>
|
|
195
|
+
</l-input>
|
|
198
196
|
<l-input v-model="data.password" label="Password" type="password" clearable show-password stackLabel
|
|
199
197
|
:rules="[v => !!v || $t('Password is required')]" @keydown.enter.prevent="submit" :outlined="false"
|
|
200
|
-
autocomplete="current-password"
|
|
201
|
-
|
|
198
|
+
autocomplete="current-password">
|
|
199
|
+
<template v-slot:prepend>
|
|
200
|
+
<q-icon name="sym_o_lock" />
|
|
201
|
+
</template>
|
|
202
|
+
</l-input>
|
|
203
|
+
<l-input v-if="twoFactorAuthentication" v-model="data.code" label="2FA code"
|
|
204
|
+
:rules="[v => !!v || $t('2FA code is required')]" type="text" clearable stackLabel :outlined="false">
|
|
205
|
+
<template v-slot:prepend>
|
|
206
|
+
<q-icon name="sym_o_key" />
|
|
207
|
+
</template>
|
|
202
208
|
</l-input>
|
|
203
209
|
</div>
|
|
204
210
|
</q-form>
|
|
@@ -2,40 +2,14 @@
|
|
|
2
2
|
import { ref, computed } from 'vue'
|
|
3
3
|
import { useLight } from '#imports'
|
|
4
4
|
const props = defineProps(["value", "dense"])
|
|
5
|
-
const menus = ref(null);
|
|
6
5
|
const light = useLight();
|
|
7
6
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
menus.value.forEach((m) => {
|
|
12
|
-
if (m.value() !== menu) {
|
|
13
|
-
m.hide()
|
|
14
|
-
}
|
|
15
|
-
})
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
defineExpose({
|
|
19
|
-
hide: () => {
|
|
20
|
-
if (expansion.value) {
|
|
21
|
-
expansion.value.hide();
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
value: () => {
|
|
25
|
-
return props.value;
|
|
26
|
-
}
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
const isShowExpansionItem = computed(() => {
|
|
30
|
-
if (props.value.children) {
|
|
31
|
-
if (props.value.children.length > 0) {
|
|
32
|
-
return true;
|
|
33
|
-
|
|
34
|
-
}
|
|
7
|
+
const isShowExpansionItem = (menu) => {
|
|
8
|
+
if (menu.children && menu.children.length > 0) {
|
|
9
|
+
return true;
|
|
35
10
|
}
|
|
36
11
|
return false;
|
|
37
|
-
|
|
38
|
-
});
|
|
12
|
+
};
|
|
39
13
|
|
|
40
14
|
const color1 = computed(() => {
|
|
41
15
|
return light.getColorValue()
|
|
@@ -48,28 +22,36 @@ const color2 = computed(() => {
|
|
|
48
22
|
return c;
|
|
49
23
|
});
|
|
50
24
|
|
|
25
|
+
//generate a random group name
|
|
26
|
+
const group = Math.random().toString(36).substring(7);
|
|
51
27
|
</script>
|
|
52
28
|
|
|
53
29
|
<style scoped>
|
|
54
30
|
.menu-list .q-item{border-radius:12px 12px 12px 12px}.menu-list .q-router-link--exact-active{background:linear-gradient(118deg,v-bind(color1),v-bind(color2));color:#fff}
|
|
55
31
|
</style>
|
|
56
32
|
<template>
|
|
57
|
-
<q-
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
33
|
+
<q-list class="menu-list" :dense="dense">
|
|
34
|
+
<template v-for="menu in value">
|
|
35
|
+
|
|
36
|
+
<q-expansion-item :label="$t(menu.label)" :icon="menu.icon" :dense="dense" v-if="isShowExpansionItem(menu)"
|
|
37
|
+
:group="group">
|
|
38
|
+
<l-menu class="q-pl-md" :value="menu.children" :dense="dense"></l-menu>
|
|
39
|
+
</q-expansion-item>
|
|
40
|
+
|
|
41
|
+
<template v-else>
|
|
42
|
+
<q-separator v-if="menu.type == 'separator'" :spaced="menu.spaced" />
|
|
43
|
+
<q-item-label header v-if="menu.type == 'header'">{{ menu.label }}</q-item-label>
|
|
44
|
+
|
|
45
|
+
<q-item v-ripple :to="menu.to" v-if="!value.type">
|
|
46
|
+
<q-item-section avatar>
|
|
47
|
+
<q-icon :name="menu.icon" />
|
|
48
|
+
</q-item-section>
|
|
49
|
+
<q-item-section>
|
|
50
|
+
<q-item-label v-text="$t(menu.label)"></q-item-label>
|
|
51
|
+
</q-item-section>
|
|
52
|
+
</q-item>
|
|
53
|
+
</template>
|
|
54
|
+
|
|
55
|
+
</template>
|
|
74
56
|
</q-list>
|
|
75
57
|
</template>
|
|
@@ -375,7 +375,6 @@ const getFilterValue = () => {
|
|
|
375
375
|
|
|
376
376
|
|
|
377
377
|
const onFilters = () => {
|
|
378
|
-
console.log("onFilters",filters.value)
|
|
379
378
|
|
|
380
379
|
//clone the filters
|
|
381
380
|
onRequest({
|
|
@@ -493,7 +492,6 @@ const localSelected = computed({
|
|
|
493
492
|
return props.selected;
|
|
494
493
|
},
|
|
495
494
|
set(val) {
|
|
496
|
-
console.log(val)
|
|
497
495
|
emits("update:selected", val);
|
|
498
496
|
}
|
|
499
497
|
});
|
|
@@ -572,7 +570,8 @@ const localSelected = computed({
|
|
|
572
570
|
|
|
573
571
|
|
|
574
572
|
<template #top-right="props" v-if="fullscreen || searchable">
|
|
575
|
-
<q-input v-if="searchable" outlined dense debounce="300" v-model="filter" :placeholder="$t('Search')"
|
|
573
|
+
<q-input v-if="searchable" outlined dense debounce="300" v-model="filter" :placeholder="$t('Search')"
|
|
574
|
+
:color="$light.color">
|
|
576
575
|
<template v-slot:append>
|
|
577
576
|
<q-icon name="search" />
|
|
578
577
|
</template>
|
|
@@ -603,13 +602,13 @@ const localSelected = computed({
|
|
|
603
602
|
<template v-if="col.searchType == 'number'">
|
|
604
603
|
<q-input style="min-width: 80px;" dense clearable filled square
|
|
605
604
|
v-model.number="filters[col.name]" @keydown.enter.prevent="onFilters" @clear="onFilters"
|
|
606
|
-
mask="##########"></q-input>
|
|
605
|
+
mask="##########" enterkeyhint="search"></q-input>
|
|
607
606
|
</template>
|
|
608
607
|
|
|
609
608
|
<template v-if="col.searchType == 'select'">
|
|
610
609
|
<q-select dense clearable filled square v-model="filters[col.name]"
|
|
611
610
|
@update:model-value="onFilters" options-dense :options="col.searchOptions" emit-value
|
|
612
|
-
map-options :multiple="col.searchMultiple" />
|
|
611
|
+
map-options :multiple="col.searchMultiple" :color="$light.color" />
|
|
613
612
|
|
|
614
613
|
</template>
|
|
615
614
|
|
|
@@ -621,7 +620,8 @@ const localSelected = computed({
|
|
|
621
620
|
|
|
622
621
|
<template v-if="!col.searchType">
|
|
623
622
|
<q-input style="min-width: 80px;" dense clearable filled square v-model="filters[col.name]"
|
|
624
|
-
@keydown.enter.prevent="onFilters" @clear="onFilters" enterkeyhint="search"
|
|
623
|
+
@keydown.enter.prevent="onFilters" @clear="onFilters" enterkeyhint="search"
|
|
624
|
+
:color="$light.color"></q-input>
|
|
625
625
|
|
|
626
626
|
</template>
|
|
627
627
|
|
|
@@ -15,7 +15,7 @@ const value = computed({
|
|
|
15
15
|
|
|
16
16
|
</script>
|
|
17
17
|
<template>
|
|
18
|
-
<l-field
|
|
18
|
+
<l-field stack-label :label="context.label" :error="error" :error-message="errorMessage">
|
|
19
19
|
<q-option-group type="checkbox" :options="context.options" v-model="value" inline v-bind="context.attrs" :color="$light.color">
|
|
20
20
|
</q-option-group>
|
|
21
21
|
</l-field>
|
|
@@ -14,8 +14,6 @@ const value = computed({
|
|
|
14
14
|
set: (val) => props.context.node.input(val)
|
|
15
15
|
})
|
|
16
16
|
|
|
17
|
-
//console.log(props.context.node.props.parsedRules);
|
|
18
|
-
|
|
19
17
|
//check required in parsedRules
|
|
20
18
|
let required = false;
|
|
21
19
|
for (let rule of props.context.node.props.parsedRules ?? []) {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { reactive } from 'vue'
|
|
3
|
-
import { m } from '
|
|
4
|
-
|
|
3
|
+
import { m } from '#imports'
|
|
4
|
+
import { useI18n } from 'vue-i18n';
|
|
5
5
|
import { query } from '@hostlink/light';
|
|
6
|
+
const { t } = useI18n();
|
|
6
7
|
|
|
7
8
|
const { app, listRole: roles } = await query({
|
|
8
9
|
app: {
|
|
@@ -15,7 +16,7 @@ const { app, listRole: roles } = await query({
|
|
|
15
16
|
})
|
|
16
17
|
|
|
17
18
|
const columns = [{
|
|
18
|
-
label: "Permission",
|
|
19
|
+
label: t("Permission"),
|
|
19
20
|
field: "permission",
|
|
20
21
|
align: "left",
|
|
21
22
|
}];
|
|
@@ -77,13 +78,9 @@ const onUpdate = (value, role, permission) => {
|
|
|
77
78
|
</q-td>
|
|
78
79
|
<q-td v-for="role in roles">
|
|
79
80
|
<q-checkbox v-model="props.row[role.name]"
|
|
80
|
-
@update:model-value="onUpdate($event, role.name, props.row.permission)"
|
|
81
|
-
|
|
82
|
-
:color="$light.color"
|
|
83
|
-
/>
|
|
81
|
+
@update:model-value="onUpdate($event, role.name, props.row.permission)" :color="$light.color" />
|
|
84
82
|
</q-td>
|
|
85
83
|
</q-tr>
|
|
86
|
-
|
|
87
84
|
</template>
|
|
88
85
|
</q-table>
|
|
89
86
|
</l-page>
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { useQuasar } from 'quasar'
|
|
3
3
|
import { q, m, notify } from '#imports'
|
|
4
4
|
import { ref } from "vue"
|
|
5
|
-
|
|
5
|
+
import { useI18n } from 'vue-i18n';
|
|
6
|
+
const { t } = useI18n();
|
|
6
7
|
const qua = useQuasar();
|
|
7
8
|
|
|
8
9
|
const loadData = async () => {
|
|
@@ -40,7 +41,7 @@ const columns = [
|
|
|
40
41
|
{
|
|
41
42
|
name: "name",
|
|
42
43
|
field: "name",
|
|
43
|
-
label: "Name",
|
|
44
|
+
label: t("Name"),
|
|
44
45
|
align: "left",
|
|
45
46
|
sortable: true,
|
|
46
47
|
}, {
|
|
@@ -124,8 +125,8 @@ const onRemoveUser = async (value, user) => {
|
|
|
124
125
|
|
|
125
126
|
<template #body-cell-children="props">
|
|
126
127
|
<q-td>
|
|
127
|
-
<q-select :options="role_options" v-model="props.row.children" multiple use-chips
|
|
128
|
-
|
|
128
|
+
<q-select :options="role_options" v-model="props.row.children" multiple use-chips dense
|
|
129
|
+
@remove="onRemoveChild(props.row.name, $event)" @add="onAddChild(props.row.name, $event)">
|
|
129
130
|
|
|
130
131
|
</q-select>
|
|
131
132
|
</q-td>
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { q } from "#imports"
|
|
3
|
+
import { Loading } from "quasar";
|
|
3
4
|
const onClickDownload = async () => {
|
|
4
5
|
|
|
6
|
+
//show loading
|
|
7
|
+
Loading.show({
|
|
8
|
+
message: "Exporting database..."
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
|
|
5
12
|
const data = await q({
|
|
6
13
|
system: {
|
|
7
14
|
database: {
|
|
@@ -10,6 +17,9 @@ const onClickDownload = async () => {
|
|
|
10
17
|
}
|
|
11
18
|
});
|
|
12
19
|
|
|
20
|
+
//hide loading
|
|
21
|
+
Loading.hide();
|
|
22
|
+
|
|
13
23
|
const file = new File([data.system.database.export], "backup.sql", {
|
|
14
24
|
type: "text/plain;charset=utf-8"
|
|
15
25
|
});
|
|
@@ -24,6 +34,6 @@ const onClickDownload = async () => {
|
|
|
24
34
|
</script>
|
|
25
35
|
<template>
|
|
26
36
|
<l-page>
|
|
27
|
-
<
|
|
37
|
+
<l-btn label="Download" icon="sym_o_download" @click="onClickDownload"></l-btn>
|
|
28
38
|
</l-page>
|
|
29
39
|
</template>
|
|
@@ -14,12 +14,11 @@ const { system: { database } } = await query({
|
|
|
14
14
|
<template>
|
|
15
15
|
<l-page>
|
|
16
16
|
<q-card flat bordered>
|
|
17
|
-
<q-list
|
|
18
|
-
<q-expansion-item :label="table.name" v-for="table in database.table">
|
|
19
|
-
<div class="
|
|
20
|
-
<q-table
|
|
21
|
-
|
|
22
|
-
dense :rows="table.columns" :rows-per-page-options="[0]" hide-pagination flat bordered></q-table>
|
|
17
|
+
<q-list class="rounded-borders" separator bordered>
|
|
18
|
+
<q-expansion-item :label="table.name" v-for="table in database.table" dense>
|
|
19
|
+
<div class="q-ma-sm">
|
|
20
|
+
<q-table separator="cell" dense :rows="table.columns" :rows-per-page-options="[0]" hide-pagination
|
|
21
|
+
flat bordered></q-table>
|
|
23
22
|
</div>
|
|
24
23
|
|
|
25
24
|
</q-expansion-item>
|
|
@@ -50,17 +50,11 @@ const columns = [
|
|
|
50
50
|
field: row => row.type,
|
|
51
51
|
format: val => `${val}`,
|
|
52
52
|
sortable: true
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
required: true,
|
|
57
|
-
label: 'Location',
|
|
53
|
+
}, {
|
|
54
|
+
name: 'data',
|
|
55
|
+
label: 'Data',
|
|
58
56
|
align: 'left',
|
|
59
|
-
field: row => row.location,
|
|
60
|
-
format: val => `${val}`,
|
|
61
|
-
sortable: true
|
|
62
57
|
}
|
|
63
|
-
|
|
64
58
|
]
|
|
65
59
|
</script>
|
|
66
60
|
<template>
|
|
@@ -79,25 +73,33 @@ const columns = [
|
|
|
79
73
|
<FormKit type="l-input" label="Name" name="name" validation="required"></FormKit>
|
|
80
74
|
<FormKit type="l-select" label="Type" name="type" validation="required" :options="[
|
|
81
75
|
{ label: 'Local', value: 'local' },
|
|
82
|
-
{ label: 'S3', value: 'S3' }
|
|
76
|
+
{ label: 'S3', value: 'S3' },
|
|
77
|
+
{ label: 'HostLink storage', value: 'hostlink' }
|
|
83
78
|
]"></FormKit>
|
|
84
79
|
|
|
85
|
-
<
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
<
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
80
|
+
<FormKit type="group" name="data">
|
|
81
|
+
<template v-if="value.type == 'local'">
|
|
82
|
+
<FormKit type="l-input" label="Location" name="location" validation="required"></FormKit>
|
|
83
|
+
</template>
|
|
84
|
+
|
|
85
|
+
<template v-if="value.type == 'S3'">
|
|
86
|
+
<FormKit type="l-input" label="Bucket" name="bucket" validation="required"></FormKit>
|
|
87
|
+
<FormKit type="l-input" label="Access Key" name="accessKey" validation="required"></FormKit>
|
|
88
|
+
<FormKit type="l-input" label="Secret Key" name="secretKey" validation="required"></FormKit>
|
|
89
|
+
<FormKit type="l-input" label="Region" name="region" validation="required"></FormKit>
|
|
90
|
+
<FormKit type="l-input" label="Endpoint" name="endpoint" validation="required"></FormKit>
|
|
91
|
+
<FormKit type="l-input" label="Prefix" name="prefix"></FormKit>
|
|
92
|
+
<FormKit type="l-select" label="Visibility" name="visibility" validation="required" :options="[
|
|
93
|
+
{ label: 'Public', value: 'public' },
|
|
94
|
+
{ label: 'Private', value: 'private' }
|
|
95
|
+
]"></FormKit>
|
|
96
|
+
</template>
|
|
97
|
+
|
|
98
|
+
<template v-if="value.type == 'hostlink'">
|
|
99
|
+
<FormKit type="l-input" label="Endpoint" name="endpoint" validation="required"></FormKit>
|
|
100
|
+
<FormKit type="l-input" label="Token" name="token" validation="required"></FormKit>
|
|
101
|
+
</template>
|
|
102
|
+
</FormKit>
|
|
101
103
|
|
|
102
104
|
</FormKit>
|
|
103
105
|
</q-card>
|
|
@@ -114,6 +116,10 @@ const columns = [
|
|
|
114
116
|
|
|
115
117
|
</template>
|
|
116
118
|
|
|
119
|
+
<template #body-cell-data="props">
|
|
120
|
+
<q-td><pre>{{ props.row.data }}</pre></q-td>
|
|
121
|
+
</template>
|
|
122
|
+
|
|
117
123
|
</l-table>
|
|
118
124
|
</l-page>
|
|
119
125
|
</template>
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { ref, computed, inject } from 'vue';
|
|
3
3
|
import { m, q } from '#imports'
|
|
4
|
-
import { useQuasar } from 'quasar';
|
|
4
|
+
import { useQuasar, Dialog } from 'quasar';
|
|
5
|
+
import { useI18n } from 'vue-i18n'
|
|
5
6
|
|
|
7
|
+
const { t } = useI18n()
|
|
6
8
|
const quasar = useQuasar();
|
|
7
9
|
const appMenus = await q("appMenus", [])
|
|
8
10
|
|
|
@@ -38,14 +40,14 @@ const onReload = async () => {
|
|
|
38
40
|
|
|
39
41
|
const onAddChild = (node) => {
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
title: 'Add
|
|
43
|
-
message: 'Enter menu label',
|
|
43
|
+
Dialog.create({
|
|
44
|
+
title: t('Add child menu'),
|
|
45
|
+
message: t('Enter menu label'),
|
|
44
46
|
prompt: {
|
|
45
47
|
model: '',
|
|
46
48
|
},
|
|
47
49
|
cancel: true,
|
|
48
|
-
persistent: true
|
|
50
|
+
persistent: true
|
|
49
51
|
}).onOk((data) => {
|
|
50
52
|
if (data === '') return;
|
|
51
53
|
|
|
@@ -106,13 +106,13 @@ const tab = ref('general')
|
|
|
106
106
|
access_token_expire: obj.access_token_expire,
|
|
107
107
|
}" v-if="tab == 'security'" @submit="onSubmit">
|
|
108
108
|
<q-field label="Password policy" stack-label>
|
|
109
|
-
<FormKit type="
|
|
109
|
+
<FormKit type="l-checkbox" label="Upper Case" name="password_contains_uppercase" true-value="1"
|
|
110
110
|
false-value="0" />
|
|
111
|
-
<FormKit type="
|
|
111
|
+
<FormKit type="l-checkbox" label="Lower Case" name="password_contains_lowercase" true-value="1"
|
|
112
112
|
false-value="0" />
|
|
113
|
-
<FormKit type="
|
|
113
|
+
<FormKit type="l-checkbox" label="Number" name="password_contains_numeric" true-value="1"
|
|
114
114
|
false-value="0" />
|
|
115
|
-
<FormKit type="
|
|
115
|
+
<FormKit type="l-checkbox" label="Special Character" name="password_contains_symbol"
|
|
116
116
|
true-value="1" false-value="0" />
|
|
117
117
|
</q-field>
|
|
118
118
|
|
|
@@ -122,7 +122,7 @@ const tab = ref('general')
|
|
|
122
122
|
|
|
123
123
|
|
|
124
124
|
<q-field label="Two factor authentication" stack-label>
|
|
125
|
-
<FormKit type="
|
|
125
|
+
<FormKit type="l-checkbox" label="Enable" name="two_factor_authentication" true-value="1"
|
|
126
126
|
false-value="0" />
|
|
127
127
|
</q-field>
|
|
128
128
|
|
|
@@ -144,7 +144,7 @@ const tab = ref('general')
|
|
|
144
144
|
|
|
145
145
|
}" v-if="tab == 'Modules'" @submit="onSubmit">
|
|
146
146
|
<q-field label="File manager" stack-label>
|
|
147
|
-
<FormKit type="
|
|
147
|
+
<FormKit type="l-checkbox" label="Show" name="file_manager" true-value="1" false-value="0" />
|
|
148
148
|
</q-field>
|
|
149
149
|
</FormKit>
|
|
150
150
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { ref, reactive } from 'vue';
|
|
3
|
-
import { m, q } from '
|
|
3
|
+
import { m, q } from '#imports'
|
|
4
4
|
import { Notify } from 'quasar';
|
|
5
|
+
import { useI18n } from 'vue-i18n';
|
|
6
|
+
const { t } = useI18n();
|
|
5
7
|
const app = await q("app", ["languages"])
|
|
6
8
|
|
|
7
9
|
const splitterModel = ref(62)
|
|
@@ -44,7 +46,7 @@ const columns = [
|
|
|
44
46
|
name: "_delete"
|
|
45
47
|
},
|
|
46
48
|
{
|
|
47
|
-
label: "Name",
|
|
49
|
+
label: t("Name"),
|
|
48
50
|
name: "name",
|
|
49
51
|
field: "name",
|
|
50
52
|
align: "left",
|
|
@@ -98,8 +100,7 @@ const onDelete = async (name) => {
|
|
|
98
100
|
<l-card>
|
|
99
101
|
<q-splitter v-model="splitterModel" style="height:680px">
|
|
100
102
|
<template #before>
|
|
101
|
-
<q-table :rows="all" flat :rows-per-page-options="[0]" :columns="columns" dense
|
|
102
|
-
separator="cell">
|
|
103
|
+
<q-table :rows="all" flat :rows-per-page-options="[0]" :columns="columns" dense separator="cell">
|
|
103
104
|
<template #body="props">
|
|
104
105
|
<q-tr :props="props">
|
|
105
106
|
<q-td key="_delete" auto-width>
|
|
@@ -69,9 +69,10 @@ eventLogCols.forEach(col => {
|
|
|
69
69
|
|
|
70
70
|
<q-separator />
|
|
71
71
|
<l-list :bordered="false">
|
|
72
|
-
|
|
73
|
-
<q-item-label header>Details</q-item-label>
|
|
72
|
+
<q-item-label header>{{ $t('Details') }}</q-item-label>
|
|
74
73
|
<l-item label="Username">{{ my.username }}</l-item>
|
|
74
|
+
<l-item label="First name">{{ my.first_name }}</l-item>
|
|
75
|
+
<l-item label="Last name">{{ my.last_name }}</l-item>
|
|
75
76
|
<l-item label="Email">{{ my.email }}</l-item>
|
|
76
77
|
<l-item label="Phone">{{ my.phone }}</l-item>
|
|
77
78
|
<l-item label="Address">{{ my.addr1 }} {{ my.addr2 }} {{ my.addr3 }}</l-item>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { reactive, onMounted } from "vue"
|
|
2
|
+
import { reactive, onMounted, nextTick } from "vue"
|
|
3
3
|
import { Notify } from "quasar";
|
|
4
|
-
import { q, m } from '
|
|
4
|
+
import { q, m } from '#imports'
|
|
5
5
|
let { app, my } = await q({ app: ['googleClientId'], my: ["gmail"] })
|
|
6
6
|
|
|
7
7
|
my = reactive(my);
|
|
@@ -25,6 +25,7 @@ const handleGoogleCredentialResponse = async (response) => {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
onMounted(() => {
|
|
28
|
+
|
|
28
29
|
if (app.googleClientId) {
|
|
29
30
|
if (!window.google) {
|
|
30
31
|
Notify.create({
|
|
@@ -38,23 +39,30 @@ onMounted(() => {
|
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
42
|
+
//nextTick
|
|
43
|
+
|
|
44
|
+
nextTick(() => {
|
|
45
|
+
|
|
46
|
+
//google
|
|
47
|
+
google.accounts.id.initialize({
|
|
48
|
+
client_id: app.googleClientId,
|
|
49
|
+
callback: handleGoogleCredentialResponse,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
google.accounts.id.renderButton(
|
|
53
|
+
document.getElementById('g_id_signin'),
|
|
54
|
+
{
|
|
55
|
+
type: 'profile',
|
|
56
|
+
shape: 'pill',
|
|
57
|
+
theme: 'outline',
|
|
58
|
+
text: 'signin_with',
|
|
59
|
+
size: 'large',
|
|
60
|
+
logo_alignment: 'left'
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
})
|
|
65
|
+
|
|
58
66
|
|
|
59
67
|
});
|
|
60
68
|
|
|
@@ -79,7 +87,7 @@ const unlink = async () => {
|
|
|
79
87
|
<div>
|
|
80
88
|
<l-btn label="Unlink" @click="unlink" icon="sym_o_delete"></l-btn>
|
|
81
89
|
</div>
|
|
82
|
-
|
|
90
|
+
|
|
83
91
|
</q-card-section>
|
|
84
92
|
|
|
85
93
|
<q-card-section v-else>
|
package/dist/runtime/plugin.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Quasar, Dialog, Notify, Loading, AppFullscreen } from "quasar";
|
|
1
|
+
import { Quasar, Dialog, Notify, Loading, AppFullscreen, AddressbarColor } from "quasar";
|
|
2
2
|
import { createI18n } from "vue-i18n";
|
|
3
3
|
import { defineNuxtPlugin } from "#app";
|
|
4
4
|
import { useRouter } from "vue-router";
|
|
@@ -60,7 +60,8 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
60
60
|
Dialog,
|
|
61
61
|
Notify,
|
|
62
62
|
Loading,
|
|
63
|
-
AppFullscreen
|
|
63
|
+
AppFullscreen,
|
|
64
|
+
AddressbarColor
|
|
64
65
|
}
|
|
65
66
|
});
|
|
66
67
|
let locale = localStorage.getItem("locale") || "en";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hostlink/nuxt-light",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.2",
|
|
4
4
|
"description": "HostLink Nuxt Light Framework",
|
|
5
5
|
"repository": "@hostlink/nuxt-light",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
],
|
|
24
24
|
"scripts": {
|
|
25
25
|
"prepack": "nuxt-module-build build",
|
|
26
|
-
"dev": "nuxi dev playground",
|
|
26
|
+
"dev": "nuxi dev playground --host",
|
|
27
27
|
"dev:build": "nuxi build playground",
|
|
28
28
|
"dev:prepare": "nuxt-module-build build --stub && nuxi prepare playground",
|
|
29
29
|
"release:org": "npm run lint && npm run test && npm run prepack && changelogen --release && npm publish && git push --follow-tags",
|