@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.
Files changed (29) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/l-app-main.vue +3 -1
  3. package/dist/runtime/components/l-checkbox.vue +1 -1
  4. package/dist/runtime/components/l-customizer.vue +4 -5
  5. package/dist/runtime/components/l-fav-menu.vue +3 -1
  6. package/dist/runtime/components/l-file-manager-labels.vue +37 -36
  7. package/dist/runtime/components/l-file-manager-preview.vue +20 -3
  8. package/dist/runtime/components/l-file-manager.vue +38 -33
  9. package/dist/runtime/components/l-file-upload.vue +4 -29
  10. package/dist/runtime/components/l-login.vue +16 -10
  11. package/dist/runtime/components/l-menu.vue +29 -47
  12. package/dist/runtime/components/l-select.vue +2 -0
  13. package/dist/runtime/components/l-table.vue +6 -6
  14. package/dist/runtime/formkit/OptionGroup.vue +1 -1
  15. package/dist/runtime/formkit/Select.vue +0 -2
  16. package/dist/runtime/locales/zh-hk.json +4 -0
  17. package/dist/runtime/pages/Permission/all.vue +5 -8
  18. package/dist/runtime/pages/Role/index.vue +5 -4
  19. package/dist/runtime/pages/System/database/backup.vue +11 -1
  20. package/dist/runtime/pages/System/database/table.vue +5 -6
  21. package/dist/runtime/pages/System/fs.vue +32 -26
  22. package/dist/runtime/pages/System/menu/index.vue +7 -5
  23. package/dist/runtime/pages/System/setting.vue +6 -6
  24. package/dist/runtime/pages/Translate/index.vue +5 -4
  25. package/dist/runtime/pages/User/add.vue +0 -2
  26. package/dist/runtime/pages/User/profile.vue +3 -2
  27. package/dist/runtime/pages/User/setting/open_id.vue +28 -20
  28. package/dist/runtime/plugin.mjs +3 -2
  29. package/package.json +2 -2
package/dist/module.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "light",
3
3
  "configKey": "light",
4
- "version": "1.5.2"
4
+ "version": "1.6.2"
5
5
  }
