CviLux_fe/src/views/graphManagement/components/GraphSidebar.vue

257 lines
6.7 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 { openToast, cancelToastOpen } = inject("app_toast");
const { changeParams, searchParams } = useSearchParams();
let raw_data = ref([]);
const { updateCurrentDir, sidebar_data } = 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]
);
sidebar_data.value = formatD;
return formatD;
});
const getData = async (id = "") => {
const res = await getSideBar();
raw_data.value = res.data;
if (id) {
const currentDir = raw_data.value.find((d) => parseInt(id) === d.id);
updateCurrentDir(currentDir);
}
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 &&
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 () => {
openToast("warning", t("msg.sure_to_delete"), "body", async () => {
await cancelToastOpen();
const filterData = raw_data.value.filter(({ id }) => id !== selectedItem.value.id)
const res = await removeSideBarTreeName(selectedItem.value.id);
if (res.isSuccess) {
raw_data.value = filterData;
cancelOpen();
openToast("success", t("msg.delete_success"));
} else {
openToast("error", res.msg);
}
});
};
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"
/>
<p class="text-xl py-1 ml-7 cursor-pointer" @click.stop.prevent="onLeftClick(-1,null)">
<font-awesome-icon
:icon="['fas', 'trash-alt']"
class="text-rose-500 text-2xl me-3 "
/>{{$t("graphManagement.staging_area")}}
</p>
</div>
</template>
<style lang="scss" scoped></style>