@hostlink/nuxt-light 1.22.1 → 1.22.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/module.json +1 -1
- package/dist/runtime/components/l-file-manager-labels.vue +6 -1
- package/dist/runtime/components/l-file-manager.vue +5 -5
- package/dist/runtime/components/l-group-select.vue +115 -0
- package/dist/runtime/components/l-login.vue +3 -3
- package/dist/runtime/formkit/GroupSelect.vue +34 -0
- package/dist/runtime/formkit/index.js +6 -0
- package/dist/runtime/pages/System/index.vue +22 -1
- package/dist/runtime/pages/System/menu/index.vue +2 -4
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -145,6 +145,7 @@ function toggleLeftDrawer() {
|
|
|
145
145
|
leftDrawerOpen.value = !leftDrawerOpen.value;
|
|
146
146
|
}
|
|
147
147
|
const loadItems = async () => {
|
|
148
|
+
|
|
148
149
|
let path = selectedPath.value;
|
|
149
150
|
|
|
150
151
|
files.value = [];
|
|
@@ -201,13 +202,15 @@ const loadItems = async () => {
|
|
|
201
202
|
item.id = selectedDrive.value + "/" + item.path;
|
|
202
203
|
return item;
|
|
203
204
|
});
|
|
205
|
+
reloadTreeFolder(selectedNodePath.value, folders.value);
|
|
204
206
|
}
|
|
205
207
|
|
|
208
|
+
|
|
206
209
|
loading.value = false;
|
|
207
210
|
|
|
208
211
|
|
|
209
212
|
|
|
210
|
-
|
|
213
|
+
|
|
211
214
|
}
|
|
212
215
|
|
|
213
216
|
|
|
@@ -292,6 +295,7 @@ const findFolder = (path, folders) => {
|
|
|
292
295
|
|
|
293
296
|
const reloadTreeFolder = (path, newFolders) => {
|
|
294
297
|
let node = folderTree.value.getNodeByKey(path);
|
|
298
|
+
|
|
295
299
|
if (node) {
|
|
296
300
|
node.lazy = false;
|
|
297
301
|
node.children = newFolders;
|
|
@@ -876,7 +880,3 @@ selectedNodePath.value = drives[0].index.toString();
|
|
|
876
880
|
</q-page-container>
|
|
877
881
|
</q-layout>
|
|
878
882
|
</template>
|
|
879
|
-
|
|
880
|
-
<style>
|
|
881
|
-
.q-item--active{background-color:#e0e0e0}
|
|
882
|
-
</style>
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import collect from "collect.js";
|
|
3
|
+
|
|
4
|
+
import { computed, onMounted, useAttrs, ref } from "vue";
|
|
5
|
+
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
options: {
|
|
8
|
+
type: Array,
|
|
9
|
+
required: true,
|
|
10
|
+
},
|
|
11
|
+
emitValue: {
|
|
12
|
+
type: Boolean,
|
|
13
|
+
default: true,
|
|
14
|
+
},
|
|
15
|
+
mapOptions: {
|
|
16
|
+
type: Boolean,
|
|
17
|
+
default: true,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const userString = ref("");
|
|
22
|
+
const items = computed(() => {
|
|
23
|
+
|
|
24
|
+
//filter out items based on user input
|
|
25
|
+
|
|
26
|
+
const filteredItems = collect(props.options)
|
|
27
|
+
.filter((item) => {
|
|
28
|
+
if (userString.value === "") {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
return item.label.toLowerCase().includes(userString.value);
|
|
32
|
+
})
|
|
33
|
+
.toArray();
|
|
34
|
+
|
|
35
|
+
const groups = collect(filteredItems).unique("group").pluck("group").sort();
|
|
36
|
+
|
|
37
|
+
const options = collect();
|
|
38
|
+
groups.each((group) => {
|
|
39
|
+
const items = collect(filteredItems).where("group", group).toArray();
|
|
40
|
+
options
|
|
41
|
+
.push({ label: group, is_group: true, disable: true })
|
|
42
|
+
.push(...items);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return options.toArray();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const model = defineModel();
|
|
49
|
+
|
|
50
|
+
const attrs = useAttrs();
|
|
51
|
+
const handleItemUniqueByGroup = (items) => {
|
|
52
|
+
return;
|
|
53
|
+
if (!attrs.hasOwnProperty("multiple") || attrs.multiple === false) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
/*
|
|
57
|
+
model.value = collect(items)
|
|
58
|
+
.unique((item) => {
|
|
59
|
+
// Allow selecting multiple items that are not grouped
|
|
60
|
+
if (item.group === null) {
|
|
61
|
+
return item.value;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return item.group;
|
|
65
|
+
})
|
|
66
|
+
.toArray(); */
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
onMounted(() => {
|
|
70
|
+
//這個功能是為了避免在多選的情況下,選擇同一個group的item
|
|
71
|
+
// handleItemUniqueByGroup(model.value);
|
|
72
|
+
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const filterFn = (val, update, abort) => {
|
|
76
|
+
if (val === "") {
|
|
77
|
+
update(() => {
|
|
78
|
+
userString.value = "";
|
|
79
|
+
});
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const needle = val.toLowerCase();
|
|
84
|
+
update(() => {
|
|
85
|
+
userString.value = needle;
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<template>
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
<q-select v-model="model" v-bind="$light.getInputProps($props)" :options="items" @update:model-value="handleItemUniqueByGroup"
|
|
95
|
+
input-debounce="0" @filter="filterFn" :color="$light.color" :style="$light.styles.input"
|
|
96
|
+
:emit-value="emitValue" :map-options="mapOptions">
|
|
97
|
+
<template v-slot:option="scope">
|
|
98
|
+
<q-item v-if="scope.opt.is_group" v-bind="{ ...scope.itemProps }" class="text-xs bg-grey-4" :key="scope.opt.label">
|
|
99
|
+
<q-item-section class="!cursor-default">
|
|
100
|
+
{{ scope.opt.label || "Ungroup" }}
|
|
101
|
+
</q-item-section>
|
|
102
|
+
</q-item>
|
|
103
|
+
<q-item v-else v-bind="{ ...scope.itemProps }" :key="scope.opt.value">
|
|
104
|
+
<q-item-section class="q-pl-md">{{ scope.opt.label }}</q-item-section>
|
|
105
|
+
</q-item>
|
|
106
|
+
</template>
|
|
107
|
+
<template v-slot:no-option>
|
|
108
|
+
<q-item>
|
|
109
|
+
<q-item-section class="text-grey">
|
|
110
|
+
No results
|
|
111
|
+
</q-item-section>
|
|
112
|
+
</q-item>
|
|
113
|
+
</template>
|
|
114
|
+
</q-select>
|
|
115
|
+
</template>
|
|
@@ -293,20 +293,20 @@ const facebookLogin = (accessToken) => {
|
|
|
293
293
|
</div>
|
|
294
294
|
<q-form ref="form1" v-if="passwordBasedEnabled">
|
|
295
295
|
<div class="q-gutter-sm">
|
|
296
|
-
<l-input v-model.trim="data.username" label="Username" :rules="[v => !!v || $t('Username is required')]"
|
|
296
|
+
<l-input color="primary" v-model.trim="data.username" label="Username" :rules="[v => !!v || $t('Username is required')]"
|
|
297
297
|
clearable :outlined="false" stackLabel autocomplete="username">
|
|
298
298
|
<template v-slot:prepend>
|
|
299
299
|
<q-icon name="sym_o_person" />
|
|
300
300
|
</template>
|
|
301
301
|
</l-input>
|
|
302
|
-
<l-input v-model="data.password" label="Password" type="password" clearable show-password stackLabel
|
|
302
|
+
<l-input color="primary" v-model="data.password" label="Password" type="password" clearable show-password stackLabel
|
|
303
303
|
:rules="[v => !!v || $t('Password is required')]" @keydown.enter.prevent="submit" :outlined="false"
|
|
304
304
|
autocomplete="off">
|
|
305
305
|
<template v-slot:prepend>
|
|
306
306
|
<q-icon name="sym_o_lock" />
|
|
307
307
|
</template>
|
|
308
308
|
</l-input>
|
|
309
|
-
<l-input v-if="twoFactorAuthentication" v-model="data.code" label="2FA code"
|
|
309
|
+
<l-input color="primary" v-if="twoFactorAuthentication" v-model="data.code" label="2FA code"
|
|
310
310
|
:rules="[v => !!v || $t('2FA code is required')]" type="text" clearable stackLabel :outlined="false">
|
|
311
311
|
<template v-slot:prepend>
|
|
312
312
|
<q-icon name="sym_o_key" />
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { getErrorMessage } from 'formkit-quasar';
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
context: Object
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
const { error, errorMessage } = getErrorMessage(props.context.node);
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
const value = computed({
|
|
13
|
+
get: () => props.context.value,
|
|
14
|
+
set: (val) => props.context.node.input(val)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
let clearable = false;
|
|
19
|
+
if (props.context.state.required) { //no clearable
|
|
20
|
+
clearable = false;
|
|
21
|
+
} else {
|
|
22
|
+
clearable = true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
</script>
|
|
26
|
+
<template>
|
|
27
|
+
<l-group-select v-model="value" :label="context.label" v-bind="context.attrs" :error="error"
|
|
28
|
+
:error-message="errorMessage" :required="context.state.required"
|
|
29
|
+
:disable="context.disabled">
|
|
30
|
+
<template v-for="(s, name) in $slots" v-slot:[name]="props" :key="name">
|
|
31
|
+
<slot :name="name" v-bind="props ?? {}"></slot>
|
|
32
|
+
</template>
|
|
33
|
+
</l-group-select>
|
|
34
|
+
</template>
|
|
@@ -14,6 +14,7 @@ import InputXlsxVue from "./InputXlsx.vue";
|
|
|
14
14
|
import FileUploadVue from "./FileUpload.vue";
|
|
15
15
|
import EditorVue from "./Editor.vue";
|
|
16
16
|
import ToggleVue from "./Toggle.vue";
|
|
17
|
+
import GroupSelect from "./GroupSelect.vue";
|
|
17
18
|
export const createLightPlugin = () => {
|
|
18
19
|
return (node) => {
|
|
19
20
|
let type = node.props.type + "";
|
|
@@ -79,6 +80,11 @@ export const createLightPlugin = () => {
|
|
|
79
80
|
type: "input",
|
|
80
81
|
component: SelectVue
|
|
81
82
|
});
|
|
83
|
+
case "l-group-select":
|
|
84
|
+
return node.define({
|
|
85
|
+
type: "input",
|
|
86
|
+
component: GroupSelect
|
|
87
|
+
});
|
|
82
88
|
case "l-repeater":
|
|
83
89
|
return node.define({
|
|
84
90
|
type: "input",
|
|
@@ -2,7 +2,14 @@
|
|
|
2
2
|
import { q } from '#imports'
|
|
3
3
|
const { system } = await q({
|
|
4
4
|
system: {
|
|
5
|
-
server: true
|
|
5
|
+
server: true,
|
|
6
|
+
arch: true,
|
|
7
|
+
os: true,
|
|
8
|
+
CPUCores: true,
|
|
9
|
+
hostname: true,
|
|
10
|
+
memoryTotal: true,
|
|
11
|
+
memoryFree: true,
|
|
12
|
+
memoryAvailable: true,
|
|
6
13
|
}
|
|
7
14
|
})
|
|
8
15
|
|
|
@@ -22,6 +29,20 @@ const columns = [
|
|
|
22
29
|
</script>
|
|
23
30
|
<template>
|
|
24
31
|
<l-page>
|
|
32
|
+
<l-card>
|
|
33
|
+
<l-list>
|
|
34
|
+
<l-item label="Arch">{{ system.arch }}</l-item>
|
|
35
|
+
<l-item label="OS">{{ system.os }}</l-item>
|
|
36
|
+
<l-item label="CPUCores">{{ system.CPUCores }}</l-item>
|
|
37
|
+
<l-item label="Hostname">{{ system.hostname }}</l-item>
|
|
38
|
+
<l-item label="Memory Total">{{ system.memoryTotal }}</l-item>
|
|
39
|
+
<l-item label="Memory Free">{{ system.memoryFree }}</l-item>
|
|
40
|
+
<l-item label="Memory Available">{{ system.memoryAvailable }}</l-item>
|
|
41
|
+
</l-list>
|
|
42
|
+
</l-card>
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
25
46
|
<l-table searchable :rows="system.server" :columns="columns" :rows-per-page-options="[0]"
|
|
26
47
|
hide-pagination></l-table>
|
|
27
48
|
</l-page>
|
|
@@ -237,7 +237,7 @@ const menusOnly = computed(() => {
|
|
|
237
237
|
</q-dialog>
|
|
238
238
|
|
|
239
239
|
<l-card>
|
|
240
|
-
<q-
|
|
240
|
+
<q-card-actions class="q-gutter-y-sm">
|
|
241
241
|
<l-btn @click="onSave" label="Save" icon="sym_o_save" />
|
|
242
242
|
<l-btn @click="onReload" label="Reload" icon="sym_o_refresh" />
|
|
243
243
|
|
|
@@ -298,7 +298,7 @@ const menusOnly = computed(() => {
|
|
|
298
298
|
</q-btn-dropdown>
|
|
299
299
|
</template>
|
|
300
300
|
</template>
|
|
301
|
-
</q-
|
|
301
|
+
</q-card-actions>
|
|
302
302
|
<q-splitter v-model="splitterModel" style="height:680px">
|
|
303
303
|
<template #before>
|
|
304
304
|
|
|
@@ -360,8 +360,6 @@ const menusOnly = computed(() => {
|
|
|
360
360
|
|
|
361
361
|
</q-splitter>
|
|
362
362
|
|
|
363
|
-
|
|
364
|
-
|
|
365
363
|
</l-card>
|
|
366
364
|
|
|
367
365
|
</l-page>
|