@@ -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 v-for=" menu in menus " :value="menu" :dense="style.dense" />
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 { computed, ref } from 'vue'
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-slider v-model="menuWidth" :min="200" :max="360" label :color="$light.color" />
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" ref="expansion" icon="sym_o_favorite">
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
- const props = defineProps({
4
- modelValue: {
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
- <q-item clickable :active="modelValue == 'document'" @click="toggleLabel('document')">
26
- <q-item-section avatar>
27
- <q-icon name="sym_o_article" />
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="sym_o_text_snippet" />
52
+ <q-icon :name="label.icon" />
52
53
  </q-item-section>
53
- <q-item-section> {{ $t('Archives') }} </q-item-section>
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>{{ modelValue.name }} </q-item-section>
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>{{ modelValue.path }} </q-item-section>
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
- closable: Boolean,
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: "last_modified_human",
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: "left",
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: "/" + ps.join("/"),
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="closable" icon="close" flat round @click="$emit('close')"></q-btn>
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-item>
538
- <q-item-section>
539
- <q-tree ref="folderTree" class="q-pl-md" :nodes="folders" node-key="path" label-key="name"
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 bordered grid :columns="columns" :rows="foldersGrid">
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 bordered grid :columns="columns" :rows="filesGrid" :pagination="pagination">
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 bordered :columns="columns" :rows="items" @row-dblclick="onDblclickRow" @row-click="onClickRow"
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 " + humanFileSize(system.maxUploadSize, false, 0),
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: ${humanFileSize(system.maxUploadSize, false, 0)}`"></q-file>
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, inject } from 'vue'
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
- <l-input v-if="twoFactorAuthentication" v-model="data.code" label="2FA code" required type="text" clearable>
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 expansion = ref(null);
9
-
10
- const onShowChild = (menu) => {
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-expansion-item v-if="isShowExpansionItem" :label="$t(value.label)" :icon="value.icon" :dense="dense" ref="expansion">
58
- <q-list class="q-pl-md">
59
- <l-menu :value="menu" v-for="menu in value.children" :dense="dense" @show="onShowChild(menu)"
60
- ref="menus"></l-menu>
61
- </q-list>
62
- </q-expansion-item>
63
- <q-list v-else class="menu-list" :dense="dense">
64
- <q-separator v-if="value.type == 'separator'" :spaced="value.spaced" />
65
- <q-item-label header v-if="value.type == 'header'">{{ value.label }}</q-item-label>
66
- <q-item v-ripple :to="value.to" v-if="!value.type">
67
- <q-item-section avatar>
68
- <q-icon :name="value.icon" />
69
- </q-item-section>
70
- <q-item-section>
71
- <q-item-label v-text="$t(value.label)"></q-item-label>
72
- </q-item-section>
73
- </q-item>
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>
@@ -118,6 +118,8 @@ const attrs = computed(() => {
118
118
  a.label = t(props.label);
119
119
  }
120
120
 
121
+ if (props.color === undefined) a.color = light.color
122
+
121
123
  return a;
122
124
 
123
125
  });
@@ -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"></q-input>
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 label="Roles" stack-label :label="context.label" :error="error" :error-message="errorMessage">
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,4 +1,8 @@
1
1
  {
2
+ "Menu width": "選單寬度",
3
+ "File system": "檔案系統",
4
+ "Folders": "資料夾",
5
+ "Files": "檔案",
2
6
  "Server time": "伺服器時間",
3
7
  "Developer": "開發者",
4
8
  "Modules": "模組",
@@ -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
- dense @remove="onRemoveChild(props.row.name, $event)" @add="onAddChild(props.row.name, $event)">
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
- <q-btn color="primary" label="Download" icon="sym_o_download" @click="onClickDownload"></q-btn>
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 bordered class="rounded-borders" separator>
18
- <q-expansion-item :label="table.name" v-for="table in database.table">
19
- <div class=" q-ma-sm">
20
- <q-table
21
- separator="cell"
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
- name: 'location',
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
- <template v-if="value.type == 'local'">
86
- <FormKit type="l-input" label="Location" name="location" validation="required"></FormKit>
87
- </template>
88
-
89
- <template v-if="value.type == 'S3'">
90
- <FormKit type="l-input" label="Bucket" name="bucket" validation="required"></FormKit>
91
- <FormKit type="l-input" label="Access Key" name="accessKey" validation="required"></FormKit>
92
- <FormKit type="l-input" label="Secret Key" name="secretKey" validation="required"></FormKit>
93
- <FormKit type="l-input" label="Region" name="region" validation="required"></FormKit>
94
- <FormKit type="l-input" label="Endpoint" name="endpoint" validation="required"></FormKit>
95
- <FormKit type="l-input" label="Prefix" name="prefix"></FormKit>
96
- <FormKit type="l-select" label="Visibility" name="visibility" validation="required" :options="[
97
- { label: 'Public', value: 'public' },
98
- { label: 'Private', value: 'private' }
99
- ]"></FormKit>
100
- </template>
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
- quasar.dialog({
42
- title: 'Add Child Menu',
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="q-checkbox" label="Upper Case" name="password_contains_uppercase" true-value="1"
109
+ <FormKit type="l-checkbox" label="Upper Case" name="password_contains_uppercase" true-value="1"
110
110
  false-value="0" />
111
- <FormKit type="q-checkbox" label="Lower Case" name="password_contains_lowercase" true-value="1"
111
+ <FormKit type="l-checkbox" label="Lower Case" name="password_contains_lowercase" true-value="1"
112
112
  false-value="0" />
113
- <FormKit type="q-checkbox" label="Number" name="password_contains_numeric" true-value="1"
113
+ <FormKit type="l-checkbox" label="Number" name="password_contains_numeric" true-value="1"
114
114
  false-value="0" />
115
- <FormKit type="q-checkbox" label="Special Character" name="password_contains_symbol"
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="q-checkbox" label="Enable" name="two_factor_authentication" true-value="1"
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="q-checkbox" label="Show" name="file_manager" true-value="1" false-value="0" />
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>
@@ -1,8 +1,6 @@
1
1
  <script setup>
2
2
  import { reactive } from "vue"
3
- import { useRouter } from "vue-router";
4
3
  import { q } from '#imports'
5
- const router = useRouter()
6
4
  const obj = reactive({
7
5
  username: null,
8
6
  password: null,
@@ -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
- //google
42
- google.accounts.id.initialize({
43
- client_id: app.googleClientId,
44
- callback: handleGoogleCredentialResponse,
45
- });
46
-
47
- google.accounts.id.renderButton(
48
- document.getElementById('g_id_signin'),
49
- {
50
- type: 'profile',
51
- shape: 'pill',
52
- theme: 'outline',
53
- text: 'signin_with',
54
- size: 'large',
55
- logo_alignment: 'left'
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>
@@ -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.5.2",
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",