@hostlink/nuxt-light 1.66.2 → 1.67.0
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.d.mts +5 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +10 -0
- package/dist/runtime/components/L/DatePicker.d.vue.ts +1 -1
- package/dist/runtime/components/L/DatePicker.vue.d.ts +1 -1
- package/dist/runtime/components/L/DeleteBtn.d.vue.ts +10 -1
- package/dist/runtime/components/L/DeleteBtn.vue +4 -2
- package/dist/runtime/components/L/DeleteBtn.vue.d.ts +10 -1
- package/dist/runtime/components/L/SearchNumber.d.vue.ts +16 -0
- package/dist/runtime/components/L/SearchNumber.vue +139 -0
- package/dist/runtime/components/L/SearchNumber.vue.d.ts +16 -0
- package/dist/runtime/components/L/Table.d.vue.ts +16 -8
- package/dist/runtime/components/L/Table.vue +75 -13
- package/dist/runtime/components/L/Table.vue.d.ts +16 -8
- package/dist/runtime/composables/useLight.js +10 -1
- package/dist/runtime/models/EventLog.js +5 -1
- package/dist/runtime/pages/Permission/all.vue +23 -14
- package/dist/runtime/plugin.js +13 -5
- package/package.json +1 -1
package/dist/module.d.mts
CHANGED
|
@@ -2,6 +2,11 @@ import * as _nuxt_schema from '@nuxt/schema';
|
|
|
2
2
|
|
|
3
3
|
interface ModuleOptions {
|
|
4
4
|
checkPagePermissions?: boolean;
|
|
5
|
+
tableActionIcons?: {
|
|
6
|
+
view?: string;
|
|
7
|
+
edit?: string;
|
|
8
|
+
delete?: string;
|
|
9
|
+
};
|
|
5
10
|
}
|
|
6
11
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
7
12
|
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -334,6 +334,16 @@ const module$1 = defineNuxtModule({
|
|
|
334
334
|
}, */
|
|
335
335
|
async setup(options, nuxt) {
|
|
336
336
|
const resolver = createResolver(import.meta.url);
|
|
337
|
+
nuxt.options.runtimeConfig = nuxt.options.runtimeConfig || {};
|
|
338
|
+
nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {};
|
|
339
|
+
nuxt.options.runtimeConfig.public.light = {
|
|
340
|
+
...nuxt.options.runtimeConfig.public.light || {},
|
|
341
|
+
tableActionIcons: {
|
|
342
|
+
view: options.tableActionIcons?.view,
|
|
343
|
+
edit: options.tableActionIcons?.edit,
|
|
344
|
+
delete: options.tableActionIcons?.delete
|
|
345
|
+
}
|
|
346
|
+
};
|
|
337
347
|
extendPages((pages) => {
|
|
338
348
|
for (const route of routes) {
|
|
339
349
|
if (route.children) {
|
|
@@ -19,13 +19,13 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
|
|
|
19
19
|
square: boolean;
|
|
20
20
|
dense: boolean;
|
|
21
21
|
disable: boolean;
|
|
22
|
+
range: boolean;
|
|
22
23
|
outlined: boolean;
|
|
23
24
|
filled: boolean;
|
|
24
25
|
stackLabel: boolean;
|
|
25
26
|
standout: string | boolean;
|
|
26
27
|
hideBottomSpace: boolean;
|
|
27
28
|
todayBtn: boolean;
|
|
28
|
-
range: boolean;
|
|
29
29
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
30
30
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
31
31
|
declare const _default: typeof __VLS_export;
|
|
@@ -19,13 +19,13 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
|
|
|
19
19
|
square: boolean;
|
|
20
20
|
dense: boolean;
|
|
21
21
|
disable: boolean;
|
|
22
|
+
range: boolean;
|
|
22
23
|
outlined: boolean;
|
|
23
24
|
filled: boolean;
|
|
24
25
|
stackLabel: boolean;
|
|
25
26
|
standout: string | boolean;
|
|
26
27
|
hideBottomSpace: boolean;
|
|
27
28
|
todayBtn: boolean;
|
|
28
|
-
range: boolean;
|
|
29
29
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
30
30
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
31
31
|
declare const _default: typeof __VLS_export;
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
declare const _default: typeof __VLS_export;
|
|
2
2
|
export default _default;
|
|
3
|
-
declare const __VLS_export:
|
|
3
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
4
|
+
type __VLS_WithSlots<T, S> = T & (new () => {
|
|
5
|
+
$slots: S;
|
|
6
|
+
});
|
|
7
|
+
declare const __VLS_base: import("vue").DefineComponent<{
|
|
8
|
+
icon?: any;
|
|
4
9
|
to?: any;
|
|
5
10
|
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
6
11
|
submit: (...args: any[]) => void;
|
|
7
12
|
}, string, import("vue").PublicProps, Readonly<{
|
|
13
|
+
icon?: any;
|
|
8
14
|
to?: any;
|
|
9
15
|
}> & Readonly<{
|
|
10
16
|
onSubmit?: ((...args: any[]) => any) | undefined;
|
|
11
17
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
18
|
+
type __VLS_Slots = {
|
|
19
|
+
default?: ((props: {}) => any) | undefined;
|
|
20
|
+
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { useQuasar } from "quasar";
|
|
3
3
|
import { useI18n } from "vue-i18n";
|
|
4
4
|
const { t } = useI18n();
|
|
5
|
-
const props = defineProps(["to"]);
|
|
5
|
+
const props = defineProps(["to", "icon"]);
|
|
6
6
|
const emit = defineEmits(["submit"]);
|
|
7
7
|
const qua = useQuasar();
|
|
8
8
|
const onDelete = () => {
|
|
@@ -19,5 +19,7 @@ const onDelete = () => {
|
|
|
19
19
|
</script>
|
|
20
20
|
|
|
21
21
|
<template>
|
|
22
|
-
<q-btn flat round icon="sym_o_delete" @click="onDelete"
|
|
22
|
+
<q-btn flat round :icon="icon || 'sym_o_delete'" @click="onDelete">
|
|
23
|
+
<slot></slot>
|
|
24
|
+
</q-btn>
|
|
23
25
|
</template>
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
declare const _default: typeof __VLS_export;
|
|
2
2
|
export default _default;
|
|
3
|
-
declare const __VLS_export:
|
|
3
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
4
|
+
type __VLS_WithSlots<T, S> = T & (new () => {
|
|
5
|
+
$slots: S;
|
|
6
|
+
});
|
|
7
|
+
declare const __VLS_base: import("vue").DefineComponent<{
|
|
8
|
+
icon?: any;
|
|
4
9
|
to?: any;
|
|
5
10
|
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
6
11
|
submit: (...args: any[]) => void;
|
|
7
12
|
}, string, import("vue").PublicProps, Readonly<{
|
|
13
|
+
icon?: any;
|
|
8
14
|
to?: any;
|
|
9
15
|
}> & Readonly<{
|
|
10
16
|
onSubmit?: ((...args: any[]) => any) | undefined;
|
|
11
17
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
18
|
+
type __VLS_Slots = {
|
|
19
|
+
default?: ((props: {}) => any) | undefined;
|
|
20
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
modelValue?: number | string | {
|
|
3
|
+
min?: number | null;
|
|
4
|
+
max?: number | null;
|
|
5
|
+
} | null;
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
};
|
|
8
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
9
|
+
change: (value: any) => any;
|
|
10
|
+
"update:modelValue": (value: any) => any;
|
|
11
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
12
|
+
onChange?: ((value: any) => any) | undefined;
|
|
13
|
+
"onUpdate:modelValue"?: ((value: any) => any) | undefined;
|
|
14
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
15
|
+
declare const _default: typeof __VLS_export;
|
|
16
|
+
export default _default;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { ref, watch, computed } from "vue";
|
|
3
|
+
import { useI18n } from "vue-i18n";
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
modelValue: { type: [Number, String, Object, null], required: false },
|
|
6
|
+
placeholder: { type: String, required: false }
|
|
7
|
+
});
|
|
8
|
+
const emit = defineEmits(["update:modelValue", "change"]);
|
|
9
|
+
const { t } = useI18n();
|
|
10
|
+
const mode = ref("equals");
|
|
11
|
+
const equalsValue = ref(null);
|
|
12
|
+
const minValue = ref(null);
|
|
13
|
+
const maxValue = ref(null);
|
|
14
|
+
watch(() => props.modelValue, (val) => {
|
|
15
|
+
if (val == null || val === "") {
|
|
16
|
+
equalsValue.value = null;
|
|
17
|
+
minValue.value = null;
|
|
18
|
+
maxValue.value = null;
|
|
19
|
+
} else if (typeof val === "object") {
|
|
20
|
+
mode.value = "range";
|
|
21
|
+
equalsValue.value = null;
|
|
22
|
+
minValue.value = val.min ?? null;
|
|
23
|
+
maxValue.value = val.max ?? null;
|
|
24
|
+
} else {
|
|
25
|
+
mode.value = "equals";
|
|
26
|
+
equalsValue.value = Number(val);
|
|
27
|
+
minValue.value = null;
|
|
28
|
+
maxValue.value = null;
|
|
29
|
+
}
|
|
30
|
+
}, { immediate: true });
|
|
31
|
+
const hasValue = computed(() => {
|
|
32
|
+
if (props.modelValue == null || props.modelValue === "") return false;
|
|
33
|
+
if (typeof props.modelValue === "object") {
|
|
34
|
+
const { min, max } = props.modelValue;
|
|
35
|
+
return min != null || max != null;
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
});
|
|
39
|
+
const displayLabel = computed(() => {
|
|
40
|
+
if (!hasValue.value) return t("Search");
|
|
41
|
+
if (typeof props.modelValue === "object") {
|
|
42
|
+
const { min, max } = props.modelValue;
|
|
43
|
+
if (min != null && max != null) return `${min} \u2013 ${max}`;
|
|
44
|
+
if (min != null) return `\u2265 ${min}`;
|
|
45
|
+
if (max != null) return `\u2264 ${max}`;
|
|
46
|
+
}
|
|
47
|
+
return `= ${props.modelValue}`;
|
|
48
|
+
});
|
|
49
|
+
const apply = () => {
|
|
50
|
+
let out = null;
|
|
51
|
+
if (mode.value === "equals") {
|
|
52
|
+
out = equalsValue.value;
|
|
53
|
+
} else {
|
|
54
|
+
const outRange = {};
|
|
55
|
+
if (minValue.value != null) outRange.min = minValue.value;
|
|
56
|
+
if (maxValue.value != null) outRange.max = maxValue.value;
|
|
57
|
+
if (Object.keys(outRange).length > 0) out = outRange;
|
|
58
|
+
}
|
|
59
|
+
emit("update:modelValue", out);
|
|
60
|
+
emit("change", out);
|
|
61
|
+
};
|
|
62
|
+
const clear = () => {
|
|
63
|
+
equalsValue.value = null;
|
|
64
|
+
minValue.value = null;
|
|
65
|
+
maxValue.value = null;
|
|
66
|
+
emit("update:modelValue", null);
|
|
67
|
+
emit("change", null);
|
|
68
|
+
};
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<template>
|
|
72
|
+
<q-btn-dropdown
|
|
73
|
+
dense flat square no-caps
|
|
74
|
+
:color="hasValue ? 'primary' : 'grey-7'"
|
|
75
|
+
:label="displayLabel"
|
|
76
|
+
:icon="hasValue ? 'sym_o_filter_alt' : 'sym_o_search'"
|
|
77
|
+
size="sm"
|
|
78
|
+
content-class="l-search-number-dropdown"
|
|
79
|
+
>
|
|
80
|
+
<div class="q-pa-md" style="min-width: 280px">
|
|
81
|
+
<q-tabs
|
|
82
|
+
v-model="mode"
|
|
83
|
+
dense
|
|
84
|
+
no-caps
|
|
85
|
+
align="left"
|
|
86
|
+
active-color="primary"
|
|
87
|
+
indicator-color="primary"
|
|
88
|
+
class="text-grey"
|
|
89
|
+
>
|
|
90
|
+
<q-tab name="equals" :label="t('Equals')" />
|
|
91
|
+
<q-tab name="range" :label="t('Range')" />
|
|
92
|
+
</q-tabs>
|
|
93
|
+
|
|
94
|
+
<q-separator class="q-mb-sm" />
|
|
95
|
+
|
|
96
|
+
<q-tab-panels v-model="mode" animated>
|
|
97
|
+
<q-tab-panel name="equals" class="q-pa-none">
|
|
98
|
+
<q-input
|
|
99
|
+
v-model.number="equalsValue"
|
|
100
|
+
dense filled square
|
|
101
|
+
type="number"
|
|
102
|
+
mask="##########"
|
|
103
|
+
:placeholder="placeholder || t('Enter a number')"
|
|
104
|
+
autofocus
|
|
105
|
+
@keydown.enter.prevent="apply"
|
|
106
|
+
/>
|
|
107
|
+
</q-tab-panel>
|
|
108
|
+
<q-tab-panel name="range" class="q-pa-none">
|
|
109
|
+
<div class="row q-gutter-sm items-center">
|
|
110
|
+
<q-input
|
|
111
|
+
v-model.number="minValue"
|
|
112
|
+
dense filled square
|
|
113
|
+
type="number"
|
|
114
|
+
mask="##########"
|
|
115
|
+
:placeholder="t('Min')"
|
|
116
|
+
class="col"
|
|
117
|
+
@keydown.enter.prevent="apply"
|
|
118
|
+
/>
|
|
119
|
+
<span class="text-grey">—</span>
|
|
120
|
+
<q-input
|
|
121
|
+
v-model.number="maxValue"
|
|
122
|
+
dense filled square
|
|
123
|
+
type="number"
|
|
124
|
+
mask="##########"
|
|
125
|
+
:placeholder="t('Max')"
|
|
126
|
+
class="col"
|
|
127
|
+
@keydown.enter.prevent="apply"
|
|
128
|
+
/>
|
|
129
|
+
</div>
|
|
130
|
+
</q-tab-panel>
|
|
131
|
+
</q-tab-panels>
|
|
132
|
+
|
|
133
|
+
<div class="row q-gutter-sm justify-end q-mt-md">
|
|
134
|
+
<q-btn flat dense :label="t('Clear')" color="negative" @click="clear" />
|
|
135
|
+
<q-btn flat dense :label="t('Apply')" color="primary" @click="apply" />
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
</q-btn-dropdown>
|
|
139
|
+
</template>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
modelValue?: number | string | {
|
|
3
|
+
min?: number | null;
|
|
4
|
+
max?: number | null;
|
|
5
|
+
} | null;
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
};
|
|
8
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
9
|
+
change: (value: any) => any;
|
|
10
|
+
"update:modelValue": (value: any) => any;
|
|
11
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
12
|
+
onChange?: ((value: any) => any) | undefined;
|
|
13
|
+
"onUpdate:modelValue"?: ((value: any) => any) | undefined;
|
|
14
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
15
|
+
declare const _default: typeof __VLS_export;
|
|
16
|
+
export default _default;
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import type { Component } from "vue";
|
|
2
2
|
import { Dialog } from 'quasar';
|
|
3
3
|
import type { QTableColumn, QTableProps } from 'quasar';
|
|
4
|
+
export interface SearchOptionsContext {
|
|
5
|
+
filters: Record<string, any>;
|
|
6
|
+
columnValue: any;
|
|
7
|
+
column: LTableColumn;
|
|
8
|
+
}
|
|
9
|
+
export type SearchOptionsResolver = (ctx: SearchOptionsContext) => Promise<Record<string, any>[]>;
|
|
4
10
|
export type LTableColumn = QTableColumn & {
|
|
5
11
|
searchType?: "date" | "text" | "number" | "select" | "boolean";
|
|
6
12
|
searchable?: boolean;
|
|
7
|
-
|
|
13
|
+
searchPlaceholder?: string;
|
|
14
|
+
searchOptions?: Record<string, any>[] | SearchOptionsResolver;
|
|
15
|
+
searchDependsOn?: string[];
|
|
8
16
|
searchMultiple?: boolean;
|
|
9
17
|
searchIndex?: string;
|
|
10
18
|
component?: Component;
|
|
@@ -15,7 +23,7 @@ export type LTableColumn = QTableColumn & {
|
|
|
15
23
|
* @deprecated use gql instead
|
|
16
24
|
*/
|
|
17
25
|
gqlField?: string | Array<string> | Object;
|
|
18
|
-
searchMethod?: "equals" | "contains";
|
|
26
|
+
searchMethod?: "equals" | "contains" | "range";
|
|
19
27
|
autoWidth?: boolean;
|
|
20
28
|
};
|
|
21
29
|
export type LTableProps = QTableProps & {
|
|
@@ -71,17 +79,17 @@ export interface LTableRequest {
|
|
|
71
79
|
}) => void;
|
|
72
80
|
}
|
|
73
81
|
declare function requestServerInteraction(): void;
|
|
74
|
-
declare var
|
|
82
|
+
declare var __VLS_135: any, __VLS_138: string, __VLS_139: any, __VLS_170: any, __VLS_173: any, __VLS_343: string, __VLS_344: any;
|
|
75
83
|
type __VLS_Slots = {} & {
|
|
76
|
-
[K in NonNullable<typeof
|
|
84
|
+
[K in NonNullable<typeof __VLS_138>]?: (props: typeof __VLS_139) => any;
|
|
77
85
|
} & {
|
|
78
|
-
[K in NonNullable<typeof
|
|
86
|
+
[K in NonNullable<typeof __VLS_343>]?: (props: typeof __VLS_344) => any;
|
|
79
87
|
} & {
|
|
80
|
-
actions?: (props: typeof
|
|
88
|
+
actions?: (props: typeof __VLS_135) => any;
|
|
81
89
|
} & {
|
|
82
|
-
'row-expand'?: (props: typeof
|
|
90
|
+
'row-expand'?: (props: typeof __VLS_170) => any;
|
|
83
91
|
} & {
|
|
84
|
-
'top-right'?: (props: typeof
|
|
92
|
+
'top-right'?: (props: typeof __VLS_173) => any;
|
|
85
93
|
};
|
|
86
94
|
declare const __VLS_base: import("vue").DefineComponent<LTableProps, {
|
|
87
95
|
requestServerInteraction: typeof requestServerInteraction;
|
|
@@ -149,7 +149,8 @@ const localColumns = computed(
|
|
|
149
149
|
...col,
|
|
150
150
|
align: col.align ?? "left",
|
|
151
151
|
field: col.field ?? col.name,
|
|
152
|
-
label: t(col.label)
|
|
152
|
+
label: t(col.label),
|
|
153
|
+
searchPlaceholder: col.searchPlaceholder ? t(col.searchPlaceholder) : void 0
|
|
153
154
|
}))
|
|
154
155
|
);
|
|
155
156
|
const emits = defineEmits(["request-data", "update:selected", "delete"]);
|
|
@@ -171,15 +172,45 @@ const table = ref();
|
|
|
171
172
|
const rows = ref(props.rows ?? []);
|
|
172
173
|
const resolvedSearchOptions = ref({});
|
|
173
174
|
const localSearchOptions = ref({});
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
resolvedSearchOptions.value[col.name] =
|
|
179
|
-
localSearchOptions.value[col.name] =
|
|
175
|
+
let resolveSeq = 0;
|
|
176
|
+
const resolveColumnOptions = async (col) => {
|
|
177
|
+
if (!col.searchOptions || !col.name) return;
|
|
178
|
+
if (Array.isArray(col.searchOptions)) {
|
|
179
|
+
resolvedSearchOptions.value[col.name] = col.searchOptions;
|
|
180
|
+
localSearchOptions.value[col.name] = col.searchOptions;
|
|
181
|
+
return;
|
|
180
182
|
}
|
|
183
|
+
const ctx = {
|
|
184
|
+
filters: { ...filters.value },
|
|
185
|
+
columnValue: filters.value[col.name],
|
|
186
|
+
column: col
|
|
187
|
+
};
|
|
188
|
+
const seq = ++resolveSeq;
|
|
189
|
+
try {
|
|
190
|
+
const result = await col.searchOptions(ctx);
|
|
191
|
+
if (seq !== resolveSeq) return;
|
|
192
|
+
resolvedSearchOptions.value[col.name] = result;
|
|
193
|
+
localSearchOptions.value[col.name] = result;
|
|
194
|
+
} catch {
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
onMounted(async () => {
|
|
198
|
+
await Promise.all(
|
|
199
|
+
(props.columns ?? []).map((col) => resolveColumnOptions(col))
|
|
200
|
+
);
|
|
181
201
|
table.value?.requestServerInteraction();
|
|
182
202
|
});
|
|
203
|
+
watch(filters, (newFilters, oldFilters) => {
|
|
204
|
+
if (!props.columns) return;
|
|
205
|
+
for (const col of props.columns) {
|
|
206
|
+
if (!col.searchDependsOn?.length) continue;
|
|
207
|
+
if (typeof col.searchOptions !== "function") continue;
|
|
208
|
+
if (!col.name) continue;
|
|
209
|
+
const deps = col.searchDependsOn;
|
|
210
|
+
const changed = deps.some((dep) => newFilters?.[dep] !== oldFilters?.[dep]);
|
|
211
|
+
if (changed) resolveColumnOptions(col);
|
|
212
|
+
}
|
|
213
|
+
}, { deep: true });
|
|
183
214
|
const primaryKey = ref(props.rowKey);
|
|
184
215
|
const modelName = ref(props.modelName);
|
|
185
216
|
const validateData = () => {
|
|
@@ -335,6 +366,18 @@ const getFilterValue = () => {
|
|
|
335
366
|
const k = col.searchIndex ?? col.name;
|
|
336
367
|
if (col.searchType == "boolean") {
|
|
337
368
|
f[k] = filters.value[col.name];
|
|
369
|
+
} else if (col.searchMethod == "range" && typeof filters.value[col.name] === "object") {
|
|
370
|
+
const rangeValue = filters.value[col.name];
|
|
371
|
+
const rangeFilter = {};
|
|
372
|
+
if (rangeValue.min !== null && rangeValue.min !== "" && rangeValue.min !== void 0) {
|
|
373
|
+
rangeFilter._gte = Number(rangeValue.min);
|
|
374
|
+
}
|
|
375
|
+
if (rangeValue.max !== null && rangeValue.max !== "" && rangeValue.max !== void 0) {
|
|
376
|
+
rangeFilter._lte = Number(rangeValue.max);
|
|
377
|
+
}
|
|
378
|
+
if (Object.keys(rangeFilter).length > 0) {
|
|
379
|
+
f[k] = rangeFilter;
|
|
380
|
+
}
|
|
338
381
|
} else if (col.searchType == "number") {
|
|
339
382
|
f[k] = filters.value[col.name];
|
|
340
383
|
} else if (col.searchType == "date") {
|
|
@@ -460,6 +503,9 @@ const localFilterMethod = (rows2, terms) => {
|
|
|
460
503
|
if (!String(cell ?? "").toLowerCase().includes(String(value.contains).toLowerCase())) return false;
|
|
461
504
|
} else if (typeof value === "object" && value !== null && "_between" in value) {
|
|
462
505
|
if (cell < value._between[0] || cell > value._between[1]) return false;
|
|
506
|
+
} else if (typeof value === "object" && value !== null && ("_gte" in value || "_lte" in value)) {
|
|
507
|
+
if ("_gte" in value && value._gte !== null && value._gte !== void 0 && cell < value._gte) return false;
|
|
508
|
+
if ("_lte" in value && value._lte !== null && value._lte !== void 0 && cell > value._lte) return false;
|
|
463
509
|
} else {
|
|
464
510
|
if (cell !== value) return false;
|
|
465
511
|
}
|
|
@@ -549,13 +595,22 @@ const hasFilters = computed(() => {
|
|
|
549
595
|
<div :class="{ 'text-grey-8': !isDark }" v-if="primaryKey">
|
|
550
596
|
|
|
551
597
|
<l-action-btn v-if="actionView && props.row.canView"
|
|
552
|
-
:to="`/${modelName}/${props.row[primaryKey]}/view`"
|
|
598
|
+
:to="`/${modelName}/${props.row[primaryKey]}/view`"
|
|
599
|
+
:icon="light.styles.table.actionIcons.view">
|
|
600
|
+
<q-tooltip>{{ $t("View") }}</q-tooltip>
|
|
601
|
+
</l-action-btn>
|
|
553
602
|
|
|
554
603
|
<l-action-btn v-if="activeEdit && props.row.canUpdate"
|
|
555
|
-
:to="`/${modelName}/${props.row[primaryKey]}/edit`"
|
|
604
|
+
:to="`/${modelName}/${props.row[primaryKey]}/edit`"
|
|
605
|
+
:icon="light.styles.table.actionIcons.edit">
|
|
606
|
+
<q-tooltip>{{ $t("Edit") }}</q-tooltip>
|
|
607
|
+
</l-action-btn>
|
|
556
608
|
|
|
557
609
|
<l-delete-btn v-if="actionDelete && props.row.canDelete"
|
|
558
|
-
|
|
610
|
+
:icon="light.styles.table.actionIcons.delete"
|
|
611
|
+
@submit="onDelete(props.row[primaryKey])" size="sm">
|
|
612
|
+
<q-tooltip>{{ $t("Delete") }}</q-tooltip>
|
|
613
|
+
</l-delete-btn>
|
|
559
614
|
|
|
560
615
|
<slot name="actions" v-bind="props"></slot>
|
|
561
616
|
</div>
|
|
@@ -648,7 +703,14 @@ const hasFilters = computed(() => {
|
|
|
648
703
|
|
|
649
704
|
<template v-if="col.searchable">
|
|
650
705
|
|
|
651
|
-
<template v-if="col.searchType == 'number'">
|
|
706
|
+
<template v-if="col.searchMethod == 'range' && (!col.searchType || col.searchType == 'number')">
|
|
707
|
+
<l-search-number
|
|
708
|
+
v-model="filters[col.name]"
|
|
709
|
+
@change="onFilters"
|
|
710
|
+
/>
|
|
711
|
+
</template>
|
|
712
|
+
|
|
713
|
+
<template v-else-if="col.searchType == 'number'">
|
|
652
714
|
<q-input style="min-width: 80px;" dense clearable filled square
|
|
653
715
|
v-model.number="tempFilters[col.name]"
|
|
654
716
|
@keydown.enter.prevent="filters[col.name] = tempFilters[col.name]"
|
|
@@ -677,9 +739,9 @@ const hasFilters = computed(() => {
|
|
|
677
739
|
|
|
678
740
|
</template>
|
|
679
741
|
|
|
680
|
-
<template v-if="!col.searchType || col.searchType == 'text'">
|
|
742
|
+
<template v-if="(!col.searchType || col.searchType == 'text') && col.searchMethod != 'range'">
|
|
681
743
|
<q-input style="min-width: 80px;" dense clearable filled square
|
|
682
|
-
v-model="tempFilters[col.name]" @keydown.enter.prevent="filters[col.name] = tempFilters[col.name]" @clear="delete filters[col.name]" :enterkeyhint="$t('search')" :style="col.searchStyle"></q-input>
|
|
744
|
+
v-model="tempFilters[col.name]" @keydown.enter.prevent="filters[col.name] = tempFilters[col.name]" @clear="delete filters[col.name]" :enterkeyhint="$t('search')" :placeholder="col.searchPlaceholder" :style="col.searchStyle"></q-input>
|
|
683
745
|
|
|
684
746
|
</template>
|
|
685
747
|
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import type { Component } from "vue";
|
|
2
2
|
import { Dialog } from 'quasar';
|
|
3
3
|
import type { QTableColumn, QTableProps } from 'quasar';
|
|
4
|
+
export interface SearchOptionsContext {
|
|
5
|
+
filters: Record<string, any>;
|
|
6
|
+
columnValue: any;
|
|
7
|
+
column: LTableColumn;
|
|
8
|
+
}
|
|
9
|
+
export type SearchOptionsResolver = (ctx: SearchOptionsContext) => Promise<Record<string, any>[]>;
|
|
4
10
|
export type LTableColumn = QTableColumn & {
|
|
5
11
|
searchType?: "date" | "text" | "number" | "select" | "boolean";
|
|
6
12
|
searchable?: boolean;
|
|
7
|
-
|
|
13
|
+
searchPlaceholder?: string;
|
|
14
|
+
searchOptions?: Record<string, any>[] | SearchOptionsResolver;
|
|
15
|
+
searchDependsOn?: string[];
|
|
8
16
|
searchMultiple?: boolean;
|
|
9
17
|
searchIndex?: string;
|
|
10
18
|
component?: Component;
|
|
@@ -15,7 +23,7 @@ export type LTableColumn = QTableColumn & {
|
|
|
15
23
|
* @deprecated use gql instead
|
|
16
24
|
*/
|
|
17
25
|
gqlField?: string | Array<string> | Object;
|
|
18
|
-
searchMethod?: "equals" | "contains";
|
|
26
|
+
searchMethod?: "equals" | "contains" | "range";
|
|
19
27
|
autoWidth?: boolean;
|
|
20
28
|
};
|
|
21
29
|
export type LTableProps = QTableProps & {
|
|
@@ -71,17 +79,17 @@ export interface LTableRequest {
|
|
|
71
79
|
}) => void;
|
|
72
80
|
}
|
|
73
81
|
declare function requestServerInteraction(): void;
|
|
74
|
-
declare var
|
|
82
|
+
declare var __VLS_135: any, __VLS_138: string, __VLS_139: any, __VLS_170: any, __VLS_173: any, __VLS_343: string, __VLS_344: any;
|
|
75
83
|
type __VLS_Slots = {} & {
|
|
76
|
-
[K in NonNullable<typeof
|
|
84
|
+
[K in NonNullable<typeof __VLS_138>]?: (props: typeof __VLS_139) => any;
|
|
77
85
|
} & {
|
|
78
|
-
[K in NonNullable<typeof
|
|
86
|
+
[K in NonNullable<typeof __VLS_343>]?: (props: typeof __VLS_344) => any;
|
|
79
87
|
} & {
|
|
80
|
-
actions?: (props: typeof
|
|
88
|
+
actions?: (props: typeof __VLS_135) => any;
|
|
81
89
|
} & {
|
|
82
|
-
'row-expand'?: (props: typeof
|
|
90
|
+
'row-expand'?: (props: typeof __VLS_170) => any;
|
|
83
91
|
} & {
|
|
84
|
-
'top-right'?: (props: typeof
|
|
92
|
+
'top-right'?: (props: typeof __VLS_173) => any;
|
|
85
93
|
};
|
|
86
94
|
declare const __VLS_base: import("vue").DefineComponent<LTableProps, {
|
|
87
95
|
requestServerInteraction: typeof requestServerInteraction;
|
|
@@ -40,7 +40,12 @@ const light = reactive({
|
|
|
40
40
|
dense: true,
|
|
41
41
|
flat: true,
|
|
42
42
|
bordered: true,
|
|
43
|
-
separator: "cell"
|
|
43
|
+
separator: "cell",
|
|
44
|
+
actionIcons: {
|
|
45
|
+
view: "sym_o_search",
|
|
46
|
+
edit: "sym_o_edit",
|
|
47
|
+
delete: "sym_o_delete"
|
|
48
|
+
}
|
|
44
49
|
},
|
|
45
50
|
card: {
|
|
46
51
|
flat: true,
|
|
@@ -220,6 +225,10 @@ const light = reactive({
|
|
|
220
225
|
light.styles.button = { ...light.styles.button, ...styles.button || {} };
|
|
221
226
|
light.styles.table = { ...light.styles.table, ...styles.table || {} };
|
|
222
227
|
light.styles.table.color = light.color;
|
|
228
|
+
light.styles.table.actionIcons = {
|
|
229
|
+
...light.styles.table.actionIcons,
|
|
230
|
+
...styles.table?.actionIcons || {}
|
|
231
|
+
};
|
|
223
232
|
watch(() => light.theme, async () => {
|
|
224
233
|
await light.setStyle("theme", light.theme);
|
|
225
234
|
});
|
|
@@ -60,27 +60,37 @@ const rows = computed(() => {
|
|
|
60
60
|
const onUpdate = async (value, role, permission) => {
|
|
61
61
|
const updateKey = `${role}-${permission}`;
|
|
62
62
|
updating.value.add(updateKey);
|
|
63
|
+
const roleObj = app.value?.roles.find((r) => r.name === role);
|
|
64
|
+
if (!roleObj) {
|
|
65
|
+
updating.value.delete(updateKey);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const originalPermissions = [...roleObj.permissions];
|
|
69
|
+
if (value) {
|
|
70
|
+
if (!roleObj.permissions.includes(permission)) {
|
|
71
|
+
roleObj.permissions = [...roleObj.permissions, permission];
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
if (roleObj.permissions.includes(permission)) {
|
|
75
|
+
roleObj.permissions = roleObj.permissions.filter((p) => p !== permission);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
63
78
|
try {
|
|
64
79
|
if (value) {
|
|
65
80
|
await m("addPermission", { value: permission, role });
|
|
66
|
-
$q.notify({
|
|
67
|
-
type: "positive",
|
|
68
|
-
message: t("Permission granted successfully")
|
|
69
|
-
});
|
|
70
81
|
} else {
|
|
71
82
|
await m("removePermission", { value: permission, role });
|
|
72
|
-
$q.notify({
|
|
73
|
-
type: "positive",
|
|
74
|
-
message: t("Permission removed successfully")
|
|
75
|
-
});
|
|
76
83
|
}
|
|
77
|
-
|
|
84
|
+
$q.notify({
|
|
85
|
+
type: "positive",
|
|
86
|
+
message: t(value ? "Permission granted successfully" : "Permission removed successfully")
|
|
87
|
+
});
|
|
78
88
|
} catch (error) {
|
|
89
|
+
roleObj.permissions = originalPermissions;
|
|
79
90
|
$q.notify({
|
|
80
91
|
type: "negative",
|
|
81
92
|
message: t("Failed to update permission")
|
|
82
93
|
});
|
|
83
|
-
app.value = await fetchApp();
|
|
84
94
|
} finally {
|
|
85
95
|
updating.value.delete(updateKey);
|
|
86
96
|
}
|
|
@@ -94,8 +104,9 @@ const toggleAllForRole = async (role, grant) => {
|
|
|
94
104
|
}
|
|
95
105
|
}
|
|
96
106
|
if (updates.length === 0) return;
|
|
107
|
+
const originalPermissions = [...role.permissions];
|
|
108
|
+
role.permissions = grant ? [...app.value.permissions] : [];
|
|
97
109
|
try {
|
|
98
|
-
loading.value = true;
|
|
99
110
|
for (const update of updates) {
|
|
100
111
|
if (update.grant) {
|
|
101
112
|
await m("addPermission", { value: update.permission, role: update.role });
|
|
@@ -103,18 +114,16 @@ const toggleAllForRole = async (role, grant) => {
|
|
|
103
114
|
await m("removePermission", { value: update.permission, role: update.role });
|
|
104
115
|
}
|
|
105
116
|
}
|
|
106
|
-
app.value = await fetchApp();
|
|
107
117
|
$q.notify({
|
|
108
118
|
type: "positive",
|
|
109
119
|
message: t(grant ? "All permissions granted" : "All permissions removed")
|
|
110
120
|
});
|
|
111
121
|
} catch (error) {
|
|
122
|
+
role.permissions = originalPermissions;
|
|
112
123
|
$q.notify({
|
|
113
124
|
type: "negative",
|
|
114
125
|
message: t("Failed to update permissions")
|
|
115
126
|
});
|
|
116
|
-
} finally {
|
|
117
|
-
loading.value = false;
|
|
118
127
|
}
|
|
119
128
|
};
|
|
120
129
|
const filter = ref("");
|
package/dist/runtime/plugin.js
CHANGED
|
@@ -3,7 +3,7 @@ import "@quasar/quasar-ui-qmarkdown/dist/index.css";
|
|
|
3
3
|
import { createClient, setApiClient, defineModel } from "@hostlink/light";
|
|
4
4
|
import { createI18n } from "vue-i18n";
|
|
5
5
|
import createLight from "./composables/createLight.js";
|
|
6
|
-
import { defineNuxtPlugin, useRoute } from "#app";
|
|
6
|
+
import { defineNuxtPlugin, useRoute, useRuntimeConfig } from "#app";
|
|
7
7
|
import "./assets/main.css";
|
|
8
8
|
import message_en from "./locales/en.json";
|
|
9
9
|
import message_zh from "./locales/zh-hk.json";
|
|
@@ -22,14 +22,14 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
22
22
|
defineModel("Config", {}).setDataPath("app.listConfig");
|
|
23
23
|
nuxtApp.vueApp.config.errorHandler = (err, instance, info) => {
|
|
24
24
|
const $route = useRoute();
|
|
25
|
-
const
|
|
26
|
-
if (
|
|
25
|
+
const light2 = useLight();
|
|
26
|
+
if (light2.devMode) {
|
|
27
27
|
console.log(err);
|
|
28
28
|
}
|
|
29
29
|
if (err instanceof Error) {
|
|
30
|
-
|
|
30
|
+
light2.addError(err.message + " at " + $route.fullPath);
|
|
31
31
|
} else {
|
|
32
|
-
|
|
32
|
+
light2.addError(JSON.stringify(err) + " at " + $route.fullPath);
|
|
33
33
|
}
|
|
34
34
|
};
|
|
35
35
|
nuxtApp.vueApp.use(QMarkdownVuePlugin);
|
|
@@ -44,6 +44,14 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
44
44
|
fallbackWarn: false,
|
|
45
45
|
missingWarn: false
|
|
46
46
|
});
|
|
47
|
+
const light = useLight();
|
|
48
|
+
const runtimeLight = useRuntimeConfig().public.light;
|
|
49
|
+
if (runtimeLight?.tableActionIcons) {
|
|
50
|
+
const t = runtimeLight.tableActionIcons;
|
|
51
|
+
if (t.view) light.styles.table.actionIcons.view = t.view;
|
|
52
|
+
if (t.edit) light.styles.table.actionIcons.edit = t.edit;
|
|
53
|
+
if (t.delete) light.styles.table.actionIcons.delete = t.delete;
|
|
54
|
+
}
|
|
47
55
|
nuxtApp.vueApp.use(createLight({
|
|
48
56
|
i18n
|
|
49
57
|
}));
|