CviLux_fe/src/views/graphManagement/components/GraphSidebar.vue
2024-10-21 10:31:55 +08:00

241 lines
6.1 KiB
Vue

<script setup>
import Collapse from "@/components/customUI/FileSystemCollapse.vue";
import GraphSidebarDropdown from "./GraphSidebarDropdown.vue";
import {
getSideBar,
updateSideBarTreeName,
removeSideBarTreeName,
addSideBarTreeName,
} from "@/apis/graph";
import { ref, onMounted, onUnmounted, provide, computed, inject } from "vue";
import useSearchParams from "@/hooks/useSearchParam";
import { useI18n } from "vue-i18n";
const { t, locale } = useI18n();
const { changeParams, searchParams } = useSearchParams();
let raw_data = ref([]);
const { updateCurrentDir } = inject("current_dir");
// 處理data資料
const graphData = computed(() => {
let data = raw_data.value.map((d) => ({ ...d, children: [] }));
let formatD = [];
data
.sort((a, b) => b.parent_id - a.parent_id)
.forEach((d) => {
if (d.parent_id === 0) {
formatD.push({ ...d, key: d.id, title: d.name });
} else {
let parent = data.find(({ id }) => id === d.parent_id);
if (parent) {
parent.children = [
...(parent?.children || []),
{ ...d, key: d.id, title: d.name },
];
}
}
});
changeParams({ id: searchParams.value.id || formatD[0]?.id });
updateCurrentDir(
data.find((d) => d.id === parseInt(searchParams.value.id)) || formatD[0]
);
return formatD;
});
const getData = async (id = "") => {
const res = await getSideBar();
raw_data.value = res.data;
id && updateCurrentDir(data.find((d) => parseInt(id) === d.id));
cancelDropdownAndInput();
};
const dropdownOpen = ref(false);
const selectedItem = ref(null);
const cancelOpen = () => {
dropdownOpen.value = false;
};
const cancelDropdownAndInput = () => {
dropdownOpen.value = false;
filenameInputIsOpen.value = false;
};
const dropdownPosition = ref({
x: 0,
y: 0,
});
const sidebar = ref(null);
const onAddRightClick = (e) => {
console.log(e);
if (e.target.nodeName === "LI") {
const targetPos = e.target.getBoundingClientRect();
const sidebarPos = e.target.closest(".sidebar").getBoundingClientRect();
dropdownPosition.value = {
left: e.offsetX + 30,
top: e.clientY - sidebarPos.y + targetPos.height,
};
} else {
dropdownPosition.value = {
left: e.offsetX + 30,
top: e.offsetY + 30,
};
}
selectedItem.value = null;
dropdownOpen.value = true;
};
const onRightClick = (e, d) => {
cancelDropdownAndInput();
const targetPos = e.target.closest("li").getBoundingClientRect();
const sidebarPos = e.target.closest(".sidebar").getBoundingClientRect();
console.log("onRightClick", sidebarPos, targetPos);
dropdownPosition.value = {
left: targetPos.x - sidebarPos.x + targetPos.width / 2 - 50,
top: targetPos.y - sidebarPos.y + 2 * targetPos.height + 5,
};
selectedItem.value = d;
dropdownOpen.value = true;
};
const onLeftClick = (id, data) => {
// if (data.children.length === 0) {
updateCurrentDir(data);
changeParams({ id });
// } else {
// changeParams({});
// }
};
const filenameInputIsOpen = ref(false);
const getNewFilename = async (e) => {
// if (!e) {
// // console.log("無資料");
// // raw_data.value = raw_data.value.filter(
// // (d) => d.id !== selectedItem.value?.id
// // );
// // selectedItem.value = null;
// return;
// }
if (selectedItem.value?.isNewDir) {
// 新增
const res = await addSideBarTreeName({
parent_id: selectedItem.value?.parent_id,
name: e?.target?.value || selectedItem.value?.title,
});
getData(res);
} else if (
e.target.value !== "" &&
e.target.value !== selectedItem.value?.title
) {
// 編輯
const res = await updateSideBarTreeName({
id: selectedItem.value?.id,
name: e.target.value,
});
getData();
} else if (selectedItem.value) {
// 以防資料被刪掉
selectedItem.value.title = selectedItem.value?.name;
}
};
// 編輯filename
const editFilename = () => {
filenameInputIsOpen.value = true;
cancelOpen();
};
// 新增filename
const openItem = ref([]);
const addFilename = (root = false) => {
const newItem = {
id: raw_data.value?.[raw_data.value.length - 1].id + 1,
key: raw_data.value?.[raw_data.value.length - 1].id + 1,
title: t("graphManagement.new_category"),
name: t("graphManagement.new_category"),
parent_id: selectedItem.value?.id || 0,
isNewDir: true,
};
raw_data.value = [...raw_data.value, newItem];
// 第二層之後
if (!root) {
let parent = raw_data.value.find(
({ id }) => id === selectedItem.value.parent_id
);
let ids = [selectedItem.value.id];
while (Boolean(parent?.id)) {
ids = [...ids, parent.id];
parent = raw_data.value.find(({ id }) => id === parent.parent_id);
}
openItem.value = ids;
}
selectedItem.value = newItem;
editFilename();
};
// 刪除filename
const deleteFilename = async () => {
const res = await removeSideBarTreeName(selectedItem.value.id);
if (res.isSuccess) {
raw_data.value = raw_data.value.filter(
({ id }) => id !== selectedItem.value.id
);
cancelOpen();
}
};
const onClickout = (e) => {
e.stopPropagation();
cancelOpen();
getNewFilename();
selectedItem.value = null;
filenameInputIsOpen.value = false;
};
onMounted(() => {
getData();
document.querySelector("body").addEventListener("click", onClickout);
});
onUnmounted(() => {
document.querySelector("body").removeEventListener("click", onClickout);
});
</script>
<template>
<div
ref="sidebar"
class="custom-border py-2 px-4 sidebar"
@contextmenu.prevent.stop="onAddRightClick"
>
<Collapse
:open="true"
:data="graphData"
:onRightClick="onRightClick"
:onClick="onLeftClick"
:filenameInputIsOpen="filenameInputIsOpen"
:selected="selectedItem"
:edit="getNewFilename"
:openItem="openItem"
>
</Collapse>
<GraphSidebarDropdown
:position="dropdownPosition"
:open="dropdownOpen"
:selectedItem="selectedItem"
:edit="editFilename"
:add="addFilename"
:remove="deleteFilename"
/>
</div>
</template>
<style lang="scss" scoped></style>
./GraphSidebarDropdown.vue