hrms-mgt/src/modules/02_organization/components/StructureOrgMain.vue

322 lines
9.3 KiB
Vue
Raw Normal View History

<script setup lang="ts">
import { ref, onMounted, watch, onBeforeMount, nextTick } from "vue";
import { useQuasar } from "quasar";
import { OrgChart } from "bma-org-chart";
import "bma-org-chart/org-chart.css";
import http from "@/plugins/http";
import config from "@/app.config";
import { useCounterMixin } from "@/stores/mixin";
import { useOrganizational } from "@/modules/02_organization/store/organizational";
2024-10-10 14:48:22 +07:00
import { useStructStore } from "@/modules/02_organization/store/chart";
import avatar from "@/assets/avatar_user.jpg";
import chartData from "@/assets/orgChartData";
2025-07-22 09:37:03 +07:00
import {
showLoadingSpinner,
exportChartToPDF,
exportChartToPNG,
} from "@/plugins/exportChart";
import type { DataOption } from "@/modules/02_organization/interface/index/Main";
const mixin = useCounterMixin();
const store = useOrganizational();
2024-10-10 14:48:22 +07:00
const storeOrg = useStructStore();
const { showLoader, hideLoader, messageError } = mixin;
const $q = useQuasar(); // show dialog
const dataSource = ref(chartData); // ข้อมูล Chart
const rootOrgID = ref(); // org id ของ root ตัวปัจจุบันที่เลือกอยู่
const dataSourceLock = ref(); // ข้อมูลตั้งต้นของ Chart ใช้ให้กดกลับไปที่ home
const chartRef = ref(); // อ้างอิงไปที่ตัว chart
const scrollContainer = ref<HTMLElement | null>(null);
const agencyId = ref<string>(""); // id ของหน่วยงานที่เลือก
const agencyData = ref<DataOption[]>([]); // ข้อมูลรายการหน่วยงาน
const agencyOp = ref<DataOption[]>([]); // ตัวเลือกหน่วยงาน
/** fetch ข้อมูล Chart โครงสร้าง*/
async function fetchOrgChart(agencyId: string = "root") {
2024-10-09 17:49:38 +07:00
if (rootOrgID.value) {
showLoader();
let urlRequest = config.API.orgChart(rootOrgID.value) + `/${agencyId}`;
2024-10-09 17:49:38 +07:00
await http
.get(urlRequest)
.then(async (response) => {
if (response.data.result.length > 0) {
2024-10-10 14:48:22 +07:00
const data = await response.data.result;
2024-10-09 17:49:38 +07:00
const updatedData = await Promise.all(data.map(addAvatarToData));
2024-10-10 14:48:22 +07:00
dataSource.value = await updatedData[0];
storeOrg.dataSource = updatedData[0];
2024-10-09 17:49:38 +07:00
if (dataSourceLock.value === undefined)
dataSourceLock.value = dataSource.value;
}
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
}
/**
* map อม Chart
* @param item อม Chart
*/
async function addAvatarToData(item: any) {
// เพิ่ม avatar ให้กับ item
const updatedItem = {
...item,
// avatar: item.avatar ? await fetchProfile(item.avatar) : avatar,
avatar: avatar,
// ตรวจสอบว่า item มี children หรือไม่
children: item.children
? await Promise.all(item.children.map(addAvatarToData))
: [],
};
return updatedItem;
}
/**
2024-09-06 13:47:50 +07:00
* fetch ปโปรไฟล return งก download ไฟล
* @param path ไฟล
*/
async function fetchProfile(path: string) {
try {
const res = await http.get(config.API.filefullPath(path));
return res.data.downloadUrl; // คืนค่า URL ของ avatar
} catch (err) {
return avatar; // คืนค่า default avatar ถ้าเกิดข้อผิดพลาด
}
}
/**
2024-11-28 13:44:28 +07:00
* เมอมการคลกท Chart ใหาน ID ของหนวยงานทกคล แลวดงขอม Chart ของหนวยงานนนๆ จาก API
* @param data
*/
async function refreshChart(data: any) {
if (data.value !== undefined) rootOrgID.value = data.value;
else rootOrgID.value = data;
if (rootOrgID.value !== 0) await fetchOrgChart();
if (data.value !== undefined) data.value = 0;
}
/** โหลด Chart รูป*/
async function savePNG() {
2025-07-22 09:37:03 +07:00
// try {
// showLoader();
// await scrollToCenter();
// await chartRef.value.savePNG();
// } catch {
// messageError($q);
// } finally {
// hideLoader();
// }
showLoadingSpinner();
setTimeout(async () => {
scrollContainer.value && (await exportChartToPNG(scrollContainer.value));
}, 500);
}
/** โหลด Chart PDF*/
async function savePDF() {
2025-07-22 09:37:03 +07:00
// try {
// showLoader();
// await scrollToCenter();
// await chartRef.value.savePDF();
// } catch {
// messageError($q);
// } finally {
// hideLoader();
// }
showLoadingSpinner();
setTimeout(async () => {
scrollContainer.value && (await exportChartToPDF(scrollContainer.value));
}, 500);
}
/** ฟังก์ชันเลื่อน scroll ไปที่กึ่งกลาง*/
async function scrollToCenter() {
const container = scrollContainer.value;
if (container) {
container.scrollLeft = (container.scrollWidth - container.clientWidth) / 2;
}
}
async function fetchAgencyData(id: string) {
agencyId.value = "";
await http
.get(config.API.orgByid(id))
.then((res) => {
const data = res.data.result.data;
agencyData.value = data.map((item: any) => ({
id: item.orgTreeId,
name: item.orgName,
}));
agencyOp.value = agencyData.value;
})
.catch((err) => {
messageError($q, err);
});
}
function onSelectAgency(val: string) {
const id = val || "root"; // ถ้าไม่มีการเลือกหน่วยงาน ให้ใช้ root
fetchOrgChart(id);
}
/**
* งกนตนหาขอมลของ Option ขอสถานะ
* @param val าทองการฟลเตอร
* @param update พเดทค
* @param refData ดาตาทองการฟลเตอร
*/
function filterOption(val: string, update: Function) {
update(() => {
agencyOp.value = agencyData.value.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
}
2024-10-09 17:49:38 +07:00
watch(
[() => store.typeOrganizational, () => store.historyId],
2024-10-10 14:48:22 +07:00
([new1], [old1]) => {
2024-10-09 17:49:38 +07:00
if (new1 === "old" && old1 !== "old") {
store.historyId = "";
}
rootOrgID.value =
store.typeOrganizational === "current"
? store.activeId
: store.typeOrganizational === "draft"
? store.draftId
: store.historyId;
2024-10-10 14:48:22 +07:00
fetchOrgChart();
fetchAgencyData(rootOrgID.value);
2024-10-09 17:49:38 +07:00
}
);
onMounted(async () => {
rootOrgID.value =
store.typeOrganizational === "current"
? store.activeId
: store.typeOrganizational === "draft"
? store.draftId
: store.historyId;
await fetchOrgChart();
await scrollToCenter();
await fetchAgencyData(rootOrgID.value);
2024-10-10 14:48:22 +07:00
});
onBeforeMount(() => {
storeOrg.dataSource = undefined;
});
</script>
<template>
<!-- <div class="toptitle text-dark col-12 row items-center">แผนภองคกร</div> -->
<div class="text-dark">
<q-card class="col-12 q-mt-sm">
<div class="q-pa-sm row">
<q-btn flat round color="primary" @click="savePNG()" icon="mdi-image">
<q-tooltip> ดาวนโหลด PNG </q-tooltip>
</q-btn>
<q-btn
flat
round
color="red-7"
@click="savePDF()"
icon="mdi-file-pdf-box"
>
<q-tooltip> ดาวนโหลด PDF </q-tooltip>
</q-btn>
<q-space />
<q-select
v-model="agencyId"
:label="`${'หน่วยงาน'}`"
option-label="name"
:options="agencyOp"
option-value="id"
dense
emit-value
map-options
use-input
hide-selected
fill-input
outlined
clearable
@clear="() => (agencyId = '')"
@update:model-value="onSelectAgency"
@filter="(inputValue:any,doneFn:Function) => filterOption(inputValue, doneFn) "
style="width: 400px;"
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey"> ไมอม </q-item-section>
</q-item>
</template>
</q-select>
</div>
<q-separator />
<div
ref="scrollContainer"
style="
overflow-x: auto;
overflow-y: auto;
height: 70vh;
position: relative;
"
class="q-pt-md"
>
<OrgChart
2024-10-10 14:48:22 +07:00
v-if="storeOrg.dataSource"
style="height: 70vh"
ref="chartRef"
class="org"
:dataSource="dataSource"
@onElementClick="refreshChart"
/>
</div>
</q-card>
</div>
</template>
<style>
.org .section-list {
padding: 15px !important;
}
.org .section-list .column-content .header {
font-size: 1rem;
line-height: 1.2rem;
font-weight: 500 !important;
}
.org .section-list .column-content .subheader {
font-weight: 500 !important;
}
.org .section-list .column-content .caption {
font-size: 0.9rem;
line-height: 1.2rem;
font-weight: 400 !important;
}
.org .element-container .column-content p {
padding-top: 3px !important;
}
.org .element-container .column-side .side-button {
background-size: 14px;
}
</style>