generated from corrad-software/corrad-af-2024
534 lines
14 KiB
Vue
534 lines
14 KiB
Vue
<script setup>
|
|
import DraggableSideMenuNested from "~/components/draggable/sideMenuNested.vue";
|
|
const props = defineProps({
|
|
menus: {
|
|
required: true,
|
|
type: Array,
|
|
},
|
|
count: {
|
|
required: false,
|
|
default: 0,
|
|
type: Number,
|
|
},
|
|
parentMenu: {
|
|
required: false,
|
|
default: [],
|
|
type: Array,
|
|
},
|
|
});
|
|
|
|
const emits = defineEmits(["changeSideMenu"]);
|
|
|
|
const showModal = ref(false);
|
|
const type = ref(null);
|
|
const formMenu = ref({
|
|
index: null,
|
|
name: null,
|
|
title: null,
|
|
path: null,
|
|
icon: null,
|
|
});
|
|
|
|
const formHeader = ref({
|
|
index: null,
|
|
header: null,
|
|
description: null,
|
|
});
|
|
|
|
const viewPermissionType = ref([
|
|
{
|
|
label: "All",
|
|
value: "all",
|
|
},
|
|
{
|
|
label: "User",
|
|
value: "user",
|
|
},
|
|
{
|
|
label: "Role",
|
|
value: "role",
|
|
},
|
|
]);
|
|
const viewPermissionTypeRadio = ref("");
|
|
const roleList = ref([]);
|
|
const userList = ref([]);
|
|
|
|
const selectListValue = ref([]);
|
|
|
|
const checkAll = ref(false);
|
|
|
|
// watch viewPermissionTypeRadio
|
|
watch(
|
|
viewPermissionTypeRadio,
|
|
async (val) => {
|
|
if (val == "") viewPermissionTypeRadio.value = "all";
|
|
else if (val == "user") await getUserList();
|
|
else if (val == "role") await getRoleList();
|
|
|
|
// Check if selectListValue doesnt match with user or role list then reset selectListValue
|
|
if (val == "user") {
|
|
selectListValue.value = selectListValue.value.filter((item) => {
|
|
return userList.value.some((user) => user.value == item.value);
|
|
});
|
|
} else if (val == "role") {
|
|
selectListValue.value = selectListValue.value.filter((item) => {
|
|
return roleList.value.some((role) => role.value == item.value);
|
|
});
|
|
}
|
|
|
|
checkAll.value = false;
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
|
|
// watch checkAll
|
|
watch(
|
|
checkAll,
|
|
(val) => {
|
|
if (val) {
|
|
if (viewPermissionTypeRadio.value == "user") {
|
|
selectListValue.value = userList.value.map((user) => {
|
|
return {
|
|
label: user.label,
|
|
value: user.value,
|
|
};
|
|
});
|
|
} else if (viewPermissionTypeRadio.value == "role") {
|
|
selectListValue.value = roleList.value.map((role) => {
|
|
return {
|
|
label: role.label,
|
|
value: role.value,
|
|
};
|
|
});
|
|
}
|
|
} else {
|
|
selectListValue.value = [];
|
|
}
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
|
|
const getUserList = async () => {
|
|
const { data } = await useFetch("/api/devtool/menu/user-list");
|
|
if (data.value?.statusCode === 200) {
|
|
userList.value = data.value.data.map((user) => {
|
|
return {
|
|
label: user.userUsername,
|
|
value: user.userUsername,
|
|
};
|
|
});
|
|
}
|
|
};
|
|
|
|
const getRoleList = async () => {
|
|
const { data } = await useFetch("/api/devtool/menu/role-list");
|
|
|
|
if (data.value?.statusCode === 200) {
|
|
roleList.value = data.value.data.map((role) => {
|
|
return {
|
|
label: role.roleName,
|
|
value: role.roleName,
|
|
};
|
|
});
|
|
}
|
|
};
|
|
|
|
const clone = (obj) => {
|
|
return JSON.parse(JSON.stringify(obj));
|
|
};
|
|
|
|
// Modal functions
|
|
const openModal = () => {
|
|
showModal.value = true;
|
|
};
|
|
|
|
const assignDataMenu = (menu) => {
|
|
formMenu.value = {
|
|
index: menu.index,
|
|
name: menu.name,
|
|
title: menu.title,
|
|
path: menu.path,
|
|
icon: menu.icon,
|
|
};
|
|
|
|
if (menu.meta?.auth?.user) {
|
|
viewPermissionTypeRadio.value = "user";
|
|
selectListValue.value = menu.meta.auth.user.map((user) => {
|
|
return {
|
|
label: user,
|
|
value: user,
|
|
};
|
|
});
|
|
} else if (menu.meta?.auth?.role) {
|
|
viewPermissionTypeRadio.value = "role";
|
|
selectListValue.value = menu.meta.auth.role.map((role) => {
|
|
return {
|
|
label: role,
|
|
value: role,
|
|
};
|
|
});
|
|
} else {
|
|
viewPermissionTypeRadio.value = "all";
|
|
}
|
|
};
|
|
|
|
const assignDataHeader = (header) => {
|
|
formHeader.value = {
|
|
index: props.menus.indexOf(header),
|
|
header: header.header,
|
|
description: header.description,
|
|
};
|
|
|
|
if (header.meta?.auth?.user) {
|
|
viewPermissionTypeRadio.value = "user";
|
|
selectListValue.value = header.meta.auth.user.map((user) => {
|
|
return {
|
|
label: user,
|
|
value: user,
|
|
};
|
|
});
|
|
} else if (header.meta?.auth?.role) {
|
|
viewPermissionTypeRadio.value = "role";
|
|
selectListValue.value = header.meta.auth.role.map((role) => {
|
|
return {
|
|
label: role,
|
|
value: role,
|
|
};
|
|
});
|
|
} else {
|
|
viewPermissionTypeRadio.value = "all";
|
|
}
|
|
};
|
|
|
|
const clickOK = () => {
|
|
showModal.value = false;
|
|
};
|
|
|
|
const clickCancel = () => {
|
|
showModal.value = false;
|
|
};
|
|
|
|
// Update the menus
|
|
const updateMenus = (menus) => {
|
|
emits("changeSideMenu", menus);
|
|
};
|
|
|
|
// Save the menu
|
|
const saveEditChanges = () => {
|
|
let newMenu = props.menus;
|
|
|
|
if (type.value == "menu") {
|
|
// Overwrite the props menus
|
|
props.menus.map((menu) => {
|
|
if (menu.path == formMenu.value.path) {
|
|
menu.title = formMenu.value.title;
|
|
menu.icon = formMenu.value.icon;
|
|
menu.meta = {};
|
|
|
|
// Add the meta auth based on viewPermissionTypeRadio
|
|
if (viewPermissionTypeRadio.value == "user") {
|
|
menu.meta.auth = {
|
|
user: selectListValue.value.map((user) => {
|
|
return user.value;
|
|
}),
|
|
};
|
|
} else if (viewPermissionTypeRadio.value == "role") {
|
|
menu.meta.auth = {
|
|
role: selectListValue.value.map((role) => {
|
|
return role.value;
|
|
}),
|
|
};
|
|
}
|
|
}
|
|
});
|
|
|
|
newMenu = props.parentMenu;
|
|
} else if (type.value == "header") {
|
|
// Overwrite the props menus
|
|
newMenu = props.menus.map((header, index) => {
|
|
if (index == formHeader.value.index) {
|
|
header.header = formHeader.value.header;
|
|
header.description = formHeader.value.description;
|
|
header.meta = {};
|
|
|
|
// Add the meta auth based on viewPermissionTypeRadio
|
|
if (viewPermissionTypeRadio.value == "user") {
|
|
header.meta.auth = {
|
|
user: selectListValue.value.map((user) => {
|
|
return user.value;
|
|
}),
|
|
};
|
|
} else if (viewPermissionTypeRadio.value == "role") {
|
|
header.meta.auth = {
|
|
role: selectListValue.value.map((role) => {
|
|
return role.value;
|
|
}),
|
|
};
|
|
}
|
|
}
|
|
return header;
|
|
});
|
|
}
|
|
|
|
// Update the menus
|
|
updateMenus(newMenu);
|
|
showModal.value = false;
|
|
};
|
|
|
|
const removeChild = (type, data) => {
|
|
// console.log(data);
|
|
// console.log(type);
|
|
let newMenu = props.menus;
|
|
if (type == "menu") {
|
|
let parentMenu = props.parentMenu;
|
|
|
|
// Overwrite the props menus
|
|
newMenu = props.menus.filter((menu) => {
|
|
return menu.path == data;
|
|
});
|
|
|
|
// Remove the newMenu from the parentMenu child
|
|
parentMenu = parentMenu.filter((menu) => {
|
|
// Level 1
|
|
if (menu.child) {
|
|
menu.child.forEach((el) => {
|
|
// Level 2
|
|
if (el.child) {
|
|
el.child = el.child.filter((child) => {
|
|
return child.path != data;
|
|
});
|
|
}
|
|
|
|
if (el.path == data) {
|
|
menu.child.splice(menu.child.indexOf(el), 1);
|
|
}
|
|
});
|
|
}
|
|
return menu;
|
|
});
|
|
|
|
newMenu = parentMenu;
|
|
} else if (type == "header") {
|
|
// Remove the object array from the props menus
|
|
newMenu = props.menus.filter((header, index) => {
|
|
return index != data;
|
|
});
|
|
}
|
|
|
|
// Update the menus
|
|
updateMenus(newMenu);
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<draggable
|
|
class="dragArea"
|
|
tag="div"
|
|
:list="menus"
|
|
:group="{ name: 'menu', put: props.count == 0 ? false : true }"
|
|
:clone="clone"
|
|
item-key="id"
|
|
>
|
|
<template #item="{ element }">
|
|
<rs-card
|
|
class="p-4 !my-4 mx-0 mb-0 relative border-2 border-[rgb(var(--border-color))]"
|
|
:class="{
|
|
'py-6': count > 0,
|
|
}"
|
|
>
|
|
<div class="flex justify-between items-center">
|
|
<div class="text-left font-normal text-xs mb-2">
|
|
<span class="uppercase text-primary dark:text-primary">{{
|
|
count == 0 && element.header
|
|
? element.header
|
|
: count === 0
|
|
? "(No Header)"
|
|
: ""
|
|
}}</span>
|
|
<p class="text-gray-500 dark:text-gray-500">
|
|
{{
|
|
count == 0 && element.description
|
|
? element.description
|
|
: count === 0
|
|
? "There will be no header shown"
|
|
: ""
|
|
}}
|
|
</p>
|
|
</div>
|
|
<div v-if="count == 0">
|
|
<Icon
|
|
name="material-symbols:edit-outline-rounded"
|
|
class="text-primary hover:text-primary/90 cursor-pointer"
|
|
size="20"
|
|
@click="
|
|
type = 'header';
|
|
assignDataHeader(element);
|
|
openModal();
|
|
"
|
|
></Icon>
|
|
<Icon
|
|
name="material-symbols:close-rounded"
|
|
class="text-primary hover:text-primary/90 cursor-pointer"
|
|
size="20"
|
|
@click="removeChild('header', menus.indexOf(element))"
|
|
></Icon>
|
|
</div>
|
|
</div>
|
|
<div class="flex justify-between items-center">
|
|
<p class="flex items-center gap-2">
|
|
<Icon v-if="element.icon" :name="element.icon" size="22"></Icon>
|
|
{{ element.title }}
|
|
</p>
|
|
<div v-if="count > 0">
|
|
<Icon
|
|
name="material-symbols:edit-outline-rounded"
|
|
class="text-primary hover:text-primary/90 cursor-pointer"
|
|
size="20"
|
|
@click="
|
|
type = 'menu';
|
|
assignDataMenu(element);
|
|
openModal();
|
|
"
|
|
></Icon>
|
|
<Icon
|
|
name="material-symbols:close-rounded"
|
|
class="text-primary hover:text-primary/90 cursor-pointer"
|
|
size="20"
|
|
@click="removeChild('menu', element.path)"
|
|
></Icon>
|
|
</div>
|
|
</div>
|
|
<div v-if="element?.meta?.auth" class="authuser-wrapper mt-3">
|
|
<div class="flex">
|
|
<div v-for="(val, index) in element.meta.auth.user">
|
|
<rs-badge
|
|
v-if="index < 5"
|
|
variant="danger"
|
|
class="mr-1 text-sm"
|
|
>
|
|
{{ val }}
|
|
</rs-badge>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex">
|
|
<div v-for="(val, index) in element.meta.auth.role">
|
|
<rs-badge
|
|
v-if="index < 5"
|
|
variant="warning"
|
|
class="mr-1 text-sm"
|
|
>
|
|
{{ val }}
|
|
</rs-badge>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<DraggableSideMenuNested
|
|
:menus="element?.child ? element.child : []"
|
|
:count="count + 1"
|
|
:parentMenu="
|
|
props.parentMenu && props.parentMenu.length > 0
|
|
? props.parentMenu
|
|
: props.menus
|
|
"
|
|
@changeSideMenu="updateMenus"
|
|
/>
|
|
</rs-card>
|
|
</template>
|
|
</draggable>
|
|
|
|
<rs-modal
|
|
:title="type == 'header' ? 'Edit Header' : 'Edit Menu'"
|
|
v-model="showModal"
|
|
ok-title="Confirm"
|
|
:ok-callback="saveEditChanges"
|
|
:cancel-callback="clickCancel"
|
|
>
|
|
<div v-if="type == 'header'">
|
|
<FormKit
|
|
type="hidden"
|
|
label="Index"
|
|
v-model="formHeader.index"
|
|
></FormKit>
|
|
<FormKit type="text" label="Name" v-model="formHeader.header"></FormKit>
|
|
<FormKit
|
|
type="text"
|
|
label="Description"
|
|
v-model="formHeader.description"
|
|
></FormKit>
|
|
</div>
|
|
<div v-else-if="type == 'menu'">
|
|
<FormKit type="text" label="Title" v-model="formMenu.title"></FormKit>
|
|
<FormKit
|
|
type="text"
|
|
label="Path"
|
|
v-model="formMenu.path"
|
|
readonly
|
|
></FormKit>
|
|
<FormKit type="text" label="Icon" v-model="formMenu.icon"></FormKit>
|
|
<div class="mb-4 text-sm">
|
|
<p class="font-semibold mb-2">
|
|
Preview Icon (<a
|
|
href="https://icones.js.org/collection/all"
|
|
class="text-primary hover:underline"
|
|
target="_blank"
|
|
>https://icones.js.org/collection/all</a
|
|
>)
|
|
</p>
|
|
<Icon v-if="formMenu.icon" :name="formMenu.icon"></Icon>
|
|
</div>
|
|
</div>
|
|
<hr class="mb-4" />
|
|
<h4 class="text-semibold mb-4">Menu Permission</h4>
|
|
<FormKit
|
|
type="radio"
|
|
label="View Type"
|
|
v-model="viewPermissionTypeRadio"
|
|
:classes="{
|
|
fieldset: 'border-0 !p-0',
|
|
legend: '!font-semibold !text-sm mb-0',
|
|
options: '!flex !flex-row gap-4 mt-3',
|
|
}"
|
|
:options="viewPermissionType"
|
|
/>
|
|
<div
|
|
v-if="viewPermissionTypeRadio && viewPermissionTypeRadio != 'all'"
|
|
class="form-wrapper"
|
|
>
|
|
<div class="flex justify-between items-center mb-2">
|
|
<label
|
|
class="formkit-label flex items-center gap-x-4 font-semibold text-gray-700 dark:text-gray-200 blockfont-semibold text-sm formkit-invalid:text-red-500 dark:formkit-invalid:text-danger"
|
|
for="input_4"
|
|
>
|
|
{{ viewPermissionTypeRadio == "user" ? "User" : "Role" }}
|
|
</label>
|
|
</div>
|
|
<v-select
|
|
class="formkit-vselect"
|
|
:options="
|
|
viewPermissionTypeRadio == 'user'
|
|
? userList
|
|
: viewPermissionTypeRadio == 'role'
|
|
? roleList
|
|
: []
|
|
"
|
|
v-model="selectListValue"
|
|
multiple
|
|
></v-select>
|
|
<FormKit
|
|
type="checkbox"
|
|
v-model="checkAll"
|
|
:label="
|
|
viewPermissionTypeRadio == 'user'
|
|
? 'Check All User'
|
|
: 'Check All Role'
|
|
"
|
|
input-class="icon-check"
|
|
/>
|
|
</div>
|
|
</rs-modal>
|
|
</div>
|
|
</template>
|