@hostlink/nuxt-light 1.1.8 → 1.3.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.json +1 -1
- package/dist/runtime/components/l-app-main.vue +96 -26
- package/dist/runtime/components/l-btn.vue +13 -15
- package/dist/runtime/components/l-card.vue +85 -10
- package/dist/runtime/components/l-customizer.vue +18 -1
- package/dist/runtime/components/l-fav-menu.vue +21 -0
- package/dist/runtime/components/l-file-upload.vue +5 -1
- package/dist/runtime/components/l-icon-picker.vue +3331 -0
- package/dist/runtime/components/l-login.vue +3 -3
- package/dist/runtime/components/l-menu.vue +4 -3
- package/dist/runtime/components/l-small-box.vue +4 -4
- package/dist/runtime/components/l-tabs.vue +4 -2
- package/dist/runtime/components/l-user-eventlog.vue +29 -0
- package/dist/runtime/components/l-user-overview.vue +79 -0
- package/dist/runtime/components/l-user-userlog.vue +13 -0
- package/dist/runtime/formkit/Select.vue +1 -1
- package/dist/runtime/index.d.ts +46 -2
- package/dist/runtime/index.mjs +35 -1
- package/dist/runtime/locales/zh-hk.json +3 -0
- package/dist/runtime/pages/System/menu/index.vue +136 -99
- package/dist/runtime/pages/System/setting.vue +96 -57
- package/dist/runtime/pages/User/_user_id/view.vue +28 -42
- package/dist/runtime/pages/User/profile.vue +47 -24
- package/dist/runtime/pages/User/setting/my_favorite.vue +74 -0
- package/dist/runtime/pages/User/setting.vue +4 -1
- package/dist/runtime/plugin.mjs +4 -2
- package/dist/runtime/routes.mjs +10 -0
- package/package.json +1 -1
|
@@ -194,16 +194,16 @@ onMounted(() => {
|
|
|
194
194
|
<q-form ref="form1">
|
|
195
195
|
<div class="q-gutter-sm">
|
|
196
196
|
<l-input v-model.trim="data.username" label="Username" :rules="[v => !!v || $t('Username is required')]"
|
|
197
|
-
clearable :outlined="false" stackLabel/>
|
|
197
|
+
clearable :outlined="false" stackLabel autocomplete="username" />
|
|
198
198
|
<l-input v-model="data.password" label="Password" type="password" clearable show-password stackLabel
|
|
199
|
-
:rules="[v => !!v || $t('Password is required')]" @keydown.enter.prevent="submit" :outlined="false" />
|
|
199
|
+
:rules="[v => !!v || $t('Password is required')]" @keydown.enter.prevent="submit" :outlined="false" autocomplete="current-password" />
|
|
200
200
|
<l-input v-if="twoFactorAuthentication" v-model="data.code" label="2FA code" required type="text" clearable>
|
|
201
201
|
</l-input>
|
|
202
202
|
</div>
|
|
203
203
|
</q-form>
|
|
204
204
|
</q-card-section>
|
|
205
205
|
<q-card-actions>
|
|
206
|
-
<l-btn label="Login" outline rounded color="primary" icon="sym_o_login" @click="submit"
|
|
206
|
+
<l-btn label="Login" outline rounded color="primary" icon="sym_o_login" @click="submit"/>
|
|
207
207
|
<l-btn v-if="hasBioLogin" outline rounded color="primary" icon="sym_o_fingerprint" @click="bioLogin" />
|
|
208
208
|
<l-btn label="Forget password" outline rounded color="primary" icon="sym_o_lock_reset" @click="forgetPassword" />
|
|
209
209
|
</q-card-actions>
|
|
@@ -3,7 +3,6 @@ import { ref } from 'vue'
|
|
|
3
3
|
const props = defineProps(["value", "dense"])
|
|
4
4
|
const menus = ref(null);
|
|
5
5
|
|
|
6
|
-
|
|
7
6
|
const expansion = ref(null);
|
|
8
7
|
|
|
9
8
|
const onShowChild = (menu) => {
|
|
@@ -28,7 +27,7 @@ defineExpose({
|
|
|
28
27
|
</script>
|
|
29
28
|
|
|
30
29
|
<style scoped>
|
|
31
|
-
.menu-list .q-item{border-radius:
|
|
30
|
+
.menu-list .q-item{border-radius:12px 12px 12px 12px}.menu-list .q-router-link--exact-active{background:linear-gradient(118deg,var(--q-primary),rgba(115,103,240,.7));color:#fff}
|
|
32
31
|
</style>
|
|
33
32
|
<template>
|
|
34
33
|
<q-expansion-item v-if="value.children?.length > 0" :label="$t(value.label)" :icon="value.icon" :dense="dense"
|
|
@@ -39,7 +38,9 @@ defineExpose({
|
|
|
39
38
|
</q-list>
|
|
40
39
|
</q-expansion-item>
|
|
41
40
|
<q-list v-else class="menu-list" :dense="dense">
|
|
42
|
-
<q-
|
|
41
|
+
<q-separator v-if="value.type == 'separator'" :spaced="value.spaced" />
|
|
42
|
+
<q-item-label header v-if="value.type=='header'">{{ value.label }}</q-item-label>
|
|
43
|
+
<q-item v-ripple :to="value.to" v-if="!value.type" >
|
|
43
44
|
<q-item-section avatar>
|
|
44
45
|
<q-icon :name="value.icon" />
|
|
45
46
|
</q-item-section>
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import { computed } from "vue"
|
|
3
3
|
|
|
4
4
|
export interface LSmallBoxProps {
|
|
5
|
-
title
|
|
6
|
-
subtitle
|
|
7
|
-
icon
|
|
8
|
-
color
|
|
5
|
+
title?: string;
|
|
6
|
+
subtitle?: string;
|
|
7
|
+
icon?: string;
|
|
8
|
+
color?: 'red' | 'pink' | 'purple' | 'deep-purple' | 'indigo' | 'blue' | 'light-blue' | 'cyan' | 'teal' | 'green' | 'light-green' | 'lime' | 'yellow' | 'amber' | 'orange' | 'deep-orange' | 'brown' | 'grey' | 'blue-grey'
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
const props = withDefaults(defineProps<LSmallBoxProps>(), {
|
|
@@ -37,13 +37,15 @@ const localValue = computed({
|
|
|
37
37
|
}
|
|
38
38
|
})
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
|
|
42
42
|
</script>
|
|
43
43
|
|
|
44
44
|
<template>
|
|
45
45
|
<l-card>
|
|
46
|
-
|
|
46
|
+
|
|
47
|
+
<q-tabs class="text-grey" :active-color="$light.color" :indicator-color="$light.color" align="justify"
|
|
48
|
+
v-model="localValue">
|
|
47
49
|
<q-tab v-for="tab in tabContents" :label="$t(tab.label)" :name="tab.name"></q-tab>
|
|
48
50
|
</q-tabs>
|
|
49
51
|
<q-tab-panels v-model="localValue">
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { model, q } from "#imports";
|
|
3
|
+
const props = defineProps(['id']);
|
|
4
|
+
</script>
|
|
5
|
+
<template>
|
|
6
|
+
<div>
|
|
7
|
+
<l-table row-key="eventlog_id" sort-by="eventlog_id:desc"
|
|
8
|
+
:columns="model('EventLog').columns(['eventlog_id', 'class', 'id', 'action', 'created_time'])" @request="async (req) => {
|
|
9
|
+
const a = {
|
|
10
|
+
listUser: {
|
|
11
|
+
__args: {
|
|
12
|
+
filters: {
|
|
13
|
+
user_id: props.id
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
data: {
|
|
17
|
+
__args: {
|
|
18
|
+
limit: 1
|
|
19
|
+
},
|
|
20
|
+
eventLog: req.gql
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let resp = await q(a);
|
|
26
|
+
req.setData(resp.listUser.data[0].eventLog)
|
|
27
|
+
}" />
|
|
28
|
+
</div>
|
|
29
|
+
</template>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
|
|
3
|
+
import { model } from "#imports";
|
|
4
|
+
const props = defineProps(['id']);
|
|
5
|
+
|
|
6
|
+
const obj = await model('User').get({ user_id: props.id }, ["user_id", "username", "first_name", "last_name", "email", "phone", "roles", 'status', 'join_date'])
|
|
7
|
+
|
|
8
|
+
</script>
|
|
9
|
+
<template>
|
|
10
|
+
<l-card class="q-mb-md">
|
|
11
|
+
<q-card-section>
|
|
12
|
+
<l-row>
|
|
13
|
+
<l-col md="5">
|
|
14
|
+
<q-list dense separator>
|
|
15
|
+
<q-item>
|
|
16
|
+
<q-item-section>
|
|
17
|
+
<q-item-label>{{ $t('Username') }}</q-item-label>
|
|
18
|
+
</q-item-section>
|
|
19
|
+
<q-item-section>{{ obj.username }}</q-item-section>
|
|
20
|
+
</q-item>
|
|
21
|
+
<q-item>
|
|
22
|
+
<q-item-section>
|
|
23
|
+
<q-item-label>{{ $t('First name') }}</q-item-label>
|
|
24
|
+
</q-item-section>
|
|
25
|
+
<q-item-section>{{ obj.first_name }}</q-item-section>
|
|
26
|
+
</q-item>
|
|
27
|
+
<q-item>
|
|
28
|
+
<q-item-section>
|
|
29
|
+
<q-item-label>{{ $t('Last name') }}</q-item-label>
|
|
30
|
+
</q-item-section>
|
|
31
|
+
<q-item-section>{{ obj.last_name }}</q-item-section>
|
|
32
|
+
</q-item>
|
|
33
|
+
<q-item>
|
|
34
|
+
<q-item-section>
|
|
35
|
+
<q-item-label>{{ $t('Email') }}</q-item-label>
|
|
36
|
+
</q-item-section>
|
|
37
|
+
<q-item-section>{{ obj.email }}</q-item-section>
|
|
38
|
+
</q-item>
|
|
39
|
+
<q-item>
|
|
40
|
+
<q-item-section>
|
|
41
|
+
<q-item-label>{{ $t('Phone') }}</q-item-label>
|
|
42
|
+
</q-item-section>
|
|
43
|
+
<q-item-section>{{ obj.phone }}</q-item-section>
|
|
44
|
+
</q-item>
|
|
45
|
+
|
|
46
|
+
<q-item>
|
|
47
|
+
<q-item-section>
|
|
48
|
+
<q-item-label>{{ $t('Roles') }}</q-item-label>
|
|
49
|
+
</q-item-section>
|
|
50
|
+
<q-item-section>
|
|
51
|
+
<div class="q-gutter-xs float-left">
|
|
52
|
+
<q-badge v-for="role in obj.roles" :key="role">{{ role }}</q-badge>
|
|
53
|
+
</div>
|
|
54
|
+
</q-item-section>
|
|
55
|
+
</q-item>
|
|
56
|
+
|
|
57
|
+
<q-item>
|
|
58
|
+
<q-item-section>
|
|
59
|
+
<q-item-label>{{ $t('Status') }}</q-item-label>
|
|
60
|
+
</q-item-section>
|
|
61
|
+
<q-item-section>{{ model('User').columns(["status"])[0].format(obj.status) }}</q-item-section>
|
|
62
|
+
</q-item>
|
|
63
|
+
|
|
64
|
+
<q-item>
|
|
65
|
+
<q-item-section>
|
|
66
|
+
<q-item-label>{{ $t('Join date') }}</q-item-label>
|
|
67
|
+
</q-item-section>
|
|
68
|
+
<q-item-section>{{ obj.join_date }}</q-item-section>
|
|
69
|
+
</q-item>
|
|
70
|
+
</q-list>
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
</l-col>
|
|
74
|
+
</l-row>
|
|
75
|
+
|
|
76
|
+
</q-card-section>
|
|
77
|
+
|
|
78
|
+
</l-card>
|
|
79
|
+
</template>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { model, q } from "#imports";
|
|
3
|
+
const props = defineProps(['id']);
|
|
4
|
+
|
|
5
|
+
const columns = model('UserLog').columns(["userlog_id", "login_dt", "last_access_time", "logout_dt", "result", "user_agent"]);
|
|
6
|
+
</script>
|
|
7
|
+
<template>
|
|
8
|
+
<div>
|
|
9
|
+
<l-table row-key="userlog_id" sort-by="userlog_id:desc" :columns="columns" @request="async (req) => {
|
|
10
|
+
req.loadObjects('UserLog', { filters: { user_id: props.id } })
|
|
11
|
+
}" />
|
|
12
|
+
</div>
|
|
13
|
+
</template>
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -1,8 +1,51 @@
|
|
|
1
|
+
declare const app: {
|
|
2
|
+
company: string;
|
|
3
|
+
companyLogo: string;
|
|
4
|
+
color: string;
|
|
5
|
+
theme: string;
|
|
6
|
+
isAdmin: boolean;
|
|
7
|
+
permissions: string[];
|
|
8
|
+
myFavorites: any[];
|
|
9
|
+
setMyFavorites: (favorites: any[]) => void;
|
|
10
|
+
reloadMyFavorites: () => Promise<void>;
|
|
11
|
+
getMyFavorites: () => any[];
|
|
12
|
+
isDarkMode: () => boolean;
|
|
13
|
+
setCompany: (company: string) => void;
|
|
14
|
+
getCompany: () => string;
|
|
15
|
+
setCompanyLogo: (logo: string) => void;
|
|
16
|
+
getCompanyLogo: () => string;
|
|
17
|
+
getVersion: () => any;
|
|
18
|
+
addError: (error: String) => void;
|
|
19
|
+
getErrors: () => String[];
|
|
20
|
+
removeError: (error: String) => void;
|
|
21
|
+
getStyle: (name: String) => any;
|
|
22
|
+
setStyles: (s: Object) => void;
|
|
23
|
+
getStyles: () => {
|
|
24
|
+
[key: string]: any;
|
|
25
|
+
};
|
|
26
|
+
setStyle: (name: String, value: any) => Promise<void>;
|
|
27
|
+
setCurrentRoute: (to: any, from: any) => void;
|
|
28
|
+
getID: () => number | null;
|
|
29
|
+
init: (styles: any) => void;
|
|
30
|
+
isGranted: (right?: string | undefined) => boolean;
|
|
31
|
+
setPermissions: (permissions: string[]) => void;
|
|
32
|
+
};
|
|
33
|
+
declare module 'vue' {
|
|
34
|
+
interface ComponentCustomProperties {
|
|
35
|
+
$light: typeof app;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
1
38
|
export declare const useLight: () => {
|
|
2
39
|
company: string;
|
|
3
40
|
companyLogo: string;
|
|
4
41
|
color: string;
|
|
5
42
|
theme: string;
|
|
43
|
+
isAdmin: boolean;
|
|
44
|
+
permissions: string[];
|
|
45
|
+
myFavorites: any[];
|
|
46
|
+
setMyFavorites: (favorites: Array<any>) => void;
|
|
47
|
+
reloadMyFavorites: () => Promise<void>;
|
|
48
|
+
getMyFavorites: () => any[];
|
|
6
49
|
isDarkMode: () => boolean;
|
|
7
50
|
setCompany: (company: string) => void;
|
|
8
51
|
getCompany: () => string;
|
|
@@ -15,12 +58,13 @@ export declare const useLight: () => {
|
|
|
15
58
|
getStyle: (name: String) => any;
|
|
16
59
|
setStyles: (s: Object) => void;
|
|
17
60
|
getStyles: () => {
|
|
18
|
-
|
|
19
|
-
color?: String | undefined;
|
|
61
|
+
[key: string]: any;
|
|
20
62
|
};
|
|
21
63
|
setStyle: (name: String, value: any) => Promise<void>;
|
|
22
64
|
setCurrentRoute: (to: any, from: any) => void;
|
|
23
65
|
getID: () => number | null;
|
|
24
66
|
init: (styles: any) => void;
|
|
67
|
+
isGranted: (right?: string) => boolean;
|
|
68
|
+
setPermissions: (permissions: Array<string>) => void;
|
|
25
69
|
};
|
|
26
70
|
export * from "./lib";
|
package/dist/runtime/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import packageJson from "../../package.json";
|
|
2
2
|
import { watch, reactive } from "vue";
|
|
3
|
-
import { m } from "./lib/index.mjs";
|
|
3
|
+
import { m, q } from "./lib/index.mjs";
|
|
4
4
|
const errors = [];
|
|
5
5
|
let styles = {};
|
|
6
6
|
let defaultStyle = {
|
|
@@ -15,6 +15,28 @@ const app = reactive({
|
|
|
15
15
|
companyLogo: "",
|
|
16
16
|
color: "primary",
|
|
17
17
|
theme: "light",
|
|
18
|
+
isAdmin: false,
|
|
19
|
+
permissions: Array(),
|
|
20
|
+
myFavorites: Array(),
|
|
21
|
+
setMyFavorites: (favorites) => {
|
|
22
|
+
app.myFavorites = favorites;
|
|
23
|
+
},
|
|
24
|
+
reloadMyFavorites: async () => {
|
|
25
|
+
const data = await q({
|
|
26
|
+
my: {
|
|
27
|
+
myFavorites: {
|
|
28
|
+
my_favorite_id: true,
|
|
29
|
+
label: true,
|
|
30
|
+
path: true,
|
|
31
|
+
icon: true
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
app.myFavorites = data.my.myFavorites;
|
|
36
|
+
},
|
|
37
|
+
getMyFavorites: () => {
|
|
38
|
+
return app.myFavorites;
|
|
39
|
+
},
|
|
18
40
|
isDarkMode: () => {
|
|
19
41
|
return app.theme == "dark";
|
|
20
42
|
},
|
|
@@ -90,6 +112,18 @@ const app = reactive({
|
|
|
90
112
|
watch(() => app.theme, async () => {
|
|
91
113
|
await app.setStyle("theme", app.theme);
|
|
92
114
|
});
|
|
115
|
+
},
|
|
116
|
+
isGranted(right) {
|
|
117
|
+
if (right === void 0)
|
|
118
|
+
return true;
|
|
119
|
+
if (app.isAdmin)
|
|
120
|
+
return true;
|
|
121
|
+
if (app.permissions.includes(right))
|
|
122
|
+
return true;
|
|
123
|
+
return false;
|
|
124
|
+
},
|
|
125
|
+
setPermissions(permissions) {
|
|
126
|
+
this.permissions = permissions;
|
|
93
127
|
}
|
|
94
128
|
});
|
|
95
129
|
let currentRoute = null;
|
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { ref, computed, inject } from 'vue';
|
|
3
|
-
import { m, q } from '
|
|
3
|
+
import { m, q } from '#imports'
|
|
4
4
|
import { useQuasar } from 'quasar';
|
|
5
5
|
|
|
6
6
|
const quasar = useQuasar();
|
|
7
|
-
const
|
|
7
|
+
const appMenus = await q("appMenus", [])
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const menus = ref([
|
|
11
|
+
{
|
|
12
|
+
label: "[Root]",
|
|
13
|
+
uuid: "ROOT",
|
|
14
|
+
children: appMenus.map(m => {
|
|
15
|
+
m.parent = 'ROOT'
|
|
16
|
+
return m
|
|
17
|
+
}),
|
|
18
|
+
type: 'root'
|
|
19
|
+
}
|
|
20
|
+
]);
|
|
8
21
|
|
|
9
|
-
const selected = ref(
|
|
22
|
+
const selected = ref('ROOT');
|
|
10
23
|
const tree1 = ref(null)
|
|
11
24
|
|
|
12
25
|
const getUUID = () => {
|
|
@@ -20,30 +33,9 @@ const getUUID = () => {
|
|
|
20
33
|
}
|
|
21
34
|
|
|
22
35
|
const onReload = async () => {
|
|
23
|
-
menus.value = await q("appMenus", []);
|
|
36
|
+
menus.value[0].children = await q("appMenus", []);
|
|
24
37
|
}
|
|
25
38
|
|
|
26
|
-
const onAdd = () => {
|
|
27
|
-
|
|
28
|
-
quasar.dialog({
|
|
29
|
-
title: 'Add Menu',
|
|
30
|
-
message: 'Enter menu label',
|
|
31
|
-
prompt: {
|
|
32
|
-
model: '',
|
|
33
|
-
},
|
|
34
|
-
cancel: true,
|
|
35
|
-
persistent: true,
|
|
36
|
-
}).onOk((data) => {
|
|
37
|
-
if (data === '') return;
|
|
38
|
-
|
|
39
|
-
menus.value.push({
|
|
40
|
-
label: data,
|
|
41
|
-
uuid: getUUID(),
|
|
42
|
-
children: [],
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
39
|
const onAddChild = (node) => {
|
|
48
40
|
|
|
49
41
|
quasar.dialog({
|
|
@@ -81,9 +73,10 @@ const onRemove = (node) => {
|
|
|
81
73
|
}
|
|
82
74
|
}
|
|
83
75
|
|
|
84
|
-
const splitterModel = ref(
|
|
76
|
+
const splitterModel = ref(20)
|
|
85
77
|
|
|
86
78
|
const selectedNode = computed(() => {
|
|
79
|
+
if (selected.value == 'ROOT') return menus.value[0];
|
|
87
80
|
return tree1.value.getNodeByKey(selected.value);
|
|
88
81
|
});
|
|
89
82
|
const reloadMenu = inject('reloadMenu')
|
|
@@ -98,7 +91,7 @@ const onSave = () => {
|
|
|
98
91
|
persistent: true,
|
|
99
92
|
}).onOk(async () => {
|
|
100
93
|
if (await m("updateAppMenus", {
|
|
101
|
-
data: menus.value
|
|
94
|
+
data: menus.value[0].children
|
|
102
95
|
})) {
|
|
103
96
|
quasar.notify({
|
|
104
97
|
message: 'Menu saved',
|
|
@@ -140,20 +133,6 @@ const onMoveConfirm = () => {
|
|
|
140
133
|
showMove.value = false;
|
|
141
134
|
|
|
142
135
|
}
|
|
143
|
-
const onMoveToRoot = () => {
|
|
144
|
-
const selectedNode = tree1.value.getNodeByKey(selected.value);
|
|
145
|
-
|
|
146
|
-
//clone selected node
|
|
147
|
-
const newNode = JSON.parse(JSON.stringify(selectedNode));
|
|
148
|
-
newNode.parent = null;
|
|
149
|
-
onRemove(selectedNode);
|
|
150
|
-
|
|
151
|
-
//add to target
|
|
152
|
-
|
|
153
|
-
menus.value.push(newNode);
|
|
154
|
-
|
|
155
|
-
showMove.value = false;
|
|
156
|
-
}
|
|
157
136
|
|
|
158
137
|
const getParentNode = (node) => {
|
|
159
138
|
if (!node.parent) return {
|
|
@@ -197,6 +176,34 @@ const onAddChildMenu = (node, type) => {
|
|
|
197
176
|
children: [],
|
|
198
177
|
});
|
|
199
178
|
}
|
|
179
|
+
|
|
180
|
+
const onAddSeparator = (node) => {
|
|
181
|
+
node.children.push({
|
|
182
|
+
label: '[Separator]',
|
|
183
|
+
uuid: getUUID(),
|
|
184
|
+
parent: node.uuid,
|
|
185
|
+
children: [],
|
|
186
|
+
type: 'separator',
|
|
187
|
+
spaced: true,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const onAddHeader = (node) => {
|
|
194
|
+
node.children.push({
|
|
195
|
+
label: '[Header]',
|
|
196
|
+
uuid: getUUID(),
|
|
197
|
+
parent: node.uuid,
|
|
198
|
+
children: [],
|
|
199
|
+
type: 'header',
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
const menusOnly = computed(() => {
|
|
205
|
+
return menus.value.filter((item) => !item.type);
|
|
206
|
+
});
|
|
200
207
|
</script>
|
|
201
208
|
|
|
202
209
|
<template>
|
|
@@ -211,79 +218,109 @@ const onAddChildMenu = (node, type) => {
|
|
|
211
218
|
<q-card-actions align="right">
|
|
212
219
|
<q-btn flat label="Cancel" color="primary" v-close-popup />
|
|
213
220
|
<q-btn flat label="Move" color="primary" @click="onMoveConfirm" />
|
|
214
|
-
|
|
221
|
+
<!-- q-btn flat label="Move to root" color="primary" @click="onMoveToRoot" /-->
|
|
215
222
|
</q-card-actions>
|
|
216
223
|
</l-card>
|
|
217
224
|
</q-dialog>
|
|
218
225
|
|
|
219
226
|
<l-card>
|
|
227
|
+
<q-card-actions v-if="selectedNode">
|
|
228
|
+
<l-btn @click="onSave" label="Save" icon="sym_o_save" />
|
|
229
|
+
<l-btn @click="onReload" label="Reload" icon="sym_o_refresh" />
|
|
230
|
+
<template v-if="selectedNode.type == 'root'">
|
|
231
|
+
<l-btn color="primary" @click="onAddChild(selectedNode)" label="Add" icon="sym_o_add" />
|
|
232
|
+
<l-btn color="primary" @click="onAddHeader(selectedNode)" label="Add Header" icon="sym_o_add" />
|
|
233
|
+
<l-btn color="primary" @click="onAddSeparator(selectedNode)" label="Add Separator" icon="sym_o_add" />
|
|
234
|
+
</template>
|
|
235
|
+
|
|
236
|
+
<template v-else>
|
|
237
|
+
<q-btn-dropdown color="primary" label="Add" icon="sym_o_add">
|
|
238
|
+
<q-list>
|
|
239
|
+
<q-item clickable v-close-popup @click="onAddChild(selectedNode)">
|
|
240
|
+
<q-item-section>
|
|
241
|
+
<q-item-label>Child</q-item-label>
|
|
242
|
+
</q-item-section>
|
|
243
|
+
</q-item>
|
|
244
|
+
<q-item clickable v-close-popup @click="onAddHeader(selectedNode)">
|
|
245
|
+
<q-item-section>
|
|
246
|
+
<q-item-label>Header</q-item-label>
|
|
247
|
+
</q-item-section>
|
|
248
|
+
</q-item>
|
|
249
|
+
<q-item clickable v-close-popup @click="onAddSeparator(selectedNode)">
|
|
250
|
+
<q-item-section>
|
|
251
|
+
<q-item-label>Separator</q-item-label>
|
|
252
|
+
</q-item-section>
|
|
253
|
+
</q-item>
|
|
254
|
+
|
|
255
|
+
</q-list>
|
|
256
|
+
</q-btn-dropdown>
|
|
257
|
+
|
|
258
|
+
<l-btn color="primary" @click="onRemove(selectedNode)" label="Remove" icon="sym_o_remove" />
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
<l-btn color="primary" @click="onMove(selectedNode)" label="Move" icon="sym_o_move" />
|
|
262
|
+
|
|
263
|
+
<l-btn color="primary" @click="onMoveUp(selectedNode)" label="Up" icon="sym_o_arrow_upward" />
|
|
264
|
+
|
|
265
|
+
<l-btn color="primary" @click="onMoveDown(selectedNode)" label="Down" icon="sym_o_arrow_downward" />
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
<q-btn-dropdown color="primary" label="Add menus" icon="sym_o_add">
|
|
269
|
+
<q-list>
|
|
270
|
+
<q-item clickable v-close-popup @click="onAddChildMenu(selectedNode, 'list')">
|
|
271
|
+
<q-item-section>
|
|
272
|
+
<q-item-label>List</q-item-label>
|
|
273
|
+
</q-item-section>
|
|
274
|
+
</q-item>
|
|
275
|
+
|
|
276
|
+
<q-item clickable v-close-popup @click="onAddChildMenu(selectedNode, 'add')">
|
|
277
|
+
<q-item-section>
|
|
278
|
+
<q-item-label>Add</q-item-label>
|
|
279
|
+
</q-item-section>
|
|
280
|
+
</q-item>
|
|
281
|
+
|
|
282
|
+
</q-list>
|
|
283
|
+
</q-btn-dropdown>
|
|
284
|
+
</template>
|
|
285
|
+
</q-card-actions>
|
|
220
286
|
<q-splitter v-model="splitterModel" style="height:680px">
|
|
221
287
|
<template #before>
|
|
222
|
-
<q-card-actions>
|
|
223
|
-
<l-btn @click="onAdd" label="Add" icon="sym_o_add" />
|
|
224
|
-
<l-btn @click="onReload" label="Reload" icon="sym_o_refresh" />
|
|
225
|
-
|
|
226
|
-
<l-btn @click="onSave" label="Save" icon="sym_o_save" />
|
|
227
|
-
</q-card-actions>
|
|
228
288
|
|
|
229
289
|
<q-tree :nodes="menus" selected-color="primary" default-expand-all v-model:selected="selected"
|
|
230
290
|
node-key="uuid" ref="tree1" />
|
|
231
291
|
</template>
|
|
232
292
|
|
|
233
293
|
<template #after v-if="selected">
|
|
234
|
-
|
|
235
|
-
<q-card-actions>
|
|
236
|
-
<l-btn outline rounded color="primary" @click="onRemove(selectedNode)" label="Remove"
|
|
237
|
-
icon="sym_o_remove" />
|
|
238
|
-
|
|
239
|
-
<l-btn outline rounded color="primary" @click="onAddChild(selectedNode)" label="Add Child"
|
|
240
|
-
icon="sym_o_add" />
|
|
241
|
-
|
|
242
|
-
<l-btn outline rounded color="primary" @click="onMove(selectedNode)" label="Move"
|
|
243
|
-
icon="sym_o_move" />
|
|
244
|
-
|
|
245
|
-
<l-btn outline rounded color="primary" @click="onMoveUp(selectedNode)" label="Up"
|
|
246
|
-
icon="sym_o_arrow_upward" />
|
|
247
|
-
|
|
248
|
-
<l-btn outline rounded color="primary" @click="onMoveDown(selectedNode)" label="Down"
|
|
249
|
-
icon="sym_o_arrow_downward" />
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
<q-btn-dropdown color="primary" label="Add menus" outline rounded icon="sym_o_add">
|
|
253
|
-
<q-list>
|
|
254
|
-
<q-item clickable v-close-popup @click="onAddChildMenu(selectedNode, 'list')">
|
|
255
|
-
<q-item-section>
|
|
256
|
-
<q-item-label>List</q-item-label>
|
|
257
|
-
</q-item-section>
|
|
258
|
-
</q-item>
|
|
259
|
-
|
|
260
|
-
<q-item clickable v-close-popup @click="onAddChildMenu(selectedNode, 'add')">
|
|
261
|
-
<q-item-section>
|
|
262
|
-
<q-item-label>Add</q-item-label>
|
|
263
|
-
</q-item-section>
|
|
264
|
-
</q-item>
|
|
265
|
-
|
|
266
|
-
</q-list>
|
|
267
|
-
</q-btn-dropdown>
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
</q-card-actions>
|
|
271
|
-
|
|
272
|
-
|
|
273
294
|
<q-card-section>
|
|
274
|
-
<
|
|
275
|
-
<
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
<
|
|
284
|
-
|
|
285
|
-
|
|
295
|
+
<template v-if="!selectedNode.type">
|
|
296
|
+
<div class="q-gutter-md">
|
|
297
|
+
<l-input label="Label" v-model="selectedNode.label" />
|
|
298
|
+
<l-input label="To" v-model="selectedNode.to" />
|
|
299
|
+
<l-input label="Icon" v-model="selectedNode.icon" hint="example: sym_o_add" />
|
|
300
|
+
<l-input label="Permission" v-model="selectedNode.permission" />
|
|
301
|
+
<l-input label="UUID" v-model="selectedNode.uuid" readonly />
|
|
302
|
+
</div>
|
|
303
|
+
|
|
304
|
+
<div>
|
|
305
|
+
<a href="https://fonts.google.com/icons" target="_blank">Material Icons</a>
|
|
306
|
+
</div>
|
|
307
|
+
|
|
308
|
+
</template>
|
|
309
|
+
|
|
310
|
+
<template v-if="selectedNode.type == 'separator'">
|
|
311
|
+
<div class="q-gutter-md">
|
|
312
|
+
<q-checkbox label="Spaced" v-model="selectedNode.spaced" />
|
|
313
|
+
|
|
314
|
+
</div>
|
|
315
|
+
</template>
|
|
316
|
+
|
|
317
|
+
<template v-if="selectedNode.type == 'header'">
|
|
318
|
+
<div class="q-gutter-md">
|
|
319
|
+
<l-input label="Label" v-model="selectedNode.label" />
|
|
320
|
+
</div>
|
|
321
|
+
</template>
|
|
286
322
|
</q-card-section>
|
|
323
|
+
|
|
287
324
|
</template>
|
|
288
325
|
|
|
289
326
|
</q-splitter>
|