@hostlink/nuxt-light 1.5.2 → 1.6.1
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-checkbox.vue +1 -1
- package/dist/runtime/components/l-customizer.vue +4 -5
- 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 +39 -33
- package/dist/runtime/components/l-file-upload.vue +4 -29
- package/dist/runtime/components/l-select.vue +2 -0
- package/dist/runtime/components/l-table.vue +4 -3
- package/dist/runtime/formkit/OptionGroup.vue +1 -1
- 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/setting.vue +6 -6
- package/dist/runtime/pages/Translate/index.vue +5 -4
- package/dist/runtime/pages/User/profile.vue +3 -2
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -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
|
|
|
@@ -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
|
}
|
|
@@ -198,6 +204,7 @@ const breadcrumbs = computed(() => {
|
|
|
198
204
|
});
|
|
199
205
|
|
|
200
206
|
const onClickRow = (evt, row, index) => {
|
|
207
|
+
console.log("click row", row)
|
|
201
208
|
if (row.type == "folder") {
|
|
202
209
|
preview.value = null;
|
|
203
210
|
return;
|
|
@@ -484,7 +491,7 @@ const isDark = computed(() => light.isDarkMode());
|
|
|
484
491
|
|
|
485
492
|
<div>
|
|
486
493
|
<q-input outlined :placeholder="$t('Search for file name')" dense @keyup.enter.native="submitSearch"
|
|
487
|
-
v-model="search">
|
|
494
|
+
v-model="search" :color="$light.color">
|
|
488
495
|
<template v-slot:append>
|
|
489
496
|
<q-btn flat dense round icon="sym_o_search"></q-btn>
|
|
490
497
|
</template>
|
|
@@ -492,7 +499,7 @@ const isDark = computed(() => light.isDarkMode());
|
|
|
492
499
|
</div>
|
|
493
500
|
|
|
494
501
|
<q-space></q-space>
|
|
495
|
-
<q-btn v-if="
|
|
502
|
+
<q-btn v-if="closeable" icon="close" flat round @click="$emit('close')"></q-btn>
|
|
496
503
|
</q-toolbar>
|
|
497
504
|
</q-header>
|
|
498
505
|
|
|
@@ -524,7 +531,7 @@ const isDark = computed(() => light.isDarkMode());
|
|
|
524
531
|
</q-item-section>
|
|
525
532
|
</q-item>
|
|
526
533
|
|
|
527
|
-
<q-list>
|
|
534
|
+
<q-list dense>
|
|
528
535
|
<q-item>
|
|
529
536
|
<q-item-section avatar>
|
|
530
537
|
<q-icon name="sym_o_storage" />
|
|
@@ -534,13 +541,9 @@ const isDark = computed(() => light.isDarkMode());
|
|
|
534
541
|
<q-btn dense round flat icon="sym_o_refresh" @click="reloadStorage"></q-btn>
|
|
535
542
|
</q-item-section>
|
|
536
543
|
</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>
|
|
544
|
+
<q-tree ref="folderTree" class="q-pl-md" :nodes="folders" node-key="path" label-key="name"
|
|
545
|
+
@lazy-load="onLazyLoad" v-model:selected="path" no-selection-unset>
|
|
546
|
+
</q-tree>
|
|
544
547
|
</q-list>
|
|
545
548
|
<!-- q-expansion-item :label="$t('Storage')" default-expand-all default-opened icon="sym_o_storage" selectable
|
|
546
549
|
@click="path = '/'">
|
|
@@ -557,7 +560,6 @@ const isDark = computed(() => light.isDarkMode());
|
|
|
557
560
|
</q-drawer>
|
|
558
561
|
|
|
559
562
|
<q-drawer side="right" show-if-above bordered>
|
|
560
|
-
|
|
561
563
|
<l-file-manager-preview v-model="preview" v-if="preview" :key="preview.path" />
|
|
562
564
|
</q-drawer>
|
|
563
565
|
|
|
@@ -566,19 +568,18 @@ const isDark = computed(() => light.isDarkMode());
|
|
|
566
568
|
<q-dialog v-model="showUploadFiles" persistent transition-show="scale" transition-hide="scale">
|
|
567
569
|
<l-card style="width:300px">
|
|
568
570
|
<q-card-section>
|
|
569
|
-
|
|
570
|
-
<q-file ref="file" v-model="uploadFiles" multiple name="file" label="Files"></q-file>
|
|
571
|
+
<q-file ref="file" v-model="uploadFiles" multiple name="file" label="Files" :color="light.color"></q-file>
|
|
571
572
|
</q-card-section>
|
|
572
573
|
|
|
573
574
|
<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>
|
|
575
|
+
<q-btn flat :label="$t('Cancel')" :color="light.color" v-close-popup></q-btn>
|
|
576
|
+
<q-btn flat :label="$t('Upload')" :color="light.color" @click="onUploadFiles"></q-btn>
|
|
576
577
|
</q-card-actions>
|
|
577
578
|
</l-card>
|
|
578
579
|
</q-dialog>
|
|
579
580
|
|
|
580
581
|
<q-toolbar>
|
|
581
|
-
<q-breadcrumbs>
|
|
582
|
+
<q-breadcrumbs :active-color="$light.color">
|
|
582
583
|
<q-breadcrumbs-el v-for="(b, index) in breadcrumbs" :label="b.label" :key="index" @click="path = b.path"
|
|
583
584
|
href="javascript:void(0)"></q-breadcrumbs-el>
|
|
584
585
|
</q-breadcrumbs>
|
|
@@ -605,10 +606,11 @@ const isDark = computed(() => light.isDarkMode());
|
|
|
605
606
|
</q-toolbar>
|
|
606
607
|
|
|
607
608
|
<template v-if="grid">
|
|
608
|
-
<q-table title="Folders" flat
|
|
609
|
+
<q-table :title="$t('Folders')" flat grid :columns="columns" :rows="foldersGrid" hide-pagination
|
|
610
|
+
:pagination="{ rowsPerPage: 0 }">
|
|
609
611
|
<template v-slot:item="props">
|
|
610
|
-
<div class="q-pa-xs col-xs-12 col-sm-6 col-md-4">
|
|
611
|
-
<q-card>
|
|
612
|
+
<div class="q-pa-xs col-xs-12 col-sm-6 col-md-4" @click="onDblclickRow(null, props.row, null)">
|
|
613
|
+
<q-card flat bordered>
|
|
612
614
|
<q-item>
|
|
613
615
|
<q-item-section avatar>
|
|
614
616
|
<q-icon name="sym_o_folder" size="sm"></q-icon>
|
|
@@ -617,7 +619,7 @@ const isDark = computed(() => light.isDarkMode());
|
|
|
617
619
|
{{ props.row.name }}
|
|
618
620
|
</q-item-section>
|
|
619
621
|
<q-item-section avatar>
|
|
620
|
-
<q-checkbox v-model="selected" :val="props.row" />
|
|
622
|
+
<q-checkbox v-model="selected" :val="props.row" :color="$light.color" />
|
|
621
623
|
</q-item-section>
|
|
622
624
|
</q-item>
|
|
623
625
|
</q-card>
|
|
@@ -625,22 +627,26 @@ const isDark = computed(() => light.isDarkMode());
|
|
|
625
627
|
</template>
|
|
626
628
|
</q-table>
|
|
627
629
|
|
|
628
|
-
<q-table title="Files" flat
|
|
630
|
+
<q-table :title="$t('Files')" flat grid :columns="columns" :rows="filesGrid" hide-pagination
|
|
631
|
+
:pagination="{ rowsPerPage: 0 }">
|
|
629
632
|
|
|
630
633
|
<template v-slot:item="props">
|
|
631
|
-
<div class="q-pa-xs col-xs-12 col-sm-6 col-md-4">
|
|
632
|
-
<q-card>
|
|
634
|
+
<div class="q-pa-xs col-xs-12 col-sm-6 col-md-4" @click="onClickRow(null, props.row, null)">
|
|
635
|
+
<q-card flat bordered>
|
|
633
636
|
<q-item>
|
|
634
637
|
<q-item-section avatar>
|
|
635
638
|
<q-icon name="sym_o_description" size="sm"></q-icon>
|
|
636
639
|
</q-item-section>
|
|
637
|
-
<q-item-section>
|
|
640
|
+
<q-item-section no-wrap>
|
|
638
641
|
{{ props.row.name }}
|
|
639
642
|
</q-item-section>
|
|
640
643
|
<q-item-section avatar>
|
|
641
|
-
<q-checkbox v-model="selected" :val="props.row" />
|
|
644
|
+
<q-checkbox v-model="selected" :val="props.row" :color="$light.color" />
|
|
642
645
|
</q-item-section>
|
|
643
646
|
</q-item>
|
|
647
|
+
|
|
648
|
+
<q-img v-if="props.row.canPreview" :src="props.row.imagePath"></q-img>
|
|
649
|
+
|
|
644
650
|
</q-card>
|
|
645
651
|
</div>
|
|
646
652
|
</template>
|
|
@@ -648,7 +654,7 @@ const isDark = computed(() => light.isDarkMode());
|
|
|
648
654
|
</template>
|
|
649
655
|
<template v-else>
|
|
650
656
|
|
|
651
|
-
<q-table flat
|
|
657
|
+
<q-table flat :columns="columns" :rows="items" @row-dblclick="onDblclickRow" @row-click="onClickRow"
|
|
652
658
|
:pagination="pagination" row-key="path" selection="multiple" v-model:selected="selected" dense
|
|
653
659
|
:loading="loading" :loading-label="$t('Loading...')" :no-data-label="$t('No data available')">
|
|
654
660
|
<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
|
|
|
@@ -375,8 +375,8 @@ const getFilterValue = () => {
|
|
|
375
375
|
|
|
376
376
|
|
|
377
377
|
const onFilters = () => {
|
|
378
|
-
console.log("onFilters",filters.value)
|
|
379
|
-
|
|
378
|
+
console.log("onFilters", filters.value)
|
|
379
|
+
|
|
380
380
|
//clone the filters
|
|
381
381
|
onRequest({
|
|
382
382
|
pagination: {
|
|
@@ -572,7 +572,8 @@ const localSelected = computed({
|
|
|
572
572
|
|
|
573
573
|
|
|
574
574
|
<template #top-right="props" v-if="fullscreen || searchable">
|
|
575
|
-
<q-input v-if="searchable" outlined dense debounce="300" v-model="filter" :placeholder="$t('Search')"
|
|
575
|
+
<q-input v-if="searchable" outlined dense debounce="300" v-model="filter" :placeholder="$t('Search')"
|
|
576
|
+
:color="$light.color">
|
|
576
577
|
<template v-slot:append>
|
|
577
578
|
<q-icon name="search" />
|
|
578
579
|
</template>
|
|
@@ -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>
|
|
@@ -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>
|
|
@@ -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>
|