660 lines
20 KiB
Vue
660 lines
20 KiB
Vue
<script setup lang="ts">
|
|
import { ref, watch } from "vue";
|
|
import { useQuasar } from "quasar";
|
|
import http from "@/plugins/http";
|
|
import config from "@/app.config";
|
|
import { checkPermission } from "@/utils/permissions";
|
|
|
|
/** importType*/
|
|
import type { ListMenu } from "@/modules/02_organization/interface/index/Main";
|
|
import type { OrgTree } from "@/modules/02_organization/interface/response/organizational";
|
|
import type { DataTree } from "@/modules/02_organization/interface/index/organizational";
|
|
|
|
/** importComponents*/
|
|
import DialogAgency from "@/modules/02_organization/components/DialogFormAgency.vue";
|
|
import DialogStructureDetail from "@/modules/02_organization/components/DialogStructureDetail.vue";
|
|
import DialogSortAgency from "@/modules/02_organization/components/DialogSortAgency.vue";
|
|
import DialogHistory from "@/modules/02_organization/components/DialogHistory.vue";
|
|
|
|
/** importStore*/
|
|
import { useOrganizational } from "@/modules/02_organization/store/organizational";
|
|
import { useCounterMixin } from "@/stores/mixin";
|
|
|
|
/** use*/
|
|
const $q = useQuasar();
|
|
const store = useOrganizational();
|
|
const { dialogRemove, showLoader, hideLoader, messageError, success } =
|
|
useCounterMixin();
|
|
|
|
/** props*/
|
|
const nodeTEST = defineModel<OrgTree[]>("nodeTree", { default: [] });
|
|
const nodeId = defineModel<string>("nodeId", { required: true });
|
|
const orgRootId = defineModel<string | undefined>("orgRootId", {
|
|
required: true,
|
|
});
|
|
const shortName = defineModel<string>("shortName", { required: true });
|
|
const props = defineProps({
|
|
fetchDataTree: {
|
|
type: Function,
|
|
require: true,
|
|
},
|
|
fetchDataTable: {
|
|
type: Function,
|
|
require: true,
|
|
},
|
|
});
|
|
|
|
/** ListMenuTree*/
|
|
const listAdd = ref<ListMenu[]>([
|
|
{
|
|
label: "เพิ่ม",
|
|
icon: "add",
|
|
type: "ADD",
|
|
color: "primary",
|
|
},
|
|
{
|
|
label: "แก้ไข",
|
|
icon: "edit",
|
|
type: "EDIT",
|
|
color: "edit",
|
|
},
|
|
{
|
|
label: "ลบ",
|
|
icon: "delete",
|
|
type: "DEL",
|
|
color: "red",
|
|
},
|
|
|
|
{
|
|
label: "ประวัติ",
|
|
icon: "history",
|
|
type: "HISTORY",
|
|
color: "purple",
|
|
},
|
|
{
|
|
label: "จัดลำดับ",
|
|
icon: "mdi-sort",
|
|
type: "SORT",
|
|
color: "blue-6",
|
|
},
|
|
{
|
|
label: "ดูรายละเอียด",
|
|
icon: "mdi-eye",
|
|
type: "DETAIL",
|
|
color: "blue-9",
|
|
},
|
|
]);
|
|
|
|
const filter = ref<string>("");
|
|
const nodes = ref<Array<OrgTree>>([]);
|
|
const dataSort = ref<Array<any>>([]);
|
|
const lazy = ref(nodes);
|
|
const expanded = ref<Array<any>>([]);
|
|
const notFound = ref<string>("ไม่พบข้อมูลที่ค้นหา");
|
|
const noData = ref<string>("ไม่มีข้อมูล");
|
|
const orgLevel = ref<number>(0);
|
|
const type = ref<number>(0);
|
|
const orgId = ref<string>("");
|
|
|
|
/**
|
|
* funtion เลือกข้อมูล Tree
|
|
* @param data ข่อมูล Tree
|
|
*/
|
|
function updateSelected(data: DataTree) {
|
|
orgRootId.value = data?.orgLevel === 0 ? data?.orgTreeId : data?.orgRootId;
|
|
store.rootId = (
|
|
data.orgLevel === 0 ? data.orgTreeId : data.orgRootId
|
|
) as string;
|
|
shortName.value = data.orgTreeShortName;
|
|
|
|
if (!store.treeId || store.treeId != data.orgTreeId) {
|
|
store.treeId = data.orgTreeId;
|
|
store.level = data.orgLevel;
|
|
|
|
nodeId.value = data.orgTreeId ? data.orgTreeId : "111";
|
|
|
|
data.orgTreeId &&
|
|
props.fetchDataTable?.(data.orgTreeId, data.orgLevel, true);
|
|
/** ดึงข้อมูลสถิติจำนวนด้านบน*/
|
|
http
|
|
.post(config.API.orgSummary, {
|
|
id: data.orgTreeId, //*Id node
|
|
type: data.orgLevel, //*ประเภทnode
|
|
isNode: false, //*นับทั้ง node ไหม
|
|
})
|
|
.then(async (res: any) => {
|
|
const data = await res.data.result;
|
|
if (data) {
|
|
store.getSumPosition({
|
|
totalPosition: data.totalPosition,
|
|
totalPositionCurrentUse: data.totalPositionCurrentUse,
|
|
totalPositionCurrentVacant: data.totalPositionCurrentVacant,
|
|
totalPositionNextUse: data.totalPositionNextUse,
|
|
totalPositionNextVacant: data.totalPositionNextVacant,
|
|
totalRootPosition: data.totalPosition,
|
|
totalRootPositionCurrentUse: data.totalPositionCurrentUse,
|
|
totalRootPositionCurrentVacant: data.totalPositionCurrentVacant,
|
|
totalRootPositionNextUse: data.totalPositionNextUse,
|
|
totalRootPositionNextVacant: data.totalPositionNextVacant,
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
const breakLoop = ref<boolean>(false);
|
|
|
|
/**
|
|
* function แก้ไขโครสร้าง
|
|
* @param id ID โครงสร้าง
|
|
* @param type ละดับโครงสร้าง
|
|
* @param data ข้อมูลโครงสร้าง
|
|
* @param orgRootCode
|
|
*/
|
|
async function edit(id: string, type: string, data: any, orgRootCode: string) {
|
|
breakLoop.value = false;
|
|
const targetNodeId = id;
|
|
|
|
for (let index = 0; index < nodes.value.length; index++) {
|
|
const element = nodes.value[index];
|
|
|
|
searchAndReplace(element, targetNodeId, data, type, orgRootCode);
|
|
|
|
if (breakLoop.value) break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* function แก้ไขโครสร้าง
|
|
* @param treeNode
|
|
* @param organizationId ID โครงสร้าง
|
|
* @param data ข้อมูลโครงสร้าง
|
|
* @param type ละดับโครงสร้าง
|
|
* @param orgRootCode
|
|
*/
|
|
function searchAndReplace(
|
|
treeNode: any,
|
|
organizationId: string,
|
|
data: any,
|
|
type: string,
|
|
orgRootCode: string
|
|
) {
|
|
if (treeNode.orgTreeId === organizationId) {
|
|
let newData = {
|
|
...treeNode,
|
|
orgTreeName: data[`org${type}Name`],
|
|
orgTreeShortName: data[`org${type}ShortName`],
|
|
orgCode:
|
|
data.orgRootRank == "DEPARTMENT"
|
|
? data[`org${type}Code`] + "00"
|
|
: orgRootCode + data[`org${type}Code`],
|
|
orgTreeCode: data[`org${type}Code`],
|
|
orgTreePhoneEx: data[`org${type}PhoneEx`],
|
|
orgTreePhoneIn: data[`org${type}PhoneIn`],
|
|
orgTreeFax: data[`org${type}Fax`],
|
|
orgTreeRank: data[`org${type}Rank`],
|
|
};
|
|
|
|
Object.assign(treeNode, newData);
|
|
breakLoop.value = true;
|
|
} else if (treeNode.children) {
|
|
for (const child of treeNode.children) {
|
|
searchAndReplace(child, organizationId, data, type, orgRootCode);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* function ลบข้อมูลโครงสร้าง
|
|
* @param rootId RootID
|
|
* @param treeId TreeID
|
|
*/
|
|
async function deleteUpdate(rootId: string, treeId: string) {
|
|
breakLoop.value = false;
|
|
if (rootId) {
|
|
for (let index = 0; index < nodes.value.length; index++) {
|
|
const element = nodes.value[index];
|
|
deleteNode(element, rootId, treeId);
|
|
|
|
if (breakLoop.value) break;
|
|
}
|
|
} else {
|
|
nodes.value = nodes.value.filter((x: any) => x.orgTreeId != treeId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* function ลบข้อมูลโครงสร้าง
|
|
* @param treeNode ข้อมูล Tree
|
|
* @param rootId RootID
|
|
* @param treeId TreeID
|
|
*/
|
|
function deleteNode(treeNode: any, rootId: string, treeId: string): boolean {
|
|
if (treeNode.orgTreeId === rootId) {
|
|
const childrenNew = treeNode.children.filter(
|
|
(x: any) => x.orgTreeId != treeId
|
|
);
|
|
let newData = {
|
|
...treeNode,
|
|
children: childrenNew,
|
|
};
|
|
Object.assign(treeNode, newData);
|
|
|
|
breakLoop.value = true;
|
|
} else if (treeNode.children) {
|
|
for (const child of treeNode.children) {
|
|
deleteNode(child, rootId, treeId);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
const modalHistory = ref<boolean>(false);
|
|
const modalSortAgency = ref<boolean>(false);
|
|
const dialogAgency = ref<boolean>(false);
|
|
const actionType = ref<string>("");
|
|
const dataNode = ref<any>();
|
|
const treeId = ref<string>("");
|
|
|
|
/**
|
|
* funcion openPopup เพิ่มหน่วยงาน
|
|
* @param level ระดับโครงสร้าง
|
|
* @param node ข้อมูล โครงสร้าง
|
|
*/
|
|
function onClickAgency(level: number, node: OrgTree | {}) {
|
|
dialogAgency.value = !dialogAgency.value;
|
|
orgLevel.value = level;
|
|
dataNode.value = node;
|
|
actionType.value = "ADD";
|
|
}
|
|
|
|
const dialogDetail = ref<boolean>(false);
|
|
/**
|
|
* funtion ดูรายละเอียดโครงสร้าง
|
|
* @param id ID โครงสร้าง
|
|
* @param level ระดับโครงสร้าง
|
|
*/
|
|
function onClickDetail(id: string, level: number) {
|
|
showLoader();
|
|
treeId.value = id;
|
|
dialogDetail.value = !dialogDetail.value;
|
|
orgLevel.value = level;
|
|
}
|
|
|
|
/**
|
|
* function openPopup แก้ไขข้อมูลโครงสร้าง
|
|
* @param node ข้อมูล โครงสร้าง
|
|
*/
|
|
async function onClickEdit(node: OrgTree) {
|
|
dialogAgency.value = !dialogAgency.value;
|
|
actionType.value = "EDIT";
|
|
orgLevel.value = node.orgLevel;
|
|
dataNode.value = node;
|
|
}
|
|
|
|
/**
|
|
* function ยื่นยันการลบโครงสร้าง
|
|
* @param type ระดับโครงสร้าง
|
|
* @param id ID โครงสร้าง
|
|
* @param rootId RootID
|
|
*/
|
|
async function onClickDel(type: number, id: string, rootId: string) {
|
|
const level = await store.checkLevel(type);
|
|
dialogRemove($q, async () => {
|
|
showLoader();
|
|
await http
|
|
.delete(config.API.orgLevelByid(level.toLocaleLowerCase(), id))
|
|
.then(async () => {
|
|
await deleteUpdate(rootId, id);
|
|
await success($q, "ลบข้อมูลสำเร็จ");
|
|
})
|
|
.catch((err) => {
|
|
messageError($q, err);
|
|
})
|
|
.finally(async () => {
|
|
hideLoader();
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* function การจัดลำดับโครงสร้าง
|
|
* @param id ID โครงสร้าง
|
|
* @param level ระดับโครงสร้าง
|
|
*/
|
|
function onClickSort(id: string, level: number) {
|
|
type.value = level;
|
|
modalSortAgency.value = true;
|
|
if (id) {
|
|
breakLoop.value = false;
|
|
const orgId = id;
|
|
for (let index = 0; index < nodes.value.length; index++) {
|
|
const data = nodes.value[index];
|
|
searchAndReplace(data, orgId);
|
|
if (breakLoop.value) break;
|
|
}
|
|
} else {
|
|
const dataList = nodes.value;
|
|
const dataMap = dataList.map((item: any) => ({
|
|
orgTreeId: item.orgTreeId,
|
|
orgLevel: item.orgLevel,
|
|
orgTreeName: item.orgTreeName,
|
|
orgTreeShortName: item.orgTreeShortName,
|
|
orgRevisionId: item.orgRevisionId,
|
|
}));
|
|
|
|
dataSort.value = dataMap;
|
|
}
|
|
|
|
function searchAndReplace(data: any, id: string) {
|
|
if (data.orgTreeId === id) {
|
|
dataSort.value = data.children;
|
|
breakLoop.value = true;
|
|
} else if (data.children) {
|
|
for (const child of data.children) {
|
|
searchAndReplace(child, id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* function ดูประวัดโครงสร้าง
|
|
* @param level ระดับโครงสร้าง
|
|
* @param id ID โครงสร้าง
|
|
*/
|
|
function onClickHistory(level: number, id: string) {
|
|
type.value = level;
|
|
orgId.value = id;
|
|
modalHistory.value = true;
|
|
}
|
|
|
|
watch(
|
|
() => nodeTEST.value,
|
|
() => {
|
|
nodes.value = nodeTEST.value;
|
|
}
|
|
);
|
|
</script>
|
|
|
|
<template>
|
|
<div class="col-12 q-py-sm q-px-sm">
|
|
<div class="q-gutter-sm">
|
|
<div class="row q-col-gutter-sm q-pl-sm">
|
|
<div class="col-2" v-if="store.typeOrganizational === 'draft'">
|
|
<q-btn
|
|
v-if="
|
|
checkPermission($route)?.attrOwnership == 'OWNER' &&
|
|
!store.isLosck
|
|
"
|
|
dense
|
|
flat
|
|
round
|
|
color="primary"
|
|
icon="add"
|
|
@click="onClickAgency(0, {})"
|
|
>
|
|
<q-tooltip>เพิ่มหน่วยงาน</q-tooltip>
|
|
</q-btn>
|
|
</div>
|
|
|
|
<div
|
|
:class="
|
|
store.typeOrganizational === 'draft' &&
|
|
checkPermission($route)?.attrOwnership == 'OWNER' &&
|
|
!store.isLosck
|
|
? 'col-10'
|
|
: 'col-12'
|
|
"
|
|
>
|
|
<q-input dense outlined v-model="filter" label="ค้นหา">
|
|
<template v-slot:append>
|
|
<q-icon name="search" color="grey-5" />
|
|
</template>
|
|
</q-input>
|
|
</div>
|
|
</div>
|
|
<div class="bg-white tree-container q-pa-xs">
|
|
<q-tree
|
|
class="q-pa-sm q-gutter-sm"
|
|
dense
|
|
default-expand-all
|
|
:nodes="lazy"
|
|
node-key="orgTreeId"
|
|
label-key="labelName"
|
|
:filter="filter.trim()"
|
|
:no-results-label="notFound"
|
|
:no-nodes-label="noData"
|
|
v-model:expanded="expanded"
|
|
>
|
|
<template v-slot:default-header="prop">
|
|
<q-item
|
|
clickable
|
|
:active="nodeId == prop.node.orgTreeId"
|
|
@click.stop="updateSelected(prop.node)"
|
|
active-class="my-list-link text-primary text-weight-medium"
|
|
class="row col-12 text-dark items-center q-py-xs q-pl-sm rounded-borders my-list"
|
|
>
|
|
<div>
|
|
<div
|
|
:class="
|
|
prop.node.isOfficer
|
|
? 'text-weight-medium text-blue'
|
|
: 'text-weight-medium'
|
|
"
|
|
>
|
|
<div
|
|
v-if="prop.node.isDeputy == true && prop.node.orgLevel == 0"
|
|
class="text-info"
|
|
>
|
|
{{ prop.node.orgTreeName }}
|
|
</div>
|
|
<div v-else>
|
|
{{ prop.node.orgTreeName }}
|
|
</div>
|
|
{{ prop.node.isOfficer ? "(สกจ.)" : "" }}
|
|
</div>
|
|
<div class="text-weight-light text-grey-8">
|
|
{{ prop.node.orgCode == null ? null : prop.node.orgCode }}
|
|
{{
|
|
prop.node.orgTreeShortName == null
|
|
? null
|
|
: prop.node.orgTreeShortName
|
|
}}
|
|
</div>
|
|
</div>
|
|
|
|
<q-btn
|
|
v-if="store.typeOrganizational === 'draft' && !store.isLosck"
|
|
@click.stop
|
|
flat
|
|
dense
|
|
color="grey-13"
|
|
icon="mdi-dots-vertical"
|
|
round
|
|
class="q-ml-xs"
|
|
>
|
|
<q-menu>
|
|
<q-list
|
|
dense
|
|
v-for="(item, index) in prop.node.orgLevel === 4
|
|
? listAdd.slice(1, 6)
|
|
: listAdd"
|
|
:key="index"
|
|
style="min-width: 100px"
|
|
>
|
|
<q-item
|
|
clickable
|
|
v-close-popup
|
|
@click="
|
|
item.type === 'EDIT'
|
|
? onClickEdit(prop.node)
|
|
: item.type === 'ADD'
|
|
? onClickAgency(prop.node.orgLevel + 1, prop.node)
|
|
: item.type === 'DETAIL'
|
|
? onClickDetail(
|
|
prop.node.orgTreeId,
|
|
prop.node.orgLevel
|
|
)
|
|
: item.type === 'DEL'
|
|
? onClickDel(
|
|
prop.node.orgLevel,
|
|
prop.node.orgTreeId,
|
|
prop.node.orgRootId
|
|
)
|
|
: item.type === 'SORT'
|
|
? onClickSort(prop.node.orgRootId, prop.node.orgLevel)
|
|
: item.type === 'HISTORY'
|
|
? onClickHistory(
|
|
prop.node.orgLevel,
|
|
prop.node.orgTreeId
|
|
)
|
|
: null
|
|
"
|
|
>
|
|
<q-item-section avatar style="min-width: 20px">
|
|
<div class="row items-center">
|
|
<q-icon
|
|
size="xs"
|
|
:color="item.color"
|
|
:name="item.icon"
|
|
/>
|
|
<div v-if="prop.node.orgLevel === 0">
|
|
<div
|
|
class="q-pl-md"
|
|
v-if="
|
|
item.type === 'EDIT' ||
|
|
item.type === 'DEL' ||
|
|
item.type === 'HISTORY' ||
|
|
item.type === 'SORT'
|
|
"
|
|
>
|
|
{{ item.label }}หน่วยงาน
|
|
</div>
|
|
|
|
<div
|
|
v-else-if="item.type === 'ADD'"
|
|
class="q-pl-md"
|
|
>
|
|
{{ item.label }}ส่วนราชการ
|
|
</div>
|
|
<div v-else class="q-pl-md">
|
|
{{ item.label }}
|
|
</div>
|
|
</div>
|
|
|
|
<div v-else>
|
|
<div
|
|
class="q-pl-md"
|
|
v-if="
|
|
item.type === 'ADD' ||
|
|
item.type === 'EDIT' ||
|
|
item.type === 'DEL' ||
|
|
item.type === 'HISTORY' ||
|
|
item.type === 'SORT'
|
|
"
|
|
>
|
|
{{ item.label }}ส่วนราชการ
|
|
</div>
|
|
<div v-else class="q-pl-md">
|
|
{{ item.label }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</q-item-section>
|
|
</q-item>
|
|
</q-list>
|
|
</q-menu>
|
|
</q-btn>
|
|
|
|
<q-btn
|
|
v-else-if="checkPermission($route)?.attrIsGet"
|
|
@click.stop
|
|
flat
|
|
dense
|
|
color="grey-13"
|
|
icon="mdi-dots-vertical"
|
|
round
|
|
>
|
|
<q-menu>
|
|
<q-list
|
|
dense
|
|
v-for="(item, index) in listAdd.slice(5, 6)"
|
|
:key="index"
|
|
style="min-width: 100px"
|
|
>
|
|
<q-item
|
|
clickable
|
|
v-close-popup
|
|
@click="
|
|
onClickDetail(prop.node.orgTreeId, prop.node.orgLevel)
|
|
"
|
|
>
|
|
<q-item-section avatar style="min-width: 20px">
|
|
<q-icon
|
|
size="xs"
|
|
:color="item.color"
|
|
:name="item.icon"
|
|
/>
|
|
</q-item-section>
|
|
<q-item-section>{{ item.label }}</q-item-section>
|
|
</q-item>
|
|
</q-list>
|
|
</q-menu>
|
|
</q-btn>
|
|
</q-item>
|
|
</template>
|
|
</q-tree>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- เพิ่มหน่วยงาน -->
|
|
<DialogAgency
|
|
:modal="dialogAgency"
|
|
:close="onClickAgency"
|
|
v-model:orgLevel="orgLevel"
|
|
:fetchDataTree="props.fetchDataTree"
|
|
:fetchDataTable="props.fetchDataTable"
|
|
v-model:actionType="actionType"
|
|
:dataNode="dataNode"
|
|
:edit="edit"
|
|
/>
|
|
|
|
<!-- รายละเอียดโครงสร้าง -->
|
|
<DialogStructureDetail
|
|
v-model:structure-detail="dialogDetail"
|
|
v-model:treeId="treeId"
|
|
v-model:orgLevel="orgLevel"
|
|
/>
|
|
|
|
<DialogSortAgency
|
|
v-model:sort-agency="modalSortAgency"
|
|
v-model:data="dataSort"
|
|
:fetchDataTree="props.fetchDataTree"
|
|
v-model:type="type"
|
|
/>
|
|
<DialogHistory
|
|
v-model:history="modalHistory"
|
|
v-model:type="type"
|
|
v-model:org-id="orgId"
|
|
/>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.tree-container {
|
|
overflow: auto;
|
|
height: 73vh;
|
|
border: 1px solid #e6e6e7;
|
|
border-radius: 10px;
|
|
}
|
|
|
|
.my-list-link {
|
|
color: rgb(118, 168, 222);
|
|
border-radius: 5px;
|
|
background: #a3d3fb48 !important;
|
|
font-weight: 600;
|
|
border: 1px solid rgba(175, 185, 196, 0.217);
|
|
}
|
|
</style>
|