@hostlink/nuxt-light 1.30.0 → 1.31.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "light",
3
3
  "configKey": "light",
4
- "version": "1.30.0",
4
+ "version": "1.31.1",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.1",
7
7
  "unbuild": "3.5.0"
@@ -175,6 +175,7 @@ requestAnimationFrame(refreshServerTime);
175
175
  <q-item-label>{{ server_date }} {{ server_time }} </q-item-label>
176
176
  </q-item-section>
177
177
  </q-item>
178
+
178
179
 
179
180
  </q-list>
180
181
  </template>
@@ -60,8 +60,8 @@ const props = defineProps({
60
60
  onBlur: { type: Function, required: false },
61
61
  onClear: { type: Function, required: false }
62
62
  });
63
- const modelValue = props.modelValue;
64
- const new_rules = props.rules || [];
63
+ const modelValue = defineModel();
64
+ const new_rules = Array.isArray(props.rules) ? [...props.rules] : [];
65
65
  if (props.required) {
66
66
  new_rules.push((val) => !!val || t("input_required", [t(props.label ?? "")]));
67
67
  }
@@ -72,28 +72,29 @@ if (props.type == "email") {
72
72
  }
73
73
  });
74
74
  }
75
- if (new_rules.indexOf("containUpper") >= 0) {
75
+ const stringRules = new_rules.filter((r) => typeof r === "string");
76
+ if (stringRules.includes("containUpper")) {
76
77
  new_rules.push((val) => {
77
78
  if (val && !val.match(/[A-Z]/)) {
78
79
  return t("Must contain at least one uppercase letter");
79
80
  }
80
81
  });
81
82
  }
82
- if (new_rules.indexOf("containLower") >= 0) {
83
+ if (stringRules.includes("containLower")) {
83
84
  new_rules.push((val) => {
84
85
  if (val && !val.match(/[a-z]/)) {
85
86
  return t("Must contain at least one lowercase letter");
86
87
  }
87
88
  });
88
89
  }
89
- if (new_rules.indexOf("containNumber") >= 0) {
90
+ if (stringRules.includes("containNumber")) {
90
91
  new_rules.push((val) => {
91
92
  if (val && !val.match(/[0-9]/)) {
92
93
  return t("Must contain at least one number");
93
94
  }
94
95
  });
95
96
  }
96
- if (new_rules.indexOf("containSpecial") >= 0) {
97
+ if (stringRules.includes("containSpecial")) {
97
98
  new_rules.push((val) => {
98
99
  if (val && !val.match(/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/)) {
99
100
  return t("Must contain at least one symbol");
@@ -154,10 +155,9 @@ const onClickTc2Sc = () => {
154
155
  </template>
155
156
 
156
157
  <template v-if="localShowPassword" v-slot:append>
157
- <q-icon name="sym_o_visibility" class="cursor-pointer" :color="showPassword ? 'primary' : 'grey-5'"
158
- @click="isShowPassword = false" v-if="isShowPassword" />
159
- <q-icon name="sym_o_visibility_off" class="cursor-pointer" :color="showPassword ? 'grey-5' : 'primary'"
160
- @click="isShowPassword = true" v-else />
158
+ <q-icon name="sym_o_visibility" class="cursor-pointer" @click="isShowPassword = false"
159
+ v-if="isShowPassword" />
160
+ <q-icon name="sym_o_visibility_off" class="cursor-pointer" @click="isShowPassword = true" v-else />
161
161
  </template>
162
162
 
163
163
  </q-input>
@@ -4,23 +4,31 @@ export interface LInputProps extends QInputProps {
4
4
  translate?: boolean;
5
5
  required?: boolean;
6
6
  }
7
+ type __VLS_Props = LInputProps;
7
8
  declare const new_rules: import("quasar").ValidationRule[];
8
9
  declare const isShowPassword: import("vue").Ref<boolean, boolean>;
9
10
  declare const localType: import("vue").ComputedRef<"number" | "textarea" | "time" | "text" | "search" | "date" | "url" | "email" | "file" | "datetime-local" | "password" | "tel" | undefined>;
10
11
  declare const localShowPassword: import("vue").ComputedRef<boolean>;
11
12
  declare const onClickTc2Sc: () => void;
13
+ type __VLS_PublicProps = __VLS_Props & {
14
+ modelValue?: any;
15
+ };
12
16
  declare const __VLS_ctx: InstanceType<__VLS_PickNotAny<typeof __VLS_self, new () => {}>>;
13
17
  declare var __VLS_31: string | number, __VLS_32: any;
14
18
  type __VLS_Slots = __VLS_PrettifyGlobal<__VLS_OmitStringIndex<typeof __VLS_ctx.$slots> & {
15
19
  [K in NonNullable<typeof __VLS_31>]?: (props: typeof __VLS_32) => any;
16
20
  }>;
17
- declare const __VLS_self: import("vue").DefineComponent<LInputProps, {
21
+ declare const __VLS_self: import("vue").DefineComponent<__VLS_PublicProps, {
18
22
  new_rules: typeof new_rules;
19
23
  isShowPassword: typeof isShowPassword;
20
24
  localType: typeof localType;
21
25
  localShowPassword: typeof localShowPassword;
22
26
  onClickTc2Sc: typeof onClickTc2Sc;
23
- }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<LInputProps> & Readonly<{}>, {
27
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
28
+ "update:modelValue": (value: any) => any;
29
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
30
+ "onUpdate:modelValue"?: ((value: any) => any) | undefined;
31
+ }>, {
24
32
  dense: boolean;
25
33
  dark: boolean | null;
26
34
  rounded: boolean;
@@ -31,7 +39,11 @@ declare const __VLS_self: import("vue").DefineComponent<LInputProps, {
31
39
  standout: string | boolean;
32
40
  stackLabel: boolean;
33
41
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
34
- declare const __VLS_component: import("vue").DefineComponent<LInputProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<LInputProps> & Readonly<{}>, {
42
+ declare const __VLS_component: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
43
+ "update:modelValue": (value: any) => any;
44
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
45
+ "onUpdate:modelValue"?: ((value: any) => any) | undefined;
46
+ }>, {
35
47
  dense: boolean;
36
48
  dark: boolean | null;
37
49
  rounded: boolean;
@@ -9,8 +9,6 @@ import { collect } from "#imports";
9
9
  import { useI18n } from "vue-i18n";
10
10
  const $q = useQuasar();
11
11
  const { t } = useI18n();
12
- const minimized = defineModel("minimized", { type: Boolean, ...{ default: false } });
13
- const maximized = defineModel("maximized", { type: Boolean, ...{ default: false } });
14
12
  const errors = ref([]);
15
13
  const props = defineProps({
16
14
  fullscreen: { type: Boolean, required: false, skipCheck: true, default: false },
@@ -95,9 +93,7 @@ const props = defineProps({
95
93
  actions: { type: Array, required: false, default: () => [] },
96
94
  sortBy: { type: null, required: false },
97
95
  modelName: { type: null, required: false },
98
- searchable: { type: null, required: false, default: false },
99
- maximizable: { type: Boolean, required: false, default: false },
100
- minimizable: { type: Boolean, required: false, default: false },
96
+ searchable: { type: Boolean, required: false, default: false },
101
97
  onRequestData: { type: Function, required: false },
102
98
  addComponent: { type: Object, required: false },
103
99
  addComponentProps: { type: null, required: false }
@@ -240,12 +236,22 @@ const onLocalRequest = async (p) => {
240
236
  pagination.value.descending = p.pagination.descending;
241
237
  pagination.value.rowsPerPage = p.pagination.rowsPerPage;
242
238
  loading.value = false;
239
+ if (modelName.value) {
240
+ localStorage.setItem(`l-table-rowsPerPage-${modelName.value}`, String(pagination.value.rowsPerPage));
241
+ }
243
242
  validateData();
244
243
  },
245
244
  loadObjects(model2, filters2 = null, fields = []) {
246
245
  return this.loadData(model2, filters2, fields);
247
246
  },
248
247
  async loadData(model2, filters2 = null, fields = []) {
248
+ const saved = Number(localStorage.getItem(`l-table-rowsPerPage-${model2}`));
249
+ if (saved && props.rowsPerPageOptions.includes(saved) && saved !== pagination.value.rowsPerPage) {
250
+ pagination.value.rowsPerPage = saved;
251
+ if (typeof p !== "undefined" && p.pagination) {
252
+ p.pagination.rowsPerPage = saved;
253
+ }
254
+ }
249
255
  fields.forEach((f) => {
250
256
  builder.add(f);
251
257
  });
@@ -443,16 +449,21 @@ const onAdd = () => {
443
449
  </script>
444
450
 
445
451
  <template>
446
- <l-card v-bind="attrs" :maximizable="maximizable" :minimizable="minimizable" :title="title"
447
- v-model:minimized="minimized" v-model:maximized="maximized">
448
-
449
- <q-toolbar v-if="addComponent">
450
- <q-btn icon="sym_o_add" :label="$t('Add')" :color="$light.color" @click="onAdd" v-if="addComponent"
451
- outline />
452
- </q-toolbar>
453
-
452
+ <q-table v-bind="attrs" :loading="loading" :rows="rows" ref="table" @request="onLocalRequest" :columns="columns"
453
+ v-model:pagination="pagination" :filter="filter" v-model:selected="localSelected" :color="$light.color">
454
+
455
+ <template #header="props">
456
+ <q-tr :props="props">
457
+ <q-td v-if="selection != 'none'" auto-width>
458
+ <q-checkbox v-model="props.selected" />
459
+ </q-td>
460
+ <q-th v-if="hasRowExpand" auto-width></q-th>
461
+ <q-th v-if="hasActions" auto-width></q-th>
462
+ <q-th v-for="col in props.cols" :key="col.name" :props="props">{{ col.label }}</q-th>
463
+ </q-tr>
464
+ </template>
454
465
 
455
- <template v-if="errors.length > 0">
466
+ <template #top v-if="errors.length > 0">
456
467
  <div class="q-mb-sm">
457
468
  <div class="q-gutter-sm">
458
469
  <l-alert type="negative" v-for="e in errors">{{ e }}</l-alert>
@@ -460,96 +471,86 @@ const onAdd = () => {
460
471
  </div>
461
472
  </template>
462
473
 
463
- <q-table v-bind="attrs" :loading="loading" :rows="rows" ref="table" @request="onLocalRequest" :columns="columns"
464
- v-model:pagination="pagination" :filter="filter" v-model:selected="localSelected" :color="$light.color">
465
-
466
- <template #header="props">
467
- <q-tr :props="props">
468
- <q-td v-if="selection != 'none'" auto-width>
469
- <q-checkbox v-model="props.selected" />
470
- </q-td>
471
- <q-th v-if="hasRowExpand" auto-width></q-th>
472
- <q-th v-if="hasActions" auto-width></q-th>
473
- <q-th v-for="col in props.cols" :key="col.name" :props="props">{{ col.label }}</q-th>
474
- </q-tr>
475
- </template>
476
474
 
475
+ <template #top-left v-if="addComponent">
476
+ <q-btn icon="sym_o_add" :label="$t('Add')" :color="$light.color" @click="onAdd" outline />
477
+ </template>
477
478
 
478
479
 
479
- <template #body="props">
480
- <q-tr :props="props">
481
- <q-td v-if="selection != 'none'" auto-width>
482
- <q-checkbox v-model="props.selected" />
483
- </q-td>
484
- <q-td v-if="hasRowExpand" auto-width>
485
- <q-btn :class="{ 'text-grey-8': !isDark }" flat round size="sm"
486
- :icon="props.expand ? 'sym_o_expand_more' : 'sym_o_expand_less'"
487
- @click="props.expand = !props.expand"></q-btn>
488
- </q-td>
480
+ <template #body="props">
481
+ <q-tr :props="props">
482
+ <q-td v-if="selection != 'none'" auto-width>
483
+ <q-checkbox v-model="props.selected" />
484
+ </q-td>
485
+ <q-td v-if="hasRowExpand" auto-width>
486
+ <q-btn :class="{ 'text-grey-8': !isDark }" flat round size="sm"
487
+ :icon="props.expand ? 'sym_o_expand_more' : 'sym_o_expand_less'"
488
+ @click="props.expand = !props.expand"></q-btn>
489
+ </q-td>
489
490
 
490
- <q-td v-if="hasActions" auto-width>
491
- <div :class="{ 'text-grey-8': !isDark }" v-if="primaryKey">
491
+ <q-td v-if="hasActions" auto-width>
492
+ <div :class="{ 'text-grey-8': !isDark }" v-if="primaryKey">
492
493
 
493
- <l-view-btn v-if="actionView && props.row.canView"
494
- :to="`/${modelName}/${props.row[primaryKey]}/view`" size="sm" />
494
+ <l-view-btn v-if="actionView && props.row.canView"
495
+ :to="`/${modelName}/${props.row[primaryKey]}/view`" size="sm" />
495
496
 
496
- <l-edit-btn v-if="activeEdit && props.row.canUpdate"
497
- :to="`/${modelName}/${props.row[primaryKey]}/edit`" size="sm" />
497
+ <l-edit-btn v-if="activeEdit && props.row.canUpdate"
498
+ :to="`/${modelName}/${props.row[primaryKey]}/edit`" size="sm" />
498
499
 
499
- <l-delete-btn v-if="actionDelete && props.row.canDelete"
500
- @submit="onDelete(props.row[primaryKey])" size="sm"></l-delete-btn>
500
+ <l-delete-btn v-if="actionDelete && props.row.canDelete"
501
+ @submit="onDelete(props.row[primaryKey])" size="sm"></l-delete-btn>
501
502
 
502
- <slot name="actions" v-bind="props"></slot>
503
- </div>
503
+ <slot name="actions" v-bind="props"></slot>
504
+ </div>
504
505
 
505
- </q-td>
506
+ </q-td>
506
507
 
507
508
 
508
509
 
509
- <template v-for="col in props.cols">
510
- <template v-if="ss.indexOf('body-cell-' + col.name) >= 0">
511
- <slot :name="'body-cell-' + col.name" v-bind="props"></slot>
512
- </template>
513
- <template v-else>
514
- <q-td :key="col.name" :props="props" :auto-width="col.autoWidth ?? false"
515
- :style="getCellStyle(col, props.row)" :class="getCellClass(col, props.row)"><template
516
- v-if="col.to" class="bg-primary">
517
- <l-link :to="col.to(props.row)">{{ col.value }}</l-link>
518
- </template>
519
- <template v-else-if="col.component">
520
- <component :is="col.component" v-model="props.row"
521
- v-bind="col.componentProps ? col.componentProps(props.row) : null" />
522
- </template>
523
- <template v-else>
524
- {{ col.value }}
525
- </template>
526
- </q-td>
527
- </template>
510
+ <template v-for="col in props.cols">
511
+ <template v-if="ss.indexOf('body-cell-' + col.name) >= 0">
512
+ <slot :name="'body-cell-' + col.name" v-bind="props"></slot>
528
513
  </template>
529
- </q-tr>
530
- <q-tr v-show="props.expand" :props="props">
531
- <q-td colspan="100%">
532
- <slot name="row-expand" v-bind="props"></slot>
533
- </q-td>
534
- </q-tr>
535
- </template>
536
-
537
-
538
- <template #top-right="props" v-if="searchable">
539
- <q-input v-if="searchable" outlined dense debounce="300" v-model="filter" :placeholder="$t('Search')"
540
- :color="$light.color">
541
- <template v-slot:append>
542
- <q-icon name="search" />
514
+ <template v-else>
515
+ <q-td :key="col.name" :props="props" :auto-width="col.autoWidth ?? false"
516
+ :style="getCellStyle(col, props.row)" :class="getCellClass(col, props.row)"><template
517
+ v-if="col.to" class="bg-primary">
518
+ <l-link :to="col.to(props.row)">{{ col.value }}</l-link>
519
+ </template>
520
+ <template v-else-if="col.component">
521
+ <component :is="col.component" v-model="props.row"
522
+ v-bind="col.componentProps ? col.componentProps(props.row) : null" />
523
+ </template>
524
+ <template v-else>
525
+ {{ col.value }}
526
+ </template>
527
+ </q-td>
543
528
  </template>
544
- </q-input>
529
+ </template>
530
+ </q-tr>
531
+ <q-tr v-show="props.expand" :props="props">
532
+ <q-td colspan="100%">
533
+ <slot name="row-expand" v-bind="props"></slot>
534
+ </q-td>
535
+ </q-tr>
536
+ </template>
537
+
545
538
 
539
+ <template #top-right="props" v-if="searchable">
540
+ <q-input v-if="searchable" outlined dense debounce="300" v-model="filter" :placeholder="$t('Search')"
541
+ :color="$light.color">
542
+ <template v-slot:append>
543
+ <q-icon name="search" />
544
+ </template>
545
+ </q-input>
546
546
 
547
547
 
548
548
 
549
- </template>
549
+
550
+ </template>
550
551
 
551
552
 
552
- <!-- template v-for="col in toColumns" v-slot:[col.slot_name]="props">
553
+ <!-- template v-for="col in toColumns" v-slot:[col.slot_name]="props">
553
554
  <q-td :props="props">
554
555
  <l-link :to="col.to(props.row)">
555
556
  {{ col.field(props.row) }}
@@ -558,49 +559,47 @@ const onAdd = () => {
558
559
  </template -->
559
560
 
560
561
 
561
- <template #top-row="props" v-if="hasSearch && isServerSide">
562
- <q-tr>
563
- <q-td v-if="selection != 'none'" auto-width />
564
- <q-td v-if="hasRowExpand" auto-width />
565
- <q-td v-if="hasActions" auto-width />
566
- <q-td v-for="col in props.cols">
567
- <template v-if="col.searchable">
568
-
569
- <template v-if="col.searchType == 'number'">
570
- <q-input style="min-width: 80px;" dense clearable filled square
571
- v-model.number="filters[col.name]" @keydown.enter.prevent="onFilters"
572
- @clear="onFilters" mask="##########" :enterkeyhint="$t('search')"></q-input>
573
- </template>
562
+ <template #top-row="props" v-if="hasSearch && isServerSide">
563
+ <q-tr>
564
+ <q-td v-if="selection != 'none'" auto-width />
565
+ <q-td v-if="hasRowExpand" auto-width />
566
+ <q-td v-if="hasActions" auto-width />
567
+ <q-td v-for="col in props.cols">
568
+ <template v-if="col.searchable">
574
569
 
575
- <template v-if="col.searchType == 'select'">
576
- <q-select dense clearable filled square v-model="filters[col.name]"
577
- @update:model-value="onFilters" options-dense :options="col.searchOptions"
578
- emit-value map-options :multiple="col.searchMultiple" :color="$light.color" />
570
+ <template v-if="col.searchType == 'number'">
571
+ <q-input style="min-width: 80px;" dense clearable filled square
572
+ v-model.number="filters[col.name]" @keydown.enter.prevent="onFilters" @clear="onFilters"
573
+ mask="##########" :enterkeyhint="$t('search')"></q-input>
574
+ </template>
579
575
 
580
- </template>
576
+ <template v-if="col.searchType == 'select'">
577
+ <q-select dense clearable filled square v-model="filters[col.name]"
578
+ @update:model-value="onFilters" options-dense :options="col.searchOptions" emit-value
579
+ map-options :multiple="col.searchMultiple" :color="$light.color" />
581
580
 
581
+ </template>
582
582
 
583
- <template v-if="col.searchType == 'date'">
584
- <l-date-picker dense clearable filled square :outlined="false" hide-bottom-space
585
- v-model="filters[col.name]" @update:model-value="onFilters" range
586
- @clear="onFilters" />
587
- </template>
588
583
 
589
- <template v-if="!col.searchType || col.searchType == 'text'">
590
- <q-input style="min-width: 80px;" dense clearable filled square
591
- v-model="filters[col.name]" @keydown.enter.prevent="onFilters" @clear="onFilters"
592
- :enterkeyhint="$t('search')" :color="$light.color"></q-input>
584
+ <template v-if="col.searchType == 'date'">
585
+ <l-date-picker dense clearable filled square :outlined="false" hide-bottom-space
586
+ v-model="filters[col.name]" @update:model-value="onFilters" range @clear="onFilters" />
587
+ </template>
593
588
 
594
- </template>
589
+ <template v-if="!col.searchType || col.searchType == 'text'">
590
+ <q-input style="min-width: 80px;" dense clearable filled square v-model="filters[col.name]"
591
+ @keydown.enter.prevent="onFilters" @clear="onFilters" :enterkeyhint="$t('search')"
592
+ :color="$light.color"></q-input>
595
593
 
596
594
  </template>
597
- </q-td>
598
- </q-tr>
599
- </template>
600
-
601
- <template v-for="slot of ss" v-slot:[slot]="props">
602
- <slot :name="slot" v-bind="props"></slot>
603
- </template>
604
- </q-table>
605
- </l-card>
595
+
596
+ </template>
597
+ </q-td>
598
+ </q-tr>
599
+ </template>
600
+
601
+ <template v-for="slot of ss" v-slot:[slot]="props">
602
+ <slot :name="slot" v-bind="props"></slot>
603
+ </template>
604
+ </q-table>
606
605
  </template>
@@ -1,7 +1,5 @@
1
1
  import { QTable, Dialog } from 'quasar';
2
2
  import type { QTableColumn, QTableProps } from 'quasar';
3
- declare const minimized: import("vue").ModelRef<boolean, string, boolean, boolean>;
4
- declare const maximized: import("vue").ModelRef<boolean, string, boolean, boolean>;
5
3
  export interface LTableColumn extends QTableColumn {
6
4
  searchable?: boolean;
7
5
  searchType?: string;
@@ -18,16 +16,13 @@ export type LTableProps = QTableProps & {
18
16
  sortBy?: any;
19
17
  rowKey?: string;
20
18
  modelName?: any;
21
- searchable?: any;
19
+ searchable?: boolean;
22
20
  selected?: Array<any>;
23
- maximizable?: boolean;
24
- minimizable?: boolean;
25
21
  onRequestData?: (request: LTableRequest) => void;
26
22
  addComponent?: Dialog;
27
23
  addComponentProps?: any;
28
24
  rows?: Array<any>;
29
25
  };
30
- type __VLS_Props = LTableProps;
31
26
  declare const isServerSide: boolean;
32
27
  declare const pagination: import("vue").Ref<{
33
28
  sortBy?: string | null | undefined;
@@ -107,25 +102,19 @@ declare const getCellClass: (col: any, row: any) => any;
107
102
  declare const localSelected: import("vue").WritableComputedRef<any[], any[]>;
108
103
  declare function requestServerInteraction(): void;
109
104
  declare const onAdd: () => void;
110
- type __VLS_PublicProps = __VLS_Props & {
111
- "minimized"?: boolean;
112
- "maximized"?: boolean;
113
- };
114
105
  declare const __VLS_ctx: InstanceType<__VLS_PickNotAny<typeof __VLS_self, new () => {}>>;
115
- declare var __VLS_100: any, __VLS_103: string, __VLS_104: any, __VLS_126: any, __VLS_192: string, __VLS_193: any;
106
+ declare var __VLS_91: any, __VLS_94: string, __VLS_95: any, __VLS_117: any, __VLS_183: string, __VLS_184: any;
116
107
  type __VLS_Slots = __VLS_PrettifyGlobal<__VLS_OmitStringIndex<typeof __VLS_ctx.$slots> & {
117
- [K in NonNullable<typeof __VLS_103>]?: (props: typeof __VLS_104) => any;
108
+ [K in NonNullable<typeof __VLS_94>]?: (props: typeof __VLS_95) => any;
118
109
  } & {
119
- [K in NonNullable<typeof __VLS_192>]?: (props: typeof __VLS_193) => any;
110
+ [K in NonNullable<typeof __VLS_183>]?: (props: typeof __VLS_184) => any;
120
111
  } & {
121
- actions?: (props: typeof __VLS_100) => any;
112
+ actions?: (props: typeof __VLS_91) => any;
122
113
  } & {
123
- 'row-expand'?: (props: typeof __VLS_126) => any;
114
+ 'row-expand'?: (props: typeof __VLS_117) => any;
124
115
  }>;
125
- declare const __VLS_self: import("vue").DefineComponent<__VLS_PublicProps, {
116
+ declare const __VLS_self: import("vue").DefineComponent<LTableProps, {
126
117
  QTable: typeof QTable;
127
- minimized: typeof minimized;
128
- maximized: typeof maximized;
129
118
  errors: typeof errors;
130
119
  isServerSide: typeof isServerSide;
131
120
  pagination: typeof pagination;
@@ -154,14 +143,10 @@ declare const __VLS_self: import("vue").DefineComponent<__VLS_PublicProps, {
154
143
  onAdd: typeof onAdd;
155
144
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
156
145
  delete: (p: any) => any;
157
- "update:minimized": (value: boolean) => any;
158
- "update:maximized": (value: boolean) => any;
159
146
  "request-data": (p: LTableRequest) => any;
160
147
  "update:selected": (p: any[]) => any;
161
- }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
148
+ }, string, import("vue").PublicProps, Readonly<LTableProps> & Readonly<{
162
149
  onDelete?: ((p: any) => any) | undefined;
163
- "onUpdate:minimized"?: ((value: boolean) => any) | undefined;
164
- "onUpdate:maximized"?: ((value: boolean) => any) | undefined;
165
150
  "onRequest-data"?: ((p: LTableRequest) => any) | undefined;
166
151
  "onUpdate:selected"?: ((p: any[]) => any) | undefined;
167
152
  }>, {
@@ -170,8 +155,6 @@ declare const __VLS_self: import("vue").DefineComponent<__VLS_PublicProps, {
170
155
  dark: boolean | null;
171
156
  bordered: boolean;
172
157
  fullscreen: boolean;
173
- minimizable: boolean;
174
- maximizable: boolean;
175
158
  selected: any[];
176
159
  actions: Array<string>;
177
160
  pagination: {
@@ -186,20 +169,16 @@ declare const __VLS_self: import("vue").DefineComponent<__VLS_PublicProps, {
186
169
  noDataLabel: string;
187
170
  rowsPerPageOptions: readonly any[];
188
171
  rowsPerPageLabel: string;
189
- searchable: any;
172
+ searchable: boolean;
190
173
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
191
- declare const __VLS_component: import("vue").DefineComponent<__VLS_PublicProps, {
174
+ declare const __VLS_component: import("vue").DefineComponent<LTableProps, {
192
175
  requestServerInteraction: typeof requestServerInteraction;
193
176
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
194
177
  delete: (p: any) => any;
195
- "update:minimized": (value: boolean) => any;
196
- "update:maximized": (value: boolean) => any;
197
178
  "request-data": (p: LTableRequest) => any;
198
179
  "update:selected": (p: any[]) => any;
199
- }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
180
+ }, string, import("vue").PublicProps, Readonly<LTableProps> & Readonly<{
200
181
  onDelete?: ((p: any) => any) | undefined;
201
- "onUpdate:minimized"?: ((value: boolean) => any) | undefined;
202
- "onUpdate:maximized"?: ((value: boolean) => any) | undefined;
203
182
  "onRequest-data"?: ((p: LTableRequest) => any) | undefined;
204
183
  "onUpdate:selected"?: ((p: any[]) => any) | undefined;
205
184
  }>, {
@@ -208,8 +187,6 @@ declare const __VLS_component: import("vue").DefineComponent<__VLS_PublicProps,
208
187
  dark: boolean | null;
209
188
  bordered: boolean;
210
189
  fullscreen: boolean;
211
- minimizable: boolean;
212
- maximizable: boolean;
213
190
  selected: any[];
214
191
  actions: Array<string>;
215
192
  pagination: {
@@ -224,7 +201,7 @@ declare const __VLS_component: import("vue").DefineComponent<__VLS_PublicProps,
224
201
  noDataLabel: string;
225
202
  rowsPerPageOptions: readonly any[];
226
203
  rowsPerPageLabel: string;
227
- searchable: any;
204
+ searchable: boolean;
228
205
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
229
206
  declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
230
207
  export default _default;
@@ -8,17 +8,28 @@ const columns = model("MailLog").columns({
8
8
  created_time: true,
9
9
  body: true
10
10
  });
11
+ function requestMailLog(event) {
12
+ event.loadObjects("MailLog", {}, ["body"]);
13
+ }
11
14
  </script>
12
15
 
13
16
  <template>
14
17
  <l-page>
15
- <l-table row-key="maillog_id" @request-data="$event.loadObjects('MailLog', {}, ['body'])" :columns="columns"
16
- sort-by="maillog_id:desc">
17
-
18
+ <l-table
19
+ row-key="maillog_id"
20
+ @request-data="requestMailLog"
21
+ :columns="columns"
22
+ sort-by="maillog_id:desc"
23
+ >
18
24
  <template #row-expand="props">
19
- <iframe width="100%" height="300px" :srcdoc="props.row.body" frameborder="0"></iframe>
25
+ <iframe
26
+ width="100%"
27
+ height="300px"
28
+ :srcdoc="props.row.body"
29
+ frameborder="0"
30
+ sandbox
31
+ ></iframe>
20
32
  </template>
21
-
22
33
  </l-table>
23
34
  </l-page>
24
35
  </template>
@@ -1,67 +1,73 @@
1
1
  <script setup>
2
- import { reactive } from "vue";
2
+ import { computed, ref } from "vue";
3
3
  import { m, api } from "#imports";
4
4
  import { useI18n } from "vue-i18n";
5
5
  const { t } = useI18n();
6
- const { app } = await api.query({
7
- app: {
8
- permissions: true,
9
- roles: {
10
- name: true,
11
- permissions: true
6
+ const fetchApp = async () => {
7
+ const { app: app2 } = await api.query({
8
+ app: {
9
+ permissions: true,
10
+ roles: {
11
+ name: true,
12
+ permissions: true
13
+ }
12
14
  }
13
- }
14
- });
15
- const roles = app.roles;
16
- const columns = [{
17
- label: t("Permission"),
18
- field: "permission",
19
- align: "left"
20
- }];
21
- roles.forEach((role) => {
22
- columns.push({
15
+ });
16
+ return app2;
17
+ };
18
+ const app = ref(await fetchApp());
19
+ const roles = computed(() => app.value.roles);
20
+ const columns = computed(() => [
21
+ { label: t("Permission"), field: "permission", align: "left" },
22
+ ...roles.value.map((role) => ({
23
23
  label: role.name,
24
24
  field: role.name,
25
25
  align: "left"
26
- });
27
- });
28
- const rows = reactive([]);
29
- app.permissions.forEach((permission) => {
30
- let row = {
31
- permission
32
- };
33
- roles.forEach((role) => {
34
- if (role.permissions.indexOf(permission) != -1) {
35
- row[role.name] = true;
36
- } else {
37
- row[role.name] = false;
38
- }
39
- });
40
- rows.push(row);
41
- });
42
- const onUpdate = (value, role, permission) => {
26
+ }))
27
+ ]);
28
+ const rows = computed(
29
+ () => app.value.permissions.map((permission) => {
30
+ const row = { permission };
31
+ roles.value.forEach((role) => {
32
+ row[role.name] = role.permissions.includes(permission);
33
+ });
34
+ return row;
35
+ })
36
+ );
37
+ const onUpdate = async (value, role, permission) => {
43
38
  if (value) {
44
- m("addPermission", { value: permission, role });
39
+ await m("addPermission", { value: permission, role });
45
40
  } else {
46
- m("removePermission", { value: permission, role });
41
+ await m("removePermission", { value: permission, role });
47
42
  }
48
- rows.forEach((row) => {
49
- if (row.permission == permission) {
50
- row[role] = value;
51
- }
52
- });
43
+ app.value = await fetchApp();
53
44
  };
45
+ const filter = ref("");
46
+ const filteredRows = computed(() => {
47
+ if (!filter.value) return rows.value;
48
+ return rows.value.filter(
49
+ (row) => row.permission.toLowerCase().includes(filter.value.toLowerCase())
50
+ );
51
+ });
54
52
  </script>
55
53
 
56
54
  <template>
57
55
  <l-page>
58
- <q-table :columns="columns" flat bordered :rows="rows" :pagination="{ rowsPerPage: 0 }" dense>
56
+ <q-table :columns="columns" flat bordered :rows="filteredRows" :pagination="{ rowsPerPage: 0 }" dense>
57
+ <template #top-left>
58
+ <q-input :color="$light.color" v-model="filter" :placeholder="t('Filter permissions')" dense clearable
59
+ outlined>
60
+ <template v-slot:append>
61
+ <q-icon name="sym_o_search" />
62
+ </template>
63
+ </q-input>
64
+ </template>
59
65
  <template #body="props">
60
66
  <q-tr :props="props">
61
67
  <q-td>
62
68
  {{ props.row.permission }}
63
69
  </q-td>
64
- <q-td v-for="role in roles">
70
+ <q-td v-for="role in roles" :key="role.name">
65
71
  <q-checkbox v-model="props.row[role.name]"
66
72
  @update:model-value="onUpdate($event, role.name, props.row.permission)"
67
73
  :color="$light.color" />
@@ -1,26 +1,28 @@
1
1
  <script setup>
2
2
  import { q } from "#imports";
3
- import { Loading } from "quasar";
3
+ import { Loading, Notify } from "quasar";
4
4
  const onClickDownload = async () => {
5
- Loading.show({
6
- message: "Exporting database..."
7
- });
8
- const data = await q({
9
- system: {
10
- database: {
11
- export: true
5
+ Loading.show({ message: "Exporting database..." });
6
+ try {
7
+ const data = await q({
8
+ system: {
9
+ database: {
10
+ export: true
11
+ }
12
12
  }
13
- }
14
- });
15
- Loading.hide();
16
- const file = new File([data.system.database.export], "backup.sql", {
17
- type: "text/plain;charset=utf-8"
18
- });
19
- const a = document.createElement("a");
20
- a.download = file.name;
21
- a.href = URL.createObjectURL(file);
22
- a.click();
23
- URL.revokeObjectURL(a.href);
13
+ });
14
+ const blob = new Blob([data.system.database.export], { type: "text/plain;charset=utf-8" });
15
+ const url = URL.createObjectURL(blob);
16
+ const a = document.createElement("a");
17
+ a.download = "backup.sql";
18
+ a.href = url;
19
+ a.click();
20
+ setTimeout(() => URL.revokeObjectURL(url), 1e3);
21
+ } catch (e) {
22
+ Notify.create({ type: "negative", message: "\u4E0B\u8F09\u5931\u6557\uFF0C\u8ACB\u7A0D\u5F8C\u518D\u8A66\u3002" });
23
+ } finally {
24
+ Loading.hide();
25
+ }
24
26
  };
25
27
  </script>
26
28
 
@@ -15,28 +15,28 @@ const { data, refresh } = await useAsyncData(async () => {
15
15
  return system;
16
16
  });
17
17
  const autoRefresh = ref(0);
18
- let interval = null;
18
+ const interval = ref(null);
19
19
  watch(autoRefresh, (value) => {
20
- if (interval)
21
- clearInterval(interval);
20
+ if (interval.value)
21
+ clearInterval(interval.value);
22
22
  if (value) {
23
- interval = setInterval(refresh, value);
23
+ interval.value = setInterval(() => {
24
+ refresh();
25
+ }, value);
24
26
  }
25
- });
27
+ }, { immediate: true });
26
28
  onUnmounted(() => {
27
- if (interval)
28
- clearInterval(interval);
29
+ if (interval.value)
30
+ clearInterval(interval.value);
29
31
  });
30
32
  </script>
31
33
 
32
34
  <template>
33
35
  <l-page title="System Database Process">
34
- <q-table :rows="data.database.processList" hide-bottom v-bind="$light.styles.table" :loading="loading">
36
+ <q-table :rows="data?.database?.processList || []" hide-bottom v-bind="$light.styles.table" :loading="loading">
35
37
  <template #top-left>
36
38
  <q-btn label="Reload" outline :color="$light.color" icon="sym_o_refresh" @click="refresh"></q-btn>
37
-
38
39
  </template>
39
-
40
40
  <template #top-right>
41
41
  <q-select label="Auto Refresh" v-model="autoRefresh" :options="[
42
42
  { label: 'Off', value: 0 },
@@ -48,11 +48,9 @@ onUnmounted(() => {
48
48
  { label: '10m', value: 600000 },
49
49
  { label: '30m', value: 1800000 },
50
50
  { label: '1h', value: 3600000 }
51
- ]" @input="autoRefresh && refresh" outlined :color="$light.color" dense="" style="min-width: 200px"
51
+ ]" @update:model-value="autoRefresh && refresh()" outlined :color="$light.color" dense style="min-width: 200px"
52
52
  map-options emit-value></q-select>
53
-
54
53
  </template>
55
54
  </q-table>
56
-
57
55
  </l-page>
58
56
  </template>
@@ -16,10 +16,10 @@ const { data, refresh } = await useAsyncData("database", async () => {
16
16
  });
17
17
  const SYSTEM_TABLES = ["Config", "EventLog", "MailLog", "Permission", "Role", "SystemValue", "Translate", "User", "UserLog", "UserRole", "MyFavorite", "CustomField"];
18
18
  const custom_tables = computed(() => {
19
- return data.value.tableStatus.filter((table) => !SYSTEM_TABLES.includes(table.Name));
19
+ return (data.value?.tableStatus ?? []).filter((table) => !SYSTEM_TABLES.includes(table.Name));
20
20
  });
21
21
  const systables = computed(() => {
22
- return data.value.tableStatus.filter((table) => SYSTEM_TABLES.includes(table.Name));
22
+ return (data.value?.tableStatus ?? []).filter((table) => SYSTEM_TABLES.includes(table.Name));
23
23
  });
24
24
  const field_add = resolveComponent("l-dialog-database-field-add");
25
25
  const table_add = resolveComponent("l-database-create-table-dialog");
@@ -51,8 +51,10 @@ const add = async (table) => {
51
51
  });
52
52
  };
53
53
  const selected = reactive({});
54
- for (let table of data.value.table) {
55
- selected[table.name] = [];
54
+ if (data.value?.table) {
55
+ for (let table of data.value.table) {
56
+ selected[table.name] = [];
57
+ }
56
58
  }
57
59
  const removeField = async (table) => {
58
60
  light.dialog({
@@ -173,14 +175,14 @@ const truncatTable = async () => {
173
175
  </l-list>
174
176
  </l-card>
175
177
 
176
- <q-card flat bordered>
178
+ <q-card>
177
179
  <q-list separator bordered>
178
180
  <q-expansion-item label="Table Status" dense>
179
- <div class="q-ma-sm q-gutter-y-sm">
181
+ <div s>
180
182
 
181
183
  <q-table :rows="custom_tables" hide-pagination flat bordered separator="cell" dense
182
184
  :rows-per-page-options="[0]" v-model:selected="selectedTable" selection="multiple"
183
- row-key="Name">
185
+ row-key="Name" :color="$light.color">
184
186
 
185
187
  <template #top-left>
186
188
  <q-btn icon="sym_o_add" @click="createTable()" round flat size="sm">
@@ -210,13 +212,12 @@ const truncatTable = async () => {
210
212
  </q-list>
211
213
  </q-card>
212
214
 
213
- <q-card flat bordered>
215
+ <q-card>
214
216
  <q-list separator bordered>
215
217
  <q-expansion-item :label="table.name" v-for="table in data.table" dense>
216
218
  <div class="q-ma-sm">
217
- <q-table row-key="Field" separator="cell" dense :rows="table.columns"
218
- :rows-per-page-options="[0]" hide-pagination flat bordered selection="multiple"
219
- v-model:selected="selected[table.name]">
219
+ <q-table row-key="Field" :rows="table.columns" :rows-per-page-options="[0]" hide-pagination flat
220
+ bordered selection="multiple" v-model:selected="selected[table.name]" :color="$light.color">
220
221
  <template #top-left>
221
222
  <q-btn icon="sym_o_add" @click="add(table.name)" round flat size="sm">
222
223
  <q-tooltip>Add field</q-tooltip>
@@ -233,6 +234,5 @@ const truncatTable = async () => {
233
234
  </q-list>
234
235
  </q-card>
235
236
 
236
-
237
237
  </l-page>
238
238
  </template>
@@ -100,6 +100,7 @@ const inputDesign = computed({
100
100
  { label: 'None', value: 'none' },
101
101
  ]" :color="$light.color" />
102
102
  </q-field>
103
+
103
104
  </q-card-section>
104
105
  </l-card>
105
106
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hostlink/nuxt-light",
3
- "version": "1.30.0",
3
+ "version": "1.31.1",
4
4
  "description": "HostLink Nuxt Light Framework",
5
5
  "repository": {
6
6
  "type": "git",