ปรับ ui รายงาน

This commit is contained in:
setthawutttty 2025-03-14 17:01:50 +07:00
parent ebb5d0e65f
commit 8a6d34a02f
10 changed files with 4166 additions and 3079 deletions

View file

@ -10,6 +10,8 @@ import { checkPermission } from "@/utils/permissions";
import { useCounterMixin } from "@/stores/mixin"; import { useCounterMixin } from "@/stores/mixin";
import type { DataOption } from "@/modules/09_leave/interface/index/Main"; import type { DataOption } from "@/modules/09_leave/interface/index/Main";
import LoadView from "@/components/LoadView.vue";
import genReportXLSX from "@/plugins/genreportxlsx"; import genReportXLSX from "@/plugins/genreportxlsx";
/** use*/ /** use*/
const isReport = ref<boolean>(true); const isReport = ref<boolean>(true);
@ -17,6 +19,8 @@ const mixin = useCounterMixin();
const $q = useQuasar(); const $q = useQuasar();
const { showLoader, hideLoader, date2Thai, dateToISO, messageError } = mixin; const { showLoader, hideLoader, date2Thai, dateToISO, messageError } = mixin;
const isLoadPDF = ref<boolean>(false);
const year = ref<number>(new Date().getFullYear()); const year = ref<number>(new Date().getFullYear());
const reportSelect = ref<DataOption>({ const reportSelect = ref<DataOption>({
@ -45,9 +49,9 @@ const detailReport = ref<any>();
* @param endDate นสนส * @param endDate นสนส
*/ */
async function getReportRecruit() { async function getReportRecruit() {
isLoadPDF.value = true;
pdfSrc.value = undefined; pdfSrc.value = undefined;
page.value = 1; page.value = 1;
showLoader();
await http await http
.get( .get(
config.API.reportRecruit(reportSelect.value.id) + config.API.reportRecruit(reportSelect.value.id) +
@ -63,7 +67,7 @@ async function getReportRecruit() {
messageError($q, err); messageError($q, err);
}) })
.finally(() => { .finally(() => {
hideLoader(); isLoadPDF.value = false;
}); });
} }
@ -134,87 +138,46 @@ function filterFnOptions(val: any, update: Function) {
); );
}); });
} }
function clearFilter() {
reportSelect.value = {
id: "",
name: "",
};
detailReport.value = undefined;
year.value = new Date().getFullYear();
pdfSrc.value = undefined;
detailReport.value = undefined;
}
</script> </script>
<template> <template>
<div class="toptitle text-dark col-12 row items-center"> <div class="toptitle text-dark col-12 row items-center">
รายงานสถสำหรบการสอบแขงข รายงานสถสำหรบการสอบแขงข
</div> </div>
<div class="q-pa-sm q-gutter-sm"> <q-form greedy @submit.prevent @validation-success="getReportRecruit">
<q-card flat bordered class="col-12"> <div class="row q-col-gutter-sm">
<div class="row q-col-gutter-sm q-pa-sm"> <div class="col-12">
<div class="row col-12 q-col-gutter-sm"> <q-card class="q-pa-sm">
<q-select <div class="row q-col-gutter-sm no-wrap">
outlined
dense
v-model="reportSelect"
:options="reportSelectOption"
label="รายงาน"
map-options
hide-selected
fill-input
option-label="name"
option-value="id"
use-input
style="width: 500px"
@update:model-value="getReportRecruit"
@filter="(inputValue: any,
doneFn: Function) => filterFnOptions(inputValue, doneFn)"
><template v-slot:no-option>
<q-item>
<q-item-section class="text-grey"> ไมอม </q-item-section>
</q-item>
</template>
</q-select>
<datepicker
v-model="year"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
@update:model-value="getReportRecruit"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
class="bg-white"
dense
outlined
:model-value="Number(year) + 543"
:label="`${'ปีงบประมาณ'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
<q-space /> <q-space />
<div>
<q-btn <q-btn
:loading="isLoadPDF"
flat flat
round round
color="primary" color="primary"
icon="download" icon="download"
:disable=" :disable="!checkPermission($route)?.attrIsGet || !detailReport"
!checkPermission($route)?.attrIsGet ||
isReport ||
reportSelect.id == ''
"
> >
<q-menu> <q-menu>
<q-list style="min-width: 150px"> <q-list style="min-width: 150px">
<q-item <q-item
clickable clickable
v-close-popup v-close-popup
@click="genReportXLSX(detailReport, reportSelect.name, 'pdf')" @click="
genReportXLSX(detailReport, reportSelect.name, 'pdf')
"
> >
<q-item-section avatar <q-item-section avatar
><q-icon color="red" name="mdi-file-pdf" ><q-icon color="red" name="mdi-file-pdf"
@ -237,15 +200,111 @@ function filterFnOptions(val: any, update: Function) {
</div> </div>
</div> </div>
</q-card> </q-card>
<q-card flat bordered class="col-12"> </div>
<q-card-section class="col-lg-9 col-md-8 col-xs-12 scroll"> <div class="col-12">
<q-card class="q-pa-sm">
<div class="row q-col-gutter-sm">
<div class="col">
<q-card flat bordered>
<q-card-section
bordered
class="bg-primary text-subtitle2 text-white q-pa-sm row 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-card-section class="q-pa-none">
<div class="col-12 q-px-sm">
<q-select
borderless
dense
v-model="reportSelect"
:options="reportSelectOption"
label="รายงาน"
map-options
hide-selected
fill-input
option-label="name"
option-value="id"
use-input
@filter="(inputValue: any,
doneFn: Function) => filterFnOptions(inputValue, doneFn)"
><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 class="col-12 q-px-sm">
<datepicker
v-model="year"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
class="bg-white"
dense
borderless
:model-value="Number(year) + 543"
:label="`${'ปีงบประมาณ'}`"
>
<template v-slot:prepend>
<q-icon
size="18px"
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn
dense
class="q-px-md"
label="ค้นหา"
unelevated
color="public"
type="submit"
:disable="!reportSelect.id"
/>
</q-card-actions>
</q-card>
</div>
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-12 col-xs-12 flex">
<q-splitter <q-splitter
v-model="splitterModel"
horizontal horizontal
style=" style="
height: 70vh; height: 75vh;
border: 1px solid rgb(210, 210, 210); border: 1px solid rgb(210, 210, 210);
border-radius: 5px; border-radius: 5px;
width: 100%;
" "
before-class="overflow-hidden disable" before-class="overflow-hidden disable"
separator-class="bg-white disabled" separator-class="bg-white disabled"
@ -286,7 +345,14 @@ function filterFnOptions(val: any, update: Function) {
</template> </template>
<template v-slot:after> <template v-slot:after>
<div class="q-pa-md"> <div class="q-pa-md">
<VuePDF ref="vuePDFRef" :pdf="pdfSrc" :page="page" fit-parent /> <LoadView v-if="isLoadPDF" />
<VuePDF
v-else-if="!isLoadPDF && pdfSrc"
ref="vuePDFRef"
:pdf="pdfSrc"
:page="page"
fit-parent
/>
</div> </div>
</template> </template>
<template v-slot:default> <template v-slot:default>
@ -325,7 +391,20 @@ function filterFnOptions(val: any, update: Function) {
</div> </div>
</template> </template>
</q-splitter> </q-splitter>
</q-card-section> </div>
</div>
</q-card>
</div>
</div>
</q-form>
<div class="q-pa-sm q-gutter-sm">
<q-card flat bordered class="col-12">
<div class="row q-col-gutter-sm q-pa-sm">
<div class="row col-12 q-col-gutter-sm">
<q-space />
</div>
</div>
</q-card> </q-card>
</div> </div>
</template> </template>

View file

@ -17,6 +17,10 @@ import LoadView from "@/components/LoadView.vue";
import genReportXLSX from "@/plugins/genreportxlsx"; import genReportXLSX from "@/plugins/genreportxlsx";
/** use*/ /** use*/
const expandedModal = ref<boolean>(false);
const org = ref<string>("");
const isLoadStructureTree = ref<boolean>(true);
const $q = useQuasar(); const $q = useQuasar();
const route = useRoute(); const route = useRoute();
const { fetchStructureTree } = useStructureTree(); const { fetchStructureTree } = useStructureTree();
@ -78,21 +82,27 @@ const node = ref<DataStructureTree[]>([]);
const expanded = ref<string[]>([]); const expanded = ref<string[]>([]);
async function fetchDataTree() { async function fetchDataTree() {
try {
isLoadStructureTree.value = true;
node.value = await fetchStructureTree(route.meta.Key as string, true); node.value = await fetchStructureTree(route.meta.Key as string, true);
} catch (err) {
messageError($q, err);
} finally {
isLoadStructureTree.value = false;
}
} }
function onUpdateYear(val: number) { function onUpdateYear(val: number) {
dateStart.value = new Date(val - 1, 9, 1); dateStart.value = new Date(val - 1, 9, 1);
dateEnd.value = new Date(val, 8, 30); dateEnd.value = new Date(val, 8, 30);
reportType.value && fetchDataReport();
} }
function onSelectedNode(id: string, level: number, orgDnaId: string) { function onSelectedNode(data: any) {
nodeId.value = id; org.value = data.orgName;
nodeLevel.value = level; nodeId.value = data.orgTreeId;
orgTreeDnaId.value = orgDnaId; nodeLevel.value = data.orgLevel;
reportType.value && fetchDataReport(); orgTreeDnaId.value = data.orgTreeDnaId;
expandedModal.value = false;
} }
function fetchDataReport() { function fetchDataReport() {
@ -175,6 +185,20 @@ function backPage() {
} }
} }
function clearFilter() {
org.value = "";
nodeId.value = "";
nodeLevel.value = 0;
orgTreeDnaId.value = "";
reportType.value = undefined;
detailReport.value = undefined;
pdfSrc.value = undefined;
year.value = new Date().getFullYear();
dateStart.value = new Date(year.value - 1, 9, 1);
dateEnd.value = new Date(year.value + 1, 8, 30);
}
onMounted(() => { onMounted(() => {
fetchDataTree(); fetchDataTree();
}); });
@ -185,40 +209,157 @@ onMounted(() => {
รายงานบรรจ แตงต าย โอน รายงานบรรจ แตงต าย โอน
</div> </div>
<div class="q-pa-sm q-gutter-sm"> <q-form greedy @submit.prevent @validation-success="fetchDataReport">
<q-card flat bordered class="col-12"> <div class="row q-col-gutter-sm">
<div class="row q-col-gutter-sm q-pa-sm"> <div class="col-12">
<div class="row col-12"> <q-card class="q-pa-sm">
<q-select <div class="row q-col-gutter-sm no-wrap">
outlined
dense
v-model="reportType"
:options="optionReport"
label="รายงาน"
map-options
option-label="name"
style="width: 500px"
@update:model-value="fetchDataReport"
>
</q-select>
<q-space /> <q-space />
<div>
<q-btn <q-btn
:loading="isLoadPDF" :loading="isLoadPDF"
flat flat
round round
color="primary" color="primary"
icon="download" icon="download"
:disable="!reportType || !nodeId" :disable="!reportType || !nodeId || !detailReport"
@click="genReportXLSX(detailReport, `${year}_${reportType?.name}`)" @click="
genReportXLSX(detailReport, `${year}_${reportType?.name}`)
"
> >
</q-btn> </q-btn>
</div> </div>
</div>
</q-card>
</div>
<div class="col-12">
<q-card class="q-pa-sm">
<div class="row q-col-gutter-sm">
<div class="col">
<q-card flat bordered>
<q-card-section
bordered
class="bg-primary text-subtitle2 text-white q-pa-sm row 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-card-section class="q-pa-none">
<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"
autogrow
borderless
label="สังกัด"
class="bg-white full-width"
>
</q-input>
</div>
<!-- @click="onOpenOrg" -->
</template>
<q-separator />
<div class="row col-12"> <q-card-section v-if="isLoadStructureTree">
<q-card bordered class="col-12 filter-card q-pa-sm"> <LoadView />
<div class="row col-12 q-col-gutter-sm"> </q-card-section>
<q-card-section v-else>
<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
dense
:nodes="node"
node-key="orgTreeId"
label-key="labelName"
v-model:expanded="expanded"
:filter="filterTree.trim()"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
class="tree-container-report q-mt-sm"
>
<template v-slot:default-header="prop">
<q-item
@click.stop="onSelectedNode(prop.node)"
:active="nodeId === prop.node.orgTreeId"
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"
>
<div> <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>
<q-separator />
<div class="col-12 q-px-sm">
<q-select
borderless
dense
v-model="reportType"
:options="optionReport"
label="รายงาน"
map-options
option-label="name"
>
</q-select>
</div>
<q-separator />
<div class="col-12 q-pa-sm">
<div class="row q-col-gutter-y-xs">
<div class="text-grey-8 text-caption">เลอกชวงเวลา</div>
<div class="col-12">
<datepicker <datepicker
v-model="year" v-model="year"
:locale="'th'" :locale="'th'"
@ -235,13 +376,13 @@ onMounted(() => {
<q-input <q-input
class="bg-white" class="bg-white"
dense dense
outlined
:model-value="Number(year) + 543" :model-value="Number(year) + 543"
:label="`${'ปีงบประมาณ'}`" :label="`${'ปีงบประมาณ'}`"
> >
<template v-slot:prepend> <template v-slot:prepend>
<q-icon <q-icon
name="event" name="event"
size="18px"
class="cursor-pointer" class="cursor-pointer"
style="color: var(--q-primary)" style="color: var(--q-primary)"
> >
@ -251,8 +392,7 @@ onMounted(() => {
</template> </template>
</datepicker> </datepicker>
</div> </div>
<div class="col-12">
<div>
<datepicker <datepicker
menu-class-name="modalfix" menu-class-name="modalfix"
v-model="dateStart" v-model="dateStart"
@ -260,7 +400,6 @@ onMounted(() => {
autoApply autoApply
:enableTimePicker="false" :enableTimePicker="false"
week-start="0" week-start="0"
@update:model-value="fetchDataReport"
:max-date="dateEnd" :max-date="dateEnd"
> >
<template #year="{ year }">{{ year + 543 }}</template> <template #year="{ year }">{{ year + 543 }}</template>
@ -270,10 +409,10 @@ onMounted(() => {
<template #trigger> <template #trigger>
<q-input <q-input
class="bg-white" class="bg-white"
outlined
dense dense
borderless :model-value="
:model-value="dateStart ? date2Thai(dateStart) : null" dateStart ? date2Thai(dateStart) : null
"
:label="`${'ตั้งเเต่วันที่'}`" :label="`${'ตั้งเเต่วันที่'}`"
> >
<template v-slot:prepend> <template v-slot:prepend>
@ -281,6 +420,7 @@ onMounted(() => {
name="event" name="event"
class="cursor-pointer" class="cursor-pointer"
color="primary" color="primary"
size="18px"
> >
</q-icon> </q-icon>
</template> </template>
@ -288,7 +428,7 @@ onMounted(() => {
</template> </template>
</datepicker> </datepicker>
</div> </div>
<div> <div class="col-12">
<datepicker <datepicker
menu-class-name="modalfix" menu-class-name="modalfix"
v-model="dateEnd" v-model="dateEnd"
@ -296,7 +436,6 @@ onMounted(() => {
autoApply autoApply
:enableTimePicker="false" :enableTimePicker="false"
week-start="0" week-start="0"
@update:model-value="fetchDataReport"
:min-date="dateStart" :min-date="dateStart"
> >
<template #year="{ year }">{{ year + 543 }}</template> <template #year="{ year }">{{ year + 543 }}</template>
@ -306,9 +445,7 @@ onMounted(() => {
<template #trigger> <template #trigger>
<q-input <q-input
class="bg-white" class="bg-white"
outlined
dense dense
borderless
:model-value="dateEnd ? date2Thai(dateEnd) : null" :model-value="dateEnd ? date2Thai(dateEnd) : null"
:label="`${'ถึงวันที่'}`" :label="`${'ถึงวันที่'}`"
> >
@ -317,6 +454,7 @@ onMounted(() => {
name="event" name="event"
class="cursor-pointer" class="cursor-pointer"
color="primary" color="primary"
size="18px"
> >
</q-icon> </q-icon>
</template> </template>
@ -325,75 +463,30 @@ onMounted(() => {
</datepicker> </datepicker>
</div> </div>
</div> </div>
</q-card>
</div>
</div>
</q-card>
<q-card flat bordered class="col-12">
<q-card-section :horizontal="$q.screen.gt.sm">
<q-card-section class="col-lg-3 col-md-4 col-xs-12 q-gutter-sm">
<div class="col">
<q-input dense outlined v-model="filterTree" label="ค้นหา">
<template v-slot:append>
<q-icon name="search" />
</template>
</q-input>
</div>
<div class="bg-white tree-container q-pa-xs">
<q-tree
dense
:nodes="node"
node-key="orgTreeId"
label-key="labelName"
v-model:expanded="expanded"
:filter="filterTree.trim()"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
>
<template v-slot:default-header="prop">
<q-item
@click.stop="
onSelectedNode(
prop.node.orgTreeId,
prop.node.orgLevel,
prop.node.orgTreeDnaId
)
"
:active="nodeId === prop.node.orgTreeId"
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"
>
<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>
</div> </div>
</q-card-section> </q-card-section>
<q-separator />
<q-separator :vertical="$q.screen.gt.xs" /> <q-card-actions align="right">
<q-btn
<q-card-section class="col-lg-9 col-md-8 col-xs-12 scroll"> dense
class="q-px-md"
label="ค้นหา"
unelevated
color="public"
type="submit"
:disable="nodeId == '' || reportType == undefined"
/>
</q-card-actions>
</q-card>
</div>
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-12 col-xs-12 flex">
<q-splitter <q-splitter
horizontal horizontal
style=" style="
height: 65vh; height: 75vh;
border: 1px solid rgb(210, 210, 210); border: 1px solid rgb(210, 210, 210);
border-radius: 5px; border-radius: 5px;
width: 100%;
" "
before-class="overflow-hidden disable" before-class="overflow-hidden disable"
separator-class="bg-white disabled" separator-class="bg-white disabled"
@ -435,8 +528,9 @@ onMounted(() => {
<template v-slot:after> <template v-slot:after>
<div class="q-pa-md"> <div class="q-pa-md">
<LoadView v-if="isLoadPDF" /> <LoadView v-if="isLoadPDF" />
<VuePDF <VuePDF
v-else v-else-if="!isLoadPDF && pdfSrc"
ref="vuePDFRef" ref="vuePDFRef"
:pdf="pdfSrc" :pdf="pdfSrc"
:page="page" :page="page"
@ -480,13 +574,15 @@ onMounted(() => {
</div> </div>
</template> </template>
</q-splitter> </q-splitter>
</q-card-section> </div>
</q-card-section> </div>
</q-card> </q-card>
</div> </div>
</div>
</q-form>
</template> </template>
<style scoped> <style lang="scss" scoped>
.tree-container { .tree-container {
overflow: auto; overflow: auto;
height: 60vh; height: 60vh;
@ -500,4 +596,17 @@ onMounted(() => {
font-weight: 600; font-weight: 600;
border: 1px solid rgba(175, 185, 196, 0.217); border: 1px solid rgba(175, 185, 196, 0.217);
} }
.tree-container-report {
overflow: auto;
max-height: 50vh;
}
:deep(.expansion-item .q-item__section) {
padding-right: 3px;
}
:deep(.expansion-item .q-item) {
min-height: 32px;
padding: 2px 9px;
}
</style> </style>

View file

@ -43,6 +43,12 @@ const optionReport = ref<DataOption[]>([
name: "จำนวนลูกจ้างประจำ กทม. ที่พ้นจากราชการ", name: "จำนวนลูกจ้างประจำ กทม. ที่พ้นจากราชการ",
}, },
]); ]);
const expandedModal = ref<boolean>(false);
const org = ref<string>("");
const isLoadStructureTree = ref<boolean>(true);
const filterType = ref<string>("MONTH"); const filterType = ref<string>("MONTH");
const filterTypeOption = ref<DataOption[]>([ const filterTypeOption = ref<DataOption[]>([
{ id: "MONTH", name: "รายเดือน" }, { id: "MONTH", name: "รายเดือน" },
@ -76,8 +82,16 @@ const nodeLevel = ref<number>(0);
const node = ref<DataStructureTree[]>([]); const node = ref<DataStructureTree[]>([]);
const expanded = ref<string[]>([]); const expanded = ref<string[]>([]);
/** ฟังก์ชันเรียกข้อมูลโครงสร้างหน่วยงาน*/
async function fetchDataTree() { async function fetchDataTree() {
try {
isLoadStructureTree.value = true;
node.value = await fetchStructureTree(route.meta.Key as string, true); node.value = await fetchStructureTree(route.meta.Key as string, true);
} catch (err) {
messageError($q, err);
} finally {
isLoadStructureTree.value = false;
}
} }
function onUpdateFilter() { function onUpdateFilter() {
@ -97,14 +111,13 @@ function onUpdateFilter() {
default: default:
break; break;
} }
fetchDataReport();
} }
function onSelectedNode(id: string, level: number) { function onSelectedNode(data: any) {
nodeId.value = id; nodeId.value = data.orgTreeId;
nodeLevel.value = level; nodeLevel.value = data.orgLevel;
fetchDataReport(); org.value = data.orgName;
expandedModal.value = false;
} }
function fetchDataReport() { function fetchDataReport() {
@ -186,6 +199,28 @@ function monthYearThai(val: DataDateMonthObject) {
else return monthYear2Thai(val.month, val.year); else return monthYear2Thai(val.month, val.year);
} }
function clearFilter() {
nodeId.value = "";
nodeLevel.value = 0;
org.value = "";
detailReport.value = undefined;
pdfSrc.value = undefined;
reportType.value = "";
filterType.value = "MONTH";
year.value = new Date().getFullYear();
dateMonth.value = {
month: new Date().getMonth(),
year: new Date().getFullYear(),
};
dateStart.value = new Date(
dateMonth.value.year,
dateMonth.value.month + 1 - 1,
1
);
dateEnd.value = new Date(dateMonth.value.year, dateMonth.value.month + 1, 0);
}
onMounted(() => { onMounted(() => {
fetchDataTree(); fetchDataTree();
}); });
@ -196,32 +231,18 @@ onMounted(() => {
รายงานพนจากราชการ รายงานพนจากราชการ
</div> </div>
<div class="q-pa-sm q-gutter-sm"> <q-form greedy @submit.prevent @validation-success="fetchDataReport">
<q-card flat bordered class="col-12"> <div class="row q-col-gutter-sm">
<div class="row q-col-gutter-sm q-pa-sm"> <div class="col-12">
<div class="row col-12"> <q-card class="q-pa-sm">
<q-select <div class="row q-col-gutter-sm no-wrap">
outlined
dense
v-model="reportType"
:options="optionReport"
label="รายงาน"
map-options
emit-value
option-label="name"
option-value="id"
style="width: 320px"
@update:model-value="fetchDataReport"
>
</q-select>
<q-space /> <q-space />
<div>
<q-btn <q-btn
:loading="isLoadPDF" :loading="isLoadPDF"
flat flat
round round
:disable="!reportType || !nodeId" :disable="!reportType || !nodeId || !detailReport"
color="primary" color="primary"
icon="download" icon="download"
> >
@ -233,7 +254,9 @@ onMounted(() => {
@click.stop.pervent=" @click.stop.pervent="
genReportXLSX( genReportXLSX(
detailReport, detailReport,
`${optionReport.find((e) => e.id === reportType)?.name}`, `${
optionReport.find((e) => e.id === reportType)?.name
}`,
'pdf' 'pdf'
) )
" "
@ -249,7 +272,9 @@ onMounted(() => {
@click.stop.pervent=" @click.stop.pervent="
genReportXLSX( genReportXLSX(
detailReport, detailReport,
`${optionReport.find((e) => e.id === reportType)?.name}` `${
optionReport.find((e) => e.id === reportType)?.name
}`
) )
" "
> >
@ -262,14 +287,137 @@ onMounted(() => {
</q-menu> </q-menu>
</q-btn> </q-btn>
</div> </div>
</div>
</q-card>
</div>
<div class="col-12">
<q-card class="q-pa-sm">
<div class="row q-col-gutter-sm">
<div class="col">
<q-card flat bordered>
<q-card-section
bordered
class="bg-primary text-subtitle2 text-white q-pa-sm row 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-card-section class="q-pa-none">
<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"
autogrow
borderless
label="สังกัด"
class="bg-white full-width"
>
</q-input>
</div>
<!-- @click="onOpenOrg" -->
</template>
<q-separator />
<div class="row col-12"> <q-card-section v-if="isLoadStructureTree">
<q-card bordered class="col-12 filter-card q-pa-sm"> <LoadView />
<div class="row col-12 q-col-gutter-sm"> </q-card-section>
<div class="col-md-3 col-xs-12"> <q-card-section v-else>
<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
dense
:nodes="node"
node-key="orgTreeId"
label-key="labelName"
v-model:expanded="expanded"
:filter="filterTree.trim()"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
class="tree-container-report q-mt-sm"
>
<template v-slot:default-header="prop">
<q-item
@click.stop="onSelectedNode(prop.node)"
:active="nodeId === prop.node.orgTreeId"
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"
>
<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>
<q-separator />
<div class="col-12 q-px-sm">
<q-select
dense
v-model="reportType"
:options="optionReport"
label="รายงาน"
map-options
borderless
emit-value
option-label="name"
option-value="id"
>
</q-select>
</div>
<q-separator />
<div class="col-12 q-px-sm">
<q-select <q-select
class="bg-white" class="bg-white"
outlined
dense dense
v-model="filterType" v-model="filterType"
:options="filterTypeOption" :options="filterTypeOption"
@ -278,10 +426,16 @@ onMounted(() => {
option-label="name" option-label="name"
option-value="id" option-value="id"
use-input use-input
borderless
@update:model-value="onUpdateFilter" @update:model-value="onUpdateFilter"
> >
</q-select> </q-select>
</div> </div>
<q-separator />
<div class="col-12 q-pa-sm">
<div class="row q-col-gutter-y-xs">
<div class="text-grey-8 text-caption">เลอกชวงเวลา</div>
<div class="col-12">
<div v-if="filterType === 'YEAR'"> <div v-if="filterType === 'YEAR'">
<datepicker <datepicker
v-model="year" v-model="year"
@ -291,7 +445,9 @@ onMounted(() => {
:enableTimePicker="false" :enableTimePicker="false"
@update:model-value="onUpdateFilter" @update:model-value="onUpdateFilter"
> >
<template #year="{ year }">{{ year + 543 }}</template> <template #year="{ year }">{{
year + 543
}}</template>
<template #year-overlay-value="{ value }">{{ <template #year-overlay-value="{ value }">{{
parseInt(value + 543) parseInt(value + 543)
}}</template> }}</template>
@ -299,12 +455,12 @@ onMounted(() => {
<q-input <q-input
class="bg-white" class="bg-white"
dense dense
outlined
:model-value="Number(year) + 543" :model-value="Number(year) + 543"
:label="`${'ปีงบประมาณ'}`" :label="`${'ปีงบประมาณ'}`"
> >
<template v-slot:prepend> <template v-slot:prepend>
<q-icon <q-icon
size="18px"
name="event" name="event"
class="cursor-pointer" class="cursor-pointer"
style="color: var(--q-primary)" style="color: var(--q-primary)"
@ -325,21 +481,22 @@ onMounted(() => {
:enableTimePicker="false" :enableTimePicker="false"
@update:model-value="onUpdateFilter" @update:model-value="onUpdateFilter"
> >
<template #year="{ year }">{{ year + 543 }}</template> <template #year="{ year }">{{
year + 543
}}</template>
<template #year-overlay-value="{ value }">{{ <template #year-overlay-value="{ value }">{{
parseInt(value + 543) parseInt(value + 543)
}}</template> }}</template>
<template #trigger> <template #trigger>
<q-input <q-input
class="bg-white" class="bg-white"
outlined
dense dense
borderless
:label="`${'เดือน'}`" :label="`${'เดือน'}`"
:model-value="monthYearThai(dateMonth)" :model-value="monthYearThai(dateMonth)"
> >
<template v-slot:prepend> <template v-slot:prepend>
<q-icon <q-icon
size="18px"
name="event" name="event"
class="cursor-pointer" class="cursor-pointer"
color="primary" color="primary"
@ -349,8 +506,8 @@ onMounted(() => {
</template> </template>
</datepicker> </datepicker>
</div> </div>
</div>
<div> <div class="col-12">
<datepicker <datepicker
menu-class-name="modalfix" menu-class-name="modalfix"
v-model="dateStart" v-model="dateStart"
@ -358,7 +515,6 @@ onMounted(() => {
autoApply autoApply
:enableTimePicker="false" :enableTimePicker="false"
week-start="0" week-start="0"
@update:model-value="fetchDataReport"
:max-date="dateEnd" :max-date="dateEnd"
> >
<template #year="{ year }">{{ year + 543 }}</template> <template #year="{ year }">{{ year + 543 }}</template>
@ -368,14 +524,15 @@ onMounted(() => {
<template #trigger> <template #trigger>
<q-input <q-input
class="bg-white" class="bg-white"
outlined
dense dense
borderless :model-value="
:model-value="dateStart ? date2Thai(dateStart) : null" dateStart ? date2Thai(dateStart) : null
"
:label="`${'ตั้งเเต่วันที่'}`" :label="`${'ตั้งเเต่วันที่'}`"
> >
<template v-slot:prepend> <template v-slot:prepend>
<q-icon <q-icon
size="18px"
name="event" name="event"
class="cursor-pointer" class="cursor-pointer"
color="primary" color="primary"
@ -386,7 +543,7 @@ onMounted(() => {
</template> </template>
</datepicker> </datepicker>
</div> </div>
<div> <div class="col-12">
<datepicker <datepicker
menu-class-name="modalfix" menu-class-name="modalfix"
v-model="dateEnd" v-model="dateEnd"
@ -394,7 +551,6 @@ onMounted(() => {
autoApply autoApply
:enableTimePicker="false" :enableTimePicker="false"
week-start="0" week-start="0"
@update:model-value="fetchDataReport"
:min-date="dateStart" :min-date="dateStart"
> >
<template #year="{ year }">{{ year + 543 }}</template> <template #year="{ year }">{{ year + 543 }}</template>
@ -404,14 +560,13 @@ onMounted(() => {
<template #trigger> <template #trigger>
<q-input <q-input
class="bg-white" class="bg-white"
outlined
dense dense
borderless
:model-value="dateEnd ? date2Thai(dateEnd) : null" :model-value="dateEnd ? date2Thai(dateEnd) : null"
:label="`${'ถึงวันที่'}`" :label="`${'ถึงวันที่'}`"
> >
<template v-slot:prepend> <template v-slot:prepend>
<q-icon <q-icon
size="18px"
name="event" name="event"
class="cursor-pointer" class="cursor-pointer"
color="primary" color="primary"
@ -423,71 +578,30 @@ onMounted(() => {
</datepicker> </datepicker>
</div> </div>
</div> </div>
</q-card>
</div>
</div>
</q-card>
<q-card flat bordered class="col-12">
<q-card-section :horizontal="$q.screen.gt.sm">
<q-card-section class="col-lg-3 col-md-4 col-xs-12 q-gutter-sm">
<div class="col">
<q-input dense outlined v-model="filterTree" label="ค้นหา">
<template v-slot:append>
<q-icon name="search" />
</template>
</q-input>
</div>
<div class="bg-white tree-container q-pa-xs">
<q-tree
dense
:nodes="node"
node-key="orgTreeId"
label-key="labelName"
v-model:expanded="expanded"
:filter="filterTree.trim()"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
>
<template v-slot:default-header="prop">
<q-item
@click.stop="
onSelectedNode(prop.node.orgTreeId, prop.node.orgLevel)
"
:active="nodeId === prop.node.orgTreeId"
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"
>
<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>
</div> </div>
</q-card-section> </q-card-section>
<q-separator />
<q-separator :vertical="$q.screen.gt.xs" /> <q-card-actions align="right">
<q-btn
<q-card-section class="col-lg-9 col-md-8 col-xs-12 scroll"> dense
class="q-px-md"
label="ค้นหา"
unelevated
color="public"
type="submit"
:disable="nodeId == '' || reportType == undefined"
/>
</q-card-actions>
</q-card>
</div>
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-12 col-xs-12 flex">
<q-splitter <q-splitter
horizontal horizontal
style=" style="
height: 65vh; height: 75vh;
border: 1px solid rgb(210, 210, 210); border: 1px solid rgb(210, 210, 210);
border-radius: 5px; border-radius: 5px;
width: 100%;
" "
before-class="overflow-hidden disable" before-class="overflow-hidden disable"
separator-class="bg-white disabled" separator-class="bg-white disabled"
@ -530,7 +644,7 @@ onMounted(() => {
<div class="q-pa-md"> <div class="q-pa-md">
<LoadView v-if="isLoadPDF" /> <LoadView v-if="isLoadPDF" />
<VuePDF <VuePDF
v-else v-else-if="!isLoadPDF && pdfSrc"
ref="vuePDFRef" ref="vuePDFRef"
:pdf="pdfSrc" :pdf="pdfSrc"
:page="page" :page="page"
@ -574,13 +688,15 @@ onMounted(() => {
</div> </div>
</template> </template>
</q-splitter> </q-splitter>
</q-card-section> </div>
</q-card-section> </div>
</q-card> </q-card>
</div> </div>
</div>
</q-form>
</template> </template>
<style scoped> <style lang="scss" scoped>
.tree-container { .tree-container {
overflow: auto; overflow: auto;
height: 60vh; height: 60vh;
@ -594,4 +710,18 @@ onMounted(() => {
font-weight: 600; font-weight: 600;
border: 1px solid rgba(175, 185, 196, 0.217); border: 1px solid rgba(175, 185, 196, 0.217);
} }
.tree-container-report {
overflow: auto;
max-height: 50vh;
}
:deep(.expansion-item .q-item__section) {
padding-right: 3px;
}
:deep(.expansion-item .q-item) {
min-height: 32px;
padding: 2px 9px;
}
</style> </style>

View file

@ -26,6 +26,10 @@ const route = useRoute();
const { fetchStructureTree } = useStructureTree(); const { fetchStructureTree } = useStructureTree();
const { messageError, showLoader, hideLoader } = useCounterMixin(); const { messageError, showLoader, hideLoader } = useCounterMixin();
const expandedModal = ref<boolean>(false);
const org = ref<string>("");
const isLoadStructureTree = ref<boolean>(true);
const employeeClass = ref<string>("officer"); const employeeClass = ref<string>("officer");
const employeeClassOption = ref<OptionData[]>([ const employeeClassOption = ref<OptionData[]>([
{ id: "officer", name: "ข้าราชการ กทม. สามัญ" }, { id: "officer", name: "ข้าราชการ กทม. สามัญ" },
@ -85,8 +89,16 @@ const pdfSrc = ref<any>();
const isLoadPDF = ref<boolean>(false); const isLoadPDF = ref<boolean>(false);
/** ฟังก์ชันเรียกข้อมูลโครงสร้างหน่วยงาน*/
async function fetchDataTree() { async function fetchDataTree() {
try {
isLoadStructureTree.value = true;
node.value = await fetchStructureTree(route.meta.Key as string, true); node.value = await fetchStructureTree(route.meta.Key as string, true);
} catch (err) {
messageError($q, err);
} finally {
isLoadStructureTree.value = false;
}
} }
/** ฟังก์ชันคึงข้อมูลรอบการเสนอขอ*/ /** ฟังก์ชันคึงข้อมูลรอบการเสนอขอ*/
@ -156,10 +168,11 @@ function filterSelector(val: string, update: Function, name: string) {
* @param id id หนวยงาน * @param id id หนวยงาน
* @param level ระดบหนวยงาน * @param level ระดบหนวยงาน
*/ */
function onSelectedNode(id: string, level: number) { function onSelectedNode(data: any) {
nodeId.value = id; nodeId.value = data.orgTreeDnaId;
nodeLevel.value = level; nodeLevel.value = data.orgLevel;
onUpdateFilter(); org.value = data.orgName;
expandedModal.value = false;
} }
const detailReport = ref<any>(); const detailReport = ref<any>();
@ -310,6 +323,18 @@ function downloadReport(type: string, download: "docx" | "" | "xlsx" = "") {
} }
} }
function clearFilter() {
detailReport.value = undefined;
pdfSrc.value = undefined;
employeeClass.value = "officer";
roundId.value = "";
roundId2.value = "";
typeReport.value = "";
org.value = "";
nodeId.value = "";
nodeLevel.value = 0;
}
onMounted(async () => { onMounted(async () => {
await Promise.all([fetchDataTree(), fecthlistRound(), fecthlistRound2()]); await Promise.all([fetchDataTree(), fecthlistRound(), fecthlistRound2()]);
}); });
@ -319,11 +344,12 @@ onMounted(async () => {
<div class="toptitle text-dark col-12 row items-center"> <div class="toptitle text-dark col-12 row items-center">
รายงานเครองราชอสรยาภรณ รายงานเครองราชอสรยาภรณ
</div> </div>
<q-form greedy @submit.prevent @validation-success="onUpdateFilter">
<div class="q-pa-sm q-gutter-sm"> <div class="row q-col-gutter-sm">
<q-card flat bordered class="col-12"> <div class="col-12">
<div class="row q-col-gutter-sm q-pa-sm"> <q-card class="q-pa-sm">
<div class="row col-12"> <div class="row q-col-gutter-sm no-wrap">
<div class="col-lg-2 col-md-3 col-sm-3 col-xs-12">
<q-select <q-select
outlined outlined
dense dense
@ -335,17 +361,16 @@ onMounted(async () => {
option-label="name" option-label="name"
option-value="id" option-value="id"
style="width: 230px" style="width: 230px"
@update:model-value="onUpdateFilter"
> >
</q-select> </q-select>
</div>
<q-space /> <q-space />
<div>
<q-btn <q-btn
flat flat
round round
:loading="isLoadPDF" :loading="isLoadPDF"
:disable="!nodeId || !employeeClass || (!roundId && !roundId2)" :disable="!detailReport"
color="primary" color="primary"
icon="download" icon="download"
v-if="checkPermission($route)?.attrIsGet" v-if="checkPermission($route)?.attrIsGet"
@ -401,15 +426,123 @@ onMounted(async () => {
</q-menu> </q-menu>
</q-btn> </q-btn>
</div> </div>
</div>
</q-card>
</div>
<div class="col-12">
<q-card class="q-pa-sm">
<div class="row q-col-gutter-sm">
<div class="col">
<q-card flat bordered>
<q-card-section
bordered
class="bg-primary text-subtitle2 text-white q-pa-sm row 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-card-section class="q-pa-none">
<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"
autogrow
borderless
label="สังกัด"
class="bg-white full-width"
>
</q-input>
</div>
<!-- @click="onOpenOrg" -->
</template>
<q-separator />
<div class="row col-12"> <q-card-section v-if="isLoadStructureTree">
<q-card bordered class="col-12 filter-card q-pa-sm"> <LoadView />
<div class="row col-12 q-col-gutter-sm"> </q-card-section>
<div class="col-md-4 col-xs-12"> <q-card-section v-else>
<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
dense
:nodes="node"
node-key="orgTreeId"
label-key="labelName"
v-model:expanded="expanded"
:filter="filterTree.trim()"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
class="tree-container-report q-mt-sm"
>
<template v-slot:default-header="prop">
<q-item
@click.stop="onSelectedNode(prop.node)"
:active="nodeId === prop.node.orgTreeId"
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"
>
<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>
<q-separator />
<div class="col-12 q-px-sm">
<q-select <q-select
class="bg-white" class="bg-white"
hide-bottom-space hide-bottom-space
outlined
dense dense
lazy-rules lazy-rules
borderless borderless
@ -419,11 +552,10 @@ onMounted(async () => {
:options="employeeClass == 'officer' ? optionReport.filter((item:any)=>item.id !== '7'):optionReport.filter((item:any)=>item.id !== '4')" :options="employeeClass == 'officer' ? optionReport.filter((item:any)=>item.id !== '7'):optionReport.filter((item:any)=>item.id !== '4')"
option-value="id" option-value="id"
option-label="name" option-label="name"
@update:model-value="onUpdateFilter"
/> />
</div> </div>
<q-separator />
<div class="col-md-4 col-xs-12"> <div class="col-12 q-px-sm">
<q-select <q-select
v-if=" v-if="
typeReport && typeReport &&
@ -436,8 +568,8 @@ onMounted(async () => {
fill-input fill-input
hide-selected hide-selected
dense dense
borderless
lazy-rules lazy-rules
outlined
v-model="roundId" v-model="roundId"
:options="roundOtion" :options="roundOtion"
label="เลือกรอบ" label="เลือกรอบ"
@ -445,7 +577,6 @@ onMounted(async () => {
option-label="name" option-label="name"
emit-value emit-value
map-options map-options
@update:model-value="onUpdateFilter"
@filter="(inputValue:string,doneFn:Function) => @filter="(inputValue:string,doneFn:Function) =>
filterSelector(inputValue, doneFn,'round') " filterSelector(inputValue, doneFn,'round') "
> >
@ -465,8 +596,8 @@ onMounted(async () => {
fill-input fill-input
hide-selected hide-selected
dense dense
borderless
lazy-rules lazy-rules
outlined
v-model="roundId2" v-model="roundId2"
:options="roundOtion2" :options="roundOtion2"
label="เลือกรอบ" label="เลือกรอบ"
@ -474,7 +605,6 @@ onMounted(async () => {
option-label="name" option-label="name"
emit-value emit-value
map-options map-options
@update:model-value="onUpdateFilter"
@filter="(inputValue:string,doneFn:Function) => @filter="(inputValue:string,doneFn:Function) =>
filterSelector(inputValue, doneFn,'round') " filterSelector(inputValue, doneFn,'round') "
> >
@ -487,72 +617,31 @@ onMounted(async () => {
</template> </template>
</q-select> </q-select>
</div> </div>
</div>
</q-card>
</div>
</div>
</q-card>
<q-card flat bordered class="col-12">
<q-card-section :horizontal="$q.screen.gt.sm">
<q-card-section class="col-lg-3 col-md-4 col-xs-12 q-gutter-sm">
<div class="col">
<q-input dense outlined v-model="filterTree" label="ค้นหา">
<template v-slot:append>
<q-icon name="search" />
</template>
</q-input>
</div>
<div class="bg-white tree-container q-pa-xs">
<q-tree
dense
:nodes="node"
node-key="orgTreeId"
label-key="labelName"
v-model:expanded="expanded"
:filter="filterTree.trim()"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
>
<template v-slot:default-header="prop">
<q-item
@click.stop="
onSelectedNode(prop.node.orgTreeDnaId, prop.node.orgLevel)
"
:active="nodeId === prop.node.orgTreeDnaId"
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"
>
<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>
</div>
</q-card-section> </q-card-section>
<q-separator />
<q-separator :vertical="$q.screen.gt.xs" /> <q-card-actions align="right">
<q-btn
<q-card-section class="col-lg-9 col-md-8 col-xs-12 scroll"> dense
class="q-px-md"
label="ค้นหา"
unelevated
color="public"
type="submit"
:disable="
nodeId == '' || !typeReport || (!roundId && !roundId2)
"
/>
</q-card-actions>
</q-card>
</div>
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-12 col-xs-12 flex">
<q-splitter <q-splitter
v-model="splitterModel"
horizontal horizontal
style=" style="
height: 65vh; height: 75vh;
border: 1px solid rgb(210, 210, 210); border: 1px solid rgb(210, 210, 210);
border-radius: 5px; border-radius: 5px;
width: 100%;
" "
before-class="overflow-hidden disable" before-class="overflow-hidden disable"
separator-class="bg-white disabled" separator-class="bg-white disabled"
@ -595,7 +684,7 @@ onMounted(async () => {
<div class="q-pa-md"> <div class="q-pa-md">
<LoadView v-if="isLoadPDF" /> <LoadView v-if="isLoadPDF" />
<VuePDF <VuePDF
v-else v-else-if="!isLoadPDF && pdfSrc"
ref="vuePDFRef" ref="vuePDFRef"
:pdf="pdfSrc" :pdf="pdfSrc"
:page="page" :page="page"
@ -639,13 +728,15 @@ onMounted(async () => {
</div> </div>
</template> </template>
</q-splitter> </q-splitter>
</q-card-section> </div>
</q-card-section> </div>
</q-card> </q-card>
</div> </div>
</div>
</q-form>
</template> </template>
<style lang="scss" scope> <style lang="scss" scoped>
.q-item.hover-green:hover { .q-item.hover-green:hover {
background-color: #d5f1ee57; background-color: #d5f1ee57;
border-radius: 2px; border-radius: 2px;
@ -667,4 +758,17 @@ onMounted(async () => {
font-weight: 600; font-weight: 600;
border: 1px solid rgba(175, 185, 196, 0.217); border: 1px solid rgba(175, 185, 196, 0.217);
} }
.tree-container-report {
overflow: auto;
max-height: 50vh;
}
:deep(.expansion-item .q-item__section) {
padding-right: 3px;
}
:deep(.expansion-item .q-item) {
min-height: 32px;
padding: 2px 9px;
}
</style> </style>

View file

@ -8,10 +8,11 @@ import { useRoute } from "vue-router";
import { checkPermission } from "@/utils/permissions"; import { checkPermission } from "@/utils/permissions";
import { useCounterMixin } from "@/stores/mixin"; import { useCounterMixin } from "@/stores/mixin";
import { useStructureTree } from "@/stores/structureTree"; import { useStructureTree } from "@/stores/structureTree";
import { useMenuDataStore } from "@/stores/menuList";
import genReportXLSX from "@/plugins/genreportxlsx"; import genReportXLSX from "@/plugins/genreportxlsx";
import http from "@/plugins/http"; import http from "@/plugins/http";
import config from "@/app.config"; import config from "@/app.config";
import type { DataStructureTree } from "@/interface/main"; import type { DataStructureTree } from "@/interface/main";
import type { import type {
DataOption, DataOption,
@ -19,10 +20,18 @@ import type {
} from "@/modules/09_leave/interface/index/Main"; } from "@/modules/09_leave/interface/index/Main";
import LoadView from "@/components/LoadView.vue"; import LoadView from "@/components/LoadView.vue";
import type { DataRoles } from "@/interface/response/main";
const expandedModal = ref<boolean>(false);
const org = ref<string>("");
const isLoadStructureTree = ref<boolean>(true);
/** use*/ /** use*/
const $q = useQuasar(); const $q = useQuasar();
const route = useRoute(); const route = useRoute();
const storeMenu = useMenuDataStore();
const { fetchStructureTree } = useStructureTree(); const { fetchStructureTree } = useStructureTree();
const { date2Thai, dateToISO, messageError, monthYear2Thai } = const { date2Thai, dateToISO, messageError, monthYear2Thai } =
useCounterMixin(); useCounterMixin();
@ -37,7 +46,8 @@ const dateMonth = ref<DataDateMonthObject>({
}); });
const typeReport = ref<string>(""); const typeReport = ref<string>("");
const optionReport = ref<DataOption[]>([ const optionReport = ref<DataOption[]>([]);
const optionReportMain = ref<DataOption[]>([
{ id: "1", name: "รายงานการลางานตามประเภทการลา" }, { id: "1", name: "รายงานการลางานตามประเภทการลา" },
{ {
id: "2", id: "2",
@ -73,7 +83,14 @@ const expanded = ref<string[]>([]);
/** ฟังก์ชันเรียกข้อมูลโครงสร้างหน่วยงาน*/ /** ฟังก์ชันเรียกข้อมูลโครงสร้างหน่วยงาน*/
async function fetchDataTree() { async function fetchDataTree() {
try {
isLoadStructureTree.value = true;
node.value = await fetchStructureTree(route.meta.Key as string, true); node.value = await fetchStructureTree(route.meta.Key as string, true);
} catch (err) {
messageError($q, err);
} finally {
isLoadStructureTree.value = false;
}
} }
/** /**
@ -81,9 +98,11 @@ async function fetchDataTree() {
* @param id id หนวยงาน * @param id id หนวยงาน
* @param level node ของหนวงงานทเลอก * @param level node ของหนวงงานทเลอก
*/ */
function onSelectedNode(id: string, level: number) { function onSelectedNode(data: any) {
nodeId.value = id; org.value = data.orgName;
nodeLevel.value = level; nodeId.value = data.orgTreeId;
nodeLevel.value = data.orgLevel;
expandedModal.value = false;
updateLeaveday(); updateLeaveday();
} }
@ -96,6 +115,10 @@ async function updateLeaveday() {
case "FULL": case "FULL":
dateStart.value = new Date(year.value - 1, 9, 1); dateStart.value = new Date(year.value - 1, 9, 1);
dateEnd.value = new Date(year.value, 8, 30); dateEnd.value = new Date(year.value, 8, 30);
dateMonth.value = {
month: new Date().getMonth(),
year: new Date().getFullYear(),
};
break; break;
case "MONTH": case "MONTH":
@ -109,11 +132,19 @@ async function updateLeaveday() {
case "FIRSTHAFT": case "FIRSTHAFT":
dateStart.value = new Date(year.value - 1, 9, 1); dateStart.value = new Date(year.value - 1, 9, 1);
dateEnd.value = new Date(year.value, 2, 31); dateEnd.value = new Date(year.value, 2, 31);
dateMonth.value = {
month: new Date().getMonth(),
year: new Date().getFullYear(),
};
break; break;
case "SECONDHAFT": case "SECONDHAFT":
dateStart.value = new Date(year.value, 3, 1); dateStart.value = new Date(year.value, 3, 1);
dateEnd.value = new Date(year.value, 8, 30); dateEnd.value = new Date(year.value, 8, 30);
dateMonth.value = {
month: new Date().getMonth(),
year: new Date().getFullYear(),
};
break; break;
default: default:
@ -123,19 +154,6 @@ async function updateLeaveday() {
if (!nodeId.value || !typeReport.value) { if (!nodeId.value || !typeReport.value) {
return false; return false;
} }
isReport.value = false;
isLoadPDF.value = true;
pdfSrc.value = undefined;
page.value = 1;
// fetchLeaveday
fetchLeaveday(
employeeClass.value,
yearType.value,
dateStart.value,
dateEnd.value
);
} }
/** /**
@ -233,6 +251,53 @@ function monthYearThai(val: DataDateMonthObject) {
else return monthYear2Thai(val.month, val.year); else return monthYear2Thai(val.month, val.year);
} }
/**
* งกนคนหาขอมลของ 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 == "typeReport") {
optionReport.value = optionReportMain.value.filter(
(v: any) => v.name.toLowerCase().indexOf(needle) > -1
);
}
});
}
function clearData() {
nodeId.value = "";
nodeLevel.value = 0;
org.value = "";
typeReport.value = "";
yearType.value = "FULL";
pdfSrc.value = undefined;
detailReport.value = undefined;
year.value = new Date().getFullYear();
dateMonth.value = {
month: new Date().getMonth(),
year: new Date().getFullYear(),
};
}
function onSearch() {
isReport.value = false;
isLoadPDF.value = true;
pdfSrc.value = undefined;
page.value = 1;
updateLeaveday();
fetchLeaveday(
employeeClass.value,
yearType.value,
dateStart.value,
dateEnd.value
);
}
onMounted(() => { onMounted(() => {
fetchDataTree(); fetchDataTree();
}); });
@ -241,10 +306,12 @@ onMounted(() => {
<template> <template>
<div class="toptitle text-dark col-12 row items-center">ญชแสดงวนลา</div> <div class="toptitle text-dark col-12 row items-center">ญชแสดงวนลา</div>
<div class="q-pa-sm q-gutter-sm"> <q-form greedy @submit.prevent @validation-success="onSearch">
<q-card flat bordered class="col-12"> <div class="row q-col-gutter-sm">
<div class="row q-col-gutter-sm q-pa-sm"> <div class="col-12">
<div class="row col-12"> <q-card class="q-pa-sm">
<div class="row q-col-gutter-sm no-wrap">
<div class="col-lg-2 col-md-3 col-sm-3 col-xs-12">
<q-select <q-select
outlined outlined
dense dense
@ -255,14 +322,14 @@ onMounted(() => {
map-options map-options
option-label="name" option-label="name"
option-value="id" option-value="id"
style="width: 230px"
@update:model-value="updateLeaveday" @update:model-value="updateLeaveday"
> >
</q-select> </q-select>
</div>
<q-space /> <q-space />
<div>
<q-btn <q-btn
:disable="!isReport || !nodeId || !employeeClass" :disable="!isReport || !employeeClass || !detailReport"
:loading="isLoadPDF" :loading="isLoadPDF"
flat flat
round round
@ -331,37 +398,158 @@ onMounted(() => {
</q-menu> </q-menu>
</q-btn> </q-btn>
</div> </div>
</div>
</q-card>
</div>
<div class="col-12">
<q-card class="q-pa-sm">
<div class="row q-col-gutter-sm">
<div class="col">
<q-card flat bordered>
<q-card-section
bordered
class="bg-primary text-subtitle2 text-white q-pa-sm row 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="clearData"
/>
</q-card-section>
<q-separator />
<q-card-section class="q-pa-none">
<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"
autogrow
borderless
label="สังกัด"
class="bg-white full-width"
>
</q-input>
</div>
<!-- @click="onOpenOrg" -->
</template>
<q-separator />
<div class="row col-12"> <q-card-section v-if="isLoadStructureTree">
<q-card bordered class="col-12 filter-card q-pa-sm"> <LoadView />
<div class="row col-12 q-col-gutter-sm"> </q-card-section>
<div class="col-md-3 col-xs-12"> <q-card-section v-else>
<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
dense
:nodes="node"
node-key="orgTreeId"
label-key="labelName"
v-model:expanded="expanded"
:filter="filterTree.trim()"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
class="tree-container-report q-mt-sm"
>
<template v-slot:default-header="prop">
<q-item
@click.stop="onSelectedNode(prop.node)"
:active="nodeId === prop.node.orgTreeId"
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"
>
<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>
<q-separator />
<div class="col-12 q-px-sm">
<q-select <q-select
class="bg-white select_ellipsis3" class="bg-white select_ellipsis3"
hide-bottom-space hide-bottom-space
outlined
dense dense
lazy-rules lazy-rules
borderless borderless
hide-selected
v-model="typeReport" v-model="typeReport"
:label="`${'รายงาน'}`" :label="`${'รายงาน'}`"
emit-value emit-value
map-options map-options
:options="optionReport" :options="optionReport"
option-value="id" option-value="id"
use-input
fill-input
option-label="name" option-label="name"
@update:model-value="updateLeaveday" @update:model-value="(value:any)=>(typeReport = value,updateLeaveday())"
/> @filter="(inputValue:any, doneFn:Function) =>
</div> filterOption(inputValue, doneFn,'typeReport') "
<div
class="row col-md-9 col-xs-12 q-col-gutter-sm"
v-if="typeReport"
> >
<div class="col-md-3 col-xs-12"> <template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
ไมอม
</q-item-section>
</q-item>
</template></q-select
>
</div>
<q-separator v-if="typeReport" />
<div class="col-12 q-pa-sm" v-if="typeReport">
<div class="row q-col-gutter-y-xs">
<div class="text-grey-8 text-caption">เลอกชวงเวลา</div>
<div class="col-12">
<q-select <q-select
class="bg-white" class="bg-white"
outlined
dense dense
v-model="yearType" v-model="yearType"
:options="yearTypeOptionOption" :options="yearTypeOptionOption"
@ -373,12 +561,7 @@ onMounted(() => {
> >
</q-select> </q-select>
</div> </div>
<div class="col-12" v-if="yearType !== 'MONTH'">
<div
class="col-md-9 col-xs-12 row q-col-gutter-sm"
v-if="yearType !== 'MONTH'"
>
<div class="col-md-4">
<datepicker <datepicker
v-model="year" v-model="year"
:locale="'th'" :locale="'th'"
@ -395,7 +578,6 @@ onMounted(() => {
<q-input <q-input
class="bg-white" class="bg-white"
dense dense
outlined
:model-value="Number(year) + 543" :model-value="Number(year) + 543"
:label="`${'ปีงบประมาณ'}`" :label="`${'ปีงบประมาณ'}`"
> >
@ -403,6 +585,7 @@ onMounted(() => {
<q-icon <q-icon
name="event" name="event"
class="cursor-pointer" class="cursor-pointer"
size="18px"
style="color: var(--q-primary)" style="color: var(--q-primary)"
> >
</q-icon> </q-icon>
@ -411,7 +594,7 @@ onMounted(() => {
</template> </template>
</datepicker> </datepicker>
</div> </div>
<div class="col-md-4"> <div class="col-12" v-if="yearType !== 'MONTH'">
<datepicker <datepicker
menu-class-name="modalfix" menu-class-name="modalfix"
v-model="dateStart" v-model="dateStart"
@ -428,15 +611,16 @@ onMounted(() => {
<template #trigger> <template #trigger>
<q-input <q-input
class="bg-white" class="bg-white"
outlined
dense dense
borderless :model-value="
:model-value="dateStart ? date2Thai(dateStart) : null" dateStart ? date2Thai(dateStart) : null
"
:label="`${'ตั้งเเต่วันที่'}`" :label="`${'ตั้งเเต่วันที่'}`"
readonly readonly
> >
<template v-slot:prepend> <template v-slot:prepend>
<q-icon <q-icon
size="18px"
name="event" name="event"
class="cursor-pointer" class="cursor-pointer"
color="primary" color="primary"
@ -447,7 +631,7 @@ onMounted(() => {
</template> </template>
</datepicker> </datepicker>
</div> </div>
<div class="col-md-4"> <div class="col-12" v-if="yearType !== 'MONTH'">
<datepicker <datepicker
menu-class-name="modalfix" menu-class-name="modalfix"
v-model="dateEnd" v-model="dateEnd"
@ -464,15 +648,14 @@ onMounted(() => {
<template #trigger> <template #trigger>
<q-input <q-input
class="bg-white" class="bg-white"
outlined
dense dense
borderless
:model-value="dateEnd ? date2Thai(dateEnd) : null" :model-value="dateEnd ? date2Thai(dateEnd) : null"
:label="`${'ถึงวันที่'}`" :label="`${'ถึงวันที่'}`"
readonly readonly
> >
<template v-slot:prepend> <template v-slot:prepend>
<q-icon <q-icon
size="18px"
name="event" name="event"
class="cursor-pointer" class="cursor-pointer"
color="primary" color="primary"
@ -483,9 +666,7 @@ onMounted(() => {
</template> </template>
</datepicker> </datepicker>
</div> </div>
</div> <div class="col-12" v-if="yearType == 'MONTH'">
<div class="row" v-else>
<datepicker <datepicker
v-model="dateMonth" v-model="dateMonth"
:locale="'th'" :locale="'th'"
@ -501,14 +682,13 @@ onMounted(() => {
<template #trigger> <template #trigger>
<q-input <q-input
class="bg-white" class="bg-white"
outlined
dense dense
borderless
:label="`${'เดือน'}`" :label="`${'เดือน'}`"
:model-value="monthYearThai(dateMonth)" :model-value="monthYearThai(dateMonth)"
> >
<template v-slot:prepend> <template v-slot:prepend>
<q-icon <q-icon
size="18px"
name="event" name="event"
class="cursor-pointer" class="cursor-pointer"
color="primary" color="primary"
@ -520,71 +700,29 @@ onMounted(() => {
</div> </div>
</div> </div>
</div> </div>
</q-card>
</div>
</div>
</q-card>
<q-card flat bordered class="col-12">
<q-card-section :horizontal="$q.screen.gt.sm">
<q-card-section class="col-lg-3 col-md-4 col-xs-12 q-gutter-sm">
<div class="col">
<q-input dense outlined v-model="filterTree" label="ค้นหา">
<template v-slot:append>
<q-icon name="search" />
</template>
</q-input>
</div>
<div class="bg-white tree-container q-pa-xs">
<q-tree
dense
:nodes="node"
node-key="orgTreeId"
label-key="labelName"
v-model:expanded="expanded"
:filter="filterTree.trim()"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
>
<template v-slot:default-header="prop">
<q-item
@click.stop="
onSelectedNode(prop.node.orgTreeId, prop.node.orgLevel)
"
:active="nodeId === prop.node.orgTreeId"
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"
>
<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>
</div>
</q-card-section> </q-card-section>
<q-separator />
<q-separator :vertical="$q.screen.gt.xs" /> <q-card-actions align="right">
<q-btn
<q-card-section class="col-lg-9 col-md-8 col-xs-12 scroll"> dense
class="q-px-md"
label="ค้นหา"
unelevated
color="public"
type="submit"
:disable="typeReport && typeReport == '2' && org"
/>
</q-card-actions>
</q-card>
</div>
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-12 col-xs-12 flex">
<q-splitter <q-splitter
horizontal horizontal
style=" style="
height: 65vh; height: 75vh;
border: 1px solid rgb(210, 210, 210); border: 1px solid rgb(210, 210, 210);
border-radius: 5px; border-radius: 5px;
width: 100%;
" "
before-class="overflow-hidden disable" before-class="overflow-hidden disable"
separator-class="bg-white disabled" separator-class="bg-white disabled"
@ -627,7 +765,7 @@ onMounted(() => {
<div class="q-pa-md"> <div class="q-pa-md">
<LoadView v-if="isLoadPDF" /> <LoadView v-if="isLoadPDF" />
<VuePDF <VuePDF
v-else v-else-if="!isLoadPDF && pdfSrc"
ref="vuePDFRef" ref="vuePDFRef"
:pdf="pdfSrc" :pdf="pdfSrc"
:page="page" :page="page"
@ -671,13 +809,15 @@ onMounted(() => {
</div> </div>
</template> </template>
</q-splitter> </q-splitter>
</q-card-section> </div>
</q-card-section> </div>
</q-card> </q-card>
</div> </div>
</div>
</q-form>
</template> </template>
<style scoped> <style lang="scss" scoped>
.tree-container { .tree-container {
overflow: auto; overflow: auto;
height: 60vh; height: 60vh;
@ -691,4 +831,18 @@ onMounted(() => {
font-weight: 600; font-weight: 600;
border: 1px solid rgba(175, 185, 196, 0.217); border: 1px solid rgba(175, 185, 196, 0.217);
} }
.tree-container-report {
overflow: auto;
max-height: 50vh;
}
:deep(.expansion-item .q-item__section) {
padding-right: 3px;
}
:deep(.expansion-item .q-item) {
min-height: 32px;
padding: 2px 9px;
}
</style> </style>

View file

@ -29,6 +29,10 @@ const route = useRoute();
const { fetchStructureTree } = useStructureTree(); const { fetchStructureTree } = useStructureTree();
const { messageError, showLoader, hideLoader } = useCounterMixin(); const { messageError, showLoader, hideLoader } = useCounterMixin();
const expandedModal = ref<boolean>(false);
const org = ref<string>("");
const isLoadStructureTree = ref<boolean>(true);
const year = ref<number>(new Date().getFullYear()); const year = ref<number>(new Date().getFullYear());
const employeeClass = ref<string>("officer"); const employeeClass = ref<string>("officer");
const employeeClassOption = ref<OptionData[]>([ const employeeClassOption = ref<OptionData[]>([
@ -104,13 +108,21 @@ const detailReport = ref<any>();
const fileBlob = ref<any>(); const fileBlob = ref<any>();
async function fetchDataTree() { async function fetchDataTree() {
try {
isLoadStructureTree.value = true;
node.value = await fetchStructureTree(route.meta.Key as string, true); node.value = await fetchStructureTree(route.meta.Key as string, true);
} catch (err) {
messageError($q, err);
} finally {
isLoadStructureTree.value = false;
}
} }
function onSelectedNode(id: string, level: number) { function onSelectedNode(data: any) {
nodeId.value = id; nodeId.value = data.orgTreeId;
nodeLevel.value = level; nodeLevel.value = data.orgLevel;
onUpdateFilter(); org.value = data.orgName;
expandedModal.value = false;
} }
/** /**
@ -142,7 +154,7 @@ async function onUpdateFilter() {
.post(config.API.disciplineReportByType(employeeClass.value), body) .post(config.API.disciplineReportByType(employeeClass.value), body)
.then(async (res) => { .then(async (res) => {
const data = res.data.result; const data = res.data.result;
// detailReport.value = data; detailReport.value = data;
data && (await genReportPreview(data)); data && (await genReportPreview(data));
}) })
.catch(async (e) => { .catch(async (e) => {
@ -264,6 +276,22 @@ function updateLevel(val: string) {
} }
} }
} }
function clearFilter() {
year.value = new Date().getFullYear();
offenseDetail.value = "ALL";
disciplinaryFaultLevel.value = "ALL";
status.value = "ALL";
posType.value = "";
posLevel.value = "";
pdfSrc.value = undefined;
detailReport.value = undefined;
org.value = "";
nodeId.value = "";
nodeLevel.value = 0;
}
onMounted(async () => { onMounted(async () => {
await Promise.all([fetchDataTree(), getType()]); await Promise.all([fetchDataTree(), getType()]);
}); });
@ -272,10 +300,12 @@ onMounted(async () => {
<template> <template>
<div class="toptitle text-dark col-12 row items-center">รายงานว</div> <div class="toptitle text-dark col-12 row items-center">รายงานว</div>
<div class="q-pa-sm q-gutter-sm"> <q-form greedy @submit.prevent @validation-success="onUpdateFilter">
<q-card flat bordered class="col-12"> <div class="row q-col-gutter-sm">
<div class="row q-col-gutter-sm q-pa-sm"> <div class="col-12">
<div class="row col-12"> <q-card class="q-pa-sm">
<div class="row q-col-gutter-sm no-wrap">
<div class="col-lg-2 col-md-3 col-sm-3 col-xs-12">
<q-select <q-select
outlined outlined
dense dense
@ -286,14 +316,14 @@ onMounted(async () => {
map-options map-options
option-label="name" option-label="name"
option-value="id" option-value="id"
style="width: 230px"
@update:model-value="getType" @update:model-value="getType"
> >
</q-select> </q-select>
</div>
<q-space /> <q-space />
<div>
<q-btn <q-btn
:disable="!nodeId" :disable="!detailReport"
flat flat
round round
color="primary" color="primary"
@ -347,18 +377,126 @@ onMounted(async () => {
</q-menu> </q-menu>
</q-btn> </q-btn>
</div> </div>
</div>
</q-card>
</div>
<div class="col-12">
<q-card class="q-pa-sm">
<div class="row q-col-gutter-sm">
<div class="col">
<q-card flat bordered>
<q-card-section
bordered
class="bg-primary text-subtitle2 text-white q-pa-sm row 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-card-section class="q-pa-none">
<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"
autogrow
borderless
label="สังกัด"
class="bg-white full-width"
>
</q-input>
</div>
<!-- @click="onOpenOrg" -->
</template>
<q-separator />
<div class="row col-12"> <q-card-section v-if="isLoadStructureTree">
<q-card bordered class="col-12 filter-card q-pa-sm"> <LoadView />
<div class="row col-12 q-col-gutter-sm"> </q-card-section>
<div class="col-md-4 col-xs-12"> <q-card-section v-else>
<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
dense
:nodes="node"
node-key="orgTreeId"
label-key="labelName"
v-model:expanded="expanded"
:filter="filterTree.trim()"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
class="tree-container-report q-mt-sm"
>
<template v-slot:default-header="prop">
<q-item
@click.stop="onSelectedNode(prop.node)"
:active="nodeId === prop.node.orgTreeId"
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"
>
<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>
<q-separator />
<div class="col-12 q-px-sm q-pb-xs">
<datepicker <datepicker
v-model="year" v-model="year"
:locale="'th'" :locale="'th'"
autoApply autoApply
year-picker year-picker
:enableTimePicker="false" :enableTimePicker="false"
@update:model-value="onUpdateFilter"
> >
<template #year="{ year }">{{ year + 543 }}</template> <template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{ <template #year-overlay-value="{ value }">{{
@ -368,12 +506,13 @@ onMounted(async () => {
<q-input <q-input
class="bg-white" class="bg-white"
dense dense
outlined borderless
:model-value="Number(year) + 543" :model-value="Number(year) + 543"
:label="`${'ปีพ.ศ.'}`" :label="`${'ปีพ.ศ.'}`"
> >
<template v-slot:prepend> <template v-slot:prepend>
<q-icon <q-icon
size="18px"
name="event" name="event"
class="cursor-pointer" class="cursor-pointer"
style="color: var(--q-primary)" style="color: var(--q-primary)"
@ -384,11 +523,11 @@ onMounted(async () => {
</template> </template>
</datepicker> </datepicker>
</div> </div>
<div class="col-md-4 col-xs-12"> <q-separator />
<div class="col-12 q-px-sm">
<q-select <q-select
class="bg-white" class="bg-white"
hide-bottom-space hide-bottom-space
outlined
dense dense
lazy-rules lazy-rules
borderless borderless
@ -399,14 +538,13 @@ onMounted(async () => {
:options="offenseDetailOp" :options="offenseDetailOp"
option-value="id" option-value="id"
option-label="name" option-label="name"
@update:model-value="onUpdateFilter"
/> />
</div> </div>
<div class="col-md-4 col-xs-12"> <q-separator />
<div class="col-12 q-px-sm">
<q-select <q-select
class="bg-white" class="bg-white"
hide-bottom-space hide-bottom-space
outlined
dense dense
lazy-rules lazy-rules
borderless borderless
@ -417,14 +555,13 @@ onMounted(async () => {
:options="disciplinaryFaultLevelOp" :options="disciplinaryFaultLevelOp"
option-value="id" option-value="id"
option-label="name" option-label="name"
@update:model-value="onUpdateFilter"
/> />
</div> </div>
<div class="col-md-4 col-xs-12"> <q-separator />
<div class="col-12 q-px-sm">
<q-select <q-select
class="bg-white" class="bg-white"
hide-bottom-space hide-bottom-space
outlined
dense dense
lazy-rules lazy-rules
borderless borderless
@ -435,21 +572,22 @@ onMounted(async () => {
:options="statusOp" :options="statusOp"
option-value="id" option-value="id"
option-label="name" option-label="name"
@update:model-value="onUpdateFilter"
/> />
</div> </div>
<div class="col-md-4 col-xs-12"> <q-separator />
<div class="col-12 q-px-sm">
<q-select <q-select
class="bg-white" class="bg-white"
hide-bottom-space hide-bottom-space
outlined
dense dense
lazy-rules lazy-rules
borderless borderless
hide-selected hide-selected
v-model="posType" v-model="posType"
:label="`${ :label="`${
employeeClass == 'officer' ? 'ประเภทตำแหน่ง' : 'กลุ่มงาน' employeeClass == 'officer'
? 'ประเภทตำแหน่ง'
: 'กลุ่มงาน'
}`" }`"
emit-value emit-value
map-options map-options
@ -464,18 +602,20 @@ onMounted(async () => {
" "
/> />
</div> </div>
<div class="col-md-4 col-xs-12"> <q-separator />
<div class="col-12 q-px-sm">
<q-select <q-select
class="bg-white" class="bg-white"
hide-bottom-space hide-bottom-space
outlined
dense dense
lazy-rules lazy-rules
borderless borderless
hide-selected hide-selected
v-model="posLevel" v-model="posLevel"
:label="`${ :label="`${
employeeClass == 'officer' ? 'ระดับตำแหน่ง' : 'ระดับชั้นงาน' employeeClass == 'officer'
? 'ระดับตำแหน่ง'
: 'ระดับชั้นงาน'
}`" }`"
emit-value emit-value
map-options map-options
@ -484,78 +624,34 @@ onMounted(async () => {
option-label="posLevelName" option-label="posLevelName"
use-input use-input
fill-input fill-input
@update:model-value="onUpdateFilter"
@filter="(inputValue:any, doneFn:Function) => @filter="(inputValue:any, doneFn:Function) =>
filterOption(inputValue, doneFn,'level') filterOption(inputValue, doneFn,'level')
" "
/> />
</div> </div>
</div>
</q-card>
</div>
</div>
</q-card>
<q-card flat bordered class="col-12">
<q-card-section :horizontal="$q.screen.gt.sm">
<q-card-section class="col-lg-3 col-md-4 col-xs-12 q-gutter-sm">
<div class="col">
<q-input dense outlined v-model="filterTree" label="ค้นหา">
<template v-slot:append>
<q-icon name="search" />
</template>
</q-input>
</div>
<div class="bg-white tree-container q-pa-xs">
<q-tree
dense
:nodes="node"
node-key="orgTreeId"
label-key="labelName"
v-model:expanded="expanded"
:filter="filterTree.trim()"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
>
<template v-slot:default-header="prop">
<q-item
@click.stop="
onSelectedNode(prop.node.orgTreeId, prop.node.orgLevel)
"
:active="nodeId === prop.node.orgTreeId"
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"
>
<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>
</div>
</q-card-section> </q-card-section>
<q-separator />
<q-separator :vertical="$q.screen.gt.xs" /> <q-card-actions align="right">
<q-btn
<q-card-section class="col-lg-9 col-md-8 col-xs-12 scroll"> dense
class="q-px-md"
label="ค้นหา"
unelevated
color="public"
type="submit"
:disable="nodeId == ''"
/>
</q-card-actions>
</q-card>
</div>
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-12 col-xs-12 flex">
<q-splitter <q-splitter
v-model="splitterModel"
horizontal horizontal
style=" style="
height: 65vh; height: 75vh;
border: 1px solid rgb(210, 210, 210); border: 1px solid rgb(210, 210, 210);
border-radius: 5px; border-radius: 5px;
width: 100%;
" "
before-class="overflow-hidden disable" before-class="overflow-hidden disable"
separator-class="bg-white disabled" separator-class="bg-white disabled"
@ -598,7 +694,7 @@ onMounted(async () => {
<div class="q-pa-md"> <div class="q-pa-md">
<LoadView v-if="isLoadPDF" /> <LoadView v-if="isLoadPDF" />
<VuePDF <VuePDF
v-else v-else-if="!isLoadPDF && pdfSrc"
ref="vuePDFRef" ref="vuePDFRef"
:pdf="pdfSrc" :pdf="pdfSrc"
:page="page" :page="page"
@ -642,13 +738,15 @@ onMounted(async () => {
</div> </div>
</template> </template>
</q-splitter> </q-splitter>
</q-card-section> </div>
</q-card-section> </div>
</q-card> </q-card>
</div> </div>
</div>
</q-form>
</template> </template>
<style lang="scss" scope> <style lang="scss" scoped>
.q-item.hover-green:hover { .q-item.hover-green:hover {
background-color: #d5f1ee57; background-color: #d5f1ee57;
border-radius: 2px; border-radius: 2px;
@ -670,4 +768,18 @@ onMounted(async () => {
font-weight: 600; font-weight: 600;
border: 1px solid rgba(175, 185, 196, 0.217); border: 1px solid rgba(175, 185, 196, 0.217);
} }
.tree-container-report {
overflow: auto;
max-height: 50vh;
}
:deep(.expansion-item .q-item__section) {
padding-right: 3px;
}
:deep(.expansion-item .q-item) {
min-height: 32px;
padding: 2px 9px;
}
</style> </style>

View file

@ -6,7 +6,7 @@ import config from "@/app.config";
import http from "@/plugins/http"; import http from "@/plugins/http";
import axios from "axios"; import axios from "axios";
import genReport from "@/plugins/genreport"; import genReport from "@/plugins/genreport";
import LoadView from "@/components/LoadView.vue";
/** /**
* importType * importType
*/ */
@ -43,13 +43,15 @@ const route = useRoute();
/** /**
* วแปรงาน * วแปรงาน
*/ */
const fullNameShow = ref<string>("");
const isLoadPDF = ref<boolean>(false);
const year = ref<number | null>(new Date().getFullYear()); const year = ref<number | null>(new Date().getFullYear());
const round = ref<string>(""); const round = ref<string>("");
const organization = ref<string>(""); const organization = ref<string>("");
const roundOp = ref<DataOption[]>([]); const roundOp = ref<DataOption[]>([]);
const organizationOpsMain = ref<DataOption[]>([]); const organizationOpsMain = ref<DataOption[]>([]);
const organizationOps = ref<DataOption[]>([]); const organizationOps = ref<DataOption[]>([]);
const typeReport = ref<string>(""); const typeReport = ref<string>("KPI1");
const listReportMain = ref<DataOption[]>([ const listReportMain = ref<DataOption[]>([
{ {
id: "KPI1", id: "KPI1",
@ -88,7 +90,7 @@ const listReportMain = ref<DataOption[]>([
name: "จัดทำประกาศผู้มีผลการปฏิบัติราชการระดับดีเด่นและดีมาก", name: "จัดทำประกาศผู้มีผลการปฏิบัติราชการระดับดีเด่นและดีมาก",
}, },
]); ]);
const listReport = ref<DataOption[]>([]); // const listReport = ref<DataOption[]>(listReportMain.value); //
/** /**
* funtion fetchขอมลรอบการประเม * funtion fetchขอมลรอบการประเม
@ -170,6 +172,7 @@ function fetchListOrg(id: string) {
* function fetch รายงาน * function fetch รายงาน
*/ */
function fetchReport() { function fetchReport() {
isLoadPDF.value = true;
showLoader(); showLoader();
const body = { const body = {
type: typeReport.value, type: typeReport.value,
@ -180,12 +183,14 @@ function fetchReport() {
http http
.post(config.API.kpiReport(), body) .post(config.API.kpiReport(), body)
.then((res) => { .then((res) => {
isLoadPDF.value = false;
dataDownload.value = res.data.result; dataDownload.value = res.data.result;
page.value = 1; page.value = 1;
genPDf(res.data.result); genPDf(res.data.result);
}) })
.catch((err) => { .catch((err) => {
messageError($q, err); messageError($q, err);
isLoadPDF.value = false;
hideLoader(); hideLoader();
}); });
} }
@ -462,8 +467,12 @@ function fetchListPerson() {
* functiuon เลอกราชช * functiuon เลอกราชช
* @param id profile ID * @param id profile ID
*/ */
function onSelectPerson(id: string) { function onSelectPerson(data: any) {
persanalId.value = id; persanalId.value = data.id;
fullNameShow.value = `${data.prefix ?? ""}${data.firstName ?? ""} ${
data.lastName ?? ""
}`;
onCloseModal(); onCloseModal();
fetchReport(); fetchReport();
} }
@ -484,6 +493,14 @@ watch(
} }
); );
function clearFilter() {
organization.value = "";
year.value = new Date().getFullYear();
typeReport.value = "KPI1";
pdfSrc.value = undefined;
dataDownload.value = undefined;
fullNameShow.value = "";
}
onMounted(() => { onMounted(() => {
fetchRoundOption(); fetchRoundOption();
fetchActiveId(); fetchActiveId();
@ -491,11 +508,15 @@ onMounted(() => {
</script> </script>
<template> <template>
<div class="toptitle text-dark col-12 row items-center">รายงาน</div> <div class="toptitle text-dark col-12 row items-center">
<q-card flat bordered> รายงานประเมนผลการปฏราชการระดบบคคล
<q-card-section class="q-gutter-y-sm"> </div>
<q-toolbar style="padding: 0">
<div class="row q-gutter-sm"> <q-form greedy @submit.prevent @validation-success="fetchReport">
<div class="row q-col-gutter-sm">
<div class="col-12">
<q-card class="q-pa-sm">
<div class="row q-col-gutter-sm no-wrap">
<q-select <q-select
:readonly="!checkPermission($route)?.attrIsGet" :readonly="!checkPermission($route)?.attrIsGet"
dense dense
@ -519,14 +540,15 @@ onMounted(() => {
> >
<template v-slot:no-option> <template v-slot:no-option>
<q-item> <q-item>
<q-item-section class="text-grey"> ไมอม </q-item-section> <q-item-section class="text-grey">
ไมอม
</q-item-section>
</q-item> </q-item>
</template></q-select </template></q-select
> >
</div>
<q-space /> <q-space />
<div>
<q-btn <q-btn
v-if="checkPermission($route)?.attrIsGet" v-if="checkPermission($route)?.attrIsGet"
flat flat
@ -560,15 +582,35 @@ onMounted(() => {
</q-list> </q-list>
</q-menu> </q-menu>
</q-btn> </q-btn>
</q-toolbar> </div>
</div>
<q-toolbar </q-card>
class="q-pa-sm bg-grey-2" </div>
style="padding: 0; border-radius: 5px" <div class="col-12">
v-if="typeReport" <q-card class="q-pa-sm">
<div class="row q-col-gutter-sm">
<div class="col">
<q-card flat bordered>
<q-card-section
bordered
class="bg-primary text-subtitle2 text-white q-pa-sm row 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-card-section class="q-pa-none">
<div <div
class="row q-gutter-sm" class="row"
v-if=" v-if="
typeReport === 'KPI1' || typeReport === 'KPI1' ||
typeReport === 'KPI2' || typeReport === 'KPI2' ||
@ -578,10 +620,10 @@ onMounted(() => {
typeReport === 'KPI9' typeReport === 'KPI9'
" "
> >
<div class="col-12 q-px-sm">
<datepicker <datepicker
menu-class-name="modalfix" menu-class-name="modalfix"
v-model="year" v-model="year"
style="width: 150px"
:locale="'th'" :locale="'th'"
autoApply autoApply
year-picker year-picker
@ -595,7 +637,7 @@ onMounted(() => {
<template #trigger> <template #trigger>
<q-input <q-input
dense dense
outlined borderless
hide-bottom-space hide-bottom-space
:model-value="!!year ? year + 543 : null" :model-value="!!year ? year + 543 : null"
:label="`${'ปีงบประมาณ'}`" :label="`${'ปีงบประมาณ'}`"
@ -612,13 +654,16 @@ onMounted(() => {
</q-input> </q-input>
</template> </template>
</datepicker> </datepicker>
</div>
<div class="col-12"><q-separator/></div>
<div class="col-12 q-px-sm">
<q-select <q-select
ref="roundRef" ref="roundRef"
class="bg-white" class="bg-white"
v-model="round" v-model="round"
outlined
label="รอบการประเมิน" label="รอบการประเมิน"
dense dense
borderless
hide-bottom-space hide-bottom-space
option-label="name" option-label="name"
option-value="id" option-value="id"
@ -635,16 +680,17 @@ onMounted(() => {
: [] : []
" "
/> />
</div>
<div class="col-12"><q-separator/></div>
<div class="col-12 q-pb-xs q-px-sm" v-if="typeReport !== 'KPI8'">
<q-select <q-select
v-if="typeReport !== 'KPI8'"
class="bg-white" class="bg-white"
:disable="roundOp.length === 0" :disable="roundOp.length === 0"
style="min-width: 100px"
dense dense
borderless
hide-selected hide-selected
fill-input fill-input
hide-bottom-space hide-bottom-space
outlined
option-label="name" option-label="name"
option-value="id" option-value="id"
emit-value emit-value
@ -660,14 +706,16 @@ onMounted(() => {
> >
<template v-slot:no-option> <template v-slot:no-option>
<q-item> <q-item>
<q-item-section class="text-grey"> ไมอม </q-item-section> <q-item-section class="text-grey">
ไมอม
</q-item-section>
</q-item> </q-item>
</template></q-select </template></q-select
> >
</div> </div>
</div>
<div <div
class="q-pa-sm" class="row q-pa-sm q-col-gutter-xs"
v-if=" v-if="
typeReport === 'KPI4' || typeReport === 'KPI4' ||
typeReport === 'KPI5' || typeReport === 'KPI5' ||
@ -675,27 +723,43 @@ onMounted(() => {
typeReport === 'KPI8' typeReport === 'KPI8'
" "
> >
<div class="col-12">
<q-input
label="ชื่อ-นามสกุล"
dense
disable
:model-value="
fullNameShow ? fullNameShow : 'กรุณาเลือกรายชื่อ'
"
>
</q-input>
</div>
<div class="col-12">
<q-btn <q-btn
class="full-width"
color="primary" color="primary"
label="เลือกรายชื่อ" label="เลือกรายชื่อ"
@click="onClickModal" @click="onClickModal"
:disable="roundOp.length === 0 && typeReport === 'KPI8'" :disable="roundOp.length === 0 && typeReport === 'KPI8'"
/> />
</div> </div>
</q-toolbar> </div>
</q-card-section>
</q-card>
</div>
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-12 col-xs-12 flex">
<q-splitter <q-splitter
v-model="splitterModel"
horizontal horizontal
style=" style="
height: 70vh; height: 75vh;
border: 1px solid rgb(210, 210, 210); border: 1px solid rgb(210, 210, 210);
border-radius: 5px; border-radius: 5px;
width: 100%;
" "
before-class="overflow-hidden disable" before-class="overflow-hidden disable"
separator-class="bg-white disabled" separator-class="bg-white disabled"
> >
<template v-slot:before v-if="pdfSrc"> <template v-slot:before>
<div class="q-px-sm"> <div class="q-px-sm">
<div class="row items-start items-center"> <div class="row items-start items-center">
<div class="col"> <div class="col">
@ -729,12 +793,19 @@ onMounted(() => {
</div> </div>
</div> </div>
</template> </template>
<template v-slot:after v-if="pdfSrc"> <template v-slot:after>
<div class="q-pa-md"> <div class="q-pa-md">
<VuePDF ref="vuePDFRef" :pdf="pdfSrc" :page="page" fit-parent /> <LoadView v-if="isLoadPDF" />
<VuePDF
v-else-if="!isLoadPDF && pdfSrc"
ref="vuePDFRef"
:pdf="pdfSrc"
:page="page"
fit-parent
/>
</div> </div>
</template> </template>
<template v-slot:default v-if="pdfSrc"> <template v-slot:default>
<div class="q-pa-md"> <div class="q-pa-md">
<div class="row items-start items-center"> <div class="row items-start items-center">
<div class="col"> <div class="col">
@ -770,6 +841,47 @@ onMounted(() => {
</div> </div>
</template> </template>
</q-splitter> </q-splitter>
</div>
</div>
</q-card>
</div>
</div>
</q-form>
<q-card flat bordered>
<q-card-section class="q-gutter-y-sm">
<q-toolbar style="padding: 0">
<div class="row q-gutter-sm"></div>
<q-space />
</q-toolbar>
<q-toolbar
class="q-pa-sm bg-grey-2"
style="padding: 0; border-radius: 5px"
v-if="typeReport"
>
<div
class="row q-gutter-sm"
v-if="
typeReport === 'KPI1' ||
typeReport === 'KPI2' ||
typeReport === 'KPI3' ||
typeReport === 'KPI7' ||
typeReport === 'KPI8' ||
typeReport === 'KPI9'
"
></div>
<div
class="q-pa-sm"
v-if="
typeReport === 'KPI4' ||
typeReport === 'KPI5' ||
typeReport === 'KPI6' ||
typeReport === 'KPI8'
"
></div>
</q-toolbar>
</q-card-section> </q-card-section>
</q-card> </q-card>
@ -853,7 +965,7 @@ onMounted(() => {
outline outline
color="primary" color="primary"
label="เลือก" label="เลือก"
@click="onSelectPerson(props.row.id)" @click="onSelectPerson(props.row)"
/> />
</q-td> </q-td>
</q-tr> </q-tr>

View file

@ -193,9 +193,17 @@ async function getOrg(id: string) {
}); });
} }
function clearFilter() {
year.value = new Date().getFullYear();
organization.value = "";
typeReport.value = "";
pdfSrc.value = undefined;
detailReport.value = undefined;
}
onMounted(async () => { onMounted(async () => {
showLoader(); showLoader();
Promise.all([getReport(), getActiveId()]) Promise.all([getActiveId()])
.then(() => { .then(() => {
hideLoader(); hideLoader();
}) })
@ -215,34 +223,17 @@ onMounted(async () => {
รายงานโครงการ/หลกสตรการฝกอบรมทหนวยงานของกรงเทพมหานครเปนผ รายงานโครงการ/หลกสตรการฝกอบรมทหนวยงานของกรงเทพมหานครเปนผ
</div> </div>
<div class="q-pa-sm q-gutter-sm"> <q-form greedy @submit.prevent @validation-success="getReport">
<q-card flat bordered class="col-12"> <div class="row q-col-gutter-sm">
<div class="row q-col-gutter-sm q-pa-sm"> <div class="col-12">
<div class="row col-12 q-col-gutter-sm"> <q-card class="q-pa-sm">
<div class="col-md-6 col-xs-12"> <div class="row q-col-gutter-sm no-wrap">
<q-select
class="bg-white"
hide-bottom-space
outlined
dense
lazy-rules
borderless
v-model="typeReport"
:label="`${'รายงาน'}`"
emit-value
map-options
:options="optionReport"
option-value="id"
option-label="name"
@update:model-value="getReport"
/>
</div>
<q-space /> <q-space />
<div> <div>
<q-btn <q-btn
:disable=" :disable="
!typeReport || !typeReport ||
!detailReport ||
((typeReport == 'main' || typeReport == 'report3') && ((typeReport == 'main' || typeReport == 'report3') &&
!organization) !organization)
" "
@ -292,46 +283,53 @@ onMounted(async () => {
</q-btn> </q-btn>
</div> </div>
</div> </div>
</q-card>
<div v-if="typeReport" class="row col-12">
<q-card bordered class="col-12 filter-card q-pa-sm">
<div class="row q-col-gutter-sm">
<div class="col-md-2 col-xs-6">
<datepicker
menu-class-name="modalfix"
v-model="year"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
@update:model-value="getReport"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
outlined
:model-value="year === 0 ? 'ทั้งหมด' : Number(year) + 543"
:label="`${'ปีงบประมาณ'}`"
bg-color="white"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div> </div>
<div class="col-12">
<q-card class="q-pa-sm">
<div class="row q-col-gutter-sm">
<div class="col">
<q-card flat bordered>
<q-card-section
bordered
class="bg-primary text-subtitle2 text-white q-pa-sm row 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-card-section class="q-pa-none">
<div class="col-12 q-px-sm">
<q-select
class="bg-white"
hide-bottom-space
dense
lazy-rules
borderless
v-model="typeReport"
:label="`${'รายงาน'}`"
emit-value
map-options
:options="optionReport"
option-value="id"
option-label="name"
@update:model-value="
(organization = ''), (year = new Date().getFullYear())
"
/>
</div>
<q-separator v-if="typeReport" />
<div <div
class="col-md-3 col-xs-6" class="col-12 q-px-sm"
v-if="typeReport == 'main' || typeReport == 'report3'" v-if="typeReport == 'main' || typeReport == 'report3'"
> >
<q-select <q-select
@ -341,7 +339,7 @@ onMounted(async () => {
hide-selected hide-selected
fill-input fill-input
hide-bottom-space hide-bottom-space
outlined borderless
option-label="name" option-label="name"
option-value="id" option-value="id"
emit-value emit-value
@ -350,7 +348,6 @@ onMounted(async () => {
:options="organizationOps" :options="organizationOps"
label="หน่วยงาน" label="หน่วยงาน"
use-input use-input
@update:model-value="getReport"
@filter="(inputValue: string, @filter="(inputValue: string,
doneFn: Function) => filterSelector(inputValue, doneFn)" doneFn: Function) => filterSelector(inputValue, doneFn)"
@ -364,23 +361,72 @@ onMounted(async () => {
</template></q-select </template></q-select
> >
</div> </div>
<q-separator
v-if="typeReport == 'main' || typeReport == 'report3'"
/>
<div class="col-12 q-px-sm q-pb-xs" v-if="typeReport">
<datepicker
menu-class-name="modalfix"
v-model="year"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
borderless
:model-value="
year === 0 ? 'ทั้งหมด' : Number(year) + 543
"
:label="`${'ปีงบประมาณ'}`"
bg-color="white"
>
<template v-slot:prepend>
<q-icon
size="18px"
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div> </div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn
dense
class="q-px-md"
label="ค้นหา"
unelevated
color="public"
type="submit"
:disable="
!typeReport ||
(typeReport == 'main' && organization == '') ||
(typeReport == 'report3' && organization == '')
"
/>
</q-card-actions>
</q-card> </q-card>
</div> </div>
</div> <div class="col-lg-9 col-md-9 col-sm-9 col-xs-12 col-xs-12 flex">
</q-card>
<q-card flat bordered class="col-12">
<q-card-section :horizontal="$q.screen.gt.sm">
<q-separator :vertical="$q.screen.gt.xs" />
<q-card-section class="col-lg-12 col-md-12 col-xs-12 scroll">
<q-splitter <q-splitter
v-model="splitterModel"
horizontal horizontal
style=" style="
height: 65vh; height: 75vh;
border: 1px solid rgb(210, 210, 210); border: 1px solid rgb(210, 210, 210);
border-radius: 5px; border-radius: 5px;
width: 100%;
" "
before-class="overflow-hidden disable" before-class="overflow-hidden disable"
separator-class="bg-white disabled" separator-class="bg-white disabled"
@ -423,7 +469,7 @@ onMounted(async () => {
<div class="q-pa-md"> <div class="q-pa-md">
<LoadView v-if="loadingBtn" /> <LoadView v-if="loadingBtn" />
<VuePDF <VuePDF
v-else v-else-if="!loadingBtn && pdfSrc"
ref="vuePDFRef" ref="vuePDFRef"
:pdf="pdfSrc" :pdf="pdfSrc"
:page="page" :page="page"
@ -467,10 +513,12 @@ onMounted(async () => {
</div> </div>
</template> </template>
</q-splitter> </q-splitter>
</q-card-section> </div>
</q-card-section> </div>
</q-card> </q-card>
</div> </div>
</div>
</q-form>
</template> </template>
<style lang="scss" scope> <style lang="scss" scope>

View file

@ -21,7 +21,12 @@ const { fetchStructureTree } = useStructureTree();
const { messageError } = useCounterMixin(); const { messageError } = useCounterMixin();
const loadingBtn = ref<boolean>(false); const loadingBtn = ref<boolean>(false);
const isReport = ref<boolean>(false);
const isLoadPDF = ref<boolean>(false);
const expandedModal = ref<boolean>(false);
const org = ref<string>("");
const isLoadStructureTree = ref<boolean>(true);
const filterTree = ref<string>(""); const filterTree = ref<string>("");
const nodeId = ref<string>(""); const nodeId = ref<string>("");
const nodeName = ref<string>(""); const nodeName = ref<string>("");
@ -35,7 +40,10 @@ const numOfPages = ref<number>(0);
const page = ref<number>(1); const page = ref<number>(1);
const pdfSrc = ref<any>(); const pdfSrc = ref<any>();
/** ฟังก์ชันเรียกข้อมูลโครงสร้างหน่วยงาน*/
async function fetchDataTree() { async function fetchDataTree() {
try {
isLoadStructureTree.value = true;
const dataTree = await fetchStructureTree("SYS_EVA_REQ", true); const dataTree = await fetchStructureTree("SYS_EVA_REQ", true);
if (dataTree) { if (dataTree) {
const data = dataTree.map((item: DataStructureTree) => ({ const data = dataTree.map((item: DataStructureTree) => ({
@ -44,6 +52,11 @@ async function fetchDataTree() {
})); }));
node.value = data; node.value = data;
} }
} catch (err) {
messageError($q, err);
} finally {
isLoadStructureTree.value = false;
}
} }
async function fetchReport() { async function fetchReport() {
@ -109,11 +122,12 @@ function onDownloadFile(type: string) {
* @param id id หนวยงาน * @param id id หนวยงาน
* @param level ระดบหนวยงาน * @param level ระดบหนวยงาน
*/ */
function onSelectedNode(id: string, name: string) { function onSelectedNode(data: any) {
if (id !== nodeId.value) { if (data.orgTreeId !== nodeId.value) {
nodeId.value = id; nodeId.value = data.orgTreeId;
nodeName.value = name; nodeName.value = data.orgTreeName;
fetchReport(); org.value = data.orgName;
expandedModal.value = false;
} }
} }
@ -131,6 +145,15 @@ function nextPage() {
} }
} }
function clearFilter() {
pdfSrc.value = undefined;
detailReport.value = undefined;
year.value = new Date().getFullYear();
org.value = "";
nodeId.value = "";
nodeName.value = "";
}
onMounted(() => { onMounted(() => {
fetchDataTree(); fetchDataTree();
}); });
@ -140,48 +163,16 @@ onMounted(() => {
<div class="toptitle text-dark col-12 row items-center"> <div class="toptitle text-dark col-12 row items-center">
รายงานประเมนบคคล รายงานประเมนบคคล
</div> </div>
<q-form greedy @submit.prevent @validation-success="fetchReport">
<div class="q-pa-sm q-gutter-sm"> <div class="row q-col-gutter-sm">
<q-card flat bordered class="col-12"> <div class="col-12">
<div class="row q-col-gutter-sm q-pa-sm"> <q-card class="q-pa-sm">
<div class="row col-12"> <div class="row q-col-gutter-sm no-wrap">
<datepicker
menu-class-name="modalfix"
v-model="year"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
@update:model-value="fetchReport"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
outlined
:model-value="year === 0 ? 'ทั้งหมด' : Number(year) + 543"
:label="`${'ปีงบประมาณ'}`"
bg-color="white"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
<q-space /> <q-space />
<div>
<q-btn <q-btn
:loading="loadingBtn" :loading="loadingBtn"
:disable="!nodeId" :disable="!detailReport"
flat flat
round round
color="primary" color="primary"
@ -216,33 +207,88 @@ onMounted(() => {
</div> </div>
</div> </div>
</q-card> </q-card>
</div>
<q-card flat bordered class="col-12"> <div class="col-12">
<q-card-section :horizontal="$q.screen.gt.sm"> <q-card class="q-pa-sm">
<q-card-section class="col-lg-3 col-md-4 col-xs-12 q-gutter-sm"> <div class="row q-col-gutter-sm">
<div class="col"> <div class="col">
<q-input dense outlined v-model="filterTree" label="ค้นหา"> <q-card flat bordered>
<template v-slot:append> <q-card-section
<q-icon name="search" /> bordered
</template> class="bg-primary text-subtitle2 text-white q-pa-sm row 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-card-section class="q-pa-none">
<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"
autogrow
borderless
label="สังกัด"
class="bg-white full-width"
>
</q-input> </q-input>
</div> </div>
<!-- @click="onOpenOrg" -->
</template>
<q-separator />
<div class="bg-white tree-containerevaluate q-pa-xs"> <q-card-section v-if="loadingBtn">
<LoadView />
</q-card-section>
<q-card-section v-else>
<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 <q-tree
dense dense
:nodes="node" :nodes="node"
node-key="orgTreeId" node-key="orgTreeId"
label-key="labelName" label-key="labelName"
v-model:expanded="node"
:filter="filterTree.trim()" :filter="filterTree.trim()"
no-results-label="ไม่พบข้อมูลที่ค้นหา" no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล" no-nodes-label="ไม่มีข้อมูล"
class="tree-container-report q-mt-sm"
> >
<template v-slot:default-header="prop"> <template v-slot:default-header="prop">
<q-item <q-item
@click.stop=" @click.stop="onSelectedNode(prop.node)"
onSelectedNode(prop.node.orgTreeId, prop.node.orgTreeName)
"
:active="nodeId === prop.node.orgTreeId" :active="nodeId === prop.node.orgTreeId"
clickable clickable
active-class="my-list-link text-primary text-weight-medium" active-class="my-list-link text-primary text-weight-medium"
@ -253,7 +299,11 @@ onMounted(() => {
{{ prop.node.orgTreeName }} {{ prop.node.orgTreeName }}
</div> </div>
<div class="text-weight-light text-grey-8"> <div class="text-weight-light text-grey-8">
{{ prop.node.orgCode == null ? null : prop.node.orgCode }} {{
prop.node.orgCode == null
? null
: prop.node.orgCode
}}
{{ {{
prop.node.orgTreeShortName == null prop.node.orgTreeShortName == null
? null ? null
@ -264,18 +314,68 @@ onMounted(() => {
</q-item> </q-item>
</template> </template>
</q-tree> </q-tree>
</q-card-section>
</q-expansion-item>
</div>
<q-separator />
<div class="col-12 q-px-sm">
<datepicker
menu-class-name="modalfix"
v-model="year"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
borderless
:model-value="
year === 0 ? 'ทั้งหมด' : Number(year) + 543
"
:label="`${'ปีงบประมาณ'}`"
bg-color="white"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div> </div>
</q-card-section> </q-card-section>
<q-separator />
<q-separator :vertical="$q.screen.gt.xs" /> <q-card-actions align="right">
<q-btn
<q-card-section class="col-lg-9 col-md-8 col-xs-12 scroll"> dense
class="q-px-md"
label="ค้นหา"
unelevated
color="public"
type="submit"
:disable="nodeId == ''"
/>
</q-card-actions>
</q-card>
</div>
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-12 col-xs-12 flex">
<q-splitter <q-splitter
horizontal horizontal
style=" style="
height: 75vh; height: 75vh;
border: 1px solid rgb(210, 210, 210); border: 1px solid rgb(210, 210, 210);
border-radius: 5px; border-radius: 5px;
width: 100%;
" "
before-class="overflow-hidden disable" before-class="overflow-hidden disable"
separator-class="bg-white disabled" separator-class="bg-white disabled"
@ -318,7 +418,7 @@ onMounted(() => {
<div class="q-pa-md"> <div class="q-pa-md">
<LoadView v-if="loadingBtn" /> <LoadView v-if="loadingBtn" />
<VuePDF <VuePDF
v-else v-else-if="!loadingBtn && pdfSrc"
ref="vuePDFRef" ref="vuePDFRef"
:pdf="pdfSrc" :pdf="pdfSrc"
:page="page" :page="page"
@ -362,13 +462,15 @@ onMounted(() => {
</div> </div>
</template> </template>
</q-splitter> </q-splitter>
</q-card-section> </div>
</q-card-section> </div>
</q-card> </q-card>
</div> </div>
</div>
</q-form>
</template> </template>
<style lang="scss" scope> <style lang="scss" scoped>
.q-item.hover-green:hover { .q-item.hover-green:hover {
background-color: #d5f1ee57; background-color: #d5f1ee57;
border-radius: 2px; border-radius: 2px;
@ -390,4 +492,22 @@ onMounted(() => {
font-weight: 600; font-weight: 600;
border: 1px solid rgba(175, 185, 196, 0.217); border: 1px solid rgba(175, 185, 196, 0.217);
} }
.tree-container-report {
overflow: auto;
max-height: 50vh;
}
:deep(.tree-container-report div) {
padding: 0;
}
:deep(.expansion-item .q-item__section) {
padding-right: 3px;
}
:deep(.expansion-item .q-item) {
min-height: 32px;
padding: 2px 9px;
}
</style> </style>

View file

@ -23,6 +23,10 @@ const storeEmp = useSalaryEmployeeListSDataStore();
const { messageError, showLoader, hideLoader } = useCounterMixin(); const { messageError, showLoader, hideLoader } = useCounterMixin();
const checkId = ref<string>("");
const isLoadStructureTree = ref<boolean>(true);
const expandedModal = ref<boolean>(false);
const org = ref<string>("");
const year = ref<number>(new Date().getFullYear()); const year = ref<number>(new Date().getFullYear());
const isLoadFilePdf = ref<boolean>(false); const isLoadFilePdf = ref<boolean>(false);
const employeeClass = ref<string>("officer"); const employeeClass = ref<string>("officer");
@ -104,20 +108,24 @@ async function fetchDataRound() {
} }
async function fetchDataOrg(revisionId: string) { async function fetchDataOrg(revisionId: string) {
if (revisionId) { if (!revisionId)
await http return (isLoadStructureTree.value = false), (checkId.value = revisionId);
.get(config.API.activeOrganizationRootById(revisionId))
.then(async (res) => { try {
isLoadStructureTree.value = true;
const res = await http.get(
config.API.activeOrganizationRootById(revisionId)
);
const data = res.data.result.map((item: DataStructureTree) => ({ const data = res.data.result.map((item: DataStructureTree) => ({
...item, ...item,
children: null, children: null,
labelName: `${item.orgRootName} ${item.orgRootCode} ${item.orgRootShortName}`, labelName: `${item.orgRootName} ${item.orgRootCode} ${item.orgRootShortName}`,
})); }));
node.value = data; node.value = data;
}) } catch (err) {
.catch((err) => {
messageError($q, err); messageError($q, err);
}); } finally {
isLoadStructureTree.value = false;
} }
} }
@ -126,11 +134,12 @@ async function fetchDataOrg(revisionId: string) {
* @param id id หนวยงาน * @param id id หนวยงาน
* @param level ระดบหนวยงาน * @param level ระดบหนวยงาน
*/ */
function onSelectedNode(id: string, name: string) { function onSelectedNode(data: any) {
if (id !== nodeId.value) { if (data.id !== nodeId.value) {
nodeId.value = id; nodeId.value = data.id;
nodeName.value = name; nodeName.value = data.orgRootName;
fetchReportPDF(); org.value = data.orgName;
expandedModal.value = false;
} }
} }
@ -139,13 +148,7 @@ function onChangeYear() {
} }
function fetchReportPDF() { function fetchReportPDF() {
if ( if (employeeClass.value && round.value && report.value && nodeId.value) {
employeeClass.value &&
round.value &&
group.value &&
report.value &&
nodeId.value
) {
fetchDataReportUnified(report.value, employeeClass.value); fetchDataReportUnified(report.value, employeeClass.value);
} }
} }
@ -278,6 +281,20 @@ function nextPage() {
} }
} }
function clearFilter() {
checkId.value = "";
year.value = new Date().getFullYear();
round.value = "";
group.value = "GROUP1";
report.value = "";
detailReport.value = undefined;
pdfSrc.value = undefined;
org.value = "";
nodeId.value = "";
nodeName.value = "";
}
onMounted(() => { onMounted(() => {
fetchDataRound(); fetchDataRound();
}); });
@ -286,10 +303,12 @@ onMounted(() => {
<template> <template>
<div class="toptitle text-dark col-12 row items-center">รายงานเงนเดอน</div> <div class="toptitle text-dark col-12 row items-center">รายงานเงนเดอน</div>
<div class="q-pa-sm q-gutter-sm"> <q-form greedy @submit.prevent @validation-success="fetchReportPDF">
<q-card flat bordered class="col-12"> <div class="row q-col-gutter-sm">
<div class="row q-col-gutter-sm q-pa-sm"> <div class="col-12">
<div class="row col-12"> <q-card class="q-pa-sm">
<div class="row q-col-gutter-sm no-wrap">
<div class="col-lg-2 col-md-3 col-sm-3 col-xs-12">
<q-select <q-select
outlined outlined
dense dense
@ -304,15 +323,16 @@ onMounted(() => {
@update:model-value="(report = ''), fetchReportPDF" @update:model-value="(report = ''), fetchReportPDF"
> >
</q-select> </q-select>
</div>
<q-space /> <q-space />
<div>
<q-btn <q-btn
:loading="isLoadFilePdf" :loading="isLoadFilePdf"
flat flat
round round
color="primary" color="primary"
icon="download" icon="download"
:disable="report === '' || nodeId === ''" :disable="!detailReport"
v-if="checkPermission($route)?.attrIsGet" v-if="checkPermission($route)?.attrIsGet"
> >
<q-menu> <q-menu>
@ -341,12 +361,128 @@ onMounted(() => {
</q-menu> </q-menu>
</q-btn> </q-btn>
</div> </div>
</div>
</q-card>
</div>
<div class="col-12">
<q-card class="q-pa-sm">
<div class="row q-col-gutter-sm">
<div class="col">
<q-card flat bordered>
<q-card-section
bordered
class="bg-primary text-subtitle2 text-white q-pa-sm row 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-card-section class="q-pa-none">
<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"
autogrow
borderless
label="สังกัด"
class="bg-white full-width"
>
</q-input>
</div>
<!-- @click="onOpenOrg" -->
</template>
<q-separator />
<div class="row col-12"> <q-card-section v-if="isLoadStructureTree && checkId">
<q-card bordered class="col-12 filter-card q-pa-sm"> <LoadView />
<div class="row col-12 q-col-gutter-sm items-center"> </q-card-section>
<div class="row col-md-8 col-sx-12 q-col-gutter-sm"> <q-card-section
<div class="col-md-3 col-xs-6"> v-else-if="isLoadStructureTree && !checkId"
class="q-pa-sm"
>
<div class="bg-grey-3 q-pa-sm rounded-borders">
ไมพบขอมลสงก
</div>
</q-card-section>
<q-card-section v-else>
<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
dense
:nodes="node"
node-key="orgTreeId"
label-key="labelName"
v-model:expanded="node"
:filter="filterTree.trim()"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
class="tree-container-report q-mt-sm"
>
<template v-slot:default-header="prop">
<q-item
@click.stop="onSelectedNode(prop.node)"
:active="nodeId === prop.node.orgTreeId"
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"
>
<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>
<q-separator />
<div class="col-12 q-px-sm">
<datepicker <datepicker
menu-class-name="modalfix" menu-class-name="modalfix"
v-model="year" v-model="year"
@ -363,7 +499,7 @@ onMounted(() => {
<template #trigger> <template #trigger>
<q-input <q-input
dense dense
outlined borderless
:model-value=" :model-value="
year === 0 ? 'ทั้งหมด' : Number(year) + 543 year === 0 ? 'ทั้งหมด' : Number(year) + 543
" "
@ -372,6 +508,7 @@ onMounted(() => {
> >
<template v-slot:prepend> <template v-slot:prepend>
<q-icon <q-icon
size="18px"
name="event" name="event"
class="cursor-pointer" class="cursor-pointer"
style="color: var(--q-primary)" style="color: var(--q-primary)"
@ -382,12 +519,13 @@ onMounted(() => {
</template> </template>
</datepicker> </datepicker>
</div> </div>
<div class="col-md-3 col-xs-6"> <q-separator />
<div class="col-12 q-px-sm">
<q-select <q-select
v-model="round" v-model="round"
label="รอบการขึ้นเงินเดือน" label="รอบการขึ้นเงินเดือน"
dense dense
outlined borderless
map-options map-options
option-label="name" option-label="name"
option-value="id" option-value="id"
@ -395,20 +533,20 @@ onMounted(() => {
lazy-rules lazy-rules
hide-bottom-space hide-bottom-space
bg-color="white" bg-color="white"
@update:model-value="(report = ''), fetchReportPDF" @update:model-value="report = ''"
> >
</q-select> </q-select>
</div> </div>
<q-separator />
<div <div
class="col-md-3 col-xs-6" class="col-12 q-px-sm"
v-if="employeeClass === 'officer'" v-if="employeeClass === 'officer'"
> >
<q-select <q-select
v-model="group" v-model="group"
label="กลุ่ม" label="กลุ่ม"
dense dense
outlined borderless
emit-value emit-value
map-options map-options
option-label="name" option-label="name"
@ -417,14 +555,12 @@ onMounted(() => {
lazy-rules lazy-rules
hide-bottom-space hide-bottom-space
bg-color="white" bg-color="white"
@update:model-value="fetchReportPDF"
/> />
</div> </div>
</div> <q-separator v-if="employeeClass === 'officer'" />
<div class="row q-col-gutter-sm col-md-12"> <div class="col-12 q-px-sm">
<div class="col-md-4 col-xs-12">
<q-select <q-select
outlined borderless
dense dense
v-model="report" v-model="report"
:options="reportOption" :options="reportOption"
@ -434,78 +570,31 @@ onMounted(() => {
option-label="name" option-label="name"
option-value="id" option-value="id"
class="bg-white" class="bg-white"
@update:model-value="fetchReportPDF"
/> />
</div> </div>
</div>
</div>
</q-card>
</div>
</div>
</q-card>
<q-card flat bordered class="col-12" v-if="isRound">
<q-card-section :horizontal="$q.screen.gt.sm">
<q-card-section class="col-lg-3 col-md-4 col-xs-12 q-gutter-sm">
<div class="col">
<q-input dense outlined v-model="filterTree" label="ค้นหา">
<template v-slot:append>
<q-icon name="search" />
</template>
</q-input>
</div>
<div class="bg-white tree-container q-pa-xs">
<q-tree
dense
:nodes="node"
node-key="id"
label-key="labelName"
:filter="filterTree.trim()"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
>
<template v-slot:default-header="prop">
<q-item
@click.stop="
onSelectedNode(prop.node.id, prop.node.orgRootName)
"
:active="nodeId === prop.node.id"
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"
>
<div>
<div class="text-weight-medium">
{{ prop.node.orgRootName }}
</div>
<div class="text-weight-light text-grey-8">
{{
prop.node.orgRootCode == null
? null
: prop.node.orgRootCode
}}
{{
prop.node.orgRootShortName == null
? null
: prop.node.orgRootShortName
}}
</div>
</div>
</q-item>
</template>
</q-tree>
</div>
</q-card-section> </q-card-section>
<q-separator :vertical="$q.screen.gt.xs" /> <q-separator />
<q-card-actions align="right">
<q-card-section class="col-lg-9 col-md-8 col-xs-12 scroll"> <q-btn
dense
class="q-px-md"
label="ค้นหา"
unelevated
color="public"
type="submit"
:disable="nodeId == '' || round == '' || report == ''"
/>
</q-card-actions>
</q-card>
</div>
<div class="col-lg-9 col-md-9 col-sm-9 col-xs-12 col-xs-12 flex">
<q-splitter <q-splitter
horizontal horizontal
style=" style="
height: 63vh; height: 75vh;
border: 1px solid rgb(210, 210, 210); border: 1px solid rgb(210, 210, 210);
border-radius: 5px; border-radius: 5px;
width: 100%;
" "
before-class="overflow-hidden disable" before-class="overflow-hidden disable"
separator-class="bg-white disabled" separator-class="bg-white disabled"
@ -592,15 +681,31 @@ onMounted(() => {
</div> </div>
</template> </template>
</q-splitter> </q-splitter>
</q-card-section> </div>
</q-card-section> </div>
</q-card> </q-card>
</div>
</div>
</q-form>
<q-card flat bordered class="col-12" v-else> <div class="q-pa-sm q-gutter-sm">
<div class="q-pa-sm"> <q-card flat bordered class="col-12">
<q-banner inline-actions rounded class="bg-grey-1 text-center"> <div class="row q-col-gutter-sm q-pa-sm">
ไมอม <div class="row col-12">
</q-banner> <q-card bordered class="col-12 filter-card q-pa-sm">
<div class="row col-12 q-col-gutter-sm items-center">
<div class="row col-md-8 col-sx-12 q-col-gutter-sm">
<div class="col-md-3 col-xs-6"></div>
<div class="col-md-3 col-xs-6"></div>
<div class="col-md-3 col-xs-6"></div>
</div>
<div class="row q-col-gutter-sm col-md-12">
<div class="col-md-4 col-xs-12"></div>
</div>
</div>
</q-card>
</div>
</div> </div>
</q-card> </q-card>
</div> </div>
@ -628,4 +733,18 @@ onMounted(() => {
font-weight: 600; font-weight: 600;
border: 1px solid rgba(175, 185, 196, 0.217); border: 1px solid rgba(175, 185, 196, 0.217);
} }
.tree-container-report {
overflow: auto;
max-height: 50vh;
}
:deep(.expansion-item .q-item__section) {
padding-right: 3px;
}
:deep(.expansion-item .q-item) {
min-height: 32px;
padding: 2px 9px;
}
</style> </style>