ปรับ 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,200 +138,275 @@ 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>
<q-form greedy @submit.prevent @validation-success="getReportRecruit">
<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-space />
<div>
<q-btn
:loading="isLoadPDF"
flat
round
color="primary"
icon="download"
:disable="!checkPermission($route)?.attrIsGet || !detailReport"
>
<q-menu>
<q-list style="min-width: 150px">
<q-item
clickable
v-close-popup
@click="
genReportXLSX(detailReport, reportSelect.name, 'pdf')
"
>
<q-item-section avatar
><q-icon color="red" name="mdi-file-pdf"
/></q-item-section>
<q-item-section>ไฟล .pdf</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="genReportXLSX(detailReport, reportSelect.name)"
>
<q-item-section avatar
><q-icon color="green" name="mdi-file-excel"
/></q-item-section>
<q-item-section>ไฟล .xlsx</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</div>
</div>
</q-card>
</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
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
horizontal
style="
height: 75vh;
border: 1px solid rgb(210, 210, 210);
border-radius: 5px;
width: 100%;
"
before-class="overflow-hidden disable"
separator-class="bg-white disabled"
>
<template v-slot:before>
<div class="q-px-sm">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
<template v-slot:after>
<div class="q-pa-md">
<LoadView v-if="isLoadPDF" />
<VuePDF
v-else-if="!isLoadPDF && pdfSrc"
ref="vuePDFRef"
:pdf="pdfSrc"
:page="page"
fit-parent
/>
</div>
</template>
<template v-slot:default>
<div class="q-pa-md">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
</q-splitter>
</div>
</div>
</q-card>
</div>
</div>
</q-form>
<div class="q-pa-sm q-gutter-sm"> <div class="q-pa-sm q-gutter-sm">
<q-card flat bordered class="col-12"> <q-card flat bordered class="col-12">
<div class="row q-col-gutter-sm q-pa-sm"> <div class="row q-col-gutter-sm q-pa-sm">
<div class="row col-12 q-col-gutter-sm"> <div class="row col-12 q-col-gutter-sm">
<q-select
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 />
<q-btn
flat
round
color="primary"
icon="download"
:disable="
!checkPermission($route)?.attrIsGet ||
isReport ||
reportSelect.id == ''
"
>
<q-menu>
<q-list style="min-width: 150px">
<q-item
clickable
v-close-popup
@click="genReportXLSX(detailReport, reportSelect.name, 'pdf')"
>
<q-item-section avatar
><q-icon color="red" name="mdi-file-pdf"
/></q-item-section>
<q-item-section>ไฟล .pdf</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="genReportXLSX(detailReport, reportSelect.name)"
>
<q-item-section avatar
><q-icon color="green" name="mdi-file-excel"
/></q-item-section>
<q-item-section>ไฟล .xlsx</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</div> </div>
</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">
<q-splitter
v-model="splitterModel"
horizontal
style="
height: 70vh;
border: 1px solid rgb(210, 210, 210);
border-radius: 5px;
"
before-class="overflow-hidden disable"
separator-class="bg-white disabled"
>
<template v-slot:before>
<div class="q-px-sm">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
<template v-slot:after>
<div class="q-pa-md">
<VuePDF ref="vuePDFRef" :pdf="pdfSrc" :page="page" fit-parent />
</div>
</template>
<template v-slot:default>
<div class="q-pa-md">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
</q-splitter>
</q-card-section>
</q-card>
</div>
</template> </template>
<style scoped></style> <style scoped></style>

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() {
node.value = await fetchStructureTree(route.meta.Key as string, true); try {
isLoadStructureTree.value = 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,308 +209,380 @@ 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 <q-space />
dense <div>
v-model="reportType" <q-btn
:options="optionReport" :loading="isLoadPDF"
label="รายงาน" flat
map-options round
option-label="name" color="primary"
style="width: 500px" icon="download"
@update:model-value="fetchDataReport" :disable="!reportType || !nodeId || !detailReport"
> @click="
</q-select> genReportXLSX(detailReport, `${year}_${reportType?.name}`)
"
<q-space /> >
<q-btn </q-btn>
:loading="isLoadPDF"
flat
round
color="primary"
icon="download"
:disable="!reportType || !nodeId"
@click="genReportXLSX(detailReport, `${year}_${reportType?.name}`)"
>
</q-btn>
</div>
<div class="row col-12">
<q-card bordered class="col-12 filter-card q-pa-sm">
<div class="row col-12 q-col-gutter-sm">
<div>
<datepicker
v-model="year"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
@update:model-value="onUpdateYear"
>
<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>
</div>
<div>
<datepicker
menu-class-name="modalfix"
v-model="dateStart"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
@update:model-value="fetchDataReport"
:max-date="dateEnd"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
class="bg-white"
outlined
dense
borderless
:model-value="dateStart ? date2Thai(dateStart) : null"
:label="`${'ตั้งเเต่วันที่'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
color="primary"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div>
<datepicker
menu-class-name="modalfix"
v-model="dateEnd"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
@update:model-value="fetchDataReport"
:min-date="dateStart"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
class="bg-white"
outlined
dense
borderless
:model-value="dateEnd ? date2Thai(dateEnd) : null"
:label="`${'ถึงวันที่'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
color="primary"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
</div> </div>
</q-card> </div>
</div> </q-card>
</div> </div>
</q-card> <div class="col-12">
<q-card class="q-pa-sm">
<q-card flat bordered class="col-12"> <div class="row q-col-gutter-sm">
<q-card-section :horizontal="$q.screen.gt.sm"> <div class="col">
<q-card-section class="col-lg-3 col-md-4 col-xs-12 q-gutter-sm"> <q-card flat bordered>
<div class="col"> <q-card-section
<q-input dense outlined v-model="filterTree" label="ค้นหา"> bordered
<template v-slot:append> class="bg-primary text-subtitle2 text-white q-pa-sm row items-center"
<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> <q-icon name="mdi-filter" class="q-mr-xs" />
<div class="text-weight-medium"> วกรอง
{{ prop.node.orgTreeName }} <q-space />
</div> <q-btn
<div class="text-weight-light text-grey-8"> dense
{{ prop.node.orgCode == null ? null : prop.node.orgCode }} size="12px"
{{ class="q-px-sm"
prop.node.orgTreeShortName == null rounded
? null flat
: prop.node.orgTreeShortName 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 />
<q-card-section v-if="isLoadStructureTree">
<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
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
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
v-model="year"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
@update:model-value="onUpdateYear"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
class="bg-white"
dense
:model-value="Number(year) + 543"
:label="`${'ปีงบประมาณ'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
size="18px"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-12">
<datepicker
menu-class-name="modalfix"
v-model="dateStart"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
:max-date="dateEnd"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
class="bg-white"
dense
:model-value="
dateStart ? date2Thai(dateStart) : null
"
:label="`${'ตั้งเเต่วันที่'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
color="primary"
size="18px"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-12">
<datepicker
menu-class-name="modalfix"
v-model="dateEnd"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
:min-date="dateStart"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
class="bg-white"
dense
:model-value="dateEnd ? date2Thai(dateEnd) : null"
:label="`${'ถึงวันที่'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
color="primary"
size="18px"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
</div> </div>
</div> </div>
</q-item> </q-card-section>
</template> <q-separator />
</q-tree> <q-card-actions align="right">
<q-btn
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
horizontal
style="
height: 75vh;
border: 1px solid rgb(210, 210, 210);
border-radius: 5px;
width: 100%;
"
before-class="overflow-hidden disable"
separator-class="bg-white disabled"
>
<template v-slot:before>
<div class="q-px-sm">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
<template v-slot:after>
<div class="q-pa-md">
<LoadView v-if="isLoadPDF" />
<VuePDF
v-else-if="!isLoadPDF && pdfSrc"
ref="vuePDFRef"
:pdf="pdfSrc"
:page="page"
fit-parent
/>
</div>
</template>
<template v-slot:default>
<div class="q-pa-md">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
</q-splitter>
</div>
</div> </div>
</q-card-section> </q-card>
</div>
<q-separator :vertical="$q.screen.gt.xs" /> </div>
</q-form>
<q-card-section class="col-lg-9 col-md-8 col-xs-12 scroll">
<q-splitter
horizontal
style="
height: 65vh;
border: 1px solid rgb(210, 210, 210);
border-radius: 5px;
"
before-class="overflow-hidden disable"
separator-class="bg-white disabled"
>
<template v-slot:before>
<div class="q-px-sm">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
<template v-slot:after>
<div class="q-pa-md">
<LoadView v-if="isLoadPDF" />
<VuePDF
v-else
ref="vuePDFRef"
:pdf="pdfSrc"
:page="page"
fit-parent
/>
</div>
</template>
<template v-slot:default>
<div class="q-pa-md">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
</q-splitter>
</q-card-section>
</q-card-section>
</q-card>
</div>
</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() {
node.value = await fetchStructureTree(route.meta.Key as string, true); try {
isLoadStructureTree.value = 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,391 +231,472 @@ 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 <q-space />
dense <div>
v-model="reportType" <q-btn
:options="optionReport" :loading="isLoadPDF"
label="รายงาน" flat
map-options round
emit-value :disable="!reportType || !nodeId || !detailReport"
option-label="name" color="primary"
option-value="id" icon="download"
style="width: 320px" >
@update:model-value="fetchDataReport" <q-menu>
> <q-list style="min-width: 150px">
</q-select> <q-item
clickable
<q-space /> v-close-popup
@click.stop.pervent="
<q-btn genReportXLSX(
:loading="isLoadPDF" detailReport,
flat `${
round optionReport.find((e) => e.id === reportType)?.name
:disable="!reportType || !nodeId" }`,
color="primary" 'pdf'
icon="download" )
> "
<q-menu>
<q-list style="min-width: 150px">
<q-item
clickable
v-close-popup
@click.stop.pervent="
genReportXLSX(
detailReport,
`${optionReport.find((e) => e.id === reportType)?.name}`,
'pdf'
)
"
>
<q-item-section avatar
><q-icon color="red" name="mdi-file-pdf"
/></q-item-section>
<q-item-section>ไฟล .pdf</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click.stop.pervent="
genReportXLSX(
detailReport,
`${optionReport.find((e) => e.id === reportType)?.name}`
)
"
>
<q-item-section avatar
><q-icon color="green" name="mdi-file-excel"
/></q-item-section>
<q-item-section>ไฟล .xlsx</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</div>
<div class="row col-12">
<q-card bordered class="col-12 filter-card q-pa-sm">
<div class="row col-12 q-col-gutter-sm">
<div class="col-md-3 col-xs-12">
<q-select
class="bg-white"
outlined
dense
v-model="filterType"
:options="filterTypeOption"
emit-value
map-options
option-label="name"
option-value="id"
use-input
@update:model-value="onUpdateFilter"
>
</q-select>
</div>
<div v-if="filterType === 'YEAR'">
<datepicker
v-model="year"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
@update:model-value="onUpdateFilter"
>
<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-item-section avatar
<q-icon ><q-icon color="red" name="mdi-file-pdf"
name="event" /></q-item-section>
class="cursor-pointer" <q-item-section>ไฟล .pdf</q-item-section>
style="color: var(--q-primary)" </q-item>
> <q-item
</q-icon> clickable
</template> v-close-popup
</q-input> @click.stop.pervent="
</template> genReportXLSX(
</datepicker> detailReport,
</div> `${
optionReport.find((e) => e.id === reportType)?.name
<div v-if="filterType === 'MONTH'"> }`
<datepicker )
v-model="dateMonth" "
:locale="'th'"
autoApply
month-picker
:enableTimePicker="false"
@update:model-value="onUpdateFilter"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
class="bg-white"
outlined
dense
borderless
:label="`${'เดือน'}`"
:model-value="monthYearThai(dateMonth)"
> >
<template v-slot:prepend> <q-item-section avatar
<q-icon ><q-icon color="green" name="mdi-file-excel"
name="event" /></q-item-section>
class="cursor-pointer" <q-item-section>ไฟล .xlsx</q-item-section>
color="primary" </q-item>
></q-icon> </q-list>
</template> </q-menu>
</q-input> </q-btn>
</template>
</datepicker>
</div>
<div>
<datepicker
menu-class-name="modalfix"
v-model="dateStart"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
@update:model-value="fetchDataReport"
:max-date="dateEnd"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
class="bg-white"
outlined
dense
borderless
:model-value="dateStart ? date2Thai(dateStart) : null"
:label="`${'ตั้งเเต่วันที่'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
color="primary"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div>
<datepicker
menu-class-name="modalfix"
v-model="dateEnd"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
@update:model-value="fetchDataReport"
:min-date="dateStart"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
class="bg-white"
outlined
dense
borderless
:model-value="dateEnd ? date2Thai(dateEnd) : null"
:label="`${'ถึงวันที่'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
color="primary"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
</div> </div>
</q-card> </div>
</div> </q-card>
</div> </div>
</q-card> <div class="col-12">
<q-card class="q-pa-sm">
<q-card flat bordered class="col-12"> <div class="row q-col-gutter-sm">
<q-card-section :horizontal="$q.screen.gt.sm"> <div class="col">
<q-card-section class="col-lg-3 col-md-4 col-xs-12 q-gutter-sm"> <q-card flat bordered>
<div class="col"> <q-card-section
<q-input dense outlined v-model="filterTree" label="ค้นหา"> bordered
<template v-slot:append> class="bg-primary text-subtitle2 text-white q-pa-sm row items-center"
<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> <q-icon name="mdi-filter" class="q-mr-xs" />
<div class="text-weight-medium"> วกรอง
{{ prop.node.orgTreeName }} <q-space />
</div> <q-btn
<div class="text-weight-light text-grey-8"> dense
{{ prop.node.orgCode == null ? null : prop.node.orgCode }} size="12px"
{{ class="q-px-sm"
prop.node.orgTreeShortName == null rounded
? null flat
: prop.node.orgTreeShortName 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 />
<q-card-section v-if="isLoadStructureTree">
<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
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
class="bg-white"
dense
v-model="filterType"
:options="filterTypeOption"
emit-value
map-options
option-label="name"
option-value="id"
use-input
borderless
@update:model-value="onUpdateFilter"
>
</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">
<div v-if="filterType === 'YEAR'">
<datepicker
v-model="year"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
@update:model-value="onUpdateFilter"
>
<template #year="{ year }">{{
year + 543
}}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
class="bg-white"
dense
: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>
<div v-if="filterType === 'MONTH'">
<datepicker
v-model="dateMonth"
:locale="'th'"
autoApply
month-picker
:enableTimePicker="false"
@update:model-value="onUpdateFilter"
>
<template #year="{ year }">{{
year + 543
}}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
class="bg-white"
dense
:label="`${'เดือน'}`"
:model-value="monthYearThai(dateMonth)"
>
<template v-slot:prepend>
<q-icon
size="18px"
name="event"
class="cursor-pointer"
color="primary"
></q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
</div>
<div class="col-12">
<datepicker
menu-class-name="modalfix"
v-model="dateStart"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
:max-date="dateEnd"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
class="bg-white"
dense
:model-value="
dateStart ? date2Thai(dateStart) : null
"
:label="`${'ตั้งเเต่วันที่'}`"
>
<template v-slot:prepend>
<q-icon
size="18px"
name="event"
class="cursor-pointer"
color="primary"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-12">
<datepicker
menu-class-name="modalfix"
v-model="dateEnd"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
:min-date="dateStart"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
class="bg-white"
dense
:model-value="dateEnd ? date2Thai(dateEnd) : null"
:label="`${'ถึงวันที่'}`"
>
<template v-slot:prepend>
<q-icon
size="18px"
name="event"
class="cursor-pointer"
color="primary"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
</div> </div>
</div> </div>
</q-item> </q-card-section>
</template> <q-separator />
</q-tree> <q-card-actions align="right">
<q-btn
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
horizontal
style="
height: 75vh;
border: 1px solid rgb(210, 210, 210);
border-radius: 5px;
width: 100%;
"
before-class="overflow-hidden disable"
separator-class="bg-white disabled"
>
<template v-slot:before>
<div class="q-px-sm">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
<template v-slot:after>
<div class="q-pa-md">
<LoadView v-if="isLoadPDF" />
<VuePDF
v-else-if="!isLoadPDF && pdfSrc"
ref="vuePDFRef"
:pdf="pdfSrc"
:page="page"
fit-parent
/>
</div>
</template>
<template v-slot:default>
<div class="q-pa-md">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
</q-splitter>
</div>
</div> </div>
</q-card-section> </q-card>
</div>
<q-separator :vertical="$q.screen.gt.xs" /> </div>
</q-form>
<q-card-section class="col-lg-9 col-md-8 col-xs-12 scroll">
<q-splitter
horizontal
style="
height: 65vh;
border: 1px solid rgb(210, 210, 210);
border-radius: 5px;
"
before-class="overflow-hidden disable"
separator-class="bg-white disabled"
>
<template v-slot:before>
<div class="q-px-sm">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
<template v-slot:after>
<div class="q-pa-md">
<LoadView v-if="isLoadPDF" />
<VuePDF
v-else
ref="vuePDFRef"
:pdf="pdfSrc"
:page="page"
fit-parent
/>
</div>
</template>
<template v-slot:default>
<div class="q-pa-md">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
</q-splitter>
</q-card-section>
</q-card-section>
</q-card>
</div>
</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() {
node.value = await fetchStructureTree(route.meta.Key as string, true); try {
isLoadStructureTree.value = 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,333 +344,399 @@ 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">
<q-select <div class="col-lg-2 col-md-3 col-sm-3 col-xs-12">
outlined <q-select
dense outlined
v-model="employeeClass" dense
:options="employeeClassOption" v-model="employeeClass"
label="สถานภาพ" :options="employeeClassOption"
emit-value label="สถานภาพ"
map-options emit-value
option-label="name" map-options
option-value="id" option-label="name"
style="width: 230px" option-value="id"
@update:model-value="onUpdateFilter" style="width: 230px"
> >
</q-select> </q-select>
<q-space />
<q-btn
flat
round
:loading="isLoadPDF"
:disable="!nodeId || !employeeClass || (!roundId && !roundId2)"
color="primary"
icon="download"
v-if="checkPermission($route)?.attrIsGet"
>
<q-menu>
<q-list style="min-width: 150px">
<q-item
clickable
v-close-popup
@click="
downloadReport(
'pdf',
!['report1', 'report2', 'report3'].includes(
typeReport.val
)
? 'xlsx'
: 'docx'
)
"
>
<q-item-section avatar
><q-icon color="red" name="mdi-file-pdf"
/></q-item-section>
<q-item-section>ไฟล .pdf</q-item-section>
</q-item>
<q-item
v-if="
typeReport.val == 'report1' ||
typeReport.val == 'report2' ||
typeReport.val == 'report3'
"
clickable
v-close-popup
@click="downloadReport('docx')"
>
<q-item-section avatar
><q-icon color="blue" name="mdi-file-word"
/></q-item-section>
<q-item-section>ไฟล .docx</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="downloadReport('xlsx')"
v-if="typeReport.val !== 'report1'"
>
<q-item-section avatar
><q-icon color="green" name="mdi-file-excel"
/></q-item-section>
<q-item-section>ไฟล .xlsx</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</div>
<div class="row col-12">
<q-card bordered class="col-12 filter-card q-pa-sm">
<div class="row col-12 q-col-gutter-sm">
<div class="col-md-4 col-xs-12">
<q-select
class="bg-white"
hide-bottom-space
outlined
dense
lazy-rules
borderless
v-model="typeReport"
:label="`${'รายงาน'}`"
map-options
:options="employeeClass == 'officer' ? optionReport.filter((item:any)=>item.id !== '7'):optionReport.filter((item:any)=>item.id !== '4')"
option-value="id"
option-label="name"
@update:model-value="onUpdateFilter"
/>
</div>
<div class="col-md-4 col-xs-12">
<q-select
v-if="
typeReport &&
(typeReport.val == 'report1' ||
typeReport.val == 'report2' ||
typeReport.val == 'report3')
"
class="bg-white"
use-input
fill-input
hide-selected
dense
lazy-rules
outlined
v-model="roundId"
:options="roundOtion"
label="เลือกรอบ"
option-value="id"
option-label="name"
emit-value
map-options
@update:model-value="onUpdateFilter"
@filter="(inputValue:string,doneFn:Function) =>
filterSelector(inputValue, doneFn,'round') "
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
ไมอม
</q-item-section>
</q-item>
</template>
</q-select>
<q-select
v-else
class="bg-white"
use-input
fill-input
hide-selected
dense
lazy-rules
outlined
v-model="roundId2"
:options="roundOtion2"
label="เลือกรอบ"
option-value="id"
option-label="name"
emit-value
map-options
@update:model-value="onUpdateFilter"
@filter="(inputValue:string,doneFn:Function) =>
filterSelector(inputValue, doneFn,'round') "
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
ไมอม
</q-item-section>
</q-item>
</template>
</q-select>
</div>
</div> </div>
</q-card> <q-space />
</div> <div>
<q-btn
flat
round
:loading="isLoadPDF"
:disable="!detailReport"
color="primary"
icon="download"
v-if="checkPermission($route)?.attrIsGet"
>
<q-menu>
<q-list style="min-width: 150px">
<q-item
clickable
v-close-popup
@click="
downloadReport(
'pdf',
!['report1', 'report2', 'report3'].includes(
typeReport.val
)
? 'xlsx'
: 'docx'
)
"
>
<q-item-section avatar
><q-icon color="red" name="mdi-file-pdf"
/></q-item-section>
<q-item-section>ไฟล .pdf</q-item-section>
</q-item>
<q-item
v-if="
typeReport.val == 'report1' ||
typeReport.val == 'report2' ||
typeReport.val == 'report3'
"
clickable
v-close-popup
@click="downloadReport('docx')"
>
<q-item-section avatar
><q-icon color="blue" name="mdi-file-word"
/></q-item-section>
<q-item-section>ไฟล .docx</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="downloadReport('xlsx')"
v-if="typeReport.val !== 'report1'"
>
<q-item-section avatar
><q-icon color="green" name="mdi-file-excel"
/></q-item-section>
<q-item-section>ไฟล .xlsx</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</div>
</div>
</q-card>
</div> </div>
</q-card> <div class="col-12">
<q-card flat bordered class="col-12"> <q-card class="q-pa-sm">
<q-card-section :horizontal="$q.screen.gt.sm"> <div class="row q-col-gutter-sm">
<q-card-section class="col-lg-3 col-md-4 col-xs-12 q-gutter-sm"> <div class="col">
<div class="col"> <q-card flat bordered>
<q-input dense outlined v-model="filterTree" label="ค้นหา"> <q-card-section
<template v-slot:append> bordered
<q-icon name="search" /> class="bg-primary text-subtitle2 text-white q-pa-sm row items-center"
</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> <q-icon name="mdi-filter" class="q-mr-xs" />
<div class="text-weight-medium"> วกรอง
{{ prop.node.orgTreeName }} <q-space />
</div> <q-btn
<div class="text-weight-light text-grey-8"> dense
{{ prop.node.orgCode == null ? null : prop.node.orgCode }} size="12px"
{{ class="q-px-sm"
prop.node.orgTreeShortName == null rounded
? null flat
: prop.node.orgTreeShortName 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 />
<q-card-section v-if="isLoadStructureTree">
<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
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
class="bg-white"
hide-bottom-space
dense
lazy-rules
borderless
v-model="typeReport"
:label="`${'รายงาน'}`"
map-options
:options="employeeClass == 'officer' ? optionReport.filter((item:any)=>item.id !== '7'):optionReport.filter((item:any)=>item.id !== '4')"
option-value="id"
option-label="name"
/>
</div>
<q-separator />
<div class="col-12 q-px-sm">
<q-select
v-if="
typeReport &&
(typeReport.val == 'report1' ||
typeReport.val == 'report2' ||
typeReport.val == 'report3')
"
class="bg-white"
use-input
fill-input
hide-selected
dense
borderless
lazy-rules
v-model="roundId"
:options="roundOtion"
label="เลือกรอบ"
option-value="id"
option-label="name"
emit-value
map-options
@filter="(inputValue:string,doneFn:Function) =>
filterSelector(inputValue, doneFn,'round') "
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
ไมอม
</q-item-section>
</q-item>
</template>
</q-select>
<q-select
v-else
class="bg-white"
use-input
fill-input
hide-selected
dense
borderless
lazy-rules
v-model="roundId2"
:options="roundOtion2"
label="เลือกรอบ"
option-value="id"
option-label="name"
emit-value
map-options
@filter="(inputValue:string,doneFn:Function) =>
filterSelector(inputValue, doneFn,'round') "
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
ไมอม
</q-item-section>
</q-item>
</template>
</q-select>
</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="
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
horizontal
style="
height: 75vh;
border: 1px solid rgb(210, 210, 210);
border-radius: 5px;
width: 100%;
"
before-class="overflow-hidden disable"
separator-class="bg-white disabled"
>
<template v-slot:before>
<div class="q-px-sm">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div> </div>
</div> </div>
</q-item> </template>
</template> <template v-slot:after>
</q-tree> <div class="q-pa-md">
<LoadView v-if="isLoadPDF" />
<VuePDF
v-else-if="!isLoadPDF && pdfSrc"
ref="vuePDFRef"
:pdf="pdfSrc"
:page="page"
fit-parent
/>
</div>
</template>
<template v-slot:default>
<div class="q-pa-md">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
</q-splitter>
</div>
</div> </div>
</q-card-section> </q-card>
</div>
<q-separator :vertical="$q.screen.gt.xs" /> </div>
</q-form>
<q-card-section class="col-lg-9 col-md-8 col-xs-12 scroll">
<q-splitter
v-model="splitterModel"
horizontal
style="
height: 65vh;
border: 1px solid rgb(210, 210, 210);
border-radius: 5px;
"
before-class="overflow-hidden disable"
separator-class="bg-white disabled"
>
<template v-slot:before>
<div class="q-px-sm">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
<template v-slot:after>
<div class="q-pa-md">
<LoadView v-if="isLoadPDF" />
<VuePDF
v-else
ref="vuePDFRef"
:pdf="pdfSrc"
:page="page"
fit-parent
/>
</div>
</template>
<template v-slot:default>
<div class="q-pa-md">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
</q-splitter>
</q-card-section>
</q-card-section>
</q-card>
</div>
</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>

File diff suppressed because it is too large Load diff

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() {
node.value = await fetchStructureTree(route.meta.Key as string, true); try {
isLoadStructureTree.value = 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,383 +300,453 @@ 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">
<q-select <div class="row q-col-gutter-sm no-wrap">
outlined <div class="col-lg-2 col-md-3 col-sm-3 col-xs-12">
dense <q-select
v-model="employeeClass" outlined
:options="employeeClassOption" dense
label="สภานภาพ" v-model="employeeClass"
emit-value :options="employeeClassOption"
map-options label="สภานภาพ"
option-label="name" emit-value
option-value="id" map-options
style="width: 230px" option-label="name"
@update:model-value="getType" option-value="id"
> @update:model-value="getType"
</q-select> >
</q-select>
<q-space /> </div>
<q-btn <q-space />
:disable="!nodeId" <div>
flat <q-btn
round :disable="!detailReport"
color="primary" flat
icon="download" round
v-if="checkPermission($route)?.attrIsGet" color="primary"
:loading="isLoadPDF" icon="download"
> v-if="checkPermission($route)?.attrIsGet"
<q-menu> :loading="isLoadPDF"
<q-list style="min-width: 150px"> >
<q-item <q-menu>
clickable <q-list style="min-width: 150px">
v-close-popup <q-item
@click=" clickable
genReportXLSX( v-close-popup
detailReport, @click="
`รายชื่อที่กระทำความผิดทางวินัย_${ genReportXLSX(
employeeClass == 'officer' detailReport,
? 'ข้าราชการ กทม. สามัญ' `รายชื่อที่กระทำความผิดทางวินัย_${
: 'ลูกจ้างประจำ กทม.' employeeClass == 'officer'
}_${year + 543}`, ? 'ข้าราชการ กทม. สามัญ'
'pdf' : 'ลูกจ้างประจำ กทม.'
) }_${year + 543}`,
" 'pdf'
> )
<q-item-section avatar "
><q-icon color="red" name="mdi-file-pdf"
/></q-item-section>
<q-item-section>ไฟล .pdf</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="
genReportXLSX(
detailReport,
`รายชื่อที่กระทำความผิดทางวินัย_${
employeeClass == 'officer'
? 'ข้าราชการ กทม. สามัญ'
: 'ลูกจ้างประจำ กทม.'
}_${year + 543}`,
'xlsx'
)
"
>
<q-item-section avatar
><q-icon color="green" name="mdi-file-excel"
/></q-item-section>
<q-item-section>ไฟล .xlsx</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</div>
<div class="row col-12">
<q-card bordered class="col-12 filter-card q-pa-sm">
<div class="row col-12 q-col-gutter-sm">
<div class="col-md-4 col-xs-12">
<datepicker
v-model="year"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
@update:model-value="onUpdateFilter"
>
<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-item-section avatar
<q-icon ><q-icon color="red" name="mdi-file-pdf"
name="event" /></q-item-section>
class="cursor-pointer" <q-item-section>ไฟล .pdf</q-item-section>
style="color: var(--q-primary)" </q-item>
> <q-item
</q-icon> clickable
v-close-popup
@click="
genReportXLSX(
detailReport,
`รายชื่อที่กระทำความผิดทางวินัย_${
employeeClass == 'officer'
? 'ข้าราชการ กทม. สามัญ'
: 'ลูกจ้างประจำ กทม.'
}_${year + 543}`,
'xlsx'
)
"
>
<q-item-section avatar
><q-icon color="green" name="mdi-file-excel"
/></q-item-section>
<q-item-section>ไฟล .xlsx</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</div>
</div>
</q-card>
</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> </template>
</q-input> <q-separator />
</template>
</datepicker> <q-card-section v-if="isLoadStructureTree">
</div> <LoadView />
<div class="col-md-4 col-xs-12"> </q-card-section>
<q-select <q-card-section v-else>
class="bg-white" <q-input
hide-bottom-space dense
outlined outlined
dense v-model="filterTree"
lazy-rules label="ค้นหา"
borderless >
v-model="offenseDetail" <template v-slot:append>
:label="`${'ลักษณะความผิด'}`" <q-icon
emit-value v-if="filterTree !== ''"
map-options name="clear"
:options="offenseDetailOp" class="cursor-pointer"
option-value="id" @click="filterTree = ''"
option-label="name" />
@update:model-value="onUpdateFilter" <q-icon v-else name="search" color="grey-5" />
/> </template>
</div> </q-input>
<div class="col-md-4 col-xs-12"> <q-tree
<q-select dense
class="bg-white" :nodes="node"
hide-bottom-space node-key="orgTreeId"
outlined label-key="labelName"
dense v-model:expanded="expanded"
lazy-rules :filter="filterTree.trim()"
borderless no-results-label="ไม่พบข้อมูลที่ค้นหา"
v-model="disciplinaryFaultLevel" no-nodes-label="ไม่มีข้อมูล"
:label="`${'ระดับความผิด'}`" class="tree-container-report q-mt-sm"
emit-value >
map-options <template v-slot:default-header="prop">
:options="disciplinaryFaultLevelOp" <q-item
option-value="id" @click.stop="onSelectedNode(prop.node)"
option-label="name" :active="nodeId === prop.node.orgTreeId"
@update:model-value="onUpdateFilter" clickable
/> active-class="my-list-link text-primary text-weight-medium"
</div> class="row col-12 items-center text-dark q-py-xs q-pl-sm rounded-borders my-list"
<div class="col-md-4 col-xs-12"> >
<q-select <div>
class="bg-white" <div class="text-weight-medium">
hide-bottom-space {{ prop.node.orgTreeName }}
outlined </div>
dense <div class="text-weight-light text-grey-8">
lazy-rules {{
borderless prop.node.orgCode == null
v-model="status" ? null
:label="`${'สถานะของเรื่อง'}`" : prop.node.orgCode
emit-value }}
map-options {{
:options="statusOp" prop.node.orgTreeShortName == null
option-value="id" ? null
option-label="name" : prop.node.orgTreeShortName
@update:model-value="onUpdateFilter" }}
/> </div>
</div> </div>
<div class="col-md-4 col-xs-12"> </q-item>
<q-select </template>
class="bg-white" </q-tree>
hide-bottom-space </q-card-section>
outlined </q-expansion-item>
dense </div>
lazy-rules <q-separator />
borderless <div class="col-12 q-px-sm q-pb-xs">
hide-selected <datepicker
v-model="posType" v-model="year"
:label="`${ :locale="'th'"
employeeClass == 'officer' ? 'ประเภทตำแหน่ง' : 'กลุ่มงาน' autoApply
}`" year-picker
emit-value :enableTimePicker="false"
map-options >
:options="posTypeOp" <template #year="{ year }">{{ year + 543 }}</template>
option-value="id" <template #year-overlay-value="{ value }">{{
option-label="posTypeName" parseInt(value + 543)
use-input }}</template>
fill-input <template #trigger>
@update:model-value="(value:string)=>(posLevel = '',onUpdateFilter(),updateLevel(value))" <q-input
@filter="(inputValue:any, doneFn:Function) => 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-separator />
<div class="col-12 q-px-sm">
<q-select
class="bg-white"
hide-bottom-space
dense
lazy-rules
borderless
v-model="offenseDetail"
:label="`${'ลักษณะความผิด'}`"
emit-value
map-options
:options="offenseDetailOp"
option-value="id"
option-label="name"
/>
</div>
<q-separator />
<div class="col-12 q-px-sm">
<q-select
class="bg-white"
hide-bottom-space
dense
lazy-rules
borderless
v-model="disciplinaryFaultLevel"
:label="`${'ระดับความผิด'}`"
emit-value
map-options
:options="disciplinaryFaultLevelOp"
option-value="id"
option-label="name"
/>
</div>
<q-separator />
<div class="col-12 q-px-sm">
<q-select
class="bg-white"
hide-bottom-space
dense
lazy-rules
borderless
v-model="status"
:label="`${'สถานะของเรื่อง'}`"
emit-value
map-options
:options="statusOp"
option-value="id"
option-label="name"
/>
</div>
<q-separator />
<div class="col-12 q-px-sm">
<q-select
class="bg-white"
hide-bottom-space
dense
lazy-rules
borderless
hide-selected
v-model="posType"
:label="`${
employeeClass == 'officer'
? 'ประเภทตำแหน่ง'
: 'กลุ่มงาน'
}`"
emit-value
map-options
:options="posTypeOp"
option-value="id"
option-label="posTypeName"
use-input
fill-input
@update:model-value="(value:string)=>(posLevel = '',onUpdateFilter(),updateLevel(value))"
@filter="(inputValue:any, doneFn:Function) =>
filterOption(inputValue, doneFn,'type') filterOption(inputValue, doneFn,'type')
" "
/> />
</div> </div>
<div class="col-md-4 col-xs-12"> <q-separator />
<q-select <div class="col-12 q-px-sm">
class="bg-white" <q-select
hide-bottom-space class="bg-white"
outlined hide-bottom-space
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 : 'ระดับชั้นงาน'
map-options }`"
:options="posLevelOp" emit-value
option-value="id" map-options
option-label="posLevelName" :options="posLevelOp"
use-input option-value="id"
fill-input option-label="posLevelName"
@update:model-value="onUpdateFilter" use-input
@filter="(inputValue:any, doneFn:Function) => fill-input
@filter="(inputValue:any, doneFn:Function) =>
filterOption(inputValue, doneFn,'level') filterOption(inputValue, doneFn,'level')
" "
/> />
</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="nodeId == ''"
/>
</q-card-actions>
</q-card>
</div> </div>
</q-card> <div class="col-lg-9 col-md-9 col-sm-9 col-xs-12 col-xs-12 flex">
</div> <q-splitter
horizontal
style="
height: 75vh;
border: 1px solid rgb(210, 210, 210);
border-radius: 5px;
width: 100%;
"
before-class="overflow-hidden disable"
separator-class="bg-white disabled"
>
<template v-slot:before>
<div class="q-px-sm">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
<template v-slot:after>
<div class="q-pa-md">
<LoadView v-if="isLoadPDF" />
<VuePDF
v-else-if="!isLoadPDF && pdfSrc"
ref="vuePDFRef"
:pdf="pdfSrc"
:page="page"
fit-parent
/>
</div>
</template>
<template v-slot:default>
<div class="q-pa-md">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
</q-splitter>
</div>
</div>
</q-card>
</div> </div>
</q-card> </div>
<q-card flat bordered class="col-12"> </q-form>
<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-separator :vertical="$q.screen.gt.xs" />
<q-card-section class="col-lg-9 col-md-8 col-xs-12 scroll">
<q-splitter
v-model="splitterModel"
horizontal
style="
height: 65vh;
border: 1px solid rgb(210, 210, 210);
border-radius: 5px;
"
before-class="overflow-hidden disable"
separator-class="bg-white disabled"
>
<template v-slot:before>
<div class="q-px-sm">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
<template v-slot:after>
<div class="q-pa-md">
<LoadView v-if="isLoadPDF" />
<VuePDF
v-else
ref="vuePDFRef"
:pdf="pdfSrc"
:page="page"
fit-parent
/>
</div>
</template>
<template v-slot:default>
<div class="q-pa-md">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
</q-splitter>
</q-card-section>
</q-card-section>
</q-card>
</div>
</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,77 +508,353 @@ 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">
รายงานประเมนผลการปฏราชการระดบบคคล
</div>
<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
:readonly="!checkPermission($route)?.attrIsGet"
dense
style="min-width: 550px"
hide-bottom-space
outlined
option-label="name"
option-value="id"
emit-value
map-options
v-model="typeReport"
:options="listReport"
label="รายงาน"
use-input
hide-selected
fill-input
@update:model-value="changOption"
@filter="(inputValue: string,
doneFn: Function) => filterSelector(inputValue, doneFn ,'typereport')"
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
ไมอม
</q-item-section>
</q-item>
</template></q-select
>
<q-space />
<div>
<q-btn
v-if="checkPermission($route)?.attrIsGet"
flat
round
color="primary"
icon="download"
:disable="!dataDownload"
>
<q-menu>
<q-list style="min-width: 150px">
<q-item
clickable
v-close-popup
@click="downloadReport(dataDownload, 'pdf')"
>
<q-item-section avatar
><q-icon color="red" name="mdi-file-pdf"
/></q-item-section>
<q-item-section>ไฟล .pdf</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="downloadReport(dataDownload, 'docx')"
>
<q-item-section avatar
><q-icon color="blue" name="mdi-file-word"
/></q-item-section>
<q-item-section>ไฟล .docx</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</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="row"
v-if="
typeReport === 'KPI1' ||
typeReport === 'KPI2' ||
typeReport === 'KPI3' ||
typeReport === 'KPI7' ||
typeReport === 'KPI8' ||
typeReport === 'KPI9'
"
>
<div class="col-12 q-px-sm">
<datepicker
menu-class-name="modalfix"
v-model="year"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
@update:model-value="fetchRoundOption()"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
borderless
hide-bottom-space
:model-value="!!year ? year + 543 : null"
:label="`${'ปีงบประมาณ'}`"
class="bg-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 class="col-12"><q-separator/></div>
<div class="col-12 q-px-sm">
<q-select
ref="roundRef"
class="bg-white"
v-model="round"
label="รอบการประเมิน"
dense
borderless
hide-bottom-space
option-label="name"
option-value="id"
:options="roundOp"
style="min-width: 150px"
emit-value
map-options
:disable="roundOp.length === 0"
@update:model-value="changOption(typeReport)"
lazy-rules
:rules="
typeReport === 'KPI8'
? [(val:string) => !!val || 'กรุณาเลือกรอบการประเมิน']
: []
"
/>
</div>
<div class="col-12"><q-separator/></div>
<div class="col-12 q-pb-xs q-px-sm" v-if="typeReport !== 'KPI8'">
<q-select
class="bg-white"
:disable="roundOp.length === 0"
dense
borderless
hide-selected
fill-input
hide-bottom-space
option-label="name"
option-value="id"
emit-value
map-options
v-model="organization"
:options="organizationOps"
label="หน่วยงาน"
use-input
@update:model-value="changOption(typeReport)"
@filter="(inputValue: string,
doneFn: Function) => filterSelector(inputValue, doneFn,'organization' )"
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
ไมอม
</q-item-section>
</q-item>
</template></q-select
>
</div>
</div>
<div
class="row q-pa-sm q-col-gutter-xs"
v-if="
typeReport === 'KPI4' ||
typeReport === 'KPI5' ||
typeReport === 'KPI6' ||
typeReport === 'KPI8'
"
>
<div class="col-12">
<q-input
label="ชื่อ-นามสกุล"
dense
disable
:model-value="
fullNameShow ? fullNameShow : 'กรุณาเลือกรายชื่อ'
"
>
</q-input>
</div>
<div class="col-12">
<q-btn
class="full-width"
color="primary"
label="เลือกรายชื่อ"
@click="onClickModal"
:disable="roundOp.length === 0 && typeReport === 'KPI8'"
/>
</div>
</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
horizontal
style="
height: 75vh;
border: 1px solid rgb(210, 210, 210);
border-radius: 5px;
width: 100%;
"
before-class="overflow-hidden disable"
separator-class="bg-white disabled"
>
<template v-slot:before>
<div class="q-px-sm">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
<template v-slot:after>
<div class="q-pa-md">
<LoadView v-if="isLoadPDF" />
<VuePDF
v-else-if="!isLoadPDF && pdfSrc"
ref="vuePDFRef"
:pdf="pdfSrc"
:page="page"
fit-parent
/>
</div>
</template>
<template v-slot:default>
<div class="q-pa-md">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
</q-splitter>
</div>
</div>
</q-card>
</div>
</div>
</q-form>
<q-card flat bordered> <q-card flat bordered>
<q-card-section class="q-gutter-y-sm"> <q-card-section class="q-gutter-y-sm">
<q-toolbar style="padding: 0"> <q-toolbar style="padding: 0">
<div class="row q-gutter-sm"> <div class="row q-gutter-sm"></div>
<q-select
:readonly="!checkPermission($route)?.attrIsGet"
dense
style="min-width: 550px"
hide-bottom-space
outlined
option-label="name"
option-value="id"
emit-value
map-options
v-model="typeReport"
:options="listReport"
label="รายงาน"
use-input
hide-selected
fill-input
@update:model-value="changOption"
@filter="(inputValue: string,
doneFn: Function) => filterSelector(inputValue, doneFn ,'typereport')"
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey"> ไมอม </q-item-section>
</q-item>
</template></q-select
>
</div>
<q-space /> <q-space />
<q-btn
v-if="checkPermission($route)?.attrIsGet"
flat
round
color="primary"
icon="download"
:disable="!dataDownload"
>
<q-menu>
<q-list style="min-width: 150px">
<q-item
clickable
v-close-popup
@click="downloadReport(dataDownload, 'pdf')"
>
<q-item-section avatar
><q-icon color="red" name="mdi-file-pdf"
/></q-item-section>
<q-item-section>ไฟล .pdf</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="downloadReport(dataDownload, 'docx')"
>
<q-item-section avatar
><q-icon color="blue" name="mdi-file-word"
/></q-item-section>
<q-item-section>ไฟล .docx</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</q-toolbar> </q-toolbar>
<q-toolbar <q-toolbar
class="q-pa-sm bg-grey-2" class="q-pa-sm bg-grey-2"
style="padding: 0; border-radius: 5px" style="padding: 0; border-radius: 5px"
@ -577,94 +870,7 @@ onMounted(() => {
typeReport === 'KPI8' || typeReport === 'KPI8' ||
typeReport === 'KPI9' typeReport === 'KPI9'
" "
> ></div>
<datepicker
menu-class-name="modalfix"
v-model="year"
style="width: 150px"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
@update:model-value="fetchRoundOption()"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
outlined
hide-bottom-space
:model-value="!!year ? year + 543 : null"
:label="`${'ปีงบประมาณ'}`"
class="bg-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-select
ref="roundRef"
class="bg-white"
v-model="round"
outlined
label="รอบการประเมิน"
dense
hide-bottom-space
option-label="name"
option-value="id"
:options="roundOp"
style="min-width: 150px"
emit-value
map-options
:disable="roundOp.length === 0"
@update:model-value="changOption(typeReport)"
lazy-rules
:rules="
typeReport === 'KPI8'
? [(val:string) => !!val || 'กรุณาเลือกรอบการประเมิน']
: []
"
/>
<q-select
v-if="typeReport !== 'KPI8'"
class="bg-white"
:disable="roundOp.length === 0"
style="min-width: 100px"
dense
hide-selected
fill-input
hide-bottom-space
outlined
option-label="name"
option-value="id"
emit-value
map-options
v-model="organization"
:options="organizationOps"
label="หน่วยงาน"
use-input
@update:model-value="changOption(typeReport)"
@filter="(inputValue: string,
doneFn: Function) => filterSelector(inputValue, doneFn,'organization' )"
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey"> ไมอม </q-item-section>
</q-item>
</template></q-select
>
</div>
<div <div
class="q-pa-sm" class="q-pa-sm"
@ -674,102 +880,8 @@ onMounted(() => {
typeReport === 'KPI6' || typeReport === 'KPI6' ||
typeReport === 'KPI8' typeReport === 'KPI8'
" "
> ></div>
<q-btn
color="primary"
label="เลือกรายชื่อ"
@click="onClickModal"
:disable="roundOp.length === 0 && typeReport === 'KPI8'"
/>
</div>
</q-toolbar> </q-toolbar>
<q-splitter
v-model="splitterModel"
horizontal
style="
height: 70vh;
border: 1px solid rgb(210, 210, 210);
border-radius: 5px;
"
before-class="overflow-hidden disable"
separator-class="bg-white disabled"
>
<template v-slot:before v-if="pdfSrc">
<div class="q-px-sm">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
<template v-slot:after v-if="pdfSrc">
<div class="q-pa-md">
<VuePDF ref="vuePDFRef" :pdf="pdfSrc" :page="page" fit-parent />
</div>
</template>
<template v-slot:default v-if="pdfSrc">
<div class="q-pa-md">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
</q-splitter>
</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,262 +223,302 @@ 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 <q-space />
class="bg-white" <div>
hide-bottom-space <q-btn
outlined :disable="
dense !typeReport ||
lazy-rules !detailReport ||
borderless ((typeReport == 'main' || typeReport == 'report3') &&
v-model="typeReport" !organization)
:label="`${'รายงาน'}`" "
emit-value :loading="loadingBtn"
map-options flat
:options="optionReport" round
option-value="id" color="primary"
option-label="name" icon="download"
@update:model-value="getReport" v-if="checkPermission($route)?.attrIsGet"
/>
</div>
<q-space />
<div>
<q-btn
:disable="
!typeReport ||
((typeReport == 'main' || typeReport == 'report3') &&
!organization)
"
:loading="loadingBtn"
flat
round
color="primary"
icon="download"
v-if="checkPermission($route)?.attrIsGet"
>
<q-menu>
<q-list style="min-width: 150px">
<q-item
clickable
v-close-popup
@click="
genReportXLSX(
detailReport,
`รายการโครงการ/หลักสูตรการฝึกอบรมที่หน่วยงานของกรุงเทพมหานครเป็นผู้จัด`,
'pdf'
)
"
>
<q-item-section avatar
><q-icon color="red" name="mdi-file-pdf"
/></q-item-section>
<q-item-section>ไฟล .pdf</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="
genReportXLSX(
detailReport,
`รายการโครงการ/หลักสูตรการฝึกอบรมที่หน่วยงานของกรุงเทพมหานครเป็นผู้จัด`,
'xlsx'
)
"
>
<q-item-section avatar
><q-icon color="green" name="mdi-file-excel"
/></q-item-section>
<q-item-section>ไฟล .xlsx</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</div>
</div>
<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
class="col-md-3 col-xs-6"
v-if="typeReport == 'main' || typeReport == 'report3'"
> >
<q-select <q-menu>
class="bg-white" <q-list style="min-width: 150px">
style="min-width: 100px" <q-item
dense clickable
hide-selected v-close-popup
fill-input @click="
hide-bottom-space genReportXLSX(
outlined detailReport,
option-label="name" `รายการโครงการ/หลักสูตรการฝึกอบรมที่หน่วยงานของกรุงเทพมหานครเป็นผู้จัด`,
option-value="id" 'pdf'
emit-value )
map-options "
v-model="organization" >
:options="organizationOps" <q-item-section avatar
label="หน่วยงาน" ><q-icon color="red" name="mdi-file-pdf"
use-input /></q-item-section>
@update:model-value="getReport" <q-item-section>ไฟล .pdf</q-item-section>
@filter="(inputValue: string, </q-item>
<q-item
clickable
v-close-popup
@click="
genReportXLSX(
detailReport,
`รายการโครงการ/หลักสูตรการฝึกอบรมที่หน่วยงานของกรุงเทพมหานครเป็นผู้จัด`,
'xlsx'
)
"
>
<q-item-section avatar
><q-icon color="green" name="mdi-file-excel"
/></q-item-section>
<q-item-section>ไฟล .xlsx</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</div>
</div>
</q-card>
</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
class="col-12 q-px-sm"
v-if="typeReport == 'main' || typeReport == 'report3'"
>
<q-select
class="bg-white"
style="min-width: 100px"
dense
hide-selected
fill-input
hide-bottom-space
borderless
option-label="name"
option-value="id"
emit-value
map-options
v-model="organization"
:options="organizationOps"
label="หน่วยงาน"
use-input
@filter="(inputValue: string,
doneFn: Function) => filterSelector(inputValue, doneFn)" doneFn: Function) => filterSelector(inputValue, doneFn)"
> >
<template v-slot:no-option> <template v-slot:no-option>
<q-item> <q-item>
<q-item-section class="text-grey"> <q-item-section class="text-grey">
ไมอม ไมอม
</q-item-section> </q-item-section>
</q-item> </q-item>
</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>
</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>
</div> </div>
</q-card> <div class="col-lg-9 col-md-9 col-sm-9 col-xs-12 col-xs-12 flex">
</div> <q-splitter
horizontal
style="
height: 75vh;
border: 1px solid rgb(210, 210, 210);
border-radius: 5px;
width: 100%;
"
before-class="overflow-hidden disable"
separator-class="bg-white disabled"
>
<template v-slot:before>
<div class="q-px-sm">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
<template v-slot:after>
<div class="q-pa-md">
<LoadView v-if="loadingBtn" />
<VuePDF
v-else-if="!loadingBtn && pdfSrc"
ref="vuePDFRef"
:pdf="pdfSrc"
:page="page"
fit-parent
/>
</div>
</template>
<template v-slot:default>
<div class="q-pa-md">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
</q-splitter>
</div>
</div>
</q-card>
</div> </div>
</q-card> </div>
<q-card flat bordered class="col-12"> </q-form>
<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
v-model="splitterModel"
horizontal
style="
height: 65vh;
border: 1px solid rgb(210, 210, 210);
border-radius: 5px;
"
before-class="overflow-hidden disable"
separator-class="bg-white disabled"
>
<template v-slot:before>
<div class="q-px-sm">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
<template v-slot:after>
<div class="q-pa-md">
<LoadView v-if="loadingBtn" />
<VuePDF
v-else
ref="vuePDFRef"
:pdf="pdfSrc"
:page="page"
fit-parent
/>
</div>
</template>
<template v-slot:default>
<div class="q-pa-md">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
</q-splitter>
</q-card-section>
</q-card-section>
</q-card>
</div>
</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,14 +40,22 @@ 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() {
const dataTree = await fetchStructureTree("SYS_EVA_REQ", true); try {
if (dataTree) { isLoadStructureTree.value = true;
const data = dataTree.map((item: DataStructureTree) => ({ const dataTree = await fetchStructureTree("SYS_EVA_REQ", true);
...item, if (dataTree) {
children: null, const data = dataTree.map((item: DataStructureTree) => ({
})); ...item,
node.value = data; children: null,
}));
node.value = data;
}
} catch (err) {
messageError($q, err);
} finally {
isLoadStructureTree.value = false;
} }
} }
@ -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,235 +163,314 @@ 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 <q-space />
menu-class-name="modalfix" <div>
v-model="year" <q-btn
:locale="'th'" :loading="loadingBtn"
autoApply :disable="!detailReport"
year-picker flat
:enableTimePicker="false" round
@update:model-value="fetchReport" color="primary"
> icon="download"
<template #year="{ year }">{{ year + 543 }}</template> v-if="checkPermission($route)?.attrIsGet"
<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-menu>
<q-icon <q-list style="min-width: 150px">
name="event" <q-item
class="cursor-pointer" clickable
style="color: var(--q-primary)" v-close-popup
> @click.stop.pervent="onDownloadFile('pdf')"
</q-icon> >
</template> <q-item-section avatar
</q-input> ><q-icon color="red" name="mdi-file-pdf"
</template> /></q-item-section>
</datepicker> <q-item-section>ไฟล .pdf</q-item-section>
</q-item>
<q-space /> <q-item
<q-btn clickable
:loading="loadingBtn" v-close-popup
:disable="!nodeId" @click.stop.pervent="onDownloadFile('xlsx')"
flat >
round <q-item-section avatar
color="primary" ><q-icon color="green" name="mdi-file-excel"
icon="download" /></q-item-section>
v-if="checkPermission($route)?.attrIsGet" <q-item-section>ไฟล .xlsx</q-item-section>
> </q-item>
<q-menu> </q-list>
<q-list style="min-width: 150px"> </q-menu>
<q-item </q-btn>
clickable </div>
v-close-popup </div>
@click.stop.pervent="onDownloadFile('pdf')" </q-card>
>
<q-item-section avatar
><q-icon color="red" name="mdi-file-pdf"
/></q-item-section>
<q-item-section>ไฟล .pdf</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click.stop.pervent="onDownloadFile('xlsx')"
>
<q-item-section avatar
><q-icon color="green" name="mdi-file-excel"
/></q-item-section>
<q-item-section>ไฟล .xlsx</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</div>
</div> </div>
</q-card> <div class="col-12">
<q-card class="q-pa-sm">
<q-card flat bordered class="col-12"> <div class="row q-col-gutter-sm">
<q-card-section :horizontal="$q.screen.gt.sm"> <div class="col">
<q-card-section class="col-lg-3 col-md-4 col-xs-12 q-gutter-sm"> <q-card flat bordered>
<div class="col"> <q-card-section
<q-input dense outlined v-model="filterTree" label="ค้นหา"> bordered
<template v-slot:append> class="bg-primary text-subtitle2 text-white q-pa-sm row items-center"
<q-icon name="search" />
</template>
</q-input>
</div>
<div class="bg-white tree-containerevaluate q-pa-xs">
<q-tree
dense
:nodes="node"
node-key="orgTreeId"
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.orgTreeId, prop.node.orgTreeName)
"
: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> <q-icon name="mdi-filter" class="q-mr-xs" />
<div class="text-weight-medium"> วกรอง
{{ prop.node.orgTreeName }} <q-space />
</div> <q-btn
<div class="text-weight-light text-grey-8"> dense
{{ prop.node.orgCode == null ? null : prop.node.orgCode }} size="12px"
{{ class="q-px-sm"
prop.node.orgTreeShortName == null rounded
? null flat
: prop.node.orgTreeShortName 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 />
<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
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
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>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn
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
horizontal
style="
height: 75vh;
border: 1px solid rgb(210, 210, 210);
border-radius: 5px;
width: 100%;
"
before-class="overflow-hidden disable"
separator-class="bg-white disabled"
>
<template v-slot:before>
<div class="q-px-sm">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div> </div>
</div> </div>
</q-item> </template>
</template> <template v-slot:after>
</q-tree> <div class="q-pa-md">
<LoadView v-if="loadingBtn" />
<VuePDF
v-else-if="!loadingBtn && pdfSrc"
ref="vuePDFRef"
:pdf="pdfSrc"
:page="page"
fit-parent
/>
</div>
</template>
<template v-slot:default>
<div class="q-pa-md">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
</q-splitter>
</div>
</div> </div>
</q-card-section> </q-card>
</div>
<q-separator :vertical="$q.screen.gt.xs" /> </div>
</q-form>
<q-card-section class="col-lg-9 col-md-8 col-xs-12 scroll">
<q-splitter
horizontal
style="
height: 75vh;
border: 1px solid rgb(210, 210, 210);
border-radius: 5px;
"
before-class="overflow-hidden disable"
separator-class="bg-white disabled"
>
<template v-slot:before>
<div class="q-px-sm">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
<template v-slot:after>
<div class="q-pa-md">
<LoadView v-if="loadingBtn" />
<VuePDF
v-else
ref="vuePDFRef"
:pdf="pdfSrc"
:page="page"
fit-parent
/>
</div>
</template>
<template v-slot:default>
<div class="q-pa-md">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
</q-splitter>
</q-card-section>
</q-card-section>
</q-card>
</div>
</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 {
const data = res.data.result.map((item: DataStructureTree) => ({ isLoadStructureTree.value = true;
...item, const res = await http.get(
children: null, config.API.activeOrganizationRootById(revisionId)
labelName: `${item.orgRootName} ${item.orgRootCode} ${item.orgRootShortName}`, );
})); const data = res.data.result.map((item: DataStructureTree) => ({
node.value = data; ...item,
}) children: null,
.catch((err) => { labelName: `${item.orgRootName} ${item.orgRootCode} ${item.orgRootShortName}`,
messageError($q, err); }));
}); node.value = data;
} catch (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,323 +303,411 @@ 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>
<q-form greedy @submit.prevent @validation-success="fetchReportPDF">
<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">
<div class="col-lg-2 col-md-3 col-sm-3 col-xs-12">
<q-select
outlined
dense
v-model="employeeClass"
:options="employeeClassOption"
label="สภานภาพ"
emit-value
map-options
option-label="name"
option-value="id"
bg-color="white"
@update:model-value="(report = ''), fetchReportPDF"
>
</q-select>
</div>
<q-space />
<div>
<q-btn
:loading="isLoadFilePdf"
flat
round
color="primary"
icon="download"
:disable="!detailReport"
v-if="checkPermission($route)?.attrIsGet"
>
<q-menu>
<q-list style="min-width: 150px">
<q-item
clickable
v-close-popup
@click.stop.pervent="onDownloadFile('pdf')"
>
<q-item-section avatar
><q-icon color="red" name="mdi-file-pdf"
/></q-item-section>
<q-item-section>ไฟล .pdf</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click.stop.pervent="onDownloadFile('xlsx')"
>
<q-item-section avatar
><q-icon color="green" name="mdi-file-excel"
/></q-item-section>
<q-item-section>ไฟล .xlsx</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</div>
</div>
</q-card>
</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 />
<q-card-section v-if="isLoadStructureTree && checkId">
<LoadView />
</q-card-section>
<q-card-section
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
menu-class-name="modalfix"
v-model="year"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
@update:model-value="onChangeYear"
>
<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>
<q-separator />
<div class="col-12 q-px-sm">
<q-select
v-model="round"
label="รอบการขึ้นเงินเดือน"
dense
borderless
map-options
option-label="name"
option-value="id"
:options="roundOptions"
lazy-rules
hide-bottom-space
bg-color="white"
@update:model-value="report = ''"
>
</q-select>
</div>
<q-separator />
<div
class="col-12 q-px-sm"
v-if="employeeClass === 'officer'"
>
<q-select
v-model="group"
label="กลุ่ม"
dense
borderless
emit-value
map-options
option-label="name"
option-value="id"
:options="groupOptions"
lazy-rules
hide-bottom-space
bg-color="white"
/>
</div>
<q-separator v-if="employeeClass === 'officer'" />
<div class="col-12 q-px-sm">
<q-select
borderless
dense
v-model="report"
:options="reportOption"
label="รายงาน"
emit-value
map-options
option-label="name"
option-value="id"
class="bg-white"
/>
</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="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
horizontal
style="
height: 75vh;
border: 1px solid rgb(210, 210, 210);
border-radius: 5px;
width: 100%;
"
before-class="overflow-hidden disable"
separator-class="bg-white disabled"
>
<template v-slot:before>
<div class="q-px-sm">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
<template v-slot:after>
<div class="q-pa-md">
<LoadView v-if="isLoadFilePdf" />
<VuePDF
v-else
ref="vuePDFRef"
:pdf="pdfSrc"
:page="page"
fit-parent
/>
</div>
</template>
<template v-slot:default>
<div class="q-pa-md">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
</q-splitter>
</div>
</div>
</q-card>
</div>
</div>
</q-form>
<div class="q-pa-sm q-gutter-sm"> <div class="q-pa-sm q-gutter-sm">
<q-card flat bordered class="col-12"> <q-card flat bordered class="col-12">
<div class="row q-col-gutter-sm q-pa-sm"> <div class="row q-col-gutter-sm q-pa-sm">
<div class="row col-12">
<q-select
outlined
dense
v-model="employeeClass"
:options="employeeClassOption"
label="สภานภาพ"
emit-value
map-options
option-label="name"
option-value="id"
bg-color="white"
@update:model-value="(report = ''), fetchReportPDF"
>
</q-select>
<q-space />
<q-btn
:loading="isLoadFilePdf"
flat
round
color="primary"
icon="download"
:disable="report === '' || nodeId === ''"
v-if="checkPermission($route)?.attrIsGet"
>
<q-menu>
<q-list style="min-width: 150px">
<q-item
clickable
v-close-popup
@click.stop.pervent="onDownloadFile('pdf')"
>
<q-item-section avatar
><q-icon color="red" name="mdi-file-pdf"
/></q-item-section>
<q-item-section>ไฟล .pdf</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click.stop.pervent="onDownloadFile('xlsx')"
>
<q-item-section avatar
><q-icon color="green" name="mdi-file-excel"
/></q-item-section>
<q-item-section>ไฟล .xlsx</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</div>
<div class="row col-12"> <div class="row col-12">
<q-card bordered class="col-12 filter-card q-pa-sm"> <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-12 q-col-gutter-sm items-center">
<div class="row col-md-8 col-sx-12 q-col-gutter-sm"> <div class="row col-md-8 col-sx-12 q-col-gutter-sm">
<div class="col-md-3 col-xs-6"> <div class="col-md-3 col-xs-6"></div>
<datepicker <div class="col-md-3 col-xs-6"></div>
menu-class-name="modalfix"
v-model="year"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
@update:model-value="onChangeYear"
>
<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 class="col-md-3 col-xs-6">
<q-select
v-model="round"
label="รอบการขึ้นเงินเดือน"
dense
outlined
map-options
option-label="name"
option-value="id"
:options="roundOptions"
lazy-rules
hide-bottom-space
bg-color="white"
@update:model-value="(report = ''), fetchReportPDF"
>
</q-select>
</div>
<div <div class="col-md-3 col-xs-6"></div>
class="col-md-3 col-xs-6"
v-if="employeeClass === 'officer'"
>
<q-select
v-model="group"
label="กลุ่ม"
dense
outlined
emit-value
map-options
option-label="name"
option-value="id"
:options="groupOptions"
lazy-rules
hide-bottom-space
bg-color="white"
@update:model-value="fetchReportPDF"
/>
</div>
</div> </div>
<div class="row q-col-gutter-sm col-md-12"> <div class="row q-col-gutter-sm col-md-12">
<div class="col-md-4 col-xs-12"> <div class="col-md-4 col-xs-12"></div>
<q-select
outlined
dense
v-model="report"
:options="reportOption"
label="รายงาน"
emit-value
map-options
option-label="name"
option-value="id"
class="bg-white"
@update:model-value="fetchReportPDF"
/>
</div>
</div> </div>
</div> </div>
</q-card> </q-card>
</div> </div>
</div> </div>
</q-card> </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-separator :vertical="$q.screen.gt.xs" />
<q-card-section class="col-lg-9 col-md-8 col-xs-12 scroll">
<q-splitter
horizontal
style="
height: 63vh;
border: 1px solid rgb(210, 210, 210);
border-radius: 5px;
"
before-class="overflow-hidden disable"
separator-class="bg-white disabled"
>
<template v-slot:before>
<div class="q-px-sm">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
<template v-slot:after>
<div class="q-pa-md">
<LoadView v-if="isLoadFilePdf" />
<VuePDF
v-else
ref="vuePDFRef"
:pdf="pdfSrc"
:page="page"
fit-parent
/>
</div>
</template>
<template v-slot:default>
<div class="q-pa-md">
<div class="row items-start items-center">
<div class="col">
<q-btn
padding="xs"
icon="mdi-chevron-left"
color="grey-2"
text-color="grey-5"
size="md"
class="my-auto"
@click="backPage"
:disable="page == 1"
/>
</div>
<div class="col-12 col-md-auto">
<div class="q-pa-md flex">
หนาท {{ page }} จาก {{ numOfPages }}
</div>
</div>
<div class="col text-right">
<q-btn
padding="xs"
icon="mdi-chevron-right"
color="grey-2"
text-color="grey-5"
size="md"
@click="nextPage"
:disable="page === numOfPages"
/>
</div>
</div>
</div>
</template>
</q-splitter>
</q-card-section>
</q-card-section>
</q-card>
<q-card flat bordered class="col-12" v-else>
<div class="q-pa-sm">
<q-banner inline-actions rounded class="bg-grey-1 text-center">
ไมอม
</q-banner>
</div>
</q-card>
</div> </div>
</template> </template>
@ -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>