API รายการการประเมินผลการปฏิบัติราชการระดับบุคคล

This commit is contained in:
DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2024-04-26 09:34:17 +07:00
parent 266ad5ef7c
commit bdbf4582ee
7 changed files with 254 additions and 107 deletions

View file

@ -25,6 +25,8 @@ interface ArrayFileList {
fileName: string; fileName: string;
} }
const isReadonly = <boolean>(route.name === "KPIEditEvaluator" ? true : false);
const documentFile = ref<any>(null); const documentFile = ref<any>(null);
const fileList = ref<ArrayFileList[]>([]); const fileList = ref<ArrayFileList[]>([]);
@ -156,7 +158,7 @@ onMounted(() => {
</div> </div>
<div class="col-12"><q-separator /></div> <div class="col-12"><q-separator /></div>
<div class="row col-12 q-col-gutter-y-sm q-pa-sm"> <div class="row col-12 q-col-gutter-y-sm q-pa-sm">
<div class="col-12 row"> <div class="col-12 row" v-if="!isReadonly">
<q-file <q-file
for="inputFiles" for="inputFiles"
class="col-12" class="col-12"

View file

@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, watch } from "vue"; import { ref, reactive, watch } from "vue";
import { useQuasar } from "quasar"; import { useQuasar } from "quasar";
import { useRoute } from "vue-router";
import config from "@/app.config"; import config from "@/app.config";
import http from "@/plugins/http"; import http from "@/plugins/http";
@ -10,6 +11,7 @@ import { useCounterMixin } from "@/stores/mixin";
import { useKpiDataStore } from "@/modules/08_KPI/store"; import { useKpiDataStore } from "@/modules/08_KPI/store";
const $q = useQuasar(); const $q = useQuasar();
const route = useRoute();
const mixin = useCounterMixin(); const mixin = useCounterMixin();
const store = useKpiDataStore(); const store = useKpiDataStore();

View file

@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue"; import { ref } from "vue";
import { useKpiDataStore } from "@/modules/08_KPI/store"; import { useKpiDataStore } from "@/modules/08_KPI/store";
import { useRoute } from "vue-router";
import Assessment from "@/modules/08_KPI/components/Tab/01_Assessment.vue"; import Assessment from "@/modules/08_KPI/components/Tab/01_Assessment.vue";
import CommanderAbove from "@/modules/08_KPI/components/Tab/02_CommanderAbove.vue"; import CommanderAbove from "@/modules/08_KPI/components/Tab/02_CommanderAbove.vue";
@ -8,6 +9,8 @@ import CommanderAboveOneStep from "@/modules/08_KPI/components/Tab/03_CommanderA
import File from "@/modules/08_KPI/components/Tab/04_File.vue"; import File from "@/modules/08_KPI/components/Tab/04_File.vue";
const store = useKpiDataStore(); const store = useKpiDataStore();
const route = useRoute();
const isReadonly = <boolean>(route.name === "KPIEditEvaluator" ? true : false);
const itemsTab = ref<any>([ const itemsTab = ref<any>([
{ {
@ -30,7 +33,6 @@ const itemsTab = ref<any>([
const splitterModel = ref<number>(12); const splitterModel = ref<number>(12);
</script> </script>
<template> <template>
<q-splitter v-model="splitterModel" disable> <q-splitter v-model="splitterModel" disable>
<template v-slot:before> <template v-slot:before>

View file

@ -26,6 +26,7 @@ const {
success, success,
} = useCounterMixin(); } = useCounterMixin();
const isReadonly = <boolean>(route.name === "KPIEditEvaluator" ? true : false);
const title = defineModel<string>("title", { required: true }); const title = defineModel<string>("title", { required: true });
const rows = defineModel<any>("data", { required: true }); const rows = defineModel<any>("data", { required: true });
const numpage = defineModel<number>("page", { required: true }); const numpage = defineModel<number>("page", { required: true });
@ -190,6 +191,7 @@ watch(
<div class="col"> <div class="col">
<span class="text-weight-medium">{{ title }}</span> <span class="text-weight-medium">{{ title }}</span>
<q-btn <q-btn
v-if="!isReadonly"
class="q-ml-xs" class="q-ml-xs"
flat flat
round round
@ -204,6 +206,7 @@ watch(
</div> </div>
<div class="col-auto"> <div class="col-auto">
<q-btn <q-btn
v-if="!isReadonly"
flat flat
round round
icon="mdi-clipboard-check-outline" icon="mdi-clipboard-check-outline"
@ -239,7 +242,7 @@ watch(
<q-th v-for="col in props.cols" :key="col.name" :props="props"> <q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span> <span class="text-weight-medium">{{ col.label }}</span>
</q-th> </q-th>
<q-th auto-width /> <q-th auto-width v-if="!isReadonly" />
</q-tr> </q-tr>
</template> </template>
<template v-slot:body="props"> <template v-slot:body="props">
@ -286,7 +289,7 @@ watch(
{{ col.value ? col.value : "-" }} {{ col.value ? col.value : "-" }}
</div> </div>
</q-td> </q-td>
<td> <td v-if="!isReadonly">
<q-btn <q-btn
flat flat
round round

View file

@ -15,7 +15,9 @@ import type {
ListCriteria, ListCriteria,
} from "@/modules/08_KPI/interface/request/index"; } from "@/modules/08_KPI/interface/request/index";
const dataListCriteria = defineModel<ListCriteria[]>("dataListCriteria", { required: true, }); const dataListCriteria = defineModel<ListCriteria[]>("dataListCriteria", {
required: true,
});
const sortedDataListCriteria = computed(() => { const sortedDataListCriteria = computed(() => {
return dataListCriteria.value.sort((a, b) => a.level - b.level); return dataListCriteria.value.sort((a, b) => a.level - b.level);
@ -26,6 +28,8 @@ const store = useKpiDataStore();
const route = useRoute(); const route = useRoute();
const id = ref<string>(route.params.id as string); const id = ref<string>(route.params.id as string);
const isReadonly = <boolean>(route.name === "KPIEditEvaluator" ? true : false);
const idCapacity = ref<string | null>(null); const idCapacity = ref<string | null>(null);
const $q = useQuasar(); const $q = useQuasar();
@ -203,6 +207,7 @@ onMounted(() => {
<div class="col"> <div class="col">
<span class="text-weight-medium">{{ item.name }}</span> <span class="text-weight-medium">{{ item.name }}</span>
<q-btn <q-btn
v-if="!isReadonly"
class="q-ml-xs" class="q-ml-xs"
flat flat
round round
@ -218,6 +223,7 @@ onMounted(() => {
<q-space /> <q-space />
<q-btn <q-btn
v-if="!isReadonly"
flat flat
round round
icon="mdi-clipboard-check-outline" icon="mdi-clipboard-check-outline"
@ -249,7 +255,7 @@ onMounted(() => {
<q-th v-for="col in props.cols" :key="col.name" :props="props"> <q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span> <span class="text-weight-medium">{{ col.label }}</span>
</q-th> </q-th>
<q-th auto-width /> <q-th auto-width v-if="!isReadonly" />
</q-tr> </q-tr>
</template> </template>
<template v-slot:body="props"> <template v-slot:body="props">
@ -295,7 +301,7 @@ onMounted(() => {
{{ col.value }} {{ col.value }}
</div> </div>
</q-td> </q-td>
<q-td auto-width> <q-td v-if="!isReadonly">
<q-btn <q-btn
flat flat
round round
@ -335,7 +341,6 @@ onMounted(() => {
v-model:type="typeCompetency" v-model:type="typeCompetency"
v-model:dataListCriteria="dataListCriteria" v-model:dataListCriteria="dataListCriteria"
:get-data="getData" :get-data="getData"
/> />
<div class="row text-body2 text-weight-bold justify-center"> <div class="row text-body2 text-weight-bold justify-center">

View file

@ -13,6 +13,8 @@ import type { FormProfile } from "@/modules/08_KPI/interface/request/index";
const route = useRoute(); const route = useRoute();
const id = ref<string>(route.params.id as string); const id = ref<string>(route.params.id as string);
const isReadonly = <boolean>(route.name === "KPIEditEvaluator" ? true : false);
const store = useKpiDataStore(); const store = useKpiDataStore();
const $q = useQuasar(); const $q = useQuasar();
const mixin = useCounterMixin(); const mixin = useCounterMixin();
@ -111,10 +113,13 @@ onMounted(() => {
flat flat
color="primary" color="primary"
class="q-mr-sm" class="q-mr-sm"
@click="router.push(`/KPI`)" @click="
isReadonly ? router.push(`/KPI-evaluator`) : router.push(`/KPI`)
"
/> />
{{ id ? `แก้ไขแบบประเมิน` : `เพิ่มแบบประเมิน` }} {{ id ? `แก้ไขแบบประเมิน` : `เพิ่มแบบประเมิน` }}
<q-space /> <q-space />
<!-- <q-btn label="บันทึก" color="secondary" unelevated @click="onSave" <!-- <q-btn label="บันทึก" color="secondary" unelevated @click="onSave"
><q-tooltip>นท</q-tooltip></q-btn ><q-tooltip>นท</q-tooltip></q-btn
> --> > -->

View file

@ -1,30 +1,38 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from "vue"; import { ref, onMounted, reactive, watch } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { useQuasar } from "quasar";
import config from "@/app.config";
import http from "@/plugins/http";
import type { DataOptions } from "@/modules/08_KPI/interface/index/Main"; import type { DataOptions } from "@/modules/08_KPI/interface/index/Main";
import type { QTableProps } from "quasar"; import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin"; import { useCounterMixin } from "@/stores/mixin";
import { useKpiDataStore } from "@/modules/08_KPI/store";
const mixin = useCounterMixin() const $q = useQuasar();
const { date2Thai } = mixin
const router = useRouter(); const router = useRouter();
const store = useKpiDataStore();
const { showLoader, hideLoader, messageError, date2Thai, dialogConfirm } =
useCounterMixin();
const filterKeyword = ref<string>(""); const filterKeyword = ref<string>("");
const rows = ref<any>(); const rows = ref<any>();
const round = ref<string>("ID1"); const year = ref<number>(new Date().getFullYear());
const roundOp = ref<DataOptions[]>([
{ const round = ref<string>("");
id: "ID1", const roundOp = ref<DataOptions[]>([]);
name: "รอบเมษา",
}, const visibleColumns = ref<string[]>([
"name",
"createdAt",
"evaluationStatus",
"evaluationStatus",
]); ]);
const visibleColumns = ref<string[]>(["name", "createDate", "status", "result"]);
const columns = ref<QTableProps["columns"]>([ const columns = ref<QTableProps["columns"]>([
{ {
name: "name", name: "name",
@ -38,77 +46,132 @@ const columns = ref<QTableProps["columns"]>([
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }), a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
}, },
{ {
name: "createDate", name: "createdAt",
align: "left", align: "left",
label: "วันที่สร้างแบบประเมิน", label: "วันที่สร้างแบบประเมิน",
sortable: true, sortable: true,
field: "createDate", field: "createdAt",
headerStyle: "font-size: 14px", headerStyle: "font-size: 14px",
style: "font-size: 14px", style: "font-size: 14px",
format: (v) => date2Thai(v),
sort: (a: string, b: string) => sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }), a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
}, },
{ {
name: "status", name: "evaluationStatus",
align: "left", align: "left",
label: "สถานะการประเมิน", label: "สถานะการประเมิน",
sortable: true, sortable: true,
field: "status", field: "evaluationStatus",
headerStyle: "font-size: 14px", headerStyle: "font-size: 14px",
style: "font-size: 14px", style: "font-size: 14px",
format: (v) => store.convertResults(v),
sort: (a: string, b: string) => sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }), a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
}, },
{ {
name: "result", name: "evaluationStatus",
align: "left", align: "left",
label: "ผลการประเมิน", label: "ผลการประเมิน",
sortable: true, sortable: true,
field: "result", field: "evaluationStatus",
headerStyle: "font-size: 14px", headerStyle: "font-size: 14px",
style: "font-size: 14px", style: "font-size: 14px",
format: (v) => store.convertResults(v),
sort: (a: string, b: string) => sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }), a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
}, },
]); ]);
/** ดึงข้อมูล */ const formQuery = reactive({
function getData() { page: 1,
const data = [ pageSize: 10,
{ });
id: "ID1", const totalList = ref<number>(1);
name: "สมศรี ใจดี",
createDate: '2024-02-16T06:01:00.000Z', function fetchRoundOption() {
status: "รอดำเนินการ", showLoader();
result: "ผ่านเกณฑ์การประเมิน", http
}, .get(
{ config.API.kpiPeriod +
id: "ID1", `?page=${1}&pageSize=${10}&keyword=${""}&year=${year.value}`
name: "สมจิตร ใจดี", )
createDate: '2024-02-16T06:01:00.000Z', .then((res) => {
status: "รอดำเนินการ", const data = res.data.result.data;
result: "ผ่านเกณฑ์การประเมิน", const list = data.map((e: any) => ({
}, id: e.id,
]; name:
rows.value = data; e.durationKPI === "OCT"
// showLoader(); ? "รอบตุลาคม"
// await http : e.durationKPI === "APR"
// .get(config.API.orgPrefix) ? "รอบเมษายน"
// .then(async (res) => { : "",
// }) }));
// .catch((err) => { roundOp.value = list;
// messageError($q, err); fetchList();
// }) })
// .finally(() => { .catch((err) => {
// hideLoader(); messageError($q, err);
// }); })
.finally(() => {
hideLoader();
});
} }
function onEdit(id:string){ function fetchList() {
router.push(`/KPI-evaluator/${id}`) showLoader();
http
.get(
config.API.kpiEvaluation +
`/admin?page=${formQuery.page}&pageSize=${formQuery.pageSize}&kpiPeriodId=${round.value}`
)
.then((res) => {
const data = res.data.result;
totalList.value = Math.ceil(data.total / formQuery.pageSize);
rows.value = data.data;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
} }
function changRound() {
formQuery.page = 1;
fetchList();
}
function redirectViewDetail(id: string) {
router.push(`/KPI-evaluator/${id}`);
}
/**
* function updatePagination
* @param newPagination อม Pagination ใหม
*/
function updatePagination(newPagination: any) {
formQuery.page = 1;
formQuery.pageSize = newPagination.rowsPerPage;
}
const pagination = ref({
sortBy: "desc",
descending: false,
page: 1,
rowsPerPage: 10,
});
watch(
() => formQuery.pageSize,
() => {
fetchList();
}
);
onMounted(async () => { onMounted(async () => {
getData(); fetchRoundOption();
}); });
</script> </script>
@ -131,18 +194,57 @@ onMounted(async () => {
<div class="col-12"> <div class="col-12">
<q-card bordered class="q-pa-md"> <q-card bordered class="q-pa-md">
<q-toolbar style="padding: 0"> <q-toolbar style="padding: 0">
<q-select <div class="row q-gutter-sm">
v-model="round" <datepicker
outlined menu-class-name="modalfix"
label="รอบการประเมิน" v-model="year"
dense :locale="'th'"
option-label="name" autoApply
option-value="id" year-picker
:options="roundOp" :enableTimePicker="false"
style="min-width: 200px" @update:model-value="fetchRoundOption()"
emit-value >
map-options <template #year="{ year }">{{ year + 543 }}</template>
/> <template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
lazy-rules
outlined
class="inputgreen"
hide-bottom-space
:model-value="!!year ? year + 543 : null"
: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-select
v-model="round"
outlined
label="รอบการประเมิน"
dense
option-label="name"
option-value="id"
:options="roundOp"
style="min-width: 200px"
emit-value
map-options
@update:model-value="changRound"
/>
</div>
<q-space /> <q-space />
<div class="row q-gutter-sm"> <div class="row q-gutter-sm">
<q-input <q-input
@ -167,41 +269,67 @@ onMounted(async () => {
/> />
</div> </div>
</q-toolbar> </q-toolbar>
<div class="col-12">
<q-table <q-table
ref="table" ref="table"
:columns="columns" :columns="columns"
:rows="rows" :rows="rows"
:filter="filterKeyword" :filter="filterKeyword"
row-key="id" row-key="id"
flat flat
bordered bordered
:paging="true" :paging="true"
dense dense
class="custom-table2" class="custom-table2"
:visible-columns="visibleColumns" :rows-per-page-options="[10, 25, 50, 100]"
> :visible-columns="visibleColumns"
<template v-slot:header="props"> v-model:pagination="pagination"
<q-tr :props="props"> @update:pagination="updatePagination"
<q-th v-for="col in props.cols" :key="col.name" :props="props" > >
<span class="text-weight-medium">{{ col.label }}</span> <template v-slot:header="props">
</q-th> <q-tr :props="props">
<q-th auto-width /> <q-th
</q-tr> v-for="col in props.cols"
</template> :key="col.name"
<template v-slot:body="props"> :props="props"
<q-tr :props="props" class="cursor-pointer"> >
<q-td v-for="col in props.cols" :key="col.id" @click="onEdit(props.row.id)"> <span class="text-weight-medium">{{ col.label }}</span>
<div v-if="col.name == 'createDate'"> </q-th>
{{ col.value ? date2Thai(col.value):'-' }} </q-tr>
</div> </template>
<div v-else> <template v-slot:body="props">
{{ col.value }} <q-tr :props="props" class="cursor-pointer">
</div> <q-td
</q-td> v-for="col in props.cols"
</q-tr> :key="col.id"
</template> @click="redirectViewDetail(props.row.id)"
</q-table> >
<div v-if="col.name === 'name'">
{{
`${props.row.prefix}${props.row.firstname} ${props.row.lastname}`
}}
</div>
<div v-else>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
<template v-slot:pagination="scope">
<q-pagination
v-model="formQuery.page"
active-color="primary"
color="dark"
:max="Number(totalList)"
size="sm"
boundary-links
direction-links
:max-pages="5"
@update:model-value="fetchList"
></q-pagination>
</template>
</q-table>
</div>
</q-card> </q-card>
</div> </div>
</div> </div>