hrms-mgt/src/modules/21_report/views/02_reportRegistry.vue
2025-02-27 09:44:19 +07:00

1679 lines
64 KiB
Vue

<script setup lang="ts">
import { ref, onMounted, watch, computed } from "vue";
import { useQuasar, type QTableProps } from "quasar";
import * as XLSX from "xlsx";
import http from "@/plugins/http";
import config from "@/app.config";
import { useRoute } from "vue-router";
import { checkPermission } from "@/utils/permissions";
import { useCounterMixin } from "@/stores/mixin";
import { useReportStore } from "@/modules/21_report/store";
import { useRegistryNewDataStore } from "@/modules/04_registryPerson/store";
import { useStructureTree } from "@/stores/structureTree";
import type { OptionData } from "@/modules/07_insignia/interface/index/Main";
import type {
RangeAge,
PosType,
PosLevel,
DataOption,
OptionExecutive,
ResOptionPerson,
DataEducationLevel,
} from "@/modules/21_report/interface/Main";
import DialogOrg from "@/modules/21_report/components/01_org/DialogOrg.vue";
import PopupPersonal from "@/components/Dialogs/PopupPersonalNew.vue";
const $q = useQuasar();
const route = useRoute();
const storeReport = useReportStore();
const stroeRegistry = useRegistryNewDataStore();
const { fetchStructureTree } = useStructureTree();
const mixin = useCounterMixin();
const { messageError, showLoader, hideLoader, date2Thai } = mixin;
const loadingBtn = ref<boolean>(false);
const total = ref<number>(0);
const employeeClass = ref<string>("officer");
const employeeClassOption = ref<OptionData[]>([
{ id: "officer", name: "ข้าราชการ กทม. สามัญ" },
{ id: "employee", name: "ลูกจ้างประจำ กทม." },
]);
const rows = ref<any[]>([]);
const detailReport = ref<any>();
/** ช่วงเวลา */
const dateStart = ref<Date | null>(null); //บรรจุตั้งเเต่วันที่
const dateEnd = ref<Date | null>(null); //บรรจุถึงวันที่
const rangeAge = ref<RangeAge>({
min: 18,
max: 60,
});
/** ตัวแปรประเภทตำแหน่ง */
const posType = ref<string>(""); //ประเภทตำแหน่ง
const posTypeMainOp = ref<PosType[]>([]); //ตัวเลือกประเภทตำแหน่งหลัก
const posTypeOp = ref<PosType[]>(posTypeMainOp.value); //ตัวเลือกประเภทตำแหน่ง
const posLevel = ref<string>(""); //ตัวแปร ระดับตำแหน่ง
const posLevelMainOp = ref<PosLevel[]>([]); //ตัวเลิอก ระดับตำแหน่งหลัก
const posLevelOp = ref<PosLevel[]>(posLevelMainOp.value); //ตัวเลิอก ระดับตำแหน่ง
const position = ref<string>(""); //ตำแหน่งในสายงาน
const positionOp = ref<DataOption[]>([]); //ตำแหน่งในสายงาน option
const positionMainOp = ref<DataOption[]>([]); //ตำแหน่งในสายงาน optionMain
const positionExecutive = ref<string>(""); //ตำแหน่งทางการบริหาร
const executiveOps = ref<DataOption[]>([]); //ตัวเลือกรายการตำแหน่งทางการบริหาร
const executiveOpsMain = ref<DataOption[]>([]); //ตัวเลือกรายการตำแหน่งทางการบริหาร
/** สภานภาพ */
const gender = ref<string>(""); //เพศ
const genderOpsMain = ref<DataOption[]>([]); //ตัวเลือกหลัก เพศ
const genderOps = ref<DataOption[]>([]); //ตัวเลือก เพศ
const status = ref<string>(""); //สถานภาพ
const statusOpsMain = ref<DataOption[]>([]); //ตัวเลือกหลัก สถานภาพ
const statusOps = ref<DataOption[]>([]); //ตัวเลือก สถานภาพ
const education = ref<string>("");
const educationOp = ref<DataOption[]>([]);
const educationOpMain = ref<DataOption[]>([]);
/** ตัวแปรสังกัด */
const modalOrg = ref<boolean>(false);
const org = ref<string>("");
const isProbation = ref<boolean>(false);
const isRetire = ref<boolean>(false);
const isRetireLaw = ref<boolean>(false);
const pagination = ref<any>({
sortBy: "dateAppoint",
descending: true,
page: 1,
rowsPerPage: 20,
});
const sortBy = ref<string>("DESC");
const retireType = ref<string>("");
const retireTypeOps = ref<DataOption[]>([]);
const retireTypeOpsMain = ref<DataOption[]>(stroeRegistry.retireTypeOps);
const retireTypeOpsMainEMP = ref<DataOption[]>(stroeRegistry.retireTypeEmpOps);
const visibleColumnsBase = ref<string[]>([
"no",
"fullName",
"posNo",
"position",
"posType",
"posLevel",
"org",
"positionExecutive",
"gender",
"status",
"education",
"dateAppoint",
"dateRetireLaw",
"age",
"currentPosition",
"lengthPosition",
]);
const columns = computed<QTableProps["columns"]>(() => {
return [
{
name: "no",
align: "left",
label: "ลำดับ",
sortable: true,
field: "no",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "fullName",
align: "left",
label: "ชื่อ-นามสกุล",
sortable: true,
field: "fullName",
headerStyle: "font-size: 14px; min-width: 200px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "posNo",
align: "left",
label: "ตำแหน่งเลขที่",
sortable: true,
field: "posNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "position",
align: "left",
label: employeeClass.value === "officer" ? "ตำแหน่งในสายงาน" : "ตำแหน่ง",
sortable: true,
field: "position",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "posType",
align: "left",
label: "ตำแหน่งประเภท",
sortable: true,
field: "posType",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "posLevel",
align: "left",
label: "ระดับ",
sortable: true,
field: "posLevel",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "org",
align: "left",
label: "สังกัด",
sortable: true,
field: "org",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "positionExecutive",
align: "left",
label: "ตำแหน่งทางการบริหาร",
sortable: true,
field: "positionExecutive",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "gender",
align: "left",
label: "เพศ",
sortable: true,
field: "gender",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "status",
align: "left",
label: "สถานภาพ",
sortable: true,
field: "status",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "education",
align: "left",
label: "วุฒิการศึกษา",
sortable: true,
field: "education",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "dateAppoint",
align: "left",
label: "วันที่บรรจุ",
sortable: true,
field: "dateAppoint",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format(val, row) {
return val ? date2Thai(val) : "";
},
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "dateRetireLaw",
align: "left",
label: "วันที่เกษียณอายุราชการตามกฏหมาย",
sortable: true,
field: "dateRetireLaw",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format(val, row) {
return val ? date2Thai(row.dateRetireLaw) : "";
},
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "age",
align: "left",
label: "อายุ",
sortable: true,
field: "age",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "currentPosition",
align: "left",
label: "ระยะเวลาดำรงตำแหน่งปัจจุบัน",
sortable: true,
field: "currentPosition",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lengthPosition",
align: "left",
label: "ระยะเวลาดำรงตำแหน่งในระดับปัจจุบัน",
sortable: true,
field: "lengthPosition",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
];
});
const visibleColumns = computed<string[]>(() => {
return employeeClass.value === "officer"
? visibleColumnsBase.value
: visibleColumnsBase.value.filter((e: string) => e !== "positionExecutive");
});
const typeTerm = ref<string>("");
const rangeTerm = ref<RangeAge>({
min: 0,
max: 20,
});
function onOpenOrg() {
modalOrg.value = true;
}
/**
* ฟังก์ชั่นค้นหาข้อมูลของ Option Filter
* @param val คำที่ค้นหา
* @param update Function
* @param typeOp ประเภทของ Select
*/
function filterOption(val: string, update: any, typeOp: string) {
update(() => {
const needle = val.toLowerCase();
if (typeOp == "type") {
posTypeOp.value = posTypeMainOp.value.filter(
(v: any) => v.posTypeName.toLowerCase().indexOf(needle) > -1
);
} else if (typeOp == "level") {
posLevelOp.value = posLevelMainOp.value.filter(
(v: any) => v.posLevelName.toString().toLowerCase().indexOf(needle) > -1
);
} else if (typeOp == "pos") {
positionOp.value = positionMainOp.value.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
} else if (typeOp == "Executive") {
executiveOps.value = executiveOpsMain.value.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
} else if (typeOp == "gender") {
genderOps.value = genderOpsMain.value.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
} else if (typeOp == "status") {
statusOps.value = statusOpsMain.value.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
} else if (typeOp == "education") {
educationOp.value = educationOpMain.value.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
} else if (typeOp == "retireType") {
retireTypeOps.value =
employeeClass.value == "officer"
? retireTypeOpsMain.value.filter(
(v: DataOption) => v.name.indexOf(val) > -1
)
: retireTypeOpsMainEMP.value.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
}
});
}
/**
* อัปเดตข้อมูล ระดับตามประเภท
* @param val ประเภท
*/
function updateLevel(val: string) {
posType.value = val;
if (val) {
const listData: any = posTypeMainOp.value.find(
(item: PosType) => item.posTypeName == val
);
if (employeeClass.value === "officer") {
posLevelMainOp.value = listData ? listData.posLevels : [];
} else {
posLevelMainOp.value = listData
? listData.posLevels.map((e: any) => ({
...e,
posLevelName: `${listData?.posTypeShortName} ${e.posLevelName}`,
}))
: [];
}
}
}
/** ฟังก์เรียกข้อมูลรายการประเภทตำแหน่ง*/
async function getType() {
posType.value = "";
posLevel.value = "";
posTypeMainOp.value = [];
posTypeOp.value = [];
posLevelOp.value = [];
const apiPath =
employeeClass.value === "officer"
? config.API.orgPosType
: config.API.orgEmployeeType;
await http.get(apiPath).then(async (res) => {
posTypeMainOp.value = await res.data.result;
});
}
/** ดึงข้อมูลตำแหน่ง */
async function getOptions() {
await http.get(config.API.orgSalaryPosition).then((res) => {
const dataOp = res.data.result;
const uniqueNames = new Set();
const filteredData = dataOp
.filter((item: any) => {
if (!uniqueNames.has(item.positionName)) {
uniqueNames.add(item.positionName);
return true;
}
return false;
})
.map((item: any) => ({
id: item.positionName,
name: item.positionName,
}));
positionMainOp.value = filteredData;
});
}
/** ฟังก์เรียกข้อมูลรายการตำแหน่งทางการบริหาร*/
async function getExecutive() {
await http
.get(config.API.orgPosExecutive)
.then(async (res) => {
const list = await res.data.result.map((e: OptionExecutive) => ({
id: e.id,
name: e.posExecutiveName,
}));
executiveOpsMain.value = list;
})
.catch((err) => {
messageError($q, err);
});
}
/** ดึงข้อมูลรายละเอียดข้อมูลทางกายภาพ */
function getDataPerson() {
http.get(config.API.profileNewMetaMain).then((res) => {
const data = res.data.result;
let optiongenders: DataOption[] = [];
data.genders.map((r: ResOptionPerson) => {
optiongenders.push({
id: r.id.toString(),
name: r.name.toString(),
});
});
genderOpsMain.value = optiongenders;
let optionrelationships: DataOption[] = [];
data.relationships.map((r: ResOptionPerson) => {
optionrelationships.push({
id: r.id.toString(),
name: r.name.toString(),
});
});
statusOpsMain.value = optionrelationships;
});
}
/** function ดึง ข้อมูลระดับการศึกษา*/
function getEducationLevel() {
http
.get(config.API.orgEducationLevel)
.then(async (res) => {
const list = res.data.result.map((r: DataEducationLevel) => ({
id: r.id,
name: r.name,
}));
educationOpMain.value = list;
})
.catch((err) => {
messageError($q, err);
});
}
async function onSearch() {
loadingBtn.value = true;
pagination.value.page = 1;
const queryParams = {
node: storeReport.formFilter.node ?? null,
nodeId: storeReport.formFilter.nodeId ?? "",
posType: posType.value,
posLevel: posLevel.value,
position: position.value,
positionExecutive: positionExecutive.value,
gender: gender.value,
status: status.value,
education: education.value.trim(),
ageMin: rangeAge.value.min,
ageMax: rangeAge.value.max,
dateStart: dateStart.value ?? null,
dateEnd: dateEnd.value ?? null,
isProbation: isProbation.value,
isRetire: isRetire.value,
retireType: retireType.value,
sortBy: sortBy.value ? "dateAppoint" : "",
sort: sortBy.value ? sortBy.value : "ASC",
isRetireLaw: isRetireLaw.value,
};
showLoader();
await http
.get(
config.API.reportOrgByType(
employeeClass.value == "officer" ? "officer" : "emp"
),
{ params: queryParams }
)
.then(async (res) => {
const data = res.data.result.data;
total.value = res.data.result.total;
rows.value = data.map((item: any, index: number) => ({
profileId: item.profileId,
no: index + 1,
fullName: item.firstName
? `${item.prefix ?? ""}${item.firstName ?? ""} ${item.lastName ?? ""}`
: "-",
posNo: item.searchShortName ?? "-",
position: item.position ?? "-",
posType: item.posTypeName ?? "-",
posLevel: item.posLevelName ?? "-",
org: item.org ?? "-",
positionExecutive: item.posExecutiveName ?? "-",
gender: item.gender ?? "-",
status: item.relationship ?? "-",
education: item.degree ?? "-",
dateAppoint: item.dateAppoint ?? "-",
age: item.age ?? "-",
currentPosition: item.currentPosition ?? "-",
lengthPosition: item.lengthPosition ?? "-",
empType: employeeClass.value,
dateRetireLaw: item.dateRetireLaw ?? "-",
}));
})
.catch((e) => {
messageError($q, e);
hideLoader();
})
.finally(() => {
hideLoader();
setTimeout(() => {
loadingBtn.value = false;
}, 500);
});
}
function exportToExcel() {
const newData = rows.value.map((e: any) => {
const { profileId, empType, ...rest } = e;
return {
...rest,
dateAppoint: date2Thai(e.dateAppoint),
dateRetireLaw: date2Thai(e.dateRetireLaw),
};
});
const headers = columns.value?.map((item: any) => item.label) || []; // หัวคอลัมน์ภาษาไทย
const worksheet = XLSX.utils.json_to_sheet(newData, {
header: visibleColumns.value,
});
//แทรกหัวคอลัมน์ภาษาไทย (ใช้ A1, B1, C1 แทน)
XLSX.utils.sheet_add_aoa(worksheet, [headers], { origin: "A1" });
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, `รายงานทะเบียนประวัติ`); //ชื่อไฟล เเละ ชื่อ sheet
XLSX.writeFile(
workbook,
`รายงานทะเบียนประวัติ${
employeeClass.value === "officer"
? "(ข้าราชการ กทม. สามัญ)"
: "(ลูกจ้างประจำ กทม.)"
}.xlsx`
);
}
/** ฟังก์ชันล้างตัวกรองทั้งหมด*/
function clearFilter() {
//สั้งกัด
org.value = "";
storeReport.formFilter.node = null;
storeReport.formFilter.nodeId = null;
posType.value = ""; //ประเภทตำแหน่ง, กลุ่มงาน
posLevel.value = ""; //ระดับตำแหน่ง, ชั้นงาน
position.value = ""; //ตำแหน่งในสายงาน
positionExecutive.value = ""; //ตำแหน่งทางการบริหาร
gender.value = ""; //เพศ
status.value = ""; //สถานภาพ
education.value = ""; //วุฒิการศึกษา
//ช่วงเวลาบรรจุ
dateStart.value = null; //ตั้งแต่วันที่
dateEnd.value = null; //ถึงวันที่
//ช่วงอายุ
rangeAge.value.min = 18;
rangeAge.value.max = 60;
isProbation.value = false; //ทดลองปฏิบัติราชการ
isRetire.value = false; //แสดงผู้พ้นจากราชการ
retireType.value = ""; //ประเภมการพ้นราชการ
expandedModal.value = false;
filterTree.value = "";
typeTerm.value = "";
rangeTerm.value.min = 0;
rangeTerm.value.max = 20;
}
watch(
() => pagination.value.descending,
() => {
if (pagination.value.sortBy === "dateAppoint") {
sortBy.value = pagination.value.descending ? "DESC" : "ASC";
}
}
);
const node = ref<any[]>([]);
const filterTree = ref<string>("");
const expanded = ref<string[]>([]);
const expandedModal = ref<boolean>(false);
async function fetchTree() {
const data = await fetchStructureTree('SYS_REGISTRY_OFFICER' as string, true);
if (data) {
node.value = data;
}
}
/**
* ฟังก์ชันเลือกหน่วยงาน/ส่วนราชการ
* @param data ข้อมูลหน่วยงาน/ส่วนราชการที่ต้องการค้นหาร
* เพื่อค้นหาข้อมูลตามหน่วยงาน/ส่วนราชการ
*/
function updateSelectedTreeMain(data: any) {
org.value = data.orgName;
storeReport.formFilter.node = data.orgLevel;
storeReport.formFilter.nodeId = data.orgTreeId;
expandedModal.value = false;
}
const personId = ref<string>("");
const empType = ref<string>("");
const modalPersonal = ref<boolean>(false);
function onViewInfo(id: string, type: string) {
personId.value = id;
empType.value = type;
modalPersonal.value = true;
}
function updatemodalPersonal(modal: boolean) {
modalPersonal.value = modal;
}
onMounted(async () => {
showLoader();
storeReport.formFilter.node = null;
storeReport.formFilter.nodeId = "";
Promise.all([
getType(),
getOptions(),
getExecutive(),
getDataPerson(),
getEducationLevel(),
fetchTree(),
])
.catch((e) => {
messageError($q, e);
})
.finally(() => {
setTimeout(() => {
loadingBtn.value = false;
hideLoader();
}, 500);
});
});
</script>
<template>
<div class="toptitle text-dark col-12 row items-center">
รายงานทะเบยนประว
</div>
<div class="col-12">
<q-form greedy @submit.prevent @validation-success="onSearch">
<div class="row col-12 q-gutter-sm">
<q-card class="col-12 q-pa-sm">
<div class="row col-12 q-col-gutter-sm no-wrap">
<div class="col-lg-2 col-md-3 col-sm-3 col-xs-12">
<q-select
outlined
dense
v-model="employeeClass"
:options="employeeClassOption"
label="สภานภาพ"
emit-value
map-options
option-label="name"
option-value="id"
@update:model-value="clearFilter(), getType()"
>
</q-select>
</div>
<!-- <div class="col-lg-9 col-md-8 col-sm-8 col-xs-11">
<q-input
outlined
dense
:model-value="org"
autogrow
label="สังกัด"
@click="onOpenOrg"
>
<template v-if="org" v-slot:append>
<q-icon
name="cancel"
@click.stop.prevent="
(org = ''),
(storeReport.formFilter.node = null),
(storeReport.formFilter.nodeId = null)
"
class="cursor-pointer"
/>
</template>
</q-input>
</div> -->
<q-space />
<div>
<q-btn
:loading="loadingBtn"
flat
round
color="primary"
icon="download"
v-if="checkPermission($route)?.attrIsGet"
:disable="rows.length == 0"
@click="exportToExcel()"
>
<!-- <q-menu>
<q-list style="min-width: 150px">
<q-item
clickable
v-close-popup
@click="
genReportXLSX(
detailReport,
`${
employeeClass == 'officer'
? 'รายงานสถิติข้อมูลข้าราชการ กทม. สามัญ'
: 'รายงานสถิติข้อมูลลูกจ้างประจำ กทม.'
}`,
'pdf'
)
"
>
<q-item-section avatar
><q-icon color="red" name="mdi-file-pdf"
/></q-item-section>
<q-item-section>ไฟล์ .pdf</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="
genReportXLSX(
detailReport,
`${
employeeClass == 'officer'
? 'รายงานสถิติข้อมูลข้าราชการ กทม. สามัญ'
: 'รายงานสถิติข้อมูลลูกจ้างประจำ กทม.'
}`,
'xlsx'
)
"
>
<q-item-section avatar
><q-icon color="green" name="mdi-file-excel"
/></q-item-section>
<q-item-section>ไฟล์ .xlsx</q-item-section>
</q-item>
</q-list>
</q-menu> -->
</q-btn>
</div>
</div>
</q-card>
<q-card class="row col-12">
<div class="col-12 row q-pa-sm q-col-gutter-sm">
<div class="col">
<q-card flat bordered class="col-12">
<q-card-section
bordered
class="bg-primary text-subtitle2 text-white q-pa-sm row col-12 items-center"
>
<q-icon name="mdi-filter" class="q-mr-xs" />
ตัวกรอง
<q-space />
<q-btn
dense
size="12px"
class="q-px-sm"
rounded
flat
label="ล้างทั้งหมด"
@click.stop.pervent="clearFilter"
/>
</q-card-section>
<q-separator />
<div class="row col-12">
<div class="row col-12 q-col-x-gutter-xs items-center">
<div class="col-12">
<q-expansion-item
v-model="expandedModal"
dense
dense-toggle
expand-separator
class="expansion-item"
>
<template #header>
<div class="full-width flex items-stretch">
<q-input
dense
:model-value="org ? org : 'ทั้งหมด'"
autogrow
borderless
label="สังกัด"
class="bg-white full-width"
>
<template v-if="org" v-slot:append>
<q-icon
name="cancel"
@click.stop.prevent="
(org = ''),
(storeReport.formFilter.node = null),
(storeReport.formFilter.nodeId = null)
"
class="cursor-pointer"
style="opacity: 0.6"
/>
</template>
</q-input>
</div>
<!-- @click="onOpenOrg" -->
</template>
<q-separator />
<q-card-section>
<q-input
dense
outlined
v-model="filterTree"
label="ค้นหา"
>
<template v-slot:append>
<q-icon
v-if="filterTree !== ''"
name="clear"
class="cursor-pointer"
@click="filterTree = ''"
/>
<q-icon v-else name="search" color="grey-5" />
</template>
</q-input>
<q-tree
class="tree-container q-mt-sm"
dense
:nodes="node"
node-key="orgTreeName"
label-key="labelName"
v-model:expanded="expanded"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
:filter="filterTree.trim()"
>
<template v-slot:default-header="prop">
<q-item
clickable
active-class="my-list-link text-primary text-weight-medium"
class="row col-12 items-center text-dark q-py-xs q-pl-sm rounded-borders my-list"
:active="
storeReport.formFilter.nodeId ===
prop.node.orgTreeId
"
@click.stop="updateSelectedTreeMain(prop.node)"
>
<div>
<div class="text-weight-medium">
{{ prop.node.orgTreeName }}
</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-item>
</template>
</q-tree>
</q-card-section>
</q-expansion-item>
</div>
<div class="col-12"><q-separator /></div>
<div class="col-12 q-px-sm">
<q-select
class="bg-white"
hide-bottom-space
dense
lazy-rules
borderless
hide-selected
:model-value="posType == '' ? 'ทั้งหมด' : posType"
:label="`${
employeeClass == 'officer'
? 'ประเภทตำแหน่ง'
: 'กลุ่มงาน'
}`"
emit-value
map-options
:options="posTypeOp"
option-value="posTypeName"
option-label="posTypeName"
use-input
fill-input
@update:model-value="(value:string)=>(posLevel = '',updateLevel(value))"
@filter="(inputValue:any, doneFn:Function) =>
filterOption(inputValue, doneFn,'type')
"
>
<template v-if="posType" v-slot:append>
<q-icon
name="cancel"
@click.stop.prevent="
(posType = ''), (posLevel = '')
"
class="cursor-pointer"
style="opacity: 0.6"
/>
</template>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
ไม่มีข้อมูล
</q-item-section>
</q-item>
</template>
</q-select>
</div>
<div class="col-12"><q-separator /></div>
<div class="col-12 q-px-sm">
<q-select
class="bg-white"
hide-bottom-space
dense
:readonly="!posType"
lazy-rules
borderless
hide-selected
:model-value="posLevel == '' ? 'ทั้งหมด' : posLevel"
:label="`${
employeeClass == 'officer'
? 'ระดับตำแหน่ง'
: 'ระดับชั้นงาน'
}`"
emit-value
map-options
:options="posLevelOp"
option-value="posLevelName"
option-label="posLevelName"
use-input
fill-input
@filter="(inputValue:any, doneFn:Function) =>
filterOption(inputValue, doneFn,'level')
"
@update:model-value="(value:string)=>(posLevel = value)"
>
<template v-if="posLevel" v-slot:append>
<q-icon
name="cancel"
@click.stop.prevent="posLevel = ''"
class="cursor-pointer"
style="opacity: 0.6"
/>
</template>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
ไม่มีข้อมูล
</q-item-section>
</q-item>
</template>
</q-select>
</div>
<div class="col-12"><q-separator /></div>
<div class="col-12 q-px-sm">
<q-select
dense
:model-value="position == '' ? 'ทั้งหมด' : position"
:label="`${
employeeClass == 'officer'
? 'ตำแหน่งในสายงาน'
: 'ตำแหน่ง'
}`"
emit-value
map-options
fill-input
hide-selected
borderless
lazy-rules
bg-color="white"
:rules="[(val:string) => !!val || `${'กรุณาเลือกตำแหน่งในสายงาน'}`,]"
hide-bottom-space
option-label="name"
option-value="name"
:options="positionOp"
use-input
@filter="(inputValue:any,doneFn:Function) => filterOption(inputValue, doneFn,'pos') "
@update:model-value="(value:string)=>(position = value)"
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
ไม่มีข้อมูล
</q-item-section>
</q-item>
</template>
<template v-if="position" v-slot:append>
<q-icon
name="cancel"
@click.stop.prevent="position = ''"
class="cursor-pointer"
style="opacity: 0.6"
/> </template
></q-select>
</div>
<div class="col-12" v-if="employeeClass === 'officer'">
<q-separator />
</div>
<div
class="col-12 q-px-sm"
v-if="employeeClass === 'officer'"
>
<q-select
label="ตำแหน่งทางการบริหาร"
:model-value="
positionExecutive == ''
? 'ทั้งหมด'
: positionExecutive
"
:options="executiveOps"
emit-value
dense
borderless
map-options
option-label="name"
option-value="name"
fill-input
use-input
hide-selected
bg-color="white"
@filter="(inputValue:any,doneFn:Function) => filterOption(inputValue, doneFn,'Executive') "
@update:model-value="(value:string)=>(positionExecutive = value)"
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
ไม่มีข้อมูล
</q-item-section>
</q-item>
</template>
<template v-if="positionExecutive" v-slot:append>
<q-icon
name="cancel"
@click.stop.prevent="positionExecutive = ''"
class="cursor-pointer"
style="opacity: 0.6"
/>
</template>
</q-select>
</div>
<div class="col-12"><q-separator /></div>
<div class="col-12 q-px-sm">
<q-select
label="เพศ"
:model-value="gender == '' ? 'ทั้งหมด' : gender"
:options="genderOps"
emit-value
dense
map-options
borderless
option-label="name"
option-value="name"
fill-input
use-input
hide-selected
bg-color="white"
@filter="(inputValue:any,doneFn:Function) => filterOption(inputValue, doneFn,'gender') "
@update:model-value="(value:string)=>(gender = value)"
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
ไม่มีข้อมูล
</q-item-section>
</q-item>
</template>
<template v-if="gender" v-slot:append>
<q-icon
name="cancel"
@click.stop.prevent="gender = ''"
class="cursor-pointer"
style="opacity: 0.6"
/>
</template>
</q-select>
</div>
<div class="col-12"><q-separator /></div>
<div class="col-12 q-px-sm">
<q-select
label="สถานภาพ"
:model-value="status == '' ? 'ทั้งหมด' : status"
:options="statusOps"
emit-value
dense
borderless
map-options
option-label="name"
option-value="name"
fill-input
use-input
hide-selected
bg-color="white"
@filter="(inputValue:any,doneFn:Function) => filterOption(inputValue, doneFn,'status') "
@update:model-value="(value:string)=>(status = value)"
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
ไม่มีข้อมูล
</q-item-section>
</q-item>
</template>
<template v-if="status" v-slot:append>
<q-icon
name="cancel"
@click.stop.prevent="status = ''"
class="cursor-pointer"
style="opacity: 0.6"
/>
</template>
</q-select>
</div>
<div class="col-12"><q-separator /></div>
<div class="col-12 q-px-sm">
<q-input
dense
v-model="education"
borderless
label="วุฒิการศึกษา"
class="bg-white full-width"
>
<template v-if="education" v-slot:append>
<q-icon
name="cancel"
@click.stop.prevent="education = ''"
class="cursor-pointer"
style="opacity: 0.6"
/>
</template>
</q-input>
<!-- <q-select
label="วุฒิการศึกษา"
:model-value="education == '' ? 'ทั้งหมด' : education"
:options="educationOp"
emit-value
dense
borderless
map-options
option-label="name"
option-value="name"
fill-input
use-input
hide-selected
bg-color="white"
@filter="(inputValue:any,doneFn:Function) => filterOption(inputValue, doneFn,'education') "
@update:model-value="(value:string)=>(education = value)"
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
ไม่มีข้อมูล
</q-item-section>
</q-item>
</template>
<template v-if="education" v-slot:append>
<q-icon
name="cancel"
@click.stop.prevent="education = ''"
class="cursor-pointer"
style="opacity: 0.6"
/>
</template>
</q-select> -->
</div>
<div class="col-12"><q-separator /></div>
<div class="col-12 q-pa-sm">
<div class="text-grey-8 text-caption">ช่วงเวลาบรรจุ</div>
<div class="col-12">
<datepicker
menu-class-name="modalfix"
v-model="dateStart"
:locale="'th'"
autoApply
date-picker
:enableTimePicker="false"
@update:model-value="dateEnd = null"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
:model-value="
dateStart === null ? '' : date2Thai(dateStart)
"
:label="`${'ตั้งเเต่วันที่'}`"
bg-color="white"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
size="18px"
>
</q-icon>
</template>
<template v-if="dateStart" v-slot:append>
<q-icon
name="cancel"
@click.stop.prevent="
(dateStart = null), (dateEnd = null)
"
class="cursor-pointer"
style="opacity: 0.6"
/>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-12">
<datepicker
menu-class-name="modalfix"
v-model="dateEnd"
:locale="'th'"
autoApply
:readonly="!dateStart"
date-picker
:enableTimePicker="false"
:min-date="dateStart"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
:readonly="!dateStart"
:model-value="
dateEnd === null ? '' : date2Thai(dateEnd)
"
:label="`${'ถึงวันที่'}`"
bg-color="white"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
size="18px"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
</div>
<div class="col-12"><q-separator /></div>
<div class="col-12 q-pa-sm">
<div class="col-12 row">
<div class="text-grey-8 text-caption">ช่วงอายุ</div>
<q-space />
</div>
<div class="q-pa-sm row">
<q-range
v-model="rangeAge"
:min="18"
:max="60"
label
color="primary"
track-size="4px"
thumb-size="16px"
switch-label-side
dense
><!-- :marker-labels="objMarkerLabel" -->
</q-range>
<div
class="col-12 justify-around items-center row no-wrap q-pt-sm"
>
<div>
<q-input
readonly
type="number"
style="max-width: 60px"
v-model="rangeAge.min"
dense
standout="bg-teal text-white"
/>
</div>
<div class="q-px-sm text-grey-8">-</div>
<div>
<q-input
readonly
type="number"
style="max-width: 60px"
v-model="rangeAge.max"
dense
standout="bg-teal text-white"
/>
</div>
</div>
</div>
</div>
<div class="col-12"><q-separator /></div>
<div
class="col-12 row q-pl-sm items-center no-wrap"
v-if="employeeClass === 'officer'"
>
<div class="text-grey-8">ทดลองปฏิบัติราชการ</div>
<q-space />
<q-toggle v-model="isProbation" size="sm" />
</div>
<div class="col-12 row q-pl-sm items-center no-wrap">
<div class="text-grey-8">
แสดงผู้เกษียณอายุราชการในปีงบประมาณปัจจุบัน
</div>
<q-space />
<q-toggle v-model="isRetireLaw" size="sm" />
</div>
<div class="col-12 row q-pl-sm items-center">
<div class="text-grey-8">แสดงผู้พ้นจากราชการ</div>
<q-space />
<q-toggle
v-model="isRetire"
size="sm"
@update:model-value="(value:string)=>(!value ? retireType = '':retireType)"
/>
<q-select
class="col-12 q-pr-sm q-pb-md"
v-if="isRetire"
:label="`ประเภท${
employeeClass == 'officer' ? `การพ้นจากราชการ` : ``
}`"
:model-value="retireType == '' ? 'ทั้งหมด' : retireType"
:options="retireTypeOps"
emit-value
dense
map-options
option-label="name"
option-value="id"
fill-input
use-input
hide-selected
bg-color="white"
@filter="(inputValue:any,doneFn:Function) => filterOption(inputValue, doneFn,'retireType') "
@update:model-value="(value:string)=>(retireType = value)"
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
ไม่มีข้อมูล
</q-item-section>
</q-item>
</template>
<template v-if="retireType" v-slot:append>
<q-icon
name="cancel"
@click.stop.prevent="retireType = ''"
class="cursor-pointer"
style="opacity: 0.6"
/>
</template>
</q-select>
</div>
<div class="col-12"><q-separator /></div>
<div class="col-12">
<div class="">
<q-radio
v-model="typeTerm"
val="position"
label="ระยะเวลาดำรงตำแหน่งปัจจุบัน"
size="xs"
class="text-grey-8"
/>
<q-radio
v-model="typeTerm"
val="level"
label="ระยะเวลาดำรงตำแหน่งในระดับปัจจุบัน"
size="xs"
class="text-grey-8"
/>
</div>
<div class="q-pa-md" v-if="typeTerm">
<q-range
v-model="rangeTerm"
:min="0"
:max="20"
label
color="primary"
track-size="4px"
thumb-size="16px"
switch-label-side
dense
>
</q-range>
<div
class="col-12 justify-around items-center row no-wrap q-pt-sm"
>
<div>
<q-input
readonly
type="number"
style="max-width: 60px"
v-model="rangeTerm.min"
dense
standout="bg-teal text-white"
/>
</div>
<div class="q-px-sm text-grey-8">-</div>
<div>
<q-input
readonly
type="number"
style="max-width: 60px"
v-model="rangeTerm.max"
dense
standout="bg-teal text-white"
/>
</div>
</div>
</div>
</div>
</div>
<div class="col-12"><q-separator /></div>
<div class="col-12 row q-pa-sm">
<q-space />
<q-btn
dense
class="q-px-md"
label="ค้นหา"
unelevated
color="public"
type="submit"
>
</q-btn>
</div>
</div>
</q-card>
</div>
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-12 col-xs-12 flex">
<q-card bordered class="col-12">
<div class="row col-12 q-pa-sm items-center">
<div class="row items-center q-pa-sm no-wrap">
<div class="q-pr-md">จำนวนที่พบทั้งหมด</div>
<q-space />
<q-badge
class="q-pa-xs text-primary text-bold text-subtitle2"
color="white"
>
{{ total.toLocaleString() }}
</q-badge>
</div>
<q-space />
<div class="text-weight-medium q-pr-sm">
เรียงตามวันที่บรรจุเเต่งตั้ง :
</div>
<q-btn-dropdown
:label="
sortBy
? sortBy == 'ASC'
? `(เก่า-ล่าสุด)`
: `(ล่าสุด-เก่า)`
: 'ลำดับการเเสดงผล'
"
dropdown-icon="mdi-chevron-down"
text-color="primary"
label-color="white"
flat
dense
rounded
>
<q-list>
<q-item
clickable
v-close-popup
@click="
(sortBy = 'DESC'),
(pagination.sortBy = 'dateAppoint'),
(pagination.descending = true)
"
>
<q-item-section>
<q-item-label>(ล่าสุด-เก่า)</q-item-label>
</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="
(sortBy = 'ASC'),
(pagination.sortBy = 'dateAppoint'),
(pagination.descending = false)
"
>
<q-item-section>
<q-item-label>(เก่า-ล่าสุด)</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</div>
<d-table
:columns="columns"
:rows="rows"
row-key="id"
:visible-columns="visibleColumns"
v-model:pagination="pagination"
:rows-per-page-options="[25, 50, 100]"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
>
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props">
<q-td v-for="col in props.cols" :key="col.id">
<div v-if="col.name === 'no'">
{{ props.rowIndex + 1 }}
</div>
<div v-else-if="col.name === 'fullName'">
{{ col.value }}
<q-btn
size="sm"
v-if="checkPermission($route)?.attrIsGet"
flat
dense
icon="info"
color="info"
@click.pervent="
onViewInfo(props.row.profileId, props.row.empType)
"
>
<q-tooltip>ทะเบยนประว</q-tooltip>
</q-btn>
</div>
<div
v-else-if="col.name === 'org'"
class="table_ellipsis"
>
{{ col.value ? col.value : "-" }}
</div>
<div v-else>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</q-card>
</div>
</div>
</q-card>
</div>
</q-form>
</div>
<DialogOrg v-model:modal="modalOrg" v-model:name="org" />
<PopupPersonal
:modal="modalPersonal"
:id="personId"
@update:modal="updatemodalPersonal"
:type="empType"
/>
</template>
<style lang="scss" scope>
.q-item.hover-green:hover {
background-color: #d5f1ee57;
border-radius: 2px;
}
.q-item.hover-green {
padding: 10px;
}
.tree-container {
overflow: auto;
height: 50vh;
}
.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);
}
.expansion-item {
.q-item__section--side {
padding-right: 3px;
}
.q-item--dense {
min-height: 32px;
padding: 2px 9px;
}
}
</style>